From 31f61e68e77af6415280be948990114e7fe8a0ac Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Thu, 11 Jun 2026 03:36:36 -0700
Subject: [PATCH 01/10] Refactor build pipeline to separate test jobs
---
diagnostics.yml | 61 +++++++++++++++++++++++++++++++++++------
eng/pipelines/build.yml | 23 ++++++++++++----
2 files changed, 70 insertions(+), 14 deletions(-)
diff --git a/diagnostics.yml b/diagnostics.yml
index 343d9e85e5..2629d44813 100644
--- a/diagnostics.yml
+++ b/diagnostics.yml
@@ -52,7 +52,7 @@ extends:
parameters:
stages:
- stage: build
- displayName: Build and Test Diagnostics
+ displayName: Build Diagnostics
jobs:
############################
@@ -79,10 +79,11 @@ extends:
jobTemplate: ${{ variables.jobTemplate }}
name: Windows
osGroup: Windows_NT
- buildOnly: ${{ parameters.buildOnly }}
+ buildOnly: true
buildConfigs:
- configuration: Debug
architecture: x64
+ artifactUploadPath: bin/Windows_NT.x64.Debug
- configuration: Release
architecture: x64
artifactUploadPath: bin
@@ -134,7 +135,7 @@ extends:
parameters:
jobTemplate: ${{ variables.jobTemplate }}
osGroup: MacOS
- buildOnly: ${{ parameters.buildOnly }}
+ buildOnly: true
buildConfigs:
- configuration: Release
architecture: x64
@@ -142,6 +143,7 @@ extends:
- ${{ if in(variables['Build.Reason'], 'PullRequest') }}:
- configuration: Debug
architecture: x64
+ artifactUploadPath: bin/osx.x64.Debug
- template: /eng/pipelines/build.yml
parameters:
@@ -212,13 +214,47 @@ extends:
artifactUploadPath: bin/linux.arm64.Release
artifactTargetPath: bin/linux-musl.arm64.Release
- ############################
- # #
- # Test only legs #
- # #
- ############################
+ ############################
+ # #
+ # Test stage #
+ # #
+ ############################
+
+ - ${{ if ne(parameters.buildOnly, true) }}:
+ - stage: test
+ displayName: Test Diagnostics
+ dependsOn: build
+ jobs:
+
+ - template: /eng/pipelines/build.yml
+ parameters:
+ jobTemplate: ${{ variables.jobTemplate }}
+ name: Windows_Test
+ osGroup: Windows_NT
+ dependsOn: Windows
+ testOnly: true
+ buildConfigs:
+ - configuration: Debug
+ architecture: x64
+ - configuration: Release
+ architecture: x64
+ - configuration: Release
+ architecture: x86
+
+ - template: /eng/pipelines/build.yml
+ parameters:
+ jobTemplate: ${{ variables.jobTemplate }}
+ name: MacOS_Test
+ osGroup: MacOS
+ dependsOn: MacOS
+ testOnly: true
+ buildConfigs:
+ - configuration: Release
+ architecture: x64
+ - ${{ if in(variables['Build.Reason'], 'PullRequest') }}:
+ - configuration: Debug
+ architecture: x64
- - ${{ if ne(parameters.buildOnly, true) }}:
- template: /eng/pipelines/build.yml
parameters:
jobTemplate: ${{ variables.jobTemplate }}
@@ -267,9 +303,16 @@ extends:
# - configuration: Debug
# architecture: x64
+ ############################
+ # #
+ # Package stage #
+ # #
+ ############################
+
- ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- stage: package
displayName: Package, Sign, and Generate BAR Manifests
+ dependsOn: build
jobs:
- template: /eng/common/templates-official/job/job.yml
parameters:
diff --git a/eng/pipelines/build.yml b/eng/pipelines/build.yml
index 058d36f3f7..4d87be1e2e 100644
--- a/eng/pipelines/build.yml
+++ b/eng/pipelines/build.yml
@@ -113,7 +113,7 @@ jobs:
${{ if ne(parameters.strategy, '') }}:
'error, we can no longer support the strategy feature in the new pipeline system. Please remove the strategy from the job template.': error
- ${{ if ne(parameters.dependsOn, '') }}:
+ ${{ if and(ne(parameters.dependsOn, ''), ne(parameters.testOnly, true)) }}:
dependsOn: ${{ parameters.dependsOn }}_${{ config.architecture }}_${{ config.configuration }}
workspace:
@@ -133,6 +133,19 @@ jobs:
- ${{ if ne(parameters.osGroup, 'Windows_NT') }}:
- _buildScript: $(Build.SourcesDirectory)/build.sh
+ # Native artifact folder names used by the test-only legs.
+ # _downloadOSFolder: the folder name as published by the build leg.
+ # _localOSFolder: the folder name expected by the build script on the test machine.
+ - ${{ if eq(parameters.osGroup, 'Windows_NT') }}:
+ - _downloadOSFolder: Windows_NT
+ - _localOSFolder: Windows_NT
+ - ${{ elseif eq(parameters.osGroup, 'MacOS') }}:
+ - _downloadOSFolder: osx
+ - _localOSFolder: osx
+ - ${{ else }}:
+ - _downloadOSFolder: linux${{ parameters.osSuffix }}
+ - _localOSFolder: linux
+
- ${{ if and(eq(parameters.testOnly, 'true'), eq(parameters.buildOnly, 'true')) }}:
'error, testOnly and buildOnly cannot be true at the same time': error
@@ -157,8 +170,8 @@ jobs:
steps:
- ${{ if eq(parameters.testOnly, true) }}:
- - ${{ if ne(parameters.osGroup, 'Linux') }}:
- - 'error, testOnly is only supported on Linux': error
+ - ${{ if notIn(parameters.osGroup, 'Windows_NT', 'MacOS', 'Linux') }}:
+ - 'error, testOnly is only supported on Windows_NT, MacOS, and Linux': error
- task: DownloadPipelineArtifact@2
displayName: 'Download Build Artifacts'
inputs:
@@ -168,8 +181,8 @@ jobs:
- task: CopyFiles@2
displayName: 'Binplace Downloaded Product'
inputs:
- sourceFolder: $(Build.ArtifactStagingDirectory)/__download__/Build_${{ parameters.dependsOn }}_${{ config.architecture }}_${{ config.configuration }}/bin/linux${{ parameters.osSuffix }}.${{ config.architecture }}.${{ config.configuration }}
- targetFolder: '$(Build.SourcesDirectory)/artifacts/bin/linux.${{ config.architecture }}.${{ config.configuration }}'
+ sourceFolder: $(Build.ArtifactStagingDirectory)/__download__/Build_${{ parameters.dependsOn }}_${{ config.architecture }}_${{ config.configuration }}/bin/$(_downloadOSFolder).${{ config.architecture }}.${{ config.configuration }}
+ targetFolder: '$(Build.SourcesDirectory)/artifacts/bin/$(_localOSFolder).${{ config.architecture }}.${{ config.configuration }}'
- script: $(_buildScript)
-ci
From b5ff03d6b0d8524e2060d1f3ca7e8177084a046f Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Fri, 26 Jun 2026 13:06:53 -0700
Subject: [PATCH 02/10] Split CI build/test outputs and move tests to net10
- Publish full test artifacts from build-only legs and consume them in test-only legs with -skipmanaged\n- Gate test artifact publish on buildOnly to avoid duplicate or invalid publish work\n- Ensure test runtimes/sdk install in skipmanaged test legs via explicit InstallRuntimes build step\n- Keep tools on net8.0 while moving unit tests to net10.0\n- Remove global.json tools.runtimes dependency and fix net10 analyzer warnings in EventLogsPipelineUnitTests\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
Directory.Build.props | 10 +++-
diagnostics.yml | 4 ++
eng/build.ps1 | 25 +++++++++
eng/build.sh | 27 ++++++++++
eng/pipelines/build.yml | 54 +++++++++++--------
global.json | 10 +---
.../DbgShim.UnitTests.csproj | 2 +-
...Diagnostics.DebugServices.UnitTests.csproj | 2 +-
.../EventLogsPipelineUnitTests.cs | 12 ++---
.../Microsoft.FileFormats.UnitTests.csproj | 2 +-
.../Microsoft.SymbolStore.UnitTests.csproj | 2 +-
src/tests/SOS.UnitTests/SOS.UnitTests.csproj | 2 +-
.../DotnetCounters.UnitTests.csproj | 2 +-
.../dotnet-trace/DotnetTrace.UnitTests.csproj | 2 +-
14 files changed, 112 insertions(+), 44 deletions(-)
diff --git a/Directory.Build.props b/Directory.Build.props
index 9fe222dc98..e6a583c5c6 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -69,10 +69,18 @@
-->
8.0
net$(NetCoreAppMinVersion)
+
+ 10.0
+ net$(NetCoreAppTestVersion)
net8.0;net9.0;net10.0;net11.0
- net8.0
+ $(NetCoreAppTestTargetFramework)
diff --git a/diagnostics.yml b/diagnostics.yml
index 2629d44813..ded5f372e1 100644
--- a/diagnostics.yml
+++ b/diagnostics.yml
@@ -80,6 +80,7 @@ extends:
name: Windows
osGroup: Windows_NT
buildOnly: true
+ publishTestArtifacts: true
buildConfigs:
- configuration: Debug
architecture: x64
@@ -102,6 +103,7 @@ extends:
container: linux_x64
crossBuild: true
buildOnly: true
+ publishTestArtifacts: true
buildConfigs:
- configuration: Release
architecture: x64
@@ -120,6 +122,7 @@ extends:
container: linux_musl_x64
crossBuild: true
buildOnly: true
+ publishTestArtifacts: true
buildConfigs:
- configuration: Release
architecture: x64
@@ -136,6 +139,7 @@ extends:
jobTemplate: ${{ variables.jobTemplate }}
osGroup: MacOS
buildOnly: true
+ publishTestArtifacts: true
buildConfigs:
- configuration: Release
architecture: x64
diff --git a/eng/build.ps1 b/eng/build.ps1
index 975097c9a6..0dceb53c6a 100644
--- a/eng/build.ps1
+++ b/eng/build.ps1
@@ -124,8 +124,33 @@ if ($test) {
$testFilterArg = "/p:TestRunnerAdditionalArguments=\`"-class $classfilter\`""
}
+ # When the managed build was skipped (e.g. the test-only CI legs that download prebuilt
+ # product binaries), the test SDK and runtimes that are normally installed during the
+ # managed build (InstallRuntimes.proj, InstallTestRuntimes AfterTargets=Build) were never
+ # installed on this machine. Build just that project here so the runtimes are present
+ # before the tests run. This is ordered ahead of -test, unlike relying on the parallel
+ # test traversal, and avoids rebuilding the downloaded product.
+ if ($skipmanaged) {
+ & "$engroot\common\build.ps1" `
+ -restore `
+ -build `
+ -projects "$engroot\InstallRuntimes.proj" `
+ -configuration $configuration `
+ -verbosity $verbosity `
+ -ci:$ci `
+ /bl:$logdir\InstallTestRuntimes.binlog `
+ /p:TargetOS=$os `
+ /p:TargetArch=$architecture `
+ /p:LiveRuntimeDir="$liveRuntimeDir"
+
+ if ($lastExitCode -ne 0) {
+ exit $lastExitCode
+ }
+ }
+
& "$engroot\common\build.ps1" `
-test `
+ -restore:$skipmanaged `
-configuration $configuration `
-verbosity $verbosity `
-ci:$ci `
diff --git a/eng/build.sh b/eng/build.sh
index 1320b0a723..d1a8599b57 100755
--- a/eng/build.sh
+++ b/eng/build.sh
@@ -353,9 +353,36 @@ if [[ "$__Test" == 1 ]]; then
__TestFilterArg="/p:TestRunnerAdditionalArguments=\"$__TestFilter\""
fi
+ # When the managed build was skipped (e.g. the test-only CI legs that download prebuilt
+ # product binaries), the test SDK and runtimes that are normally installed during the
+ # managed build (InstallRuntimes.proj, InstallTestRuntimes AfterTargets=Build) were never
+ # installed on this machine. Build just that project here so the runtimes are present
+ # before the tests run. This is ordered ahead of --test, unlike relying on the parallel
+ # test traversal, and avoids rebuilding the downloaded product.
+ __TestRestoreArg=
+ if [[ "$__ManagedBuild" == 0 ]]; then
+ __TestRestoreArg=--restore
+
+ "$__RepoRootDir/eng/common/build.sh" \
+ --restore \
+ --build \
+ --projects "$__RepoRootDir/eng/InstallRuntimes.proj" \
+ --configuration "$__BuildType" \
+ /bl:"$__LogsDir"/InstallTestRuntimes.binlog \
+ /p:TargetArch="$__TargetArch" \
+ /p:TargetRid="$__TargetRid" \
+ /p:LiveRuntimeDir="$__LiveRuntimeDir" \
+ $__CommonMSBuildArgs
+
+ if [ "$?" != 0 ]; then
+ exit 1
+ fi
+ fi
+
# __CommonMSBuildArgs contains TargetOS property
"$__RepoRootDir/eng/common/build.sh" \
--test \
+ $__TestRestoreArg \
--configuration "$__BuildType" \
/bl:"$__LogsDir"/Test.binlog \
/p:TargetArch="$__TargetArch" \
diff --git a/eng/pipelines/build.yml b/eng/pipelines/build.yml
index 4d87be1e2e..0b90f515f8 100644
--- a/eng/pipelines/build.yml
+++ b/eng/pipelines/build.yml
@@ -60,6 +60,12 @@ parameters:
type: boolean
default: false
+ # Optional: publish the full build outputs (managed + native + debuggees) as a
+ # 'TestArtifacts_' artifact so the matching test-only legs can run without rebuilding.
+- name: publishTestArtifacts
+ type: boolean
+ default: false
+
# Optional: architecture cross build if true
- name: crossBuild
type: boolean
@@ -133,24 +139,11 @@ jobs:
- ${{ if ne(parameters.osGroup, 'Windows_NT') }}:
- _buildScript: $(Build.SourcesDirectory)/build.sh
- # Native artifact folder names used by the test-only legs.
- # _downloadOSFolder: the folder name as published by the build leg.
- # _localOSFolder: the folder name expected by the build script on the test machine.
- - ${{ if eq(parameters.osGroup, 'Windows_NT') }}:
- - _downloadOSFolder: Windows_NT
- - _localOSFolder: Windows_NT
- - ${{ elseif eq(parameters.osGroup, 'MacOS') }}:
- - _downloadOSFolder: osx
- - _localOSFolder: osx
- - ${{ else }}:
- - _downloadOSFolder: linux${{ parameters.osSuffix }}
- - _localOSFolder: linux
-
- ${{ if and(eq(parameters.testOnly, 'true'), eq(parameters.buildOnly, 'true')) }}:
'error, testOnly and buildOnly cannot be true at the same time': error
- ${{ if eq(parameters.testOnly, 'true') }}:
- - _TestArgs: '-test -skipnative'
+ - _TestArgs: '-test -skipnative -skipmanaged'
- ${{ if eq(parameters.buildOnly, 'true') }}:
- _TestArgs: ''
@@ -172,17 +165,16 @@ jobs:
- ${{ if eq(parameters.testOnly, true) }}:
- ${{ if notIn(parameters.osGroup, 'Windows_NT', 'MacOS', 'Linux') }}:
- 'error, testOnly is only supported on Windows_NT, MacOS, and Linux': error
+ # The matching build leg published its full outputs (managed assemblies, native
+ # binaries, and debuggees) as TestArtifacts_. Restore them into the repo
+ # artifacts directory so the test run can skip both the native and managed builds
+ # (-skipnative -skipmanaged) and only execute the tests.
- task: DownloadPipelineArtifact@2
displayName: 'Download Build Artifacts'
inputs:
- targetPath: '$(Build.ArtifactStagingDirectory)/__download__'
- itemPattern: Build_${{ parameters.dependsOn }}_${{ config.architecture }}_${{ config.configuration }}/bin/**
+ artifact: TestArtifacts_${{ parameters.dependsOn }}_${{ config.architecture }}_${{ config.configuration }}
+ targetPath: '$(Build.SourcesDirectory)/artifacts'
checkDownloadedFiles: true
- - task: CopyFiles@2
- displayName: 'Binplace Downloaded Product'
- inputs:
- sourceFolder: $(Build.ArtifactStagingDirectory)/__download__/Build_${{ parameters.dependsOn }}_${{ config.architecture }}_${{ config.configuration }}/bin/$(_downloadOSFolder).${{ config.architecture }}.${{ config.configuration }}
- targetFolder: '$(Build.SourcesDirectory)/artifacts/bin/$(_localOSFolder).${{ config.architecture }}.${{ config.configuration }}'
- script: $(_buildScript)
-ci
@@ -215,6 +207,26 @@ jobs:
targetPath: '$(Build.ArtifactStagingDirectory)/artifacts'
artifactName: Build_$(_PhaseName)
+ # Publish the full build outputs for the matching test-only legs to consume. This lets the
+ # test legs run with -skipnative -skipmanaged instead of rebuilding the product themselves.
+ # Only valid on build-only legs (publishTestArtifacts without buildOnly is a configuration error).
+ - ${{ if and(eq(parameters.publishTestArtifacts, true), eq(parameters.buildOnly, true)) }}:
+ - task: CopyFiles@2
+ displayName: Gather test artifacts for publish
+ inputs:
+ SourceFolder: '$(Build.SourcesDirectory)/artifacts'
+ Contents: |
+ bin/**
+ test/**
+ TargetFolder: $(Build.ArtifactStagingDirectory)/testartifacts
+
+ - template: /eng/pipelines/publish-pipeline-artifact-shim.yml@self
+ parameters:
+ displayName: Publish Test Artifacts
+ inputs:
+ targetPath: '$(Build.ArtifactStagingDirectory)/testartifacts'
+ artifactName: TestArtifacts_$(_PhaseName)
+
- ${{ if ne(parameters.buildOnly, 'true') }}:
# Publish test results to Azure Pipelines
- task: PublishTestResults@2
diff --git a/global.json b/global.json
index 75792e25d1..c212145c75 100644
--- a/global.json
+++ b/global.json
@@ -5,15 +5,7 @@
"rollForward": "major"
},
"tools": {
- "dotnet": "10.0.108",
- "runtimes": {
- "dotnet": [
- "$(MicrosoftNETCoreApp80Version)"
- ],
- "dotnet/x86": [
- "$(MicrosoftNETCoreApp80Version)"
- ]
- }
+ "dotnet": "10.0.108"
},
"msbuild-sdks": {
"Microsoft.Build.NoTargets": "3.7.0",
diff --git a/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj b/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj
index c931e79852..3b9c84c185 100644
--- a/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj
+++ b/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
true
$(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt
1.0.351101
diff --git a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj
index 9a4014149c..e71b2f7d7f 100644
--- a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj
+++ b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
$(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt
1.0.351101
diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs
index c8ed1e4f24..1b1d00e0e8 100644
--- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs
+++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs
@@ -62,7 +62,7 @@ public async Task TestLogsAllCategoriesAllLevels(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
///
@@ -84,7 +84,7 @@ public async Task TestLogsAllCategoriesDefaultLevel(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
///
@@ -116,7 +116,7 @@ public async Task TestLogsAllCategoriesDefaultLevelFallback(TestConfiguration co
ValidateLoggerRemoteCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
///
@@ -157,7 +157,7 @@ public async Task TestLogsUseAppFilters(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
///
@@ -182,7 +182,7 @@ public async Task TestLogsUseAppFiltersAndFilterSpecs(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
///
@@ -213,7 +213,7 @@ public async Task TestLogsWildcardCategory(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
private async Task GetLogsAsync(TestConfiguration config, Action settingsCallback = null)
diff --git a/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj b/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj
index 808617aa05..513ca6baa5 100644
--- a/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj
+++ b/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj
@@ -1,6 +1,6 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
;1591;1701
diff --git a/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj b/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj
index e2dc9e42a7..3805aa1334 100644
--- a/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj
+++ b/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj
@@ -1,6 +1,6 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
;1591;1701
diff --git a/src/tests/SOS.UnitTests/SOS.UnitTests.csproj b/src/tests/SOS.UnitTests/SOS.UnitTests.csproj
index d2f3b4cbb7..df9a23e046 100644
--- a/src/tests/SOS.UnitTests/SOS.UnitTests.csproj
+++ b/src/tests/SOS.UnitTests/SOS.UnitTests.csproj
@@ -1,6 +1,6 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
false
;1591;1701
$(DefineConstants);CORE_CLR
diff --git a/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj b/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj
index aa872e9b45..6472bcfb65 100644
--- a/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj
+++ b/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
diff --git a/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj b/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj
index 9d70543023..056dd23295 100644
--- a/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj
+++ b/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
From 3b05a959acc099512cf2a81df0e8d2da3620d037 Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Fri, 26 Jun 2026 18:57:06 -0700
Subject: [PATCH 03/10] Keep tests on net8
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
Directory.Build.props | 10 +---------
global.json | 10 +++++++++-
src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj | 2 +-
...rosoft.Diagnostics.DebugServices.UnitTests.csproj | 2 +-
.../EventLogsPipelineUnitTests.cs | 12 ++++++------
.../Microsoft.FileFormats.UnitTests.csproj | 2 +-
.../Microsoft.SymbolStore.UnitTests.csproj | 2 +-
src/tests/SOS.UnitTests/SOS.UnitTests.csproj | 2 +-
.../dotnet-counters/DotnetCounters.UnitTests.csproj | 2 +-
src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj | 2 +-
10 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/Directory.Build.props b/Directory.Build.props
index e6a583c5c6..9fe222dc98 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -69,18 +69,10 @@
-->
8.0
net$(NetCoreAppMinVersion)
-
- 10.0
- net$(NetCoreAppTestVersion)
net8.0;net9.0;net10.0;net11.0
- $(NetCoreAppTestTargetFramework)
+ net8.0
diff --git a/global.json b/global.json
index c212145c75..75792e25d1 100644
--- a/global.json
+++ b/global.json
@@ -5,7 +5,15 @@
"rollForward": "major"
},
"tools": {
- "dotnet": "10.0.108"
+ "dotnet": "10.0.108",
+ "runtimes": {
+ "dotnet": [
+ "$(MicrosoftNETCoreApp80Version)"
+ ],
+ "dotnet/x86": [
+ "$(MicrosoftNETCoreApp80Version)"
+ ]
+ }
},
"msbuild-sdks": {
"Microsoft.Build.NoTargets": "3.7.0",
diff --git a/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj b/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj
index 3b9c84c185..c931e79852 100644
--- a/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj
+++ b/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppTestTargetFramework)
+ $(NetCoreAppMinTargetFramework)
true
$(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt
1.0.351101
diff --git a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj
index e71b2f7d7f..9a4014149c 100644
--- a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj
+++ b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppTestTargetFramework)
+ $(NetCoreAppMinTargetFramework)
$(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt
1.0.351101
diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs
index 1b1d00e0e8..c8ed1e4f24 100644
--- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs
+++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs
@@ -62,7 +62,7 @@ public async Task TestLogsAllCategoriesAllLevels(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
+ Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
}
///
@@ -84,7 +84,7 @@ public async Task TestLogsAllCategoriesDefaultLevel(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
+ Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
}
///
@@ -116,7 +116,7 @@ public async Task TestLogsAllCategoriesDefaultLevelFallback(TestConfiguration co
ValidateLoggerRemoteCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
+ Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
}
///
@@ -157,7 +157,7 @@ public async Task TestLogsUseAppFilters(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
+ Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
}
///
@@ -182,7 +182,7 @@ public async Task TestLogsUseAppFiltersAndFilterSpecs(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
+ Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
}
///
@@ -213,7 +213,7 @@ public async Task TestLogsWildcardCategory(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
+ Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
}
private async Task GetLogsAsync(TestConfiguration config, Action settingsCallback = null)
diff --git a/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj b/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj
index 513ca6baa5..808617aa05 100644
--- a/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj
+++ b/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj
@@ -1,6 +1,6 @@
- $(NetCoreAppTestTargetFramework)
+ $(NetCoreAppMinTargetFramework)
;1591;1701
diff --git a/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj b/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj
index 3805aa1334..e2dc9e42a7 100644
--- a/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj
+++ b/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj
@@ -1,6 +1,6 @@
- $(NetCoreAppTestTargetFramework)
+ $(NetCoreAppMinTargetFramework)
;1591;1701
diff --git a/src/tests/SOS.UnitTests/SOS.UnitTests.csproj b/src/tests/SOS.UnitTests/SOS.UnitTests.csproj
index df9a23e046..d2f3b4cbb7 100644
--- a/src/tests/SOS.UnitTests/SOS.UnitTests.csproj
+++ b/src/tests/SOS.UnitTests/SOS.UnitTests.csproj
@@ -1,6 +1,6 @@
- $(NetCoreAppTestTargetFramework)
+ $(NetCoreAppMinTargetFramework)
false
;1591;1701
$(DefineConstants);CORE_CLR
diff --git a/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj b/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj
index 6472bcfb65..aa872e9b45 100644
--- a/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj
+++ b/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppTestTargetFramework)
+ $(NetCoreAppMinTargetFramework)
diff --git a/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj b/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj
index 056dd23295..9d70543023 100644
--- a/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj
+++ b/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppTestTargetFramework)
+ $(NetCoreAppMinTargetFramework)
From 7895776ee60a1a8f9f4afa257e191a97ea674e16 Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:56:02 -0700
Subject: [PATCH 04/10] Skip debuggee/runtime rebuild on test legs; bundle
dotnet-test
Build legs already compile all debuggee TFMs and install test runtimes; test
legs were redundantly rebuilding both. Publish artifacts/dotnet-test in the
TestArtifacts package so test legs download SDK+runtimes and skip InstallRuntimes
and BuildDebuggees via SkipTestArtifactsBuild. buildOnly still builds everything.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
eng/build.ps1 | 27 +++++----------------------
eng/build.sh | 26 ++++++--------------------
eng/pipelines/build.yml | 1 +
src/tests/dirs.proj | 8 +++++---
4 files changed, 17 insertions(+), 45 deletions(-)
diff --git a/eng/build.ps1 b/eng/build.ps1
index 0dceb53c6a..632edd263c 100644
--- a/eng/build.ps1
+++ b/eng/build.ps1
@@ -125,28 +125,10 @@ if ($test) {
}
# When the managed build was skipped (e.g. the test-only CI legs that download prebuilt
- # product binaries), the test SDK and runtimes that are normally installed during the
- # managed build (InstallRuntimes.proj, InstallTestRuntimes AfterTargets=Build) were never
- # installed on this machine. Build just that project here so the runtimes are present
- # before the tests run. This is ordered ahead of -test, unlike relying on the parallel
- # test traversal, and avoids rebuilding the downloaded product.
- if ($skipmanaged) {
- & "$engroot\common\build.ps1" `
- -restore `
- -build `
- -projects "$engroot\InstallRuntimes.proj" `
- -configuration $configuration `
- -verbosity $verbosity `
- -ci:$ci `
- /bl:$logdir\InstallTestRuntimes.binlog `
- /p:TargetOS=$os `
- /p:TargetArch=$architecture `
- /p:LiveRuntimeDir="$liveRuntimeDir"
-
- if ($lastExitCode -ne 0) {
- exit $lastExitCode
- }
- }
+ # product binaries), the test SDK, runtimes, and debuggees that are normally produced by
+ # the managed build (InstallRuntimes.proj + BuildDebuggees in src/tests/dirs.proj) were
+ # downloaded as part of TestArtifacts. Skip rebuilding them so this leg only runs tests.
+ $skipTestArtifactsBuild = if ($skipmanaged) { 'true' } else { 'false' }
& "$engroot\common\build.ps1" `
-test `
@@ -158,6 +140,7 @@ if ($test) {
/p:TargetOS=$os `
/p:TargetArch=$architecture `
/p:TestArchitectures=$architecture `
+ /p:SkipTestArtifactsBuild=$skipTestArtifactsBuild `
/p:DotnetRuntimeVersion="$dotnetruntimeversion" `
/p:DotnetRuntimeDownloadVersion="$dotnetruntimedownloadversion" `
/p:RuntimeSourceFeed="$runtimesourcefeed" `
diff --git a/eng/build.sh b/eng/build.sh
index d1a8599b57..b6053294ae 100755
--- a/eng/build.sh
+++ b/eng/build.sh
@@ -354,29 +354,14 @@ if [[ "$__Test" == 1 ]]; then
fi
# When the managed build was skipped (e.g. the test-only CI legs that download prebuilt
- # product binaries), the test SDK and runtimes that are normally installed during the
- # managed build (InstallRuntimes.proj, InstallTestRuntimes AfterTargets=Build) were never
- # installed on this machine. Build just that project here so the runtimes are present
- # before the tests run. This is ordered ahead of --test, unlike relying on the parallel
- # test traversal, and avoids rebuilding the downloaded product.
+ # product binaries), the test SDK, runtimes, and debuggees that are normally produced by
+ # the managed build (InstallRuntimes.proj + BuildDebuggees in src/tests/dirs.proj) were
+ # downloaded as part of TestArtifacts. Skip rebuilding them so this leg only runs tests.
__TestRestoreArg=
+ __SkipTestArtifactsBuild=false
if [[ "$__ManagedBuild" == 0 ]]; then
__TestRestoreArg=--restore
-
- "$__RepoRootDir/eng/common/build.sh" \
- --restore \
- --build \
- --projects "$__RepoRootDir/eng/InstallRuntimes.proj" \
- --configuration "$__BuildType" \
- /bl:"$__LogsDir"/InstallTestRuntimes.binlog \
- /p:TargetArch="$__TargetArch" \
- /p:TargetRid="$__TargetRid" \
- /p:LiveRuntimeDir="$__LiveRuntimeDir" \
- $__CommonMSBuildArgs
-
- if [ "$?" != 0 ]; then
- exit 1
- fi
+ __SkipTestArtifactsBuild=true
fi
# __CommonMSBuildArgs contains TargetOS property
@@ -387,6 +372,7 @@ if [[ "$__Test" == 1 ]]; then
/bl:"$__LogsDir"/Test.binlog \
/p:TargetArch="$__TargetArch" \
/p:TargetRid="$__TargetRid" \
+ /p:SkipTestArtifactsBuild="$__SkipTestArtifactsBuild" \
/p:DotnetRuntimeVersion="$__DotnetRuntimeVersion" \
/p:DotnetRuntimeDownloadVersion="$__DotnetRuntimeDownloadVersion" \
/p:RuntimeSourceFeed="$__RuntimeSourceFeed" \
diff --git a/eng/pipelines/build.yml b/eng/pipelines/build.yml
index 0b90f515f8..7906f41137 100644
--- a/eng/pipelines/build.yml
+++ b/eng/pipelines/build.yml
@@ -218,6 +218,7 @@ jobs:
Contents: |
bin/**
test/**
+ dotnet-test/**
TargetFolder: $(Build.ArtifactStagingDirectory)/testartifacts
- template: /eng/pipelines/publish-pipeline-artifact-shim.yml@self
diff --git a/src/tests/dirs.proj b/src/tests/dirs.proj
index c57f908ddc..aef9406cb7 100644
--- a/src/tests/dirs.proj
+++ b/src/tests/dirs.proj
@@ -16,8 +16,10 @@
-
+ result cache, even when multiple test projects depend on it.
+ Skipped on test-only legs ($(SkipTestArtifactsBuild)) which download a
+ prebuilt artifacts/dotnet-test directory instead of installing locally. -->
+
From a5ed15679f118240014837dc5b3b2c8374616067 Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Sun, 28 Jun 2026 23:51:27 -0700
Subject: [PATCH 05/10] Install runtimes fresh on test legs instead of bundling
dotnet-test
CopyFiles/DownloadPipelineArtifact dropped exec bits on the bundled dotnet-test host, breaking every test that launches a debuggee. Install runtimes locally on test legs (cheap, correct perms) and only skip the expensive debuggee rebuild via SkipTestArtifactsBuild.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
eng/build.ps1 | 20 ++++++++++++++++++--
eng/build.sh | 17 +++++++++++++++--
eng/pipelines/build.yml | 1 -
src/tests/dirs.proj | 6 +++---
4 files changed, 36 insertions(+), 8 deletions(-)
diff --git a/eng/build.ps1 b/eng/build.ps1
index 632edd263c..0f64647493 100644
--- a/eng/build.ps1
+++ b/eng/build.ps1
@@ -125,11 +125,27 @@ if ($test) {
}
# When the managed build was skipped (e.g. the test-only CI legs that download prebuilt
- # product binaries), the test SDK, runtimes, and debuggees that are normally produced by
- # the managed build (InstallRuntimes.proj + BuildDebuggees in src/tests/dirs.proj) were
+ # product binaries), the debuggees built by BuildDebuggees in src/tests/dirs.proj were
# downloaded as part of TestArtifacts. Skip rebuilding them so this leg only runs tests.
+ # Test runtimes are still installed locally below (cheap, ensures correct file permissions).
$skipTestArtifactsBuild = if ($skipmanaged) { 'true' } else { 'false' }
+ # The managed build normally installs the test SDK/runtimes via an InstallRuntimes.proj
+ # ProjectReference. The -test step runs with Build=false, so install them explicitly here.
+ if ($skipmanaged) {
+ & "$engroot\common\build.ps1" `
+ -restore -build `
+ -projects "$engroot\InstallRuntimes.proj" `
+ -configuration $configuration `
+ -verbosity $verbosity `
+ -ci:$ci `
+ /p:TargetOS=$os `
+ /p:TargetArch=$architecture
+ if ($lastExitCode -ne 0) {
+ exit $lastExitCode
+ }
+ }
+
& "$engroot\common\build.ps1" `
-test `
-restore:$skipmanaged `
diff --git a/eng/build.sh b/eng/build.sh
index b6053294ae..76a5874586 100755
--- a/eng/build.sh
+++ b/eng/build.sh
@@ -354,14 +354,27 @@ if [[ "$__Test" == 1 ]]; then
fi
# When the managed build was skipped (e.g. the test-only CI legs that download prebuilt
- # product binaries), the test SDK, runtimes, and debuggees that are normally produced by
- # the managed build (InstallRuntimes.proj + BuildDebuggees in src/tests/dirs.proj) were
+ # product binaries), the debuggees built by BuildDebuggees in src/tests/dirs.proj were
# downloaded as part of TestArtifacts. Skip rebuilding them so this leg only runs tests.
+ # Test runtimes are still installed locally below (cheap, ensures correct file permissions).
__TestRestoreArg=
__SkipTestArtifactsBuild=false
if [[ "$__ManagedBuild" == 0 ]]; then
__TestRestoreArg=--restore
__SkipTestArtifactsBuild=true
+
+ # The managed build normally installs the test SDK/runtimes via an InstallRuntimes.proj
+ # ProjectReference. The --test step runs with Build=false, so install them explicitly here.
+ "$__RepoRootDir/eng/common/build.sh" \
+ --restore --build \
+ --projects "$__RepoRootDir/eng/InstallRuntimes.proj" \
+ --configuration "$__BuildType" \
+ /p:TargetArch="$__TargetArch" \
+ /p:TargetRid="$__TargetRid" \
+ $__CommonMSBuildArgs
+ if [ $? != 0 ]; then
+ exit 1
+ fi
fi
# __CommonMSBuildArgs contains TargetOS property
diff --git a/eng/pipelines/build.yml b/eng/pipelines/build.yml
index 7906f41137..0b90f515f8 100644
--- a/eng/pipelines/build.yml
+++ b/eng/pipelines/build.yml
@@ -218,7 +218,6 @@ jobs:
Contents: |
bin/**
test/**
- dotnet-test/**
TargetFolder: $(Build.ArtifactStagingDirectory)/testartifacts
- template: /eng/pipelines/publish-pipeline-artifact-shim.yml@self
diff --git a/src/tests/dirs.proj b/src/tests/dirs.proj
index aef9406cb7..a37899c9ce 100644
--- a/src/tests/dirs.proj
+++ b/src/tests/dirs.proj
@@ -17,9 +17,9 @@
-
+ Test-only legs install runtimes locally too (cheap, gets correct file perms)
+ and only skip the expensive debuggee rebuild via $(SkipTestArtifactsBuild). -->
+
8.0
net$(NetCoreAppMinVersion)
+
+ 10.0
+ net$(NetCoreAppTestVersion)
net8.0;net9.0;net10.0;net11.0
- net8.0
+ $(NetCoreAppTestTargetFramework)
diff --git a/global.json b/global.json
index 75792e25d1..c212145c75 100644
--- a/global.json
+++ b/global.json
@@ -5,15 +5,7 @@
"rollForward": "major"
},
"tools": {
- "dotnet": "10.0.108",
- "runtimes": {
- "dotnet": [
- "$(MicrosoftNETCoreApp80Version)"
- ],
- "dotnet/x86": [
- "$(MicrosoftNETCoreApp80Version)"
- ]
- }
+ "dotnet": "10.0.108"
},
"msbuild-sdks": {
"Microsoft.Build.NoTargets": "3.7.0",
diff --git a/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj b/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj
index c931e79852..3b9c84c185 100644
--- a/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj
+++ b/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
true
$(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt
1.0.351101
diff --git a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj
index 9a4014149c..e71b2f7d7f 100644
--- a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj
+++ b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
$(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt
1.0.351101
diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs
index c8ed1e4f24..1b1d00e0e8 100644
--- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs
+++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs
@@ -62,7 +62,7 @@ public async Task TestLogsAllCategoriesAllLevels(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
///
@@ -84,7 +84,7 @@ public async Task TestLogsAllCategoriesDefaultLevel(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
///
@@ -116,7 +116,7 @@ public async Task TestLogsAllCategoriesDefaultLevelFallback(TestConfiguration co
ValidateLoggerRemoteCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
///
@@ -157,7 +157,7 @@ public async Task TestLogsUseAppFilters(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
///
@@ -182,7 +182,7 @@ public async Task TestLogsUseAppFiltersAndFilterSpecs(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
///
@@ -213,7 +213,7 @@ public async Task TestLogsWildcardCategory(TestConfiguration config)
ValidateAppLoggerCategoryWarningMessage(reader);
ValidateAppLoggerCategoryErrorMessage(reader);
- Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+ Assert.True(string.IsNullOrEmpty(await reader.ReadToEndAsync()), "Expected to have read all entries from stream.");
}
private async Task GetLogsAsync(TestConfiguration config, Action settingsCallback = null)
diff --git a/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj b/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj
index 808617aa05..513ca6baa5 100644
--- a/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj
+++ b/src/tests/Microsoft.FileFormats.UnitTests/Microsoft.FileFormats.UnitTests.csproj
@@ -1,6 +1,6 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
;1591;1701
diff --git a/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj b/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj
index e2dc9e42a7..3805aa1334 100644
--- a/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj
+++ b/src/tests/Microsoft.SymbolStore.UnitTests/Microsoft.SymbolStore.UnitTests.csproj
@@ -1,6 +1,6 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
;1591;1701
diff --git a/src/tests/SOS.UnitTests/SOS.UnitTests.csproj b/src/tests/SOS.UnitTests/SOS.UnitTests.csproj
index d2f3b4cbb7..df9a23e046 100644
--- a/src/tests/SOS.UnitTests/SOS.UnitTests.csproj
+++ b/src/tests/SOS.UnitTests/SOS.UnitTests.csproj
@@ -1,6 +1,6 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
false
;1591;1701
$(DefineConstants);CORE_CLR
diff --git a/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj b/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj
index aa872e9b45..6472bcfb65 100644
--- a/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj
+++ b/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
diff --git a/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj b/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj
index 9d70543023..056dd23295 100644
--- a/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj
+++ b/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj
@@ -1,7 +1,7 @@
- $(NetCoreAppMinTargetFramework)
+ $(NetCoreAppTestTargetFramework)
From 2409ca22f86fdcc0e26d9c18612c9a2c6e65ebe9 Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Mon, 29 Jun 2026 20:16:03 -0700
Subject: [PATCH 07/10] Install x86 net10 runtime for x86 test host
The x86 test legs failed because Arcade launches the xUnit runner via
$(DotNetRoot)x86/dotnet.exe and silently falls back to the x64 host when
that path is missing. Without an x86 runtime in .dotnet/x86, the runner ran
64-bit, so the DbgShim/SOS harness resolved the native binary directory from
the process architecture as Windows_NT.x64.Release and failed to load the
x86 dbgshim.dll/SOS bits.
Restore the tools.runtimes dotnet/x86 entry (pointed at net10) so Arcade
provisions the x86 host the test runner needs. dotnet-test only provides the
arch-matched runtime for debuggees, not the runner host itself.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
global.json | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/global.json b/global.json
index c212145c75..979ba15a3a 100644
--- a/global.json
+++ b/global.json
@@ -5,7 +5,12 @@
"rollForward": "major"
},
"tools": {
- "dotnet": "10.0.108"
+ "dotnet": "10.0.108",
+ "runtimes": {
+ "dotnet/x86": [
+ "$(MicrosoftNETCoreApp100Version)"
+ ]
+ }
},
"msbuild-sdks": {
"Microsoft.Build.NoTargets": "3.7.0",
From 6fce10de133b504cf99499b1d278e9ea021d25e3 Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Tue, 30 Jun 2026 00:03:10 -0700
Subject: [PATCH 08/10] Publish obj artifacts and use test.cmd/test.sh for
test-only legs
- Include obj/** in the published TestArtifacts so the test-only legs have the
MSBuild intermediate outputs. Without them the incremental up-to-date check
misses and CoreCompile/Csc re-runs for product and test projects on the test
leg even though bin/** was downloaded.
- Test-only legs now invoke test.cmd/test.sh, which already encode
'-test -skipmanaged -skipnative' and do not prepend the inert '-restore -build'
that build.cmd/build.sh add.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
eng/pipelines/build.yml | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/eng/pipelines/build.yml b/eng/pipelines/build.yml
index 0b90f515f8..d6b95a98ad 100644
--- a/eng/pipelines/build.yml
+++ b/eng/pipelines/build.yml
@@ -134,16 +134,24 @@ jobs:
- _TestArgs: '-test'
- _Cross: ''
- - ${{ if eq(parameters.osGroup, 'Windows_NT') }}:
- - _buildScript: $(Build.SourcesDirectory)\build.cmd
- - ${{ if ne(parameters.osGroup, 'Windows_NT') }}:
- - _buildScript: $(Build.SourcesDirectory)/build.sh
-
- ${{ if and(eq(parameters.testOnly, 'true'), eq(parameters.buildOnly, 'true')) }}:
'error, testOnly and buildOnly cannot be true at the same time': error
+ # Test-only legs invoke test.cmd/test.sh, which already encode
+ # '-test -skipmanaged -skipnative' and do not prepend '-restore -build'. All other legs
+ # (build-only and build+test) use build.cmd/build.sh, which prepend '-restore -build'.
- ${{ if eq(parameters.testOnly, 'true') }}:
- - _TestArgs: '-test -skipnative -skipmanaged'
+ - ${{ if eq(parameters.osGroup, 'Windows_NT') }}:
+ - _buildScript: $(Build.SourcesDirectory)\test.cmd
+ - ${{ if ne(parameters.osGroup, 'Windows_NT') }}:
+ - _buildScript: $(Build.SourcesDirectory)/test.sh
+ - _TestArgs: ''
+
+ - ${{ if ne(parameters.testOnly, 'true') }}:
+ - ${{ if eq(parameters.osGroup, 'Windows_NT') }}:
+ - _buildScript: $(Build.SourcesDirectory)\build.cmd
+ - ${{ if ne(parameters.osGroup, 'Windows_NT') }}:
+ - _buildScript: $(Build.SourcesDirectory)/build.sh
- ${{ if eq(parameters.buildOnly, 'true') }}:
- _TestArgs: ''
@@ -217,6 +225,7 @@ jobs:
SourceFolder: '$(Build.SourcesDirectory)/artifacts'
Contents: |
bin/**
+ obj/**
test/**
TargetFolder: $(Build.ArtifactStagingDirectory)/testartifacts
From be8763ff7ce096e0a99e833af74b63b68ce93d59 Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Tue, 30 Jun 2026 12:10:57 -0700
Subject: [PATCH 09/10] Drop Traversal Test->Build dependency on prebuilt test
legs
The Microsoft.Build.Traversal SDK hardcodes TestDependsOn=Build, so every
traversal's Test target (build.proj, src/dirs.proj, src/tests/dirs.proj)
rebuilds its entire subgraph before running tests, regardless of whether
-build was passed. On the test-only CI legs the product and test binaries
are downloaded as prebuilt TestArtifacts; rebuilding them is wasted work,
and because pipeline artifact downloads do not preserve file timestamps the
incremental check fails and it becomes a full recompile.
Override TestDependsOn (empty) via CustomAfterTraversalTargets when the
managed build was skipped (SkipTestArtifactsBuild=true, set by build.ps1/
build.sh for -skipmanaged). The Test target still invokes Test on each
referenced project, so tests run directly against the downloaded binaries.
Validated locally: test leg binlog shows Csc=0, CoreCompile not invoked,
RunTests executed on all test projects.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
Directory.Build.props | 6 ++++++
eng/TraversalOverrides.targets | 22 ++++++++++++++++++++++
2 files changed, 28 insertions(+)
create mode 100644 eng/TraversalOverrides.targets
diff --git a/Directory.Build.props b/Directory.Build.props
index e6a583c5c6..0f9ee24cc2 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -22,6 +22,12 @@
diagnostics
+
+
+ $(MSBuildThisFileDirectory)eng/TraversalOverrides.targets
+
+
+
+
+
+
+
From fc39af3438a30f0773da363a261f892d70d538dc Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Tue, 30 Jun 2026 15:47:20 -0700
Subject: [PATCH 10/10] Reduce verbose MSBuild logging in test/debuggee builds
Lower the importance of two diagnostic messages that were emitted at High
importance (shown even at minimal verbosity) and added noise to every build:
- SdkPackOverrides.targets: OverrideTestingVersions message -> Low
(folds in dotnet/diagnostics#5869).
- Debuggees Directory.Build.targets: interpreter-test skip message -> default
importance.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
eng/AuxMsbuildFiles/SdkPackOverrides.targets | 2 +-
src/tests/SOS.UnitTests/Debuggees/Directory.Build.targets | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/eng/AuxMsbuildFiles/SdkPackOverrides.targets b/eng/AuxMsbuildFiles/SdkPackOverrides.targets
index a2c3023663..94e01592e9 100644
--- a/eng/AuxMsbuildFiles/SdkPackOverrides.targets
+++ b/eng/AuxMsbuildFiles/SdkPackOverrides.targets
@@ -13,7 +13,7 @@
@(RuntimeTestVersions->WithMetadataValue('TargetFramework', 'net11.0')->Metadata('AspNet'))
-
+
diff --git a/src/tests/SOS.UnitTests/Debuggees/Directory.Build.targets b/src/tests/SOS.UnitTests/Debuggees/Directory.Build.targets
index 309df60cdd..3671dfe974 100644
--- a/src/tests/SOS.UnitTests/Debuggees/Directory.Build.targets
+++ b/src/tests/SOS.UnitTests/Debuggees/Directory.Build.targets
@@ -40,8 +40,7 @@
-
+