How to Implement Test Sharding for Parallel Execution in Catch2
Catch2 implements test sharding through the --shard-count and --shard-index CLI flags (or Bazel environment variables), which partition the test suite deterministically across workers via the createShard algorithm in src/catch2/internal/catch_sharding.hpp.
Test sharding enables parallel execution of large test suites by splitting tests across multiple workers, CPU cores, or CI nodes. In the catchorg/Catch2 framework, this capability is built directly into the test runner and requires no external tools. By configuring test sharding parameters, you can distribute your test load evenly while maintaining deterministic assignment and standard reporting workflows.
Command-Line Interface for Test Sharding
Catch2 exposes sharding control through two primary command-line options parsed during startup:
--shard-count=<n>– Specifies the total number of shards to divide the test suite into.--shard-index=<i>– Specifies the zero-based index of the current shard to execute.
These values are parsed into the Config object defined in src/catch2/catch_config.hpp and accessed at runtime via Config::shardCount() and Config::shardIndex(). If sharding options are omitted, shardCount defaults to 1, causing the runner to execute the entire suite without partitioning.
# Execute shard 2 of 4 total shards
./tests/MyTests --shard-count=4 --shard-index=2
Bazel Integration
When running under Bazel, Catch2 detects the BAZEL_TEST=1 environment variable and reads sharding configuration from standard Bazel environment variables instead of CLI flags:
| Environment Variable | Equivalent CLI Flag | Purpose |
|---|---|---|
TEST_TOTAL_SHARDS |
--shard-count |
Total number of shards |
TEST_SHARD_INDEX |
--shard-index |
Index of current shard |
TEST_SHARD_STATUS_FILE |
N/A | Path to status file created on successful completion |
The integration is verified by the test script in tests/TestScripts/testBazelSharding.py, which demonstrates launching the binary with these variables and validating the creation of the status file.
export BAZEL_TEST=1
export TEST_TOTAL_SHARDS=4
export TEST_SHARD_INDEX=1
export TEST_SHARD_STATUS_FILE=/tmp/shard.status
./tests/MyTests
The Sharding Algorithm
The core partitioning logic resides in src/catch2/internal/catch_sharding.hpp within the createShard function. This algorithm ensures deterministic and even distribution using integer arithmetic:
- Validation – Verifies that
shardCountis greater thanshardIndex. - Base calculation – Computes
totalTestCount / shardCountas the base number of tests per shard. - Remainder distribution – The first
totalTestCount % shardCountshards receive one additional test to handle uneven division. - Slice extraction – Calculates start and end indices for the requested shard, then constructs a new container containing only the tests for that slice.
Because the algorithm relies solely on deterministic arithmetic, test assignment remains stable across runs provided the underlying test order is consistent (e.g., using --order decl).
Implementation Flow
Test sharding integrates into the execution pipeline in src/catch2/catch_session.cpp. The Session class orchestrates the following sequence:
-
Configuration parsing – Command-line options populate the
Configobject. -
Test collection – All test cases matching the current
TestSpecare gathered into aTestGroup. -
Shard creation – The session calls
createShardwith the collected tests,shardCount, andshardIndex:m_tests = createShard( m_tests, m_config->shardCount(), m_config->shardIndex()); -
Execution – The
TestGroupruns the filtered (sharded) test set, with reporters processing results as usual.
Practical Examples
Manual Sharding via CLI
Run a binary across three separate terminal sessions to parallelize execution:
# Terminal 1
./build/Tests/MyTests --shard-count=3 --shard-index=0
# Terminal 2
./build/Tests/MyTests --shard-count=3 --shard-index=1
# Terminal 3
./build/Tests/MyTests --shard-count=3 --shard-index=2
Parallel CI Job Using Bash
Distribute tests across a single machine's cores using background processes:
#!/usr/bin/env bash
TEST_BINARY=./build/Tests/MyTests
TOTAL_SHARDS=5
for i in $(seq 0 $((TOTAL_SHARDS-1))); do
$TEST_BINARY --shard-count=$TOTAL_SHARDS --shard-index=$i &
done
wait # Wait for all shards to finish
Bazel Test Rule Configuration
Define sharding directly in your BUILD file:
cc_test(
name = "my_test",
srcs = ["my_test.cpp"],
shard_count = 4, # Bazel automatically sets TEST_TOTAL_SHARDS
)
Summary
- Test sharding in Catch2 is controlled via
--shard-countand--shard-indexflags or Bazel environment variables. - The
createShardfunction insrc/catch2/internal/catch_sharding.hppimplements deterministic partitioning based on integer division and remainder distribution. - Configuration values are accessed through
Config::shardCount()andConfig::shardIndex()as defined insrc/catch2/catch_config.hpp. - The
Sessionclass applies sharding during test execution insrc/catch2/catch_session.cppbefore running the filtered test set. - Bazel integration requires setting
BAZEL_TEST=1and usingTEST_TOTAL_SHARDS,TEST_SHARD_INDEX, andTEST_SHARD_STATUS_FILE.
Frequently Asked Questions
How does Catch2 distribute tests across shards?
Catch2 uses the createShard algorithm in src/catch2/internal/catch_sharding.hpp to divide tests evenly. Each shard receives totalTestCount / shardCount tests, with the first totalTestCount % shardCount shards receiving one additional test to account for remainders.
What happens if I don't specify sharding options?
If --shard-count is not specified, it defaults to 1. The createShard function returns the original test container unchanged, causing the runner to execute the entire suite without partitioning.
How do I enable Bazel-compatible test sharding?
Set the BAZEL_TEST=1 environment variable and provide TEST_TOTAL_SHARDS, TEST_SHARD_INDEX, and optionally TEST_SHARD_STATUS_FILE. Catch2 will automatically detect these variables and skip parsing the corresponding CLI flags.
Is the test distribution deterministic across runs?
Yes, provided the underlying test order is deterministic (e.g., using --order decl or consistent sorting). The sharding algorithm uses only arithmetic operations on the total test count and shard index, ensuring the same tests always map to the same shard indices.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →