Skip to content

Reduce MSTest source generation startup overhead - #10545

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/optimize-sourcegen-startup
Aug 11, 2026
Merged

Reduce MSTest source generation startup overhead#10545
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/optimize-sourcegen-startup

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Summary

  • remove generated DataRows and parameter-name descriptors that runtime registration never consumes
  • enumerate methods and properties once per test class while preserving exact overload matching and reflection fallback
  • pre-size registration dictionaries and pass immutable assembly attributes without a startup copy
  • strengthen generator and packaged acceptance coverage for DataRow, DynamicData, overloads, inherited metadata, and compact generated output

Performance

Representative six-class test assembly, 100 warmed process starts:

Metric Before After Change
Median startup 150.41 ms 133.79 ms -11.0%
Mean startup 159.22 ms 134.18 ms -15.7%
Registry source 95,900 B 84,188 B -12.2%
Generated assembly 47,616 B 45,568 B -4.3%

Validation

  • 125 MSTest.SourceGeneration unit tests
  • focused packaged acceptance tests on net8.0 and net10.0 (4 cases)
  • repository package build with metadata validation

No tracking issue.

Remove unused DataRow and parameter-name descriptors, reuse per-class reflection enumerations, pre-size registration dictionaries, and avoid copying assembly attributes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Emit zero-copy assembly metadata without array covariance, clarify trimming and ownership contracts, and make reflection-free acceptance coverage explicit.

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

Copilot-Session: 22842b75-382d-49db-9517-c146e91547f2
Copilot AI balanced review requested due to automatic review settings August 10, 2026 13:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Reduces reflection-free MSTest source-generation startup overhead while preserving metadata resolution and fallback behavior.

Changes:

  • Removes unused DataRow and parameter-name descriptors.
  • Reuses reflected member arrays and pre-sizes registration dictionaries.
  • Expands generator and packaged acceptance coverage.
Show a summary per file
File Description
test/UnitTests/MSTest.SourceGeneration.UnitTests/MSTestReflectionMetadataGeneratorTests.cs Updates generated-output expectations and coverage.
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SourceGenerationNonAotTests.cs Validates compact output, overloads, and runtime behavior.
src/Analyzers/MSTest.SourceGeneration/Models/TestClassModel.cs Removes unused model fields.
src/Analyzers/MSTest.SourceGeneration/Generators/TestClassModelBuilder.cs Stops constructing unused metadata.
src/Analyzers/MSTest.SourceGeneration/Generators/RuntimeRegistrationEmitter.cs Optimizes reflection enumeration and collection allocation.
src/Analyzers/MSTest.SourceGeneration/Generators/MetadataRegistryEmitter.cs Emits a smaller registry and zero-copy assembly attributes.
src/Analyzers/MSTest.SourceGeneration/Generators/DataRowBuilder.cs Removes obsolete DataRow descriptor generation.
src/Adapter/MSTestAdapter.PlatformServices/SourceGeneration/ReflectionMetadataHook.cs Clarifies collection ownership and immutability expectations.

Review details

Tip

Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 8/8 changed files
  • Comments generated: 0
  • Review effort level: Balanced

@github-actions

Copy link
Copy Markdown
Contributor

🧵 Parallel-safety audit — PR #10545

Parallelization — one row per test assembly audited:

Test assembly Scope Workers Analyzer coverage
MSTest.SourceGeneration.UnitTests off (no opt-in found: no [assembly: Parallelize], .runsettings, testconfig.json, or MSTestParallelizeScope) n/a n/a
MSTest.Acceptance.IntegrationTests off (no opt-in found for this project) n/a n/a

⚠️ Parallelization is OFF for both audited assemblies — this is a readiness checklist, not live bugs. Nothing here fails today.

Findings: A (global-state) 0 · B (paths) 0 · C (declaration) 0 · D (over-serialization) 0 — by severity: Critical 0 · High 0 · Warning 0 · Info 0.

Top actions (by expected value): none — no unsafe call sites found in the changed test regions.


Details

The two changed test files are:

  • test/UnitTests/MSTest.SourceGeneration.UnitTests/MSTestReflectionMetadataGeneratorTests.cs — changes only update Should().Contain/NotContain assertions on generator output strings (e.g. AssemblyAttributes, DataRowAttribute shape, ResolveMethod/ResolveProperty signatures). No Environment.*, Directory.*, File.*, static-field mutation, [ResourceLock]/[DoNotParallelize] declaration change, or lifecycle-member change is present in the diff.
  • test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SourceGenerationNonAotTests.cs — adds two [TestMethod]s (Overload()/Overload(int)) to an embedded synthetic test-project string, sets <MSTestSourceGenMode>ReflectionFree</MSTestSourceGenMode> in that same embedded .csproj string, and asserts on File.ReadAllText of generated .g.cs files under the asset's own obj/ output — all within the acceptance test's dedicated per-asset scratch directory (generator.TargetAssetPath), so no shared/relative path collision.

