Files
ykxmz/_tests/verify_all.ps1
T
xmz7891 df8056dd16 修四个安装/卸载卡死的 bug(都实测复现过)
1. 卸载里 input() 在无 stdin 时永久阻塞
   从控制面板卸载**永远卡住不结束**。安装包的 [UninstallRun]
   用 runhidden 调用,没有 stdin,input() 就一直等。
   改成只在 sys.stdin.isatty() 时才等回车。

2. Popen 没重定向子进程 stdio,导致 capture_output 等管道 EOF
   _schedule_self_delete 起的 cmd 继承了父进程的 stdout/stderr 句柄,
   任何用 capture_output 调 --uninstall 的地方都会一直等,
   表现为"程序卡死"(实测 90 秒不返回,改后 0.2 秒)。
   Popen 加 stdin/stdout/stderr=DEVNULL。
   顺带把延迟从 timeout 换成 ping —— timeout 在 DETACHED_PROCESS
   下没有控制台会立刻失败,等于没延迟直接删。

3. _kill_running 用 taskkill /IM ykxmz.exe 把**自己**杀了
   --uninstall 本身就跑在 ykxmz.exe 里,这条命令一发,
   后面的"删开机自启""删数据目录"全都没执行。
   现象:卸载看着完成了,HKCU 的自启注册表项还留着。
   改成 /FI "PID ne <self>" 排除自己。

4. 安装器关不掉以管理员身份运行的旧实例 -> 整个安装回滚
   Inno 自带机制只会"请求"对方退出,遇上常驻的服务进程关不掉,
   弹"无法自动关闭所有应用程序",静默模式下被压成"终止",
   安装直接回滚 —— 用户看到的是"装了没反应/还是旧版本"。
   改成 PrepareToInstall 里自己 taskkill /F,关掉 Inno 那套。

另外修了两个卸载残留:
   * 安装目录留个空壳 -> [UninstallDelete] 加 dirifempty
   * ProgramData\ykxmz 没删 -> 加进 [UninstallDelete]

还有一处 Inno 写法陷阱(踩了三次):
   注释里写 [区段名] 或 {常量} 会让编译直接失败,
   因为 [Code] 解析器连注释内容也处理。
   加了 _build/check_iss.py 专门扫这个,并进全量套件。

实测结果:
   静默安装  5 秒内退出,退出码 0,配对码正确写入,程序自动跑起来
   静默卸载  0.7 秒退出(原来无限卡住)
   全量回归  29 项全通过
   卸载逻辑  11 项全通过
2026-10-07 01:19:11 +08:00

