Leaky, but better, test abstraction

Conrad Irwin created

Change summary

crates/command_palette2/src/command_palette.rs |  7 -
crates/editor2/src/editor_tests.rs             | 68 ++++++++-----------
crates/file_finder2/src/file_finder.rs         | 50 +++++---------
crates/gpui2/src/app/test_context.rs           |  6 +
4 files changed, 54 insertions(+), 77 deletions(-)

Detailed changes

crates/command_palette2/src/command_palette.rs 🔗

@@ -385,8 +385,7 @@ mod tests {
         let app_state = init_test(cx);
 
         let project = Project::test(app_state.fs.clone(), [], cx).await;
-        let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
-        let cx = &mut cx;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
 
         let editor = cx.build_view(|cx| {
             let mut editor = Editor::single_line(cx);
@@ -417,7 +416,7 @@ mod tests {
             assert!(is_sorted(&palette.delegate.commands));
         });
 
-        cx.simulate_keystrokes("b c k s p");
+        cx.simulate_input("bcksp");
 
         palette.update(cx, |palette, _| {
             assert_eq!(palette.delegate.matches[0].string, "editor: backspace");
@@ -439,7 +438,7 @@ mod tests {
         });
 
         cx.simulate_keystrokes("cmd-shift-p");
-        cx.simulate_keystrokes("b c k s p");
+        cx.simulate_input("bcksp");
 
         let palette = workspace.update(cx, |workspace, cx| {
             workspace

crates/editor2/src/editor_tests.rs 🔗

@@ -3851,12 +3851,12 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
         Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
     });
     let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (view, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
+    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 
     view.condition::<crate::Event>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
         .await;
 
-    view.update(&mut cx, |view, cx| {
+    view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
@@ -3867,7 +3867,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
         view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
     });
     assert_eq!(
-        view.update(&mut cx, |view, cx| { view.selections.display_ranges(cx) }),
+        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
         &[
             DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
             DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
@@ -3875,50 +3875,50 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
         ]
     );
 
-    view.update(&mut cx, |view, cx| {
+    view.update(cx, |view, cx| {
         view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
     });
     assert_eq!(
-        view.update(&mut cx, |view, cx| view.selections.display_ranges(cx)),
+        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
         &[
             DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
             DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
         ]
     );
 
-    view.update(&mut cx, |view, cx| {
+    view.update(cx, |view, cx| {
         view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
     });
     assert_eq!(
-        view.update(&mut cx, |view, cx| view.selections.display_ranges(cx)),
+        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
         &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
     );
 
     // Trying to expand the selected syntax node one more time has no effect.
-    view.update(&mut cx, |view, cx| {
+    view.update(cx, |view, cx| {
         view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
     });
     assert_eq!(
-        view.update(&mut cx, |view, cx| view.selections.display_ranges(cx)),
+        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
         &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
     );
 
-    view.update(&mut cx, |view, cx| {
+    view.update(cx, |view, cx| {
         view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
     });
     assert_eq!(
-        view.update(&mut cx, |view, cx| view.selections.display_ranges(cx)),
+        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
         &[
             DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
             DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
         ]
     );
 
-    view.update(&mut cx, |view, cx| {
+    view.update(cx, |view, cx| {
         view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
     });
     assert_eq!(
-        view.update(&mut cx, |view, cx| view.selections.display_ranges(cx)),
+        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
         &[
             DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
             DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
@@ -3926,11 +3926,11 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
         ]
     );
 
-    view.update(&mut cx, |view, cx| {
+    view.update(cx, |view, cx| {
         view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
     });
     assert_eq!(
-        view.update(&mut cx, |view, cx| view.selections.display_ranges(cx)),
+        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
         &[
             DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
             DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
@@ -3939,11 +3939,11 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
     );
 
     // Trying to shrink the selected syntax node one more time has no effect.
-    view.update(&mut cx, |view, cx| {
+    view.update(cx, |view, cx| {
         view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
     });
     assert_eq!(
-        view.update(&mut cx, |view, cx| view.selections.display_ranges(cx)),
+        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
         &[
             DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
             DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
@@ -3953,7 +3953,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 
     // Ensure that we keep expanding the selection if the larger selection starts or ends within
     // a fold.
-    view.update(&mut cx, |view, cx| {
+    view.update(cx, |view, cx| {
         view.fold_ranges(
             vec![
                 Point::new(0, 21)..Point::new(0, 24),
@@ -3965,7 +3965,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
         view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
     });
     assert_eq!(
-        view.update(&mut cx, |view, cx| view.selections.display_ranges(cx)),
+        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
         &[
             DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
             DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
@@ -4017,8 +4017,7 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
         Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
     });
     let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    let cx = &mut cx;
+    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
     editor
         .condition::<crate::Event>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
         .await;
@@ -4583,8 +4582,7 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
         Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
     });
     let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (view, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    let cx = &mut cx;
+    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
     view.condition::<crate::Event>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
         .await;
 
@@ -4734,8 +4732,7 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
         Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
     });
     let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    let cx = &mut cx;
+    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
     editor
         .condition::<crate::Event>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
         .await;
@@ -4957,8 +4954,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
     let fake_server = fake_servers.next().await.unwrap();
 
     let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    let cx = &mut cx;
+    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
     assert!(cx.read(|cx| editor.is_dirty(cx)));
 
@@ -5077,8 +5073,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
     let fake_server = fake_servers.next().await.unwrap();
 
     let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    let cx = &mut cx;
+    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
     assert!(cx.read(|cx| editor.is_dirty(cx)));
 
@@ -5205,8 +5200,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
     let fake_server = fake_servers.next().await.unwrap();
 
     let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    let cx = &mut cx;
+    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
 
     let format = editor
@@ -5993,8 +5987,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
         multibuffer
     });
 
-    let (view, mut cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
-    let cx = &mut cx;
+    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
     view.update(cx, |view, cx| {
         assert_eq!(view.text(cx), "aaaa\nbbbb");
         view.change_selections(None, cx, |s| {
@@ -6064,8 +6057,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
         multibuffer
     });
 
-    let (view, mut cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
-    let cx = &mut cx;
+    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
     view.update(cx, |view, cx| {
         let (expected_text, selection_ranges) = marked_text_ranges(
             indoc! {"
@@ -6302,8 +6294,7 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
         Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
     });
     let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (view, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    let cx = &mut cx;
+    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
     view.condition::<crate::Event>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
         .await;
 
@@ -8112,8 +8103,7 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
 
     let buffer_text = "one\ntwo\nthree\n";
     let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    let cx = &mut cx;
+    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
     editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
 
     editor

crates/file_finder2/src/file_finder.rs 🔗

@@ -775,8 +775,7 @@ mod tests {
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
 
-        let (picker, workspace, mut cx) = build_find_picker(project, cx);
-        let cx = &mut cx;
+        let (picker, workspace, cx) = build_find_picker(project, cx);
 
         cx.simulate_input("bna");
 
@@ -815,8 +814,7 @@ mod tests {
 
         let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
 
-        let (picker, workspace, mut cx) = build_find_picker(project, cx);
-        let cx = &mut cx;
+        let (picker, workspace, cx) = build_find_picker(project, cx);
 
         let file_query = &first_file_name[..3];
         let file_row = 1;
@@ -891,8 +889,7 @@ mod tests {
 
         let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
 
-        let (picker, workspace, mut cx) = build_find_picker(project, cx);
-        let cx = &mut cx;
+        let (picker, workspace, cx) = build_find_picker(project, cx);
 
         let file_query = &first_file_name[..3];
         let file_row = 200;
@@ -967,8 +964,7 @@ mod tests {
 
         let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await;
 
-        let (picker, _, mut cx) = build_find_picker(project, cx);
-        let cx = &mut cx;
+        let (picker, _, cx) = build_find_picker(project, cx);
 
         let query = test_path_like("hi");
         picker
@@ -1055,8 +1051,7 @@ mod tests {
         )
         .await;
 
-        let (picker, _, mut cx) = build_find_picker(project, cx);
-        let cx = &mut cx;
+        let (picker, _, cx) = build_find_picker(project, cx);
 
         picker
             .update(cx, |picker, cx| {
@@ -1082,8 +1077,7 @@ mod tests {
         )
         .await;
 
-        let (picker, _, mut cx) = build_find_picker(project, cx);
-        let cx = &mut cx;
+        let (picker, _, cx) = build_find_picker(project, cx);
 
         // Even though there is only one worktree, that worktree's filename
         // is included in the matching, because the worktree is a single file.
@@ -1139,8 +1133,7 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let cx = &mut cx;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
 
         let worktree_id = cx.read(|cx| {
             let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
@@ -1198,8 +1191,8 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let (picker, _workspace, mut cx) = build_find_picker(project, cx);
-        let cx = &mut cx;
+        let (picker, _workspace, cx) = build_find_picker(project, cx);
+
         picker
             .update(cx, |f, cx| {
                 f.delegate.spawn_search(test_path_like("dir"), cx)
@@ -1231,8 +1224,7 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let cx = &mut cx;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
         let worktree_id = cx.read(|cx| {
             let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
             assert_eq!(worktrees.len(), 1);
@@ -1395,8 +1387,7 @@ mod tests {
         .detach();
         cx.background_executor.run_until_parked();
 
-        let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let cx = &mut cx;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
         let worktree_id = cx.read(|cx| {
             let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
             assert_eq!(worktrees.len(), 1,);
@@ -1488,8 +1479,7 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let cx = &mut cx;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
 
         // generate some history to select from
         open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
@@ -1547,8 +1537,7 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let cx = &mut cx;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
         let worktree_id = cx.read(|cx| {
             let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
             assert_eq!(worktrees.len(), 1,);
@@ -1652,8 +1641,7 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let cx = &mut cx;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
         // generate some history to select from
         open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
         open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
@@ -1709,9 +1697,7 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let cx = &mut cx;
-        // generate some history to select from
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); // generate some history to select from
         open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
         open_close_queried_buffer("non", 1, "nonexistent.rs", &workspace, cx).await;
         open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await;
@@ -1807,10 +1793,10 @@ mod tests {
     ) -> (
         View<Picker<FileFinderDelegate>>,
         View<Workspace>,
-        VisualTestContext,
+        &mut VisualTestContext,
     ) {
-        let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let picker = open_file_picker(&workspace, &mut cx);
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+        let picker = open_file_picker(&workspace, cx);
         (picker, workspace, cx)
     }
 

crates/gpui2/src/app/test_context.rs 🔗

@@ -140,7 +140,7 @@ impl TestAppContext {
         .any_handle
     }
 
-    pub fn add_window_view<F, V>(&mut self, build_window: F) -> (View<V>, VisualTestContext)
+    pub fn add_window_view<F, V>(&mut self, build_window: F) -> (View<V>, &mut VisualTestContext)
     where
         F: FnOnce(&mut ViewContext<V>) -> V,
         V: Render,
@@ -149,7 +149,9 @@ impl TestAppContext {
         let window = cx.open_window(WindowOptions::default(), |cx| cx.build_view(build_window));
         drop(cx);
         let view = window.root_view(self).unwrap();
-        (view, VisualTestContext::from_window(*window.deref(), self))
+        let cx = Box::new(VisualTestContext::from_window(*window.deref(), self));
+        // it might be nice to try and cleanup these at the end of each test.
+        (view, Box::leak(cx))
     }
 
     pub fn simulate_new_path_selection(