Skip to main content

cnf_lib/test/
mock.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2// SPDX-FileCopyrightText: (C) 2023 Andreas Hartmann <hartan@7x.de>
3// This file is part of cnf-lib, available at <https://gitlab.com/hartang/rust/cnf>
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::ExecutionError;
15use crate::environment::prelude::*;
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    /// Create a new instance.
63    pub fn new() -> Self {
64        Self::default()
65    }
66
67    /// Push a raw result into the result queue.
68    pub fn push_raw(&self, entry: Result<String, ExecutionError>) {
69        self.queue.lock().unwrap().borrow_mut().push_back(entry);
70    }
71
72    /// Pop a raw result from the result queue.
73    pub fn pop_raw(&self) -> Result<String, ExecutionError> {
74        self.queue
75            .lock()
76            .unwrap()
77            .borrow_mut()
78            .pop_front()
79            .expect("requested more entries from mock env than were previously created")
80    }
81
82    /// Convert into an `Arc<Environment>`.
83    pub fn to_env(self) -> Arc<Environment> {
84        Arc::new(Environment::Mock(self))
85    }
86}
87
88impl fmt::Display for Mock {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(f, "MOCK (test-only)")
91    }
92}
93
94#[async_trait]
95impl environment::IsEnvironment for Mock {
96    type Err = std::convert::Infallible;
97
98    async fn exists(&self) -> bool {
99        true
100    }
101
102    async fn execute(&self, _command: CommandLine) -> Result<Command, Self::Err> {
103        // We just assume 'rustc' to be available on any system capable of running `cargo
104        // test`.
105        let mut cmd = Command::new("rustc");
106        cmd.arg("--version");
107        Ok(cmd)
108    }
109}