Per-test configuration
Some aspects of the test runner can be enforced on a per-test or per-suite basis using special attributes, instead of relying on command line options.
Enforce sequential execution
Parallelism of the test runner is normally controlled by the --test-threads command line argument. It is possible to enforce sequential execution for all tests within a test suite by putting the #[sequential] attribute on the module representing the suite:
#![allow(unused)]
fn main() {
use test_r::{sequential, test};
#[sequential]
mod suite {
#[test]
fn test1() {
assert!(true);
}
#[test]
fn test2() {
assert!(true);
}
}
}
The rest of the tests in the crate will still be parallelized based on the --test-threads argument.
When #[sequential] is applied to a module that contains nested sub-modules, the sequential behavior propagates to the entire subtree. All tests in the module and its descendants are guaranteed to run one at a time, gated by a single lock.
The #[sequential] attribute can only be used on inline modules due to a limitation in the current stable Rust compiler.
For non-inline modules, you can use the sequential_suite! macro instead in the following way:
#![allow(unused)]
fn main() {
use test_r::sequential_suite;
mod suite;
sequential_suite!(suite);
}
Always or never capture output
Two attributes can enforce capturing or not capturing the standard output and error of a test. Without these attributes, the runner will either capture (by default), or not (if the --nocapture command line argument is passed).
When the #[always_capture] attribute is used on a #[test], the output will be captured even if the --nocapture argument is passed. Conversely, the #[never_capture] attribute will prevent capturing the output even if the --nocapture argument is not passed.
Timeout
The #[timeout(duration)] attribute can be used to enforce a timeout for a test. The timeout is specified in milliseconds as a number:
#![allow(unused)]
fn main() {
use test_r::{test, timeout};
#[timeout(1000)]
#[test]
async fn test1() {
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
assert!(true);
}
}
Alternatively a human-readable duration string can be used, parsed by the humantime crate:
#![allow(unused)]
fn main() {
use test_r::{test, timeout};
#[timeout("1s")]
#[test]
async fn test1() {
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
assert!(true);
}
}
This feature only works when using the async test runner (enabled by the tokio feature).
Suite-level timeout
It is possible to apply a timeout to all tests in a test suite by putting the #[timeout(duration)] attribute on the module:
#![allow(unused)]
fn main() {
use test_r::{test, timeout};
#[timeout("3s")]
mod suite {
use super::*;
#[test]
async fn test1() {
// This test will be timed out after 3 seconds
}
#[test]
#[timeout(60000)]
async fn test2() {
// This test overrides the suite timeout with its own
}
}
}
If an individual test within the suite has its own #[timeout] attribute, the per-test timeout takes precedence over the suite-level one.
The #[timeout] attribute can only be used on inline modules due to a limitation in the current stable Rust compiler.
For non-inline modules, you can use the timeout_suite! macro instead:
#![allow(unused)]
fn main() {
use test_r::timeout_suite;
mod suite;
timeout_suite!(suite, "3s");
}
The second parameter of timeout_suite! accepts the same values as the #[timeout] attribute: an integer (milliseconds) or a human-readable duration string.
Reporting / ensuring time per test
There are command line arguments to enable reporting test run times and ensuring that each test runs within a certain time limit. The command line arguments enable these features for all tests. It is possible to individually configure this behavior per test using the following attributes:
#[always_report_time]will report the time taken by the test even if the--report-timeargument is not passed.#[never_report_time]will prevent reporting the time taken by the test even if the--report-timeargument is passed.#[always_ensure_time]will ensure that the test runs within the specified duration even if the--ensure-timeargument is not passed.#[never_ensure_time]will ignore the--ensure-timeargument for this test
Note that for ensuring time, it is not possible to overwrite the global time limit set using environment variables, which is the way the built-in Rust test runner works. For better control, use the #[timeout(duration)] attribute instead.