Skip to content

Feat/test quality execution#32

Merged
SaschaOnTour merged 7 commits into
mainfrom
feat/test-quality-execution
Jun 4, 2026
Merged

Feat/test quality execution#32
SaschaOnTour merged 7 commits into
mainfrom
feat/test-quality-execution

Conversation

@SaschaOnTour

Copy link
Copy Markdown
Owner

No description provided.

SaschaOnTour and others added 7 commits June 1, 2026 14:51
…1.3.1)

has_test_attr matched only path segments test/rstest/test_case, so a
#[quickcheck] property fn (segment `quickcheck`, not ending in `test`)
was treated as production code and could be flagged DEAD_CODE / uncovered.
Add `quickcheck` to the last-segment match. Failing-first regression test
added to framework_test_attributes_recognized.

Slice 0a of the test-quality-execution plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
syn does not expand function-like macros, so #[test] fns declared inside
proptest!{} / quickcheck!{} were opaque Item::Macro nodes — invisible to
test classification and every analyzer dimension.

Add a macro-expansion pre-pass (adapters/shared/macro_expansion.rs) run
once at the top of run_analysis: it replaces a recognised test-macro
invocation with the fn items it declares, each marked #[test] so they
route through the shared cfg_test::has_test_attr recognition. Lenient by
design — proptest's non-Rust `x in <strategy>` params are dropped (body
preserved), a leading #![proptest_config(..)] is tolerated, and any parse
failure keeps the original opaque macro (blind spot, never a regression).
Recognition lives next to cfg_test in adapters/shared (one place for all
test-recognition policy).

run_analysis now takes parsed by value so the pre-pass can rewrite in
place; the single hook covers both production entry points and all tests.

Failing-first regression tests in
src/adapters/shared/tests/macro_expansion.rs. Slice 0b of the
test-quality-execution plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduces the configuration surface for applying a curated subset of
checks to test code (Phase #1, Slice 1a — scaffolding only, no behaviour
change yet).

TestsConfig holds Option<usize> overrides — max_function_lines (LONG_FN),
file_length_baseline, file_length_ceiling, max_methods — each defaulting
to None, meaning the matching production threshold is inherited. So out of
the box test code is judged by the same limits as production; a project can
relax them per field. The curated APPLY/KEEP split itself is fixed (not
configurable). Field names mirror [complexity] / [srp].

Resolver methods that read these are added in the consuming slices (1c
LONG_FN, 1d SRP) to avoid dead code. Documented in rustqual.toml and
book/reference-configuration.md; tests in config/tests/sections.rs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tests

Slice 1b cleanup (gate still off — pure test refactor). The pub_fns tests
each repeated the same ~13-line PubFnInputs construction (empty wrapper /
promoted-attr / workspace sets) verbatim across ~39 tests. Extract a
pub_fns_by_layer(&files) helper; the 3 tests that need a non-empty set
still call collect_pub_fns_by_layer directly.

Cuts the DRY fallout on pub_fns.rs from 988 fragment entries → 2 and 56
duplicate-fn entries → 14 (measured with DRY applied to tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 1 of the test-quality-execution plan: a curated subset of quality
checks now runs on test code, at `[tests]`-section thresholds that default
to the production values.

- DRY (DRY-001/004/005) run on tests; `[duplicates] ignore_tests` removed
  (BREAKING config: a stale `ignore_tests` line now fails to parse).
- LONG_FN runs on test fns at `[tests].max_function_lines`.
- SRP file-length (SRP_MODULE) runs on test files at
  `[tests].file_length_baseline`/`ceiling`. The SRP cohesion
  (independent-cluster) check stays PRODUCTION-ONLY — a test file's many
  independent `#[test]` fns are its purpose, not a low-cohesion smell.
  `analyze_module_srp` takes a `test_length_thresholds: (usize, usize)`
  tuple (one param to stay within SRP_PARAMS).

Remediation done as real fixes, not suppressions:
- ~25 oversized test files split along behavioral seams into focused
  directory sub-modules (mod.rs + thematic parts).
- All 20 transient LONG_FN test suppressions added during 1c were then
  eliminated via arrange/act separation (const-hoisted fixtures/tables,
  fixture builders, dead-fixture removal, imports/docs lifted out of fn
  bodies). The total `qual:allow(complexity)` count is back to the
  pre-work baseline (9, all in production code) — net zero new
  suppressions across the whole effort.

Docs: CLAUDE.md Test-aware bullet, README, CHANGELOG [1.4.0],
rustqual.toml + book/reference-configuration.md `[tests]` section.

Self-analysis (`cargo run -- . --fail-on-warnings`) = 0 findings across
all seven dimensions; nextest green; clippy -Dwarnings clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…4.1)

