百科事典をテストする

問題を報告 ソースを表示

テスト実行環境の完全な仕様。

背景

Bazel BUILD 言語には、多くの言語で自動テスト プログラムを定義するために使用できるルールが含まれています。

テストは bazel test を使用して実行されます。

ユーザーは、テストバイナリを直接実行することもできます。このような呼び出しは以下に説明する要件を遵守しないため、許可されていますが、承認されていません。

テストは密閉型であるべきです。つまり、宣言された依存関係があるリソースにのみアクセスするようにします。テストが適切に密閉型でないと、歴史的に再現可能な結果が得られません。これは、問題の原因の特定(テストに違反した変更の判断)、リリース エンジニアリングの監査可能性、テストのリソース分離(自動テスト フレームワークは、一部のテストとやり取りすることがあるため、サーバーを DDoS にすべきではありません)にとって重大な問題となる可能性があります。

目標

このページの目的は、Bazel テストのランタイム環境と予想される動作を正式に確立することです。また、テストランナーとビルドシステムに要件が適用されます。

テスト環境の仕様によって、テスト作成者が不明な動作に依存せずに、テスト インフラストラクチャが自由に実装を変更できるようになります。この仕様では、適切に密閉型、確定的、リエントラントでなくても、現在は多くのテストに合格する可能性のあるいくつかの穴を解消しています。

このページは、規範的かつ信頼できる情報を提供することを目的としています。この仕様とテストランナーの実装動作の間に相違がある場合は、仕様が優先されます。

仕様案

キーワード「MUST」、「MUST NOT」、「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD NOT」、「Recommended」、「MAY」、「OPTIONAL」は、IETF RFC 2119 に記載されているとおりに解釈されます。

テストの目的

Bazel テストの目的は、リポジトリにチェックインされたソースファイルのプロパティを確認することです。(このページの「ソースファイル」には、テストデータやゴールデン出力など、バージョン管理下にあるすべてのものが含まれています)。あるユーザーが、維持されることが期待される不変条件をアサートするテストを作成します。他のユーザーは後でテストを実行して、不変条件が壊れているかどうかを確認します。テストがソースファイル以外の変数(非密閉型)に依存している場合、その値は減少します。テストが合格すると、以降のユーザーは変更に問題があることを確認できないためです。

そのため、テスト結果は以下のみに依存して判断する必要があります。

  • テストの依存関係が宣言されているソースファイル
  • テストの依存関係が宣言されているビルドシステムのプロダクト
  • 動作が安定していることがテストランナーによって保証されているリソース

現在、このような動作は強制されません。ただし、テストランナーは、今後このような適用を追加する権利を有します。

ビルドシステムの役割

テストルールは、実行可能プログラムを生成する必要があるバイナリルールに似ています。一部の言語では、これは言語固有のハーネスとテストコードを組み合わせたスタブプログラムです。テストルールでは、他の出力も生成する必要があります。テストランナーには、主要なテスト実行可能ファイルに加えて、runfile(実行時にテストで使用できるようにする入力ファイル)のマニフェストが必要です。また、テストのタイプ、サイズ、タグに関する情報が必要になる場合もあります。

ビルドシステムは、コードやデータを配信するためにランファイルを使用できます。(これは、ダイナミック リンクを使用するなどしてテスト間でファイルを共有し、各テストバイナリを小さくするための最適化として使用できます)。ビルドシステムは、生成された実行可能ファイルがソースまたは出力ツリー内の絶対場所への絶対参照をハードコードするのではなく、テストランナーが提供する runfile イメージを介してこれらのファイルを読み込むようにする必要があります。

テストランナーのロール

テストランナーから見ると、各テストは execve() で呼び出せるプログラムです。テストを実行する方法は他にもある可能性があります。たとえば、IDE がプロセス内での Java テストの実行を許可する場合があります。ただし、テストをスタンドアロン プロセスとして実行した結果は、信頼できるものと見なされる必要があります。テストプロセスが正常に完了し、終了コードがゼロで正常に終了した場合、テストは合格です。その他の結果は、テストの不合格とみなされます。特に、文字列 PASS または FAIL を stdout に書き込むことは、テストランナーにとって重要ではありません。

テストの実行に時間がかかりすぎる場合、リソースの上限を超えている場合、またはテストランナーが他の方法で禁止されている動作を検出した場合は、テストを強制終了して実行を失敗として処理することがあります。ランナーは、テストプロセスまたはその子にシグナルを送信した後に、テストを合格として報告してはなりません。

