Skip to content

Kill mutation-testing survivors and widen scaffolding excludes (#566, #567, #569, #570, #593, #595, #598) - #612

Merged
leynos merged 15 commits into
mainfrom
kill-mutation-survivors
Jul 17, 2026
Merged

Kill mutation-testing survivors and widen scaffolding excludes (#566, #567, #569, #570, #593, #595, #598)#612
leynos merged 15 commits into
mainfrom
kill-mutation-survivors

Conversation

@leynos

@leynos leynos commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Summary

Addresses #566, #567, #569, #570, #593, #595, and #598, plus the
mutation-testing worklist's scaffolding-exclude and equivalent-mutant items
(dataset: run 29074771985, main @ 94a5d44). This is a survivor-triage pass:
it kills the highest-value category-1 survivors, annotates equivalent
mutants, and widens the caller's scaffolding exclude-globs. Survivors that
need heavy asynchronous or fragmentation harnesses are deferred to their
issues (listed below) to stay within the survivor-triage tolerance.

Review walkthrough

Scaffolding exclude-glob fix (32 noise survivors)

The src/test_helpers.rs exclude glob matched only the module root, so the
src/test_helpers/ submodules (22 noise survivors) and the cfg(test)-only
companion tests.rs files (10 survivors) were still mutated. The
caller workflow
now excludes src/test_helpers/** and a repo-wide src/**/tests.rs, and the
workflow contract test
pins both. make test-workflow-contracts passes.

Category 1: survivors killed (before → after, per file)

Issue Survivor File Before After
#567 WireframeError::is_clean_closetrue src/error.rs 1 0
#598 protocol() / message_assembler()None src/app/builder/protocol.rs 2 0
#598 trait max_frame_length0/1 src/codec.rs 2 0
#598 ServiceRequest::frame → leaked vecs src/middleware.rs 3 0
#598 should_deserialize_after_parsetrue src/serializer.rs 1 0
#598 take_body_streamNone src/extractor/request.rs 1 0
#569 Packet::is_stream_terminatortrue src/app/envelope.rs 1 0
#569 is_terminatedtrue (mid-stream) src/client/response_stream.rs 1 0
#569 is_shutting_downfalse src/connection/state.rs 1 0
#593, #570 chunk_sizeNone/Some(0)/Some(1); timeoutNone src/client/send_streaming.rs 4 0
#595 check_aggregate_budgets / check_size_limit >>= src/message_assembler/budget.rs 2 0
#595 accept_first_frame_at >>=; purge_expiredvec![] src/message_assembler/state.rs 2 0

Totals for the files touched by this PR: 21 targeted survivors before, 0
after (confirmed by scoped re-runs, see Validation).

New tests live in
tests/codec_error.rs,
tests/wireframe_protocol.rs,
tests/middleware.rs,
tests/serializer.rs (new file),
tests/extractor.rs,
tests/packet_parts.rs,
tests/connection.rs,
src/codec/tests.rs,
src/client/tests/streaming.rs,
src/client/tests/send_streaming.rs, and
src/message_assembler/budget_enforcement_tests.rs.
A small start_shutdown helper was added to
src/connection/test_support.rs
to drive the shutting-down polarity.

Category 2: equivalent mutants annotated

Marked with #[cfg_attr(test, mutants::skip)] (new mutants = "0.0.4"
dev-dependency; no production dependency) with a one-line justification each:

  • src/client/connect_parts.rsINITIAL_READ_BUFFER_CAPACITY (pre-allocation hint, min'd against max_frame_length; pure constant arithmetic)
  • src/client/runtime.rsbuilder (Default is literally Self::new(); pure constructor identity)
  • src/push/queues/mod.rsbuilder (blanket From<T> for T identity; pure constructor identity)
  • src/request/mod.rsselect_correlation guard (pure function; the equal-id path returns the identical value bar a spurious warn!)
  • src/server/runtime/backoff.rsnormalized (pure function; the >= variant swaps two equal Durations, a no-op)
  • src/message_assembler/budget.rsis_enabled (Mutation testing: message assembler budget and expiry survivors #595 accepted; pure function over two Options guarding a hot-path shortcut whose slow path returns the same result)

All six retained skips are stateless equivalences of pure functions or
constants
— the mutated expression's value is provably identical for every
input, so no example, property, or model-checking harness could distinguish
them; a justification comment is the proportionate evidence.

Four skips originally proposed here were removed in review and replaced
with unit tests that kill the mutants (red-green verified, scoped
cargo mutants confirmed):

  • src/client/pool/slot.rs try_acquire_permit — the reviewer is right that
    forcing None removes the non-blocking fast path; a permit-capacity unit
    test now kills all variants.
  • src/client/pool/slot.rs clear_last_returned_at — a stale timestamp can
    trigger a spurious recycle of a fresh connection whose lease never wrote a
    new timestamp; a recycle-decision unit test now kills the empty-body mutant.
  • src/session.rs get cleanup predicate — retaining the dead weak entry
    grows the registry until a later prune; a unit test asserts the failed
    lookup evicts the entry.
  • src/connection/multi_packet.rs is_stamping_enabled — although the
    disabled polarity is unreachable through today's actor API, it is directly
    assertable at unit level, so a stamping-invariant test replaces the
    reachability argument.

Deferred to the issues (outside this PR's tolerance)

Validation

Documentation

The developers' guide
mutation-testing section and
ADR-007
now record the eight-shard dispatch fan-out, the widened exclude-globs, the
workflow contract test, and the mutants::skip annotation convention with its
dev-dependency.

No production behaviour changed: every non-test source edit is either a
mutants::skip annotation with a justification comment or a #[cfg(test)]
module.

@sourcery-ai sourcery-ai Bot 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.

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The PR expands mutation-testing exclusions, annotates equivalent mutants, and adds focused tests for budget boundaries, expiry, client configuration, connection state, codec behaviour, errors, serializers, requests, packets, and protocol accessors.

Changes

Mutation and contract coverage

Layer / File(s) Summary
Mutation-testing setup and equivalent-mutant exclusions
.github/workflows/mutation-testing.yml, Cargo.toml, src/client/..., src/connection/multi_packet.rs, src/message_assembler/budget.rs, src/push/queues/mod.rs, src/request/mod.rs, src/server/runtime/backoff.rs, src/session.rs, tests/workflow_contracts/...
Expand mutation exclusions, add the mutants development dependency, and skip documented equivalent mutants under test builds.
Budget boundaries and expiry validation
src/message_assembler/budget_enforcement_tests.rs
Test exact budget and size limits, plus immediate expiry and state clearing from purge_expired().
Client, connection, and codec coverage
src/client/tests/..., src/codec/tests.rs, src/connection/test_support.rs, tests/connection.rs
Test streaming configuration accessors, pre-termination state, trait-dispatched frame limits, and actor shutdown state.
Error, request, packet, serializer, and protocol contracts
tests/codec_error.rs, tests/extractor.rs, tests/middleware.rs, tests/packet_parts.rs, tests/serializer.rs, tests/wireframe_protocol.rs
Test clean-close classification, one-shot body extraction, frame access, envelope termination, serializer behaviour, and installed protocol or assembler accessors.

Possibly related issues

Possibly related PRs

Suggested labels: Issue

Poem

Mark the mutants, test the bounds,
Let clean errors make no sounds.
Streams know when they should be done,
Builders show what they have won.
Exclude the scaffolds; let coverage run!

🚥 Pre-merge checks | ✅ 17 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Developer Documentation ⚠️ Warning Docs lag the change: the workflow now excludes extra paths and adds mutants, but developers-guide/ADR records were not updated to match. Update docs/developers-guide.md and the mutation-testing ADR/execplan to cover the new mutants dev-dependency, added exclude globs, and current workflow contract.
Testing (Property / Proof) ⚠️ Warning Stateful equivalence claims in session, pool-slot, and multi-packet code are only justified by comments; no property/model proof covers those transitions. Add property/Kani checks for those transition invariants, or drop the skip annotations and keep example tests for the real behaviours.
Testing (Unit And Behavioural) ❓ Inconclusive placeholder placeholder
✅ Passed checks (17 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Testing (Overall) ✅ Passed The new tests exercise real boundary and state-transition behaviour (stream termination, budget limits, shutdown, accessors) rather than mere setup.
User-Facing Documentation ✅ Passed PASS: The PR is test/internal tooling only; no new user-facing behaviour is introduced, and the users guide already covers the touched public APIs.
Module-Level Documentation ✅ Passed Every touched Rust module begins with a //! doc block describing its role, and the new test modules do likewise.
Testing (Compile-Time / Ui) ✅ Passed PASS: the PR only adds runtime/unit and workflow tests; it introduces no compile-time or UI-output behaviour, so no trybuild or snapshot harness was required.
Unit Architecture ✅ Passed Production edits are limited to test-only mutation-skip annotations; new helpers/tests stay in test support and exercise explicit state transitions without new hidden side-effects.
Domain Architecture ✅ Passed PASS: edits stay in test-only config, test support, and cfg_attr(test) skips; no domain logic now depends on transport or infrastructure concerns.
Observability ✅ Passed PASS: The PR only adds tests, mutation-skip annotations, and a dev-dependency; no production operational path or new failure mode is introduced.
Security And Privacy ✅ Passed Only dev-only mutation-testing annotations/tests changed; workflow stays least-privilege and pinned, with no secrets, auth bypasses, or sensitive-data exposure.
Performance And Resource Use ✅ Passed PASS: only test additions, comments, and test-only mutation skips were introduced; no production hot paths, loops, or allocations changed.
Concurrency And State ✅ Passed State stays behind Mutex/DashMap/Semaphore or actor ownership; no lock is held across awaits, and the new shutdown helper/test makes the lifecycle transition explicit.
Architectural Complexity And Maintainability ✅ Passed PASS: The PR adds only test coverage, dev-only mutation annotations, and a thin test helper; it introduces no new runtime layer, registry, or abstraction.
Rust Compiler Lint Integrity ✅ Passed No new #[allow]/#[expect] suppressions or artificial references were added in the touched Rust files, and the only new clone is cheap Arc ownership in tests.
Title check ✅ Passed The title matches the mutation-testing survivor work and includes the linked issue numbers.
Description check ✅ Passed The description clearly describes the same mutation-testing exclusions, tests, and equivalent-mutant annotations.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kill-mutation-survivors

Comment @coderabbitai help to get the list of available commands.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@pandalump
pandalump marked this pull request as ready for review July 17, 2026 15:53
@leynos
leynos force-pushed the kill-mutation-survivors branch from ac9a598 to d769287 Compare July 17, 2026 16:05
codescene-access[bot]

This comment was marked as outdated.

leynos added 11 commits July 17, 2026 17:11
The mutation-testing caller's `src/test_helpers.rs` exclude glob matched
only the module root, leaving the `src/test_helpers/` submodules
(frame_codec.rs, pool_client.rs) to emit noise survivors. Companion
`tests.rs` files carrying cfg(test)-only modules were likewise mutated
because cargo-mutants cannot detect them as test code.

Add `src/test_helpers/**` and a repo-wide `src/**/tests.rs` glob to the
caller and pin both in the workflow contract test.
The WireframeError wrapper only had its clean-close true case asserted;
every false-case assertion targeted CodecError directly, so a wrapper
that unconditionally reported a clean close passed the suite. Add
false-case assertions for the Io, mid-frame Codec, and Protocol variants
at the WireframeError level.

Addresses #567.
A cluster of public accessors and trait methods was never asserted
directly, so each could be replaced with a constant. Add direct
assertions for WireframeApp::protocol and message_assembler, the
FrameCodec trait's max_frame_length via UFCS dispatch (the inherent
method shadows it), ServiceRequest::frame, BincodeSerializer's
should_deserialize_after_parse, and MessageRequest::take_body_stream's
take-once semantics.

Addresses #598.
Several state predicates were asserted in only one polarity, so forcing
them to a constant survived the suite. Assert a plain Envelope reports
is_stream_terminator() == false, that a response stream is not terminated
mid-stream, and that the actor state reports is_shutting_down() == true
during shutdown (via a new ActorStateHarness::start_shutdown helper).

Addresses #569.
Nine surviving mutants are equivalent: they cannot change observable
behaviour, so no test can kill them. Add the mutants dev-dependency and
mark each with #[cfg_attr(test, mutants::skip)] plus a one-line
justification, keeping them out of future survivor reports without a
production dependency.

Addresses #566, #595, and the worklist equivalent-mutant list.
The budget and size-limit checks were only asserted strictly over their
limits, and the wall-clock purge_expired() was never called. Add
exact-fit assertions for the connection budget, the per-message size
limit, and a first frame declaring exactly the limit, plus a zero-timeout
test that drives the no-argument purge_expired().

Addresses #595.
SendStreamingConfig's chunk_size and timeout getters have no production
callers, so their accessor mutants survived. Assert both reflect the
builder and default to None.

Addresses #593 and #570.
Reflow the assertions added in the preceding survivor-kill commits to
satisfy the formatting gate.
is_stamping_enabled's false polarity is unreachable during frame
processing: the sole MultiPacketContext construction site always installs
the channel with install(Some(rx), _), which yields Enabled, so a
Disabled stamp never coexists with an active channel. The survivor is
equivalent, not a test gap; #569's proposed disabled-stamping test cannot
be constructed through the actor API. Mark it accordingly.

Addresses #569.
Drop the Result return from the two accessor tests (panic_in_result_fn)
and replace an unwrap with expect in the declared-total submission helper
(unwrap_used).
Move the send-streaming config accessor test into its own module so
send_streaming.rs stays under the 400-line cap, and inline the
declared-total submission into its test function so the expect call
lives inside a test rather than a plain helper.
@pandalump
pandalump force-pushed the kill-mutation-survivors branch from d769287 to 57fce33 Compare July 17, 2026 16:11
codescene-access[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot added the Issue label Jul 17, 2026

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/client/pool/slot.rs`:
- Around line 50-54: Remove the mutation-suppression attributes covering
try_acquire_permit, clear_last_returned_at, and the cleanup predicate using
strong_count() == 0. Preserve mutation coverage for the immediate lease path,
timestamp clearing, and removal of dead weak registry entries; add targeted
tests or narrowly scoped exclusions only if a specific mutation is proven
equivalent. Apply the changes in src/client/pool/slot.rs lines 50-54 and
101-104, and src/session.rs lines 96-100.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4537fb9a-09d1-4f0e-8650-0bf018bc5667

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba1473 and 57fce33.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (25)
  • .github/workflows/mutation-testing.yml
  • Cargo.toml
  • src/client/connect_parts.rs
  • src/client/pool/slot.rs
  • src/client/runtime.rs
  • src/client/tests/mod.rs
  • src/client/tests/send_streaming_config.rs
  • src/client/tests/streaming.rs
  • src/codec/tests.rs
  • src/connection/multi_packet.rs
  • src/connection/test_support.rs
  • src/message_assembler/budget.rs
  • src/message_assembler/budget_enforcement_tests.rs
  • src/push/queues/mod.rs
  • src/request/mod.rs
  • src/server/runtime/backoff.rs
  • src/session.rs
  • tests/codec_error.rs
  • tests/connection.rs
  • tests/extractor.rs
  • tests/middleware.rs
  • tests/packet_parts.rs
  • tests/serializer.rs
  • tests/wireframe_protocol.rs
  • tests/workflow_contracts/mutation_testing_test.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/rust-prover-tools (auto-detected)
  • leynos/whitaker (auto-detected)
  • leynos/shared-actions (auto-detected) → reviewed against open PR #355 kill-mutation-survivors instead of the default branch

Comment thread src/client/pool/slot.rs Outdated
@leynos leynos changed the title Kill mutation-testing survivors and widen scaffolding excludes Kill mutation-testing survivors and widen scaffolding excludes (#566, #567, #569, #570, #593, #595, #598) Jul 17, 2026
leynos added 4 commits July 17, 2026 18:46
Review of the survivor-triage PR challenged three mutants::skip
annotations as false equivalents: the pool slot's non-blocking permit
fast path, the idle-timestamp clear that guards against recycling a
freshly created connection, and the session registry's lazy eviction of
dead weak entries. Each mutation does change observable behaviour, so
drop the skips and add unit tests that kill the mutants directly. Also
drop the is_stamping_enabled skip: the disabled polarity is directly
assertable at unit level, making a reachability argument unnecessary.

Addresses review feedback on the survivor-triage changes for #566, #569,
and the worklist equivalents.
Record in the developers' guide and ADR-007 the eight-shard dispatch
fan-out, the added exclude-globs (src/test_helpers/** and
src/**/tests.rs, #599), the workflow contract test that pins the caller
configuration, and the convention for annotating proven-equivalent
mutants with cfg_attr(test, mutants::skip) via the mutants
dev-dependency.
Use minute-based Duration construction and an infallible SocketAddr
literal so the new slot tests pass clippy's duration-unit lint and
whitaker's no-expect-outside-tests rule.
The new typos gate on main enforces en-GB-oxendict, which requires the
-ize form.
codescene-access[bot]

This comment was marked as outdated.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No quality gates enabled for this code.

@leynos
leynos marked this pull request as draft July 17, 2026 17:01
@leynos
leynos marked this pull request as ready for review July 17, 2026 19:15

@sourcery-ai sourcery-ai Bot 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.

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@leynos
leynos merged commit 0a7e58b into main Jul 17, 2026
11 checks passed
@leynos
leynos deleted the kill-mutation-survivors branch July 17, 2026 19:16

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1df84ef92f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# helper submodules (frame_codec.rs, pool_client.rs). `src/**/tests.rs`
# covers the cfg(test)-only test modules split into companion files,
# which cargo-mutants cannot detect as test code (#599).
exclude-globs: "src/codec/examples.rs,src/test_helpers.rs,src/test_helpers/**,src/connection/test_support.rs,src/**/tests.rs"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude test submodule directories too

In the mutation workflow I inspected, this glob only excludes files whose path ends in tests.rs; it does not cover cfg(test)-only submodule files such as the newly added src/client/tests/send_streaming_config.rs or the existing src/fragment/tests/*.rs files loaded from #[cfg(test)] mod tests;. cargo-mutants documents that --exclude filters source files by glob and that path globs match root-relative file paths, so these files remain in scope and the false-survivor noise described in the comment continues for test-only submodules. Add an exclude such as src/**/tests/** or the specific test directories as well.

Useful? React with 👍 / 👎.

Comment thread src/request/mod.rs
// Equivalent mutant (`found != expected` guard → `true`): when the ids are
// equal the data path is identical to the mismatch arm bar a spurious
// `warn!`, so the returned correlation is unchanged.
#[cfg_attr(test, mutants::skip)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep testing the correlation mismatch guard

This mutant is not equivalent when a request already has the same correlation id as the source: forcing the guard to the mismatch arm still returns the same value, but inherit_correlation then emits the mismatch log::warn!. That warning is observable diagnostics and would incorrectly report healthy requests as mismatched, so marking the guard with mutants::skip hides a real regression rather than an equivalent mutant.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant