Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 72 additions & 7 deletions .azure/pipelines/templates/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ jobs:
- ${{ if and(eq(parameters.skip_test, false), ne(variables.runCodeQL3000, 'true')) }}:
- ${{ each suite in parameters.suites }}:
- ${{ each framework in parameters.frameworks }}:
- job:
- job: Test_${{suite}}_${{ replace(framework, '.', '_') }}
displayName: ${{suite}} on ${{framework}}
timeoutInMinutes: 120
dependsOn: Build
Expand Down Expand Up @@ -233,7 +233,7 @@ jobs:
inputs:
version: ${{variables.testFrameworkToInstall}}
displayName: 'Install .NET Core sdk used in tests'
- task: DotNetCoreCLI@2
- task: PowerShell@2
displayName: Test
env:
${{ if eq(variables['System.TeamProject'], 'internal') }}:
Expand All @@ -242,11 +242,51 @@ jobs:
SERVICE_CONNECTION_ID: $(serviceConnectionId)
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
inputs:
command: 'test'
projects: '$(solution)'
testRunTitle: ${{suite}} on ${{framework}}
arguments: '--no-build --report-trx --report-trx-filename "testresults-${{framework}}-${{suite}}-{asm}_{tfm}_{arch}.trx" --framework ${{framework}} --configuration "${{parameters.build_configuration}}" --filter-query "/[(Provider=None)&(Suite=${{suite}})]" --minimum-expected-tests 1 --crashdump --crashdump-type Full --hangdump --hangdump-timeout 10m --hangdump-type Full --max-parallel-test-modules 1'
publishTestResults: false # The next step publishes all module-specific TRX files as one test run.
pwsh: true
targetType: inline
workingDirectory: '$(Build.SourcesDirectory)'
script: |
$coverageDirectory = '$(Build.ArtifactStagingDirectory)/test_outputs_${{suite}}_${{framework}}_$(Build.BuildId)'
New-Item -ItemType Directory -Force -Path $coverageDirectory | Out-Null

$toolPath = Join-Path '$(Agent.TempDirectory)' 'orleans-dotnet-coverage-$(System.JobId)'
./.github/scripts/setup-coverage.ps1 -InstallPath $toolPath

$command = @(
'dotnet'
'test'
'--solution'
'$(solution)'
'--no-build'
'--report-trx'
'--report-trx-filename'
'testresults-${{framework}}-${{suite}}-{asm}_{tfm}_{arch}.trx'
'--framework'
'${{framework}}'
'--configuration'
'${{parameters.build_configuration}}'
'--filter-query'
'/[(Provider=None)&(Suite=${{suite}})]'
'--minimum-expected-tests'
'1'
'--crashdump'
'--crashdump-type'
'Full'
'--hangdump'
'--hangdump-timeout'
'10m'
'--hangdump-type'
'Full'
'--max-parallel-test-modules'
'1'
)

