Skip to main content

proc_cli/core/
apply.rs

1//! Run one action over several processes and sort the results
2
3use super::Process;
4use crate::error::Result;
5
6/// Runs `act` on each process in order. Returns the processes it worked on and
7/// the ones it failed on (with the error text), each in the order given.
8///
9/// This is the loop `kill`, `stop`, `freeze`, `thaw` and `orphans --kill` share;
10/// taking the action as a parameter is what lets a test make it fail.
11pub fn apply_to_each(
12    processes: &[Process],
13    mut act: impl FnMut(&Process) -> Result<()>,
14) -> (Vec<Process>, Vec<(Process, String)>) {
15    let mut succeeded = Vec::new();
16    let mut failed = Vec::new();
17    for proc in processes {
18        match act(proc) {
19            Ok(()) => succeeded.push(proc.clone()),
20            Err(e) => failed.push((proc.clone(), e.to_string())),
21        }
22    }
23    (succeeded, failed)
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29    use crate::error::ProcError;
30
31    /// Three records of this very process, told apart by the PID field
32    fn three() -> Vec<Process> {
33        let me = Process::find_by_pid(std::process::id())
34            .unwrap()
35            .expect("this test process");
36        (1..=3).map(|pid| Process { pid, ..me.clone() }).collect()
37    }
38
39    #[test]
40    fn sorts_each_process_by_what_the_action_did_to_it_in_order() {
41        let processes = three();
42        let (worked, failed) = apply_to_each(&processes, |p| {
43            if p.pid == 2 {
44                Err(ProcError::SignalError("no permission".to_string()))
45            } else {
46                Ok(())
47            }
48        });
49        assert_eq!(worked.iter().map(|p| p.pid).collect::<Vec<_>>(), [1, 3]);
50        assert_eq!(failed.len(), 1);
51        assert_eq!(failed[0].0.pid, 2);
52        assert_eq!(failed[0].1, "Signal failed: no permission");
53    }
54
55    #[test]
56    fn runs_the_action_once_per_process_even_after_a_failure() {
57        let processes = three();
58        let mut seen = Vec::new();
59        let (worked, failed) = apply_to_each(&processes, |p| {
60            seen.push(p.pid);
61            Err(ProcError::ProcessGone(p.pid))
62        });
63        assert_eq!(seen, [1, 2, 3]);
64        assert!(worked.is_empty());
65        assert_eq!(failed.len(), 3);
66        assert_eq!(failed[2].1, "Process 3 is no longer running");
67    }
68}