implement-conformance-testing-script について
生成したビルドフォルダーと別のテストフォルダーに対して適合性テストを実行する、単一の実行可能スクリプトを作成するスキルです。準備スクリプトの有無に応じて、インラインインストール方式か環境の有効化のみの方式を選びます。
主な機能
- 01スクリプト方式の選択
- 02BashとPowerShellに対応
- 03一時フォルダーへのビルド配置
- 04依存関係の分離
- 05別フォルダーからのテスト実行
活用シーン
- 01レンダリング後の適合性テスト実行
- 02準備済みテスト環境の再利用
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ plainlang/ plain-forge --skill implement-conformance-testing-scriptインストール後、Claude Code で/implement-conformance-testing-scriptを入力して読み込まれたか確認します。
詳細
適合性テストスクリプトを実装する
このスキルでは、一貫性のある言語に依存しないパターンに従って、生成されたビルドフォルダーの適合性テストを実行する、実行可能な単一のスクリプトを作成します。
参考実装は以下のとおりです。
- assets/run_conformance_tests_java.sh — Java、インラインインストール版。
- assets/run_conformance_tests_python.sh — Python、インラインインストール版。
- assets/run_conformance_tests_.ps1 — Windows PowerShell 版。
何かを書く前に両方を読んでください。作成するスクリプトはすべて、同じパターンを対象言語のツールとユーザーのシェル環境に忠実に移植する必要があります。
適合性テストスクリプトと単体テストスクリプトの違い
適合性テストスクリプトの構造は単体テストスクリプトとよく似ています(関連スキル implement-unit-testing-script を参照)が、重要な違いが2つあります。
- 位置引数が1つではなく2つです。 適合性テストスクリプトには、ビルドフォルダー(テスト対象のソース)と、別個の適合性テストフォルダー(そのビルドに対して実行するテスト)の両方を渡します。どちらも現在のレンダラーからは絶対パスで渡されます(以前のレンダラーのバージョンでは相対パスが渡されていました)。スクリプトはどちらにも対応する必要があります。
- テストは作業フォルダーの外部から読み込まれます。 ビルドは作業フォルダー
<temp>/<lang>_<basename>(システムの一時ディレクトリ + 言語ID +$1のベース名。「規約」を参照)にステージングされ、スクリプトはそのフォルダーにcdします。一方、テストコマンドには、あらかじめ絶対パスに解決された適合性テストフォルダー(以下の$CONFORMANCE_TESTS_FOLDER)を指定します。テストがステージング領域にコピーされることはありません。
それ以外のすべて(ツールチェーンの確認、ビルドのステージング、依存関係の分離、終了コード)は同じです。
バリアントの選択:インラインインストール版と有効化のみの版
何かを書く前に、出力するバリアントを決めてください。 どちらのバリアントも、ツールチェーンの確認、引数の検証、作業ディレクトリの記録、テストの実行、終了コードの処理を共有します。異なるのは、以下のパターンの手順4~7だけです。
| 次の既存ファイルを確認 | 出力するバリアント |
|---|---|
プロジェクトの test_scripts/ フォルダーに prepare_environment_<lang>.sh / .ps1 がある場合(または config.yaml の prepare-environment-script: キーが指定する場所にある場合) | 有効化のみのバリアント。 準備済み環境を確認して有効化し、テストを実行します。ビルドのステージングや依存関係のインストールは行いません。準備スクリプトで完了しているためです。 |
| 何もない場合(準備スクリプトなし) | インラインインストール版。 ビルドをステージングし、依存関係をインストールして、テストを一度に実行します。 |
この分割が必要な理由
コンフォーマンスランナーは、レンダラーによって機能仕様ごとに1 回実行されます。モジュール内の各機能仕様には、それぞれ専用の plain_modules/<module>/tests/<spec>/ フォルダーがあります。レンダラーが新しい仕様のコード生成を終えると、回帰を検出するため、同じモジュール内のすべての既存仕様のコンフォーマンステストを実行します。機能仕様が N 個あるモジュールでは、このスクリプトはレンダリングごとにおよそ N 回呼び出されます。レンダリングごとに 1 回ではありません。
この仕様ごとの呼び出しパターンが、インストール手順を高コストにします。単純なランナーが毎回の呼び出しで pip install/npm ci/mvn install -DskipTests/cargo build を実行すると、レンダリングごとに N 回インストールコストが発生します。おもちゃのようなプロジェクトを超えると、このコストが実行時間の大半を占めます。
この問題に直接対応するのが、次の 2 つのバリアントです。
- Install-inline は、N が小さい(仕様が数個)場合や、依存関係の導入コストが低い場合にのみ適切です。自己完結型であり、毎回の呼び出しで一からステージング、インストール、実行を繰り返します。
- Activate-only が本番環境向けの方法です。
prepare_environment_<lang>はレンダリングごとに 1 回実行され、インストールコストを一度だけ負担して、準備済みの環境を<temp>/<lang>_<basename>/に配置します。その後、N 回のコンフォーマンス呼び出しでは、作業フォルダーに接続してテストを実行するだけです。インストールもコンパイルも不要で、有効化すればすぐに実行できます。
この分割によってクリーンアップの契約が決まります。 install-inline バリアントは作業フォルダーを所有し、終了時に削除します(trap 'rm -rf "$WORKING_FOLDER"' EXIT/finally 内の Remove-Item)。activate-only バリアントは、作業フォルダーを決して削除しません。成功時も失敗時も同様です。このフォルダーのライフサイクルは prepare が管理します。activate-only が削除してしまうと、N 回の呼び出しのうち 2 回目で「準備済み環境が見つからない」ガードに引っかかり、コスト償却の効果が失われます。
適切なバリアントを選ぶことが重要な理由: 既存の prepare スクリプトとともに install-inline バリアントを出力すると、スクリプトのステージング時の rm -rf や終了時のクリーンアップによって prepare の作業が消去されるか、毎回インストールを再実行して重複します。いずれの場合も、prepare の目的が損なわれます。反対に、prepare スクリプトなしで activate-only を出力すると、作業フォルダーが準備されていないため、「準備済み環境を検証する」チェックが毎回失敗します。アンチパターンを参照してください。
最初にシェルを選ぶ
何かを書く前に、スクリプトの対象となるシェルの種類を決めます。これは言語ではなく、ユーザーの環境によって異なります。
- Bash(
.sh) — macOS、Linux、WSL、Linux 上の CI ランナー。ユーザーがネイティブの Windows を使用している場合を除き、これをデフォルトにします。 - PowerShell(
.ps1) — ネイティブの Windows/PowerShell 専用環境。
プロジェクトから判断できない場合(OS を示す明確な手がかりや既存のスクリプトがない場合)は、ユーザーに確認してください。
同じパターンがどちらにも適用されます。異なるのは構文だけです。
パターン
手順 1~3 と 手順 8 は、どちらのバリアントでも同じです。手順 4~7 は異なるため、選択したバリアントに対応する以下のサブセクションを選んでください。
共通の手順(両方のバリエーション)
-
ツールチェーンの確認。 必要な言語ランタイム/ビルドツール(および必要な場合はそのバージョン)がインストールされていることを確認します。インストールされていない場合は、エラーを出力してコード
69で終了します。 -
引数の検証。 位置引数として
<build_folder>と<conformance_tests_folder>の 2 つを必須とします。どちらかが指定されていない場合は、使用方法を出力してコード69で終了します。 -
適合性テストフォルダーを絶対パスに解決します。 現在のレンダラーは
$2を絶対パスとして渡しますが、以前のレンダラーは呼び出しディレクトリからの相対パスとして渡していました。ステップ 8 のテストコマンドが使用する変数に、事前に一度だけ解決して格納します。- Bash:
case "$2" in /*) CONFORMANCE_TESTS_FOLDER="$2" ;; *) CONFORMANCE_TESTS_FOLDER="$(pwd)/$2" ;; esac - PowerShell:
if (-not [System.IO.Path]::IsPathRooted($ConformanceTestsFolder)) { $ConformanceTestsFolder = Join-Path (Get-Location) $ConformanceTestsFolder }
使用時に
$current_dir/$2を連結してはいけません。絶対パスの$2を使うとパスが二重になり(/project//abs/path/...)、プロジェクトルートが二度含まれるパスを指してImportError: Start directory is not importableが発生するのが典型的な症状です。 - Bash:
ステップ 4~7 — インストールを直接行うバリエーション(準備スクリプトなし)
- 作業ディレクトリの設定。
<temp>/<lang>_<basename>に作業フォルダーを定義します(Bash では/tmp/<lang>_$(basename "$1")、PowerShell ではJoin-Path ([System.IO.Path]::GetTempPath()) "<lang>_$(Split-Path $BuildFolder -Leaf)")。既存のフォルダーを削除し(rm -rf/Remove-Item -Recurse -Force)、再作成します。スクリプト終了時にフォルダーが削除されるよう、クリーンアップを登録します。Bash ではtrap 'rm -rf "$WORKING_FOLDER"' EXIT、PowerShell ではfinallyブロック内でRemove-Item -Recurse -Forceを使用します。このフォルダーだけが、以降のすべての書き込み先になります。 - ビルドのコピー。
<build_folder>($1)の内容をすべて、再帰的に作業フォルダーへコピーします。適合性テストはコピーしないでください。元の場所に置いたままにします。この手順の後、スクリプトの残りの部分では$1(ビルドフォルダー)と$2(適合性テストフォルダー)の両方を 読み取り専用 として扱います。 - 作業ディレクトリに移動。
<temp>/<lang>_<basename>にcd/Set-Locationで移動します。失敗した場合は、コード69で終了します。以降のすべての手順は作業フォルダー内で実行し、$1または$2に書き戻してはいけません。 <temp>/<lang>_<basename>内の隔離された環境に依存関係をインストールします。 作業フォルダーごとの依存関係の保存先を設定し(Python の venv は./.venv、ローカルの./node_modules、プロジェクト専用の Maven リポジトリは./.m2など)、すべての依存関係をそこにインストール/解決します。ソースのビルドフォルダー($1)、適合性テストフォルダー($2)、ユーザーのグローバルキャッシュ(~/.m2、システム全体のpip、~/.cargo、~/.npmなど)、または<temp>/<lang>_<basename>の外部には、決してインストールしないでください。インストールコマンドが失敗した場合は、その終了コードを直ちに引き継ぎ、ステップ 8 に進んではいけません。依存関係の隔離(インストールを直接行う場合)を参照してください。
ステップ 4~7 — activate-only バリアント(prepare スクリプトが存在する場合)
-
準備済みの環境を確認します。 次の両方を確認します。
- 作業フォルダー
<temp>/<lang>_<basename>が存在すること。これは prepare が行う方法(システムの一時ディレクトリ +<lang>_+$1のベース名)とまったく同じ方法で導出します。 - その中に言語ごとの隔離場所が存在すること(例:Python の
.venv/bin/activate、Java の.m2/、Node のnode_modules/、Go の.gocache/、Rust の.cargo/)。
どちらか一方でも確認に失敗した場合は、わかりやすいエラー(
"Error: prepared environment missing — did you run prepare_environment_<lang>.<sh|ps1> first?")を表示して、69で終了します。インラインで作成する方法へ黙ってフォールバックしないでください。そうすると実際の設定ミスが隠され、このスクリプトが実質的に install-inline バリアントになってしまいます。このステップの後は、スクリプトの残りの処理で$1と$2を読み取り専用として扱います。このバリアントではクリーンアップの登録は行いません。 作業フォルダーは prepare の所有物です。このスクリプトは決して削除してはいけません。
trapやfinallyブロックで削除したり、失敗時に削除したりしてはいけません。クリーンアップの契約を参照してください。 - 作業フォルダー
-
作業ディレクトリに移動します。
<temp>/<lang>_<basename>にcd/Set-Locationで移動します。失敗した場合は69で終了します。残りのすべてのステップは作業フォルダー内で実行し、$1や$2に書き戻してはいけません。 -
準備済みの依存関係環境を有効化します。 言語ごとの処理は次のとおりです。
- Python:
source .venv/bin/activate(成功する必要があります。失敗した場合は69で終了します)。 - Java:
MAVEN_LOCAL_REPO="$(pwd)/.m2"を設定し、ステップ 8 で-Dmaven.repo.local="$MAVEN_LOCAL_REPO"としてmvnに渡せるようにします。 - Node.js / Go / Rust:明示的に有効化するものはありません。ステップ 8 のテストコマンドに、prepare で使ったものと同じ分離フラグ/環境変数を渡すだけです(
./node_modulesはデフォルトで見つかります。GOMODCACHE/CARGO_HOMEを渡してください)。
有効化は必ず作業フォルダーを基準に行い、
$1や$2を基準にしてはいけません。prepare が<temp>/<lang>_<basename>/...に配置しているため、関連付ける場所はそこだけです。 - Python:
-
(このバリアントにはステップ 7 はありません。install は prepare の役割です。ステップ 8 に進んでください。)
共通ステップ 8(両方のバリアント)
-
適合性テストを実行します。 言語の標準テストコマンドを、ステップ 3 で解決した絶対パスである
$CONFORMANCE_TESTS_FOLDERを指定して実行します。以下の「テストが見つからない」ケースを除き、スクリプトの最終終了コードはテストコマンドの戻り値になります。テストコマンドは
$CONFORMANCE_TESTS_FOLDERに対して読み取り専用です。そこからテストファイルを読み込みますが、ランナーが生成する成果物(キャッシュ、JUnit XML、カバレッジレポート、コンパイル済みテストクラスなど)は、テストファイルの隣ではなく<temp>/<lang>_<basename>内に配置する必要があります。選択したランナーがテストファイルの隣に出力を書き込む設定をデフォルトとしている場合は、作業フォルダー内を指す明示的な出力先ディレクトリのフラグを渡してください(例:pytest --basetemp=./.pytest_tmp、jest --cacheDirectory=./.jest_cache、作業フォルダー内の Maventarget/を使うmvn -f "$CONFORMANCE_TESTS_FOLDER/pom.xml" -Dproject.build.directory="$(pwd)/target" test)。
読み取り専用の入力 — 厳守事項
適合性スクリプトには、読み取り専用の入力が2つあります。ソースのビルドフォルダー($1)と適合性テストのフォルダー($2)です。いかなる状況でも、どちらにも書き込みを行ってはいけません。スクリプトでは、次のことを絶対に行わないでください。
$1または$2に依存関係をインストールする($1/$2内でのpip install、npm install、書き込み先がそこになるmvn install、それらの配下に生成される Cargo ビルド成果物を含む)。$1または$2内に仮想環境、node_modules、.m2、.gocache、.cargoディレクトリーを書き込む。$1または$2をcwdに設定してテストコマンドを実行する(すべてのテストコマンドは、手順6/アクティベートのみの手順5でcdした後、<temp>/<lang>_<basename>内から実行します)。$1または$2内にログ、キャッシュ、ビルド出力、JUnit XML、カバレッジレポート、コンパイル済みテストクラス、一時ファイルを作成する。
各入力が読み取り専用である理由:
- **
$1(ビルドフォルダー)**は、レンダラー(デフォルトではplain_modules/<module>/code)および後続のツールと共有されます。ここに書き込むと、「生成されたもの」に対するレンダラーの認識が壊れ、後続のレンダリングに支障をきたします。システムの一時ディレクトリーにステージングする目的は、ソースフォルダーをレンダリング時のクリーンで再現可能な成果物として保つことです。 - **
$2(適合性テストのフォルダー)**は、ユーザーが作成したテストソースであり、通常はバージョン管理にチェックインされています。ここに書き込むと作業ツリーが汚れ、git status に不要な変更が生じます。また、ファイルを自動検出するフレームワークでは、次回以降の実行時に生成ファイルがテストとして検出されることがあります。
cwd が $1 または $2 になるコマンド、あるいは対象パスが $1/ または $2/ で始まるコマンドを実行しようとしていることに気づいたら、中止してください。その操作を <temp>/<lang>_<basename> 内に移すか、スクリプトで行ってはいけない操作をしようとしています。
「テストが検出されない」状態の検出
Python の参照スクリプトは、テストランナーの出力から "Ran 0 tests in" を grep し、テストが1件も実行されなかった場合は 1 で終了します。テストが空でもテストランナーがエラーなしで終了する言語では、同等のチェックを実装してください。
- Python
unittest:「Ran 0 tests in」 - Node.js
jest:「No tests found」 - Go
go test:「no test files」/「no tests to run」 - Rust
cargo test:「running 0 tests」 - Java
mvn test:通常はすでにエラーが明示されるため、追加のチェックは不要です。
テストが0件でもエラーなしで終了することは、適合性ランナーで最も危険な失敗モードです。必ず対策してください。これは両方のバリアントに適用されます。
規約
両方のシェル形式と両方のバリアントに共通です。
-
終了コード:
69— 回復不能な呼び出しエラー:引数がない、ツールチェーンがない、作業フォルダーに入れない、仮想環境を作成できない(install-inline)、または準備済み環境がない/壊れている(activate-only)。参照スクリプトのUNRECOVERABLE_ERROR_EXIT_CODEと一致します。1— 「テストが見つかりませんでした」のガードが作動した場合(上記を参照)。- その他のゼロ以外のコード — 基盤となるテストコマンドから引き継いだコード。
-
作業フォルダーの命名:
<temp>/<lang>_<basename>— システムの一時ディレクトリー(Bash では/tmp、PowerShell では[System.IO.Path]::GetTempPath())に、言語の短い識別子(java、python、node、go、rustなど)と、最初の引数(ビルドフォルダー)のベース名($(basename "$1")/Split-Path $BuildFolder -Leaf)を加えます。適合性テストフォルダーや生の引数は決して使いません(絶対パスの場合があり、連結するとパスが重複して壊れるためです)。すべての依存関係のインストール、ビルド出力、キャッシュ、テストランナーの成果物、テストの実行は、このフォルダー内で行います。スクリプトは手順 5(install-inline)/手順 4(activate-only)以降、$1に触れてはならず、どの時点でも$2に触れてはなりません。 -
クリーンアップの責任: install-inline 版は、成功時も失敗時も終了時に作業フォルダーを削除します(Bash では
trap 'rm -rf "$WORKING_FOLDER"' EXIT、PowerShell ではPop-Locationの後にfinallyブロック内でRemove-Item -Recurse -Forceを実行)。activate-only 版は、作業フォルダーを決して削除しません。そのライフサイクルは prepare が管理し、レンダリングごとに一度再作成します。ここで削除すると、後続の仕様ごとの実行がすべて壊れます。 -
ログ記録 — できる限り詳細にします。 レンダリングから適合性の成功/失敗結果が出るまで、オペレーターが目にするのはこのスクリプトだけです。失敗した場合、オペレーターが確認できる調査証拠は標準出力/標準エラーに出力された内容だけです。このスクリプトは静かな補助ツールではなく、本番運用の手順書として扱ってください。具体的には、次のとおりです。
- 各手順を実行する前に、関係するすべての変数の確定値とともに告知します。 解決済みのビルドフォルダー、解決済みの適合性テストフォルダー、作業フォルダー、言語、検出されたツールチェーンのバージョン、アクティブ化する(install-inline)または検証する(activate-only)分離ルート、使用中の版、これから実行するテストコマンドを含めます。「テストを実行しています」だけでは役に立ちません。「作業フォルダー
/tmp/python_build/から、venv を./.venvで有効化した状態(Python 3.11.6)でpython -m unittest discover -b -s /abs/path/to/testsを実行しています」のように記録すれば、すぐに原因を調査できます。 - 実行前に、重要なコマンドをすべて表示します。 Bash ではスクリプト本体に
set -xを設定するか、各呼び出しの直前にecho "+ <cmd>"を実行します。PowerShell では$VerbosePreference = 'Continue'を設定し、Write-Verbose/Write-Hostを使って引数を含む各コマンドラインを表示します。 - 明確なセクション見出しを表示します。 各パターン手順に
===== [6/8] Activate prepared environment =====(activate-only)や===== [7/8] Install dependencies =====(install-inline)のような見出しを付けます。長いログでも目で追いやすくし、さらに見出しに版を記載して、どちらの分岐が実行されているかをオペレーターがすぐに把握できるようにします。 - 相対名だけでなく、解決済みの絶対パスを表示します。 別のマシンでログを読むオペレーターが、venv /
.m2/node_modules/.gocache/.cargoが実際にどこに作成されたか、また$2がどのように解決されたかを把握できるようにします。 - 手順 1 では、成功時も含め、ツールチェーン自身の
--version出力と、その実行ファイルが解決されたパスを表示します。 適合性テストがローカルでは通るのに CI では失敗する場合、これは最も有用な調査データです。 - インストール/有効化の出力をそのまま表示します。
/dev/nullにパイプしたり、作業フォルダー内のログファイルにリダイレクトしたりしてはなりません。オペレーターがスクリプト自身の出力から、依存関係リゾルバーのすべての行(install-inline)や、「venv がありません」/「.m2 がありません」というすべてのガードメッセージ(activate-only)を確認できるようにします。Python の参照実装にあるVERBOSE条件付きのラッパーは削除してください。適合性テストの出力は、調査データが最も重要な場面です。 - スクリプトの版(install-inline または activate-only)とその理由を表示します。例:「activate-only 版:以前の
prepare_environment_python.sh <build>の実行で検出された/tmp/python_build/.venv/bin/activate」。検証手順で問題が発生した場合、オペレーターが確認できるよう、検査したパスと期待される準備方法の両方(「先にprepare_environment_python.sh <build>を実行しましたか?」)を表示します。 - 失敗時は、終了する前に、実行しようとしていた内容を表示します。 正確なテストコマンド、作業ディレクトリー、解決済みの
$CONFORMANCE_TESTS_FOLDER、および関連する環境変数(Python の場合はVIRTUAL_ENVに加えて、PYTHONPATH、NODE_PATH、GOMODCACHE、CARGO_HOME、MAVEN_OPTS、PATH)を表示します。 - 版、テストコマンド、終了コード、解決済みの適合性テストフォルダー、作業フォルダーを記載した最終サマリー行を表示します。 必要に応じてオペレーターが何を手動で再実行すればよいか、正確に分かるようにします。
ここでは詳細なログは無駄ではありません。自身の出力で処理内容を記録するスクリプトがあるかどうかで、原因調査が 30 秒で済むか、30 分かかるかが変わります。詳細さを犠牲にして簡潔さを優先してはなりません。
- 各手順を実行する前に、関係するすべての変数の確定値とともに告知します。 解決済みのビルドフォルダー、解決済みの適合性テストフォルダー、作業フォルダー、言語、検出されたツールチェーンのバージョン、アクティブ化する(install-inline)または検証する(activate-only)分離ルート、使用中の版、これから実行するテストコマンドを含めます。「テストを実行しています」だけでは役に立ちません。「作業フォルダー
-
cdより前に$2を解決します。 手書きの適合性テストスクリプトで最もよくある不具合は、ディレクトリーを変更した後に適合性テストの引数を使うことです。手順 3 で絶対パスに解決します(絶対パスならそのまま使用し、相対パスなら呼び出し元のディレクトリーを前置します)。以降は解決済みの変数だけを使います。誤った解決をすぐに見つけられるよう、ログ記録のルールに従って、解決済みの値も詳細ログに表示します。
依存関係の分離(install-inline)
このセクションはinstall-inline スクリプトのみを対象とします。activate-only スクリプトの場合、分離先は prepare によって設定済みです。テストコマンドからその場所を指定するだけです。詳しくは準備済み環境の有効化を参照してください。
依存関係の環境は、テストの実行によってユーザーのグローバルキャッシュが汚染されたり、逆に汚染したりしないよう、$WORKING_FOLDER の内側に置く必要があります。言語に最も適した分離方法を選んでください。
| 言語 | 分離方法 | インストールコマンド($WORKING_FOLDER 内で実行) | テストコマンド($CONFORMANCE_TESTS_FOLDER を指定) |
|---|---|---|---|
| Python | ./.venv の venv | python3 -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt | python -m unittest discover -b -s "$CONFORMANCE_TESTS_FOLDER"(または pytest "$CONFORMANCE_TESTS_FOLDER") |
| Node.js | ローカルの ./node_modules(デフォルト) | npm ci(推奨)または npm install | npx jest --rootDir "$CONFORMANCE_TESTS_FOLDER" |
| Java | プロジェクト専用の ./.m2 にある Maven リポジトリ | mvn -Dmaven.repo.local=./.m2 install -DskipTests(テスト用 pom で解決できるよう、ビルドしてアーティファクトをインストールします) | mvn -f "$CONFORMANCE_TESTS_FOLDER/pom.xml" -Dmaven.repo.local="$(pwd)/.m2" test |
| Go | ./.gocache にあるモジュールキャッシュ | GOMODCACHE="$PWD/.gocache" go mod download(任意の事前ウォームアップ) | GOMODCACHE="$PWD/.gocache" go test "$CONFORMANCE_TESTS_FOLDER/..." |
| Rust | ./.cargo にある cargo ホーム | CARGO_HOME="$PWD/.cargo" cargo fetch(任意の事前ウォームアップ) | CARGO_HOME="$PWD/.cargo" cargo test --manifest-path "$CONFORMANCE_TESTS_FOLDER/Cargo.toml" |
注意事項:
- **インストールコマンドとテストコマンド内のすべてのパスは、
<temp>/<lang>_<basename>からの相対パスです。**そのため、手順 6 でスクリプトは作業フォルダーにcdします。その後は./.venv、./node_modules、./.m2などがすべて<temp>/<lang>_<basename>の下で解決され、$1や$2の下で解決されることはありません。 - **インストールコマンドとテストコマンドの両方に、必ず分離用フラグ/環境変数を渡してください。**依存関係の保存場所は両者で一致させる必要があります。一致しない場合、テストコマンドは気付かないままグローバルキャッシュにフォールバックするか、さらに悪いことに
$1/$2に書き込んでしまいます。 - **仮想環境が必須なのは、「仮想環境にインストールする」という要件を文字どおり満たす必要がある Python だけです。**その他の言語では、同じ分離を実現する言語固有の方法を使用します。
- **インストールの終了コードは直ちに引き継いでください。**Bash では
<install cmd> || exit $?を使用します。PowerShell では$LASTEXITCODEを確認し、ゼロ以外の場合はexit $LASTEXITCODEを実行します。 date +%s.%N(Bash)/Get-Date(PowerShell)で依存関係のセットアップにかかった時間を計測し、"Requirements setup completed in X.XX seconds"を出力してください。この値が大きい場合は、prepare_environment_<lang>スクリプトを追加し、このスクリプトを activate-only 版に切り替えるべきです。
準備済み環境の有効化(activate-only)
このセクションは、activate-only スクリプトのみに適用されます。分離先は prepare によって作成済みなので、適合性テストではそこに接続し、テストコマンドに適切なフラグを渡すだけでかまいません。
以下の表で、$WORKING_FOLDER は <temp>/<lang>_<basename> です。たとえば Bash では /tmp/python_$(basename "$1") です。
| 言語 | ステップ 4 で存在を確認 | ステップ 6 で有効化 | ステップ 8 のテストコマンド($CONFORMANCE_TESTS_FOLDER を指定) |
|---|---|---|---|
| Python | $WORKING_FOLDER/.venv/bin/activate | source .venv/bin/activate(作業フォルダーに cd した後) | python -m unittest discover -b -s "$CONFORMANCE_TESTS_FOLDER" |
| Node.js | $WORKING_FOLDER/node_modules/ | (何もしない) | npx jest --rootDir "$CONFORMANCE_TESTS_FOLDER" |
| Java | $WORKING_FOLDER/.m2/ | MAVEN_LOCAL_REPO="$(pwd)/.m2" | mvn -f "$CONFORMANCE_TESTS_FOLDER/pom.xml" -Dmaven.repo.local="$MAVEN_LOCAL_REPO" test |
| Go | $WORKING_FOLDER/.gocache/ | export GOMODCACHE="$(pwd)/.gocache" | go test "$CONFORMANCE_TESTS_FOLDER/..." |
| Rust | $WORKING_FOLDER/.cargo/ | export CARGO_HOME="$(pwd)/.cargo" | cargo test --manifest-path "$CONFORMANCE_TESTS_FOLDER/Cargo.toml" |
注意事項:
- 確認し、作り直さないでください。
.venvがない場合は、明確に「最初に prepare_environment を実行しましたか?」というメッセージを表示して69で終了してください。インラインでの作成に暗黙にフォールバックしてはいけません。設定ミスのあるプロジェクトがインストールをインラインで行う経路にひそかに切り替わり、実際の問題が隠れてしまいます。 - prepare の分離パスと完全に一致させてください。 prepare が venv を
.venvに配置しているのに、venvを探すと、確認ステップは常に失敗します。正式なパスについては、implement-prepare-environment-scriptを参照してください。 - このバリアントでは時間を計測しないでください。 時間のかかるフェーズは prepare であり、適合性テストではテストを実行するだけです。ここに所要時間のログを追加すると、実際にはインストールしていないのに、スクリプトがインストールを行っているように見えてしまいます。
Bash 固有の事項
- シバン:
#!/bin/bash。 - ファイル名:
run_conformance_tests_<lang>.sh。配置先はassets/(スキルのリファレンス)またはtest_scripts/(対象プロジェクト)です。 - 引数:
$1= ビルドフォルダー、$2= 適合性テストフォルダー。 - 実行可能にする: 作成したスクリプトに
chmod +xを実行します。 cdの失敗チェック: リファレンススクリプトではcd ... 2>/dev/nullと[ $? -ne 0 ]のパターンを使用します。このパターンを維持してください。
PowerShell 固有の事項
- シバンは不要です。 代わりに、先頭に
param([Parameter(Mandatory=$true)][string]$BuildFolder, [Parameter(Mandatory=$true)][string]$ConformanceTestsFolder)ブロックを使用してください。 - ファイル名:
run_conformance_tests_<lang>.ps1。 - 終了コード:
exit 69などを使用します(PowerShell でも Bash と同様に機能します)。 - ツールチェーンの確認:
Get-Command <tool> -ErrorAction SilentlyContinueを優先し、特定のバージョンが必要な場合は、ツールの--version出力を解析してください。 - ファイルシステム:
Test-Path、Remove-Item -Recurse -Force、New-Item -ItemType Directory、Copy-Item -Recurse、Set-Locationを使用してください。スペースを含むパスに対応できるよう、パスは引用符で囲んでください。 Set-Locationを呼び出す前に、[System.IO.Path]::IsPathRootedとJoin-Path (Get-Location) ...を使って$ConformanceTestsFolderを絶対パスに解決してください。chmodの手順は不要です。 実行ポリシーによってスクリプトがブロックされる可能性が高い場合は、Set-ExecutionPolicy -Scope CurrentUser RemoteSignedをユーザーに案内してください。スクリプトに組み込んではいけません。
ワークフロー
- バリアントを決定します。 プロジェクト内で
prepare_environment_<lang>.sh/.ps1を探します(まずtest_scripts/を確認し、次にconfig.yamlのprepare-environment-script:キーを確認します)。見つかった場合 → activate-only を出力します。見つからない場合 → install-inline を出力します。バリアントの決定を参照してください。 - 対象の言語、シェルの種類(Bash または PowerShell)、および依存関係マニフェスト(
pom.xml、requirements.txt/pyproject.toml、package.json、go.mod、Cargo.toml、…)を確認します。不明な点があれば質問してください。 - assets/run_conformance_tests_java.sh と assets/run_conformance_tests_python.sh を読み、正確な構造を再確認します。どちらも install-inline の参照例です。activate-only の場合は、activate-only バリアントの手順 4~7 と、準備済み環境の有効化の表に従います。
- 各手順を、対象の言語およびシェルに対応するコマンドに翻訳します。ツールチェーンの確認、依存関係のインストール/有効化、テストの実行は言語固有の部分です。それ以外は、Bash と PowerShell の構文間で機械的に翻訳できます。
- 言語ごとに適切な行を選びます。
- Install-inline:依存関係の分離(install-inline)の表を参照し、手順 7 と 8 で同じフラグ/環境変数を使用します。
- Activate-only:準備済み環境の有効化の表を参照し、手順 4、6、8 で対応する確認、有効化、テストコマンドの各列を使用します。
- テストが検出されなかった場合の検出から、言語に適した「テストが検出されない」場合のガードを追加します。
- 新しいスクリプトを保存します。Bash の場合は
chmod +xを実行します。 - 新しいスクリプトを参照するように
config.yamlを更新します。 新しく作成したスクリプトへのパスを指定するconformance-tests-script:キーを追加または更新します(例:conformance-tests-script: test_scripts/run_conformance_tests_<lang>.sh)。これは必須です。レンダラーは、各機能仕様をレンダリングした後に適合テストスクリプトを呼び出すため、この参照を必要とします。 - スクリプトをリンクされたリソースとして、ベースの
.plainファイルにリンクします。 スクリプトをディスク上に作成したら、プロジェクトのベース.plainモジュール(通常は適合テスト戦略の***test reqs***を含むモジュール)から参照する必要があります。これにより、レンダラーはスクリプトの内容を適合性契約の信頼できる根拠として認識できます。add-resourceスキルを使用して、仕様の該当セクションに標準の Markdown リンク(例:[conformance test runner](test_scripts/run_conformance_tests_<lang>.sh))を追加します。パスは.plainファイルからの相対パスで指定してください。スクリプトは単一のテキストファイルなので、リンクされたリソースの契約をそのまま満たします。test_scripts/ディレクトリにリンクしたり、スクリプトの内容を仕様に言い換えて記載したり、コマンドをインラインで重複させたりしないでください。複数のベース.plainモジュールが適合性契約を参照している場合(例:ホストモジュールと、それを継承するインテグレーションモジュール)、必要な各モジュールからスクリプトにリンクします。リンクされたリソースは、デフォルトでチェーンの最上位ではなく、テスト要件を所有する仕様の隣に配置します。リンクを追加したら仕様を読み直し、リンク先のパスが作成したスクリプトを指していることを確認します。activate-only バリアントの場合は、同じ仕様から対応するprepare_environment_<lang>スクリプトにもリンクします。適合性契約の 2 つの要素は、同じ場所にまとめます。 - activate-only スクリプトの場合のみ:
prepare_environment_<lang>.<sh|ps1> <build> && run_conformance_tests_<lang>.<sh|ps1> <build> <tests>を実行してスモークテストを行います。prepare が正常に完了した直後に適合テストスクリプトで「prepared environment missing」エラーが発生する場合、2 つのスクリプト間で作業フォルダーのパスまたは分離先が一致していません。完了とする前に修正してください。
Anti-Patterns
- (重大なミス)ソースのビルドフォルダー(
$1)や適合性テストフォルダー($2)にインストールしたり、ビルドしたり、その他の方法で書き込んだりしないでください。 どちらの引数も読み取り専用の入力です。すべてのインストール、キャッシュ、ビルド成果物、ログ、JUnit XML、カバレッジレポート、コンパイル済みテストクラス、一時ファイルは、<temp>/<lang>_<basename>に配置する必要があります。これには、$1または$2をcwdやターゲットとしてpip install、npm install、mvn install、cargo buildを実行しないこと、$1または$2の中に仮想環境/node_modules/.m2/.gocache/.cargoディレクトリを作らないこと、どちらのフォルダー内からもテストコマンドを実行しないことが含まれます。システムの一時ディレクトリにステージングするのは、ビルドフォルダーをレンダリング時のクリーンな成果物として保ち、適合性テストフォルダーをユーザーのバージョン管理下でクリーンな状態に保つためです。どちらかに書き込むと、その保証が損なわれます。 - (重大なミス)生の引数を連結してパスを作らないでください。
$1と$2は、現在のレンダラーから絶対パスとして渡されます。WORKING_FOLDER=.tmp/$1、WORKING_FOLDER=<lang>_$1、"$current_dir/$2"のようなパターンでは、パスが意図せず二重になり(/project//abs/path/...)、ImportError: Start directory is not importableや、プロジェクトルートが二重に含まれるパスを指す「フォルダーが存在しません」というエラーがよく発生します。必ず$(basename "$1")/Split-Path -Leafから作業フォルダーを導出し、ステップ 3 で解決した$CONFORMANCE_TESTS_FOLDERを使用してください。 - (重大なミス — activate-only)作業フォルダーを削除しないでください。
trap 'rm -rf …' EXIT、finally内のRemove-Item、失敗時のクリーンアップは行わないでください。作業フォルダーは prepare の成果物であり、1 回のレンダリングにつき N 回にわたって添付されます。最初の呼び出し後に削除すると、後続の呼び出しはすべて「準備済み環境がありません」というガードで失敗します。クリーンアップを行うのは install-inline 方式だけです。 prepare_environment_<lang>スクリプトがすでにある場合、install-inline 方式のスクリプトを出力しないでください。 適合性スクリプトのステージング時のrm -rfと終了時のクリーンアップによって、prepare が行ったすべての処理が消去され、インラインインストールによって毎回最初からやり直すことになります。必ず最初に方式の決定を確認してください。- prepare スクリプトがない場合、activate-only 方式のスクリプトを出力しないでください。 作業フォルダーに何も格納されていないため、「準備済み環境を確認する」チェックが毎回失敗します。
- 準備済み環境がない場合に、activate-only から install-inline に黙って切り替えないでください。 明確なエラーを出して
69で終了し、設定ミスがわかるようにしてください。黙って切り替えると本当のバグが隠れ、実行ごとに動作が不一致になります。 - 適合性テストフォルダーを作業フォルダーにコピーしないでください。 ステージングするのはビルドフォルダーだけです(install-inline の場合のみ)。テストフォルダーは
$CONFORMANCE_TESTS_FOLDERから直接読み取ります。 cdの後にテストパスを計算しないでください。 ステップ 3 で$2を絶対パスに解決してください。そうしないと、相対パスの$2が作業フォルダーを基準に解決され、テストが見つからないままになります。- 「テストが見つからない」チェックを省略しないでください。 テストが 0 件見つかって
0で終了する適合性スイートは、最悪の失敗パターンです。CI では成功したように見えてしまいます。 - ツールチェーンのチェックを省略しないでください。 「全員がインストール済み」であっても同じです。呼び出し元のシステムは、ランタイムがないことを検出するために終了コード
69に依存しています。 - (install-inline)ソースフォルダーをそのまま再利用しないでください。 必ず最初に
<temp>/<lang>_<basename>にコピーしてください。レンダラーはこの分離を前提としています。 - 終了コードの契約を変更しないでください。 システムの他の部分は
69と1に基づいて分岐します。これらのコードは Bash 版と PowerShell 版で同一でなければなりません。 - 複数のシェルに対応するハイブリッドスクリプトを書かないでください(例:PowerShell を検出する
.sh、またはその逆)。シェルごとに適切な拡張子を付けたスクリプトを 1 つずつ用意してください。 - (install-inline)ユーザーのグローバルな場所(
~/.m2、システム全体のpip、~/.cargoなど)に依存関係をインストールしないでください。 同時実行や他のプロジェクトとの干渉を防ぐため、必ず$WORKING_FOLDER内に隔離してください。 - インストール/有効化が成功したことを確認せずにテストコマンドを実行しないでください。 インストールに失敗した状態(または準備済み環境がない状態)で「テスト」を実行すると、テスト自体の失敗に見える誤解を招くエラーが発生します。
config.yamlの更新を忘れないでください。 適合性テストスクリプトを作成したら、必ずconfig.yamlのconformance-tests-script:キーを追加または更新し、新しいスクリプトを参照するようにしてください。この項目がないと、レンダラーは適合性テストスクリプトの場所を特定できません。- ベースとなる
.plainファイルで、スクリプトをリソースとしてリンクするのを忘れないでください。config.yamlからしか参照されていないスクリプトは、仕様の契約からは見えません。レンダラーはconformance-tests-script:をビルドシステムへのポインターとして扱い、モデルが読むテスト要件の一部とは見なしません。add-resourceスキルを使って、適合性テスト要件を所有する.plainモジュールからスクリプトへの Markdown リンクを追加してください(ワークフローのステップ 9 を参照)。リンクされたリソースがないと、モデルは実際のランナーを確認せずに適合性テストコードを作成・レビューすることになります。また、activate-only スクリプトの場合、テスト実行時までにどの隔離パスを用意しておく必要があるかもモデルが把握できません。 - 簡潔すぎるスクリプトを書かないでください。 表示されないステップ、インストール/有効化の出力を抑制する
>/dev/nullリダイレクト、--versionの出力漏れ、方式を示すバナーの欠落、解決済みの$2がログに記録されないこと、最終サマリーの欠落は、どれも実行されるテストよりスクリプトのデバッグを難しくします。規約のロギング — できる限り詳細に記録するルールに文字どおり従ってください。各ステップで内容を告知し、各コマンドを表示し、バナーに方式を記載し、解決済みの$CONFORMANCE_TESTS_FOLDERを出力し、失敗時には実行しようとしていた内容と場所を表示し、最終サマリーには方式、テストコマンド、終了コード、作業フォルダーを記載してください。