テスト実行環境の詳細な仕様。
背景
Bazel BUILD 言語には、多くの言語で自動テストプログラムを定義するために使用できるルールが含まれています。
テストは bazel test
を使用して実行されます。
テストバイナリを直接実行することもできます。これは許可されますが、推奨されません。このような呼び出しは、後述の要件に準拠していないためです。
テストは気密性が保たれている必要があります。つまり、宣言された依存関係があるリソースにのみアクセスする必要があります。テストが適切に密閉されていない場合、過去に再現可能な結果は得られません。これは、原因の特定(どの変更がテストを破壊したかの特定)、リリース エンジニアリングの監査可能性、テストのリソース分離にとって重大な問題になる可能性があります(一部のテストがサーバーと通信するため、自動テスト フレームワークはサーバーを DDoS してはなりません)。
目標
このページの目的は、Bazel テストのランタイム環境と想定される動作を正式に確立することです。また、テストランナーとビルドシステムにも要件が課せられます。
テスト環境仕様により、テスト作成者は未指定の動作に依存しなくて済むため、テスト インフラストラクチャで実装の変更をより自由に行えるようになります。この仕様では、現在、適切に完全性、確定性、再入性を備えていないにもかかわらず、多くのテストが合格しているいくつかの穴を締めています。
このページは、規範的かつ信頼できる情報として提供されています。この仕様とテストランナーの実装された動作が一致しない場合、仕様が優先されます。
提案された仕様
キーワード「MUST」、「MUST NOT」、「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、「RECOMMENDED」、「MAY」、「OPTIONAL」は、IETF RFC 2119 に記載されているように解釈されます。
テストの目的
Bazel テストの目的は、リポジトリにチェックインされたソースファイルのプロパティを確認することです。(このページの「ソースファイル」には、テストデータ、ゴールド出力、バージョン管理対象のその他のファイルが含まれます)。1 人のユーザーが、維持されることを想定している不変量をアサートするテストを作成します。他のユーザーが後でテストを実行して、不変条件が破られているかどうかを確認します。テストがソースファイル以外の変数に依存している場合(非完全性)、テストが合格しなくなったときに、後続のユーザーが変更が原因であると確信できないため、その価値は低下します。
したがって、テストの結果は次のものにのみ依存する必要があります。
- テストで宣言された依存関係があるソースファイル
- テストで宣言された依存関係があるビルドシステムのプロダクト
- テストランナーによって動作が一定に保たれることが保証されているリソース
現在のところ、このような動作は適用されていません。ただし、テストランナーは、今後このような適用を追加する権利を留保します。
ビルドシステムの役割
テストルールは、それぞれが実行可能プログラムを生成しなければならないという点で、バイナリルールに似ています。言語によっては、これは言語固有のハーネスとテストコードを組み合わせたスタブ プログラムです。テストルールは、他の出力も生成する必要があります。テストランナーは、メインのテスト実行可能ファイルに加えて、runfiles のマニフェスト(実行時にテストで使用できる入力ファイル)が必要になります。また、テストのタイプ、サイズ、タグに関する情報が必要になる場合があります。
ビルドシステムは、ランファイルを使用してコードとデータを配信する場合があります。(これは、動的リンクの使用など、テスト間でファイルを共有することで各テストバイナリを小さくするための最適化として使用できます)。ビルドシステムは、生成された実行可能ファイルが、ソースツリーまたは出力ツリー内の絶対的な場所へのハードコードされた参照ではなく、テストランナーから提供される runfiles イメージを介してこれらのファイルを読み込むようにする必要があります。
テストランナーの役割
テストランナーの観点から、各テストは execve()
で呼び出せるプログラムです。テストを実行する方法は他にもあります。たとえば、IDE で Java テストをプロセス内で実行できます。ただし、テストをスタンドアロン プロセスとして実行した結果は、信頼できるものとして見なす必要があります。テストプロセスが完了し、終了コードがゼロで正常に終了した場合、テストは合格です。その他の結果はテスト失敗とみなされます。特に、PASS
または FAIL
のいずれかの文字列を stdout に書き込むことは、テストランナーにとって意味がありません。
テストの実行に時間がかかりすぎる、リソースの上限を超えている、またはテストランナーが禁止されている動作を検出した場合は、テストを強制終了し、実行を失敗として扱うことがあります。ランナーは、テストプロセスまたはその子プロセスにシグナルを送信した後に、テストが合格したと報告してはなりません。
テスト対象全体(個々のメソッドやテストではなく)に、完了までの実行時間が制限されます。テストの制限時間は、次の表に従って timeout
属性に基づいています。
timeout | 制限時間(秒) |
---|---|
short | 60 |
中 | 300 |
long | 900 |
永遠 | 3600 |
タイムアウトを明示的に指定していないテストには、次のようにテストの size
に基づいてタイムアウトが暗黙的に設定されます。
サイズ | 暗黙的なタイムアウトのラベル |
---|---|
小 | short |
中 | 中 |
大 | long |
巨大 | 永遠 |
明示的なタイムアウト設定のない「大規模」テストには、実行に 900 秒が割り当てられます。タイムアウトが「短い」の「中程度」のテストには 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 ファイルで指定します。指定しない場合、テストのサイズはデフォルトで「medium」になります。
テストのメインプロセスが終了しても、その子プロセスの一部がまだ実行中の場合、テストランナーは実行が完了したと見なし、メインプロセスから検出された終了コードに基づいて成功または失敗としてカウントする必要があります。テストランナーは、不要なプロセスを強制終了することがあります。テストでは、このような方法でプロセスをリークしないでください。
テストのシャーディング
テストは、テスト シャーディングを使用して並列化できます。テスト シャーディングを有効にするには、--test_sharding_strategy
と shard_count
をご覧ください。シャーディングが有効になっている場合、テストランナーはシャードごとに 1 回起動されます。環境変数 TEST_TOTAL_SHARDS
はシャード数で、TEST_SHARD_INDEX
はシャード インデックス(0 から始まる)です。ランナーは、この情報を使用して、実行するテストを選択します(たとえば、ラウンド ロビン戦略を使用します)。すべてのテストランナーがシャーディングをサポートしているわけではありません。ランナーがシャーディングをサポートしている場合、TEST_SHARD_STATUS_FILE
で指定されたファイルの最終更新日を作成または更新する必要があります。それ以外の場合、Bazel はシャーディングをサポートしていないと想定し、追加のランナーを起動しません。
初期条件
テストを実行する際、テストランナーは特定の初期条件を確立する必要があります。
テストランナーは、argv[0]
にあるテスト実行可能ファイルのパスで各テストを呼び出す必要があります。このパスは、テストの現在のディレクトリ(runfiles ツリー内、後述)の下にある相対パスにする必要があります。ユーザーが明示的にリクエストしない限り、テストランナーは他の引数をテストに渡さないでください。
初期環境ブロックは次のように構成します。
変数 | 値 | ステータス |
---|---|---|
HOME |
$TEST_TMPDIR の値 |
推奨 |
LANG |
未設定 | 必須 |
LANGUAGE |
未設定 | 必須 |
LC_ALL |
未設定 | 必須 |
LC_COLLATE |
未設定 | 必須 |
LC_CTYPE |
未設定 | 必須 |
LC_MESSAGES |
未設定 | 必須 |
LC_MONETARY |
未設定 | 必須 |
LC_NUMERIC |
未設定 | 必須 |
LC_TIME |
未設定 | 必須 |
LD_LIBRARY_PATH |
共有ライブラリを含むディレクトリをコロンで区切ったリスト | 省略可 |
JAVA_RUNFILES |
$TEST_SRCDIR の値 |
廃止 |
LOGNAME |
$USER の値 |
必須 |
PATH |
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:. |
推奨 |
PWD |
$TEST_SRCDIR/workspace-name |
推奨 |
SHLVL |
2 |
推奨 |
TEST_INFRASTRUCTURE_FAILURE_FILE |
書き込み可能なディレクトリ内の非公開ファイルへの絶対パス(このファイルは、テスト インフラストラクチャに起因する障害を報告する場合にのみ使用し、テストの不安定な障害を報告するための一般的なメカニズムとしては使用しないでください。このコンテキストでは、テスト インフラストラクチャは、テスト固有ではないが、誤動作によってテストの失敗を引き起こす可能性があるシステムまたはライブラリと定義されます。最初の行は、障害の原因となったテスト インフラストラクチャ コンポーネントの名前で、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 |
runfiles ツリーのベースへの絶対パス | 必須 |
TEST_TOTAL_SHARDS |
合計 shard count (sharding が使用されている場合) |
省略可 |
TEST_TMPDIR |
書き込み可能なプライベート ディレクトリへの絶対パス | 必須 |
TEST_WORKSPACE |
ローカル リポジトリのワークスペース名 | 省略可 |
TEST_UNDECLARED_OUTPUTS_DIR |
書き込み可能な非公開ディレクトリへの絶対パス(宣言されていないテスト出力の書き込みに使用) | 省略可 |
TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR |
書き込み可能な非公開ディレクトリの絶対パス(宣言されていないテスト出力アノテーション .part ファイルと .pb ファイルを書き込むために使用されます)。 |
省略可 |
TEST_WARNINGS_OUTPUT_FILE |
書き込み可能なディレクトリ内の非公開ファイルへの絶対パス(テスト対象の警告の書き込みに使用) | 省略可 |
TESTBRIDGE_TEST_ONLY |
--test_filter の値(指定されている場合) |
省略可 |
TZ |
UTC |
必須 |
USER |
getpwuid(getuid())->pw_name の値 |
必須 |
XML_OUTPUT_FILE |
テストアクションがテスト結果の XML 出力ファイルを書き込む場所。それ以外の場合、Bazel はテストアクションの一部としてテストログをラップするデフォルトの XML 出力ファイルを生成します。XML スキーマは JUnit テスト結果スキーマに基づいています。 | 省略可 |
BAZEL_TEST |
テスト実行可能ファイルが bazel test によって実行されていることを示します。 |
必須 |
環境には追加のエントリが含まれている場合があります。テストは、上記に記載されていない環境変数の存在、不在、値に依存しないでください。
初期作業ディレクトリは $TEST_SRCDIR/$TEST_WORKSPACE
です。
現在のプロセス ID、プロセス グループ ID、セッション ID、親プロセス ID は指定されていません。プロセスは、プロセス グループ リーダーまたはセッション リーダーである場合とそうでない場合があります。プロセスには制御ターミナルが存在する場合と存在しない場合がある。プロセスには、実行中または未回収の子プロセスが 0 個以上存在する場合があります。テストコードが制御を取得したときに、プロセスに複数のスレッドが存在してはなりません。
ファイル記述子 0(stdin
)は読み取り用に開く必要がありますが、何に接続されているかは指定されていません。テストで読み取ることはできません。ファイル記述子 1(stdout
)と 2(stderr
)は書き込み用に開く必要がありますが、何に接続されているかは指定されていません。ターミナル、パイプ、通常のファイルなど、文字を書き込むことができる任意の場所です。オープン ファイル テーブル内のエントリを共有する場合があります(つまり、個別にシークすることはできません)。テストは、他の開いているファイル記述子を継承しないでください。
初期の umask は 022
または 027
にする必要があります。
アラームやインターバル タイマーは保留にできません。
ブロックされたシグナルの初期マスクは空にする必要があります。すべてのシグナルはデフォルトのアクションに設定する必要があります。
初期リソースの上限(ソフトとハード)は、次のように設定する必要があります。
リソース | 上限 |
---|---|
RLIMIT_AS |
無制限 |
RLIMIT_CORE |
指定なし |
RLIMIT_CPU |
無制限 |
RLIMIT_DATA |
無制限 |
RLIMIT_FSIZE |
無制限 |
RLIMIT_LOCKS |
無制限 |
RLIMIT_MEMLOCK |
無制限 |
RLIMIT_MSGQUEUE |
指定なし |
RLIMIT_NICE |
指定なし |
RLIMIT_NOFILE |
1,024 以上 |
RLIMIT_NPROC |
指定なし |
RLIMIT_RSS |
無制限 |
RLIMIT_RTPRIO |
指定なし |
RLIMIT_SIGPENDING |
指定なし |
RLIMIT_STACK |
無制限、または 2044 KB <= rlim <= 8192 KB |
最初の処理時間(times()
によって返される)とリソース使用率(getrusage()
によって返される)は指定されていません。
初期のスケジューリング ポリシーと優先度は指定されていません。
ホストシステムの役割
テスト実行の有効性を確保するには、テストランナーの直接制御下にあるユーザー コンテキストの要素に加えて、テストを実行するオペレーティング システムが特定のプロパティを満たしている必要があります。
ファイルシステム
テストで検出されたルート ディレクトリが、実際のルート ディレクトリである場合もあれば、そうでない場合もあります。
/proc
をマウントする必要があります。
すべてのビルドツールは、ローカル インストールで使用される /usr
の絶対パスに存在する必要があります。
/home
で始まるパスは使用できない場合があります。テストでは、このようなパスにアクセスしないでください。
/tmp
は書き込み可能である必要がありますが、テストではこれらのパスを使用しないでください。
テストでは、定数パスが排他的に使用可能であると想定しない。
テストでは、マウントされたファイル システムで atime が有効になっていると想定してはなりません。
ユーザーとグループ
ユーザー root、nobody、unittest が存在している必要があります。グループ root、nobody、eng が存在している必要があります。
テストは root 以外のユーザーとして実行する必要があります。実際のユーザー ID と有効なユーザー 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
(設定されている場合)で指定されたディレクトリ内にのみファイルを作成する必要があります。
これらのディレクトリは最初は空です。
テストでは、これらのディレクトリの削除、chmod、その他の変更を試みてはなりません。
これらのディレクトリはシンボリック リンクである可能性があります。
$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 ツリー内に ..
コンポーネントを含むパスを使用しないでください。
runfiles ツリー内のディレクトリ、ファイル、シンボリック リンク(シンボリック リンクを移動するパスを含む)は、書き込み可能にする必要があります。(したがって、最初の作業ディレクトリは書き込み可能にする必要があります)。テストでは、実行ファイルの一部が書き込み可能であるか、現在のユーザーが所有していることを前提とすることはできません(たとえば、chmod
と chgrp
が失敗する可能性があります)。
テストの実行中に、runfiles ツリー(シンボリック リンクを移動するパスを含む)が変更されてはなりません。親ディレクトリとファイル システムのマウントを、runfiles ツリー内のパスの解決結果に影響する方法で変更しないでください。
早期終了をキャッチするために、テストは開始時に TEST_PREMATURE_EXIT_FILE
で指定されたパスにファイルを作成し、終了時に削除することがあります。テストの終了時に Bazel がファイルを見つけると、テストが途中で終了したと見なされ、失敗としてマークされます。
タグの規則
テストルールのタグには、特殊な意味を持つものもあります。tags
属性に関する Bazel Build Encyclopedia もご覧ください。
タグ | 意味 |
---|---|
exclusive |
他のテストを同時に実行しない |
external |
テストに外部依存関係がある場合: テストのキャッシュを無効にする |
large |
test_suite 規則: 大規模なテストスイート |
manual * |
ワイルドカード ターゲット パターン(:... 、:* 、:all など)にテストターゲットを含めない |
medium |
test_suite 規則: 中程度のテストスイート |
small |
test_suite 規則: 一連の小規模なテスト |
smoke |
test_suite 規則。コード変更をバージョン管理システムに commit する前に実行する必要があります。 |
Runfiles
以下では、//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 の runfiles への 2 番目のリンクがあります。シンボリック リンクの名前は$(WORKSPACE)/package_name/rule_name.runfiles
です。シンボリック リンクのターゲットは runfiles ディレクトリです。たとえば、すべてのサブプログラムは共通の runfiles ディレクトリを共有します。