./.github/scripts/invoke-coverage.ps1 `
-Settings ./.github/coverage.config.xml `
-Output "$coverageDirectory/coverage-${{suite}}-${{framework}}.cobertura.xml" `
-RetryLogFile "$coverageDirectory/coverage-${{suite}}-${{framework}}.retry.log" `
-CoverageCommand (Join-Path $toolPath 'dotnet-coverage.exe') `
-Command $command
- task: PublishTestResults@2
displayName: Publishing test results
condition: succeededOrFailed()
Expand All @@ -269,3 +309,28 @@ jobs:
Contents: '**\*.dmp'
TargetFolder: '$(Build.ArtifactStagingDirectory)/test_outputs_${{suite}}_${{framework}}_$(Build.BuildId)'
OverWrite: true
- job: PublishCodeCoverage
displayName: Publish code coverage
dependsOn:
- ${{ each suite in parameters.suites }}:
- ${{ each framework in parameters.frameworks }}:
- Test_${{suite}}_${{ replace(framework, '.', '_') }}
condition: succeededOrFailed()
steps:
- checkout: self
- task: UseDotNet@2
displayName: 'Use .NET Core sdk'
inputs:
useGlobalJson: true
- task: DownloadPipelineArtifact@2
displayName: Download code coverage
inputs:
buildType: current
itemPattern: '**/*.cobertura.xml'
targetPath: '$(Pipeline.Workspace)/coverage'
- task: PublishCodeCoverageResults@2
displayName: Publish code coverage
inputs:
summaryFileLocation: '$(Pipeline.Workspace)/coverage/**/*.cobertura.xml'
pathToSources: '$(Build.SourcesDirectory)'
failIfCoverageEmpty: true
1 change: 1 addition & 0 deletions .azure/pipelines/templates/vars.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ variables:
codesign_runtime: '2.1.x'
GDN_SUPPRESS_FORKED_BUILD_WARNING: true # Avoid warning "Guardian is not supported for builds from forked GitHub repositories"
MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)'
DOTNET_COVERAGE_VERSION: 18.5.2
# Auto-injection is not necessary because the tasks are explicitly included where they're enabled.
Codeql.SkipTaskAutoInjection: true
${{ if eq(variables['System.TeamProject'], 'GitHub - PR Builds') }}:
Expand Down
3 changes: 1 addition & 2 deletions .github/actions/dotnet-test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ runs:
- name: Build and discover tests for static coverage
if: github.event_name == 'pull_request' && (runner.os == 'macOS' || inputs.static-instrumentation == 'true')
shell: pwsh
run: dotnet test --solution Orleans.slnx --framework "${{ inputs.framework }}" --filter-query "${{ inputs.filter-query }}" --list-tests --minimum-expected-tests 1 --max-parallel-test-modules 1 -p:ContinuousIntegrationBuild=false
run: dotnet test --solution Orleans.slnx --framework "${{ inputs.framework }}" --filter-query "${{ inputs.filter-query }}" --list-tests --minimum-expected-tests 1 --max-parallel-test-modules 1
- name: Test
if: github.event_name != 'pull_request'
shell: pwsh
Expand Down Expand Up @@ -62,7 +62,6 @@ runs:
'test_results_${{ inputs.result-id }}_{asm}_{tfm}_{arch}.trx'
'--max-parallel-test-modules'
'1'
'-p:ContinuousIntegrationBuild=false'
)
./.github/scripts/invoke-coverage.ps1 -Settings ./.github/coverage.config.xml -Output "TestResults/${{ inputs.coverage-id }}.cobertura.xml" -RetryLogFile "logs/${{ inputs.coverage-id }}.retry.log" -Command $command
- name: Test with static coverage
Expand Down
3 changes: 2 additions & 1 deletion .github/coverage.config.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Configuration>
<DeterministicReport>True</DeterministicReport>
<IncludeTestAssembly>False</IncludeTestAssembly>
<ExcludeAssembliesWithoutSources>MissingAll</ExcludeAssembliesWithoutSources>
<ExcludeAssembliesWithoutSources>None</ExcludeAssembliesWithoutSources>
<CodeCoverage>
<Sources>
<Include>
Expand Down
3 changes: 2 additions & 1 deletion .github/coverage.static.config.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Configuration>
<DeterministicReport>True</DeterministicReport>
<IncludeTestAssembly>False</IncludeTestAssembly>
<ExcludeAssembliesWithoutSources>MissingAll</ExcludeAssembliesWithoutSources>
<ExcludeAssembliesWithoutSources>None</ExcludeAssembliesWithoutSources>
<CodeCoverage>
<Sources>
<Include>
Expand Down
18 changes: 15 additions & 3 deletions .github/scripts/setup-coverage.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[CmdletBinding()]
param(
[string] $Version = $env:DOTNET_COVERAGE_VERSION
[string] $Version = $env:DOTNET_COVERAGE_VERSION,

[string] $InstallPath
)

Set-StrictMode -Version Latest
Expand All @@ -24,12 +26,22 @@ if ([string]::IsNullOrWhiteSpace($Version)) {
throw 'DOTNET_COVERAGE_VERSION must specify the dotnet-coverage tool version'
}

$toolPath = Join-Path $env:GITHUB_WORKSPACE '.tools'
$toolPath = $InstallPath
if ([string]::IsNullOrWhiteSpace($toolPath)) {
if ([string]::IsNullOrWhiteSpace($env:GITHUB_WORKSPACE)) {
throw 'InstallPath must be specified outside GitHub Actions'
}

$toolPath = Join-Path $env:GITHUB_WORKSPACE '.tools'
}

Assert-NotReparsePoint $toolPath
dotnet tool install --tool-path $toolPath dotnet-coverage --version $Version
if ($LASTEXITCODE -ne 0) {
throw "dotnet-coverage installation failed with exit code $LASTEXITCODE"
}

Assert-NotReparsePoint $toolPath
$toolPath >> $env:GITHUB_PATH
if (-not [string]::IsNullOrWhiteSpace($env:GITHUB_PATH)) {
$toolPath >> $env:GITHUB_PATH
}
69 changes: 60 additions & 9 deletions .github/scripts/test-summarize-coverage.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$scriptPath = Join-Path $PSScriptRoot 'summarize-coverage.ps1'
$coverageConfigPath = Join-Path $PSScriptRoot '../coverage.config.xml'
$coverageReportScriptPath = Join-Path $PSScriptRoot 'coverage-report.ps1'
$coverageStaticConfigPath = Join-Path $PSScriptRoot '../coverage.static.config.xml'
$archiveTestResultsActionPath = Join-Path $PSScriptRoot '../actions/archive-test-results/action.yml'
$azureBuildTemplatePath = Join-Path $PSScriptRoot '../../.azure/pipelines/templates/build.yaml'
$azureVariablesPath = Join-Path $PSScriptRoot '../../.azure/pipelines/templates/vars.yaml'
$dotnetTestActionPath = Join-Path $PSScriptRoot '../actions/dotnet-test/action.yml'
$invokeCoverageScriptPath = Join-Path $PSScriptRoot 'invoke-coverage.ps1'
$runTestsActionPath = Join-Path $PSScriptRoot '../actions/run-tests/action.yml'
Expand Down Expand Up @@ -276,14 +280,28 @@ try {
Invoke-Test 'uses external coverage collection for CI builds' {
$dotnetTestAction = Get-Content -Raw -LiteralPath $dotnetTestActionPath
$coverageReportScript = Get-Content -Raw -LiteralPath $coverageReportScriptPath
$coverageConfigs = @(
Get-Content -Raw -LiteralPath $coverageConfigPath
Get-Content -Raw -LiteralPath $coverageStaticConfigPath
)
Assert-Matches `
$dotnetTestAction `
'invoke-coverage\.ps1' `
'Coverage must use the external collector with ContinuousIntegrationBuild.'
Assert-Matches `
$dotnetTestAction `
'-p:ContinuousIntegrationBuild=false' `
'Coverage builds must disable deterministic CI instrumentation.'
'Coverage must use the external collector during CI.'
Assert-Equal `
0 `
([regex]::Matches($dotnetTestAction, 'ContinuousIntegrationBuild=false')).Count `
'GitHub coverage must preserve continuous integration build semantics.'
foreach ($coverageConfig in $coverageConfigs) {
Assert-Matches `
$coverageConfig `
'<DeterministicReport>True</DeterministicReport>' `
'Coverage reports must preserve deterministic source paths.'
Assert-Matches `
$coverageConfig `
'<ExcludeAssembliesWithoutSources>None</ExcludeAssembliesWithoutSources>' `
'Coverage collection must retain symbol-bearing assemblies with deterministic sources.'
}
Assert-Matches `
$dotnetTestAction `
'-IncludeFiles' `
Expand Down Expand Up @@ -373,11 +391,11 @@ exit 0
-Output $coverageOutput `
-RetryLogFile $retryLog `
-CoverageCommand $fakeCollector `
-Command @('dotnet', 'test', '-p:ContinuousIntegrationBuild=false')
-Command @('dotnet', 'test', '--forwarded-argument')
Assert-Equal 0 $LASTEXITCODE 'The retry should succeed.'
Assert-Equal 2 ([int] (Get-Content -Raw -LiteralPath $attemptFile)) 'The collector attempt count differs.'
$retryArguments = Get-Content -LiteralPath "$collectorArguments.2.txt"
Assert-Equal $true ($retryArguments -contains '-p:ContinuousIntegrationBuild=false') 'The inner MSBuild property must be forwarded.'
Assert-Equal $true ($retryArguments -contains '--forwarded-argument') 'The inner test argument must be forwarded.'
Assert-Equal $true ($retryArguments -contains '--log-file') 'The retry must capture a collector log.'
Assert-Equal $true ($retryArguments -contains 'Verbose') 'The retry collector log must be verbose.'

