# 画面設計書 32-testament r

## 概要

本ドキュメントは、Nimコンパイラプロジェクトのテストフレームワーク「Testament」における「r」サブコマンドの画面設計書である。testament rは単一のテストファイルを実行するためのCLIインターフェースを提供する。

### 本画面の処理概要

testament rコマンドは、Nimコンパイラのテストスイートにおいて、個別のテストファイルを指定して実行する機能を提供する。開発中の特定のテストを素早く確認したい場合や、失敗したテストを再実行する場合に使用される。

**業務上の目的・背景**：Nimコンパイラの開発において、特定のバグ修正や新機能追加の際に、関連する単一のテストケースを迅速に実行・確認したいケースが頻繁に発生する。全テストやカテゴリ単位での実行では時間がかかりすぎるため、単一テスト実行機能が必要となる。これにより、開発者は変更の影響を即座に確認でき、イテレーションを高速化できる。

**画面へのアクセス方法**：コマンドラインから`testament r <test>`または`testament run <test>`の形式で実行する。testにはテストファイルのパスを指定する。パスは相対パスでも絶対パスでも指定可能。

**主要な操作・処理内容**：
1. コマンドライン引数からテストファイルパスを取得
2. splitTestFile()でカテゴリとパスを分離
3. processSingleTest()を呼び出して単一テスト実行
4. parseSpec()でテストスペックを解析
5. 指定されたターゲット（デフォルトはC）でコンパイル・実行
6. テスト結果を検証し、成功/失敗を判定
7. 結果を標準出力に表示

**画面遷移**：
- 遷移元：testament メイン画面（コマンドライン引数に「r」を指定）
- 遷移先：なし（テスト結果を標準出力に表示して終了）

**権限による表示制御**：特になし。CLIツールとして実行権限があれば使用可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | Testament | 主機能 | 単一テストファイルの実行処理 |
| 1 | Nimコンパイラ本体 | 遷移先機能 | テスト実行時のコンパイラ呼び出し |

## 画面種別

CLIコマンド実行画面（単一テスト実行）

## URL/ルーティング

```
testament r <test> [options]
testament run <test> [options]
```

## 入出力項目

### 入力項目

| 項目名 | 型 | 必須 | 説明 | 例 |
|--------|-----|------|------|-----|
| test | string | Yes | テストファイルのパス | tests/gc/gctest.nim |
| --print | flag | No | 結果をコンソールに出力 | - |
| --verbose | flag | No | 詳細な実行コマンドを表示 | - |
| --targets | string | No | 実行対象のターゲット | "c cpp js objc" |
| --nim | path | No | 使用するnimコンパイラのパス | /usr/local/bin/nim |
| --directory | path | No | テストディレクトリの変更 | ./tests |
| --colors | on/off | No | 出力の色付け制御 | on |
| --backendLogging | on/off | No | バックエンドログの有効/無効 | on |
| --valgrind | on/off | No | valgrindサポートの有効/無効 | on |

### 出力項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| テスト名 | string | 実行されたテストの名前 |
| ターゲット | string | 使用されたターゲット（c/cpp/js/objc） |
| 結果 | enum | PASS/FAIL/SKIP |
| 実行時間 | float | テスト実行にかかった秒数 |
| 期待値 | string | 失敗時の期待される出力 |
| 実際の値 | string | 失敗時の実際の出力 |
| diff | string | 失敗時の期待値と実際値の差分 |

## 表示項目

### テスト実行結果表示

| 項目 | 表示形式 | 説明 |
|------|----------|------|
| PASS | 緑色で"PASS:"表示 + テスト名 + 実行時間 | テスト成功 |
| FAIL | 赤色で"FAIL:"表示 + 詳細情報 | テスト失敗 |
| SKIP | 黄色で"SKIP:"表示 | テストスキップ |

### 失敗時の詳細表示

| 項目 | 表示形式 | 説明 |
|------|----------|------|
| Failure | 赤色太字で失敗理由 | reMsgsDiffer, reOutputsDiffer等 |
| Expected | 黄色で"Expected:"表示 + 期待値 | スペックで指定された出力 |
| Gotten | 黄色で"Gotten:"表示 + 実際の値 | 実際に得られた出力 |
| diff | diff形式の差分表示 | 期待値と実際値の差分 |

## イベント仕様

### 1-単一テスト実行

ユーザーが`testament r <test>`を実行すると以下の処理が行われる：

