# テスト網羅性向上計画 (Test Coverage Improvement Plan)

## 目的 (Purpose)

人間による実機確認を「カメラ・光源 SDK の実呼び出し」と「実画像の見た目」だけに絞り，それ以外の回帰を自動テストで担保する．

方針の本体（常に従う規約）は [testing.md](../../.claude/rules/testing.md) に置き，本書はそこへ移行するための段取り（フェーズ・完了条件・リスク）を定める．

## 背景 (Background)

- 2026-10-06 の `/auto-audit` では，報告した指摘 26 件の多くが「`CameraBase`・`Form1`・カメラ層で再現テストを書けない」ことを理由に自動修正できなかった．中でも保存失敗が成功扱いになる件や終了時の画像取りこぼしは，データ欠損に直結する．
- 旧方針は「ハードウェア結合層は自動テスト対象外」としていた．しかし実際には，偽カメラ（`SelfTest/SelfTestCamera`）とヘッドレス `Form1`（`Form1.SelfTest.cs`）がすでにあり，セルフテストでは `CameraBase` の校正〜保存を通している．NUnit で細かく検証していないだけで，自動化できない範囲は思っていたより狭い．
- 異常系（保存失敗・終了時・設定ファイル破損）に問題が集中していた．正常系の目視確認だけでは見つからない種類の不具合である．

## 現状の手動確認の内訳 (Current Manual Checks)

| 確認内容 | 本当に実機が要るか | 移行先 |
| --- | --- | --- |
| カメラ接続・露光/ゲインの反映・フレーム取得 | 要る（SDK の実呼び出し） | 実機に残す（層④） |
| 光源の ON/OFF | 要る（シリアル通信） | 実機に残す（層④） |
| 校正の自動開始〜完了，撮影ボタンの有効化 | 判断ロジックは不要 | 層②（偽カメラ＋ヘッドレス Form1） |
| 単発・連続撮影でファイルが保存される | 不要 | 層②・セルフテスト |
| 保存失敗・ディスク満杯・設定破損時の挙動 | 不要（失敗を注入できる） | 層② |
| アプリを閉じてもエラーが出ない | 終了順序の判断は不要 | 層②（SDK の停止そのものは層④） |
| 校正結果・色の妥当性 | 合成チャートでは不十分 | 実機録画データによる回帰（層①の拡張） |

## 目標とする 4 層モデル (Target Four-Layer Model)

| 層 | 対象 | テスト手段 |
| --- | --- | --- |
| ① ピュアロジック | 色補正・チャート検出・CSV・設定・ゲイン計算・画像統計 | NUnit（実値で検証） |
| ② 結合ロジック | `CameraBase` の校正の状態遷移・撮影ループ・`SaveThread`・失敗通知・終了処理，`Form1` の UI 連携メソッド | NUnit＋偽カメラ＋ヘッドレス `Form1`（STA スレッドでメッセージを処理するハーネス） |
| ③ SDK アダプタ | `IScam`・`Lucam`・`LightSource` | 判断ロジックを純粋関数へ切り出して層①で検証する．SDK 呼び出しそのものは層④ |
| ④ 実機 | SDK の実呼び出し・実画像の見た目 | 短い手動チェックリスト（TEST_01） |

詳細な判断基準は [testing.md](../../.claude/rules/testing.md) を参照する．

## 決定事項 (Decisions)

2026-10-06 にユーザーと合意した．

