テスト実行環境の完全な仕様。
背景
Bazel BUILD 言語には、自動テストプログラムを多くの言語で定義するために使用できるルールが含まれています。
テストは bazel test
を使用して実行されます。
テストバイナリを直接実行することもできます。このような呼び出しは、後述するマンデートを遵守しないため、許可されますが、推奨されません。
テストは密閉型である必要があります。つまり、宣言された依存関係を持つリソースにのみアクセスする必要があります。テストが密閉されていないと、過去の再現性のある結果が得られません。これは、問題の原因(テストでどの変更が破損したかを判断)、リリースのエンジニアリングの監査可能性、テストのリソースの分離(一部のテストでたびたび発生するため、サーバーを DDOS にするべきではありません)にとって重大な問題になる可能性があります。
目的
このページでは、Bazel テストのランタイム環境と期待される動作について正式に説明します。また、テストランナーとビルドシステムに対しても要件が課されます。
テスト環境の仕様により、テストの作成者は不特定の動作に依存せずに済むため、テスト インフラストラクチャは自由に実装を変更することができます。この仕様では、適切に密閉的、確定的、リエントラントでなくても、現在多くのテストに合格できるホールが厳しくなります。
このページは、規範的かつ信頼できるものとして作成されました。この仕様とテストランナーの動作実装が一致しない場合、その仕様が優先されます。
提案された仕様
「MUST」、「MUST NOT」、「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、「RECOMMENDED」、「MAY」、「オプション」という用語は、IETF RFC 2119 に記載されているように解釈されます。
テストの目的
Bazel テストの目的は、リポジトリにチェックインされたソースファイルのプロパティを確認することです。(このページの「ソースファイル」には、テストデータ、ゴールデン出力、バージョン管理下にあるその他のデータが含まれます)。あるユーザーは、テストを行い、メンテナンスが想定されている不変量をアサートします。他のユーザーは、後でテストを実行して、不変量が壊れたかどうかを確認します。テストがソースファイル(非密閉型)以外の変数に依存している場合、テストでは合格しない変更に失敗していると、その後のユーザーが判断できないため、値が低下します。
そのため、テストの結果は次の条件にのみ依存する必要があります。
- テストの依存関係が宣言されているソースファイル
- テストの依存関係が宣言されているビルドシステムのプロダクト
- テストランナーが一定の動作を保証するリソース
現在、このような動作は強制されません。ただし、テストランナーは、将来的にそのような措置を追加する権利を留保します。
ビルドシステムの役割
テストルールは、実行可能プログラムを生成する必要があるという点でバイナリルールに似ています。一部の言語では、言語固有のハーネスとテストコードを組み合わせたスタブ プログラムになります。テストルールでも他の出力を生成する必要があります。テスト実行には、プライマリ テスト実行ファイルに加えて、runfiles のマニフェストと、実行時にテストに使用できる入力ファイルが必要です。また、テストのタイプ、サイズ、タグに関する情報が必要になることもあります。
ビルドシステムは、コードとともにデータを提供するためにランファイルを使用できます。(ダイナミック リンクの使用などでテスト間でファイルを共有することで、各テストバイナリを縮小するための最適化に使用される場合があります)。ビルドシステムは、生成された実行可能ファイルが、ソースツリーまたは出力ツリー内の絶対位置への参照をハードコードするのではなく、テストランナーが提供するランファイル イメージを介してこれらのファイルを読み込むようにします。
テストランナーの役割
テストランナーの観点から、各テストは execve()
で呼び出すことができるプログラムです。テストを実行する方法は他にもあります。たとえば、IDE で処理中の Java テストの実行を許可する場合があります。ただし、テストをスタンドアロン プロセスとして実行した結果が、信頼できるものと見なされる必要があります。テストプロセスが完了まで実行され、終了コードがゼロで正常に終了すると、テストは合格しています。その他の結果はすべてテストの失敗とみなされます。特に、文字列 PASS
または FAIL
の stdout への書き込みは、テストランナーにとって重要ではありません。
テストの実行に時間がかかりすぎている場合、またはリソース上限を超過している場合や、テストランナーが禁止された動作を検出した場合は、テストを強制終了して実行を失敗として扱うことがあります。テストプロセスまたはその子にシグナルを送信した後、ランナーはテストに合格したことを報告してはなりません。
個々のターゲットやテストではなく、テスト ターゲット全体を実行するまでの時間は限られています。テストの制限時間は、次の表の timeout
属性に基づいています。
timeout | 制限時間(秒) |
---|---|
short | 60 |
やや不足 | 300 |
long | 900 |
エターナル | 3,600 |
タイムアウトを明示的に指定しない場合、次のようにテストの size
に基づいてタイムアウトが暗黙的に指定されます。
size | 暗黙のタイムアウト ラベル |
---|---|
small | short |
中程度の幅 | やや不足 |
大 | long |
膨大な | エターナル |
明示的なタイムアウト設定がない「大規模」テストでは、実行に 900 秒が割り当てられます。タイムアウトが「short」の「medium」テストには 60 秒が割り当てられます。
timeout
とは異なり、size
は、ローカルでテストを実行するときに、他のリソース(RAM など)で想定されるピーク使用量を決定します。詳細については、一般的な定義 をご覧ください。
size
ラベルと timeout
ラベルの組み合わせは合法であるため、「タイムアウト」テストのタイムアウトは「短い」と宣言できます。何か恐ろしいことをするとすぐに
タイムアウトに関係なく、テストは任意の速度で返されることがあります。過剰なタイムアウトに対してテストのペナルティが課されることはありませんが、警告が発行されることがあります。通常は、不安定さを発生させることなく、タイムアウトをできるだけ短く設定する必要があります。
手動で遅延が発生している状況の場合、--test_timeout
bazel フラグを使用してテスト タイムアウトをオーバーライドできます。--test_timeout
値は秒単位です。たとえば、--test_timeout=120
はテスト タイムアウトを 2 分に設定します。
また、次のようにテスト タイムアウトの下限も推奨されます。
timeout | 最小時間(秒) |
---|---|
short | 0 |
やや不足 | 30 |
long | 300 |
エターナル | 900 |
たとえば、「中」テストが 5.5 秒で完了する場合は、timeout =
"short"
または size = "small"
の設定を検討してください。bazel の --test_verbose_timeout_warnings
コマンドライン オプションを使用すると、指定したサイズが大きすぎるテストが表示されます。
テストのサイズとタイムアウトは、こちらの仕様に従って BUILD ファイルで指定されます。指定しない場合、テストサイズはデフォルトで「中」になります。
テストのメインプロセスが終了しても、その子の一部がまだ実行されている場合、テストランナーは実行を完了とみなし、メインプロセスから観測された終了コードに基づいて成功または失敗としてカウントする必要があります。テストランナーは不要なプロセスを強制終了できます。この方法でテストがプロセスにリークすることはありません。
テストのシャーディング
テストのシャーディングによってテストを並列化できます。テストのシャーディングを有効にするには、--test_sharding_strategy
と shard_count
をご覧ください。シャーディングを有効にすると、テストランナーはシャードごとに 1 回起動されます。環境変数 TEST_TOTAL_SHARDS
はシャード数、TEST_SHARD_INDEX
は 0 から始まるシャード インデックスです。ランナーはこの情報を使用して、ラウンドロビン戦略を使用するなど、実行するテストを選択します。すべてのテストランナーがシャーディングをサポートしているわけではありません。ランナーがシャーディングをサポートしている場合、TEST_SHARD_STATUS_FILE
で指定されたファイルの最終更新日を、作成または更新する必要があります。それ以外の場合、Bazel はシャーディングをサポートしていないとみなし、追加のランナーを起動しません。
初期条件
テストを実行する場合、テストランナーは特定の初期条件を確立する必要があります。
テストランナーは、argv[0]
にあるテスト実行ファイルのパスを使用して、各テストを呼び出す必要があります。このパスは、テストの現在のディレクトリの下の相対パスで指定する必要があります(runfile ツリーにあります)。ユーザーが明示的にリクエストしない限り、テストランナーは他の引数をテストに渡さないでください。
初期環境のブロックは次のように構成する必要があります。
変数 | 値 | ステータス |
---|---|---|
HOME |
$TEST_TMPDIR の値 |
推奨 |
LANG |
未設定 | required |
LANGUAGE |
未設定 | required |
LC_ALL |
未設定 | required |
LC_COLLATE |
未設定 | required |
LC_CTYPE |
未設定 | required |
LC_MESSAGES |
未設定 | required |
LC_MONETARY |
未設定 | required |
LC_NUMERIC |
未設定 | required |
LC_TIME |
未設定 | required |
LD_LIBRARY_PATH |
共有ライブラリを含むディレクトリのコロン区切りリスト | 省略可 |
JAVA_RUNFILES |
$TEST_SRCDIR の値 |
廃止予定 |
LOGNAME |
$USER の値 |
required |
PATH |
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:. |
推奨 |
PWD |
$TEST_SRCDIR/workspace-name |
推奨 |
SHLVL |
2 |
推奨 |
TEST_INFRASTRUCTURE_FAILURE_FILE |
書き込み可能ディレクトリ内のプライベート ファイルへの絶対パス(このファイルは、テストの不安定な失敗を報告する一般的なメカニズムとしてではなく、テスト インフラストラクチャで発生した障害の報告にのみ使用してください)。この場合、テスト インフラストラクチャはテストに固有のものではなく、誤動作によってテストが失敗する原因となるシステムまたはライブラリとして定義されます。1 行目は障害の原因となったテスト インフラストラクチャ コンポーネントの名前、2 行目は人が読める形式の失敗の説明です。追加された行は無視されます)。 | 省略可 |
TEST_LOGSPLITTER_OUTPUT_FILE |
書き込み可能なディレクトリ内のプライベート ファイルへの絶対パス(Logsplitter protobuffer ログの書き込みに使用) | 省略可 |
TEST_PREMATURE_EXIT_FILE |
書き込み可能ディレクトリ内のプライベート ファイルの絶対パス(exit() の呼び出しをキャッチする際に使用) |
省略可 |
TEST_RANDOM_SEED |
--runs_per_test オプションを使用する場合、個々のテスト実行ごとに TEST_RANDOM_SEED が run number(1 から開始)に設定されます。 |
省略可 |
TEST_RUN_NUMBER |
--runs_per_test オプションを使用する場合、個々のテスト実行ごとに TEST_RUN_NUMBER が run number(1 から開始)に設定されます。 |
省略可 |
TEST_TARGET |
テスト対象の名前 | 省略可 |
TEST_SIZE |
テスト size |
省略可 |
TEST_TIMEOUT |
テスト timeout (秒) |
省略可 |
TEST_SHARD_INDEX |
シャード インデックス(sharding を使用する場合) |
省略可 |
TEST_SHARD_STATUS_FILE |
sharding のサポートを示すタップ先のファイルパス |
省略可 |
TEST_SRCDIR |
ランファイル ツリーのベースに対する絶対パス | required |
TEST_TOTAL_SHARDS |
合計 shard count (sharding が使用されている場合) |
省略可 |
TEST_TMPDIR |
書き込み可能な非公開のディレクトリへの絶対パス | required |
TEST_WORKSPACE |
ローカル リポジトリのワークスペース名 | 省略可 |
TEST_UNDECLARED_OUTPUTS_DIR |
非公開の書き込み可能なディレクトリへの絶対パス(宣言されていないテスト出力の書き込みに使用されます)。TEST_UNDECLARED_OUTPUTS_DIR ディレクトリに書き込まれたファイルは圧縮され、bazel-testlogs の outputs.zip ファイルに追加されます。 |
省略可 |
TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR |
書き込み可能な非公開のディレクトリへの絶対パス(宣言されていないテスト出力アノテーション .part と .pb ファイルの書き込みに使用されます)。 |
省略可 |
TEST_WARNINGS_OUTPUT_FILE |
書き込み可能なディレクトリ内のプライベート ファイルへの絶対パス(テスト ターゲットの警告の書き込みに使用) | 省略可 |
TESTBRIDGE_TEST_ONLY |
--test_filter の値(指定されている場合) |
省略可 |
TZ |
UTC |
required |
USER |
getpwuid(getuid())->pw_name の値 |
required |
XML_OUTPUT_FILE |
テスト結果が XML 出力ファイルを書き込む場所。それ以外の場合、Bazel はテストアクションの一部としてテストログをラップするデフォルトの XML 出力ファイルを生成します。XML スキーマは、JUnit テスト結果スキーマに基づいています。 | 省略可 |
BAZEL_TEST |
テスト実行可能ファイルが bazel test によって駆動されていることを示します |
required |
環境によっては、追加のエントリが含まれる場合があります。テストは、上記以外の環境変数の存在、不在、値に依存してはなりません。
初期作業ディレクトリは $TEST_SRCDIR/$TEST_WORKSPACE
とします。
現在のプロセス ID、プロセス グループ ID、セッション ID、親プロセス ID が指定されていません。プロセスが、プロセス グループ リーダーか、セッション リーダーかは問いません。このプロセスには制御端末がある場合とない場合があります。プロセスには、実行中の子プロセスまたは未完了の子プロセスが 0 個以上存在する場合があります。テストコードが制御を取得したとき、プロセスに複数のスレッドを含めないでください。
ファイル記述子 0(stdin
)は読み取り用に開いておく必要がありますが、アタッチされる内容が未指定です。テストは読み取りできません。ファイル記述子 1(stdout
)と 2(stderr
)は書き込み用に開いておく必要がありますが、アタッチされる内容は指定されません。ターミナル、パイプ、通常のファイルなど、文字を書き込めるあらゆるものがこれに該当します。開いているファイル テーブル内のエントリを共有する(つまり、ユーザーが個別にシークできない)場合があります。テストでは、他のオープン ファイル記述子は継承しません。
最初の umask は 022
または 027
です。
アラームやインターバル タイマーは保留になりません。
ブロックするシグナルの最初のマスクは空にします。すべてのシグナルをデフォルトのアクションに設定する必要があります。
初期リソース上限(ソフトとハードの両方)は次のように設定する必要があります。
リソース | 上限 |
---|---|
RLIMIT_AS |
unlimited |
RLIMIT_CORE |
未設定 |
RLIMIT_CPU |
unlimited |
RLIMIT_DATA |
unlimited |
RLIMIT_FSIZE |
unlimited |
RLIMIT_LOCKS |
unlimited |
RLIMIT_MEMLOCK |
unlimited |
RLIMIT_MSGQUEUE |
未設定 |
RLIMIT_NICE |
未設定 |
RLIMIT_NOFILE |
1024 以上 |
RLIMIT_NPROC |
未設定 |
RLIMIT_RSS |
unlimited |
RLIMIT_RTPRIO |
未設定 |
RLIMIT_SIGPENDING |
未設定 |
RLIMIT_STACK |
無制限、または 2,044 KB <= rlim <= 8,192 KB |
初期プロセス時間(times()
で返される)とリソース使用率(getrusage()
で返される)は指定されていません。
最初のスケジューリング ポリシーと優先度が指定されていません。
ホストシステムの役割
テストランナーを直接制御するユーザー コンテキストの側面に加えて、テストが実行されるオペレーティング システムは、有効なテスト実行を行うために特定のプロパティを満たす必要があります。
ファイルシステム
テストで観察されるルート ディレクトリは、実際のルート ディレクトリである場合もあれば、そうでない場合もあります。
/proc
がマウントされます。
すべてのビルドツールは、ローカル インストールで使用される /usr
の下の絶対パスに存在する必要があります。
/home
で始まるパスは使用できません。テストでは、このようなパスにアクセスできません。
/tmp
は書き込み可能でなければなりませんが、テストではこれらのパスを使用しないでください。
テストでは、専用のパスが排他的に使用できると仮定しないでください。
テストでは、マウントされたファイル システムで時間が有効になっていると想定してはいけません。
ユーザーとグループ
ユーザー root、なし、単体テストが存在する必要があります。グループ root、nobody、eng が存在する必要があります。
root 以外のユーザーとしてテストを実行する必要があります。実際のユーザー ID はグループ ID も同じにする必要があります。さらに、現在のユーザー ID、グループ ID、ユーザー名、グループ名は指定されていません。補助グループ ID のセットが指定されていません。
現在のユーザー ID とグループ ID には、getpwuid()
と getgrgid()
で取得できる対応する名前が必要です。補助グループ ID について、同じことが当てはまらない場合もあります。
現在のユーザーにはホーム ディレクトリが必要です。書き込み可能ではない可能性があります。テストは、書き込みを試行してはなりません。
ネットワーキング
ホスト名が指定されていません。ドットを含めることもできます。ホスト名を解決すると、現在のホストの IP アドレスが必要になります。最初のドットの後に続くホスト名の解決も機能するはずです。ホスト名 localhost が解決する必要があります。
その他のリソース
テストには、少なくとも 1 つの CPU コアが付与されます。その他の識別子も利用できる可能性がありますが、提供できる保証はありません。このコアのその他のパフォーマンス特性は指定されていません。テストルールに「cpu:n」(n は正の数)タグを追加すると、予約をより多くの CPU コアに増やすことができます。マシンの CPU コアの合計がリクエストされた数よりも少ない場合でも、Bazel はテストを実行します。テストでシャーディングを使用する場合、個々のシャードはここで指定されている CPU コア数を予約します。
テストではサブプロセスを作成できますが、グループやセッションは作成できません。
テストで使用できる入力ファイルの数には上限があります。この上限は変更される可能性がありますが、現在、数万個の入力範囲があります。
日時
現在の日時が指定されていません。システムのタイムゾーンが指定されていません。
X Windows は使用できない場合があります。X サーバーを必要とするテストでは、Xvfb を起動する必要があります。
ファイル システムとのやり取りをテストする
特に指定しない限り、テスト環境変数で指定されたすべてのファイルパスは、ローカル ファイル システム上の場所を参照します。
テストでは、$TEST_TMPDIR
と $TEST_UNDECLARED_OUTPUTS_DIR
(設定されている場合)で指定されたディレクトリ内にのみファイルを作成する必要があります。
これらのディレクトリは、最初は空です。
テストでは、これらのディレクトリを削除、変更、変更しないでください。
これらのディレクトリは、シンボリック リンクになることがあります。
$TEST_TMPDIR/.
のファイルシステム タイプは指定されていません。
テストでは、宣言されていない出力ファイルにアノテーションとして .part ファイルを $TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR
に書き込むこともできます。
まれに、テストで /tmp
にファイルを作成しなければならない場合があります。たとえば、Unix ドメイン ソケットのパスの長さの上限は通常、/tmp
の下にソケットを作成する必要があります。Bazel はこのようなファイルをトラッキングできません。テスト自体を密閉させ、一意のパスを使用して、同時に実行される他のテストプロセスや非テストプロセスとの競合を回避し、/tmp
内に作成するファイルをクリーンアップする必要があります。
一般的なテスト フレームワーク(JUnit4 TemporaryFolder
や Go TempDir
など)には、/tmp
の下に一時ディレクトリを作成する方法があります。これらのテスト フレームワークには、/tmp
内のファイルをクリーンアップする機能が含まれているため、TEST_TMPDIR
の外部でファイルを作成する場合でも使用できます。
テストでは、runfiles メカニズム、または入力ファイルを利用できるようにする実行環境の他の部分から入力にアクセスする必要があります。
テストは、実行可能ファイルの場所から推測されたパスでビルドシステムの他の出力にアクセスしてはなりません。
runfiles ツリーに通常のファイル、シンボリック リンク、混合のいずれが含まれているかは不明です。runfiles ツリーには、ディレクトリへのシンボリック リンクが含まれていることがあります。テストでは、runfiles ツリー内で ..
コンポーネントを含むパスを使用しないでください。
実行ファイル ツリー内のディレクトリ、ファイル、シンボリック リンク(シンボリック リンクを通過するパスを含む)は書き込み可能ではありません。(つまり、最初の作業ディレクトリは書き込み可能でない可能性があります)。テストでは、実行ファイルのどの部分も書き込み可能である、または現在のユーザーが所有すると仮定してはなりません(たとえば、chmod
と chgrp
が失敗する可能性があります)。
テスト実行時にランファイル ツリー(シンボリック リンクを走査するパスを含む)を変更しないでください。親ディレクトリとファイル システムのマウントを、runfiles ツリー内のパスの解決結果に影響するような方法で変更しないでください。
早期終了をキャッチするために、テストでは開始時に TEST_PREMATURE_EXIT_FILE
が指定するパスにファイルを作成し、終了時に削除します。テストの終了時に Bazel がファイルを確認した場合は、テストが途中で終了したと見なし、不合格としてマークします。
タグ規則
テストルールの一部のタグは特別な意味を持ちます。tags
属性に関する Bazel ビルド百科事典もご覧ください。
タグ | 意味 |
---|---|
exclusive |
他のテストを同時に実行しない |
external |
test に外部依存関係があるので、テスト キャッシュを無効にします |
large |
test_suite 規則。大規模なテストのスイート。 |
manual * |
ワイルドカード ターゲット パターン(:... 、:* 、:all など)にテスト ターゲットを含めないでください。 |
medium |
test_suite 規則。中規模テストスイート。 |
small |
test_suite 従来型の小さなテストスイート |
smoke |
test_suite 規則。コード変更をバージョン管理システムに commit する前に実行する必要がある。 |
ランファイル
以下の例では、//foo/bar:unittest
というラベルの付いた *_binary() ルールがあり、//deps/server:server
というラベルの付いたルールに実行時に依存関係があるとします。
場所
ターゲット //foo/bar:unittest
の runfiles ディレクトリは、$(WORKSPACE)/$(BINDIR)/foo/bar/unittest.runfiles
ディレクトリです。このパスは runfiles_dir
と呼ばれます。
依存関係
runfiles ディレクトリは、*_binary()
ルールのコンパイル時の依存関係として宣言されます。runfiles ディレクトリは、*_binary()
ルールや、コンパイル時または実行時依存関係のいずれかに影響する BUILD ファイルのセットに依存します。ソースファイルを変更しても runfiles ディレクトリの構造には影響しません。したがって、再ビルドは行われません。
目次
runfiles ディレクトリに以下の行があります。
- 実行時の依存関係へのシンボリック リンク:
*_binary()
ルールの実行時の依存関係である各 OutputFile と CommandRule は、runfiles ディレクトリ内の 1 つのシンボリック リンクで表されます。シンボリック リンクの名前は$(WORKSPACE)/package_name/rule_name
です。たとえば、サーバーのシンボリック リンクの名前は$(WORKSPACE)/deps/server/server
、フルパスは$(WORKSPACE)/foo/bar/unittest.runfiles/$(WORKSPACE)/deps/server/server
です。シンボリック リンクの宛先は、OutputFile または CommandRule の OutputFileName() で、絶対パスで表されます。したがって、シンボリック リンクの宛先は$(WORKSPACE)/linux-dbg/deps/server/42/server
になります。 - サブランファイルへのシンボリック リンク:
*_binary()
C のランタイム依存関係であるすべての*_binary()
Z に対して、C の runfiles ディレクトリに Z のランファイルへの 2 番目のリンクがあります。シンボリック リンクの名前は$(WORKSPACE)/package_name/rule_name.runfiles
です。シンボリック リンクのターゲットは runfiles ディレクトリです。たとえば、すべてのサブプログラムは共通のランファイル ディレクトリを共有します。