183 lines
7.1 KiB
PowerShell
183 lines
7.1 KiB
PowerShell
param(
|
|
[ValidateSet('T0', 'ALL')]
|
|
[string]$Tier = 'T0',
|
|
[string]$OutputDir = ''
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$root = Split-Path -Parent $PSScriptRoot
|
|
$manifestPath = Join-Path $PSScriptRoot 'night-run.manifest.json'
|
|
$manifest = Get-Content -LiteralPath $manifestPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
|
|
if (-not $OutputDir) {
|
|
$OutputDir = Join-Path ([System.IO.Path]::GetTempPath()) ('jiapuapp-night-run-' + (Get-Date -Format 'yyyyMMdd-HHmmss'))
|
|
}
|
|
New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
|
|
|
|
$inventory = @(
|
|
Get-ChildItem -LiteralPath $PSScriptRoot -File -Recurse |
|
|
Where-Object { $_.Extension -in '.ps1', '.js' -and $_.Name -ne 'night-run.ps1' } |
|
|
ForEach-Object { $_.FullName.Substring($root.Length + 1).Replace('\', '/') } |
|
|
Sort-Object
|
|
)
|
|
$powerShellCount = @($inventory | Where-Object { $_.EndsWith('.ps1') }).Count
|
|
$nodeCount = @($inventory | Where-Object { $_.EndsWith('.js') }).Count
|
|
$newExecutableTests = $inventory.Count - $manifest.baselineInventory.powershell - $manifest.baselineInventory.node
|
|
if ($newExecutableTests -lt 0) {
|
|
throw "夜跑 inventory 少于冻结基线:actual=$($inventory.Count) baseline=$($manifest.baselineInventory.powershell + $manifest.baselineInventory.node)"
|
|
}
|
|
|
|
$expectedBlocked = @{}
|
|
foreach ($entry in @($manifest.expectedBlocked)) {
|
|
$expectedBlocked[$entry.script] = [string]$entry.marker
|
|
}
|
|
$h5ChromeRuntimeTests = @{}
|
|
foreach ($script in @($manifest.h5ChromeRuntimeTests)) {
|
|
$h5ChromeRuntimeTests[[string]$script] = $true
|
|
}
|
|
|
|
if ($Tier -eq 'T0') {
|
|
$selected = @($manifest.t0 | Sort-Object -Unique)
|
|
} else {
|
|
$selected = $inventory
|
|
}
|
|
$unknown = @($selected | Where-Object { $_ -notin $inventory })
|
|
if ($unknown.Count -gt 0) {
|
|
throw "夜跑清单引用不存在的测试:$($unknown -join '、')"
|
|
}
|
|
|
|
function Test-H5ChromeRuntime {
|
|
try {
|
|
$pages = Invoke-RestMethod -Uri 'http://127.0.0.1:9222/json/list' -TimeoutSec 2
|
|
return @($pages | Where-Object {
|
|
$_.type -eq 'page' -and $_.url -like 'http://localhost:5173*'
|
|
}).Count -gt 0
|
|
} catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
$needsH5ChromeRuntime = @($selected | Where-Object { $h5ChromeRuntimeTests.ContainsKey($_) }).Count -gt 0
|
|
$h5ChromeRuntimeReady = -not $needsH5ChromeRuntime -or (Test-H5ChromeRuntime)
|
|
|
|
function Test-ExactLine {
|
|
param([string]$Text, [string]$Marker)
|
|
return [regex]::IsMatch($Text, "(?m)^$([regex]::Escape($Marker))`r?$")
|
|
}
|
|
|
|
function Invoke-ManagedTest {
|
|
param([string]$RelativePath)
|
|
$startedAt = Get-Date
|
|
if ($h5ChromeRuntimeTests.ContainsKey($RelativePath) -and -not $h5ChromeRuntimeReady) {
|
|
return [pscustomobject]@{
|
|
script = $RelativePath
|
|
startedAt = $startedAt.ToString('o')
|
|
finishedAt = (Get-Date).ToString('o')
|
|
durationMs = 0
|
|
exitCode = -2
|
|
checkResult = 'INFRA_ERROR'
|
|
expectedBlockedMarker = $null
|
|
infraReason = 'H5_CHROME_RUNTIME_UNAVAILABLE'
|
|
outputPath = $null
|
|
errorPath = $null
|
|
}
|
|
}
|
|
$absolutePath = Join-Path $root $RelativePath.Replace('/', '\')
|
|
$outputPath = Join-Path $OutputDir (($RelativePath -replace '[\\/]', '__') + '.stdout.txt')
|
|
$errorPath = Join-Path $OutputDir (($RelativePath -replace '[\\/]', '__') + '.stderr.txt')
|
|
$fileName = if ($RelativePath.EndsWith('.ps1')) { 'powershell.exe' } else { 'node.exe' }
|
|
$arguments = if ($RelativePath.EndsWith('.ps1')) {
|
|
@('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $absolutePath)
|
|
} else {
|
|
@($absolutePath)
|
|
}
|
|
$startInfo = [System.Diagnostics.ProcessStartInfo]::new()
|
|
$startInfo.FileName = $fileName
|
|
$startInfo.Arguments = (($arguments | ForEach-Object {
|
|
'"' + ([string]$_).Replace('"', '\"') + '"'
|
|
}) -join ' ')
|
|
$startInfo.UseShellExecute = $false
|
|
$startInfo.CreateNoWindow = $true
|
|
$startInfo.RedirectStandardOutput = $true
|
|
$startInfo.RedirectStandardError = $true
|
|
$process = [System.Diagnostics.Process]::new()
|
|
$process.StartInfo = $startInfo
|
|
if (-not $process.Start()) {
|
|
throw "无法启动夜跑子进程:$RelativePath"
|
|
}
|
|
$standardOutputTask = $process.StandardOutput.ReadToEndAsync()
|
|
$standardErrorTask = $process.StandardError.ReadToEndAsync()
|
|
$timedOut = -not $process.WaitForExit([int]$manifest.defaultTimeoutSeconds * 1000)
|
|
if ($timedOut) {
|
|
$process.Kill()
|
|
$process.WaitForExit()
|
|
}
|
|
$exitCode = if ($timedOut) { -1 } else { [int]$process.ExitCode }
|
|
$standardOutput = $standardOutputTask.GetAwaiter().GetResult()
|
|
$standardError = $standardErrorTask.GetAwaiter().GetResult()
|
|
[System.IO.File]::WriteAllText($outputPath, $standardOutput, [System.Text.UTF8Encoding]::new($false))
|
|
[System.IO.File]::WriteAllText($errorPath, $standardError, [System.Text.UTF8Encoding]::new($false))
|
|
$text = ($standardOutput + "`n" + $standardError).Trim()
|
|
$marker = $expectedBlocked[$RelativePath]
|
|
$result = if ($timedOut) {
|
|
'INFRA_ERROR'
|
|
} elseif ($marker) {
|
|
$passMarker = $marker -replace ' BLOCKED$', ' PASS'
|
|
if ($exitCode -eq 0 -and (Test-ExactLine $text $passMarker)) { 'PASS' }
|
|
elseif ($exitCode -ne 0 -and (Test-ExactLine $text $marker)) { 'EXPECTED_BLOCKED' }
|
|
else { 'FAIL' }
|
|
} elseif ($exitCode -eq 0) {
|
|
'PASS'
|
|
} else {
|
|
'FAIL'
|
|
}
|
|
return [pscustomobject]@{
|
|
script = $RelativePath
|
|
startedAt = $startedAt.ToString('o')
|
|
finishedAt = (Get-Date).ToString('o')
|
|
durationMs = [int]((Get-Date) - $startedAt).TotalMilliseconds
|
|
exitCode = $exitCode
|
|
checkResult = $result
|
|
expectedBlockedMarker = $marker
|
|
outputPath = $outputPath
|
|
errorPath = $errorPath
|
|
}
|
|
}
|
|
|
|
$records = @()
|
|
foreach ($test in $selected) {
|
|
$record = Invoke-ManagedTest $test
|
|
$records += $record
|
|
$checkpoint = [pscustomobject]@{
|
|
baselineInventoryTests = [int]$manifest.baselineInventory.powershell + [int]$manifest.baselineInventory.node
|
|
newExecutableTests = $newExecutableTests
|
|
inventoryTests = $inventory.Count
|
|
selectedTests = $selected.Count
|
|
records = $records
|
|
}
|
|
$checkpoint | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDir 'checkpoint.json') -Encoding UTF8
|
|
Write-Output ("{0} {1} exit={2}" -f $record.checkResult, $record.script, $record.exitCode)
|
|
}
|
|
|
|
$summary = [pscustomobject]@{
|
|
baselineInventoryTests = [int]$manifest.baselineInventory.powershell + [int]$manifest.baselineInventory.node
|
|
newExecutableTests = $newExecutableTests
|
|
inventoryTests = $inventory.Count
|
|
scheduledTests = $selected.Count
|
|
executedTests = $records.Count
|
|
passTests = @($records | Where-Object checkResult -eq 'PASS').Count
|
|
failTests = @($records | Where-Object checkResult -eq 'FAIL').Count
|
|
expectedBlockedTests = @($records | Where-Object checkResult -eq 'EXPECTED_BLOCKED').Count
|
|
infraErrorTests = @($records | Where-Object checkResult -eq 'INFRA_ERROR').Count
|
|
timedOutTests = @($records | Where-Object { $_.exitCode -eq -1 }).Count
|
|
notRunScheduledTests = 0
|
|
notRunTests = $inventory.Count - $selected.Count
|
|
outputDir = $OutputDir
|
|
}
|
|
$summary | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $OutputDir 'summary.json') -Encoding UTF8
|
|
Write-Output ('NIGHT-RUN-SUMMARY ' + ($summary | ConvertTo-Json -Compress))
|
|
|
|
if ($summary.infraErrorTests -gt 0) { exit 4 }
|
|
if ($summary.failTests -gt 0) { exit 2 }
|
|
exit 0
|