[CmdletBinding()] param() function Assert-Containment { [CmdletBinding(DefaultParameterSetName = "LiteralMessage")] param( [string]$Text, [string[]]$Substrings, [Parameter(Mandatory, ParameterSetName = "LiteralMessage")] [string]$Message, [Parameter(Mandatory, ParameterSetName = "FormattedMessage")] [scriptblock]$ItemMessageFormatter, [scriptblock]$SubstringFormatter = { param($substring) $substring }, [switch]$Absent ) foreach ($substring in $Substrings) { $expectedSubstring = & $SubstringFormatter $substring $containsSubstring = $Text.Contains($expectedSubstring) $validationFailed = if ($Absent) { $containsSubstring } else { -not $containsSubstring } if ($validationFailed) { $formattedMessage = if ($PSCmdlet.ParameterSetName -eq "FormattedMessage") { & $ItemMessageFormatter $substring } else { $Message } throw $formattedMessage } } } $repoRoot = Split-Path $PSScriptRoot -Parent $globalJsonPath = Join-Path $repoRoot "global.json" $versionsPropsPath = Join-Path $repoRoot "eng/Versions.props" $pipelinePath = Join-Path $repoRoot "azure-pipelines.yml" $testTemplatePath = Join-Path $repoRoot "eng/pipelines/steps/test-windows-configuration-tests.yml" $accessDatabaseInstallerPath = Join-Path $repoRoot "eng/install-access-database-engine.ps1" $wellKnownEnvironmentVariablesPath = Join-Path $repoRoot "test/Utilities/Microsoft.Testing.TestInfrastructure/WellKnownEnvironmentVariables.cs" $configuration = Get-Content -LiteralPath $globalJsonPath -Raw | ConvertFrom-Json $affectedTests = $configuration.test.affectedTests if ($null -eq $affectedTests) { throw "global.json must define test.affectedTests." } foreach ($path in @( "changes.ignore", "changes.forceAllTests" )) { $value = $affectedTests foreach ($segment in $path.Split(".")) { $value = $value.$segment } if ($value -isnot [System.Array] -or $value.Count -eq 0) { throw "global.json test.affectedTests.$path must be a non-empty array." } } foreach ($requiredForceAllPattern in @( "Directory.Build.*", "src/**/Directory.Build.*", "test/**/Directory.Build.*" )) { if ($requiredForceAllPattern -notin $affectedTests.changes.forceAllTests) { throw "global.json test.affectedTests.changes.forceAllTests must include '$requiredForceAllPattern'." } } $globalJsonText = Get-Content -LiteralPath $globalJsonPath -Raw if ($globalJsonText -match '(?i)"[^"]*(token|secret|password|connectionString|sas)[^"]*"\s*:') { throw "global.json must not contain affected-test credentials or secret-bearing settings." } $pipeline = Get-Content -LiteralPath $pipelinePath -Raw $affectedTestsCall = [regex]::Match( $pipeline, '(?s)enableAffectedTests:\s*(?true|false)\s+\$\{\{ if eq\(variables\[''Build\.SourceBranch''\], ''refs/heads/main''\) \}\}:\s+affectedTestsMode:\s*collect\s+\$\{\{ else \}\}:\s+affectedTestsMode:\s*run') if (-not $affectedTestsCall.Success) { throw "The pipeline must route the Windows test call site to collection on main and selection on PRs." } $affectedTestsEnabled = $affectedTestsCall.Groups["enabled"].Value -eq "true" $versionsProps = Get-Content -LiteralPath $versionsPropsPath -Raw $affectedTestsPackageVersionMatch = [regex]::Match( $versionsProps, '(?[^<]+)') if (-not $affectedTestsPackageVersionMatch.Success) { throw "eng/Versions.props must define MicrosoftTestingExtensionsAffectedTestsVersion." } $affectedTestsPackageVersion = $affectedTestsPackageVersionMatch.Groups["version"].Value $azureDevOpsStorageVersionsMissingContentRange = @( "18.12.0-preview.26473.3", "18.12.0-preview.26474.2" ) if ($affectedTestsEnabled -and $affectedTestsPackageVersion -in $azureDevOpsStorageVersionsMissingContentRange) { throw "Affected-test execution must remain disabled with Azure DevOps storage package version '$affectedTestsPackageVersion', which omits the required Content-Range upload header." } $bootstrappedSdk = [System.Management.Automation.SemanticVersion]$configuration.tools.dotnet $selectedSdk = [System.Management.Automation.SemanticVersion]$configuration.sdk.version if ($bootstrappedSdk -ne $selectedSdk) { throw "global.json tools.dotnet and sdk.version must match." } $affectedTestsSdkVersionsWithCollectionHandshakeFailure = @( [System.Management.Automation.SemanticVersion]"11.0.100-rc.2.26471.109", [System.Management.Automation.SemanticVersion]"11.0.100-rtm.26476.107", [System.Management.Automation.SemanticVersion]"12.0.100-alpha.1.26506.125" ) if ($affectedTestsEnabled -and $selectedSdk -in $affectedTestsSdkVersionsWithCollectionHandshakeFailure) { throw "Affected-test execution must remain disabled with SDK '$selectedSdk', which reports successful collection children as handshake failures." } $minimumAffectedTestsSdk = [System.Management.Automation.SemanticVersion]"12.0.100-alpha.1.26509.101" if ($affectedTestsEnabled -and $bootstrappedSdk -lt $minimumAffectedTestsSdk) { throw "Affected-test execution requires SDK $minimumAffectedTestsSdk or newer with dotnet/sdk#56538." } if ($null -eq $affectedTests.storage) { throw "Affected-test pipeline execution requires test.affectedTests.storage." } if ($affectedTests.storage.type -ne "azureDevOpsArtifact" -or $affectedTests.storage.project -ne "public" -or $affectedTests.storage.buildDefinitionId -ne 209 -or $affectedTests.storage.artifactName -ne "TestFx_AffectedTestsMaps") { throw "Affected-test pipeline execution must use the testfx Azure DevOps artifact provider configuration." } $directoryPackagesPath = Join-Path $repoRoot "Directory.Packages.props" $directoryPackages = Get-Content -LiteralPath $directoryPackagesPath -Raw Assert-Containment ` -Text $directoryPackages ` -Substrings '' ` -Message "Directory.Packages.props must use the affected-tests dependency version." Assert-Containment ` -Text $directoryPackages ` -Substrings '' ` -Message "Directory.Packages.props must align the Azure DevOps provider with the affected-tests dependency." $directoryBuildTargetsPath = Join-Path $repoRoot "Directory.Build.targets" $directoryBuildTargets = Get-Content -LiteralPath $directoryBuildTargetsPath -Raw Assert-Containment ` -Text $directoryBuildTargets ` -Substrings '