# 機能設計書 122-テスト準備

## 概要

本ドキュメントは、Roslynプロジェクトにおけるテスト準備機能の設計仕様を記載する。テスト実行前の準備処理として、テストアセンブリの検出、テストケースの発見、およびテスト配布用ファイルの最小化を行うツールである。

### 本機能の処理概要

テスト準備機能は、CI/CDパイプライン（特にHelix分散テストシステム）でのテスト実行を効率化するための前処理ツールである。大規模なテストスイートを分散実行するための準備を行う。

**業務上の目的・背景**：Roslynプロジェクトは数千のテストケースを持つ大規模プロジェクトであり、テスト実行の効率化が重要である。本機能はHelix分散テストシステムでのテスト実行を最適化し、ビルドパイプラインの実行時間短縮に貢献する。テストファイルの重複排除によりネットワーク転送量を削減し、テスト実行環境の準備時間を短縮する。

**機能の利用シーン**：CIビルドパイプラインでテスト実行前に自動的に実行される。ビルドアーティファクトを分散テスト環境に転送する前の最適化処理として使用される。

**主要な処理内容**：
1. テストアセンブリ（*UnitTests.dll、*IntegrationTests.dll）の検出
2. TestDiscoveryWorkerを使用したテストケース発見
3. PEファイル（DLL）のMVID（Module Version ID）による重複検出
4. ハードリンクを使用したファイル最小化
5. Windows/Unix両環境向けのrehydrateスクリプト生成

**関連システム・外部連携**：Helix分散テストシステムと連携し、テストファイルの配布と実行を効率化する。Azure DevOpsパイプラインから呼び出される。

**権限による制御**：特になし。ビルドシステムから自動実行される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | UIなし（コマンドラインツール） |

## 機能種別

データ連携 / ファイル処理 / ビルドツール

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| --source | string | Yes | ビルド成果物のソースディレクトリ | 存在するディレクトリパス |
| --destination | string | Yes | 出力先ディレクトリ | 書き込み可能なパス |
| --unix | flag | No | Unix環境向け準備を行う | フラグのみ |
| --dotnetPath | string | No | dotnet CLIのパス | デフォルト: "dotnet" |

### 入力データソース

- ビルド成果物ディレクトリ（artifacts/bin）
- テストアセンブリ（*UnitTests.dll、*IntegrationTests.dll）
- TestDiscoveryWorkerツール

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| testlist.json | JSON | 各テストアセンブリのテストケース一覧 |
| .duplicate/ | Directory | 重複PEファイルの格納ディレクトリ |
| rehydrate.cmd/sh | Script | 重複ファイル復元スクリプト |
| rehydrate-all.cmd/sh | Script | 全体復元スクリプト |

### 出力先

- 指定された出力ディレクトリ（--destination）
- 各テストプロジェクトディレクトリ内

## 処理フロー

### 処理シーケンス