523 lines
24 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 局域网遥控 全量验证脚本
#
# 依次执行:
# 0. 清理上一次测试可能残留的进程(否则发现端口会被抢占,导致假失败)
# 1. Python 被控端协议回归(含屏幕镜像/截屏)
# 2. 手表屏幕布局适配核算(490x602)
# 3. 屏幕共享可见提示(真实界面 + Win32 窗口枚举)
# 4. 小窗口 / 无托盘 / 后台常驻 / 双启动找回窗口
# 5. Java 协议层编译
# 6. Java <-> 桩服务端(含帧流、截屏、自动发现、单播探测)
# 7. Java <-> 打包后的 EXE 端到端
#
# 用法: powershell -ExecutionPolicy Bypass -File _tests\verify_all.ps1
$ErrorActionPreference = "Continue"
$root = Split-Path -Parent $PSScriptRoot
$tc = Join-Path $root "..\starklink\toolchain"
$jdk = Join-Path $tc "jdk17"
$java = Join-Path $jdk "bin\java.exe"
$javac= Join-Path $jdk "bin\javac.exe"
$out = Join-Path $root "_tests\out_java"
$exe = Join-Path $root "dist_exe\ykxmz\ykxmz.exe"
$androidJar = Join-Path $tc "android-sdk\platforms\android-34\android.jar"
$script:results = @()
function Record($name, $ok) {
$script:results += [pscustomobject]@{ 项目 = $name; 结果 = $(if ($ok) { "PASS" } else { "FAIL" }) }
}
function Banner($text) {
Write-Host ""
Write-Host ("=" * 70) -ForegroundColor DarkCyan
Write-Host " $text" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor DarkCyan
}
function Show-Fails($lines) {
# 失败的阶段光看最后几行没用,得把 [FAIL] 打出来
$fails = $lines | Select-String -Pattern '\[FAIL\]'
if ($fails) {
$fails | Select-Object -First 8 | ForEach-Object {
Write-Host " $($_.Line.Trim())" -ForegroundColor Red
}
}
$lines | Select-Object -Last 1 | ForEach-Object {
Write-Host " $($_.Trim())"
}
}
function Close-TestWindows {
# 测试会弹真实的 Tk 窗口(首次设置、投图、屏幕共享提示…)。
# 正常路径下测试自己会关;但用例失败/超时时进程还挂着,
# 窗口就留在屏幕上不走了。这里统一收尾。
$closed = 0
Get-Process python, pythonw -ErrorAction SilentlyContinue |
Where-Object { $_.Id -ne $PID } |
ForEach-Object {
$line = (Get-CimInstance Win32_Process -Filter "ProcessId=$($_.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($line -and ($line -match '_tests' -or $line -match 'run_agent' `
-or $line -match 'relay\.py')) {
# 先好好关主窗口
try { $_.CloseMainWindow() | Out-Null } catch { }
Start-Sleep -Milliseconds 250
if (-not $_.HasExited) {
Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
}
$closed++
}
}
# 被控端自己(测试起过 ykxmz.exe)
Get-Process ykxmz -ErrorAction SilentlyContinue | ForEach-Object {
try { $_.CloseMainWindow() | Out-Null } catch { }
Start-Sleep -Milliseconds 250
if (-not $_.HasExited) {
Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
}
$closed++
}
# Java 测试进程
Get-Process java -ErrorAction SilentlyContinue | ForEach-Object {
$line = (Get-CimInstance Win32_Process -Filter "ProcessId=$($_.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($line -and ($line -match 'JavaClientTest' -or $line -match 'JavaRelayTest')) {
Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
$closed++
}
}
if ($closed -gt 0) {
Write-Host " 关了 $closed 个测试进程/窗口" -ForegroundColor DarkGray
}
Start-Sleep -Milliseconds 400
}
function Stop-Strays {
Get-Process ykxmz -ErrorAction SilentlyContinue |
ForEach-Object { Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue }
# 各测试会临时起 relay.py / stub / java。
# 只清 run_agent/run_stub 是不够的 —— 漏掉的 relay.py 如果没及时退出,
# 端口就占着,下一个测试起不来,报出"假失败"
# (实测:chunked 单跑 9/9、协议单跑 69/69,套件里却 FAIL)。
Get-Process python, pythonw -ErrorAction SilentlyContinue |
Where-Object { $_.Id -ne $PID } |
ForEach-Object {
$line = (Get-CimInstance Win32_Process -Filter "ProcessId=$($_.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($line -and ($line -match 'run_agent' -or $line -match 'run_stub' `
-or $line -match 'relay\.py' -or $line -match '_tests')) {
Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
}
}
# Java 测试进程(JavaClientTest / JavaRelayTest)
Get-Process java -ErrorAction SilentlyContinue |
ForEach-Object {
$line = (Get-CimInstance Win32_Process -Filter "ProcessId=$($_.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($line -and ($line -match 'JavaClientTest' -or $line -match 'JavaRelayTest')) {
Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
}
}
# 等到关键端口真的空出来(最多 6 秒),别让下一个测试撞上
for ($i = 0; $i -lt 12; $i++) {
$busy = $false
foreach ($p in 8765, 8766, 8799, 8130, 8140) {
if (Get-NetTCPConnection -LocalPort $p -State Listen -ErrorAction SilentlyContinue) {
$busy = $true
}
if (Get-NetUDPEndpoint -LocalPort $p -ErrorAction SilentlyContinue) {
$busy = $true
}
}
if (-not $busy) { break }
Start-Sleep -Milliseconds 500
}
Start-Sleep -Milliseconds 300
}
# ---------------------------------------------------------------- 0
Banner "0/7 清理残留进程与端口"
Stop-Strays
$busy = @()
foreach ($p in 8765, 8766, 8799, 8801, 8803, 8805) {
if (Get-NetUDPEndpoint -LocalPort $p -ErrorAction SilentlyContinue) { $busy += "UDP $p" }
if (Get-NetTCPConnection -LocalPort $p -State Listen -ErrorAction SilentlyContinue) { $busy += "TCP $p" }
}
if ($busy.Count -eq 0) {
Write-Host "端口干净" -ForegroundColor Green
} else {
Write-Host ("仍被占用: " + ($busy -join ", ")) -ForegroundColor Yellow
}
# ---------------------------------------------------------------- 0a
# 清掉上一次测试留在 received 里的文件。
# 不清的话会越攒越多,把 file_list 的前 100 个窗口挤满,
# 导致"看不到刚上传的文件"这类假失败(真踩过:攒到 116 个)。
$receivedDir = Join-Path $root "_tests\received"
if (Test-Path $receivedDir) {
$old = (Get-ChildItem $receivedDir -File -ErrorAction SilentlyContinue).Count
if ($old -gt 0) {
Get-ChildItem $receivedDir -File | Remove-Item -Force -ErrorAction SilentlyContinue
Write-Host " 清掉上次测试残留的 $old 个文件" -ForegroundColor DarkGray
}
}
# ---------------------------------------------------------------- 0
# 编码审计放最前面:编码错了后面全白搭
Banner "0/7 编码审计(BOM / 行尾 / 乱码)"
& python (Join-Path $root "_tests\audit_encoding.py") *> (Join-Path $env:TEMP "lr_enc.txt")
$encCode = $LASTEXITCODE
Get-Content (Join-Path $env:TEMP "lr_enc.txt") -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 3
Record "编码审计" ($encCode -eq 0)
# ---------------------------------------------------------------- 1
Banner "1/7 Python 被控端协议回归"
& python (Join-Path $root "_tests\test_agent_protocol.py") 2>&1 | Select-Object -Last 4
Record "Python 被控端协议回归" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 1b
Banner "1b/7 防火墙阻止规则检测(真机连不上的根因)"
& python (Join-Path $root "_tests\test_firewall.py") 2>&1 | Select-Object -Last 3
Record "防火墙阻止规则检测" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 1c
Banner "1c/7 连不上?-> 电脑打开网络自检(UDP 链路)"
Stop-Strays
& python (Join-Path $root "_tests\test_selfcheck.py") *> (Join-Path $env:TEMP "lr_sc.txt")
$scCode = $LASTEXITCODE
Get-Content (Join-Path $env:TEMP "lr_sc.txt") -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 2
Record "自检请求链路" ($scCode -eq 0)
# ---------------------------------------------------------------- 1e
Banner "1e/7 新增功能(电量/磁盘/性能/进程/剪贴板历史/安全边界)"
Stop-Strays
& python (Join-Path $root "_tests\test_new_features.py") *> (Join-Path $env:TEMP "lr_feat.txt")
$featCode = $LASTEXITCODE
Get-Content (Join-Path $env:TEMP "lr_feat.txt") -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 2
Record "新增功能" ($featCode -eq 0)
# ---------------------------------------------------------------- 1f
Banner "1f/7 电脑功能注册表(39 个功能 + 安全边界 + 可配置)"
Stop-Strays
& python (Join-Path $root "_tests\test_features.py") *> (Join-Path $env:TEMP "lr_feats.txt")
$featsCode = $LASTEXITCODE
Get-Content (Join-Path $env:TEMP "lr_feats.txt") -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 2
Record "电脑功能注册表" ($featsCode -eq 0)
# ---------------------------------------------------------------- 1g
Banner "1g/7 功能模糊测试(105 个功能 × 空参数 / 垃圾参数 / 越权路径)"
Stop-Strays
& python (Join-Path $root "_tests\fuzz_features.py") *> (Join-Path $env:TEMP "lr_fuzz.txt")
$fuzzCode = $LASTEXITCODE
Get-Content (Join-Path $env:TEMP "lr_fuzz.txt") -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 3
Record "功能模糊测试" ($fuzzCode -eq 0)
# ---------------------------------------------------------------- 1h
Banner "1h/7 端到端(打包后的 EXE:配对 / 功能列表 / 越权 / 防爆破)"
Stop-Strays
& python (Join-Path $root "_tests\test_e2e_features.py") *> (Join-Path $env:TEMP "lr_e2e.txt")
$e2eCode = $LASTEXITCODE
Get-Content (Join-Path $env:TEMP "lr_e2e.txt") -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 3
Record "端到端(打包 EXE)" ($e2eCode -eq 0)
# ---------------------------------------------------------------- 1i
Banner "1i/7 静默模式(0 窗口 / 无终端 / 认证仍生效)"
Stop-Strays
& python (Join-Path $root "_tests\test_silent_mode.py") *> (Join-Path $env:TEMP "lr_silent.txt")
$silentCode = $LASTEXITCODE
Get-Content (Join-Path $env:TEMP "lr_silent.txt") -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 3
Record "静默模式" ($silentCode -eq 0)
# ---------------------------------------------------------------- 1j
Banner "1j/7 配一次→零窗口(令牌重连,全程无窗口)"
Stop-Strays
& python (Join-Path $root "_tests\test_pair_once_flow.py") *> (Join-Path $env:TEMP "lr_flow.txt")
$flowCode = $LASTEXITCODE
Get-Content (Join-Path $env:TEMP "lr_flow.txt") -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 3
Record "配一次→零窗口" ($flowCode -eq 0)
# ---------------------------------------------------------------- 1k
Banner "1k/7 首次安装设置窗口(出现一次,之后不再出现)"
Stop-Strays
& python (Join-Path $root "_tests\test_first_run_setup.py") *> (Join-Path $env:TEMP "lr_setup.txt")
$setupCode2 = $LASTEXITCODE
Get-Content (Join-Path $env:TEMP "lr_setup.txt") -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 3
Record "首次设置窗口" ($setupCode2 -eq 0)
# ---------------------------------------------------------------- 1l
Banner "1l/7 开机零窗口(照注册表里的自启命令真跑一遍)"
Stop-Strays
& python (Join-Path $root "_tests\test_boot_silent.py") *> (Join-Path $env:TEMP "lr_boot.txt")
$bootCode = $LASTEXITCODE
Get-Content (Join-Path $env:TEMP "lr_boot.txt") -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 3
Record "开机零窗口" ($bootCode -eq 0)
# ---------------------------------------------------------------- 2
Banner "2/7 手表屏幕布局适配核算 (490x602)"
& python (Join-Path $root "_tests\check_layout.py") 2>&1 | Select-Object -Last 3
Record "手表布局适配" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 1z
Banner "1z/7 手表分页完整性(防白屏:方法定义了必须被调用)"
& python (Join-Path $root "_tests\test_no_orphan_methods.py") 2>&1 |
Select-Object -Last 6
Record "手表分页完整性" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 2a2
Banner "2a2/7 APK 内容(诊断已合并进主程序)"
& python (Join-Path $root "_tests\test_apk_contents.py") *> (Join-Path $env:TEMP "lr_apk.txt")
$apkCode = $LASTEXITCODE
Get-Content (Join-Path $env:TEMP "lr_apk.txt") -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 2
Record "APK 内容" ($apkCode -eq 0)
# ---------------------------------------------------------------- 2a
Banner "2a/7 网络调优(MTU 修复)"
# 这是可用率从 48% 提到 100% 的关键,而且容易被后人误改
# (比如"顺手把接口 MTU 也降一点" —— 实测那样会掉到 50%)。
& python (Join-Path $root "_tests\test_net_tune.py") 2>&1 |
Select-Object -Last 4
Record "网络调优(MTU)" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 2b
Banner "2b/7 投图显示方式(屏幕中间 / 全屏 / 自定义大小)"
Stop-Strays
$imgLog = Join-Path $env:TEMP "lr_imgmode.txt"
& python (Join-Path $root "_tests\test_image_modes.py") *> $imgLog
$imgCode = $LASTEXITCODE
if ($imgCode -eq 0) {
Get-Content $imgLog -Encoding UTF8 -ErrorAction SilentlyContinue |
Select-Object -Last 3
} else {
Write-Host "失败输出:" -ForegroundColor Red
Get-Content $imgLog -Encoding UTF8 -ErrorAction SilentlyContinue |
Where-Object { $_ -match "FAIL|通过|Traceback|Error" }
}
Record "投图显示方式" ($imgCode -eq 0)
# ---------------------------------------------------------------- 3
Banner "3/7 屏幕共享可见提示(真实界面 + Win32 窗口枚举)"
Stop-Strays
& python (Join-Path $root "_tests\test_indicator_visible.py") 2>&1 | Select-Object -Last 3
Record "屏幕共享强制提示" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 2b
Banner "2b/7 中转 chunked 请求体(防连接错位)"
# Cloudflare 转发时可能用 Transfer-Encoding: chunked。
# 中转原来只读 Content-Length,残留的 "0\r\n\r\n" 会被当成下一个请求行,
# 报 "Bad request syntax ('0')",整条连接错位 -> 隧道大面积 502。
$stageOut = & python (Join-Path $root "_tests\test_chunked_body.py") 2>&1
Show-Fails $stageOut
Record "中转 chunked 请求体" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 3a
Banner "3a/7 局域网直连路径(不经中转)"
# 手表和电脑同一个 WiFi 时走这条:UDP 广播找到电脑 + TCP 直连。
# 这条路径完全不碰中转,所以单独盯一下。
$stageOut = & python (Join-Path $root "_tests\test_lan_path.py") 2>&1
Show-Fails $stageOut
Record "局域网直连路径" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 3b
Banner "3b/7 Python 静态检查(pyflakes)"
# 这一层能抓到"运行到某行才炸"的问题。
# 踩过的坑:check_code_available() 用了没导入的 DEFAULT_RELAY_URL,
# 结果装到最后一步弹 NameError —— 而回归全绿、编码审计也过。
$stageOut = & python (Join-Path $root "_tests\audit_python_names.py") 2>&1
Show-Fails $stageOut
Record "Python 静态检查" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 3b3
Banner "3b3/7 安装包脚本语法(Inno 的注释陷阱)"
# 踩过的坑:Inno 的 [Code] 解析器**连注释内容也处理**。
# 注释里写 [区段名] 报"无效的区段标记",写 {常量} 报 "Syntax error",
# 都是直接编译失败。这个检查专门拦这类。
$stageOut = & python (Join-Path $root "_build\check_iss.py") 2>&1
Show-Fails $stageOut
Record "安装包脚本语法" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 3b4
Banner "3b4/7 卸载逻辑(不能杀自己 / 不能阻塞)"
# 踩过的坑:
# 1. taskkill /IM ykxmz.exe 把正在卸载的进程自己杀了 -> 删自启没执行
# 2. Popen 不重定向 stdio -> capture_output 等管道 EOF -> "卡死"
# 3. input() 在无 stdin 时永久阻塞 -> 控制面板卸载卡死
# 4. keep_dir 缺失 -> 自删和 Inno 卸载器抢目录
$stageOut = & python (Join-Path $root "_build\test_uninstall_logic.py") 2>&1
Show-Fails $stageOut
Record "卸载逻辑" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 3b2
Banner "3b2/7 管理页网络体检"
# 用户看到"连不上"时最需要知道:隧道几条连接、公网通不通、延迟多少。
# 中转和 cloudflared 同机,能直接读它的指标。
$stageOut = & python (Join-Path $root "_tests\test_admin_netcheck.py") 2>&1
Show-Fails $stageOut
Record "管理页网络体检" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 3c
Banner "3c/7 中转管理端批量删除"
$stageOut = & python (Join-Path $root "_tests\test_admin_batch_delete.py") 2>&1
Show-Fails $stageOut
Record "中转批量删除" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 3d
Banner "3d/7 管理页 JS 合法性(防幽灵按键)"
# 管理页的 JS 是拼出来的,字符串里混进真换行会让整段脚本报错,
# 按钮点了没反应。这个阶段逐字符扫描验证。
$stageOut = & python (Join-Path $root "_tests\test_admin_js.py") 2>&1
Show-Fails $stageOut
Record "管理页 JS 合法性" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 4b
Banner "4b/7 手表 Activity 骨架(防白屏)"
# onCreate 被删掉过一次:照样编译通过,但打开就是白屏。
# 这一阶段专门断言生命周期骨架完整。
$stageOut = & python (Join-Path $root "_tests\test_watch_skeleton.py") 2>&1
Show-Fails $stageOut
Record "手表 Activity 骨架" ($LASTEXITCODE -eq 0)
# ---------------------------------------------------------------- 4c
Banner "4c/7 编译整个手表 App(含界面代码)"
# 这一步不能省:只编协议层的话,MainActivity.java 里的错误
# 根本检查不到 —— 改界面时删坏过字段声明,协议层照样通过,
# 回归全绿,结果 APK 编不出来。用 android.jar 做 classpath 编全量。
if (-not (Test-Path $androidJar)) {
Write-Host "找不到 android.jar: $androidJar" -ForegroundColor Red
Record "手表 App 整体编译" $false
} else {
$appOut = Join-Path $root "_tests\out_app"
if (Test-Path $appOut) { Remove-Item -Recurse -Force $appOut }
New-Item -ItemType Directory -Force -Path $appOut | Out-Null
$appSrcs = Get-ChildItem (Join-Path $root "android\src") -Recurse -Filter "*.java" |
ForEach-Object { $_.FullName }
$env:JAVA_TOOL_OPTIONS = "-Duser.language=en -Duser.country=US"
& $javac --release 8 -encoding UTF-8 -cp $androidJar -d $appOut $appSrcs 2>&1 |
Select-String -Pattern "error" | Select-Object -First 8 | ForEach-Object { Write-Host " $_" }
$appOk = ($LASTEXITCODE -eq 0)
$env:JAVA_TOOL_OPTIONS = ""
Write-Host $(if ($appOk) { " App 编译通过" } else { " App 编译失败" })
Record "手表 App 整体编译" $appOk
}
# ---------------------------------------------------------------- 5
Banner "5/7 编译 Java 协议层"
if (Test-Path $out) { Remove-Item -Recurse -Force $out }
New-Item -ItemType Directory -Force -Path $out | Out-Null
# Transport / RelayTransport 是跨网络用的:把"怎么传字节"抽出来,
# 这样 RemoteClient 的协议逻辑不用改就能走中转。
& $javac --release 8 -encoding UTF-8 -d $out `
(Join-Path $root "android\src\com\lanremote\watch\Json.java") `
(Join-Path $root "android\src\com\lanremote\watch\B64.java") `
(Join-Path $root "android\src\com\lanremote\watch\DiscoveryCore.java") `
(Join-Path $root "android\src\com\lanremote\watch\Transport.java") `
(Join-Path $root "android\src\com\lanremote\watch\RelayTransport.java") `
(Join-Path $root "android\src\com\lanremote\watch\TlsCompat.java") `
(Join-Path $root "android\src\com\lanremote\watch\RemoteClient.java") `
(Join-Path $root "_tests\java\JavaClientTest.java") 2>&1 | Out-Null
$compiled = ($LASTEXITCODE -eq 0)
Write-Host $(if ($compiled) { "编译通过" } else { "编译失败" })
Record "Java 协议层编译" $compiled
if (-not $compiled) { return }
# ---------------------------------------------------------------- 6
Banner "6/7 Java <-> 桩服务端 跨语言联调"
Stop-Strays
$stubData = Join-Path $root "_tests\_stubdata"
Remove-Item -Recurse -Force $stubData -ErrorAction SilentlyContinue
$codeFile = Join-Path $env:TEMP "lr_verify_code.txt"
Remove-Item -Force $codeFile -ErrorAction SilentlyContinue
$stub = Start-Process -FilePath "python" `
-ArgumentList "_tests\run_stub_server.py", "--port", "8799",
"--code-file", $codeFile, "--data-dir", $stubData `
-WorkingDirectory $root -PassThru -WindowStyle Hidden `
-RedirectStandardOutput (Join-Path $env:TEMP "lr_verify_stub.txt") `
-RedirectStandardError (Join-Path $env:TEMP "lr_verify_stub_err.txt")
$deadline = (Get-Date).AddSeconds(25)
while (-not (Test-Path $codeFile) -and (Get-Date) -lt $deadline) { Start-Sleep -Milliseconds 300 }
if (-not (Test-Path $codeFile)) {
Write-Host "桩服务端未就绪" -ForegroundColor Red
Get-Content (Join-Path $env:TEMP "lr_verify_stub_err.txt") -ErrorAction SilentlyContinue
Stop-Process -Id $stub.Id -Force -ErrorAction SilentlyContinue
Record "Java <-> 桩服务端" $false
} else {
& $java "-Dfile.encoding=UTF-8" -cp $out JavaClientTest "127.0.0.1" 8799 "246813" "stub" 2>&1 |
Select-Object -Last 4
Record "Java <-> 桩服务端" ($LASTEXITCODE -eq 0)
Stop-Process -Id $stub.Id -Force -ErrorAction SilentlyContinue
}
# ---------------------------------------------------------------- 7
Banner "7/7 Java <-> 打包后的 EXE 端到端联调"
Stop-Strays
if (-not (Test-Path $exe)) {
Write-Host "未找到打包后的 EXE: $exe" -ForegroundColor Yellow
Write-Host "请先运行: powershell -File windows\build_exe.ps1" -ForegroundColor Yellow
Record "Java <-> 打包 EXE" $false
} else {
$appDir = Split-Path -Parent $exe
Remove-Item -Recurse -Force (Join-Path $appDir "data"), (Join-Path $appDir "received") -ErrorAction SilentlyContinue
# --add-device:静默运行 + 开放配对(默认流程会弹设置窗口,测试会卡住)
$proc = Start-Process -FilePath $exe -ArgumentList "--port", "8801", "--add-device", "10" -PassThru
Start-Sleep -Seconds 7
$settings = Join-Path $appDir "data\settings.json"
$deadline = (Get-Date).AddSeconds(25)
while (-not (Test-Path $settings) -and (Get-Date) -lt $deadline) { Start-Sleep -Milliseconds 400 }
if (-not (Test-Path $settings)) {
Write-Host "EXE 未生成配置,启动失败" -ForegroundColor Red
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
Record "Java <-> 打包 EXE" $false
} else {
$cfg = Get-Content $settings -Raw | ConvertFrom-Json
$cfg.image_timeout = 4
$cfg | ConvertTo-Json | Set-Content $settings -Encoding UTF8
Write-Host "EXE 配对码: $($cfg.pairing_code)"
& $java "-Dfile.encoding=UTF-8" -cp $out JavaClientTest "127.0.0.1" 8801 $cfg.pairing_code "real" 2>&1 |
Select-Object -Last 4
Record "Java <-> 打包 EXE" ($LASTEXITCODE -eq 0)
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 1
Write-Host "`nEXE 接收到的文件:"
Get-ChildItem (Join-Path $appDir "received") -ErrorAction SilentlyContinue |
ForEach-Object { Write-Host (" {0,8:N0} B {1}" -f $_.Length, $_.Name) }
}
}
Stop-Strays
# ---------------------------------------------------------------- 汇总
# 收尾:把测试过程中弹出来的窗口关掉(用户要求)
Close-TestWindows
Banner "验证汇总"
$script:results | Format-Table -AutoSize
$failed = ($script:results | Where-Object { $_.结果 -eq "FAIL" }).Count
if ($failed -eq 0) {
Write-Host "全部通过" -ForegroundColor Green
} else {
Write-Host "$failed 项失败" -ForegroundColor Red
}
exit $failed