test: add reproducible device verification
This commit is contained in:
+4
-4
@@ -1,7 +1,7 @@
|
||||
# 本地开发环境基线
|
||||
|
||||
> 状态:已从当前主机实测
|
||||
> 最后核验:2026-08-07
|
||||
> 最后核验:2026-08-08
|
||||
> 适用范围:`D:\OPC\brainwave` 的 Android 开发、构建和设备验证
|
||||
|
||||
本文件记录项目当前可用的本地工具链,不是期望环境清单。后续实现必须先使用这里已经确认的能力;需要升级或安装新工具时,应说明原因并在完成后更新本文件。
|
||||
@@ -125,7 +125,7 @@ Android Studio 不是当前环境的可用前提。项目必须先支持 PowerSh
|
||||
| 物理分辨率 | `1080 × 2280` |
|
||||
| 物理密度 | `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. 网络与代理状态
|
||||
|
||||
@@ -154,10 +154,10 @@ Android Studio 不是当前环境的可用前提。项目必须先支持 PowerSh
|
||||
- Android Studio 未安装。
|
||||
- Android Emulator、system image 和 AVD 不可用。
|
||||
- 本机只确认安装了 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 依赖下载成功,但不能据此保证未来网络始终可用。
|
||||
|
||||
已解除的缺口: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)处理。环境缺口不是跳过验证的理由;无法运行的门禁必须在交付报告中准确说明。
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 分阶段实施计划
|
||||
|
||||
> 状态:P-1 v0.3 待视觉复核;P0 Android 壳与本地门禁已完成,真机闭环正在执行;P1 已完成并通过穷举测试;P2 内容契约已建立但授权内容未开始
|
||||
> 状态:P0 Android 壳、本地门禁与三星 API 31 真机闭环已完成;P-1 v0.3 待视觉复核;P1 已完成并通过穷举测试;P2 内容契约已建立但授权内容未开始
|
||||
> 计划原则:先用原型确认高返工成本体验,再锁定确定性领域核心,随后接内容和 UI,最后接网络 AI
|
||||
|
||||
## 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:领域核心
|
||||
|
||||
目标:在纯 Kotlin 中完成并证明三枚铜币算法。
|
||||
|
||||
@@ -35,7 +35,13 @@
|
||||
.\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. 测试层次
|
||||
|
||||
|
||||
@@ -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."
|
||||
@@ -15,6 +15,7 @@ const checkedExtensions = new Set([
|
||||
".md",
|
||||
".mjs",
|
||||
".properties",
|
||||
".ps1",
|
||||
".toml",
|
||||
".xml",
|
||||
".yaml",
|
||||
|
||||
Reference in New Issue
Block a user