# 通知設計書 18-coded/pushProjectTests

## 概要

本ドキュメントは、カスタム通知 `coded/pushProjectTests` の設計仕様を定義する。この通知はサーバー（serve-d）からクライアント（エディタ）に送信され、プロジェクトのユニットテスト情報をクライアントに送信する。

### 本通知の処理概要

`coded/pushProjectTests` 通知は、serve-dがプロジェクト内のD言語ユニットテストを検出・更新した際にクライアントへ送信する通知である。`UnittestProject` 構造体が送信され、テストモジュール一覧と各テスト情報を含む。

**業務上の目的・背景**：D言語プロジェクトのユニットテスト情報をエディタのテストランナーUIに提供するため、テスト検出結果をクライアントに送信する必要がある。テストの検出・更新は、プロジェクトのロード時およびファイル保存時に行われる。

**通知の送信タイミング**：(1) プロジェクトプレビュー時（`onPreviewProjectForUnittest`、初期状態の空テスト情報送信）、(2) プロジェクトロード完了時（`rescanProject`、全ソースファイルスキャン後）、(3) ファイル保存時（`onDidSaveCheckForUnittest`、変更ファイルの再スキャン後）に送信される。いずれも `doTrackTests` フラグが有効な場合のみ動作する。

**通知の受信者**：LSPクライアント（エディタ拡張機能）が受信する。クライアントはテストランナーUIを更新する。

**通知内容の概要**：`UnittestProject` 構造体が送信され、ワークスペースURI、パッケージ名、テストモジュール一覧（各モジュールのテスト名・位置情報を含む）、および初期ロード状態フラグが含まれる。

**期待されるアクション**：クライアントはテストランナーのUIを更新し、検出されたユニットテストの一覧を表示する。ユーザーはUIから個別のテストを実行可能になる。

## 通知種別

LSPカスタム通知（JSON-RPC notification）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（Fiber上で実行） |
| 優先度 | 中 |
| リトライ | 無し |

### 送信先決定ロジック

サーバーからクライアントへの一方向通知。LSP接続で確立されたクライアントプロセスに固定。

## 通知テンプレート

### JSON-RPC通知メッセージ

| 項目 | 内容 |
|-----|------|
| メソッド名 | `coded/pushProjectTests` |
| パラメータ型 | `UnittestProject` |
| レスポンス | なし（通知） |

### メッセージ構造

```json
{
  "jsonrpc": "2.0",
  "method": "coded/pushProjectTests",
  "params": {
    "workspaceUri": "file:///path/to/project",
    "name": "my-project",
    "modules": [
      {
        "moduleName": "app",
        "uri": "file:///path/to/project/source/app.d",
        "tests": [
          {
            "id": "unittest_L42_C1",
            "name": "test addition",
            "containerName": "app",
            "range": {"start": {"line": 41, "character": 0}, "end": {"line": 50, "character": 1}}
          }
        ]
      }
    ],
    "needsLoad": false
  }
}
```

### 添付ファイル

該当なし。

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| workspaceUri | ワークスペースのURI | instance.cwd.uriFromFile | Yes |
| name | パッケージ名 | DubComponent.rootPackageBuildSettings().packageName | No |
| modules | テストモジュール一覧 | ソースファイル解析結果 | Yes |
| needsLoad | 初期ロード必要フラグ | 初期状態true、スキャン後false | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 内部処理 | プロジェクトプレビュー | doTrackTests==true | onPreviewProjectForUnittest（test_provider.d L22-32） |
| 内部処理 | プロジェクトロード完了 | doTrackTests==true かつ DubComponent存在 | rescanProject関数の最終段階（test_provider.d L186） |
| ファイル保存 | .dファイル保存 | doTrackTests==true かつ DubComponent存在 | onDidSaveCheckForUnittest（test_provider.d L48-69） |
| API | served/rescanTests通知 | インスタンス存在 | rescanTests関数からrescanProject呼び出し（test_provider.d L71-92） |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| doTrackTests==false | `--provide test-runner` 引数なしで起動した場合、テスト追跡は無効 |
| DubComponentなし | プロジェクトにDubComponentが存在しない場合はスキャンが実行されない |
| 非.dファイル保存 | .d拡張子以外のファイル保存時はスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A{トリガー種別} --> B[プロジェクトプレビュー]
    A --> C[プロジェクトロード完了]
    A --> D[ファイル保存]
    A --> E[served/rescanTests]
    B --> F[空テスト情報でcoded/pushProjectTests送信]
    C --> G[rescanProject: 全ソースファイルスキャン]
    D --> H[rescanFile: 保存ファイルのみスキャン]
    E --> G
    G --> I[coded/pushProjectTests送信: 全テスト情報]
    H --> J[coded/pushProjectTests送信: 更新テスト情報]
    F --> K[終了]
    I --> K
    J --> K
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（データベースを使用しない）。インメモリの `projectTests` 連想配列（`UnittestProject[DocumentUri]`）を使用。

