# 帳票設計書 76-コードカバレッジレポート

## 概要

本ドキュメントは、coverage-instrumentedバイナリ（カバレッジ計測用にビルドされたバイナリ）のテストカバレッジ情報をファイルに出力する帳票の設計書である。`pkg/util/coverage` パッケージとして提供される。

### 本帳票の処理概要

本帳票は、Goのカバレッジフレームワークを活用して、本番バイナリの実行中にコードカバレッジ情報を収集し、定期的にファイルにフラッシュする機能を提供する。

**業務上の目的・背景**：通常のユニットテストだけでなく、インテグレーションテストやE2Eテスト時に実際のバイナリのカバレッジを計測することは、テスト品質の評価と改善に有用である。本パッケージは、`coverage`ビルドタグ付きでビルドされたバイナリが実行中にカバレッジデータを収集し、指定したファイルに定期的に出力することで、実行時カバレッジの可視化を実現する。

**帳票の利用シーン**：E2Eテストやインテグレーションテスト時のカバレッジ計測に利用される。例えば、kube-apiserverやkubelet等のコンポーネントをcoverage-instrumentedバイナリとして起動し、テスト実行後にカバレッジファイルを収集・分析する場合に使用される。

**主要な出力内容**：
1. Goカバレッジプロファイル（coverprofile形式）
2. カバレッジ率サマリ（stdout出力、例: "coverage: 5% of statements"）
3. 定期的なフラッシュ（デフォルト5秒間隔）

**帳票の出力タイミング**：InitCoverage呼び出し後、設定された間隔（デフォルト5秒）で定期的にフラッシュされる。FlushCoverage関数の直接呼び出しでも即座にフラッシュ可能。

**帳票の利用者**：テストエンジニア、CI/CDパイプライン管理者、開発者

## 帳票種別

集計表（カバレッジプロファイル）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | CLI/テスト環境 | - | coverage-instrumentedバイナリの起動 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | Go coverprofile形式（テキスト） |
| 用紙サイズ | N/A |
| 向き | N/A |
| ファイル名 | 環境変数 KUBE_COVERAGE_FILE（デフォルト: /tmp/k8s-{name}.cov） |
| 出力方法 | ファイル書き込み（アトミック更新: tmpファイル -> rename） |
| 文字コード | UTF-8 |

### カバレッジプロファイル仕様

| 項目 | 内容 |
|-----|------|
| フォーマット | Go標準のcoverprofile形式 |
| 出力先 | KUBE_COVERAGE_FILEで指定されたパス |
| 一時ファイル | {出力先}.tmp |
| 更新方式 | 一時ファイルに書き込み後、os.Renameでアトミック置換 |
| フラッシュ間隔 | KUBE_COVERAGE_FLUSH_INTERVAL（デフォルト: 5秒） |

## 帳票レイアウト

### レイアウト概要

Go標準のcoverprofile形式で出力される。1行目はモード行、2行目以降は各ステートメントのカバレッジ情報。

