115 lines
11 KiB
PowerShell
115 lines
11 KiB
PowerShell
param([string]$EvidenceDir='artifacts/verification/FirstSlice', [string]$Serial='emulator-5554')
|
|
$ErrorActionPreference='Stop'
|
|
$EvidenceDir=(Resolve-Path -LiteralPath $EvidenceDir).Path
|
|
$apkPath=(Resolve-Path -LiteralPath 'artifacts/android/FirstSlice/MagnetismHeroes.apk').Path
|
|
$checks=[Collections.Generic.List[object]]::new()
|
|
$recordings=[Collections.Generic.List[object]]::new()
|
|
function Step([string]$Name,[string[]]$Arguments,[int]$Delay=800) {
|
|
& (Join-Path $PSScriptRoot 'observe.ps1') -Step $Name -AdbArguments $Arguments -Serial $Serial -EvidenceDir $EvidenceDir -DelayMilliseconds $Delay
|
|
}
|
|
function Save-State([string]$Name) {
|
|
Step $Name @('pull','/sdcard/Android/data/com.magnetismheroes.game/files/progress.json',(Join-Path $EvidenceDir "$Name.json"))
|
|
return (Get-Content -LiteralPath (Join-Path $EvidenceDir "$Name.json") -Raw | ConvertFrom-Json)
|
|
}
|
|
function Assert-State([string]$Id,[bool]$Passed,[string]$Actual) {
|
|
$checks.Add([ordered]@{ id=$Id; passed=$Passed; actual=$Actual })
|
|
if (!$Passed) { throw "저장 검증 실패: $Id ($Actual)" }
|
|
}
|
|
function Start-Audio([string]$Name,[int]$Seconds) {
|
|
$audioPath=Join-Path $EvidenceDir "$Name.wav"
|
|
$args=@('-NoProfile','-File',(Join-Path $PSScriptRoot 'record-audio.ps1'),'-OutputPath',$audioPath,'-Seconds',"$Seconds")
|
|
$process=Start-Process -FilePath 'pwsh' -ArgumentList ($args | ForEach-Object { '"'+$_+'"' }) -WindowStyle Hidden -RedirectStandardOutput (Join-Path $EvidenceDir "$Name.stdout") -RedirectStandardError (Join-Path $EvidenceDir "$Name.stderr") -PassThru
|
|
$recordings.Add(@{ name=$Name; process=$process; command=('pwsh '+($args -join ' ')) })
|
|
Start-Sleep -Seconds 2
|
|
}
|
|
function Restart([string]$Prefix) {
|
|
Step "$Prefix-pause" @('shell','input','keyevent','3')
|
|
Step "$Prefix-force-stop" @('shell','am','force-stop','com.magnetismheroes.game')
|
|
Step "$Prefix-home" @('shell','am','start','-W','-n','com.magnetismheroes.game/com.unity3d.player.UnityPlayerGameActivity') 10000
|
|
}
|
|
$result=[ordered]@{ sourceCommit=(& git rev-parse HEAD); sourceDirty=[bool](& git status --porcelain); apkSha256=(Get-FileHash -LiteralPath $apkPath -Algorithm SHA256).Hash; serial=$Serial; status='failed'; recordings=@(); checks=@() }
|
|
try {
|
|
Assert-State 'clean-source' (!$result.sourceDirty) $result.sourceCommit
|
|
Step 'final-00-airplane-off' @('shell','cmd','connectivity','airplane-mode','disable')
|
|
Step 'final-00-wifi-on' @('shell','svc','wifi','enable')
|
|
Step 'final-00-data-on' @('shell','svc','data','enable')
|
|
# 검증 담당자가 이 실행에서 설치한 앱 전용 데이터만 초기화합니다.
|
|
Step 'final-00-reset-own-test-app' @('shell','pm','clear','com.magnetismheroes.game')
|
|
Step 'final-01-home' @('shell','am','start','-W','-n','com.magnetismheroes.game/com.unity3d.player.UnityPlayerGameActivity') 10000
|
|
Step 'final-02-selection' @('shell','input','tap','1200','616')
|
|
Start-Audio 'audio-online-guidecollect' 8
|
|
Step 'final-03-stage' @('shell','input','tap','1200','560') 2500
|
|
Step 'final-04-precollect-tap' @('shell','input','swipe','930','510','930','510','200')
|
|
$uncollected=Save-State 'final-04-uncollected'
|
|
Assert-State 'direct-collection-required' ($uncollected.puzzleStates[0].collectedBallIds.Count -eq 0 -and $uncollected.puzzleStates[0].countedBallIds.Count -eq 0 -and $uncollected.completedStageIds.Count -eq 0) ($uncollected | ConvertTo-Json -Depth 8 -Compress)
|
|
Start-Audio 'audio-online-guidecount' 8
|
|
Step 'final-05-direct-collection' @('shell','input','swipe','930','510','1200','510','800') 4000
|
|
Step 'final-06-camera-orbit' @('shell','input','swipe','1720','400','1860','470','600')
|
|
$before=Save-State 'final-06-incomplete-before'
|
|
Assert-State 'collected-with-empty-count' ($before.puzzleStates[0].collectedBallIds.Count -eq 1 -and $before.puzzleStates[0].countedBallIds.Count -eq 0) ($before | ConvertTo-Json -Depth 8 -Compress)
|
|
Restart 'final-07'
|
|
Step 'final-07-selection' @('shell','input','tap','1200','616')
|
|
Step 'final-07-restored-incomplete' @('shell','input','tap','1200','560')
|
|
$after=Save-State 'final-07-incomplete-after'
|
|
Assert-State 'incomplete-json-restored' (($before | ConvertTo-Json -Depth 8 -Compress) -eq ($after | ConvertTo-Json -Depth 8 -Compress)) '배치/카메라/그룹/수집/빈 계수 전체 JSON 동일'
|
|
Start-Audio 'audio-online-count01' 8
|
|
Step 'final-08-success-same-board' @('shell','input','swipe','1182','514','1182','514','200') 1500
|
|
$success=Save-State 'final-08-success'
|
|
Assert-State 'completion-atomic-state' ($success.completedStageIds.Count -eq 1 -and $success.completedStageIds[0] -eq 1 -and $success.puzzleStates.Count -eq 0 -and $success.latestFreePlay.sourceStageId -eq 1) ($success | ConvertTo-Json -Depth 8 -Compress)
|
|
Assert-State 'success-preserves-board-camera' (($before.puzzleStates[0].balls | ConvertTo-Json -Depth 6 -Compress) -eq ($success.latestFreePlay.balls | ConvertTo-Json -Depth 6 -Compress) -and ($before.puzzleStates[0].camera | ConvertTo-Json -Depth 6 -Compress) -eq ($success.latestFreePlay.camera | ConvertTo-Json -Depth 6 -Compress)) '성공 전후 balls/camera 동일'
|
|
Step 'final-09-free-move' @('shell','input','swipe','1182','514','1380','470','800')
|
|
$freeBefore=Save-State 'final-09-free-before'
|
|
Assert-State 'free-move-changes-position' (($freeBefore.latestFreePlay.balls | ConvertTo-Json -Depth 6 -Compress) -ne ($success.latestFreePlay.balls | ConvertTo-Json -Depth 6 -Compress)) '실제 자유 놀이 balls 위치 변경'
|
|
Restart 'final-10'
|
|
Start-Audio 'audio-free-restore' 8
|
|
Step 'final-10-continue-free' @('shell','input','tap','1200','810')
|
|
$freeAfter=Save-State 'final-10-free-after'
|
|
Assert-State 'free-json-restored' (($freeBefore | ConvertTo-Json -Depth 8 -Compress) -eq ($freeAfter | ConvertTo-Json -Depth 8 -Compress)) '자유 놀이 전체 JSON 동일'
|
|
$restoreRecorder=$recordings | Where-Object name -eq 'audio-free-restore' | Select-Object -First 1
|
|
if (!$restoreRecorder.process.WaitForExit(30000)) { throw '복원 음성 검사 수집 미종료' }
|
|
$silence=Get-Content -LiteralPath (Join-Path $EvidenceDir 'audio-free-restore.wav.json') -Raw | ConvertFrom-Json
|
|
Assert-State 'restore-no-historical-voice' ($silence.packets -gt 0 -and $silence.pcmBytes -gt 0 -and $silence.nonSilentSamples -eq 0) ($silence | ConvertTo-Json -Compress)
|
|
Step 'final-11-airplane-on' @('shell','cmd','connectivity','airplane-mode','enable')
|
|
Step 'final-11-wifi-off' @('shell','svc','wifi','disable')
|
|
Step 'final-11-data-off' @('shell','svc','data','disable')
|
|
Step 'final-11-wifi-state' @('shell','settings','get','global','wifi_on')
|
|
Step 'final-11-data-state' @('shell','settings','get','global','mobile_data')
|
|
Step 'final-11-connectivity' @('shell','dumpsys','connectivity')
|
|
Step 'final-11-airplane-state' @('shell','settings','get','global','airplane_mode_on')
|
|
$wifiState=Get-Content -LiteralPath (Join-Path $EvidenceDir 'final-11-wifi-state-commands.json') -Raw | ConvertFrom-Json
|
|
$dataState=Get-Content -LiteralPath (Join-Path $EvidenceDir 'final-11-data-state-commands.json') -Raw | ConvertFrom-Json
|
|
$connectivity=Get-Content -LiteralPath (Join-Path $EvidenceDir 'final-11-connectivity-commands.json') -Raw | ConvertFrom-Json
|
|
$airplaneState=Get-Content -LiteralPath (Join-Path $EvidenceDir 'final-11-airplane-state-commands.json') -Raw | ConvertFrom-Json
|
|
Assert-State 'offline-network-disabled' ($wifiState.commands[0].output.Trim() -eq '0' -and $dataState.commands[0].output.Trim() -eq '0' -and $airplaneState.commands[0].output.Trim() -eq '1' -and $connectivity.commands[0].output -match 'Active default network: none') 'airplane=1, wifi=0, mobile_data=0, Active default network: none'
|
|
Step 'final-12-home' @('shell','input','tap','310','108')
|
|
Step 'final-12-selection' @('shell','input','tap','1200','616')
|
|
Start-Audio 'audio-offline' 20
|
|
Step 'final-12-offline-stage' @('shell','input','tap','1200','560') 3000
|
|
Step 'final-12-offline-collect' @('shell','input','swipe','930','510','1200','510','800') 4500
|
|
$offlineCollected=Save-State 'final-12-offline-collected'
|
|
Assert-State 'offline-collected-unfinished' ($offlineCollected.puzzleStates.Count -eq 1 -and $offlineCollected.puzzleStates[0].collectedBallIds.Count -eq 1 -and $offlineCollected.puzzleStates[0].countedBallIds.Count -eq 0) '오프라인 실제 수집됨/계수 전 0'
|
|
Step 'final-12-offline-count-success' @('shell','input','swipe','1182','510','1182','510','200') 1500
|
|
$offlineSuccess=Save-State 'final-12-offline-success'
|
|
Assert-State 'offline-collection-count-actually-success' ($offlineSuccess.completedStageIds.Count -eq 1 -and $offlineSuccess.completedStageIds[0] -eq 1 -and $offlineSuccess.puzzleStates.Count -eq 0 -and $offlineSuccess.latestFreePlay.sourceStageId -eq 1 -and ($offlineCollected.puzzleStates[0].balls | ConvertTo-Json -Depth 6 -Compress) -eq ($offlineSuccess.latestFreePlay.balls | ConvertTo-Json -Depth 6 -Compress)) '완료1/미완료없음/수집 전후 같은 IDs와 배치 자유 전이'
|
|
Step 'final-13-offline-free-move' @('shell','input','swipe','1182','510','1350','450','800')
|
|
$offlineBefore=Save-State 'final-13-offline-before'
|
|
Assert-State 'offline-free-move-changes-position' (($offlineBefore.latestFreePlay.balls | ConvertTo-Json -Depth 6 -Compress) -ne ($offlineSuccess.latestFreePlay.balls | ConvertTo-Json -Depth 6 -Compress)) '오프라인 실제 자유 놀이 balls 위치 변경'
|
|
Restart 'final-14'
|
|
Step 'final-14-offline-continue' @('shell','input','tap','1200','810')
|
|
$offlineAfter=Save-State 'final-14-offline-after'
|
|
Assert-State 'offline-json-restored' (($offlineBefore | ConvertTo-Json -Depth 8 -Compress) -eq ($offlineAfter | ConvertTo-Json -Depth 8 -Compress)) '오프라인 자유 놀이 전체 JSON 동일'
|
|
foreach ($recording in $recordings) {
|
|
if (!$recording.process.WaitForExit(30000)) { throw "오디오 수집 미종료: $($recording.name)" }
|
|
$recording.process.Refresh()
|
|
$result.recordings += [ordered]@{ name=$recording.name; command=$recording.command; exitCode=$recording.process.ExitCode }
|
|
Assert-State "audio-recorder-$($recording.name)" ($recording.process.ExitCode -eq 0) "$($recording.process.ExitCode)"
|
|
}
|
|
$result.status='passed'
|
|
} catch { $result.error=$_.Exception.Message; Write-Host $result.error -ForegroundColor Red }
|
|
finally {
|
|
$result.checks=$checks.ToArray(); $result.finishedUtc=[DateTime]::UtcNow.ToString('o')
|
|
$result | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath (Join-Path $EvidenceDir 'android-state-checks.json') -Encoding utf8
|
|
}
|
|
if ($result.status -ne 'passed') { exit 1 }
|
|
Write-Host "실제 Android 저장/복원 검사 $($checks.Count)개 통과. PNG 의미 검수는 별도입니다."
|