Skip to content

Implement dynamic test fixtures for lint exclusions (#111)#214

Open
leynos wants to merge 13 commits into
mainfrom
test-fixtures-for-exclusion-lint-t0egzl
Open

Implement dynamic test fixtures for lint exclusions (#111)#214
leynos wants to merge 13 commits into
mainfrom
test-fixtures-for-exclusion-lint-t0egzl

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Apr 15, 2026

Summary

  • Introduces dynamic, runtime-generated fixtures for exclusion lint tests in integration_exclusion.rs. Replaces static fixture files with on-the-fly crates created in temporary directories.

Changes

  • Added dynamic fixture generator in tests/integration_exclusion.rs:
    • FixtureProject struct to hold a TempDir and root path
    • create_fixture_project(crate_name, is_excluded) to build a temporary crate with Cargo.toml, dylint.toml, and src/lib.rs
    • fixture_dylint_config(crate_name, is_excluded) to configure excluded_crates according to the test
    • fixture_source(crate_name) to generate a small crate source implementing open_file
    • assert_fixture_behaviour(...) helper to run cargo-dylint and validate results
  • Replaced reliance on static fixtures with runtime fixtures for excluded vs. non-excluded crates:
    • excluded_crate_suppresses_diagnostics test
    • non_excluded_crate_emits_diagnostics test
  • Introduced dependency on tempfile for temporary directories and updated relevant crates to reference it
  • Removed obsolete static fixtures under tests/fixtures (e.g., excluded_project and non_excluded_project)
  • Updated test helpers and flow to align with dynamic fixture creation (e.g., run_cargo_dylint, diagnostic_count usage)

Why

  • Dynamic, isolated fixtures reduce duplication and maintenance of large on-disk fixtures and simplify extending tests for lint exclusions.

Migration / Compatibility

  • Tests no longer rely on static fixture files; they generate fixtures at runtime in temporary directories.
  • The tests are annotated with #[ignore] by default because they require cargo-dylint and a built lint library.

Test plan

  • To run locally (requires cargo-dylint and a built lint library):
    • cargo test -p whitaker --test integration_exclusion -- --ignored
  • The suite covers:
    • Excluded crate: no diagnostics emitted and test succeeds
    • Non-excluded crate: diagnostics emitted and test fails as expected

Diffs touched

  • tests/integration_exclusion.rs: add dynamic fixture generation and related helpers
  • crates/no_std_fs_operations/tests/fixtures/...: removed obsolete static fixtures
  • project Cargo.toml: add tempfile dependency for tests
  • Cargo.lock: updated to include tempfile (dependency graph reflects changes)

◳ Generated by DevBoxer


ℹ️ Tag @devboxerhub to ask questions and address PR feedback

📎 Task: https://www.devboxer.com/task/a2b8d62b-4c8f-4dc0-b582-42de342d2760

📝 Closes #111

Summary by Sourcery

Replace static on-disk lint exclusion fixtures with dynamically generated temporary projects in integration tests.

New Features:

  • Add runtime-generated fixture projects for testing excluded and non-excluded crates in no_std_fs_operations integration tests.

Enhancements:

  • Refactor exclusion integration test into explicit excluded and non-excluded crate scenarios using a shared assertion helper.
  • Introduce a temporary project abstraction to encapsulate per-test fixture directories and configuration.

Build:

  • Add tempfile as a test dependency for creating temporary integration test fixtures.

Tests:

  • Update exclusion integration tests to construct fixtures in temporary directories instead of using pre-checked-in fixture projects.
  • Remove obsolete fixture projects under tests/fixtures now that tests generate their own crates at runtime.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 15, 2026

Reviewer's Guide

Implements dynamic, runtime-generated integration test fixtures for lint exclusion behavior in the no_std_fs_operations crate, replacing on-disk fixture projects with temporary crates and updating tests and dependencies accordingly.

Sequence diagram for dynamic lint exclusion integration tests

sequenceDiagram
    actor Tester
    participant Test as excluded_crate_suppresses_diagnostics
    participant Fixture as create_fixture_project
    participant Config as fixture_dylint_config
    participant Source as fixture_source
    participant Assert as assert_fixture_behaviour
    participant CargoDylint as run_cargo_dylint
    participant Diagnostics as diagnostic_count

    Tester->>Test: invoke
    Test->>Fixture: create_fixture_project(crate_name, is_excluded=true)
    Fixture->>Fixture: create TempDir
    Fixture->>Config: fixture_dylint_config(crate_name, is_excluded=true)
    Config-->>Fixture: dylint_toml_contents
    Fixture->>Source: fixture_source(crate_name)
    Source-->>Fixture: lib_rs_contents
    Fixture->>Fixture: write Cargo.toml, dylint.toml, src/lib.rs
    Fixture-->>Test: FixtureProject

    Test->>Assert: assert_fixture_behaviour(FixtureProject, expect_diagnostics=false)
    Assert->>CargoDylint: run_cargo_dylint(FixtureProject)
    CargoDylint-->>Assert: output
    Assert->>Diagnostics: diagnostic_count(output)
    Diagnostics-->>Assert: count
    Assert->>Assert: assert count == 0
    Assert-->>Test: success

    Tester->>Test: invoke non_excluded_crate_emits_diagnostics
    Test->>Fixture: create_fixture_project(crate_name, is_excluded=false)
    Fixture->>Config: fixture_dylint_config(crate_name, is_excluded=false)
    Config-->>Fixture: dylint_toml_contents
    Fixture-->>Test: FixtureProject

    Test->>Assert: assert_fixture_behaviour(FixtureProject, expect_diagnostics=true)
    Assert->>CargoDylint: run_cargo_dylint(FixtureProject)
    CargoDylint-->>Assert: output
    Assert->>Diagnostics: diagnostic_count(output)
    Diagnostics-->>Assert: count
    Assert->>Assert: assert count > 0 and test fails as expected
Loading

Updated class diagram for dynamic fixture helpers in integration_exclusion tests

classDiagram
    class FixtureProject {
        +TempDir temp_dir
        +PathBuf root_path
        +PathBuf root()
        +PathBuf src_dir()
        +PathBuf cargo_toml_path()
        +PathBuf dylint_toml_path()
        +PathBuf lib_rs_path()
    }

    class TempDir {
        +path() PathBuf
        +close() Result
    }

    class IntegrationExclusionTests {
        +excluded_crate_suppresses_diagnostics()
        +non_excluded_crate_emits_diagnostics()
    }

    class FixtureFunctions {
        +create_fixture_project(crate_name, is_excluded) FixtureProject
        +fixture_dylint_config(crate_name, is_excluded) String
        +fixture_source(crate_name) String
        +assert_fixture_behaviour(fixture_project, expect_diagnostics)
    }

    class CargoDylintRunner {
        +run_cargo_dylint(fixture_project) String
        +diagnostic_count(output) usize
    }

    FixtureProject --> TempDir : owns
    IntegrationExclusionTests --> FixtureFunctions : uses
    FixtureFunctions --> FixtureProject : creates
    FixtureFunctions --> CargoDylintRunner : uses
    CargoDylintRunner --> FixtureProject : reads
Loading

File-Level Changes

Change Details Files
Replace static on-disk fixture projects with dynamically generated temporary crates for exclusion tests.
  • Introduce FixtureProject struct wrapping a TempDir and root PathBuf to manage temporary test projects
  • Add create_fixture_project to write minimal Cargo.toml, dylint.toml, and src/lib.rs into a TempDir based on excluded/non-excluded behavior
  • Generate fixture dylint configuration via fixture_dylint_config, toggling excluded_crates based on is_excluded
  • Generate a simple library source with an open_file helper via fixture_source using the crate name
crates/no_std_fs_operations/tests/integration_exclusion.rs
Refactor exclusion behavior tests to use explicit dynamic fixtures and a shared assertion helper instead of rstest-based, static-fixture parametrization.
  • Remove rstest usage, including the lint_library_path fixture annotation and parameterized exclusion_behaviour_matches_fixture_configuration test
  • Introduce explicit #[test] functions excluded_crate_suppresses_diagnostics and non_excluded_crate_emits_diagnostics that each create an appropriate fixture project
  • Add assert_fixture_behaviour helper to run cargo-dylint, count diagnostics, and assert success/diagnostic expectations with clearer error messages
crates/no_std_fs_operations/tests/integration_exclusion.rs
Update test dependencies and remove obsolete fixture directories now replaced by dynamic fixtures.
  • Add tempfile as a workspace-scoped dependency in the no_std_fs_operations crate for use in tests
  • Delete legacy fixture projects under tests/fixtures/excluded_project and tests/fixtures/non_excluded_project, including their Cargo.toml, dylint.toml, and src/lib.rs files
  • Regenerate Cargo.lock to include tempfile and reflect the updated dependency graph
crates/no_std_fs_operations/Cargo.toml
crates/no_std_fs_operations/tests/fixtures/excluded_project/Cargo.toml
crates/no_std_fs_operations/tests/fixtures/excluded_project/dylint.toml
crates/no_std_fs_operations/tests/fixtures/excluded_project/src/lib.rs
crates/no_std_fs_operations/tests/fixtures/non_excluded_project/Cargo.toml
crates/no_std_fs_operations/tests/fixtures/non_excluded_project/dylint.toml
crates/no_std_fs_operations/tests/fixtures/non_excluded_project/src/lib.rs
Cargo.lock

Assessment against linked issues

Issue Objective Addressed Explanation
#111 Provide controllable fixture projects (with crate using std::fs and configurable dylint.toml exclusions) to enable integration testing of no_std_fs_operations lint exclusion behaviour.
#111 Implement an integration test harness that runs cargo-dylint on the fixture projects, parses compiler output to count diagnostics, and asserts behaviour based on exclusion configuration.
#111 Add integration tests that validate both behaviours: excluded crates emit no diagnostics and succeed; non-excluded crates emit diagnostics and fail.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

codescene-delta-analysis[bot]

This comment was marked as outdated.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 15, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f1fc5ecf-7b65-4810-b6b0-00518b0fbc92

📥 Commits

Reviewing files that changed from the base of the PR and between 66247e4 and 154fdc4.

📒 Files selected for processing (3)
  • .config/nextest.toml
  • crates/no_std_fs_operations/tests/integration_exclusion.rs
  • crates/no_std_fs_operations/tests/test_support/mod.rs

Walkthrough

Refactor integration tests to generate TempDir-backed fixture projects at runtime, centralise assertions via a fallible harness using FixtureProject and OnceLock-cached lint builds, remove static fixtures, add workspace/dev-deps (insta, tempfile), run cargo dylint subprocesses, and update docs and nextest config.

Changes

Integration Testing Infrastructure Refactor

Layer / File(s) Summary
Workspace Dependency
Cargo.toml
Add insta = { version = "1", features = ["json"] } to workspace dependencies.
Dependency Addition
crates/no_std_fs_operations/Cargo.toml
Add tempfile = { workspace = true } under [dev-dependencies].
Fixture Removal
crates/no_std_fs_operations/tests/fixtures/...
Remove static fixture projects: excluded_project/* and non_excluded_project/* including Cargo.toml, dylint.toml, and src/lib.rs.
Fixture Generation (Data Shape / Inputs)
crates/no_std_fs_operations/tests/test_support/mod.rs
Introduce FixtureProject backed by tempfile::TempDir and helpers that write Cargo.toml, dylint.toml (with excluded_crates set per case), and src/lib.rs for a named crate; add unit tests for TOML-escaping.
Core Test Flow
crates/no_std_fs_operations/tests/integration_exclusion.rs
Refactor harness to create temporary projects, build and cache lint library, stage toolchain-qualified cdylib, run cargo dylint as a subprocess, collect JSON-formatted diagnostics, and return contextual anyhow::Result errors instead of panicking.
Error Handling / Result Shape
crates/no_std_fs_operations/tests/integration_exclusion.rs
Convert artifact discovery and helpers to return anyhow::Result (replace panics with anyhow::bail!) and change diagnostic counting to Result<usize, anyhow::Error> with raw stdout on parse failures.
Assertions / Wiring
crates/no_std_fs_operations/tests/integration_exclusion.rs
Add run_exclusion_test, assert_fixture_behaviour, evaluate_fixture, and paramaterised tests that assert cargo dylint exit status and validate no_std_fs_operations diagnostic counts (0 when excluded, >0 otherwise); redact absolute paths before snapshotting.
Cleanup / Removed Helpers
crates/no_std_fs_operations/tests/integration_exclusion.rs
Remove prior static-fixture helpers and consolidate flow through the new fixture-creation and evaluation helpers.
Docs & CI config
docs/developers-guide.md, docs/roadmap.md, .config/nextest.toml
Document runtime-generated fixtures, the new harness and CI environment contract, mark roadmap task 4.1.4 completed, and add nextest override for serial execution of integration_exclusion.

Sequence Diagram

sequenceDiagram
  autonumber
  participant TestHarness as TestHarness
  participant Fixture as FixtureProject
  participant FS as Filesystem (TempDir)
  participant Cargo as Cargo/Dylint subprocess
  participant Parser as JSONParser
  participant Assert as Assertions

  TestHarness->>Fixture: create_fixture_project(name, excluded?)
  Fixture->>FS: write `Cargo.toml`, `dylint.toml`, `src/lib.rs`
  TestHarness->>Cargo: run `cargo dylint` in TempDir
  Cargo-->>TestHarness: exit code + stdout (JSON)
  TestHarness->>Parser: parse JSON diagnostics
  Parser-->>TestHarness: diagnostic count for no_std_fs_operations
  TestHarness->>Assert: assert expected count (0 or >0) and exit status
Loading

Poem

TempDir arise where fixtures used to stay,
Cargo dylint speak JSON on display,
Tests spun at runtime, errors now explain,
Exclusions stay quiet, others still complain,
Lints verified ephemeral—run again.


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 2 warnings)

Check name Status Explanation Resolution
Unit Architecture ❌ Error Snapshot test hides fallible JSON parsing behind unwrap_or_else panics (lines 357–362, 372–374), violating explicit fallibility requirements. Replace unwrap_or_else panics with explicit error propagation using Result and anyhow::Context, matching the pattern used in diagnostic_count() and evaluate_fixture().
Observability ⚠️ Warning Snapshot test hides JSON parsing and serialisation errors behind panic!() via unwrap_or_else() rather than propagating through anyhow::Result. Replace unwrap_or_else panics with .context() to propagate parse and serialisation errors as Result values, maintaining consistency with other test helpers.
Concurrency And State ⚠️ Warning Function non_excluded_crate_diagnostics_match_snapshot() returns Result<()> but panics on parse errors instead of propagating, violating stated PR objective to "avoid hidden panics". Replace unwrap_or_else(|e| panic!(...)) with map_err(...)? to propagate cargo JSON and serialisation errors via proper Result channels.
✅ Passed checks (15 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Implement dynamic test fixtures for lint exclusions (#111)' directly relates to the main changeset, which replaces static on-disk fixtures with runtime-generated temporary projects for exclusion-lint integration tests and closes issue #111.
Description check ✅ Passed The description accurately covers the changeset: dynamic fixture generators, temporary directory abstractions, removal of static fixtures, new dependencies, and test case refactoring.
Linked Issues check ✅ Passed The PR fully implements issue #111 requirements: creates fixture infrastructure (FixtureProject, create_fixture_project), runs lint via subprocess with cargo-dylint, parses JSON diagnostics, validates exclusion behaviour, and verifies both excluded and non-excluded crate scenarios.
Out of Scope Changes check ✅ Passed All changes align with issue #111 scope: fixture infrastructure (test_support/mod.rs), integration test refactoring (integration_exclusion.rs), dependency updates (tempfile, insta), removal of obsolete static fixtures, documentation updates, and test scheduling (.config/nextest.toml).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Testing (Overall) ✅ Passed Tests are substantive: parametrised tests with explicit diagnostic count assertions for excluded and non-excluded cases; snapshot tests verify actual output; tests fail if logic inverted.
User-Facing Documentation ✅ Passed PR #214 introduces no new user-facing functionality. It implements integration tests for the excluded_crates feature previously introduced and documented in PR #109.
Developer Documentation ✅ Passed New APIs documented with docstrings; developers-guide has dedicated section for exclusion tests; roadmap 4.1.4 marked complete with issue #111 reference.
Module-Level Documentation ✅ Passed Both modules carry proper module-level documentation: integration_exclusion.rs explains exclusion testing and prerequisites; test_support/mod.rs documents its role as a shared fixture-helper module.
Testing (Unit And Behavioural) ✅ Passed Unit tests validate fixture generation, config parsing, matching, and error paths. Behavioural tests exercise cargo-dylint invocation, JSON parsing, and verify excluded crates suppress diagnostics.
Testing (Property / Proof) ✅ Passed Integration tests validate excluded crates skip diagnostics; non-excluded emit them. No invariants or proof assumptions requiring property tests or formal verification are introduced.
Testing (Compile-Time / Ui) ✅ Passed Snapshot test with 4 specific diagnostics, stable JSON, [FIXTURE_ROOT] redaction of nondeterministic paths, and focused filtering; UI tests cover compile-time behaviour.
Domain Architecture ✅ Passed NoStdFsConfig, ConfigReader, exclusion matching contain no filesystem/subprocess concerns. Test infrastructure isolated with clean boundaries.
Security And Privacy ✅ Passed No security or privacy concerns found. Credentials properly excluded, injection risks mitigated via toml::Value and Command::arg, temporary files use RAII isolation, snapshots redact sensitive paths.
Performance And Resource Use ✅ Passed OnceLock caches lint build once per session; collections bounded by small cargo outputs; subprocess I/O serialised via nextest; TempDir auto-cleanup; no algorithmic regressions.
📋 Issue Planner

Built with CodeRabbit's Coding Plans for faster development and fewer bugs.

View plan used: #111

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test-fixtures-for-exclusion-lint-t0egzl

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

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 1, 2026

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response.

crates/no_std_fs_operations/tests/integration_exclusion.rs

Comment on lines +293 to +305

fn excluded_crate_suppresses_diagnostics() {
    let lint_library_path = lint_library_path();
    let fixture = create_fixture_project("excluded_test_crate", true);
    assert_fixture_behaviour(
        fixture.root(),
        &lint_library_path,
        "excluded_test_crate",
        Expectation {
            should_emit_diagnostics: false,
            should_succeed: true,
        },
    );
}

❌ New issue: Code Duplication
The module contains 2 functions with similar structure: excluded_crate_suppresses_diagnostics,non_excluded_crate_emits_diagnostics

@coderabbitai

This comment was marked as resolved.

codescene-delta-analysis[bot]

This comment was marked as outdated.

…mic temp dirs for integration tests

- Removed static fixture crates under tests/fixtures in favor of ephemeral projects created in temporary directories
- Added FixtureProject utility to create per-test temporary crates with configurable exclusion
- Updated integration_exclusion.rs tests to use dynamic fixture projects instead of static
- Added tempfile dependency for temporary directory management
- Improves test isolation and flexibility for exclusion behavior tests

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 1, 2026

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Ensure that this is validated against the current version of the codegraph.

If further refinement to address this finding would be deleterious, please supply a clear explanatory one to two paragraph markdown message I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

Code Duplication

crates/no_std_fs_operations/tests/integration_exclusion.rs:

What lead to degradation?

The module contains 2 functions with similar structure: excluded_crate_suppresses_diagnostics,non_excluded_crate_emits_diagnostics

Why does this problem occur?

Duplicated code often leads to code that's harder to change since the same logical change has to be done in multiple functions. More duplication gives lower code health.

How to fix it?

A certain degree of duplicated code might be acceptable. The problems start when it is the same behavior that is duplicated across the functions in the module, ie. a violation of the Don't Repeat Yourself (DRY) principle. DRY violations lead to code that is changed together in predictable patterns, which is both expensive and risky. DRY violations can be identified using CodeScene's X-Ray analysis to detect clusters of change coupled functions with high code similarity. Read More
Once you have identified the similarities across functions, look to extract and encapsulate the concept that varies into its own function(s). These shared abstractions can then be re-used, which minimizes the amount of duplication and simplifies change.

Add a shared helper for the exclusion integration tests so the excluded
and non-excluded cases differ only in their fixture inputs and expected
outcome.

Prefer the rustup-managed Cargo path in the Makefile when it exists so
hook environments with a minimal `PATH` can still run the standard gates.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat crates/no_std_fs_operations/tests/integration_exclusion.rs

Length of output: 10825


@leynos: Having validated the current state of crates/no_std_fs_operations/tests/integration_exclusion.rs, the fix is already in place. Both excluded_crate_suppresses_diagnostics and non_excluded_crate_emits_diagnostics now delegate entirely to the run_exclusion_test helper; their bodies contain no duplicated logic whatsoever. The CodeScene finding was raised against an earlier snapshot of the diff and no longer reflects the live codegraph.

Use the following message in CodeScene's diagnostic suppression UI:


This finding is a false positive on the current revision of the code. The duplication originally flagged between excluded_crate_suppresses_diagnostics and non_excluded_crate_emits_diagnostics was eliminated in this pull request by extracting a shared run_exclusion_test helper function. Both test functions now consist of a single delegating call to that helper; no shared logic is repeated between them. The two test functions must remain distinct top-level #[test] items so that the test runner can discover, name, and report them individually — collapsing them further into a single function would sacrifice test-result clarity for no structural gain. The DRY principle is fully satisfied in the current code.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@lodyai lodyai Bot force-pushed the test-fixtures-for-exclusion-lint-t0egzl branch from a8ae630 to 1367e2c Compare May 1, 2026 23:15
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos marked this pull request as ready for review May 2, 2026 09:12
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

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 2500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai coderabbitai Bot added the Issue label May 2, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@crates/no_std_fs_operations/tests/integration_exclusion.rs`:
- Around line 297-323: Replace the two duplicated test functions
excluded_crate_suppresses_diagnostics and non_excluded_crate_emits_diagnostics
with a single parameterized rstest that calls run_exclusion_test for each case;
create a single test function (keep #[ignore = "..."] and #[serial]) annotated
with #[rstest(crate_name, is_excluded, expected => case("excluded_test_crate",
true, Expectation { should_emit_diagnostics: false, should_succeed: true }),
case("non_excluded_crate", false, Expectation { should_emit_diagnostics: true,
should_succeed: false })) and inside invoke run_exclusion_test(crate_name,
is_excluded, expected) so the duplicated bodies are collapsed into one
matrix-driven test while preserving the same run_exclusion_test and Expectation
usage.
🪄 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: 8ac30d8b-5250-4bef-9d4f-ecfc785c2ba4

📥 Commits

Reviewing files that changed from the base of the PR and between d5c6912 and 1367e2c.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • crates/no_std_fs_operations/Cargo.toml
  • crates/no_std_fs_operations/tests/fixtures/excluded_project/Cargo.toml
  • crates/no_std_fs_operations/tests/fixtures/excluded_project/dylint.toml
  • crates/no_std_fs_operations/tests/fixtures/excluded_project/src/lib.rs
  • crates/no_std_fs_operations/tests/fixtures/non_excluded_project/Cargo.toml
  • crates/no_std_fs_operations/tests/fixtures/non_excluded_project/dylint.toml
  • crates/no_std_fs_operations/tests/fixtures/non_excluded_project/src/lib.rs
  • crates/no_std_fs_operations/tests/integration_exclusion.rs
💤 Files with no reviewable changes (6)
  • crates/no_std_fs_operations/tests/fixtures/excluded_project/dylint.toml
  • crates/no_std_fs_operations/tests/fixtures/excluded_project/Cargo.toml
  • crates/no_std_fs_operations/tests/fixtures/non_excluded_project/Cargo.toml
  • crates/no_std_fs_operations/tests/fixtures/non_excluded_project/dylint.toml
  • crates/no_std_fs_operations/tests/fixtures/excluded_project/src/lib.rs
  • crates/no_std_fs_operations/tests/fixtures/non_excluded_project/src/lib.rs

Comment thread crates/no_std_fs_operations/tests/integration_exclusion.rs Outdated
Refactor the integration exclusion tests in no_std_fs_operations
from duplicate #[test] functions into a single #[rstest] matrix.
Keep ignore/serial attributes and shared run_exclusion_test helper usage.
codescene-delta-analysis[bot]

This comment was marked as outdated.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/no_std_fs_operations/tests/integration_exclusion.rs (1)

264-267: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Stop panicking on malformed cargo JSON without fixture context.

Return a Result from diagnostic_count and surface parse failures with crate name and stderr in assert_fixture_behaviour. Keep failure reporting actionable when cargo dylint emits non-JSON stdout.

Patch
-fn diagnostic_count(output: &[u8]) -> usize {
-    Message::parse_stream(Cursor::new(output))
-        .map(|message| message.expect("cargo dylint should emit valid JSON messages"))
-        .filter_map(|message| match message {
-            Message::CompilerMessage(message) => Some(message.message),
-            _ => None,
-        })
-        .filter(|diagnostic| {
-            diagnostic
-                .code
-                .as_ref()
-                .is_some_and(|code| code.code == LINT_CRATE_NAME)
-        })
-        .count()
+fn diagnostic_count(output: &[u8]) -> Result<usize, String> {
+    let mut count = 0usize;
+
+    for message in Message::parse_stream(Cursor::new(output)) {
+        let message = message.map_err(|err| format!("invalid cargo JSON message: {err}"))?;
+        if let Message::CompilerMessage(message) = message {
+            if message
+                .message
+                .code
+                .as_ref()
+                .is_some_and(|code| code.code == LINT_CRATE_NAME)
+            {
+                count += 1;
+            }
+        }
+    }
+
+    Ok(count)
 }
@@
-    let result = run_cargo_dylint(fixture_dir, lint_library_path);
-    let count = diagnostic_count(&result.stdout);
+    let result = run_cargo_dylint(fixture_dir, lint_library_path);
@@
-    if expectation.should_emit_diagnostics {
+    let count = diagnostic_count(&result.stdout).unwrap_or_else(|err| {
+        panic!(
+            "crate `{crate_name}` produced non-JSON cargo output: {err}\nstderr was:\n{}",
+            result.stderr
+        )
+    });
+
+    if expectation.should_emit_diagnostics {

Also applies to: 325-333

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/no_std_fs_operations/tests/integration_exclusion.rs` around lines 264
- 267, Change diagnostic_count to return Result<usize, E> (e.g., Result<usize,
anyhow::Error>) instead of panicking: have it attempt to parse messages with
Message::parse_stream and propagate any parse errors (including context of the
raw stdout) rather than calling expect(). Update callers (notably
assert_fixture_behaviour) to handle the Result: on Err, produce an actionable
failure that includes the crate name and the captured stderr/stdout so users can
see the malformed cargo output; on Ok, continue using the usize count. Also
update the other affected call sites (the block around the other occurrence at
the 325-333 region) to use the new Result-returning diagnostic_count and handle
errors similarly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@crates/no_std_fs_operations/tests/integration_exclusion.rs`:
- Around line 264-267: Change diagnostic_count to return Result<usize, E> (e.g.,
Result<usize, anyhow::Error>) instead of panicking: have it attempt to parse
messages with Message::parse_stream and propagate any parse errors (including
context of the raw stdout) rather than calling expect(). Update callers (notably
assert_fixture_behaviour) to handle the Result: on Err, produce an actionable
failure that includes the crate name and the captured stderr/stdout so users can
see the malformed cargo output; on Ok, continue using the usize count. Also
update the other affected call sites (the block around the other occurrence at
the 325-333 region) to use the new Result-returning diagnostic_count and handle
errors similarly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7f34bf15-818f-47a8-b179-2853eab463ce

📥 Commits

Reviewing files that changed from the base of the PR and between 1367e2c and 373aa07.

📒 Files selected for processing (1)
  • crates/no_std_fs_operations/tests/integration_exclusion.rs

`diagnostic_count` in the exclusion integration tests previously called
`.expect()` on each `Message::parse_stream` result, panicking on the
first malformed JSON line.  This produced unhelpful diagnostics when
cargo dylint emitted unexpected output.

Change `diagnostic_count` to collect parse results with
`collect::<Result<Vec<_>, _>>()` and propagate parse errors via
`anyhow::Error`, including the raw stdout in the error message so
callers can inspect the malformed output.  Update
`assert_fixture_behaviour` to handle the `Result` by producing an
actionable panic that includes the crate name and captured stderr
alongside the parse error.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

Convert all fallible build-layer and fixture-creation functions to return
`anyhow::Result` instead of panicking with `.expect()`.  Separate the
subprocess-execution portion of `assert_fixture_behaviour` into
`evaluate_fixture`, which returns `Result<(bool, usize)>` so callers can
inspect results programmatically.  Store `lint_library_path` as
`OnceLock<anyhow::Result<PathBuf>>` so a build failure propagates to
callers rather than being silently cached.

Add `use anyhow::Context as _;` and use `.context()` / `.with_context()`
throughout for actionable error messages.
codescene-delta-analysis[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 4, 2026

@coderabbitai these should both be dealt with as part of the current PR, not as separate issues.

The first is a simple documentation change, the second is introduction of standard tests. Insta is a standard part of the testing toolkit used at df12, and is not some weird outlier.

⚠️ Developer Documentation — Not Resolved

docs/developers-guide.md still contains a stale description of the old static-fixture approach, including references to deleted directories (tests/fixtures/excluded_project, tests/fixtures/non_excluded_project) and an incorrect test function name (exclusion_behaviour_matches_fixture_configuration instead of exclusion_crates_behaviour_test). FixtureProject, create_fixture_project, and TempDir are not mentioned. docs/roadmap.md does not mark issue #111 complete.

This is out of scope for the primary change in this PR. Would you like me to open a GitHub issue to track the documentation update?


⚠️ Testing (Compile-Time / UI) — Not Resolved

insta is not present in any Cargo.toml in the workspace. No snapshot testing of cargo dylint JSON output has been added.

This is also out of scope for this PR. Would you like me to open a GitHub issue to track adding insta-based snapshot tests for the cargo dylint JSON output?

Please suggest a fix for these concerns and provide an AI coding agent prompt for the fixes.

@coderabbitai

This comment was marked as resolved.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot added Roadmap and removed Issue labels May 4, 2026
coderabbitai[bot]

This comment was marked as resolved.

Update the developer guide and roadmap to describe the runtime-generated
`TempDir` fixture projects used by the `no_std_fs_operations` exclusion
tests.

Add an insta JSON snapshot for the non-excluded diagnostic path so the
structured `cargo dylint` output is covered without relying on absolute
fixture paths.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot added Issue and removed Roadmap labels May 7, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/developers-guide.md (1)

58-85: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use Oxford spellings and fix the missing comma in this new section.

Replace parametrised/serialised with parametrized/serialized, and add the comma after For each case in the final paragraph.

Triage: [type:spelling] [type:grammar] [type:docstyle]

Suggested fix
-The shared helper `run_exclusion_test(crate_name, is_excluded, expectation)`
+The shared helper `run_exclusion_test(crate_name, is_excluded, expectation)`
 resolves the lint library path via a `OnceLock`-cached `build_lint_library`
 call, creates the fixture project, and delegates to `assert_fixture_behaviour`.
-Both parametrised cases in `exclusion_crates_behaviour_test` delegate to this
+Both parametrized cases in `exclusion_crates_behaviour_test` delegate to this
 helper.
@@
-for correct serialised execution because nextest runs each test in a separate
+for correct serialized execution because nextest runs each test in a separate
 process, so the in-process `#[serial]` mutex alone is not sufficient.
@@
-The parametrised `#[rstest]` case `exclusion_crates_behaviour_test` covers both
-fixture configurations. For each case it asserts the subprocess exit status and
+The parametrized `#[rstest]` case `exclusion_crates_behaviour_test` covers both
+fixture configurations. For each case, it asserts the subprocess exit status and
 the `no_std_fs_operations` diagnostic count, so the test verifies both the
 success path for excluded crates (zero diagnostics, exit 0) and the failure
 path for non-excluded crates (one or more diagnostics, non-zero exit).

As per coding guidelines, "Documentation must use en-GB-oxendict spelling and grammar" and "Use the Oxford comma: 'ships, planes, and hovercraft' where it aids comprehension".

🤖 Prompt for 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.

In `@docs/developers-guide.md` around lines 58 - 85, Replace the British "-ised"
spellings with Oxford "-ized" forms by changing "parametrised" to "parametrized"
and "serialised" to "serialized" (maintaining en-GB-oxendict style), and add the
missing comma after the phrase "For each case" in the final paragraph so it
reads "For each case, it asserts..."; locate these tokens in the section
describing run_exclusion_test and the parametrised #[rstest] paragraph to apply
the edits.
🤖 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 `@crates/no_std_fs_operations/tests/integration_exclusion.rs`:
- Around line 174-186: The generated Cargo.toml written by the fs::write call
for root.join("Cargo.toml") needs an empty workspace table to prevent Cargo from
inheriting an outer workspace; modify the format! argument (the concat! block)
used in that fs::write invocation to include a line for "[workspace]\n" (e.g.,
insert "\n[workspace]\n" or add "[workspace]\n" into the concatenated string) so
the written manifest contains an explicit empty workspace entry while still
using the same crate_name interpolation.
- Around line 454-468: The pipeline silently drops parse/serialize errors by
using filter_map(Result::ok) and .ok(); change it so
Message::parse_stream(Cursor::new(&result.stdout)) is collected as a Result
sequence and any Err from parsing or any Err from serde_json::to_value on the
CompilerMessage case fails the test immediately (use collect::<Result<Vec<_>,
_>>() or map + expect with a clear message) instead of filtering them
out—specifically replace filter_map(Result::ok) and the .ok() after
serde_json::to_value with error-propagating handling so malformed parse results
or serialization failures surface as test failures.

---

Outside diff comments:
In `@docs/developers-guide.md`:
- Around line 58-85: Replace the British "-ised" spellings with Oxford "-ized"
forms by changing "parametrised" to "parametrized" and "serialised" to
"serialized" (maintaining en-GB-oxendict style), and add the missing comma after
the phrase "For each case" in the final paragraph so it reads "For each case, it
asserts..."; locate these tokens in the section describing run_exclusion_test
and the parametrised #[rstest] paragraph to apply the edits.
🪄 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: 40cc9bac-6400-4419-bd5b-81995b309ef7

📥 Commits

Reviewing files that changed from the base of the PR and between 7ba2c8f and 37c4d62.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • crates/no_std_fs_operations/tests/snapshots/integration_exclusion__non_excluded_crate_diagnostics.snap is excluded by !**/*.snap
📒 Files selected for processing (5)
  • Cargo.toml
  • crates/no_std_fs_operations/Cargo.toml
  • crates/no_std_fs_operations/tests/integration_exclusion.rs
  • docs/developers-guide.md
  • docs/roadmap.md

Comment on lines +174 to +186
fs::write(
root.join("Cargo.toml"),
format!(
concat!(
"[package]\n",
"name = \"{crate_name}\"\n",
"version = \"0.1.0\"\n",
"edition = \"2024\"\n",
"\n",
"[dependencies]\n",
),
crate_name = crate_name
),
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 7, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add an empty [workspace] to the generated fixture manifest.

Emit the empty workspace table here. Without it, Cargo can walk upwards from the temp directory and attach the fixture to an enclosing workspace when TMPDIR points inside a checkout, which makes this integration test environment-dependent.

Suggested fix
     fs::write(
         root.join("Cargo.toml"),
         format!(
             concat!(
                 "[package]\n",
                 "name = \"{crate_name}\"\n",
                 "version = \"0.1.0\"\n",
                 "edition = \"2024\"\n",
                 "\n",
+                "[workspace]\n",
+                "\n",
                 "[dependencies]\n",
             ),
             crate_name = crate_name
         ),
     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fs::write(
root.join("Cargo.toml"),
format!(
concat!(
"[package]\n",
"name = \"{crate_name}\"\n",
"version = \"0.1.0\"\n",
"edition = \"2024\"\n",
"\n",
"[dependencies]\n",
),
crate_name = crate_name
),
fs::write(
root.join("Cargo.toml"),
format!(
concat!(
"[package]\n",
"name = \"{crate_name}\"\n",
"version = \"0.1.0\"\n",
"edition = \"2024\"\n",
"\n",
"[workspace]\n",
"\n",
"[dependencies]\n",
),
crate_name = crate_name
),
🤖 Prompt for 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.

In `@crates/no_std_fs_operations/tests/integration_exclusion.rs` around lines 174
- 186, The generated Cargo.toml written by the fs::write call for
root.join("Cargo.toml") needs an empty workspace table to prevent Cargo from
inheriting an outer workspace; modify the format! argument (the concat! block)
used in that fs::write invocation to include a line for "[workspace]\n" (e.g.,
insert "\n[workspace]\n" or add "[workspace]\n" into the concatenated string) so
the written manifest contains an explicit empty workspace entry while still
using the same crate_name interpolation.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please mark it as such using the API. Otherwise, please provide an AI agent prompt for the remaining work to be done to address this 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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd 'mod.rs' --full-path --glob '*test_support*' | xargs cat

Length of output: 102


@leynos — Not resolved. The [workspace] table is still absent from the generated manifest. crates/no_std_fs_operations/tests/test_support/mod.rs currently writes:

[package]
name = "{crate_name}"
version = "0.1.0"
edition = "2024"

[dependencies]

The [workspace] table must be inserted before [dependencies].

Apply the fix below:

In `crates/no_std_fs_operations/tests/test_support/mod.rs`, locate the `fs::write` call for `root.join("Cargo.toml")` inside `create_fixture_project`. The `concat!` block currently contains:

```
"[package]\n",
"name = \"{crate_name}\"\n",
"version = \"0.1.0\"\n",
"edition = \"2024\"\n",
"\n",
"[dependencies]\n",
```

Insert two lines immediately after the blank line (`"\n"`) and before `"[dependencies]\n"`:

```
"[workspace]\n",
"\n",
```

so the manifest written to disk becomes:

```
[package]
name = "<crate_name>"
version = "0.1.0"
edition = "2024"

[workspace]

[dependencies]
```

This prevents Cargo from walking upward and attaching the temporary fixture to an enclosing workspace when `TMPDIR` is located inside a checkout tree.

🧬 Code Graph Analysis Results
  • File: crates/no_std_fs_operations/src/driver.rs, lines 89-91
pub fn is_excluded(&self, crate_name: &str) -> bool {
        self.excluded_crates.contains(crate_name)
    }
  • File: crates/no_std_fs_operations/tests/test_support/mod.rs, lines 32-70
pub(super) fn create_fixture_project(
    crate_name: &str,
    is_excluded: bool,
) -> anyhow::Result<FixtureProject> {
    let temp_dir = TempDir::new().context("failed to create temporary fixture directory")?;
    let root = temp_dir.path().to_path_buf();

    fs::write(
        root.join("Cargo.toml"),
        format!(
            concat!(
                "[package]\n",
                "name = \"{crate_name}\"\n",
                "version = \"0.1.0\"\n",
                "edition = \"2024\"\n",
                "\n",
                "[dependencies]\n",
            ),
            crate_name = crate_name
        ),
    )
    .context("failed to write fixture Cargo.toml")?;

    fs::write(
        root.join("dylint.toml"),
        fixture_dylint_config(crate_name, is_excluded),
    )
    .context("failed to write fixture dylint.toml")?;

    let source_dir = root.join("src");
    fs::create_dir(&source_dir).context("failed to create fixture src directory")?;
    fs::write(source_dir.join("lib.rs"), fixture_source(crate_name))
        .context("failed to write fixture source")?;

    Ok(FixtureProject {
        _temp_dir: temp_dir,
        root,
    })
}

🧠 Learnings used
Learnt from: leynos
Repo: leynos/whitaker PR: 130
File: crates/module_must_have_inner_docs/src/driver/inner_attr.rs:66-66
Timestamp: 2026-02-01T03:25:03.269Z
Learning: In Rust, when a variable is initialized with an integer literal and the only subsequent usage is a method like saturating_sub that exists on multiple integer types, an explicit type annotation is required. Do not rely on the compiler to infer the concrete integer type from the method alone; if the code could apply to multiple integer types, specify the type (e.g., 'let mut depth: usize = 1;') to ensure the intended type is used and avoid type-ambiguity during compilation.

Learnt from: leynos
Repo: leynos/whitaker PR: 140
File: installer/tests/behaviour_toolchain.rs:0-0
Timestamp: 2026-02-04T00:00:24.433Z
Learning: In Rust, use 'use path as _;' to bring trait methods into scope without binding the trait name. This pattern does not work for functions; importing a function with 'as _' discards the name and makes the function uncallable. Apply this guidance to Rust source files (not just tests) by ensuring underscore imports are used only for traits, not for functions.

Comment thread crates/no_std_fs_operations/tests/integration_exclusion.rs Outdated
@leynos leynos changed the title Implement dynamic test fixtures for lint exclusions Implement dynamic test fixtures for lint exclusions (#111) May 19, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== integration_exclusion.rs (first 50 lines for module doc check) ==="
head -60 crates/no_std_fs_operations/tests/integration_exclusion.rs

echo ""
echo "=== integration_exclusion.rs (full file) ==="
cat crates/no_std_fs_operations/tests/integration_exclusion.rs

echo ""
echo "=== workspace Cargo.toml (insta check) ==="
grep -n "insta" Cargo.toml

echo ""
echo "=== no_std_fs_operations Cargo.toml (insta dev-dep check) ==="
grep -n "insta" crates/no_std_fs_operations/Cargo.toml

echo ""
echo "=== docs/developers-guide.md ==="
cat docs/developers-guide.md

echo ""
echo "=== docs/roadmap.md ==="
cat docs/roadmap.md

echo ""
echo "=== snapshot files ==="
fd "\.snap$" crates/no_std_fs_operations/

echo ""
echo "=== snapshot file content ==="
fd "\.snap$" crates/no_std_fs_operations/ --exec cat {}

Length of output: 145412


@leynos: All four checks are now resolved in the current branch.

Check Status Evidence
Module-Level Documentation ✅ Resolved integration_exclusion.rs opens with a 14-line //! inner doc block covering purpose, prerequisites, and the #[ignore] policy.
Unit Architecture ✅ Resolved Every build and fixture helper returns anyhow::Result<_>. lint_library_path caches an OnceLock<anyhow::Result<PathBuf>>. evaluate_fixture separates subprocess execution from assertion. assert_fixture_behaviour uses unwrap_or_else(|e| panic!(...)) with structured messages. No hidden .expect/panic! remain in the build layer.
Developer Documentation ✅ Resolved docs/developers-guide.md documents create_fixture_project, FixtureProject, TempDir, run_exclusion_test, assert_fixture_behaviour, run_cargo_dylint, and diagnostic_count. The stale static fixture directory references and the old test function name are gone. docs/roadmap.md item 4.1.4 is marked [x] and closes #111.
Testing (Compile-Time / UI) ✅ Resolved insta = { version = "1", features = ["json"] } is in the workspace Cargo.toml. insta = { workspace = true } is in crates/no_std_fs_operations/Cargo.toml. non_excluded_crate_diagnostics_match_snapshot is implemented and the committed snapshot at tests/snapshots/integration_exclusion__non_excluded_crate_diagnostics.snap contains only relative paths (src/lib.rs) with no absolute TempDir paths leaking through.

leynos added 2 commits May 19, 2026 13:43
Route exclusion fixture assertions through the fallible evaluation helper
so subprocess execution and diagnostic parsing stay outside the assertion
logic.

Keep the ignored integration test attributes unchanged while preserving the
existing fixture behaviour checks for excluded and non-excluded crates.
Move the temporary fixture generator into test support and keep its exposed
surface narrow for the integration exclusion tests.

Return `anyhow::Result` from the exclusion test driver so setup failures carry
context through the test harness instead of panicking via `expect`.
codescene-delta-analysis[bot]

This comment was marked as outdated.

Replace the blanket Clippy allow on `lint_library_path` with an explicit
`expect` that records why the `Result` conversion needs the current shape.
codescene-delta-analysis[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
crates/no_std_fs_operations/tests/integration_exclusion.rs (1)

353-367: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail the snapshot test on malformed output instead of silently dropping errors.

The pipeline uses filter_map(Result::ok) at line 354 and .ok() at line 363, which silently discard parse and serialisation failures. This contradicts the approach in diagnostic_count (lines 187-195) which correctly propagates errors. A broken harness or unexpected cargo output format change would produce a partial or empty snapshot rather than a failing test.

Collect parse results fallibly and propagate serialisation errors so the test fails loudly on malformed output.

Proposed fix
-    let diagnostics: Vec<serde_json::Value> = Message::parse_stream(Cursor::new(&result.stdout))
-        .filter_map(Result::ok)
+    let messages: Vec<Message> = Message::parse_stream(Cursor::new(&result.stdout))
+        .collect::<Result<Vec<_>, _>>()
+        .unwrap_or_else(|e| {
+            panic!(
+                "non_excluded_crate_snap produced malformed cargo output: {e}\nstderr:\n{}",
+                result.stderr
+            )
+        });
+
+    let diagnostics: Vec<serde_json::Value> = messages
+        .into_iter()
         .filter_map(|message| match message {
             Message::CompilerMessage(message)
                 if message
                     .message
                     .code
                     .as_ref()
                     .is_some_and(|code| code.code == LINT_CRATE_NAME) =>
             {
-                serde_json::to_value(message.message).ok()
+                Some(
+                    serde_json::to_value(&message.message)
+                        .expect("failed to serialise diagnostic for snapshot"),
+                )
             }
             _ => None,
         })
         .collect();
🤖 Prompt for 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.

In `@crates/no_std_fs_operations/tests/integration_exclusion.rs` around lines 353
- 367, The current test silently drops parse and serialization failures by using
filter_map(Result::ok) and .ok(); change the pipeline to collect and propagate
errors so the test fails on malformed output: replace the filter_map(Result::ok)
and the .ok() call in the chain that starts from
Message::parse_stream(Cursor::new(&result.stdout)) with a fallible mapping that
returns Result<serde_json::Value, E> for each message (map parse errors through
and convert serde_json::to_value errors into Err), then
collect::<Result<Vec<serde_json::Value>, _>>() (or use try_collect) to get a
Result<Vec<_>, _> and unwrap or propagate the error in the test; reference
Message::parse_stream and the conversion via serde_json::to_value when making
the change.
🤖 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.

Duplicate comments:
In `@crates/no_std_fs_operations/tests/integration_exclusion.rs`:
- Around line 353-367: The current test silently drops parse and serialization
failures by using filter_map(Result::ok) and .ok(); change the pipeline to
collect and propagate errors so the test fails on malformed output: replace the
filter_map(Result::ok) and the .ok() call in the chain that starts from
Message::parse_stream(Cursor::new(&result.stdout)) with a fallible mapping that
returns Result<serde_json::Value, E> for each message (map parse errors through
and convert serde_json::to_value errors into Err), then
collect::<Result<Vec<serde_json::Value>, _>>() (or use try_collect) to get a
Result<Vec<_>, _> and unwrap or propagate the error in the test; reference
Message::parse_stream and the conversion via serde_json::to_value when making
the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3f7ea3a9-f6d3-4ae2-9d7d-92e1f1e4fd26

📥 Commits

Reviewing files that changed from the base of the PR and between 138ced4 and 66247e4.

📒 Files selected for processing (1)
  • crates/no_std_fs_operations/tests/integration_exclusion.rs

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 19, 2026

@coderabbitai Have the following now been resolved?

crates/no_std_fs_operations/tests/integration_exclusion.rs (1)

123-149: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win
Propagate cargo build JSON parse errors instead of silently dropping them.
Line 131 silently discards every Err from Message::parse_stream(), causing malformed build output to be misreported as "cdylib artifact not found" rather than surfacing the actual parse failure. The diagnostic_count() function in this same file demonstrates the correct pattern: propagate parse errors with .map_err().

Patch
fn find_cdylib_in_artifacts(
     stdout: &[u8],
     package_id: &cargo_metadata::PackageId,
 ) -> anyhow::Result<PathBuf> {
     for message in Message::parse_stream(Cursor::new(stdout)) {
-        let Ok(Message::CompilerArtifact(artifact)) = message else {
+        let message = message.context("failed to parse cargo build JSON output")?;
+        let Message::CompilerArtifact(artifact) = message else {
             continue;
         };
 
         if artifact.package_id != *package_id {
             continue;
🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @crates/no_std_fs_operations/tests/integration_exclusion.rs around lines 123

  • 149, In find_cdylib_in_artifacts, do not silently ignore Err items from
    Message::parse_stream; instead propagate parse failures like diagnostic_count
    does by mapping the Err into an anyhow error and returning it (e.g., call
    .map_err(...) or use message.map_err(...)? on each loop iteration), then
    continue to match only Ok(Message::CompilerArtifact(artifact)) for processing;
    this ensures JSON parse errors from Message::parse_stream(Cursor::new(stdout))
    are returned rather than folding into the final "cdylib artifact not found"
    error while keeping the rest of the logic that checks artifact.package_id,
    artifact.target.is_cdylib(), and filenames/*.ends_with(env::consts::DLL_SUFFIX)
    intact.

crates/no_std_fs_operations/tests/integration_exclusion.rs (2)

351-365: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win
Stop dropping malformed snapshot input.
Lines 351-362 use filter_map(Result::ok) and .ok(), which can snapshot a partial diagnostic set and bless a broken harness. Fail the test on parse or serialisation errors instead.

Patch
let diagnostics: Vec<serde_json::Value> = Message::parse_stream(Cursor::new(&result.stdout))
-        .filter_map(Result::ok)
+        .collect::<Result<Vec<_>, _>>()
+        .unwrap_or_else(|e| {
+            panic!(
+                "non_excluded_crate_diagnostics_match_snapshot produced malformed cargo output: {e}\nstderr:\n{}",
+                result.stderr
+            )
+        })
+        .into_iter()
         .filter_map(|message| match message {
             Message::CompilerMessage(message)
                 if message
                     .message
                     .code
                     .as_ref()
                     .is_some_and(|code| code.code == LINT_CRATE_NAME) =>
             {
-                serde_json::to_value(message.message).ok()
+                Some(
+                    serde_json::to_value(message.message)
+                        .expect("failed to serialise diagnostic for snapshot"),
+                )
             }
             _ => None,
         })
         .collect();
🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @crates/no_std_fs_operations/tests/integration_exclusion.rs around lines 351

  • 365, The test currently swallows parse and serialization errors by using
    filter_map(Result::ok) and .ok(), which can hide malformed input; change the
    pipeline that builds diagnostics so parse failures and serde_json::to_value
    failures cause the test to fail: replace the filter_map(Result::ok) with mapping
    the Result and propagate errors (e.g., collect into a Result<Vec<_>, _> or use
    expect/unwrap with clear messages) and likewise handle serde_json::to_value so
    serialization errors are returned as Err instead of .ok(), referencing
    Message::parse_stream, Message::CompilerMessage and serde_json::to_value to
    locate the code to update.

235-236: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
Replace the forbidden #[allow(...)] with scoped #[expect(..., reason = "...")].
Line 235 still uses a blanket suppression on lint_library_path. Keep the exemption narrow and justified so the file stays compliant with the repo lint policy.

Patch
-#[allow(clippy::useless_asref, clippy::redundant_closure)]
+#[expect(
+    clippy::useless_asref,
+    reason = "anyhow::Error is not Clone; .as_ref().map(Clone::clone) converts &Result<PathBuf, Error> into Result<PathBuf, Error>"
+)]
+#[expect(
+    clippy::redundant_closure,
+    reason = "closure form is required for the non-Clone anyhow::Error conversion pattern"
+)]
 fn lint_library_path() -> anyhow::Result<PathBuf> {

As per coding guidelines, "#[allow] is forbidden" and only narrowly scoped #[expect(..., reason = \"...\")] suppressions are allowed.

🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @crates/no_std_fs_operations/tests/integration_exclusion.rs around lines 235

  • 236, Remove the forbidden blanket attribute on lint_library_path and replace
    it with a narrowly scoped expect attribute for the specific Clippy lints: remove
    #[allow(clippy::useless_asref, clippy::redundant_closure)] and add
    #[expect(clippy::useless_asref, reason = "explain why as_ref use is
    intentional")] and #[expect(clippy::redundant_closure, reason = "explain why
    closure form is required")] scoped as close as possible to the offending
    expression(s) or, if unavoidable, on the lint_library_path function signature so
    the justification is explicit and minimal.

crates/no_std_fs_operations/tests/test_support/mod.rs (1)

39-53: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win
Add an empty [workspace] table to the generated fixture manifest.
Line 42 writes a standalone Cargo.toml without [workspace]. When TMPDIR lands under another Cargo workspace, Cargo can attach this temp crate to the parent workspace or fail because it is not listed, which makes this integration test environment-dependent.

Patch
concat!(
                 "[package]\n",
                 "name = \"{crate_name}\"\n",
                 "version = \"0.1.0\"\n",
                 "edition = \"2024\"\n",
                 "\n",
+                "[workspace]\n",
+                "\n",
                 "[dependencies]\n",
             ),
🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @crates/no_std_fs_operations/tests/test_support/mod.rs around lines 39 - 53,
The generated fixture Cargo.toml written by fs::write(root.join("Cargo.toml"),
format!(...)) lacks a [workspace] table; update the format! call in
tests/test_support/mod.rs (the block that builds the Cargo.toml string) to
include an empty "[workspace]\n" section (e.g., append a blank [workspace] table
inside the concat! string) so the temporary crate cannot be implicitly joined to
a parent workspace.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 19, 2026

@coderabbitai Have the following now been resolved?

crates/no_std_fs_operations/tests/integration_exclusion.rs (1)

1-414: 🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoff
Decompose this file to stay within the 400-line limit.
At 415 lines, this file exceeds the 400-line maximum. Extract the build helpers (build_lint_library, run_lint_crate_build, stage_toolchain_qualified_library, find_package_id, find_cdylib_in_artifacts, lint_library_path) into a separate test_utils or build_helpers module.

Suggested structure
//! Helpers for building and locating the lint library in integration tests.

use std::env;
use std::io::Cursor;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::OnceLock;

use anyhow::Context as _;
use cargo_metadata::{Message, Metadata, MetadataCommand};

pub const LINT_CRATE_NAME: &str = "no_std_fs_operations";

pub fn lint_library_path() -> anyhow::Result<PathBuf> { /* ... */ }
fn build_lint_library() -> anyhow::Result<PathBuf> { /* ... */ }
// ... remaining build helpers

As per coding guidelines, "Files must not exceed 400 lines in length" and "Large modules must be decomposed."

🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @crates/no_std_fs_operations/tests/integration_exclusion.rs around lines 1 -
414, The file exceeds the 400-line limit; extract build helpers into a new test
utils module and import them. Move LINT_CRATE_NAME, build_lint_library,
run_lint_crate_build, stage_toolchain_qualified_library, find_package_id,
find_cdylib_in_artifacts, and lint_library_path (plus their required
use/imports) into a new file (e.g. tests/test_utils/lint_build.rs), make
LINT_CRATE_NAME and lint_library_path public (pub) so callers can use them, and
in integration_exclusion.rs add mod test_utils; use
test_utils::lint_build::{lint_library_path, LINT_CRATE_NAME}; update any
visibility or import paths as needed so functions like run_exclusion_test,
evaluate_fixture, and assert_fixture_behaviour compile against the moved
symbols.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== integration_exclusion.rs lines 120-160 ==="
sed -n '120,160p' crates/no_std_fs_operations/tests/integration_exclusion.rs

echo ""
echo "=== integration_exclusion.rs lines 230-245 ==="
sed -n '230,245p' crates/no_std_fs_operations/tests/integration_exclusion.rs

echo ""
echo "=== integration_exclusion.rs lines 345-380 ==="
sed -n '345,380p' crates/no_std_fs_operations/tests/integration_exclusion.rs

echo ""
echo "=== test_support/mod.rs lines 35-60 ==="
sed -n '35,60p' crates/no_std_fs_operations/tests/test_support/mod.rs

echo ""
echo "=== full test_support/mod.rs ==="
cat crates/no_std_fs_operations/tests/test_support/mod.rs

Length of output: 7251


@leynos: Having validated the current codegraph, three of the four findings remain unresolved.

Finding Status
find_cdylib_in_artifacts — silently drops Err from Message::parse_stream ❌ Not resolved
Snapshot test — filter_map(Result::ok) / .ok() swallows errors ❌ Not resolved
#[allow(...)]#[expect(..., reason = "...")] on lint_library_path ✅ Resolved
[workspace] table missing from generated fixture Cargo.toml ❌ Not resolved

Below are the agent prompts for the three remaining issues.


1 — find_cdylib_in_artifacts: propagate parse errors

**File:** `crates/no_std_fs_operations/tests/integration_exclusion.rs`
**Symbol:** `find_cdylib_in_artifacts`

## Task

In `find_cdylib_in_artifacts`, replace the irrefutable `let Ok(…) = … else { continue }` pattern that silently drops `Err` items with explicit error propagation.

## Change

```diff
 fn find_cdylib_in_artifacts(
     stdout: &[u8],
     package_id: &cargo_metadata::PackageId,
 ) -> anyhow::Result<PathBuf> {
     for message in Message::parse_stream(Cursor::new(stdout)) {
-        let Ok(Message::CompilerArtifact(artifact)) = message else {
+        let message = message.context("failed to parse cargo build JSON output")?;
+        let Message::CompilerArtifact(artifact) = message else {
             continue;
         };
```

## Acceptance criteria

- The function compiles without warnings under `RUSTFLAGS=-D warnings`.
- A malformed JSON line in `stdout` now causes `find_cdylib_in_artifacts` to
  return an `Err` immediately, rather than silently continuing and ultimately
  returning the generic "cdylib artifact not found" error.
- All other logic (`artifact.package_id`, `artifact.target.is_cdylib()`,
  `filenames/*.ends_with(env::consts::DLL_SUFFIX)`) remains unchanged.
- No other functions in the file are modified.

2 — Snapshot test: fail on malformed input instead of silently dropping it

**File:** `crates/no_std_fs_operations/tests/integration_exclusion.rs`
**Symbol:** `non_excluded_crate_diagnostics_match_snapshot`

## Task

Replace the two silent-drop patterns (`filter_map(Result::ok)` and
`serde_json::to_value(…).ok()`) so that parse or serialisation failures cause
the test to panic with a clear message rather than producing a partial snapshot.

## Change

```diff
     let diagnostics: Vec<serde_json::Value> = Message::parse_stream(Cursor::new(&result.stdout))
-        .filter_map(Result::ok)
+        .collect::<Result<Vec<_>, _>>()
+        .unwrap_or_else(|e| {
+            panic!(
+                "non_excluded_crate_diagnostics_match_snapshot produced malformed cargo output: {e}\nstderr:\n{}",
+                result.stderr
+            )
+        })
+        .into_iter()
         .filter_map(|message| match message {
             Message::CompilerMessage(message)
                 if message
                     .message
                     .code
                     .as_ref()
                     .is_some_and(|code| code.code == LINT_CRATE_NAME) =>
             {
-                serde_json::to_value(message.message).ok()
+                Some(
+                    serde_json::to_value(message.message)
+                        .expect("failed to serialise diagnostic for snapshot"),
+                )
             }
             _ => None,
         })
         .collect();
```

## Acceptance criteria

- The function compiles without warnings under `RUSTFLAGS=-D warnings`.
- A malformed JSON line from `Message::parse_stream` causes the test to panic
  immediately with a message that includes the raw `stderr`, not a silently
  truncated snapshot.
- A `serde_json::to_value` failure causes the test to panic with
  `"failed to serialise diagnostic for snapshot"`.
- The existing snapshot file is unchanged; the test still passes against it when
  `cargo dylint` output is well-formed.
- No other functions in the file are modified.

3 — Add [workspace] table to the generated fixture Cargo.toml

**File:** `crates/no_std_fs_operations/tests/test_support/mod.rs`
**Symbol:** `create_fixture_project`

## Task

Add an empty `[workspace]` table to the `Cargo.toml` written by
`create_fixture_project` so that Cargo treats each temporary crate as its own
workspace root and never attaches it to an enclosing workspace.

## Change

```diff
     fs::write(
         root.join("Cargo.toml"),
         format!(
             concat!(
                 "[package]\n",
                 "name = \"{crate_name}\"\n",
                 "version = \"0.1.0\"\n",
                 "edition = \"2024\"\n",
                 "\n",
+                "[workspace]\n",
+                "\n",
                 "[dependencies]\n",
             ),
             crate_name = crate_name
         ),
     )
```

## Acceptance criteria

- The written `Cargo.toml` now contains an empty `[workspace]` section.
- Running `cargo metadata --no-deps --manifest-path <fixture>/Cargo.toml`
  (when invoked from any directory) resolves only the fixture crate and not any
  parent workspace.
- The file compiles without warnings under `RUSTFLAGS=-D warnings`.
- No other functions in the file are modified.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 19, 2026

@coderabbitai Have the following now been resolved?

crates/no_std_fs_operations/tests/integration_exclusion.rs (1)

1-410: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift
Decompose this test module to satisfy the 400-line cap.
Extract fixture-construction helpers (FixtureProject, create_fixture_project, fixture_dylint_config, fixture_source) into a dedicated test-support module and keep this file at or below 400 lines.
As per coding guidelines, "Files must not exceed 400 lines in length" and large modules must be decomposed.

🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @crates/no_std_fs_operations/tests/integration_exclusion.rs around lines 1 -
410, Split out the fixture helper types and functions into a new test-support
module: move FixtureProject, create_fixture_project, fixture_dylint_config, and
fixture_source into their own file (e.g., tests/test_support.rs) and export any
required items (make functions/structs pub as needed). In the original
integration_exclusion.rs remove the moved definitions and add use/imports to
refer to the new module (e.g., use test_support::{FixtureProject,
create_fixture_project, fixture_dylint_config, fixture_source};) or declare mod
test_support; so the rest of the file (build_lint_library, run_cargo_dylint,
diagnostic_count, lint_library_path, run_exclusion_test,
assert_fixture_behaviour, evaluate_fixture, etc.) compiles unchanged; ensure
path handling and crate_name types/signatures remain identical and update
visibility (pub) on moved symbols if they are referenced from other
modules/tests.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== Line count of integration_exclusion.rs ==="
wc -l crates/no_std_fs_operations/tests/integration_exclusion.rs

echo ""
echo "=== Full file ==="
cat crates/no_std_fs_operations/tests/integration_exclusion.rs

echo ""
echo "=== test_support/mod.rs ==="
cat crates/no_std_fs_operations/tests/test_support/mod.rs

echo ""
echo "=== All files under tests/ ==="
fd . crates/no_std_fs_operations/tests/ --type f

Length of output: 15932


@leynos: Yes — this finding is resolved. integration_exclusion.rs now stands at 380 lines, which is within the 400-line limit. No explicit module extraction was required; prior refactoring (collapsing the two test functions into a single #[rstest]-parametrised case and splitting assert_fixture_behaviour into evaluate_fixture + assert_fixture_behaviour) brought the count below the threshold organically.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== integration_exclusion.rs line count ==="
wc -l crates/no_std_fs_operations/tests/integration_exclusion.rs

echo ""
echo "=== full integration_exclusion.rs ==="
cat -n crates/no_std_fs_operations/tests/integration_exclusion.rs

echo ""
echo "=== test_support/mod.rs ==="
cat -n crates/no_std_fs_operations/tests/test_support/mod.rs

echo ""
echo "=== check for fixture_dylint_config and fixture_source in both files ==="
echo "--- integration_exclusion.rs ---"
rg -n "fixture_dylint_config|fixture_source|FixtureProject|create_fixture_project" crates/no_std_fs_operations/tests/integration_exclusion.rs

echo "--- test_support/mod.rs ---"
rg -n "fixture_dylint_config|fixture_source|FixtureProject|create_fixture_project" crates/no_std_fs_operations/tests/test_support/mod.rs

echo ""
echo "=== check mod test_support declaration in integration_exclusion.rs ==="
rg -n "mod test_support\|use test_support" crates/no_std_fs_operations/tests/integration_exclusion.rs

Length of output: 20002


@leynos: The decomposition finding is resolved.

Check Status Evidence
400-line cap ✅ Resolved integration_exclusion.rs is 380 lines — within the limit.
FixtureProject extracted ✅ Resolved Defined in test_support/mod.rs with pub(super) visibility.
create_fixture_project extracted ✅ Resolved Lives in test_support/mod.rs; imported via use test_support::create_fixture_project;.
fixture_dylint_config / fixture_source extracted ✅ Resolved Both are private helpers inside test_support/mod.rs; no copies remain in integration_exclusion.rs.

Note that three findings from the previous round remain unresolved in the current codegraph. Supply the following prompt to your AI coding agent to address them all in one pass:

**Files to modify:**
- `crates/no_std_fs_operations/tests/integration_exclusion.rs`
- `crates/no_std_fs_operations/tests/test_support/mod.rs`

---

## Finding 1 — `find_cdylib_in_artifacts`: propagate `Err` from `Message::parse_stream`

**Location:** `integration_exclusion.rs`, function `find_cdylib_in_artifacts`

Line 128 uses `let Ok(Message::CompilerArtifact(artifact)) = message else { continue; }`,
which silently discards every `Err` variant. Replace it so parse failures are
propagated immediately.

```diff
     for message in Message::parse_stream(Cursor::new(stdout)) {
-        let Ok(Message::CompilerArtifact(artifact)) = message else {
+        let message = message.context("failed to parse cargo build JSON output")?;
+        let Message::CompilerArtifact(artifact) = message else {
             continue;
         };
```

---

## Finding 2 — `non_excluded_crate_diagnostics_match_snapshot`: fail on malformed input

**Location:** `integration_exclusion.rs`, function `non_excluded_crate_diagnostics_match_snapshot`

Lines 353–367 use `filter_map(Result::ok)` and `serde_json::to_value(…).ok()`,
which silently produce a partial snapshot on malformed output. Replace the
pipeline so every error panics with a clear message.

```diff
     let diagnostics: Vec<serde_json::Value> = Message::parse_stream(Cursor::new(&result.stdout))
-        .filter_map(Result::ok)
+        .collect::<Result<Vec<_>, _>>()
+        .unwrap_or_else(|e| {
+            panic!(
+                "non_excluded_crate_diagnostics_match_snapshot produced malformed cargo output: {e}\nstderr:\n{}",
+                result.stderr
+            )
+        })
+        .into_iter()
         .filter_map(|message| match message {
             Message::CompilerMessage(message)
                 if message
                     .message
                     .code
                     .as_ref()
                     .is_some_and(|code| code.code == LINT_CRATE_NAME) =>
             {
-                serde_json::to_value(message.message).ok()
+                Some(
+                    serde_json::to_value(message.message)
+                        .expect("failed to serialise diagnostic for snapshot"),
+                )
             }
             _ => None,
         })
         .collect();
```

---

## Finding 3 — `create_fixture_project`: add `[workspace]` table to generated `Cargo.toml`

**Location:** `test_support/mod.rs`, function `create_fixture_project`

The `Cargo.toml` written at lines 39–52 lacks a `[workspace]` table. When
`TMPDIR` falls under another Cargo workspace, Cargo can attach the fixture to
that workspace or reject it. Add an empty `[workspace]` section to make the
fixture a self-contained workspace root.

```diff
         format!(
             concat!(
                 "[package]\n",
                 "name = \"{crate_name}\"\n",
                 "version = \"0.1.0\"\n",
                 "edition = \"2024\"\n",
                 "\n",
+                "[workspace]\n",
+                "\n",
                 "[dependencies]\n",
             ),
             crate_name = crate_name
         ),
```

---

## Acceptance criteria

- `find_cdylib_in_artifacts` returns `Err` immediately on a malformed JSON line
  rather than falling through to "cdylib artifact not found".
- `non_excluded_crate_diagnostics_match_snapshot` panics with a message
  containing the raw `stderr` when `Message::parse_stream` yields an `Err`, and
  panics with `"failed to serialise diagnostic for snapshot"` when
  `serde_json::to_value` fails.
- Every generated fixture `Cargo.toml` contains a `[workspace]` section.
- The file compiles without warnings under `RUSTFLAGS=-D warnings`.
- No other functions in either file are modified.

Propagate integration fixture evaluation and snapshot setup errors through
`anyhow::Result` instead of converting them into hidden panics.

Encode generated fixture TOML values through `toml::Value` so crate names
cannot inject manifest or `dylint.toml` keys, and add regression coverage
for escaped names.

Serialise the ignored exclusion integration binary under nextest's
`serial-dylint-ui` group because it builds and stages the lint library.
codescene-delta-analysis[bot]

This comment was marked as outdated.

Propagate cargo JSON parse errors while locating the built lint cdylib so
malformed build output reports the parsing failure directly.

Make the snapshot test fail on malformed cargo output or diagnostic
serialisation failures instead of silently dropping bad records. Add an
empty `[workspace]` table to generated fixture manifests so each temporary
crate is resolved as its own workspace root.
codescene-delta-analysis[bot]

This comment was marked as outdated.

Align the exclusion snapshot test panic paths with the crate-specific
fixture name. Keep malformed cargo JSON and diagnostic serialisation
failures explicit so the snapshot cannot be built from partial output.
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.

Create workspace fixture for behavioural testing of lint exclusion

1 participant