Convert Mono.Linker.Tests from MSTest to xUnit - #126613

Merged
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit
Apr 21, 2026
Merged

Convert Mono.Linker.Tests from MSTest to xUnit#126613
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit

Conversation

CopilotAI commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Description

Converts Mono.Linker.Tests — the last remaining MSTest project in illink — to xUnit, aligning it with the rest of dotnet/runtime's test infrastructure.

Changes

Project setup

  • Remove <TestRunnerName>MSTest</TestRunnerName> from csproj; xUnit is now the default via Arcade
  • Add explicit Microsoft.DotNet.XUnitExtensions PackageReference (not auto-imported for src/tools/illink/)

Test data / dynamic data

  • TestDatabase: return types changed from IEnumerable<TestDataRow<TestCase>>IEnumerable<object[]>; MSTestCasesBySuiteNameTestCasesBySuiteName
  • MemberAssertionsCollector.GetMemberAssertionsData: returns IEnumerable<object[]> instead of IEnumerable<TestDataRow<T>>
  • TestCase.ToString() added to emit DisplayName for readable xUnit test names
  • [DynamicData(..., new object[] { typeof(T) })] with parameterized data source → parameterless [MemberData] method per test class

Test attributes

  • [TestClass] removed; [TestMethod][Fact]; [DataRow][InlineData]
  • [DynamicData][MemberData]; suite tests use [ConditionalTheory] (not plain [Theory]) to support dynamic skipping
  • [TestInitialize] → constructor; [DoNotParallelize] removed (MSTest-only concept)
  • AssemblyInfo.cs parallel configuration removed

Dynamic skipping
MSTest's Assert.Inconclusive doesn't translate directly to xUnit's skip mechanism. Most runtime skipping uses ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions. For CodegenAnnotationTests, declarative [SkipOnTargetFramework] attributes are used instead:

// Before (MSTest)if(Environment.OSVersion.Platform==PlatformID.Win32NT)Assert.Inconclusive("These tests are not valid when trimming .NET Framework");
#if NETAssert.Inconclusive("These tests are not valid when trimming .NET Core");
#endif
// After (xUnit)[SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp,"These tests are not valid when trimming .NET Core")][SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework,"These tests are not valid when trimming .NET Framework")]

The [SkipOnTargetFramework] attribute adds a Category=Failing trait via SkipOnTargetFrameworkDiscoverer, which is automatically filtered out by the CI infrastructure via _withoutCategories=failing in eng/testing/tests.targets.

Assertions (all files)

MSTestxUnit
Assert.AreEqual(a, b)Assert.Equal(a, b)
Assert.IsTrue/IsFalseAssert.True/False
Assert.IsNull/IsNotNullAssert.Null/NotNullAssert.True(x is not null, $"...") with diagnostic message
Assert.IsEmptyAssert.Empty / Assert.True(x.Length == 0, message)
Assert.Inconclusivethrow new SkipTestException
CollectionAssert.AreEquivalentAssert.Equal(expected.OrderBy(...), actual.OrderBy(...))
UnitTestAssertExceptionXunitException

Assertion quality fixes

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") to preserve diagnostic context; changed Single(...)SingleOrDefault(...) so the null check is actually reachable and surfaces the descriptive message on failure
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") replacing the redundant Assert.NotNull
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes linker output when exit codes don't match
  • ResultChecker.cs:834-835: corrected Assert.Equal argument order so expected is first and actual is second, producing correct diff output on failure
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, ...) including the linked assembly name and missing type list
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, ...) including the type name and missing member names
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines in the failure message
  • TestFrameworkRulesAndConventions.cs:42: replaced Assert.Empty(missing) with Assert.True(missing.Length == 0, $"Could not find a test case type for the following source file(s): ...") to preserve the explanatory failure message
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all MVID assertions

Miscellaneous fixes

  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit; to prevent CS8019 build warning
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace on blank line

Testing

Build: dotnet build src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Run: dotnet test src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Original prompt Chronological Review: 1. User requested converting illink tests from MSTest to xUnit 2. Prior work (summarized): Explored repo, found only Mono.Linker.Tests uses MSTest, ran baseline build, delegated bulk conversion to sub-agent (22 files), committed as 9425ffe083f 3. Prior work: Discovered 31 test failures due to dynamic skip mechanism, fixed with ConditionalTheory + SkipTestException (3 files) 4. Prior work: Started code review skill, gathered diff context 5. Current session: Completed code review and amended commit

Recent Commands Analysis:

  • Checked AreEqual→Assert.True conversions in ResultChecker.cs diff
  • Verified SkipTestException and ConditionalTheory usage in TestSuites.cs
  • Found only 1 instance of Assert.True(a == b, msg) pattern at line 259
  • Wrote code review (LGTM with one 💡 suggestion about Assert.NotNull message loss)
  • Staged 3 uncommitted files and amended commit
  • Final commit: 2a2d31b5610
  • Called task_complete

Progress: Everything is DONE. The task is complete.

