完成70%
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
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
|
||||
Reference in New Issue
Block a user