# 帳票設計書 5-ユニットテスト一覧レポート

## 概要

本ドキュメントは、serve-dのユニットテスト一覧レポート機能の設計を記述する。プロジェクト内のD言語ソースファイルを解析し、ユニットテスト（unittest）の一覧をモジュール単位で出力する帳票である。

### 本帳票の処理概要

ユニットテスト一覧レポートは、DUBプロジェクト内の全ソースファイルを走査してunittestブロックを検出し、プロジェクト単位・モジュール単位でテスト一覧をLSPクライアントに通知する帳票である。

**業務上の目的・背景**：D言語プロジェクト内のユニットテストを自動的に検出し、テストランナーUIに提供することで、開発者がテストの存在を把握し、個別にテストを実行できるようにする。コードの品質保証プロセスにおいて、テストカバレッジの可視化と効率的なテスト実行を支援する。

**帳票の利用シーン**：`--provide test-runner`オプションを付けてserve-dを起動した場合に有効になる。プロジェクトの追加時に全ファイルをスキャンし、ファイル保存時に該当ファイルを再スキャンする。また、`served/rescanTests`通知で手動再スキャンも可能。

**主要な出力内容**：
1. プロジェクト情報（workspaceUri、パッケージ名）
2. モジュール一覧（モジュール名、URI）
3. 各モジュール内のユニットテスト一覧（テストID、テスト名、コンテナ名、ソースコード範囲）

**帳票の出力タイミング**：プロジェクト追加時（初回スキャン）、D言語ファイル保存時（増分更新）、served/rescanTests通知受信時に出力される。`coded/pushProjectTests`通知としてクライアントに送信される。

**帳票の利用者**：D言語開発者（テストランナーUIを使用するプログラマ）

## 帳票種別

テストレポート（LSP通知）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | テストランナーUI | `coded/pushProjectTests` 通知 | プロジェクト追加・ファイル保存・手動再スキャン時 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JSON-RPC（LSPプロトコル） |
| 用紙サイズ | N/A（デジタル出力） |
| 向き | N/A |
| ファイル名 | N/A（ストリーム出力） |
| 出力方法 | coded/pushProjectTests通知 |
| 文字コード | UTF-8 |

### PDF固有設定

N/A

### Excel固有設定

N/A

## 帳票レイアウト

### レイアウト概要

UnittestProject構造体としてプロジェクト単位のテスト一覧を出力する。

