1 //! Module `time` contains everything related to the time measurement of unit tests
3 //! Two main purposes of this module:
4 //! - Check whether test is timed out.
5 //! - Provide helpers for `report-time` and `measure-time` options.
10 use std::time::{Duration, Instant};
12 use super::types::{TestDesc, TestType};
14 pub const TEST_WARN_TIMEOUT_S: u64 = 60;
16 /// This small module contains constants used by `report-time` option.
17 /// Those constants values will be used if corresponding environment variables are not set.
19 /// To override values for unit-tests, use a constant `RUST_TEST_TIME_UNIT`,
20 /// To override values for integration tests, use a constant `RUST_TEST_TIME_INTEGRATION`,
21 /// To override values for doctests, use a constant `RUST_TEST_TIME_DOCTEST`.
23 /// Example of the expected format is `RUST_TEST_TIME_xxx=100,200`, where 100 means
24 /// warn time, and 200 means critical time.
25 pub mod time_constants {
26 use super::TEST_WARN_TIMEOUT_S;
27 use std::time::Duration;
29 /// Environment variable for overriding default threshold for unit-tests.
30 pub const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT";
32 // Unit tests are supposed to be really quick.
33 pub const UNIT_WARN: Duration = Duration::from_millis(50);
34 pub const UNIT_CRITICAL: Duration = Duration::from_millis(100);
36 /// Environment variable for overriding default threshold for unit-tests.
37 pub const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION";
39 // Integration tests may have a lot of work, so they can take longer to execute.
40 pub const INTEGRATION_WARN: Duration = Duration::from_millis(500);
41 pub const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000);
43 /// Environment variable for overriding default threshold for unit-tests.
44 pub const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST";
46 // Doctests are similar to integration tests, because they can include a lot of
47 // initialization code.
48 pub const DOCTEST_WARN: Duration = INTEGRATION_WARN;
49 pub const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL;
51 // Do not suppose anything about unknown tests, base limits on the
52 // `TEST_WARN_TIMEOUT_S` constant.
53 pub const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S);
54 pub const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2);
57 /// Returns an `Instance` object denoting when the test should be considered
59 pub fn get_default_test_timeout() -> Instant {
60 Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S)
63 /// The meassured execution time of a unit test.
64 #[derive(Debug, Clone, PartialEq)]
65 pub struct TestExecTime(pub Duration);
67 impl fmt::Display for TestExecTime {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 write!(f, "{:.3}s", self.0.as_secs_f64())
73 /// Structure denoting time limits for test execution.
74 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
75 pub struct TimeThreshold {
77 pub critical: Duration,
81 /// Creates a new `TimeThreshold` instance with provided durations.
82 pub fn new(warn: Duration, critical: Duration) -> Self {
83 Self { warn, critical }
86 /// Attempts to create a `TimeThreshold` instance with values obtained
87 /// from the environment variable, and returns `None` if the variable
89 /// Environment variable format is expected to match `\d+,\d+`.
93 /// Panics if variable with provided name is set but contains inappropriate
95 pub fn from_env_var(env_var_name: &str) -> Option<Self> {
96 let durations_str = env::var(env_var_name).ok()?;
98 // Split string into 2 substrings by comma and try to parse numbers.
99 let mut durations = durations_str.splitn(2, ',').map(|v| {
100 u64::from_str(v).unwrap_or_else(|_| {
102 "Duration value in variable {} is expected to be a number, but got {}",
108 // Callback to be called if the environment variable has unexpected structure.
109 let panic_on_incorrect_value = || {
111 "Duration variable {} expected to have 2 numbers separated by comma, but got {}",
112 env_var_name, durations_str
116 let (warn, critical) = (
117 durations.next().unwrap_or_else(panic_on_incorrect_value),
118 durations.next().unwrap_or_else(panic_on_incorrect_value),
122 panic!("Test execution warn time should be less or equal to the critical time");
125 Some(Self::new(Duration::from_millis(warn), Duration::from_millis(critical)))
129 /// Structure with parameters for calculating test execution time.
130 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
131 pub struct TestTimeOptions {
132 /// Denotes if the test critical execution time limit excess should be considered
134 pub error_on_excess: bool,
136 pub unit_threshold: TimeThreshold,
137 pub integration_threshold: TimeThreshold,
138 pub doctest_threshold: TimeThreshold,
141 impl TestTimeOptions {
142 pub fn new_from_env(error_on_excess: bool, colored: bool) -> Self {
143 let unit_threshold = TimeThreshold::from_env_var(time_constants::UNIT_ENV_NAME)
144 .unwrap_or_else(Self::default_unit);
146 let integration_threshold =
147 TimeThreshold::from_env_var(time_constants::INTEGRATION_ENV_NAME)
148 .unwrap_or_else(Self::default_integration);
150 let doctest_threshold = TimeThreshold::from_env_var(time_constants::DOCTEST_ENV_NAME)
151 .unwrap_or_else(Self::default_doctest);
153 Self { error_on_excess, colored, unit_threshold, integration_threshold, doctest_threshold }
156 pub fn is_warn(&self, test: &TestDesc, exec_time: &TestExecTime) -> bool {
157 exec_time.0 >= self.warn_time(test)
160 pub fn is_critical(&self, test: &TestDesc, exec_time: &TestExecTime) -> bool {
161 exec_time.0 >= self.critical_time(test)
164 fn warn_time(&self, test: &TestDesc) -> Duration {
165 match test.test_type {
166 TestType::UnitTest => self.unit_threshold.warn,
167 TestType::IntegrationTest => self.integration_threshold.warn,
168 TestType::DocTest => self.doctest_threshold.warn,
169 TestType::Unknown => time_constants::UNKNOWN_WARN,
173 fn critical_time(&self, test: &TestDesc) -> Duration {
174 match test.test_type {
175 TestType::UnitTest => self.unit_threshold.critical,
176 TestType::IntegrationTest => self.integration_threshold.critical,
177 TestType::DocTest => self.doctest_threshold.critical,
178 TestType::Unknown => time_constants::UNKNOWN_CRITICAL,
182 fn default_unit() -> TimeThreshold {
183 TimeThreshold::new(time_constants::UNIT_WARN, time_constants::UNIT_CRITICAL)
186 fn default_integration() -> TimeThreshold {
187 TimeThreshold::new(time_constants::INTEGRATION_WARN, time_constants::INTEGRATION_CRITICAL)
190 fn default_doctest() -> TimeThreshold {
191 TimeThreshold::new(time_constants::DOCTEST_WARN, time_constants::DOCTEST_CRITICAL)