```
1. コマンドライン引数の解析
   └─ source、destination、unix、dotnetPathの取得
2. テストディスカバリ実行（TestDiscovery.RunDiscovery）
   ├─ テストアセンブリの検出
   ├─ TestDiscoveryWorkerの起動
   └─ testlist.jsonの生成
3. ファイル最小化実行（MinimizeUtil.Run）
   ├─ 個別ファイルのコピー（global.json、NuGet.config）
   ├─ ディレクトリウォーク（initialWalk）
   │   ├─ PEファイルのMVID取得
   │   └─ 非PEファイルのハードリンク作成
   ├─ 重複解決（resolveDuplicates）
   │   └─ MVIDベースの重複PEファイルを.duplicateに集約
   └─ rehydrateスクリプト生成（writeHydrateFile）
4. 終了コード返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[引数解析]
    B --> C{source/destination<br/>指定あり?}
    C -->|No| D[エラー終了]
    C -->|Yes| E[TestDiscovery実行]
    E --> F{成功?}
    F -->|No| D
    F -->|Yes| G[MinimizeUtil実行]
    G --> H[個別ファイルコピー]
    H --> I[ディレクトリウォーク]
    I --> J[PEファイルMVID取得]
    J --> K[重複解決]
    K --> L[rehydrateスクリプト生成]
    L --> M[正常終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | Unix除外 | Unix環境ではnet472アセンブリを除外 | --unix指定時 |
| BR-002 | MVID重複判定 | 同一MVIDのPEファイルは重複とみなす | PEファイル処理時 |
| BR-003 | ハードリンク使用 | ファイルコピーではなくハードリンクを使用 | 全ファイル処理 |
| BR-004 | 実行権限保持 | Unix環境で実行権限を持つファイルはchmod +xで復元 | Unix環境 |

### 計算ロジック

MVIDによる重複判定:
- PEファイルからMetadataReaderでMVIDを取得
- 同一MVIDのファイルは.duplicateディレクトリに1つだけ格納
- 元の場所にはrehydrateスクリプトでハードリンクを復元

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし |

### テーブル別操作詳細

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | ExitFailure | --source引数が未指定 | 正しい引数を指定して再実行 |
| 1 | ExitFailure | --destination引数が未指定 | 正しい引数を指定して再実行 |
| 1 | ExitFailure | テストディスカバリ失敗 | ログを確認してエラー原因を特定 |
| - | IOException | ハードリンク作成失敗 | ファイルシステム権限を確認 |

### リトライ仕様

リトライ処理は実装されていない。エラー発生時は処理を中断して終了コード1を返す。

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

ファイルシステム操作のため、トランザクション管理は行わない。処理中断時は出力ディレクトリに不完全なファイルが残る可能性がある。

## パフォーマンス要件

- テストディスカバリは並列実行（Parallel.ForEach）で高速化
- ディレクトリウォークも並列実行で処理
- ハードリンク使用によりディスクI/Oを最小化

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

- ビルドシステム内で実行されるため、外部入力の検証は最小限
- ファイルシステム操作のため、適切なアクセス権限が必要
- 機密情報は扱わない

## 備考

- Windows環境ではKernel32.dllのCreateHardLinkを使用
- Unix環境ではlibcのlink関数を使用
- Helix環境変数HELIX_CORRELATION_PAYLOADを使用して.duplicateディレクトリを参照

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Program.cs | `src/Tools/PrepareTests/Program.cs` | コマンドライン処理とメイン処理フロー |

**主要処理フロー**:
1. **17-28行目**: Mono.Optionsによるコマンドライン引数解析
2. **31-41行目**: 必須引数のバリデーション
3. **48-52行目**: TestDiscovery.RunDiscoveryの呼び出し
4. **54行目**: MinimizeUtil.Runの呼び出し

#### Step 2: テストディスカバリを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | TestDiscovery.cs | `src/Tools/PrepareTests/TestDiscovery.cs` | テストアセンブリ検出とテストケース発見 |

**主要処理フロー**:
- **19-56行目**: `RunDiscovery`メソッドでテストディスカバリを実行
- **21-24行目**: テストアセンブリとワーカーのパス取得
- **31-43行目**: 並列でTestDiscoveryWorkerを起動
- **74-103行目**: `RunWorker`でワーカープロセスを実行
- **105-123行目**: `GetAssemblies`でテストアセンブリを検出

**読解のコツ**: `Parallel.ForEach`による並列処理と、ワーカープロセス起動のパターンに注目

#### Step 3: ファイル最小化を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | MinimizeUtil.cs | `src/Tools/PrepareTests/MinimizeUtil.cs` | PEファイル重複検出とハードリンク処理 |

**主要処理フロー**:
- **24-46行目**: `Run`メソッドのメイン処理
- **48-72行目**: `initialWalk`でディレクトリを走査
- **74-114行目**: `walkDirectory`で各ファイルを処理
- **117-137行目**: `resolveDuplicates`で重複を解決
- **143-311行目**: `writeHydrateFile`でrehydrateスクリプト生成
- **314-341行目**: `CreateHardLink`でOS別ハードリンク作成
- **343-364行目**: `TryGetMvid`でPEファイルからMVIDを取得

**読解のコツ**:
- `FilePathInfo`レコードでファイル情報を管理
- `s_executablesGroup`は実行権限保持用の特殊グループ
- Windows/Unix両方のrehydrateスクリプト生成に注目

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

```
Program.Main
    │
    ├─ OptionSet.Parse
    │      └─ コマンドライン引数解析
    │
    ├─ TestDiscovery.RunDiscovery
    │      ├─ GetAssemblies
    │      │      └─ *UnitTests.dll, *IntegrationTests.dll検出
    │      ├─ GetWorkers
    │      │      └─ TestDiscoveryWorker.dll/exe取得
    │      └─ Parallel.ForEach
    │             └─ RunWorker
    │                    └─ testlist.json生成
    │
    └─ MinimizeUtil.Run
           ├─ CreateHardLink (individual files)
           │      └─ global.json, NuGet.config
           ├─ initialWalk
           │      └─ walkDirectory
           │             ├─ TryGetMvid (PEファイル)
           │             └─ CreateHardLink (非PEファイル)
           ├─ resolveDuplicates
           │      └─ CreateHardLink (.duplicate)
           └─ writeHydrateFile
                  ├─ writeWindowsRehydrateContent
                  └─ writeUnixRehydrateContent
```

### データフロー図

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

ソースディレクトリ ─────────▶ TestDiscovery ────────▶ testlist.json
(artifacts/bin)                    │
                                   ▼
                              MinimizeUtil
                                   │
テストアセンブリ ─────────────────▶│──────────────────▶ .duplicate/
(*UnitTests.dll)                   │                    (重複PEファイル)
                                   │
                                   │──────────────────▶ rehydrate.cmd/sh
                                   │                    (復元スクリプト)
                                   │
                                   ▼
                              出力ディレクトリ
                              (最小化済み)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Program.cs | `src/Tools/PrepareTests/Program.cs` | ソース | エントリーポイント |
| TestDiscovery.cs | `src/Tools/PrepareTests/TestDiscovery.cs` | ソース | テストディスカバリ処理 |
| MinimizeUtil.cs | `src/Tools/PrepareTests/MinimizeUtil.cs` | ソース | ファイル最小化処理 |
| TestDiscoveryWorker | `src/Tools/TestDiscoveryWorker/` | ツール | テストケース発見ワーカー |