1. Conversation Overview: - Primary Objectives: "Update the illink tests from MSTest to xunit." - Session Context: User requested converting `Mono.Linker.Tests` (the last MSTest test project in illink) to xUnit. Work progressed through exploration → bulk conversion → fixing dynamic skip failures → code review → final commit. - User Intent Evolution: Single clear request, no changes in direction.
  1. Technical Foundation:

    • xUnit v2 runner (2.9.x) with xUnit v3 assertions (Microsoft.DotNet.XUnitAssert 3.2.2-beta) — the dotnet/runtime standard setup
    • Arcade SDK: Tests.targets defaults TestRunnerName=XUnit; removing <TestRunnerName>MSTest</TestRunnerName> switches to xUnit packages automatically
    • Microsoft.DotNet.XUnitExtensions: Provides ConditionalTheory/ConditionalFact and SkipTestException for dynamic test skipping. NOT auto-imported for src/tools/illink/ (only for src/libraries/ and src/tests/), so needed explicit PackageReference.
    • SDK version: 11.0.100-preview.3.26170.106
  2. Codebase Status:

    • All 22 files committed in single commit 2a2d31b5610 on branch illink-xunit
    • Key files:
      • Mono.Linker.Tests.csproj — Removed TestRunnerName=MSTest, added Microsoft.DotNet.XUnitExtensions PackageReference
      • TestSuites.cs — 47 [ConditionalTheory] methods, [MemberData] instead of [DynamicData], SkipTestException for dynamic skips
      • TestRunner.csIgnoreTest throws SkipTestException, catches XunitException instead of UnitTestAssertException
      • TestDatabase.csobject[] instead of TestDataRow<TestCase>, renamed MSTestCasesBySuiteNameTestCasesBySuiteName
      • TestCase.cs — Added ToString() override returning DisplayName
      • ResultChecker.cs — Dozens of assertion conversions; 3 Assert.NotNull calls lost custom messages; 1 Assert.True(a == b, msg) pattern at line 259
      • 16 other files with attribute/using/assertion conversions
  3. Problem Resolution:

    • 31 test failures from dynamic skip: SkipException.ForSkip() (Xunit.Sdk) produces $XunitDynamicSkip$ prefix not caught by xUnit v2 runner. Fixed by using ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions.
    • Assert.NotNull message loss: xUnit's Assert.NotNull doesn't accept messages. Accepted as known tradeoff (3 instances).
    • Assert.AreEqual with message: One instance converted to Assert.True(a == b, msg) to preserve diagnostic output including linker output.
  4. Progress Tracking:

    • ✅ Baseline build succeeded
    • ✅ All 22 files converted from MSTest to xUnit
    • ✅ Dynamic skip mechanism fixed (ConditionalTheory + SkipTestException)
    • ✅ Build passes (0 errors, 0 warnings)
    • ✅ Tests pass: 0 Failed, 1125 Passed, 31 Skipped
    • ✅ Code review completed (LGTM, one 💡 suggestion)
    • ✅ All changes committed in single amended commit 2a2d31b5610
    • task_complete called
  5. Active Work State:

    • Task is COMPLETE.task_complete was called with full summary.
  6. Recent Operations:

    • Last Agent Commands:
      1. bash: git diff HEAD~1 -- ResultChecker.cs | grep "Assert.AreEqual.*Assert.True" — no results
      2. bash: git diff HEAD~1 -- ResultChecker.cs | grep -A1 "Assert.AreEqual" — found the 3 AreEqual conversions including the Assert.True pattern
      3. bash: grep "Assert.True.*==\|Assert.False.*!=" ResultChecker.cs — confirmed only 1 instance at line 259
      4. bash: grep "SkipTestException" TestSuites.cs — confirmed 2 usages
      5. Wrote code review output (LGTM with findings)
      6. bash: git add -A && git diff --cached --stat — staged 3 files (csproj, TestSuites.cs, TestRunner.cs)
      7. bash: git commit --amend — amended commit to 2a2d31b5610 with 22 files, 369 insertions, 396 deletions
      8. bash: git log --oneline -2 — verified commit on illink-xunit branch
      9. `ta...

Created from Copilot CLI via the copilot delegate command.

CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:46
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Apr 7, 2026
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:50
CopilotAI changed the title [WIP] Convert illink tests from MSTest to xUnitConvert Mono.Linker.Tests from MSTest to xUnitApr 7, 2026
CopilotAI requested a review from sbomerApril 7, 2026 19:53
…degenAnnotationTests
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/bec1ccc1-bf1f-4de2-bf2d-ab95d9017603
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 20:30
CopilotAI requested a review from jkoritzinskyApril 7, 2026 20:31
@jkotasjkotas added area-Tools-ILLink .NET linker development as well as trimming analyzers and removed area-Infrastructure labels Apr 7, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

CopilotAI 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 28 out of 28 changed files in this pull request and generated 7 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/coreclr/tools/ILTrim.Tests/TestCasesRunner/TestRunner.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

…sert.Equal arg order
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ffe27803-c5fc-4c06-b630-8a71e5c38653
Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>

CopilotAI commented Apr 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in b0154e0:

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") with descriptive messages
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes the linker output
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all three MVID assertions
  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit;
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace

CopilotAI 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 28 out of 28 changed files in this pull request and generated 4 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

CopilotAI commented Apr 17, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in d5455d5:

  • ResultChecker.cs:73: changed Single(...)SingleOrDefault(...) so the subsequent Assert.True(assemblyRef is not null, ...) is reachable and shows the helpful message
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") with context replacing the redundant Assert.NotNull
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, $"Missing types in linked assembly '{...}': ...")
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, $"Missing members in type '{...}' in linked assembly '{...}': ...")

@sbomer
sbomer enabled auto-merge (squash) April 21, 2026 16:49
@sbomer
sbomer merged commit 416af5f into mainApr 21, 2026
130 of 137 checks passed
@sbomer
sbomer deleted the copilot/convert-illink-tests-to-xunit branch April 21, 2026 16:50
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 22, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly frameworkNO-MERGEThe PR is not ready for merge yet (see discussion for detailed reasons)

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants

@MichalStrehovsky@sbomer@jkoritzinsky@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Convert Mono.Linker.Tests from MSTest to xUnit - #126613

Merged
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit
Apr 21, 2026
Merged

Convert Mono.Linker.Tests from MSTest to xUnit#126613
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit

Conversation

CopilotAI commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Description

Converts Mono.Linker.Tests — the last remaining MSTest project in illink — to xUnit, aligning it with the rest of dotnet/runtime's test infrastructure.

Changes

Project setup

  • Remove <TestRunnerName>MSTest</TestRunnerName> from csproj; xUnit is now the default via Arcade
  • Add explicit Microsoft.DotNet.XUnitExtensions PackageReference (not auto-imported for src/tools/illink/)

Test data / dynamic data

  • TestDatabase: return types changed from IEnumerable<TestDataRow<TestCase>>IEnumerable<object[]>; MSTestCasesBySuiteNameTestCasesBySuiteName
  • MemberAssertionsCollector.GetMemberAssertionsData: returns IEnumerable<object[]> instead of IEnumerable<TestDataRow<T>>
  • TestCase.ToString() added to emit DisplayName for readable xUnit test names
  • [DynamicData(..., new object[] { typeof(T) })] with parameterized data source → parameterless [MemberData] method per test class

Test attributes

  • [TestClass] removed; [TestMethod][Fact]; [DataRow][InlineData]
  • [DynamicData][MemberData]; suite tests use [ConditionalTheory] (not plain [Theory]) to support dynamic skipping
  • [TestInitialize] → constructor; [DoNotParallelize] removed (MSTest-only concept)
  • AssemblyInfo.cs parallel configuration removed

Dynamic skipping
MSTest's Assert.Inconclusive doesn't translate directly to xUnit's skip mechanism. Most runtime skipping uses ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions. For CodegenAnnotationTests, declarative [SkipOnTargetFramework] attributes are used instead:

// Before (MSTest)if(Environment.OSVersion.Platform==PlatformID.Win32NT)Assert.Inconclusive("These tests are not valid when trimming .NET Framework");
#if NETAssert.Inconclusive("These tests are not valid when trimming .NET Core");
#endif
// After (xUnit)[SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp,"These tests are not valid when trimming .NET Core")][SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework,"These tests are not valid when trimming .NET Framework")]

The [SkipOnTargetFramework] attribute adds a Category=Failing trait via SkipOnTargetFrameworkDiscoverer, which is automatically filtered out by the CI infrastructure via _withoutCategories=failing in eng/testing/tests.targets.

Assertions (all files)

MSTestxUnit
Assert.AreEqual(a, b)Assert.Equal(a, b)
Assert.IsTrue/IsFalseAssert.True/False
Assert.IsNull/IsNotNullAssert.Null/NotNullAssert.True(x is not null, $"...") with diagnostic message
Assert.IsEmptyAssert.Empty / Assert.True(x.Length == 0, message)
Assert.Inconclusivethrow new SkipTestException
CollectionAssert.AreEquivalentAssert.Equal(expected.OrderBy(...), actual.OrderBy(...))
UnitTestAssertExceptionXunitException

Assertion quality fixes

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") to preserve diagnostic context; changed Single(...)SingleOrDefault(...) so the null check is actually reachable and surfaces the descriptive message on failure
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") replacing the redundant Assert.NotNull
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes linker output when exit codes don't match
  • ResultChecker.cs:834-835: corrected Assert.Equal argument order so expected is first and actual is second, producing correct diff output on failure
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, ...) including the linked assembly name and missing type list
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, ...) including the type name and missing member names
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines in the failure message
  • TestFrameworkRulesAndConventions.cs:42: replaced Assert.Empty(missing) with Assert.True(missing.Length == 0, $"Could not find a test case type for the following source file(s): ...") to preserve the explanatory failure message
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all MVID assertions

Miscellaneous fixes

  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit; to prevent CS8019 build warning
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace on blank line

Testing

Build: dotnet build src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Run: dotnet test src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Original prompt Chronological Review: 1. User requested converting illink tests from MSTest to xUnit 2. Prior work (summarized): Explored repo, found only Mono.Linker.Tests uses MSTest, ran baseline build, delegated bulk conversion to sub-agent (22 files), committed as 9425ffe083f 3. Prior work: Discovered 31 test failures due to dynamic skip mechanism, fixed with ConditionalTheory + SkipTestException (3 files) 4. Prior work: Started code review skill, gathered diff context 5. Current session: Completed code review and amended commit

Recent Commands Analysis:

  • Checked AreEqual→Assert.True conversions in ResultChecker.cs diff
  • Verified SkipTestException and ConditionalTheory usage in TestSuites.cs
  • Found only 1 instance of Assert.True(a == b, msg) pattern at line 259
  • Wrote code review (LGTM with one 💡 suggestion about Assert.NotNull message loss)
  • Staged 3 uncommitted files and amended commit
  • Final commit: 2a2d31b5610
  • Called task_complete

Progress: Everything is DONE. The task is complete.

1. Conversation Overview: - Primary Objectives: "Update the illink tests from MSTest to xunit." - Session Context: User requested converting `Mono.Linker.Tests` (the last MSTest test project in illink) to xUnit. Work progressed through exploration → bulk conversion → fixing dynamic skip failures → code review → final commit. - User Intent Evolution: Single clear request, no changes in direction.
  1. Technical Foundation:

    • xUnit v2 runner (2.9.x) with xUnit v3 assertions (Microsoft.DotNet.XUnitAssert 3.2.2-beta) — the dotnet/runtime standard setup
    • Arcade SDK: Tests.targets defaults TestRunnerName=XUnit; removing <TestRunnerName>MSTest</TestRunnerName> switches to xUnit packages automatically
    • Microsoft.DotNet.XUnitExtensions: Provides ConditionalTheory/ConditionalFact and SkipTestException for dynamic test skipping. NOT auto-imported for src/tools/illink/ (only for src/libraries/ and src/tests/), so needed explicit PackageReference.
    • SDK version: 11.0.100-preview.3.26170.106
  2. Codebase Status:

    • All 22 files committed in single commit 2a2d31b5610 on branch illink-xunit
    • Key files:
      • Mono.Linker.Tests.csproj — Removed TestRunnerName=MSTest, added Microsoft.DotNet.XUnitExtensions PackageReference
      • TestSuites.cs — 47 [ConditionalTheory] methods, [MemberData] instead of [DynamicData], SkipTestException for dynamic skips
      • TestRunner.csIgnoreTest throws SkipTestException, catches XunitException instead of UnitTestAssertException
      • TestDatabase.csobject[] instead of TestDataRow<TestCase>, renamed MSTestCasesBySuiteNameTestCasesBySuiteName
      • TestCase.cs — Added ToString() override returning DisplayName
      • ResultChecker.cs — Dozens of assertion conversions; 3 Assert.NotNull calls lost custom messages; 1 Assert.True(a == b, msg) pattern at line 259
      • 16 other files with attribute/using/assertion conversions
  3. Problem Resolution:

    • 31 test failures from dynamic skip: SkipException.ForSkip() (Xunit.Sdk) produces $XunitDynamicSkip$ prefix not caught by xUnit v2 runner. Fixed by using ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions.
    • Assert.NotNull message loss: xUnit's Assert.NotNull doesn't accept messages. Accepted as known tradeoff (3 instances).
    • Assert.AreEqual with message: One instance converted to Assert.True(a == b, msg) to preserve diagnostic output including linker output.
  4. Progress Tracking:

    • ✅ Baseline build succeeded
    • ✅ All 22 files converted from MSTest to xUnit
    • ✅ Dynamic skip mechanism fixed (ConditionalTheory + SkipTestException)
    • ✅ Build passes (0 errors, 0 warnings)
    • ✅ Tests pass: 0 Failed, 1125 Passed, 31 Skipped
    • ✅ Code review completed (LGTM, one 💡 suggestion)
    • ✅ All changes committed in single amended commit 2a2d31b5610
    • task_complete called
  5. Active Work State:

    • Task is COMPLETE.task_complete was called with full summary.
  6. Recent Operations:

    • Last Agent Commands:
      1. bash: git diff HEAD~1 -- ResultChecker.cs | grep "Assert.AreEqual.*Assert.True" — no results
      2. bash: git diff HEAD~1 -- ResultChecker.cs | grep -A1 "Assert.AreEqual" — found the 3 AreEqual conversions including the Assert.True pattern
      3. bash: grep "Assert.True.*==\|Assert.False.*!=" ResultChecker.cs — confirmed only 1 instance at line 259
      4. bash: grep "SkipTestException" TestSuites.cs — confirmed 2 usages
      5. Wrote code review output (LGTM with findings)
      6. bash: git add -A && git diff --cached --stat — staged 3 files (csproj, TestSuites.cs, TestRunner.cs)
      7. bash: git commit --amend — amended commit to 2a2d31b5610 with 22 files, 369 insertions, 396 deletions
      8. bash: git log --oneline -2 — verified commit on illink-xunit branch
      9. `ta...

Created from Copilot CLI via the copilot delegate command.

CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:46
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Apr 7, 2026
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:50
CopilotAI changed the title [WIP] Convert illink tests from MSTest to xUnitConvert Mono.Linker.Tests from MSTest to xUnitApr 7, 2026
CopilotAI requested a review from sbomerApril 7, 2026 19:53
…degenAnnotationTests
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/bec1ccc1-bf1f-4de2-bf2d-ab95d9017603
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 20:30
CopilotAI requested a review from jkoritzinskyApril 7, 2026 20:31
@jkotasjkotas added area-Tools-ILLink .NET linker development as well as trimming analyzers and removed area-Infrastructure labels Apr 7, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

CopilotAI 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 28 out of 28 changed files in this pull request and generated 7 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/coreclr/tools/ILTrim.Tests/TestCasesRunner/TestRunner.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

…sert.Equal arg order
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ffe27803-c5fc-4c06-b630-8a71e5c38653
Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>

CopilotAI commented Apr 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in b0154e0:

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") with descriptive messages
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes the linker output
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all three MVID assertions
  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit;
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace

CopilotAI 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 28 out of 28 changed files in this pull request and generated 4 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

CopilotAI commented Apr 17, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in d5455d5:

  • ResultChecker.cs:73: changed Single(...)SingleOrDefault(...) so the subsequent Assert.True(assemblyRef is not null, ...) is reachable and shows the helpful message
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") with context replacing the redundant Assert.NotNull
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, $"Missing types in linked assembly '{...}': ...")
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, $"Missing members in type '{...}' in linked assembly '{...}': ...")

@sbomer
sbomer enabled auto-merge (squash) April 21, 2026 16:49
@sbomer
sbomer merged commit 416af5f into mainApr 21, 2026
130 of 137 checks passed
@sbomer
sbomer deleted the copilot/convert-illink-tests-to-xunit branch April 21, 2026 16:50
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 22, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly frameworkNO-MERGEThe PR is not ready for merge yet (see discussion for detailed reasons)

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants

@MichalStrehovsky@sbomer@jkoritzinsky@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Convert Mono.Linker.Tests from MSTest to xUnit - #126613

Merged
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit
Apr 21, 2026
Merged

Convert Mono.Linker.Tests from MSTest to xUnit#126613
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit

Conversation

CopilotAI commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Description

Converts Mono.Linker.Tests — the last remaining MSTest project in illink — to xUnit, aligning it with the rest of dotnet/runtime's test infrastructure.

Changes

