# 帳票設計書 3-コードカバレッジレポート（LCOV形式）

## 概要

本ドキュメントは、Bunテストランナーが出力するLCOV形式のコードカバレッジレポートの設計仕様を記載したものである。

### 本帳票の処理概要

コードカバレッジレポート（LCOV形式）は、テスト実行時のコードカバレッジ情報をLCOV形式のファイルとして出力する帳票である。行ごとの実行回数、関数の実行状況を記録し、genhtml等の外部ツールでHTML化可能な標準フォーマットで提供する。

**業務上の目的・背景**：LCOVはLinux Test Projectで開発されたカバレッジデータフォーマットであり、多くのカバレッジツールやCI/CDサービスでサポートされている。Codecov、Coveralls、SonarQubeなどのカバレッジ可視化サービスとの連携が主目的である。genhtmlツールを使用したHTMLレポート生成により、ブラウザでの視覚的なカバレッジ確認も可能となる。

**帳票の利用シーン**：CI/CDパイプラインでのカバレッジデータ収集とサードパーティサービスへのアップロード、HTMLカバレッジレポート生成のための中間データ、複数テスト実行結果のカバレッジデータマージ、履歴的なカバレッジトレンド分析に利用される。

**主要な出力内容**：
1. ソースファイルパス（SF行）
2. 関数の総数と実行された関数数（FNF/FNH行）
3. 行ごとの実行回数（DA行）
4. 実行可能行数と実行された行数（LF/LH行）
5. レコード区切り（end_of_record）

**帳票の出力タイミング**：`bun test --coverage --coverage-reporter=lcov`コマンド実行完了時、または`bunfig.toml`の`test.coverageReporter=lcov`設定時に出力される。

**帳票の利用者**：DevOpsエンジニア、CI/CDパイプライン、カバレッジ可視化サービス、品質管理チーム

## 帳票種別

テスト結果レポート / LCOV形式ファイル

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| CLI-001 | コマンドライン | `bun test --coverage --coverage-reporter=lcov` | テスト実行完了時に自動出力 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | LCOV（テキストベース） |
| 用紙サイズ | N/A（電子ファイル） |
| 向き | N/A |
| ファイル名 | `{coverageDir}/lcov.info`（デフォルト: `coverage/lcov.info`） |
| 出力方法 | ファイル書き込み |
| 文字コード | UTF-8 |

### LCOV形式固有設定

| 項目 | 内容 |
|-----|------|
| テスト名 | 空（TN:のみ） |
| ブランチカバレッジ | 未対応（JavaScriptCoreの制限） |
| 関数名 | 未対応（JavaScriptCoreの制限） |

## 帳票レイアウト

### レイアウト概要

LCOV形式は行指向のテキストフォーマットで、各レコードがファイル単位のカバレッジ情報を表す。

```
TN:
SF:path/to/file1.ts
FNF:10
FNH:8
DA:1,5
DA:2,3
DA:5,0
LF:20
LH:18
end_of_record
TN:
SF:path/to/file2.ts
...
end_of_record
```

### レコード構成

| No | 行タイプ | 説明 | データ取得元 | 表示形式 |
|----|---------|------|-------------|---------|
| 1 | TN: | テスト名（空値） | 固定 | `TN:` |
| 2 | SF: | ソースファイルパス | report.source_url（相対パス） | `SF:{path}` |
| 3 | FNF: | 関数の総数（Functions Found） | report.functions.items.len | `FNF:{count}` |
| 4 | FNH: | 実行された関数数（Functions Hit） | functions_which_have_executed.count() | `FNH:{count}` |
| 5 | DA: | 行データ（Data Line） | executable_lines, line_hits | `DA:{line},{hits}` |
| 6 | LF: | 実行可能行数（Lines Found） | executable_lines.count() | `LF:{count}` |
| 7 | LH: | 実行された行数（Lines Hit） | lines_which_have_executed.count() | `LH:{count}` |
| 8 | end_of_record | レコード終端 | 固定 | `end_of_record` |

### DA行の詳細

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | line | 行番号（1始まり） | イテレータのインデックス + 1 | 整数 |
| 2 | hits | 実行回数 | line_hits[line] | 整数（0以上） |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| --coverage | CLIオプションでカバレッジを有効化 | No |
| --coverage-reporter=lcov | LCOVレポーターを指定 | Yes（LCOV出力時） |
| bunfig.toml test.coverageReporter | 配列または文字列で"lcov"を含む | No |
| coveragePathIgnorePatterns | 除外パターンにマッチしないこと | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | ファイルパス | 昇順（アルファベット順） |
| 2 | DA行の行番号 | 昇順（行番号順） |

### 改ページ条件

