测试与发布证据

Android 本地模型文件与状态事务测试

测试当前 ModelDownloader/ModelManager 的文件、下载、状态与加载边界;明确仓库没有 ModelRepository 类,不声称已有可注入网络/文件系统接口。

审核状态: 内容已校验;中文稿供用户审核 · 2026-10-06 · Fact IDs: COVE-DOWNLOAD-01, COVE-MODEL-02

直接回答

测试本地模型持久化边界时,应围绕应用实际存在的组件,而不是假设每个代码库都有名为 ModelRepository 的类。本仓库中 ModelDownloader 负责文件传输并接收存储目录;ModelManager 协调模型选择、存储查找、下载状态、加载和引擎获取。这些组件相关但职责不同。文章可以把 repository 作为一般架构概念,但必须写出当前真实所有者,不能声称构造函数已经暴露可注入的文件系统或网络 provider。 先准备与用户数据隔离的存储夹具。ModelDownloader 构造函数接收 File 目录,因此临时测试目录可用于验证目标文件和临时文件位置,不触碰共享模型目录。用测试 ModelConfig 固定文件名和期望大小,再逐条断言每种结果后的文件状态。当前下载器先写 .tmp,发出 Progress,再尝试重命名为最终文件名;它没有检查 renameTo 返回值就发出 Completed。因此测试必须核对最终文件和字节,不能仅从终止事件推断文件放置或完整性验证通过。 网络与平台边界都要如实描述。ModelDownloader 内部创建 HttpURLConnection,同时使用 Android 的 StatFs 和 Log;构造函数接受 File 目录,不代表收集其下载 Flow 就是无需 Android 的纯 JVM 单测。普通 JVM 可以验证提取出来的文件管理逻辑;执行当前下载器则需 Android 感知的测试环境,或明确记录的平台替身。该类没有 DownloadProvider 注入参数,不能假造构造函数。窄 connection factory 是可提议的代码改进;受控本地 HTTP server 是适当环境中的集成测试选项。文件系统、Android 空间查询与传输行为应分开核验,并写明各个边界如何提供。 下载结果应与现有代码一致。当前路径会预检空间、写入临时制品、报告字节进度、对已有部分文件请求 HTTP Range,并可在连接超时或 DNS 失败后尝试配置的镜像。只有 ModelConfig 中存在预期摘要时才会做 SHA-256 校验;COVE-MODEL-02 表明当前 Gemma 4 E2B 摘要字段为空,因此不能声称当前制品已哈希验证。可以用配置了已知摘要的测试制品覆盖分支,但这不表示随仓库提供的模型配置有摘要。摘要不匹配时,当前逻辑会删除临时文件并发出错误,应把这一实际行为写进测试结果。 ModelManager 状态测试不应只断言枚举存在。其 StateFlow 初值是 NOT_DOWNLOADED;下载和加载方法会更新状态;查找模型会检查共享目录和应用专属目录。loadModel 在互斥锁内运行;捕获到任何 Exception 时会删除所选模型文件、清空引擎引用并回到 NOT_DOWNLOADED。该删除路径不能证明文件被摘要校验判为损坏,错误也可能来自 native 初始化。测试结果要把现有实现与未来更安全的修复策略分开,例如非完整性类加载失败后是否应保留已知可用制品。 不要夸大并发或原子性。loadModel 和 setVariant 使用同一把 loadMutex;release 并没有获取这把锁后再关闭引擎,而是直接调用具体引擎 close 并设为 DOWNLOADED。ModelManager 通过当前存储目录创建单独的 downloader;startBackgroundDownload 会先检查 StateFlow 是否已为 DOWNLOADING,但源码不足以证明跨实例或跨进程的所有竞态都安全。测试可以断言一个实例在特定调度下的顺序,同时说明其他所有者的并发行为未测。StateFlow 保存当前状态,晚订阅者可能错过快速中间状态;应分别断言当前值与用户可见结果,不强求每个观察者看到所有过渡。 可从真实所有者的方法建立转换矩阵:没有制品、最终文件在各支持目录、临时下载、进度、完成、下载错误、变种切换、加载成功、加载失败、release 和删除。逐项记录初始文件清单、调用、收集到的 DownloadState、ModelManager 当前状态、剩余文件和下一项可用操作。模型文件、部分传输和用户创建数据要放在不同夹具里。除非测试还检查独立存储,模型清理用例不能声称聊天或照片数据得到了保留。 错误断言应保留因果。超时和 DNS 场景中检查配置的镜像是否实际尝试;其他 HTTP 或文件异常应记录实际终止状态和临时文件残留。摘要测试用已知非空的测试值区分匹配与不匹配;空配置要报告为未做摘要校验。对 ModelManager.loadModel,要明确当前 caught Exception 后会删除所选文件,并把保留旧制品写成待讨论的产品修复策略。不要把宽泛异常捕获伪装成精确损坏诊断或回滚功能。 最终报告应注明被测类、存储夹具、准确 ModelConfig 字段、响应来源、摘要配置、前后文件、收集到的状态和异常类别。临时目录单测能证明夹具内的文件管理;受控 HTTP 测试能证明某条响应路径;两者都不证明任意 Android 存储卷、服务器、设备或模型运行时都相同。如果边界没有依赖注入,就把它作为测试性限制并提出最小接口,而不是在文章里假造 API。

