mark_flaky_tests/lib.rs
1//! There're some tests that sometimes pass and sometimes fail. We call them "flaky".
2//!
3//! This crate provides a macro attribute `#[flaky]` that allows you to mark all the flaky tests
4//! in your codebase. You then have two options:
5//!
6//! 1. In default mode, `#[flaky]` will retry a test for a few times and pass it if at least one
7//! run has passed.
8//! 2. In strict mode, `#[flaky]` will still run test for a few times, but will only pass it
9//! if every run has passed.
10//!
11//! To enable strict mode, set the environment variable `MARK_FLAKY_TESTS_STRICT=true`.
12//!
13//! To adjust the amount of times a test is retried, set the environment variable
14//! `MARK_FLAKY_TESTS_RETRIES` to the desired amount. Default is 3.
15//!
16//! To use `#[flaky]` with `#[tokio::test]`, enable the `tokio` feature.
17//!
18//! Tests that return [`ExitCode`] are currently not supported due to std API limitations.
19//!
20//! [`ExitCode`]: ::std::process::ExitCode
21
22// lint me harder
23#![forbid(unsafe_code, non_ascii_idents)]
24#![deny(
25 future_incompatible,
26 keyword_idents,
27 noop_method_call,
28 unused_qualifications,
29 clippy::wildcard_dependencies,
30 clippy::empty_line_after_outer_attr
31)]
32#![warn(clippy::pedantic, missing_docs)]
33
34/// Mark test as flaky.
35///
36/// See [crate docs][crate] for details.
37pub use mark_flaky_tests_macro::flaky;
38
39#[doc(hidden)]
40pub mod _priv {
41 #[cfg(feature = "tokio")]
42 pub use futures;
43
44 /// Defines whether a result is considered a test failure.
45 pub trait IsFailure {
46 fn is_failure(&self) -> bool;
47 }
48
49 // Tests returning unit types succeed unless panic.
50 impl IsFailure for () {
51 fn is_failure(&self) -> bool {
52 false
53 }
54 }
55
56 // Tests returning `Result<T, E>` succeed if result is ok and the inner value succeeds.
57 impl<T: IsFailure, E> IsFailure for Result<T, E> {
58 fn is_failure(&self) -> bool {
59 self.as_ref().map(T::is_failure).unwrap_or(true)
60 }
61 }
62
63 // Not sure why would you want to make tests that never return, but whatever floats your boat.
64 impl IsFailure for std::convert::Infallible {
65 fn is_failure(&self) -> bool {
66 match *self {}
67 }
68 }
69
70 // We even can implement this for the never type.
71 impl IsFailure for Never {
72 fn is_failure(&self) -> bool {
73 *self
74 }
75 }
76
77 // Thanks to the `never-say-never` crate for publishing this trick!
78 pub trait GetNever {
79 type Never;
80 }
81
82 impl<R, F: FnOnce() -> R> GetNever for F {
83 type Never = R;
84 }
85
86 type Never = <fn() -> ! as GetNever>::Never;
87}