Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
cmake_minimum_required (VERSION 3.16.3)
project (TESTCONTAINERS-C)

include(CTest)

include_directories(${CMAKE_CURRENT_BINARY_DIR}/testcontainers-c)

add_subdirectory(testcontainers-c)
add_subdirectory(modules)
if(NOT DEFINED SKIP_DEMOS)
add_subdirectory(demo)
endif()
cmake_minimum_required (VERSION 3.26)
project (TESTCONTAINERS-C
VERSION 0.1.0
DESCRIPTION "Testcontainers for C and other native languages"
LANGUAGES C CXX
)

include(GNUInstallDirs)
include(CTest)

add_subdirectory(testcontainers-bridge)
add_subdirectory(testcontainers-c)
add_subdirectory(testcontainers-cpp)
add_subdirectory(modules)
if(NOT DEFINED SKIP_DEMOS)
add_subdirectory(demo)
endif()
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ cmake -DSKIP_DEMOS=true .
## Contributing to the Documentation

The documentation is structured in the MkDocs format and uses Material for MkDocs.
To develop the site in this repository, start it in the [Dev Containers](.devcontainer/README.md)
To develop the site in this repository, start it in a [Dev Container](.devcontainer/README.md)
and use the following commands:

```shell
Expand All @@ -57,4 +57,4 @@ mkdocs build
- `tc_` is used as a prefix for all exported Testcontainers functions
- When possible, we try to avoid special Golang types in public API and try to expose wrapper types
- `const` is important for users, and please add it to your arguments when possible.
There is no Const in Golang, so some `typedef` injection is needed when importing CGo
There is no `const` in Golang, so some `typedef` injection is needed when importing CGo
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@
[![Stability: Experimental](https://masterminds.github.io/stability/experimental.svg)](https://masterminds.github.io/stability/experimental.html)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/oleg-nenashev/testcontainers-c)](https://github.com/oleg-nenashev/testcontainers-c/releases)

!!! warning
This is a prototype.
There is a lot to do before it can be distributed and used in production, see the GitHub Issues
and the [project roadmap](./ROADMAP.md)
> [!WARNING]
> This is a prototype. There is a lot to do before it can be distributed and used in production, see the GitHub Issues and the [project roadmap](./ROADMAP.md)

This is not a standalone [Testcontainers](https://testcontainers.org/) engine,
but a C-style shared library adapter for native languages like C/C++, D, Lua, Swift, etc.
Expand Down Expand Up @@ -88,9 +86,7 @@ describes how it can be done in principle.
## Credits

Using a complex Golang framework from C/C++ is not trivial.
Neither the CMake files are.
This project would not succeed without many quality articles
and help from the community.
Neither are the CMake files. This project would not succeed without many quality articles and help from the community.

Kudos to:

Expand All @@ -102,7 +98,7 @@ Kudos to:
[An Adventure into CGO - Calling Go code with C](https://medium.com/@ben.mcclelland/an-adventure-into-cgo-calling-go-code-with-c-b20aa6637e75)
- [Insu Jang](https://github.com/insujang) for
[Implementing Kubernetes C++ Client Library using Go Client Library](https://insujang.github.io/2019-11-28/implementing-kubernetes-cpp-client-library)
- Infinite number of StackOverflow contributors
- An infinite number of StackOverflow contributors

## Discuss

Expand Down
1 change: 1 addition & 0 deletions demo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
add_subdirectory(generic-container)
add_subdirectory(wiremock)
add_subdirectory(google-test)
add_subdirectory(google-test-cpp)
14 changes: 7 additions & 7 deletions demo/generic-container/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
project(testcontainers-c-generic-container-demo
VERSION 0.0.1
DESCRIPTION "Demonstrates usage of the generic container API in a simple main app")
cmake_minimum_required (VERSION 3.26)
project (generic-container-demo
VERSION 0.1.0
DESCRIPTION "Demonstrates usage of the generic container API in a simple main app"
LANGUAGES C
)

set(TARGET_OUT demo_generic_container.out)
set(TARGET_OUT ${PROJECT_NAME}.out)

include_directories(${testcontainers-c_SOURCE_DIR})
# WORKING_DIRECTORY breaks shared lib loading
file(COPY test_data DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

# Vanilla Demo for WireMock
add_executable(${TARGET_OUT} generic_container_demo.c)
add_dependencies(${TARGET_OUT} testcontainers-c-shim)
target_link_libraries(${TARGET_OUT} PRIVATE testcontainers-c)
add_test(NAME generic_container_demo COMMAND ${TARGET_OUT})
31 changes: 16 additions & 15 deletions demo/generic-container/generic_container_demo.c
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
#include <stdio.h>
#include "testcontainers-c.h"
#include "testcontainers-c/container.h"

#define DEFAULT_IMAGE "wiremock/wiremock:3.0.1-1"

int main() {
printf("Using WireMock with the Testcontainers C binding:\n");

printf("Creating new container: %s\n", DEFAULT_IMAGE);
int requestId = tc_new_container_request(DEFAULT_IMAGE);
tc_with_exposed_tcp_port(requestId, 8080);
tc_with_wait_for_http(requestId, 8080, "/__admin/mappings");
tc_with_file(requestId, "test_data/hello.json", "/home/wiremock/mappings/hello.json");
struct tc_run_container_return ret = tc_run_container(requestId);
int containerId = ret.r0;
if (!ret.r1) {
printf("Failed to run the container: %s\n", ret.r2);
int requestId = tc_container_create(DEFAULT_IMAGE);
tc_container_with_exposed_tcp_port(requestId, 8080);
tc_container_with_wait_for_http(requestId, 8080, "/__admin/mappings");
tc_container_with_file(requestId, "test_data/hello.json", "/home/wiremock/mappings/hello.json");
char* error;
int containerId = tc_container_run(requestId, error);
if (containerId == -1) {
printf("Failed to run the container: %s\n", error);
return -1;
}

printf("Sending HTTP request to the container\n");
struct tc_send_http_get_return response = tc_send_http_get(containerId, 8080, "/hello");
if (response.r0 == -1) {
printf("Failed to send HTTP request: %s\n", response.r2);
char *response_body;
int response_code = tc_container_send_http_get(containerId, 8080, "/hello", response_body, error);
if (response_code == -1) {
printf("Failed to send HTTP request: %s\n", error);
return -1;
}
if (response.r0 != 200) {
printf("Received wrong response code: %d instead of %d\n%s\n%s\n", response.r0, 200, response.r1, response.r2);
if (response_code != 200) {
printf("Received wrong response code: %d instead of %d\n%s\n%s\n", response_code, 200, response_body, error);
return -1;
}
printf("Server Response: HTTP-%d\n%s\n\n", response.r0, response.r1);
printf("Server Response: HTTP-%d\n%s\n\n", response_code, response_body);
return 0;
}

32 changes: 32 additions & 0 deletions demo/google-test-cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Google Test demo
# This is based on https://google.github.io/googletest/quickstart-cmake.html
cmake_minimum_required (VERSION 3.26)
project (google-test-cpp-demo
VERSION 0.1.0
DESCRIPTION "Demonstrates usage of Testcontainers C++ in Google Test"
LANGUAGES CXX
)

set(TARGET_OUT ${PROJECT_NAME}.out)

# GoogleTest requires at least C++14
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.17.0
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

enable_testing()
file(COPY test_data DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

add_executable(${TARGET_OUT} test.cpp)
target_link_libraries(${TARGET_OUT} PRIVATE testcontainers-cpp)
target_link_libraries(${TARGET_OUT} PRIVATE GTest::gtest_main)

include(GoogleTest)
gtest_discover_tests(${TARGET_OUT})
28 changes: 28 additions & 0 deletions demo/google-test-cpp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Using Testcontainers C in Google Test

Demonstrates usage of Testcontainers C in [Google Test](https://github.com/google/googletest).
See [test.cpp](./test.cpp) for the code.

## Run the demo

```bash
cmake .
cmake --build .
cd demo/google-test
ctest --output-on-failure
```

## Sample output

```shell
onenashev:~/testcontainers-c/demo/google-test$ ctest --output-on-failure
Test project /home/onenashev/testcontainers-c/demo/google-test
Start 1: WireMockTestContainer.HelloWorld
1/5 Test #1: WireMockTestContainer.HelloWorld ...................... Passed 3.31 sec
Start 2: WireMockTestContainer.HelloWorldFromResource
2/5 Test #2: WireMockTestContainer.HelloWorldFromResource .......... Passed 3.97 sec
Start 3: WireMockTestContainer.HelloWorldFromMissingResource
3/5 Test #3: WireMockTestContainer.HelloWorldFromMissingResource ... Passed 3.91 sec