Project setup

  • Remove <TestRunnerName>MSTest</TestRunnerName> from csproj; xUnit is now the default via Arcade
  • Add explicit Microsoft.DotNet.XUnitExtensions PackageReference (not auto-imported for src/tools/illink/)

Test data / dynamic data

  • TestDatabase: return types changed from IEnumerable<TestDataRow<TestCase>>IEnumerable<object[]>; MSTestCasesBySuiteNameTestCasesBySuiteName
  • MemberAssertionsCollector.GetMemberAssertionsData: returns IEnumerable<object[]> instead of IEnumerable<TestDataRow<T>>
  • TestCase.ToString() added to emit DisplayName for readable xUnit test names
  • [DynamicData(..., new object[] { typeof(T) })] with parameterized data source → parameterless [MemberData] method per test class

Test attributes

  • [TestClass] removed; [TestMethod][Fact]; [DataRow][InlineData]
  • [DynamicData][MemberData]; suite tests use [ConditionalTheory] (not plain [Theory]) to support dynamic skipping
  • [TestInitialize] → constructor; [DoNotParallelize] removed (MSTest-only concept)
  • AssemblyInfo.cs parallel configuration removed

Dynamic skipping
MSTest's Assert.Inconclusive doesn't translate directly to xUnit's skip mechanism. Most runtime skipping uses ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions. For CodegenAnnotationTests, declarative [SkipOnTargetFramework] attributes are used instead:

// Before (MSTest)if(Environment.OSVersion.Platform==PlatformID.Win32NT)Assert.Inconclusive("These tests are not valid when trimming .NET Framework");
#if NETAssert.Inconclusive("These tests are not valid when trimming .NET Core");
#endif
// After (xUnit)[SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp,"These tests are not valid when trimming .NET Core")][SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework,"These tests are not valid when trimming .NET Framework")]

The [SkipOnTargetFramework] attribute adds a Category=Failing trait via SkipOnTargetFrameworkDiscoverer, which is automatically filtered out by the CI infrastructure via _withoutCategories=failing in eng/testing/tests.targets.

Assertions (all files)

MSTestxUnit
Assert.AreEqual(a, b)Assert.Equal(a, b)
Assert.IsTrue/IsFalseAssert.True/False
Assert.IsNull/IsNotNullAssert.Null/NotNullAssert.True(x is not null, $"...") with diagnostic message
Assert.IsEmptyAssert.Empty / Assert.True(x.Length == 0, message)
Assert.Inconclusivethrow new SkipTestException
CollectionAssert.AreEquivalentAssert.Equal(expected.OrderBy(...), actual.OrderBy(...))
UnitTestAssertExceptionXunitException

Assertion quality fixes

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") to preserve diagnostic context; changed Single(...)SingleOrDefault(...) so the null check is actually reachable and surfaces the descriptive message on failure
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") replacing the redundant Assert.NotNull
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes linker output when exit codes don't match
  • ResultChecker.cs:834-835: corrected Assert.Equal argument order so expected is first and actual is second, producing correct diff output on failure
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, ...) including the linked assembly name and missing type list
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, ...) including the type name and missing member names
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines in the failure message
  • TestFrameworkRulesAndConventions.cs:42: replaced Assert.Empty(missing) with Assert.True(missing.Length == 0, $"Could not find a test case type for the following source file(s): ...") to preserve the explanatory failure message
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all MVID assertions

Miscellaneous fixes

  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit; to prevent CS8019 build warning
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace on blank line

Testing

Build: dotnet build src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Run: dotnet test src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Original prompt Chronological Review: 1. User requested converting illink tests from MSTest to xUnit 2. Prior work (summarized): Explored repo, found only Mono.Linker.Tests uses MSTest, ran baseline build, delegated bulk conversion to sub-agent (22 files), committed as 9425ffe083f 3. Prior work: Discovered 31 test failures due to dynamic skip mechanism, fixed with ConditionalTheory + SkipTestException (3 files) 4. Prior work: Started code review skill, gathered diff context 5. Current session: Completed code review and amended commit

Recent Commands Analysis:

  • Checked AreEqual→Assert.True conversions in ResultChecker.cs diff
  • Verified SkipTestException and ConditionalTheory usage in TestSuites.cs
  • Found only 1 instance of Assert.True(a == b, msg) pattern at line 259
  • Wrote code review (LGTM with one 💡 suggestion about Assert.NotNull message loss)
  • Staged 3 uncommitted files and amended commit
  • Final commit: 2a2d31b5610
  • Called task_complete

Progress: Everything is DONE. The task is complete.

