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