perl_subprocess_runtime/
mock.rs1use crate::{SubprocessError, SubprocessOutput, SubprocessRuntime};
2use std::sync::{Arc, Mutex, MutexGuard};
3
4fn lock<'a, T>(mutex: &'a Mutex<T>) -> MutexGuard<'a, T> {
5 match mutex.lock() {
6 Ok(guard) => guard,
7 Err(poisoned) => poisoned.into_inner(),
8 }
9}
10
11#[derive(Debug, Clone)]
13pub struct CommandInvocation {
14 pub program: String,
16 pub args: Vec<String>,
18 pub stdin: Option<Vec<u8>>,
20}
21
22#[derive(Debug, Clone)]
24pub struct MockResponse {
25 pub stdout: Vec<u8>,
27 pub stderr: Vec<u8>,
29 pub status_code: i32,
31}
32
33impl MockResponse {
34 pub fn success(stdout: impl Into<Vec<u8>>) -> Self {
36 Self { stdout: stdout.into(), stderr: Vec::new(), status_code: 0 }
37 }
38
39 pub fn failure(stderr: impl Into<Vec<u8>>, status_code: i32) -> Self {
41 Self { stdout: Vec::new(), stderr: stderr.into(), status_code }
42 }
43}
44
45pub struct MockSubprocessRuntime {
47 invocations: Arc<Mutex<Vec<CommandInvocation>>>,
48 responses: Arc<Mutex<Vec<MockResponse>>>,
49 default_response: MockResponse,
50}
51
52impl MockSubprocessRuntime {
53 pub fn new() -> Self {
55 Self {
56 invocations: Arc::new(Mutex::new(Vec::new())),
57 responses: Arc::new(Mutex::new(Vec::new())),
58 default_response: MockResponse::success(Vec::new()),
59 }
60 }
61
62 pub fn add_response(&self, response: MockResponse) {
64 lock(&self.responses).push(response);
65 }
66
67 pub fn set_default_response(&mut self, response: MockResponse) {
69 self.default_response = response;
70 }
71
72 pub fn invocations(&self) -> Vec<CommandInvocation> {
74 lock(&self.invocations).clone()
75 }
76
77 pub fn clear_invocations(&self) {
79 lock(&self.invocations).clear();
80 }
81}
82
83impl Default for MockSubprocessRuntime {
84 fn default() -> Self {
85 Self::new()
86 }
87}
88
89impl SubprocessRuntime for MockSubprocessRuntime {
90 fn run_command(
91 &self,
92 program: &str,
93 args: &[&str],
94 stdin: Option<&[u8]>,
95 ) -> Result<SubprocessOutput, SubprocessError> {
96 lock(&self.invocations).push(CommandInvocation {
97 program: program.to_string(),
98 args: args.iter().map(|s| s.to_string()).collect(),
99 stdin: stdin.map(|s| s.to_vec()),
100 });
101
102 let response = {
103 let mut responses = lock(&self.responses);
104 if responses.is_empty() { self.default_response.clone() } else { responses.remove(0) }
105 };
106
107 Ok(SubprocessOutput {
108 stdout: response.stdout,
109 stderr: response.stderr,
110 status_code: response.status_code,
111 })
112 }
113}