cnf_lib/test/
mock.rs

1// Copyright (C) 2023 Andreas Hartmann <hartan@7x.de>
2// GNU General Public License v3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5//! Mockups for unit testing.
6//!
7//! At the moment only contains a [`Mock` env](Mock) implementation, used to simulate command
8//! executions in unit tests with [`output_of()`](crate::environment::Environment::output_of).
9use std::cell::RefCell;
10use std::collections::VecDeque;
11use std::sync::Arc;
12use std::sync::Mutex;
13
14use crate::environment::prelude::*;
15use crate::environment::ExecutionError;
16
17/// Mock environment.
18///
19/// Only available when running tests, implements the [`IsEnvironment`][crate::env::IsEnvironment]
20/// trait to allow testing [`Provider`](crate::provider::Provider) implementations. Holds an
21/// internal queue of "replies" to return, using the [`Mock::push_raw()`] and [`Mock::pop_raw()`]
22/// functions. Replies are returned from [`pop_raw()`](Mock::pop_raw) in the exact order they were
23/// pushed in.
24#[derive(Debug, Serialize, Deserialize)]
25pub struct Mock {
26    #[serde(skip)]
27    queue: Mutex<RefCell<VecDeque<Result<String, ExecutionError>>>>,
28}
29
30// I don't care about these, but they're needed due to the trait requirements on `IsEnvironment`.
31impl PartialEq for Mock {
32    fn eq(&self, _other: &Self) -> bool {
33        true
34    }
35}
36impl Eq for Mock {}
37
38// We don't really care how instances of `Mock` are sorted. This trait bound mainly originates from
39// external use of environment instaces for purposes of ordering etc, but this is irrelevant for
40// testing.
41#[allow(clippy::non_canonical_partial_ord_impl)]
42impl PartialOrd for Mock {
43    fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
44        Some(std::cmp::Ordering::Equal)
45    }
46}
47impl Ord for Mock {
48    fn cmp(&self, _other: &Self) -> std::cmp::Ordering {
49        std::cmp::Ordering::Equal
50    }
51}
52
53impl Default for Mock {
54    fn default() -> Self {
55        Self {
56            queue: Mutex::new(RefCell::new(VecDeque::new())),
57        }
58    }
59}
60
61impl Mock {
62    pub fn new() -> Self {
63        Self::default()
64    }
65
66    /// Push a raw result into the result queue.
67    pub fn push_raw(&self, entry: Result<String, ExecutionError>) {
68        self.queue.lock().unwrap().borrow_mut().push_back(entry);
69    }
70
71    /// Pop a raw result from the result queue.
72    pub fn pop_raw(&self) -> Result<String, ExecutionError> {
73        self.queue
74            .lock()
75            .unwrap()
76            .borrow_mut()
77            .pop_front()
78            .expect("requested more entries from mock env than were previously created")
79    }
80
81    /// Convert into an `Arc<Environment>`.
82    pub fn to_env(self) -> Arc<Environment> {
83        Arc::new(Environment::Mock(self))
84    }
85}
86
87impl fmt::Display for Mock {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        write!(f, "MOCK (test-only)")
90    }
91}
92
93#[async_trait]
94impl environment::IsEnvironment for Mock {
95    type Err = std::convert::Infallible;
96
97    async fn exists(&self) -> bool {
98        true
99    }
100
101    async fn execute(&self, _command: CommandLine) -> Result<Command, Self::Err> {
102        // We just assume 'rustc' to be available on any system capable of running `cargo
103        // test`.
104        let mut cmd = Command::new("rustc");
105        cmd.arg("--version");
106        Ok(cmd)
107    }
108}