| 項目 | 決定 |
| --- | --- |
| テストプロジェクトへの `System.Windows.Forms` 参照追加 | 許可する（.NET Framework 標準アセンブリで NuGet 依存は増えない） |
| 層②（`CameraBase` を NUnit で検証） | 行う |
| 層③（SDK アダプタからの判断ロジック切り出し） | 撮影・保存の信頼性修正（`/auto-audit` 報告の束 B）と同時に行う |
| 実機録画データ | 撮る．**舌診チャートと白板のみ**（舌の画像は個人情報のため扱わない） |
| カバレッジ | 数値目標は課さず，「一度も通っていない箇所」を見つける地図として使う |
| 実機録画データの置き場所 | **Git LFS** で管理する．CI が無くテストは開発マシンでしか回らないため，コードと同じ版のデータが clone で確実に手元に来る方式を選んだ．1 機種・1 場面あたり 3〜5 フレームに絞り，合計 100〜150MB 程度に抑える（GitHub の LFS 無料枠はストレージ 1GB・転送量 1GB/月） |
| 層②のテストの実行時間 | **層②全体で 30 秒を目安**とし，**1 件あたり 15 秒のタイムアウト**を付ける．超えそうなときはテスト用設定（校正フレーム数等）を見直して速くする．`/auto-audit` は修正 1 件ごとにテスト一式を何度も回すため，ここが延びると全作業が遅くなる |
| `/verify` に含めるか | **常に含める**（`run_selftest.py` は絞り込まずに全件実行するので追加設定は不要）．CI が無く，`/verify`・`/auto-audit` が実質唯一の自動チェックのため，カテゴリで分けて通常は回さない運用にはしない |
| メモリ量で判定するテスト | 2026-10-06 に追加したリーク再現テスト等は `[Category("Memory")]` に分け，環境による不安定さが出たら通常の実行から外せるようにする |

## フェーズ (Phases)

### フェーズ 0: 方針の文書化

- 本書を作成し，[testing.md](../../.claude/rules/testing.md) を 4 層モデルに改訂する
- 完了条件: ユーザーのレビューを経て main に取り込む

### フェーズ 1: 層②の基盤と試作

- テストプロジェクトに `System.Windows.Forms` の参照を追加する
- テスト用ハーネスを作る
  - STA スレッドを立て，ヘッドレス `Form1` を生成してハンドルを作る（`SelfTestStart`）
  - 条件が満たされるまでメッセージを処理する待機処理（タイムアウト必須）．`SelfTestRunner` の `Application.DoEvents` による待機と同じ考え方で，可能なら共通化する
  - 一時フォルダと一時 `config.xml` を用意する（`Config.Load(path)` を使う）
- テスト用の一時設定では校正フレーム数（`Calib/Frames`）等を小さくし，1 件あたり 1 秒前後で終わるようにする
- 既存のメモリ量判定テストに `[Category("Memory")]` を付ける
- 試作として「保存失敗が成功扱いになる」件（`/auto-audit` 報告台帳）の赤テストを書き，修正する
- 完了条件:
  - ハーネスを使うテストが 3 回連続で安定して緑になる
  - 層②の各テストに 15 秒のタイムアウトが付いている
  - Release ビルドに影響しない（`SELFTEST` は Debug 系のみ）
- **状態: 完了（2026-10-06）**
  - ハーネスは `TIASshot.Tests/IntegrationHarness.cs`．一時 config の作成と待機処理は `TIASshot/SelfTest/SelfTestConfig.cs`・`SelfTestPump.cs` に切り出し，セルフテストのランナーと共用する
  - 試作テスト `CameraBaseSaveIntegrationTests`（保存成功・単発の保存失敗・連続撮影の途中の保存失敗）は 3 回連続で緑．保存失敗の 2 件は修正前のコードで赤になることを確認した．層② 3 件の所要時間は計約 5 秒
  - 失敗の注入は，偽カメラの派生で保存先と同名のフォルダを作り `Cv2.ImWrite` に false を返させる方法をとった（例外ではなく false になることを単体テストで固定している）
  - 引き続き残る課題: `CalcTcc` の校正画像の保存失敗は「自動校正完了」と表示される（失敗時に校正済みとするかが仕様判断のため未修正）

### フェーズ 2: 層②の拡充

- 次の観点を網羅する．各観点に**失敗を注入するケースを 1 つ以上**含める
  - 校正の状態遷移（チャート検出 → 固定判定 → 変換行列算出 → 撮影ボタン有効化）
  - 撮影ループ（単発・連続・枚数 0・間隔 0）
  - `SaveThread`（保存失敗・例外時の後始末・残りフレームの破棄）
  - 切断・終了の順序（保存中の終了，校正中の終了）
  - `Form1` の UI 連携メソッド（UI スレッド外からの呼び出しを含む）
