# 機能設計書 69-ユニットテスト検出・追跡

## 概要

本ドキュメントは、serve-dにおけるユニットテスト検出・追跡機能の設計を記述する。DUBプロジェクト内のユニットテストを検出し、テストランナーに情報を提供する。`--provide test-runner` オプションが必要。

### 本機能の処理概要

**業務上の目的・背景**：D言語のプロジェクトではソースコード内にunittestブロックとしてユニットテストが記述される。エディタのテストエクスプローラ機能と連携し、プロジェクト内のユニットテストを自動的に検出・追跡することで、テストの実行・管理をエディタ上で直感的に行えるようにする。

**機能の利用シーン**：
- プロジェクトが追加された時に全ソースファイルのユニットテストをスキャンする
- ファイル保存時に変更されたファイルのユニットテストを再スキャンする
- served/rescanTests通知による手動テスト再スキャン

**主要な処理内容**：
1. プロジェクト利用可能時にテストプロジェクト情報を初期化してクライアントに通知する
2. プロジェクト追加時に全ソースファイルをスキャンしてユニットテストを検出する
3. ファイル保存時に変更されたファイルのユニットテストを再スキャンする
4. served/rescanTests通知で指定インスタンスのテストを再スキャンする
5. テスト情報をcoded/pushProjectTests通知でクライアントに配信する

**関連システム・外部連携**：
- DubComponentによるビルド設定・ソースファイル一覧の取得
- DscannerComponentによるシンボル情報（unittest検出）
- ModulemanComponentによるモジュール名の解析
- クライアントのテストエクスプローラ（coded/pushProjectTests通知）

**権限による制御**：`--provide test-runner` コマンドラインオプションにより機能の有効/無効が制御される。doTrackTestsフラグがfalseの場合は全処理がスキップされる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 69 | テストプロバイダー | 主画面 | DUBプロジェクト内のユニットテストを検出しテストランナーに情報を提供する |

## 機能種別

データ検出・追跡・通知

## 入力仕様

### 入力パラメータ

#### onPreviewProjectForUnittest
| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| instance | WorkspaceD.Instance | Yes | ワークスペースインスタンス | - |
| dir | string | Yes | ディレクトリパス | - |
| rootFolderUri | string | Yes | ルートフォルダURI | - |

#### onDidSaveCheckForUnittest
| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| params | DidSaveTextDocumentParams | Yes | 保存通知パラメータ | .dファイルのみ処理 |

#### rescanTests
| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| params | RescanTestsParams | Yes | 再スキャンパラメータ | params.uriで対象を指定（空なら全て） |

### 入力データソース

- `@onProjectAvailable` イベント（プロジェクト利用可能時）
- `@onAddedProject` イベント（プロジェクト追加時）
- `textDocument/didSave` LSP通知（ファイル保存時）
- `served/rescanTests` LSP通知（手動再スキャン）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| coded/pushProjectTests | UnittestProject | プロジェクトのユニットテスト情報をクライアントに配信 |

### 出力先

LSPクライアントへの通知（coded/pushProjectTests）。

## 処理フロー

### 処理シーケンス

```
1. プロジェクト利用可能イベント (onPreviewProjectForUnittest)
   └─ テスト追跡が有効（doTrackTests）なら空のプロジェクト情報を送信
2. プロジェクト追加イベント (onDidAddProjectForUnittest)
   └─ spawnFiberで非同期にrescanProjectを実行
3. rescanProject処理
   a. DubComponentからビルド設定を取得
   b. ソースファイル一覧を取得
   c. 各.d/.dpp/.diファイルに対してrescanFileを実行
   d. coded/pushProjectTestsで結果を通知
4. rescanFile処理
   a. ドキュメントシンボルを取得（provideDocumentSymbolsOld）
   b. SymbolKindEx.testのシンボルを抽出
   c. テスト名・コンテナ名・範囲をUnittestInfoに変換
   d. モジュール名を取得してUnittestModuleを構築
   e. projectのmodulesリストを更新（ソート順序を維持）
5. ファイル保存イベント (onDidSaveCheckForUnittest)
   └─ .dファイルの場合、spawnFiberで非同期にrescanFileを実行し結果を通知
```

### フローチャート