Expand All @@ -388,7 +406,7 @@ exit 0
-Output $coverageOutput `
-RetryLogFile $retryLog `
-CoverageCommand $fakeCollector `
-Command @('dotnet', 'test', '-p:ContinuousIntegrationBuild=false')
-Command @('dotnet', 'test', '--forwarded-argument')
Assert-Equal 1 $LASTEXITCODE 'An unrelated test failure should be preserved.'
Assert-Equal 1 ([int] (Get-Content -Raw -LiteralPath $attemptFile)) 'An unrelated failure must not be retried.'

Expand All @@ -399,7 +417,7 @@ exit 0
-Output $coverageOutput `
-RetryLogFile $retryLog `
-CoverageCommand $fakeCollector `
-Command @('dotnet', 'test', '-p:ContinuousIntegrationBuild=false')
-Command @('dotnet', 'test', '--forwarded-argument')
Assert-Equal 1 $LASTEXITCODE 'A persistent collector failure should be preserved.'
Assert-Equal 2 ([int] (Get-Content -Raw -LiteralPath $attemptFile)) 'The collector must retry only once.'
} finally {
Expand Down Expand Up @@ -484,12 +502,45 @@ exit 0
Assert-Equal 0 ([regex]::Matches($dotnetTestAction, '--project|--test-modules')).Count 'Native test action must discover projects from the solution.'
}

