We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Modules, Visibility, Testing and Docs step 22 of 22
Test selection: filters, #[ignore], and the libtest CLI
cargo test builds your test targets and runs each one with Rust's built-in
test runner, called libtest. The runner can be told to run only some
tests: name filters, --skip, --exact and the #[ignore] options. This
exercise models how those options combine.
pub fn select(
tests: Vec<(String, bool)>,
filters: Vec<String>,
skip: Vec<String>,
exact: bool,
ignored_mode: String,
) -> Vec<String>
Each test is (full_path, is_ignored). The full path is the test's name as
libtest prints it, such as tests::math::adds for a function adds inside
module math inside module tests. is_ignored is true when the test is
marked #[ignore]. Return the names of the tests that would actually run,
in input order.
The rules to implement
- Include: if
filtersis empty, every test is included. Otherwise a test is included when it matches at least one filter. - Exclude: remove any included test that matches at least one
skipentry, using the same matching rule. - Ignore mode: of the tests that remain,
"default"keeps only tests that are not ignored,"ignored"keeps only ignored tests,"include_ignored"keeps both.
A name matches a filter when the filter is a substring of the full path
(str::contains). When exact is true it matches only when the two strings
are equal. Matching is case-sensitive and compares the strings exactly as
given; no trimming or normalization is done.
Contract details:
- Any
ignored_modeother than"ignored"or"include_ignored"is treated as"default". (This fallback is part of the exercise, not a libtest rule; libtest takes these modes as separate flags.) - An empty filter or skip string
""is a substring of every name. Withoutexact,filters: [""]includes everything andskip: [""]excludes everything. Withexact,""matches only a test whose name is empty. filtersthat match nothing produce an empty result. That is different from an emptyfilterslist, which includes everything.- Names may repeat. Keep each entry that passes, in its original position.
- Empty
testsreturns an empty list.
Worked example
Tests, in order: tests::math::adds, tests::math::adds_negatives,
tests::io::reads, tests::slow::big_matrix (ignored) and bench::adds.
- Filter
adds, default mode: the three names containingadds, in input order.tests::slow::big_matrixis not a match, and would be dropped by default mode anyway. - With
exactand the filtertests::math::adds: onlytests::math::adds. - No filters, skip
math:tests::io::readsandbench::adds. The twomathtests are skipped and the ignored test is dropped by default mode. - Filter
big_matrix, default mode: nothing. Naming an ignored test in a filter does not make it run.
The same rules on the command line
A filter is a substring, so cargo test adds runs every test whose full path
contains adds, not just a test named adds. To run one test, pass its full
path with --exact: cargo test -- --exact tests::math::adds.
That command has a -- in it. Cargo reads the arguments before -- itself
and passes the arguments after -- to the test binary. A single test-name
filter is the exception: Cargo accepts one before -- and forwards it. The
runner's own options must come after --:
cargo test adds # one filter, forwarded by Cargo
cargo test -- adds reads # several filters
cargo test -- --skip bench # exclude by substring
cargo test -- --exact tests::math::adds # whole-name match
cargo test -- --ignored # only #[ignore] tests
cargo test -- --include-ignored # ignored and not ignored
cargo test -- --list # print test names, run nothing
cargo test -- --nocapture # print test output instead of capturing it
Putting one of these runner options before --, as in
cargo test --nocapture, makes Cargo report an unexpected argument, because
Cargo itself has no option with that name.
Two options with similar purposes are easy to confuse. cargo test -j N (or
--jobs N) limits how many build jobs Cargo runs while compiling.
cargo test -- --test-threads=N limits how many tests a test binary runs
at the same time. If a suite passes when tests are run one at a time but
fails when they run together, --test-threads=1 is a quick way to check
whether tests interfere with each other.
#[ignore] compared with #[cfg]
A test marked #[ignore] (optionally with a reason, #[ignore = "slow"]) is
still compiled. In a normal run it is listed and reported as ignored
instead of being executed. Because it is compiled with the rest of the test
build, a change that breaks its code is a compile error you see immediately.
Its runtime behavior is not checked until someone runs it with --ignored or
--include-ignored.
Code under #[cfg(feature = "slow")] is different: when the feature is off,
the code is removed before type checking, so it can stop compiling without
anyone noticing until the feature is turned on.
Shared state between tests
By default, libtest runs the tests in one test binary on several threads of
a single process. Process-wide state is shared among them: environment
variables, the current working directory, the panic hook and any static
values. Two tests that change the same environment variable can see each
other's changes depending on timing, so the suite can pass in one run and
fail in another. In edition 2024, std::env::set_var is an unsafe
function partly for this reason: changing the environment while other
threads read it is not safe on every platform.
The same idea makes tests of concurrent code more reliable. Check a result that does not depend on scheduling, such as a final count, a sorted merge or a set of events, rather than an elapsed time or a particular interleaving.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.