```
mode: set
k8s.io/kubernetes/pkg/foo/bar.go:10.2,12.3 1 1
k8s.io/kubernetes/pkg/foo/bar.go:14.2,16.3 1 0
k8s.io/kubernetes/pkg/foo/baz.go:20.2,22.3 1 1
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | mode | カバレッジモード | Go testing framework | "mode: set" / "mode: count" / "mode: atomic" |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | ファイル:行.カラム | ソース位置（開始） | Go coverage instrumentation | {パッケージ/ファイル}:{行}.{カラム} | 可変 |
| 2 | 行.カラム | ソース位置（終了） | Go coverage instrumentation | ,{行}.{カラム} | 可変 |
| 3 | ステートメント数 | ブロック内のステートメント数 | Go coverage instrumentation | 整数 | 可変 |
| 4 | 実行回数 | ブロックの実行回数 | Go coverage instrumentation | 整数（0=未実行） | 可変 |

### フッター部

フッター部は存在しない。

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| coverageビルドタグ | `go build -tags coverage` でビルドされていること | Yes |
| KUBE_COVERAGE_FILE | 出力先ファイルパス（環境変数） | No（デフォルト: /tmp/k8s-{name}.cov） |
| KUBE_COVERAGE_FLUSH_INTERVAL | フラッシュ間隔（環境変数） | No（デフォルト: 5s） |

### ソート順

Go coverprofileのデフォルト順序（パッケージ・ファイル順）。

### 改ページ条件

該当なし。

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

### 参照テーブル一覧

本帳票はデータベースを参照しない。Go testing frameworkの内部カバレッジカウンタからデータを収集する。

| データソース | 用途 | 備考 |
|------------|------|------|
| Go coverage counters | 各ステートメントブロックの実行回数 | -test.coverprofileフラグで有効化 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| カバレッジ率 | (実行されたステートメント数 / 全ステートメント数) * 100 | Go testing frameworkのデフォルト | stdoutに "coverage: X% of statements" として出力 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[InitCoverage呼び出し] --> B[KUBE_COVERAGE_FILE環境変数読み込み]
    B --> C{環境変数設定あり?}
    C -->|Yes| D[指定パスを使用]
    C -->|No| E[デフォルトパス /tmp/k8s-{name}.cov]
    D --> F[KUBE_COVERAGE_FLUSH_INTERVAL読み込み]
    E --> F
    F --> G[flag.CommandLine.Parse: -test.coverprofile設定]
    G --> H[go wait.Forever: 定期フラッシュ開始]
    H --> I[FlushCoverage: ダミーテスト実行]
    I --> J[testing.MainStart + Run]
    J --> K[tmpファイルにcoverprofile書き込み]
    K --> L[os.Rename: アトミック置換]
    L --> M[次のフラッシュまで待機]
    M --> I
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ビルドタグなし | coverage未指定でInitCoverage呼び出し | panic("Called InitCoverage when not built with coverage instrumentation.") | -tags coverage でビルド |
| 無効なインターバル | KUBE_COVERAGE_FLUSH_INTERVALが不正 | panic("Invalid KUBE_COVERAGE_FLUSH_INTERVAL value; try something like '30s'.") | 有効なduration文字列を設定 |
| リネームエラー | os.Renameが失敗 | klog.Errorf("Couldn't move coverage file from %s to %s") | ファイルパス・権限確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 計測対象のステートメント数に依存（数千〜数十万行） |
| 目標出力時間 | フラッシュ間隔内に完了（デフォルト5秒以内） |
| 同時出力数上限 | 1（単一バイナリ内で1インスタンス） |

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

- カバレッジファイルにはソースコードのパス情報が含まれるため、本番環境での使用は推奨しない
- coverageビルドタグ付きバイナリはテスト環境でのみ使用すべき
- 出力先ファイルのデフォルトが/tmpであるため、マルチユーザ環境でのファイルアクセス権限に注意
- coverage_disabled.go（ビルドタグなし）ではInitCoverageはpanicし、FlushCoverageはno-opとなる

## 備考

- coverage.goはビルドタグ `//go:build coverage` が必要
- coverage_disabled.goはビルドタグ `//go:build !coverage` で、coverage未指定時のフォールバック
- FlushCoverage関数は空のテストスイート（tests=[], benchmarks=[], examples=[], fuzztargets=[]）をtesting.MainStartに渡し、Run()を呼ぶことでGoにカバレッジデータをディスクに書かせるトリックを使用
- fakeTestDeps構造体はtesting.testDepsインタフェースの空実装で、テスト依存を満たすためだけに存在する
- アトミック更新（tmpファイル作成 -> os.Rename）により、他プロセスからのファイル読み込み時にデータ破損を防止

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | coverage.go | `pkg/util/coverage/coverage.go` | パッケージ変数coverageFile（行34）で出力先パスの管理を理解 |
| 1-2 | fake_test_deps.go | `pkg/util/coverage/fake_test_deps.go` | fakeTestDeps構造体（行30）でtesting.testDepsインタフェースの空実装を確認 |

