Repository navigation
ci(mutation): disable Stryker mutators on static initializer tables - #229
Merged
samtrion merged 2 commits intoOct 29, 2026
Merged
Conversation
Stryker 5.0.0 with the Microsoft Testing Platform runner (preview) leaves mutants in static field initializers as Survived: the mutant is switched on in a warm test host after the type initializer already ran, so no test can fail. In the latest run 202 of 491 survivors were static mutants (CSharpKeywords 127, CollectionExpressionTargetTypes 46, AllowedCollectionMetadataNames 11, SupportedAsyncReturnTypeMetadataNames 5, others), even though tests such as ArrayList_ImmediateClear_Reports would fail if the entry were dropped. Scope the disable/restore pragmas to the individual static field declarations so the surrounding logic stays mutated. Remove once the MTP runner handles static mutants.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #229 +/- ##
=======================================
Coverage 94.72% 94.72%
=======================================
Files 48 48
Lines 2557 2557
Branches 449 449
=======================================
Hits 2422 2422
Misses 58 58
Partials 77 77 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
samtrion
deleted the
fix/stryker-static-initializer-mutants-never-killed
branch
October 29, 2026 15:42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In the latest mutation run (run 40488214820) the score is 59.95% (Killed 793, Timeout 5, Survived 491, NoCoverage 42). 202 of the 491 survivors are mutants inside static field initializers (
static=truein the report: 202 Survived / 3 Killed / 3 CompileError):Helpers/CSharpKeywords.csUsage/RequireCancellationCheckCodeFixProvider.cs(CollectionExpressionTargetTypes)Maintainability/AvoidRedundantCollectionClearAnalyzer.cs(AllowedCollectionMetadataNames)Helpers/CancellationTokenCallSites.csDiagnosticDescriptors,UseLangwordSuppressor,RequireNumericLiteralSuffixSuppressor,TargetFrameworkMonikers, othersThey survive although existing tests would fail if the value were really changed:
"System.Collections.ArrayList" -> ""and"System.Collections.Generic.List1" -> ""survive whileAvoidRedundantCollectionClearAnalyzerTestshasArrayList_ImmediateClear_Reportsand about 20Listtests. The three killed static mutants are inDiagnosticIds.HelpLink`, an expression-bodied static method that is evaluated at test time, not a field initializer.Root cause
Consistent with the Stryker 5.0.0 source and not verified by a dedicated run: the MTP runner (preview, log: "Microsoft Test Platform testrunner is currently in preview", "Starting per-test coverage capture for MTP runner") switches the active mutant by writing the mutant id into a warm test host and only restarts the host after a timeout. A static field initializer has therefore already run before the mutant is read, so the mutation never takes effect. Static mutants are run against the whole suite and still survive; this is a tool artefact, not missing tests. Local confirmation was not possible: the repository does not build on the maintainer's Windows machine (
NetEvolve.Analyzer.Dogfood.dllcopy error, MSB3030), so Stryker aborts with "Failed to analyze project builds". The branch run below is the confirmation.Change
// Stryker disable all : .../// Stryker restore allpragma pairs placed directly around the individual static table declarations only (7 files, comments only, no behaviour change):CSharpKeywords: all five tablesRequireCancellationCheckCodeFixProvider.CollectionExpressionTargetTypesAvoidRedundantCollectionClearAnalyzer.AllowedCollectionMetadataNamesCancellationTokenCallSites.SupportedAsyncReturnTypeMetadataNamesUseLangwordSuppressor.Suppression,RequireNumericLiteralSuffixSuppressor.SuppressionTargetFrameworkMonikers.UnifiedTfmPatternSurrounding logic in those classes stays mutated.
DiagnosticIdsis untouched (its lazy members are killable).DiagnosticDescriptorsfields are not covered (the per-field mutants there were not identified), so the expected effect is slightly below the full 202. This repository has no earlier "Stryker disable" usage, so this introduces a new convention.Before / After
Run: https://github.com/dailydevops/analyzer/actions/runs/40815422362
198 of the 202 static survivors moved to Ignored (the rest are outside the scoped tables, e.g.
DiagnosticDescriptors). The Killed count did not change, so no real test strength was lost or gained. Duration is not a controlled comparison (different runner load). The score gain is purely a denominator change.Risk / what this does not change
coverage-analysis perTestInIsolation) was not tried; mutation runs would still use the warm host.