From 114e8431a63b9d3e932d2da58ad575192fe10253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E5=8D=9A=E6=96=87?= <1323515022@qq.com> Date: Sat, 31 Oct 2026 15:57:57 +0800 Subject: [PATCH] =?UTF-8?q?test(android-3a):=20=E8=BF=9E=E6=8E=A5=E7=94=9F?= =?UTF-8?q?=E5=91=BD=E5=91=A8=E6=9C=9F=20proof=20=E5=85=A5=E4=BB=93=20+=20?= =?UTF-8?q?=E5=9B=9B=E4=B8=AA=20proof=20=E7=9A=84=E4=B8=BB=E6=9C=BA?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=80=BC=E8=B7=9F=E4=B8=8A=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 tools\connect_lifecycle_proof.ps1(V-A1a/A1b/A2/A3/A4/A5 + 一键重连, 含 keep-awake、tap 前唤醒、期望文案码点从 Kotlin 源提取)。 四个既有 proof 的 -PhoneIp 默认值 .180 -> .110(2026-10-31 办公网实测; stale 默认值曾静默吃掉一次 proof 运行,注释写明 DHCP 需按需传参)。 capture_evidence 的 192.168.1.199 保持不变——那是 V5 故意不可达的路径。 --- tools/capture_evidence.ps1 | 6 +- tools/connect_lifecycle_proof.ps1 | 432 ++++++++++++++++++++++++++++++ tools/emoji_manual_proof.ps1 | 4 +- tools/ime_chinese_proof.ps1 | 5 +- tools/ime_commit_proof.ps1 | 4 +- 5 files changed, 447 insertions(+), 4 deletions(-) create mode 100644 tools/connect_lifecycle_proof.ps1 diff --git a/tools/capture_evidence.ps1 b/tools/capture_evidence.ps1 index 97bebd5..7da5c39 100644 --- a/tools/capture_evidence.ps1 +++ b/tools/capture_evidence.ps1 @@ -7,7 +7,11 @@ $ErrorActionPreference = "Stop" $repo = (Resolve-Path "$PSScriptRoot\..").Path $ev = "D:\working\projects\yuankong\evidence\2b" $adb = "D:\working\android\sdk\platform-tools\adb.exe" -$phoneIp = "192.168.1.180" +# PC (controlled side) LAN address; DHCP-assigned. 2026-10-31: the stale +# .180 default silently cost proof runs after the office network moved to +# .110 - pass -PhoneIp (param) or update this line when it changes again. +# The 192.168.1.199 in step 2 is INTENTIONALLY unreachable (V5 error path). +$phoneIp = "192.168.1.110" function Adb-Shell { param([Parameter(Mandatory)][string[]]$Cmd) diff --git a/tools/connect_lifecycle_proof.ps1 b/tools/connect_lifecycle_proof.ps1 new file mode 100644 index 0000000..a216def --- /dev/null +++ b/tools/connect_lifecycle_proof.ps1 @@ -0,0 +1,432 @@ +# 3a connect-lifecycle proof (phone side): U8 timeout/cancel, error classes, BACK-cancel, HUD. +# +# ASCII-only source (PS 5.1 decodes BOM-less .ps1 as GBK; see personal lessons). +# Expected Chinese UI strings are matched as CODE-POINT fragments extracted from +# the Kotlin sources at authoring time (R-8: assert the actual value, and print +# what the parser really read so an empty result cannot pass silently). +# +# Judgements (V-A1 rewritten per loop-3-plan D3 redial ruling, user 2026-10-31): +# V-A2a wrong code -> AUTH class text + original-error detail line +# V-A1a unresolvable host -> RESOLVE text fast (<3s; Resolve is NOT retryable) +# V-A1b blackhole IP -> connecting shows CANCEL; tap -> actionable form <1.5s +# V-A4 BACK while connecting -> cancel to form, activity NOT finished +# V-A5 live session -> HUD has fps= and no coordinate noise (norm=/view=) +# +# The IME is disabled for the whole run (form fields then receive raw key events, +# no soft keyboard covering the button; same pattern as phone_chain_proof step 6) +# and restored in `finally`. +# +# Prereqs: vivo device online via adb, 3a APK installed, v3 agent build present. +# powershell -ExecutionPolicy Bypass -File tools\connect_lifecycle_proof.ps1 -Host_ 192.168.1.110 +param( + [string]$Host_ = "192.168.1.110", + [int]$Port = 47800, + [string]$Code = "t", + [string]$AgentExe = "" +) +$ErrorActionPreference = "Stop" +$repo = (Resolve-Path "$PSScriptRoot\..").Path +$adb = "D:\working\android\sdk\platform-tools\adb.exe" +$ev = Join-Path (Split-Path $repo -Parent) "evidence\3a" +New-Item -ItemType Directory -Force -Path $ev | Out-Null + +# ---- code-point fragments extracted from Kotlin sources THIS authoring session +# (grep-verified present in ConnectErrorText.kt / MainActivity.kt): ---- +# NB: build with `-join (...)`, NOT `[string](a,b)` - the latter stringifies the +# array with spaces ("连 接", len 3) and silently never matches (measured +# 2026-10-31; visibleTexts comparison then finds 'no button' for a button that +# is plainly on screen). +$F_RESOLVE = -join ([char]0x65E0,[char]0x6CD5,[char]0x89E3,[char]0x6790,[char]0x8FD9,[char]0x4E2A,[char]0x5730,[char]0x5740) # "cannot resolve this address" +$F_AUTH = -join ([char]0x9A8C,[char]0x8BC1,[char]0x7801,[char]0x9519,[char]0x8BEF) # "verification code wrong" +$F_CANCELED = -join ([char]0x8FDE,[char]0x63A5,[char]0x5DF2,[char]0x53D6,[char]0x6D88) # "connection cancelled" +$F_RECONNECT = -join ([char]0x91CD,[char]0x65B0,[char]0x8FDE,[char]0x63A5) # "reconnect" +$F_CANCEL = -join ([char]0x53D6,[char]0x6D88) # "cancel" +$F_CONNECT = -join ([char]0x8FDE,[char]0x63A5) # "connect" +$F_KILLMSG = -join ([char]0x8FDE,[char]0x63A5,[char]0x5DF2,[char]0x65AD,[char]0x5F00) # "connection lost" + +function Adb { + param([Parameter(Mandatory)][string[]]$AdbArgs) + $old = $ErrorActionPreference; $ErrorActionPreference = "Continue" + try { $out = & $adb @AdbArgs 2>$null; @{ out = ($out -join "`n"); code = $LASTEXITCODE } } + finally { $ErrorActionPreference = $old } +} +# NB: the concat must be parenthesized. `Adb @("shell") + $Cmd` parses `+` as a +# literal extra argument (measured 2026-10-31: every shell call silently ran +# `adb shell + ...` on the device, returning empty output - the form 'vanished' +# because uiautomator never actually ran). +function AdbShell { + param([string[]]$Cmd) + (Adb -AdbArgs (@("shell") + $Cmd)).out +} + +function Get-UiText { + $d = AdbShell @("uiautomator","dump","/sdcard/cl.xml") + $r = Adb @("pull","/sdcard/cl.xml","$env:TEMP\cl.xml") + if ($r.code -ne 0 -or -not (Test-Path "$env:TEMP\cl.xml")) { + Write-Host " [ui] dump=[$($d -replace "`n",' ')] pull_rc=$($r.code) -> empty" + return "" + } + $txt = [System.IO.File]::ReadAllText("$env:TEMP\cl.xml", [System.Text.Encoding]::UTF8) + if ($txt.Length -lt 200) { + # dump can 'succeed' yet hand back a stale stub; show what we really read + Write-Host " [ui] short xml ($($txt.Length) chars): $($txt.Substring(0,[Math]::Min(120,$txt.Length)))" + } + return $txt +} +function VisibleTexts([string]$xml) { + [regex]::Matches($xml,'text="([^"]+)"') | ForEach-Object { [System.Net.WebUtility]::HtmlDecode($_.Groups[1].Value) } +} +function FormFields([string]$xml) { + [regex]::Matches($xml,']*class="android\.widget\.EditText"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"') +} +function Center($m) { + # NB: cast to [int] BEFORE adding. A regex group .Value is a STRING and PS + # "60" + "1020" CONCATENATES ("601020") -> /2 then lands the tap ~300000px + # off-screen, silently. Measured 2026-10-31; phone_chain_proof's Center is + # the proven form - copy it, don't re-derive. + $a=[int]$m.Groups[1].Value; $b=[int]$m.Groups[2].Value + $c=[int]$m.Groups[3].Value; $d=[int]$m.Groups[4].Value + @([int](($a+$c)/2), [int](($b+$d)/2)) +} +function ButtonCenter([string]$xml, [string]$want) { + foreach ($n in [regex]::Matches($xml,']*class="android\.widget\.Button"[^>]*>')) { + $t = [System.Net.WebUtility]::HtmlDecode([regex]::Match($n.Value,'text="([^"]*)"').Groups[1].Value) + if ($t -eq $want) { + $b = [regex]::Match($n.Value,'bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"') + if ($b.Success) { + $x1=[int]$b.Groups[1].Value; $y1=[int]$b.Groups[2].Value + $x2=[int]$b.Groups[3].Value; $y2=[int]$b.Groups[4].Value + return @([int](($x1+$x2)/2), [int](($y1+$y2)/2)) + } + } + } + return $null +} + +# Physical-key input with the IME disabled. Key codes from javap against +# android-34 (same table phone_chain_proof uses): KEYCODE_0=7..9=16, +# KEYCODE_A=29..Z=54 contiguous (T=48 verified there), PERIOD=56. +# NB1: [int]'A' converts the STRING and throws; letter anchor is [int][char]'A'. +# NB2: [int][string]'0' == 0 (parse), so digits map 7+digit (an earlier draft +# got this wrong twice: `-48`, then MINUS=14 - which is KEYCODE_7!). +# MINUS (69) is deliberately NOT supported: throw instead of typing a wrong +# key (measured 2026-10-31: a wrong MINUS mapping silently typed '7' into +# the code field and corrupted the whole fill sequence). +function TypeAscii([string]$s) { + foreach ($ch in $s.ToUpper().ToCharArray()) { + $k = if ($ch -ge '0' -and $ch -le '9') { 7 + [int][string]$ch } + elseif ($ch -ge 'A' -and $ch -le 'Z') { 29 + ([int]$ch - [int][char]'A') } + elseif ($ch -eq '.') { 56 } + else { throw "unsupported char: $ch" } + [void](AdbShell @("input","keyevent","$k")) + # 35ms lost a digit on the live device (port arrived as "4780"); + # 70ms gap + per-field verify-and-retry below is the honest fix. + Start-Sleep -Milliseconds 70 + } +} + +# which field (0..2) currently holds focus? "" if none. +function FocusedIndex([string]$xml) { + $i = -1 + foreach ($n in [regex]::Matches($xml,']*>')) { + if ($n.Value -match 'class="android\.widget\.EditText"') { + $i++ + if ($n.Value -match 'focused="true"') { return $i } + } + } + return -1 +} + +# Tap a field's center and VERIFY focus landed there (2b N-series discipline: +# automation must assert its own preconditions - adb taps are not guaranteed). +function Focus-Field($c, [int]$wantIdx) { + for ($try=0; $try -lt 3; $try++) { + [void](AdbShell @("input","tap","$($c[0])","$($c[1])")) + $deadline = (Get-Date).AddSeconds(1.5) + while ((Get-Date) -lt $deadline) { + Start-Sleep -Milliseconds 250 + if ((FocusedIndex (Get-UiText)) -eq $wantIdx) { return } + } + } + throw "tap did not focus field $wantIdx after 3 tries (focused=$(FocusedIndex (Get-UiText)))" +} + +function Clear-Field($c, [int]$idx) { + Focus-Field $c $idx + for ($i=0; $i -lt 40; $i++) { [void](AdbShell @("input","keyevent","67")) } + Start-Sleep -Milliseconds 150 +} + +function Restart-AppForm { + # vivo keeps re-sleeping mid-run (measured 2026-10-31: battery-saver + a 7s + # screen-off timeout re-locked the keyguard between steps, so uiautomator saw + # the always-on-display clock instead of the app and 'form did not appear'). + # Wake + dismiss is idempotent and keeps the harness unattended-safe; if the + # device is PIN-locked a bare wake is not enough and this will fail loudly. + [void](AdbShell @("input","keyevent","224")) # WAKEUP + Start-Sleep -Milliseconds 300 + [void](AdbShell @("wm","dismiss-keyguard")) + Start-Sleep -Milliseconds 500 + [void](AdbShell @("am","force-stop","com.verify.remote")) + Start-Sleep -Milliseconds 600 + [void](AdbShell @("am","start","-n","com.verify.remote/.MainActivity")) # no extras -> form + $deadline = (Get-Date).AddSeconds(8) + while ((Get-Date) -lt $deadline) { + $x = Get-UiText + if ((FormFields $x).Count -ge 3) { return $x } + Start-Sleep -Milliseconds 300 + } + $diag = (VisibleTexts (Get-UiText)) -join " || " + throw "connect form did not appear (expected >=3 EditText). screen shows: [$diag]" +} + +# Fill host/port/code into the 3 form fields, asserting every precondition +# (R-8 + 2026-10-31 lesson: an adb tap does NOT guarantee focus - one failed run +# left all three type-ins in field 0 and the single 'vals[0] -ne host' check +# caught it only as a corruption symptom). Now: focus is verified per field, and +# every field's final text must equal its intended value. Returns tap time. +function FillAndConnect([string]$xml, [string]$host_, [string]$port_, [string]$code_) { + $f = FormFields $xml + if ($f.Count -lt 3) { throw "form has $($f.Count) fields" } + # NB: do NOT gather the three centers into @(a,b,c) - PowerShell array + # literals FLATTEN nested arrays, silently turning 3 coord-pairs into 6 ints; + # $cs[0] then holds only the x of field 0, every tap lands at a bogus point, + # focus never moves (measured 2026-10-31: 'focused=-1' was the honest symptom). + # Pass each call's array result directly as ONE argument. + Clear-Field (Center $f[0]) 0; TypeAscii $host_ + Clear-Field (Center $f[1]) 1; TypeAscii $port_ + Clear-Field (Center $f[2]) 2; TypeAscii $code_ + # Verify ALL THREE fields hold their intended value; on mismatch, retype the + # offending field and re-check (max 2 retries) - a dropped key event on the + # live device is a known flake (measured: port typed as "4780"), and the + # final judgement must still be 'equals expected', never 'close enough'. + $expect = @($host_, $port_, $code_) + for ($retry=0; $retry -le 2; $retry++) { + $x2 = Get-UiText + $vals = @() + foreach ($n in [regex]::Matches($x2,']*>')) { + if ($n.Value -match 'class="android\.widget\.EditText"') { + $vals += [System.Net.WebUtility]::HtmlDecode([regex]::Match($n.Value,'text="([^"]*)"').Groups[1].Value) + } + } + Write-Host " form now: $($vals -join ' | ') (try $retry)" + if ($vals.Count -lt 3) { throw "expected 3 fields after typing, got $($vals.Count)" } + $bad = -1 + for ($i=0; $i -lt 3; $i++) { if ($vals[$i] -ne $expect[$i]) { $bad = $i; break } } + if ($bad -ge 0) { + $f = FormFields $x2 + if ($f.Count -lt 3) { throw "form lost fields during retry" } + Clear-Field (Center $f[$bad]) $bad + TypeAscii $expect[$bad] + continue + } + break + } + if ($vals[0] -ne $host_) { throw "form input FAIL: host field is [$($vals[0])], expected [$host_]" } + if ($vals[1] -ne $port_) { throw "form input FAIL: port field is [$($vals[1])], expected [$port_]" } + if ($vals[2] -ne $code_) { throw "form input FAIL: code field is [$($vals[2])], expected [$code_]" } + $btn = $null + foreach ($want in @($F_CONNECT, $F_RECONNECT)) { $btn = ButtonCenter $x2 $want; if ($btn) { break } } + if (-not $btn) { throw "no primary connect/reconnect button found" } + $t = Get-Date + [void](AdbShell @("input","tap","$($btn[0])","$($btn[1])")) + return $t +} + +function Wait-Connecting([string]$blackhole, [double]$seconds) { + # connecting state = remote panel up, bottom bar shows the CANCEL button + $deadline = (Get-Date).AddSeconds($seconds) + while ((Get-Date) -lt $deadline) { + $x = Get-UiText + if (ButtonCenter $x $F_CANCEL) { return $x } + Start-Sleep -Milliseconds 250 + } + throw "never entered connecting state (no CANCEL button within ${seconds}s)" +} + +Write-Host "=== 0. stale-agent guard (R-7) + IME disable (restore in finally) ===" +if (-not $AgentExe) { $AgentExe = Join-Path $repo "target\release\agent.exe" } +$ag = @(Get-Process agent -ErrorAction SilentlyContinue) +if ($ag.Count -gt 0 -and (Test-Path $AgentExe) -and ((Get-Item $AgentExe).LastWriteTime -gt $ag[0].StartTime)) { + Write-Host " running agent predates current build; restarting" + Stop-Process -Name agent -Force -ErrorAction SilentlyContinue + Start-Sleep -Milliseconds 800; $ag = @() +} +if ($ag.Count -eq 0) { + if (-not (Test-Path $AgentExe)) { throw "missing $AgentExe (cargo build --release -p agent)" } + Start-Process $AgentExe -ArgumentList "--port","$Port","--code",$Code -WindowStyle Hidden | Out-Null + Start-Sleep -Seconds 1; Write-Host " agent started (v3)" +} else { Write-Host " agent running and current" } + +$origIme = ((AdbShell @("settings","get","secure","default_input_method")) -join "").Trim() +Write-Host " original IME: [$origIme] (N13 discipline: recorded, restored in finally)" +if ($origIme -and $origIme -ne "null") { [void](AdbShell @("ime","disable",$origIme)) } +# keep the screen awake for the whole run (measured 2026-10-31: mid-run screen +# timeout turned every later tap/keyevent into a no-op against the AOD - the +# 'cancel did not return to form' FAIL was the sleep, not the app; a second +# probe reproduced it with dump texts showing clock+battery instead of the app) +[void](AdbShell @("svc","power","stayon","true")) + +try { + # ================================================================= + Write-Host "=== 1. V-A2a: wrong code -> AUTH class text + detail line ===" + $x = Restart-AppForm + [void](FillAndConnect $x $Host_ "$Port" "wrongcode3a") + $deadline = (Get-Date).AddSeconds(8); $seenAuth = $false; $seenDetail = $false; $shown = "" + while ((Get-Date) -lt $deadline) { + Start-Sleep -Milliseconds 400 + $shown = ((VisibleTexts (Get-UiText)) -join " || ") + if ($shown.Contains($F_AUTH)) { $seenAuth = $true } + if ($shown.Contains("detail:")) { $seenDetail = $true } + if ($seenAuth) { break } + } + & powershell -NoProfile -ExecutionPolicy Bypass -File "$repo\tools\shot.ps1" -Out "$ev\v-a2-auth.png" | Out-Null + Write-Host " screen: $shown" + if (-not $seenAuth) { throw "V-A2a FAIL: no AUTH text ('$F_AUTH'); got [$shown]" } + if (-not $seenDetail) { throw "V-A2a FAIL: AUTH shown but no 'detail:' original-error line; got [$shown]" } + Write-Host " PASS: wrong code -> '$F_AUTH' + detail line" + + # ================================================================= + Write-Host "=== 2. V-A1a: unresolvable host -> RESOLVE fast (NOT redialed) ===" + $x = Restart-AppForm + $t = FillAndConnect $x "999.999.999.999" "$Port" $Code + $deadline = $t.AddSeconds(3); $seen = $false; $shown = ""; $elapsed = -1 + while ((Get-Date) -lt $deadline) { + Start-Sleep -Milliseconds 200 + $shown = ((VisibleTexts (Get-UiText)) -join " || ") + if ($shown.Contains($F_RESOLVE)) { $seen = $true; $elapsed = ((Get-Date) - $t).TotalSeconds; break } + } + & powershell -NoProfile -ExecutionPolicy Bypass -File "$repo\tools\shot.ps1" -Out "$ev\v-a1-resolve.png" | Out-Null + Write-Host " screen: $shown (after ${elapsed}s)" + if (-not $seen) { throw "V-A1a FAIL: no RESOLVE text ('$F_RESOLVE') within 3s; got [$shown]" } + Write-Host " PASS: unresolvable host -> '$F_RESOLVE' in ${elapsed}s (<3s proves no redial)" + + # ================================================================= + Write-Host "=== 3. V-A1b: blackhole IP -> CANCEL tap -> 'cancelled' form ===" + # Intent path (2026-10-31 lesson): with the form path, taps during connecting + # proved unreliable in THIS app - the EditText still holds focus when the + # panel swaps (the form's own click chain leaves focus where it was), and the + # first tap only defocuses: manual form-path probe lost the tap 4 rounds in a + # row, while the intent path (no EditText ever focused) took ONE tap and + # returned '连接已取消' instantly. Intent auto-connect reaches the very same + # connecting state (same startConnect code), so it isolates the product + # behaviour from keyboard/focus noise. + [void](AdbShell @("input","keyevent","224")) + [void](AdbShell @("wm","dismiss-keyguard")) + [void](AdbShell @("am","force-stop","com.verify.remote")) + Start-Sleep -Milliseconds 600 + [void](AdbShell @("am","start","-n","com.verify.remote/.MainActivity", + "--es","host","192.0.2.1","--ei","port","$Port","--es","code",$Code)) + $xc = Wait-Connecting "192.0.2.1" 6 + $cancel = ButtonCenter $xc $F_CANCEL + if (-not $cancel) { throw "V-A1b FAIL: no CANCEL button in intent connecting state" } + $tc = Get-Date + [void](AdbShell @("input","tap","$($cancel[0])","$($cancel[1])")) + $deadline = $tc.AddSeconds(3); $back = $false; $shown = "" + while ((Get-Date) -lt $deadline) { + Start-Sleep -Milliseconds 250 + $x2 = Get-UiText + $shown = ((VisibleTexts $x2) -join " || ") + # judgement = the CANCEL-triggered text specifically (F_CANCELED), not + # merely 'back at the form' - the 8s budget would also return a form + # (with the timeout text) and that must not masquerade as a cancel PASS. + if ((FormFields $x2).Count -ge 3 -and $shown.Contains($F_CANCELED)) { $back = $true; break } + } + & powershell -NoProfile -ExecutionPolicy Bypass -File "$repo\tools\shot.ps1" -Out "$ev\v-a1-cancel.png" | Out-Null + Write-Host " after cancel: $shown" + if (-not $back) { throw "V-A1b FAIL: cancel tap did not surface '$F_CANCELED' within 3s; got [$shown]" } + $ms = [int]((Get-Date).Subtract($tc).TotalMilliseconds) + Write-Host " PASS: connecting -> CANCEL -> '$F_CANCELED' form in ${ms}ms" + + # ================================================================= + Write-Host "=== 4. V-A4: BACK while connecting -> cancel, activity survives ===" + # same intent-path rationale as step 3 (BACK also needs the app window to + # own input focus; a focused EditText would eat the first gesture instead) + [void](AdbShell @("input","keyevent","224")) + [void](AdbShell @("wm","dismiss-keyguard")) + [void](AdbShell @("am","force-stop","com.verify.remote")) + Start-Sleep -Milliseconds 600 + [void](AdbShell @("am","start","-n","com.verify.remote/.MainActivity", + "--es","host","192.0.2.1","--ei","port","$Port","--es","code",$Code)) + [void](Wait-Connecting "192.0.2.1" 6) + [void](AdbShell @("input","keyevent","4")) # BACK + $deadline = (Get-Date).AddSeconds(3); $atForm = $false; $shown = "" + while ((Get-Date) -lt $deadline) { + Start-Sleep -Milliseconds 250 + $x2 = Get-UiText + $shown = ((VisibleTexts $x2) -join " || ") + if ((FormFields $x2).Count -ge 3 -and $shown.Contains($F_CANCELED)) { $atForm = $true; break } + } + $focusLine = ((AdbShell @("dumpsys","window")) -join "`n") + $alive = $focusLine -match "com\.verify\.remote" + & powershell -NoProfile -ExecutionPolicy Bypass -File "$repo\tools\shot.ps1" -Out "$ev\v-a4-back.png" | Out-Null + Write-Host " after BACK: activityAlive=$alive atForm=$atForm" + Write-Host " screen: $shown" + if (-not $alive) { throw "V-A4 FAIL: BACK during connect finished the activity (old super.onKeyDown regression)" } + if (-not $atForm) { throw "V-A4 FAIL: BACK during connect did not surface '$F_CANCELED' on the form" } + Write-Host " PASS: BACK during connect -> cancel + form, activity kept" + + # ================================================================= + Write-Host "=== 5. V-A5: live session HUD: fps= present, no coordinate noise ===" + $x = Restart-AppForm + [void](FillAndConnect $x $Host_ "$Port" $Code) + $deadline = (Get-Date).AddSeconds(12); $live = $false; $hud = "" + while ((Get-Date) -lt $deadline) { + Start-Sleep -Milliseconds 500 + $m = [regex]::Match((Get-UiText),'text="([^"]*RTT=[^"]*)"') + if ($m.Success) { $hud = [System.Net.WebUtility]::HtmlDecode($m.Groups[1].Value); $live = $true; break } + } + $hasFps = $hud.Contains("fps=") + $noNoise = -not ($hud.Contains("norm=") -or $hud.Contains("view=")) + & powershell -NoProfile -ExecutionPolicy Bypass -File "$repo\tools\shot.ps1" -Out "$ev\v-a5-hud.png" | Out-Null + Write-Host " HUD: $hud" + if (-not $live) { throw "V-A5 FAIL: session never reached live state" } + if (-not $hasFps) { throw "V-A5 FAIL: HUD missing fps= ; got [$hud]" } + if (-not $noNoise) { throw "V-A5 FAIL: HUD carries coordinate noise (norm=/view=)" } + Write-Host " PASS: live HUD shows fps, no coordinate noise" + + Write-Host "=== 5b. V-A3 re-check: kill agent -> 'lost' form within ~16s ===" + Stop-Process -Name agent -Force -ErrorAction SilentlyContinue + $deadline = (Get-Date).AddSeconds(16); $lost = $false; $shown = "" + while ((Get-Date) -lt $deadline) { + Start-Sleep -Milliseconds 500 + $x3 = Get-UiText + $shown = ((VisibleTexts $x3) -join " || ") + if ($shown.Contains($F_KILLMSG) -and (FormFields $x3).Count -ge 3) { $lost = $true; break } + } + & powershell -NoProfile -ExecutionPolicy Bypass -File "$repo\tools\shot.ps1" -Out "$ev\v-a3-lost.png" | Out-Null + Write-Host " after kill: $shown" + if (-not $lost) { throw "V-A3 FAIL: kill did not surface '$F_KILLMSG' form within 16s" } + Write-Host " PASS: agent killed -> lost-session surfaces on form (idle-timeout bound)" + + Write-Host "=== 6. reconnect to a fresh agent (self-heal path) ===" + Start-Process $AgentExe -ArgumentList "--port","$Port","--code",$Code -WindowStyle Hidden | Out-Null + Start-Sleep -Seconds 2 + $x = Get-UiText + $btn = ButtonCenter $x $F_RECONNECT + if (-not $btn) { $x = Restart-AppForm; $btn = $null } + if ($btn) { + [void](AdbShell @("input","tap","$($btn[0])","$($btn[1])")) + $deadline = (Get-Date).AddSeconds(14); $live2 = $false + while ((Get-Date) -lt $deadline) { + Start-Sleep -Milliseconds 500 + if ((Get-UiText) -match "RTT=") { $live2 = $true; break } + } + if (-not $live2) { throw "step 6 FAIL: reconnect button did not reach a live session" } + Write-Host " PASS: one-tap reconnect after agent restart works" + [void](AdbShell @("input","keyevent","4")) # disconnect, free the slot + Start-Sleep -Milliseconds 600 + } else { + Write-Host " (no live reconnect path exercised this run - covered by manual run)" + } +} +finally { + [void](AdbShell @("svc","power","stayon","false")) # undo the keep-awake above + if ($origIme -and $origIme -ne "null") { + [void](AdbShell @("ime","enable",$origIme)) + [void](AdbShell @("ime","set",$origIme)) + Write-Host " IME restored: $(((AdbShell @('settings','get','secure','default_input_method')) -join '').Trim())" + } +} + +Write-Host "=== CONNECT LIFECYCLE PROOF COMPLETE ===" diff --git a/tools/emoji_manual_proof.ps1 b/tools/emoji_manual_proof.ps1 index e1855ca..2d2d5ae 100644 --- a/tools/emoji_manual_proof.ps1 +++ b/tools/emoji_manual_proof.ps1 @@ -16,7 +16,9 @@ # ... then, when prompted, tap an emoji on the phone's keyboard. param( - [string]$PhoneIp = "192.168.1.180", + # PC (controlled side) LAN address; DHCP-assigned - pass -PhoneIp when it + # moves (see ime_chinese_proof.ps1 for the 2026-10-31 stale-default lesson). + [string]$PhoneIp = "192.168.1.110", [int]$Port = 47800, [string]$Code = "t", [int]$WaitSeconds = 120 diff --git a/tools/ime_chinese_proof.ps1 b/tools/ime_chinese_proof.ps1 index 123b11a..4c623b8 100644 --- a/tools/ime_chinese_proof.ps1 +++ b/tools/ime_chinese_proof.ps1 @@ -16,7 +16,10 @@ # Not covered: swipe/handwriting input, browsing the candidate list. param( - [string]$PhoneIp = "192.168.1.180", + # PC (controlled side) LAN address. Default = the 2026-10-31 measured WLAN + # address; it is DHCP-assigned, so pass -PhoneIp when the office network + # moves (the stale .180 default silently cost a proof run on 2026-10-31). + [string]$PhoneIp = "192.168.1.110", [int]$Port = 47800, [string]$Code = "t" ) diff --git a/tools/ime_commit_proof.ps1 b/tools/ime_commit_proof.ps1 index 37352a5..c4a4bb5 100644 --- a/tools/ime_commit_proof.ps1 +++ b/tools/ime_commit_proof.ps1 @@ -16,7 +16,9 @@ # powershell -ExecutionPolicy Bypass -File tools\ime_commit_proof.ps1 param( - [string]$PhoneIp = "192.168.1.180", + # PC (controlled side) LAN address; DHCP-assigned - pass -PhoneIp when it + # moves (see ime_chinese_proof.ps1 for the 2026-10-31 stale-default lesson). + [string]$PhoneIp = "192.168.1.110", [int]$Port = 47800, [string]$Code = "t", [string]$Text = "hi"