MVP 可勾选测试契约表(Tool / ToolFlow · v1.9)
www 工作副本(2026-10-21 Asia/Shanghai) 双轨:正文迁自 mvdocs test-contracts。 实现边界:表内勾选保持未勾。外形对拍在薄摘 TC-MEM · TC-SESS · TC-ACQ · TC-OUT · TC-DISP。smoke 与 Controls.Tests 不等于本表通过。 关联:toolflow · examples-scenarios
MVP 可勾选测试契约表(Tool / ToolFlow · v1.9)
目标设计草案,尚未实施。总体边界见设计概述;编排权威见 toolflow.md( v1.9)。
定调(勿回退):Tool / ToolFlow;命名空间
vyntek::toolflow/vyntek::imaging; ToolId; DiagnosticCode。不混 MPF / 旧 DAG;不写第二执行引擎;不写覆盖率 KPI。验收以本表勾选 + 单测/headless 为准。Alive 拍板(R4):将 toolflow §10.1 / §3.4 H1–H10 / §7.4.6 Diagnostic / 场景 S01–S08 收成「可勾选测试契约」:每条 = 前置 · 动作 · 期望 code/状态 · 对应章节。§10.1 保留摘要并链到本文;场景文提供骨架,不以场景代替本表勾选正文。
Alive 拍板(R6):场景 S09a–d 补齐嵌套/Encapsulation、SourceDisabled、InvalidBinding、IoFailure、停相机≠自动 Cancel、可选 §9.2 两流串联;本表更新场景映射并增
TC-RUN-06/TC-RUN-07。不改 v1.9 语义与 Diagnostic 码表。Alive 拍板(Arden · R9):将 output-inspection.md T10 集合事件与观察不变量收成可勾选 TC-OUT-*;场景 S10 对齐。不改 T10 事件闭集与 toolflow v1.9 码表/语义。勿与 T15 开放风险 R9(编辑 UI)撞号(对照见 glossary.md §2)。
Alive 拍板(Arden · R10):将 camera-acquisition.md / session-lifecycle.md 已拍板散文收成可勾选 TC-ACQ-* + TC-SESS-*;薄场景 S11 对齐。不改 Diagnostic / T9 停止矩阵语义与 toolflow v1.9 码表。勿与 T15 开放风险 R10(C++ UI·已关闭)撞号(对照见 glossary.md §2)。
Alive 拍板(Arden · R11):将 memory-management.md 已拍板散文(工厂接管 / 最后引用 / pin / 授予·取消竞争 / 初始化·Invalid / 归还隔离)收成可勾选 TC-MEM-*;薄场景 S12 对齐。不改 Diagnostic / T9 停止矩阵与 toolflow v1.9。 T15 无 R11——本回合编号独立(对照见 glossary.md §2)。
Alive 拍板(Arden · R12):将 image-display.md 已拍板散文(登记≠授予 / Share 不接管 / Retain·Snapshot·Detach / 回执四信号 / 失败保留 / 四格式取样 / ROI·负步长 / 卸载·重挂·context / 预算失败不丢旧图 / 显示只观察)收成可勾选 TC-DISP-;薄场景 S13 对齐。不改 Diagnostic / T9 停止矩阵与 toolflow v1.9。显示契约仅 C#*;勿与 T15 开放风险 R10(C++ UI·已关闭)互替(对照见 glossary.md §2)。
关联: toolflow.md §3.4 / §7.4.3 / §7.4.6 / §7.6 / §9 / §10.1 · examples-scenarios.md(S01–S13) · output-inspection.md( T10 · Arden · R9) · camera-acquisition.md / session-lifecycle.md( Arden · R10 · TC-ACQ / TC-SESS) · memory-management.md( Arden · R11 · TC-MEM) · image-display.md( Arden · R12 · TC-DISP;仅 C#) · host-integration.md( R7 · S07 同核;两端都停;TC-CTL-05 交叉) · sketch/README.md(R5 伪树·示意对照,不以碎片代替本表勾选) · glossary.md( R8;Arden · R9 / R10 / R11 / R12 撞号) · validation-and-roadmap.md · cpp-headers.md · csharp-binding.md
用法
| 约定 | 说明 |
|---|---|
| 勾选列 | 实现期单测/headless 通过后勾 [x];文档侧默认全 [ ] |
| 期望列 | 写 DiagnosticCode 名,或明确状态(ok / Outputs 可读 / Access=ReadOnly 等) |
| Severity | 未另写时,失败码默认 Severity::Error;成功路径允许空诊断或仅 Info |
| ID | TC-* 供单测命名与回链;S0x 为场景骨架映射,不是第二套验收正文 |
| 非目标 | 见文末「非目标门禁」;不得误交付脚本/MPF/DAG/产线 IO |
1. 注册 / AddTool / fake 注册
| ID | 勾 | 前置 | 动作 | 期望 code / 状态 | 对应章节 | 场景 |
|---|---|---|---|---|---|---|
| TC-REG-01 | [ ] | 空 ToolRegistry | register_fake_tools(或等价)注册 §7.6.3 至少 fake_acq / fake_pm_align / fake_passthrough / fake_fail_once | 均可 Create(type_id);实例非空 | toolflow §7.6 / §10.1 对象模型 | S08 |
| TC-REG-02 | [ ] | 已注册某 type_id | 再次 Register 同 type_id | 拒绝(生产默认);不覆盖既有工厂 | §7.6.2 | S08 |
| TC-REG-03 | [ ] | Registry 无该 id | Create("type_id_not_registered", …) 或 Load 含未知 type | DiagnosticCode::UnknownTypeId;无半初始化成功流 | §7.4.6 / §7.6.2 / §10.1 | S04 / S08 |
| TC-REG-04 | [ ] | fake 已注册 | flow->AddTool(registry.Create(…)) | 所有权在 ToolFlow;registry 不保留实例;tool_id 流内稳定 | §7.4.1 / §7.6.2 | S01 |
| TC-REG-05 | [ ] | 可定义终端 | AddInput / AddOutput(名称、TypeDesc、稳定 id) | 终端可 Link;嵌套流仅经终端对外(封装) | §3 / §7.4.1 / §10.1 | S01 / S08 / S09a / S09d |
| TC-REG-06 | [ ] | fake 与 builtin 同表 | 用同一 Registry Create / Load | 共用工厂契约;禁止反序列化旁路注册表 | §7.6 / §10.1(T7) | S08 |
2. Link / Validate
| ID | 勾 | 前置 | 动作 | 期望 code / 状态 | 对应章节 | 场景 |
|---|---|---|---|---|---|---|
| TC-LNK-01 | [ ] | 两工具类型兼容的端子 | Link 合法边 | 成功;不留下半条边;Validate 无 Error | §4.2 / §5 / §7.4.2 | S02 |
| TC-LNK-02 | [ ] | 类型不兼容(含图像跨格式) | Link 或 Validate | DiagnosticCode::TypeMismatch;拒绝建链或 Validate 失败 | §5 / §7.4.6 | S02(负例) |
| TC-LNK-03 | [ ] | 源在目标列表之下 | Link 或 Validate | DiagnosticCode::OrderViolation | §4.1 / §7.4.6 / §10.1 | S02 |
| TC-LNK-04 | [ ] | 外层绑内层私有属性 | Validate / Link | DiagnosticCode::Encapsulation | §7.4.6 / §9.3 / §10.1 | S09a |
| TC-LNK-05 | [ ] | 端点缺失 / 方向非法 | Validate / Link / Load | DiagnosticCode::InvalidBinding;不留残链 | §7.4.6 / §6.6.4 | S09b(主断言;原 S04 附带升格) |
| TC-LNK-06 | [ ] | 源工具 disabled;下游依赖其输出 | Run 拉值(MVP 严格) | DiagnosticCode::SourceDisabled;下游不得假成功 | §4.3 / §7.4.2 / §7.4.6 / §10.1 | S09b(S02 负例指针) |
| TC-LNK-07 | [ ] | 已知 type_id,缺必填配置属性 | Validate(Load 后或内存建流) | DiagnosticCode::MissingRequiredProperty;无静默默认使校验通过 | §6.5 / §7.4.6(T8) / §10.1 | S04 |
| TC-LNK-08 | [ ] | 合法流 | Validate | 无 Severity::Error;可进入 Run | §7.4.4 / §10.1 | S01 / S02 |
3. 顺序 Run / Outputs
| ID | 勾 | 前置 | 动作 | 期望 code / 状态 | 对应章节 | 场景 |
|---|---|---|---|---|---|---|
| TC-RUN-01 | [ ] | 单工具合法流(如 fake_constant) | RunSession::Run | r.ok==true;Outputs 可读且值符合配置/链接 | §7.4 / §9.4 / §10.1 | S01 |
| TC-RUN-02 | [ ] | 多工具合法链(列表序) | 一次 Run | 自上而下执行;链接赋值生效;无「边就绪即跑」;Outputs 符合链 | §4.1 / §4.2 / §9.1 | S02 |
| TC-RUN-03 | [ ] | §7.6.3 fake 拼装(Acq→PMAlign→Passthrough/Constant) | headless Run | ok;Outputs / 中间属性可读;等价 §9.1 | §7.6.3 / §9.1 / §10.1 | S03 |
| TC-RUN-04 | [ ] | 同一流连续两次 Run | 第二次 Run | 两次独立执行;无环边;第二次覆盖上次 Outputs | §4.1 / §10.1 | S07(批跑) |
| TC-RUN-05 | [ ] | 观察订阅已挂(可选) | Run 成功/失败 | 观察不驱动下一次 Run;失败保留上次成功(T10) | output-inspection / §10.1 非编排驱动;升格正文见 TC-OUT-05 / TC-OUT-09(Arden · R9) | S03 / S10 |
| TC-RUN-06 | [ ] | 内层合法流已 Validate;外层 AddTool 嵌套 | 外层只链内层终端后 Run | ok;Outputs 可读;嵌套 Run=子流完整列表序;绑私有属性见 TC-LNK-04 | §4.3 / §9.3 / §10.1 | S09a |
| TC-RUN-07 | [ ] | 两子流(Locate/Defect)各有终端 | 外层列表序串联终端后 Run | ok;先 Locate 再 Defect;只链终端;非 DAG | §9.2 / §4.1 / §10.1 | S09d(可选) |
4. Cancel / 失败中止 / Busy
| ID | 勾 | 前置 | 动作 | 期望 code / 状态 | 对应章节 | 场景 |
|---|---|---|---|---|---|---|
| TC-CTL-01 | [ ] | 流含 fake_cancel_check + 后续工具 | Run 中 RequestCancel(协作式) | DiagnosticCode::Cancelled;r.ok==false;当前工具合作返回后不再启动后续 | §4.3 / §7.4.3(T9) / §7.4.6 / §10.1 | S06 |
| TC-CTL-02 | [ ] | abort_on_tool_failure=true;中途 fake_fail_* | Run | DiagnosticCode::Aborted;failed_tool_id 指向失败工具;后续不 Run | §4.3 / §7.4.3 / §10.1 | S06 |
| TC-CTL-03 | [ ] | 同 RunSession 已有 Run 在途 | 第二次 Run | DiagnosticCode::Busy;首次不受损;不排队 | §7.4.3 / §7.4.6 / §10.1 | S06 |
| TC-CTL-04 | [ ] | Run 在途 | 宿主停相机 | 不自动 Cancel 在途 Run | §7.4.3 停止矩阵 / session-lifecycle | S09c(独立步骤;S06 附注) |
| TC-CTL-05 | [ ] | Run 在途或已结束 | 关闭 ImageDisplay / 显示宿主 | 显示只观察;不停编排 / 不 Cancel Run | §7.4.3 / csharp-binding / image-display | S05 / S06 / S07 |
5. Load / SchemaTooNew / UnknownTypeId / 序列化
| ID | 勾 | 前置 | 动作 | 期望 code / 状态 | 对应章节 | 场景 |
|---|---|---|---|---|---|---|
| TC-SER-01 | [ ] | 合法流已 Save | Load → 结构一致 → 再 headless Run | Load ok;序/终端/链接/禁用/参数一致;Run ok | §6.3–6.6 / §10.1 | S04 / S07 |
| TC-SER-02 | [ ] | .avtf 的 schema_version 高于加载器 | Load | DiagnosticCode::SchemaTooNew;严格失败 | §6.5 / §7.4.6(T8) / §10.1 | S04 |
| TC-SER-03 | [ ] | 文档含未知 type_id | Load | DiagnosticCode::UnknownTypeId;无占位成功 | §6.5 / §7.4.6 / §10.1 | S04 |
| TC-SER-04 | [ ] | 缺或坏 component_version;type 已知 | Load | 不拦 Load(与 schema_version 门禁独立) | §6.5 / §6.6(T8) | S04 |
| TC-SER-05 | [ ] | 含未知可选字段 | Load | 成功或带 Warning/Info;字段保留不剥掉 | §6.5 / §10.1 | (单测) |
| TC-SER-06 | [ ] | 已 Run 过的流 | 默认 Save | 运行时态(LastRun、注入值)不进配方;金样图不默认内嵌 | §6.3 / §10.1 | S03 / S07 |
| TC-SER-07 | [ ] | 文件不可读 / 截断 | Load / Save | DiagnosticCode::IoFailure | §7.4.6 | S09c |
6. 图像句柄交接 H1–H10
断言细节以 toolflow §3.4 表为准;本表供 §10.1 / 场景勾选对照。链接 ≠ DAG。
| ID | 勾 | 前置 | 动作 | 期望 code / 状态 | 对应章节 | 场景 |
|---|---|---|---|---|---|---|
| TC-H1 | [ ] | 工具持有 WritableView 写毕 | Complete;再对同一 writer Complete/GetPixelRow | 再写失败(Disposed/InvalidOperation);未 Complete 就 Dispose → Invalid,不得作下游输入 | §3.4 H1 | S05 |
| TC-H2 | [ ] | 写毕待发布 | CompleteAndOpenRead → ShareImage | 原子边界内无第二写者;返回 ReadOnly;access()==ReadOnly | §3.4 H2 | S05 / S08 |
| TC-H3 | [ ] | 已有稳定 ReadOnlyView | ShareImage | 新句柄 Access=ReadOnly;OpenWrite → NotSupported;同 BufferId(不因 Share 拷像素) | §3.4 H3 | S05 / S03 |
| TC-H4 | [ ] | 源已是 ReadOnly 句柄 | Share 得第二句柄 | 两句柄皆 ReadOnly;独立关闭;OpenWrite 拒绝 | §3.4 H4 | S05 |
| TC-H5 | [ ] | 源为 ReadWrite 根 | 仅 Share(未再 Complete/ShareImage) | 不得冒充稳定 ReadOnly 下游输入;校验/Run 可拒绝不稳定交接 | §3.4 H5 | S05(负例单测) |
| TC-H6 | [ ] | 合法图像链接 | Run 前链接赋值写入下游输入 | 目标输入 access()==ReadOnly;OpenWrite 立即拒绝;列表序不变 | §3.4 H6 / §4.2 | S02 / S03 / S05 |
| TC-H7 | [ ] | 输出已交下游且下游将/正 Run | 生产者再 OpenWrite/改已发布存储 | 被稳定读引用挡住或失败;下游两次 OpenRead 所见代次一致 | §3.4 H7 | S05 |
| TC-H8 | [ ] | 输入 ReadOnly 需改像素 | Clone → OpenWrite → Complete(…) → 再发布 | 新 BufferId 可写;源仍只读;失败不留半份可交接输出 | §3.4 H8 | S05 / S08 |
| TC-H9 | [ ] | ReadOnly 根上 ROI | GetRegion 得 ROI 句柄;企图 OpenWrite | ROI ReadOnly;写入拒绝;不得把根退化为可写 | §3.4 H9 | S05 |
| TC-H10 | [ ] | 工具失败 / 未 Complete;abort_on_tool_failure=true | Run 失败路径 | 下游拉值不得得假成功 ReadOnly;后续工具不 Run(与 TC-CTL-02 一致) | §3.4 H10 / §4.3 | S06 / S08 |
7. 输出观察 TC-OUT-*(T10 · Arden · R9)
事件闭集与语义权威见 output-inspection.md( T10);本表只做可勾选断言。不改事件名/码表。对外标识 ToolId。场景骨架 S10。观察侧断言与编排 Run 契约交叉时,以本表 +
TC-RUN-05互指为准。
| ID | 勾 | 前置 | 动作 | 期望 code / 状态 | 对应章节 | 场景 |
|---|---|---|---|---|---|---|
| TC-OUT-01 | [ ] | KeepLatest;工具成功且输出集合非空 | 一次 Run 后观察面收到集合事件 | 发 SnapshotReplaced;当前可打开集合身份已更新;不得静默跳过 | output-inspection §集合级事件 | S10 |
| TC-OUT-02 | [ ] | KeepLatest;工具成功且输出集合为空 | 一次 Run | 必须发 EmptySnapshot;禁止静默无事件;不得继续把上次非空集合标为「当前」 | output-inspection(T10 空快照) | S10 |
| TC-OUT-03 | [ ] | KeepLatest;保留/复制失败或配额不足(观察侧) | 业务工具仍可成功并完成路由 | 观察面发 Unavailable;不得把旧成功集合冒充为本轮新结果;业务 Outputs 路由不受观察失败改写 | output-inspection | S10 |
| TC-OUT-04 | [ ] | 已保留集合;触发会话淘汰/预算回收/策略关闭 | 淘汰已保留集合 | 发 Evicted;此后对该身份 TryOpenLatest 不可用 | output-inspection | S10 |
| TC-OUT-05 | [ ] | 已有上次成功快照;本轮工具失败 | Run 失败路径 | 不替换最近成功集合;观察「当前成功快照」仍指向上次成功身份,并与最新失败状态区分(对齐 TC-RUN-05) | output-inspection;§10.1 | S10 / S03 |
| TC-OUT-06 | [ ] | KeepLatest;当前已有保留集合(含空) | Subscribe(replay_latest=true)再收后续事件 | 连接后立即重放当前保留对应事件(含 EmptySnapshot);之后不漏更新;回调不得隐式 Run | output-inspection 动态连接 | S10 |
| TC-OUT-07 | [ ] | 已 TryOpen 得查看者句柄;随后集合被 Evicted | 淘汰 + 继续持有已打开查看者 | 已打开查看者不被强制关闭;新的 TryOpenLatest 对该身份不可用;关闭查看者后不阻挡会话清理契约 | output-inspection 查看者寿命 | S10 |
| TC-OUT-08 | [ ] | 对照两会话:A=KeepLatest,B=ReleaseAfterRouting | A 跑成功后订阅可重放;B 路由后无保留再连调试器 | A:可重放当前集合;B:已回收过去结果无法补取;动态连接 ≠ 无限历史;ReleaseAfterRouting 不冒充可恢复 | output-inspection KeepLatest vs ReleaseAfterRouting | S10 |
| TC-OUT-09 | [ ] | 任意观察订阅/重放/TryOpen 已挂 | 仅观察回调或查询(不显式 Run) | 永不隐式触发 Run、链接赋值或重新采集(升格/对齐 TC-RUN-05;与显示只观察一致) | output-inspection;toolflow §7.4.3;TC-RUN-05 | S10 / S03 |
8. 采集接入 TC-ACQ-*(阶段 D · Arden · R10)
语义权威见 camera-acquisition.md;本表只做可勾选断言。不改停止矩阵 / Diagnostic 码表。假采集适配(fake SDK 持帧/回调临时指针)即可;不以真实供应商驱动代替本表勾选正文。场景骨架 S11。交叉:停相机 ≠ 自动 Cancel 见
TC-CTL-04/TC-SESS-05。
| ID | 勾 | 前置 | 动作 | 期望 code / 状态 | 对应章节 | 场景 |
|---|---|---|---|---|---|---|
| TC-ACQ-01 | [ ] | SDK 可显式持帧、地址稳定、待归还后复用 | 适配器 ImportNative(ReadOnly);外借结束经一次 releaseAsync 归还 | 在途图像保持该份 SDK 引用;Share 不重复 AddRef SDK;最后归还 Release 一次;新外借周期新 BufferId(即使物理地址相同) | camera-acquisition「两种采集接入」 | S11 |
| TC-ACQ-02 | [ ] | 像素仅回调期有效,或 SDK 无法保证后续稳定 | 回调内同步 Copy 到受控自有存储后,按协议立即归还原 SDK 缓冲 | 回调结束后不得再持原 SDK 地址当稳定输入;无法保证时必须走复制,禁止用托管引用计数掩盖 SDK 提前复用 | camera-acquisition | S11 |
| TC-ACQ-03 | [ ] | 已有稳定 ReadOnly 图像且正好一个待交接 handle | TryPostOwned(std::move(image)):成功 / 拒绝 / 抛错三路径 | 成功→入口接管所有权;拒绝→关闭未接管 image;抛错→未接管(不得外层 finally 无条件 SDK Release);导入成功后只能经图像关闭触发归还 | camera-acquisition「入口接管」 | S11 |
| TC-ACQ-04 | [ ] | 自有池或入口预算已满 / 无余量 | 复制前取池与入口预算;满载时 TryPostOwned 或拒绝路径 | 禁止先拷贝再发现无容量;满池/满入口→拒绝入队并可观察;不无限积压 | camera-acquisition | S11 |
| TC-ACQ-05 | [ ] | 图像关闭已触发 releaseAsync;归还泵贯穿采集会话 | 排队归还 → SDK 专属线程确认 | ReturnCompleted 仅在 SDK 确认归还后完成;归还队列有界或入口预留归还容量;归还失败=会话故障,不吞掉、不对同一 Release 自动重试 | camera-acquisition「归还泵」 | S11 |
| TC-ACQ-06 | [ ] | 连续采集;预览走 Latest 槽 | 多帧到达;UI 定时/合并取最新 | 有所有权的 Latest 槽;被替换项立即关闭;禁止为每次采集无限 Post 带图像引用的 dispatcher 回调;实时预览默认 Snapshot 口径(UI 暂停最多一显示副本) | camera-acquisition「预览解耦」 | S11 |
| TC-ACQ-07 | [ ] | 采集回调线程在途 | 回调内仅 TryPostOwned(或有时限的 SDK 专用接入);不挂起等 UI/算法/同 SDK 线程归还 | 回调不堵 SDK 归还路径;不得在回调内同步等待 ReturnCompleted 或同线程 Release | camera-acquisition | S11 |
9. 会话停流 TC-SESS-*(阶段 D · Arden · R10)
语义权威见 session-lifecycle.md;停止矩阵权威仍为 toolflow §7.4.3(T9)——本文不改其语义。本表只做排空/归还/故障可勾选断言。场景骨架 S11。宿主侧分派见 host-integration.md §4。
| ID | 勾 | 前置 | 动作 | 期望 code / 状态 | 对应章节 | 场景 |
|---|---|---|---|---|---|---|
| TC-SESS-01 | [ ] | 采集会话 Running;有在途帧/工作 | 走完整停流:Quiescing → Draining/Cancelling → Releasing → Returning → Stopped | 结束须同时:无新回调、队列空、工具工作结束、会话拥有的图像/访问已关闭、SDK 归还已完成;不得边持最后引用边等自己的 ReturnCompleted | session-lifecycle 状态机 / 结束条件 | S11 |
| TC-SESS-02 | [ ] | 宿主已把图像导出到会话外窗口/历史(外部保留) | 会话停流并企图销毁 SDK | 停止结果报告「仍有外部保留」;禁止假称设备资源已归还;要立即毁 SDK 须先复制到独立存储并关闭原图 | session-lifecycle「外部保留」 | S11 |
| TC-SESS-03 | [ ] | 停流等待中;调用方超时触发 | 超时返回 | 返回未排空/故障结果(可含 BufferId、活动访问、pin、归还队列、持有时长);底层安全清理继续;不得强行回收仍被 native 使用的地址 | session-lifecycle「超时」 | S11 |
| TC-SESS-04 | [ ] | 归还失败 / 相机断线 / 消费者异常等 | 故障注入(fake) | 进入可观察故障路径;归还不自动再次 Release;清理泵继续处理其他可归还项;最终停止结果汇总失败;ReturnFailed / 断线可观测 | session-lifecycle「故障处置」 | S11 |
| TC-SESS-05 | [ ] | Run 在途且采集在跑 | 宿主要「相机与 Run 一起停」 | 必须显式既停采集 又 RequestCancel;二者不隐含联动(对齐停止矩阵 / TC-CTL-04;host-integration「两端都停」) | session-lifecycle 停止矩阵;toolflow §7.4.3;host-integration §4 | S11 / S09c |
10. 存储 / pin / 归还 TC-MEM-*(阶段 B · Arden · R11)
语义权威见 memory-management.md;首次根范围 / padding 薄指 image-model.md。本表只做可勾选断言( fake buffer 即可)。不改 Diagnostic / T9 停止矩阵与 toolflow v1.9。场景骨架 S12。交叉:句柄 H1–H10 见 §6;采集归还泵见
TC-ACQ-05(不扩本表)。
| ID | 勾 | 前置 | 动作 | 期望 code / 状态 | 对应章节 | 场景 |
|---|---|---|---|---|---|---|
| TC-MEM-01 | [ ] | 进程默认或自定义 MemoryPool 可用 | ImageBuffer::Rent(minimum_capacity, pool) 成功 / 失败两路径 | 成功→框架接管租出 owner;失败→归还内部已租出存储且无半接管实体;ReadOnly + Uninitialized 创建时拒绝;capacity>0 | memory-management「工厂约束」 | S12 |
| TC-MEM-02 | [ ] | 调用方持有独立 MemoryOwner | TakeOwnership(owner, …) 成功 / 抛错 | 成功→接管 owner,调用方不再访问/释放;抛错→不释放调用方 owner(无双释放) | memory-management「工厂约束」 | S12 |
| TC-MEM-03 | [ ] | 调用方已取得有效 SDK/原生引用 + release 回调 | ImportNative(address, capacity, access, release, …) 成功 / 抛错 | 成功→接管该份引用;抛错→不调用 releaseAsync/release;地址非零、容量合法 | memory-management「工厂约束」 | S12 |
| TC-MEM-04 | [ ] | 多 handle + 活动读/写 + 已入队权限申请 | 逐个关闭 handle / 结束访问 / 取消或完成排队申请 | 仅当全部 handle、活动访问与排队申请结束后才进入 Returning;关闭单一 handle 不触发归还 | memory-management「handle、异步等待与清理」 | S12 |
| TC-MEM-05 | [ ] | 调用方持有该实体最后一份存活引用 | 边持引用边 AwaitReturnCompleted / 等自身 ReturnCompleted | 禁止(等待环);须先关闭自身 handle/引用,再等待可重复的 ReturnCompleted | memory-management「handle、异步等待与清理」 | S12 |
| TC-MEM-06 | [ ] | 已 OpenRead 并 Pin();父 reader 先 Dispose | 企图 OpenWrite / 推进归还;读 pin 的 access() | pin 仍阻止写入与归还直至放 pin;读 pin Access 永为 ReadOnly(不因底层可写而提升) | memory-management「span 与 pin 的有效期」 | S12 |
| TC-MEM-07 | [ ] | 已 OpenWrite 并持有写 pin | Complete / CompleteAndOpenRead(pin 未放);或父 writer 先 Dispose | 存在写 pin → Complete 抛 InvalidOperation(或等价);父先 Dispose → 内容 Invalid,但独占权限持续到末写 pin 释放,期间不授新访问 | memory-management「span 与 pin」/「写入提交」 | S12 |
| TC-MEM-08 | [ ] | 存在冲突读者/写者或排队中;带 CancelToken | OpenRead/OpenWrite + cancel;发起申请的 handle 随后 Dispose | 取消令牌可撤销等待;发起 handle Dispose 不撤销已入队申请;轮到读时若未初始化/上次写失败 → 立即 InvalidOperation,不无限等未登记写者 | memory-management「访问授予」/「handle」 | S12 |
| TC-MEM-09 | [ ] | reader/writer/buffer 上并发 Share/ShareBuffer 与 Dispose | 竞争双方 | 单一协调点原子裁定;成功返回的子对象始终独立有效;已关闭对象再 Share/申请 → Disposed(或等价) | memory-management「访问授予」 | S12 |
| TC-MEM-10 | [ ] | 已获写权限;未 Complete | Dispose writer | 内容 → Invalid;不回滚字节;不得把旧 Generation 重发为有效;新读拒绝 | memory-management「写入提交」/「首次初始化」 | S12 |
| TC-MEM-11 | [ ] | 类型化 Rent 未首次发布;或已 Ready 后 ROI 写入 | 仅写 ROI 企图首次 Complete;或 ROI 写入中止 | 首次须根范围覆盖全部逻辑像素后 Complete;未完成首次前拒绝不同范围初始化视图;任一写入中止 → 整实体 Invalid(不局限矩形);小 ROI Complete 不能把整份标回有效 | memory-management「首次初始化、ROI 写入中止与恢复」;image-model 根范围 | S12 |
| TC-MEM-12 | [ ] | 内容已 Invalid | 带 expectedVersion 的 OpenWrite;以及无期望版本的完整范围恢复写 | 带期望版本 → 拒绝(不授予);无期望版本的完整初始化范围写入 Complete 后可回到 Ready;框架无字节级回滚 | memory-management「首次初始化…恢复」 | S12 |
| TC-MEM-13 | [ ] | 多实体并行归还;其一 releaseAsync 卡住或失败 | 关闭触发清理;观察各项 ReturnCompleted / ReturnFailed | releaseAsync 恰好一次;一项卡住不串行阻塞其他存储归还;ReturnFailed → 不自动重试、不回可用池、不恢复访问;失败可观察 | memory-management「handle、异步等待与清理」 | S12 |
11. 显示集成 TC-DISP-*(阶段 F · Arden · R12)
语义权威见 image-display.md。显示契约仅 C#(控件封装 / UI 层);像素经绑定自 C++
vyntek::imaging。勿写成 C++ UI;勿与 T15 开放风险 R10(C++ UI·已关闭)互替。本表只做可勾选断言。不改 Diagnostic / T9 停止矩阵与 toolflow v1.9。场景骨架 S13。交叉:显示只观察见TC-CTL-05(不扩 T9)。
| ID | 勾 | 前置 | 动作 | 期望 code / 状态 | 对应章节 | 场景 |
|---|---|---|---|---|---|---|
| TC-DISP-01 | [ ] | UI 线程;合法 IPixelImage(稳定或可写) | SetImage / Source 登记 | 同步取得独立 Share 并返回 ImageDisplayUpdate;不承诺已 OpenRead/已安装;可写输入须后台授予时才捕获 | image-display「请求登记与读取授予」 | S13 |
| TC-DISP-02 | [ ] | 调用方持有有效图像;可多控件同图 | SetImage 成功后调用方 Dispose/Close 自有 handle;或另一控件再 SetImage 同图 | 控件 不接管、不 Dispose 调用方传入 handle;Source 仅引用非所有权转移;Share 延长稳定内容寿命 | image-display「多窗口与所有权」 | S13 |
| TC-DISP-03 | [ ] | 已以 RetainInput(默认)安装可写原图 | 企图对同一 Buffer 原地 OpenWrite / 写入 | 显示快照持稳定读 → 原地写入等待控件及其他读者退出;属性改 InputRetention 不偷偷改当前已装内容存储 | image-display「输入保留与显示副本」 | S13 |
| TC-DISP-04 | [ ] | 以 Snapshot 提交并完成安装 | 等待 InputReleased 后对原图写入;屏幕仍可显示旧图 | InputReleased 后控件不再阻止原图写入;Source 不因释放而置空;刷新须再次 SetImage | image-display「输入保留与显示副本」 | S13 |
| TC-DISP-05 | [ ] | 当前为 RetainInput 已装 / 或在途候选 | update.DetachInputAsync;含预算不足路径 | 成功 → Released 且 InputReleased 已完成,公开 CurrentImageInfo/CurrentVersion 保持来源身份;CpuBudgetExceeded → 不破坏当前内容及原图读取,可重试 | image-display「脱离和清空」 | S13 |
| TC-DISP-06 | [ ] | 一次成功/失败更新在途 | 分别观察 Completion、InputReleased、ImageRendered、底层 ReturnCompleted | 四信号互不等价:Installed ≠ Rendered;InputReleased 可先/后于 Completion;ImageRendered 可重复或从未发生;ReturnCompleted 属 SDK/owner 归还,≠ 控件 InputReleased | image-display「回执、绘制和输入释放」 | S13 |
| TC-DISP-07 | [ ] | 控件已有已装旧画面 | 新请求 Failed(含 VersionMismatch / ReadFailed / UnsupportedImage 等) | 保留旧画面;失败回执 Info/Version 为空、带 Failure;不得让新 Source 信息与旧画面混合 | image-display「内部显示快照」/「回执」 | S13 |
| TC-DISP-08 | [ ] | 当前已装 Gray8 / Rgb24 / Bgr24 / Depth16 之一 | TryGetPixel → TryGetGray8/Rgb24/Bgr24/Depth16 | 仅格式相符时成功;无隐式跨格式转换;Depth 无效码仍可 TryGetDepth16(IsValid=false);TryGetDepthMeters 仅尺度已声明且有效时成功 | image-display「多格式像素查询」 | S13 |
| TC-DISP-09 | [ ] | 已装含 ROI 偏移和/或负步长的图像 | TryGetPixel(viewPosition, …) | 按控件逆变换到逻辑像素坐标(ROI 内 X/Y);负步长/ROI 由临时读取封装处理;禁止用 viewPosition 直接作缓冲偏移;界外 → false | image-display「多格式像素查询与坐标」 | S13 |
| TC-DISP-10 | [ ] | 有在途候选/已装内容;含隐藏或零尺寸 | 卸载控件;随后重挂载仍有效 Source;或图形 context 重建 | 卸载:取消候选、清 UI 快照、通知 render 退出,安全排空异步完成(不 UI 同步死等);重挂载 = 新请求,不复用已失效读取;context 重建从显示副本/快照恢复,不改公开来源身份 | image-display「内部显示快照」/「多格式渲染」 | S13 |
| TC-DISP-11 | [ ] | 已装旧图;新请求将超 CPU/GPU 预算 | SetImage / Snapshot 复制 / Detach 复制致预算不足 | 登记 Failed(如 CpuBudgetExceeded);释放本次取得的引用;保留旧画面;不先丢旧图凑预算;无自动重试队列 | image-display「预算、线程与资源归还」 | S13 |
| TC-DISP-12 | [ ] | Run 在途或已结束;显示已挂 | 卸图 / ClearImageAsync / Dispose ImageDisplay / 关显示宿主 | 显示只观察:不停编排、不 RequestCancel、不停相机(交叉升格/对齐 TC-CTL-05;不扩 T9) | image-display 文首 T9;toolflow §7.4.3;TC-CTL-05 | S13 / S05 / S07 |
12. 场景 S01–S13 映射(每场景 ≥1 条)
| 场景 | 至少覆盖的契约 ID | 主期望(摘要) | 文档回链 |
|---|---|---|---|
| S01 | TC-RUN-01、TC-REG-04、TC-LNK-08 | ok;Outputs 可读;Validate 无 Error | examples-scenarios S01;§7.4 / §9.4 |
| S02 | TC-RUN-02、TC-LNK-01、TC-LNK-03 | 顺序链 ok;坏序 → OrderViolation | S02;§4.1 / §5 |
| S03 | TC-RUN-03、TC-H6、TC-REG-01、TC-RUN-05(薄) | fake 流水线 Outputs 可读;观察不驱动 Run(细项见 S10 / TC-OUT-*) | S03;§7.6.3 / §9.1;Arden · R9 |
| S04 | TC-SER-02、TC-SER-03、TC-LNK-07、TC-SER-04 | SchemaTooNew / UnknownTypeId / MissingRequiredProperty;component 不拦 Load | S04;§6.5–6.6 |
| S05 | TC-H1…TC-H9(主路径)、TC-CTL-05 | Complete→ReadOnly;显示仅 C# | S05;§3.4 |
| S06 | TC-CTL-01、TC-CTL-02、TC-CTL-03、TC-H10 | Cancelled / Aborted / Busy;H10 | S06;§4.3 / §7.4.3 |
| S07 | TC-RUN-04、TC-SER-01、TC-CTL-05 | headless 批跑 ok;带显示宿主不改编排语义 | S07;§7.2–7.3; host-integration.md |
| S08 | TC-REG-01…TC-REG-06、TC-H2、TC-H8、TC-H10 | fake 注册 + 最小 ITool;未知 → UnknownTypeId | S08;§7.6 |
| S09a | TC-RUN-06、TC-LNK-04、TC-REG-05 | 嵌套合法 ok;私有属性 → Encapsulation | S09a;§9.3 / §7.4.6 |
| S09b | TC-LNK-06、TC-LNK-05 | SourceDisabled;InvalidBinding 主断言 | S09b;§4.3 / §7.4.6 |
| S09c | TC-SER-07、TC-CTL-04 | IoFailure;停相机 ≠ 自动 Cancel | S09c;§7.4.3 / §7.4.6 |
| S09d | TC-RUN-07、TC-LNK-01、TC-REG-05 | §9.2 两流终端串联 ok(可选) | S09d;§9.2 |
| S10 | TC-OUT-01…TC-OUT-09;TC-RUN-05(交叉) | T10 集合事件可勾选;观察永不驱动 Run;KeepLatest vs ReleaseAfterRouting | S10;output-inspection; Arden · R9 |
| S11 | TC-ACQ-01…TC-ACQ-07;TC-SESS-01…TC-SESS-05;交叉 TC-CTL-04 | 持帧/Copy、TryPostOwned、满池预算、归还泵、Latest、回调不堵归还;停流结束条件、外保留、超时续清、ReturnFailed/断线、两端都停须显式 | S11;camera-acquisition;session-lifecycle; Arden · R10 |
| S12 | TC-MEM-01…TC-MEM-13 | 工厂接管;最后引用→Returning;禁等自身 ReturnCompleted;pin 阻写/归还;写 pin 阻 Complete;授予/取消与 Share/Dispose 原子;未 Complete→Invalid;首次根范围/ROI 中止整实体;Invalid 恢复;releaseAsync 一次与归还隔离/ReturnFailed | S12;memory-management; Arden · R11 |
| S13 | TC-DISP-01…TC-DISP-12;交叉 TC-CTL-05 | 登记≠授予;Share 不接管;Retain/Snapshot/Detach;四回执互不等价;失败/VersionMismatch 保留旧图;四格式 TryGet 无隐式转换;ROI/负步长;卸载/重挂/context;预算失败不丢旧图;显示只观察 | S13;image-display; Arden · R12(仅 C#;≠ T15 R10) |
13. Diagnostic 码覆盖清单(对照 §7.4.6)
| Code | 本表代表契约 | 出现于 |
|---|---|---|
TypeMismatch | TC-LNK-02 | Validate / Link / Run 拉值 |
OrderViolation | TC-LNK-03 | Validate / Link |
Encapsulation | TC-LNK-04 | Validate / Link;S09a |
Busy | TC-CTL-03 | Run |
UnknownTypeId | TC-REG-03 / TC-SER-03 | Load / Create |
SchemaTooNew | TC-SER-02 | Load |
IoFailure | TC-SER-07 | Load / Save;S09c |
SourceDisabled | TC-LNK-06 | Run 拉值;S09b |
InvalidBinding | TC-LNK-05 | Validate / Load / Run;S09b |
MissingRequiredProperty | TC-LNK-07 | Validate |
Aborted | TC-CTL-02 | Run |
Cancelled | TC-CTL-01 | Run |
成功路径期望:无上述 Error,或明确状态 ok / Outputs 可读 / Access=ReadOnly。
14. 非目标门禁(MVP 不得误交付)
| ID | 勾 | 断言(负向) | 对应 |
|---|---|---|---|
| TC-NG-01 | [ ] | 无脚本引擎合入 MVP 交付面 | toolflow §10.2 |
| TC-NG-02 | [ ] | 无 MPF_ToolBlock / .vpp 加载 | §1.2 / §10.2 |
| TC-NG-03 | [ ] | 无 DAG 拓扑调度器;链接不是调度边 | §3.4 / §4.1 / §10.2 |
| TC-NG-04 | [ ] | 无完整产线 IO 协议 | §10.2 |
| TC-NG-05 | [ ] | ImageDisplay 不是 C++ UI(显示契约仅 C#; Arden · R12 / ≠ T15 R10) | csharp-binding / examples T12;image-display;TC-DISP-* |
15. 与 §10.1 / T13 的关系
| 建议测试阶段(T13) | 本表章节 |
|---|---|
| 注册 | §1 |
| 建流 | §1(终端)+ §3 前置 |
| Link / Validate | §2 |
| Run | §3 |
| Cancel / 失败中止 | §4 |
| Load 复载 | §5 |
| 句柄 H1–H10 | §6 |
| 输出观察(T10) | §7(TC-OUT-* · Arden · R9) |
| 采集接入(阶段 D) | §8(TC-ACQ-* · Arden · R10) |
| 会话停流(阶段 D) | §9(TC-SESS-* · Arden · R10) |
| 存储/pin/归还(阶段 B) | §10(TC-MEM-* · Arden · R11) |
| 显示集成(阶段 F) | §11(TC-DISP-* · Arden · R12;仅 C#;≠ T15 R10) |
| fake 注册与 headless | §1 + §3(TC-RUN-03)+ §12(S03/S08/S10/S11/S12/S13) |
§10.1 摘要仍留在 toolflow.md;全文可勾选契约以本文为准。场景骨架见 examples-scenarios.md(含 S09a–d / R6; S10 / Arden · R9; S11 / Arden · R10; S12 / Arden · R11; S13 / Arden · R12)。 Arden · R13:toolflow §10.1 T13 阶段索引已与上表 OUT/ACQ/SESS/MEM/DISP + S10–S13 对齐(仅索引/回链,不开新域)。
文档状态:测试契约草案(2026-10-21,Asia/Shanghai),对齐 toolflow v1.9(R4; R6 增 S09 映射 / TC-RUN-06·07; R7 S07↔host-integration; R8 术语索引; Arden · R9 增 TC-OUT- / S10; Arden · R10 增 TC-ACQ-* / TC-SESS-* / S11; Arden · R11 增 TC-MEM-* / S12; Arden · R12 增 TC-DISP-* / S13,显示仅 C#,≠ T15 R10; Arden · R13 §10.1 T13 索引对齐,本批横切收束)。实施以代码与本表勾选为准;不写覆盖率 KPI。*
本仓对拍
本表是可勾选契约迁入。实现是否够起草测试,看上面五份薄摘的「实现对拍状态」。没有一项在本表打勾。
单测覆盖度记录(2026-10-21 Asia/Shanghai)
按本文档 line 3 与五份薄摘的约定,单测通过 ≠ 本表验收通过(验收另需供应商适配、真实渲染 / GPU 预算 / context 重建等实机条件,见各薄摘「实现对拍状态」与 image-display.md:340)。故本表勾选列保持全 [ ] 未勾。以下只记录「哪些契约已有单测断言覆盖」,供验收时对照,不代表验收结论:
| 契约族 | 条数 | 单测覆盖位置 | 验收仍缺 |
|---|---|---|---|
| TC-REG / LNK / RUN / CTL / SER / H / NG | 6+8+7+5+7+10+5 | tests/orchestration_contracts.cpp | 供应商实机链路 |
| TC-OUT | 9 | tests/smoke_headers.cpp | 同上 |
| TC-ACQ | 7 | tests/smoke_headers.cpp(含本轮新增 03/04/05/06) | 供应商 SDK 适配未关(tc-acq.md:31) |
| TC-SESS | 5 | tests/smoke_headers.cpp(含本轮新增 01/03/04) | 实机停流时序 |
| TC-MEM | 13 | tests/smoke_headers.cpp(含本轮新增 02/03/09) | 真实池压力 |
| TC-DISP | 12 | bindings/csharp/Vyntek.Controls.Tests/(37 项) | 无 UI 框架:真实渲染 / GPU 预算 / context 重建不可验(image-display.md:340) |
本轮验证口径:cmake --build --preset ninja-debug-tests 与 ninja-debug-shared 均构建通过、ctest 全绿;dotnet test 控件测试 37 项全过。TC-MEM-02/03/09、TC-ACQ-03/04/05/06、TC-SESS-01/03/04 为本轮新增断言,并经变异测试(故意注入缺陷后如期失败)确认具判别力,非恒真通过。
本轮同时修掉的实现缺陷:TC-DISP-04 的 Snapshot 策略此前未做像素私有副本(只 Dispose 读视图却仍持原图 Share 就发 InputReleased),违反 image-display.md:159/168;已新增 ManagedPixelCopy 物化副本并在安装后关闭原图引用,像素隔离断言改为真实断言(改写调用方像素后控件仍返回旧值)。
已修(原记债项):DetachInputAsync 把 RetainInput 转 Snapshot 的路径已物化私有副本——三阶段实现(锁内校验+原子预留预算 → 锁外物化副本,符合「复制仍在后台完成」→ 锁内以 _generation + ReferenceEquals 双重校验后 AdoptPrivateCopy 换入副本并关闭原引用)。并发脱离复用同一份工作(后到者 JOIN 同一 Task,预算只计一次),陈旧或失败路径按语义退还预留。回归测试见 TC_DISP_05b/05c/05d,其中 05d 用取样门做确定性会合,并经变异验证(禁用复用后如期失败于「B must wait on A's in-flight work」,即双重计费被当场捕获)。