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}