windows: Fix more windows platform test (#19802)

CharlesChen0823 and Kirill Bulatov created

Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>

Change summary

crates/editor/src/test/editor_test_context.rs | 18 ++++++++++++++--
crates/fs/src/fs.rs                           | 22 +++++++++++++-------
2 files changed, 29 insertions(+), 11 deletions(-)

Detailed changes

crates/editor/src/test/editor_test_context.rs 🔗

@@ -17,6 +17,7 @@ use project::{FakeFs, Project};
 use std::{
     any::TypeId,
     ops::{Deref, DerefMut, Range},
+    path::Path,
     sync::{
         atomic::{AtomicUsize, Ordering},
         Arc,
@@ -42,17 +43,18 @@ impl EditorTestContext {
     pub async fn new(cx: &mut gpui::TestAppContext) -> EditorTestContext {
         let fs = FakeFs::new(cx.executor());
         // fs.insert_file("/file", "".to_owned()).await;
+        let root = Self::root_path();
         fs.insert_tree(
-            "/root",
+            root,
             serde_json::json!({
                 "file": "",
             }),
         )
         .await;
-        let project = Project::test(fs, ["/root".as_ref()], cx).await;
+        let project = Project::test(fs, [root], cx).await;
         let buffer = project
             .update(cx, |project, cx| {
-                project.open_local_buffer("/root/file", cx)
+                project.open_local_buffer(root.join("file"), cx)
             })
             .await
             .unwrap();
@@ -71,6 +73,16 @@ impl EditorTestContext {
         }
     }
 
+    #[cfg(target_os = "windows")]
+    fn root_path() -> &'static Path {
+        Path::new("C:\\root")
+    }
+
+    #[cfg(not(target_os = "windows"))]
+    fn root_path() -> &'static Path {
+        Path::new("/root")
+    }
+
     pub async fn for_editor(editor: WindowHandle<Editor>, cx: &mut gpui::TestAppContext) -> Self {
         let editor_view = editor.root_view(cx).unwrap();
         Self {

crates/fs/src/fs.rs 🔗

@@ -875,9 +875,11 @@ impl FakeFsState {
                         canonical_path.clear();
                         match prefix {
                             Some(prefix_component) => {
-                                canonical_path.push(prefix_component.as_os_str());
+                                canonical_path = PathBuf::from(prefix_component.as_os_str());
+                                // Prefixes like `C:\\` are represented without their trailing slash, so we have to re-add it.
+                                canonical_path.push(std::path::MAIN_SEPARATOR_STR);
                             }
-                            None => canonical_path.push("/"),
+                            None => canonical_path = PathBuf::from(std::path::MAIN_SEPARATOR_STR),
                         }
                     }
                     Component::CurDir => {}
@@ -900,7 +902,7 @@ impl FakeFsState {
                                 }
                             }
                             entry_stack.push(entry.clone());
-                            canonical_path.push(name);
+                            canonical_path = canonical_path.join(name);
                         } else {
                             return None;
                         }
@@ -962,6 +964,10 @@ pub static FS_DOT_GIT: std::sync::LazyLock<&'static OsStr> =
 
 #[cfg(any(test, feature = "test-support"))]
 impl FakeFs {
+    /// We need to use something large enough for Windows and Unix to consider this a new file.
+    /// https://doc.rust-lang.org/nightly/std/time/struct.SystemTime.html#platform-specific-behavior
+    const SYSTEMTIME_INTERVAL: u64 = 100;
+
     pub fn new(executor: gpui::BackgroundExecutor) -> Arc<Self> {
         Arc::new(Self {
             executor,
@@ -995,7 +1001,7 @@ impl FakeFs {
         let new_mtime = state.next_mtime;
         let new_inode = state.next_inode;
         state.next_inode += 1;
-        state.next_mtime += Duration::from_nanos(1);
+        state.next_mtime += Duration::from_nanos(Self::SYSTEMTIME_INTERVAL);
         state
             .write_path(path, move |entry| {
                 match entry {
@@ -1048,7 +1054,7 @@ impl FakeFs {
         let inode = state.next_inode;
         let mtime = state.next_mtime;
         state.next_inode += 1;
-        state.next_mtime += Duration::from_nanos(1);
+        state.next_mtime += Duration::from_nanos(Self::SYSTEMTIME_INTERVAL);
         let file = Arc::new(Mutex::new(FakeFsEntry::File {
             inode,
             mtime,
@@ -1399,7 +1405,7 @@ impl Fs for FakeFs {
 
             let inode = state.next_inode;
             let mtime = state.next_mtime;
-            state.next_mtime += Duration::from_nanos(1);
+            state.next_mtime += Duration::from_nanos(Self::SYSTEMTIME_INTERVAL);
             state.next_inode += 1;
             state.write_path(&cur_path, |entry| {
                 entry.or_insert_with(|| {
@@ -1425,7 +1431,7 @@ impl Fs for FakeFs {
         let mut state = self.state.lock();
         let inode = state.next_inode;
         let mtime = state.next_mtime;
-        state.next_mtime += Duration::from_nanos(1);
+        state.next_mtime += Duration::from_nanos(Self::SYSTEMTIME_INTERVAL);
         state.next_inode += 1;
         let file = Arc::new(Mutex::new(FakeFsEntry::File {
             inode,
@@ -1560,7 +1566,7 @@ impl Fs for FakeFs {
         let mut state = self.state.lock();
         let mtime = state.next_mtime;
         let inode = util::post_inc(&mut state.next_inode);
-        state.next_mtime += Duration::from_nanos(1);
+        state.next_mtime += Duration::from_nanos(Self::SYSTEMTIME_INTERVAL);
         let source_entry = state.read_path(&source)?;
         let content = source_entry.lock().file_content(&source)?.clone();
         let mut kind = Some(PathEventKind::Created);