Files
MagnetismHeroes/tools/android/capture-playground-observation.ps1
T

45 lines
4.9 KiB
PowerShell

param([Parameter(Mandatory)][string]$ApkPath,[Parameter(Mandatory)][string]$EvidenceDir,[Parameter(Mandatory)][string]$Name,[ValidateSet('Phone','Tablet')][string]$Viewport='Phone',[int]$ObserveSeconds=0,[string]$Serial='emulator-5554',[string]$ApkSourceCommit)
$ErrorActionPreference='Stop'
if($Serial-ne'emulator-5554'){throw 'Only the authorized emulator-5554 may be observed'}
if($Name-notmatch'^[a-zA-Z0-9_-]+$'){throw 'Observation name must be a simple artifact label'}
New-Item -ItemType Directory -Path $EvidenceDir -Force|Out-Null
$adb=Join-Path $env:LOCALAPPDATA 'Android/Sdk/platform-tools/adb.exe';$package='com.magnetismheroes.game'
$workspaceCommit=(& git rev-parse HEAD);if(!$ApkSourceCommit){$ApkSourceCommit=$workspaceCommit}
$commands=[Collections.Generic.List[object]]::new();$result=[ordered]@{status='failed';scope='Actual AVD capture/resource observation only; direct visual judgement and game behavior are separate. APK source attribution must be linked to build or baseline-product-equivalence evidence by the observation owner.';name=$Name;serial=$Serial;viewport=$Viewport;sourceCommit=$ApkSourceCommit;workspaceCommit=$workspaceCommit;sourceDirty=[bool](& git status --porcelain);apkSha256=(Get-FileHash -LiteralPath $ApkPath).Hash;startedUtc=[DateTime]::UtcNow.ToString('o')}
function Adb([string[]]$Arguments){$output=& $adb -s $Serial @Arguments 2>&1;$code=$LASTEXITCODE;$commands.Add(@{command='adb -s '+$Serial+' '+($Arguments-join' ');exitCode=$code;utc=[DateTime]::UtcNow.ToString('o');output=$output-join"`n"});if($code-ne0){throw "ADB observation exit $code"};return ($output-join"`n").Trim()}
$sizeBefore=$null;$statsEnabled=$false
try{
if($result.sourceDirty){throw 'Observation source is dirty'}
if((Adb @('emu','avd','name'))-notmatch'^Medium_Phone_API_37\.0'){throw 'Unexpected AVD'}
$remoteApk=(Adb @('shell','pm','path',$package)).Replace('package:','').Split("`n")[0].Trim()
$installed=Join-Path $EvidenceDir "$Name-installed-base.apk";Adb @('pull',$remoteApk,$installed)|Out-Null
if((Get-FileHash -LiteralPath $installed).Hash-ne$result.apkSha256){throw 'Installed APK differs from supplied observation APK'}
$sizeBefore=Adb @('shell','wm','size');$result.sizeBefore=$sizeBefore
if($Viewport-eq'Tablet'){Adb @('shell','wm','size','1000x1600')|Out-Null}else{Adb @('shell','wm','size','reset')|Out-Null}
Start-Sleep -Milliseconds 1500
$png=Join-Path $EvidenceDir "$Name.png";Adb @('shell','screencap','-p',"/sdcard/$Name.png")|Out-Null;Adb @('pull',"/sdcard/$Name.png",$png)|Out-Null
Add-Type -AssemblyName System.Drawing;$image=[Drawing.Bitmap]::new($png)
try{$result.width=$image.Width;$result.height=$image.Height}finally{$image.Dispose()}
$expected=if($Viewport-eq'Tablet'){@(1600,1000)}else{@(2400,1080)}
if($result.width-ne$expected[0]-or$result.height-ne$expected[1]){throw 'Actual PNG dimensions differ from requested landscape viewport'}
$result.pngSha256=(Get-FileHash -LiteralPath $png).Hash
$save=Join-Path $EvidenceDir "$Name-progress.json";Adb @('pull',"/sdcard/Android/data/$package/files/progress.json",$save)|Out-Null;$result.progressSha256=(Get-FileHash -LiteralPath $save).Hash
if($ObserveSeconds-gt0){
if($ObserveSeconds-lt5-or$ObserveSeconds-gt60){throw 'Resource sample must be between 5 and 60 seconds'}
Adb @('shell','dumpsys','SurfaceFlinger','--timestats','-clear','-enable')|Set-Content -LiteralPath (Join-Path $EvidenceDir "$Name-timestats-start.txt");$statsEnabled=$true
Start-Sleep -Seconds $ObserveSeconds
Adb @('shell','dumpsys','SurfaceFlinger','--timestats','-dump')|Set-Content -LiteralPath (Join-Path $EvidenceDir "$Name-timestats.txt")
Adb @('shell','dumpsys','meminfo',$package)|Set-Content -LiteralPath (Join-Path $EvidenceDir "$Name-meminfo.txt")
$result.observeSeconds=$ObserveSeconds;$result.resourceMethod='Actual same-AVD SurfaceView presentToPresent histograms and total process PSS raw dumps; root must compare matching camera/viewport/board/load order/warmup/sample duration. AVD observation is not real-device FPS.'
}
$result.status='passed'
}catch{$result.error=$_.Exception.Message}
finally{
try{
if($statsEnabled){Adb @('shell','dumpsys','SurfaceFlinger','--timestats','-disable')|Out-Null}
if($sizeBefore){if($sizeBefore-match'Override size: (\d+x\d+)'){Adb @('shell','wm','size',$Matches[1])|Out-Null}else{Adb @('shell','wm','size','reset')|Out-Null};$result.sizeAfter=Adb @('shell','wm','size');if($result.sizeAfter-ne$sizeBefore){throw 'Original display size was not restored'}}
}catch{$result.status='failed';$result.restoreError=$_.Exception.Message}
$result.commands=$commands.ToArray();$result.finishedUtc=[DateTime]::UtcNow.ToString('o');$result|ConvertTo-Json -Depth 8|Set-Content -LiteralPath (Join-Path $EvidenceDir "$Name-observation.json") -Encoding utf8
}
if($result.status-ne'passed'){Write-Host $result.error;exit 1};exit 0