**読解のコツ**: このパッケージはGoのtesting frameworkの内部機能を活用するトリック的な実装。testing.MainStartにダミーのテストスイートを渡してRun()を呼ぶことで、カバレッジデータを書き出させている。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | coverage.go | `pkg/util/coverage/coverage.go` | InitCoverage関数（行44-68）で初期化処理の全体像を理解 |

**主要処理フロー**:
1. **行47-49**: KUBE_COVERAGE_FILE環境変数の読み込み、未設定時はデフォルト`/tmp/k8s-{name}.cov`
2. **行53-61**: KUBE_COVERAGE_FLUSH_INTERVAL環境変数の読み込み、デフォルト5秒
3. **行64**: flag.CommandLine.Parseで-test.coverprofileフラグを設定（出力先をtmpファイルに）
4. **行67**: go wait.Foreverで定期フラッシュを開始（goroutineで非同期実行）

#### Step 3: フラッシュ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | coverage.go | `pkg/util/coverage/coverage.go` | FlushCoverage関数（行73-93）でカバレッジデータのフラッシュロジックを理解 |
| 3-2 | coverage.go | `pkg/util/coverage/coverage.go` | tempCoveragePath関数（行38-40）で一時ファイルパスの生成を確認 |

**主要処理フロー**:
- **行77-80**: 空のテストスイート（tests, benchmarks, examples, fuzztargets）を作成
- **行84**: testing.MainStartにfakeTestDepsとダミーテストスイートを渡す
- **行85**: dummyRun.Run()でGoにカバレッジデータをtmpファイルに書かせる
- **行90-92**: os.RenameでtmpファイルをcoverageFileにアトミック移動

#### Step 4: ビルドタグなし時の挙動を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | coverage_disabled.go | `pkg/util/coverage/coverage_disabled.go` | ビルドタグ`!coverage`時のInitCoverage（panic）とFlushCoverage（no-op）を確認 |

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

```
コンポーネントバイナリ（例: kube-apiserver）
    |
    +-- InitCoverage(name) (coverage.go:44)
            |
            +-- os.Getenv("KUBE_COVERAGE_FILE") (coverage.go:47)
            +-- os.Getenv("KUBE_COVERAGE_FLUSH_INTERVAL") (coverage.go:55)
            +-- flag.CommandLine.Parse (coverage.go:64)
            +-- go wait.Forever(FlushCoverage, flushInterval) (coverage.go:67)
                    |
                    +-- FlushCoverage() (coverage.go:73) [定期実行]
                            |
                            +-- testing.MainStart(fakeTestDeps, [], [], [], []) (coverage.go:84)
                            +-- dummyRun.Run() (coverage.go:85)
                            +-- os.Rename(tempPath, coverageFile) (coverage.go:90)
```

### データフロー図

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

Go coverage counters      FlushCoverage()
(計測カウンタ) ----------> testing.MainStart + Run() --------+
                                                             |
環境変数                   os.Rename()                        +--> coverprofileファイル
KUBE_COVERAGE_FILE -----> (アトミック置換) -------------------+    (例: /tmp/k8s-apiserver.cov)
                                                             |
                                                             +--> stdout
                                                                  ("coverage: X% of statements")
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| coverage.go | `pkg/util/coverage/coverage.go` | ソース | カバレッジ収集・フラッシュ本体（coverage ビルドタグ） |
| coverage_disabled.go | `pkg/util/coverage/coverage_disabled.go` | ソース | coverageなし時のフォールバック（!coverage ビルドタグ） |
| fake_test_deps.go | `pkg/util/coverage/fake_test_deps.go` | ソース | testing.testDepsインタフェースの空実装 |