Invoke-Test 'publishes Azure Pipelines coverage' {
$azureBuildTemplate = Get-Content -Raw -LiteralPath $azureBuildTemplatePath
$azureVariables = Get-Content -Raw -LiteralPath $azureVariablesPath
Assert-Matches `
$azureVariables `
'DOTNET_COVERAGE_VERSION:\s*\d+\.\d+\.\d+' `
'Azure Pipelines must pin the coverage collector version.'
Assert-Matches `
$azureBuildTemplate `
'(?s)setup-coverage\.ps1.*?invoke-coverage\.ps1.*?coverage-\$\{\{suite\}\}-\$\{\{framework\}\}\.cobertura\.xml' `
'Azure Pipelines must use the shared coverage scripts to collect a distinct report from every test job.'
Assert-Equal `
0 `
([regex]::Matches($azureBuildTemplate, 'ContinuousIntegrationBuild=false')).Count `
'Azure Pipelines coverage must preserve continuous integration build semantics.'
Assert-Matches `
$azureBuildTemplate `
'(?s)job: PublishCodeCoverage.*?dependsOn:.*?Test_\$\{\{suite\}\}_\$\{\{ replace\(framework.*?DownloadPipelineArtifact@2.*?itemPattern: ''\*\*/\*\.cobertura\.xml''' `
'Azure Pipelines must aggregate coverage after every test matrix job.'
Assert-Matches `
$azureBuildTemplate `
'(?s)PublishCodeCoverageResults@2.*?summaryFileLocation:.*?\*\*/\*\.cobertura\.xml.*?failIfCoverageEmpty: true' `
'Azure Pipelines must publish the aggregated coverage and require results.'
}

Invoke-Test 'validates the coverage tool version' {
$setupCoverageScript = Get-Content -Raw -LiteralPath $setupCoverageScriptPath
Assert-Matches `
$setupCoverageScript `
'DOTNET_COVERAGE_VERSION must specify' `
'Coverage setup must reject a missing tool version.'
Assert-Matches `
$setupCoverageScript `
'InstallPath must be specified outside GitHub Actions' `
'Coverage setup must require an explicit installation path in other CI systems.'
Assert-Matches `
$setupCoverageScript `
'if \(-not \[string\]::IsNullOrWhiteSpace\(\$env:GITHUB_PATH\)\)' `
'Coverage setup must continue adding the tool to the GitHub Actions path.'
Assert-Equal 2 ([regex]::Matches($setupCoverageScript, 'Assert-NotReparsePoint \$toolPath')).Count 'Coverage tool path validation count differs.'
}

Expand Down