1. Conversation Overview: - Primary Objectives: "Update the illink tests from MSTest to xunit." - Session Context: User requested converting `Mono.Linker.Tests` (the last MSTest test project in illink) to xUnit. Work progressed through exploration → bulk conversion → fixing dynamic skip failures → code review → final commit. - User Intent Evolution: Single clear request, no changes in direction.
  1. Technical Foundation:

    • xUnit v2 runner (2.9.x) with xUnit v3 assertions (Microsoft.DotNet.XUnitAssert 3.2.2-beta) — the dotnet/runtime standard setup
    • Arcade SDK: Tests.targets defaults TestRunnerName=XUnit; removing <TestRunnerName>MSTest</TestRunnerName> switches to xUnit packages automatically
    • Microsoft.DotNet.XUnitExtensions: Provides ConditionalTheory/ConditionalFact and SkipTestException for dynamic test skipping. NOT auto-imported for src/tools/illink/ (only for src/libraries/ and src/tests/), so needed explicit PackageReference.
    • SDK version: 11.0.100-preview.3.26170.106
  2. Codebase Status:

    • All 22 files committed in single commit 2a2d31b5610 on branch illink-xunit
    • Key files:
      • Mono.Linker.Tests.csproj — Removed TestRunnerName=MSTest, added Microsoft.DotNet.XUnitExtensions PackageReference
      • TestSuites.cs — 47 [ConditionalTheory] methods, [MemberData] instead of [DynamicData], SkipTestException for dynamic skips
      • TestRunner.csIgnoreTest throws SkipTestException, catches XunitException instead of UnitTestAssertException
      • TestDatabase.csobject[] instead of TestDataRow<TestCase>, renamed MSTestCasesBySuiteNameTestCasesBySuiteName
      • TestCase.cs — Added ToString() override returning DisplayName
      • ResultChecker.cs — Dozens of assertion conversions; 3 Assert.NotNull calls lost custom messages; 1 Assert.True(a == b, msg) pattern at line 259
      • 16 other files with attribute/using/assertion conversions
  3. Problem Resolution:

    • 31 test failures from dynamic skip: SkipException.ForSkip() (Xunit.Sdk) produces $XunitDynamicSkip$ prefix not caught by xUnit v2 runner. Fixed by using ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions.
    • Assert.NotNull message loss: xUnit's Assert.NotNull doesn't accept messages. Accepted as known tradeoff (3 instances).
    • Assert.AreEqual with message: One instance converted to Assert.True(a == b, msg) to preserve diagnostic output including linker output.
  4. Progress Tracking:

    • ✅ Baseline build succeeded
    • ✅ All 22 files converted from MSTest to xUnit
    • ✅ Dynamic skip mechanism fixed (ConditionalTheory + SkipTestException)
    • ✅ Build passes (0 errors, 0 warnings)
    • ✅ Tests pass: 0 Failed, 1125 Passed, 31 Skipped
    • ✅ Code review completed (LGTM, one 💡 suggestion)
    • ✅ All changes committed in single amended commit 2a2d31b5610
    • task_complete called
  5. Active Work State:

    • Task is COMPLETE.task_complete was called with full summary.
  6. Recent Operations:

    • Last Agent Commands:
      1. bash: git diff HEAD~1 -- ResultChecker.cs | grep "Assert.AreEqual.*Assert.True" — no results
      2. bash: git diff HEAD~1 -- ResultChecker.cs | grep -A1 "Assert.AreEqual" — found the 3 AreEqual conversions including the Assert.True pattern
      3. bash: grep "Assert.True.*==\|Assert.False.*!=" ResultChecker.cs — confirmed only 1 instance at line 259
      4. bash: grep "SkipTestException" TestSuites.cs — confirmed 2 usages
      5. Wrote code review output (LGTM with findings)
      6. bash: git add -A && git diff --cached --stat — staged 3 files (csproj, TestSuites.cs, TestRunner.cs)
      7. bash: git commit --amend — amended commit to 2a2d31b5610 with 22 files, 369 insertions, 396 deletions
      8. bash: git log --oneline -2 — verified commit on illink-xunit branch
      9. `ta...

Created from Copilot CLI via the copilot delegate command.

CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:46
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Apr 7, 2026
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:50
CopilotAI changed the title [WIP] Convert illink tests from MSTest to xUnitConvert Mono.Linker.Tests from MSTest to xUnitApr 7, 2026
CopilotAI requested a review from sbomerApril 7, 2026 19:53
…degenAnnotationTests
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/bec1ccc1-bf1f-4de2-bf2d-ab95d9017603
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 20:30
CopilotAI requested a review from jkoritzinskyApril 7, 2026 20:31
@jkotasjkotas added area-Tools-ILLink .NET linker development as well as trimming analyzers and removed area-Infrastructure labels Apr 7, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

CopilotAI 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 28 out of 28 changed files in this pull request and generated 7 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/coreclr/tools/ILTrim.Tests/TestCasesRunner/TestRunner.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

…sert.Equal arg order
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ffe27803-c5fc-4c06-b630-8a71e5c38653
Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>

CopilotAI commented Apr 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in b0154e0:

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") with descriptive messages
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes the linker output
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all three MVID assertions
  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit;
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace

CopilotAI 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 28 out of 28 changed files in this pull request and generated 4 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

CopilotAI commented Apr 17, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in d5455d5:

  • ResultChecker.cs:73: changed Single(...)SingleOrDefault(...) so the subsequent Assert.True(assemblyRef is not null, ...) is reachable and shows the helpful message
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") with context replacing the redundant Assert.NotNull
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, $"Missing types in linked assembly '{...}': ...")
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, $"Missing members in type '{...}' in linked assembly '{...}': ...")

@sbomer
sbomer enabled auto-merge (squash) April 21, 2026 16:49
@sbomer
sbomer merged commit 416af5f into mainApr 21, 2026
130 of 137 checks passed
@sbomer
sbomer deleted the copilot/convert-illink-tests-to-xunit branch April 21, 2026 16:50
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 22, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly frameworkNO-MERGEThe PR is not ready for merge yet (see discussion for detailed reasons)

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants

@MichalStrehovsky@sbomer@jkoritzinsky@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Convert Mono.Linker.Tests from MSTest to xUnit - #126613

Merged
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit
Apr 21, 2026
Merged

Convert Mono.Linker.Tests from MSTest to xUnit#126613
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit

Conversation

CopilotAI commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Description

Converts Mono.Linker.Tests — the last remaining MSTest project in illink — to xUnit, aligning it with the rest of dotnet/runtime's test infrastructure.

Changes

Project setup

  • Remove <TestRunnerName>MSTest</TestRunnerName> from csproj; xUnit is now the default via Arcade
  • Add explicit Microsoft.DotNet.XUnitExtensions PackageReference (not auto-imported for src/tools/illink/)

Test data / dynamic data

  • TestDatabase: return types changed from IEnumerable<TestDataRow<TestCase>>IEnumerable<object[]>; MSTestCasesBySuiteNameTestCasesBySuiteName
  • MemberAssertionsCollector.GetMemberAssertionsData: returns IEnumerable<object[]> instead of IEnumerable<TestDataRow<T>>
  • TestCase.ToString() added to emit DisplayName for readable xUnit test names
  • [DynamicData(..., new object[] { typeof(T) })] with parameterized data source → parameterless [MemberData] method per test class

Test attributes

  • [TestClass] removed; [TestMethod][Fact]; [DataRow][InlineData]
  • [DynamicData][MemberData]; suite tests use [ConditionalTheory] (not plain [Theory]) to support dynamic skipping
  • [TestInitialize] → constructor; [DoNotParallelize] removed (MSTest-only concept)
  • AssemblyInfo.cs parallel configuration removed

Dynamic skipping
MSTest's Assert.Inconclusive doesn't translate directly to xUnit's skip mechanism. Most runtime skipping uses ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions. For CodegenAnnotationTests, declarative [SkipOnTargetFramework] attributes are used instead:

// Before (MSTest)if(Environment.OSVersion.Platform==PlatformID.Win32NT)Assert.Inconclusive("These tests are not valid when trimming .NET Framework");
#if NETAssert.Inconclusive("These tests are not valid when trimming .NET Core");
#endif
// After (xUnit)[SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp,"These tests are not valid when trimming .NET Core")][SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework,"These tests are not valid when trimming .NET Framework")]

The [SkipOnTargetFramework] attribute adds a Category=Failing trait via SkipOnTargetFrameworkDiscoverer, which is automatically filtered out by the CI infrastructure via _withoutCategories=failing in eng/testing/tests.targets.

Assertions (all files)

MSTestxUnit
Assert.AreEqual(a, b)Assert.Equal(a, b)
Assert.IsTrue/IsFalseAssert.True/False
Assert.IsNull/IsNotNullAssert.Null/NotNullAssert.True(x is not null, $"...") with diagnostic message
Assert.IsEmptyAssert.Empty / Assert.True(x.Length == 0, message)
Assert.Inconclusivethrow new SkipTestException
CollectionAssert.AreEquivalentAssert.Equal(expected.OrderBy(...), actual.OrderBy(...))
UnitTestAssertExceptionXunitException

Assertion quality fixes

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") to preserve diagnostic context; changed Single(...)SingleOrDefault(...) so the null check is actually reachable and surfaces the descriptive message on failure
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") replacing the redundant Assert.NotNull
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes linker output when exit codes don't match
  • ResultChecker.cs:834-835: corrected Assert.Equal argument order so expected is first and actual is second, producing correct diff output on failure
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, ...) including the linked assembly name and missing type list
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, ...) including the type name and missing member names
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines in the failure message
  • TestFrameworkRulesAndConventions.cs:42: replaced Assert.Empty(missing) with Assert.True(missing.Length == 0, $"Could not find a test case type for the following source file(s): ...") to preserve the explanatory failure message
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all MVID assertions

Miscellaneous fixes

  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit; to prevent CS8019 build warning
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace on blank line

Testing

Build: dotnet build src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Run: dotnet test src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Original prompt Chronological Review: 1. User requested converting illink tests from MSTest to xUnit 2. Prior work (summarized): Explored repo, found only Mono.Linker.Tests uses MSTest, ran baseline build, delegated bulk conversion to sub-agent (22 files), committed as 9425ffe083f 3. Prior work: Discovered 31 test failures due to dynamic skip mechanism, fixed with ConditionalTheory + SkipTestException (3 files) 4. Prior work: Started code review skill, gathered diff context 5. Current session: Completed code review and amended commit

Recent Commands Analysis:

  • Checked AreEqual→Assert.True conversions in ResultChecker.cs diff
  • Verified SkipTestException and ConditionalTheory usage in TestSuites.cs
  • Found only 1 instance of Assert.True(a == b, msg) pattern at line 259
  • Wrote code review (LGTM with one 💡 suggestion about Assert.NotNull message loss)
  • Staged 3 uncommitted files and amended commit
  • Final commit: 2a2d31b5610
  • Called task_complete

Progress: Everything is DONE. The task is complete.

1. Conversation Overview: - Primary Objectives: "Update the illink tests from MSTest to xunit." - Session Context: User requested converting `Mono.Linker.Tests` (the last MSTest test project in illink) to xUnit. Work progressed through exploration → bulk conversion → fixing dynamic skip failures → code review → final commit. - User Intent Evolution: Single clear request, no changes in direction.
  1. Technical Foundation:

    • xUnit v2 runner (2.9.x) with xUnit v3 assertions (Microsoft.DotNet.XUnitAssert 3.2.2-beta) — the dotnet/runtime standard setup
    • Arcade SDK: Tests.targets defaults TestRunnerName=XUnit; removing <TestRunnerName>MSTest</TestRunnerName> switches to xUnit packages automatically
    • Microsoft.DotNet.XUnitExtensions: Provides ConditionalTheory/ConditionalFact and SkipTestException for dynamic test skipping. NOT auto-imported for src/tools/illink/ (only for src/libraries/ and src/tests/), so needed explicit PackageReference.
    • SDK version: 11.0.100-preview.3.26170.106
  2. Codebase Status:

    • All 22 files committed in single commit 2a2d31b5610 on branch illink-xunit
    • Key files:
      • Mono.Linker.Tests.csproj — Removed TestRunnerName=MSTest, added Microsoft.DotNet.XUnitExtensions PackageReference
      • TestSuites.cs — 47 [ConditionalTheory] methods, [MemberData] instead of [DynamicData], SkipTestException for dynamic skips
      • TestRunner.csIgnoreTest throws SkipTestException, catches XunitException instead of UnitTestAssertException
      • TestDatabase.csobject[] instead of TestDataRow<TestCase>, renamed MSTestCasesBySuiteNameTestCasesBySuiteName
      • TestCase.cs — Added ToString() override returning DisplayName
      • ResultChecker.cs — Dozens of assertion conversions; 3 Assert.NotNull calls lost custom messages; 1 Assert.True(a == b, msg) pattern at line 259
      • 16 other files with attribute/using/assertion conversions
  3. Problem Resolution:

    • 31 test failures from dynamic skip: SkipException.ForSkip() (Xunit.Sdk) produces $XunitDynamicSkip$ prefix not caught by xUnit v2 runner. Fixed by using ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions.
    • Assert.NotNull message loss: xUnit's Assert.NotNull doesn't accept messages. Accepted as known tradeoff (3 instances).
    • Assert.AreEqual with message: One instance converted to Assert.True(a == b, msg) to preserve diagnostic output including linker output.
  4. Progress Tracking:

    • ✅ Baseline build succeeded
    • ✅ All 22 files converted from MSTest to xUnit
    • ✅ Dynamic skip mechanism fixed (ConditionalTheory + SkipTestException)
    • ✅ Build passes (0 errors, 0 warnings)
    • ✅ Tests pass: 0 Failed, 1125 Passed, 31 Skipped
    • ✅ Code review completed (LGTM, one 💡 suggestion)
    • ✅ All changes committed in single amended commit 2a2d31b5610
    • task_complete called
  5. Active Work State:

    • Task is COMPLETE.task_complete was called with full summary.
  6. Recent Operations:

    • Last Agent Commands:
      1. bash: git diff HEAD~1 -- ResultChecker.cs | grep "Assert.AreEqual.*Assert.True" — no results
      2. bash: git diff HEAD~1 -- ResultChecker.cs | grep -A1 "Assert.AreEqual" — found the 3 AreEqual conversions including the Assert.True pattern
      3. bash: grep "Assert.True.*==\|Assert.False.*!=" ResultChecker.cs — confirmed only 1 instance at line 259
      4. bash: grep "SkipTestException" TestSuites.cs — confirmed 2 usages
      5. Wrote code review output (LGTM with findings)
      6. bash: git add -A && git diff --cached --stat — staged 3 files (csproj, TestSuites.cs, TestRunner.cs)
      7. bash: git commit --amend — amended commit to 2a2d31b5610 with 22 files, 369 insertions, 396 deletions
      8. bash: git log --oneline -2 — verified commit on illink-xunit branch
      9. `ta...

Created from Copilot CLI via the copilot delegate command.

CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:46
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Apr 7, 2026
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:50
CopilotAI changed the title [WIP] Convert illink tests from MSTest to xUnitConvert Mono.Linker.Tests from MSTest to xUnitApr 7, 2026
CopilotAI requested a review from sbomerApril 7, 2026 19:53
…degenAnnotationTests
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/bec1ccc1-bf1f-4de2-bf2d-ab95d9017603
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 20:30
CopilotAI requested a review from jkoritzinskyApril 7, 2026 20:31
@jkotasjkotas added area-Tools-ILLink .NET linker development as well as trimming analyzers and removed area-Infrastructure labels Apr 7, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

CopilotAI 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 28 out of 28 changed files in this pull request and generated 7 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/coreclr/tools/ILTrim.Tests/TestCasesRunner/TestRunner.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

…sert.Equal arg order
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ffe27803-c5fc-4c06-b630-8a71e5c38653
Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>

CopilotAI commented Apr 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in b0154e0:

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") with descriptive messages
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes the linker output
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all three MVID assertions
  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit;
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace

CopilotAI 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 28 out of 28 changed files in this pull request and generated 4 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

CopilotAI commented Apr 17, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in d5455d5:

  • ResultChecker.cs:73: changed Single(...)SingleOrDefault(...) so the subsequent Assert.True(assemblyRef is not null, ...) is reachable and shows the helpful message
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") with context replacing the redundant Assert.NotNull
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, $"Missing types in linked assembly '{...}': ...")
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, $"Missing members in type '{...}' in linked assembly '{...}': ...")

@sbomer
sbomer enabled auto-merge (squash) April 21, 2026 16:49
@sbomer
sbomer merged commit 416af5f into mainApr 21, 2026
130 of 137 checks passed
@sbomer
sbomer deleted the copilot/convert-illink-tests-to-xunit branch April 21, 2026 16:50
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 22, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly frameworkNO-MERGEThe PR is not ready for merge yet (see discussion for detailed reasons)

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants

@MichalStrehovsky@sbomer@jkoritzinsky@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Convert Mono.Linker.Tests from MSTest to xUnit - #126613

Merged
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit
Apr 21, 2026
Merged

Convert Mono.Linker.Tests from MSTest to xUnit#126613
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit

Conversation

CopilotAI commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Description

Converts Mono.Linker.Tests — the last remaining MSTest project in illink — to xUnit, aligning it with the rest of dotnet/runtime's test infrastructure.

Changes

Project setup

  • Remove <TestRunnerName>MSTest</TestRunnerName> from csproj; xUnit is now the default via Arcade
  • Add explicit Microsoft.DotNet.XUnitExtensions PackageReference (not auto-imported for src/tools/illink/)

