From ab04d715083a3db6a929353b74dd7408ee59ddf8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 May 2021 18:20:49 +0200 Subject: [PATCH] Use `gpui::test` in async tests in zed Co-Authored-By: Max Brunsfeld --- zed/src/file_finder.rs | 382 +++++++++++++------------- zed/src/workspace.rs | 589 ++++++++++++++++++++--------------------- zed/src/worktree.rs | 410 ++++++++++++++-------------- 3 files changed, 674 insertions(+), 707 deletions(-) diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index 0f966cc5789d1ab7318566e197d7eb0221837a9b..e2427adddd3313975d5a31bb58e78becad2dbdf9 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -453,220 +453,208 @@ impl FileFinder { mod tests { use super::*; use crate::{editor, settings, test::temp_tree, workspace::Workspace}; - use gpui::App; use serde_json::json; use std::fs; use tempdir::TempDir; - #[test] - fn test_matching_paths() { - App::test_async((), |mut app| async move { - let tmp_dir = TempDir::new("example").unwrap(); - fs::create_dir(tmp_dir.path().join("a")).unwrap(); - fs::write(tmp_dir.path().join("a/banana"), "banana").unwrap(); - fs::write(tmp_dir.path().join("a/bandana"), "bandana").unwrap(); - app.update(|ctx| { - super::init(ctx); - editor::init(ctx); - }); - - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (window_id, workspace) = app.add_window(|ctx| { - let mut workspace = Workspace::new(0, settings, ctx); - workspace.add_worktree(tmp_dir.path(), ctx); - workspace - }); - app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) - .await; - app.dispatch_action( - window_id, - vec![workspace.id()], - "file_finder:toggle".into(), - (), - ); + #[gpui::test] + async fn test_matching_paths(mut app: gpui::TestAppContext) { + let tmp_dir = TempDir::new("example").unwrap(); + fs::create_dir(tmp_dir.path().join("a")).unwrap(); + fs::write(tmp_dir.path().join("a/banana"), "banana").unwrap(); + fs::write(tmp_dir.path().join("a/bandana"), "bandana").unwrap(); + app.update(|ctx| { + super::init(ctx); + editor::init(ctx); + }); - let finder = app.read(|ctx| { - workspace - .read(ctx) - .modal() - .cloned() - .unwrap() - .downcast::() - .unwrap() - }); - let query_buffer = app.read(|ctx| finder.read(ctx).query_buffer.clone()); - - let chain = vec![finder.id(), query_buffer.id()]; - app.dispatch_action(window_id, chain.clone(), "buffer:insert", "b".to_string()); - app.dispatch_action(window_id, chain.clone(), "buffer:insert", "n".to_string()); - app.dispatch_action(window_id, chain.clone(), "buffer:insert", "a".to_string()); - finder - .condition(&app, |finder, _| finder.matches.len() == 2) - .await; - - let active_pane = app.read(|ctx| workspace.read(ctx).active_pane().clone()); - app.dispatch_action( - window_id, - vec![workspace.id(), finder.id()], - "menu:select_next", - (), - ); - app.dispatch_action( - window_id, - vec![workspace.id(), finder.id()], - "file_finder:confirm", - (), - ); - active_pane - .condition(&app, |pane, _| pane.active_item().is_some()) - .await; - app.read(|ctx| { - let active_item = active_pane.read(ctx).active_item().unwrap(); - assert_eq!(active_item.title(ctx), "bandana"); - }); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (window_id, workspace) = app.add_window(|ctx| { + let mut workspace = Workspace::new(0, settings, ctx); + workspace.add_worktree(tmp_dir.path(), ctx); + workspace + }); + app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) + .await; + app.dispatch_action( + window_id, + vec![workspace.id()], + "file_finder:toggle".into(), + (), + ); + + let finder = app.read(|ctx| { + workspace + .read(ctx) + .modal() + .cloned() + .unwrap() + .downcast::() + .unwrap() + }); + let query_buffer = app.read(|ctx| finder.read(ctx).query_buffer.clone()); + + let chain = vec![finder.id(), query_buffer.id()]; + app.dispatch_action(window_id, chain.clone(), "buffer:insert", "b".to_string()); + app.dispatch_action(window_id, chain.clone(), "buffer:insert", "n".to_string()); + app.dispatch_action(window_id, chain.clone(), "buffer:insert", "a".to_string()); + finder + .condition(&app, |finder, _| finder.matches.len() == 2) + .await; + + let active_pane = app.read(|ctx| workspace.read(ctx).active_pane().clone()); + app.dispatch_action( + window_id, + vec![workspace.id(), finder.id()], + "menu:select_next", + (), + ); + app.dispatch_action( + window_id, + vec![workspace.id(), finder.id()], + "file_finder:confirm", + (), + ); + active_pane + .condition(&app, |pane, _| pane.active_item().is_some()) + .await; + app.read(|ctx| { + let active_item = active_pane.read(ctx).active_item().unwrap(); + assert_eq!(active_item.title(ctx), "bandana"); }); } - #[test] - fn test_matching_cancellation() { - App::test_async((), |mut app| async move { - let tmp_dir = temp_tree(json!({ - "hello": "", - "goodbye": "", - "halogen-light": "", - "happiness": "", - "height": "", - "hi": "", - "hiccup": "", - })); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, workspace) = app.add_window(|ctx| { - let mut workspace = Workspace::new(0, settings.clone(), ctx); - workspace.add_worktree(tmp_dir.path(), ctx); - workspace - }); - app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) - .await; - let (_, finder) = - app.add_window(|ctx| FileFinder::new(settings, workspace.clone(), ctx)); - - let query = "hi".to_string(); - finder.update(&mut app, |f, ctx| f.spawn_search(query.clone(), ctx)); - finder.condition(&app, |f, _| f.matches.len() == 5).await; - - finder.update(&mut app, |finder, ctx| { - let matches = finder.matches.clone(); - - // Simulate a search being cancelled after the time limit, - // returning only a subset of the matches that would have been found. - finder.spawn_search(query.clone(), ctx); - finder.update_matches( - ( - finder.latest_search_id, - true, // did-cancel - query.clone(), - vec![matches[1].clone(), matches[3].clone()], - ), - ctx, - ); - - // Simulate another cancellation. - finder.spawn_search(query.clone(), ctx); - finder.update_matches( - ( - finder.latest_search_id, - true, // did-cancel - query.clone(), - vec![matches[0].clone(), matches[2].clone(), matches[3].clone()], - ), - ctx, - ); + #[gpui::test] + async fn test_matching_cancellation(mut app: gpui::TestAppContext) { + let tmp_dir = temp_tree(json!({ + "hello": "", + "goodbye": "", + "halogen-light": "", + "happiness": "", + "height": "", + "hi": "", + "hiccup": "", + })); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, workspace) = app.add_window(|ctx| { + let mut workspace = Workspace::new(0, settings.clone(), ctx); + workspace.add_worktree(tmp_dir.path(), ctx); + workspace + }); + app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) + .await; + let (_, finder) = app.add_window(|ctx| FileFinder::new(settings, workspace.clone(), ctx)); + + let query = "hi".to_string(); + finder.update(&mut app, |f, ctx| f.spawn_search(query.clone(), ctx)); + finder.condition(&app, |f, _| f.matches.len() == 5).await; + + finder.update(&mut app, |finder, ctx| { + let matches = finder.matches.clone(); + + // Simulate a search being cancelled after the time limit, + // returning only a subset of the matches that would have been found. + finder.spawn_search(query.clone(), ctx); + finder.update_matches( + ( + finder.latest_search_id, + true, // did-cancel + query.clone(), + vec![matches[1].clone(), matches[3].clone()], + ), + ctx, + ); - assert_eq!(finder.matches, matches[0..4]) - }); + // Simulate another cancellation. + finder.spawn_search(query.clone(), ctx); + finder.update_matches( + ( + finder.latest_search_id, + true, // did-cancel + query.clone(), + vec![matches[0].clone(), matches[2].clone(), matches[3].clone()], + ), + ctx, + ); + + assert_eq!(finder.matches, matches[0..4]) }); } - #[test] - fn test_single_file_worktrees() { - App::test_async((), |mut app| async move { - let temp_dir = TempDir::new("test-single-file-worktrees").unwrap(); - let dir_path = temp_dir.path().join("the-parent-dir"); - let file_path = dir_path.join("the-file"); - fs::create_dir(&dir_path).unwrap(); - fs::write(&file_path, "").unwrap(); - - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, workspace) = app.add_window(|ctx| { - let mut workspace = Workspace::new(0, settings.clone(), ctx); - workspace.add_worktree(&file_path, ctx); - workspace - }); - app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) - .await; - let (_, finder) = - app.add_window(|ctx| FileFinder::new(settings, workspace.clone(), ctx)); - - // Even though there is only one worktree, that worktree's filename - // is included in the matching, because the worktree is a single file. - finder.update(&mut app, |f, ctx| f.spawn_search("thf".into(), ctx)); - finder.condition(&app, |f, _| f.matches.len() == 1).await; - - app.read(|ctx| { - let finder = finder.read(ctx); - let (file_name, file_name_positions, full_path, full_path_positions) = - finder.labels_for_match(&finder.matches[0], ctx).unwrap(); - - assert_eq!(file_name, "the-file"); - assert_eq!(file_name_positions, &[0, 1, 4]); - assert_eq!(full_path, "the-file"); - assert_eq!(full_path_positions, &[0, 1, 4]); - }); - - // Since the worktree root is a file, searching for its name followed by a slash does - // not match anything. - finder.update(&mut app, |f, ctx| f.spawn_search("thf/".into(), ctx)); - finder.condition(&app, |f, _| f.matches.len() == 0).await; + #[gpui::test] + async fn test_single_file_worktrees(mut app: gpui::TestAppContext) { + let temp_dir = TempDir::new("test-single-file-worktrees").unwrap(); + let dir_path = temp_dir.path().join("the-parent-dir"); + let file_path = dir_path.join("the-file"); + fs::create_dir(&dir_path).unwrap(); + fs::write(&file_path, "").unwrap(); + + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, workspace) = app.add_window(|ctx| { + let mut workspace = Workspace::new(0, settings.clone(), ctx); + workspace.add_worktree(&file_path, ctx); + workspace }); + app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) + .await; + let (_, finder) = app.add_window(|ctx| FileFinder::new(settings, workspace.clone(), ctx)); + + // Even though there is only one worktree, that worktree's filename + // is included in the matching, because the worktree is a single file. + finder.update(&mut app, |f, ctx| f.spawn_search("thf".into(), ctx)); + finder.condition(&app, |f, _| f.matches.len() == 1).await; + + app.read(|ctx| { + let finder = finder.read(ctx); + let (file_name, file_name_positions, full_path, full_path_positions) = + finder.labels_for_match(&finder.matches[0], ctx).unwrap(); + + assert_eq!(file_name, "the-file"); + assert_eq!(file_name_positions, &[0, 1, 4]); + assert_eq!(full_path, "the-file"); + assert_eq!(full_path_positions, &[0, 1, 4]); + }); + + // Since the worktree root is a file, searching for its name followed by a slash does + // not match anything. + finder.update(&mut app, |f, ctx| f.spawn_search("thf/".into(), ctx)); + finder.condition(&app, |f, _| f.matches.len() == 0).await; } - #[test] - fn test_multiple_matches_with_same_relative_path() { - App::test_async((), |mut app| async move { - let tmp_dir = temp_tree(json!({ - "dir1": { "a.txt": "" }, - "dir2": { "a.txt": "" } - })); - let settings = settings::channel(&app.font_cache()).unwrap().1; + #[gpui::test] + async fn test_multiple_matches_with_same_relative_path(mut app: gpui::TestAppContext) { + let tmp_dir = temp_tree(json!({ + "dir1": { "a.txt": "" }, + "dir2": { "a.txt": "" } + })); + let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, workspace) = app.add_window(|ctx| Workspace::new(0, settings.clone(), ctx)); + let (_, workspace) = app.add_window(|ctx| Workspace::new(0, settings.clone(), ctx)); - workspace - .update(&mut app, |workspace, ctx| { - workspace.open_paths( - &[tmp_dir.path().join("dir1"), tmp_dir.path().join("dir2")], - ctx, - ) - }) - .await; - app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) - .await; - - let (_, finder) = - app.add_window(|ctx| FileFinder::new(settings, workspace.clone(), ctx)); - - // Run a search that matches two files with the same relative path. - finder.update(&mut app, |f, ctx| f.spawn_search("a.t".into(), ctx)); - finder.condition(&app, |f, _| f.matches.len() == 2).await; - - // Can switch between different matches with the same relative path. - finder.update(&mut app, |f, ctx| { - assert_eq!(f.selected_index(), 0); - f.select_next(&(), ctx); - assert_eq!(f.selected_index(), 1); - f.select_prev(&(), ctx); - assert_eq!(f.selected_index(), 0); - }); + workspace + .update(&mut app, |workspace, ctx| { + workspace.open_paths( + &[tmp_dir.path().join("dir1"), tmp_dir.path().join("dir2")], + ctx, + ) + }) + .await; + app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) + .await; + + let (_, finder) = app.add_window(|ctx| FileFinder::new(settings, workspace.clone(), ctx)); + + // Run a search that matches two files with the same relative path. + finder.update(&mut app, |f, ctx| f.spawn_search("a.t".into(), ctx)); + finder.condition(&app, |f, _| f.matches.len() == 2).await; + + // Can switch between different matches with the same relative path. + finder.update(&mut app, |f, ctx| { + assert_eq!(f.selected_index(), 0); + f.select_next(&(), ctx); + assert_eq!(f.selected_index(), 1); + f.select_prev(&(), ctx); + assert_eq!(f.selected_index(), 0); }); } } diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 1b254731733fbdcaf22076f540760fec5652d1ea..90f22eaba93c2f6d8328323a40f19973cf7c0cd6 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -729,7 +729,6 @@ impl WorkspaceHandle for ViewHandle { mod tests { use super::*; use crate::{editor::BufferView, settings, test::temp_tree}; - use gpui::App; use serde_json::json; use std::collections::HashSet; use tempdir::TempDir; @@ -793,332 +792,324 @@ mod tests { assert_eq!(app.window_ids().count(), 2); } - #[test] - fn test_open_entry() { - App::test_async((), |mut app| async move { - let dir = temp_tree(json!({ - "a": { - "file1": "contents 1", - "file2": "contents 2", - "file3": "contents 3", - }, - })); - - let settings = settings::channel(&app.font_cache()).unwrap().1; - - let (_, workspace) = app.add_window(|ctx| { - let mut workspace = Workspace::new(0, settings, ctx); - workspace.add_worktree(dir.path(), ctx); - workspace - }); - - app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) - .await; - let entries = app.read(|ctx| workspace.file_entries(ctx)); - let file1 = entries[0].clone(); - let file2 = entries[1].clone(); - let file3 = entries[2].clone(); + #[gpui::test] + async fn test_open_entry(mut app: gpui::TestAppContext) { + let dir = temp_tree(json!({ + "a": { + "file1": "contents 1", + "file2": "contents 2", + "file3": "contents 3", + }, + })); - // Open the first entry - workspace - .update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx)) - .unwrap() - .await; - app.read(|ctx| { - let pane = workspace.read(ctx).active_pane().read(ctx); - assert_eq!( - pane.active_item().unwrap().entry_id(ctx), - Some(file1.clone()) - ); - assert_eq!(pane.items().len(), 1); - }); + let settings = settings::channel(&app.font_cache()).unwrap().1; - // Open the second entry + let (_, workspace) = app.add_window(|ctx| { + let mut workspace = Workspace::new(0, settings, ctx); + workspace.add_worktree(dir.path(), ctx); workspace - .update(&mut app, |w, ctx| w.open_entry(file2.clone(), ctx)) - .unwrap() - .await; - app.read(|ctx| { - let pane = workspace.read(ctx).active_pane().read(ctx); - assert_eq!( - pane.active_item().unwrap().entry_id(ctx), - Some(file2.clone()) - ); - assert_eq!(pane.items().len(), 2); - }); - - // Open the first entry again. The existing pane item is activated. - workspace.update(&mut app, |w, ctx| { - assert!(w.open_entry(file1.clone(), ctx).is_none()) - }); - app.read(|ctx| { - let pane = workspace.read(ctx).active_pane().read(ctx); - assert_eq!( - pane.active_item().unwrap().entry_id(ctx), - Some(file1.clone()) - ); - assert_eq!(pane.items().len(), 2); - }); - - // Split the pane with the first entry, then open the second entry again. - workspace.update(&mut app, |w, ctx| { - w.split_pane(w.active_pane().clone(), SplitDirection::Right, ctx); - assert!(w.open_entry(file2.clone(), ctx).is_none()); - assert_eq!( - w.active_pane() - .read(ctx) - .active_item() - .unwrap() - .entry_id(ctx.as_ref()), - Some(file2.clone()) - ); - }); - - // Open the third entry twice concurrently. Two pane items - // are added. - let (t1, t2) = workspace.update(&mut app, |w, ctx| { - ( - w.open_entry(file3.clone(), ctx).unwrap(), - w.open_entry(file3.clone(), ctx).unwrap(), - ) - }); - t1.await; - t2.await; - app.read(|ctx| { - let pane = workspace.read(ctx).active_pane().read(ctx); - assert_eq!( - pane.active_item().unwrap().entry_id(ctx), - Some(file3.clone()) - ); - let pane_entries = pane - .items() - .iter() - .map(|i| i.entry_id(ctx).unwrap()) - .collect::>(); - assert_eq!(pane_entries, &[file1, file2, file3.clone(), file3]); - }); }); - } - #[test] - fn test_open_paths() { - App::test_async((), |mut app| async move { - let dir1 = temp_tree(json!({ - "a.txt": "", - })); - let dir2 = temp_tree(json!({ - "b.txt": "", - })); - - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, workspace) = app.add_window(|ctx| { - let mut workspace = Workspace::new(0, settings, ctx); - workspace.add_worktree(dir1.path(), ctx); - workspace - }); - app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) - .await; - - // Open a file within an existing worktree. - app.update(|ctx| { - workspace.update(ctx, |view, ctx| { - view.open_paths(&[dir1.path().join("a.txt")], ctx) - }) - }) + app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) .await; - app.read(|ctx| { - assert_eq!( - workspace - .read(ctx) - .active_pane() - .read(ctx) - .active_item() - .unwrap() - .title(ctx), - "a.txt" - ); - }); + let entries = app.read(|ctx| workspace.file_entries(ctx)); + let file1 = entries[0].clone(); + let file2 = entries[1].clone(); + let file3 = entries[2].clone(); + + // Open the first entry + workspace + .update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx)) + .unwrap() + .await; + app.read(|ctx| { + let pane = workspace.read(ctx).active_pane().read(ctx); + assert_eq!( + pane.active_item().unwrap().entry_id(ctx), + Some(file1.clone()) + ); + assert_eq!(pane.items().len(), 1); + }); - // Open a file outside of any existing worktree. - app.update(|ctx| { - workspace.update(ctx, |view, ctx| { - view.open_paths(&[dir2.path().join("b.txt")], ctx) - }) - }) + // Open the second entry + workspace + .update(&mut app, |w, ctx| w.open_entry(file2.clone(), ctx)) + .unwrap() .await; - app.read(|ctx| { - let worktree_roots = workspace + app.read(|ctx| { + let pane = workspace.read(ctx).active_pane().read(ctx); + assert_eq!( + pane.active_item().unwrap().entry_id(ctx), + Some(file2.clone()) + ); + assert_eq!(pane.items().len(), 2); + }); + + // Open the first entry again. The existing pane item is activated. + workspace.update(&mut app, |w, ctx| { + assert!(w.open_entry(file1.clone(), ctx).is_none()) + }); + app.read(|ctx| { + let pane = workspace.read(ctx).active_pane().read(ctx); + assert_eq!( + pane.active_item().unwrap().entry_id(ctx), + Some(file1.clone()) + ); + assert_eq!(pane.items().len(), 2); + }); + + // Split the pane with the first entry, then open the second entry again. + workspace.update(&mut app, |w, ctx| { + w.split_pane(w.active_pane().clone(), SplitDirection::Right, ctx); + assert!(w.open_entry(file2.clone(), ctx).is_none()); + assert_eq!( + w.active_pane() .read(ctx) - .worktrees() - .iter() - .map(|w| w.read(ctx).abs_path()) - .collect::>(); - assert_eq!( - worktree_roots, - vec![dir1.path(), &dir2.path().join("b.txt")] - .into_iter() - .collect(), - ); - assert_eq!( - workspace - .read(ctx) - .active_pane() - .read(ctx) - .active_item() - .unwrap() - .title(ctx), - "b.txt" - ); - }); + .active_item() + .unwrap() + .entry_id(ctx.as_ref()), + Some(file2.clone()) + ); + }); + + // Open the third entry twice concurrently. Two pane items + // are added. + let (t1, t2) = workspace.update(&mut app, |w, ctx| { + ( + w.open_entry(file3.clone(), ctx).unwrap(), + w.open_entry(file3.clone(), ctx).unwrap(), + ) + }); + t1.await; + t2.await; + app.read(|ctx| { + let pane = workspace.read(ctx).active_pane().read(ctx); + assert_eq!( + pane.active_item().unwrap().entry_id(ctx), + Some(file3.clone()) + ); + let pane_entries = pane + .items() + .iter() + .map(|i| i.entry_id(ctx).unwrap()) + .collect::>(); + assert_eq!(pane_entries, &[file1, file2, file3.clone(), file3]); }); } - #[test] - fn test_open_and_save_new_file() { - App::test_async((), |mut app| async move { - let dir = TempDir::new("test-new-file").unwrap(); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, workspace) = app.add_window(|ctx| { - let mut workspace = Workspace::new(0, settings, ctx); - workspace.add_worktree(dir.path(), ctx); - workspace - }); - let tree = app.read(|ctx| { + #[gpui::test] + async fn test_open_paths(mut app: gpui::TestAppContext) { + let dir1 = temp_tree(json!({ + "a.txt": "", + })); + let dir2 = temp_tree(json!({ + "b.txt": "", + })); + + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, workspace) = app.add_window(|ctx| { + let mut workspace = Workspace::new(0, settings, ctx); + workspace.add_worktree(dir1.path(), ctx); + workspace + }); + app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) + .await; + + // Open a file within an existing worktree. + app.update(|ctx| { + workspace.update(ctx, |view, ctx| { + view.open_paths(&[dir1.path().join("a.txt")], ctx) + }) + }) + .await; + app.read(|ctx| { + assert_eq!( workspace .read(ctx) - .worktrees() - .iter() - .next() + .active_pane() + .read(ctx) + .active_item() .unwrap() - .clone() - }); - tree.flush_fs_events(&app).await; + .title(ctx), + "a.txt" + ); + }); - // Create a new untitled buffer - let editor = workspace.update(&mut app, |workspace, ctx| { - workspace.open_new_file(&(), ctx); - workspace - .active_item(ctx) - .unwrap() - .to_any() - .downcast::() - .unwrap() - }); - editor.update(&mut app, |editor, ctx| { - assert!(!editor.is_dirty(ctx.as_ref())); - assert_eq!(editor.title(ctx.as_ref()), "untitled"); - editor.insert(&"hi".to_string(), ctx); - assert!(editor.is_dirty(ctx.as_ref())); - }); - - // Save the buffer. This prompts for a filename. - workspace.update(&mut app, |workspace, ctx| { - workspace.save_active_item(&(), ctx) - }); - app.simulate_new_path_selection(|parent_dir| { - assert_eq!(parent_dir, dir.path()); - Some(parent_dir.join("the-new-name")) - }); - app.read(|ctx| { - assert!(editor.is_dirty(ctx)); - assert_eq!(editor.title(ctx), "untitled"); - }); - - // When the save completes, the buffer's title is updated. - tree.update(&mut app, |tree, ctx| tree.next_scan_complete(ctx)) - .await; - app.read(|ctx| { - assert!(!editor.is_dirty(ctx)); - assert_eq!(editor.title(ctx), "the-new-name"); - }); - - // Edit the file and save it again. This time, there is no filename prompt. - editor.update(&mut app, |editor, ctx| { - editor.insert(&" there".to_string(), ctx); - assert_eq!(editor.is_dirty(ctx.as_ref()), true); - }); - workspace.update(&mut app, |workspace, ctx| { - workspace.save_active_item(&(), ctx) - }); - assert!(!app.did_prompt_for_new_path()); - editor - .condition(&app, |editor, ctx| !editor.is_dirty(ctx)) - .await; - app.read(|ctx| assert_eq!(editor.title(ctx), "the-new-name")); - - // Open the same newly-created file in another pane item. The new editor should reuse - // the same buffer. - workspace.update(&mut app, |workspace, ctx| { - workspace.open_new_file(&(), ctx); - workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, ctx); - assert!(workspace - .open_entry((tree.id(), Path::new("the-new-name").into()), ctx) - .is_none()); - }); - let editor2 = workspace.update(&mut app, |workspace, ctx| { + // Open a file outside of any existing worktree. + app.update(|ctx| { + workspace.update(ctx, |view, ctx| { + view.open_paths(&[dir2.path().join("b.txt")], ctx) + }) + }) + .await; + app.read(|ctx| { + let worktree_roots = workspace + .read(ctx) + .worktrees() + .iter() + .map(|w| w.read(ctx).abs_path()) + .collect::>(); + assert_eq!( + worktree_roots, + vec![dir1.path(), &dir2.path().join("b.txt")] + .into_iter() + .collect(), + ); + assert_eq!( workspace - .active_item(ctx) - .unwrap() - .to_any() - .downcast::() + .read(ctx) + .active_pane() + .read(ctx) + .active_item() .unwrap() - }); - app.read(|ctx| { - assert_eq!(editor2.read(ctx).buffer(), editor.read(ctx).buffer()); - }) + .title(ctx), + "b.txt" + ); }); } - #[test] - fn test_pane_actions() { - App::test_async((), |mut app| async move { - app.update(|ctx| pane::init(ctx)); - - let dir = temp_tree(json!({ - "a": { - "file1": "contents 1", - "file2": "contents 2", - "file3": "contents 3", - }, - })); - - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (window_id, workspace) = app.add_window(|ctx| { - let mut workspace = Workspace::new(0, settings, ctx); - workspace.add_worktree(dir.path(), ctx); - workspace - }); - app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) - .await; - let entries = app.read(|ctx| workspace.file_entries(ctx)); - let file1 = entries[0].clone(); + #[gpui::test] + async fn test_open_and_save_new_file(mut app: gpui::TestAppContext) { + let dir = TempDir::new("test-new-file").unwrap(); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, workspace) = app.add_window(|ctx| { + let mut workspace = Workspace::new(0, settings, ctx); + workspace.add_worktree(dir.path(), ctx); + workspace + }); + let tree = app.read(|ctx| { + workspace + .read(ctx) + .worktrees() + .iter() + .next() + .unwrap() + .clone() + }); + tree.flush_fs_events(&app).await; + + // Create a new untitled buffer + let editor = workspace.update(&mut app, |workspace, ctx| { + workspace.open_new_file(&(), ctx); + workspace + .active_item(ctx) + .unwrap() + .to_any() + .downcast::() + .unwrap() + }); + editor.update(&mut app, |editor, ctx| { + assert!(!editor.is_dirty(ctx.as_ref())); + assert_eq!(editor.title(ctx.as_ref()), "untitled"); + editor.insert(&"hi".to_string(), ctx); + assert!(editor.is_dirty(ctx.as_ref())); + }); + + // Save the buffer. This prompts for a filename. + workspace.update(&mut app, |workspace, ctx| { + workspace.save_active_item(&(), ctx) + }); + app.simulate_new_path_selection(|parent_dir| { + assert_eq!(parent_dir, dir.path()); + Some(parent_dir.join("the-new-name")) + }); + app.read(|ctx| { + assert!(editor.is_dirty(ctx)); + assert_eq!(editor.title(ctx), "untitled"); + }); - let pane_1 = app.read(|ctx| workspace.read(ctx).active_pane().clone()); + // When the save completes, the buffer's title is updated. + tree.update(&mut app, |tree, ctx| tree.next_scan_complete(ctx)) + .await; + app.read(|ctx| { + assert!(!editor.is_dirty(ctx)); + assert_eq!(editor.title(ctx), "the-new-name"); + }); + // Edit the file and save it again. This time, there is no filename prompt. + editor.update(&mut app, |editor, ctx| { + editor.insert(&" there".to_string(), ctx); + assert_eq!(editor.is_dirty(ctx.as_ref()), true); + }); + workspace.update(&mut app, |workspace, ctx| { + workspace.save_active_item(&(), ctx) + }); + assert!(!app.did_prompt_for_new_path()); + editor + .condition(&app, |editor, ctx| !editor.is_dirty(ctx)) + .await; + app.read(|ctx| assert_eq!(editor.title(ctx), "the-new-name")); + + // Open the same newly-created file in another pane item. The new editor should reuse + // the same buffer. + workspace.update(&mut app, |workspace, ctx| { + workspace.open_new_file(&(), ctx); + workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, ctx); + assert!(workspace + .open_entry((tree.id(), Path::new("the-new-name").into()), ctx) + .is_none()); + }); + let editor2 = workspace.update(&mut app, |workspace, ctx| { workspace - .update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx)) + .active_item(ctx) .unwrap() - .await; - app.read(|ctx| { - assert_eq!( - pane_1.read(ctx).active_item().unwrap().entry_id(ctx), - Some(file1.clone()) - ); - }); + .to_any() + .downcast::() + .unwrap() + }); + app.read(|ctx| { + assert_eq!(editor2.read(ctx).buffer(), editor.read(ctx).buffer()); + }) + } + + #[gpui::test] + async fn test_pane_actions(mut app: gpui::TestAppContext) { + app.update(|ctx| pane::init(ctx)); + + let dir = temp_tree(json!({ + "a": { + "file1": "contents 1", + "file2": "contents 2", + "file3": "contents 3", + }, + })); + + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (window_id, workspace) = app.add_window(|ctx| { + let mut workspace = Workspace::new(0, settings, ctx); + workspace.add_worktree(dir.path(), ctx); + workspace + }); + app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) + .await; + let entries = app.read(|ctx| workspace.file_entries(ctx)); + let file1 = entries[0].clone(); + + let pane_1 = app.read(|ctx| workspace.read(ctx).active_pane().clone()); + + workspace + .update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx)) + .unwrap() + .await; + app.read(|ctx| { + assert_eq!( + pane_1.read(ctx).active_item().unwrap().entry_id(ctx), + Some(file1.clone()) + ); + }); - app.dispatch_action(window_id, vec![pane_1.id()], "pane:split_right", ()); - app.update(|ctx| { - let pane_2 = workspace.read(ctx).active_pane().clone(); - assert_ne!(pane_1, pane_2); + app.dispatch_action(window_id, vec![pane_1.id()], "pane:split_right", ()); + app.update(|ctx| { + let pane_2 = workspace.read(ctx).active_pane().clone(); + assert_ne!(pane_1, pane_2); - let pane2_item = pane_2.read(ctx).active_item().unwrap(); - assert_eq!(pane2_item.entry_id(ctx.as_ref()), Some(file1.clone())); + let pane2_item = pane_2.read(ctx).active_item().unwrap(); + assert_eq!(pane2_item.entry_id(ctx.as_ref()), Some(file1.clone())); - ctx.dispatch_action(window_id, vec![pane_2.id()], "pane:close_active_item", ()); - let workspace_view = workspace.read(ctx); - assert_eq!(workspace_view.panes.len(), 1); - assert_eq!(workspace_view.active_pane(), &pane_1); - }); + ctx.dispatch_action(window_id, vec![pane_2.id()], "pane:close_active_item", ()); + let workspace_view = workspace.read(ctx); + assert_eq!(workspace_view.panes.len(), 1); + assert_eq!(workspace_view.active_pane(), &pane_1); }); } } diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index be895533f123a1fcd713bf26122f1da739101e4c..30383f5a0a85a1e3e4d46d0ca5f06fcae4367b0c 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -1353,7 +1353,6 @@ mod tests { use crate::editor::Buffer; use crate::test::*; use anyhow::Result; - use gpui::App; use rand::prelude::*; use serde_json::json; use std::env; @@ -1361,248 +1360,237 @@ mod tests { use std::os::unix; use std::time::{SystemTime, UNIX_EPOCH}; - #[test] - fn test_populate_and_search() { - App::test_async((), |mut app| async move { - let dir = temp_tree(json!({ - "root": { - "apple": "", - "banana": { - "carrot": { - "date": "", - "endive": "", - } - }, - "fennel": { - "grape": "", + #[gpui::test] + async fn test_populate_and_search(mut app: gpui::TestAppContext) { + let dir = temp_tree(json!({ + "root": { + "apple": "", + "banana": { + "carrot": { + "date": "", + "endive": "", } + }, + "fennel": { + "grape": "", } - })); + } + })); - let root_link_path = dir.path().join("root_link"); - unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap(); - unix::fs::symlink( - &dir.path().join("root/fennel"), - &dir.path().join("root/finnochio"), - ) - .unwrap(); + let root_link_path = dir.path().join("root_link"); + unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap(); + unix::fs::symlink( + &dir.path().join("root/fennel"), + &dir.path().join("root/finnochio"), + ) + .unwrap(); - let tree = app.add_model(|ctx| Worktree::new(root_link_path, ctx)); + let tree = app.add_model(|ctx| Worktree::new(root_link_path, ctx)); - app.read(|ctx| tree.read(ctx).scan_complete()).await; - app.read(|ctx| { - let tree = tree.read(ctx); - assert_eq!(tree.file_count(), 5); + app.read(|ctx| tree.read(ctx).scan_complete()).await; + app.read(|ctx| { + let tree = tree.read(ctx); + assert_eq!(tree.file_count(), 5); - assert_eq!( - tree.inode_for_path("fennel/grape"), - tree.inode_for_path("finnochio/grape") - ); + assert_eq!( + tree.inode_for_path("fennel/grape"), + tree.inode_for_path("finnochio/grape") + ); - let results = match_paths( - Some(tree.snapshot()).iter(), - "bna", - false, - false, - false, - 10, - Default::default(), - ctx.thread_pool().clone(), - ) - .into_iter() - .map(|result| result.path) - .collect::>>(); - assert_eq!( - results, - vec![ - PathBuf::from("banana/carrot/date").into(), - PathBuf::from("banana/carrot/endive").into(), - ] - ); - }) - }); + let results = match_paths( + Some(tree.snapshot()).iter(), + "bna", + false, + false, + false, + 10, + Default::default(), + ctx.thread_pool().clone(), + ) + .into_iter() + .map(|result| result.path) + .collect::>>(); + assert_eq!( + results, + vec![ + PathBuf::from("banana/carrot/date").into(), + PathBuf::from("banana/carrot/endive").into(), + ] + ); + }) } - #[test] - fn test_save_file() { - App::test_async((), |mut app| async move { - let dir = temp_tree(json!({ - "file1": "the old contents", - })); + #[gpui::test] + async fn test_save_file(mut app: gpui::TestAppContext) { + let dir = temp_tree(json!({ + "file1": "the old contents", + })); - let tree = app.add_model(|ctx| Worktree::new(dir.path(), ctx)); - app.read(|ctx| tree.read(ctx).scan_complete()).await; - app.read(|ctx| assert_eq!(tree.read(ctx).file_count(), 1)); + let tree = app.add_model(|ctx| Worktree::new(dir.path(), ctx)); + app.read(|ctx| tree.read(ctx).scan_complete()).await; + app.read(|ctx| assert_eq!(tree.read(ctx).file_count(), 1)); - let buffer = - app.add_model(|ctx| Buffer::new(1, "a line of text.\n".repeat(10 * 1024), ctx)); + let buffer = + app.add_model(|ctx| Buffer::new(1, "a line of text.\n".repeat(10 * 1024), ctx)); - let path = tree.update(&mut app, |tree, ctx| { - let path = tree.files(0).next().unwrap().path().clone(); - assert_eq!(path.file_name().unwrap(), "file1"); - smol::block_on(tree.save(&path, buffer.read(ctx).snapshot(), ctx.as_ref())) - .unwrap(); - path - }); + let path = tree.update(&mut app, |tree, ctx| { + let path = tree.files(0).next().unwrap().path().clone(); + assert_eq!(path.file_name().unwrap(), "file1"); + smol::block_on(tree.save(&path, buffer.read(ctx).snapshot(), ctx.as_ref())).unwrap(); + path + }); - let history = app - .read(|ctx| tree.read(ctx).load_history(&path, ctx)) - .await - .unwrap(); - app.read(|ctx| { - assert_eq!(history.base_text.as_ref(), buffer.read(ctx).text()); - }); + let history = app + .read(|ctx| tree.read(ctx).load_history(&path, ctx)) + .await + .unwrap(); + app.read(|ctx| { + assert_eq!(history.base_text.as_ref(), buffer.read(ctx).text()); }); } - #[test] - fn test_save_in_single_file_worktree() { - App::test_async((), |mut app| async move { - let dir = temp_tree(json!({ - "file1": "the old contents", - })); - - let tree = app.add_model(|ctx| Worktree::new(dir.path().join("file1"), ctx)); - app.read(|ctx| tree.read(ctx).scan_complete()).await; - app.read(|ctx| assert_eq!(tree.read(ctx).file_count(), 1)); + #[gpui::test] + async fn test_save_in_single_file_worktree(mut app: gpui::TestAppContext) { + let dir = temp_tree(json!({ + "file1": "the old contents", + })); - let buffer = - app.add_model(|ctx| Buffer::new(1, "a line of text.\n".repeat(10 * 1024), ctx)); + let tree = app.add_model(|ctx| Worktree::new(dir.path().join("file1"), ctx)); + app.read(|ctx| tree.read(ctx).scan_complete()).await; + app.read(|ctx| assert_eq!(tree.read(ctx).file_count(), 1)); - let file = app.read(|ctx| tree.file("", ctx)); - app.update(|ctx| { - assert_eq!(file.path().file_name(), None); - smol::block_on(file.save(buffer.read(ctx).snapshot(), ctx.as_ref())).unwrap(); - }); + let buffer = + app.add_model(|ctx| Buffer::new(1, "a line of text.\n".repeat(10 * 1024), ctx)); - let history = app.read(|ctx| file.load_history(ctx)).await.unwrap(); - app.read(|ctx| assert_eq!(history.base_text.as_ref(), buffer.read(ctx).text())); + let file = app.read(|ctx| tree.file("", ctx)); + app.update(|ctx| { + assert_eq!(file.path().file_name(), None); + smol::block_on(file.save(buffer.read(ctx).snapshot(), ctx.as_ref())).unwrap(); }); + + let history = app.read(|ctx| file.load_history(ctx)).await.unwrap(); + app.read(|ctx| assert_eq!(history.base_text.as_ref(), buffer.read(ctx).text())); } - #[test] - fn test_rescan_simple() { - App::test_async((), |mut app| async move { - let dir = temp_tree(json!({ - "a": { - "file1": "", - "file2": "", - "file3": "", - }, - "b": { - "c": { - "file4": "", - "file5": "", - } + #[gpui::test] + async fn test_rescan_simple(mut app: gpui::TestAppContext) { + let dir = temp_tree(json!({ + "a": { + "file1": "", + "file2": "", + "file3": "", + }, + "b": { + "c": { + "file4": "", + "file5": "", } - })); - - let tree = app.add_model(|ctx| Worktree::new(dir.path(), ctx)); - let (file2, file3, file4, file5, non_existent_file) = app.read(|ctx| { - ( - tree.file("a/file2", ctx), - tree.file("a/file3", ctx), - tree.file("b/c/file4", ctx), - tree.file("b/c/file5", ctx), - tree.file("a/filex", ctx), - ) - }); + } + })); + + let tree = app.add_model(|ctx| Worktree::new(dir.path(), ctx)); + let (file2, file3, file4, file5, non_existent_file) = app.read(|ctx| { + ( + tree.file("a/file2", ctx), + tree.file("a/file3", ctx), + tree.file("b/c/file4", ctx), + tree.file("b/c/file5", ctx), + tree.file("a/filex", ctx), + ) + }); - // The worktree hasn't scanned the directories containing these paths, - // so it can't determine that the paths are deleted. - assert!(!file2.is_deleted()); - assert!(!file3.is_deleted()); - assert!(!file4.is_deleted()); - assert!(!file5.is_deleted()); - assert!(!non_existent_file.is_deleted()); + // The worktree hasn't scanned the directories containing these paths, + // so it can't determine that the paths are deleted. + assert!(!file2.is_deleted()); + assert!(!file3.is_deleted()); + assert!(!file4.is_deleted()); + assert!(!file5.is_deleted()); + assert!(!non_existent_file.is_deleted()); + + // After scanning, the worktree knows which files exist and which don't. + app.read(|ctx| tree.read(ctx).scan_complete()).await; + assert!(!file2.is_deleted()); + assert!(!file3.is_deleted()); + assert!(!file4.is_deleted()); + assert!(!file5.is_deleted()); + assert!(non_existent_file.is_deleted()); + + tree.flush_fs_events(&app).await; + std::fs::rename(dir.path().join("a/file3"), dir.path().join("b/c/file3")).unwrap(); + std::fs::remove_file(dir.path().join("b/c/file5")).unwrap(); + std::fs::rename(dir.path().join("b/c"), dir.path().join("d")).unwrap(); + std::fs::rename(dir.path().join("a/file2"), dir.path().join("a/file2.new")).unwrap(); + tree.update(&mut app, |tree, ctx| tree.next_scan_complete(ctx)) + .await; - // After scanning, the worktree knows which files exist and which don't. - app.read(|ctx| tree.read(ctx).scan_complete()).await; + app.read(|ctx| { + assert_eq!( + tree.read(ctx) + .paths() + .map(|p| p.to_str().unwrap()) + .collect::>(), + vec![ + "a", + "a/file1", + "a/file2.new", + "b", + "d", + "d/file3", + "d/file4" + ] + ); + + assert_eq!(file2.path().to_str().unwrap(), "a/file2.new"); + assert_eq!(file4.path().as_ref(), Path::new("d/file4")); + assert_eq!(file5.path().as_ref(), Path::new("d/file5")); assert!(!file2.is_deleted()); - assert!(!file3.is_deleted()); assert!(!file4.is_deleted()); - assert!(!file5.is_deleted()); - assert!(non_existent_file.is_deleted()); - - tree.flush_fs_events(&app).await; - std::fs::rename(dir.path().join("a/file3"), dir.path().join("b/c/file3")).unwrap(); - std::fs::remove_file(dir.path().join("b/c/file5")).unwrap(); - std::fs::rename(dir.path().join("b/c"), dir.path().join("d")).unwrap(); - std::fs::rename(dir.path().join("a/file2"), dir.path().join("a/file2.new")).unwrap(); - tree.update(&mut app, |tree, ctx| tree.next_scan_complete(ctx)) - .await; - - app.read(|ctx| { - assert_eq!( - tree.read(ctx) - .paths() - .map(|p| p.to_str().unwrap()) - .collect::>(), - vec![ - "a", - "a/file1", - "a/file2.new", - "b", - "d", - "d/file3", - "d/file4" - ] - ); + assert!(file5.is_deleted()); - assert_eq!(file2.path().to_str().unwrap(), "a/file2.new"); - assert_eq!(file4.path().as_ref(), Path::new("d/file4")); - assert_eq!(file5.path().as_ref(), Path::new("d/file5")); - assert!(!file2.is_deleted()); - assert!(!file4.is_deleted()); - assert!(file5.is_deleted()); - - // Right now, this rename isn't detected because the target path - // no longer exists on the file system by the time we process the - // rename event. - assert_eq!(file3.path().as_ref(), Path::new("a/file3")); - assert!(file3.is_deleted()); - }); + // Right now, this rename isn't detected because the target path + // no longer exists on the file system by the time we process the + // rename event. + assert_eq!(file3.path().as_ref(), Path::new("a/file3")); + assert!(file3.is_deleted()); }); } - #[test] - fn test_rescan_with_gitignore() { - App::test_async((), |mut app| async move { - let dir = temp_tree(json!({ - ".git": {}, - ".gitignore": "ignored-dir\n", - "tracked-dir": { - "tracked-file1": "tracked contents", - }, - "ignored-dir": { - "ignored-file1": "ignored contents", - } - })); - - let tree = app.add_model(|ctx| Worktree::new(dir.path(), ctx)); - app.read(|ctx| tree.read(ctx).scan_complete()).await; - tree.flush_fs_events(&app).await; - app.read(|ctx| { - let tree = tree.read(ctx); - let tracked = tree.entry_for_path("tracked-dir/tracked-file1").unwrap(); - let ignored = tree.entry_for_path("ignored-dir/ignored-file1").unwrap(); - assert_eq!(tracked.is_ignored(), false); - assert_eq!(ignored.is_ignored(), true); - }); + #[gpui::test] + async fn test_rescan_with_gitignore(mut app: gpui::TestAppContext) { + let dir = temp_tree(json!({ + ".git": {}, + ".gitignore": "ignored-dir\n", + "tracked-dir": { + "tracked-file1": "tracked contents", + }, + "ignored-dir": { + "ignored-file1": "ignored contents", + } + })); + + let tree = app.add_model(|ctx| Worktree::new(dir.path(), ctx)); + app.read(|ctx| tree.read(ctx).scan_complete()).await; + tree.flush_fs_events(&app).await; + app.read(|ctx| { + let tree = tree.read(ctx); + let tracked = tree.entry_for_path("tracked-dir/tracked-file1").unwrap(); + let ignored = tree.entry_for_path("ignored-dir/ignored-file1").unwrap(); + assert_eq!(tracked.is_ignored(), false); + assert_eq!(ignored.is_ignored(), true); + }); - fs::write(dir.path().join("tracked-dir/tracked-file2"), "").unwrap(); - fs::write(dir.path().join("ignored-dir/ignored-file2"), "").unwrap(); - tree.update(&mut app, |tree, ctx| tree.next_scan_complete(ctx)) - .await; - app.read(|ctx| { - let tree = tree.read(ctx); - let dot_git = tree.entry_for_path(".git").unwrap(); - let tracked = tree.entry_for_path("tracked-dir/tracked-file2").unwrap(); - let ignored = tree.entry_for_path("ignored-dir/ignored-file2").unwrap(); - assert_eq!(tracked.is_ignored(), false); - assert_eq!(ignored.is_ignored(), true); - assert_eq!(dot_git.is_ignored(), true); - }); + fs::write(dir.path().join("tracked-dir/tracked-file2"), "").unwrap(); + fs::write(dir.path().join("ignored-dir/ignored-file2"), "").unwrap(); + tree.update(&mut app, |tree, ctx| tree.next_scan_complete(ctx)) + .await; + app.read(|ctx| { + let tree = tree.read(ctx); + let dot_git = tree.entry_for_path(".git").unwrap(); + let tracked = tree.entry_for_path("tracked-dir/tracked-file2").unwrap(); + let ignored = tree.entry_for_path("ignored-dir/ignored-file2").unwrap(); + assert_eq!(tracked.is_ignored(), false); + assert_eq!(ignored.is_ignored(), true); + assert_eq!(dot_git.is_ignored(), true); }); }