Status: Canonical Scope: All C++ test files under tests/ and related test registration in CMake.
This document defines binding standards for:
- CTest registration and execution behavior
- GoogleTest file structure and naming
- Focused test targets and stability requirements
If another tests document conflicts with this file, this file wins for test structure and execution rules.
- Every actively maintained test binary must be registered in CTest.
- Each CTest test name must be unique and deterministic.
- No active CTest entry may reference a missing executable.
- Tests intended for regular runs must not rely on EXCLUDE_FROM_ALL.
- Use a consistent CTest naming scheme by module and purpose.
- Focused tests must be clearly identifiable by name and mapped target.
- Avoid ambiguous aliases that hide the real binary under test.
- Set explicit timeout values for all non-trivial tests.
- Use labels consistently (module, phase, risk class, release_critical if needed).
- Enable output-on-failure behavior in standard run flows.
Minimum validation gates for structural health:
- ctest --output-on-failure must run without registration errors.
- No "Could not find executable" for active tests.
- Focused test mappings must resolve to fresh binaries, not stale artifacts.
- Include order must be consistent within each test file.
- Use TEST, TEST_F, and TEST_P according to the fixture/parameterization need.
- Shared helpers must be in a local namespace (or anonymous namespace).
- Avoid global helper symbol collisions across multiple test files.
- Do not define a custom
int main()that only calls::testing::InitGoogleTestandRUN_ALL_TESTS(). LinkGTest::gtest_maininstead and omit the function entirely. A custommain()is only permitted when the binary has non-trivial setup not expressible through GTest fixtures (e.g., a combined GTest + benchmark runner). In that case the file must not linkGTest::gtest_mainand the reason must be documented in a comment above the function.
- Test suite names describe the unit or behavior under test.
- Test case names describe expected behavior, not implementation details.
- Keep behavior assertions stable across platforms where contract allows.
- Prefer assertions with actionable failure diagnostics.
- Avoid fragile temporary-lifetime or undefined-behavior patterns.
- Do not hide failures through silent retries inside test logic.
- Focused binaries must have explicit scope and corresponding CTest mapping.
- If a CTest case points to a focused binary, that binary must be built before run.
- Focused filters should target the intended subset only.
- Module tests should be registered in module-local CMake files where possible.
- Root-level registrations are allowed only when target ownership is clear.
- Use
themis_register_module_test()(fromtests/cmake/RegisterModuleTests.cmake) for all new module-level registrations — never rawadd_test(). - Use
themis_register_test_target()(fromtests/cmake/TestPolicy.cmake) when a test is registered at the root level without a module context. - Raw
add_test()+set_tests_properties()blocks are legacy patterns; any new or touched registration must migrate to the canonical helpers.
# New module-level registration (preferred)
themis_register_module_test(
MODULE "<module>"
NAME "<ModuleName>Tests"
TARGET test_<module>_<feature>
TIER unit # unit | integration | stress
KIND focused # standard | focused
TIMEOUT 60
LABELS <tag1> <tag2>
)
# Focused test convenience wrapper
themis_register_module_focused_test(
MODULE "<module>"
NAME "<ModuleName>FocusedTests"
TARGET test_<module>_<feature>_focused
TIMEOUT 30
)The root tests/CMakeLists.txt contains a large number of legacy
add_test() registrations predating the module helpers. These are migrated
incrementally: any registration block touched in a PR must be converted to
themis_register_module_test() or themis_register_test_target().
Priority migration order:
- Tests with no
$<TARGET_FILE:...>in their COMMAND — highest risk of "could not find executable" failures. - Tests without labels — cannot be filtered by module or tier.
- Tests without explicit TIMEOUT — may run unbounded in CI.
- When touching an existing test file, migrate it to this standard in the same PR.
- Resolve ODR-prone helper patterns during migration.
- Keep migration behavior-preserving unless a defect fix is intentional.
Older analysis or summary documents in tests/ remain useful evidence, but are non-canonical for current standards unless explicitly aligned with this file.
- CTest registration is complete and executable-resolved
- Naming follows module-consistent scheme
- Timeout and labels are set consistently
- GoogleTest file structure follows this standard
- Helper symbols are scoped to avoid collisions
- Focused test mappings are correct and reproducible