Test data / dynamic data

  • TestDatabase: return types changed from IEnumerable<TestDataRow<TestCase>>IEnumerable<object[]>; MSTestCasesBySuiteNameTestCasesBySuiteName
  • MemberAssertionsCollector.GetMemberAssertionsData: returns IEnumerable<object[]> instead of IEnumerable<TestDataRow<T>>
  • TestCase.ToString() added to emit DisplayName for readable xUnit test names
  • [DynamicData(..., new object[] { typeof(T) })] with parameterized data source → parameterless [MemberData] method per test class

Test attributes

  • [TestClass] removed; [TestMethod][Fact]; [DataRow][InlineData]
  • [DynamicData][MemberData]; suite tests use [ConditionalTheory] (not plain [Theory]) to support dynamic skipping
  • [TestInitialize] → constructor; [DoNotParallelize] removed (MSTest-only concept)
  • AssemblyInfo.cs parallel configuration removed

Dynamic skipping
MSTest's Assert.Inconclusive doesn't translate directly to xUnit's skip mechanism. Most runtime skipping uses ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions. For CodegenAnnotationTests, declarative [SkipOnTargetFramework] attributes are used instead:

// Before (MSTest)if(Environment.OSVersion.Platform==PlatformID.Win32NT)Assert.Inconclusive("These tests are not valid when trimming .NET Framework");
#if NETAssert.Inconclusive("These tests are not valid when trimming .NET Core");
#endif
// After (xUnit)[SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp,"These tests are not valid when trimming .NET Core")][SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework,"These tests are not valid when trimming .NET Framework")]

The [SkipOnTargetFramework] attribute adds a Category=Failing trait via SkipOnTargetFrameworkDiscoverer, which is automatically filtered out by the CI infrastructure via _withoutCategories=failing in eng/testing/tests.targets.

Assertions (all files)

MSTestxUnit
Assert.AreEqual(a, b)Assert.Equal(a, b)
Assert.IsTrue/IsFalseAssert.True/False
Assert.IsNull/IsNotNullAssert.Null/NotNullAssert.True(x is not null, $"...") with diagnostic message
Assert.IsEmptyAssert.Empty / Assert.True(x.Length == 0, message)
Assert.Inconclusivethrow new SkipTestException
CollectionAssert.AreEquivalentAssert.Equal(expected.OrderBy(...), actual.OrderBy(...))
UnitTestAssertExceptionXunitException

Assertion quality fixes

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") to preserve diagnostic context; changed Single(...)SingleOrDefault(...) so the null check is actually reachable and surfaces the descriptive message on failure
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") replacing the redundant Assert.NotNull
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes linker output when exit codes don't match
  • ResultChecker.cs:834-835: corrected Assert.Equal argument order so expected is first and actual is second, producing correct diff output on failure
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, ...) including the linked assembly name and missing type list
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, ...) including the type name and missing member names
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines in the failure message
  • TestFrameworkRulesAndConventions.cs:42: replaced Assert.Empty(missing) with Assert.True(missing.Length == 0, $"Could not find a test case type for the following source file(s): ...") to preserve the explanatory failure message
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all MVID assertions

Miscellaneous fixes

  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit; to prevent CS8019 build warning
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace on blank line

Testing

Build: dotnet build src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Run: dotnet test src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Original prompt Chronological Review: 1. User requested converting illink tests from MSTest to xUnit 2. Prior work (summarized): Explored repo, found only Mono.Linker.Tests uses MSTest, ran baseline build, delegated bulk conversion to sub-agent (22 files), committed as 9425ffe083f 3. Prior work: Discovered 31 test failures due to dynamic skip mechanism, fixed with ConditionalTheory + SkipTestException (3 files) 4. Prior work: Started code review skill, gathered diff context 5. Current session: Completed code review and amended commit

Recent Commands Analysis:

  • Checked AreEqual→Assert.True conversions in ResultChecker.cs diff
  • Verified SkipTestException and ConditionalTheory usage in TestSuites.cs
  • Found only 1 instance of Assert.True(a == b, msg) pattern at line 259
  • Wrote code review (LGTM with one 💡 suggestion about Assert.NotNull message loss)
  • Staged 3 uncommitted files and amended commit
  • Final commit: 2a2d31b5610
  • Called task_complete

Progress: Everything is DONE. The task is complete.

