Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Compiletest

はじめに

compiletest は Rust テストスイートの主要なテストハーネスです。 これにより、テスト作成者は大量のテスト(Rust コンパイラには数千ものテストがあります)を整理し、効率的にテストを実行(並列実行がサポートされています)し、個別のテストおよびテストのグループの挙動と期待される結果を設定できます。

macOS ユーザーへの注意

macOS ユーザーの場合、SIP(System Integrity Protection)が Apple にネットワークリクエストを送信して、コンパイル済みバイナリを継続的にチェックする場合があるため、テストの実行時に大幅なパフォーマンス低下が発生することがあります。

次の設定を調整することで解決できます: Privacy & Security -> Developer Tools -> Add Terminal (Or VsCode, etc.)

compiletest は、テストコードがコンパイル時または実行時に成功/失敗するかをチェックできます。

テストは通常、テストコードの前および/または内部のコメントにアノテーションを付けた Rust ソースファイルとして構成されます。 これらのコメントは、テストを実行するかどうか、どのように実行するか、どのような挙動を期待するかなどを compiletest に指示する役割を果たします。 これらのアノテーションの詳細については、ディレクティブおよび以下のテストスイートのドキュメントを参照してください。

新しいテストを作成するためのチュートリアルや、良いテストを書くための助言については、新しいテストの追加およびベストプラクティスの章を参照してください。また、テストスイートの実行方法については、テストの実行の章を参照してください。

compiletest には --test-args を使用するか、-- の後に配置することで引数を渡せます。例:

  • x test --test-args --force-rerun
  • x test -- --force-rerun

さらに、bootstrap は一般的な引数をいくつか直接受け付けます。例:

x test --no-capture --force-rerun --run --pass

Compiletest 自体は、関係するアーティファクト(主にコンパイラ)が変更されていない場合、テストの実行を避けようとします。 入力のいずれも変更されていない場合でもテストを再実行するには、x test --test-args --force-rerun を使用できます。

テストスイート

すべてのテストは tests ディレクトリ内にあります。 テストは「スイート」に整理され、各スイートは個別のサブディレクトリにあります。 各テストスイートは、コンパイラの挙動や正しさのチェック方法が異なり、少しずつ異なる動作をします。 たとえば、tests/incremental ディレクトリにはインクリメンタルコンパイルのテストが含まれています。 各種スイートは、src/tools/compiletest/src/common.rspub enum Mode 宣言で定義されています。

詳細情報へのリンク付きで、次のテストスイートを利用できます:

コンパイラ固有のテストスイート

テストスイート目的
uiコンパイルおよび/または生成された実行ファイルの実行から得られる stdout/stderr スナップショットをチェックする
ui-fulldepsrustc のリンク可能なビルドを必要とする ui テスト(extern crate rustc_span; を使用する場合やプラグインとして使用される場合など)
prettypretty printing をチェックする
incrementalインクリメンタルコンパイルの挙動をチェックする
debuginfoデバッガを実行して debuginfo の生成をチェックする
codegen-*コード生成をチェックする
codegen-unitscodegen unit の分割をチェックする
assemblyアセンブリ出力をチェックする
mir-optMIR の生成と最適化をチェックする
coverageカバレッジ計測をチェックする
coverage-run-rustdoc計測された doctest も実行する coverage テスト
crashes意図しない修正を検出するため、特定の入力でコンパイラが ICE/panic/crash することをチェックする

汎用テストスイート

run-make は、Rust プログラムを使用する汎用テストです。

build-std テストスイート

build-std は -Zbuild-std が動作することをテストします。

Rustdoc テストスイート

