Newer
Older
TIASShot / docs / 03_PLAN / PLAN_05_テスト網羅性向上計画.md

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

目的 (Purpose)

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

方針の本体(常に従う規約)は 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 を参照する.

決定事項 (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 を 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 秒の目安に収まる
  • 状態: 完了(2026-10-06)
    • 層②のテストは 3 ファイル 21 件を追加し計 24 件.3 回連続で緑,全体で約 19 秒
    • 報告台帳から 4 件を修正した: Form1 の取得系メソッドが UI スレッド外で値を返さない,再検出時と切断時にチャートマスクを解放しない,ResetShotQueue が残った画像を解放しない,撮影失敗行の Note 欄のクォート漏れ.再現テスト 7 件は修正前のコードで赤になることを確認した
    • 撮影系のテストは変換次元を 1 つ(Calib/ConversionChannels=4)に絞って時間を抑えた.全次元での保存はフェーズ 1 の単発撮影テストで確認している
    • 方針未決のため観点から外したもの: CalcTcc の例外境界と校正画像の保存失敗,Form1_FormClosing,SaveThread の同期 Invoke,DrainShots の待ち時間,撮影枚数 0 の完了表示.束 B(フェーズ 3 と同時)で方針を決めてから扱う
    • 実機でしか確かめられない残り: 実カメラの Disconnect でストリーム停止の直後にマスクを解放してよいか(SDK がコールバックの終了を待つか)

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

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

束 B の方針 (Decisions for Shot/Save Reliability)

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

論点方針
校正中の例外(CalcTcc・ProcessCalibrationFrame)カメラのコールバックへ例外を伝えない.捕捉してログを残し,「校正に失敗しました」と通知し,チャート検出からやり直せる状態(未校正・マスク解放)に戻す.通知がチャート検出の進捗表示ですぐ上書きされないよう,次に校正が完了するまで検出中の表示を「校正に失敗しました(理由).チャートの検出からやり直しています」に差し替えて出し続ける(エラーダイアログはカメラのコールバックを止めるため使わない)
校正画像(TccImage・TccRoisImage)の保存失敗変換行列はメモリ上で有効なので校正済みのままにし,「自動校正完了(校正画像の保存に失敗)」のように警告を表示する
アプリを閉じるとき(Form1_FormClosing)保存中なら閉じるのを取り消して「保存中」と知らせる(閉じる理由は区別しない.Windows のシャットダウン時も取り消し,待つか強制終了するかはユーザーに選ばせる).閉じ始めたらカメラのコールバックから UI への呼び出しを行わない(ハングの防止).光源のポートを閉じる(消灯はしない.終了後も光源は点いたまま.上流 v1.7 と同じ動作で,2026-10-08 にユーザーが仕様として確定)
保存スレッドから UI への通知進捗表示などは UI スレッドを待たない非同期の呼び出しにする(撮影中でも保存が進み,画像がキューに溜まり続けない)
保存キューの待ち時間(DrainShots)固定 30 秒をやめ,「30 秒」と「撮影間隔の 2 倍」の大きい方にする
  • 3a の状態: 完了(2026-10-06)
    • 上表の方針をすべて実装した.判断(閉じてよいか,待ち時間,通知文の組み立て)は Form1.CanClose・CameraBase.GetDrainIdleTimeout・FormatCalibrationFailure・ShouldNotifyCalibrationFailure・FormatChartDetectionMessage・FormatCalibrationDone として切り出し,層①でテストした
    • 層②の新規・書き換えテストは修正前のコードで 14 件が赤になることを確認した.層②は計 36 件で 1 回あたり約 21〜26 秒(目安 30 秒に近づいてきたので,次に層②を増やすときはテスト用設定の見直しを先に検討する)
    • 保存中の切断テストは,通知の非同期化で「保存スレッドが UI を待つ間は必ず生きている」前提が崩れたため,保存スレッドを意図的に止めておく形に書き直した
    • 実機でしか確かめられない残り: 保存中・プレビュー中・校正中に閉じたときの挙動(SDK のストリーム停止との待ち合い),光源のポートの解放(終了後も点灯したままであること),撮影と保存が並行するようになった後の連写間隔の精度
  • 3b(未着手): ROI とフレームサイズの照合,フレームバッファのサイズ検査,光源ポートを開けなかったときの扱い(/auto-audit 報告台帳の該当項目)

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

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

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

  • Microsoft.CodeCoverage(テストプロジェクトに導入済み)で未通過の箇所を確認し,重要な経路の抜けを埋める
  • TEST_01 の手動テストを下記の目標形に改訂する
  • 完了条件: TEST_01 の改訂が main に取り込まれる

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

当初はフェーズ 5 完了後に残す項目を想定していたが,実機を Claude が操作して確かめる実機テスト(PLAN_06)ができたため,2026-10-09 に PLAN_06 フェーズ 3 で TEST_01 の手動テストを先に縮めた.現在 TEST_01 に残している項目は次のとおり(詳細は TEST_01「手動テスト」).

  • 通常起動でカメラが自動で選ばれて接続され,プレビューが表示される(実機テストは機種を指定して接続するため)
  • プレビューの映り方が自然である(目視)
  • ボタンをマウスで押して操作でき,表示が自然に変わる(実機テストはボタンの処理を直接呼ぶため)
  • 撮影した sRGB 変換画像の色が目視で妥当である(17 次元の変換のノイズの出方も見る)
  • 撮影の途中で閉じても固まらない(未自動化)
  • 閉じた後も光源が点いたまま残る
  • Lumenera を使う場合のみ: Lumenera で起動・校正・撮影ができる(実機テストの対象外)

※ 接続・プレビューのフレーム・露光/ゲイン・光源・校正・撮影と保存・プレビュー中/保存中/校正中に閉じる,は実機テストで,保存ファイルの有無・内容・失敗時の通知・撮影ボタンの有効/無効は層②とセルフテストで確かめ,手動では確認しない.

リスクと対策 (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/月)を超えそうなら,データの削減か有料枠を検討する