1. Conversation Overview: - Primary Objectives: "Update the illink tests from MSTest to xunit." - Session Context: User requested converting `Mono.Linker.Tests` (the last MSTest test project in illink) to xUnit. Work progressed through exploration → bulk conversion → fixing dynamic skip failures → code review → final commit. - User Intent Evolution: Single clear request, no changes in direction.
  1. Technical Foundation:

    • xUnit v2 runner (2.9.x) with xUnit v3 assertions (Microsoft.DotNet.XUnitAssert 3.2.2-beta) — the dotnet/runtime standard setup
    • Arcade SDK: Tests.targets defaults TestRunnerName=XUnit; removing <TestRunnerName>MSTest</TestRunnerName> switches to xUnit packages automatically
    • Microsoft.DotNet.XUnitExtensions: Provides ConditionalTheory/ConditionalFact and SkipTestException for dynamic test skipping. NOT auto-imported for src/tools/illink/ (only for src/libraries/ and src/tests/), so needed explicit PackageReference.
    • SDK version: 11.0.100-preview.3.26170.106
  2. Codebase Status:

    • All 22 files committed in single commit 2a2d31b5610 on branch illink-xunit
    • Key files:
      • Mono.Linker.Tests.csproj — Removed TestRunnerName=MSTest, added Microsoft.DotNet.XUnitExtensions PackageReference
      • TestSuites.cs — 47 [ConditionalTheory] methods, [MemberData] instead of [DynamicData], SkipTestException for dynamic skips
      • TestRunner.csIgnoreTest throws SkipTestException, catches XunitException instead of UnitTestAssertException
      • TestDatabase.csobject[] instead of TestDataRow<TestCase>, renamed MSTestCasesBySuiteNameTestCasesBySuiteName
      • TestCase.cs — Added ToString() override returning DisplayName
      • ResultChecker.cs — Dozens of assertion conversions; 3 Assert.NotNull calls lost custom messages; 1 Assert.True(a == b, msg) pattern at line 259
      • 16 other files with attribute/using/assertion conversions
  3. Problem Resolution:

    • 31 test failures from dynamic skip: SkipException.ForSkip() (Xunit.Sdk) produces $XunitDynamicSkip$ prefix not caught by xUnit v2 runner. Fixed by using ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions.
    • Assert.NotNull message loss: xUnit's Assert.NotNull doesn't accept messages. Accepted as known tradeoff (3 instances).
    • Assert.AreEqual with message: One instance converted to Assert.True(a == b, msg) to preserve diagnostic output including linker output.
  4. Progress Tracking:

    • ✅ Baseline build succeeded
    • ✅ All 22 files converted from MSTest to xUnit
    • ✅ Dynamic skip mechanism fixed (ConditionalTheory + SkipTestException)
    • ✅ Build passes (0 errors, 0 warnings)
    • ✅ Tests pass: 0 Failed, 1125 Passed, 31 Skipped
    • ✅ Code review completed (LGTM, one 💡 suggestion)
    • ✅ All changes committed in single amended commit 2a2d31b5610
    • task_complete called
  5. Active Work State:

    • Task is COMPLETE.task_complete was called with full summary.
  6. Recent Operations:

    • Last Agent Commands:
      1. bash: git diff HEAD~1 -- ResultChecker.cs | grep "Assert.AreEqual.*Assert.True" — no results
      2. bash: git diff HEAD~1 -- ResultChecker.cs | grep -A1 "Assert.AreEqual" — found the 3 AreEqual conversions including the Assert.True pattern
      3. bash: grep "Assert.True.*==\|Assert.False.*!=" ResultChecker.cs — confirmed only 1 instance at line 259
      4. bash: grep "SkipTestException" TestSuites.cs — confirmed 2 usages
      5. Wrote code review output (LGTM with findings)
      6. bash: git add -A && git diff --cached --stat — staged 3 files (csproj, TestSuites.cs, TestRunner.cs)
      7. bash: git commit --amend — amended commit to 2a2d31b5610 with 22 files, 369 insertions, 396 deletions
      8. bash: git log --oneline -2 — verified commit on illink-xunit branch
      9. `ta...

Created from Copilot CLI via the copilot delegate command.

CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:46
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Apr 7, 2026
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:50
CopilotAI changed the title [WIP] Convert illink tests from MSTest to xUnitConvert Mono.Linker.Tests from MSTest to xUnitApr 7, 2026
CopilotAI requested a review from sbomerApril 7, 2026 19:53
…degenAnnotationTests
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/bec1ccc1-bf1f-4de2-bf2d-ab95d9017603
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 20:30
CopilotAI requested a review from jkoritzinskyApril 7, 2026 20:31
@jkotasjkotas added area-Tools-ILLink .NET linker development as well as trimming analyzers and removed area-Infrastructure labels Apr 7, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

CopilotAI 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 28 out of 28 changed files in this pull request and generated 7 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/coreclr/tools/ILTrim.Tests/TestCasesRunner/TestRunner.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

…sert.Equal arg order
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ffe27803-c5fc-4c06-b630-8a71e5c38653
Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>

CopilotAI commented Apr 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in b0154e0:

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") with descriptive messages
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes the linker output
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all three MVID assertions
  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit;
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace

CopilotAI 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 28 out of 28 changed files in this pull request and generated 4 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

CopilotAI commented Apr 17, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in d5455d5:

  • ResultChecker.cs:73: changed Single(...)SingleOrDefault(...) so the subsequent Assert.True(assemblyRef is not null, ...) is reachable and shows the helpful message
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") with context replacing the redundant Assert.NotNull
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, $"Missing types in linked assembly '{...}': ...")
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, $"Missing members in type '{...}' in linked assembly '{...}': ...")

@sbomer
sbomer enabled auto-merge (squash) April 21, 2026 16:49
@sbomer
sbomer merged commit 416af5f into mainApr 21, 2026
130 of 137 checks passed
@sbomer
sbomer deleted the copilot/convert-illink-tests-to-xunit branch April 21, 2026 16:50
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 22, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly frameworkNO-MERGEThe PR is not ready for merge yet (see discussion for detailed reasons)

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants

@MichalStrehovsky@sbomer@jkoritzinsky@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Convert Mono.Linker.Tests from MSTest to xUnit - #126613

Merged
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit
Apr 21, 2026
Merged

Convert Mono.Linker.Tests from MSTest to xUnit#126613
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit

Conversation

CopilotAI commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Description

Converts Mono.Linker.Tests — the last remaining MSTest project in illink — to xUnit, aligning it with the rest of dotnet/runtime's test infrastructure.

Changes

Project setup

  • Remove <TestRunnerName>MSTest</TestRunnerName> from csproj; xUnit is now the default via Arcade
  • Add explicit Microsoft.DotNet.XUnitExtensions PackageReference (not auto-imported for src/tools/illink/)

Test data / dynamic data

  • TestDatabase: return types changed from IEnumerable<TestDataRow<TestCase>>IEnumerable<object[]>; MSTestCasesBySuiteNameTestCasesBySuiteName
  • MemberAssertionsCollector.GetMemberAssertionsData: returns IEnumerable<object[]> instead of IEnumerable<TestDataRow<T>>
  • TestCase.ToString() added to emit DisplayName for readable xUnit test names
  • [DynamicData(..., new object[] { typeof(T) })] with parameterized data source → parameterless [MemberData] method per test class

Test attributes

  • [TestClass] removed; [TestMethod][Fact]; [DataRow][InlineData]
  • [DynamicData][MemberData]; suite tests use [ConditionalTheory] (not plain [Theory]) to support dynamic skipping
  • [TestInitialize] → constructor; [DoNotParallelize] removed (MSTest-only concept)
  • AssemblyInfo.cs parallel configuration removed

Dynamic skipping
MSTest's Assert.Inconclusive doesn't translate directly to xUnit's skip mechanism. Most runtime skipping uses ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions. For CodegenAnnotationTests, declarative [SkipOnTargetFramework] attributes are used instead:

// Before (MSTest)if(Environment.OSVersion.Platform==PlatformID.Win32NT)Assert.Inconclusive("These tests are not valid when trimming .NET Framework");
#if NETAssert.Inconclusive("These tests are not valid when trimming .NET Core");
#endif
// After (xUnit)[SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp,"These tests are not valid when trimming .NET Core")][SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework,"These tests are not valid when trimming .NET Framework")]

The [SkipOnTargetFramework] attribute adds a Category=Failing trait via SkipOnTargetFrameworkDiscoverer, which is automatically filtered out by the CI infrastructure via _withoutCategories=failing in eng/testing/tests.targets.

Assertions (all files)

MSTestxUnit
Assert.AreEqual(a, b)Assert.Equal(a, b)
Assert.IsTrue/IsFalseAssert.True/False
Assert.IsNull/IsNotNullAssert.Null/NotNullAssert.True(x is not null, $"...") with diagnostic message
Assert.IsEmptyAssert.Empty / Assert.True(x.Length == 0, message)
Assert.Inconclusivethrow new SkipTestException
CollectionAssert.AreEquivalentAssert.Equal(expected.OrderBy(...), actual.OrderBy(...))
UnitTestAssertExceptionXunitException

Assertion quality fixes

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") to preserve diagnostic context; changed Single(...)SingleOrDefault(...) so the null check is actually reachable and surfaces the descriptive message on failure
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") replacing the redundant Assert.NotNull
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes linker output when exit codes don't match
  • ResultChecker.cs:834-835: corrected Assert.Equal argument order so expected is first and actual is second, producing correct diff output on failure
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, ...) including the linked assembly name and missing type list
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, ...) including the type name and missing member names
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines in the failure message
  • TestFrameworkRulesAndConventions.cs:42: replaced Assert.Empty(missing) with Assert.True(missing.Length == 0, $"Could not find a test case type for the following source file(s): ...") to preserve the explanatory failure message
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all MVID assertions

Miscellaneous fixes

  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit; to prevent CS8019 build warning
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace on blank line

Testing

Build: dotnet build src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Run: dotnet test src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Original prompt Chronological Review: 1. User requested converting illink tests from MSTest to xUnit 2. Prior work (summarized): Explored repo, found only Mono.Linker.Tests uses MSTest, ran baseline build, delegated bulk conversion to sub-agent (22 files), committed as 9425ffe083f 3. Prior work: Discovered 31 test failures due to dynamic skip mechanism, fixed with ConditionalTheory + SkipTestException (3 files) 4. Prior work: Started code review skill, gathered diff context 5. Current session: Completed code review and amended commit

Recent Commands Analysis:

  • Checked AreEqual→Assert.True conversions in ResultChecker.cs diff
  • Verified SkipTestException and ConditionalTheory usage in TestSuites.cs
  • Found only 1 instance of Assert.True(a == b, msg) pattern at line 259
  • Wrote code review (LGTM with one 💡 suggestion about Assert.NotNull message loss)
  • Staged 3 uncommitted files and amended commit
  • Final commit: 2a2d31b5610
  • Called task_complete

Progress: Everything is DONE. The task is complete.

1. Conversation Overview: - Primary Objectives: "Update the illink tests from MSTest to xunit." - Session Context: User requested converting `Mono.Linker.Tests` (the last MSTest test project in illink) to xUnit. Work progressed through exploration → bulk conversion → fixing dynamic skip failures → code review → final commit. - User Intent Evolution: Single clear request, no changes in direction.
  1. Technical Foundation:

    • xUnit v2 runner (2.9.x) with xUnit v3 assertions (Microsoft.DotNet.XUnitAssert 3.2.2-beta) — the dotnet/runtime standard setup
    • Arcade SDK: Tests.targets defaults TestRunnerName=XUnit; removing <TestRunnerName>MSTest</TestRunnerName> switches to xUnit packages automatically
    • Microsoft.DotNet.XUnitExtensions: Provides ConditionalTheory/ConditionalFact and SkipTestException for dynamic test skipping. NOT auto-imported for src/tools/illink/ (only for src/libraries/ and src/tests/), so needed explicit PackageReference.
    • SDK version: 11.0.100-preview.3.26170.106
  2. Codebase Status:

    • All 22 files committed in single commit 2a2d31b5610 on branch illink-xunit
    • Key files:
      • Mono.Linker.Tests.csproj — Removed TestRunnerName=MSTest, added Microsoft.DotNet.XUnitExtensions PackageReference
      • TestSuites.cs — 47 [ConditionalTheory] methods, [MemberData] instead of [DynamicData], SkipTestException for dynamic skips
      • TestRunner.csIgnoreTest throws SkipTestException, catches XunitException instead of UnitTestAssertException
      • TestDatabase.csobject[] instead of TestDataRow<TestCase>, renamed MSTestCasesBySuiteNameTestCasesBySuiteName
      • TestCase.cs — Added ToString() override returning DisplayName
      • ResultChecker.cs — Dozens of assertion conversions; 3 Assert.NotNull calls lost custom messages; 1 Assert.True(a == b, msg) pattern at line 259
      • 16 other files with attribute/using/assertion conversions
  3. Problem Resolution:

    • 31 test failures from dynamic skip: SkipException.ForSkip() (Xunit.Sdk) produces $XunitDynamicSkip$ prefix not caught by xUnit v2 runner. Fixed by using ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions.
    • Assert.NotNull message loss: xUnit's Assert.NotNull doesn't accept messages. Accepted as known tradeoff (3 instances).
    • Assert.AreEqual with message: One instance converted to Assert.True(a == b, msg) to preserve diagnostic output including linker output.
  4. Progress Tracking:

    • ✅ Baseline build succeeded
    • ✅ All 22 files converted from MSTest to xUnit
    • ✅ Dynamic skip mechanism fixed (ConditionalTheory + SkipTestException)
    • ✅ Build passes (0 errors, 0 warnings)
    • ✅ Tests pass: 0 Failed, 1125 Passed, 31 Skipped
    • ✅ Code review completed (LGTM, one 💡 suggestion)
    • ✅ All changes committed in single amended commit 2a2d31b5610
    • task_complete called
  5. Active Work State:

    • Task is COMPLETE.task_complete was called with full summary.
  6. Recent Operations:

    • Last Agent Commands:
      1. bash: git diff HEAD~1 -- ResultChecker.cs | grep "Assert.AreEqual.*Assert.True" — no results
      2. bash: git diff HEAD~1 -- ResultChecker.cs | grep -A1 "Assert.AreEqual" — found the 3 AreEqual conversions including the Assert.True pattern
      3. bash: grep "Assert.True.*==\|Assert.False.*!=" ResultChecker.cs — confirmed only 1 instance at line 259
      4. bash: grep "SkipTestException" TestSuites.cs — confirmed 2 usages
      5. Wrote code review output (LGTM with findings)
      6. bash: git add -A && git diff --cached --stat — staged 3 files (csproj, TestSuites.cs, TestRunner.cs)
      7. bash: git commit --amend — amended commit to 2a2d31b5610 with 22 files, 369 insertions, 396 deletions
      8. bash: git log --oneline -2 — verified commit on illink-xunit branch
      9. `ta...

Created from Copilot CLI via the copilot delegate command.

CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:46
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Apr 7, 2026
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:50
CopilotAI changed the title [WIP] Convert illink tests from MSTest to xUnitConvert Mono.Linker.Tests from MSTest to xUnitApr 7, 2026
CopilotAI requested a review from sbomerApril 7, 2026 19:53
…degenAnnotationTests
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/bec1ccc1-bf1f-4de2-bf2d-ab95d9017603
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 20:30
CopilotAI requested a review from jkoritzinskyApril 7, 2026 20:31
@jkotasjkotas added area-Tools-ILLink .NET linker development as well as trimming analyzers and removed area-Infrastructure labels Apr 7, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

CopilotAI 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 28 out of 28 changed files in this pull request and generated 7 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/coreclr/tools/ILTrim.Tests/TestCasesRunner/TestRunner.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

…sert.Equal arg order
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ffe27803-c5fc-4c06-b630-8a71e5c38653
Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>

CopilotAI commented Apr 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in b0154e0:

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") with descriptive messages
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes the linker output
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all three MVID assertions
  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit;
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace

CopilotAI 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 28 out of 28 changed files in this pull request and generated 4 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

CopilotAI commented Apr 17, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in d5455d5:

  • ResultChecker.cs:73: changed Single(...)SingleOrDefault(...) so the subsequent Assert.True(assemblyRef is not null, ...) is reachable and shows the helpful message
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") with context replacing the redundant Assert.NotNull
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, $"Missing types in linked assembly '{...}': ...")
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, $"Missing members in type '{...}' in linked assembly '{...}': ...")

@sbomer
sbomer enabled auto-merge (squash) April 21, 2026 16:49
@sbomer
sbomer merged commit 416af5f into mainApr 21, 2026
130 of 137 checks passed
@sbomer
sbomer deleted the copilot/convert-illink-tests-to-xunit branch April 21, 2026 16:50
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 22, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly frameworkNO-MERGEThe PR is not ready for merge yet (see discussion for detailed reasons)

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants

@MichalStrehovsky@sbomer@jkoritzinsky@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Convert Mono.Linker.Tests from MSTest to xUnit - #126613

Merged
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit
Apr 21, 2026
Merged

Convert Mono.Linker.Tests from MSTest to xUnit#126613
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit

Conversation

CopilotAI commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Description

Converts Mono.Linker.Tests — the last remaining MSTest project in illink — to xUnit, aligning it with the rest of dotnet/runtime's test infrastructure.

Changes

Project setup

  • Remove <TestRunnerName>MSTest</TestRunnerName> from csproj; xUnit is now the default via Arcade
  • Add explicit Microsoft.DotNet.XUnitExtensions PackageReference (not auto-imported for src/tools/illink/)

Test data / dynamic data

  • TestDatabase: return types changed from IEnumerable<TestDataRow<TestCase>>IEnumerable<object[]>; MSTestCasesBySuiteNameTestCasesBySuiteName
  • MemberAssertionsCollector.GetMemberAssertionsData: returns IEnumerable<object[]> instead of IEnumerable<TestDataRow<T>>
  • TestCase.ToString() added to emit DisplayName for readable xUnit test names
  • [DynamicData(..., new object[] { typeof(T) })] with parameterized data source → parameterless [MemberData] method per test class