断点续传完成后,本地模型加载失败

测试在临时目录里放置部分模型文件,用受控响应恢复下载并写到最终路径。随后 ModelManager 加载时 native 初始化失败;团队需要报告当前删除行为,但不能称文件已被摘要校验判坏。

决策

分别记录传输完成、摘要是否配置、加载结果、ModelState 和文件删除;旧制品保留只写成产品策略建议,除非代码和测试已证明存在。

预期结果

结果区分传输、可选摘要校验、native 加载和当前恢复行为,不把所有失败归因于文件损坏。

需要收集的证据

只有将以下证据与同一模型文件和测试运行关联起来,结论才可复核:

  • COVE-DOWNLOAD-01:当前 ModelDownloader 有空间预检、临时文件、Range、进度、特定网络错误镜像回退,以及配置 SHA-256 时的可选校验。
  • COVE-MODEL-02:当前 Gemma 4 E2B 配置的 SHA-256 字段为空;不能声称当前发布制品已按哈希验证。

发布结论只对所记录的配置和验收集有效;扩大适用范围需要更多记录。

实施流程

  1. 写明实际组件边界. 注明测试 ModelDownloader、ModelManager、ModelConfig 还是已实现的 repository。当前项目使用前两者;不能把 ModelManager 说成 Repository 类。
  2. 创建隔离存储夹具. 将 ModelDownloader 指向临时目录,固定文件名、期望大小、摘要配置及初始部分/最终文件。不要写入共享模型目录或用户数据。
  3. 控制网络响应来源. 当前 URLConnection 创建过程没有 provider 参数。若需确定传输测试,应新增窄 connection factory 或使用受控本地 server;文件系统断言与网络行为分开。
  4. 逐项断言发射和状态. 收集 DownloadState,并对模型查找、进度、完成、错误、变种、加载、release 和删除观察 ModelManager StateFlow。注意 StateFlow 可能合并晚订阅者看不到的快速状态。
  5. 区分完整性与初始化. 用已知非空的测试摘要覆盖匹配和不匹配。空摘要表示没有做 hash 校验;不能把一般 native 加载异常报告为摘要错误。
  6. 记录清理和限制. 比较前后文件与状态,注明当前加载失败删除行为,并将其与待讨论的保留策略或未验证的跨实例竞态分开。

让每个阶段的状态都可观察。失败信息应指出阶段、模型、运行时和恢复动作,同时避免记录用户私密内容。

验收标准

  • 测试注明具体 downloader/manager API、存储夹具、响应来源、配置元数据、状态观察和剩余文件。
  • 无摘要、摘要匹配、不匹配、下载异常与 native 初始化异常是不同用例,不互相冒充完整性结论。
  • 准确报告当前行为:捕获加载异常会移除所选文件;未实现和测试的回滚或保留策略只能标为建议。

决策对照表

维度选择或证据解读方式
ModelDownloader 文件测试临时目录和收集的 DownloadState可验证实际文件/进度路径;URLConnection 当前没有构造函数注入。
ModelManager 生命周期测试基于 Context 的目录查找、StateFlow、加载、变种、release 和删除检查现有协调器,不证明仓库里存在 ModelRepository 类。
未来 repository 契约测试fake 文件系统和下载 provider 接口在接口引入后有用;不能假装当前实现已能接收它们。

需要避免的失败模式

  • 把 ModelManager 称作 ModelRepository,或虚构当前构造函数支持的 fake 下载 provider/文件系统注入。
  • 把任何初始化异常说成 hash 损坏,或把当前删文件行为误写成旧制品保留与原子回滚。

还要避免静默回退、版本未固定、缺少取消处理,以及把不同配置的结果混在一起下结论。失败运行也应与成功记录一并保存。

最小复现记录

层级记录内容
设备厂商、型号、芯片、内存档位、操作系统版本
软件应用版本和 Git 提交号
模型系列、变体、版本、格式、文件长度、摘要、量化方式
运行时名称、版本、请求后端及实际后端证据
工作负载样例版本、输入摘要、提示词摘要、输出策略
结果完成、失败、取消、回退及保护隐私的诊断信息

发布检查表

  • ☐ 主要搜索问题已回答,且没有未经支持的数字。
  • ☐ 所有模型文件和运行时版本均已固定。
  • ☐ 代表性任务和失败处理策略均有说明。
  • ☐ 生命周期、取消、清理和回退均已测试。
  • ☐ 用户内容与网络边界均有说明。
  • ☐ 结论只适用于被记录的配置。
  • ☐ 新增测试结果时链接到原始方法或证据。

来源与相关证据

中文部署与排错专题见。奇连 AI 端侧专题。

常见问题

GemmaApp 当前有 ModelRepository 类吗?

检查到的 core 代码有 ModelDownloader 和 ModelManager,没有发现 ModelRepository 类。应写真实组件;repository 抽象只能作为可能的后续设计。

ModelManager 加载失败能证明模型文件损坏吗?

不能。当前代码捕获 Exception 后删除所选文件并回到 NOT_DOWNLOADED;这不说明摘要校验发现损坏,也不确定具体失败原因。

SHA-256 为空是否代表模型通过了校验?

不是。它表示下载器没有预期摘要可以比较。应写为未执行摘要验证,而不是已经通过哈希验证;结论还要绑定实际配置和测试制品。