個々のメソッドやテストではなく、テスト ターゲット全体に対して、完了までの時間が制限されています。テストの時間制限は、次の表に示すように、timeout 属性に基づきます。

timeout 制限時間(秒)
short 60
やや不足 300
long 900
永遠 3600

タイムアウトを明示的に指定していないテストには、次のようにテストの size に基づいて暗黙的にタイムアウトが設定されます。

size 暗黙のタイムアウト ラベル
小さく初めて short
medium やや不足
long
巨大 永遠

明示的なタイムアウト設定がない大規模テストには 900 秒が割り当てられます。タイムアウトが「short」の「中」のテストには 60 秒が割り当てられます。

さらに、sizetimeout とは異なり、一般的な定義で説明されているように、テストをローカルで実行するときの他のリソース(RAM など)の想定されるピーク使用量を決定します。

size ラベルと timeout ラベルの組み合わせはすべて有効なため、「巨大な」テストの場合はタイムアウトを「short」と宣言できます。恐ろしいことがすぐに起こるかもしれません

テストは、タイムアウトに関係なく、任意の速さで結果が返されることがあります。テストでは、過剰なタイムアウトに対してペナルティが科されることはありませんが、警告は発行される場合があります。通常は、脆弱性を発生させることなく、可能な限り厳しくタイムアウトを設定します。

低速であることがわかっている条件下で手動で実行する場合は、テストのタイムアウトを --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_strategyshard_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 書き込み可能なディレクトリにあるプライベート ファイルへの絶対パス(このファイルは、テストの不安定な失敗を報告するための一般的なメカニズムとしてではなく、テスト インフラストラクチャに起因する障害を報告するためにのみ使用してください)。ここでいうテスト インフラストラクチャとは、固有ではないものの誤動作によってテストの失敗を引き起こす可能性があるシステムまたはライブラリを指します。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 runfiles ツリーのベースへの絶対パス required
TEST_TOTAL_SHARDS 合計 shard countsharding が使用されている場合) 省略可
TEST_TMPDIR 書き込み可能なプライベート ディレクトリへの絶対パス required
TEST_WORKSPACE ローカル リポジトリのワークスペース名 省略可
TEST_UNDECLARED_OUTPUTS_DIR 書き込み可能なプライベート ディレクトリへの絶対パス(宣言されていないテスト出力の書き込みに使用)。TEST_UNDECLARED_OUTPUTS_DIR ディレクトリに書き込まれるすべてのファイルは圧縮され、bazel-testlogsoutputs.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(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 1,024 以上
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 とグループ 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 TemporaryFolderGo TempDir などの一般的なテスト フレームワークには、/tmp の下に一時ディレクトリを作成する独自の方法があります。これらのテスト フレームワークには、/tmp 内のファイルをクリーンアップする機能が含まれているため、TEST_TMPDIR の外部にファイルを作成しても、使用できます。

テストでは、runfile メカニズムを使用するか、入力ファイルを使用可能にするための実行環境の他の部分を介して入力にアクセスする必要があります。

テストでは、その実行可能ファイルの場所から推測されるパスでビルドシステムの他の出力にアクセスすることはできません。

runfiles ツリーに通常のファイル、シンボリック リンク、混合物のどれが含まれているかは指定されていません。runfiles ツリーにはディレクトリへのシンボリック リンクが含まれている場合があります。テストでは、runfiles ツリー内に .. コンポーネントを含むパスを使用しないでください。

runfiles ツリー内のディレクトリ、ファイル、シンボリック リンク(シンボリック リンクを走査するパスを含む)に書き込み可能にすることはできません。(つまり、最初の作業ディレクトリは書き込み可能であってはなりません)。テストでは、ランファイルのどの部分も書き込み可能である、または現在のユーザーが所有するものであることを前提としてはなりません(たとえば、chmodchgrp が失敗する可能性があります)。

テストの実行中に 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 規則。コードの変更をバージョン管理システムにコミットする前に実行する必要があることを意味します。

実行ファイル

以下では、//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 ごとに、Z の runfile への 2 番目のリンクが C の runfiles ディレクトリにあります。シンボリック リンクの名前は $(WORKSPACE)/package_name/rule_name.runfiles です。シンボリック リンクのターゲットは runfiles ディレクトリです。たとえば、すべてのサブプログラムが共通の runfiles ディレクトリを共有します。