```
┌──────────────────────────────────────────┐
│  UnittestProject                          │
├──────────────────────────────────────────┤
│  workspaceUri: DocumentUri                │
│  name: string (パッケージ名)              │
│  needsLoad: bool                          │
│  modules: UnittestModule[]                │
│    ├─ moduleName: string                  │
│    ├─ uri: DocumentUri                    │
│    └─ tests: UnittestInfo[]               │
│        ├─ id: string                      │
│        ├─ name: string                    │
│        ├─ containerName: string           │
│        └─ range: TextRange                │
└──────────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | workspaceUri | ワークスペースURI | instance.cwd.uriFromFile | URI文字列 |
| 2 | name | パッケージ名 | settings.packageName | 文字列 |
| 3 | needsLoad | 読み込み必要フラグ | 初期値true、スキャン後false | ブール値 |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | moduleName | モジュール名 | ModulemanComponent.moduleName()またはファイル名 | 文字列 | - |
| 2 | uri | ソースファイルURI | uriFromFile(fullPath) | URI文字列 | - |
| 3 | id | テストID | SymbolInformationExのname | 文字列 | - |
| 4 | name | テスト名 | SymbolInformationExのdetail（あればdetail、なければname） | 文字列 | - |
| 5 | containerName | コンテナ名 | SymbolInformationExのcontainerName | 文字列 | - |
| 6 | range | テストのソースコード範囲 | SymbolInformationExのlocation.range | TextRange | - |

### フッター部

N/A

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| テストランナー有効 | doTrackTests == true（--provide test-runnerオプション） | Yes |
| DUBコンポーネント有効 | instance.has!DubComponentがtrueであること | Yes |
| D言語ファイル | ファイル拡張子が.d、.dpp、.diのいずれか | Yes |
| テスト種別シンボル | extendedType == SymbolKindEx.test であるシンボルのみ抽出 | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | moduleName | 昇順（assumeSorted使用） |

### 改ページ条件

N/A

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

### 参照テーブル一覧

本帳票はデータベースを使用しない。ソースコードの解析結果を使用する。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| N/A | - | - |

### テーブル別参照項目詳細

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| moduleName | ModulemanComponent.moduleName(rawText)、取得できない場合は"(file) " + baseName | - | 行113-115 |
| name | test.detail.length ? test.detail : test.name | - | 行107-108 |
| modules配列の更新 | trisectによるソート済み配列への挿入/更新/削除 | - | 行118-134 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A1[プロジェクト追加] --> B1[onDidAddProjectForUnittest]
    A2[ファイル保存] --> B2[onDidSaveCheckForUnittest]
    A3[手動再スキャン] --> B3[rescanTests]
    B1 --> C{doTrackTests?}
    B2 --> C
    B3 --> C
    C -->|No| Z[終了]
    C -->|Yes| D{スキャン種別}
    D -->|プロジェクト全体| E[rescanProject]
    D -->|単一ファイル| F[rescanFile]
    E --> G[DUBソースファイル一覧取得]
    G --> H[各ファイルを解析]
    H --> F
    F --> I[provideDocumentSymbolsOld呼び出し]
    I --> J[testタイプのシンボルをフィルタ]
    J --> K[UnittestInfo生成]
    K --> L[UnittestModule生成]
    L --> M[projectTestsに格納]
    M --> N[coded/pushProjectTests通知送信]
    N --> Z
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ファイル読み込み失敗 | ソースファイルが存在しないまたは読み込み不可 | warningログ: "Failed to read file {path} for tests: {e}" | 該当ファイルをスキップ（行167-169） |
| ファイル解析失敗 | ソースファイルの構文解析に失敗 | warningログ: "Failed to analyze file {path} for tests: {e}" | 該当ファイルをスキップ（行181-183） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | プロジェクト全体で数十〜数百モジュール |
| 目標出力時間 | rescanProjectのStopWatchでログ記録（行149-193） |
| 同時出力数上限 | spawnFiberにより非同期実行（requiredLibdparsePageCount * 2のスタック） |

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

- ソースファイルの読み込みのみ行い、外部リソースへのアクセスは行わない
- 一時読み込みしたドキュメントはスコープ終了時にアンロード（行172-174）

## 備考

- doTrackTestsフラグは`--provide test-runner`オプションで設定される（行18）
- projectTestsはDocumentUriをキーとしたグローバル連想配列で管理（行20）
- rescanFileではtrisect（3分割検索）を使用してソート済みモジュール配列を効率的に更新（行118-134）
- rescanProjectではStopWatchで処理時間を計測し、infoログに出力（行149, 188-193）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | protoext.d | `source/served/lsp/protoext.d` | UnittestProject構造体（行279-293）: workspaceUri, name, modules, needsLoad |
| 1-2 | protoext.d | `source/served/lsp/protoext.d` | UnittestModule構造体（行295-300）: moduleName, uri, tests |
| 1-3 | protoext.d | `source/served/lsp/protoext.d` | UnittestInfo構造体（行302-307）: id, name, containerName, range |

**読解のコツ**: UnittestProjectはmodules配列をmoduleNameでソート済みとして管理する。更新時にtrisectを使用してソート状態を維持する。

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

3つのイベントハンドラがエントリーポイントとなる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | test_provider.d | `source/served/commands/test_provider.d` | onPreviewProjectForUnittest（行22-32）: @onProjectAvailable、needsLoad=trueのプロジェクト通知 |
| 2-2 | test_provider.d | `source/served/commands/test_provider.d` | onDidAddProjectForUnittest（行34-46）: @onAddedProject、プロジェクト全体スキャン |
| 2-3 | test_provider.d | `source/served/commands/test_provider.d` | onDidSaveCheckForUnittest（行48-69）: @protocolNotification("textDocument/didSave")、単一ファイル再スキャン |

#### Step 3: スキャン処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | test_provider.d | `source/served/commands/test_provider.d` | rescanProject関数（行137-194）: プロジェクト全体のスキャン |
| 3-2 | test_provider.d | `source/served/commands/test_provider.d` | rescanFile関数（行94-135）: 単一ファイルのスキャンとモジュール配列更新 |

**主要処理フロー（rescanFile）**:
- **行97**: provideDocumentSymbolsOldでシンボル一覧取得
- **行101-111**: testタイプのシンボルをフィルタしてUnittestInfo生成
- **行113-115**: モジュール名取得
- **行118-134**: trisectによるソート済み配列の効率的更新

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

```
onPreviewProjectForUnittest()              [test_provider.d:22]
    +-- rpc.notifyMethod("coded/pushProjectTests")  [test_provider.d:31]

onDidAddProjectForUnittest()               [test_provider.d:34]
    +-- rescanProject(instance)             [test_provider.d:44]
            +-- DubComponent.rootPackageBuildSettings() [test_provider.d:143]
            +-- [foreach sourceFile]
            |       +-- documents.getOrFromFilesystem() [test_provider.d:165]
            |       +-- rescanFile(project, textDoc)    [test_provider.d:178]
            |               +-- provideDocumentSymbolsOld() [test_provider.d:97]
            |               +-- ModulemanComponent.moduleName() [test_provider.d:113]
            +-- rpc.notifyMethod("coded/pushProjectTests") [test_provider.d:186]

onDidSaveCheckForUnittest()                [test_provider.d:48]
    +-- rescanFile(project, textDoc)        [test_provider.d:65]
    +-- rpc.notifyMethod("coded/pushProjectTests") [test_provider.d:67]
```

### データフロー図

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

DUBソースファイル一覧 ──────> rescanProject()           ──> UnittestProject
(settings.sourceFiles)        (全ファイルスキャン)

D言語ソースファイル ────────> provideDocumentSymbolsOld() ──> SymbolInformationEx[]
(.d, .dpp, .di)               (シンボル解析)

SymbolInformationEx[] ──────> rescanFile()              ──> UnittestModule
(testタイプフィルタ)           (テスト情報抽出)

UnittestProject ────────────> rpc.notifyMethod()        ──> coded/pushProjectTests通知
                                                            (テストランナーUI)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| test_provider.d | `source/served/commands/test_provider.d` | ソース | テスト一覧の主処理（スキャン・通知） |
| symbol_search.d | `source/served/commands/symbol_search.d` | ソース | provideDocumentSymbolsOld関数（シンボル解析） |
| protoext.d | `source/served/lsp/protoext.d` | ソース | UnittestProject/Module/Info構造体定義 |
| types.d | `source/served/types.d` | ソース | 共通型定義 |