Phase 2 of the test-quality effort: prove the suite actually catches
defects (cargo-mutants) and close the gaps it surfaced.

Mutation coverage (cargo-mutants, --no-shuffle, nextest):
- shared/cfg_test*: 89/89 non-equivalent viable mutants caught.
- normalize.rs + macro_expansion.rs: survivors 70 -> 4 (128/132 caught);
  the 4 remaining are documented semantic equivalents (jaccard ||->&&,
  and the Call/Block/Paren arm deletions that fall to an identical
  default-recurse).

Tests added:
- shared/tests/normalize_coverage.rs: enumeration tests pinning the exact
  NormalizedToken for every binary op (28), unary op (3), expr-kind
  (incl. an expr-position `let _ = m!();` reaching the Expr::Macro arm),
  pattern-kind (11), and an or-pattern pipe-count test.
- shared/tests/cfg_test.rs: path x test-attribute variant matrix + two
  proptest properties (qualified `::test` always recognized; non-test
  attrs never recognized).
- shared/tests/cfg_test_files.rs: nested-src/bin and inline-mod
  propagation mutation-killers.
- macro_expansion: non_test_macro_left_untouched strengthened to carry a
  parseable fn so `is_test_macro -> true` is forced to surface it.

Production fixes surfaced during the effort:
- TQ-001: is_assertion_macro now recognizes proptest's prop_assert!/
  prop_assert_eq!/prop_assert_ne! so property tests are not false-flagged
  as assertion-free.
- F1: architecture matcher's has_cfg_test_attr delegates to the shared
  cfg_test::has_cfg_test, giving #[cfg(test)] detection one source of truth.

proptest added as a dev-dependency. .gitignore excludes mutants.out/.
Version 1.4.1, CHANGELOG updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 3 — full mutation sweep (cargo-mutants) extended from the shared core to
every dimension plus app and report. All reachable survivors killed; only
documented semantic equivalents remain. Drove asserts/oracles into many existing
tests and split oversized test files along behaviour seams.

Phase 4 — per-dimension relevance review against book/*-quality.md: positive AND
negative per-rule coverage, oracle quality, behaviour- vs implementation-coupling,
redundancy, test intent. Strengthened weak oracles (dry near-duplicate threshold,
srp named clusters, architecture PatternScope::accepts scoping, coupling
SDP-from-source) and added missing end-to-end pins.

Follow-up design + review-finding fixes:
- SRP: the god-struct check (SRP-001) deliberately applies to test code (a
  god-fixture wiring up many concerns is a real smell); only module-cohesion
  (independent clusters) stays production-only. Removed the dead
  [tests].max_methods knob and pinned the contract.
- TQ-001: no longer flags quickcheck! `-> bool` properties as assertion-free —
  the boolean return is the property's oracle (a plain #[test] can't return bool).
- DEH: honours a function's own #[test]/#[cfg(test)] attrs, so expanded
  quickcheck!/proptest! property bodies (and hand-written top-level #[test] fns)
  are no longer falsely flagged as production downcast escape hatches.
- `rustqual --init` templates now include the [tests] section.
- Docs: book BTC stub forms drop Default::default(); CLAUDE.md DRY rule IDs match
  the emitted ones (DRY-003 fragment, DRY-004 wildcard).

Four gates green: cargo fmt, 1851 nextest, self-analysis 0 findings, clippy.
CHANGELOG updated under [1.4.1]; no version bump.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 4, 2026 13:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

@SaschaOnTour SaschaOnTour merged commit 065a7a9 into main Jun 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants