A test runner that builds tests with rustc or cargo (or any other compiler with some configuration effort) and compares the output of the compiler with a file that you check into git. This allows you to test how your libraries show up to your users when the library is used wrongly and emits errors.
See examples directory for how to use this in your own crate.
To be able to use it with cargo test, you need to put
[[test]]
name = "your_test_file"
harness = falseinto your Cargo.toml, otherwise cargo test will only look for #[test]s and
not run your fn main() that actually executes ui_test
- Tests are run in order of their filenames (files first, then recursing into folders). So if you have any slow tests, prepend them with a small integral number to make them get run first, taking advantage of parallelism as much as possible (instead of waiting for the slow tests at the end).
cargo test --test your_test_name -- --helplists the commands you can specify for filtering, blessing and making your tests less verbose.- Since
cargo teston its own runs all tests, usingcargo test -- --checkwill not work on its own, butcargo test -- --quietandcargo test -- some_test_namewill work just fine, as the CLI matches.
- Since
- if there is a
.stdinfile with the same filename as your test, it will be piped as standard input to your program.
If your test tests for failure, you need to add a //~ annotation where the error is happening
to ensure that the test will always keep failing at the annotated line. This will match any substring
of the error. These comments can take two forms:
//~ LEVEL: XXXmatches by error level and message textLEVELcan be one of the following (descending order):ERROR,HELP,WARN,NOTEorICE- If a level is specified explicitly, all diagnostics of that level or higher need an annotation. To avoid this see
//@require-annotations-for-level - This checks the output before normalization, so you can check things that get normalized away, but need to be careful not to accidentally have a pattern that differs between platforms.
- if
XXXis of the form/XXX/it is treated as a regex instead of a substring and will succeed if the regex matches.
//~ CODEmatches by diagnostic code.CODEcan take multiple forms such as:E####,lint_name,tool::lint_name.- This will only match a diagnostic at the
ERRORlevel.
In order to change how a single test is tested, you can add various //@ comments to the test.
Any other comments will be ignored, and all //@ comments must be formatted precisely as
their command specifies, or the test will fail without even being run.
-
//@ignore-Cavoids running the test when conditionCis met.Ccan betarget: XXX YYY, which checks whether the target triple containsXXXorYYY.Ccan behost: XXX YYY, which checks whether the host triple containsXXXorYYY.Ccan also bebitwidth:followed by one or more space separated integer size like64,32or16.Ccan also beon-host, which will only run the test during cross compilation testing.
-
//@only-Conly runs the test when conditionCis met. The conditions are the same as withignore. -
//@needs-asm-supportonly runs the test when the target supportsasm!. -
//@stderr-per-bitwidthproduces one stderr file per bitwidth, as they may differ significantly sometimes -
//@error-in-other-file: XXXcan be used to check for errors that can't have//~patterns due to being reported in other files. -
//@revisions: XXX YYYruns the test once for each space separated name in the list- emits one stderr file per revision
//~comments can be restricted to specific revisions by adding the revision name after the~in square brackets://~[XXX]//@comments can be restricted to specific revisions by adding the revision name after the@in square brackets://@[XXX]- Note that you cannot add revisions to the
revisionscommand.
- Note that you cannot add revisions to the
For example:
//@revisions: edition2021 edition2024 //@[edition2021] edition:2021 //@[edition2024] edition:2024 //@[edition2024] check-pass // Then in code: let x = 12; //~[edition2021] dead_code let x = 12; //~[edition2021]^ dead_code
-
//@compile-flags: XXXappendsXXXto the command line arguments passed to the rustc driver- you can specify this multiple times, and all the flags will accumulate
-
//@rustc-env: XXX=YYYsets the env varXXXtoYYYfor the rustc driver execution.- for Miri these env vars are used during compilation via rustc and during the emulation of the program
- you can specify this multiple times, accumulating all the env vars
-
//@normalize-stdout-test: "REGEX" -> "REPLACEMENT"///@normalize-stderr-test: "REGEX" -> "REPLACEMENT"replaces all matches ofREGEXin the stdout/stderr withREPLACEMENT. The replacement may specify$1and similar backreferences to paste captures. Note that this doesn't apply to error strings specified by//~comments.- you can specify multiple such commands, there is no need to create a single regex that handles multiple replacements that you want to perform.
-
//@require-annotations-for-level: LEVELcan be used to change the level of diagnostics that require a corresponding annotation.- this is only useful if there are any annotations like
HELP,WARNorNOTE, as these would automatically require annotations for all other diagnostics of the same or higher level.
- this is only useful if there are any annotations like
-
//@check-passrequires that a test has no error annotations, emits no errors, and exits successfully with exit/status code 0. -
//@edition: EDITIONoverwrites the default edition (2021) to the given edition. -
//@no-rustfixdo not run rustfix on tests that have machine applicable suggestions. -
//@aux-build: filenamelooks for a file in theauxiliarydirectory (within the directory of the test), compiles it as a library and links the current crate against it. This allows you import the crate withextern crateor just viausestatements. This will automatically detect aux files that are proc macros and build them as proc macros. -
//@runcompiles the test and runs the resulting binary. The resulting binary must exit successfully. Stdout and stderr are taken from the resulting binary. Any warnings during compilation are ignored.- You can also specify a different exit code/status that is expected via e.g.
//@run: 1or//@run: 101(the latter is the standard Rust exit code for panics). - run tests collect the run output into
.run.stderrand.run.stdoutrespectively. - if a
.run.stdinfile exists, it will be piped as standard input to your test's execution.
- You can also specify a different exit code/status that is expected via e.g.
- target selection is done via
ignore-target: xxxandonly-target: xxxinstead of compiletest'signore-xxx/only-xxx. Thexxxmust also be a substring of the target triple; special collections such asmacos/unixin compiletest is not supported. - only supports
uitests - tests are run in named order, so you can prefix slow tests with
0in order to make them get run first aux-builds require specifying nested aux builds explicitly and will not allow you to reference siblingaux-builds' artifacts.