### 更新テーブル一覧

該当なし。`projectTests` 連想配列がテスト情報のキャッシュとして更新される。

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ファイル読み込み失敗 | ソースファイルが見つからない | FileExceptionをキャッチし警告ログ出力、該当ファイルをスキップ |
| 解析失敗 | ソースファイルの解析に失敗 | Exceptionをキャッチし警告ログ出力、該当ファイルをスキップ |
| クライアント未対応 | クライアントがcoded/pushProjectTestsをハンドリングしない | 通知は無視される |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし。

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

- ソースファイルのURI、モジュール名、テスト名が含まれる
- ソースコードの内容自体は送信されないが、テスト関数名やモジュール構造が露出する

## 備考

- `doTrackTests` はサーバー起動時に `--provide test-runner` 引数が指定された場合にtrueに設定される（test_provider.d L18）
- `projectTests` はグローバル連想配列（`UnittestProject[DocumentUri]`）でテスト情報をキャッシュする（test_provider.d L20）
- テストモジュール一覧は `moduleName` でソートされた状態で管理される（test_provider.d L118-119）
- `rescanProject` はFiber上で実行され、大規模プロジェクトでも協調的にスケジューリングされる

---

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

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

### 推奨読解順序

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

テスト情報のデータ構造を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | protoext.d | `source/served/lsp/protoext.d` | `UnittestProject`（L279-293）、`UnittestModule`（L295-300）、`UnittestInfo`（L302-307）構造体の定義 |

**読解のコツ**: `UnittestProject` はワークスペース単位のテスト情報コンテナ、`UnittestModule` はモジュール単位、`UnittestInfo` は個別テスト情報を表す。`needsLoad` フラグはまだプロジェクトが完全にスキャンされていないことを示す。

#### Step 2: 送信トリガーを理解する

test_provider.d内の各送信箇所を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | test_provider.d | `source/served/commands/test_provider.d` | `onPreviewProjectForUnittest`（L22-32）：プレビュー時の初期テスト情報送信 |
| 2-2 | test_provider.d | `source/served/commands/test_provider.d` | `onDidSaveCheckForUnittest`（L48-69）：ファイル保存時の更新送信 |
| 2-3 | test_provider.d | `source/served/commands/test_provider.d` | `rescanProject`（L137-194）：プロジェクト全体スキャン後の送信（L186） |

**主要処理フロー（rescanProject）**:
1. **L142-143**: DubComponentからビルド設定を取得
2. **L155-184**: 各ソースファイルを走査してテストを検出
3. **L186**: `coded/pushProjectTests` で全テスト情報を送信

#### Step 3: テスト検出処理を理解する

rescanFile関数でのテスト検出ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | test_provider.d | `source/served/commands/test_provider.d` | `rescanFile`（L94-135）：個別ファイルのテスト検出 |

**主要処理フロー**:
- **L97**: `provideDocumentSymbolsOld` でドキュメントシンボルを取得
- **L101-110**: `SymbolKindEx.test` タイプのシンボルをフィルタリング
- **L118-134**: テストモジュール情報をソート済みリストに挿入/更新

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

```
[プレビュー時]
onPreviewProjectForUnittest (test_provider.d:22)
    |
    +-- rpc.notifyMethod("coded/pushProjectTests", project) (L31)

[ロード完了時 / served/rescanTests]
rescanProject (test_provider.d:137)
    |
    +-- DubComponent.rootPackageBuildSettings() (L143)
    +-- [各ソースファイルに対して]
    |       |
    |       +-- rescanFile() (L178)
    |               +-- provideDocumentSymbolsOld() (L97)
    |               +-- テストシンボル抽出 (L101-110)
    |
    +-- rpc.notifyMethod("coded/pushProjectTests", project) (L186)

[ファイル保存時]
onDidSaveCheckForUnittest (test_provider.d:48)
    |
    +-- rescanFile() (L65)
    +-- rpc.notifyMethod("coded/pushProjectTests", project) (L67)
```

### データフロー図

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

ソースファイル             rescanFile()                  coded/pushProjectTests
(.d, .dpp, .di)       --> (test_provider.d:94)          UnittestProject {
                          |                               workspaceUri,
                          +-- provideDocumentSymbolsOld   name,
                          |   (シンボル解析)               modules: [{
                          |                                 moduleName,
                          +-- SymbolKindEx.test            uri,
                              フィルタリング                 tests: [{id, name, range}]
                                                          }],
                                                          needsLoad
                                                         }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| test_provider.d | `source/served/commands/test_provider.d` | ソース | テスト検出・coded/pushProjectTests送信のメイン実装 |
| protoext.d | `source/served/lsp/protoext.d` | ソース | UnittestProject/UnittestModule/UnittestInfo構造体定義 |
| symbol_search.d | `source/served/commands/symbol_search.d` | ソース | provideDocumentSymbolsOld関数（テスト検出に使用） |
