nextest_runner/config/elements/
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::{core::NextestConfig, utils::test_helpers::*};
28    use camino_tempfile::tempdir;
29    use indoc::indoc;
30    use nextest_filtering::ParseContext;
31    use test_case::test_case;
32
33    #[test_case(
34        "",
35        Ok(GlobalTimeout { period: Duration::from_secs(946728000) }),
36        None
37
38        ; "empty config is expected to use the hardcoded values"
39    )]
40    #[test_case(
41        indoc! {r#"
42            [profile.default]
43            global-timeout = "30s"
44        "#},
45        Ok(GlobalTimeout { period: Duration::from_secs(30) }),
46        None
47
48        ; "overrides the default profile"
49    )]
50    #[test_case(
51        indoc! {r#"
52            [profile.default]
53            global-timeout = "30s"
54
55            [profile.ci]
56            global-timeout = "60s"
57        "#},
58        Ok(GlobalTimeout { period: Duration::from_secs(30) }),
59        Some(GlobalTimeout { period: Duration::from_secs(60) })
60
61        ; "adds a custom profile 'ci'"
62    )]
63    fn globaltimeout_adheres_to_hierarchy(
64        config_contents: &str,
65        expected_default: Result<GlobalTimeout, &str>,
66        maybe_expected_ci: Option<GlobalTimeout>,
67    ) {
68        let workspace_dir = tempdir().unwrap();
69
70        let graph = temp_workspace(&workspace_dir, config_contents);
71
72        let pcx = ParseContext::new(&graph);
73
74        let nextest_config_result = NextestConfig::from_sources(
75            graph.workspace().root(),
76            &pcx,
77            None,
78            &[][..],
79            &Default::default(),
80        );
81
82        match expected_default {
83            Ok(expected_default) => {
84                let nextest_config = nextest_config_result.expect("config file should parse");
85
86                assert_eq!(
87                    nextest_config
88                        .profile("default")
89                        .expect("default profile should exist")
90                        .apply_build_platforms(&build_platforms())
91                        .global_timeout(),
92                    expected_default,
93                );
94
95                if let Some(expected_ci) = maybe_expected_ci {
96                    assert_eq!(
97                        nextest_config
98                            .profile("ci")
99                            .expect("ci profile should exist")
100                            .apply_build_platforms(&build_platforms())
101                            .global_timeout(),
102                        expected_ci,
103                    );
104                }
105            }
106
107            Err(expected_err_str) => {
108                let err_str = format!("{:?}", nextest_config_result.unwrap_err());
109
110                assert!(
111                    err_str.contains(expected_err_str),
112                    "expected error string not found: {err_str}",
113                )
114            }
115        }
116    }
117}