Skip to content

test: Phase 2.5 coverage expansion (facade/factory fakes + tier-2 targets + A-1-4 regression) - #4

Merged
Pluglug merged 7 commits into
mainfrom
test/phase-2-5-coverage
Jun 12, 2026
Merged

test: Phase 2.5 coverage expansion (facade/factory fakes + tier-2 targets + A-1-4 regression)#4
Pluglug merged 7 commits into
mainfrom
test/phase-2-5-coverage

Conversation

@Pluglug

@Pluglug Pluglug commented Jun 12, 2026

Copy link
Copy Markdown
Owner

発注書 dev-note/orders/2026-06-13-test-coverage.md(#1)の納品 PR です。

1. 結果サマリー(数値はすべてツール出力から)

指標
core/pattern/facade.py カバレッジ 23% 94%
core/pattern/factory.py カバレッジ 31% 100%
Tier 1 (uv run pytest) 100 passed / 6 xfailed (0.25s) 166 passed / 6 xfailed (0.27s)
Tier 2 (tests/blender/run.sh) 19 passed 39 passed (0.15s)

新規テスト 86 件の内訳: facade 21 / factory 13 / File 純関数 32(以上 Tier 1)、ShapeKey 4 / Strip 4 / Collection 4 / Material 2 / props.py A-1-4 回帰 6(以上 Tier 2)。既存テストは無変更で全緑、xfail 6 件(02章の原理的限界)は不変。

2. 設計判断と根拠

facade のフェイク戦略 — Tier 1 のダックタイピング代役を採用(Tier 2 案は不採用)

  • factory は入力を hasattr/getattr でしか読まず(factory.py:103-141)、facade が prefs に求めるのも patterns / get_active_pattern() の2点だけ(facade.py:48-49, 121, 150, 179)。get_prefscontext.preferences.addons[ADDON_ID].preferences を辿るだけ(addon.py:76-93)なので、tests/unit/fakes.py に追加した FakeElementProperty / FakePatternProperty / FakePrefs / make_fake_context で全経路に到達できる
  • 製品コード自体がリロード問題対応で isinstance を避けたダックタイピング運用(analyze/04章 §2)であり、既存 fakes.py の哲学(名義型を継承しない)にそのまま乗れる
  • Tier 2(addon_utils.enable)で攻める案は、0.3 秒の Tier 1 スイートを保てない・CI の bpy wheel 依存を増やす・PropertyGroup 実物を通る経路は A-1-4 回帰テストが別途カバーする、の3点で見送り
  • 唯一 Blender 固有の挙動が要る factory.py:138-141 の「.values() を持つコレクション → 名前リスト変換」は FakeBpyCollection で固定

props.py A-1-4 は発注書の想定どおり Tier 2 で実物駆動(tests/blender/test_props_uniqueness.py)。addon_utils.enable(default_set=True) → 実物 prefs.patterns 上で add_element を駆動し、修正前の「Name.001.001 増殖」(commit 35690ab)の反転テストを含む 6 件。module 終了時に必ず disable、userpref は保存しません。

File は発注書の代替方針に切替。FileSelectEntry はヘッドレスで生成不能のため、純関数群(サニタイズ / Windows 予約名 / 拡張子 allowlist、file.py:114-178)を Tier 1 で固定(32 件)。FileRenameTarget 本体は Tier 3(手動)領域。

検証プロセス: ドメイン読込は並列リーダー3体、Tier 2 ターゲットテストはサブエージェントに分担、納品前にフレッシュなコンテキストの検証エージェントが 00-policy 準拠・無主張テスト・数字と実態の一致を独立検証(指摘は最終コミット c8020e6 で反映済み、重大指摘ゼロ)。

3. 発見事項(修正はしていません — 記録のみ)

  1. factory のエラー処理の非対称: pattern.validate() のエラーは error ログのみで握りつぶされ欠陥パターンがそのまま返る(factory.py:49-53 — raise がコメントアウト)。numeric_counter 欠落はここを素通りし、後段 parse_name の StopIteration(model.py:78-83 の FIXME)に到達し得る。一方、要素レベルの設定不備(text の items 空)は registry の ValueError(registry.py:156-157)が factory.py:68 の except (KeyError, TypeError) に捕まらず呼び出し元へ素通し
  2. facade の context 異常系の不統一: アドオン prefs が引けない context では構築自体が KeyError で死ぬ(facade.py:121 経由に例外処理なし)のに、context=None なら warning だけで空キャッシュ構築に成功する(facade.py:112-114)
  3. facade に実質到達不能の dead path: _remove_deleted_patterns の削除ループ(facade.py:155-158)は、常に先行する _synchronize_modified_patterns 内の整合性ループ(facade.py:136-139)が同じ削除を済ませるため到達不能。未カバー 6 行の正体(94% 止まりの理由)
  4. 非 ID 型の衝突無報告は KeyBlock / Strip にも同型で存在(characterization として固定済み): 直代入経路は衝突時に Blender が黙って .001 を付け、戻り値は常に "RENAMED"(ボーンの 03章 §2.4 と同じ機構)
  5. Blender 5.0.1 の API 事実: Sequence 系 API は完全消滅(strips/strips_all のみ)、new_effectframe_endlength に破壊的変更
  6. addon_utils.enable/disable サイクルは終了時に「Not freed memory blocks: 1 (61 bytes)」を残す(exit code・テスト結果への影響なし)

上記 1〜3 は dev-note/testing/01-phase-plan.md の「Phase 2.5 の新発見」にも記録済み。

4. 受け入れ条件チェックリスト

  • core/pattern/facade.pyfactory.py のカバレッジ 70% 以上 → 94% / 100%
  • props.py のユニーク化ロジックに回帰テストが存在する → test_props_uniqueness.py(6件、増殖バグの反転テスト含む)
  • Tier 2 ターゲットテストが ShapeKey / Strip / Collection をカバー(File は代替方針) → 各専用ファイル + Material 代表を test_id_targets.py に追記
  • 既存テストすべて緑のまま: uv run pytest(166 passed / 6 xfailed)と tests/blender/run.sh(39 passed)の両方ローカル確認済み
  • CI(unit + blender 両ジョブ)が PR 上で緑 → 確認済み(unit: 166 passed / 6 xfailed + カバレッジサマリー表示、blender: 39 passed — bpy wheel 環境でも addon_utils.enable による props_uniqueness 6 件を含め全件 PASSED)
  • 00-policy.md の全原則に準拠(検証エージェントが独立確認: conftest スタブ追加なし、1ファイル1主題、characterization 表記、日本語コメント、新規 xfail なし)
  • dev-note/testing/01-phase-plan.md の Phase 2.5 と dev-note/plans/2026-06-12-milestone.md §2 を実績で更新

制約の遵守

  • 製品コードの変更: ゼロ(テスト・CI ワークフロー・dev-note のみ)
  • 既存 xfail 6 件: 未変更
  • ストレッチ: CI unit ジョブに --cov のログ表示のみ追加(バッジ・ゲートなし)

🤖 Generated with Claude Code

Pluglug and others added 7 commits June 13, 2026 01:22
PatternFactory reads its input only through hasattr/getattr
(factory.py:103-141) and PatternFacade only needs prefs.patterns and
get_active_pattern() (facade.py:48-49, 121, 150, 179), so duck-typed
fakes in tests/unit/fakes.py cover both without real Blender.
Coverage: facade 23% -> 94%, factory 31% -> 100%.

Characterized along the way: pattern.validate() errors are swallowed
(factory.py:49-53) while element-level config errors leak as ValueError
past the except (KeyError, TypeError) at factory.py:68, and facade
construction dies on KeyError when addon prefs are missing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FileSelectEntry cannot be created headlessly, so per the order's
fallback plan the sanitize / validation / extension-allowlist functions
(targets/file.py:114-178) are pinned in tier 1 instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pins namespace/name APIs against real bpy.data, plus characterization
of silent .001 collision handling for the non-ID paths (KeyBlock,
Strip) mirroring the bone findings. Material is the minimal second ID
representative; collision details stay with the Object tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drives the real prefs.patterns via addon_utils.enable(default_set=True)
because the fixed logic lives inside a PropertyGroup method
(props.py:360-405). Includes the inversion test for the pre-fix
'Name.001.001' growth (commit 35690ab).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Log-only visualization; no badge, no gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Also checks off the two Phase 3 leftovers that are now factually done
(__tmp_ swap tests landed with A-2; target coverage finished by this
order).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- get_active_pattern None test now uses empty patterns, the only
  configuration the real prefs can produce (preferences.py:138-149
  clamps out-of-range indices)
- Import ADDON_ID instead of hardcoding it in the props tier-2 test
- Disambiguate base.py citations to core/contracts/base.py and soften
  an overclaiming strip docstring
- Record the unreachable _remove_deleted_patterns dead path
  (facade.py:136-139 always deletes first) in the phase plan

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Pluglug

Pluglug commented Jun 12, 2026

Copy link
Copy Markdown
Owner Author

司令塔レビュー(同一アカウントのため approve 不可、コメントで記録): 全受け入れ条件を独立検証で確認。

  • 製品コード変更ゼロ(git diff origin/main -- src/ = 0行)
  • Tier 1: 166 passed / 6 xfailed、Tier 2: 39 passed(ローカル実測)
  • facade 94% / factory 100%(--cov 実測、報告値と一致)
  • CI 4チェック SUCCESS
  • A-1-4 回帰テストの反転(Name.001.001 増殖の終止)とfixture規律(enable→必ずdisable)を精読確認
  • fakes.py の最小契約文書化、00-policy 準拠、発見事項5件の記録を確認

マージします。

@Pluglug
Pluglug merged commit 5528fbf into main Jun 12, 2026
4 checks passed
@Pluglug
Pluglug deleted the test/phase-2-5-coverage branch June 12, 2026 16:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant