Match native rounded-pixel grab rays and retry missing startup PID
This commit is contained in:
@@ -8,10 +8,11 @@ $checks=[Collections.Generic.List[object]]::new(); $commands=[Collections.Generi
|
|||||||
$result=[ordered]@{milestone='Content';sourceCommit=(& git rev-parse HEAD);sourceDirty=[bool](& git status --porcelain);serial=$Serial;apkSha256=(Get-FileHash -LiteralPath $ApkPath -Algorithm SHA256).Hash;status='failed'}
|
$result=[ordered]@{milestone='Content';sourceCommit=(& git rev-parse HEAD);sourceDirty=[bool](& git status --porcelain);serial=$Serial;apkSha256=(Get-FileHash -LiteralPath $ApkPath -Algorithm SHA256).Hash;status='failed'}
|
||||||
$result.rendererProjection=@{fieldOfView=62;fixedWorldPivot=@(0,.65,0);rawCamera='Saved Board.camera JSON remains canonical';viewport='Landscape wm size max/min; remaining UI and physical touch helper assume sole 2400x1080 AVD'}
|
$result.rendererProjection=@{fieldOfView=62;fixedWorldPivot=@(0,.65,0);rawCamera='Saved Board.camera JSON remains canonical';viewport='Landscape wm size max/min; remaining UI and physical touch helper assume sole 2400x1080 AVD'}
|
||||||
$networkBefore=$null; $musicBefore=$null
|
$networkBefore=$null; $musicBefore=$null
|
||||||
function Adb([string[]]$Arguments) {
|
function Adb([string[]]$Arguments,[switch]$AllowMissingPid) {
|
||||||
$started=[DateTime]::UtcNow.ToString('o'); $output=& $adb -s $Serial @Arguments 2>&1; $code=$LASTEXITCODE
|
$started=[DateTime]::UtcNow.ToString('o'); $output=& $adb -s $Serial @Arguments 2>&1; $code=$LASTEXITCODE
|
||||||
$commands.Add(@{startedUtc=$started;finishedUtc=[DateTime]::UtcNow.ToString('o');command=('adb -s '+$Serial+' '+($Arguments -join ' '));exitCode=$code;output=($output -join "`n")})
|
$commands.Add(@{startedUtc=$started;finishedUtc=[DateTime]::UtcNow.ToString('o');command=('adb -s '+$Serial+' '+($Arguments -join ' '));exitCode=$code;output=($output -join "`n")})
|
||||||
if ($code -ne 0) { throw "ADB exit $code : $($Arguments -join ' ')" }; return ($output -join "`n").Trim()
|
$missingPid=$AllowMissingPid -and $code-eq1 -and $Arguments.Count-eq3 -and $Arguments[0]-eq'shell' -and $Arguments[1]-eq'pidof' -and $Arguments[2]-eq$package -and [string]::IsNullOrWhiteSpace(($output -join "`n"))
|
||||||
|
if ($code -ne 0 -and !$missingPid) { throw "ADB exit $code : $($Arguments -join ' ')" }; return ($output -join "`n").Trim()
|
||||||
}
|
}
|
||||||
function Check([string]$Id,[bool]$Passed,[string]$Actual) {
|
function Check([string]$Id,[bool]$Passed,[string]$Actual) {
|
||||||
$checks.Add(@{id=$Id;passed=$Passed;actual=$Actual}); if (!$Passed) { throw "Content check failed: $Id ($Actual)" }
|
$checks.Add(@{id=$Id;passed=$Passed;actual=$Actual}); if (!$Passed) { throw "Content check failed: $Id ($Actual)" }
|
||||||
@@ -49,7 +50,7 @@ function StartHome([string]$Name) {
|
|||||||
$ready=$false; $pauseRequested=$false
|
$ready=$false; $pauseRequested=$false
|
||||||
Add-Type -AssemblyName System.Drawing
|
Add-Type -AssemblyName System.Drawing
|
||||||
for($attempt=0;$attempt-lt30;$attempt++) {
|
for($attempt=0;$attempt-lt30;$attempt++) {
|
||||||
$pidText=Adb @('shell','pidof',$package)
|
$pidText=Adb @('shell','pidof',$package) -AllowMissingPid
|
||||||
$saveText=Adb @('shell',('if test -s '+$remote+'; then cat '+$remote+'; fi'))
|
$saveText=Adb @('shell',('if test -s '+$remote+'; then cat '+$remote+'; fi'))
|
||||||
$validSave=$false
|
$validSave=$false
|
||||||
try { $state=$saveText|ConvertFrom-Json -ErrorAction Stop; $validSave=$state.schemaVersion-eq1 -and $null-ne$state.completedStageIds -and $null-ne$state.puzzleStates } catch { }
|
try { $state=$saveText|ConvertFrom-Json -ErrorAction Stop; $validSave=$state.schemaVersion-eq1 -and $null-ne$state.completedStageIds -and $null-ne$state.puzzleStates } catch { }
|
||||||
@@ -84,6 +85,7 @@ function StartHome([string]$Name) {
|
|||||||
$pauseRequested=$true
|
$pauseRequested=$true
|
||||||
}
|
}
|
||||||
if($pidText-match'^\d+'-and$validSave-and$ui){$ready=$true;$saveText|Set-Content (Join-Path $EvidenceDir "$Name-ready-save.json") -Encoding utf8;break}
|
if($pidText-match'^\d+'-and$validSave-and$ui){$ready=$true;$saveText|Set-Content (Join-Path $EvidenceDir "$Name-ready-save.json") -Encoding utf8;break}
|
||||||
|
if($pidText-notmatch'^\d+'){Adb @('shell','am','start','-W','-n',$activity)|Out-Null}
|
||||||
}
|
}
|
||||||
$observations.ToArray()|ConvertTo-Json -Depth 6|Set-Content (Join-Path $EvidenceDir "$Name-readiness.json") -Encoding utf8
|
$observations.ToArray()|ConvertTo-Json -Depth 6|Set-Content (Join-Path $EvidenceDir "$Name-readiness.json") -Encoding utf8
|
||||||
Check "$Name-home-ready" $ready 'Actual app PID, landscape home colors/button/title and app save JSON; bounded30 observations, black/Unity splash excluded, PNG retained for direct review'
|
Check "$Name-home-ready" $ready 'Actual app PID, landscape home colors/button/title and app save JSON; bounded30 observations, black/Unity splash excluded, PNG retained for direct review'
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ $checks=[Collections.Generic.List[object]]::new(); $commands=[Collections.Generi
|
|||||||
$result=[ordered]@{milestone='Final';sourceCommit=(& git rev-parse HEAD);sourceDirty=[bool](& git status --porcelain);serial=$Serial;apkSha256=(Get-FileHash -LiteralPath $ApkPath -Algorithm SHA256).Hash;status='failed'}
|
$result=[ordered]@{milestone='Final';sourceCommit=(& git rev-parse HEAD);sourceDirty=[bool](& git status --porcelain);serial=$Serial;apkSha256=(Get-FileHash -LiteralPath $ApkPath -Algorithm SHA256).Hash;status='failed'}
|
||||||
$result.rendererProjection=@{fieldOfView=62;fixedWorldPivot=@(0,.65,0);rawCamera='Saved Board.camera JSON remains canonical';viewport='Landscape wm size max/min; remaining UI and physical touch helper assume sole 2400x1080 AVD'}
|
$result.rendererProjection=@{fieldOfView=62;fixedWorldPivot=@(0,.65,0);rawCamera='Saved Board.camera JSON remains canonical';viewport='Landscape wm size max/min; remaining UI and physical touch helper assume sole 2400x1080 AVD'}
|
||||||
$networkBefore=$null; $musicBefore=$null
|
$networkBefore=$null; $musicBefore=$null
|
||||||
function Adb([string[]]$Arguments) {
|
function Adb([string[]]$Arguments,[switch]$AllowMissingPid) {
|
||||||
$started=[DateTime]::UtcNow.ToString('o'); $output=& $adb -s $Serial @Arguments 2>&1; $code=$LASTEXITCODE
|
$started=[DateTime]::UtcNow.ToString('o'); $output=& $adb -s $Serial @Arguments 2>&1; $code=$LASTEXITCODE
|
||||||
$commands.Add(@{startedUtc=$started;finishedUtc=[DateTime]::UtcNow.ToString('o');command=('adb -s '+$Serial+' '+($Arguments -join ' '));exitCode=$code;output=($output -join "`n")})
|
$commands.Add(@{startedUtc=$started;finishedUtc=[DateTime]::UtcNow.ToString('o');command=('adb -s '+$Serial+' '+($Arguments -join ' '));exitCode=$code;output=($output -join "`n")})
|
||||||
if ($code -ne 0) { throw "ADB exit $code : $($Arguments -join ' ')" }; return ($output -join "`n").Trim()
|
$missingPid=$AllowMissingPid -and $code-eq1 -and $Arguments.Count-eq3 -and $Arguments[0]-eq'shell' -and $Arguments[1]-eq'pidof' -and $Arguments[2]-eq$package -and [string]::IsNullOrWhiteSpace(($output -join "`n"))
|
||||||
|
if ($code -ne 0 -and !$missingPid) { throw "ADB exit $code : $($Arguments -join ' ')" }; return ($output -join "`n").Trim()
|
||||||
}
|
}
|
||||||
function Check([string]$Id,[bool]$Passed,[string]$Actual) {
|
function Check([string]$Id,[bool]$Passed,[string]$Actual) {
|
||||||
$checks.Add(@{id=$Id;passed=$Passed;actual=$Actual}); if (!$Passed) { throw "Content check failed: $Id ($Actual)" }
|
$checks.Add(@{id=$Id;passed=$Passed;actual=$Actual}); if (!$Passed) { throw "Content check failed: $Id ($Actual)" }
|
||||||
@@ -111,18 +112,26 @@ function BallClear($Board,[string]$Id) {
|
|||||||
$camera=$Board.camera
|
$camera=$Board.camera
|
||||||
$rotation=[Numerics.Quaternion]::new($camera.rotation.x,$camera.rotation.y,$camera.rotation.z,$camera.rotation.w)
|
$rotation=[Numerics.Quaternion]::new($camera.rotation.x,$camera.rotation.y,$camera.rotation.z,$camera.rotation.w)
|
||||||
$origin=[Numerics.Vector3]::new($camera.target.x,($camera.target.y+.65),$camera.target.z)+[Numerics.Vector3]::Transform([Numerics.Vector3]::new(0,0,-$camera.distance),$rotation)
|
$origin=[Numerics.Vector3]::new($camera.target.x,($camera.target.y+.65),$camera.target.z)+[Numerics.Vector3]::Transform([Numerics.Vector3]::new(0,0,-$camera.distance),$rotation)
|
||||||
|
$size=Adb @('shell','wm','size'); if ($size -notmatch '(\d+)x(\d+)') { throw "Unknown device resolution: $size" }
|
||||||
|
$width=[Math]::Max([int]$Matches[1],[int]$Matches[2]); $height=[Math]::Min([int]$Matches[1],[int]$Matches[2])
|
||||||
|
$scale=[Math]::Tan(31*[Math]::PI/180)/($height/2)
|
||||||
|
# Match the rounded native DOWN pixel, including its actual target entry distance.
|
||||||
|
$direction=[Numerics.Vector3]::Normalize([Numerics.Vector3]::Transform([Numerics.Vector3]::new(($point[0]-$width/2)*$scale,($height/2-$point[1])*$scale,1),$rotation))
|
||||||
$target=@($Board.balls|Where-Object id -eq $Id)[0].position
|
$target=@($Board.balls|Where-Object id -eq $Id)[0].position
|
||||||
$direction=[Numerics.Vector3]::new($target.x,$target.y,$target.z)-$origin
|
$delta=[Numerics.Vector3]::new($target.x,$target.y,$target.z)-$origin
|
||||||
$targetHit=$direction.Length()-.5
|
$along=[Numerics.Vector3]::Dot($direction,$delta)
|
||||||
$direction=[Numerics.Vector3]::Normalize($direction)
|
$perpendicularSquared=($delta-$direction*$along).LengthSquared()
|
||||||
|
if($perpendicularSquared-gt.25){return $false}
|
||||||
|
$targetHit=$along-[Math]::Sqrt([Math]::Max(0.0,.25-$perpendicularSquared))
|
||||||
|
if($targetHit-lt0){return $false}
|
||||||
foreach($ball in $Board.balls){
|
foreach($ball in $Board.balls){
|
||||||
if($ball.id-eq$Id){continue}
|
if($ball.id-eq$Id){continue}
|
||||||
$delta=[Numerics.Vector3]::new($ball.position.x,$ball.position.y,$ball.position.z)-$origin
|
$delta=[Numerics.Vector3]::new($ball.position.x,$ball.position.y,$ball.position.z)-$origin
|
||||||
$along=[Numerics.Vector3]::Dot($direction,$delta)
|
$along=[Numerics.Vector3]::Dot($direction,$delta)
|
||||||
$perpendicularSquared=$delta.LengthSquared()-$along*$along
|
$perpendicularSquared=($delta-$direction*$along).LengthSquared()
|
||||||
if($perpendicularSquared-le.25){
|
if($perpendicularSquared-le.25){
|
||||||
$near=$along-[Math]::Sqrt([Math]::Max(0,.25-$perpendicularSquared))
|
$near=$along-[Math]::Sqrt([Math]::Max(0.0,.25-$perpendicularSquared))
|
||||||
if($near-ge0-and$near-lt$targetHit){return $false}
|
if($near-ge0-and$near-le$targetHit){return $false}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $true
|
return $true
|
||||||
@@ -160,7 +169,7 @@ function StartHome([string]$Name) {
|
|||||||
$ready=$false; $pauseRequested=$false
|
$ready=$false; $pauseRequested=$false
|
||||||
Add-Type -AssemblyName System.Drawing
|
Add-Type -AssemblyName System.Drawing
|
||||||
for($attempt=0;$attempt-lt30;$attempt++) {
|
for($attempt=0;$attempt-lt30;$attempt++) {
|
||||||
$pidText=Adb @('shell','pidof',$package)
|
$pidText=Adb @('shell','pidof',$package) -AllowMissingPid
|
||||||
$saveText=Adb @('shell',('if test -s '+$remote+'; then cat '+$remote+'; fi'))
|
$saveText=Adb @('shell',('if test -s '+$remote+'; then cat '+$remote+'; fi'))
|
||||||
$validSave=$false
|
$validSave=$false
|
||||||
try { $state=$saveText|ConvertFrom-Json -ErrorAction Stop; $validSave=$state.schemaVersion-eq1 -and $null-ne$state.completedStageIds -and $null-ne$state.puzzleStates } catch { }
|
try { $state=$saveText|ConvertFrom-Json -ErrorAction Stop; $validSave=$state.schemaVersion-eq1 -and $null-ne$state.completedStageIds -and $null-ne$state.puzzleStates } catch { }
|
||||||
@@ -195,6 +204,7 @@ function StartHome([string]$Name) {
|
|||||||
$pauseRequested=$true
|
$pauseRequested=$true
|
||||||
}
|
}
|
||||||
if($pidText-match'^\d+'-and$validSave-and$ui){$ready=$true;$saveText|Set-Content (Join-Path $EvidenceDir "$Name-ready-save.json") -Encoding utf8;break}
|
if($pidText-match'^\d+'-and$validSave-and$ui){$ready=$true;$saveText|Set-Content (Join-Path $EvidenceDir "$Name-ready-save.json") -Encoding utf8;break}
|
||||||
|
if($pidText-notmatch'^\d+'){Adb @('shell','am','start','-W','-n',$activity)|Out-Null}
|
||||||
}
|
}
|
||||||
$observations.ToArray()|ConvertTo-Json -Depth 6|Set-Content (Join-Path $EvidenceDir "$Name-readiness.json") -Encoding utf8
|
$observations.ToArray()|ConvertTo-Json -Depth 6|Set-Content (Join-Path $EvidenceDir "$Name-readiness.json") -Encoding utf8
|
||||||
Check "$Name-home-ready" $ready 'Actual app PID, landscape home colors/button/title and app save JSON; bounded30 observations, black/Unity splash excluded, PNG retained for direct review'
|
Check "$Name-home-ready" $ready 'Actual app PID, landscape home colors/button/title and app save JSON; bounded30 observations, black/Unity splash excluded, PNG retained for direct review'
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
param([Parameter(Mandatory)][string]$EvidenceDir,[Parameter(Mandatory)][string]$Before,[Parameter(Mandatory)][string]$After,[Parameter(Mandatory)][string]$Baseline,[string]$Source=(Join-Path $PSScriptRoot 'final-common.ps1'))
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
New-Item -ItemType Directory -Path $EvidenceDir -Force|Out-Null
|
||||||
|
$checks=[Collections.Generic.List[object]]::new()
|
||||||
|
function Record($Id,$Passed,$Actual){$checks.Add(@{id=$Id;passed=[bool]$Passed;actual=$Actual})}
|
||||||
|
function Definition($Path,$Name){$ast=[Management.Automation.Language.Parser]::ParseFile($Path,[ref]$null,[ref]$null);return $ast.Find({param($n)$n-is[Management.Automation.Language.FunctionDefinitionAst]-and$n.Name-eq$Name},$true).Extent.Text}
|
||||||
|
foreach($name in @('BallPoint','BallClear','WorldSafe','Same','AssertNativeTranslation','AssertNativeMerge')){
|
||||||
|
$expected=(Definition $Source $name).Replace("function $name","function global:$name")
|
||||||
|
. ([scriptblock]::Create($expected))
|
||||||
|
Record "$name-production-body-identity" ((Get-Command $name).ScriptBlock.Ast.Extent.Text-ceq$expected) $Source
|
||||||
|
}
|
||||||
|
function HashText($Text){return [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData([Text.Encoding]::UTF8.GetBytes($Text)))}
|
||||||
|
foreach($name in @('NativePointer','NativeMoveAndHold','AssertNativeTranslation','AssertNativeMerge')){Record "$name-contract-unchanged" ((Definition $Source $name)-ceq(Definition $Baseline $name)) @{baseline=$Baseline;beforeSha256=(HashText (Definition $Baseline $name));afterSha256=(HashText (Definition $Source $name))}}
|
||||||
|
$script:resolution='Physical size: 1080x2400';$script:adbCalls=0
|
||||||
|
function Adb($Arguments){$script:adbCalls++;return $script:resolution}
|
||||||
|
function Check($Id,$Passed,$Actual){if(!$Passed){throw "$Id : $Actual"}}
|
||||||
|
function Ball($Id,$X,$Y,$Z){return [pscustomobject]@{id=$Id;position=[pscustomobject]@{x=$X;y=$Y;z=$Z}}}
|
||||||
|
function Board($Balls){return [pscustomobject]@{balls=@($Balls);groups=@([pscustomobject]@{id='same';ballIds=@($Balls.id)});camera=[pscustomobject]@{target=[pscustomobject]@{x=0;y=.6;z=0};rotation=[pscustomobject]@{x=0;y=0;z=0;w=1};distance=8}}}
|
||||||
|
try{
|
||||||
|
$beforeState=Get-Content -LiteralPath $Before -Raw|ConvertFrom-Json;$afterState=Get-Content -LiteralPath $After -Raw|ConvertFrom-Json
|
||||||
|
$b=@($beforeState.puzzleStates|Where-Object stageId -eq 16)[0];$a=@($afterState.puzzleStates|Where-Object stageId -eq 16)[0]
|
||||||
|
Record 'integer-max-overload-reproduced-and-double-preserves-radius' ([Math]::Max(0,.25-.09)-eq0-and[Math]::Abs([Math]::Max(0.0,.25-.09)-.16)-lt1e-12) @{integerMax=[Math]::Max(0,.25-.09);doubleMax=[Math]::Max(0.0,.25-.09)}
|
||||||
|
$point=BallPoint $b 's16-b02'
|
||||||
|
Record 'captured-wrong-source-pixel-rejected' (!(BallClear $b 's16-b02')-and$point[0]-eq733-and$point[1]-eq346) @{point=$point;requested='s16-b02';actualMoved='s16-b05'}
|
||||||
|
$errorText=$null;try{AssertNativeMerge 'captured' $b $a 's16-b02'}catch{$errorText=$_.Exception.Message}
|
||||||
|
Record 'captured-wrong-source-strict-merge-still-rejected' ($errorText-match'requested-source-actually-moved') $errorText
|
||||||
|
foreach($case in @(
|
||||||
|
@{id='single';balls=@((Ball 'target' 0 1.25 0));clear=$true},
|
||||||
|
@{id='front';balls=@((Ball 'target' 0 1.25 0),(Ball 'front' 0 1.25 -1));clear=$false},
|
||||||
|
@{id='behind';balls=@((Ball 'target' 0 1.25 0),(Ball 'behind' 0 1.25 1));clear=$true},
|
||||||
|
# Target projects to1200.45 and rounds1200. Center ray misses the left sphere,
|
||||||
|
# while the actual pixel ray intersects it. Geometry expectations are analytic.
|
||||||
|
@{id='rounded-pixel-front';balls=@((Ball 'target' .004 1.25 0),(Ball 'front' -.497 1.25 -1));clear=$false},
|
||||||
|
@{id='rounded-pixel-outside';balls=@((Ball 'target' .004 1.25 0),(Ball 'front' -.501 1.25 -1));clear=$true},
|
||||||
|
@{id='exact-tangent-front';balls=@((Ball 'target' 0 1.25 0),(Ball 'front' -.5 1.25 -1));clear=$false},
|
||||||
|
@{id='equal-entry-ambiguous';balls=@((Ball 'target' 0 1.25 0),(Ball 'same' 0 1.25 0));clear=$false},
|
||||||
|
@{id='behind-camera';balls=@((Ball 'target' 0 1.25 -9));clear=$false},
|
||||||
|
@{id='unsafe-ui';balls=@((Ball 'target' 20 1.25 0));clear=$false}
|
||||||
|
)){$board=Board $case.balls;$snapshot=$board|ConvertTo-Json -Depth 12;$actual=BallClear $board 'target';Record $case.id ($actual-eq$case.clear-and($board|ConvertTo-Json -Depth 12)-ceq$snapshot) @{expected=$case.clear;actual=$actual}}
|
||||||
|
foreach($kind in @('missing','duplicate')){
|
||||||
|
$board=Board @((Ball 'target' 0 1.25 0));if($kind-eq'duplicate'){$board.balls+=Ball 'target' 0 1.25 1}
|
||||||
|
$script:adbCalls=0;$rejected=$false;try{BallClear $board $(if($kind-eq'missing'){'absent'}else{'target'})|Out-Null}catch{$rejected=$_.Exception.Message-match'exactly one existing ball ID'}
|
||||||
|
Record "$kind-ID-rejected-before-projection-input" ($rejected-and$script:adbCalls-eq0) $kind
|
||||||
|
}
|
||||||
|
$script:resolution='unavailable';$rejected=$false;try{BallClear (Board @((Ball 'target' 0 1.25 0))) 'target'|Out-Null}catch{$rejected=$_.Exception.Message-match'Unknown device resolution'}
|
||||||
|
Record 'unknown-resolution-rejected' $rejected $script:resolution
|
||||||
|
}catch{Record 'production-evaluation' $false $_.Exception.Message}
|
||||||
|
$passed=$checks.Count-gt0-and@($checks|Where-Object passed -ne $true).Count-eq0
|
||||||
|
@{passed=$passed;scope='Offline actual function bodies and captured wrong-source save. Analytic edge cases are counterfactual; no native raycast or product pass claim.';source=$Source;sourceSha256=(Get-FileHash $Source).Hash;before=$Before;beforeSha256=(Get-FileHash $Before).Hash;after=$After;afterSha256=(Get-FileHash $After).Hash;checks=$checks.ToArray()}|ConvertTo-Json -Depth 12|Set-Content (Join-Path $EvidenceDir 'pixel-ray-result.json')
|
||||||
|
if(!$passed){exit 1};exit 0
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
param([Parameter(Mandatory)][string]$EvidenceDir,[Parameter(Mandatory)][string]$HomePng,[string]$Source=(Join-Path $PSScriptRoot 'final-common.ps1'))
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
New-Item -ItemType Directory -Path $EvidenceDir -Force|Out-Null
|
||||||
|
$EvidenceDir=(Resolve-Path $EvidenceDir).Path
|
||||||
|
$checks=[Collections.Generic.List[object]]::new();$commands=[Collections.Generic.List[object]]::new()
|
||||||
|
function Record($Id,$Passed,$Actual){$checks.Add(@{id=$Id;passed=[bool]$Passed;actual=$Actual})}
|
||||||
|
foreach($name in @('Adb','StartHome')){
|
||||||
|
$ast=[Management.Automation.Language.Parser]::ParseFile($Source,[ref]$null,[ref]$null)
|
||||||
|
$definition=$ast.Find({param($n)$n-is[Management.Automation.Language.FunctionDefinitionAst]-and$n.Name-eq$Name},$true)
|
||||||
|
$expected=$definition.Extent.Text.Replace("function $name","function global:$name")
|
||||||
|
. ([scriptblock]::Create($expected))
|
||||||
|
Record "$name-production-body-identity" ((Get-Command $name).ScriptBlock.Ast.Extent.Text-ceq$expected) $Source
|
||||||
|
}
|
||||||
|
$adb='OfflineNative';$Serial='offline';$package='com.magnetismheroes.game';$activity="$package/activity";$remote='/offline/progress.json'
|
||||||
|
function OfflineNative {
|
||||||
|
$text=$args-join' ';$global:LASTEXITCODE=0
|
||||||
|
if($script:mode-eq'exit-control'){$global:LASTEXITCODE=$script:mockExit;return $script:mockOutput}
|
||||||
|
if($text-match'pidof'){
|
||||||
|
$script:pidCalls++
|
||||||
|
if($script:mode-eq'other-error'){$global:LASTEXITCODE=2;return ''}
|
||||||
|
if($script:mode-eq'absent'-or($script:mode-eq'eventually-ready'-and$script:pidCalls-le2)){$global:LASTEXITCODE=1;return ''}
|
||||||
|
if($script:mode-eq'empty-zero') {return ''}
|
||||||
|
if($script:mode-eq'malformed-pid') {return 'unavailable'}
|
||||||
|
return '77'
|
||||||
|
}
|
||||||
|
if($text-match'am start'){$script:starts++;if($script:mode-eq'start-error'){$global:LASTEXITCODE=1;return ''};return 'Status: ok; delivered-to-top-instance'}
|
||||||
|
if($text-match'cat '){if($script:mode-eq'save-error'){$global:LASTEXITCODE=1;return ''};if($script:mode-eq'invalid-save'){return '{}'};return '{"schemaVersion":1,"completedStageIds":[],"puzzleStates":[]}'}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
function Step($Name,$Arguments=@(),$Delay=500){
|
||||||
|
$destination=Join-Path $EvidenceDir "$Name.png"
|
||||||
|
if($script:mode-eq'black-ui'){$image=[Drawing.Bitmap]::new(2400,1080);try{$image.Save($destination)}finally{$image.Dispose()}}else{Copy-Item -LiteralPath $HomePng -Destination $destination -Force}
|
||||||
|
}
|
||||||
|
function Start-Sleep {}
|
||||||
|
function Check($Id,$Passed,$Actual){if(!$Passed){throw "$Id : $Actual"}}
|
||||||
|
try{
|
||||||
|
$script:mode='exit-control';$script:mockOutput='';$script:mockExit=1
|
||||||
|
$rejected=$false;try{Adb @('shell','pidof',$package)|Out-Null}catch{$rejected=$_.Exception.Message-match'ADB exit 1'}
|
||||||
|
Record 'default-adb-exit1-still-rejected' ($rejected-and$commands[-1].exitCode-eq1) $commands[-1]
|
||||||
|
$errorText=$null;try{$empty=Adb @('shell','pidof',$package) -AllowMissingPid}catch{$errorText=$_.Exception.Message}
|
||||||
|
Record 'allowed-pid-absence-retains-raw-exit1' (!$errorText-and$empty-eq''-and$commands[-1].exitCode-eq1) $commands[-1]
|
||||||
|
$script:mockExit=2;$rejected=$false;try{Adb @('shell','pidof',$package) -AllowMissingPid|Out-Null}catch{$rejected=$_.Exception.Message-match'ADB exit 2'}
|
||||||
|
Record 'other-pid-error-still-rejected' ($rejected-and$commands[-1].exitCode-eq2) $commands[-1]
|
||||||
|
$script:mockExit=1
|
||||||
|
foreach($arguments in @(@('shell','wm','size'),@('shell','pidof','another.package'),@('shell','am','start','-W','-n',$activity))){
|
||||||
|
$rejected=$false;try{Adb $arguments -AllowMissingPid|Out-Null}catch{$rejected=$_.Exception.Message-match'ADB exit 1'}
|
||||||
|
Record 'missing-pid-opt-in-cannot-hide-other-command-exit1' ($rejected-and$commands[-1].exitCode-eq1) $commands[-1]
|
||||||
|
}
|
||||||
|
$script:mockOutput='pidof: error';$rejected=$false;try{Adb @('shell','pidof',$package) -AllowMissingPid|Out-Null}catch{$rejected=$_.Exception.Message-match'ADB exit 1'}
|
||||||
|
Record 'pidof-exit1-with-error-output-still-rejected' ($rejected-and$commands[-1].output-eq'pidof: error') $commands[-1]
|
||||||
|
foreach($mode in @('eventually-ready','absent','empty-zero','malformed-pid','black-ui','invalid-save','other-error','start-error','save-error')){
|
||||||
|
$script:mode=$mode;$script:pidCalls=0;$script:starts=0;$commands.Clear();$errorText=$null
|
||||||
|
try{StartHome "offline-$mode"}catch{$errorText=$_.Exception.Message}
|
||||||
|
$observationPath=Join-Path $EvidenceDir "offline-$mode-readiness.json"
|
||||||
|
$observations=if(Test-Path $observationPath){@(Get-Content $observationPath -Raw|ConvertFrom-Json)}else{@()}
|
||||||
|
if($mode-eq'eventually-ready'){
|
||||||
|
Record 'absent-pid-relaunches-and-requires-real-home-save-pid' (!$errorText-and$observations.Count-eq3-and$script:starts-eq3-and$observations[0].pid-eq''-and$observations[-1].pid-eq'77'-and$observations[-1].validAppSave-and$observations[-1].readyUi) @{observations=$observations;starts=$script:starts}
|
||||||
|
Record 'absent-pid-raw-exits-remain-in-command-evidence' (@($commands|Where-Object {$_.command-match'pidof'-and$_.exitCode-eq1}).Count-eq2) $commands.ToArray()
|
||||||
|
}elseif($mode-eq'other-error'){
|
||||||
|
Record 'startup-does-not-swallow-other-adb-error' ($errorText-match'ADB exit 2'-and$script:pidCalls-eq1) $errorText
|
||||||
|
}elseif($mode-eq'start-error'-or$mode-eq'save-error'){
|
||||||
|
Record 'startup-does-not-swallow-other-exit1' ($errorText-match'ADB exit 1') @{kind=$mode;error=$errorText}
|
||||||
|
}else{
|
||||||
|
Record 'unready-home-cannot-pass-thirty-bound' ($errorText-match'home-ready'-and$script:pidCalls-eq30-and$observations.Count-eq30) @{kind=$mode;error=$errorText;observations=$observations.Count;starts=$script:starts}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch{Record 'production-evaluation' $false $_.Exception.Message}
|
||||||
|
$passed=$checks.Count-gt0-and@($checks|Where-Object passed -ne $true).Count-eq0
|
||||||
|
@{passed=$passed;scope='Offline StartHome/Adb production bodies with leaf process/PNG mocks. Recorded Home PNG is real; process absence, invalid Save and black screen sequences are counterfactual, not native startup proof.';source=$Source;sourceSha256=(Get-FileHash $Source).Hash;homePng=$HomePng;homePngSha256=(Get-FileHash $HomePng).Hash;checks=$checks.ToArray()}|ConvertTo-Json -Depth 14|Set-Content (Join-Path $EvidenceDir 'startup-result.json')
|
||||||
|
if(!$passed){exit 1};exit 0
|
||||||
Reference in New Issue
Block a user