Files
Cathy Sullivan 42e29303b4 Add coverage-analysis skill to dotnet-test plugin (#499)
* Add coverage-analysis skill to dotnet-test plugin

Adds an automated, project-wide code coverage and CRAP score analysis skill
for .NET projects. Complements the existing crap-score skill (which is
manual/targeted for single methods) with an end-to-end pipeline:

- Auto-detects solution structure and test projects
- Detects coverage provider (Microsoft.Testing.Extensions.CodeCoverage or
  Coverlet) per test project and selects the correct dotnet test arguments
- Runs coverage collection, generates reports via ReportGenerator
- Calculates CRAP scores per method via bundled PowerShell scripts
- Surfaces risk hotspots ranked by danger (complex + uncovered code)
- Produces actionable recommendations with projected score improvements

Boundary with crap-score: coverage-analysis is project-wide and automated;
crap-score is targeted and LLM-directed. The frontmatter descriptions
cross-reference each other so the skill router disambiguates correctly.

Includes eval.yaml with 3 scenarios (existing data analysis, from-scratch
coverage collection, coverage plateau diagnosis) and shared buildable
fixtures.

Fixes #457

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update plugins/dotnet-test/skills/coverage-analysis/SKILL.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Address PR review: coverage output path and scoped tool install

- Add --coverage-output $rawDir to all MS CodeCoverage dotnet test
  commands (unified, .NET 9, .NET 10+, and mixed-project modes) so
  Cobertura XML lands in the expected directory for downstream search
- Replace dotnet tool install --global with --tool-path scoped to
  the coverage output directory to avoid mutating the user environment

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Cobertura XML fixture line numbers to match StudentService.cs

FindById starts at line 101 (was 88), Search starts at line 109 (was 96).
Updated both partial-coverage and plateau fixtures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Wrap Get-ChildItem pipelines in @() for single-result array safety

PowerShell returns a scalar FileInfo (not array) when a pipeline yields
exactly one result, making .Count return null. Wrapping in @() ensures
the result is always an array so .Count works reliably for the common
single-test-project case.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove CODEOWNERS change - not needed for this PR

The existing /plugins/dotnet-test/ and /tests/dotnet-test/ wildcards
already route reviews to @dotnet/dotnet-testing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address review: .csproj fallback, cross-platform paths, .sln header

- Add .csproj fallback discovery in Step 1 when no .sln found (emit
  ENTRY_TYPE:Project) to match documented behavior
- Fix ProjectReference backslash to forward slash in test fixture .csproj
- Remove leading blank line from ContosoUniversity.sln fixture

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add plateau/stuck trigger language to frontmatter description

The skill was NOT ACTIVATED for the 'Coverage plateau diagnosis' eval
scenario because the frontmatter description (used by the skill router)
lacked plateau-related keywords. The prompt 'coverage is stuck' and
'what's blocking me' didn't match the existing description text.

Add 'stuck', 'plateaued', and 'blocking improvement' to the frontmatter
Use-when sentence so the skill router activates for plateau diagnosis
prompts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Make CRAP scores mandatory, add existing-data shortcut path

The plateau eval scenario scored 1/5 on CRAP scores across all modes
because the LLM shortcut the workflow when existing Cobertura XML was
provided — it read the XML directly and gave a quick diagnostic answer
without computing CRAP scores or following the output template.

- Add existing-data guidance at top of Workflow section: skip test
  execution but still run CRAP score computation (Steps 5-6)
- Add mandatory CRAP rule to guidelines.md: Risk Hotspots table is
  required in every output regardless of analysis mode

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update plugins/dotnet-test/skills/coverage-analysis/SKILL.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-07 18:27:11 +02:00

77 lines
4.4 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<coverage line-rate="0.58" branch-rate="0.45" version="1.0" timestamp="1712437200" lines-covered="35" lines-valid="60" branches-covered="9" branches-valid="20">
<packages>
<package name="ContosoUniversity.Services" line-rate="0.58" branch-rate="0.45" complexity="18">
<classes>
<class name="ContosoUniversity.Services.StudentService" filename="ContosoUniversity/StudentService.cs" line-rate="0.58" branch-rate="0.45" complexity="18">
<methods>
<method name="Enroll" signature="(System.String,System.String,System.Int32)" line-rate="0.75" branch-rate="0.67" complexity="6">
<lines>
<line number="13" hits="3" branch="true" condition-coverage="100% (2/2)" />
<line number="14" hits="1" branch="false" />
<line number="16" hits="2" branch="true" condition-coverage="50% (1/2)" />
<line number="17" hits="0" branch="false" />
<line number="19" hits="2" branch="false" />
<line number="20" hits="2" branch="false" />
<line number="22" hits="2" branch="true" condition-coverage="50% (1/2)" />
<line number="23" hits="0" branch="false" />
<line number="25" hits="2" branch="false" />
<line number="26" hits="2" branch="false" />
<line number="27" hits="2" branch="false" />
<line number="28" hits="2" branch="false" />
<line number="29" hits="2" branch="false" />
<line number="30" hits="2" branch="false" />
<line number="32" hits="2" branch="false" />
<line number="33" hits="2" branch="false" />
</lines>
</method>
<method name="CalculateGpa" signature="(System.Int32,System.Collections.Generic.List`1&lt;ContosoUniversity.Services.Grade&gt;)" line-rate="0.25" branch-rate="0.20" complexity="10">
<lines>
<line number="45" hits="1" branch="false" />
<line number="46" hits="1" branch="true" condition-coverage="50% (1/2)" />
<line number="47" hits="0" branch="false" />
<line number="49" hits="1" branch="true" condition-coverage="50% (1/2)" />
<line number="50" hits="0" branch="false" />
<line number="52" hits="0" branch="false" />
<line number="53" hits="0" branch="false" />
<line number="54" hits="0" branch="false" />
<line number="56" hits="0" branch="false" />
<line number="57" hits="0" branch="true" condition-coverage="0% (0/2)" />
<line number="58" hits="0" branch="false" />
<line number="59" hits="0" branch="false" />
<line number="61" hits="0" branch="false" />
<line number="62" hits="0" branch="false" />
<line number="63" hits="0" branch="false" />
<line number="64" hits="0" branch="false" />
<line number="65" hits="0" branch="false" />
<line number="66" hits="0" branch="false" />
<line number="68" hits="0" branch="false" />
<line number="69" hits="0" branch="false" />
<line number="71" hits="0" branch="true" condition-coverage="0% (0/2)" />
<line number="72" hits="0" branch="false" />
<line number="74" hits="0" branch="false" />
<line number="76" hits="0" branch="false" />
</lines>
</method>
<method name="FindById" signature="(System.Int32)" line-rate="1.0" branch-rate="1.0" complexity="1">
<lines>
<line number="101" hits="4" branch="false" />
</lines>
</method>
<method name="Search" signature="(System.String)" line-rate="0.50" branch-rate="0.50" complexity="2">
<lines>
<line number="109" hits="1" branch="true" condition-coverage="50% (1/2)" />
<line number="110" hits="1" branch="false" />
<line number="112" hits="0" branch="false" />
<line number="113" hits="0" branch="false" />
<line number="114" hits="0" branch="false" />
<line number="115" hits="0" branch="false" />
</lines>
</method>
</methods>
</class>
</classes>
</package>
</packages>
</coverage>