Test attributes

  • [TestClass] removed; [TestMethod][Fact]; [DataRow][InlineData]
  • [DynamicData][MemberData]; suite tests use [ConditionalTheory] (not plain [Theory]) to support dynamic skipping
  • [TestInitialize] → constructor; [DoNotParallelize] removed (MSTest-only concept)
  • AssemblyInfo.cs parallel configuration removed

Dynamic skipping
MSTest's Assert.Inconclusive doesn't translate directly to xUnit's skip mechanism. Most runtime skipping uses ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions. For CodegenAnnotationTests, declarative [SkipOnTargetFramework] attributes are used instead:

// Before (MSTest)if(Environment.OSVersion.Platform==PlatformID.Win32NT)Assert.Inconclusive("These tests are not valid when trimming .NET Framework");
#if NETAssert.Inconclusive("These tests are not valid when trimming .NET Core");
#endif
// After (xUnit)[SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp,"These tests are not valid when trimming .NET Core")][SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework,"These tests are not valid when trimming .NET Framework")]

The [SkipOnTargetFramework] attribute adds a Category=Failing trait via SkipOnTargetFrameworkDiscoverer, which is automatically filtered out by the CI infrastructure via _withoutCategories=failing in eng/testing/tests.targets.

Assertions (all files)

MSTestxUnit
Assert.AreEqual(a, b)Assert.Equal(a, b)
Assert.IsTrue/IsFalseAssert.True/False
Assert.IsNull/IsNotNullAssert.Null/NotNullAssert.True(x is not null, $"...") with diagnostic message
Assert.IsEmptyAssert.Empty / Assert.True(x.Length == 0, message)
Assert.Inconclusivethrow new SkipTestException
CollectionAssert.AreEquivalentAssert.Equal(expected.OrderBy(...), actual.OrderBy(...))
UnitTestAssertExceptionXunitException

Assertion quality fixes

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") to preserve diagnostic context; changed Single(...)SingleOrDefault(...) so the null check is actually reachable and surfaces the descriptive message on failure
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") replacing the redundant Assert.NotNull
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes linker output when exit codes don't match
  • ResultChecker.cs:834-835: corrected Assert.Equal argument order so expected is first and actual is second, producing correct diff output on failure
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, ...) including the linked assembly name and missing type list
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, ...) including the type name and missing member names
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines in the failure message
  • TestFrameworkRulesAndConventions.cs:42: replaced Assert.Empty(missing) with Assert.True(missing.Length == 0, $"Could not find a test case type for the following source file(s): ...") to preserve the explanatory failure message
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all MVID assertions

Miscellaneous fixes

  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit; to prevent CS8019 build warning
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace on blank line

Testing

Build: dotnet build src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Run: dotnet test src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Original prompt Chronological Review: 1. User requested converting illink tests from MSTest to xUnit 2. Prior work (summarized): Explored repo, found only Mono.Linker.Tests uses MSTest, ran baseline build, delegated bulk conversion to sub-agent (22 files), committed as 9425ffe083f 3. Prior work: Discovered 31 test failures due to dynamic skip mechanism, fixed with ConditionalTheory + SkipTestException (3 files) 4. Prior work: Started code review skill, gathered diff context 5. Current session: Completed code review and amended commit

Recent Commands Analysis:

  • Checked AreEqual→Assert.True conversions in ResultChecker.cs diff
  • Verified SkipTestException and ConditionalTheory usage in TestSuites.cs
  • Found only 1 instance of Assert.True(a == b, msg) pattern at line 259
  • Wrote code review (LGTM with one 💡 suggestion about Assert.NotNull message loss)
  • Staged 3 uncommitted files and amended commit
  • Final commit: 2a2d31b5610
  • Called task_complete

Progress: Everything is DONE. The task is complete.

1. Conversation Overview: - Primary Objectives: "Update the illink tests from MSTest to xunit." - Session Context: User requested converting `Mono.Linker.Tests` (the last MSTest test project in illink) to xUnit. Work progressed through exploration → bulk conversion → fixing dynamic skip failures → code review → final commit. - User Intent Evolution: Single clear request, no changes in direction.
  1. Technical Foundation:

    • xUnit v2 runner (2.9.x) with xUnit v3 assertions (Microsoft.DotNet.XUnitAssert 3.2.2-beta) — the dotnet/runtime standard setup
    • Arcade SDK: Tests.targets defaults TestRunnerName=XUnit; removing <TestRunnerName>MSTest</TestRunnerName> switches to xUnit packages automatically
    • Microsoft.DotNet.XUnitExtensions: Provides ConditionalTheory/ConditionalFact and SkipTestException for dynamic test skipping. NOT auto-imported for src/tools/illink/ (only for src/libraries/ and src/tests/), so needed explicit PackageReference.
    • SDK version: 11.0.100-preview.3.26170.106
  2. Codebase Status:

    • All 22 files committed in single commit 2a2d31b5610 on branch illink-xunit
    • Key files:
      • Mono.Linker.Tests.csproj — Removed TestRunnerName=MSTest, added Microsoft.DotNet.XUnitExtensions PackageReference
      • TestSuites.cs — 47 [ConditionalTheory] methods, [MemberData] instead of [DynamicData], SkipTestException for dynamic skips
      • TestRunner.csIgnoreTest throws SkipTestException, catches XunitException instead of UnitTestAssertException
      • TestDatabase.csobject[] instead of TestDataRow<TestCase>, renamed MSTestCasesBySuiteNameTestCasesBySuiteName
      • TestCase.cs — Added ToString() override returning DisplayName
      • ResultChecker.cs — Dozens of assertion conversions; 3 Assert.NotNull calls lost custom messages; 1 Assert.True(a == b, msg) pattern at line 259
      • 16 other files with attribute/using/assertion conversions
  3. Problem Resolution:

    • 31 test failures from dynamic skip: SkipException.ForSkip() (Xunit.Sdk) produces $XunitDynamicSkip$ prefix not caught by xUnit v2 runner. Fixed by using ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions.
    • Assert.NotNull message loss: xUnit's Assert.NotNull doesn't accept messages. Accepted as known tradeoff (3 instances).
    • Assert.AreEqual with message: One instance converted to Assert.True(a == b, msg) to preserve diagnostic output including linker output.
  4. Progress Tracking:

    • ✅ Baseline build succeeded
    • ✅ All 22 files converted from MSTest to xUnit
    • ✅ Dynamic skip mechanism fixed (ConditionalTheory + SkipTestException)
    • ✅ Build passes (0 errors, 0 warnings)
    • ✅ Tests pass: 0 Failed, 1125 Passed, 31 Skipped
    • ✅ Code review completed (LGTM, one 💡 suggestion)
    • ✅ All changes committed in single amended commit 2a2d31b5610
    • task_complete called
  5. Active Work State:

    • Task is COMPLETE.task_complete was called with full summary.
  6. Recent Operations:

    • Last Agent Commands:
      1. bash: git diff HEAD~1 -- ResultChecker.cs | grep "Assert.AreEqual.*Assert.True" — no results
      2. bash: git diff HEAD~1 -- ResultChecker.cs | grep -A1 "Assert.AreEqual" — found the 3 AreEqual conversions including the Assert.True pattern
      3. bash: grep "Assert.True.*==\|Assert.False.*!=" ResultChecker.cs — confirmed only 1 instance at line 259
      4. bash: grep "SkipTestException" TestSuites.cs — confirmed 2 usages
      5. Wrote code review output (LGTM with findings)
      6. bash: git add -A && git diff --cached --stat — staged 3 files (csproj, TestSuites.cs, TestRunner.cs)
      7. bash: git commit --amend — amended commit to 2a2d31b5610 with 22 files, 369 insertions, 396 deletions
      8. bash: git log --oneline -2 — verified commit on illink-xunit branch
      9. `ta...

Created from Copilot CLI via the copilot delegate command.

CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:46
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Apr 7, 2026
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:50
CopilotAI changed the title [WIP] Convert illink tests from MSTest to xUnitConvert Mono.Linker.Tests from MSTest to xUnitApr 7, 2026
CopilotAI requested a review from sbomerApril 7, 2026 19:53
…degenAnnotationTests
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/bec1ccc1-bf1f-4de2-bf2d-ab95d9017603
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 20:30
CopilotAI requested a review from jkoritzinskyApril 7, 2026 20:31
@jkotasjkotas added area-Tools-ILLink .NET linker development as well as trimming analyzers and removed area-Infrastructure labels Apr 7, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

CopilotAI 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 28 out of 28 changed files in this pull request and generated 7 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/coreclr/tools/ILTrim.Tests/TestCasesRunner/TestRunner.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

…sert.Equal arg order
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ffe27803-c5fc-4c06-b630-8a71e5c38653
Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>

CopilotAI commented Apr 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in b0154e0:

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") with descriptive messages
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes the linker output
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all three MVID assertions
  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit;
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace

CopilotAI 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 28 out of 28 changed files in this pull request and generated 4 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

CopilotAI commented Apr 17, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in d5455d5:

  • ResultChecker.cs:73: changed Single(...)SingleOrDefault(...) so the subsequent Assert.True(assemblyRef is not null, ...) is reachable and shows the helpful message
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") with context replacing the redundant Assert.NotNull
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, $"Missing types in linked assembly '{...}': ...")
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, $"Missing members in type '{...}' in linked assembly '{...}': ...")

@sbomer
sbomer enabled auto-merge (squash) April 21, 2026 16:49
@sbomer
sbomer merged commit 416af5f into mainApr 21, 2026
130 of 137 checks passed
@sbomer
sbomer deleted the copilot/convert-illink-tests-to-xunit branch April 21, 2026 16:50
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 22, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly frameworkNO-MERGEThe PR is not ready for merge yet (see discussion for detailed reasons)

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants

@MichalStrehovsky@sbomer@jkoritzinsky@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Convert Mono.Linker.Tests from MSTest to xUnit - #126613

Merged
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit
Apr 21, 2026
Merged

Convert Mono.Linker.Tests from MSTest to xUnit#126613
sbomer merged 11 commits into
mainfrom
copilot/convert-illink-tests-to-xunit

Conversation

CopilotAI commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Description

Converts Mono.Linker.Tests — the last remaining MSTest project in illink — to xUnit, aligning it with the rest of dotnet/runtime's test infrastructure.

Changes

Project setup

  • Remove <TestRunnerName>MSTest</TestRunnerName> from csproj; xUnit is now the default via Arcade
  • Add explicit Microsoft.DotNet.XUnitExtensions PackageReference (not auto-imported for src/tools/illink/)

Test data / dynamic data

  • TestDatabase: return types changed from IEnumerable<TestDataRow<TestCase>>IEnumerable<object[]>; MSTestCasesBySuiteNameTestCasesBySuiteName
  • MemberAssertionsCollector.GetMemberAssertionsData: returns IEnumerable<object[]> instead of IEnumerable<TestDataRow<T>>
  • TestCase.ToString() added to emit DisplayName for readable xUnit test names
  • [DynamicData(..., new object[] { typeof(T) })] with parameterized data source → parameterless [MemberData] method per test class

Test attributes

  • [TestClass] removed; [TestMethod][Fact]; [DataRow][InlineData]
  • [DynamicData][MemberData]; suite tests use [ConditionalTheory] (not plain [Theory]) to support dynamic skipping
  • [TestInitialize] → constructor; [DoNotParallelize] removed (MSTest-only concept)
  • AssemblyInfo.cs parallel configuration removed

Dynamic skipping
MSTest's Assert.Inconclusive doesn't translate directly to xUnit's skip mechanism. Most runtime skipping uses ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions. For CodegenAnnotationTests, declarative [SkipOnTargetFramework] attributes are used instead:

// Before (MSTest)if(Environment.OSVersion.Platform==PlatformID.Win32NT)Assert.Inconclusive("These tests are not valid when trimming .NET Framework");
#if NETAssert.Inconclusive("These tests are not valid when trimming .NET Core");
#endif
// After (xUnit)[SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp,"These tests are not valid when trimming .NET Core")][SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework,"These tests are not valid when trimming .NET Framework")]

The [SkipOnTargetFramework] attribute adds a Category=Failing trait via SkipOnTargetFrameworkDiscoverer, which is automatically filtered out by the CI infrastructure via _withoutCategories=failing in eng/testing/tests.targets.

Assertions (all files)

MSTestxUnit
Assert.AreEqual(a, b)Assert.Equal(a, b)
Assert.IsTrue/IsFalseAssert.True/False
Assert.IsNull/IsNotNullAssert.Null/NotNullAssert.True(x is not null, $"...") with diagnostic message
Assert.IsEmptyAssert.Empty / Assert.True(x.Length == 0, message)
Assert.Inconclusivethrow new SkipTestException
CollectionAssert.AreEquivalentAssert.Equal(expected.OrderBy(...), actual.OrderBy(...))
UnitTestAssertExceptionXunitException

Assertion quality fixes

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") to preserve diagnostic context; changed Single(...)SingleOrDefault(...) so the null check is actually reachable and surfaces the descriptive message on failure
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") replacing the redundant Assert.NotNull
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes linker output when exit codes don't match
  • ResultChecker.cs:834-835: corrected Assert.Equal argument order so expected is first and actual is second, producing correct diff output on failure
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, ...) including the linked assembly name and missing type list
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, ...) including the type name and missing member names
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines in the failure message
  • TestFrameworkRulesAndConventions.cs:42: replaced Assert.Empty(missing) with Assert.True(missing.Length == 0, $"Could not find a test case type for the following source file(s): ...") to preserve the explanatory failure message
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all MVID assertions

Miscellaneous fixes

  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit; to prevent CS8019 build warning
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace on blank line

Testing

Build: dotnet build src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Run: dotnet test src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj -c Release

Original prompt Chronological Review: 1. User requested converting illink tests from MSTest to xUnit 2. Prior work (summarized): Explored repo, found only Mono.Linker.Tests uses MSTest, ran baseline build, delegated bulk conversion to sub-agent (22 files), committed as 9425ffe083f 3. Prior work: Discovered 31 test failures due to dynamic skip mechanism, fixed with ConditionalTheory + SkipTestException (3 files) 4. Prior work: Started code review skill, gathered diff context 5. Current session: Completed code review and amended commit

Recent Commands Analysis:

  • Checked AreEqual→Assert.True conversions in ResultChecker.cs diff
  • Verified SkipTestException and ConditionalTheory usage in TestSuites.cs
  • Found only 1 instance of Assert.True(a == b, msg) pattern at line 259
  • Wrote code review (LGTM with one 💡 suggestion about Assert.NotNull message loss)
  • Staged 3 uncommitted files and amended commit
  • Final commit: 2a2d31b5610
  • Called task_complete

Progress: Everything is DONE. The task is complete.

1. Conversation Overview: - Primary Objectives: "Update the illink tests from MSTest to xunit." - Session Context: User requested converting `Mono.Linker.Tests` (the last MSTest test project in illink) to xUnit. Work progressed through exploration → bulk conversion → fixing dynamic skip failures → code review → final commit. - User Intent Evolution: Single clear request, no changes in direction.
  1. Technical Foundation:

    • xUnit v2 runner (2.9.x) with xUnit v3 assertions (Microsoft.DotNet.XUnitAssert 3.2.2-beta) — the dotnet/runtime standard setup
    • Arcade SDK: Tests.targets defaults TestRunnerName=XUnit; removing <TestRunnerName>MSTest</TestRunnerName> switches to xUnit packages automatically
    • Microsoft.DotNet.XUnitExtensions: Provides ConditionalTheory/ConditionalFact and SkipTestException for dynamic test skipping. NOT auto-imported for src/tools/illink/ (only for src/libraries/ and src/tests/), so needed explicit PackageReference.
    • SDK version: 11.0.100-preview.3.26170.106
  2. Codebase Status:

    • All 22 files committed in single commit 2a2d31b5610 on branch illink-xunit
    • Key files:
      • Mono.Linker.Tests.csproj — Removed TestRunnerName=MSTest, added Microsoft.DotNet.XUnitExtensions PackageReference
      • TestSuites.cs — 47 [ConditionalTheory] methods, [MemberData] instead of [DynamicData], SkipTestException for dynamic skips
      • TestRunner.csIgnoreTest throws SkipTestException, catches XunitException instead of UnitTestAssertException
      • TestDatabase.csobject[] instead of TestDataRow<TestCase>, renamed MSTestCasesBySuiteNameTestCasesBySuiteName
      • TestCase.cs — Added ToString() override returning DisplayName
      • ResultChecker.cs — Dozens of assertion conversions; 3 Assert.NotNull calls lost custom messages; 1 Assert.True(a == b, msg) pattern at line 259
      • 16 other files with attribute/using/assertion conversions
  3. Problem Resolution:

    • 31 test failures from dynamic skip: SkipException.ForSkip() (Xunit.Sdk) produces $XunitDynamicSkip$ prefix not caught by xUnit v2 runner. Fixed by using ConditionalTheory + SkipTestException from Microsoft.DotNet.XUnitExtensions.
    • Assert.NotNull message loss: xUnit's Assert.NotNull doesn't accept messages. Accepted as known tradeoff (3 instances).
    • Assert.AreEqual with message: One instance converted to Assert.True(a == b, msg) to preserve diagnostic output including linker output.
  4. Progress Tracking:

    • ✅ Baseline build succeeded
    • ✅ All 22 files converted from MSTest to xUnit
    • ✅ Dynamic skip mechanism fixed (ConditionalTheory + SkipTestException)
    • ✅ Build passes (0 errors, 0 warnings)
    • ✅ Tests pass: 0 Failed, 1125 Passed, 31 Skipped
    • ✅ Code review completed (LGTM, one 💡 suggestion)
    • ✅ All changes committed in single amended commit 2a2d31b5610
    • task_complete called
  5. Active Work State:

    • Task is COMPLETE.task_complete was called with full summary.
  6. Recent Operations:

    • Last Agent Commands:
      1. bash: git diff HEAD~1 -- ResultChecker.cs | grep "Assert.AreEqual.*Assert.True" — no results
      2. bash: git diff HEAD~1 -- ResultChecker.cs | grep -A1 "Assert.AreEqual" — found the 3 AreEqual conversions including the Assert.True pattern
      3. bash: grep "Assert.True.*==\|Assert.False.*!=" ResultChecker.cs — confirmed only 1 instance at line 259
      4. bash: grep "SkipTestException" TestSuites.cs — confirmed 2 usages
      5. Wrote code review output (LGTM with findings)
      6. bash: git add -A && git diff --cached --stat — staged 3 files (csproj, TestSuites.cs, TestRunner.cs)
      7. bash: git commit --amend — amended commit to 2a2d31b5610 with 22 files, 369 insertions, 396 deletions
      8. bash: git log --oneline -2 — verified commit on illink-xunit branch
      9. `ta...