テストスイート目的
rustdoc-htmlrustdoc の HTML 出力をチェックする
rustdoc-guiWeb ブラウザを使用して rustdoc の GUI をチェックする
rustdoc-jsrustdoc の検索エンジンとインデックスをチェックする
rustdoc-js-stdstd ライブラリドキュメント上の rustdoc の検索エンジンとインデックスをチェックする
rustdoc-jsonrustdoc の JSON 出力をチェックする
rustdoc-uirustdoc のターミナル出力をチェックする(関連項目

rustdoc 固有のテストの一部は ui/rustdoc/ にもあります。 これらのテストは、rustdoc の実行の一部として出力される特定の lint が、rustc の実行時にも実行されることを保証します。 rustdoc に関する Run-make テストは通常、run-make/rustdoc-*/ という名前です。

Pretty-printer テスト

tests/pretty 内のテストは、rustc の「pretty-printing」機能を実行します。 rustc-Z unpretty CLI オプションを使用すると、入力ソースを、マクロ展開後の Rust ソースなど、さまざまな形式に変換します。 プリティプリンターテストには、以下で説明するいくつかのディレクティブがあります。 これらのコマンドはテストの挙動を大きく変更できますが、コマンドがない場合の デフォルトの挙動は次のとおりです。

  1. ソースファイルに対して rustc -Zunpretty=normal を実行します。
  2. 前のステップの出力に対して rustc -Zunpretty=normal を実行します。
  3. 前の 2 つのステップの出力は同じである必要があります。
  4. 型チェックできることを確認するために、出力に対して rustc -Zno-codegen を実行します (cargo check と同様)。

上記のコマンドのいずれかが失敗した場合、そのテストは失敗します。

プリティプリントテストのディレクティブは次のとおりです。

  • pretty-mode は、プリティプリントテストを実行するモード(つまり、 -Zunpretty への引数)を指定します。 指定されていない場合のデフォルトは normal です。
  • pretty-compare-only は、プリティテストでプリティプリントされた 出力のみを比較するようにします(上記のステップ 3 の後で停止します)。 型チェックのために展開された出力をコンパイルしようとはしません。 これは、有効な Rust に展開されない pretty-mode や、 展開された出力をコンパイルできないその他の状況で必要です。
  • pp-exact は、プリティプリントテストが特定の出力になることを保証するために使用されます。 値なしで指定された場合、プリティプリント出力は 元のソースと一致する必要があることを意味します。 //@ pp-exact:foo.pp のように値付きで指定された場合、プリティプリントされた出力が 指定されたファイルの内容と一致することを保証します。 それ以外の場合、pp-exact が指定されていなければ、 プリティプリントされた出力はもう一度プリティプリントされ、2 回の プリティプリントの出力が比較されて、プリティプリントされた出力が 定常状態に収束することを確認します。

インクリメンタルテスト

tests/incremental のテストはインクリメンタルコンパイルをテストします。 これらは revisions ディレクティブを使用して、compiletest にコンパイラーを 一連のステップで実行するよう指示します。

Compiletest は -C incremental フラグ付きの空のディレクトリから開始し、 その後、前のステップのインクリメンタル結果を再利用しながら、各リビジョンに対してコンパイラーを実行します。

各リビジョン名は、次のいずれかで始まる必要があります。

  • cpass - テストは正常にコンパイルされなければなりません(チェックビルド、コード生成なし)
  • bfail — テストはコンパイルに失敗しなければなりません(完全ビルド、コード生成あり)
  • bpass — テストは正常にコンパイルされなければなりません(完全ビルド、コード生成あり)
  • rpass — テストは正常にコンパイルおよび実行されなければなりません

リビジョンを一意にするには、rpass1rpass2 のようなサフィックスを追加する必要があります。

ソースの変更をシミュレートするために、compiletest は現在のリビジョン名を指定した --cfg フラグも渡します。

たとえば、これは関数の変更をシミュレートして 2 回実行されます。

//@ revisions: rpass1 rpass2

#[cfg(rpass1)]
fn foo() {
    println!("one");
}

#[cfg(rpass2)]
fn foo() {
    println!("two");
}

fn main() { foo(); }

インクリメンタルテストは、特定の部分文字列がコンパイラー出力のどこにも 現れてはならないことを指定する forbid-output ディレクティブをサポートしています。 これは、特定のエラーが現れないことを保証するのに有用ですが、エラーメッセージは 時間とともに変わるため壊れやすく、テストがもはや適切な内容をチェックしていなくても合格し続ける可能性があります。

インクリメンタルテストでは #[rustc_clean(...)] 属性を使用できます。 この属性は、現在のコンパイルセッションのフィンガープリントを前回のものと比較します。 最初のリビジョンでは、有効な rustc_clean 属性を決して持つべきではありません。これは常に dirty になるためです。

デフォルトモードでは、フィンガープリントが同じでなければならないことをアサートします。 この属性は次の引数を取ります。

  • cfg="<cond>" — cfg 条件 <cond> をチェックし、その cfg 条件が true と評価された場合にのみチェックを実行します。 これは、特定のリビジョンでのみ rustc_clean 属性を実行するために使用できます。
  • except="<query1>,<query2>,..." — 列挙されたクエリについて、クエリ結果が同じではなく 異なっていなければならないことをアサートします。
  • loaded_from_disk="<query1>,<query2>,..." — 列挙されたクエリについて、クエリ結果が 実際にディスクから読み込まれたことをアサートします(単に green とマークされたのではありません)。 これは、テストが特定のクエリ結果について実際にデシリアライズ ロジックを実行していることを保証するのに有用です。 これは except と組み合わせることができます。

rustc_clean を使用するテストの簡単な例は hello_world test です。

Debuginfo テスト

tests/debuginfo のテストはデバッグ情報の生成をテストします。 これらはプログラムをビルドし、デバッガーを起動し、デバッガーにコマンドを発行します。 1 つのテストは cdb、gdb、lldb で動作できます。

ほとんどのテストには、適切なデバッグ情報を生成するために、 //@ compile-flags: -g ディレクティブまたはそれに類するものを含めるべきです。

行にブレークポイントを設定するには、その行に // #break コメントを追加します。

デバッグ情報テストは、一連のデバッガーコマンドと、 デバッガーから期待される出力を指定する “check” 行で構成されます。

コマンドは // $DEBUGGER-command:$COMMAND という形式のコメントであり、 $DEBUGGER は使用されているデバッガー、$COMMAND は実行するデバッガーコマンドです。

デバッガーの値として使用できるものは次のとおりです。

  • cdb
  • gdb
  • gdbg — Rust サポートなしの GDB(7.11 より古いバージョン)
  • gdbr — Rust サポートありの GDB
  • lldb
  • lldbg — Rust サポートなしの LLDB
  • lldbr — Rust サポートありの LLDB(これは現在は存在しません)

出力をチェックするコマンドは // $DEBUGGER-check:$OUTPUT という形式であり、 $OUTPUT は期待される出力です。

たとえば、次はテストをビルドし、デバッガーを開始し、 ブレークポイントを設定し、プログラムを起動し、値を調べ、デバッガーが出力する内容をチェックします。

//@ compile-flags: -g

//@ lldb-command: run
//@ lldb-command: print foo
//@ lldb-check: $0 = 123

fn main() {
    let foo = 123;
    b(); // #break
}

fn b() {}

現在使用されているデバッガーに基づいてテストを無効化するために、次の ディレクティブを使用できます。

  • min-cdb-version: 10.0.18317.1001 — cdb のバージョンが指定されたバージョン未満の場合、 テストを無視します
  • min-gdb-version: 8.2 — gdb のバージョンが指定されたバージョン未満の場合、テストを無視します
  • ignore-gdb-version: 9.2 — gdb のバージョンが指定されたバージョンと等しい場合、 テストを無視します
  • ignore-gdb-version: 7.11.90 - 8.0.9 — gdb のバージョンが 範囲内(両端を含む)の場合、テストを無視します
  • min-lldb-version: 310 — lldb のバージョンが指定されたバージョン未満の場合、テストを無視します
  • rust-lldb — lldb に Rust プラグインが含まれていない場合、テストを無視します。 注: “Rust” 版の LLDB はもう存在しないため、これは常に無視されます。 これはおそらく削除すべきです。

compiletest に --debugger オプションを渡すことで、テストを実行する単一のデバッガーを指定できます。 たとえば、./x test tests/debuginfo -- --debugger gdb は GDB コマンドのみをテストします。

lldb debuginfo テストをローカルで実行する際の注意

lldb debuginfo テストをローカルで実行したい場合、現時点では Windows で 次のことが必要です:

  • Python 3.10 がインストールされていること。
  • python310.dllPATH 環境変数で利用可能であること。これは python.org から入手する標準の Python インストーラーでは提供されません。 手動で PATH に追加する必要があります。

そうしないと、lldb debuginfo テストが不可解な形でクラッシュする可能性があります。

Windows 11 で cdb.exe を取得する際の注意

cdb.exe は、Visual Studio インストーラー(例: Visual Studio 2022 インストーラー)の 「Desktop Development with C++」ワークロードプロファイルの一部である、適切な 「Windows 11 SDK」とともに取得されます。

ただし、既定ではこれだけでは十分ではありません。cdb.exe が必要な場合は、 Installed Apps に移動し、最新の「Windows Software Development Kit」を見つける必要があります (そして、OS が Windows 11 と呼ばれているにもかかわらず、これが Windows 10.0.22161.3233 と表示されることもあります)。その後、 cdb.exe を取得するために、「Modify」->「Change」をクリックしてから、 「Debugging Tools for Windows」を選択する必要があります。

コード生成テスト

tests/codegen-llvm のテストは LLVM コード生成をテストします。 これらは、--emit=llvm-ir フラグを指定してテストをコンパイルし、LLVM IR を出力します。 その後、LLVM の FileCheck ツールを実行します。 テストには、生成されたコードをチェックするためにさまざまな // CHECK コメントが注釈として付けられます。 チュートリアルおよび詳細については、FileCheck ドキュメントを参照してください。

同様の一連のテストについては、アセンブリテスト も参照してください。

既定では、コード生成テストには //@ needs-target-std暗黙的に 指定されます (ターゲットが std をサポートする必要があることを意味します)。ただし、 テストソースで #![no_std]/#![no_core] 属性が指定されている場合は除きます。 この動作を上書きして、テストが #![no_std]/#![no_core] であっても、 ターゲットが std をサポートする場合にのみテストを実行するように、 明示的に //@ needs-target-std と書くことができます。

#![no_std] のクロスコンパイルテストを扱う必要がある場合は、 minicore テスト補助 の章を参照してください。

アセンブリテスト

tests/assembly-llvm のテストは LLVM アセンブリ出力をテストします。 これらは、--emit=asm フラグを指定してテストをコンパイルし、アセンブリ出力を含む .s ファイルを出力します。 その後、LLVM の FileCheck ツールを実行します。

各テストには、アセンブリ出力の種類を示すために、 emit-asm または ptx-linker のいずれかの値を持つ //@ assembly-output: ディレクティブを注釈として付ける必要があります。

次に、アセンブリ出力をチェックするために、さまざまな // CHECK コメントを注釈として付ける必要があります。 チュートリアルおよび詳細については、FileCheck ドキュメントを参照してください。

同様の一連のテストについては、コード生成テスト も参照してください。

#![no_std] のクロスコンパイルテストを扱う必要がある場合は、 minicore テスト補助 の章を参照してください。

命令サポートに基づく条件付きアセンブリテスト

特定のアセンブリ命令が利用可能であることに依存するテストでは、 //@ needs-asm-mnemonic: <MNEMONIC> ディレクティブを使用できます。 ターゲットバックエンドが指定された命令ニーモニックをサポートしていない場合、このテストはスキップされます。

たとえば、RET 命令を必要とするテストは次のとおりです:

//@ needs-asm-mnemonic: RET

Codegen-units テスト

tests/codegen-units のテストは、 単相化 コレクターと CGU 分割をテストします。

これらのテストは、単相化コレクションパスの結果を出力するフラグ、 すなわち -Zprint-mono-items を指定して rustc を実行し、 その後、ファイル内の特別な注釈を使用してそれと比較することで動作します。

次に、テストには //~ MONO_ITEM name という形式のコメントを注釈として付ける必要があります。 ここで name は、fn <u32 as Trait>::foo のように rustc によって出力される単相化済み文字列です。

CGU 分割をチェックするには、//~ MONO_ITEM name @@ cgu という形式のコメントを使用します。 ここで cgu は、CGU 名と角括弧内のリンケージ情報を空白区切りにしたリストです。 例: //~ MONO_ITEM static function::FOO @@ statics[Internal]

Mir-opt テスト

tests/mir-opt のテストは、生成された MIR の一部をチェックし、 それが正しく生成され、期待される最適化を行っていることを確認します。 詳細については、MIR 最適化 の章を確認してください。

Compiletest は、MIR 出力をダンプして最適化のベースラインを設定するために、 いくつかのフラグを指定してテストをビルドします:

  • -Copt-level=1
  • -Zdump-mir=all
  • -Zmir-opt-level=4
  • -Zvalidate-mir
  • -Zdump-mir-exclude-pass-number

テストには、期待される MIR 出力を含むファイルを指定する // EMIT_MIR コメントを注釈として付ける必要があります。 初期の期待ファイルを作成するには、x test --bless を使用できます。

EMIT_MIR コメントにはいくつかの形式があります:

  • // EMIT_MIR $MIR_PATH.mir — これは、指定されたファイル名が MIR ダンプからの正確な出力と一致することをチェックします。 たとえば、 my_test.main.SimplifyCfg-elaborate-drops.after.mir はそのファイルを テストディレクトリから読み込み、rustc からのダンプと比較します。

    “after” ファイル(最適化後のもの)をチェックすることは、 最適化後の最終状態に関心がある場合に有用です。 完全性のために “before” ファイルを使用したいまれなケースもあります。

  • // EMIT_MIR $MIR_PATH.diff — ここで $MIR_PATH は、 my_test_name.my_function.EarlyOtherwiseBranch のような MIR ダンプのファイル名です。 Compiletest は .before.mir ファイルと .after.mir ファイルの差分を取り、その diff 出力を EMIT_MIR コメントの期待される .diff ファイルと比較します。

    これは、最適化によって MIR がどのように変化するかを確認したい場合に有用です。

  • // EMIT_MIR $MIR_PATH.dot — 追加の MIR データをダンプする特定のフラグ (例: .dot ファイルを生成する -Z dump-mir-graphviz)を使用している場合、 これは出力が指定されたファイルと一致することをチェックします。

既定では、32 ビットターゲットと 64 ビットターゲットは同じダンプファイルを使用しますが、 定数内のポインターやその他のビット幅に依存するものが存在する場合、これは問題になる可能性があります。 その場合は、テストに // EMIT_MIR_FOR_EACH_BIT_WIDTH を追加することで、 32bit システムと 64bit システム用に別々のファイルが生成されるようにできます。

run-make テスト

tests/run-make および tests/run-make-cargo のテストは、Rust レシピ を使用する汎用 テストです。これは任意の Rust コード(rustc の呼び出しなど)を可能にする小さなプログラム (rmake.rs)であり、run_make_support ライブラリによってサポートされています。 Rust レシピを使用すると、究極の柔軟性が得られます。

run-make テストは、他のどのテストスイートもニーズにより適していない場合に使用する必要があります。 run-make-cargo テストスイートはさらに、ツリー内の cargo をビルドして、 ツリー内の rustc と組み合わせてツリー内の cargo をテストする必要がある ユースケースをサポートします。 run-make テストスイートはツリー内の cargo にアクセスできません(そのため、 反復をより高速に行えるテストスイートにできます)。

build-std テスト

tests/build-std 内のテストは、-Zbuild-std が動作することを確認します。 これは現在、単一のレシピを持つ run-make テストスイートにすぎません。 このレシピはテストケースを生成し、それらを並列に実行します。

Rust レシピの使用

各テストは、rmake.rs Rust プログラムを持つ個別のディレクトリに配置する必要があります。 このプログラムはレシピと呼ばれます。レシピは、run_make_support ライブラリがリンクされた状態で compiletest によってコンパイルされ、実行されます。

新しいユーティリティや機能が必要な場合は、 run_make_support ライブラリを拡張し、改善することを検討してください。

//@ only-<target>//@ ignore-<target> のような Compiletest ディレクティブは、 UI テストと同様に rmake.rs でサポートされています。 ただし、リビジョンやディレクティブによる補助ファイルのビルドは、現在サポートされていません。

rmake.rsrun-make-support は、nightly/unstable 機能を使用してはなりません。 これらは、beta または stable rustc である可能性のある stage 0 rustc でコンパイルできなければならないためです。

デフォルトでは、run-make テストは各サブプロセスコマンドとその stdout/stderr を出力します。 cg_clif などの panic=abort テストスイートで --no-capture を指定して実行すると、 これにより端末が大量の出力で埋め尽くされる可能性があります。 成功したテストでこの出力を抑制するには、 --verbose-run-make-subprocess-output を省略してください。失敗したテストは常に出力されます。

./x test tests/run-make --no-capture --verbose-run-make-subprocess-output=false

rmake.rs テストがコンパイルできるかを素早く確認する

rmake.rs を stage0 コンパイラでコンパイルするよう強制することで、 stage1 rustc をビルドせずに rmake.rs テストがコンパイルできるかを素早く確認できます。

$ COMPILETEST_FORCE_STAGE0=1 x test --stage 0 tests/run-make/<test-name>

もちろん、一部のテストはこの方法では正常に実行されません。

rmake.rs で rust-analyzer を使用する

他のテストプログラムと同様に、run-make テストで使用される rmake.rs スクリプトには、 デフォルトでは rust-analyzer 連携がありません。

特定のテストで作業しているときにこれを回避するには、 テストのディレクトリに一時的に Cargo.toml ファイルを作成します (例: tests/run-make/sysroot-crates-are-unstable/Cargo.toml)。 内容は次のとおりです。

この Cargo.toml やその Cargo.lock を実際の PR に追加しないよう注意してください!

# これが外側のワークスペースの一部ではないことを cargo に認識させる。
[workspace]

[package]
name = "rmake"
version = "0.1.0"
edition = "2021"

[dependencies]
run_make_support = { path = "../../../src/tools/run-make-support" }

[[bin]]
name = "rmake"
path = "rmake.rs"

次に、対応するエントリを "rust-analyzer.linkedProjects" に追加します (例: .vscode/settings.json)。

"rust-analyzer.linkedProjects": [
  "tests/run-make/sysroot-crates-are-unstable/Cargo.toml"
],

カバレッジテスト

tests/coverage 内のテストは、異なる方法でカバレッジ計測をテストする複数のテストモードで共有されます。 coverage テストスイートを実行すると、 各テストがすべての異なるカバレッジモードで自動的に実行されます。

各モードには、そのモードだけでカバレッジテストを実行するためのエイリアスもあります。

./x test coverage # tests/coverage のすべてをすべてのカバレッジモードで実行する
./x test tests/coverage # 上と同じ

./x test tests/coverage/if.rs # 指定されたテストをすべてのカバレッジモードで実行する

./x test coverage-map # tests/coverage のすべてを "coverage-map" モードのみで実行する
./x test coverage-run # tests/coverage のすべてを "coverage-run" モードのみで実行する

./x test coverage-map -- tests/coverage/if.rs # 指定されたテストを "coverage-map" モードのみで実行する

何らかの理由で、特定のテストをカバレッジテストモードのいずれかで実行すべきでない場合は、 //@ ignore-coverage-map または //@ ignore-coverage-run ディレクティブを使用してください。

coverage-map スイート

coverage-map モードでは、これらのテストは LLVM によって出力される ソースコード領域とカバレッジカウンターの間のマッピングを検証します。 テストを --emit=llvm-ir でコンパイルし、その後カスタムツール(src/tools/coverage-dump)を使用して、 IR に埋め込まれたカバレッジマッピングを抽出し、整形して出力します。 これらのテストはプロファイラーランタイムを必要としないため、PR CI ジョブで実行され、ローカルでの 実行や bless が容易です。

これらのカバレッジマップテストは、MIR lowering や MIR 最適化の変更に影響を受けやすく、 異なるものの同一のカバレッジレポートを生成するマッピングを生じることがあります。

経験則として、カバレッジ固有のコードを変更しない PR では、 coverage-run テストが引き続き成功している限り、実際の変更を気にすることなく、 必要に応じて coverage-map テストを遠慮なく re-bless してください。

coverage-run スイート

coverage-run モードでは、これらのテストはカバレッジレポートのエンドツーエンドテストを実行します。 カバレッジ計測を有効にしてテストプログラムをコンパイルし、そのプログラムを実行して 生のカバレッジデータを生成し、その後 LLVM ツールを使用してそのデータを 人間が読めるコードカバレッジレポートに処理します。

計測されたバイナリは LLVM プロファイラーランタイムにリンクされる必要があるため、 coverage-run テストは、bootstrap.toml でプロファイラーランタイムが有効になっていない限り 自動的にスキップされます。

build.profiler = true

これはまた、これらが通常 PR CI ジョブでは実行されないことを意味します。ただし、マージに使用される CI ジョブ一式の一部としては実行されます。

coverage-run-rustdoc スイート

tests/coverage-run-rustdoc 内のテストは、計測された doctest も実行し、 それらをカバレッジレポートに含めます。 これにより、メインの coverage スイートのみを実行する場合に rustdoc をビルドする必要がなくなります。

クラッシュテスト

tests/crashes は、コンパイラが ICE、panic、またはその他の方法でクラッシュすることが期待される テストのコレクションとして機能し、意図しない修正が追跡されるようにします。 以前は、これは https://github.com/rust-lang/glacier で行われていましたが、 rust-lang/rust testsuite の内部で行う方が便利です。

このスイート内のテストが rustc に ICE、panic、またはその他の方法でクラッシュを引き起こさせることは必須です。 rustc が 1 または 0 以外の終了ステータスで終了した場合、テストは「成功」します。

詳細な stdout/stderr を確認したい場合は、 COMPILETEST_VERBOSE_CRASHES=1 を設定する必要があります。例:

$ COMPILETEST_VERBOSE_CRASHES=1 ./x test tests/crashes/999999.rs --stage 1

誰でも、issue tracker から “untracked” crashes を追加できます。 1 つの PR に複数の issue のテストケースを含めることを強く推奨します。 そうする場合、各 issue 番号をファイル名に記載し(12345.rs で十分です)、 さらにファイル内にも //@ known-bug: #12345 ディレクティブで記載してください。 PR がマージされたら、関連する issue に S-bug-has-testラベル付けしてください。

クラッシュのいずれかを修正した場合は、それを tests/ui 内の適切な サブディレクトリに移動し、意味のある名前を付けてください。 このテストが存在する理由を説明する doc comment をファイルの先頭に追加してください。 以前はその例によって rustc がどのようにクラッシュしていたのか、 またそれを修正するために何が行われたのかを簡潔に説明できるとなお良いです。

プルリクエストの説明に

Fixes #NNNNN
Fixes #MMMMM

を追加すると、マージ時に対応するチケットが自動的にクローズされます。

修正がまず issue のサブセットだけでなく、根本原因を実際に修正していることを確認してください。 issue 番号は、ファイル名またはテストファイル内の //@ known-bug ディレクティブで確認できます。

補助クレートのビルド

一部のテストでは、追加の補助クレートをコンパイルする必要があることがよくあります。 これを支援する複数のディレクティブがあります。

  • aux-build
  • aux-crate
  • aux-bin
  • aux-codegen-backend
  • proc-macro

aux-build は、指定されたソースファイルから別のクレートをビルドします。 ソースファイルは、テストファイルの隣にある auxiliary というディレクトリ内に置く必要があります。

//@ aux-build: my-helper.rs

extern crate my_helper;
// ... my_helper を使用できます。

aux クレートは、可能であれば dylib としてビルドされます(それをサポートしていないプラットフォームの場合、 または aux ファイル内で no-prefer-dynamic ヘッダーが指定されている場合を除きます)。 extern クレートを見つけるために -L フラグが使用されます。

aux-crateaux-build と非常によく似ています。 ただし、extern クレートにリンクするために --extern フラグを使用し、 そのクレートを extern prelude として利用できるようにします。 これにより、依存関係の名前変更など、--extern フラグの追加構文を指定できます。 たとえば、//@ aux-crate:foo=bar.rsauxiliary/bar.rs をコンパイルし、テスト内で foo という名前で利用できるようにします。 これは Cargo が依存関係の名前変更を行う方法に似ています。 --extern 修飾子を 指定することもできます。 たとえば、//@ aux-crate:noprelude:foo=bar.rs です。

aux-binaux-build と似ていますが、ライブラリではなくバイナリをビルドします。 バイナリは、テストの作業ディレクトリを基準とした auxiliary/bin で利用できます。

aux-codegen-backendaux-build と似ていますが、その後、メインファイルをビルドする際に コンパイル済みの dylib を -Zcodegen-backend に渡します。 これは、コンパイラクレートの使用を必要とするため、tests/ui-fulldeps 内のテストでのみ機能します。

補助 proc-macro

proc-macro 依存関係が必要な場合は、proc-macro ディレクティブを使用できます。このディレクティブは aux-build とまったく同じように動作します。つまり、 proc-macro テスト補助ファイルを、メインテストファイルと同じ親フォルダー配下の auxiliary フォルダーに置く必要があります。 ただし、proc-macro テスト補助については、aux-build と比較して さらに 4 つの追加の既定動作があります。

  1. aux テストファイルは --crate-type=proc-macro でビルドされます。
  2. aux テストファイルは -C prefer-dynamic なしでビルドされます。つまり、aux クレートの dylib を生成しようとはしません。
  3. aux クレートは、--extern <aux_crate_name> により、extern prelude 経由で テストファイルから利用できるようになります。 UI テストはデフォルトで edition 2015 であるため、メインテストファイルが 2018 以降の edition を使用していない限り、 use インポートで aux クレート名を使用したい場合は、引き続き extern <aux_crate_name> を指定する必要があることに注意してください。
  4. proc_macro クレートは extern prelude モジュールとして利用できるようになります。 extern proc_macro; についても、同じ edition 2015 とそれ以降の edition の区別が適用されます。

たとえば、テスト tests/ui/cat/meow.rs と proc-macro 補助 tests/ui/cat/auxiliary/whiskers.rs があるとします。

tests/ui/cat/
    meow.rs                 # メインテストファイル
    auxiliary/whiskers.rs   # 補助
// tests/ui/cat/meow.rs

//@ proc-macro: whiskers.rs

extern crate whiskers; // ui テストはデフォルトで edition 2015 のため必要

fn main() {
  whiskers::identity!();
}
// tests/ui/cat/auxiliary/whiskers.rs

extern crate proc_macro;
use proc_macro::*;

#[proc_macro]
pub fn identity(ts: TokenStream) -> TokenStream {
    ts
}

注記: 現在、proc-macro ヘッダーは rustdoc テストの build-aux-doc ヘッダーと併用できません。その場合は、 aux-build ヘッダーを使用し、proc-macro 内で #![crate_type="proc_macro"]//@ force-host および //@ no-prefer-dynamic ヘッダーを使用する必要があります。

リビジョン

リビジョンを使用すると、1 つのテストファイルを複数のテストに使用できます。 これは、ファイルの先頭に特別なディレクティブを追加することで行います。

//@ revisions: foo bar baz

これにより、テストは 3 回コンパイル(およびテスト)されます。1 回は --cfg foo、1 回は --cfg bar、もう 1 回は --cfg baz です。 したがって、テスト内で #[cfg(foo)] などを使用して、これらの結果をそれぞれ調整できます。

ディレクティブと期待されるエラーメッセージを特定のリビジョンに合わせてカスタマイズすることもできます。 これを行うには、ディレクティブの場合は //@ の後に [revision-name] を追加し、 UI エラー注釈の場合は // の後に追加します。次のようになります。

// cfg `foo` の場合にのみ渡すフラグ:
//@[foo]compile-flags: -Z verbose-internals

#[cfg(foo)]
fn test_foo() {
    let x: usize = 32_u32; //[foo]~ ERROR mismatched types
}

//[foo,bar,baz]~^ のように、複数のリビジョンをカンマ区切りリストで指定できます。

LLVM FileCheck ツールを使用するテストスイートでは、現在のリビジョン名も FileCheck ディレクティブの追加プレフィックスとして登録されます。

//@ revisions: NORMAL COVERAGE
//@[COVERAGE] compile-flags: -Cinstrument-coverage
//@[COVERAGE] needs-profiler-runtime

// COVERAGE:   @__llvm_coverage_mapping
// NORMAL-NOT: @__llvm_coverage_mapping

// CHECK: main
fn main() {}

リビジョンに合わせてカスタマイズした場合に、すべてのディレクティブが意味を持つわけではないことに注意してください。 たとえば、ignore-test ディレクティブ(およびすべての「ignore」ディレクティブ)は現在、 特定のリビジョンではなく、テスト全体にのみ適用されます。 リビジョンに合わせてカスタマイズした場合に実際に機能することを意図しているディレクティブは、 エラーパターンとコンパイラフラグだけです。

これらのテストスイートはリビジョンをサポートしていないことに注意してください。

  • codegen-units
  • run-make
  • rustdoc-html
  • rustdoc-json

未使用のリビジョン名を無視する

通常、他のディレクティブやエラーアノテーションで言及されるリビジョン名は、revisions ディレクティブで宣言された実際のリビジョンに対応していなければなりません。 これは ./x test tidy チェックによって強制されます。

何らかの理由でリビジョン名をリビジョンリストから一時的に削除する必要がある場合は、代わりにそのリビジョン名を //@ unused-revision-names: ヘッダーに追加することで、上記のチェックを抑制できます。

未使用名として * を指定すると(つまり //@ unused-revision-names: *)、任意の未使用リビジョン名に言及できるようになります。

比較モード

Compiletest は、比較モード と呼ばれるさまざまなモードで実行できます。比較モードは、異なるコンパイラフラグを有効にした状態で、すべてのテストの動作を比較するために使用できます。 これにより、特定のフラグでどのような違いが現れる可能性があるかを明らかにし、発生し得る問題を確認できます。

別のモードでテストを実行するには、--compare-mode CLI フラグを渡す必要があります。

./x test tests/ui --compare-mode=next-solver

指定できる比較モードは次のとおりです。

  • polonius-Zpolonius=next を指定して Polonius で実行します。
  • next-solver-Znext-solver を指定して次世代 trait solver で実行します。
  • next-solver-coherence-Znext-solver=coherence を指定して次世代 trait solver で coherence を実行します。
  • split-dwarf-Csplit-debuginfo=unpacked を指定して、アンパックされた split-DWARF で実行します。
  • split-dwarf-single-Csplit-debuginfo=packed を指定して、パックされた split-DWARF で実行します。

UI テストが異なるモードに対して異なる出力をどのようにサポートしているかについて詳しくは、UI 比較モードを参照してください。

CI では、比較モードは 1 つの Linux ビルダーでのみ、かつ次の設定でのみ使用されます。

  • tests/debuginfo: split-dwarf モードを使用します。 これは、split-DWARF を有効にしたときに debuginfo テストが影響を受けないことを確認するのに役立ちます。

比較モードはリビジョンとは別のものであることに注意してください。 ./x test tests/ui を実行するとすべてのリビジョンがテストされますが、比較モードは --compare-mode フラグを介して個別に手動で実行する必要があります。

並列フロントエンド

Compiletest は、--parallel-frontend-threads フラグを指定して、コンパイラを並列モードで実行できます。 これは、コンパイラが並列モードでも非並列モードと同じ出力を生成することを確認し、並列モードで発生し得る問題を確認するために使用できます。

並列モードでテストを実行するには、--parallel-frontend-threads CLI フラグを渡す必要があります。

./x test tests/ui -- --parallel-frontend-threads=N --iteration-count=M

ここで、N は並列フロントエンドで使用するスレッド数、M は各テストを並列モードで実行する回数です(非決定性を検出できる可能性を高めるため)。

また、--parallel-frontend-threads を指定して実行する場合、並列フロントエンドからの出力は行の順序という点で非決定的になり得るため、すべてのテストに対して compare-output-by-lines ディレクティブが暗黙的に指定されます。

並列フロントエンドは現時点では UI テストでのみ利用可能で、現在は他のテストスイートではサポートされていません。