```mermaid
flowchart TD
    A[プロジェクト利用可能] --> B{doTrackTests?}
    B -->|No| Z[終了]
    B -->|Yes| C[空のUnittestProject通知]

    D[プロジェクト追加] --> E{doTrackTests?}
    E -->|No| Z
    E -->|Yes| F{DubComponent あり?}
    F -->|No| Z
    F -->|Yes| G[rescanProject 非同期実行]
    G --> H[DUBビルド設定からソースファイル一覧取得]
    H --> I[各.dファイルに対して rescanFile 実行]
    I --> J[coded/pushProjectTests 通知]

    K[textDocument/didSave] --> L{doTrackTests?}
    L -->|No| Z
    L -->|Yes| M{.dファイル?}
    M -->|No| Z
    M -->|Yes| N[rescanFile 非同期実行]
    N --> O[coded/pushProjectTests 通知]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-69-01 | テスト追跡有効チェック | doTrackTestsがfalseなら全処理をスキップ | 常時 |
| BR-69-02 | D言語ファイル限定 | .d, .dpp, .diファイルのみスキャン対象 | rescanProject内 |
| BR-69-03 | テストシンボル判定 | SymbolKindEx.testのシンボルのみユニットテストとして検出 | rescanFile内 |
| BR-69-04 | ソート順維持 | modules配列はモジュール名でソートされた状態を維持 | rescanFile内 |
| BR-69-05 | 一時ドキュメント管理 | ファイルシステムから読み込んだドキュメントはスコープ終了時にアンロード | rescanProject内 |
| BR-69-06 | テスト名の決定 | detail（テスト説明）がある場合はそれをname、なければシンボル名を使用 | rescanFile内 |

### 計算ロジック

特になし。

## データベース操作仕様

### 操作別データベース影響一覧

データベース操作はない。メモリ上のprojectTests辞書で管理。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | FileException | ファイル読み取り失敗 | warningログ出力してスキップ |
| - | Exception | ファイル分析失敗 | warningログ出力してスキップ |

### リトライ仕様

エラーが発生したファイルはスキップされ、次回の保存時または手動再スキャン時に再試行される。

## トランザクション仕様

トランザクション管理は不要。

## パフォーマンス要件

- spawnFiberで非同期実行されるため、UIブロッキングしない
- 各ファイルの分析は独立して実行される
- StopWatchで処理時間を計測しログに出力
- requiredLibdparsePageCount * 2 のスタックサイズでファイバーを生成

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

ソースコードの読み取りのみ。テスト結果の実行は行わない。

## 備考

- doTrackTestsフラグはapp.dで `--provide test-runner` オプション指定時にtrueに設定される
- projectTests（UnittestProject[DocumentUri]型）はモジュールレベルのグローバル辞書
- UnittestProject構造体にはworkspaceUri, name, modules, needsLoadフィールドがある
- coded/pushProjectTests通知でクライアントのテストエクスプローラを更新
- rescanTests通知でparams.uriが空の場合は全インスタンスを対象に再スキャン

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | test_provider.d | `source/served/commands/test_provider.d` | doTrackTestsフラグ（18行目）、projectTests辞書（20行目） |
| 1-2 | types.d | `source/served/types.d` | UnittestProject, UnittestModule, UnittestInfo型の定義 |

**読解のコツ**: `@onProjectAvailable` と `@onAddedProject` はserve-d独自のイベントハンドラUDA。ワークスペース管理イベントに対応する。

#### Step 2: プロジェクトイベントハンドラを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | test_provider.d | `source/served/commands/test_provider.d` | onPreviewProjectForUnittest（22-32行目）、onDidAddProjectForUnittest（34-46行目） |

**主要処理フロー**:
1. **22行目**: `@onProjectAvailable` でプロジェクト利用可能イベントにフック
2. **28行目**: rootUriの取得とプロジェクト辞書への登録
3. **31行目**: coded/pushProjectTestsで空プロジェクト通知
4. **34行目**: `@onAddedProject` でプロジェクト追加イベントにフック
5. **40行目**: spawnFiberでrescanProjectを非同期実行

#### 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行目） |

**rescanProjectの主要処理フロー**:
1. **142-143行目**: DubComponentからビルド設定取得
2. **152行目**: プロジェクト名設定
3. **155-184行目**: 各ソースファイルのスキャンループ
4. **186行目**: coded/pushProjectTests通知

**rescanFileの主要処理フロー**:
1. **96-97行目**: ドキュメント取得とシンボル一覧取得
2. **101-111行目**: テストシンボルの抽出とUnittestInfoへの変換
3. **113-115行目**: モジュール名の取得
4. **118-134行目**: moduels配列のソート済み更新

#### Step 4: ファイル保存時の処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | test_provider.d | `source/served/commands/test_provider.d` | onDidSaveCheckForUnittest（48-69行目） |

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

```
[プロジェクトイベント]
onPreviewProjectForUnittest() [test_provider.d:22-32]
    +-- rpc.notifyMethod("coded/pushProjectTests", ...)

onDidAddProjectForUnittest() [test_provider.d:34-46]
    +-- spawnFiber("Scan DUB project for unittests")
        +-- rescanProject(instance) [test_provider.d:137-194]
            +-- DubComponent.rootPackageBuildSettings()
            +-- documents.getOrFromFilesystem(uri)
            +-- rescanFile(project, documentId) [test_provider.d:94-135]
            |   +-- provideDocumentSymbolsOld()
            |   +-- ModulemanComponent.moduleName()
            +-- rpc.notifyMethod("coded/pushProjectTests", ...)

[ファイル保存イベント]
onDidSaveCheckForUnittest(params) [test_provider.d:48-69]
    +-- spawnFiber("Run didSave processors")
        +-- rescanFile(project, documentId)
        +-- rpc.notifyMethod("coded/pushProjectTests", ...)

[手動再スキャン]
rescanTests(params) [test_provider.d:71-92]
    +-- rescanProject(instance)
```

### データフロー図

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

@onProjectAvailable            ──▶  onPreviewProjectForUnittest()      ──▶  coded/pushProjectTests
@onAddedProject                ──▶  onDidAddProjectForUnittest()       ──▶  coded/pushProjectTests
textDocument/didSave           ──▶  onDidSaveCheckForUnittest()        ──▶  coded/pushProjectTests
served/rescanTests             ──▶  rescanTests()                      ──▶  coded/pushProjectTests
                                        |
DUBビルド設定                    ──▶  rescanProject()
ソースファイル（.d/.dpp/.di）    ──▶  rescanFile()
                                        +-- provideDocumentSymbolsOld()
                                        +-- ModulemanComponent.moduleName()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| test_provider.d | `source/served/commands/test_provider.d` | ソース | ユニットテスト検出・追跡の全処理（全196行） |
| symbol_search.d | `source/served/commands/symbol_search.d` | ソース | provideDocumentSymbolsOld - シンボル検出 |
| extension.d | `source/served/extension.d` | ソース | バックエンドインスタンス管理 |
| types.d | `source/served/types.d` | ソース | UnittestProject, UnittestModule, UnittestInfo型 |
| async.d | `source/served/utils/async.d` | ソース | spawnFiberユーティリティ |