N/A（テキストファイルのため改ページなし。ファイル単位でend_of_recordで区切り）

## データベース参照仕様

### 参照テーブル一覧

N/A（データベースを使用しない。JavaScriptCoreのコントロールフロープロファイラからデータ取得）

### データ構造

#### CodeCoverage.Report構造体（共通）

| フィールド | 型 | 用途 |
|-----------|------|------|
| source_url | ZigString.Slice | ソースファイルパス |
| executable_lines | Bitset | 実行可能な行のビットセット |
| lines_which_have_executed | Bitset | 実行された行のビットセット |
| line_hits | BabyList(u32) | 行ごとの実行回数 |
| functions | ArrayListUnmanaged(Block) | 関数ブロック情報 |
| functions_which_have_executed | Bitset | 実行された関数のビットセット |
| total_lines | u32 | 総行数 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| FNF（関数総数） | functions.items.len | 整数 | - |
| FNH（実行関数数） | functions_which_have_executed.count() | 整数 | - |
| LF（実行可能行数） | executable_lines.count() | 整数 | - |
| LH（実行行数） | lines_which_have_executed.count() | 整数 | - |
| DA行番号 | イテレータインデックス + 1 | 整数 | 1始まり |
| DA実行回数 | line_hits[line] | 整数 | 0以上 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[テスト実行完了] --> B{coverage有効?}
    B -->|No| Z[終了]
    B -->|Yes| C{coverageReporter=lcov?}
    C -->|No| Z
    C -->|Yes| D[coverageDirの存在確認・作成]
    D --> E[一時ファイル作成]
    E --> F[ByteRangeMapping.mapからエントリ取得]
    F --> G[ファイルパスでソート]
    G --> H{coveragePathIgnorePatterns?}
    H -->|マッチ| I[スキップ]
    H -->|マッチしない| J[Report.generate]
    J --> K[Lcov.writeFormat]
    K --> L[TN:出力]
    L --> M[SF:{path}出力]
    M --> N[FNF/FNH出力]
    N --> O[DA行ループ出力]
    O --> P[LF/LH出力]
    P --> Q[end_of_record出力]
    Q --> R{次のファイル?}
    R -->|Yes| H
    R -->|No| S[バッファフラッシュ]
    S --> T[一時ファイルをlcov.infoにリネーム]
    T --> Z
    I --> R
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ディレクトリ作成失敗 | coverageDirの親ディレクトリが存在しない等 | "Failed to create lcov file" | パス設定の確認 |
| ファイル作成失敗 | ディスク容量不足、権限不足 | "Failed to create lcov file" | ディスク容量・権限の確認 |
| ファイル書き込み失敗 | I/Oエラー | "Failed to save lcov.info file" | ディスク状態の確認 |
| リネーム失敗 | 一時ファイルからlcov.infoへの移動失敗 | "Failed to save lcov.info file" | ファイルシステム状態の確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 数百〜数千ファイル |
| 目標出力時間 | テスト実行時間の5%未満 |
| 同時出力数上限 | 1（シングルスレッド出力） |
| バッファサイズ | 64KB |

## セキュリティ考慮事項

- 出力ディレクトリはユーザー指定のため、ディレクトリトラバーサルに注意
- ファイルパスはプロジェクトルートからの相対パスで記録
- 一時ファイル使用によりアトミックな書き込みを実現（部分的なファイルが残らない）
- node_modulesディレクトリは自動的に除外

## 備考

- LCOV形式はgenhtml（LCOVパッケージ）でHTML化可能
- ブランチカバレッジ（BRDA/BRF/BRH行）はJavaScriptCoreの制限により未対応
- 関数名（FN行）はJavaScriptCoreの制限により未対応
- 一時ファイルを使用してアトミックな書き込みを実現
- coverageDirのデフォルトは`coverage`ディレクトリ
- 複数のcoverageReporter指定時は両方出力可能（text, lcov）

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

カバレッジデータの収集と保持に使用される構造体を理解することが重要（テキスト形式と共通）。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | CodeCoverage.zig | `src/sourcemap/CodeCoverage.zig` | Report構造体（17-26行目）- カバレッジデータの核となる構造 |
| 1-2 | CodeCoverage.zig | `src/sourcemap/CodeCoverage.zig` | line_hitsフィールド（21行目）- 行ごとの実行回数を保持するBabyList |

**読解のコツ**: LCOV形式のDA行出力にはline_hitsが重要。Bitsetではなく実際の実行回数を記録。

#### Step 2: LCOV出力処理を理解する

LCOV形式固有の出力ロジックを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | CodeCoverage.zig | `src/sourcemap/CodeCoverage.zig` | Lcov.writeFormat関数（210-262行目）- LCOV形式出力の中核 |

