# 画面設計書 69-テストプロバイダー

## 概要

本ドキュメントは、serve-d LSPサーバーのテストプロバイダー機能の画面設計書である。DUBプロジェクト内のユニットテストを検出し、テストランナーにテスト情報を提供する。

### 本画面の処理概要

本機能は、D言語プロジェクト内のユニットテストを自動検出し、テストの一覧情報をクライアントに通知する機能を提供する。

**業務上の目的・背景**：D言語にはビルトインのunittest機能があり、ソースコード内にテストコードを直接記述できる。IDE/エディタでこれらのユニットテストを一覧表示し、個別に実行できるようにするため、テストの検出と追跡が必要である。本機能は `--provide test-runner` オプションでserve-dを起動した場合に有効になり、ファイル保存時にテストの再スキャンを自動的に実行する。

**画面へのアクセス方法**：本機能は自動的に動作する。`--provide test-runner` でserve-dを起動すると、プロジェクトの読み込み時およびファイル保存時にテスト情報が `coded/pushProjectTests` 通知としてクライアントに自動送信される。手動トリガーとして `served/rescanTests` 通知も利用可能。

**主要な操作・処理内容**：
1. プロジェクト読み込み時にDUBプロジェクトの全ソースファイルをスキャンしユニットテストを検出する
2. ファイル保存時に変更されたファイルのテスト情報を再スキャンする
3. 検出されたテスト情報を `coded/pushProjectTests` 通知でクライアントに送信する
4. `served/rescanTests` 通知による手動再スキャンに対応する

**画面遷移**：
- 遷移元：プロジェクト読み込み、textDocument/didSave
- 遷移先：coded/pushProjectTests通知をクライアントに送信

**権限による表示制御**：`--provide test-runner` オプションでserve-dを起動した場合のみ有効（`doTrackTests` フラグで制御）。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 69 | ユニットテスト検出・追跡 | 主機能 | DUBプロジェクト内のユニットテストを検出しテストランナーに情報を提供する |
| 10 | ドキュメント保存 (textDocument/didSave) | 補助機能 | ファイル保存時にユニットテストの再スキャンがトリガーされる |

## 画面種別

バックグラウンド処理 / LSP通知（テスト情報プッシュ型）

## URL/ルーティング

| メソッド | 種別 | 説明 |
|---------|------|------|
| coded/pushProjectTests | 通知（サーバー→クライアント） | テスト情報のプッシュ |
| served/rescanTests | 通知（クライアント→サーバー） | 手動テスト再スキャン |

## 入出力項目

### served/rescanTests パラメータ

| 項目名 | 型 | 必須 | 説明 |
|--------|------|------|------|
| uri | string | いいえ | 再スキャン対象のプロジェクトURI（空で全プロジェクト） |

### coded/pushProjectTests 通知データ（UnittestProject）

| 項目名 | 型 | 説明 |
|--------|------|------|
| workspaceUri | DocumentUri | ワークスペースのURI |
| name | string | プロジェクト名（DUBパッケージ名） |
| modules | UnittestModule[] | テストを含むモジュールの配列 |
| needsLoad | bool | ロード待ちフラグ |

### UnittestModule構造

| 項目名 | 型 | 説明 |
|--------|------|------|
| moduleName | string | モジュール名 |
| uri | DocumentUri | ファイルのURI |
| tests | UnittestInfo[] | テスト情報の配列 |

### UnittestInfo構造

| 項目名 | 型 | 説明 |
|--------|------|------|
| id | string | テストの識別子（test.name） |
| name | string | テストの表示名（test.detailがあればそれ、なければtest.name） |
| containerName | string | テストを含むコンテナの名前 |
| range | TextRange | テストのソースコード上の範囲 |

## 表示項目

クライアント側でテストエクスプローラーUIに使用される。プロジェクト > モジュール > テスト の階層構造で表示される。

## イベント仕様

### 1-プロジェクト利用可能時（onPreviewProjectForUnittest）

1. doTrackTestsがfalseの場合はスキップする
2. プロジェクトのルートURIからUnittestProject構造体を取得または新規作成する（needsLoad=true）
3. `coded/pushProjectTests` 通知でクライアントに送信する（ロード待ち状態）

### 2-プロジェクト追加時（onDidAddProjectForUnittest）

1. doTrackTestsがfalseの場合はスキップする
2. DubComponentが存在しない場合はスキップする
3. spawnFiberで非同期処理を開始する
4. `rescanProject` でプロジェクト全体のテストをスキャンする

### 3-ファイル保存時（onDidSaveCheckForUnittest）

1. doTrackTestsがfalseの場合はスキップする
2. .dファイル以外はスキップする
3. DubComponentを持つ最適なインスタンスを取得する
4. spawnFiberで非同期処理を開始する
5. `rescanFile` で保存されたファイルのテスト情報を再スキャンする
6. `coded/pushProjectTests` 通知でクライアントに送信する

### 4-手動再スキャン（served/rescanTests）

1. URIが指定されている場合は対応するインスタンスのみ、指定されていない場合は全インスタンスを対象とする
2. LazyInstanceの場合、アクセスされていない（didCallAccess=false）かつURI未指定のインスタンスはスキップする
3. 対象インスタンスに対して `rescanProject` を実行する

### 5-プロジェクト全体スキャン（rescanProject）

1. DubComponentからルートパッケージのビルド設定を取得する
2. ソースファイル一覧を走査する
3. .d, .dpp, .di ファイルのみを対象とする
4. 各ファイルについてdocumentsから取得（なければファイルシステムから読み込み）
5. `rescanFile` で各ファイルのテストを検出する
6. `coded/pushProjectTests` 通知を送信する
7. スキャン所要時間をログに出力する

