Skip to content

[fix] Replace TestSDKAutoGeneratedCode with ExcludeFromCodeCoverage in auto-generated Program files - #16101

Merged
Jakub Jareš (nohwnd) merged 5 commits into
mainfrom
fix/issue-3876-testsdkautogeneratedcode-9cc463cc0061ee87
Jun 12, 2026
Merged

[fix] Replace TestSDKAutoGeneratedCode with ExcludeFromCodeCoverage in auto-generated Program files#16101
Jakub Jareš (nohwnd) merged 5 commits into
mainfrom
fix/issue-3876-testsdkautogeneratedcode-9cc463cc0061ee87

Conversation

@nohwnd

@nohwnd Jakub Jareš (nohwnd) commented Jun 8, 2026

Copy link
Copy Markdown
Member

Fixes #3876.

Two test projects in the same directory share obj/. NuGet writes project.assets.json per-project, so if the class library is restored after the test project, it overwrites the test project''s assets file. MSBuild then compiles the test project against the wrong assets, the auto-generated Main can''t resolve Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode, and the build fails with CS0234.

The fix is to stop referencing that attribute from the auto-generated Program.{cs,vb,fs} files. [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] lives in System.Runtime/mscorlib, is always resolvable regardless of what''s in project.assets.json, and is already in vstest''s default coverage exclusion config alongside TestSDKAutoGeneratedCode — so coverage behavior is unchanged.

Follow-ups in the same PR:

  • [EditorBrowsable(Never)] on TestSDKAutoGeneratedCode so new code doesn''t discover it via IntelliSense. Deliberately not [Obsolete] — that would emit CS0618 from the old auto-generated Main in any consumer where NuGet unifies a newer Microsoft.TestPlatform.ObjectModel against an older Microsoft.NET.Test.Sdk (and break builds with TreatWarningsAsErrors).
  • Comment next to the TestSDKAutoGeneratedCode regex in EnableCodeCoverageArgumentProcessor.cs explaining why it stays. It has to remain until test assemblies built with the old SDK are out of support.
  • Dropped now-unused using System; / Imports System from the generated Program files (Copilot reviewer caught it; would have tripped IDE0005 in consumer builds).

Testing

  • Microsoft.TestPlatform.Utilities.UnitTests — pass on net11.0 + net481.
  • vstest.console.UnitTests — pass on net11.0 + net481 (5 pre-existing [Ignore]).

🔍 Triaged by Issue Repro Triage & Auto-Fix 🔍

