From 720056d0db4e9a16e21620d98b158a94be941139 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 28 Feb 2022 11:10:22 +0100 Subject: [PATCH] Add unit test for project search --- Cargo.lock | 1 + crates/project/src/project.rs | 3 + crates/search/Cargo.toml | 1 + crates/search/src/project_search.rs | 156 +++++++++++++++++++++++++++- 4 files changed, 157 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dcd0896e4d..e8cf0a12f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4175,6 +4175,7 @@ dependencies = [ "log", "postage", "project", + "serde_json", "theme", "unindent", "util", diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 30c75443f1..b8363bf9df 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2063,6 +2063,9 @@ impl Project { let background = cx.background().clone(); let path_count: usize = snapshots.iter().map(|s| s.visible_file_count()).sum(); + if path_count == 0 { + return Task::ready(Ok(Default::default())); + } let workers = background.num_cpus().min(path_count); let (matching_paths_tx, mut matching_paths_rx) = smol::channel::bounded(1024); cx.background() diff --git a/crates/search/Cargo.toml b/crates/search/Cargo.toml index a6ed1e4cd9..cee9f156e0 100644 --- a/crates/search/Cargo.toml +++ b/crates/search/Cargo.toml @@ -22,5 +22,6 @@ postage = { version = "0.4.1", features = ["futures-traits"] } [dev-dependencies] editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } +serde_json = { version = "1.0.64", features = ["preserve_order"] } workspace = { path = "../workspace", features = ["test-support"] } unindent = "0.1" diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 2a211a005a..78031dd951 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -150,7 +150,7 @@ impl Item for ProjectSearch { nav_history: ItemNavHistory, cx: &mut gpui::ViewContext, ) -> Self::View { - ProjectSearchView::new(model, nav_history, workspace.settings(), cx) + ProjectSearchView::new(model, Some(nav_history), workspace.settings(), cx) } fn project_path(&self) -> Option { @@ -316,7 +316,12 @@ impl ItemView for ProjectSearchView { Self: Sized, { let model = self.model.update(cx, |model, cx| model.clone(cx)); - Some(Self::new(model, nav_history, self.settings.clone(), cx)) + Some(Self::new( + model, + Some(nav_history), + self.settings.clone(), + cx, + )) } fn navigate(&mut self, data: Box, cx: &mut ViewContext) { @@ -332,7 +337,7 @@ impl ItemView for ProjectSearchView { impl ProjectSearchView { fn new( model: ModelHandle, - nav_history: ItemNavHistory, + nav_history: Option, settings: watch::Receiver, cx: &mut ViewContext, ) -> Self { @@ -370,7 +375,7 @@ impl ProjectSearchView { let results_editor = cx.add_view(|cx| { let mut editor = Editor::for_buffer(excerpts, Some(project), settings.clone(), cx); editor.set_searchable(false); - editor.set_nav_history(Some(nav_history)); + editor.set_nav_history(nav_history); editor }); cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab)) @@ -698,3 +703,146 @@ impl ProjectSearchView { .boxed() } } + +#[cfg(test)] +mod tests { + use super::*; + use editor::DisplayPoint; + use gpui::{color::Color, TestAppContext}; + use project::FakeFs; + use serde_json::json; + use std::sync::Arc; + + #[gpui::test] + async fn test_project_search(mut cx: TestAppContext) { + let fonts = cx.font_cache(); + let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default()); + theme.search.match_background = Color::red(); + let settings = Settings::new("Courier", &fonts, Arc::new(theme)).unwrap(); + let settings = watch::channel_with(settings).1; + + let fs = FakeFs::new(cx.background()); + fs.insert_tree( + "/dir", + json!({ + "one.rs": "const ONE: usize = 1;", + "two.rs": "const TWO: usize = one::ONE + one::ONE;", + "three.rs": "const THREE: usize = one::ONE + two::TWO;", + "four.rs": "const FOUR: usize = one::ONE + three::THREE;", + }), + ) + .await; + let project = Project::test(fs.clone(), &mut cx); + let (tree, _) = project + .update(&mut cx, |project, cx| { + project.find_or_create_local_worktree("/dir", false, cx) + }) + .await + .unwrap(); + cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete()) + .await; + + let search = cx.add_model(|cx| ProjectSearch::new(project, cx)); + let search_view = cx.add_view(Default::default(), |cx| { + ProjectSearchView::new(search.clone(), None, settings, cx) + }); + + search_view.update(&mut cx, |search_view, cx| { + search_view + .query_editor + .update(cx, |query_editor, cx| query_editor.set_text("TWO", cx)); + search_view.search(&Search, cx); + }); + search_view.next_notification(&cx).await; + search_view.update(&mut cx, |search_view, cx| { + assert_eq!( + search_view + .results_editor + .update(cx, |editor, cx| editor.display_text(cx)), + "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;" + ); + assert_eq!( + search_view + .results_editor + .update(cx, |editor, cx| editor.all_highlighted_ranges(cx)), + &[ + ( + DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35), + Color::red() + ), + ( + DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40), + Color::red() + ), + ( + DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9), + Color::red() + ) + ] + ); + assert_eq!(search_view.active_match_index, Some(0)); + assert_eq!( + search_view + .results_editor + .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)] + ); + + search_view.select_match(&SelectMatch(Direction::Next), cx); + }); + + search_view.update(&mut cx, |search_view, cx| { + assert_eq!(search_view.active_match_index, Some(1)); + assert_eq!( + search_view + .results_editor + .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)] + ); + search_view.select_match(&SelectMatch(Direction::Next), cx); + }); + + search_view.update(&mut cx, |search_view, cx| { + assert_eq!(search_view.active_match_index, Some(2)); + assert_eq!( + search_view + .results_editor + .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)] + ); + search_view.select_match(&SelectMatch(Direction::Next), cx); + }); + + search_view.update(&mut cx, |search_view, cx| { + assert_eq!(search_view.active_match_index, Some(0)); + assert_eq!( + search_view + .results_editor + .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)] + ); + search_view.select_match(&SelectMatch(Direction::Prev), cx); + }); + + search_view.update(&mut cx, |search_view, cx| { + assert_eq!(search_view.active_match_index, Some(2)); + assert_eq!( + search_view + .results_editor + .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)] + ); + search_view.select_match(&SelectMatch(Direction::Prev), cx); + }); + + search_view.update(&mut cx, |search_view, cx| { + assert_eq!(search_view.active_match_index, Some(1)); + assert_eq!( + search_view + .results_editor + .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)] + ); + }); + } +}