1. main()関数でコマンドライン引数を解析
2. `action`が"r", "run"の場合、processSingleTestを呼び出し
3. splitTestFile()でファイルパスからカテゴリを抽出
4. targetsSetがfalseの場合、デフォルトターゲット（C、jsカテゴリならJS）を設定
5. makeTest()でTTest構造体を作成
6. testSpec()でテストを実行
7. 結果を集計してTResults構造体に格納
8. 結果を標準出力に表示

### 2-テストスペック解析

テストファイルの先頭にあるスペック（discard """..."""ブロック）を解析：

1. extractSpec()でスペック文字列を抽出
2. CfgParserでkey-value形式を解析
3. action, output, exitCode, targets等のフィールドを設定
4. インラインエラーマーカー（#[tt.Error...）も解析

### 3-テスト実行・検証

テストファイルをコンパイル・実行し、結果を検証：

1. callNimCompiler()でコンパイラを呼び出し
2. actionに応じた処理：
   - actionCompile: コンパイル成功のみを検証
   - actionRun: コンパイル後に実行、出力・終了コードを検証
   - actionReject: コンパイルエラーを期待し、エラーメッセージを検証
3. 結果をaddResult()で記録

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| テスト結果記録 | testresults/*.json | INSERT | テスト実行結果をJSONファイルに保存 |

### テーブル別更新項目詳細

#### testresults/*.json

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name | テスト名 | ファイルパスとオプション |
| INSERT | category | カテゴリ名 | パスから抽出 |
| INSERT | target | ターゲット | c/cpp/js/objc |
| INSERT | action | アクション | run/compile/reject |
| INSERT | result | 結果 | reSuccess/reDisabled等 |
| INSERT | expected | 期待値 | テストスペックから |
| INSERT | given | 実際の値 | 実行結果から |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG001 | 情報 | "PASS: {テスト名} ({時間} sec)" | テスト成功時 |
| MSG002 | エラー | "FAIL: {テスト名}" | テスト失敗時 |
| MSG003 | 警告 | "SKIP: {テスト名}" | テストスキップ時 |
| MSG004 | エラー | "Failure: {失敗理由}" | テスト失敗時の詳細 |
| MSG005 | 情報 | "Expected: {期待値}" | 失敗時の期待値表示 |
| MSG006 | 情報 | "Gotten: {実際の値}" | 失敗時の実際値表示 |
| MSG007 | 情報 | "Used {nim} to run the tests." | 実行完了時 |

## 例外処理

| 例外条件 | 処理内容 | エラーメッセージ |
|----------|----------|-----------------|
| テストファイルが存在しない | アサーション失敗 | "{test} test does not exist" |
| スペック解析エラー | 無効スペックとして記録 | parseErrors内容を表示 |
| コンパイラが見つからない | パス検索失敗 | findExe("nim")が空の場合 |
| 実行ファイルが見つからない | reExeNotFound | "executable not found: {exe}" |
| Node.jsが見つからない | reExeNotFound | "nodejs binary not in PATH" |
| タイムアウト | reTimeout | spec.timeout秒超過時 |

## 備考

- テストファイルのパスは「tests/カテゴリ/ファイル名」の形式が推奨される
- splitTestFile()でカテゴリを自動判定し、適切な処理を適用
- --targetsが指定されていない場合、スペックのtargetsフィールドまたはデフォルト（C）が使用される
- jsカテゴリのテストは自動的にtargetJSが選択される
- valgrindはLinux 64bitのみで有効、Windowsでは無視される

---

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

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

### 推奨読解順序

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

まず、テストスペック（TSpec）とテスト（TTest）の構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | specs.nim | `testament/specs.nim` | TSpec型（75-106行目）でテストの仕様を定義 |
| 1-2 | testament.nim | `testament/testament.nim` | TTest型（92-99行目）で実行するテストの情報を定義 |

**読解のコツ**: TSpecのactionフィールド（actionRun/actionCompile/actionReject）によってテストの実行方法が決まる。outputフィールドは期待される出力、exitCodeは期待される終了コード。

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

処理の起点となるmain()関数とrunコマンドの処理を特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | testament.nim | `testament/testament.nim` | main()関数（684-841行目）でコマンドライン解析 |
| 2-2 | testament.nim | `testament/testament.nim` | 822-824行目で"r", "run"コマンドの処理 |

**主要処理フロー**:
1. **822行目**: splitTestFile()でカテゴリとパスを分離
2. **824行目**: processSingleTest()を呼び出し
3. **gTargets, targetsSet**でターゲット設定を管理

#### Step 3: 単一テスト処理を理解する

processSingleTestの処理フローを把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | categories.nim | `testament/categories.nim` | processSingleTest()関数（522-528行目）で単一テスト実行 |
| 3-2 | categories.nim | `testament/categories.nim` | splitTestFile()（lib/stdtest/specialpathsからインポート）でパス分離 |

**主要処理フロー**:
- **523-526行目**: ターゲットのデフォルト設定（jsカテゴリならtargetJS、それ以外はtargetC）
- **527行目**: ファイル存在確認のアサート
- **528行目**: testSpec()でテスト実行

#### Step 4: テスト実行の詳細を理解する

testSpec()とtestSpecHelper()の実装を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | testament.nim | `testament/testament.nim` | testSpec()関数（603-619行目）でターゲット別実行 |
| 4-2 | testament.nim | `testament/testament.nim` | testSpecHelper()関数（489-570行目）で実際の実行処理 |

**主要処理フロー**:
- **507行目**: callNimCompiler()でコンパイラ呼び出し
- **509行目**: actionCompileの場合、compilerOutputTests()
- **511-562行目**: actionRunの場合、実行ファイルを起動し出力を検証
- **563-569行目**: actionRejectの場合、エラーメッセージを検証

#### Step 5: 結果検証を理解する

テスト結果の検証ロジックを把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | testament.nim | `testament/testament.nim` | compilerOutputTests()関数（458-475行目）でコンパイル出力検証 |
| 5-2 | testament.nim | `testament/testament.nim` | cmpMsgs()関数（402-421行目）でエラーメッセージ比較 |
| 5-3 | testament.nim | `testament/testament.nim` | addResult()関数（279-345行目）で結果記録・表示 |

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

```
main()
    │
    ├─ initOptParser() / オプション解析
    │
    └─ [action = "r"/"run"]
           │
           ├─ splitTestFile()
           │       └─ カテゴリとパスを分離
           │
           └─ processSingleTest()
                   │
                   ├─ makeTest()
                   │       └─ parseSpec()
                   │
                   └─ testSpec()
                           │
                           └─ testSpecHelper()
                                   │
                                   ├─ callNimCompiler()
                                   │
                                   ├─ [actionRun] execCmdEx2()
                                   │       └─ 実行ファイル起動
                                   │
                                   ├─ compilerOutputTests()
                                   │
                                   └─ cmpMsgs()
                                           │
                                           └─ finishTestRetryable()
                                                   │
                                                   └─ addResult()
```

### データフロー図

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

テストファイルパス ───▶ splitTestFile() ───▶ (カテゴリ, パス)
       │                                │
       ▼                                ▼
ファイル内容 ────────▶ extractSpec() ───▶ スペック文字列
       │                                │
       ▼                                ▼
スペック文字列 ─────▶ parseSpec() ───▶ TSpec構造体
       │                                │
       ▼                                ▼
TSpec + ファイル ──▶ makeTest() ───▶ TTest構造体
       │                                │
       ▼                                ▼
TTest ──────────▶ callNimCompiler() ───▶ コンパイル結果
       │                                │
       ▼                                ▼
実行ファイル ───────▶ execCmdEx2() ───▶ 実行出力
       │                                │
       ▼                                ▼
期待値 vs 実際値 ──▶ compilerOutputTests() ───▶ 検証結果
       │                                │
       ▼                                ▼
検証結果 ──────────▶ addResult() ───▶ 標準出力 / JSON
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| testament.nim | `testament/testament.nim` | ソース | メインプログラム、テスト実行制御 |
| specs.nim | `testament/specs.nim` | ソース | テストスペックの定義と解析 |
| categories.nim | `testament/categories.nim` | ソース | processSingleTest()定義（includeファイル） |
| specialpaths.nim | `lib/stdtest/specialpaths.nim` | ソース | splitTestFile()の実装 |
| backend.nim | `testament/backend.nim` | ソース | テスト結果のJSON出力 |
| gitutils.nim | `std/private/gitutils.nim` | ソース | diffStrings()による差分表示 |
| nodejs.nim | `compiler/nodejs.nim` | ソース | Node.js実行パス検索 |
