Skip to content

TST-04 (CoverletCollectorRule): Incomplete validation — passes when coverlet is pinned but not actually referenced in test project #20

@davidnmbond

Description

@davidnmbond

Problem

TST-04 (CoverletCollectorRule) currently gives a false pass when coverlet.collector appears in Directory.Packages.props but is not referenced as a in any test project .csproj.

With central package management (ManagePackageVersionsCentrally=true), having a entry in Directory.Packages.props only pins the version — it does not cause the package to be included in any project. The test project must also contain:

<PackageReference Include="coverlet.collector">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

Real-world example

This exact bug was found in Lifx.Api: coverlet.collector 8.0.1 was pinned in Directory.Packages.props for months, TST-04 would have passed, but coverage was never collected because the test project didn't reference it.

Current behaviour

// CoverletCollectorRule.EvaluateAsync — passes immediately if found in Directory.Packages.props
var dirPackages = context.GetFileContent("Directory.Packages.props");
if (Contains(dirPackages, "coverlet.collector"))
{
    return Task.FromResult(Pass("coverlet.collector is referenced."));
}

Finding coverlet.collector anywhere in Directory.Packages.props short-circuits to pass without checking the test project.

Recommended fix

1. Rule logic change

When central package management is in use, the rule should verify both:

  • Directory.Packages.props contains <PackageVersion Include="coverlet.collector" ...>
  • At least one .Test.csproj also contains <PackageReference Include="coverlet.collector" ...>

Suggested logic:

var usesCpm = Contains(dirPackages, "ManagePackageVersionsCentrally")
    && Contains(dirPackages, "true");

var pinnedInProps = Contains(dirPackages, "coverlet.collector");

var testProjects = context.FindFiles(".csproj")
    .Where(f => f.Contains(".Test", StringComparison.OrdinalIgnoreCase));

var referencedInTestProject = testProjects
    .Any(tp => Contains(context.GetFileContent(tp), "coverlet.collector"));

if (usesCpm)
{
    // Both pin AND reference required
    if (pinnedInProps && referencedInTestProject)
        return Pass("coverlet.collector is pinned and referenced in test project.");
    if (pinnedInProps && !referencedInTestProject)
        return Fail("coverlet.collector is pinned in Directory.Packages.props but not referenced in any test project .csproj.");
    // ... other cases
}

2. Missing unit tests

Add tests for these scenarios:

Scenario Expected
In Directory.Packages.props only (CPM enabled) Fail
In both Directory.Packages.props and .Test.csproj (CPM enabled) Pass
In .Test.csproj only (no CPM) Pass
Not present anywhere Fail (existing test)

3. Consider a new companion rule (TST-05?)

Optionally, validate that the repo has a coverlet.runsettings file or that CI (.github/workflows/*.yml) invokes --collect:"XPlat Code Coverage". This would catch repos that reference coverlet but never actually collect coverage.

Impact

Without this fix, TST-04 provides false confidence across all repos using central package management — which is likely the majority.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions