nextest_runner/config/
global_timeout.rs

1// Copyright (c) The nextest Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use serde::{Deserialize, Deserializer};
5use std::time::Duration;
6
7/// Type for the global-timeout config key.
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub struct GlobalTimeout {
10    pub(crate) period: Duration,
11}
12
13impl<'de> Deserialize<'de> for GlobalTimeout {
14    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
15    where
16        D: Deserializer<'de>,
17    {
18        Ok(GlobalTimeout {
19            period: humantime_serde::deserialize(deserializer)?,
20        })
21    }
22}
23
24#[cfg(test)]
25mod tests {
26    use super::*;
27    use crate::config::{
28        NextestConfig,
29        test_helpers::{build_platforms, temp_workspace},
30    };
31    use camino_tempfile::tempdir;
32    use indoc::indoc;
33    use nextest_filtering::ParseContext;
34    use test_case::test_case;
35
36    #[test_case(
37        "",
38        Ok(GlobalTimeout { period: Duration::from_secs(946728000) }),
39        None
40
41        ; "empty config is expected to use the hardcoded values"
42    )]
43    #[test_case(
44        indoc! {r#"
45            [profile.default]
46            global-timeout = "30s"
47        "#},
48        Ok(GlobalTimeout { period: Duration::from_secs(30) }),
49        None
50
51        ; "overrides the default profile"
52    )]
53    #[test_case(
54        indoc! {r#"
55            [profile.default]
56            global-timeout = "30s"
57
58            [profile.ci]
59            global-timeout = "60s"
60        "#},
61        Ok(GlobalTimeout { period: Duration::from_secs(30) }),
62        Some(GlobalTimeout { period: Duration::from_secs(60) })
63
64        ; "adds a custom profile 'ci'"
65    )]
66    fn globaltimeout_adheres_to_hierarchy(
67        config_contents: &str,
68        expected_default: Result<GlobalTimeout, &str>,
69        maybe_expected_ci: Option<GlobalTimeout>,
70    ) {
71        let workspace_dir = tempdir().unwrap();
72
73        let graph = temp_workspace(&workspace_dir, config_contents);
74
75        let pcx = ParseContext::new(&graph);
76
77        let nextest_config_result = NextestConfig::from_sources(
78            graph.workspace().root(),
79            &pcx,
80            None,
81            &[][..],
82            &Default::default(),
83        );
84
85        match expected_default {
86            Ok(expected_default) => {
87                let nextest_config = nextest_config_result.expect("config file should parse");
88
89                assert_eq!(
90                    nextest_config
91                        .profile("default")
92                        .expect("default profile should exist")
93                        .apply_build_platforms(&build_platforms())
94                        .global_timeout(),
95                    expected_default,
96                );
97
98                if let Some(expected_ci) = maybe_expected_ci {
99                    assert_eq!(
100                        nextest_config
101                            .profile("ci")
102                            .expect("ci profile should exist")
103                            .apply_build_platforms(&build_platforms())
104                            .global_timeout(),
105                        expected_ci,
106                    );
107                }
108            }
109
110            Err(expected_err_str) => {
111                let err_str = format!("{:?}", nextest_config_result.unwrap_err());
112
113                assert!(
114                    err_str.contains(expected_err_str),
115                    "expected error string not found: {err_str}",
116                )
117            }
118        }
119    }
120}