Created from Copilot CLI via the copilot delegate command.

CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:46
@dotnet-policy-servicedotnet-policy-serviceBot added the linkable-framework Issues associated with delivering a linker friendly framework label Apr 7, 2026
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 19:50
CopilotAI changed the title [WIP] Convert illink tests from MSTest to xUnitConvert Mono.Linker.Tests from MSTest to xUnitApr 7, 2026
CopilotAI requested a review from sbomerApril 7, 2026 19:53
…degenAnnotationTests
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/bec1ccc1-bf1f-4de2-bf2d-ab95d9017603
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
CopilotAI requested review from Copilot and removed request for CopilotApril 7, 2026 20:30
CopilotAI requested a review from jkoritzinskyApril 7, 2026 20:31
@jkotasjkotas added area-Tools-ILLink .NET linker development as well as trimming analyzers and removed area-Infrastructure labels Apr 7, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

CopilotAI 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 28 out of 28 changed files in this pull request and generated 7 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCases/IndividualTests.cs Outdated
Comment threadsrc/coreclr/tools/ILTrim.Tests/TestCasesRunner/TestRunner.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

…sert.Equal arg order
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ffe27803-c5fc-4c06-b630-8a71e5c38653
Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>

CopilotAI commented Apr 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in b0154e0:

  • ResultChecker.cs:64-74: replaced Assert.NotNull(exportedType/assemblyRef) with Assert.True(x is not null, $"...") with descriptive messages
  • ResultChecker.cs:260: replaced Assert.Equal(expectedExitCode, linkResult.ExitCode) with a conditional Assert.Fail(...) that includes the linker output
  • ResultChecker.cs:1209-1212: replaced Assert.Empty(remainingErrors) with Assert.True(!remainingErrors.Any(), ...) including the joined error lines
  • IndividualTests.cs:216,251-252: fixed Assert.Equal argument order (expected first, actual second) in all three MVID assertions
  • ILTrim.Tests/TestCasesRunner/TestRunner.cs: removed unused using Xunit;
  • ParseResponseFileLinesTests.cs:11: removed trailing whitespace

CopilotAI 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 28 out of 28 changed files in this pull request and generated 4 comments.

Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
Comment threadsrc/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs Outdated
@sbomer

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

CopilotAI commented Apr 17, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot apply changes based on the comments in this thread

Done in d5455d5:

  • ResultChecker.cs:73: changed Single(...)SingleOrDefault(...) so the subsequent Assert.True(assemblyRef is not null, ...) is reachable and shows the helpful message
  • ResultChecker.cs:81-85: same fix — Single(...)SingleOrDefault(...) + Assert.True(assemblyRef is not null, $"...") with context replacing the redundant Assert.NotNull
  • ResultChecker.cs:867-870: replaced Assert.Empty(missingInLinked) with Assert.True(missingInLinked.Count == 0, $"Missing types in linked assembly '{...}': ...")
  • ResultChecker.cs:878-880: replaced Assert.Empty(missingMembersInLinked) with Assert.True(missingMembersInLinked.Count == 0, $"Missing members in type '{...}' in linked assembly '{...}': ...")

@sbomer
sbomer enabled auto-merge (squash) April 21, 2026 16:49
@sbomer
sbomer merged commit 416af5f into mainApr 21, 2026
130 of 137 checks passed
@sbomer
sbomer deleted the copilot/convert-illink-tests-to-xunit branch April 21, 2026 16:50
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 22, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-Tools-ILLink.NET linker development as well as trimming analyzerslinkable-frameworkIssues associated with delivering a linker friendly frameworkNO-MERGEThe PR is not ready for merge yet (see discussion for detailed reasons)

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants

@MichalStrehovsky@sbomer@jkoritzinsky@jkotas