#![forbid(unsafe_code)]
pub mod fake_clipboard;
pub mod fake_fs;
pub mod fake_git;
pub mod fake_permission_store;
pub mod fake_tasks;
pub mod scripted_agent;
pub mod scripted_lsp;
pub mod scripted_pty;
pub mod scripted_search;
pub use fake_clipboard::FakeClipboard;
pub use fake_fs::FakeFileSystem;
pub use fake_git::{FakeGitCall, FakeGitControl, FakeGitService};
pub use fake_permission_store::FakePermissionStore;
pub use fake_tasks::FakeTaskService;
pub use scripted_agent::{ScriptedAgent, ScriptedUpdate};
pub use scripted_lsp::{FakeLspCall, FakeLspControl, ScriptedLanguageServer};
pub use scripted_pty::{ScriptedPty, ScriptedPtyControl};
pub use scripted_search::{ScriptedSearch, ScriptedSearchControl};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use termesh_core::{DirEntryInfo, FsResult};
use termesh_filesystem::FileSystemService;
pub struct CountingFileSystem {
inner: FakeFileSystem,
read_dir_calls: AtomicUsize,
read_file_calls: AtomicUsize,
}
impl CountingFileSystem {
pub fn new(inner: FakeFileSystem) -> Self {
Self { inner, read_dir_calls: AtomicUsize::new(0), read_file_calls: AtomicUsize::new(0) }
}
pub fn read_dir_calls(&self) -> usize {
self.read_dir_calls.load(Ordering::Relaxed)
}
pub fn read_file_calls(&self) -> usize {
self.read_file_calls.load(Ordering::Relaxed)
}
}
impl FileSystemService for CountingFileSystem {
fn read_dir(&self, path: &Path) -> FsResult<Vec<DirEntryInfo>> {
self.read_dir_calls.fetch_add(1, Ordering::Relaxed);
self.inner.read_dir(path)
}
fn read_file(&self, path: &Path) -> FsResult<Vec<u8>> {
self.read_file_calls.fetch_add(1, Ordering::Relaxed);
self.inner.read_file(path)
}
fn create_file(&self, path: &Path) -> FsResult<()> {
self.inner.create_file(path)
}
fn write_file(&self, path: &Path, contents: &[u8]) -> FsResult<()> {
self.inner.write_file(path, contents)
}
fn create_dir(&self, path: &Path) -> FsResult<()> {
self.inner.create_dir(path)
}
fn rename(&self, from: &Path, to: &Path) -> FsResult<()> {
self.inner.rename(from, to)
}
fn remove_file(&self, path: &Path) -> FsResult<()> {
self.inner.remove_file(path)
}
fn remove_dir_all(&self, path: &Path) -> FsResult<()> {
self.inner.remove_dir_all(path)
}
fn canonicalize(&self, path: &Path) -> FsResult<PathBuf> {
self.inner.canonicalize(path)
}
}
pub fn synthetic_tree(depth: usize, width: usize) -> FakeFileSystem {
let fs = FakeFileSystem::new();
fs.add_file("/big/Cargo.toml", b"[package]\nname = \"big\"\nversion = \"0.0.0\"\n");
let mut trunk = PathBuf::from("/big");
for level in 0..depth {
for branch in 0..width {
let directory = trunk.join(format!("level-{level}-branch-{branch}"));
fs.add_dir(&directory);
fs.add_file(directory.join("source.rs"), b"fn synthetic() {}\n");
}
trunk.push(format!("level-{level}-branch-0"));
}
fs
}
#[cfg(test)]
mod phase_05_search_tests {
use std::path::PathBuf;
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};
use termesh_core::{SearchEvent, SearchMode, SearchRequest, SearchRequestId};
use termesh_search::{SearchEventSink, SearchService};
use crate::ScriptedSearch;
#[test]
fn scripted_search_records_requests_and_replays_the_matching_script() {
let request = SearchRequest {
id: SearchRequestId::new(8),
root: PathBuf::from("/repo"),
mode: SearchMode::Files,
query: "main".into(),
limit: 20,
};
let event = SearchEvent::Finished { id: request.id, truncated: false };
let mut search =
ScriptedSearch::new().with_script(SearchMode::Files, "main", vec![event.clone()]);
let control = search.control();
let received = Arc::new(Mutex::new(Vec::new()));
let received_for_sink = received.clone();
let sink: SearchEventSink = Arc::new(move |event| {
received_for_sink.lock().unwrap().push(event);
});
search.search(&request, &AtomicBool::new(false), &sink).unwrap();
assert_eq!(control.requests(), vec![request]);
assert_eq!(*received.lock().unwrap(), vec![event]);
}
}