1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// Copyright (C) 2023 Andreas Hartmann <hartan@7x.de>
// GNU General Public License v3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
// SPDX-License-Identifier: GPL-3.0-or-later

//! Mockups for unit testing.
//!
//! At the moment only contains a [`Mock` env](Mock) implementation, used to simulate command
//! executions in unit tests with [`output_of()`](crate::environment::Environment::output_of).
use std::cell::RefCell;
use std::collections::VecDeque;
use std::sync::Arc;
use std::sync::Mutex;

use crate::environment::prelude::*;
use crate::environment::ExecutionError;

/// Mock environment.
///
/// Only available when running tests, implements the [`IsEnvironment`][crate::env::IsEnvironment]
/// trait to allow testing [`Provider`](crate::provider::Provider) implementations. Holds an
/// internal queue of "replies" to return, using the [`Mock::push_raw()`] and [`Mock::pop_raw()`]
/// functions. Replies are returned from [`pop_raw()`](Mock::pop_raw) in the exact order they were
/// pushed in.
#[derive(Debug, Serialize, Deserialize)]
pub struct Mock {
    #[serde(skip)]
    queue: Mutex<RefCell<VecDeque<Result<String, ExecutionError>>>>,
}

// I don't care about these, but they're needed due to the trait requirements on `IsEnvironment`.
impl PartialEq for Mock {
    fn eq(&self, _other: &Self) -> bool {
        true
    }
}
impl Eq for Mock {}
impl PartialOrd for Mock {
    fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
        Some(std::cmp::Ordering::Equal)
    }
}
impl Ord for Mock {
    fn cmp(&self, _other: &Self) -> std::cmp::Ordering {
        std::cmp::Ordering::Equal
    }
}

impl Mock {
    pub fn new() -> Self {
        Mock {
            queue: Mutex::new(RefCell::new(VecDeque::new())),
        }
    }

    /// Push a raw result into the result queue.
    pub fn push_raw(&self, entry: Result<String, ExecutionError>) {
        self.queue.lock().unwrap().borrow_mut().push_back(entry);
    }

    /// Pop a raw result from the result queue.
    pub fn pop_raw(&self) -> Result<String, ExecutionError> {
        self.queue
            .lock()
            .unwrap()
            .borrow_mut()
            .pop_front()
            .expect("requested more entries from mock env than were previously created")
    }

    /// Convert into an `Arc<Environment>`.
    pub fn to_env(self) -> Arc<Environment> {
        Arc::new(Environment::Mock(self))
    }
}

impl fmt::Display for Mock {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "MOCK (test-only)")
    }
}

impl environment::IsEnvironment for Mock {
    type Err = std::convert::Infallible;

    fn exists(&self) -> bool {
        true
    }

    fn execute(&self, _command: CommandLine) -> Result<Command, Self::Err> {
        // We just assume 'rustc' to be available on any system capable of running `cargo
        // test`.
        let mut cmd = Command::new("rustc");
        cmd.arg("--version");
        Ok(cmd)
    }
}