Update worktree randomized test to use worktree's public interface and the fake fs
This commit is contained in:
parent
be5868e1c0
commit
c730dca3c5
5 changed files with 191 additions and 159 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -4625,7 +4625,9 @@ dependencies = [
|
||||||
"client",
|
"client",
|
||||||
"clock",
|
"clock",
|
||||||
"collections",
|
"collections",
|
||||||
|
"ctor",
|
||||||
"db",
|
"db",
|
||||||
|
"env_logger",
|
||||||
"fs",
|
"fs",
|
||||||
"fsevent",
|
"fsevent",
|
||||||
"futures 0.3.25",
|
"futures 0.3.25",
|
||||||
|
|
|
@ -380,6 +380,8 @@ struct FakeFsState {
|
||||||
next_inode: u64,
|
next_inode: u64,
|
||||||
next_mtime: SystemTime,
|
next_mtime: SystemTime,
|
||||||
event_txs: Vec<smol::channel::Sender<Vec<fsevent::Event>>>,
|
event_txs: Vec<smol::channel::Sender<Vec<fsevent::Event>>>,
|
||||||
|
events_paused: bool,
|
||||||
|
buffered_events: Vec<fsevent::Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
@ -483,15 +485,21 @@ impl FakeFsState {
|
||||||
I: IntoIterator<Item = T>,
|
I: IntoIterator<Item = T>,
|
||||||
T: Into<PathBuf>,
|
T: Into<PathBuf>,
|
||||||
{
|
{
|
||||||
let events = paths
|
self.buffered_events
|
||||||
.into_iter()
|
.extend(paths.into_iter().map(|path| fsevent::Event {
|
||||||
.map(|path| fsevent::Event {
|
|
||||||
event_id: 0,
|
event_id: 0,
|
||||||
flags: fsevent::StreamFlags::empty(),
|
flags: fsevent::StreamFlags::empty(),
|
||||||
path: path.into(),
|
path: path.into(),
|
||||||
})
|
}));
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
|
if !self.events_paused {
|
||||||
|
self.flush_events(self.buffered_events.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_events(&mut self, mut count: usize) {
|
||||||
|
count = count.min(self.buffered_events.len());
|
||||||
|
let events = self.buffered_events.drain(0..count).collect::<Vec<_>>();
|
||||||
self.event_txs.retain(|tx| {
|
self.event_txs.retain(|tx| {
|
||||||
let _ = tx.try_send(events.clone());
|
let _ = tx.try_send(events.clone());
|
||||||
!tx.is_closed()
|
!tx.is_closed()
|
||||||
|
@ -514,6 +522,8 @@ impl FakeFs {
|
||||||
next_mtime: SystemTime::UNIX_EPOCH,
|
next_mtime: SystemTime::UNIX_EPOCH,
|
||||||
next_inode: 1,
|
next_inode: 1,
|
||||||
event_txs: Default::default(),
|
event_txs: Default::default(),
|
||||||
|
buffered_events: Vec::new(),
|
||||||
|
events_paused: false,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -567,6 +577,18 @@ impl FakeFs {
|
||||||
state.emit_event(&[path]);
|
state.emit_event(&[path]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn pause_events(&self) {
|
||||||
|
self.state.lock().await.events_paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn buffered_event_count(&self) -> usize {
|
||||||
|
self.state.lock().await.buffered_events.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn flush_events(&self, count: usize) {
|
||||||
|
self.state.lock().await.flush_events(count);
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn insert_tree<'a>(
|
pub fn insert_tree<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
@ -868,7 +890,7 @@ impl Fs for FakeFs {
|
||||||
.ok_or_else(|| anyhow!("cannot remove the root"))?;
|
.ok_or_else(|| anyhow!("cannot remove the root"))?;
|
||||||
let base_name = path.file_name().unwrap();
|
let base_name = path.file_name().unwrap();
|
||||||
|
|
||||||
let state = self.state.lock().await;
|
let mut state = self.state.lock().await;
|
||||||
let parent_entry = state.read_path(parent_path).await?;
|
let parent_entry = state.read_path(parent_path).await?;
|
||||||
let mut parent_entry = parent_entry.lock().await;
|
let mut parent_entry = parent_entry.lock().await;
|
||||||
let entry = parent_entry
|
let entry = parent_entry
|
||||||
|
@ -892,7 +914,7 @@ impl Fs for FakeFs {
|
||||||
e.remove();
|
e.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
state.emit_event(&[path]);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,8 @@ thiserror = "1.0.29"
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
ctor = "0.1"
|
||||||
|
env_logger = "0.9"
|
||||||
pretty_assertions = "1.3.0"
|
pretty_assertions = "1.3.0"
|
||||||
client = { path = "../client", features = ["test-support"] }
|
client = { path = "../client", features = ["test-support"] }
|
||||||
collections = { path = "../collections", features = ["test-support"] }
|
collections = { path = "../collections", features = ["test-support"] }
|
||||||
|
|
|
@ -14,6 +14,14 @@ use std::{cell::RefCell, os::unix, rc::Rc, task::Poll};
|
||||||
use unindent::Unindent as _;
|
use unindent::Unindent as _;
|
||||||
use util::{assert_set_eq, test::temp_tree};
|
use util::{assert_set_eq, test::temp_tree};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[ctor::ctor]
|
||||||
|
fn init_logger() {
|
||||||
|
if std::env::var("RUST_LOG").is_ok() {
|
||||||
|
env_logger::init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_symlinks(cx: &mut gpui::TestAppContext) {
|
async fn test_symlinks(cx: &mut gpui::TestAppContext) {
|
||||||
let dir = temp_tree(json!({
|
let dir = temp_tree(json!({
|
||||||
|
|
|
@ -2257,10 +2257,6 @@ impl BackgroundScanner {
|
||||||
self.snapshot.lock().abs_path.clone()
|
self.snapshot.lock().abs_path.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot(&self) -> LocalSnapshot {
|
|
||||||
self.snapshot.lock().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(mut self, events_rx: impl Stream<Item = Vec<fsevent::Event>>) {
|
async fn run(mut self, events_rx: impl Stream<Item = Vec<fsevent::Event>>) {
|
||||||
if self.notify.unbounded_send(ScanState::Initializing).is_err() {
|
if self.notify.unbounded_send(ScanState::Initializing).is_err() {
|
||||||
return;
|
return;
|
||||||
|
@ -2657,8 +2653,7 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_ignore_statuses(&self) {
|
async fn update_ignore_statuses(&self) {
|
||||||
let mut snapshot = self.snapshot();
|
let mut snapshot = self.snapshot.lock().clone();
|
||||||
|
|
||||||
let mut ignores_to_update = Vec::new();
|
let mut ignores_to_update = Vec::new();
|
||||||
let mut ignores_to_delete = Vec::new();
|
let mut ignores_to_delete = Vec::new();
|
||||||
for (parent_abs_path, (_, scan_id)) in &snapshot.ignores_by_parent_abs_path {
|
for (parent_abs_path, (_, scan_id)) in &snapshot.ignores_by_parent_abs_path {
|
||||||
|
@ -3115,19 +3110,13 @@ impl<'a> TryFrom<(&'a CharBag, proto::Entry)> for Entry {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use anyhow::Result;
|
|
||||||
use client::test::FakeHttpClient;
|
use client::test::FakeHttpClient;
|
||||||
use fs::repository::FakeGitRepository;
|
use fs::repository::FakeGitRepository;
|
||||||
use fs::{FakeFs, RealFs};
|
use fs::{FakeFs, RealFs};
|
||||||
use gpui::{executor::Deterministic, TestAppContext};
|
use gpui::{executor::Deterministic, TestAppContext};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::{
|
use std::{env, fmt::Write};
|
||||||
env,
|
|
||||||
fmt::Write,
|
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
|
||||||
};
|
|
||||||
|
|
||||||
use util::test::temp_tree;
|
use util::test::temp_tree;
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -3572,7 +3561,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
#[gpui::test(iterations = 100)]
|
||||||
fn test_random(mut rng: StdRng) {
|
async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng) {
|
||||||
let operations = env::var("OPERATIONS")
|
let operations = env::var("OPERATIONS")
|
||||||
.map(|o| o.parse().unwrap())
|
.map(|o| o.parse().unwrap())
|
||||||
.unwrap_or(40);
|
.unwrap_or(40);
|
||||||
|
@ -3580,90 +3569,80 @@ mod tests {
|
||||||
.map(|o| o.parse().unwrap())
|
.map(|o| o.parse().unwrap())
|
||||||
.unwrap_or(20);
|
.unwrap_or(20);
|
||||||
|
|
||||||
let root_dir = tempdir::TempDir::new("worktree-test").unwrap();
|
let root_dir = Path::new("/test");
|
||||||
|
let fs = FakeFs::new(cx.background()) as Arc<dyn Fs>;
|
||||||
|
fs.as_fake().insert_tree(root_dir, json!({})).await;
|
||||||
for _ in 0..initial_entries {
|
for _ in 0..initial_entries {
|
||||||
randomly_mutate_tree(root_dir.path(), 1.0, &mut rng).unwrap();
|
randomly_mutate_tree(&fs, root_dir, 1.0, &mut rng).await;
|
||||||
}
|
}
|
||||||
log::info!("Generated initial tree");
|
log::info!("generated initial tree");
|
||||||
|
|
||||||
let (notify_tx, _notify_rx) = mpsc::unbounded();
|
let next_entry_id = Arc::new(AtomicUsize::default());
|
||||||
let fs = Arc::new(RealFs);
|
let client = cx.read(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
|
||||||
let next_entry_id = Arc::new(AtomicUsize::new(0));
|
let worktree = Worktree::local(
|
||||||
let mut initial_snapshot = LocalSnapshot {
|
client.clone(),
|
||||||
removed_entry_ids: Default::default(),
|
root_dir,
|
||||||
ignores_by_parent_abs_path: Default::default(),
|
true,
|
||||||
git_repositories: Default::default(),
|
|
||||||
next_entry_id: next_entry_id.clone(),
|
|
||||||
snapshot: Snapshot {
|
|
||||||
id: WorktreeId::from_usize(0),
|
|
||||||
entries_by_path: Default::default(),
|
|
||||||
entries_by_id: Default::default(),
|
|
||||||
abs_path: root_dir.path().into(),
|
|
||||||
root_name: Default::default(),
|
|
||||||
root_char_bag: Default::default(),
|
|
||||||
scan_id: 0,
|
|
||||||
completed_scan_id: 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
initial_snapshot.insert_entry(
|
|
||||||
Entry::new(
|
|
||||||
Path::new("").into(),
|
|
||||||
&smol::block_on(fs.metadata(root_dir.path()))
|
|
||||||
.unwrap()
|
|
||||||
.unwrap(),
|
|
||||||
&next_entry_id,
|
|
||||||
Default::default(),
|
|
||||||
),
|
|
||||||
fs.as_ref(),
|
|
||||||
);
|
|
||||||
let mut scanner = BackgroundScanner::new(
|
|
||||||
Arc::new(Mutex::new(initial_snapshot.clone())),
|
|
||||||
Arc::new(Mutex::new(HashMap::default())),
|
|
||||||
notify_tx,
|
|
||||||
fs.clone(),
|
fs.clone(),
|
||||||
Arc::new(gpui::executor::Background::new()),
|
next_entry_id.clone(),
|
||||||
);
|
&mut cx.to_async(),
|
||||||
smol::block_on(scanner.scan_dirs()).unwrap();
|
)
|
||||||
scanner.snapshot().check_invariants();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
worktree
|
||||||
|
.update(cx, |tree, _| tree.as_local_mut().unwrap().scan_complete())
|
||||||
|
.await;
|
||||||
|
|
||||||
let mut events = Vec::new();
|
|
||||||
let mut snapshots = Vec::new();
|
let mut snapshots = Vec::new();
|
||||||
let mut mutations_len = operations;
|
let mut mutations_len = operations;
|
||||||
while mutations_len > 1 {
|
while mutations_len > 1 {
|
||||||
if !events.is_empty() && rng.gen_bool(0.4) {
|
randomly_mutate_tree(&fs, root_dir, 1.0, &mut rng).await;
|
||||||
let len = rng.gen_range(0..=events.len());
|
let buffered_event_count = fs.as_fake().buffered_event_count().await;
|
||||||
let to_deliver = events.drain(0..len).collect::<Vec<_>>();
|
if buffered_event_count > 0 && rng.gen_bool(0.3) {
|
||||||
log::info!("Delivering events: {:#?}", to_deliver);
|
let len = rng.gen_range(0..=buffered_event_count);
|
||||||
smol::block_on(scanner.process_events(to_deliver, false));
|
log::info!("flushing {} events", len);
|
||||||
scanner.snapshot().check_invariants();
|
fs.as_fake().flush_events(len).await;
|
||||||
} else {
|
} else {
|
||||||
events.extend(randomly_mutate_tree(root_dir.path(), 0.6, &mut rng).unwrap());
|
randomly_mutate_tree(&fs, root_dir, 0.6, &mut rng).await;
|
||||||
mutations_len -= 1;
|
mutations_len -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cx.foreground().run_until_parked();
|
||||||
if rng.gen_bool(0.2) {
|
if rng.gen_bool(0.2) {
|
||||||
snapshots.push(scanner.snapshot());
|
log::info!("storing snapshot {}", snapshots.len());
|
||||||
|
let snapshot =
|
||||||
|
worktree.read_with(cx, |tree, _| tree.as_local().unwrap().snapshot());
|
||||||
|
snapshots.push(snapshot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::info!("Quiescing: {:#?}", events);
|
|
||||||
smol::block_on(scanner.process_events(events, false));
|
|
||||||
scanner.snapshot().check_invariants();
|
|
||||||
|
|
||||||
let (notify_tx, _notify_rx) = mpsc::unbounded();
|
log::info!("quiescing");
|
||||||
let mut new_scanner = BackgroundScanner::new(
|
fs.as_fake().flush_events(usize::MAX).await;
|
||||||
Arc::new(Mutex::new(initial_snapshot)),
|
cx.foreground().run_until_parked();
|
||||||
Arc::new(Mutex::new(HashMap::default())),
|
let snapshot = worktree.read_with(cx, |tree, _| tree.as_local().unwrap().snapshot());
|
||||||
notify_tx,
|
snapshot.check_invariants();
|
||||||
scanner.fs.clone(),
|
|
||||||
scanner.executor.clone(),
|
|
||||||
);
|
|
||||||
smol::block_on(new_scanner.scan_dirs()).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
scanner.snapshot().to_vec(true),
|
|
||||||
new_scanner.snapshot().to_vec(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
for mut prev_snapshot in snapshots {
|
{
|
||||||
|
let new_worktree = Worktree::local(
|
||||||
|
client.clone(),
|
||||||
|
root_dir,
|
||||||
|
true,
|
||||||
|
fs.clone(),
|
||||||
|
next_entry_id,
|
||||||
|
&mut cx.to_async(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
new_worktree
|
||||||
|
.update(cx, |tree, _| tree.as_local_mut().unwrap().scan_complete())
|
||||||
|
.await;
|
||||||
|
let new_snapshot =
|
||||||
|
new_worktree.read_with(cx, |tree, _| tree.as_local().unwrap().snapshot());
|
||||||
|
assert_eq!(snapshot.to_vec(true), new_snapshot.to_vec(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, mut prev_snapshot) in snapshots.into_iter().enumerate() {
|
||||||
let include_ignored = rng.gen::<bool>();
|
let include_ignored = rng.gen::<bool>();
|
||||||
if !include_ignored {
|
if !include_ignored {
|
||||||
let mut entries_by_path_edits = Vec::new();
|
let mut entries_by_path_edits = Vec::new();
|
||||||
|
@ -3683,54 +3662,66 @@ mod tests {
|
||||||
prev_snapshot.entries_by_id.edit(entries_by_id_edits, &());
|
prev_snapshot.entries_by_id.edit(entries_by_id_edits, &());
|
||||||
}
|
}
|
||||||
|
|
||||||
let update = scanner
|
let update = snapshot.build_update(&prev_snapshot, 0, 0, include_ignored);
|
||||||
.snapshot()
|
prev_snapshot.apply_remote_update(update.clone()).unwrap();
|
||||||
.build_update(&prev_snapshot, 0, 0, include_ignored);
|
|
||||||
prev_snapshot.apply_remote_update(update).unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
prev_snapshot.to_vec(true),
|
prev_snapshot.to_vec(include_ignored),
|
||||||
scanner.snapshot().to_vec(include_ignored)
|
snapshot.to_vec(include_ignored),
|
||||||
|
"wrong update for snapshot {i}. update: {:?}",
|
||||||
|
update
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn randomly_mutate_tree(
|
async fn randomly_mutate_tree(
|
||||||
|
fs: &Arc<dyn Fs>,
|
||||||
root_path: &Path,
|
root_path: &Path,
|
||||||
insertion_probability: f64,
|
insertion_probability: f64,
|
||||||
rng: &mut impl Rng,
|
rng: &mut impl Rng,
|
||||||
) -> Result<Vec<fsevent::Event>> {
|
) {
|
||||||
let root_path = root_path.canonicalize().unwrap();
|
let mut files = Vec::new();
|
||||||
let (dirs, files) = read_dir_recursive(root_path.clone());
|
let mut dirs = Vec::new();
|
||||||
|
for path in fs.as_fake().paths().await {
|
||||||
let mut events = Vec::new();
|
if path.starts_with(root_path) {
|
||||||
let mut record_event = |path: PathBuf| {
|
if fs.is_file(&path).await {
|
||||||
events.push(fsevent::Event {
|
files.push(path);
|
||||||
event_id: SystemTime::now()
|
} else {
|
||||||
.duration_since(UNIX_EPOCH)
|
dirs.push(path);
|
||||||
.unwrap()
|
}
|
||||||
.as_secs(),
|
}
|
||||||
flags: fsevent::StreamFlags::empty(),
|
}
|
||||||
path,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (files.is_empty() && dirs.len() == 1) || rng.gen_bool(insertion_probability) {
|
if (files.is_empty() && dirs.len() == 1) || rng.gen_bool(insertion_probability) {
|
||||||
let path = dirs.choose(rng).unwrap();
|
let path = dirs.choose(rng).unwrap();
|
||||||
let new_path = path.join(gen_name(rng));
|
let new_path = path.join(gen_name(rng));
|
||||||
|
|
||||||
if rng.gen() {
|
if rng.gen() {
|
||||||
log::info!("Creating dir {:?}", new_path.strip_prefix(root_path)?);
|
log::info!(
|
||||||
std::fs::create_dir(&new_path)?;
|
"Creating dir {:?}",
|
||||||
|
new_path.strip_prefix(root_path).unwrap()
|
||||||
|
);
|
||||||
|
fs.create_dir(&new_path).await.unwrap();
|
||||||
} else {
|
} else {
|
||||||
log::info!("Creating file {:?}", new_path.strip_prefix(root_path)?);
|
log::info!(
|
||||||
std::fs::write(&new_path, "")?;
|
"Creating file {:?}",
|
||||||
|
new_path.strip_prefix(root_path).unwrap()
|
||||||
|
);
|
||||||
|
fs.create_file(&new_path, Default::default()).await.unwrap();
|
||||||
}
|
}
|
||||||
record_event(new_path);
|
|
||||||
} else if rng.gen_bool(0.05) {
|
} else if rng.gen_bool(0.05) {
|
||||||
let ignore_dir_path = dirs.choose(rng).unwrap();
|
let ignore_dir_path = dirs.choose(rng).unwrap();
|
||||||
let ignore_path = ignore_dir_path.join(&*GITIGNORE);
|
let ignore_path = ignore_dir_path.join(&*GITIGNORE);
|
||||||
|
|
||||||
let (subdirs, subfiles) = read_dir_recursive(ignore_dir_path.clone());
|
let subdirs = dirs
|
||||||
|
.iter()
|
||||||
|
.filter(|d| d.starts_with(&ignore_dir_path))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let subfiles = files
|
||||||
|
.iter()
|
||||||
|
.filter(|d| d.starts_with(&ignore_dir_path))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let files_to_ignore = {
|
let files_to_ignore = {
|
||||||
let len = rng.gen_range(0..=subfiles.len());
|
let len = rng.gen_range(0..=subfiles.len());
|
||||||
subfiles.choose_multiple(rng, len)
|
subfiles.choose_multiple(rng, len)
|
||||||
|
@ -3746,7 +3737,8 @@ mod tests {
|
||||||
ignore_contents,
|
ignore_contents,
|
||||||
"{}",
|
"{}",
|
||||||
path_to_ignore
|
path_to_ignore
|
||||||
.strip_prefix(&ignore_dir_path)?
|
.strip_prefix(&ignore_dir_path)
|
||||||
|
.unwrap()
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
)
|
)
|
||||||
|
@ -3754,11 +3746,16 @@ mod tests {
|
||||||
}
|
}
|
||||||
log::info!(
|
log::info!(
|
||||||
"Creating {:?} with contents:\n{}",
|
"Creating {:?} with contents:\n{}",
|
||||||
ignore_path.strip_prefix(&root_path)?,
|
ignore_path.strip_prefix(&root_path).unwrap(),
|
||||||
ignore_contents
|
ignore_contents
|
||||||
);
|
);
|
||||||
std::fs::write(&ignore_path, ignore_contents).unwrap();
|
fs.save(
|
||||||
record_event(ignore_path);
|
&ignore_path,
|
||||||
|
&ignore_contents.as_str().into(),
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
let old_path = {
|
let old_path = {
|
||||||
let file_path = files.choose(rng);
|
let file_path = files.choose(rng);
|
||||||
|
@ -3777,7 +3774,15 @@ mod tests {
|
||||||
let overwrite_existing_dir =
|
let overwrite_existing_dir =
|
||||||
!old_path.starts_with(&new_path_parent) && rng.gen_bool(0.3);
|
!old_path.starts_with(&new_path_parent) && rng.gen_bool(0.3);
|
||||||
let new_path = if overwrite_existing_dir {
|
let new_path = if overwrite_existing_dir {
|
||||||
std::fs::remove_dir_all(&new_path_parent).ok();
|
fs.remove_dir(
|
||||||
|
&new_path_parent,
|
||||||
|
RemoveOptions {
|
||||||
|
recursive: true,
|
||||||
|
ignore_if_not_exists: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
new_path_parent.to_path_buf()
|
new_path_parent.to_path_buf()
|
||||||
} else {
|
} else {
|
||||||
new_path_parent.join(gen_name(rng))
|
new_path_parent.join(gen_name(rng))
|
||||||
|
@ -3785,53 +3790,46 @@ mod tests {
|
||||||
|
|
||||||
log::info!(
|
log::info!(
|
||||||
"Renaming {:?} to {}{:?}",
|
"Renaming {:?} to {}{:?}",
|
||||||
old_path.strip_prefix(&root_path)?,
|
old_path.strip_prefix(&root_path).unwrap(),
|
||||||
if overwrite_existing_dir {
|
if overwrite_existing_dir {
|
||||||
"overwrite "
|
"overwrite "
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
new_path.strip_prefix(&root_path)?
|
new_path.strip_prefix(&root_path).unwrap()
|
||||||
);
|
);
|
||||||
std::fs::rename(&old_path, &new_path)?;
|
fs.rename(
|
||||||
record_event(old_path.clone());
|
&old_path,
|
||||||
record_event(new_path);
|
&new_path,
|
||||||
} else if old_path.is_dir() {
|
fs::RenameOptions {
|
||||||
let (dirs, files) = read_dir_recursive(old_path.clone());
|
overwrite: true,
|
||||||
|
ignore_if_exists: true,
|
||||||
log::info!("Deleting dir {:?}", old_path.strip_prefix(&root_path)?);
|
},
|
||||||
std::fs::remove_dir_all(&old_path).unwrap();
|
)
|
||||||
for file in files {
|
.await
|
||||||
record_event(file);
|
.unwrap();
|
||||||
}
|
} else if fs.is_file(&old_path).await {
|
||||||
for dir in dirs {
|
log::info!(
|
||||||
record_event(dir);
|
"Deleting file {:?}",
|
||||||
}
|
old_path.strip_prefix(&root_path).unwrap()
|
||||||
|
);
|
||||||
|
fs.remove_file(old_path, Default::default()).await.unwrap();
|
||||||
} else {
|
} else {
|
||||||
log::info!("Deleting file {:?}", old_path.strip_prefix(&root_path)?);
|
log::info!(
|
||||||
std::fs::remove_file(old_path).unwrap();
|
"Deleting dir {:?}",
|
||||||
record_event(old_path.clone());
|
old_path.strip_prefix(&root_path).unwrap()
|
||||||
|
);
|
||||||
|
fs.remove_dir(
|
||||||
|
&old_path,
|
||||||
|
RemoveOptions {
|
||||||
|
recursive: true,
|
||||||
|
ignore_if_not_exists: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(events)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_dir_recursive(path: PathBuf) -> (Vec<PathBuf>, Vec<PathBuf>) {
|
|
||||||
let child_entries = std::fs::read_dir(&path).unwrap();
|
|
||||||
let mut dirs = vec![path];
|
|
||||||
let mut files = Vec::new();
|
|
||||||
for child_entry in child_entries {
|
|
||||||
let child_path = child_entry.unwrap().path();
|
|
||||||
if child_path.is_dir() {
|
|
||||||
let (child_dirs, child_files) = read_dir_recursive(child_path);
|
|
||||||
dirs.extend(child_dirs);
|
|
||||||
files.extend(child_files);
|
|
||||||
} else {
|
|
||||||
files.push(child_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(dirs, files)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_name(rng: &mut impl Rng) -> String {
|
fn gen_name(rng: &mut impl Rng) -> String {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue