Skip to main content
Version: 3.3.0

Testing Guidelines

Every new feature and bug fix should include tests. ChronoLog uses Google Test (GTest) for unit tests and custom executables or shell scripts for the rest, registered through CTest except for the integration scripts that deploy their own daemons.

Test Categories​

CategoryDirectoryPurposeNeeds a running deployment
Unittests/unit/One component in isolation; also shell tests of the deploy and CI scriptsNo
Functionaltests/functional/One component's real code paths, such as file deletionNo
Integrationtests/integration/<scope>/Two or more components talking to each otherYes
Integration scriptstests/integration/*.shScenarios across keepers, graphers and players; each deploys and stops its own daemonsNo (they start one)
End-to-endtests/end-to-end/Client APIs against a deployment: tail read, destroy, data integrityYes
Distributedtests/distributed/The NFS pass that the distributed pipeline runsRun by the pipeline
Communicationtests/communication/Thallium RPC and MPIYes
Overheadtests/overhead/Clock and lock costsNo

Writing Unit Tests​

Unit tests use GTest and live under tests/unit/<component>/, with the component's name in the file name:

tests/unit/
├── chrono-common/
│ ├── chrono_common_story_chunk_test.cpp
│ └── chrono_common_archive_manifest_test.cpp
├── chrono-player/
│ └── chrono_player_archive_manifest_index_test.cpp
├── chrono-visor/
│ └── story_group_selection_test.cpp
└── ...

A unit test must not need running daemons. It may start a margo engine on loopback when the code under test is an RPC client or server, as watermark_report_transport_test.cpp does.

CTest registration​

Register GTest binaries with gtest_discover_tests and the prefix Unit_<Component>_, so every test case becomes its own CTest entry:

add_executable(story_group_selection_test story_group_selection_test.cpp)
target_link_libraries(story_group_selection_test PRIVATE GTest::gtest_main chrono_common)
set_target_properties(story_group_selection_test PROPERTIES OUTPUT_NAME chronolog-test-story-group-selection)
gtest_discover_tests(story_group_selection_test TEST_PREFIX "Unit_ChronoVisor_")

A binary with its own main() is not a GTest binary: register it with a plain add_test(). gtest_discover_tests runs the binary at build time to list its tests, so a custom main() would run the whole program during the build.

Best Practices​

  • Use test fixtures (TEST_F) to share setup and teardown between related tests.
  • Keep each test on one behavior, and name it after that behavior: TEST(StoryGroupSelection, SameStoryGetsTheSameGroupEveryTime).
  • For a bug fix, write the test that reproduces the bug first and watch it fail.

Writing Integration and End-to-End Tests​

Integration and end-to-end tests are custom executables (GTest or not) that connect to a deployment someone else started. Name them Integration_<Scope>_<Subject> or EndToEnd_<Area>_<Subject>:

add_test(NAME EndToEnd_TailRead_Playback
COMMAND tail_read_playback_test --events 25 --max-wait 120)
set_tests_properties(EndToEnd_TailRead_Playback PROPERTIES TIMEOUT 180)

The local pipeline in CI runs them against its single-host deployment. Locally, start a deployment first; see Running Tests.

A scenario that needs control over the daemons themselves (stopping a grapher, freezing a keeper, patching the configuration) belongs in an integration script under tests/integration/ that deploys its own daemons, like watermark_loop_test.sh.

Test Expectations for PRs​

Change typeExpected tests
New public API or componentUnit tests covering the new interface
Bug fixAt least one test that reproduces the bug
Inter-component changesIntegration tests exercising the affected interaction
Keeper retention, watermark protocol, replay path or playerThe four integration scripts, with their results in the PR
RPC endpoints, ports, configuration plumbing, deploy scriptsdocker/local_distributed_ci.sh
Archive layout, manifest, archive reads or destroydocker/local_distributed_ci.sh --nfs
All PRsExisting tests must continue to pass

Disabled Tests​

Use the DISABLED_ prefix (GTest) or the CTest DISABLED property for a test that is temporarily broken while you land other work, and file an issue to track re-enabling it. ctest -N marks disabled entries, and ctest -R <name> runs one anyway.

Further Reading​

See Running Tests for the full test catalog and instructions on running tests locally, and CI/CD for what the pipelines run.