-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix
purge_all
and restore_all
reading invvalid memory and not exe…
…cuting the operation on the requested items. Add test cases for `purge_all` and `restore_all`. Test are now thread safe.
- Loading branch information
1 parent
e06c825
commit 22d5181
Showing
3 changed files
with
149 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,170 @@ | ||
use crate::{remove, remove_all}; | ||
use crate as trash; | ||
use std::fs::{create_dir, File}; | ||
use std::path::PathBuf; | ||
use std::collections::{HashMap, hash_map::Entry}; | ||
use std::sync::atomic::{AtomicI64, Ordering}; | ||
|
||
use chrono; | ||
use lazy_static::lazy_static; | ||
|
||
// WARNING Expecting that `cargo test` won't be invoked on the same computer more than once within | ||
// a single millisecond | ||
lazy_static! { | ||
static ref INSTANCE_ID: i64 = chrono::Local::now().timestamp_millis(); | ||
static ref ID_OFFSET: AtomicI64 = AtomicI64::new(0); | ||
} | ||
fn get_unique_name() -> String { | ||
let id = ID_OFFSET.fetch_add(1, Ordering::SeqCst); | ||
format!("trash-test-{}-{}", *INSTANCE_ID, id) | ||
} | ||
|
||
#[test] | ||
fn create_remove() { | ||
let path = "test_file_to_remove"; | ||
File::create(path).unwrap(); | ||
|
||
remove(path).unwrap(); | ||
assert!(File::open(path).is_err()); | ||
let file_name = get_unique_name(); | ||
File::create(&file_name).unwrap(); | ||
trash::remove(&file_name).unwrap(); | ||
assert!(File::open(&file_name).is_err()); | ||
} | ||
|
||
#[test] | ||
fn create_remove_folder() { | ||
let path = PathBuf::from("test_folder_to_remove"); | ||
let folder_name = get_unique_name(); | ||
let path = PathBuf::from(folder_name); | ||
create_dir(&path).unwrap(); | ||
File::create(path.join("file_in_folder")).unwrap(); | ||
|
||
assert!(path.exists()); | ||
remove(&path).unwrap(); | ||
trash::remove(&path).unwrap(); | ||
assert!(path.exists() == false); | ||
} | ||
|
||
#[test] | ||
fn create_multiple_remove_all() { | ||
let file_name_prefix = get_unique_name(); | ||
let count: usize = 3; | ||
|
||
let paths: Vec<_> = (0..count) | ||
.map(|i| format!("test_file_to_remove_{}", i)) | ||
.map(|i| format!("{}#{}", file_name_prefix, i)) | ||
.collect(); | ||
for path in paths.iter() { | ||
File::create(path).unwrap(); | ||
} | ||
|
||
remove_all(&paths).unwrap(); | ||
trash::remove_all(&paths).unwrap(); | ||
for path in paths.iter() { | ||
assert!(File::open(path).is_err()); | ||
} | ||
} | ||
|
||
#[test] | ||
fn list() { | ||
let file_name_prefix = get_unique_name(); | ||
let batches: usize = 2; | ||
let files_per_batch: usize = 3; | ||
let names: Vec<_> = (0..files_per_batch) | ||
.map(|i| format!("{}#{}", file_name_prefix, i)) | ||
.collect(); | ||
for _ in 0..batches { | ||
for path in names.iter() { | ||
File::create(path).unwrap(); | ||
} | ||
trash::remove_all(&names).unwrap(); | ||
} | ||
let items = trash::list().unwrap(); | ||
let items: HashMap<_, Vec<_>> = | ||
items.into_iter().filter(|x| x.name.starts_with(&file_name_prefix)) | ||
.fold(HashMap::new(), |mut map, x| { | ||
match map.entry(x.name.clone()) { | ||
Entry::Occupied(mut entry) => { | ||
entry.get_mut().push(x); | ||
} | ||
Entry::Vacant(entry) => { | ||
entry.insert(vec![x]); | ||
} | ||
} | ||
map | ||
}); | ||
for name in names { | ||
assert_eq!(items.get(&name).unwrap().len(), batches); | ||
} | ||
|
||
// Let's try to purge all the items we just created but ignore any errors | ||
// as this test should succeed as long as `list` works properly. | ||
let _ = trash::purge_all(items.into_iter().map(|(_name, item)| item).flatten()); | ||
} | ||
|
||
#[test] | ||
fn purge() { | ||
let file_name_prefix = get_unique_name(); | ||
let batches: usize = 2; | ||
let files_per_batch: usize = 3; | ||
let names: Vec<_> = (0..files_per_batch) | ||
.map(|i| format!("{}#{}", file_name_prefix, i)) | ||
.collect(); | ||
for _ in 0..batches { | ||
for path in names.iter() { | ||
File::create(path).unwrap(); | ||
} | ||
trash::remove_all(&names).unwrap(); | ||
} | ||
|
||
// Collect it because we need the exact number of items gathered. | ||
let targets: Vec<_> = trash::list() | ||
.unwrap() | ||
.into_iter() | ||
.filter(|x| x.name.starts_with(&file_name_prefix)) | ||
.collect(); | ||
assert_eq!(targets.len(), batches * files_per_batch); | ||
trash::purge_all(targets).unwrap(); | ||
// Ugly hack but need to wait for example on Windows one must wait a bit | ||
// before the items acctually leave the trash | ||
//std::thread::sleep(std::time::Duration::from_secs(8)); | ||
let remaining = trash::list() | ||
.unwrap() | ||
.into_iter() | ||
.filter(|x| x.name.starts_with(&file_name_prefix)) | ||
.count(); | ||
assert_eq!(remaining, 0); | ||
} | ||
|
||
#[test] | ||
fn restore() { | ||
let file_name_prefix = get_unique_name(); | ||
let file_count: usize = 3; | ||
let names: Vec<_> = (0..file_count) | ||
.map(|i| format!("{}#{}", file_name_prefix, i)) | ||
.collect(); | ||
for path in names.iter() { | ||
File::create(path).unwrap(); | ||
} | ||
trash::remove_all(&names).unwrap(); | ||
|
||
// Collect it because we need the exact number of items gathered. | ||
let targets: Vec<_> = trash::list() | ||
.unwrap() | ||
.into_iter() | ||
.filter(|x| x.name.starts_with(&file_name_prefix)) | ||
.collect(); | ||
assert_eq!(targets.len(), file_count); | ||
trash::restore_all(targets).unwrap(); | ||
|
||
// Ugly hack but need to wait for example on Windows one must wait a bit | ||
// before the items acctually leave the trash | ||
//std::thread::sleep(std::time::Duration::from_secs(8)); | ||
|
||
let remaining = trash::list() | ||
.unwrap() | ||
.into_iter() | ||
.filter(|x| x.name.starts_with(&file_name_prefix)) | ||
.count(); | ||
assert_eq!(remaining, 0); | ||
|
||
// They are not in the trash anymore but they should be at their original location | ||
for path in names.iter() { | ||
assert!(File::open(path).is_ok()); | ||
} | ||
|
||
// Good ol' remove to clean up | ||
for path in names.iter() { | ||
std::fs::remove_file(path).unwrap(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters