Skip to content

Commit 062bdb8

Browse files
committed
chore: overhaul provider example
This is a complete redesign of the provider example, intended to build on top of the consumer example. It (hopefully) provides a reference as to how the FFI is to be used, on both consumer and provider sides. Signed-off-by: JP-Ellis <josh@jpellis.me>
1 parent 22890a1 commit 062bdb8

17 files changed

Lines changed: 2086 additions & 38 deletions

File tree

c/consumer/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ This project uses Conan for dependency management and CMake for configuration an
2727
just run
2828
```
2929

30-
The results of the tests are printed to stdout and will exit with a non-zero status if any tests fail.
30+
The results of the tests are printed to stdout and will exit with a non-zero status if any tests fail. On success, the generated Pact contract files will be available in the `pacts/` directory.
3131

3232
## Prerequisites
3333

c/justfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
build:
22
just consumer/build
3+
just provider/build
34

45
clean:
56
just consumer/clean
7+
just provider/clean
68

79
run: build
810
just consumer/run
11+
just provider/run

c/provider-verification/CMakeLists.txt

Lines changed: 0 additions & 12 deletions
This file was deleted.

c/provider-verification/src/main.c

Lines changed: 0 additions & 25 deletions
This file was deleted.

c/provider/CMakeLists.txt

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
2+
################################################################################
3+
# Preamble
4+
################################################################################
5+
6+
# Minimum CMake version
7+
cmake_minimum_required(VERSION 3.24)
8+
9+
# Include Conan toolchain if it exists
10+
if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/conan_toolchain.cmake")
11+
include("${CMAKE_CURRENT_BINARY_DIR}/conan_toolchain.cmake")
12+
endif()
13+
14+
# We use [corrosion](https://github.com/corrosion-rs/corrosion) to integrate
15+
# Rust into our C project.
16+
include(FetchContent)
17+
FetchContent_Declare(
18+
Corrosion
19+
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
20+
GIT_TAG v0.5
21+
)
22+
FetchContent_MakeAvailable(Corrosion)
23+
24+
get_filename_component(PACT_FFI_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../rust/pact_ffi" ABSOLUTE)
25+
26+
################################################################################
27+
# Project definition
28+
################################################################################
29+
project(
30+
pact-provider
31+
DESCRIPTION "PACT Provider in C"
32+
LANGUAGES C
33+
)
34+
35+
################################################################################
36+
# Dependencies
37+
################################################################################
38+
39+
# cJSON is used for JSON parsing and generation
40+
find_package(cJSON REQUIRED)
41+
42+
# libmicrohttpd is used to create a simple HTTP server for the provider
43+
find_package(libmicrohttpd REQUIRED)
44+
45+
# Additional dependencies of the Pact FFI
46+
find_package(Threads REQUIRED)
47+
find_package(LibLZMA REQUIRED)
48+
find_package(BZip2 REQUIRED)
49+
find_package(ZLIB REQUIRED)
50+
51+
# The Pact FFI is a Rust project
52+
corrosion_import_crate(
53+
MANIFEST_PATH "${PACT_FFI_ROOT}/Cargo.toml"
54+
)
55+
corrosion_experimental_cbindgen(
56+
TARGET pact_ffi
57+
HEADER_NAME pact.h
58+
)
59+
60+
target_link_libraries(
61+
pact_ffi
62+
INTERFACE
63+
Threads::Threads
64+
LibLZMA::LibLZMA
65+
BZip2::BZip2
66+
ZLIB::ZLIB
67+
)
68+
69+
################################################################################
70+
# Targets
71+
################################################################################
72+
file(GLOB_RECURSE SRC_FILES src/*.c)
73+
add_executable(pact-provider ${SRC_FILES})
74+
75+
# Set C standard properties
76+
set_property(TARGET pact-provider PROPERTY C_STANDARD 17)
77+
set_property(TARGET pact-provider PROPERTY C_STANDARD_REQUIRED ON)
78+
79+
target_include_directories(pact-provider PRIVATE ${CMAKE_SOURCE_DIR}/include)
80+
81+
target_link_libraries(
82+
pact-provider
83+
PRIVATE
84+
pact_ffi
85+
cjson::cjson
86+
libmicrohttpd::libmicrohttpd
87+
)
88+
89+
# Link Windows system libraries if building on Windows
90+
# These are needed by the sysinfo crate used by pact_ffi
91+
if(WIN32)
92+
target_link_libraries(
93+
pact-provider
94+
PRIVATE
95+
pdh # Performance Data Helper (Pdh* functions)
96+
netapi32 # Network Management API (NetUser*, NetGroup* functions)
97+
powrprof # Power Management (CallNtPowerInformation)
98+
psapi # Process Status API (GetProcessMemoryInfo, GetModuleFileNameExW)
99+
ws2_32 # Winsock 2 (socket functions)
100+
)
101+
endif()
102+
103+
# Link macOS system frameworks if building on Apple platforms
104+
if(APPLE)
105+
target_link_libraries(
106+
pact-provider
107+
PRIVATE
108+
"$<LINK_LIBRARY:FRAMEWORK,Security>"
109+
"$<LINK_LIBRARY:FRAMEWORK,CoreFoundation>"
110+
"$<LINK_LIBRARY:FRAMEWORK,IOKit>"
111+
)
112+
endif()

c/provider/README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Pact C Provider Example (`c/provider`)
2+
3+
This directory serves two purposes:
4+
5+
1. **Example for C Providers:** Demonstrates how to use the Pact FFI (Foreign Function Interface) in C to verify a provider implementation against consumer contracts.
6+
2. **Reference for FFI Maintainers:** Provides a working example for maintainers of libraries in other languages that use the Pact FFI internally, showing how to implement provider verification.
7+
8+
It includes utilities, example provider implementation, and contract verification code for both use cases.
9+
10+
## Directory Structure
11+
12+
- `include/`: Header files for the provider code
13+
- `src/`: Source files for the provider implementation:
14+
- `main.c`: Main entry point that starts the HTTP server and runs verification
15+
- `logging.c`, `http_server.c`: Utility implementations
16+
- `provider/`: Provider-specific code including state handlers and HTTP endpoint handlers
17+
- `CMakeLists.txt`: CMake build configuration
18+
- `justfile`: Justfile for building and running the provider verification
19+
- `conanfile.txt`: Conan configuration for dependencies
20+
21+
## Building and Running
22+
23+
This project uses Conan for dependency management and CMake for configuration and building. It also provides a justfile for convenience:
24+
25+
```console
26+
just run
27+
```
28+
29+
The results of the verification tests are printed to stdout and will exit with a non-zero status if verification fails.
30+
31+
It assumes that the consumer contracts have already been generated by running the consumer tests in the `c/consumer` directory (or calling `just run` in the parent directory which runs both consumer and provider).
32+
33+
## Prerequisites
34+
35+
The verification tests require:
36+
37+
- A C and Rust compiler
38+
- CMake (version 3.24 or higher)
39+
- Conan (version 2.0 or higher)
40+
41+
The CMake configuration will automatically build the Pact FFI library from the Rust source, hence the need for the Rust toolchain. In practice, you would typically download the pre-built binaries for your platform from the [Pact FFI releases](https://github.com/pact-foundation/pact-reference/releases), and link against those instead.
42+
43+
All other dependencies (libmicrohttpd for HTTP server) will be automatically downloaded and/or built by Conan as needed.
44+
45+
### Installing Conan
46+
47+
If you don't have Conan installed, you can install it via `uv`:
48+
49+
```console
50+
uv tool install conan
51+
```
52+
53+
For more installation options, see the [Conan installation documentation](https://docs.conan.io/2/installation.html).
54+
55+
## How Provider Verification Works
56+
57+
Provider verification in Pact ensures that the provider implementation satisfies the contracts defined by consumers. The process:
58+
59+
1. **Start Provider:** Launch the actual HTTP server that implements the provider API
60+
2. **Configure Verifier:** Set up the Pact verifier with provider details and contract files
61+
3. **Setup Provider States:** For each interaction, configure the provider to be in the required state (e.g., "user exists")
62+
4. **Run Verification:** The verifier replays each interaction from the contract against the running provider
63+
5. **Report Results:** Output verification results and any mismatches
64+
65+
This example demonstrates all these steps using the Pact FFI in C.

c/provider/conanfile.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[requires]
2+
cjson/[~1]
3+
libmicrohttpd/[~0.9]
4+
xz_utils/[~5]
5+
bzip2/[~1]
6+
zlib/[~1]
7+
8+
[generators]
9+
CMakeDeps
10+
CMakeToolchain

0 commit comments

Comments
 (0)