Skip to content

[BUG] --coverage reports 0% for contract/library source when tests live in the recommended separate test crate #24988

Description

@shiqicao

Summary

The docs tell us to keep all #[test] functions in a dedicated test crate, and aztec test warns when tests are found in a contract crate. But --coverage only attributes execution to files of the crate directly under test, so a project laid out the recommended way reports 0% for every contract and library file it owns — no matter how thorough its tests are.

The two pieces of guidance are mutually exclusive today, and there's no documented way to satisfy both:

  • Follow "keep tests in the test crate" → your contract/library source is never attributed → permanently 0%.
  • Get a real number → tests must be co-located in the crate under test → warning, unnecessary recompilation, and test source embedded in the shipped artifact (below).

From the docs (Keep tests in the test crate):

Keep all #[test] functions in the test crate, not in the contract crate.

and errors/1:

WARNING: Found tests in contract crate(s): … Tests should be in a dedicated test crate, not in the contract crate.

Environment

  • aztec 5.0.1, bundled nargo 1.0.0-beta.22 (noirc 1.0.0-beta.22+c57152f91260ecdb9faad4efc20abb14b6d2ece7)
  • Command: aztec test --coverage on a workspace of X / X_test pairs

Reproduction

A workspace laid out exactly as the docs recommend:

Nargo.toml                   # workspace members: common, common_test, …
common/                      # type = "lib"      — library under test, no #[test] fns
common_test/                 # type = "lib"      — deps: common; contains all #[test] fns
token_bridge_contract/       # type = "contract" — no #[test] fns
token_bridge_contract_test/  # type = "lib"      — deps: token_bridge_contract; all #[test] fns

Run aztec test --coverage, then inspect target/coverage/<package>/lcov.info.

Observed:

  1. target/coverage/common_test/lcov.info emits SF: entries only for its own common_test/src/*_test.nr files. It never emits an SF: for common/src/*.nr, although every one of those files is exercised by those tests.
  2. target/coverage/common/lcov.info does list all 7 common/src/*.nr files — with all 155 instrumented lines at zero hits.
  3. Every production contract reads 0%: token_bridge_contract/src/main.nr 0/147, token_vault_bridge_contract/src/main.nr 0/203, and so on.

This is about placement, not test quality. We took an existing test out of common_test/src/packable_bounded_vec_test.nr and put an equivalent mod test inside common/src/packable_bounded_vec.nr — same assertions, no new logic. That file went from 0% to 97% (32/33 lines).

Cause

The evaluation tracker is narrowed to the crate under test (tooling/nargo_cli/src/cli/test_cmd.rs#L690-L693):

if let Some(evaluation_tracker) = context.evaluation_tracker.as_mut() {
    let crate_files = context.def_maps[&crate_id].file_ids();
    evaluation_tracker.restrict_to_files(&crate_files);
}

and the baseline is built from the same set (test_cmd/coverage.rs#L22-L41):

let def_map = &context.def_maps[&crate_id];
let allowed_files = def_map.file_ids();

So a separate X_test package — the documented layout — can never contribute coverage to X's source.

Why co-locating tests isn't an acceptable workaround

Beyond the recompilation reason the docs give, co-located test source is embedded in the compiled artifact's file_map, including on a production aztec-nargo compile. functions/bytecode stay clean, so there's no circuit or ABI impact — but file_map is the largest section of the artifact (~810KB of a ~994KB contract JSON in our case), and projects that publish their contracts as an npm package ship those target/*.json files to consumers. So co-locating means shipping test source downstream.

It also wouldn't actually work for TXE tests: see #24987 — under --coverage, oracle-calling tests are skipped entirely, so co-locating them yields no coverage either.

Ask

  1. Attribute coverage across workspace path dependencies: when a test in crate B executes code from A (a path dependency in the same workspace), count it toward A's source, the way cargo llvm-cov and gcov-based tooling do — ideally keyed by executed source location rather than package ownership.
  2. Or add an opt-in flag (--coverage-include-deps / --coverage-workspace) that widens instrumentation to workspace path dependencies.
  3. Failing either, please document how a project that follows "Keep tests in the test crate" is supposed to obtain coverage for its own contract and library code. As written, the testing-layout guidance and the --coverage feature contradict each other, and we'd rather not build a workaround around a gap you intend to close.

Note: --coverage is implemented in Noir (nargo test --coverage), so the fix may belong in noir-lang/noir. Filing here because the test-layout recommendation is Aztec's and aztec test is the documented entry point — happy to re-file upstream if you prefer.

Related: #24987 (--coverage silently skips oracle-calling tests and still reports them as passed).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions