Skip to main content

termesh_test_support/
fake_tasks.rs

1//! Deterministic language-neutral task catalog and decoder.
2
3use std::sync::Mutex;
4
5use termesh_core::{Problem, TaskSpec};
6use termesh_filesystem::FileSystemService;
7use termesh_tasks::{DecodedTaskOutput, TaskOutputDecoder, TaskService};
8use termesh_workspace::WorkspaceRoot;
9
10pub struct FakeTaskService {
11    catalog: Vec<TaskSpec>,
12    problems: Vec<Problem>,
13    requested: Mutex<Vec<String>>,
14}
15
16impl FakeTaskService {
17    pub fn new(catalog: Vec<TaskSpec>) -> Self {
18        Self { catalog, problems: Vec::new(), requested: Mutex::new(Vec::new()) }
19    }
20
21    pub fn with_problems(mut self, problems: Vec<Problem>) -> Self {
22        self.problems = problems;
23        self
24    }
25
26    pub fn requested(&self) -> Vec<String> {
27        self.requested.lock().expect("fake task state poisoned").clone()
28    }
29}
30
31impl TaskService for FakeTaskService {
32    fn catalog(&self, _root: &WorkspaceRoot, _fs: &dyn FileSystemService) -> Vec<TaskSpec> {
33        self.catalog.clone()
34    }
35
36    fn decoder(&self, task: &TaskSpec) -> Option<Box<dyn TaskOutputDecoder>> {
37        if !self.catalog.iter().any(|candidate| candidate.id == task.id) {
38            return None;
39        }
40        self.requested.lock().expect("fake task state poisoned").push(task.id.clone());
41        Some(Box::new(FakeDecoder { problems: self.problems.clone(), emitted: false }))
42    }
43}
44
45struct FakeDecoder {
46    problems: Vec<Problem>,
47    emitted: bool,
48}
49
50impl TaskOutputDecoder for FakeDecoder {
51    fn push(&mut self, bytes: &[u8]) -> DecodedTaskOutput {
52        let problems = if self.emitted {
53            Vec::new()
54        } else {
55            self.emitted = true;
56            self.problems.clone()
57        };
58        DecodedTaskOutput { display: bytes.to_vec(), problems }
59    }
60
61    fn finish(&mut self) -> DecodedTaskOutput {
62        DecodedTaskOutput::default()
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use crate::FakeFileSystem;
70    use std::path::PathBuf;
71    use termesh_workspace::ProjectKind;
72
73    #[test]
74    fn fake_records_decoder_requests_and_passes_output_through() {
75        let task = TaskSpec {
76            id: "demo".into(),
77            label: "Demo".into(),
78            program: "demo".into(),
79            args: Vec::new(),
80            cwd: PathBuf::from("/p"),
81        };
82        let fake = FakeTaskService::new(vec![task.clone()]);
83        let root = WorkspaceRoot {
84            path: "/p".into(),
85            kind: ProjectKind::Unknown,
86            kinds: Vec::new(),
87            detected: true,
88        };
89        assert_eq!(fake.catalog(&root, &FakeFileSystem::new()), vec![task.clone()]);
90        let mut decoder = fake.decoder(&task).unwrap();
91        assert_eq!(decoder.push(b"hello").display, b"hello");
92        assert_eq!(fake.requested(), vec!["demo"]);
93    }
94}