Host based test automation for C/C++
- About
- Quick Start
- Sample Project
- Prerequisites
- Setup Instructions
- Usage
- CMake Variables
- Testing the CMake Scripts
- License
hosta is a CMake framework for building and running C/C++ unit tests on the host platform, even when your project uses a cross-compilation toolchain.
In cross-compilation environments (e.g., building for ARM with arm-none-eabi-gcc), unit tests cannot run on the developer's machine because the binaries are built for a different target. hosta solves this by automatically detecting and using the host compiler alongside the cross-compiler within the same CMake build, enabling you to compile and execute tests locally.
- Automatic host compiler detection independent of the cross-compilation toolchain
- Dual-targeting: cross-compile for the target platform and build tests for the host in one build
- Static, shared, and interface host libraries with full dependency management
- Shared library versioning (VERSION/SOVERSION), soname, and automatic RPATH handling
- Integration with Unity and Google Test frameworks
- Test registration and execution through CTest
Below is a minimal example showing how hosta enables host-based testing in a cross-compilation project:
cmake_minimum_required(VERSION 3.17)
# Cross-compiler toolchain configuration
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
project(my_project LANGUAGES C)
include(cmake/HostTest.cmake)
enable_testing()
# Build a host library for testing
add_host_library(mylib STATIC
SOURCES src/mylib.c
INCLUDE_DIRECTORIES PUBLIC src
)
# Build and register a test executable for the host
add_host_executable(mylib_test
SOURCES test/mylib_test.c
LINK_LIBRARIES PRIVATE Host::mylib Host::unity
)
add_host_test(Host::mylib_test)cmake -S . -B build
cmake --build build --target host-targets
cd build && ctestThe sample/ directory contains an expression calculator that demonstrates all major hosta features in a realistic project. It parses and evaluates mathematical expressions like "1 + 2 * (3 - 4)".
# INTERFACE library: shared error types (header-only)
add_host_library(error INTERFACE
INCLUDE_DIRECTORIES PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/error
)
# STATIC library: tokenizer
add_host_library(tokenizer STATIC
SOURCES tokenizer/tokenizer.c
INCLUDE_DIRECTORIES PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/tokenizer
LINK_LIBRARIES PUBLIC Host::error
)
# SHARED library: evaluator (with versioning)
add_host_library(eval SHARED
SOURCES evaluator/evaluator.c
INCLUDE_DIRECTORIES PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/evaluator
LINK_LIBRARIES PUBLIC Host::parser Host::tokenizer Host::error
VERSION 1.0.0
SOVERSION 1
)
# Host executable
add_host_executable(calc
SOURCES calculator/main.c
LINK_LIBRARIES PRIVATE Host::eval Host::parser Host::tokenizer Host::error
)| Library Type | Target | Description |
|---|---|---|
INTERFACE |
error |
Shared error types (header-only) |
STATIC |
tokenizer, parser |
Static archives for tokenization and parsing |
SHARED |
eval (VERSION 1.0.0) |
Shared library with versioning and soname |
Tests cover all three supported frameworks:
- Unity — tokenizer unit tests (
add_host_test) - Unity Fixture — parser tests with groups (
unity_fixture_add_host_tests) - Google Test — evaluator and end-to-end integration tests (
gtest_add_host_tests)
cd sample
cmake -S . -B build
cmake --build build --target host-targets
cd build && ctest --output-on-failureSee sample/README.md for full documentation.
- CMake (3.17 or higher)
- C/C++ host compiler toolchain (e.g., GCC, Clang)
The following additional packages are required for running the test suite:
- build-essential, gcc, g++, clang
- gcc-mingw-w64, g++-mingw-w64 (cross-compilation tests)
- ninja-build
- gcovr (coverage reports)
- python3, python3-pytest, python3-pytest-xdist
To streamline the setup process, you can build a Docker image with all the necessary packages:
docker build -t host-test-dev .
docker run --rm -it -v `pwd`:/test host-test-dev
cd /testTo integrate hosta into your project, follow these steps:
- Copy the files from the
cmake/directory to your project's CMake script directory. - Add the following line to your top-level
CMakeLists.txtfile:
include(cmake/HostTest.cmake)To define an executable target for the host platform, use the add_host_executable function:
add_host_executable(<target>
[SOURCES <source>...]
[INCLUDE_DIRECTORIES <PRIVATE|PUBLIC> <include_directory>...]
[COMPILE_OPTIONS <PRIVATE|PUBLIC> <compile_option>...]
[LINK_OPTIONS <PRIVATE|PUBLIC> <link_option>...]
[LINK_LIBRARIES <PRIVATE|PUBLIC> <library>...]
[DEPENDS <depend>...]
[EXCLUDE_FROM_ALL]
)| Parameter | Description |
|---|---|
target |
Name of the executable target |
SOURCES |
List of source files |
INCLUDE_DIRECTORIES |
List of include directories |
COMPILE_OPTIONS |
List of compile options |
LINK_OPTIONS |
List of link options |
LINK_LIBRARIES |
List of host libraries |
DEPENDS |
List of dependencies |
EXCLUDE_FROM_ALL |
Do not include the binary in the default build target |
Scope: Arguments following both
PRIVATEandPUBLICare used to build the current target.
To define a library target for the host platform, use the add_host_library function:
add_host_library(<target> <type>
[SOURCES <source>...]
[INCLUDE_DIRECTORIES <PRIVATE|PUBLIC> <include_directory>...]
[COMPILE_OPTIONS <PRIVATE|PUBLIC> <compile_option>...]
[LINK_OPTIONS <PRIVATE|PUBLIC> <link_option>...]
[LINK_LIBRARIES <PRIVATE|PUBLIC> <library>...]
[DEPENDS <depend>...]
[VERSION <version>]
[SOVERSION <soversion>]
[EXCLUDE_FROM_ALL]
)| Parameter | Description |
|---|---|
target |
Name of the library target |
type |
Type of the library (STATIC, SHARED, or INTERFACE) |
SOURCES |
List of source files (INTERFACE library requires no source files) |
INCLUDE_DIRECTORIES |
List of include directories |
COMPILE_OPTIONS |
List of compile options |
LINK_OPTIONS |
List of link options |
LINK_LIBRARIES |
List of host libraries |
DEPENDS |
List of dependencies |
VERSION |
Library version for SHARED libraries (e.g., 1.2.3) |
SOVERSION |
SO version for SHARED libraries (e.g., 1) |
EXCLUDE_FROM_ALL |
Do not include the binary in the default build target |
Scope: Arguments following both
PRIVATEandPUBLICare used to build the current target. Arguments followingPUBLICare also used to build another target that links to the current target.
| Type | Description |
|---|---|
STATIC |
A static archive (.a). Requires SOURCES. |
SHARED |
A shared library (.so/.dylib/.dll). Requires SOURCES. Automatically compiles with position-independent code (-fPIC). Supports VERSION and SOVERSION for soname and symlink management. |
INTERFACE |
A header-only library. Does not require SOURCES. Only PUBLIC properties are used. |
add_host_library(mylib SHARED
SOURCES src/mylib.c
INCLUDE_DIRECTORIES PUBLIC src
VERSION 1.2.3
SOVERSION 1
)
add_host_executable(mylib_test
SOURCES test/mylib_test.c
LINK_LIBRARIES PRIVATE Host::mylib
)When VERSION and SOVERSION are specified, the build produces:
libmylib.so.1.2.3— the actual shared librarylibmylib.so.1— soname symlinklibmylib.so— development symlink
RPATH is automatically set so that executables linked against host shared libraries can find them at runtime.
The host functions create target names with the virtual namespace prefix Host:: to distinguish them from ordinary target names. Use the Host:: prefix when defining dependencies between host targets:
add_host_executable(hello
SOURCES hello.c
LINK_LIBRARIES Host::world
)
add_host_library(world STATIC
SOURCES world.c
)- Only direct dependencies between host targets are allowed. Indirect dependencies are not properly reflected.
- Only host libraries are allowed for
LINK_LIBRARIES. Non-host libraries are not permitted even if they are host-compatible. Non-existing host libraries cause build failures.
To add an executable target as a test with CTest, use the add_host_test function:
add_host_test(<target> [PREFIX <prefix>] [EXTRA_ARGS <extra_args>...])| Parameter | Description |
|---|---|
target |
Name of the executable target created with add_host_executable |
PREFIX |
Prefix to be prepended to the test case name |
EXTRA_ARGS |
Additional arguments to pass on the command line |
To automatically add an executable target as tests with CTest by scanning the source code for Unity fixture test macros, use the unity_fixture_add_host_tests function:
unity_fixture_add_host_tests(<target> [PREFIX <prefix>] [EXTRA_ARGS <extra_args>...])| Parameter | Description |
|---|---|
target |
Name of the executable target created with add_host_executable |
PREFIX |
Prefix to be prepended to the name of each test case |
EXTRA_ARGS |
Additional arguments to pass on the command line |
To dynamically discover tests at CTest runtime, use the unity_fixture_discover_host_tests function:
unity_fixture_discover_host_tests(<target>
[PREFIX <prefix>]
[WORKING_DIRECTORY <directory>]
[TEST_LIST <name>]
[DISCOVERY_TIMEOUT <second>]
[EXTRA_ARGS <extra_args>...]
[PROPERTIES <properties>...]
)| Parameter | Description |
|---|---|
target |
Name of the executable target created with add_host_executable |
PREFIX |
Prefix to be prepended to the name of each test case |
WORKING_DIRECTORY |
Directory in which to run the discovered tests |
TEST_LIST |
Variable name to store the list of tests (default: <target>_TESTS) |
DISCOVERY_TIMEOUT |
How long (in seconds) CMake will wait for the executable to enumerate available tests |
EXTRA_ARGS |
Extra arguments to pass on the command line to each test case |
PROPERTIES |
Additional properties to be set on all discovered tests |
Note: This feature requires the
-d(dry-run) option provided by Unity fixture. Make sure that your Unity fixture version supports this option before using the feature. See Unity fixture for details.
To automatically add an executable target as tests with CTest by scanning the source code for Google Test macros, use the gtest_add_host_tests function:
gtest_add_host_tests(<target> [PREFIX <prefix>] [EXTRA_ARGS <extra_args>...])| Parameter | Description |
|---|---|
target |
Name of the executable target created with add_host_executable |
PREFIX |
Prefix to be prepended to the name of each test case |
EXTRA_ARGS |
Additional arguments to pass on the command line |
To dynamically discover tests at CTest runtime, use the gtest_discover_host_tests function:
gtest_discover_host_tests(<target>
[PREFIX <prefix>]
[NO_PRETTY_TYPES]
[NO_PRETTY_VALUES]
[WORKING_DIRECTORY <directory>]
[TEST_LIST <name>]
[DISCOVERY_TIMEOUT <second>]
[EXTRA_ARGS <extra_args>...]
[PROPERTIES <properties>...]
)| Parameter | Description |
|---|---|
target |
Name of the executable target created with add_host_executable |
PREFIX |
Prefix to be prepended to the name of each test case |
NO_PRETTY_TYPES |
Use the type index instead of the actual type name in type-parameterized test names |
NO_PRETTY_VALUES |
Use the value index instead of the actual value in value-parameterized test names |
WORKING_DIRECTORY |
Directory in which to run the discovered tests |
TEST_LIST |
Variable name to store the list of tests (default: <target>_TESTS) |
DISCOVERY_TIMEOUT |
How long (in seconds) CMake will wait for the executable to enumerate available tests |
EXTRA_ARGS |
Extra arguments to pass on the command line to each test case |
PROPERTIES |
Additional properties to be set on all discovered tests |
The following CMake variables can be used to configure internal behaviors. ${lang} refers to C or CXX.
| Variable | Description |
|---|---|
CMAKE_HOST${lang}_COMPILER_LIST |
Compiler candidates for host compiler detection |
CMAKE_HOST${lang}_STANDARD |
Language standard version (e.g., 11, 14, 17) |
CMAKE_HOST${lang}_EXTENSIONS |
Whether compiler-specific extensions are enabled |
CMAKE_HOST${lang}_FLAGS |
Global compiler flags |
CMAKE_HOST${lang}_OUTPUT_EXTENSION |
Extension for object files |
ENABLE_HOST_LANGUAGES |
Preferred host languages (default: C CXX) |
| Variable | Description |
|---|---|
CMAKE_HOST_EXE_LINKER_FLAGS |
Global linker flags for executables |
CMAKE_HOST_STATIC_LINKER_FLAGS |
Global linker flags for static libraries |
CMAKE_HOST_SHARED_LINKER_FLAGS |
Global linker flags for shared libraries |
CMAKE_HOST_EXECUTABLE_SUFFIX |
Extension for executable files |
CMAKE_HOST_STATIC_LIBRARY_PREFIX |
Prefix for static libraries |
CMAKE_HOST_STATIC_LIBRARY_SUFFIX |
Extension for static libraries |
CMAKE_HOST_SHARED_LIBRARY_PREFIX |
Prefix for shared libraries |
CMAKE_HOST_SHARED_LIBRARY_SUFFIX |
Extension for shared libraries |
CMAKE_HOST_SKIP_BUILD_RPATH |
If set, do not add build directory RPATH for shared library dependencies |
CMAKE_HOST_BUILD_RPATH |
Custom RPATH to embed in host executables |
| Variable | Description |
|---|---|
CMAKE_HOST_BUILD_TARGET |
Target name for building host targets (default: host-targets) |
CMAKE_HOST_INCLUDE_PATH |
Additional include directories for host targets |
| Variable | Description |
|---|---|
ENABLE_HOST_UNITY_FIXTURE_EXACT_MATCH |
Only run tests whose group and name exactly match the specified value. Requires -G and -N options provided by Unity fixture. Disabled by default for backward compatibility. |
To run the full test suite:
pytest -n autoTo run a specific test file:
pytest tests/add_host_executable_test.py -xvvThis project is licensed under the MIT License. For more details, see the LICENSE file.