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 @@ - +