diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index be59a1a16f80809784fa23330dc593dbe9a37459..601eb9512cdef472ce0a5d660309d671c339ebe9 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -1,5 +1,5 @@ use crate::{ - AnchorRangeExt, DisplayPoint, Editor, MultiBuffer, RowExt, + AnchorRangeExt, DisplayPoint, Editor, ExcerptId, MultiBuffer, MultiBufferSnapshot, RowExt, display_map::{HighlightKey, ToDisplayPoint}, }; use buffer_diff::DiffHunkStatusKind; @@ -24,6 +24,7 @@ use std::{ atomic::{AtomicUsize, Ordering}, }, }; +use text::Selection; use util::{ assert_set_eq, test::{generate_marked_text, marked_text_ranges}, @@ -388,6 +389,23 @@ impl EditorTestContext { #[track_caller] pub fn assert_excerpts_with_selections(&mut self, marked_text: &str) { + let actual_text = self.to_format_multibuffer_as_marked_text(); + let fmt_additional_notes = || { + struct Format<'a, T: std::fmt::Display>(&'a str, &'a T); + + impl std::fmt::Display for Format<'_, T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "\n\n----- EXPECTED: -----\n\n{}\n\n----- ACTUAL: -----\n\n{}\n\n", + self.0, self.1 + ) + } + } + + Format(marked_text, &actual_text) + }; + let expected_excerpts = marked_text .strip_prefix("[EXCERPT]\n") .unwrap() @@ -408,9 +426,10 @@ impl EditorTestContext { assert!( excerpts.len() == expected_excerpts.len(), - "should have {} excerpts, got {}", + "should have {} excerpts, got {}{}", expected_excerpts.len(), - excerpts.len() + excerpts.len(), + fmt_additional_notes(), ); for (ix, (excerpt_id, snapshot, range)) in excerpts.into_iter().enumerate() { @@ -424,18 +443,25 @@ impl EditorTestContext { if !expected_selections.is_empty() { assert!( is_selected, - "excerpt {ix} should be selected. got {:?}", + "excerpt {ix} should contain selections. got {:?}{}", self.editor_state(), + fmt_additional_notes(), ); } else { assert!( !is_selected, - "excerpt {ix} should not be selected, got: {selections:?}", + "excerpt {ix} should not contain selections, got: {selections:?}{}", + fmt_additional_notes(), ); } continue; } - assert!(!is_folded, "excerpt {} should not be folded", ix); + assert!( + !is_folded, + "excerpt {} should not be folded{}", + ix, + fmt_additional_notes() + ); assert_eq!( multibuffer_snapshot .text_for_range(Anchor::range_in_buffer( @@ -444,7 +470,9 @@ impl EditorTestContext { range.context.clone() )) .collect::(), - expected_text + expected_text, + "{}", + fmt_additional_notes(), ); let selections = selections @@ -460,13 +488,38 @@ impl EditorTestContext { .collect::>(); // todo: selections that cross excerpt boundaries.. assert_eq!( - selections, expected_selections, - "excerpt {} has incorrect selections", + selections, + expected_selections, + "excerpt {} has incorrect selections{}", ix, + fmt_additional_notes() ); } } + fn to_format_multibuffer_as_marked_text(&mut self) -> FormatMultiBufferAsMarkedText { + let (multibuffer_snapshot, selections, excerpts) = self.update_editor(|editor, _, cx| { + let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx); + + let selections = editor.selections.disjoint_anchors_arc().to_vec(); + let excerpts = multibuffer_snapshot + .excerpts() + .map(|(e_id, snapshot, range)| { + let is_folded = editor.is_buffer_folded(snapshot.remote_id(), cx); + (e_id, snapshot.clone(), range, is_folded) + }) + .collect::>(); + + (multibuffer_snapshot, selections, excerpts) + }); + + FormatMultiBufferAsMarkedText { + multibuffer_snapshot, + selections, + excerpts, + } + } + /// Make an assertion about the editor's text and the ranges and directions /// of its selections using a string containing embedded range markers. /// @@ -571,6 +624,63 @@ impl EditorTestContext { } } +struct FormatMultiBufferAsMarkedText { + multibuffer_snapshot: MultiBufferSnapshot, + selections: Vec>, + excerpts: Vec<(ExcerptId, BufferSnapshot, ExcerptRange, bool)>, +} + +impl std::fmt::Display for FormatMultiBufferAsMarkedText { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { + multibuffer_snapshot, + selections, + excerpts, + } = self; + + for (excerpt_id, snapshot, range, is_folded) in excerpts.into_iter() { + write!(f, "[EXCERPT]\n")?; + if *is_folded { + write!(f, "[FOLDED]\n")?; + } + + let mut text = multibuffer_snapshot + .text_for_range(Anchor::range_in_buffer( + *excerpt_id, + snapshot.remote_id(), + range.context.clone(), + )) + .collect::(); + + let selections = selections + .iter() + .filter(|&s| s.head().excerpt_id == *excerpt_id) + .map(|s| { + let head = text::ToOffset::to_offset(&s.head().text_anchor, &snapshot) + - text::ToOffset::to_offset(&range.context.start, &snapshot); + let tail = text::ToOffset::to_offset(&s.head().text_anchor, &snapshot) + - text::ToOffset::to_offset(&range.context.start, &snapshot); + tail..head + }) + .rev() + .collect::>(); + + for selection in selections { + if selection.is_empty() { + text.insert(selection.start, 'ˇ'); + continue; + } + text.insert(selection.end, '»'); + text.insert(selection.start, '«'); + } + + write!(f, "{text}")?; + } + + Ok(()) + } +} + #[track_caller] pub fn assert_state_with_diff( editor: &Entity, diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs index 6b70f1975e8f361b04fb2ce2eb4966b5da968936..e0eda7d8bef40ae622eb03b2df981d28d61de5d0 100644 --- a/crates/git_ui/src/project_diff.rs +++ b/crates/git_ui/src/project_diff.rs @@ -1619,14 +1619,13 @@ mod tests { project_diff::{self, ProjectDiff}, }; - #[cfg_attr(windows, ignore = "currently fails on windows")] #[gpui::test] async fn test_go_to_prev_hunk_multibuffer(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/a", + path!("/a"), json!({ ".git": {}, "a.txt": "created\n", @@ -1637,7 +1636,7 @@ mod tests { .await; fs.set_head_and_index_for_repo( - Path::new("/a/.git"), + Path::new(path!("/a/.git")), &[ ("b.txt", "before\n".to_string()), ("c.txt", "unchanged\n".to_string()), @@ -1645,7 +1644,7 @@ mod tests { ], ); - let project = Project::test(fs, [Path::new("/a")], cx).await; + let project = Project::test(fs, [Path::new(path!("/a"))], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); @@ -1707,7 +1706,6 @@ mod tests { )); } - #[cfg_attr(windows, ignore = "currently fails on windows")] #[gpui::test] async fn test_excerpts_splitting_after_restoring_the_middle_excerpt(cx: &mut TestAppContext) { init_test(cx); @@ -1747,7 +1745,7 @@ mod tests { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/a", + path!("/a"), json!({ ".git": {}, "main.rs": buffer_contents, @@ -1756,11 +1754,11 @@ mod tests { .await; fs.set_head_and_index_for_repo( - Path::new("/a/.git"), + Path::new(path!("/a/.git")), &[("main.rs", git_contents.to_owned())], ); - let project = Project::test(fs, [Path::new("/a")], cx).await; + let project = Project::test(fs, [Path::new(path!("/a"))], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); @@ -1925,6 +1923,7 @@ mod tests { cx.run_until_parked(); let editor = diff.read_with(cx, |diff, _| diff.editor.clone()); + assert_state_with_diff( &editor, cx, diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 14bdc18fbf3f0956267fb7452b017e6a80369e39..626a2793d78a578931094a39b2ecb2f6c3df3489 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -94,6 +94,9 @@ async fn test_block_via_smol(cx: &mut gpui::TestAppContext) { task.await; } +// NOTE: +// While POSIX symbolic links are somewhat supported on Windows, they are an opt in by the user, and thus +// we assume that they are not supported out of the box. #[cfg(not(windows))] #[gpui::test] async fn test_symlinks(cx: &mut gpui::TestAppContext) { @@ -8536,6 +8539,7 @@ async fn test_update_gitignore(cx: &mut gpui::TestAppContext) { // a directory which some program has already open. // This is a limitation of the Windows. // See: https://stackoverflow.com/questions/41365318/access-is-denied-when-renaming-folder +// See: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_rename_information #[gpui::test] #[cfg_attr(target_os = "windows", ignore)] async fn test_rename_work_directory(cx: &mut gpui::TestAppContext) { @@ -8615,7 +8619,8 @@ async fn test_rename_work_directory(cx: &mut gpui::TestAppContext) { // NOTE: This test always fails on Windows, because on Windows, unlike on Unix, // you can't rename a directory which some program has already open. This is a // limitation of the Windows. See: -// https://stackoverflow.com/questions/41365318/access-is-denied-when-renaming-folder +// See: https://stackoverflow.com/questions/41365318/access-is-denied-when-renaming-folder +// See: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_rename_information #[gpui::test] #[cfg_attr(target_os = "windows", ignore)] async fn test_file_status(cx: &mut gpui::TestAppContext) { diff --git a/crates/project_panel/src/project_panel_tests.rs b/crates/project_panel/src/project_panel_tests.rs index d7dc63dec892a9d90c3fe7707b24652cc26cfce1..3f1e1e1b3c3fd909f667ebf9dc8e717b0d116c78 100644 --- a/crates/project_panel/src/project_panel_tests.rs +++ b/crates/project_panel/src/project_panel_tests.rs @@ -2983,6 +2983,12 @@ async fn test_new_file_move(cx: &mut gpui::TestAppContext) { ); } +// NOTE: This test is skipped on Windows, because on Windows, unlike on Unix, +// you can't rename a directory which some program has already open. This is a +// limitation of the Windows. Since Zed will have the root open, it will hold an open handle +// to it, and thus renaming it will fail on Windows. +// See: https://stackoverflow.com/questions/41365318/access-is-denied-when-renaming-folder +// See: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_rename_information #[gpui::test] #[cfg_attr(target_os = "windows", ignore)] async fn test_rename_root_of_worktree(cx: &mut gpui::TestAppContext) { diff --git a/crates/remote_server/src/remote_editing_tests.rs b/crates/remote_server/src/remote_editing_tests.rs index fc21e7548272b56f2c482ec2e2843f812c2b11bc..f6cddc65688a35b6ed67bfaa13bccb1ff5bde2c2 100644 --- a/crates/remote_server/src/remote_editing_tests.rs +++ b/crates/remote_server/src/remote_editing_tests.rs @@ -1327,8 +1327,6 @@ async fn test_copy_file_into_remote_project( ); } -// TODO: this test fails on Windows. -#[cfg(not(windows))] #[gpui::test] async fn test_remote_git_diffs(cx: &mut TestAppContext, server_cx: &mut TestAppContext) { let text_2 = "