From a89cdd7dc74d98066b4064250ce4e4e54bf61854 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Mon, 6 Apr 2026 12:05:39 -0300 Subject: [PATCH 1/3] Added an explicit pipeline variable boolean indicating that test setup has succeeded to gate test runs. --- .../templates/jobs/ci-run-tests-job.yml | 36 +++++++++++-------- .../templates/steps/run-all-tests-step.yml | 24 ++++++------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml index 76a3bf3c75..ae25d435f0 100644 --- a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml +++ b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml @@ -326,6 +326,28 @@ jobs: ${{ if parameters.configProperties.FileStreamDirectory }}: fileStreamDirectory: ${{ parameters.configProperties.FileStreamDirectory }} + # Set up for x86 tests by manually installing dotnet for x86 to an alternative location. This + # is only used to execute the test runtime (framework to test is specified in build params), so + # it should be acceptable to just install a specific version in all cases. + # @TODO: This setup is very confusing. Ideally we should just be utilizing the dotnet installation + # earlier in the job. There has to be a cleaner way of doing this. + - ${{ if and(eq(parameters.enableX86Test, true), eq(parameters.operatingSystem, 'Windows')) }}: + - ${{ if ne(variables['dotnetx86RootPath'], '') }}: + # Install the .NET SDK and Runtimes for x86. + - template: /eng/pipelines/steps/install-dotnet.yml@self + parameters: + architecture: x86 + debug: ${{ parameters.debug }} + installDir: $(dotnetx86RootPath) + runtimes: [8.x, 9.x] + + # Gate: record that all setup steps (SDK install, build, SQL config, x86 SDK + # install, etc.) completed successfully. Test steps condition on this variable + # so they are skipped when setup fails, yet remain independent of each + # other's results. + - script: echo "##vso[task.setvariable variable=setupSucceeded]true" + displayName: 'Gate: Mark Setup Succeeded' + - ${{ if eq(parameters.enableX64Test, true) }}: # run native tests - template: /eng/pipelines/common/templates/steps/run-all-tests-step.yml@self # run tests parameters: @@ -340,20 +362,6 @@ jobs: mdsPackageVersion: ${{ parameters.mdsPackageVersion }} - ${{ if and(eq(parameters.enableX86Test, true), eq(parameters.operatingSystem, 'Windows')) }}: - # Set up for x86 tests by manually installing dotnet for x86 to an alternative location. This - # is only used to execute the test runtime (framework to test is specified in build params), so - # it should be acceptable to just install a specific version in all cases. - # @TODO: This setup is very confusing. Ideally we should just be utilizing the dotnet installation - # earlier in the job. There has to be a cleaner way of doing this. - - ${{ if ne(variables['dotnetx86RootPath'], '') }}: - # Install the .NET SDK and Runtimes for x86. - - template: /eng/pipelines/steps/install-dotnet.yml@self - parameters: - architecture: x86 - debug: ${{ parameters.debug }} - installDir: $(dotnetx86RootPath) - runtimes: [8.x, 9.x] - - template: /eng/pipelines/common/templates/steps/run-all-tests-step.yml@self parameters: debug: ${{ parameters.debug }} diff --git a/eng/pipelines/common/templates/steps/run-all-tests-step.yml b/eng/pipelines/common/templates/steps/run-all-tests-step.yml index 2c407f7d1e..68689adf44 100644 --- a/eng/pipelines/common/templates/steps/run-all-tests-step.yml +++ b/eng/pipelines/common/templates/steps/run-all-tests-step.yml @@ -79,7 +79,7 @@ steps: - ${{if eq(parameters.referenceType, 'Project')}}: - task: MSBuild@1 displayName: 'Run Unit Tests ${{parameters.msbuildArchitecture }}' - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) inputs: solution: build2.proj msbuildArchitecture: ${{parameters.msbuildArchitecture }} @@ -101,7 +101,7 @@ steps: - task: MSBuild@1 displayName: 'Run Flaky Unit Tests ${{parameters.msbuildArchitecture }}' - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) inputs: solution: build2.proj msbuildArchitecture: ${{parameters.msbuildArchitecture }} @@ -128,7 +128,7 @@ steps: - task: MSBuild@1 displayName: 'Run Functional Tests ${{parameters.msbuildArchitecture }}' - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) inputs: solution: build2.proj msbuildArchitecture: ${{parameters.msbuildArchitecture }} @@ -155,7 +155,7 @@ steps: -p:TestResultsFolderPath=TestResults - task: MSBuild@1 - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) displayName: 'Run Flaky Functional Tests ${{parameters.msbuildArchitecture }}' inputs: solution: build2.proj @@ -188,7 +188,7 @@ steps: continueOnError: true - task: MSBuild@1 - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) displayName: 'Run Manual Tests ${{parameters.msbuildArchitecture }}' inputs: solution: build2.proj @@ -219,7 +219,7 @@ steps: retryCountOnTaskFailure: ${{parameters.retryCountOnManualTests }} - task: MSBuild@1 - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) displayName: 'Run Flaky Manual Tests ${{parameters.msbuildArchitecture }}' inputs: solution: build2.proj @@ -257,7 +257,7 @@ steps: - ${{if eq(parameters.referenceType, 'Project')}}: - task: DotNetCoreCLI@2 displayName: 'Run Unit Tests' - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) inputs: command: custom projects: build2.proj @@ -276,7 +276,7 @@ steps: - task: DotNetCoreCLI@2 displayName: 'Run Flaky Unit Tests' - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) inputs: command: custom projects: build2.proj @@ -298,7 +298,7 @@ steps: - task: DotNetCoreCLI@2 displayName: 'Run Functional Tests' - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) inputs: command: custom projects: build2.proj @@ -316,7 +316,7 @@ steps: verbosityPack: Detailed - task: DotNetCoreCLI@2 - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) displayName: 'Run Flaky Functional Tests' inputs: command: custom @@ -339,7 +339,7 @@ steps: - task: DotNetCoreCLI@2 displayName: 'Run Manual Tests' - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) inputs: command: custom projects: build2.proj @@ -360,7 +360,7 @@ steps: - task: DotNetCoreCLI@2 displayName: 'Run Flaky Manual Tests' - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) inputs: command: custom projects: build2.proj From 649edffe341f01401558f4483293999236556e8a Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Mon, 6 Apr 2026 12:18:48 -0300 Subject: [PATCH 2/3] Address Copilot review: use pwsh for gate step, gate publish steps on setupSucceeded - Use pwsh instead of script for the gate step to avoid cmd.exe quote handling issues on Windows agents. - Gate publish-test-results and publish-pipeline-artifact steps on setupSucceeded to prevent secondary errors when TestResults/ doesn't exist because tests were skipped. --- eng/pipelines/common/templates/jobs/ci-run-tests-job.yml | 2 +- .../common/templates/steps/publish-test-results-step.yml | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml index ae25d435f0..78b4a8eb52 100644 --- a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml +++ b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml @@ -345,7 +345,7 @@ jobs: # install, etc.) completed successfully. Test steps condition on this variable # so they are skipped when setup fails, yet remain independent of each # other's results. - - script: echo "##vso[task.setvariable variable=setupSucceeded]true" + - pwsh: Write-Host '##vso[task.setvariable variable=setupSucceeded]true' displayName: 'Gate: Mark Setup Succeeded' - ${{ if eq(parameters.enableX64Test, true) }}: # run native tests diff --git a/eng/pipelines/common/templates/steps/publish-test-results-step.yml b/eng/pipelines/common/templates/steps/publish-test-results-step.yml index 9cb356aa45..f7db2f619a 100644 --- a/eng/pipelines/common/templates/steps/publish-test-results-step.yml +++ b/eng/pipelines/common/templates/steps/publish-test-results-step.yml @@ -45,13 +45,14 @@ steps: TestResults/*.trx TestResults/**/*.coverage testRunTitle: 'Unix Tests' - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) - powershell: | cd TestResults $TF="${{parameters.targetFramework }}" Get-ChildItem -Filter "*.coverage" -Recurse | Rename-Item -NewName {"$TF" + $_.name} displayName: 'Rename coverage files' + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) - ${{ if eq(parameters.debug, true)}}: - powershell: | @@ -64,4 +65,4 @@ steps: inputs: targetPath: TestResults artifact: '${{parameters.targetFramework }}WinAz$(System.JobId)' - condition: succeededOrFailed() + condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) From e99fc4f4b0c114221c9e08f66597a7583acccee7 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Mon, 6 Apr 2026 12:19:50 -0300 Subject: [PATCH 3/3] Create TestResults dir during setup instead of gating publish steps Ensure TestResults/ exists before the gate step so publish-test-results steps can run unconditionally (succeededOrFailed) without failing when tests are skipped due to a setup failure. --- eng/pipelines/common/templates/jobs/ci-run-tests-job.yml | 5 +++++ .../common/templates/steps/publish-test-results-step.yml | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml index 78b4a8eb52..8666cd0dbb 100644 --- a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml +++ b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml @@ -341,6 +341,11 @@ jobs: installDir: $(dotnetx86RootPath) runtimes: [8.x, 9.x] + # Ensure TestResults directory exists so that publish steps don't fail when + # tests are skipped due to a setup failure. + - pwsh: New-Item -ItemType Directory -Path TestResults -Force | Out-Null + displayName: 'Create TestResults Directory' + # Gate: record that all setup steps (SDK install, build, SQL config, x86 SDK # install, etc.) completed successfully. Test steps condition on this variable # so they are skipped when setup fails, yet remain independent of each diff --git a/eng/pipelines/common/templates/steps/publish-test-results-step.yml b/eng/pipelines/common/templates/steps/publish-test-results-step.yml index f7db2f619a..9cb356aa45 100644 --- a/eng/pipelines/common/templates/steps/publish-test-results-step.yml +++ b/eng/pipelines/common/templates/steps/publish-test-results-step.yml @@ -45,14 +45,13 @@ steps: TestResults/*.trx TestResults/**/*.coverage testRunTitle: 'Unix Tests' - condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) + condition: succeededOrFailed() - powershell: | cd TestResults $TF="${{parameters.targetFramework }}" Get-ChildItem -Filter "*.coverage" -Recurse | Rename-Item -NewName {"$TF" + $_.name} displayName: 'Rename coverage files' - condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) - ${{ if eq(parameters.debug, true)}}: - powershell: | @@ -65,4 +64,4 @@ steps: inputs: targetPath: TestResults artifact: '${{parameters.targetFramework }}WinAz$(System.JobId)' - condition: and(eq(variables['setupSucceeded'], 'true'), succeededOrFailed()) + condition: succeededOrFailed()