termesh_test_support/
lib.rs1#![forbid(unsafe_code)]
7
8pub mod fake_clipboard;
9pub mod fake_fs;
10pub mod fake_git;
11pub mod fake_permission_store;
12pub mod fake_tasks;
13pub mod scripted_agent;
14pub mod scripted_lsp;
15pub mod scripted_pty;
16pub mod scripted_search;
17
18pub use fake_clipboard::FakeClipboard;
19pub use fake_fs::FakeFileSystem;
20pub use fake_git::{FakeGitCall, FakeGitControl, FakeGitService};
21pub use fake_permission_store::FakePermissionStore;
22pub use fake_tasks::FakeTaskService;
23pub use scripted_agent::{ScriptedAgent, ScriptedUpdate};
24pub use scripted_lsp::{FakeLspCall, FakeLspControl, ScriptedLanguageServer};
25pub use scripted_pty::{ScriptedPty, ScriptedPtyControl};
26pub use scripted_search::{ScriptedSearch, ScriptedSearchControl};
27
28use std::path::{Path, PathBuf};
29use std::sync::atomic::{AtomicUsize, Ordering};
30use termesh_core::{DirEntryInfo, FsResult};
31use termesh_filesystem::FileSystemService;
32
33pub struct CountingFileSystem {
37 inner: FakeFileSystem,
38 read_dir_calls: AtomicUsize,
39 read_file_calls: AtomicUsize,
40}
41
42impl CountingFileSystem {
43 pub fn new(inner: FakeFileSystem) -> Self {
44 Self { inner, read_dir_calls: AtomicUsize::new(0), read_file_calls: AtomicUsize::new(0) }
45 }
46
47 pub fn read_dir_calls(&self) -> usize {
48 self.read_dir_calls.load(Ordering::Relaxed)
49 }
50
51 pub fn read_file_calls(&self) -> usize {
52 self.read_file_calls.load(Ordering::Relaxed)
53 }
54}
55
56impl FileSystemService for CountingFileSystem {
57 fn read_dir(&self, path: &Path) -> FsResult<Vec<DirEntryInfo>> {
58 self.read_dir_calls.fetch_add(1, Ordering::Relaxed);
59 self.inner.read_dir(path)
60 }
61
62 fn read_file(&self, path: &Path) -> FsResult<Vec<u8>> {
63 self.read_file_calls.fetch_add(1, Ordering::Relaxed);
64 self.inner.read_file(path)
65 }
66
67 fn create_file(&self, path: &Path) -> FsResult<()> {
68 self.inner.create_file(path)
69 }
70
71 fn write_file(&self, path: &Path, contents: &[u8]) -> FsResult<()> {
72 self.inner.write_file(path, contents)
73 }
74
75 fn create_dir(&self, path: &Path) -> FsResult<()> {
76 self.inner.create_dir(path)
77 }
78
79 fn rename(&self, from: &Path, to: &Path) -> FsResult<()> {
80 self.inner.rename(from, to)
81 }
82
83 fn remove_file(&self, path: &Path) -> FsResult<()> {
84 self.inner.remove_file(path)
85 }
86
87 fn remove_dir_all(&self, path: &Path) -> FsResult<()> {
88 self.inner.remove_dir_all(path)
89 }
90
91 fn canonicalize(&self, path: &Path) -> FsResult<PathBuf> {
92 self.inner.canonicalize(path)
93 }
94}
95
96pub fn synthetic_tree(depth: usize, width: usize) -> FakeFileSystem {
100 let fs = FakeFileSystem::new();
101 fs.add_file("/big/Cargo.toml", b"[package]\nname = \"big\"\nversion = \"0.0.0\"\n");
102 let mut trunk = PathBuf::from("/big");
103 for level in 0..depth {
104 for branch in 0..width {
105 let directory = trunk.join(format!("level-{level}-branch-{branch}"));
106 fs.add_dir(&directory);
107 fs.add_file(directory.join("source.rs"), b"fn synthetic() {}\n");
108 }
109 trunk.push(format!("level-{level}-branch-0"));
110 }
111 fs
112}
113
114#[cfg(test)]
115mod phase_05_search_tests {
116 use std::path::PathBuf;
117 use std::sync::atomic::AtomicBool;
118 use std::sync::{Arc, Mutex};
119
120 use termesh_core::{SearchEvent, SearchMode, SearchRequest, SearchRequestId};
121 use termesh_search::{SearchEventSink, SearchService};
122
123 use crate::ScriptedSearch;
124
125 #[test]
126 fn scripted_search_records_requests_and_replays_the_matching_script() {
127 let request = SearchRequest {
128 id: SearchRequestId::new(8),
129 root: PathBuf::from("/repo"),
130 mode: SearchMode::Files,
131 query: "main".into(),
132 limit: 20,
133 };
134 let event = SearchEvent::Finished { id: request.id, truncated: false };
135 let mut search =
136 ScriptedSearch::new().with_script(SearchMode::Files, "main", vec![event.clone()]);
137 let control = search.control();
138 let received = Arc::new(Mutex::new(Vec::new()));
139 let received_for_sink = received.clone();
140 let sink: SearchEventSink = Arc::new(move |event| {
141 received_for_sink.lock().unwrap().push(event);
142 });
143 search.search(&request, &AtomicBool::new(false), &sink).unwrap();
144 assert_eq!(control.requests(), vec![request]);
145 assert_eq!(*received.lock().unwrap(), vec![event]);
146 }
147}