…ry (#3876)

Replace [TestSDKAutoGeneratedCode] with [ExcludeFromCodeCoverage] in the
auto-generated Microsoft.NET.Test.Sdk.Program files (C#, VB, F#).

Root cause: when a test project and a class library project are in the same
directory, both projects share the same obj/ folder. The project.assets.json
file gets overwritten by the other project's restore, temporarily losing the
reference to Microsoft.TestPlatform.ObjectModel.dll. This causes the compiler
to fail to resolve [Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode]
with CS0234: 'VisualStudio' does not exist in namespace 'Microsoft'.

Fix: replace with [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage],
which is a BCL attribute that requires no extra assembly references. The
AutoGeneratedProgram class is still excluded from code coverage because
ExcludeFromCodeCoverageAttribute is already in the default code coverage
exclusion config (EnableCodeCoverageArgumentProcessor.cs line 137).

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

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

This PR updates the auto-generated entry-point templates shipped in Microsoft.NET.Test.Sdk (netcoreapp build assets) to avoid referencing Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode, eliminating a nondeterministic CS0234 failure when multiple projects share the same obj/ folder and project.assets.json gets overwritten.

Changes:

  • Replaced [Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode] with [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] in the C# auto-generated Program template.
  • Replaced the same attribute in the VB auto-generated Program template.
  • Replaced the same attribute in the F# auto-generated Program template.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/package/Microsoft.NET.Test.Sdk/netcoreapp/Microsoft.NET.Test.Sdk.Program.cs Switches the generated C# entry point to use a BCL-only coverage exclusion attribute.
src/package/Microsoft.NET.Test.Sdk/netcoreapp/Microsoft.NET.Test.Sdk.Program.vb Switches the generated VB entry point to use a BCL-only coverage exclusion attribute.
src/package/Microsoft.NET.Test.Sdk/netcoreapp/Microsoft.NET.Test.Sdk.Program.fs Switches the generated F# entry point to use a BCL-only coverage exclusion attribute.

Comment on lines +2 to +3
using System;
[Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Right, dropped it. Also removed Imports System from the VB file for the same reason.

@nohwnd Jakub Jareš (nohwnd) left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Code Review — PR #16101

Dimensions activated (files in src/package/): Dependency & Package Integrity · Cross-TFM & Framework Resolution · Backward Compatibility & Rollback Safety · Build Script & Infrastructure Hygiene


What was checked

Dependency & Package Integrity
The three auto-generated Program files (Microsoft.NET.Test.Sdk.Program.{cs,vb,fs}) are included directly into user projects via the GenerateProgramFile MSBuild target as <Compile Include> items. Replacing TestSDKAutoGeneratedCode (from Microsoft.TestPlatform.ObjectModel) with ExcludeFromCodeCoverage (BCL) eliminates the external assembly reference from these files. No other files in the package were affected.

Cross-TFM & Framework Resolution
System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute is available in mscorlib (net4.0+) and System.Runtime (netstandard1.0+, all .NET Core/5+). This covers every TFM vstest targets. The attribute's valid targets (Class | Struct | Constructor | Method | Property | Event) are appropriate: applied to a class in the C# file, and to a method in the VB and F# files.

Backward Compatibility & Rollback Safety
The PR description's core claim is verified: EnableCodeCoverageArgumentProcessor.cs lines 137–138 confirm that ExcludeFromCodeCoverageAttribute is already in the default code coverage exclusion config alongside TestSDKAutoGeneratedCode. Users' coverage results will not change. Third-party coverage tools (Coverlet etc.) natively honor ExcludeFromCodeCoverage, so this change actually improves coverage exclusion for non-VSTest coverage flows.

Build Script & Infrastructure Hygiene
No .csproj, .nuspec, or .targets file changes were required — the template files are included verbatim, so no packaging metadata updates are needed. No changes to eng/expected-nupkg-file-counts.json or eng/expected-dll-frameworks.json are required.


Findings

No issues found. The fix is mechanically correct, minimal in scope, and preserves all existing behavior.


🧠 Reviewed by Expert Reviewer

🧠 Reviewed by Expert Code Reviewer 🧠

…tays

The attribute is now unused by the auto-generated Program files but must
remain for back-compat: test assemblies built against older
Microsoft.NET.Test.Sdk versions still carry it, and the default code
coverage exclusion config keeps matching on it.

- [EditorBrowsable(Never)] on the attribute so new code doesn't pick it
  up by IntelliSense. No [Obsolete] - a deprecation warning emitted from
  the old generated Main file could bleed into builds that resolve a
  newer ObjectModel against an older Microsoft.NET.Test.Sdk.
- Expand the XML doc to point at ExcludeFromCodeCoverageAttribute and
  explain the back-compat role.
- Comment next to the runsettings regex so a future "this looks dead"
  cleanup doesn't drop it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Jareš <me@jakubjares.com>
Copilot AI review requested due to automatic review settings June 11, 2026 15:47
@" <Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>" + Environment.NewLine +
@" <Attribute>^System\.Diagnostics\.DebuggerNonUserCodeAttribute$</Attribute>" + Environment.NewLine +
@" <Attribute>^System\.Diagnostics\.CodeAnalysis\.ExcludeFromCodeCoverageAttribute$</Attribute>" + Environment.NewLine +
// Kept for back-compat: test assemblies built against Microsoft.NET.Test.Sdk <= 17.x carry

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

version does not seem accurate?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, main is on 18.9 so <= 17.x was wrong. Dropped the version pin — older versions reads accurately whether this ships in 18.9 or gets backported.

- Remove `using System;` from Program.cs and `Imports System` from
  Program.vb. Both are unused now that the attribute is fully qualified;
  leaving them in trips IDE0005 in consumer builds that treat analyzer
  warnings as errors. (Copilot reviewer + nohwnd suggestion.)
- Drop the "<= 17.x" version pin in the EnableCodeCoverageArgumentProcessor
  comment - main is on 18.9 and the change will ship there, so the pin
  was wrong. "Older versions" stays correct under any backport.

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

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

@nohwnd Jakub Jareš (nohwnd) left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Code Review — PR #16101 (follow-up commits)

Dimensions activated (files in src/Microsoft.TestPlatform.ObjectModel/ and src/vstest.console/): Public API Surface Protection · Backward Compatibility & Rollback Safety · Cross-TFM & Framework Resolution


What was checked (new commits since 2026-06-08 review)

Public API Surface Protection — TestSDKAutoGeneratedCode.cs
[EditorBrowsable(EditorBrowsableState.Never)] is the correct soft-deprecation tool here. It hides the type from IntelliSense in new code without touching the binary surface — the class stays public and fully callable, so NuGet-unified consumers of older Microsoft.NET.Test.Sdk that still emit [TestSDKAutoGeneratedCode] on their generated Main are unaffected at compile time. Choosing [EditorBrowsable(Never)] over [Obsolete] is deliberate and correct: [Obsolete] would emit CS0618 in any consumer that unifies a newer ObjectModel against an older SDK version and has TreatWarningsAsErrors set.

Backward Compatibility & Rollback Safety
Both using System.ComponentModel; and using System; are still present in the file (needed for EditorBrowsable/EditorBrowsableState and Attribute/AttributeUsage/AttributeTargets respectively). No dead imports introduced.

Cross-TFM & Framework Resolution
System.ComponentModel.EditorBrowsableAttribute is available in mscorlib (net4.0+) and System.Runtime (netstandard1.0+, all .NET Core/5+). No TFM gap.

Back-compat comment in EnableCodeCoverageArgumentProcessor.cs
Comment-only change; no behavioral impact. The text is accurate: the version pin ("≤ 17.x") was correctly dropped in the latest commit since the version boundary isn't known at ship time.


Findings

No issues found. The follow-up commits are clean.


🧠 Reviewed by Expert Code Reviewer 🧠

🧠 Reviewed by Expert Code Reviewer 🧠

@nohwnd

Copy link
Copy Markdown
Member Author

The Build Windows Release failure is an infrastructure regression on main, not caused by this PR's changes.

Evidence:

The regression was introduced by one of the commits merged to main between 14:54–15:09 on 2026-06-11:

SHA Message
d497abd Fix broken docs URLs
6ef39f14 Fix build -pack on Linux
e0114576 Add DotnetTestHostManager filesystem tests
86055d8d Fix BlameCollector race + PortArgumentProcessor + EnableLoggersArgumentProcessor fixes
10cbb5db Fix missing xlf entries / add CouldNotFindTesthost to Designer.cs

Once the main regression is identified and fixed, a CI re-run here should go green. No code changes are needed in this PR.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • dev.azure.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "dev.azure.com"

See Network Configuration for more information.

🔧 Iterated by PR Iteration Agent 🔧

The previous Windows Release CI failure (build #1459853) was an
infrastructure regression on main, not caused by this PR's changes.
PR #16108 (different code) failed the same check with the same
pattern during that window. Since then, main has been stabilized
and PR #16108 passes cleanly.

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

Copy link
Copy Markdown
Member Author

Commit pushed: 604066e

🔧 Iterated by PR Iteration Agent 🔧

@nohwnd

Copy link
Copy Markdown
Member Author

fhnaseer quick sanity check please, we have our own attribute which avoids auto-generated main being in CC, and replacing it with the standard attribute. We keep the old attribute around so people can still build in case they mixobject model versions etc. Seems safe to me, wdyt?

This was referenced Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dotnet build and test commands fail in nondeterministic way when using the Microsoft.NET.Test.Sdk package

2 participants