加快门禁:删除测试窗口、释放桩线程并并行运行三个阶段
关闭后未删除的测试窗口让每次主题切换重排全部残留控件,ui_check 每次关窗还要等桩线程 5 秒。Linux 上结论不变,test_ui_smoke 75→64 s、ui_check 52→16 s;Windows 门禁结果见 .scratch/gate-speed/。
This commit is contained in:
+105
-18
@@ -3,13 +3,17 @@
|
||||
Merge gate for orisys-app: lint the change, run both test suites, run the UI check.
|
||||
|
||||
.DESCRIPTION
|
||||
One command that a change cannot skip. It runs, in order:
|
||||
One command that a change cannot skip. It runs:
|
||||
|
||||
1. tools/lint_gate.py the narrow lint rule set over the changed files
|
||||
2. pytest apps/studio/tests full, with the ``timing`` tests deselected
|
||||
3. pytest shared/tests full, with the ``timing`` tests deselected
|
||||
4. apps/studio/tools/ui_check.py the layout / theme / record-channel checks
|
||||
|
||||
Lint runs first; 2-4 then run at the same time (they write to separate places), so
|
||||
the gate takes as long as the slowest of them. -Serial runs 2-4 one after another
|
||||
with live output; the verdict is the same either way.
|
||||
|
||||
and then holds the run to three contracts:
|
||||
|
||||
* tools/known_failures.txt must be empty. Any content fails the gate: a known
|
||||
@@ -44,6 +48,11 @@
|
||||
.PARAMETER SkipLint
|
||||
Run only the suites and the UI check (used when diagnosing a red gate).
|
||||
|
||||
.PARAMETER Serial
|
||||
Run the studio suite, the shared suite and ui_check one after another with live
|
||||
output, instead of at the same time. The verdict is the same; use it to read a
|
||||
failing stage as it runs, or when a parallel run is suspected of interfering.
|
||||
|
||||
.EXAMPLE
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File tools/gate.ps1
|
||||
|
||||
@@ -58,7 +67,8 @@ param(
|
||||
[string]$Task = 'gate',
|
||||
[string]$Python = '',
|
||||
[string]$LintBase = 'origin/main',
|
||||
[switch]$SkipLint
|
||||
[switch]$SkipLint,
|
||||
[switch]$Serial
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
@@ -218,22 +228,21 @@ try {
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------- test suites
|
||||
function Invoke-Suite([string]$Name, [string]$Path) {
|
||||
function Get-SuiteArguments([string]$Name, [string]$Path) {
|
||||
$safe = $Name.Replace(' ', '-')
|
||||
$log = Join-Path $Scratch "$safe.log"
|
||||
$xml = Join-Path $Scratch "$safe.xml"
|
||||
$base = Join-Path $Scratch "pytest-$safe"
|
||||
Write-Step "$Name"
|
||||
$exit = Invoke-Native $Python @(
|
||||
return @(
|
||||
'-m', 'pytest', $Path,
|
||||
'-m', 'not timing',
|
||||
'-p', 'no:cacheprovider',
|
||||
"--basetemp=$base",
|
||||
"--junitxml=$xml"
|
||||
) $log
|
||||
"--basetemp=$(Join-Path $Scratch "pytest-$safe")",
|
||||
"--junitxml=$(Join-Path $Scratch "$safe.xml")"
|
||||
)
|
||||
}
|
||||
|
||||
function Read-Suite([string]$Name, [int]$Exit) {
|
||||
$xml = Join-Path $Scratch "$($Name.Replace(' ', '-')).xml"
|
||||
$counts = [pscustomobject]@{
|
||||
Tests = 0; Failures = 0; Errors = 0; Skipped = 0; Exit = $exit; Xml = (Test-Path -LiteralPath $xml)
|
||||
Tests = 0; Failures = 0; Errors = 0; Skipped = 0; Exit = $Exit; Xml = (Test-Path -LiteralPath $xml)
|
||||
}
|
||||
if ($counts.Xml) {
|
||||
try {
|
||||
@@ -252,6 +261,51 @@ try {
|
||||
return $counts
|
||||
}
|
||||
|
||||
# A stage started in the background: stdout and stderr go to two files, joined into
|
||||
# <stage>.log when it ends, so every stage leaves the same one log as a serial run.
|
||||
function Start-Stage([string]$Name, [string[]]$Arguments) {
|
||||
$safe = $Name.Replace(' ', '-')
|
||||
$out = Join-Path $Scratch "$safe.out.tmp"
|
||||
$err = Join-Path $Scratch "$safe.err.tmp"
|
||||
# Start-Process joins -ArgumentList with spaces and quotes nothing, so each
|
||||
# argument is quoted here ('not timing' is one argument).
|
||||
$quoted = @($Arguments | ForEach-Object { '"' + ($_ -replace '"', '\"') + '"' })
|
||||
$options = @{
|
||||
FilePath = $Python
|
||||
ArgumentList = $quoted
|
||||
WorkingDirectory = $Repo
|
||||
RedirectStandardOutput = $out
|
||||
RedirectStandardError = $err
|
||||
NoNewWindow = $true
|
||||
PassThru = $true
|
||||
}
|
||||
$began = Get-Date
|
||||
$process = Start-Process @options
|
||||
# Windows PowerShell 5.1 reports no ExitCode for a -PassThru process unless its
|
||||
# handle was read while the process ran.
|
||||
$null = $process.Handle
|
||||
return [pscustomobject]@{
|
||||
Name = $Name; Process = $process; Out = $out; Err = $err; Began = $began
|
||||
Log = (Join-Path $Scratch "$safe.log")
|
||||
}
|
||||
}
|
||||
|
||||
function Complete-Stage($Stage) {
|
||||
$Stage.Process.WaitForExit()
|
||||
$exit = $Stage.Process.ExitCode
|
||||
# No exit code is not a pass: treat it like a crash that left no answer.
|
||||
if ($null -eq $exit) { $exit = -1 }
|
||||
$text = New-Object System.Collections.ArrayList
|
||||
foreach ($part in @($Stage.Out, $Stage.Err)) {
|
||||
if (Test-Path -LiteralPath $part) {
|
||||
foreach ($line in (Get-Content -LiteralPath $part -Encoding UTF8)) { [void]$text.Add($line) }
|
||||
Remove-Item -LiteralPath $part -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
Set-Content -LiteralPath $Stage.Log -Value $text -Encoding UTF8
|
||||
return $exit
|
||||
}
|
||||
|
||||
#: An access violation while Qt tears the window down after the last assertion has
|
||||
#: been seen (exit 0xC0000005 as a signed int). It is allowed and reported; every
|
||||
#: other non-zero exit from a suite with a clean JUnit report is a gate failure.
|
||||
@@ -311,18 +365,48 @@ try {
|
||||
exit 2
|
||||
}
|
||||
|
||||
Write-Step 'studio suite'
|
||||
$studio = Invoke-Suite 'studio suite' 'apps/studio/tests'
|
||||
# -------------------------------------------------------------- suites and ui_check
|
||||
# The three stages are independent processes and write to separate places (studio
|
||||
# tests: apps/studio/outputs/_test_record*, shared tests: shared/tests/_scratch,
|
||||
# ui_check: apps/studio/outputs/_*_check, each pytest run its own --basetemp), so by
|
||||
# default they run at the same time and the gate takes as long as the slowest one.
|
||||
# Child output is UTF-8: a finding with Chinese text must not crash a cp1252 console.
|
||||
$env:PYTHONIOENCODING = 'utf-8'
|
||||
$stages = @(
|
||||
[pscustomobject]@{ Name = 'studio suite'; Args = (Get-SuiteArguments 'studio suite' 'apps/studio/tests') },
|
||||
[pscustomobject]@{ Name = 'shared suite'; Args = (Get-SuiteArguments 'shared suite' 'shared/tests') },
|
||||
[pscustomobject]@{ Name = 'ui_check'; Args = @((Join-Path $Repo 'apps\studio\tools\ui_check.py')) }
|
||||
)
|
||||
$exits = @{}
|
||||
$durations = @{}
|
||||
if ($Serial) {
|
||||
foreach ($stage in $stages) {
|
||||
Write-Step $stage.Name
|
||||
$log = Join-Path $Scratch "$($stage.Name.Replace(' ', '-')).log"
|
||||
$began = Get-Date
|
||||
$exits[$stage.Name] = Invoke-Native $Python $stage.Args $log
|
||||
$durations[$stage.Name] = ((Get-Date) - $began).TotalSeconds
|
||||
}
|
||||
} else {
|
||||
Write-Step 'studio suite, shared suite and ui_check, in parallel (-Serial for live output)'
|
||||
$running = @(foreach ($stage in $stages) { Start-Stage $stage.Name $stage.Args })
|
||||
foreach ($stage in $running) {
|
||||
$exits[$stage.Name] = Complete-Stage $stage
|
||||
# StartTime cannot be read once a process has exited; the launch time is ours.
|
||||
$durations[$stage.Name] = ($stage.Process.ExitTime - $stage.Began).TotalSeconds
|
||||
Write-Host (" {0,-13} exit {1} {2:n0} s ({3})" -f $stage.Name, $exits[$stage.Name], $durations[$stage.Name], $stage.Log)
|
||||
}
|
||||
}
|
||||
|
||||
$studio = Read-Suite 'studio suite' $exits['studio suite']
|
||||
Test-Suite 'studio suite' $studio $budget 'studio_min_tests' 'studio_max_skips'
|
||||
|
||||
Write-Step 'shared suite'
|
||||
$shared = Invoke-Suite 'shared suite' 'shared/tests'
|
||||
$shared = Read-Suite 'shared suite' $exits['shared suite']
|
||||
Test-Suite 'shared suite' $shared $budget 'shared_min_tests' 'shared_max_skips'
|
||||
|
||||
# -------------------------------------------------------------- ui_check
|
||||
Write-Step 'ui_check'
|
||||
$uiLog = Join-Path $Scratch 'ui_check.log'
|
||||
$uiExit = Invoke-Native $Python @((Join-Path $Repo 'apps\studio\tools\ui_check.py')) $uiLog
|
||||
$uiExit = $exits['ui_check']
|
||||
# ui_check prints "scenes: 4 checks: 631 failures: 2"; the last number is the
|
||||
# verdict. Its exit code is also the verdict: a script that dies partway through
|
||||
# (missing worker signature, an import error) must not pass on a stale summary line.
|
||||
@@ -358,6 +442,9 @@ try {
|
||||
[void]$lines.Add("Platform : Windows")
|
||||
[void]$lines.Add("Python : $Python")
|
||||
[void]$lines.Add("LintBase : $LintBase")
|
||||
$mode = if ($Serial) { 'serial' } else { 'parallel' }
|
||||
$timing = ($stages | ForEach-Object { "{0} {1:n0} s" -f $_.Name, $durations[$_.Name] }) -join ', '
|
||||
[void]$lines.Add("Stages : $mode; $timing")
|
||||
[void]$lines.Add("Scratch : $Scratch")
|
||||
[void]$lines.Add('')
|
||||
[void]$lines.Add('| Check | Result | Detail |')
|
||||
|
||||
Reference in New Issue
Block a user