diff --git a/.github/skills/diagnostics-pr-build-results/SKILL.md b/.github/skills/diagnostics-pr-build-results/SKILL.md new file mode 100644 index 0000000000..a4b8d2df93 --- /dev/null +++ b/.github/skills/diagnostics-pr-build-results/SKILL.md @@ -0,0 +1,163 @@ +--- +name: diagnostics-pr-build-results +description: Look up CI build results for a pull request to the diagnostics repository. Use this when asked to check build status, investigate CI failures, or get test results for a PR number. Covers ADO pipeline results and Helix test work items. +--- + +# Diagnostics PR Build Results Lookup + +Use this skill to find and analyze CI build results for pull requests in the dotnet/diagnostics repository. + +## Pipeline Details + +- **ADO Organization**: `dnceng-public` +- **ADO Project**: `public` +- **Pipeline Definition ID**: `25` (diagnostics-public-ci) +- **Pipeline URL**: https://dev.azure.com/dnceng-public/public/_build?definitionId=25 +- **Helix API**: https://helix.dot.net/api + +## Step 1: Find the ADO Build for a PR + +### Option A: From GitHub (preferred) + +Use the GitHub MCP server to get check runs for the PR: + +1. Use `github-mcp-server-pull_request_read` with `method: "get_status"` on `owner: "dotnet"`, `repo: "diagnostics"`, `pullNumber: ` to get the commit status and check runs. +2. Look for the check run from Azure Pipelines — it will have a `target_url` pointing to `dev.azure.com/dnceng-public/public/_build/results?buildId=`. +3. Extract the `buildId` from the URL. + +### Option B: From ADO directly + +Query ADO for builds triggered by the PR branch: + +``` +GET https://dev.azure.com/dnceng-public/public/_apis/build/builds?definitions=25&branchName=refs/pull//merge&$top=5&api-version=7.0 +``` + +Use the ADO PAT for authentication (Basic auth with `:PAT` base64-encoded). + +## Step 2: Get Build Results from ADO + +Once you have the `buildId`: + +1. **Get the build timeline** to see all jobs and their status: + ``` + GET https://dev.azure.com/dnceng-public/public/_apis/build/builds//timeline?api-version=7.0 + ``` + +2. **Find "Send Tests to Helix" tasks** in the timeline records: + - Filter for records where `name` contains "Send Tests to Helix" + - Check `result` (succeeded/failed) and `state` (completed) + - Each has a parent record chain: Stage > Phase > Job > Task + +3. **Get Helix Job IDs** from the task logs: + - Get the log URL from `record.log.url` + - Fetch the log content and search for `Sent Helix Job.*jobs/([a-f0-9-]+)` to extract Helix job IDs + +4. **Map jobs to build configurations** by traversing the parent chain in the timeline: + - Task → Job → Phase (has config name like `MacOS_arm64_Debug`) → Stage + +## Step 3: Query Helix for Test Results + +With the Helix job ID and access token: + +1. **List work items**: + ``` + GET https://helix.dot.net/api/jobs//workitems?api-version=2019-06-17&access_token= + ``` + +2. **Get work item details** (for failed items): + ``` + GET https://helix.dot.net/api/jobs//workitems/?api-version=2019-06-17&access_token= + ``` + Returns `State` (Failed/Passed), `ExitCode`, `ConsoleOutputUri`, `Files`. + +3. **Get console log** for a failed work item: + ``` + GET &access_token= + ``` + Search for `FAIL`, `Error Message:`, `Assert`, `Exception` lines. + +## Step 4: Summarize Results + +Present results in a table format grouped by platform: + +``` +| Platform | Config | Test Suite | Result | Details | +|-------------------|---------|-------------------|--------|-------------------| +| Linux_x64 | Debug | EventPipe | PASS | | +| Linux_x64 | Debug | SOS.UnitTests | FAIL | LLDB 14 broken | +``` + +## Authentication + +### ADO PAT +Use Basic authentication with the PAT. The user should provide the PAT, or it may be in the session context. +```powershell +$b64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$pat")) +$hdrs = @{ Authorization = "Basic $b64" } +Invoke-RestMethod -Uri $url -Headers $hdrs +``` + +### Helix Access Token +Append `&access_token=` to Helix API URLs. The user should provide the token, or it may be in the session context. + +## Common Test Suites + +These are the Helix work items sent by the diagnostics pipeline: + +| Work Item | Description | +|-----------|-------------| +| EventPipe.UnitTests | EventPipe functionality | +| Microsoft.Diagnostics.Monitoring.UnitTests | Monitoring library | +| Microsoft.FileFormats.UnitTests | File format parsing | +| Microsoft.SymbolStore.UnitTests | Symbol store | +| DotnetCounters.UnitTests | dotnet-counters tool | +| DotnetTrace.UnitTests | dotnet-trace tool | +| Microsoft.Diagnostics.NETCore.Client.UnitTests | Diagnostics client library | +| Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests | EventPipe monitoring | +| SOS.UnitTests | SOS debugger extension (uses LLDB/cdb) | +| DbgShim.UnitTests | Debug shim (native dependencies) | + +## Build Configurations + +The pipeline runs these configurations (defined in `diagnostics.yml`): + +| OS | Architecture | Config | Helix Queue | +|----|-------------|--------|-------------| +| Windows_NT | x64 | Debug/Release | Windows.Amd64.Server2022.Open | +| Windows_NT | x86 | Release | Windows.Amd64.Server2022.Open | +| linux | x64 | Debug/Release | Ubuntu.2204.Amd64.Open | +| linux (musl) | x64 | Debug/Release | Ubuntu.2204.Amd64.Open | +| osx | x64 | Debug/Release | OSX.15.Amd64.Open | +| osx | arm64 | Debug/Release | OSX.15.Arm64.Open | + +## Quick Reference Script + +Here's a PowerShell script to quickly get all Helix results for a build: + +```powershell +$pat = "" +$helixToken = "" +$buildId = "" + +$b64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$pat")) +$hdrs = @{ Authorization = "Basic $b64" } + +# Get timeline +$timeline = Invoke-RestMethod -Uri "https://dev.azure.com/dnceng-public/public/_apis/build/builds/$buildId/timeline?api-version=7.0" -Headers $hdrs + +# Find Helix tasks and extract job IDs +$helixTasks = $timeline.records | Where-Object { $_.name -like "*Send Tests to Helix*" -and $_.state -eq 'completed' } +foreach ($ht in $helixTasks) { + $parent = $timeline.records | Where-Object { $_.id -eq $ht.parentId } + $grandparent = $timeline.records | Where-Object { $_.id -eq $parent.parentId } + $logContent = Invoke-RestMethod -Uri $ht.log.url -Headers $hdrs + if ($logContent -match 'jobs/([a-f0-9-]+)') { + $jobId = $Matches[1] + Write-Host "$($grandparent.name): $($ht.result) | jobId=$jobId" + # Get work items + $items = Invoke-RestMethod "https://helix.dot.net/api/jobs/$jobId/workitems?api-version=2019-06-17&access_token=$helixToken" + # ... process items + } +} +``` diff --git a/Directory.Build.props b/Directory.Build.props index c2fceffbab..b92fa8c8f7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,15 +4,6 @@ false - - false - Latest - 4 - true - true - <_SkipUpgradeNetAnalyzersNuGetWarning>true - - http://go.microsoft.com/fwlink/?LinkID=288859 git @@ -21,6 +12,13 @@ diagnostics + + - 8.0 - net$(NetCoreAppMinVersion) - - net8.0;net9.0;net10.0 - - net8.0 - - diff --git a/diagnostics.yml b/diagnostics.yml index 389526e741..17112808d2 100644 --- a/diagnostics.yml +++ b/diagnostics.yml @@ -19,6 +19,10 @@ parameters: displayName: Build only (skip tests) type: boolean default: false +- name: useHelix + displayName: Run tests on Helix (distributed testing) + type: boolean + default: true trigger: none @@ -52,7 +56,7 @@ extends: parameters: stages: - stage: build - displayName: Build and Test Diagnostics + displayName: Build Diagnostics jobs: ############################ @@ -80,6 +84,9 @@ extends: name: Windows osGroup: Windows_NT buildOnly: ${{ parameters.buildOnly }} + useHelix: ${{ parameters.useHelix }} + isOfficialBuild: ${{ eq(variables['System.TeamProject'], 'internal') }} + helixQueues: $(HelixQueuesWindows_x64) buildConfigs: - configuration: Debug architecture: x64 @@ -100,7 +107,10 @@ extends: osGroup: Linux container: linux_x64 crossBuild: true - buildOnly: true + buildOnly: ${{ parameters.buildOnly }} + useHelix: ${{ parameters.useHelix }} + isOfficialBuild: ${{ eq(variables['System.TeamProject'], 'internal') }} + helixQueues: $(HelixQueuesLinux_x64) buildConfigs: - configuration: Release architecture: x64 @@ -118,7 +128,10 @@ extends: osSuffix: -musl container: linux_musl_x64 crossBuild: true - buildOnly: true + buildOnly: ${{ parameters.buildOnly }} + useHelix: ${{ parameters.useHelix }} + isOfficialBuild: ${{ eq(variables['System.TeamProject'], 'internal') }} + helixQueues: $(HelixQueuesLinux_musl_x64) buildConfigs: - configuration: Release architecture: x64 @@ -135,6 +148,9 @@ extends: jobTemplate: ${{ variables.jobTemplate }} osGroup: MacOS buildOnly: ${{ parameters.buildOnly }} + useHelix: ${{ parameters.useHelix }} + isOfficialBuild: ${{ eq(variables['System.TeamProject'], 'internal') }} + helixQueues: $(HelixQueuesMacOS_x64) buildConfigs: - configuration: Release architecture: x64 @@ -148,7 +164,10 @@ extends: jobTemplate: ${{ variables.jobTemplate }} osGroup: MacOS crossBuild: true - buildOnly: true + buildOnly: ${{ parameters.buildOnly }} + useHelix: ${{ parameters.useHelix }} + isOfficialBuild: ${{ eq(variables['System.TeamProject'], 'internal') }} + helixQueues: $(HelixQueuesMacOS_arm64) buildConfigs: - configuration: Release architecture: arm64 @@ -176,7 +195,10 @@ extends: osGroup: Linux container: linux_arm64 crossBuild: true - buildOnly: true + buildOnly: ${{ parameters.buildOnly }} + useHelix: ${{ parameters.useHelix }} + isOfficialBuild: ${{ eq(variables['System.TeamProject'], 'internal') }} + helixQueues: $(HelixQueuesLinux_arm64) buildConfigs: - configuration: Release architecture: arm64 @@ -205,86 +227,20 @@ extends: osSuffix: -musl container: linux_musl_arm64 crossBuild: true - buildOnly: true + buildOnly: ${{ parameters.buildOnly }} + useHelix: ${{ parameters.useHelix }} + isOfficialBuild: ${{ eq(variables['System.TeamProject'], 'internal') }} + helixQueues: $(HelixQueuesLinux_musl_arm64) buildConfigs: - configuration: Release architecture: arm64 artifactUploadPath: bin/linux.arm64.Release artifactTargetPath: bin/linux-musl.arm64.Release - ############################ - # # - # Test only legs # - # # - ############################ - - - ${{ if ne(parameters.buildOnly, true) }}: - - template: /eng/pipelines/build.yml - parameters: - jobTemplate: ${{ variables.jobTemplate }} - name: Ubuntu_22_04 - osGroup: Linux - container: test_ubuntu_22_04 - dependsOn: Linux - testOnly: true - buildConfigs: - - configuration: Release - architecture: x64 - - ${{ if in(variables['Build.Reason'], 'PullRequest') }}: - - configuration: Debug - architecture: x64 - - - template: /eng/pipelines/build.yml - parameters: - jobTemplate: ${{ variables.jobTemplate }} - name: Alpine3_19 - osGroup: Linux - osSuffix: -musl - container: test_linux_musl_x64 - dependsOn: Linux_musl - testOnly: true - disableComponentGovernance: true - buildConfigs: - - configuration: Release - architecture: x64 - - ${{ if in(variables['Build.Reason'], 'PullRequest') }}: - - configuration: Debug - architecture: x64 - - - ${{ if ne(variables['System.TeamProject'], 'public') }}: - - template: /eng/pipelines/build.yml - parameters: - jobTemplate: ${{ variables.jobTemplate }} - name: Debian_Bullseye - osGroup: Linux - container: test_debian_11_amd64 - dependsOn: Linux - testOnly: true - buildConfigs: - - configuration: Release - architecture: x64 - - ${{ if in(variables['Build.Reason'], 'PullRequest') }}: - - configuration: Debug - architecture: x64 - - - template: /eng/pipelines/build.yml - parameters: - jobTemplate: ${{ variables.jobTemplate }} - name: Fedora_39 - osGroup: Linux - container: test_fedora - dependsOn: Linux - testOnly: true - buildConfigs: - - configuration: Release - architecture: x64 - - ${{ if in(variables['Build.Reason'], 'PullRequest') }}: - - configuration: Debug - architecture: x64 - - ${{ 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/AuxMsbuildFiles/Directory.Build.props b/eng/AuxMsbuildFiles/Directory.Build.props index 8231a8a4fd..c01f53523a 100644 --- a/eng/AuxMsbuildFiles/Directory.Build.props +++ b/eng/AuxMsbuildFiles/Directory.Build.props @@ -1,8 +1,6 @@ - - - + + bin\ diff --git a/eng/AuxMsbuildFiles/Directory.Build.targets b/eng/AuxMsbuildFiles/Directory.Build.targets index 6384491004..8bf348bb69 100644 --- a/eng/AuxMsbuildFiles/Directory.Build.targets +++ b/eng/AuxMsbuildFiles/Directory.Build.targets @@ -1,6 +1,4 @@ - - diff --git a/eng/Common.props b/eng/Common.props new file mode 100644 index 0000000000..9011cb719e --- /dev/null +++ b/eng/Common.props @@ -0,0 +1,38 @@ + + + + Latest + false + 4 + true + true + <_SkipUpgradeNetAnalyzersNuGetWarning>true + + net462 + + 8.0 + net$(NetCoreAppMinVersion) + + net8.0;net9.0;net10.0 + + net8.0 + + diff --git a/eng/helix.proj b/eng/helix.proj new file mode 100644 index 0000000000..06986c26c5 --- /dev/null +++ b/eng/helix.proj @@ -0,0 +1,296 @@ + + + + + + + msbuild + net8.0 + + + pr/$(BUILD_REPOSITORY_NAME)/$(BUILD_SOURCEBRANCH) + local/diagnostics + test/$(Configuration)/$(TargetArchitecture)/ + $(BUILD_BUILDNUMBER) + 0.0.0.0 + $(TargetOS)$(OSSuffix) $(TargetArchitecture) $(Configuration) @ + + + $(ArtifactsDir) + 01:00:00 + + + true + true + + + true + sdk + $([System.Text.RegularExpressions.Regex]::Match('$(MicrosoftNETSdkVersion)', '^\d+\.\d+\.\d+').Value) + + win-x86 + + + <_CorrelationPayload Condition="'$(TargetOS)' == 'Windows_NT'">%HELIX_CORRELATION_PAYLOAD% + <_CorrelationPayload Condition="'$(TargetOS)' != 'Windows_NT'">$HELIX_CORRELATION_PAYLOAD + <_UploadRoot Condition="'$(TargetOS)' == 'Windows_NT'">%HELIX_WORKITEM_UPLOAD_ROOT% + <_UploadRoot Condition="'$(TargetOS)' != 'Windows_NT'">$HELIX_WORKITEM_UPLOAD_ROOT + <_PathSep Condition="'$(TargetOS)' == 'Windows_NT'">\ + <_PathSep Condition="'$(TargetOS)' != 'Windows_NT'">/ + <_DotNetCliDir>$(_CorrelationPayload)$(_PathSep)dotnet-cli + <_ArtifactsDir>$(_CorrelationPayload)$(_PathSep)artifacts$(_PathSep) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @(HelixPreCommand) + + + + + + $(HelixPreCommands);export DOTNET_ROOT=%24(python3 -c "import os; print(os.path.realpath('$DOTNET_ROOT'))") + + + + + + <_VersionsStagingDir>$(ArtifactsDir)tmp\helix\dotnet-test\ + + <_BuildConfigStagingDir>$(ArtifactsDir)tmp\helix\build-config\ + + + + + + + runtime + + + aspnetcore-runtime + + + + + + + + + + + + + + + + + + + 01:30:00 + + + + + + + + + + + artifacts/bin/dotnet-trace + + + artifacts/bin/dotnet-dump + + + artifacts/bin/dotnet-counters + + + + + + + artifacts/test + + + + + + + artifacts/bin/$(TargetOS).$(TargetArchitecture).$(Configuration) + + + + + + + artifacts/bin/SOS.UnitTests + + + + + + <_DebuggeeName Include="Tracee;EventPipeTracee;ExitCodeTracee;StackTracee" /> + <_DebuggeeName Include="SimpleDebuggee;AsyncMain;DivZero;DumpGCData;WebApp3;LineNums;Overflow;SimpleThrow" /> + <_DebuggeeName Include="NestedExceptionTest;TaskNestedException;ReflectionTest;GCPOH;GCWhere" /> + <_DebuggeeName Include="MiniDumpLocalVarLookup;VarargPInvokeInteropMD;DynamicMethod;DotnetDumpCommands" /> + <_DebuggeeName Include="FindRootsOlderGeneration;SymbolTestApp;SymbolTestDll;RandomUserLibrary;TestExtension" /> + + + + + + artifacts/bin/%(Identity) + + + + + + + + + + + <_VersionsLines Include="<Configuration>" /> + <_VersionsLines Include="@(RuntimeTestVersions->' <RuntimeVersion%(Identity)>%(Runtime)</RuntimeVersion%(Identity)>')" /> + <_VersionsLines Include="@(RuntimeTestVersions->' <AspNetCoreVersion%(Identity)>%(AspNet)</AspNetCoreVersion%(Identity)>')" /> + <_VersionsLines Include="@(RuntimeTestVersions->' <TargetFramework%(Identity)>%(TargetFramework)</TargetFramework%(Identity)>')" /> + <_VersionsLines Include="</Configuration>" /> + + + + + + + + + + artifacts/dotnet-test + + + + + + + + + + + + + + + + + + artifacts + + + + + + + + + + + $(TestArtifactsDir)bin\%(HelixTestProject.Identity)\$(Configuration)\$(NetCoreAppMinTargetFramework) + dotnet test %(HelixTestProject.Identity).dll --logger "trx;LogFileName=$(_UploadRoot)$(_PathSep)%(HelixTestProject.Identity).trx" + %(HelixTestProject.CustomTimeout) + $(TestTimeout) + + + + + + + diff --git a/eng/pipelines/build.yml b/eng/pipelines/build.yml index ba061cc2f2..9028093d69 100644 --- a/eng/pipelines/build.yml +++ b/eng/pipelines/build.yml @@ -50,25 +50,16 @@ parameters: type: string default: '' - # Optional: build only job if true + # Optional: build only, skip tests - name: buildOnly type: boolean default: false - # Optional: test only job if true -- name: testOnly - type: boolean - default: false - # Optional: architecture cross build if true - name: crossBuild type: boolean default: false -- name: dependsOn - type: string - default: '' - - name: isCodeQLRun type: boolean default: false @@ -77,6 +68,21 @@ parameters: type: boolean default: false + # Send tests to Helix for distributed execution +- name: useHelix + type: boolean + default: false + + # Helix queue to target (required when useHelix is true) +- name: helixQueues + type: string + default: '' + + # Whether this is an official (non-PR, internal) build +- name: isOfficialBuild + type: boolean + default: false + jobs: - ${{ each config in parameters.buildConfigs }}: - template: ${{ parameters.jobTemplate }} @@ -118,9 +124,6 @@ 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, '') }}: - dependsOn: ${{ parameters.dependsOn }}_${{ config.architecture }}_${{ config.configuration }} - workspace: clean: all @@ -130,7 +133,6 @@ jobs: - _PhaseName: ${{ coalesce(parameters.name, parameters.osGroup) }}_${{ config.architecture }}_${{ config.configuration }} - _Pipeline_StreamDumpDir: $(Build.SourcesDirectory)/artifacts/tmp/${{ config.configuration }}/streams - - _TestArgs: '-test' - _Cross: '' - ${{ if eq(parameters.osGroup, 'Windows_NT') }}: @@ -138,15 +140,6 @@ jobs: - ${{ 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 - - - ${{ if eq(parameters.testOnly, 'true') }}: - - _TestArgs: '-test -skipnative' - - - ${{ if or(eq(parameters.buildOnly, 'true'), eq(parameters.isCodeQLRun, 'true')) }}: - - _TestArgs: '' - # For testing msrc's and service releases. The RuntimeSourceVersion is either "default" or the service release version to test - _InternalInstallArgs: '' - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.isCodeQLRun, 'false')) }}: @@ -160,22 +153,11 @@ jobs: - ${{ if eq(parameters.crossBuild, true) }}: - _Cross: -cross - steps: - - ${{ if eq(parameters.testOnly, true) }}: - - ${{ if ne(parameters.osGroup, 'Linux') }}: - - 'error, testOnly is only supported on Linux': error - - task: DownloadPipelineArtifact@2 - displayName: 'Download Build Artifacts' - inputs: - targetPath: '$(Build.ArtifactStagingDirectory)/__download__' - itemPattern: Build_${{ parameters.dependsOn }}_${{ config.architecture }}_${{ config.configuration }}/bin/** - checkDownloadedFiles: true - - 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 }}' + # Helix variable group (only when useHelix is true, for official builds) + - ${{ if and(eq(parameters.useHelix, true), eq(parameters.isOfficialBuild, true)) }}: + - group: DotNet-HelixApi-Access + steps: - ${{ if eq(parameters.isCodeQLRun, 'true') }}: - task: CodeQL3000Init@0 displayName: CodeQL Initialize @@ -185,22 +167,27 @@ jobs: -binaryLog -configuration ${{ config.configuration }} -architecture ${{ config.architecture }} - $(_TestArgs) $(_Cross) $(_InternalInstallArgs) /p:OfficialBuildId=$(BUILD.BUILDNUMBER) - ${{ if eq(parameters.testOnly, 'true') }}: - displayName: Test - ${{ elseif eq(parameters.buildOnly, 'true') }}: - displayName: Build - ${{ else }}: - displayName: Build / Test + displayName: Build condition: succeeded() - ${{ if eq(parameters.isCodeQLRun, 'true') }}: - task: CodeQL3000Finalize@0 displayName: CodeQL Finalize + # Send tests to Helix (when useHelix is true and not buildOnly) + - ${{ if and(eq(parameters.useHelix, true), ne(parameters.buildOnly, true)) }}: + - template: /eng/pipelines/send-to-helix.yml@self + parameters: + configuration: ${{ config.configuration }} + architecture: ${{ config.architecture }} + osGroup: ${{ parameters.osGroup }} + osSuffix: ${{ parameters.osSuffix }} + helixQueues: ${{ parameters.helixQueues }} + isOfficialBuild: ${{ parameters.isOfficialBuild }} + - ${{ if ne(config.artifactUploadPath, '') }}: - task: CopyFiles@2 displayName: Gather binaries for publish @@ -258,18 +245,3 @@ jobs: sbomEnabled: false # we don't need SBOM for logs continueOnError: true condition: always() - - - ${{ if and(ne(parameters.buildOnly, 'true'), ne(parameters.isCodeQLRun, 'true')) }}: - # Publish test results to Azure Pipelines - - task: PublishTestResults@2 - inputs: - testResultsFormat: xUnit - testResultsFiles: '**/*.xml' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults' - testRunTitle: 'Tests $(_PhaseName)' - failTaskOnFailedTests: true - publishRunAttachments: true - mergeTestResults: true - buildConfiguration: ${{ config.configuration }} - continueOnError: true - condition: always() diff --git a/eng/pipelines/global-variables.yml b/eng/pipelines/global-variables.yml index 92a3439d9f..0818636278 100644 --- a/eng/pipelines/global-variables.yml +++ b/eng/pipelines/global-variables.yml @@ -57,3 +57,46 @@ variables: - ${{ if eq(parameters.runtimeFeedToken, 'dotnetclimsrc-sas-token-base64') }}: - name: RuntimeFeedBase64SasToken value: $(dotnetclimsrc-read-sas-token-base64) + +# Helix queue configuration +# Public (open) queues +- ${{ if eq(variables['System.TeamProject'], 'public') }}: + - name: HelixQueuesWindows_x64 + value: Windows.Amd64.Server2022.Open + - name: HelixQueuesWindows_x86 + value: Windows.Amd64.Server2022.Open + - name: HelixQueuesWindows_arm64 + value: Windows.11.Arm64.Open + - name: HelixQueuesLinux_x64 + value: Ubuntu.2204.Amd64.Open + - name: HelixQueuesLinux_arm64 + value: Ubuntu.2204.ArmArch.Open + - name: HelixQueuesLinux_musl_x64 + value: '(Alpine.323.Amd64.Open)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.23-helix-amd64' + - name: HelixQueuesLinux_musl_arm64 + value: '(Alpine.323.ArmArch.Open)Ubuntu.2204.ArmArch.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.23-helix-arm64v8' + - name: HelixQueuesMacOS_x64 + value: OSX.15.Amd64.Open + - name: HelixQueuesMacOS_arm64 + value: OSX.15.Arm64.Open + +# Internal queues +- ${{ if eq(variables['System.TeamProject'], 'internal') }}: + - name: HelixQueuesWindows_x64 + value: Windows.Amd64.Server2022 + - name: HelixQueuesWindows_x86 + value: Windows.Amd64.Server2022 + - name: HelixQueuesWindows_arm64 + value: Windows.11.Arm64 + - name: HelixQueuesLinux_x64 + value: Ubuntu.2204.Amd64 + - name: HelixQueuesLinux_arm64 + value: Ubuntu.2204.ArmArch + - name: HelixQueuesLinux_musl_x64 + value: '(Alpine.323.Amd64)Ubuntu.2204.Amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.23-helix-amd64' + - name: HelixQueuesLinux_musl_arm64 + value: '(Alpine.323.ArmArch)Ubuntu.2204.ArmArch@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.23-helix-arm64v8' + - name: HelixQueuesMacOS_x64 + value: OSX.15.Amd64 + - name: HelixQueuesMacOS_arm64 + value: OSX.15.Arm64 diff --git a/eng/pipelines/send-to-helix.yml b/eng/pipelines/send-to-helix.yml new file mode 100644 index 0000000000..448a2b9395 --- /dev/null +++ b/eng/pipelines/send-to-helix.yml @@ -0,0 +1,151 @@ +# Step template for sending tests to Helix. +# This template is included by build.yml to send test workloads to Helix +# machines for distributed execution. +# +# Official vs PR builds: +# - Official (isOfficialBuild=true): uses HelixAccessToken from DotNet-HelixApi-Access variable group +# - PR/public (isOfficialBuild=false): uses Creator for anonymous submission +# +# Prerequisites: The DotNet-HelixApi-Access variable group must be included +# at the job level for official builds (provides HelixApiAccessToken). + +parameters: + # Build configuration (e.g. Debug, Release) +- name: configuration + type: string + + # Target architecture (e.g. x64, arm64) +- name: architecture + type: string + + # Target OS group +- name: osGroup + type: string + default: Windows_NT + values: + - Windows_NT + - Linux + - MacOS + + # OS suffix (e.g. -musl for Alpine Linux) +- name: osSuffix + type: string + default: '' + + # Helix queue to target +- name: helixQueues + type: string + + # Whether this is an official (non-PR, internal) build +- name: isOfficialBuild + type: boolean + default: false + +steps: +# Official builds: use HelixAccessToken for internal queues +- ${{ if eq(parameters.isOfficialBuild, true) }}: + - ${{ if eq(parameters.osGroup, 'Windows_NT') }}: + - powershell: > + & $(Build.SourcesDirectory)\eng\common\msbuild.ps1 + $(Build.SourcesDirectory)\eng\helix.proj + /restore + /p:Configuration=${{ parameters.configuration }} + /p:TargetArchitecture=${{ parameters.architecture }} + /p:TargetOS=Windows_NT + "/p:HelixTargetQueues=${{ parameters.helixQueues }}" + /p:HelixSource=official/diagnostics/$(Build.SourceBranch) + /p:HelixType=test/${{ parameters.configuration }}/${{ parameters.architecture }}/ + /p:HelixAccessToken=$(HelixApiAccessToken) + /bl:$(Build.SourcesDirectory)/artifacts/log/${{ parameters.configuration }}/SendToHelix.binlog + displayName: Send Tests to Helix + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + + - ${{ if eq(parameters.osGroup, 'Linux') }}: + - script: > + $(Build.SourcesDirectory)/eng/common/msbuild.sh + $(Build.SourcesDirectory)/eng/helix.proj + /restore + /p:Configuration=${{ parameters.configuration }} + /p:TargetArchitecture=${{ parameters.architecture }} + /p:TargetOS=linux + /p:OSSuffix=${{ parameters.osSuffix }} + "/p:HelixTargetQueues=${{ parameters.helixQueues }}" + /p:HelixSource=official/diagnostics/$(Build.SourceBranch) + /p:HelixType=test/${{ parameters.configuration }}/${{ parameters.architecture }}/ + /p:HelixAccessToken=$(HelixApiAccessToken) + /bl:$(Build.SourcesDirectory)/artifacts/log/${{ parameters.configuration }}/SendToHelix.binlog + displayName: Send Tests to Helix + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + + - ${{ if eq(parameters.osGroup, 'MacOS') }}: + - script: > + $(Build.SourcesDirectory)/eng/common/msbuild.sh + $(Build.SourcesDirectory)/eng/helix.proj + /restore + /p:Configuration=${{ parameters.configuration }} + /p:TargetArchitecture=${{ parameters.architecture }} + /p:TargetOS=osx + "/p:HelixTargetQueues=${{ parameters.helixQueues }}" + /p:HelixSource=official/diagnostics/$(Build.SourceBranch) + /p:HelixType=test/${{ parameters.configuration }}/${{ parameters.architecture }}/ + /p:HelixAccessToken=$(HelixApiAccessToken) + /bl:$(Build.SourcesDirectory)/artifacts/log/${{ parameters.configuration }}/SendToHelix.binlog + displayName: Send Tests to Helix + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# PR/public builds: use Creator for anonymous submission +- ${{ else }}: + - ${{ if eq(parameters.osGroup, 'Windows_NT') }}: + - powershell: > + & $(Build.SourcesDirectory)\eng\common\msbuild.ps1 + $(Build.SourcesDirectory)\eng\helix.proj + /restore + /p:Configuration=${{ parameters.configuration }} + /p:TargetArchitecture=${{ parameters.architecture }} + /p:TargetOS=Windows_NT + "/p:HelixTargetQueues=${{ parameters.helixQueues }}" + /p:HelixSource=pr/diagnostics/$(Build.SourceBranch) + /p:HelixType=test/${{ parameters.configuration }}/${{ parameters.architecture }}/ + /p:Creator=$(Build.RequestedFor) + /bl:$(Build.SourcesDirectory)/artifacts/log/${{ parameters.configuration }}/SendToHelix.binlog + displayName: Send Tests to Helix + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + + - ${{ if eq(parameters.osGroup, 'Linux') }}: + - script: > + $(Build.SourcesDirectory)/eng/common/msbuild.sh + $(Build.SourcesDirectory)/eng/helix.proj + /restore + /p:Configuration=${{ parameters.configuration }} + /p:TargetArchitecture=${{ parameters.architecture }} + /p:TargetOS=linux + /p:OSSuffix=${{ parameters.osSuffix }} + "/p:HelixTargetQueues=${{ parameters.helixQueues }}" + /p:HelixSource=pr/diagnostics/$(Build.SourceBranch) + /p:HelixType=test/${{ parameters.configuration }}/${{ parameters.architecture }}/ + /p:Creator=$(Build.RequestedFor) + /bl:$(Build.SourcesDirectory)/artifacts/log/${{ parameters.configuration }}/SendToHelix.binlog + displayName: Send Tests to Helix + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + + - ${{ if eq(parameters.osGroup, 'MacOS') }}: + - script: > + $(Build.SourcesDirectory)/eng/common/msbuild.sh + $(Build.SourcesDirectory)/eng/helix.proj + /restore + /p:Configuration=${{ parameters.configuration }} + /p:TargetArchitecture=${{ parameters.architecture }} + /p:TargetOS=osx + "/p:HelixTargetQueues=${{ parameters.helixQueues }}" + /p:HelixSource=pr/diagnostics/$(Build.SourceBranch) + /p:HelixType=test/${{ parameters.configuration }}/${{ parameters.architecture }}/ + /p:Creator=$(Build.RequestedFor) + /bl:$(Build.SourcesDirectory)/artifacts/log/${{ parameters.configuration }}/SendToHelix.binlog + displayName: Send Tests to Helix + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) \ No newline at end of file diff --git a/eng/send-to-helix.cmd b/eng/send-to-helix.cmd new file mode 100644 index 0000000000..c9cdde80a2 --- /dev/null +++ b/eng/send-to-helix.cmd @@ -0,0 +1,170 @@ +@echo off +REM Licensed to the .NET Foundation under one or more agreements. +REM The .NET Foundation licenses this file to you under the MIT license. + +REM Send diagnostics tests to Helix for remote execution. +REM +REM Usage: send-to-helix.cmd [options] +REM +REM Options: +REM -configuration Build configuration (default: Debug) +REM -architecture Target architecture (default: x64) +REM -queue Helix queue to target (default: Windows.10.Amd64.Open) +REM -accesstoken Helix access token for internal queues (optional) +REM -creator Creator name for public builds (optional) +REM -bl Generate binary log +REM -help Show this help message + +setlocal enabledelayedexpansion + +set "EngRoot=%~dp0" +set "EngRoot=%EngRoot:~0,-1%" +for %%i in ("%EngRoot%\..") do set "RepoRoot=%%~fi" +set "Configuration=Debug" +set "Architecture=x64" +set "TargetOS=Windows_NT" +set "HelixQueue=windows.amd64.server2022" +set "HelixAccessToken=AAAE1Eq55IR3-s8xjJvkFcHtmI0" +set "Creator=" +set "BinaryLog=" + +REM Parse arguments +:parse_args +if "%~1"=="" goto :done_parsing +if /i "%~1"=="-configuration" ( + set "Configuration=%~2" + shift + shift + goto :parse_args +) +if /i "%~1"=="-c" ( + set "Configuration=%~2" + shift + shift + goto :parse_args +) +if /i "%~1"=="-architecture" ( + set "Architecture=%~2" + shift + shift + goto :parse_args +) +if /i "%~1"=="-a" ( + set "Architecture=%~2" + shift + shift + goto :parse_args +) +if /i "%~1"=="-queue" ( + set "HelixQueue=%~2" + shift + shift + goto :parse_args +) +if /i "%~1"=="-accesstoken" ( + set "HelixAccessToken=%~2" + shift + shift + goto :parse_args +) +if /i "%~1"=="-creator" ( + set "Creator=%~2" + shift + shift + goto :parse_args +) +if /i "%~1"=="-bl" ( + if not exist "%RepoRoot%\artifacts\helix-log" mkdir "%RepoRoot%\artifacts\helix-log" + set "BinaryLog=/bl:%RepoRoot%\artifacts\helix-log\SendToHelix.binlog" + shift + goto :parse_args +) +if /i "%~1"=="-help" goto :show_help +if /i "%~1"=="-h" goto :show_help +if /i "%~1"=="/?" goto :show_help + +echo Unknown argument: %~1 +goto :show_help + +:done_parsing + +REM Set required environment variables for local Helix execution +if "%BUILD_SOURCEBRANCH%"=="" set "BUILD_SOURCEBRANCH=local" +if "%BUILD_REPOSITORY_NAME%"=="" set "BUILD_REPOSITORY_NAME=diagnostics" +if "%SYSTEM_TEAMPROJECT%"=="" set "SYSTEM_TEAMPROJECT=dnceng" +if "%BUILD_REASON%"=="" set "BUILD_REASON=Manual" + +REM Build creator argument if specified +set "CreatorArg=" +if not "%Creator%"=="" set "CreatorArg=/p:Creator=%Creator%" + +REM Build access token argument if specified +set "AccessTokenArg=" +if not "%HelixAccessToken%"=="" set "AccessTokenArg=/p:HelixAccessToken=%HelixAccessToken%" + +echo. +echo =========================== +echo Sending tests to Helix +echo =========================== +echo Configuration: %Configuration% +echo Architecture: %Architecture% +echo Target OS: %TargetOS% +echo Helix Queue: %HelixQueue% +echo Artifacts Dir: %RepoRoot%\artifacts\ +echo. + +REM Verify artifacts exist +if not exist "%RepoRoot%\artifacts\bin" ( + echo ERROR: Build artifacts not found at %RepoRoot%\artifacts\bin + echo Please run Build.cmd first to build the tests. + exit /b 1 +) + +REM Send to Helix using PowerShell (use /tl:off to disable terminal logger for better output) +REM The PrepareCorrelationPayload target runs automatically before Test +REM Set environment variables in PowerShell scope for the Helix SDK to read +powershell -ExecutionPolicy ByPass -NoProfile -command "$env:BUILD_SOURCEBRANCH='%BUILD_SOURCEBRANCH%'; $env:BUILD_REPOSITORY_NAME='%BUILD_REPOSITORY_NAME%'; $env:SYSTEM_TEAMPROJECT='%SYSTEM_TEAMPROJECT%'; $env:BUILD_REASON='%BUILD_REASON%'; & '%RepoRoot%\eng\common\msbuild.ps1' '%RepoRoot%\eng\helix.proj' /restore /t:Test /tl:off /p:Configuration=%Configuration% /p:TargetArchitecture=%Architecture% /p:TargetOS=%TargetOS% /p:HelixTargetQueues=%HelixQueue% /p:TestArtifactsDir='%RepoRoot%\artifacts\' /p:EnableAzurePipelinesReporter=false %AccessTokenArg% %CreatorArg% %BinaryLog%" + +if %ERRORLEVEL% neq 0 ( + echo. + echo ERROR: Failed to send tests to Helix + exit /b %ERRORLEVEL% +) + +echo. +echo Tests submitted to Helix successfully! +echo View results at: https://helix.dot.net/ +exit /b 0 + +:show_help +echo. +echo Send diagnostics tests to Helix for remote execution. +echo. +echo Usage: send-to-helix.cmd [options] +echo. +echo Options: +echo -configuration ^ Build configuration (default: Debug) +echo -architecture ^ Target architecture (default: x64) +echo -queue ^ Helix queue to target (default: Windows.10.Amd64.Open) +echo -accesstoken ^ Helix access token for internal queues +echo -creator ^ Creator name for public builds +echo -bl Generate binary log +echo -help Show this help message +echo. +echo Examples: +echo send-to-helix.cmd +echo send-to-helix.cmd -configuration Release -queue Windows.11.Amd64.Open +echo send-to-helix.cmd -architecture arm64 -queue Windows.11.Arm64.Open +echo send-to-helix.cmd -creator "MyName" -bl +echo. +echo Notes: +echo - Run Build.cmd first to build the test artifacts +echo - For internal queues (not ending in .Open), provide -accesstoken +echo - Public queues require -creator to be set +echo. +echo Available public Windows queues: +echo Windows.10.Amd64.Open +echo Windows.11.Amd64.Open +echo Windows.11.Arm64.Open +echo. +exit /b 0 diff --git a/eng/send-to-helix.sh b/eng/send-to-helix.sh new file mode 100644 index 0000000000..e4e3232625 --- /dev/null +++ b/eng/send-to-helix.sh @@ -0,0 +1,217 @@ +#!/usr/bin/env bash +# Licensed to the .NET Foundation under one or more agreements. +# The .NET Foundation licenses this file to you under the MIT license. + +# Send diagnostics tests to Helix for remote execution. +# +# Usage: send-to-helix.sh [options] +# +# Options: +# -configuration Build configuration (default: Debug) +# -architecture Target architecture (default: x64) +# -os Target OS (default: auto-detected) +# -queue Helix queue to target (default: auto-selected based on OS) +# -accesstoken Helix access token for internal queues (optional) +# -creator Creator name for public builds (optional) +# -bl Generate binary log +# -help Show this help message + +set -e + +# Get script directory +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$script_dir/.." && pwd)" + +# Default values +configuration="Debug" +architecture="x64" +target_os="" +helix_queue="" +helix_access_token="" +creator="" +binary_log="" + +# Detect OS +detect_os() { + case "$(uname -s)" in + Linux*) echo "linux" ;; + Darwin*) echo "osx" ;; + *) echo "unknown" ;; + esac +} + +# Get default Helix queue based on OS and architecture +get_default_queue() { + local os=$1 + local arch=$2 + + case "$os" in + linux) + case "$arch" in + x64) echo "Ubuntu.2204.Amd64.Open" ;; + arm64) echo "Ubuntu.2204.Arm64.Open" ;; + *) echo "Ubuntu.2204.Amd64.Open" ;; + esac + ;; + osx) + case "$arch" in + x64) echo "OSX.1200.Amd64.Open" ;; + arm64) echo "OSX.1200.Arm64.Open" ;; + *) echo "OSX.1200.Amd64.Open" ;; + esac + ;; + *) + echo "Ubuntu.2204.Amd64.Open" + ;; + esac +} + +show_help() { + cat << EOF + +Send diagnostics tests to Helix for remote execution. + +Usage: send-to-helix.sh [options] + +Options: + -configuration Build configuration (default: Debug) + -architecture Target architecture (default: x64) + -os Target OS (default: auto-detected) + -queue Helix queue to target (default: auto-selected based on OS) + -accesstoken Helix access token for internal queues + -creator Creator name for public builds + -bl Generate binary log + -help Show this help message + +Examples: + ./send-to-helix.sh + ./send-to-helix.sh -configuration Release -queue Ubuntu.2404.Amd64.Open + ./send-to-helix.sh -architecture arm64 -os linux + ./send-to-helix.sh -creator "MyName" -bl + +Notes: + - Run build.sh first to build the test artifacts + - For internal queues (not ending in .Open), provide -accesstoken + - Public queues require -creator to be set + +Available public Linux queues: + Ubuntu.2204.Amd64.Open + Ubuntu.2404.Amd64.Open + Ubuntu.2204.Arm64.Open + Debian.12.Amd64.Open + +Available public macOS queues: + OSX.1200.Amd64.Open + OSX.1200.Arm64.Open + OSX.1300.Amd64.Open + +EOF + exit 0 +} + +# Parse arguments +while [[ $# -gt 0 ]]; do + opt="$(echo "${1}" | tr '[:upper:]' '[:lower:]')" + case "$opt" in + -configuration|-c) + configuration="$2" + shift 2 + ;; + -architecture|-a) + architecture="$2" + shift 2 + ;; + -os) + target_os="$2" + shift 2 + ;; + -queue) + helix_queue="$2" + shift 2 + ;; + -accesstoken) + helix_access_token="$2" + shift 2 + ;; + -creator) + creator="$2" + shift 2 + ;; + -bl) + binary_log="/bl:$repo_root/artifacts/log/$configuration/SendToHelix.binlog" + shift + ;; + -help|-h|--help) + show_help + ;; + *) + echo "Unknown argument: $1" + show_help + ;; + esac +done + +# Auto-detect OS if not specified +if [[ -z "$target_os" ]]; then + target_os=$(detect_os) +fi + +# Auto-select queue if not specified +if [[ -z "$helix_queue" ]]; then + helix_queue=$(get_default_queue "$target_os" "$architecture") +fi + +# Set required environment variables for local Helix execution +export BUILD_SOURCEBRANCH="${BUILD_SOURCEBRANCH:-local}" +export BUILD_REPOSITORY_NAME="${BUILD_REPOSITORY_NAME:-diagnostics}" +export SYSTEM_TEAMPROJECT="${SYSTEM_TEAMPROJECT:-dnceng}" +export BUILD_REASON="${BUILD_REASON:-Manual}" + +# Build optional arguments +creator_arg="" +if [[ -n "$creator" ]]; then + creator_arg="/p:Creator=$creator" +fi + +access_token_arg="" +if [[ -n "$helix_access_token" ]]; then + access_token_arg="/p:HelixAccessToken=$helix_access_token" +fi + +echo "" +echo "===========================" +echo "Sending tests to Helix" +echo "===========================" +echo "Configuration: $configuration" +echo "Architecture: $architecture" +echo "Target OS: $target_os" +echo "Helix Queue: $helix_queue" +echo "Artifacts Dir: $repo_root/artifacts/" +echo "" + +# Verify artifacts exist +if [[ ! -d "$repo_root/artifacts/bin" ]]; then + echo "ERROR: Build artifacts not found at $repo_root/artifacts/bin" + echo "Please run build.sh first to build the tests." + exit 1 +fi + +# Send to Helix (use -tl:off to disable terminal logger for better output) +"$repo_root/eng/common/msbuild.sh" \ + "$repo_root/eng/helix.proj" \ + /restore \ + /t:Test \ + -tl:off \ + /p:Configuration="$configuration" \ + /p:TargetArchitecture="$architecture" \ + /p:TargetOS="$target_os" \ + /p:HelixTargetQueues="$helix_queue" \ + /p:TestArtifactsDir="$repo_root/artifacts/" \ + /p:EnableAzurePipelinesReporter=false \ + $access_token_arg \ + $creator_arg \ + $binary_log + +echo "" +echo "Tests submitted to Helix successfully!" +echo "View results at: https://helix.dot.net/" diff --git a/global.json b/global.json index 7dfb33ff29..b2d6d6e227 100644 --- a/global.json +++ b/global.json @@ -18,6 +18,7 @@ "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.5.0", "Microsoft.Build.Traversal": "3.4.0", - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26110.1" + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26110.1", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.26110.1" } } diff --git a/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs b/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs index 01821e3244..ccbd53dae2 100644 --- a/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs +++ b/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs @@ -38,6 +38,10 @@ private static TestRunConfiguration ParseDefaultConfigFile() private readonly DateTime _timestamp = DateTime.Now; + // Properties set from Helix environment variables that should not be overwritten + // by values in imported config files (e.g., Debugger.Tests.Common.txt). + private readonly HashSet _helixProtectedProperties = new(); + public IEnumerable Configurations { get; private set; } private void ParseConfigFile(string path) @@ -86,7 +90,35 @@ private void ParseConfigFile(string path) { initialConfig["WinDir"] = Path.GetFullPath(Environment.GetEnvironmentVariable("WINDIR")); } + + // On Helix, override ArtifactsDir and DotNetRoot before parsing config files + // so all $(ArtifactsDir) and $(DotNetRoot) references resolve to Helix paths. + // These are marked as protected so imported config files cannot overwrite them. + if (Environment.GetEnvironmentVariable("RunningOnHelix") == "true") + { + initialConfig["RunningOnHelix"] = "true"; + + string helixArtifactsDir = Environment.GetEnvironmentVariable("DIAGNOSTICS_ARTIFACTS_DIR"); + if (!string.IsNullOrEmpty(helixArtifactsDir)) + { + initialConfig["ArtifactsDir"] = helixArtifactsDir; + _helixProtectedProperties.Add("ArtifactsDir"); + } + + string helixDotNetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT"); + if (string.IsNullOrEmpty(helixDotNetRoot)) + { + helixDotNetRoot = Environment.GetEnvironmentVariable("DIAGNOSTICS_DOTNET_ROOT"); + } + if (!string.IsNullOrEmpty(helixDotNetRoot)) + { + initialConfig["DotNetRoot"] = helixDotNetRoot; + _helixProtectedProperties.Add("DotNetRoot"); + } + } + IEnumerable> configs = ParseConfigFile(path, new Dictionary[] { initialConfig }); + Configurations = configs.Select(c => new TestConfiguration(c)).ToList(); } @@ -179,6 +211,11 @@ private Dictionary[] ParseConfigSetting(Dictionary $(ArtifactsDir)/dotnet-test - + $(ArtifactsDir)/bin/$(OS).$(TargetArchitecture).$(TargetConfiguration) $(ArtifactsDir)/TestResults/$(TargetConfiguration)/common.unittests_$(Timestamp) diff --git a/src/tests/CommonTestRunner/ConfigFiles/Windows/Debugger.Tests.Config.txt b/src/tests/CommonTestRunner/ConfigFiles/Windows/Debugger.Tests.Config.txt index 2e49e7dc21..f651aa0010 100644 --- a/src/tests/CommonTestRunner/ConfigFiles/Windows/Debugger.Tests.Config.txt +++ b/src/tests/CommonTestRunner/ConfigFiles/Windows/Debugger.Tests.Config.txt @@ -1,7 +1,7 @@ $(ArtifactsDir)\dotnet-test - + $(ArtifactsDir)\bin\Windows_NT.$(TargetArchitecture).$(TargetConfiguration) $(ArtifactsDir)\TestResults\$(TargetConfiguration)\common.unittests_$(Timestamp) diff --git a/src/tests/DbgShim.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt b/src/tests/DbgShim.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt index 0100eff6a7..3e39f6a4c5 100644 --- a/src/tests/DbgShim.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt +++ b/src/tests/DbgShim.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt @@ -1,7 +1,7 @@ $(ArtifactsDir)/dotnet-test - + $(ArtifactsDir)/bin/$(OS).$(TargetArchitecture).$(TargetConfiguration) $(ArtifactsDir)/TestResults/$(TargetConfiguration)/dbgshim.unittests_$(Timestamp) diff --git a/src/tests/DbgShim.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt b/src/tests/DbgShim.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt index 4687626e3f..ae82bb14a2 100644 --- a/src/tests/DbgShim.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt +++ b/src/tests/DbgShim.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt @@ -1,7 +1,7 @@ $(ArtifactsDir)\dotnet-test - + $(ArtifactsDir)\bin\Windows_NT.$(TargetArchitecture).$(TargetConfiguration) $(ArtifactsDir)\TestResults\$(TargetConfiguration)\dbgshim.unittests_$(Timestamp) diff --git a/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj b/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj index c931e79852..f17b7599e9 100644 --- a/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj +++ b/src/tests/DbgShim.UnitTests/DbgShim.UnitTests.csproj @@ -6,6 +6,8 @@ $(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt 1.0.351101 true + true + true @@ -28,16 +30,6 @@ - - - - - - - - - - @@ -48,10 +40,10 @@ $(Configuration) $(ArtifactsDir) - $(NuGetPackageRoot)testassets.windows.x64.6.0\$(TestAssetsVersion)\content - $(NuGetPackageRoot)testassets.windows.x86.6.0\$(TestAssetsVersion)\content - $(NuGetPackageRoot)testassets.linux.x64.6.0\$(TestAssetsVersion)\content - $(NuGetPackageRoot)testassets.linux.arm64.6.0\$(TestAssetsVersion)\content + %24(ArtifactsDir)test\packages\testassets.windows.x64.6.0\$(TestAssetsVersion)\content + %24(ArtifactsDir)test\packages\testassets.windows.x86.6.0\$(TestAssetsVersion)\content + %24(ArtifactsDir)test\packages\testassets.linux.x64.6.0\$(TestAssetsVersion)\content + %24(ArtifactsDir)test\packages\testassets.linux.arm64.6.0\$(TestAssetsVersion)\content ]]> @@ -68,8 +60,8 @@ $(Configuration) $(ArtifactsDir) - $(NuGetPackageRoot)testassets.linux.x64.6.0/$(TestAssetsVersion)/content - $(NuGetPackageRoot)testassets.linux.arm64.6.0/$(TestAssetsVersion)/content + %24(ArtifactsDir)test/packages/testassets.linux.x64.6.0/$(TestAssetsVersion)/content + %24(ArtifactsDir)test/packages/testassets.linux.arm64.6.0/$(TestAssetsVersion)/content ]]> diff --git a/src/tests/Directory.Build.props b/src/tests/Directory.Build.props index e36d68d633..9f63e06a15 100644 --- a/src/tests/Directory.Build.props +++ b/src/tests/Directory.Build.props @@ -1,3 +1,12 @@ + + + $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'test', 'packages')) + + + + true + true + diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index ab42c9ad55..8ee633b6fb 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -6,7 +6,9 @@ Projects opt-in by setting CopyDebuggeeSourcesToArtifacts=true. Optional: Set CopyLldbPluginTests=true to also copy lldbplugin.tests. --> - + $(ArtifactsDir)test/DebuggeeSources/$(MSBuildProjectName)/Debuggees $(ArtifactsDir)test/AuxMsbuildFiles @@ -32,4 +34,80 @@ + + + + <_AuxDir>$(ArtifactsDir)test/AuxMsbuildFiles/ + + + + + + + + + + + + + + + + + + + + + + + + + <_cdbFiles Include="$(NuGetPackageRoot)cdb-sos\$(cdbsosversion)\**\*" + Condition="'$(NeedsCdb)' == 'true'" /> + + <_testAssetsWinx64 Include="$(NuGetPackageRoot)testassets.windows.x64.6.0\$(TestAssetsVersion)\**\*" + Condition="'$(NeedsTestAssets)' == 'true' and '$(OS)' == 'Windows_NT'" /> + + <_testAssetsWinx86 Include="$(NuGetPackageRoot)testassets.windows.x86.6.0\$(TestAssetsVersion)\**\*" + Condition="'$(NeedsTestAssets)' == 'true' and '$(OS)' == 'Windows_NT'" /> + + <_testAssetsLinux64 Include="$(NuGetPackageRoot)testassets.linux.x64.6.0\$(TestAssetsVersion)\**\*" + Condition="'$(NeedsTestAssets)' == 'true'" /> + + <_testAssetsLinuxArm64 Include="$(NuGetPackageRoot)testassets.linux.arm64.6.0\$(TestAssetsVersion)\**\*" + Condition="'$(NeedsTestAssets)' == 'true'" /> + + + + + + + + + + + + diff --git a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt index e72e581b9e..400f9cf56d 100644 --- a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt +++ b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt @@ -1,8 +1,7 @@ - $(RepoRootDir)/artifacts - $(RootBinDir)/bin/$(OS).$(TargetArchitecture).$(TargetConfiguration) + $(ArtifactsDir)bin/$(OS).$(TargetArchitecture).$(TargetConfiguration) ]]> diff --git a/src/tests/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt b/src/tests/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt index 2d0c1d3e75..73f496669a 100644 --- a/src/tests/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt +++ b/src/tests/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt @@ -10,7 +10,7 @@ $(ArtifactsDir)/dotnet-test - + ProjectK $(ArtifactsDir)/bin/SOS.UnitTests/Scripts diff --git a/src/tests/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt b/src/tests/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt index c99d4f2daf..efe29c2de1 100644 --- a/src/tests/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt +++ b/src/tests/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt @@ -10,7 +10,7 @@ $(ArtifactsDir)\dotnet-test - + $(ArtifactsDir)\bin\SOS.UnitTests\Scripts $(ArtifactsDir)\bin\Windows_NT.$(TargetArchitecture).$(TargetConfiguration) diff --git a/src/tests/SOS.UnitTests/SOS.UnitTests.csproj b/src/tests/SOS.UnitTests/SOS.UnitTests.csproj index f4a9e017d0..ef2ff4364a 100644 --- a/src/tests/SOS.UnitTests/SOS.UnitTests.csproj +++ b/src/tests/SOS.UnitTests/SOS.UnitTests.csproj @@ -8,6 +8,7 @@ $(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt true true + true @@ -36,10 +37,6 @@ - - - - @@ -49,7 +46,7 @@ $(DesktopTargetFramework) $(NetCoreAppMinTargetFramework) $(ArtifactsDir) - $(NuGetPackageRoot)cdb-sos\$(cdbsosversion)\runtimes\win-%24(TargetArchitecture)\native\cdb.exe + %24(ArtifactsDir)test\packages\cdb-sos\$(cdbsosversion)\runtimes\win-%24(TargetArchitecture)\native\cdb.exe ]]> diff --git a/src/tests/SOS.UnitTests/SOS.cs b/src/tests/SOS.UnitTests/SOS.cs index 6f652276ff..3da9c380e6 100644 --- a/src/tests/SOS.UnitTests/SOS.cs +++ b/src/tests/SOS.UnitTests/SOS.cs @@ -715,16 +715,19 @@ public async Task LLDBPluginTests(TestConfiguration config) } else { - program = Environment.GetEnvironmentVariable("PYTHONPATH"); - if (program == null) + string pythonPath = Environment.GetEnvironmentVariable("PYTHONPATH"); + if (pythonPath != null) + { + // PYTHONPATH is a colon-separated search path; the first entry may be the python binary. + program = pythonPath.Split(':')[0]; + } + else { - // We should verify what python version this is. 2.7 is out of - // support for a while now, but we have old OS's. program = "/usr/bin/python"; } if (!File.Exists(program)) { - throw new ArgumentException($"{program} does not exists"); + throw new ArgumentException($"Cannot find python at {program}"); } } string artifactsDir = TestConfiguration.MakeCanonicalPath(config.AllSettings["ArtifactsDir"]); diff --git a/src/tests/dotnet-trace/CollectCommandFunctionalTests.cs b/src/tests/dotnet-trace/CollectCommandFunctionalTests.cs index 634834b07c..a0a4b076d3 100644 --- a/src/tests/dotnet-trace/CollectCommandFunctionalTests.cs +++ b/src/tests/dotnet-trace/CollectCommandFunctionalTests.cs @@ -91,6 +91,10 @@ public async Task CollectCommand_InvalidProcessSpecifierConfigurations(CollectAr private static async Task RunAsync(CollectArgs config, MockConsole console, bool hasChildProcess = false) { + // Suppress timing-dependent status output (e.g. "Recording trace 0.00 (B)") + // so tests can validate provider configuration output deterministically. + console.IsOutputRedirected = true; + var handler = new CollectCommandHandler(console); handler.StartTraceSessionAsync = (client, cfg, ct) => Task.FromResult(new TestCollectSession()); handler.ResumeRuntimeAsync = (client, ct) => Task.CompletedTask;