テスト実行環境の包括的な仕様。
背景
Bazel BUILD 言語には、多くの言語で自動テスト プログラムを定義するために使用できるルールが含まれています。
テストは bazel test
を使用して実行されます。
ユーザーはテストバイナリを直接実行することもできます。このような呼び出しは、下記の指示に従っていないため、許可されますが、推奨されません。
テストは密閉型である必要があります。つまり、依存関係が宣言されているリソースにのみアクセスする必要があります。テストが適切に密閉されていない場合、歴史的に再現可能な結果が得られません。これは、問題の原因(テストでどの変更がテストされなかったかの判断)、リリース エンジニアリングの監査可能性、テストのリソースの分離(一部のテストが偶発的に通信する DDOS ではなく)を必要とします。
目標
このページの目的は、Bazel テストのランタイム環境と予期される動作を正式に確立することです。また、テストランナーとビルドシステムに要件も課します。
テスト環境の仕様により、テストの作成者は不特定の動作に依存せずに、テスト インフラストラクチャに実装の変更の自由度を高めることができます。この仕様では、現在、適切な密閉性、確定性、リエントラントではないにもかかわらず、多くのテストに合格できる穴が絞り込まれています。
このページは、規範的かつ信頼できるページであることを前提としています。この仕様と、実装されたテストランナーの動作が一致しない場合は、仕様が優先されます。
提案された仕様
「MUST」、「MUST NOT」、「REQUIRED」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、「REMAENDED」、「MAY」は、IETF RFC 2119 の説明のとおりです。
テストの目的
Bazel テストの目的は、リポジトリにチェックインされたソースファイルのプロパティを確認することです。(このページの「ソースファイル」には、テストデータやゴールデン出力など、バージョン管理の対象となっているものが含まれます)。あるユーザーは、テストを作成して、維持されることを期待する不変性をアサートします。他のユーザーは、後でテストを実行して、不変条件が破損しているかどうかを確認します。テストがソースファイル以外の変数(非密閉)に依存している場合、テストに合格しなかった場合は後で変更に失敗するのを確認できないため、その値は小さくなります。
そのため、テストの結果は次の条件にのみ依存する必要があります。
- テストで依存関係が宣言されているソースファイル
- テストの依存関係が宣言されているビルドシステムのプロダクト
- テストランナーにより動作が一定であることが保証されているリソース
現在、このような動作は適用されません。ただし、テストランナーは今後、このような違反措置を追加する権限を有します。
ビルドシステムの役割
テストルールは、それぞれが実行可能プログラムを生成する必要があるという点でバイナリルールに似ています。一部の言語では、これは言語固有のハーネスをテストコードと組み合わせるスタブ プログラムです。テストルールでも他の出力を生成する必要があります。テストランナーは、プライマリ テストの実行可能ファイルに加えて、テストファイルのマニフェストと実行時にテストで使用できるようにする必要がある入力ファイルを必要とします。また、テストのタイプ、サイズ、タグに関する情報が必要になることもあります。
ビルドシステムは、実行ファイルを使用してコードのほかデータを提供する場合があります。(これは、ダイナミック リンクの使用など、テスト間でファイルを共有することで、各テストバイナリを縮小するための最適化として使用できます)。ビルドシステムは、生成された実行可能ファイルが、ソースツリーまたは出力ツリー内の絶対位置への参照をハードコードするのではなく、テストランナーが提供する実行ファイル イメージを介してこれらのファイルを読み込むようにする必要があります。
テストランナーの役割
テストランナーの観点から、各テストは execve()
で呼び出すことができるプログラムです。テストを実行する方法は他にもあります。たとえば、IDE ではプロセス内の Java テストの実行を許可します。ただし、テストをスタンドアロン プロセスとして実行した結果は信頼できるものと考える必要があります。テストプロセスが完了まで実行され、終了コードが 0 で正常に終了した場合、テストは合格です。それ以外の結果は、テストの不合格とみなされます。特に、文字列 PASS
または FAIL
の stdout への書き込みは、テストランナーにとって重要ではありません。
テストの実行に時間がかかりすぎる場合や、テストランナーが禁止された動作を検出した場合、テストを強制終了して、実行を失敗として扱うことがあります。テストプロセスまたはその子にシグナルを送信した後に、ランナーがテストに合格したことを報告してはなりません。
個々のメソッドやテストではなく、テスト ターゲット全体(実行が完了するまでの時間)には制限があります。テストの制限時間は、次の表の timeout
属性に基づいています。
timeout | 制限時間(秒) |
---|---|
short | 60 |
やや不足 | 300 |
long | 900 |
永遠 | 3,600 |
タイムアウトを明示的に指定しない場合は、次のようにテストの size
に基づいてタイムアウトが暗黙的に指定されます。
size | 暗黙のタイムアウト ラベル |
---|---|
小さく初めて | short |
中程度の幅 | やや不足 |
大 | long |
巨大 | 永遠 |
明示的なタイムアウトが設定されていない「大規模」テストは、900 秒実行されるよう割り当てられます。タイムアウトが「short」の「中」テストには 60 秒が割り当てられます。
また、共通定義で説明されているように、size
は timeout
とは異なり、テストをローカルで実行するときに想定される他のリソース(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
で指定されたファイルの最終変更日を作成または更新する必要があります。それ以外の場合、--incompatible_check_sharding_support
が有効で、シャーディングされている場合、Bazel はテストに失敗します。
初期条件
テストを実行する際、テストランナーは特定の初期条件を設定する必要があります。
テストランナーは、argv[0]
内のテスト実行可能ファイルのパスを使用して各テストを呼び出す必要があります。このパスは、相対パスで、テストの現在のディレクトリ(runfiles ツリーにあります)の下にする必要があります(下記を参照)。ユーザーが明示的にリクエストしない限り、テストランナーは他の引数をテストに渡さないでください。
初期の環境ブロックは次のように構成します。
変数 | 値 | ステータス |
---|---|---|
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 |
書き込み可能なディレクトリ内のプライベート ファイルへの絶対パス。このファイルは、テストの不安定な障害を報告する一般的なメカニズムとしてではなく、テスト インフラストラクチャで発生した障害を報告する場合にのみ使用してください。この場合、テスト インフラストラクチャは、テスト固有ではないが、誤動作によってテストが失敗する可能性があるシステムやライブラリとして定義されます。最初の行は障害の原因となったテスト インフラストラクチャ コンポーネントの名前、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 ツリーのベースへの絶対パス | 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
)は書き込み用に開いている必要がありますが、アタッチされる内容が指定されていません。端子、パイプ、通常のファイルなど、文字を書き込むことができるあらゆるものを使用できます。開いているファイル テーブルのエントリを共有する場合があります(つまり、ユーザーが個別にシークすることはできません)。テストは、他のオープン ファイル記述子を継承しないでください。
最初のマスクは 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
は書き込み可能ですが、テストではこれらのパスを使用しないでください。
テストでは、定数パスが排他的に使用できると仮定しないでください。
テストでは、マウントされたファイル システムに対して atime が有効になっていると仮定しないでください。
ユーザーとグループ
ユーザーの root、nobody、unittest が存在する必要があります。グループ 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 ツリー(シンボリック リンクを横断するパスを含む)を変更しないでください。親ディレクトリとファイル システムのマウントは、runfiles ツリー内のパスを解決した結果に影響するような変更を行ってはなりません。
テストでは、早期終了を捕捉するために、起動時に TEST_PREMATURE_EXIT_FILE
で指定されたパスにファイルを作成し、終了時に削除します。テストの終了時に Bazel がファイルを認識した場合、テストが途中で終了したとみなして失敗とマークします。
タグ規則
テストルールの一部のタグは、特別な意味を持ちます。tags
属性の Bazel ビルド百科事典もご覧ください。
タグ | 意味 |
---|---|
exclusive |
他のテストを同時に実行していない |
external |
テストに外部依存関係があります。テスト キャッシュを無効にします |
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 ディレクトリです。たとえば、すべてのサブプログラムは共通のランファイル ディレクトリを共有します。