**主要処理フロー**:
1. **221-223行目**: TN:（空）出力
2. **225-227行目**: SF:{filename}出力
3. **232-233行目**: FNF:{count}出力
4. **236行目**: FNH:{count}出力
5. **240-242行目**: executable_linesのクローン作成
6. **248-252行目**: DA:{line},{hits}のループ出力
7. **255行目**: LF:{count}出力
8. **258行目**: LH:{count}出力
9. **260行目**: end_of_record出力

#### Step 3: ファイル出力制御を理解する

printCodeCoverage内のLCOV出力制御ロジックを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | test_command.zig | `src/cli/test_command.zig` | printCodeCoverage関数のLCOV部分（1078-1138行目） |
| 3-2 | test_command.zig | `src/cli/test_command.zig` | LCOV書き込みとリネーム（1227-1244行目） |

**主要処理フロー**:
- **1083-1092行目**: coverageDirの存在確認・作成（mkdirRecursive）
- **1094-1098行目**: 一時ファイル名の生成（.lcov.info.{random}.tmp）
- **1100-1127行目**: 一時ファイルのオープンとバッファライター作成
- **1176-1182行目**: Lcov.writeFormatの呼び出し
- **1228行目**: バッファのフラッシュ
- **1229行目**: 一時ファイルのクローズ
- **1231-1243行目**: 一時ファイルをlcov.infoにリネーム（moveFileZ）

#### Step 4: 設定と有効化を理解する

LCOV出力の有効化と設定の読み込みを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | bunfig.zig | `src/bunfig.zig` | test.coverageReporter設定（303-331行目）- "lcov"文字列のパース |
| 4-2 | bunfig.zig | `src/bunfig.zig` | test.coverageDir設定（333-336行目）- 出力ディレクトリ設定 |
| 4-3 | Arguments.zig | `src/cli/Arguments.zig` | --coverage-dir引数（226行目） |

**主要処理フロー**:
- **305行目**: coverageReporter設定時にデフォルトをリセット（text=false, lcov=false）
- **309-310行目**: "lcov"文字列マッチでlcov=true設定
- **319-327行目**: 配列形式での複数レポーター指定対応

### プログラム呼び出し階層図

```
TestCommand.exec (1307行目)
    │
    ├─ ctx.test_options.coverage.enabled チェック (1420行目)
    │
    ├─ vm.transpiler.options.code_coverage = true (1421行目)
    │      └─ JavaScriptCore コントロールフロープロファイラ有効化
    │
    ├─ runAllTests (1541行目)
    │      └─ テスト実行中にカバレッジデータ収集
    │
    └─ generateCodeCoverage (1546行目)
           │
           ├─ ByteRangeMapping.map からエントリ取得 (976-977行目)
           │
           └─ printCodeCoverage (997行目)
                  │
                  ├─ coverageDirの作成 (1083-1092行目)
                  │
                  ├─ 一時ファイルオープン (1100-1127行目)
                  │
                  ├─ Report.generate (1159行目)
                  │
                  ├─ Lcov.writeFormat (1176-1182行目)
                  │      ├─ TN:/SF: 出力 (221-227行目)
                  │      ├─ FNF:/FNH: 出力 (232-236行目)
                  │      ├─ DA: ループ出力 (248-252行目)
                  │      └─ LF:/LH:/end_of_record 出力 (255-260行目)
                  │
                  └─ ファイルリネーム (1231-1243行目)
```

### データフロー図

```
[入力]                         [処理]                              [出力]

JavaScriptCore              ─────▶ ByteRangeMapping               ─────▶ 一時ファイル
Control Flow Profiler              └─ 行オフセット計算                    .lcov.info.xxx.tmp

テスト対象                  ─────▶ Report.generate                 ─────▶ LCOV形式
ソースコード                       ├─ line_hits構築                       データ
                                   └─ 実行回数カウント

bunfig.toml設定             ─────▶ CodeCoverageOptions             ─────▶ coverage/lcov.info
(coverageDir)                      └─ 出力ディレクトリ決定                 (アトミックリネーム)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| CodeCoverage.zig | `src/sourcemap/CodeCoverage.zig` | ソース | カバレッジデータ構造、Lcov.writeFormat関数 |
| test_command.zig | `src/cli/test_command.zig` | ソース | テストコマンド実行、LCOV出力制御、ファイル書き込み |
| bunfig.zig | `src/bunfig.zig` | ソース | bunfig.tomlのcoverageReporter、coverageDir設定パース |
| Arguments.zig | `src/cli/Arguments.zig` | ソース | --coverage-reporter, --coverage-dir等のCLI引数定義 |