### 6-ファイル単位スキャン（rescanFile）

1. `provideDocumentSymbolsOld` でドキュメントシンボルを取得する
2. extendedTypeが `test` のシンボルのみをフィルタする
3. テスト情報（id, name, containerName, range）を構築する
4. ModulemanComponentでモジュール名を取得する
5. UnittestModule構造体を構築し、プロジェクトのモジュール一覧を更新する

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| プロジェクトスキャン | projectTests（インメモリ） | INSERT/UPDATE | プロジェクトのテスト情報を登録・更新 |
| ファイル保存 | projectTests（インメモリ） | UPDATE | 変更ファイルのテスト情報を更新 |

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|---------|--------------|
| なし（ログ） | 警告 | ファイル読み込み失敗時 | "Failed to read file {fullPath} for tests: {error}" |
| なし（ログ） | 警告 | テスト解析失敗時 | "Failed to analyze file {fullPath} for tests: {error}" |
| なし（ログ） | 情報 | プロジェクトスキャン完了時 | "Found {count} modules with tests in {name} ({uri}) in {time}" |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| ファイル読み込み失敗（FileException） | warningログ出力しスキップ |
| テスト解析失敗（Exception） | warningログ出力しスキップ |
| DubComponentが未ロード | 処理をスキップ |
| doTrackTestsがfalse | 全処理をスキップ |

## 備考

- `doTrackTests` グローバル変数は app.d で `--provide test-runner` オプションが指定された場合にtrueに設定される
- テストの検出はDScannerのシンボル解析を使用し、`SymbolKindEx.test` タイプのシンボルをユニットテストとして識別する
- プロジェクトスキャン時にはファイバーに通常の2倍のスタックサイズ（requiredLibdparsePageCount * 2）を割り当てる
- モジュール一覧はソート済み状態で管理され、`trisect` による高速な更新が行われる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | test_provider.d | `source/served/commands/test_provider.d` | UnittestProject, UnittestModule, UnittestInfo構造体（protoext.dで定義されている可能性） |
| 1-2 | test_provider.d | `source/served/commands/test_provider.d` | 行18のdoTrackTestsフラグ、行20のprojectTestsグローバル変数 |

**読解のコツ**: `projectTests` は `UnittestProject[DocumentUri]` 型の連想配列で、プロジェクトURI単位でテスト情報を管理する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | test_provider.d | `source/served/commands/test_provider.d` | `onPreviewProjectForUnittest`（行22-32）、`onDidAddProjectForUnittest`（行34-46）がプロジェクトスキャンの起点 |
| 2-2 | test_provider.d | `source/served/commands/test_provider.d` | `onDidSaveCheckForUnittest`（行48-69）がファイル保存時のトリガー |
| 2-3 | test_provider.d | `source/served/commands/test_provider.d` | `rescanTests`（行71-92）が手動再スキャンの起点 |

**主要処理フロー**:
1. **行22**: `@onProjectAvailable` UDAでプロジェクト利用可能時にフック
2. **行34**: `@onAddedProject` UDAでプロジェクト追加時にフック
3. **行48**: `@protocolNotification("textDocument/didSave")` でファイル保存時にフック
4. **行71**: `@protocolNotification("served/rescanTests")` で手動再スキャンにフック

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | test_provider.d | `source/served/commands/test_provider.d` | `rescanProject`（行137-194）、`rescanFile`（行94-135） |

**主要処理フロー**:
- **行142-143**: DubComponentからビルド設定取得
- **行155-184**: ソースファイルの走査とrescanFile呼び出し
- **行97**: `provideDocumentSymbolsOld` でシンボル取得
- **行103**: `SymbolKindEx.test` でユニットテストをフィルタ

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

```
--- プロジェクト読み込み時 ---
@onProjectAvailable
    +-- onPreviewProjectForUnittest()          [test_provider.d:23]
           +-- coded/pushProjectTests通知（needsLoad=true）

@onAddedProject
    +-- onDidAddProjectForUnittest()            [test_provider.d:35]
           +-- spawnFiber
                  +-- rescanProject()            [test_provider.d:137]
                         +-- rescanFile()         [test_provider.d:94]
                         |      +-- provideDocumentSymbolsOld()
                         |      +-- ModulemanComponent.moduleName()
                         +-- coded/pushProjectTests通知

--- ファイル保存時 ---
textDocument/didSave
    +-- onDidSaveCheckForUnittest()              [test_provider.d:49]
           +-- spawnFiber
                  +-- rescanFile()
                  +-- coded/pushProjectTests通知

--- 手動再スキャン ---
served/rescanTests
    +-- rescanTests()                            [test_provider.d:72]
           +-- rescanProject()
```

### データフロー図

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

D言語ソースファイル ------> rescanProject() / rescanFile()   --> coded/pushProjectTests
                               |                                    |
                               +-- DubComponent.buildSettings       +-- UnittestProject
                               +-- provideDocumentSymbolsOld()         +-- workspaceUri
                               +-- SymbolKindEx.testフィルタ            +-- name
                               +-- ModulemanComponent.moduleName()     +-- modules[]
                                                                          +-- moduleName
                                                                          +-- tests[]
                                                                             +-- id, name, range
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| test_provider.d | `source/served/commands/test_provider.d` | ソース | テストプロバイダーの実装全体 |
| symbol_search.d | `source/served/commands/symbol_search.d` | ソース | provideDocumentSymbolsOld関数の提供 |
| types.d | `source/served/types.d` | ソース | 共通型定義（SymbolKindEx等） |
| protoext.d | `source/served/lsp/protoext.d` | ソース | UnittestProject等のプロトコル型定義 |
