* fix(release): repair package repo publish
Arch repo metadata and R2 uploads need to be reproducible from CI, with retry hardening for transient Cloudflare failures.
* fix(daemon): stop shutdown log storm
Init waiter must treat ShuttingDown as terminal so daemon shutdown tests do not flood Meson logs while the daemon is stopping.
* refactor(cli): delegate serve to MCP binary
Keep yams serve UX while removing MCP server linkage from yams-cli. The standalone MCP binary owns stdio serving and exits on stdin EOF.
* refactor(cli): move daemon path helpers
Keep CLI path resolution out of YamsDaemon so later link cuts can separate CLI and daemon internals. Full yams_daemon removal still needs a socket-only daemon-client split.
* refactor(daemon): split embedded client
* perf(search): add lean-guided benchmark gates
Add Lean topology/search models, ingestion ablation docs, and focused regression tests for topology and compression threshold behavior.
Extract search observability, keep benchmark-only ablation controls, and make retrieval quality benchmarks default to Simeon while preserving explicit ONNX opt-in.
* refactor(config): move daemon policies
* refactor(daemon): extract plugin runtime
* refactor(daemon): untangle service coordinators
Untangle CLI/MCP from daemon service coordinators and the daemon static lib.
- MetadataRepository uses a daemon-neutral tree-diff callback. The
GraphComponent wrapper moves to src/daemon/components/metadata_graph_bridge.cpp,
owned by the daemon library.
- WriteCoordinator moves to a dedicated build-only yams_write_coordinator
static lib. Drop ResourceGovernor coupling inside the writer loop; batch
sizing still respects config.maxBatchSize.
- AppContext gains enqueuePostIngest callback. ServiceManager wires it to
its own enqueuePostIngest; app services no longer call ServiceManager
directly.
- Embedding repair uses BulkIngestLease/BeginBulkIngestCallback instead of
concrete VectorIndexCoordinator::BulkScope. Daemon dispatchers provide a
lease factory backed by the real coordinator.
- CLI daemon_command uses config helpers instead of YamsDaemon::getXDGRuntimeDir.
- Remove explicit dependency('yams_daemon') from src/cli and tools/yams-mcp.
- Declare missing yams_extraction/yams_ingest deps on yams_app_services so
app services link cleanly without daemon.
Validated:
meson compile -C build/release -j4 yams-daemon yams-cli yams-mcp-server
ninja -C build/release -t commands tools/yams-cli/yams-cli tools/yams-mcp/yams-mcp-server | grep -c libyams_daemon.a -> 0
yams-cli --version, yams-mcp-server init, yams serve --help smoke tests
Size drop on macOS release: yams-cli 41M -> 32M, yams-mcp-server 37M -> 22M.
* fix(cli): keep stdin add local fallback
No embedded daemon now. When yams add - buffers stdin and daemon IPC is missing, store via local app services instead of failing socket_missing.
Validated:
meson compile -C build/debug -j4 yams-cli catch2_cli_commands
MESON_BUILD_ROOT=/Users/trevon/work/tools/yams/build/debug build/debug/tests/catch2_cli_commands 'AddCommand - reads from piped stdin and stores content'
MESON_BUILD_ROOT=/Users/trevon/work/tools/yams/build/debug build/debug/tests/catch2_cli_commands
meson compile -C build/release -j4 yams-cli yams-mcp-server
meson compile -C build/asan-api -j2 yams-cli catch2_cli_commands
MESON_BUILD_ROOT=/Users/trevon/work/tools/yams/build/asan-api ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1 build/asan-api/tests/catch2_cli_commands 'AddCommand - reads from piped stdin and stores content'
ninja -C build/release -t commands tools/yams-cli/yams-cli tools/yams-mcp/yams-mcp-server | grep libyams_daemon.a -> no output
* feat(bench): add xplan multi-arm experiment harness
Unified plan runner, ablation axes, REPORT/ablation docs, and
KPI snapshot so optimization loops stop living in shell matrices.
* feat(formal): expand topology/search Lean contracts
Boundary models for search provenance, construction gates,
and cluster/route objectives so Lean guides correctness while
xplan ranks performance levers.
* feat(search,topology): construction purity + graph expansion path
Raise topology build defaults (min edge 0.25, max component 64) and
split oversized CC pieces so hybrid assist is quality-positive on
scifact. Add GraphNeighbors expansion with medoid-anchor fallback,
structured helpers instead of nested session logic, and BEIR-default
xplan plans. Search engine extraction (fanout/classic pipeline) rides
along as the integration surface for routing.
* feat(topology): add KMeans engine and SGC identity contracts
Register kmeans in the factory with deterministic spherical k-means
over embeddings, and enforce SGC document-identity invariants in
debug builds with matching unit coverage.
* feat(daemon): topology readiness gates and selectable reranker
Drive topologyReady from published epoch/freshness/rebuild/dirty
ingest state (not just KG drain), surface those fields in search
stats, and wire onnx|simeon|auto|none cross-reranker selection.
* chore(search): drop meta_path tuner knobs
Meta-path routing is gone; remove dead TunedParams fields and the
ConfigResolver test for [search.meta_path].
* feat(bench): add topology_purity_validate plan
Repeats=3 validation matrix for construction-purity winners vs
topo_off so MRR deltas can clear run-to-run noise.
* chore: format add_command and config include order
Mechanical clang-format / include reorder only; no behavior change.
* feat(bench): decision-grade xplan loop for search engine
Document search-engine measurement in AGENTS; harden xplan with
repeats=3 plans, quality×latency primaries, mechanism metrics from
debug.jsonl, multi-rep log promotion, and archive superseded topology
plans.
* feat(graph): denser product semantic_neighbor topK
Raise default YAMS_GRAPH_SEMANTIC_TOPK fallback from 4 to 8 so product
graphs leave fewer degree-0 docs for GraphNeighbors expansion.
* feat(search): extract topology assist and cross-rerank seams
Pull topology assist into search_topology_stage (seed merge, skip
reasons, optional vector seed probe default 0). Extract cross_rerank
compute from search_engine. Wire probe via typed config/ConfigResolver.
* feat(bench): product-path search engine ablation plans
Add topology-off component, vector-weight, and tiered-execution xplan
plans (repeats=3) and wire them into the AGENTS measurement loop table.
* fix(ci): repair pre-push fallbacks
* feat(bench): lock-free mid-run builds + multi-corpus gates
xplan skips meson rebuild when the quality binary exists (flock +
YAMS_BENCH_FORCE_BUILD override) to stop multi-arm link races. Add
graph_vector weight and nfcorpus transfer-gate plans; document in AGENTS.
* fix(daemon): keep SearchEngine alive via shared_ptr cache
getCachedEngine previously published a raw pointer that metrics polling
used through rebuild, racing destructor of SearchEngine::Impl (TSAN).
Store and return a shared_ptr snapshot so diagnostic readers pin the
engine for the duration of getConfig/health checks.
Also adds the cautious vectorWeight 0.30→0.20 multi-corpus xplan plan.
* feat(search): route vectors through cached topology
Build and reuse validated topology snapshots, rank routed members by query similarity, and narrow the vector leg to the accepted document set with explicit work diagnostics.
Remove the retired HDBSCAN engine and vendor dependency, preserve typed topology policy through search tuning, and cover the runtime/configuration contracts.
* feat(bench): consolidate search decision plans
Enforce deterministic experiment identity and declared baselines, expose routed/global vector work metrics, and register the focused benchmark contracts.
Promote the compact repeats=3 routing-budget gate, archive superseded topology mechanism screens, and add multi-corpus product calibration plans.
* feat(search): refine topology vector routing
* fix(search): propagate daemon readiness
* feat(bench): measure generalized memory health
* fix(cli): honor in-process fallbacks
* ci: harden smolvm debug builds
* perf(search): route topology through filtered ANN
* feat(bench): measure read-write pressure
* perf(metadata): index symbol lookups on read pool
* fix(daemon): isolate startup reads and maintenance
* feat(bench): replay isolated generalized corpora
* feat(search): add evidence routing pipeline
Replace the blocking smolvm pre-push path with host ASan/TSan validation, harden sanitizer-sensitive smoke tests, and add recovery cleanup for malformed SQLite WAL sidecars.
* refactor(search): unify topology evidence path
Remove classic and sidecar fusion lanes so topology augments the recall-preserving ANN and lexical candidate pool through one product path.
* refactor(bench): prune superseded search lanes
Measure the topology-plus-ANN product path with stable experiment identity and remove obsolete sidecar and tiered controls.
* docs(search): retire stale experiment notes
Keep the benchmark handoff focused on the topology-plus-ANN product lane and its active decision plans.
* refactor(search): prune dead search surfaces
Remove unused fusion, reranker, policy, and router abstractions.
Bound topology materialization and keep document trace payloads opt-in.
* fix(cli): clear build warnings
* fix(xplan): reject stale run identity
* refactor(search)!: remove legacy query stack
Replace the unused AST/ranker/post-processor surface with focused facet aggregation used by product search.
BREAKING CHANGE: removes query parser, result ranker, search filter, and SearchResults container APIs from yams_search.
* refactor(search): type value stage traces
Remove synthetic ComponentResult markers for embedding and concept stages. Preserve failure and timeout states instead of overwriting them with fake successful results.
* refactor(search): isolate benchmark env overlays
Move test-only search environment parsing behind an injectable internal adapter. Keep all existing overlay keys and tuner pin semantics while restoring SearchEngineBuilder to composition.
* refactor(search): unify topology routing options
Snapshot topology policy once at the assist boundary and pass it through routing sessions and diagnostics. Remove repeated scalar copying without changing routing behavior or metric keys.
* fix(search): unify tuner config ownership
Use one complete config-to-params overlay for runtime seeding and no-tuner policy resolution. Reseed attached tuners on setConfig so reported and executed configuration cannot diverge.
* fix(search): snapshot tuner state per query
Read tuned config, params, and state under one lock before routing. Add a TSAN correlation test so adaptive observation cannot produce a torn logical query snapshot.
* fix(search): enforce topology routing contract
Generated configs must preserve hybrid-assist shadow routing, while daemon-backed search must abstain when published topology artifacts are stale. Keep standalone validation and global ANN recall intact.
* fix(daemon): let SQLite reclaim its file
Directly resizing a live database from sampled page counts can truncate pages allocated by concurrent writers. VACUUM must remain the only file-reclamation mechanism.
* fix(metadata): tolerate missing symbol FTS
Readiness lookups can race an unavailable optional FTS migration. Retain the indexed fast path, but retry against the base table when prepare or cached-statement execution proves the capability absent.
* build(fuzz): drop removed query parser target
The optional AFL build still referenced the deleted legacy query parser API and could no longer compile.
* fix(bench): include writes in drain oracle
A zero post-ingest queue is not durable drain while coordinated writes remain queued or in flight.
* fix(daemon): vacuum padded WAL databases safely
|
||
|---|---|---|
| .. | ||
| acceptance | ||
| benchmarks | ||
| common | ||
| fixtures | ||
| integration | ||
| manual | ||
| mobile | ||
| plugins | ||
| scripts | ||
| sdk | ||
| stress | ||
| support | ||
| unit | ||
| .clang-tidy | ||
| CATCH2_PATTERNS.md | ||
| meson.build | ||
| README.md | ||
YAMS Test Suite Documentation
Overview
The YAMS test suite provides comprehensive testing coverage including unit tests, integration tests, performance benchmarks, and stress tests. This document explains how to write, run, and maintain tests for YAMS.
Quick Start
Running Tests (Meson)
# Typical debug path in this repo
./setup.sh Debug
meson configure builddir -Dbuild-tests=true
meson compile -C builddir
# Run suites
meson test -C builddir # all
meson test -C builddir --suite unit # unit only
meson test -C builddir --suite integration
meson test -C builddir --suite stress # long-running
# List current tests and tags
meson test -C builddir --list
Use meson test --list as the source of truth for current test names/tags. Older
examples that rely on historical suite labels are easy to drift.
--allow-running-no-tests is reserved for Meson shard wrappers and opt-in
filtered runs where "no matching tests" is expected. Do not use it as a normal
catch-all for day-to-day local test commands.
Integration timeouts and stress iters (Meson options)
You can tune integration-suite timeouts without editing sources by passing Meson options at setup/configure time:
# Examples (defaults: services=900s, ui=600s, smoke=300s)
meson configure builddir -Dintegration-timeout=900 -Dintegration-ui-timeout=600 -Dintegration-smoke-timeout=300 -Dstress-iters=100
# Or set at setup:
meson setup builddir --buildtype=debug -Dbuild-tests=true \
-Dintegration-timeout=900 -Dintegration-ui-timeout=600 -Dintegration-smoke-timeout=300 -Dstress-iters=100
Profiles via native files (no scripts)
You can also use Meson native files to version preset profiles:
# quicker local runs
meson setup build/fast --buildtype=debug -Dbuild-tests=true --native-file meson/profiles/ci-fast.ini
meson test -C build/fast integration_services -t 1
# nightly soak
meson setup build/soak --buildtype=debug -Dbuild-tests=true --native-file meson/profiles/nightly-soak.ini
meson test -C build/soak integration_services
Services Integration Suite (isolated)
The Services suite exercises add/list/get/grep via a live daemon in a temporary sandbox.
# Run only the services integration executable (isolated; serial)
meson test -C builddir integration_services -v
# Notes
# - Requires AF_UNIX socket availability (macOS/Linux). If your environment forbids AF_UNIX
# binds, affected tests will SKIP with an explanatory message.
# - Logs: builddir/meson-logs/testlog.txt
UI/CLI Integration (services layer)
We mirror CLI expectations at the services layer to keep tests fast and
deterministic (no external shell). The custom shard integration_ui runs these
checks serially against a temporary daemon sandbox.
# Run only the UI/CLI expectations shard
meson test -C builddir integration_ui -v
# What it includes
# - UiCliExpectationsIT.* (search/list/get structure and options)
# - GrepServiceExpectationsIT.* (regex pathsOnly, count mode, negatives)
# Notes
# - Tests bring up their own daemon with mock embeddings
# - AF_UNIX is required; tests skip with a helpful message if unavailable
Legacy CMake/CTest (for reference)
These commands are retained for contributors still using the legacy build. Prefer Meson going forward.
cmake -B build -DBUILD_TESTS=ON -DENABLE_COVERAGE=ON
cmake --build build
cd build && ctest -V
Running Benchmarks
The benchmark suite is now consolidated into a few executables (api_benchmarks, core_benchmarks, search_benchmarks).
# Run all benchmarks
cd build/tests/benchmarks
./run_all_benchmarks
# Run a specific benchmark suite
./core_benchmarks
# Pass arguments to the benchmark runner
./api_benchmarks --iterations 100 --output my_results.json
Test Organization
tests/
├── common/ # Shared test utilities
│ ├── test_helpers.h # Basic test helpers
│ ├── test_data_generator.h # Test data generation
│ ├── fixture_manager.h # Test fixture management
│ └── benchmark_tracker.h # Benchmark tracking
├── unit/ # Unit tests for each component
│ ├── crypto/
│ ├── storage/
│ └── ...
├── integration/ # End-to-end and multi-component tests
│ ├── daemon/
│ └── ...
├── benchmarks/ # Performance benchmarks
│ ├── api_benchmarks.cpp
│ ├── core_benchmarks.cpp
│ └── search_benchmarks.cpp
├── stress/ # Stress tests
├── data/ # Test data files
├── fixtures/ # Test fixtures
└── scripts/ # Test automation scripts
Writing Tests
Unit Tests
Unit tests verify individual components in isolation. Place unit tests in tests/unit/<module>/.
All tests use Catch2 v3; see tests/CATCH2_PATTERNS.md for canonical patterns.
#include <catch2/catch_test_macros.hpp>
#include <yams/extraction/pdf_extractor.h>
#include "tests/common/fixture_manager.h"
using namespace yams;
using namespace yams::test;
namespace {
struct PDFExtractorFixture {
PDFExtractorFixture() { extractor_ = std::make_unique<extraction::PDFExtractor>(); }
std::unique_ptr<extraction::PDFExtractor> extractor_;
};
} // namespace
TEST_CASE_METHOD(PDFExtractorFixture, "ExtractTextFromSimplePDF", "[pdf]") {
auto fixture = FixtureManager::getSimplePDF();
auto result = extractor_->extract(fixture.path);
REQUIRE(result.has_value());
CHECK_FALSE(result->text.empty());
CHECK(result->metadata["pages"] == "1");
}
TEST_CASE_METHOD(PDFExtractorFixture, "HandleCorruptedPDF", "[pdf]") {
auto fixture = FixtureManager::getCorruptedPDF();
auto result = extractor_->extract(fixture.path);
REQUIRE_FALSE(result.has_value());
CHECK(result.error().code == ErrorCode::InvalidFormat);
}
Integration Tests
Integration tests verify multiple components working together. Place in tests/integration/.
#include <catch2/catch_test_macros.hpp>
#include "tests/common/test_helpers_catch2.h"
namespace {
struct DocumentLifecycleFixture {
DocumentLifecycleFixture() { initializeYAMS(testDir); }
std::filesystem::path testDir = yams::test::make_temp_dir("yams_doc_lifecycle");
};
} // namespace
TEST_CASE_METHOD(DocumentLifecycleFixture, "CompleteDocumentWorkflow", "[integration]") {
auto addResult = runCommand("yams add test.txt --tags test");
REQUIRE(addResult.isOk());
std::string hash = extractHash(addResult.value());
auto searchResult = runCommand("yams search test");
REQUIRE(searchResult.value().contains(hash));
auto updateResult = runCommand(
"yams update --hash " + hash + " --metadata status=processed");
REQUIRE(updateResult.isOk());
auto getResult = runCommand("yams get " + hash);
CHECK(getResult.value() == readFile("test.txt"));
}
Performance Benchmarks
Benchmarks use a custom BenchmarkBase class for consistency. Place new benchmarks in the appropriate consolidated file (core_benchmarks.cpp, search_benchmarks.cpp, etc.).
#include "benchmark_base.h"
#include <yams/crypto/hasher.h>
using namespace yams::benchmark;
// Define a benchmark class that inherits from a suitable base or BenchmarkBase itself
class HashingBenchmark : public BenchmarkBase {
public:
HashingBenchmark(const std::string& name, size_t dataSize)
: BenchmarkBase("Hashing_" + name), dataSize_(dataSize) {
// Setup code here
hasher_ = yams::crypto::createSHA256Hasher();
data_.resize(dataSize_);
}
protected:
// The core work of the benchmark goes here
size_t runIteration() override {
hasher_->hash(data_);
return 1; // Number of operations in this iteration
}
private:
std::unique_ptr<yams::crypto::IContentHasher> hasher_;
std::vector<std::byte> data_;
size_t dataSize_;
};
// In main(), instantiate and run the benchmark
int main() {
BenchmarkBase::Config config;
// ... configure ...
auto bench = std::make_unique<HashingBenchmark>("SHA256_1MB", 1024 * 1024);
bench->run();
return 0;
}
Test Data Management
Using Test Data Generator
#include "tests/common/test_data_generator.h"
TestDataGenerator generator;
// Generate text document
std::string text = generator.generateTextDocument(1024); // 1KB
// Generate PDF
auto pdfData = generator.generatePDF(5); // 5 pages
// Generate corpus
auto corpus = generator.generateCorpus(100, 4096, 0.1); // 100 docs, 4KB avg, 10% dups
// Generate metadata
auto metadata = generator.generateMetadata(10, {"string", "number", "boolean"});
Using Fixture Manager
#include "tests/common/fixture_manager.h"
#include "tests/common/test_data_generator.h"
using yams::test::FixtureManager;
using yams::test::TestDataGenerator;
// Get predefined fixtures
auto simplePDF = FixtureManager::getSimplePDF();
auto complexPDF = FixtureManager::getComplexPDF();
auto largeText = FixtureManager::getLargeTextFile();
// Create custom fixtures rooted in a temporary directory
FixtureManager manager;
TestDataGenerator generator;
auto textFixture = manager.createTextFixture("custom.txt", "content");
auto binaryFixture = manager.createBinaryFixture("data.bin", generator.generateRandomBytes(512));
// Create a synthetic corpus for search/metadata tests
auto fixtures = manager.createCorpus(50, 1024); // 50 docs, 1KB each
Search Corpus Presets
Search-heavy suites (daemon search, CLI regressions, benchmarking) reuse a deterministic preset corpus so they can share documents, tags, and query workloads.
#include "tests/common/fixture_manager.h"
#include "tests/common/search_corpus_presets.h"
yams::test::FixtureManager fixtures(tmpDir);
auto spec = yams::test::defaultSearchCorpusSpec();
spec.commonTags.push_back("regression");
auto corpus = fixtures.createSearchCorpus(spec);
yams::app::services::DocumentIngestionService ingiter;
yams::app::services::AddOptions opts;
opts.socketPath = sock;
opts.explicitDataDir = storageDir;
opts.path = (fixtures.root() / spec.rootDirectory).string();
opts.recursive = true;
opts.noEmbeddings = false; // ensure vectors are generated
auto addRes = ingiter.addViaDaemon(opts);
ASSERT_TRUE(addRes);
createSearchCorpus returns both a flat list (corpus.fixtures) and a topic map (corpus.fixturesByTopic) so callers can pick targeted subsets (for example topic:python). All artifacts live under FixtureManager::root() and are removed when the manager is destroyed.
Tip: Integration smoke tests (daemon ingestion) and metadata-heavy unit suites (search service) are wired to
FixtureManager; prefer extending those helpers over ad-hocstd::ofstreamusage when adding new file-backed scenarios.
Code Coverage
Generating Coverage Reports
# Build with coverage enabled
cmake -B build -DENABLE_COVERAGE=ON
cmake --build build
# Run tests
cd build
ctest
# Generate coverage report
make coverage
# View HTML report
open coverage_report/index.html
# Generate LCOV report
lcov --capture --directory . --output-file coverage.info
lcov --remove coverage.info '/usr/*' '*/tests/*' --output-file coverage.info
genhtml coverage.info --output-directory coverage_html
Coverage Goals
- Unit tests: >80% line coverage
- Integration tests: >70% scenario coverage
- New features: Must include tests before merge
- Critical paths: 100% coverage required
Continuous Integration
GitHub Actions Workflow
The CI pipeline runs on every push and pull request:
- Build Matrix: Tests on Ubuntu, macOS, Windows
- Test Execution: All test categories
- Coverage Upload: Results sent to Codecov
- Benchmark Regression: Compares with baseline
- Report Generation: Creates test artifacts
Pre-commit Hooks
Install pre-commit hooks to run tests locally:
# Install hook
cp tests/scripts/pre-commit.sh .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
# Hook runs:
# - Quick unit tests
# - Code coverage check
# - Benchmark regression check
Best Practices
General Guidelines
-
Test Naming: Use descriptive names that explain what is being tested
- Good:
ExtractTextFromMultiPagePDF - Bad:
Test1
- Good:
-
Test Independence: Tests should not depend on execution order
- Use
SetUp()andTearDown()for initialization/cleanup - Don't share state between tests
- Use
-
Assertions: Use appropriate Catch2 macros
REQUIRE(...): Fatal failures that abort the testCHECK(...): Non-fatal failures that continue the testSKIP("reason"): Skip a test case at runtime- Filter by tag:
./test_binary "[tag]"(replaces--gtest_filter)
-
Test Data: Use generators and fixtures instead of hardcoded data
- Ensures reproducibility
- Simplifies maintenance
-
Error Testing: Always test error conditions
- Invalid input
- Resource failures
- Boundary conditions
Performance Testing
-
Benchmark Isolation: Minimize external factors
- Use
benchmark::DoNotOptimize()to prevent optimization - Run multiple iterations for stability
- Use
-
Regression Detection: Set appropriate thresholds
- Time-based: 10% increase is regression
- Throughput: 10% decrease is regression
-
Environment Recording: Capture system information
- Platform, compiler, build type
- Hardware specifications
- Resource availability
Integration Testing
-
Realistic Scenarios: Test actual user workflows
- Complete operations end-to-end
- Include error recovery paths
-
Concurrency Testing: Verify thread safety
- Multiple readers/writers
- Race condition detection
- Deadlock prevention
-
Data Integrity: Verify consistency
- Transaction atomicity
- Rollback behavior
- Data persistence
Troubleshooting
Common Issues
Tests fail with "fixture not found"
- Ensure test data directory exists
- Check file permissions
- Verify fixture manager initialization
Benchmarks show high variance
- Increase iteration count
- Close other applications
- Use performance CPU governor
- Run in release mode
Coverage reports missing files
- Check CMake coverage flags
- Ensure gcov/llvm-cov installed
- Verify source file paths
Integration tests timeout
- Check for deadlocks
- Increase timeout limits
- Verify resource cleanup
Debug Techniques
# Run single test case by tag (Catch2 filter)
./builddir/tests/<test_binary> "[pdf]"
# Run under gdb and break inside a Catch2 TEST_CASE
gdb ./builddir/tests/<test_binary>
(gdb) break Catch::RunContext::runCurrentTest
(gdb) run "[pdf]"
# Verbose test output
meson test -C builddir -v --print-errorlogs
# Run with sanitizers (configure once)
meson setup build/asan --buildtype=debug -Dbuild-tests=true -Db_sanitize=address
meson test -C build/asan
Contributing Tests
When adding new features:
- Write tests first (TDD approach)
- Cover happy path and error cases
- Add benchmarks for performance-critical code
- Document test purpose in comments
- Update this README if adding new patterns
Checklist for New Tests
- Unit tests for new components
- Integration tests for workflows
- Benchmarks for performance-critical paths
- Error handling tests
- Documentation in test files
- Coverage meets requirements
- CI passes on all platforms
Performance Baselines
Current performance baselines (as of v0.0.7):
| Operation | Baseline | Unit | Notes |
|---|---|---|---|
| SHA256 (1MB) | 3.5 | ms | Single-threaded |
| PDF Extract (10 pages) | 50 | ms | With PDFium |
| Search (10K docs) | 25 | ms | Full-text search |
| Metadata Update | 2 | ms | Single field |
| Document Ingestion | 100 | docs/s | 1KB documents |
Contact
For test-related questions:
- Check existing test examples
- Consult this documentation
- Review CI logs for patterns
- Open an issue with test label