- `/auto-audit` 報告台帳のうち，層②で再現テストを書けるようになった項目をここで修正する
- 完了条件: 上記の各観点に正常系と失敗注入のテストがそろい，層②全体の実行時間が 30 秒の目安に収まる

### フェーズ 3: 層③の切り出し

- 撮影・保存の信頼性修正（束 B）と同時に，`IScam`・`Lucam`・`LightSource` から判断ロジックを純粋関数として切り出す
  - 例: ROI とフレームサイズの照合，ホワイトバランスのゲイン算出，フレームバッファのサイズ検査，光源ポートを開けなかったときの扱い，例外を通知に変える処理
- SDK を呼ぶ部分は「受け取って渡すだけ」の薄いアダプタに残す
- 完了条件: 切り出した関数に層①のテストがあり，切り出し直後に実機確認を 1 回行う

### フェーズ 4: 実機録画データによる回帰

- 開始時に Git LFS を準備する（このマシンへの `git lfs` の導入確認，`.gitattributes` での対象パターン指定，GitHub 側の LFS の利用確認）
- 人間が実機（ImagingSource・Lumenera の両方）で舌診チャートと白板を撮影し，フレームを可逆形式（PNG）で保存する．1 機種・1 場面あたり 3〜5 フレームとする
- 録画フレームを繰り返し流して校正を通し，チャート検出のマスク位置，変換行列（基準 CSV との一致．許容誤差つき），変換後の色票の色差を自動で比較するテストを作る
- 完了条件: 両機種の録画データで回帰テストが緑になり，データが Git LFS で管理されている

### フェーズ 5: 手動チェックリストの縮小とカバレッジの確認

- `Microsoft.CodeCoverage`（テストプロジェクトに導入済み）で未通過の箇所を確認し，重要な経路の抜けを埋める
- [TEST_01](../06_TEST/TEST_01_テスト仕様.md) の手動テストを下記の目標形に改訂する
- 完了条件: TEST_01 の改訂が main に取り込まれる

## 手動確認の目標形 (Target Manual Checklist)

フェーズ 5 完了後に TEST_01 に残す項目の想定．

- 両機種（ImagingSource・Lumenera）でカメラが自動接続され，プレビューが出る
- 露光・ゲインの設定がプレビューに反映されている（目視）
- 実物の舌診チャートで自動校正が完了する
- 撮影した画像の色が目視で妥当である
- 光源が ON/OFF する
- 撮影中・保存中にアプリを閉じても固まらない（SDK の停止が絡むため最終確認として残す）

※ 保存ファイルの有無・内容，失敗時の通知，撮影ボタンの有効/無効といった項目は層②とセルフテストに移し，手動では確認しない．

## リスクと対策 (Risks)

| リスク | 対策 |
| --- | --- |
| WinForms のテストがハング・不安定になる（STA・メッセージ処理の誤り） | ハーネスを 1 か所に集約し，待機には必ずタイムアウトを付ける．新しいハーネス利用テストは 3 回連続実行で安定性を確認してから取り込む |
| テストの実行時間が延びて `/verify` や `/auto-audit` が遅くなる | 層②のテストに `[Category("Integration")]` を付けて所要時間を計測し，全体 30 秒の目安を超えそうならテスト用設定を見直して速くする（`/verify` から外すことはしない） |
| テストが `Form1` の内部構造に依存して壊れやすくなる | `Form1.SelfTest.cs` のアクセサ（`SelfTestMessage`・`SelfTestLastError`・`SelfTestShotsEnabled` 等）経由でのみ観測する．足りないアクセサはそこに追加する |
| メモリ量で判定するテストが環境によって不安定になる | 2026-10-06 に追加したリーク再現テスト（プライベートメモリ増分のしきい値判定，OpenCV 内部の参照カウント読み取り）を棚卸しし，必要なら `[Category("Memory")]` に分ける |
| 実機録画データが大きい／個人情報を含む | 舌診チャートと白板に限る．フレーム数を絞り Git LFS で管理する．LFS の転送量の無料枠（1GB/月）を超えそうなら，データの削減か有料枠を検討する |
