@@ -1337,7 +1337,10 @@ impl FakeFs {
pub fn paths(&self, include_dot_git: bool) -> Vec<PathBuf> {
let mut result = Vec::new();
let mut queue = collections::VecDeque::new();
- queue.push_back((PathBuf::from("/"), self.state.lock().root.clone()));
+ queue.push_back((
+ PathBuf::from(util::path!("/")),
+ self.state.lock().root.clone(),
+ ));
while let Some((path, entry)) = queue.pop_front() {
if let FakeFsEntry::Dir { entries, .. } = &*entry.lock() {
for (name, entry) in entries {
@@ -1358,7 +1361,10 @@ impl FakeFs {
pub fn directories(&self, include_dot_git: bool) -> Vec<PathBuf> {
let mut result = Vec::new();
let mut queue = collections::VecDeque::new();
- queue.push_back((PathBuf::from("/"), self.state.lock().root.clone()));
+ queue.push_back((
+ PathBuf::from(util::path!("/")),
+ self.state.lock().root.clone(),
+ ));
while let Some((path, entry)) = queue.pop_front() {
if let FakeFsEntry::Dir { entries, .. } = &*entry.lock() {
for (name, entry) in entries {
@@ -2020,7 +2026,11 @@ pub async fn copy_recursive<'a>(
let Ok(item_relative_path) = item.strip_prefix(source) else {
continue;
};
- let target_item = target.join(item_relative_path);
+ let target_item = if item_relative_path == Path::new("") {
+ target.to_path_buf()
+ } else {
+ target.join(item_relative_path)
+ };
if is_dir {
if !options.overwrite && fs.metadata(&target_item).await.is_ok_and(|m| m.is_some()) {
if options.ignore_if_exists {
@@ -2174,6 +2184,142 @@ mod tests {
);
}
+ #[gpui::test]
+ async fn test_copy_recursive_with_single_file(executor: BackgroundExecutor) {
+ let fs = FakeFs::new(executor.clone());
+ fs.insert_tree(
+ path!("/outer"),
+ json!({
+ "a": "A",
+ "b": "B",
+ "inner": {}
+ }),
+ )
+ .await;
+
+ assert_eq!(
+ fs.files(),
+ vec![
+ PathBuf::from(path!("/outer/a")),
+ PathBuf::from(path!("/outer/b")),
+ ]
+ );
+
+ let source = Path::new(path!("/outer/a"));
+ let target = Path::new(path!("/outer/a copy"));
+ copy_recursive(fs.as_ref(), source, target, Default::default())
+ .await
+ .unwrap();
+
+ assert_eq!(
+ fs.files(),
+ vec![
+ PathBuf::from(path!("/outer/a")),
+ PathBuf::from(path!("/outer/a copy")),
+ PathBuf::from(path!("/outer/b")),
+ ]
+ );
+
+ let source = Path::new(path!("/outer/a"));
+ let target = Path::new(path!("/outer/inner/a copy"));
+ copy_recursive(fs.as_ref(), source, target, Default::default())
+ .await
+ .unwrap();
+
+ assert_eq!(
+ fs.files(),
+ vec![
+ PathBuf::from(path!("/outer/a")),
+ PathBuf::from(path!("/outer/a copy")),
+ PathBuf::from(path!("/outer/b")),
+ PathBuf::from(path!("/outer/inner/a copy")),
+ ]
+ );
+ }
+
+ #[gpui::test]
+ async fn test_copy_recursive_with_single_dir(executor: BackgroundExecutor) {
+ let fs = FakeFs::new(executor.clone());
+ fs.insert_tree(
+ path!("/outer"),
+ json!({
+ "a": "A",
+ "empty": {},
+ "non-empty": {
+ "b": "B",
+ }
+ }),
+ )
+ .await;
+
+ assert_eq!(
+ fs.files(),
+ vec![
+ PathBuf::from(path!("/outer/a")),
+ PathBuf::from(path!("/outer/non-empty/b")),
+ ]
+ );
+ assert_eq!(
+ fs.directories(false),
+ vec![
+ PathBuf::from(path!("/")),
+ PathBuf::from(path!("/outer")),
+ PathBuf::from(path!("/outer/empty")),
+ PathBuf::from(path!("/outer/non-empty")),
+ ]
+ );
+
+ let source = Path::new(path!("/outer/empty"));
+ let target = Path::new(path!("/outer/empty copy"));
+ copy_recursive(fs.as_ref(), source, target, Default::default())
+ .await
+ .unwrap();
+
+ assert_eq!(
+ fs.files(),
+ vec![
+ PathBuf::from(path!("/outer/a")),
+ PathBuf::from(path!("/outer/non-empty/b")),
+ ]
+ );
+ assert_eq!(
+ fs.directories(false),
+ vec![
+ PathBuf::from(path!("/")),
+ PathBuf::from(path!("/outer")),
+ PathBuf::from(path!("/outer/empty")),
+ PathBuf::from(path!("/outer/empty copy")),
+ PathBuf::from(path!("/outer/non-empty")),
+ ]
+ );
+
+ let source = Path::new(path!("/outer/non-empty"));
+ let target = Path::new(path!("/outer/non-empty copy"));
+ copy_recursive(fs.as_ref(), source, target, Default::default())
+ .await
+ .unwrap();
+
+ assert_eq!(
+ fs.files(),
+ vec![
+ PathBuf::from(path!("/outer/a")),
+ PathBuf::from(path!("/outer/non-empty/b")),
+ PathBuf::from(path!("/outer/non-empty copy/b")),
+ ]
+ );
+ assert_eq!(
+ fs.directories(false),
+ vec![
+ PathBuf::from(path!("/")),
+ PathBuf::from(path!("/outer")),
+ PathBuf::from(path!("/outer/empty")),
+ PathBuf::from(path!("/outer/empty copy")),
+ PathBuf::from(path!("/outer/non-empty")),
+ PathBuf::from(path!("/outer/non-empty copy")),
+ ]
+ );
+ }
+
#[gpui::test]
async fn test_copy_recursive(executor: BackgroundExecutor) {
let fs = FakeFs::new(executor.clone());
@@ -2185,7 +2331,8 @@ mod tests {
"b": "B",
"inner3": {
"d": "D",
- }
+ },
+ "inner4": {}
},
"inner2": {
"c": "C",
@@ -2203,6 +2350,17 @@ mod tests {
PathBuf::from(path!("/outer/inner1/inner3/d")),
]
);
+ assert_eq!(
+ fs.directories(false),
+ vec![
+ PathBuf::from(path!("/")),
+ PathBuf::from(path!("/outer")),
+ PathBuf::from(path!("/outer/inner1")),
+ PathBuf::from(path!("/outer/inner2")),
+ PathBuf::from(path!("/outer/inner1/inner3")),
+ PathBuf::from(path!("/outer/inner1/inner4")),
+ ]
+ );
let source = Path::new(path!("/outer"));
let target = Path::new(path!("/outer/inner1/outer"));
@@ -2223,6 +2381,22 @@ mod tests {
PathBuf::from(path!("/outer/inner1/outer/inner1/inner3/d")),
]
);
+ assert_eq!(
+ fs.directories(false),
+ vec![
+ PathBuf::from(path!("/")),
+ PathBuf::from(path!("/outer")),
+ PathBuf::from(path!("/outer/inner1")),
+ PathBuf::from(path!("/outer/inner2")),
+ PathBuf::from(path!("/outer/inner1/inner3")),
+ PathBuf::from(path!("/outer/inner1/inner4")),
+ PathBuf::from(path!("/outer/inner1/outer")),
+ PathBuf::from(path!("/outer/inner1/outer/inner1")),
+ PathBuf::from(path!("/outer/inner1/outer/inner2")),
+ PathBuf::from(path!("/outer/inner1/outer/inner1/inner3")),
+ PathBuf::from(path!("/outer/inner1/outer/inner1/inner4")),
+ ]
+ );
}
#[gpui::test]
@@ -4958,14 +4958,14 @@ async fn test_create_entry(cx: &mut gpui::TestAppContext) {
assert_eq!(
fs.paths(true),
vec![
- PathBuf::from("/"),
- PathBuf::from("/one"),
- PathBuf::from("/one/two"),
- PathBuf::from("/one/two/c.rs"),
- PathBuf::from("/one/two/three"),
- PathBuf::from("/one/two/three/a.txt"),
- PathBuf::from("/one/two/three/b.."),
- PathBuf::from("/one/two/three/four"),
+ PathBuf::from(path!("/")),
+ PathBuf::from(path!("/one")),
+ PathBuf::from(path!("/one/two")),
+ PathBuf::from(path!("/one/two/c.rs")),
+ PathBuf::from(path!("/one/two/three")),
+ PathBuf::from(path!("/one/two/three/a.txt")),
+ PathBuf::from(path!("/one/two/three/b..")),
+ PathBuf::from(path!("/one/two/three/four")),
]
);