Skip to main content
Version: 3.3.0

Running Tests

ChronoLog's tests come in three groups:

  • CTest entries in the build tree: unit tests that need nothing running, and integration, end-to-end, communication and overhead tests that need a running deployment.
  • Integration scripts in tests/integration/*.sh that deploy their own daemons, run a scenario and stop them. They are not registered with CTest.
  • The distributed pipeline, a 7-container deployment that CI runs on every pull request and that docker/local_distributed_ci.sh runs locally. See CI/CD.

A Debug build registers 527 CTest entries: 480 unit tests, 6 functional tests (5 disabled), 16 integration tests, 12 end-to-end tests and their fixtures, 4 package discovery tests, 4 communication tests and 5 overhead tests. ctest -N lists them for your build; a Release build has fewer, because some logger tests exist only in Debug.


How to Run Tests​

Unit tests​

Build through the deploy script, then run CTest in the build directory. The script builds into <build dir>/<build type>, so the path ends in Debug:

tools/deploy/local_single_user_deploy.sh -b -t Debug
tools/deploy/local_single_user_deploy.sh -i -t Debug

ctest --test-dir ~/chronolog-build/Debug -R 'Unit_' --output-on-failure -j4

The unit tests need no running daemons. A few bind a fixed loopback port (Unit_ChronoCommon_ExtractionChainConfig uses 2225), so stop a local deployment or another test run that holds it. Some binaries get their library paths only at install, so install before running CTest.

Other useful forms:

ctest --test-dir ~/chronolog-build/Debug -N                       # list without running
ctest --test-dir ~/chronolog-build/Debug -R 'Unit_ChronoPlayer_' # one component
ctest --test-dir ~/chronolog-build/Debug -R 'ArchiveManifest' # any name matching a regex

GTest suites are registered case by case, so -R Unit_ChronoCommon_StoryChunk runs all 47 StoryChunk cases.

Tests that need a running deployment​

The Integration_, EndToEnd_, Communication_ and Overhead_ entries connect to a running ChronoLog. Start one first, then run them:

~/chronolog-install/chronolog/tools/deploy/deploy_local.sh -d -w ~/chronolog-install/chronolog -k 2 -r 1
ctest --test-dir ~/chronolog-build/Debug -R 'Integration_Client_|EndToEnd_' --output-on-failure
~/chronolog-install/chronolog/tools/deploy/deploy_local.sh -s -w ~/chronolog-install/chronolog

The local pipeline in CI runs the whole CTest set against a single-host deployment of 5 keepers and 3 recording groups.

Disabled tests​

These are registered but excluded from the default run. Force one with ctest -R <name>:

CTest NameSource File
Functional_ChronoKeeper_ExtractionChaintests/functional/chrono-keeper/chrono_keeper_extraction_chain_test.cpp
Functional_ChronoGrapher_ExtractionChaintests/functional/chrono-grapher/chrono_grapher_extraction_chain_test.cpp
Functional_ChronoPlayer_ChunkConsumertests/functional/chrono-player/story_chunk_consumer_service_test.cpp
Functional_ChronoPlayer_QueryResponseAgenttests/functional/chrono-player/chrono_player_transfer_agent_test.cpp
Functional_ChronoPlayer_PlaybackServiceLifecycletests/functional/chrono-player/chrono_player_playback_service_test.cpp

Unit Tests​

One row per test binary. Cases is the number of CTest entries it registers.

Common (tests/unit/chrono-common/)​

CTest prefixCasesSource FileWhat it covers
Unit_ChronoCommon_StoryChunk_47chrono_common_story_chunk_test.cppInserting, merging, erasing and extracting events in a StoryChunk, including window boundaries and duplicates
Unit_ChronoCommon_StoryPipeline_37chrono_common_story_pipeline_test.cppThe story pipeline's timeline: chunk allocation, ingestion, decay, prepend/append, finalize, receipts and incarnation numbers
Unit_ChronoCommon_ArchiveManifest27chrono_common_archive_manifest_test.cppManifest records, the writer's appends and the reader's tail, including a log replaced on the same inode
Unit_ChronoCommon_ChunkWriter, Unit_ChronoCommon_ArchiveLayout14chrono_common_story_chunk_writer_test.cppWriting a window through a temporary name into its story directory; name encoding and file names
Unit_ChronoCommon_ConfTemplate6chrono_common_conf_template_test.cppThe shipped configuration template keeps the timing rules between components
Unit_ChronoCommon_DataStoreConf6chrono_common_data_store_conf_test.cppValidation of the DataStoreInternals settings
Unit_ChronoCommon_ExtractionModule5chrono_common_extraction_module_test.cppThe drain loop shared by the keeper and the grapher
Unit_ChronoCommon_ChunkExtractorCSV2chrono_common_chunk_extractor_csv_test.cppThe CSV extractor and empty windows
Unit_ChronoCommon_ExtractionChainConfig1chrono_common_extraction_chain_config_test.cppParsing extraction chain configurations (custom main, not GTest)

ChronoKeeper (tests/unit/chrono-keeper/)​

CTest prefixCasesSource FileWhat it covers
Unit_ChronoKeeper_KeeperChunkRetentionStore57chrono_keeper_retention_store_test.cppThe keeper's retention store: the tail index, retained chunks, and freeing a chunk only once the grapher has written it
Unit_ChronoKeeper_KeeperReleaseDataLoss, ..._KeeperOrphanAtRetirementDataLoss5chrono_keeper_release_dataloss_test.cppReleasing a story does not lose events still in its pipeline
Unit_ChronoKeeper_KeeperExtractionChain4chrono_keeper_extraction_chain_test.cppThe chain hands extracted chunks to the retention store instead of deleting them
Unit_ChronoKeeper_KeeperOrphanRecovery3chrono_keeper_orphan_recovery_test.cppEvents that arrive for a story with no pipeline are sealed and kept
Unit_ChronoKeeper_KeeperChunkRetentionCap2chrono_keeper_retention_cap_test.cppThe retention_cap_mb alarm
Unit_ChronoKeeper_KeeperStoryRetirement2chrono_keeper_story_retirement_test.cppA retired story frees its chunks only when they are written and out of the tail

ChronoGrapher (tests/unit/chrono-grapher/)​

CTest prefixCasesSource FileWhat it covers
Unit_ChronoGrapher_StoryWatermarkRegistry31story_watermark_registry_test.cppThe per-story persisted watermark W and the receipts of delivered keeper chunks
Unit_ChronoGrapher_HDF5FileChunkExtractorWatermark26hdf5_file_chunk_extractor_test.cppThe HDF5 extractor advances W and settles receipts only after a window and its manifest record are written
Unit_ChronoGrapher_GrapherConfiguration8grapher_configuration_test.cppA grapher refuses a chain without the HDF5 extractor
Unit_ChronoGrapher_WatermarkReportTransport7watermark_report_transport_test.cppWatermark reports from the grapher to the keepers over real engines on loopback
Unit_ChronoGrapher_GrapherDestroy, ..._ExtractionQueue, ..._GrapherExtractionChain5grapher_destroy_test.cppA destroy waits for windows still being written
Unit_ChronoGrapher_GrapherDataCollectionReport2grapher_data_collection_report_test.cppWatermark reports sent from the data-collection loop
Unit_ChronoGrapher_GrapherPipelineRetirement2grapher_pipeline_retirement_test.cppA stopped story's pipeline writes its last windows before it is retired

ChronoPlayer (tests/unit/chrono-player/)​

CTest prefixCasesSource FileWhat it covers
Unit_ChronoPlayer_ArchiveManifestIndex22chrono_player_archive_manifest_index_test.cppThe player finds archive files only through the manifest, and drops the files of destroyed stories
Unit_ChronoPlayer_HotRangeSplit, Unit_ChronoPlayer_ReplayPlan13chrono_player_hot_range_split_test.cppWhere a replay splits between the archive and the keepers
Unit_ChronoPlayer_ArchiveNumberedFiles5chrono_player_archive_numbered_files_test.cppA window written more than once is read from every file
Unit_ChronoPlayer_KeeperHotFetch4chrono_player_keeper_hot_fetch_client_test.cppFetches from all keepers at once; a silent keeper costs one deadline
Unit_ChronoPlayer_ReplayEventMerge4chrono_player_replay_event_merge_test.cppMerging archive and keeper events without duplicates
Unit_ChronoPlayer_ArchiveClientId2chrono_player_archive_client_id_test.cppThe full 64-bit client id survives the archive
Unit_ChronoPlayer_ArchiveVisibility2chrono_player_archive_visibility_test.cppArchive reads at the split point
Unit_ChronoPlayer_PlayerReaderConf2chrono_player_reader_conf_test.cppThe ArchiveReaders block; removed settings are ignored
Unit_ChronoPlayer_PlayerPipelineRetirement2player_pipeline_retirement_test.cppA stopped story still answers queries until it is retired
Unit_ChronoPlayer_HDF5ArchiveReadingAgent1chrono_player_hdf5_archive_reader_test.cppReading story chunks from HDF5 files (custom main, not GTest)

ChronoVisor (tests/unit/chrono-visor/)​

CTest prefixCasesSource FileWhat it covers
Unit_ChronoVisor_StoryGroupSelection, ..._ActiveGroups, ..._ActiveRecordingGroup12story_group_selection_test.cppA story's recording group comes from its story id, stays the same when it is acquired again, and moves only when the groups change
Unit_ChronoVisor_ChronicleStoryId2chronicle_story_id_test.cppName pairs that concatenate alike get different story ids

Client library (tests/unit/chronolog-client/)​

CTest prefixCasesSource FileWhat it covers
Unit_ChronologClient_QueryServiceAddressTest10query_service_address_test.cppThe address a reader listens on when the visor is on another host
Unit_ChronologClient_ClientIdentityTest5chronolog_client_identity_test.cppThe ClientId layout
Unit_ChronologClient_ClientLoginName4client_login_name_test.cppThe logged user name when the process has no login name

Plugins​

CTest prefixCasesSource FileWhat it covers
Unit_ChronoSQL_ChronoSQLParser, ..._RowCodec, ..._Metadata28tests/unit/chronosql/chronosql_*_test.cppChronoSQL's parser, row encoding and table metadata
Unit_ChronoKVS_ChronoKVSLogger_25tests/unit/chronokvs/chronokvs_logger_test.cppChronoKVS logging levels and macros (Debug build; Release registers fewer)
Unit_ChronoPubSub_ChronoPubSubLogger_25tests/unit/chronopubsub/chronopubsub_logger_test.cppThe same for ChronoPubSub
Unit_ChronoKVS_ChronoKVSConfig, Unit_ChronoPubSub_ChronoPubSubConfig, Unit_ChronoSQL_ChronoSQLConfig6tests/unit/chrono{kvs,pubsub,sql}/*_config_test.cppLoading each plugin's configuration file

Deploy and CI scripts (tests/unit/*.sh)​

Shell tests that check scripts without deploying anything. Each is one CTest entry.

CTest NameSource FileWhat it covers
Unit_Deploy_ArchiveCleanupdeploy_archive_cleanup_test.shdeploy_local.sh -c cleans the archive through the manifest and leaves other data
Unit_Performance_ArchiveCleanupperformance_archive_cleanup_test.shThe same cleanup as the performance scripts run it
Unit_NFS_ComposeOverridenfs_compose_override_test.shThe compose override that mounts each NFS profile
Unit_NFS_ServerDryRunnfs_server_dry_run_test.shWhat nfs_server.sh would run, without sudo
Unit_NFS_Classifynfs_classify_test.shHow the NFS pass classifies a replay
Unit_NFS_Argsnfs_args_test.shThe NFS pass scripts refuse an option with no value
Unit_LocalCI_BadNfsProfilelocal_ci_bad_profile_test.shlocal_distributed_ci.sh refuses a bad --nfs-profile before touching anything

Functional Tests​

CTest NameSource FileWhat it covers
Functional_ChronoGrapher_DestroyFilestests/functional/chrono-grapher/chrono_grapher_destroy_files_test.cppDeleting a story's or chronicle's archive files; needs no deployment

The other five functional tests are disabled (see above).


Tests That Need a Running Deployment​

Integration​

CTest NameSource FileWhat it covers
Integration_Client_ConnectRpctests/integration/client/client_connect_rpc_test.cppConnect to ChronoVisor
Integration_Client_MetadataRpctests/integration/client/client_metadata_rpc_test.cppCreate and destroy chronicles and stories
Integration_Client_MultiPthreadtests/integration/client/client_multi_pthread_test.cppPOSIX threads sharing one client
Integration_Client_ThreadInterdependencytests/integration/client/client_thread_interdependency_test.cppThreads that depend on each other's stories
Integration_Client_MultiStorytellerstests/integration/client/client_multi_storytellers_test.cppSeveral story handles writing at once
Integration_Client_StoryReadertests/integration/client/client_story_reader_test.cppReplaying events a writer logged
Integration_Client_ReplayCallbacktests/integration/client/client_replay_callback_test.cppThe callback form of ReplayStory
Integration_Client_ClientIdRoundtriptests/integration/client/client_clientid_roundtrip_test.cppA writer's ClientId survives the archive and comes back in a replay
Integration_Client_MultiArgobotstests/integration/client/client_multi_argobots_test.cppArgobots threads sharing one client
Integration_Client_MultiOpenmptests/integration/client/client_multi_openmp_test.cppOpenMP threads writing in parallel
Integration_Client_HybridArgobotstests/integration/client/client_hybrid_argobots_test.cppArgobots and POSIX threads together
Integration_KeeperGrapher_StoryChunkIngesttests/integration/keeper-grapher/keeper_grapher_ingest_test.cppKeeper ingestion into the grapher
Integration_KeeperGrapher_StoryChunkExtracttests/integration/keeper-grapher/keeper_grapher_extract_test.cppChunk extraction from keeper to grapher
Integration_ChronoKVS_PluginIntegrationtests/integration/chronokvs/chronokvs_integration_test.cppChronoKVS writes and reads (300 s timeout)
Integration_ChronoPubSub_PluginIntegrationtests/integration/chronopubsub/chronopubsub_integration_test.cppChronoPubSub publish and subscribe (600 s timeout)
Integration_ChronoSQL_PluginIntegrationtests/integration/chronosql/chronosql_integration_test.cppChronoSQL on a small YCSB-shaped workload (600 s timeout)

End-to-end​

CTest NameSource FileWhat it covers
EndToEnd_TailRead_Playbacktests/end-to-end/tail-read/tail_read_playback_test.cppStoryHandle::playback(N) returns the newest N events from the keepers
EndToEnd_DestructiveAPIs_DestroyStorytests/end-to-end/destructive-apis/destructive_apis_destroy_story_test.cppDestroying a story removes its archive files
EndToEnd_DestructiveAPIs_DestroyChronicletests/end-to-end/destructive-apis/destructive_apis_destroy_chronicle_test.cppDestroying a chronicle removes every story's files
EndToEnd_DestructiveAPIs_DestroyRefusedtests/end-to-end/destructive-apis/destructive_apis_destroy_refused_test.cppA destroy is refused while another client holds the story
EndToEnd_DestructiveAPIs_DisconnectAutoReleasetests/end-to-end/destructive-apis/destructive_apis_disconnect_autorelease_test.cppDisconnect releases the stories a client holds
EndToEnd_DataIntegrity_StoryId, ..._EventCount, ..._EventOrdertests/end-to-end/data-integrity/data_integrity_*_test.cppArchived events match a reference input: story id, count and order

DestructiveAPIs_Setup/_Cleanup and DataIntegrity_Setup/_Cleanup are CTest fixtures for these tests; they check the deployment but do not start one. DataIntegrity_Setup writes its input with chrono-client-admin, which the build no longer produces, so the data-integrity tests do not run. With the template's timings, the destructive-API tests can time out waiting for a story's first archive file, since the grapher's acceptance window alone is 60 s.

Package discovery​

CTest NameWhat it covers
PackageDiscovery_CMakeFindPackagefind_package(ChronoLog) in the build tree
PackageDiscovery_PkgConfigpkg-config chronolog in the build tree
PackageDiscovery_Installed_CMakeFindPackagefind_package(ChronoLog) after install
PackageDiscovery_Installed_PkgConfigpkg-config after install

Communication and overhead​

CTest NameSource FileWhat it covers
Communication_Thallium_ServerRepeaterRpctests/communication/thallium_server_test.cppA Thallium RPC server
Communication_Thallium_ClientSendRecvOrRdmatests/communication/thallium_client_test.cppSend/receive and RDMA against that server
Communication_Thallium_ClientMpitests/communication/thallium_client_mpi_test.cppThe same from MPI ranks
Communication_Thallium_ProtocolScripttests/communication/thallium_protocol_test.shEach supported Thallium transport
Overhead_Clock_HighResolutionClocktests/overhead/clock/clock_high_resolution_test.cppClock resolution and monotonicity
Overhead_Clock_TimestampCollectiontests/overhead/clock/clock_timestamp_collection_test.cppCost of collecting timestamps
Overhead_Clock_TimestampCollisionDetectiontests/overhead/clock/clock_timestamp_collision_test.cppTimestamp collisions under concurrency
Overhead_Clock_Scripttests/overhead/clock/clock_cset_shield_test.shClock cost with CPU-set shielding
Overhead_Lock_ContentionBenchmarktests/overhead/lock/lock_overhead_test.cppMutex and spinlock contention

Integration Scripts​

These deploy real daemons on one host, run a scenario and stop the daemons. They take minutes and are not in CI; run them by hand before a pull request that touches the keeper's retention store, the watermark protocol, the replay path or the player.

WORK_DIR=~/chronolog-install/chronolog BUILD_DIR=~/chronolog-build/Debug \
tests/integration/watermark_loop_test.sh # ~5 min, expect 5/5
WORK_DIR=~/chronolog-install/chronolog BUILD_DIR=~/chronolog-build/Debug \
tests/integration/watermark_replay_split_test.sh # ~4 min, expect 9/9
WORK_DIR=~/chronolog-install/chronolog BUILD_DIR=~/chronolog-build/Debug \
tests/integration/replay_fanout_timing_test.sh # ~3 min, expect 4/4
WORK_DIR=~/chronolog-install/chronolog BUILD_DIR=~/chronolog-build/Debug \
tests/integration/watermark_exact_split_test.sh # ~3 min, expect 20/20
ScriptWhat it protects
watermark_loop_test.shChunks are freed only once the grapher confirms them, none early, nothing lost across a grapher SIGSTOP, and the events are on disk afterwards.
watermark_replay_split_test.shA replay returns the same events whether they are on the keepers, archived, or split across both, with no duplicates.
replay_fanout_timing_test.shThe player fetches from its keepers together: two keepers frozen with SIGSTOP cost one deadline, not two.
watermark_exact_split_test.shA replay splits exactly where the watermark puts it, while the data is on the keepers, archived, split, with the grapher stopped and after it resumes; one archive file per window, none rewritten.

Run them one at a time; each deploys its own cluster on the same ports. Each restores the configuration it patched and stops its daemons on exit. If one is interrupted, check for leftover daemons and for a *.backup file in ~/chronolog-install/chronolog/conf/.


The Distributed Pipeline Locally​

The pull-request check that runs a multi-host deployment is the distributed pipeline: 7 containers (1 visor, 2 keepers, 2 graphers, 2 players), with cross-host RPC, MPI clients on several containers and, at the end, the archive on an NFS export. docker/local_distributed_ci.sh runs the same stages against your working tree:

docker/local_distributed_ci.sh -i IMAGE                 # the same topology as CI
docker/local_distributed_ci.sh -i IMAGE -k 1 -g 1 -p 1 # 4 containers, faster
docker/local_distributed_ci.sh -i IMAGE --keep # leave the containers up
docker/local_distributed_ci.sh -i IMAGE --nfs # also run the NFS pass (needs sudo)

IMAGE is the base image with Spack and the dependencies, as CI builds it (ghcr.io/grc-iit/chronolog-base:sha-<hash>). A full run with --nfs takes about 22 minutes. See CI/CD for what each stage checks and what the NFS pass expects.


AddressSanitizer​

The normal Debug build has no sanitizer. Build a separate tree with the flags passed through the deploy script, install it, and run the unit tests:

tools/deploy/local_single_user_deploy.sh -b -t Debug -B ~/chronolog-build/Asan -I ~/chronolog-install-asan \
--cxx-flags "-fsanitize=address -fno-omit-frame-pointer"
tools/deploy/local_single_user_deploy.sh -i -t Debug -B ~/chronolog-build/Asan -I ~/chronolog-install-asan

ASAN_OPTIONS=detect_leaks=1 ctest --test-dir ~/chronolog-build/Asan/Debug -R 'Unit_' --output-on-failure

Keep ASan and normal objects in separate build trees. As of 3.3.0, 26 unit tests fail under ASan, all with leaks: the StoryPipeline tests (its ingestion handle is never freed) and the ChronoKVS, ChronoPubSub and ChronoSQL configuration tests. Compare a run against that, not against a clean result.


Workload Script​

tests/synthetic_workload/distributed_syslog.sh is not registered with CTest. It ingests /var/log/syslog with chrono-bench from several nodes (the hosts in conf/hosts_client) against a running deployment.