test: add reproducible device verification

This commit is contained in:
QiuSW
2026-08-08 09:19:40 +08:00
parent 730bc528be
commit d82ab836a2
5 changed files with 67 additions and 6 deletions
+4 -4
View File
@@ -1,7 +1,7 @@
# 本地开发环境基线 # 本地开发环境基线
> 状态:已从当前主机实测 > 状态:已从当前主机实测
> 最后核验:2026-08-07 > 最后核验:2026-08-08
> 适用范围:`D:\OPC\brainwave` 的 Android 开发、构建和设备验证 > 适用范围:`D:\OPC\brainwave` 的 Android 开发、构建和设备验证
本文件记录项目当前可用的本地工具链,不是期望环境清单。后续实现必须先使用这里已经确认的能力;需要升级或安装新工具时,应说明原因并在完成后更新本文件。 本文件记录项目当前可用的本地工具链,不是期望环境清单。后续实现必须先使用这里已经确认的能力;需要升级或安装新工具时,应说明原因并在完成后更新本文件。
@@ -125,7 +125,7 @@ Android Studio 不是当前环境的可用前提。项目必须先支持 PowerSh
| 物理分辨率 | `1080 × 2280` | | 物理分辨率 | `1080 × 2280` |
| 物理密度 | `480 dpi` | | 物理密度 | `480 dpi` |
该设备满足 `minSdk=26`,可用于 `adb` 安装、手工验收和 `connectedDebugAndroidTest`。测试脚本不得硬编码序列号;设备可能随时断开,环境独立门禁必须单独可运行。本机 `PATH` 还存在 `D:\Portable\adb\adb.exe`,曾运行旧版 ADB server;项目设备命令应显式使用 `$env:ANDROID_HOME\platform-tools\adb.exe`,避免与 SDK Platform Tools 37.0.0 争用 server。ADB server 重启后若接口消失,应先保持手机解锁并重新确认 USB 调试授权,不应把“Windows 能看到 USB 复合设备”误当作 ADB 已连接。 该设备满足 `minSdk=26`,可用于 `adb` 安装、手工验收和仪器测试。测试脚本不得硬编码序列号;设备可能随时断开,环境独立门禁必须单独可运行。本机 `PATH` 中的 `D:\Portable\adb\adb.exe` 为 ADB 1.0.32,SDK Platform Tools 37.0.0 提供 ADB 1.0.41;两者会争用 5037 端口并互相重启 server。本轮三星设备只在便携 ADB server 下稳定枚举,SDK ADB 重启后设备接口消失,因此真机门禁通过 `run-connected-tests.ps1 -AdbPath` 显式选择单一 ADB。不要同时启动两个 server,也不要把“Windows 能看到 USB 复合设备”误当作 ADB 已连接。
## 8. 网络与代理状态 ## 8. 网络与代理状态
@@ -154,10 +154,10 @@ Android Studio 不是当前环境的可用前提。项目必须先支持 PowerSh
- Android Studio 未安装。 - Android Studio 未安装。
- Android Emulator、system image 和 AVD 不可用。 - Android Emulator、system image 和 AVD 不可用。
- 本机只确认安装了 Android Platform 34 / Build Tools 34.0.0。 - 本机只确认安装了 Android Platform 34 / Build Tools 34.0.0。
- `PATH` 中旧便携 ADB 与 SDK ADB 可能争用 server;设备接口消失时需要手机端重新授权或重新连接。 - `PATH` 中 ADB 1.0.32 与 SDK ADB 1.0.41 会争用 server;当前三星设备的可重复门禁需要显式选择便携 ADB,后续应评估统一驱动和 Platform Tools。
- 代理已配置;本轮 Maven 依赖下载成功,但不能据此保证未来网络始终可用。 - 代理已配置;本轮 Maven 依赖下载成功,但不能据此保证未来网络始终可用。
已解除的缺口:Gradle Wrapper、Version Catalog、根 `AGENTS.md`、CI、正式 Android 身份和 application 壳均已建立;`assembleDebug`、13 个 debug 单元测试、`lintDebug` 与测试 APK 编译已在 JDK 17 上通过。`verifyLocal` 现已包含 Android lint 与 debug APK,设备测试仍按环境单独执行。 已解除的缺口:Gradle Wrapper、Version Catalog、根 `AGENTS.md`、CI、正式 Android 身份和 application 壳均已建立;`assembleDebug`、13 个 debug 单元测试、`lintDebug` 与测试 APK 编译已在 JDK 17 上通过。2026-08-08,`app-debug.apk` 已安装并冷启动到 `MainActivity`,Compose 仪器测试 `MainActivityTest` 以 `OK (1 test)` 通过,首页与结果页语义树和截图人工复核通过,未发现应用崩溃日志。
这些缺口分别由[实施计划](implementation-plan.md)的 P0 和[质量门禁](quality-gates.md)处理。环境缺口不是跳过验证的理由;无法运行的门禁必须在交付报告中准确说明。 这些缺口分别由[实施计划](implementation-plan.md)的 P0 和[质量门禁](quality-gates.md)处理。环境缺口不是跳过验证的理由;无法运行的门禁必须在交付报告中准确说明。
+3 -1
View File
@@ -1,6 +1,6 @@
# 分阶段实施计划 # 分阶段实施计划
> 状态:P-1 v0.3 待视觉复核;P0 Android 壳与本地门禁已完成,真机闭环正在执行;P1 已完成并通过穷举测试;P2 内容契约已建立但授权内容未开始 > 状态:P0 Android 壳、本地门禁与三星 API 31 真机闭环已完成;P-1 v0.3 待视觉复核;P1 已完成并通过穷举测试;P2 内容契约已建立但授权内容未开始
> 计划原则:先用原型确认高返工成本体验,再锁定确定性领域核心,随后接内容和 UI,最后接网络 AI > 计划原则:先用原型确认高返工成本体验,再锁定确定性领域核心,随后接内容和 UI,最后接网络 AI
## 1. 依赖图 ## 1. 依赖图
@@ -62,6 +62,8 @@ P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制原
- 开发验证应用可在授权真机启动,并从首页导航到已知领域夹具结果页。 - 开发验证应用可在授权真机启动,并从首页导航到已知领域夹具结果页。
- 没有生产服务密钥或真实内容。 - 没有生产服务密钥或真实内容。
完成证据(2026-08-08):`verifyLocal`、13 个 debug 单元测试、`lintDebug`、主/测试 APK 构建均通过;调试 APK 在 Samsung API 31 设备冷启动到 `MainActivity`,`MainActivityTest` 真机执行 1/1 通过,并验证首页进入“复 24 → 坤 2、初爻动”结果页。因本机 ADB 1.0.32/1.0.41 server 冲突,设备门禁使用仓库脚本显式选择可枚举该设备的 ADB,未改写 SDK 工具。
## 4. P1:领域核心 ## 4. P1:领域核心
目标:在纯 Kotlin 中完成并证明三枚铜币算法。 目标:在纯 Kotlin 中完成并证明三枚铜币算法。
+7 -1
View File
@@ -35,7 +35,13 @@
.\gradlew.bat connectedDebugAndroidTest .\gradlew.bat connectedDebugAndroidTest
``` ```
若采用不同格式化插件,命令可以调整,但必须在本文件和 CI 同步更新。`connectedDebugAndroidTest` 需要模拟器或设备,继续与环境独立的 `verifyLocal` 分开;`verifyLocal` 通过证明 APK 已构建,但不等于真机测试已经通过。 若本机存在两个不兼容的 ADB server,可在只保留一个 server 的前提下使用等价的可重复入口:
```powershell
.\scripts\run-connected-tests.ps1 -AdbPath <能够稳定识别设备的 adb.exe>
```
该脚本重新构建主/测试 APK、检查授权状态、覆盖安装两者并直接调用同一个 `AndroidJUnitRunner`;失败、零测试或缺少 `OK` 结果都会使脚本失败。若采用不同格式化插件,命令可以调整,但必须在本文件和 CI 同步更新。设备测试继续与环境独立的 `verifyLocal` 分开;`verifyLocal` 通过证明 APK 已构建,但不等于真机测试已经通过。
## 3. 测试层次 ## 3. 测试层次
+52
View File
@@ -0,0 +1,52 @@
[CmdletBinding()]
param(
[string]$AdbPath = (Join-Path $env:ANDROID_HOME "platform-tools\adb.exe")
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repositoryRoot = Split-Path -Parent $PSScriptRoot
$gradleWrapper = Join-Path $repositoryRoot "gradlew.bat"
$mainApk = Join-Path $repositoryRoot "app\build\outputs\apk\debug\app-debug.apk"
$testApk = Join-Path $repositoryRoot "app\build\outputs\apk\androidTest\debug\app-debug-androidTest.apk"
$instrumentation = "net.opcapp.flash.test/androidx.test.runner.AndroidJUnitRunner"
if (-not (Test-Path -LiteralPath $AdbPath -PathType Leaf)) {
throw "ADB executable not found: $AdbPath"
}
& $gradleWrapper :app:assembleDebug :app:assembleDebugAndroidTest --no-daemon --console=plain
if ($LASTEXITCODE -ne 0) {
throw "Android app or test APK build failed."
}
$deviceState = (& $AdbPath get-state 2>$null)
if ($LASTEXITCODE -ne 0 -or $deviceState.Trim() -ne "device") {
throw "No authorized Android device is available through: $AdbPath"
}
& $AdbPath install -r $mainApk
if ($LASTEXITCODE -ne 0) {
throw "Main APK installation failed."
}
& $AdbPath install -r $testApk
if ($LASTEXITCODE -ne 0) {
throw "Test APK installation failed."
}
$instrumentationOutput = & $AdbPath shell am instrument -w -r $instrumentation 2>&1
$instrumentationExitCode = $LASTEXITCODE
$instrumentationOutput | Write-Output
$joinedOutput = $instrumentationOutput -join "`n"
if (
$instrumentationExitCode -ne 0 -or
$joinedOutput -match "FAILURES!!!" -or
$joinedOutput -notmatch "OK \(\d+ tests?\)"
) {
throw "Connected Android instrumentation tests failed."
}
Write-Output "Connected Android instrumentation tests passed."
+1
View File
@@ -15,6 +15,7 @@ const checkedExtensions = new Set([
".md", ".md",
".mjs", ".mjs",
".properties", ".properties",
".ps1",
".toml", ".toml",
".xml", ".xml",
".yaml", ".yaml",