100% tests passed, 0 tests failed out of 3
```
61 changes: 61 additions & 0 deletions demo/google-test-cpp/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include <iostream>
#include <string>
#include <gtest/gtest.h>
#include "testcontainers.hpp"

using namespace testcontainers;

class WireMockTestContainerClass : public ::testing::Test {

const char* WIREMOCK_IMAGE = "wiremock/wiremock:3.0.1-1";
const char* WIREMOCK_ADMIN_MAPPING_ENDPOINT = "/__admin/mappings";

protected:
void SetUp() override {
std::cout << "Creating new container: " << WIREMOCK_IMAGE << '\n';

container = std::make_unique<Container>(WIREMOCK_IMAGE);
container->expose_port(TcpPort{8080});
container->wait_for_http(TcpPort{8080}, WIREMOCK_ADMIN_MAPPING_ENDPOINT);
container->with_file("test_data/hello.json", "/home/wiremock/mappings/hello.json");
container->with_file("test_data/hello_with_resource.json", "/home/wiremock/mappings/hello2.json");
container->with_file("test_data/hello_with_missing_resource.json", "/home/wiremock/mappings/hello3.json");
container->with_file("test_data/response.xml", "/home/wiremock/__files/response.xml");

auto result = container->run();

EXPECT_TRUE(!result.has_value()) << "Failed to run the container: " << result.error();
};

std::unique_ptr<Container> container;
};

TEST_F(WireMockTestContainerClass, HelloWorld) {
std::cout << "Sending HTTP request to the container\n";

auto [code, response] = container->send_http(HttpMethod::Get, TcpPort{8080}, "/hello");

ASSERT_TRUE(response.has_value()) << "Failed to send HTTP request: " << response.error();
ASSERT_EQ(code, 200) << "Received wrong response code: " << response.error();

std::cout << "Server Response: HTTP-" << code << '\n' << response.value() << '\n';
}

TEST_F(WireMockTestContainerClass, HelloWorldFromResource) {
std::cout << "Sending HTTP request to the container\n";

auto [code, response] = container->send_http(HttpMethod::Get, TcpPort{8080}, "/hello-from-resource");

ASSERT_TRUE(response.has_value()) << "Failed to send HTTP request: " << response.error();
ASSERT_EQ(code, 200) << "Received wrong response code: " << response.error();

std::cout << "Server Response: HTTP-" << code << '\n' << response.value() << '\n';
}

TEST_F(WireMockTestContainerClass, HelloWorldFromMissingResource) {
std::cout << "Sending HTTP request to the container\n";

auto [code, response] = container->send_http(HttpMethod::Get, TcpPort{8080}, "/hello-from-missing-resource");

ASSERT_EQ(code, 500) << "The request should have failed";
}
12 changes: 12 additions & 0 deletions demo/google-test-cpp/test_data/hello.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"request": {
"method": "GET",
"url": "/hello"
},

"response": {
"status": 200,
"body": "Hello, world!"
}
}

10 changes: 10 additions & 0 deletions demo/google-test-cpp/test_data/hello_with_missing_resource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"request": {
"method": "GET",
"url": "/hello-from-missing-resource"
},
"response": {
"status": 200,
"bodyFileName": "response_missing.xml"
}
}
10 changes: 10 additions & 0 deletions demo/google-test-cpp/test_data/hello_with_resource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"request": {
"method": "GET",
"url": "/hello-from-resource"
},
"response": {
"status": 200,
"bodyFileName": "response.xml"
}
}
6 changes: 6 additions & 0 deletions demo/google-test-cpp/test_data/response.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<note>
<to>you</to>
<from>WireMock</from>
<heading>Response</heading>
<body>Hello, world!</body>
</note>
13 changes: 8 additions & 5 deletions demo/google-test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Google Test demo
# This is based on https://google.github.io/googletest/quickstart-cmake.html
project(google-test-demo
VERSION 0.0.1
DESCRIPTION "Demonstrates usage of Testcontainers C in Google Test")
cmake_minimum_required (VERSION 3.26)
project (google-test-demo
VERSION 0.1.0
DESCRIPTION "Demonstrates usage of Testcontainers C in Google Test"
LANGUAGES CXX
)

set(TARGET_OUT ${PROJECT_NAME}.out)

Expand All @@ -12,7 +15,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.17.0
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
Expand All @@ -21,7 +25,6 @@ enable_testing()
file(COPY test_data DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

add_executable(${TARGET_OUT} test.cpp)
add_dependencies(${TARGET_OUT} testcontainers-c-shim)
target_link_libraries(${TARGET_OUT} PRIVATE testcontainers-c)
target_link_libraries(${TARGET_OUT} PRIVATE GTest::gtest_main)

Expand Down
Loading
Loading