KK Camera v0.1.2 测试报告

  • **被测对象**:KK Camera v0.1.2(versionCode 3),工程 `/root/kk-camera/`,APK `/var/www/kk-camera/apk/KKCamera-v0.1.2-debug.apk`
  • **测试日期**:2026-08-21
  • **测试人**:测试工程师(Hermes 子代理)
  • 测试环境

    | 项目 | 值 |

    |---|---|

    | 服务器 | Linux 5.10.134-19.2.al8.x86_64(2G 内存,无模拟器/真机) |

    | JDK | OpenJDK 17(JAVA_HOME=/usr/lib/jvm/java-17-openjdk) |

    | Gradle | 8.5(/opt/gradle-8.5/bin/gradle,--no-daemon) |

    | Android SDK | /opt/android-sdk,build-tools 34.0.0(aapt) |

    | 测试框架 | JUnit 4.13.2(testImplementation,本次新增) |

    | 限制 | 无法做真机/模拟器运行时测试,运行时行为未覆盖 |

    项目值
    服务器Linux 5.10.134-19.2.al8.x86_64(2G 内存,无模拟器/真机)
    JDKOpenJDK 17(JAVA_HOME=/usr/lib/jvm/java-17-openjdk)
    Gradle8.5(/opt/gradle-8.5/bin/gradle,--no-daemon)
    Android SDK/opt/android-sdk,build-tools 34.0.0(aapt)
    测试框架JUnit 4.13.2(testImplementation,本次新增)
    限制无法做真机/模拟器运行时测试,运行时行为未覆盖

    一、JVM 单元测试

    命令:`/opt/gradle-8.5/bin/gradle --no-daemon testDebugUnitTest --console=plain`

    结果:**BUILD SUCCESSFUL in 1m 58s**,9/9 通过(failures=0 errors=0 skipped=0)。

    说明:任务书中的方法签名为 `advise(signal: SceneSignal)`,实际生产代码为 `DecisionEngine.decide(signal)`,且 `SceneSignal` 实际字段为 `faceBrightRatio`/`brightness`/`histogramBins`/`tiltAngleDeg`(非 faceBright/hist/tiltDeg)。测试按真实代码编写。`UpdateChecker.isNewer` 为 private,按要求未改生产代码,使用 Kotlin 反射调用。

    | 用例 | 预期 | 实际 | 结论 |

    |---|---|---|---|

    | DecisionEngine 逆光 → evBias | -0.7 | -0.7 | PASS |

    | DecisionEngine 低光 → evBias>0 且有 ISO 建议 | >0,含 "ISO" | +0.3,"ISO 拉到 800~1600…" | PASS |

    | DecisionEngine 顺光 → evBias | 0 | 0 | PASS |

    | DecisionEngine 正常 → evBias | 0 | 0 | PASS |

    | DecisionEngine 四场景文案非空 | reason/isoSuggestion 非空 | 均非空 | PASS |

    | isNewer("0.1.2","0.1.1") | true | true | PASS |

    | isNewer("1.0","0.9.9") | true | true | PASS |

    | isNewer("0.1.2","0.1.2") | false | false | PASS |

    | isNewer("0.10.0","0.9.0") | true(数值比较) | true | PASS |

    用例预期实际结论
    DecisionEngine 逆光 → evBias-0.7-0.7PASS
    DecisionEngine 低光 → evBias>0 且有 ISO 建议>0,含 "ISO"+0.3,"ISO 拉到 800~1600…"PASS
    DecisionEngine 顺光 → evBias00PASS
    DecisionEngine 正常 → evBias00PASS
    DecisionEngine 四场景文案非空reason/isoSuggestion 非空均非空PASS
    isNewer("0.1.2","0.1.1")truetruePASS
    isNewer("1.0","0.9.9")truetruePASS
    isNewer("0.1.2","0.1.2")falsefalsePASS
    isNewer("0.10.0","0.9.0")true(数值比较)truePASS

    测试文件(本次新建):

  • `app/src/test/java/cn/sunearthl2/kkcamera/DecisionEngineTest.kt`(5 用例)
  • `app/src/test/java/cn/sunearthl2/kkcamera/UpdateCheckerVersionTest.kt`(4 用例)
  • `app/build.gradle.kts` 增加 `testImplementation("junit:junit:4.13.2")`
  • 编译期发现(非阻断):`CrashLogger.kt:20` 警告 "Variable 'dir' is never used"。

    二、APK 静态检查(aapt + unzip)

    命令:`aapt dump badging` / `unzip -l`

    | 检查项 | 预期 | 实际 | 结论 |

    |---|---|---|---|

    | 包名 | cn.sunearthl2.kkcamera | cn.sunearthl2.kkcamera | PASS |

    | versionName | 0.1.2 | 0.1.2 | PASS |

    | versionCode | 3 | 3 | PASS |

    | minSdk | 29 | 29 | PASS |

    | targetSdk | 34 | 34 | PASS |

    | ABI 列表 | arm64-v8a, armeabi-v7a | lib/ 下仅含 arm64-v8a、armeabi-v7a(4 个 .so) | PASS |

    | 权限 | 仅 CAMERA + INTERNET | 源 Manifest 只有 CAMERA+INTERNET,但合并后 APK 额外引入 ACCESS_NETWORK_STATE、WAKE_LOCK、RECEIVE_BOOT_COMPLETED、FOREGROUND_SERVICE、DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION(来自 ML Kit 等三方库 manifest 合并) | WARN |

    | AndroidManifest.xml 在包内 | 存在 | 存在(17792 字节) | PASS |

    | res/xml/file_paths.xml 在包内 | 存在 | 存在(300 字节) | PASS |

    | aapt 解析 | 无错误 | `android:icon` 引用 `@android:drawable/ic_menu_camera` 导致 aapt 报 "attribute value reference does not exist"(框架图标,badging 主体信息仍可读) | WARN |

    检查项预期实际结论
    包名cn.sunearthl2.kkcameracn.sunearthl2.kkcameraPASS
    versionName0.1.20.1.2PASS
    versionCode33PASS
    minSdk2929PASS
    targetSdk3434PASS
    ABI 列表arm64-v8a, armeabi-v7alib/ 下仅含 arm64-v8a、armeabi-v7a(4 个 .so)PASS
    权限仅 CAMERA + INTERNET源 Manifest 只有 CAMERA+INTERNET,但合并后 APK 额外引入 ACCESS_NETWORK_STATE、WAKE_LOCK、RECEIVE_BOOT_COMPLETED、FOREGROUND_SERVICE、DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION(来自 ML Kit 等三方库 manifest 合并)WARN
    AndroidManifest.xml 在包内存在存在(17792 字节)PASS
    res/xml/file_paths.xml 在包内存在存在(300 字节)PASS
    aapt 解析无错误`android:icon` 引用 `@android:drawable/ic_menu_camera` 导致 aapt 报 "attribute value reference does not exist"(框架图标,badging 主体信息仍可读)WARN

    三、更新链路端到端

    | 检查项 | 预期 | 实际 | 结论 |

    |---|---|---|---|

    | GET version.json | HTTP 200 | HTTP/2 200(nginx/1.20.1) | PASS |

    | GET APK | HTTP 200 | HTTP/2 200,content-length 61665659 | PASS |

    | fileSize 一致性 | 与实际字节数一致 | version.json fileSize=61665659 = 服务器本地 `stat` 61665659 = content-length 61665659 | PASS |

    | downloadUrl 有效性 | 域名路径可下载 | `https://kk.sunearthl2.cn/apk/KKCamera-v0.1.2-debug.apk` 返回 200 | PASS |

    | isNewer(0.1.2, 已装0.1.1) 模拟 | true → 提示更新 | python3 复刻同逻辑:installed=0.1.1 → True(会提示更新);installed=0.1.2 → False(不提示) | PASS |

    | version.json Content-Type | application/json | `application/vnd.android.package-archive`(nginx 按扩展名/路径误配) | WARN |

    检查项预期实际结论
    GET version.jsonHTTP 200HTTP/2 200(nginx/1.20.1)PASS
    GET APKHTTP 200HTTP/2 200,content-length 61665659PASS
    fileSize 一致性与实际字节数一致version.json fileSize=61665659 = 服务器本地 `stat` 61665659 = content-length 61665659PASS
    downloadUrl 有效性域名路径可下载`https://kk.sunearthl2.cn/apk/KKCamera-v0.1.2-debug.apk` 返回 200PASS
    isNewer(0.1.2, 已装0.1.1) 模拟true → 提示更新python3 复刻同逻辑:installed=0.1.1 → True(会提示更新);installed=0.1.2 → False(不提示)PASS
    version.json Content-Typeapplication/json`application/vnd.android.package-archive`(nginx 按扩展名/路径误配)WARN

    四、代码审查(grep)

    | 检查项 | 预期 | 实际 | 结论 |

    |---|---|---|---|

    | CrashLogger 在 MainActivity.onCreate 初始化 | 是 | MainActivity.kt:119 `CrashLogger.init(this)`(onCreate 首行,setContentView 之前) | PASS |

    | FileProvider 注册 + authorities | `${applicationId}.fileprovider` | AndroidManifest.xml:18-26,authorities=`${applicationId}.fileprovider`;installApk 使用 `${activity.packageName}.fileprovider`,一致 | PASS |

    | DownloadManager 路径 vs file_paths.xml | external-files-path Download/ 匹配 | `setDestinationInExternalFilesDir(activity, DIRECTORY_DOWNLOADS, fileName)` ↔ file_paths.xml `external-files-path path="Download/"`,一致 | PASS |

    | ML Kit pose-detection 依赖 | 已声明 | `com.google.mlkit:pose-detection:18.0.0-beta5`(注意为 beta 版) | PASS |

    | gradle.properties 低内存四项 | -Xmx1024m / daemon=false / kotlin 800m / parallel=false | 四项全部存在(另含 useAndroidX、nonTransitiveRClass) | PASS |

    检查项预期实际结论
    CrashLogger 在 MainActivity.onCreate 初始化是MainActivity.kt:119 `CrashLogger.init(this)`(onCreate 首行,setContentView 之前)PASS
    FileProvider 注册 + authorities`${applicationId}.fileprovider`AndroidManifest.xml:18-26,authorities=`${applicationId}.fileprovider`;installApk 使用 `${activity.packageName}.fileprovider`,一致PASS
    DownloadManager 路径 vs file_paths.xmlexternal-files-path Download/ 匹配`setDestinationInExternalFilesDir(activity, DIRECTORY_DOWNLOADS, fileName)` ↔ file_paths.xml `external-files-path path="Download/"`,一致PASS
    ML Kit pose-detection 依赖已声明`com.google.mlkit:pose-detection:18.0.0-beta5`(注意为 beta 版)PASS
    gradle.properties 低内存四项-Xmx1024m / daemon=false / kotlin 800m / parallel=false四项全部存在(另含 useAndroidX、nonTransitiveRClass)PASS

    问题清单

    | 编号 | 严重度 | 描述 |

    |---|---|---|

    | P1 | WARN | APK 合并 manifest 引入了 5 项额外权限(ACCESS_NETWORK_STATE、WAKE_LOCK、RECEIVE_BOOT_COMPLETED、FOREGROUND_SERVICE、自定义签名权限),全部来自 ML Kit 等三方库,源码只申请 CAMERA+INTERNET。功能影响低,但与"最小权限"预期不符,可考虑 manifest merger `tools:node="remove"` 收敛 |

    | P2 | WARN | 应用图标使用框架内置 `@android:drawable/ic_menu_camera`,aapt badging 报 icon 属性引用错误;不同 ROM 上显示可能不一致,建议换自有 mipmap 图标 |

    | P3 | WARN | `https://kk.sunearthl2.cn/apk/version.json` 响应 Content-Type 为 `application/vnd.android.package-archive` 而非 `application/json`(nginx 对 /apk/ 路径统一配置了 APK 的 MIME)。当前代码手动解析 JSON 不受影响,仅影响规范性与缓存/中间件行为 |

    | P4 | INFO | ML Kit pose-detection 使用 beta 版本 18.0.0-beta5,建议关注正式版发布后升级 |

    | P5 | INFO | 任务书与代码签名不一致:DecisionEngine 方法为 `decide` 而非 `advise`,SceneSignal 字段命名亦有出入(文档/任务描述需同步) |

    | P6 | INFO | CrashLogger.kt:20 存在未使用变量 `dir` 编译警告;version.json 的 content-length 显示 202 字节与实际 JSON 体一致 |

    编号严重度描述
    P1WARNAPK 合并 manifest 引入了 5 项额外权限(ACCESS_NETWORK_STATE、WAKE_LOCK、RECEIVE_BOOT_COMPLETED、FOREGROUND_SERVICE、自定义签名权限),全部来自 ML Kit 等三方库,源码只申请 CAMERA+INTERNET。功能影响低,但与"最小权限"预期不符,可考虑 manifest merger `tools:node="remove"` 收敛
    P2WARN应用图标使用框架内置 `@android:drawable/ic_menu_camera`,aapt badging 报 icon 属性引用错误;不同 ROM 上显示可能不一致,建议换自有 mipmap 图标
    P3WARN`https://kk.sunearthl2.cn/apk/version.json` 响应 Content-Type 为 `application/vnd.android.package-archive` 而非 `application/json`(nginx 对 /apk/ 路径统一配置了 APK 的 MIME)。当前代码手动解析 JSON 不受影响,仅影响规范性与缓存/中间件行为
    P4INFOML Kit pose-detection 使用 beta 版本 18.0.0-beta5,建议关注正式版发布后升级
    P5INFO任务书与代码签名不一致:DecisionEngine 方法为 `decide` 而非 `advise`,SceneSignal 字段命名亦有出入(文档/任务描述需同步)
    P6INFOCrashLogger.kt:20 存在未使用变量 `dir` 编译警告;version.json 的 content-length 显示 202 字节与实际 JSON 体一致

    总体结论

    **通过(可发布)**。核心逻辑(决策引擎四场景规则、版本号数值比较)、更新链路(version.json 可达、fileSize 与实际字节数完全一致、下载 URL 有效、版本判定正确)、关键静态配置(FileProvider 注册与路径匹配、CrashLogger 初始化、低内存构建配置)全部符合预期,JVM 单元测试 9/9 通过。发现 4 项 WARN(合并权限偏多、图标引用、MIME 配置、beta 依赖)与若干 INFO,均不阻塞发布,建议下个迭代处理。运行时行为(相机、ML Kit 推理、DownloadManager 实际下载安装)因无模拟器/真机未覆盖,建议在真机上补充冒烟测试。