2582 lines · 75.4 KB · Rust
RawDownload
use crate::{    worktree_settings::WorktreeSettings, Entry, EntryKind, Event, PathChange, Snapshot, Worktree,    WorktreeModelHandle,};use anyhow::Result;use client::Client;use clock::FakeSystemClock;use fs::{repository::GitFileStatus, FakeFs, Fs, RealFs, RemoveOptions};use git::GITIGNORE;use gpui::{ModelContext, Task, TestAppContext};use parking_lot::Mutex;use postage::stream::Stream;use pretty_assertions::assert_eq;use rand::prelude::*;use serde_json::json;use settings::{Settings, SettingsStore};use std::{    env,    fmt::Write,    mem,    path::{Path, PathBuf},    sync::Arc,};use text::BufferId;use util::{http::FakeHttpClient, test::temp_tree, ResultExt}; #[gpui::test]async fn test_traversal(cx: &mut TestAppContext) {    init_test(cx);    let fs = FakeFs::new(cx.background_executor.clone());    fs.insert_tree(        "/root",        json!({           ".gitignore": "a/b\n",           "a": {               "b": "",               "c": "",           }        }),    )    .await;     let tree = Worktree::local(        build_client(cx),        Path::new("/root"),        true,        fs,        Default::default(),        &mut cx.to_async(),    )    .await    .unwrap();    cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())        .await;     tree.read_with(cx, |tree, _| {        assert_eq!(            tree.entries(false)                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            vec![                Path::new(""),                Path::new(".gitignore"),                Path::new("a"),                Path::new("a/c"),            ]        );        assert_eq!(            tree.entries(true)                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            vec![                Path::new(""),                Path::new(".gitignore"),                Path::new("a"),                Path::new("a/b"),                Path::new("a/c"),            ]        );    })} #[gpui::test]async fn test_descendent_entries(cx: &mut TestAppContext) {    init_test(cx);    let fs = FakeFs::new(cx.background_executor.clone());    fs.insert_tree(        "/root",        json!({            "a": "",            "b": {               "c": {                   "d": ""               },               "e": {}            },            "f": "",            "g": {                "h": {}            },            "i": {                "j": {                    "k": ""                },                "l": {                 }            },            ".gitignore": "i/j\n",        }),    )    .await;     let tree = Worktree::local(        build_client(cx),        Path::new("/root"),        true,        fs,        Default::default(),        &mut cx.to_async(),    )    .await    .unwrap();    cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())        .await;     tree.read_with(cx, |tree, _| {        assert_eq!(            tree.descendent_entries(false, false, Path::new("b"))                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            vec![Path::new("b/c/d"),]        );        assert_eq!(            tree.descendent_entries(true, false, Path::new("b"))                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            vec![                Path::new("b"),                Path::new("b/c"),                Path::new("b/c/d"),                Path::new("b/e"),            ]        );         assert_eq!(            tree.descendent_entries(false, false, Path::new("g"))                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            Vec::<PathBuf>::new()        );        assert_eq!(            tree.descendent_entries(true, false, Path::new("g"))                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            vec![Path::new("g"), Path::new("g/h"),]        );    });     // Expand gitignored directory.    tree.read_with(cx, |tree, _| {        tree.as_local()            .unwrap()            .refresh_entries_for_paths(vec![Path::new("i/j").into()])    })    .recv()    .await;     tree.read_with(cx, |tree, _| {        assert_eq!(            tree.descendent_entries(false, false, Path::new("i"))                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            Vec::<PathBuf>::new()        );        assert_eq!(            tree.descendent_entries(false, true, Path::new("i"))                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            vec![Path::new("i/j/k")]        );        assert_eq!(            tree.descendent_entries(true, false, Path::new("i"))                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            vec![Path::new("i"), Path::new("i/l"),]        );    })} #[gpui::test(iterations = 10)]async fn test_circular_symlinks(cx: &mut TestAppContext) {    init_test(cx);    let fs = FakeFs::new(cx.background_executor.clone());    fs.insert_tree(        "/root",        json!({            "lib": {                "a": {                    "a.txt": ""                },                "b": {                    "b.txt": ""                }            }        }),    )    .await;    fs.create_symlink("/root/lib/a/lib".as_ref(), "..".into())        .await        .unwrap();    fs.create_symlink("/root/lib/b/lib".as_ref(), "..".into())        .await        .unwrap();     let tree = Worktree::local(        build_client(cx),        Path::new("/root"),        true,        fs.clone(),        Default::default(),        &mut cx.to_async(),    )    .await    .unwrap();     cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())        .await;     tree.read_with(cx, |tree, _| {        assert_eq!(            tree.entries(false)                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            vec![                Path::new(""),                Path::new("lib"),                Path::new("lib/a"),                Path::new("lib/a/a.txt"),                Path::new("lib/a/lib"),                Path::new("lib/b"),                Path::new("lib/b/b.txt"),                Path::new("lib/b/lib"),            ]        );    });     fs.rename(        Path::new("/root/lib/a/lib"),        Path::new("/root/lib/a/lib-2"),        Default::default(),    )    .await    .unwrap();    cx.executor().run_until_parked();    tree.read_with(cx, |tree, _| {        assert_eq!(            tree.entries(false)                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            vec![                Path::new(""),                Path::new("lib"),                Path::new("lib/a"),                Path::new("lib/a/a.txt"),                Path::new("lib/a/lib-2"),                Path::new("lib/b"),                Path::new("lib/b/b.txt"),                Path::new("lib/b/lib"),            ]        );    });} #[gpui::test]async fn test_symlinks_pointing_outside(cx: &mut TestAppContext) {    init_test(cx);    let fs = FakeFs::new(cx.background_executor.clone());    fs.insert_tree(        "/root",        json!({            "dir1": {                "deps": {                    // symlinks here                },                "src": {                    "a.rs": "",                    "b.rs": "",                },            },            "dir2": {                "src": {                    "c.rs": "",                    "d.rs": "",                }            },            "dir3": {                "deps": {},                "src": {                    "e.rs": "",                    "f.rs": "",                },            }        }),    )    .await;     // These symlinks point to directories outside of the worktree's root, dir1.    fs.create_symlink("/root/dir1/deps/dep-dir2".as_ref(), "../../dir2".into())        .await        .unwrap();    fs.create_symlink("/root/dir1/deps/dep-dir3".as_ref(), "../../dir3".into())        .await        .unwrap();     let tree = Worktree::local(        build_client(cx),        Path::new("/root/dir1"),        true,        fs.clone(),        Default::default(),        &mut cx.to_async(),    )    .await    .unwrap();     cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())        .await;     let tree_updates = Arc::new(Mutex::new(Vec::new()));    tree.update(cx, |_, cx| {        let tree_updates = tree_updates.clone();        cx.subscribe(&tree, move |_, _, event, _| {            if let Event::UpdatedEntries(update) = event {                tree_updates.lock().extend(                    update                        .iter()                        .map(|(path, _, change)| (path.clone(), *change)),                );            }        })        .detach();    });     // The symlinked directories are not scanned by default.    tree.read_with(cx, |tree, _| {        assert_eq!(            tree.entries(true)                .map(|entry| (entry.path.as_ref(), entry.is_external))                .collect::<Vec<_>>(),            vec![                (Path::new(""), false),                (Path::new("deps"), false),                (Path::new("deps/dep-dir2"), true),                (Path::new("deps/dep-dir3"), true),                (Path::new("src"), false),                (Path::new("src/a.rs"), false),                (Path::new("src/b.rs"), false),            ]        );         assert_eq!(            tree.entry_for_path("deps/dep-dir2").unwrap().kind,            EntryKind::UnloadedDir        );    });     // Expand one of the symlinked directories.    tree.read_with(cx, |tree, _| {        tree.as_local()            .unwrap()            .refresh_entries_for_paths(vec![Path::new("deps/dep-dir3").into()])    })    .recv()    .await;     // The expanded directory's contents are loaded. Subdirectories are    // not scanned yet.    tree.read_with(cx, |tree, _| {        assert_eq!(            tree.entries(true)                .map(|entry| (entry.path.as_ref(), entry.is_external))                .collect::<Vec<_>>(),            vec![                (Path::new(""), false),                (Path::new("deps"), false),                (Path::new("deps/dep-dir2"), true),                (Path::new("deps/dep-dir3"), true),                (Path::new("deps/dep-dir3/deps"), true),                (Path::new("deps/dep-dir3/src"), true),                (Path::new("src"), false),                (Path::new("src/a.rs"), false),                (Path::new("src/b.rs"), false),            ]        );    });    assert_eq!(        mem::take(&mut *tree_updates.lock()),        &[            (Path::new("deps/dep-dir3").into(), PathChange::Loaded),            (Path::new("deps/dep-dir3/deps").into(), PathChange::Loaded),            (Path::new("deps/dep-dir3/src").into(), PathChange::Loaded)        ]    );     // Expand a subdirectory of one of the symlinked directories.    tree.read_with(cx, |tree, _| {        tree.as_local()            .unwrap()            .refresh_entries_for_paths(vec![Path::new("deps/dep-dir3/src").into()])    })    .recv()    .await;     // The expanded subdirectory's contents are loaded.    tree.read_with(cx, |tree, _| {        assert_eq!(            tree.entries(true)                .map(|entry| (entry.path.as_ref(), entry.is_external))                .collect::<Vec<_>>(),            vec![                (Path::new(""), false),                (Path::new("deps"), false),                (Path::new("deps/dep-dir2"), true),                (Path::new("deps/dep-dir3"), true),                (Path::new("deps/dep-dir3/deps"), true),                (Path::new("deps/dep-dir3/src"), true),                (Path::new("deps/dep-dir3/src/e.rs"), true),                (Path::new("deps/dep-dir3/src/f.rs"), true),                (Path::new("src"), false),                (Path::new("src/a.rs"), false),                (Path::new("src/b.rs"), false),            ]        );    });     assert_eq!(        mem::take(&mut *tree_updates.lock()),        &[            (Path::new("deps/dep-dir3/src").into(), PathChange::Loaded),            (                Path::new("deps/dep-dir3/src/e.rs").into(),                PathChange::Loaded            ),            (                Path::new("deps/dep-dir3/src/f.rs").into(),                PathChange::Loaded            )        ]    );} #[cfg(target_os = "macos")]#[gpui::test]async fn test_renaming_case_only(cx: &mut TestAppContext) {    cx.executor().allow_parking();    init_test(cx);     const OLD_NAME: &str = "aaa.rs";    const NEW_NAME: &str = "AAA.rs";     let fs = Arc::new(RealFs);    let temp_root = temp_tree(json!({        OLD_NAME: "",    }));     let tree = Worktree::local(        build_client(cx),        temp_root.path(),        true,        fs.clone(),        Default::default(),        &mut cx.to_async(),    )    .await    .unwrap();     cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())        .await;    tree.read_with(cx, |tree, _| {        assert_eq!(            tree.entries(true)                .map(|entry| entry.path.as_ref())                .collect::<Vec<_>>(),            vec![Path::new(""), Path::new(OLD_NAME)]        );    });     fs.rename(        &temp_root.path().join(OLD_NAME),        &temp_root.path().join(NEW_NAME),        fs::RenameOptions {            overwrite: true,            ignore_if_exists: true,        },    )    .await    .unwrap();     tree.flush_fs_events(cx).await;

Lines 1–500 of 2582.