Coverage reports
Policies that keep the org coherent
Org-wide line/branch coverage for novolis-* test hosts, collected in parallel and merged into one HTML report.
Preferred: novolis-coverage (dotnet tool)
dotnet tool install --global Novolis.Tools.Coverage.Cli --version 2026.1.*
# Platform.slnx + ProjectRef (skip rebuild when already warm)
novolis-coverage collect --platform --skip-build --fail-below -1 --out d:\novolis\coverage
# List hosts
novolis-coverage list --platform
# Analyze an existing Cobertura (library API; no test run)
novolis-coverage gaps --cobertura d:\novolis\coverage\report\Cobertura.xml --target 95 --write d:\novolis\coverage\GAPS.md
# Static test-host / assembly linkage gaps (no test run)
novolis-coverage test-gaps --no-fail --out d:\novolis\artifacts\test-gaps
# One markdown file: Platform.slnx Cobertura fan-in (parallel), caller's cwd or --out
novolis-coverage crap --fail-above -1
novolis-coverage crap --out d:\novolis\CRAP.md --coverage-dir d:\novolis\coverageLibrary (preferred over growing PowerShell): Novolis.Tools.Coverage — parse/analyze/gate/CRAP.
Test authoring helpers: Novolis.Testing.Coverage (PublicApiSurface).
Governance scripts remain thin policy wrappers around the tool.
Local without install:
dotnet run --project d:\novolis\novolis-tools\src\Novolis.Tools.Coverage.Cli -p:NovolisUseProjectReferences=true -- collect --platform --skip-build --fail-below -1 --out d:\novolis\coverage
dotnet run --project d:\novolis\novolis-tools\src\Novolis.Tools.Coverage.Cli -p:NovolisUseProjectReferences=true -- gaps --cobertura d:\novolis\coverage\Cobertura.xml --target 95
dotnet run --project d:\novolis\novolis-tools\src\Novolis.Tools.Coverage.Cli -p:NovolisUseProjectReferences=true -- crap --out d:\novolis\CRAP.md --fail-above -1HTML: d:\novolis\COVERAGE.html (single-file summary + risk hotspots, next to Novolis.Platform.slnx). Full drill-down still under d:\novolis\coverage\.
PowerShell (CI / legacy)
# List which repos would run (after excludes)
pwsh -File d:\novolis\novolis-governance\scripts\get-coverage-report.ps1 -ListRepos
# Full parallel run → artifacts/coverage/ (NuGet mode: published packages)
pwsh -File d:\novolis\novolis-governance\scripts\get-coverage-report.ps1 -OpenReport
# Subset
pwsh -File d:\novolis\novolis-governance\scripts\get-coverage-report.ps1 -Include novolis-astro,novolis-io,novolis-math
# Extra excludes + throttle
pwsh -File d:\novolis\novolis-governance\scripts\get-coverage-report.ps1 -Exclude novolis-raylib,novolis-audio -ThrottleLimit 6Platform gate (ProjectReference mode)
Org coverage gate evaluates local source via regenerated Novolis.Platform.slnx and NovolisUseProjectReferences=true. Default -FailBelow is 95 in this mode.
# Regenerate map + slnx, then collect coverage from test hosts listed in the platform solution
pwsh -File d:\novolis\novolis-governance\scripts\get-coverage-report.ps1 -PlatformSlnx -RegenerateSlnx
# Already regenerated
pwsh -File d:\novolis\novolis-governance\scripts\get-coverage-report.ps1 -PlatformSlnx -FailBelow 95
# List hosts that would run from the platform slnx (respects coverage-excludes.txt)
pwsh -File d:\novolis\novolis-governance\scripts\get-coverage-report.ps1 -PlatformSlnx -ListReposRequires .NET 10 SDK (MTP --coverage) and reportgenerator (dotnet tool install -g dotnet-reportgenerator-globaltool — auto-installed if missing).
Do not pass --nologo to dotnet test under MTP — it is treated as an unknown argument and yields exit code 5 with zero tests.
Test gaps (no coverage run needed)
dotnet run --project d:\novolis\novolis-tools\src\Novolis.Tools.Coverage.Cli\Novolis.Tools.Coverage.Cli.csproj --no-launch-profile -- test-gaps --no-failReports:
- Repos/solutions with no test hosts
- Production assemblies under
src//codegen/with no direct test `ProjectReference`
Same -Exclude / coverage-excludes.txt / -Include as the coverage collector. Output: artifacts/test-gaps/SUMMARY.md.
Platform / native packages (allowed gaps)
These packables are intentionally without headless unit ProjectReference linkage (GPU, OS UI, P/Invoke, or Docker):
| Package | Why skipped |
|---|---|
Novolis.IO.Mobile.Android | Host-side ADB protocol / device tooling (headless tests cover parsers only; live ADB is dogfood) |
Novolis.Audio.Bindings / .Native | Native audio P/Invoke |
Novolis.Audio.Output.NAudio | Windows audio device |
Novolis.Audio.Playback / .Runtime / .Live.Visuals | Device playback / runtime / live-visual hosts |
Novolis.Audio.Voice.Platform.Maui / .Windows | Platform voice hosts |
Novolis.Audio.Voice.SherpaOnnx / .AzureSpeech | Native ONNX / Azure network TTS hosts |
Novolis.Avalonia.Mobile.Android / .Desktop | Mobile/desktop UI hosts |
Novolis.MachineLearning.TestSupport | Shared ML test-support helpers (not product logic) |
Novolis.Raylib / .Native / .Raygui.Native / .Runtime / .Raygui / .Bindings | Native window / P/Invoke / runtime hosts |
Novolis.Raylib.Game / .Capture / .Hosting / .Loaders / .Input / .Manifests | Native window / game / capture / input hosts |
Novolis.Raylib.Pipeline / .CodeGen / .CodeGen.Hooks / .CodeGen.Abstractions / .Testing | Native binding codegen / test-helper packages |
Novolis.Tools.Cli / .Docs / .Docs.Cli | Interactive CLI / docs tooling hosts |
Novolis.Tools.Coverage.Cli | Interactive org coverage CLI host |
Novolis.Tools.Coverage CoverageCollector / ReportGeneratorInvoker | Process orchestration (dotnet test / ReportGenerator); workspace/discovery/Cobertura helpers remain scored |
Novolis.Tools.Sqlite.Cli / .LiteDb.Cli | Interactive SQLite / LiteDB CLI hosts |
Novolis.Logging.Transports | HTTP / LocalIpc log transport hosts |
Novolis.Messaging.Coordination.Redis | Redis / Garnet network coordination host (requires live Redis; Testcontainers gated) |
Novolis.Modeling.Import.AssimpSkinnedMeshImporter | Native Assimp skinned FBX/glTF import (bone weights); guard clauses remain unit-tested |
Novolis.Markup.Html | HTML layout/render host surface |
Novolis.Analyzers.Licensing | Roslyn licensing analyzer (excluded via ReportGenerator assembly filter; netstandard2.0 cannot use assembly-level ExcludeFromCodeCoverage) |
Novolis.Geopolitics.Scenarios | Scenario pack host helpers |
Novolis.Rendering.Presentation.Abstractions / .Raylib / .Silk | GPU presentation bridge / hosts |
Novolis.Rendering.Backends.Vulkan / .Igpu | GPU device backends |
Novolis.Rendering.Backends.TwoD.Silk | Silk.NET OpenGL 2D window / game-loop host |
Novolis.Rendering.PathTrace.Demos | GPU path-trace demos |
Novolis.Simulation.View | Camera / view rig (GPU presentation bridge; headless sim tests skip) |
Novolis.Transports.Torrent | BitTorrent / P2P network host |
Novolis.Transports.WireFish | OS packet-capture host |
Novolis.Testing.Testcontainers | Requires Docker |
Novolis.Video.Rtc.Abstractions | RTC / capture device abstractions (device host surface) |
Packables in this table carry [assembly: ExcludeFromCodeCoverage] in their project (see each package README or AssemblyInfo.cs). Org coverage collectors honor that attribute; do not filter these assemblies manually in scripts unless debugging.
LoggingSurface (in Novolis.Logging.Agent) is also [ExcludeFromCodeCoverage] — it only attaches AgentSurface / LogTransports network hosts.
Generated / wire-only surfaces
| Surface | Treatment |
|---|---|
Novolis.Xsd.Ubl / .Ubl.Lean Generated/**/*.g.cs | ReportGenerator -filefilters:-*.g.cs (do not assembly-exclude — hand-written UblDocument / *BaseMapper stay scored) |
Novolis.Xsd.Peppol XSCG SBDH DTOs (*.g.cs) | Same .g.cs filter; envelope helpers remain covered |
Novolis.Xsd.Ubl.Lean.StripEmbeddedMapper | [ExcludeFromCodeCoverage] — reflective Wire↔Base projection (same role as generated mappers) |
Novolis.Xsd.Ubl.Validation.SchemaSetFromDirectory | [ExcludeFromCodeCoverage] — schema disk I/O / XmlSchema.Read error edges; public validators remain scored |
Novolis.Analyzers.Licensing | Assembly filter (netstandard2.0) |
Novolis.Logging.Agent.LoggingAgentSurfaceContract | Attribute reflection glue for AgentSurface attach |
ProjectRef transitive bleed (per-repo reports)
Under Platform.slnx ProjectRef mode, consumer Cobertura files often include sibling assemblies (e.g. Novolis.Simulation.Humanoid under gaming, Novolis.Economy.Core under civics, Novolis.Storage.* under tools, Novolis.CodeGen.* under xsd). Per-repo ReportGenerator merges use home-assembly include filters (Get-RepoAssemblyFilter / CoverageWorkspace.RepoAssemblyFilter) so SUMMARY line % reflects that repo's packages. Aggregate org merges still combine all Cobertura files so each assembly is scored from its home repo when present.
ReportGenerator also excludes MessagePack.* generated formatters and legacy Frank.* assemblies from the merged aggregate so the gate reflects Novolis production source.
novolis-avalonia is listed in `coverage-excludes.txt` for the org gate: Avalonia visual-tree / UI-thread hosts are not scored in the aggregate line metric (Mobile Android/Desktop packables also carry ExcludeFromCodeCoverage).
novolis-workspaces is included in Platform coverage (Snapshots / Timeline / Workspaces). Agent tests are discovered on disk even when omitted from the meta slnx, and collected serially to avoid LocalIpc hangs.
Outputs
Under <Root>/artifacts/coverage/ (gitignored via repo artifacts/):
| Path | Content |
|---|---|
SUMMARY.md | Per-repo table for PRs / agents |
summary.json | Machine-readable totals |
report/index.html | Merged ReportGenerator HTML |
raw/<repo>/*.cobertura.xml | Per-test-project Cobertura |
logs/<repo>.log | Build/test transcript on failure |
Excludes
Default list: `scripts/coverage-excludes.txt`.
- Edit that file for standing skips (apps, templates, workflows, …).
- Pass
-Exclude repo1,repo2for one-off skips (merged with the file). - Pass
-Include repo1,repo2to run only those repos (still applies excludes). - Platform mode still applies these excludes when selecting test hosts from
Novolis.Platform.slnx.
How it works
NuGet mode (default)
- Discover
tests/**/*.csprojthat reference TUnit / VSTest / xUnit / NUnit. - Build each repo (unless
-SkipBuild), thendotnet test --coverage --coverage-output-format coberturawithNovolisUseProjectReferences=false. - Repos run in parallel (
ForEach-Object -Parallel,-ThrottleLimit). - ReportGenerator merges Cobertura → HTML + Markdown + aggregate Cobertura.
Platform mode (-PlatformSlnx)
- Optionally regenerate via `build/Generate-Platform-Slnx.ps1` (
-RegenerateSlnx). - Enumerate MTP test hosts listed in
Novolis.Platform.slnx(skipcoverage-excludes.txtrepos and non-hosts such as TestSupport). dotnet build/dotnet test --coverageeach selected host withNovolisUseProjectReferences=true(parallel by repo).- Merge and gate: default FailBelow 95 when
-FailBelowis omitted.
Gate
# NuGet mode — opt-in threshold
pwsh -File d:\novolis\novolis-governance\scripts\get-coverage-report.ps1 -FailBelow 60
# Platform org gate (defaults to FailBelow 95)
pwsh -File d:\novolis\novolis-governance\scripts\get-coverage-report.ps1 -PlatformSlnx -RegenerateSlnxExits 1 if aggregate line or branch coverage is below the threshold, or if any selected repo fails.