mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 12:30:44 +00:00
用户这一轮报了两个问题,都修了。
== 一、Win7 上语音播报不能用 ==
用户原话:"我 win7 上的语音播报好像不能用,你再试一下别的版本能不能用"
**原来只有一条路**:
powershell -NoProfile -WindowStyle Hidden -Command
"Add-Type -AssemblyName System.Speech; $s = New-Object ...; $s.Speak('...')"
这在 Win7 上有三个坑,任何一个都能让它彻底不出声:
1. **`powershell` 只按名字找**
Win7 的 powershell.exe 在 `%SystemRoot%\System32\WindowsPowerShell\v1.0\`。
精简版系统、或从服务/计划任务拉起时 PATH 里可能没有 ——
CreateProcess 按名字找不到时是**静默失败**的。
2. **Win7 默认 PowerShell 2.0,它按 ANSI 代码页解析 `-Command`**
PS 3.0 起才按 UTF-16 处理。中文经 `-Command` 传进去容易乱码 ——
表现是念出来是乱码,或者干脆不念。
3. **`System.Speech` 需要 .NET Framework 3.0+**
而系统自带的 **SAPI5** 从 Windows XP 就有,**根本不需要 .NET**。
**改成多方案降级**,按"依赖越少越优先"排:
1. cscript + VBScript + SAPI5 <- Win98 起就有,不用装任何东西
2. wscript + VBScript + SAPI5 <- 上一条被安全软件拦时的备选
3. powershell + SAPI5 (COM) <- 绕开 .NET
4. powershell + System.Speech <- 原来的做法,改用绝对路径
5. powershell(PATH) + System.Speech <- 最原始的,兜底
一条通了**记住它**,以后直接用(不用每次从头试)。
**顺带修掉一个"骗人"的行为**:原来 `say()` 用 `Popen` 不等进程结束,
失败了也返回"已播报" —— 明明没出声却报成功。
现在等退出码,**全失败会明确报错并列出试过哪几条**。
代价是同步等待(念多久等多久),但"报成功其实没出声"比"慢一点"糟糕得多。
还加了 `_build/diag_tts.py`:在一台机器上把 5 条方案全试一遍并报告
哪条能用 —— 下次再有"某台机器上不行",跑一下就知道。
VBScript 的细节:**必须用 ANSI(mbcs) 写**,写成 UTF-8 的话
cscript 读出来中文是乱码(VBScript 的老规矩,很隐蔽)。
长文本还要分句 —— VBScript 单行有长度上限。
== 二、快捷栏按钮太小 + 4 格都要能换 ==
用户原话:"屏幕最上面这个按钮有些按不到,按钮太小了,做大一点,
然后呢,这个每每个四格算换一下吧,第一个第二个第三个
第四个都能换东西"
* **按钮做大**:30dp -> 46dp(接近 Android 建议的最小可点区域 48),
字号 11 -> 14。30dp 在表盘上手指按上去一半在栏外。
**栏高做成常量 QUICK_BAR_DP,栏高 / 面板 topMargin / addView
三处用同一个值** —— 分三处写的话改一个忘一个,
表现是"内容被盖住"或"上面空一条"。
* **4 格全部可自定义**。用户最早说"其中有一个按钮不能改,
功能就是一键返回桌面",第一版就按"第 4 格写死 desktop"做的。
现在他改主意了 —— 所以 desktop 变成**普通可选动作**,
默认仍放第 4 格(习惯不用改),但想换也能换。
可选动作从 7 个变成 8 个。
**教训(写进测试注释了)**:别把"用户当时的一句话"当成永久约束
焊进结构里;真是约束的话,注释里要写清是什么时候的要求,
下次他改主意时才知道该动哪儿。
== 三、修一个"测试自己没擦干净"造成的假 bug ==
全量回归报:
[FAIL] 配对码正确写入 实际 '(旧的残留值)'
而 `(旧的残留值)` 是**上一次测试遗留**的配对码 —— 数据目录
`C:\ProgramData\ykxmz\data` 在卸载时**故意不删**(那里存着用户的
中转地址、配对码等,卸载不该删),所以机器上会留着上一轮的
settings.json。测试装的时候虽然传了 `/CODE=517284`,但环境没擦干净时
就可能读到旧值 —— 表现成"安装包有 bug"。
**修法**:`test_installer_cycle.py` 加一步"装前先清残留"
(正规卸载 -> 杀进程 -> 删安装目录 -> **删数据目录**)。
数据目录那一步是关键 —— 卸载器不删它。
== 四、其他 ==
* `docs/项目规矩.md` 第一节改成**按预案执行**:
预案一 = 做完测试没问题就上传;预案二 = 做完测试但不上传,直接给用户。
用户没说预案几时默认按预案一,并在交付时说明。
* 顺手把那份文档里的老域名 `relay.xxmxxs.top` 改成现在的
`yk.xxmxxs.top`(老服务器早就退役了)。
* 修掉自己造的重复定义:往 actions.py 插降级链时,
`list_voices` / `say` 各插了两份(pyflakes 报 redefinition)。
* 一次危险的误操作:`Path.write_text(..., newline='\n')` 会**先截断文件
再校验参数**,参数非法时文件已经空了 —— 我用它把
`_tests/test_quickbar.py` 清空了。已重写。
**以后不给 write_text 传 newline。**
== 验证 ==
全量回归(21 关)全部 PASS
新增关卡:3a13 手表快捷栏 / 3a14 语音播报降级
安装包周期 23 项全过(装 -> 验 -> 卸 -> 再验)
Java 编译通过(android-34, --release 8)
本机实测语音播报:cscript 那条通了(2.4 秒)
全部产物重建,Win7 兼容校验通过
844 lines
42 KiB
PowerShell
844 lines
42 KiB
PowerShell
# 局域网遥控 全量验证脚本
|
||
#
|
||
# 依次执行:
|
||
# 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 = @()
|
||
|
||
# 当前会话是不是锁屏 / 没有交互桌面?
|
||
# 锁屏时截屏类测试**必然**失败(拿不到屏幕内容),但那不是代码问题。
|
||
# 不识别的话汇总表一片红,真问题反而被淹没。
|
||
#
|
||
# ⚠ 判断不能只看 OpenInputDesktop 成不成功 —— **锁屏时它同样成功**
|
||
# (那时输入桌面是 Winlogon)。实测漏判过一次:屏幕明明锁着,
|
||
# desktopAvailable 却是 true,于是 6/7、7/7 两个截屏阶段记成 FAIL,
|
||
# 把真正的结论淹掉了。
|
||
# 正确做法是拿输入桌面的**名字**:Default = 正常,Winlogon = 锁屏。
|
||
$script:desktopAvailable = $true
|
||
$script:desktopName = "(未检测)"
|
||
try {
|
||
Add-Type -Namespace Ykxmz -Name Desk -MemberDefinition @'
|
||
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError=true)]
|
||
public static extern System.IntPtr OpenInputDesktop(int flags, bool inherit, int access);
|
||
[System.Runtime.InteropServices.DllImport("user32.dll")]
|
||
public static extern bool CloseDesktop(System.IntPtr h);
|
||
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError=true, CharSet=System.Runtime.InteropServices.CharSet.Unicode)]
|
||
public static extern bool GetUserObjectInformationW(System.IntPtr h, int idx, System.Text.StringBuilder pv, int len, ref int need);
|
||
public static string InputDesktopName() {
|
||
System.IntPtr h = OpenInputDesktop(0, false, 0x0001);
|
||
if (h == System.IntPtr.Zero) return "";
|
||
try {
|
||
System.Text.StringBuilder sb = new System.Text.StringBuilder(256);
|
||
int need = 0;
|
||
if (GetUserObjectInformationW(h, 2, sb, 512, ref need)) return sb.ToString();
|
||
return "";
|
||
} finally { CloseDesktop(h); }
|
||
}
|
||
public static bool HasDesktop() {
|
||
string n = InputDesktopName();
|
||
if (n.Length == 0) return false;
|
||
if (n.Equals("Winlogon", System.StringComparison.OrdinalIgnoreCase)) return false;
|
||
return true;
|
||
}
|
||
'@ -ErrorAction Stop
|
||
$script:desktopName = [Ykxmz.Desk]::InputDesktopName()
|
||
if ($script:desktopName -eq "") { $script:desktopName = "(拿不到)" }
|
||
$script:desktopAvailable = [Ykxmz.Desk]::HasDesktop()
|
||
} catch {
|
||
$script:desktopName = "(检测失败: $($_.Exception.GetType().Name))"
|
||
$script:desktopAvailable = $true # 查不出来就当可用,别把失败藏起来
|
||
}
|
||
|
||
# 无论可不可用都打一行 —— 上次漏判就是因为什么都没打,无从判断。
|
||
Write-Host (" 输入桌面: {0} -> {1}" -f $script:desktopName,
|
||
$(if ($script:desktopAvailable) { "可用" } else { "不可用(锁屏)" })) `
|
||
-ForegroundColor $(if ($script:desktopAvailable) { "DarkGray" } else { "Yellow" })
|
||
|
||
if (-not $script:desktopAvailable) {
|
||
Write-Host ""
|
||
Write-Host " ⚠ 检测到电脑处于锁屏状态。" -ForegroundColor Yellow
|
||
Write-Host " 截屏相关的测试会失败,这是环境限制,不是代码问题。" -ForegroundColor Yellow
|
||
Write-Host " 这些阶段会记为 SKIP 而不是 FAIL。解锁后重跑即可验证。" -ForegroundColor Yellow
|
||
}
|
||
|
||
function Record($name, $ok, $needsDesktop = $false) {
|
||
# needsDesktop 的阶段在锁屏时记 SKIP —— 避免误报把真问题淹没
|
||
if ($needsDesktop -and -not $script:desktopAvailable) {
|
||
$script:results += [pscustomobject]@{
|
||
项目 = $name; 结果 = "SKIP(锁屏)" }
|
||
return
|
||
}
|
||
$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 {
|
||
# ⚠ 这里可能是 ErrorRecord(子进程的 stderr 被塞进输出流时),
|
||
# 而 ErrorRecord 没有 .Trim() 方法 —— 直接调会抛
|
||
# "does not contain a method named 'Trim'",反而把真正的
|
||
# 失败信息盖住。所以先转成字符串再 Trim。
|
||
Write-Host " $([string]$_.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 被控端协议回归"
|
||
$stageOut = & python (Join-Path $root "_tests\test_agent_protocol.py") 2>&1
|
||
$stageCode = $LASTEXITCODE
|
||
$stageOut | Select-Object -Last 4
|
||
Record "Python 被控端协议回归" ($stageCode -eq 0) $true
|
||
|
||
# ---------------------------------------------------------------- 1a2
|
||
Banner "1a2/7 手表面板接线(每个面板都要挂到 root)"
|
||
$stageOut = & python (Join-Path $root "_tests\test_watch_panels_wired.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "手表面板接线" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 1a3
|
||
Banner "1a3/7 网络热门音效(生成/列表/播放/自放素材)"
|
||
$stageOut = & python (Join-Path $root "_tests\test_sfx.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "网络热门音效" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 1b
|
||
Banner "1b/7 防火墙阻止规则检测(真机连不上的根因)"
|
||
$stageOut = & python (Join-Path $root "_tests\test_firewall.py") 2>&1
|
||
$stageCode = $LASTEXITCODE
|
||
$stageOut | Select-Object -Last 3
|
||
Record "防火墙阻止规则检测" ($stageCode -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
|
||
# ---------------------------------------------------------------- 隐藏桌面
|
||
# 下面 6 个阶段会真的开窗口,其中「投图显示方式」还会开**全屏** ——
|
||
# 直接在用户眼前跑的话屏幕会被锁住,没法用电脑。
|
||
#
|
||
# 现在统一套一层 hidden_desktop.py:它新建一个 Windows 桌面,把子进程
|
||
# 放上去,所有窗口都创建在那个不显示的桌面上。
|
||
# **窗口是真的**(全屏、可见、几何、Win32 枚举都真实),
|
||
# 只是用户看不到 —— 测试有效性没有损失。
|
||
#
|
||
# ⚠ 新增会弹窗的测试时,记得也套一层,否则用户屏幕又会被闪。
|
||
|
||
Banner "1e/7 新增功能(电量/磁盘/性能/进程/剪贴板历史/安全边界)"
|
||
Stop-Strays
|
||
& python (Join-Path $root "_tests\hidden_desktop.py") -- 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\hidden_desktop.py") -- 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\hidden_desktop.py") -- 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\hidden_desktop.py") -- 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)"
|
||
$stageOut = & python (Join-Path $root "_tests\check_layout.py") 2>&1
|
||
$stageCode = $LASTEXITCODE
|
||
$stageOut | Select-Object -Last 3
|
||
Record "手表布局适配" ($stageCode -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\hidden_desktop.py") -- 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
|
||
$stageOut = & python (Join-Path $root "_tests\hidden_desktop.py") -- python (Join-Path $root "_tests\test_indicator_visible.py") 2>&1
|
||
$stageCode = $LASTEXITCODE
|
||
$stageOut | Select-Object -Last 3
|
||
Record "屏幕共享强制提示" ($stageCode -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)
|
||
|
||
# ---------------------------------------------------------------- 3a2
|
||
Banner "3a2/9 局域网优化(网段掩码 / 发现重发 / 回切)"
|
||
# 盯四件事:
|
||
# 1. 网段判断按网卡真实掩码,不再写死 /24(/16 网络下能直连却去走中转)
|
||
# 2. 广播发现重发 + 多网段(原来只发一次、只探一个网段)
|
||
# 3. 走中转之后能定期回切局域网
|
||
# 4. 默认中转地址为空白时,局域网照样能配对、能执行命令
|
||
$stageOut = & python (Join-Path $root "_tests\test_lan_optimize.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "局域网优化" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a3
|
||
Banner "3a3/9 手表局域网切换逻辑(源码断言)"
|
||
$stageOut = & python (Join-Path $root "_tests\test_watch_lan_switch.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "手表局域网切换" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a3b
|
||
Banner "3a3b/7 中转默认必须为空(防内置作者服务器)"
|
||
$stageOut = & python (Join-Path $root "_tests\test_relay_default_blank.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "中转默认空白" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a3c
|
||
Banner "3a3c/7 后台抢救报告分区(鉴权 + 不缓存 + 防穿越)"
|
||
$stageOut = & python (Join-Path $root "_tests\test_rescue_section.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "抢救报告分区" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a4
|
||
Banner "3a4/9 编码与换行(.java/.py 必须 UTF-8 无 BOM + LF)"
|
||
# 踩过的坑:edit 工具会把行尾写成 CRLF,而仓库约定是 LF。
|
||
# 这个检查专门盯住,免得改动悄悄把整个文件的行尾换掉。
|
||
$stageOut = & python (Join-Path $root "_tests\check_encoding.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "编码与换行" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a7
|
||
Banner "3a7/9 Python 语法(必须用 compile,不能用 ast.parse)"
|
||
# 这一轮被同一件事咬了三次:补丁脚本把 import 插到 from __future__
|
||
# 前面,整个文件就废了。而更糟的是 ast.parse 查不出来(它只解析,
|
||
# 不做编译期检查)—— 前几次「全仓语法检查 0 问题」都是假绿。
|
||
$stageOut = & python (Join-Path $root "_tests\check_python_syntax.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "Python 语法检查" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a8
|
||
Banner "3a8/9 版本号一致性(防漏改)"
|
||
$stageOut = & python (Join-Path $root "_tests\check_version_consistency.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "版本号一致性" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a9
|
||
Banner "3a9/9 Windows 7 兼容(学校电脑是 Win7)"
|
||
# 用户报:学校电脑是 Windows 7,装完报"dll 缺失"。
|
||
#
|
||
# 根因不是漏打包,是 **Python 版本越界**:
|
||
# Python 3.8 -> 最后一个支持 Windows 7 的版本
|
||
# Python 3.9+ -> 只支持 Windows 8 及以上
|
||
# Python 3.9 起用到 api-ms-win-core-path-l1-1-0.dll 里的
|
||
# PathCchCanonicalizeEx,而这个 DLL **Win7 上根本不存在**,
|
||
# 也没法用 api-set 转发 —— 那个 DLL 放进去也没用。
|
||
#
|
||
# 这个关卡盯三件事:
|
||
# 1. 内嵌的是 python38.dll(不是 python311.dll 之类)
|
||
# 2. **没有** api-ms-win-core-path-l1-1-0.dll
|
||
# 3. exe 是 32 位(Win7 的 32/64 位系统都能跑;64 位在 32 位系统上起不来)
|
||
# 打包脚本 windows\build_exe.ps1 里也调了同一个检查,
|
||
# 不过就直接拒绝出包 —— 这里是回归层面的双保险。
|
||
$stageOut = & python (Join-Path $root "_build\check_win7_compat.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "Windows 7 兼容" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a10
|
||
Banner "3a10/9 静默自动更新链路"
|
||
# 用户要求:"手表端点击一个更新按钮,电脑端就可以自动进行静默更新,
|
||
# 其他什么设置都不变 …… 不弹任何窗口出来"
|
||
# 这条链是:中转 /v1/update/latest -> 被控端 updater.py 下载校验
|
||
# -> 分离 .cmd 帮手 -> 静默装 -> 重启。
|
||
#
|
||
# 这里验协议常量、版本比较(按数字段,不是字符串)、下载校验、
|
||
# 以及**服务端 dispatch 的结构没被补丁弄坏**。
|
||
$stageOut = & python (Join-Path $root "_tests\test_update_flow.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "静默自动更新" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a11
|
||
Banner "3a11/9 远程鼠标 + 一键显示桌面"
|
||
# 鼠标注入是最"有力量"的功能,参数校验写错的话表现是"手表点了没反应",
|
||
# 教室里没法排查。这里盯坐标归一化(除以 宽-1 那条规则)、
|
||
# 滚轮夹取、Win+D 的事件顺序、以及非法参数必须被拒。
|
||
$stageOut = & python (Join-Path $root "_tests\test_mouse_control.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "远程鼠标控制" ($LASTEXITCODE -eq 0)
|
||
|
||
|
||
# ---------------------------------------------------------------- 3a12
|
||
Banner "3a12/9 后台更新按钮 + 弹窗位置/大小"
|
||
# 用户要求:后台能点一下让电脑自己更新;弹文字窗口位置和大小可自定义。
|
||
# 后台那条链路横跨三端(JS -> 中转 -> 被控端),错了的表现是
|
||
# "我点了更新,电脑没反应",所以几个边界必须卡死:
|
||
# 设备不存在 / 离线 / 重复点 / 在线。
|
||
$stageOut = & python (Join-Path $root "_tests\test_admin_update.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "后台更新按钮" ($LASTEXITCODE -eq 0)
|
||
|
||
$stageOut = & python (Join-Path $root "_tests\test_notice_options.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "弹窗位置和大小" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a13
|
||
Banner "3a13/9 手表快捷栏 + 允许新设备连接"
|
||
# 用户要求:"手表端不管干什么,上面都有一栏,栏里的按钮可以在设置页
|
||
# 自定义,最多可以加 4 个,其中有一个按钮不能改,
|
||
# 功能就是一键返回桌面"。
|
||
#
|
||
# 四个地方最容易漏,漏了都不会报错、只会让人困惑:
|
||
# 1. buildQuickBar 没挂进 onCreate -> 栏根本不显示
|
||
# 2. 没加进 showOnly 的数组 -> 面板永远压在上面
|
||
# 3. 第 4 格做成了可配置 -> 用户找半天"显示桌面"没了
|
||
# 4. 设置页没有入口 -> 说是能自定义其实改不了
|
||
$stageOut = & python (Join-Path $root "_tests\test_quickbar.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "手表快捷栏" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a14
|
||
Banner "3a14/9 语音播报降级链(修 Win7 上用不了)"
|
||
# 用户反馈:"我 win7 上的语音播报好像不能用,你再试一下别的版本能不能用"。
|
||
#
|
||
# 原来只有一条路(powershell + System.Speech),而 Win7 上
|
||
# PowerShell 2.0 + 中文 + PATH 三样都可能出问题。改成降级链之后必须保证:
|
||
# * cscript/VBScript 排最前(从 Win98 就有,SAPI5 是系统组件,不用装东西)
|
||
# * 前一条失败要接着试下一条,不是试一条就放弃
|
||
# * 全失败要**报错**,不能像原来那样 Popen 不等待、明明没出声却说"已播报"
|
||
# * VBScript 用 ANSI 写(UTF-8 的话 cscript 读出来中文是乱码)
|
||
$stageOut = & python (Join-Path $root "_tests\test_tts.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "语音播报降级" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a5
|
||
Banner "3a5/9 局域网/中转对比基准(防测试自己骗人)"
|
||
# 这个基准一度是红的,而且报告里写着「这是中转链路的产品 bug」——
|
||
# 实际是基准自己有两个 bug:两条路用了同一个配对码、
|
||
# 以及 attach 用了旧码(打到已经死掉的设备上)。
|
||
# 一个已经修好的产品因此背了黑锅,而且症状(连接超时)
|
||
# 看起来完全像产品问题,极难自己发现。
|
||
$stageOut = & python (Join-Path $root "_tests\test_lan_bench_guard.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "局域网对比基准" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3a6
|
||
Banner "3a6/9 管理页 JS 语法(防按钮全死)"
|
||
# 踩过两次的坑:admin.py 的 ADMIN_JS 是普通三引号字符串,
|
||
# 里面写 \n 会被 Python 变成真实换行;而 JS 的行注释 // 不能跨行,
|
||
# 一断行整段 <script> 语法错误 -> 页面上所有按钮失效。
|
||
# 第二次尤其讽刺:注释为了举例说明「不能写 \n」,自己写了一个 \n,
|
||
# 于是注释被断成两行,把整段 JS 弄坏了。
|
||
# 原来那个「管理页 JS 合法性」阶段只查 token 在不在,查不出语法错 ——
|
||
# 「测过」和「能用」是两件事。
|
||
$stageOut = & python (Join-Path $root "_tests\test_admin_js_syntax.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "管理页 JS 语法" ($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)
|
||
|
||
# ---------------------------------------------------------------- 3b5
|
||
Banner "3b5/7 安装包完整周期(装 -> 验 -> 卸 -> 再验)"
|
||
# 这一步真的装一遍、再卸一遍。踩过的四个卡死 bug 全靠它才发现:
|
||
# input() 无 stdin 阻塞 / Popen 不重定向 stdio /
|
||
# taskkill 把自己杀了 / 关不掉管理员权限的旧实例导致安装回滚。
|
||
# 注意:需要管理员权限,且会真的往 Program Files 装东西。
|
||
$stageOut = & python (Join-Path $root "_build\test_installer_cycle.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "安装包完整周期" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3b6
|
||
Banner "3b6/7 网页响应头(Content-Type / HEAD)"
|
||
# 踩过的坑:重做管理页时 _send() 没传 content_type,
|
||
# 默认成了 application/json —— 浏览器把整页 HTML 当纯文本显示,
|
||
# 用户看到的是一屏源码。
|
||
# 更糟的是当时没发现:只验了 HTTP 200 和字节数,两个都对。
|
||
# **200 不等于页面能看。**
|
||
$stageOut = & python (Join-Path $root "_build\test_response_headers.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "网页响应头" ($LASTEXITCODE -eq 0)
|
||
|
||
# ---------------------------------------------------------------- 3b7
|
||
Banner "3b7/7 对外材料清洁度(不许出现管理后台)"
|
||
# 用户规矩:对外公开的内容里只能提官网,不能提中转管理后台。
|
||
# 文档没人看,所以把规矩变成会红的断言。
|
||
$stageOut = & python (Join-Path $root "_build\test_public_clean.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)
|
||
|
||
# ---------------------------------------------------------------- 3c2
|
||
Banner "3c2/7 中转删除设备(含孤儿绑定自愈)"
|
||
# 这个测试原来**不在套件里**(僵尸),所以一直是红的没人知道:
|
||
# * 它抓的是 get("/"),而管理页早就搬到 /zhongzhuanzhan 了
|
||
# -> 后面三条"找不到删除按钮"
|
||
# * 它断言"孤儿绑定会挡着配对码",但那个行为已经被**有意修好**了
|
||
# (snapshot() 现在会把指向已不存在设备的码当废码扔掉并落盘)
|
||
# -> 用户报过的"设备下线了码还占着"问题已经不需要手动清场
|
||
# 修好之后 31/31 通过,收进套件。顺带把「孤儿自愈」也纳入回归。
|
||
$stageOut = & python (Join-Path $root "_tests\test_admin_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)
|
||
|
||
# ---------------------------------------------------------------- 3e
|
||
Banner "3e/7 管理页设备控制(接入开关 + 改配对码)"
|
||
# 两个功能各自最容易写错的地方:
|
||
# * 判断"新设备"必须按 deviceId,不能按配对码 ——
|
||
# 否则管理员给老设备改过码之后,老设备会被开关挡在外面
|
||
# * 改码按钮不能带 data-device ——
|
||
# 现有"单台删除"按 button.danger[data-device] 绑,
|
||
# 带上就会被当成删除按钮,点改码变删设备
|
||
$stageOut = & python (Join-Path $root "_tests\test_admin_device_control.py") 2>&1
|
||
Show-Fails $stageOut
|
||
Record "管理页设备控制" ($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 $true
|
||
} else {
|
||
# 先把退出码存下来再走管道。
|
||
# 直接 & java ... | Select-Object 然后读 $LASTEXITCODE 是不可靠的 ——
|
||
# 实测测试明明全过了,Record 却记成 FAIL(管道会干扰 $LASTEXITCODE)。
|
||
$javaOut = & $java "-Dfile.encoding=UTF-8" -cp $out JavaClientTest "127.0.0.1" 8799 "246813" "stub" 2>&1
|
||
$javaCode = $LASTEXITCODE
|
||
$javaOut | Select-Object -Last 4
|
||
Record "Java <-> 桩服务端" ($javaCode -eq 0) $true
|
||
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)"
|
||
|
||
# 同上:先存退出码,别让管道把它冲掉
|
||
$javaOut = & $java "-Dfile.encoding=UTF-8" -cp $out JavaClientTest "127.0.0.1" 8801 $cfg.pairing_code "real" 2>&1
|
||
$javaCode = $LASTEXITCODE
|
||
$javaOut | Select-Object -Last 4
|
||
Record "Java <-> 打包 EXE" ($javaCode -eq 0) $true
|
||
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
|