Files
ARCADIA-Studio/harness/experiments/scenario-label-fields-4c9bc87-20260913/original/run.ps1
T

99 lines
7.9 KiB
PowerShell

#Requires -Version 7.0
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][ValidateSet('list','normal','font','ffi','auto-transition','timing')][string]$Stage,
[Parameter(Mandatory=$true)][string]$Output
)
Set-StrictMode -Version Latest
$ErrorActionPreference='Stop'
$PSNativeCommandUseErrorActionPreference=$false
$Output=(Resolve-Path -LiteralPath $Output).Path
function Hash([string]$p){(Get-FileHash -LiteralPath $p -Algorithm SHA256).Hash.ToLowerInvariant()}
function Json([string]$p){Get-Content -LiteralPath $p -Raw|ConvertFrom-Json}
function Save([string]$p,$v){$b=[Text.UTF8Encoding]::new($false).GetBytes(($v|ConvertTo-Json -Depth 16)+[Environment]::NewLine);$f=[IO.File]::Open($p,'CreateNew','Write','Read');try{$f.Write($b,0,$b.Length)}finally{$f.Dispose()}}
$receiptPath=Join-Path $Output 'build-receipt.json'
$r=Json $receiptPath
if($r.exit_code -ne 0 -or -not $r.inputs_unchanged -or $null -ne $r.postflight_failure -or $r.tests_executed){throw 'Closed successful compile-only receipt required'}
$expectedPath=Join-Path $PSScriptRoot 'expected-tests.json'
$e=Json $expectedPath
if($r.source_commit -cne '4c9bc87e4e66bba573dd63b23df5ea039f85d0ca' -or $r.source_commit -cne $e.source_commit){throw 'Wrong source pin'}
if(@($e.tests).Count -ne 25 -or @($e.normal).Count -ne 21 -or @($e.isolated).Count -ne 4 -or @($e.tests|Sort-Object -Unique).Count -ne 25 -or (Compare-Object ($e.tests|Sort-Object) (@($e.normal)+@($e.isolated)|Sort-Object))){throw 'Expected inventory is not a disjoint complete 25-test partition'}
$exe=$r.binary.path
$dll=Join-Path $Output 'libunwind.dll'
if((Hash $exe) -cne $r.binary.sha256 -or (Get-Item -LiteralPath $exe).Length -ne $r.binary.bytes -or (Hash $dll) -cne $r.runtime_sha256){throw 'Executable or runtime differs from build'}
$manifestPath=Join-Path $Output 'inputs-after.json'
if((Hash $manifestPath) -cne $r.after_manifest_sha256){throw 'Build input capture changed'}
$sourceInputs=@((Json $manifestPath)|Where-Object {$_.path -match 'scenario-label-fields-(harness|ytr83r)'})
if(-not @($sourceInputs|Where-Object {$_.path -like '*fixtures*NotoSans-Regular.ttf'}).Count){throw 'Build capture omitted the font fixture'}
foreach($s in $sourceInputs){if((Hash $s.path) -cne $s.sha256 -or (Get-Item -LiteralPath $s.path).Length -ne $s.bytes){throw ('Source/recipe changed: '+$s.path)}}
$prior=switch($Stage){normal{'list'} font{'normal'} ffi{'font'} auto-transition{'ffi'} timing{'auto-transition'} default{$null}}
$paths=@($PSCommandPath,$receiptPath,$manifestPath,$expectedPath,$exe,$dll)+@($sourceInputs.path)
$priorReceipt=$null
if($prior){
$p=Join-Path $Output ($prior+'-run-receipt.json')
$previous=Json $p
if($previous.stage -cne $prior -or -not $previous.passed -or $previous.source_commit -cne $r.source_commit -or $previous.build_receipt_sha256 -cne (Hash $receiptPath) -or $previous.expected_tests_sha256 -cne (Hash $expectedPath) -or $previous.binary_sha256 -cne $r.binary.sha256 -or $previous.runtime_sha256 -cne $r.runtime_sha256){throw 'Required exact previous stage did not pass'}
$priorLog=Join-Path $Output ($prior+'.log')
if((Hash $priorLog) -cne $previous.log_sha256){throw 'Previous raw log changed'}
$paths+=@($p,$priorLog)
$priorReceipt=[ordered]@{stage=$prior;path=$p;sha256=(Hash $p);log_sha256=(Hash $priorLog)}
}
function Capture {@($paths|Sort-Object -Unique|ForEach-Object {[ordered]@{path=$_;sha256=(Hash $_);bytes=(Get-Item -LiteralPath $_).Length}})}
$log=Join-Path $Output ($Stage+'.log')
$receipt=Join-Path $Output ($Stage+'-run-receipt.json')
if((Test-Path -LiteralPath $log) -or (Test-Path -LiteralPath $receipt)){throw 'Preserve existing stage evidence; use a new recipe/output for retry'}
$before=@(Capture)
$font=$e.stages.font
$ffi=$e.stages.ffi
$auto=$e.stages.'auto-transition'
$timing=$e.stages.timing
$argsForExe=@(switch($Stage){list{'--list'} normal{'--test-threads=1';'--show-output'} font{'--exact';$font;'--ignored';'--show-output';'--test-threads=1'} ffi{'--exact';$ffi;'--ignored';'--show-output';'--test-threads=1'} auto-transition{'--exact';$auto;'--ignored';'--show-output';'--test-threads=1'} timing{'--exact';$timing;'--ignored';'--show-output';'--test-threads=1'}})
$oldPin=$env:SCENARIO_LABEL_FIELDS_SOURCE_PIN
$start=[DateTime]::UtcNow.ToString('o')
try{
$env:SCENARIO_LABEL_FIELDS_SOURCE_PIN=$r.source_commit
# Explicit native arguments are intentional: the former scalar --list wrapper
# accidentally executed default tests. Do not replace these with that dispatch.
switch($Stage){
list {& $exe --list *> $log}
normal {& $exe --test-threads=1 --show-output *> $log}
font {& $exe --exact $font --ignored --show-output --test-threads=1 *> $log}
ffi {& $exe --exact $ffi --ignored --show-output --test-threads=1 *> $log}
auto-transition {& $exe --exact $auto --ignored --show-output --test-threads=1 *> $log}
timing {& $exe --exact $timing --ignored --show-output --test-threads=1 *> $log}
}
$code=$LASTEXITCODE
}finally{$env:SCENARIO_LABEL_FIELDS_SOURCE_PIN=$oldPin}
$end=[DateTime]::UtcNow.ToString('o')
$failure=$null;$passed=$false;$after=@();$inventory=@();$results=@();$summary=$null
try{
$after=@(Capture)
if(($before|ConvertTo-Json -Depth 6 -Compress) -cne ($after|ConvertTo-Json -Depth 6 -Compress)){throw 'Run inputs changed'}
$raw=Get-Content -LiteralPath $log -Raw
if($Stage -eq 'list'){
$inventory=@([regex]::Matches($raw,'(?m)^(.+): test\r?$')|ForEach-Object {$_.Groups[1].Value})
if($inventory.Count -ne 25 -or @($inventory|Sort-Object -Unique).Count -ne 25 -or (Compare-Object ($e.tests|Sort-Object) ($inventory|Sort-Object)) -or $raw -notmatch '(?m)^25 tests, 0 benchmarks\r?$' -or $raw -match '(?m)^test result:|^running \d+ tests'){throw 'Actual list differs or unexpectedly executed tests'}
}else{
$rows=@([regex]::Matches($raw,'(?m)^test (.+?) \.\.\. (ok|FAILED|ignored)(?:, [^\r\n]*)?\r?$'))
$inventory=@($rows|ForEach-Object {$_.Groups[1].Value})
$want=@(switch($Stage){normal{$e.tests} font{$font} ffi{$ffi} auto-transition{$auto} timing{$timing}})
if($inventory.Count -ne $want.Count -or @($inventory|Sort-Object -Unique).Count -ne $want.Count -or (Compare-Object ($want|Sort-Object) ($inventory|Sort-Object))){throw 'Executed membership differs'}
foreach($row in $rows){
$status=if($Stage -eq 'normal' -and $row.Groups[1].Value -in $e.isolated){'ignored'}else{'ok'}
$results+=[ordered]@{name=$row.Groups[1].Value;result=$row.Groups[2].Value}
if($row.Groups[2].Value -cne $status){throw ('Unexpected outcome: '+$row.Value)}
}
$summaries=[regex]::Matches($raw,'(?m)^test result: ok\. (\d+) passed; (\d+) failed; (\d+) ignored; (\d+) measured; (\d+) filtered out; finished in ([\d.]+)s\r?$')
if($summaries.Count -ne 1){throw 'Missing or duplicate successful test summary'}
$match=$summaries[0]
$wantSummary=if($Stage -eq 'normal'){@('21','0','4','0','0')}else{@('1','0','0','0','24')}
for($i=0;$i -lt 5;$i++){if($match.Groups[$i+1].Value -cne $wantSummary[$i]){throw 'Unexpected test summary'}}
$summary=[ordered]@{passed=[int]$match.Groups[1].Value;failed=[int]$match.Groups[2].Value;ignored=[int]$match.Groups[3].Value;measured=[int]$match.Groups[4].Value;filtered=[int]$match.Groups[5].Value;seconds=$match.Groups[6].Value;raw=$match.Value}
}
if($code -ne 0){throw ('Executable exit '+$code)}
$passed=$true
}catch{$failure=$_.Exception.Message}
Save $receipt ([ordered]@{schema='scenario_label_field_run_v3';stage=$Stage;source_commit=$r.source_commit;command=@($exe)+$argsForExe;start_utc=$start;end_utc=$end;exit_code=$code;passed=$passed;failure=$failure;build_receipt_sha256=(Hash $receiptPath);expected_tests_sha256=(Hash $expectedPath);run_script_sha256=(Hash $PSCommandPath);binary_sha256=$r.binary.sha256;runtime_sha256=$r.runtime_sha256;prior=$priorReceipt;log_sha256=(Hash $log);inventory=$inventory;results=$results;summary=$summary;before=$before;after=$after;scope='Isolated CPU Iced/Cosmic label checks; timing optional; no native renderer or navigation acceptance'})
Write-Output ($Stage+': passed='+$passed+'; exit='+$code)
if(-not $passed){throw $failure}