The [assembly: Parallelize(...)] literals found via grep in MSTestReflectionMetadataGeneratorTests.cs (lines ~1391–1481) are all inside userCode/MinimalMSTestStub string constants fed to the source generator under test — synthetic input, not a real compiled attribute on the MSTest.SourceGeneration.UnitTests assembly itself. Correctly excluded per the workflow's validation rule; not a configuration change.

No .runsettings, testconfig.json, Directory.Build.props/.targets, or .csproj parallelization settings were touched by this PR.

Advisory only — heuristic, non-blocking. Re-run with /parallel-audit. This audit answers "is it parallel-safe?"; for testability, smells, or flakiness see the detect-static-dependencies / test-smell-detection / test-anti-patterns analyses.

🤖 Automated content by GitHub Copilot. Generated by the Parallel-safety audit on PR (on open / sync) workflow. · auto · 73.3 AIC · ⌖ 4.73 AIC · ⊞ 24.8K · [◷]( · )

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Expert test review — PR #10545

GradeTestMutationNotesHow to improve
B (80–89) mod MSTestReflectionMetadataGeneratorTests.
Generator_
OverriddenVirtualMethod_
HonorsInheritedAttributeUsage
3/4 killed Confirms the base [DataRow(1)] isn't duplicated onto the override, but never asserts it's still emitted for the base method. Assert the registry still contains DataRowAttribute(1) for the base method.
A (90–100) mod MSTestReflectionMetadataGeneratorTests.
Generator_
DoesNotEmitRedundantDataRowsDescriptor
2/2 killed Checks both support-types and registry sources exclude the removed DataRows descriptor.
A (90–100) mod MSTestReflectionMetadataGeneratorTests.
Generator_
EmitsDataRowAttributeWithoutDuplicateArgumentArray
2/2 killed Asserts the exact emitted DataRowAttribute construction and absence of the old DataRows array.
A (90–100) mod MSTestReflectionMetadataGeneratorTests.
Generator_
EmitsMultipleDataRowAttributes_
InDeclarationOrder
3/3 killed Verifies each generated DataRowAttribute and that declaration order is preserved via index comparisons.
A (90–100) mod MSTestReflectionMetadataGeneratorTests.
Generator_
LeavesDataRowParamsMaterializationToAttribute
2/2 killed Confirms the variadic params tail is passed straight through to the attribute constructor, not pre-flattened.
A (90–100) mod MSTestReflectionMetadataGeneratorTests.
Generator_
EmitsModuleInitializer_
RegisteringAssemblyWithAttributes_
AndCompilesAgainstHook
4/4 killed Broad assertions pin the new cached-array ResolveMethod/ResolveProperty pattern and reject the old per-call GetMethods(flags) call.
A (90–100) mod MSTestReflectionMetadataGeneratorTests.
RuntimeRegistration_
RegistersCompleteMethodAttributes
2/2 killed Ordering assertions (resolvedGuard < completenessGuard < assignment) protect the guard sequencing, not just presence.
A (90–100) mod SourceGenerationNonAotTests.
SourceGenerationNonAot_
BuildsAndRunsTests_
WithExitCodeZero
N/A End-to-end acceptance test now also asserts generated-file contents (no DataRows/ParameterNames) and an updated pass count reflecting the new Overload test methods.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Suggestions on the Files
changed tab can be applied with one click. Re-run with
/review-tests.

🤖 Automated content by GitHub Copilot. Generated by the Test Reviewer on PR (on open / sync) workflow. · auto · 115.2 AIC · ⌖ 10.8 AIC · ⊞ 16.9K · [◷]( · )

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 Automated content by GitHub Copilot. Generated by the Test Reviewer on PR (on open / sync) workflow. · auto · 115.2 AIC · ⌖ 10.8 AIC · ⊞ 16.9K ·

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) August 10, 2026 17:01
@Evangelink Amaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Aug 10, 2026
@Evangelink
Amaury Levé (Evangelink) merged commit 9b170ef into main Aug 11, 2026
42 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/optimize-sourcegen-startup branch August 11, 2026 08:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-review Awaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants