diff options
author | Bryan Newbold <bnewbold@robocracy.org> | 2017-11-19 21:19:17 -0800 |
---|---|---|
committer | Bryan Newbold <bnewbold@robocracy.org> | 2017-11-19 21:19:17 -0800 |
commit | 403475b9d737cfddeee732aa4f7dbab3c2409251 (patch) | |
tree | 4d8c5aadee73abdeac0bf51d81cb989e3f40056c | |
parent | 93c7dde0b079f0d2bf6ae419be16c80d801b29e2 (diff) | |
download | geniza-403475b9d737cfddeee732aa4f7dbab3c2409251.tar.gz geniza-403475b9d737cfddeee732aa4f7dbab3c2409251.zip |
drive: tests, bugfixes
-rw-r--r-- | src/drive.rs | 142 |
1 files changed, 128 insertions, 14 deletions
diff --git a/src/drive.rs b/src/drive.rs index 740e949..0351587 100644 --- a/src/drive.rs +++ b/src/drive.rs @@ -237,6 +237,10 @@ impl<'a> DatDrive { // 2. find longest common prefix; take all entries from that level let mut common_components = longest_common_prefix(path, ¤t.path); assert!(common_components >= 1); + if current.children.len() == 0 { + // Empty drive tree + return Ok(None); + } let mut entries = current.children[(common_components-1) as usize].clone(); // 3. for each of those entries (going in recent-first order): @@ -345,13 +349,27 @@ impl<'a> DatDrive { stat.set_blocks(data_entries); stat.set_offset(data_offset); stat.set_byteOffset(data_byte_offset); - return self.append_metadata_entry(&path, Some(&stat)); + return self.append_metadata_entry(&path, Some(&stat), None); } - fn append_metadata_entry<P: AsRef<Path>>(&mut self, path: P, stat: Option<&Stat>) -> Result <()> { + /// If this metadata entry represents a change (overwriting a previous entry), then `remove` + /// should be set to the old index. + /// If this entry is a deletion/removal, `remove` should be set and `stat` should be None. + fn append_metadata_entry<P: AsRef<Path>>(&mut self, path: P, stat: Option<&Stat>, remove: Option<u64>) -> Result <()> { let index = self.entry_count()? + 1; let path = path.as_ref(); - let children = self.new_child_index(&path, index)?; + let mut children = self.new_child_index(&path, index)?; + if remove.is_some() { + // This is a removal; delete both current and old from all children + // (Vec.remove_item() is still nightly) + for dir_level in 0..children.len() { + children[dir_level].retain( + |&x| x != remove.unwrap() && (x != index || stat.is_some())); + } + + // Cleanup by removing trailing empty dir levels + children.retain(|ref x| x.len() > 0); + } let children = encode_children(&children, index)?; let mut node = Node::new(); node.set_name(path.to_string_lossy().into_owned()); @@ -403,7 +421,6 @@ impl<'a> DatDrive { Ok(children) } - // XXX: needs test /// Copies Stat metadata and all content from a file in the "real" filesystem into the /// DatDrive. pub fn import_file<P: AsRef<Path>, Q: AsRef<Path>>(&mut self, source: P, dest: Q) -> Result<()> { @@ -419,7 +436,6 @@ impl<'a> DatDrive { self.add_file(dest, &mut stat, in_file) } - // XXX: needs test /// Copies a file from the drive to the "real" filesystem, preserving Stat metadata. pub fn export_file<P: AsRef<Path>, Q: AsRef<Path>>(&mut self, source: P, dest: Q) -> Result<()> { @@ -470,18 +486,16 @@ impl<'a> DatDrive { Ok(()) } - // XXX: needs test pub fn remove_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> { let path = path.as_ref(); let current = self.get_file_entry(path)?; if let Some(val) = current { - return self.append_metadata_entry(&val.path, None); + return self.append_metadata_entry(&val.path, None, Some(val.index)); } else { bail!("Tried to delete non-existant file: {}", path.display()); } } - // XXX: needs test pub fn remove_dir_all<P: AsRef<Path>>(&mut self, path: P) -> Result<()> { // Crude implementation: // 1. get list of all file paths @@ -495,7 +509,6 @@ impl<'a> DatDrive { Ok(()) } - // XXX: needs test pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(&mut self, from: P, to: Q) -> Result<()> { let from = from.as_ref(); let to = to.as_ref(); @@ -513,10 +526,9 @@ impl<'a> DatDrive { } else { bail!("'from' file was deleted"); }; - return self.append_metadata_entry(&to, Some(&stat)); + return self.append_metadata_entry(&to, Some(&stat), None); } - // XXX: needs test pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(&mut self, from: P, to: Q) -> Result<()> { // Crude implementation: // 1. copy file @@ -650,6 +662,103 @@ fn test_dd_read_file_bytes() { } */ +#[test] +fn test_dd_import_file() { + + use tempdir::TempDir; + let tmp_dir = TempDir::new("geniza-test").unwrap(); + let mut dd = DatDrive::create(tmp_dir.path()).unwrap(); + + dd.import_file("test-data/dat/alphabet/a", "/a").unwrap(); + dd.import_file("test-data/dat/alphabet/b", "/subdir/b.txt").unwrap(); + + assert_eq!(dd.history(0).count(), 2); + assert_eq!(&dd.read_file_bytes("/a").unwrap()[..], + "a".as_bytes()); + assert_eq!(&dd.read_file_bytes("/subdir/b.txt").unwrap()[..], + "b".as_bytes()); + + assert!(dd.import_file("/non-existant-file-path", "/z").is_err()); +} + +#[test] +fn test_dd_export_file() { + + use tempdir::TempDir; + let tmp_dir = TempDir::new("geniza-test").unwrap(); + let mut dd = DatDrive::create(tmp_dir.path()).unwrap(); + + dd.import_file("test-data/dat/alphabet/a", "/a").unwrap(); + + dd.export_file("/a", tmp_dir.path().join("a.txt")).unwrap(); + assert!(dd.export_file("/z", tmp_dir.path().join("never-created")).is_err()); +} + +#[test] +fn test_dd_remove_file() { + + use tempdir::TempDir; + let tmp_dir = TempDir::new("geniza-test").unwrap(); + let mut dd = DatDrive::create(tmp_dir.path()).unwrap(); + + dd.import_file("test-data/dat/alphabet/a", "/a").unwrap(); + dd.import_file("test-data/dat/alphabet/b", "/b").unwrap(); + assert_eq!(dd.read_dir("/").count(), 2); + + dd.remove_file("/a").unwrap(); + assert_eq!(dd.read_dir_recursive("/").count(), 1); + dd.remove_file("/b").unwrap(); + assert_eq!(dd.read_dir("/").count(), 0); + assert!(&dd.read_file_bytes("/b").is_err()); + + assert!(dd.remove_file("/a").is_err()); +} + +#[test] +fn test_dd_remove_dir_all() { + + use tempdir::TempDir; + let tmp_dir = TempDir::new("geniza-test").unwrap(); + let mut dd = DatDrive::create(tmp_dir.path()).unwrap(); + + dd.import_file("test-data/dat/alphabet/a", "/a").unwrap(); + dd.import_file("test-data/dat/alphabet/b", "/sub/b").unwrap(); + dd.import_file("test-data/dat/alphabet/c", "/sub/c").unwrap(); + dd.import_file("test-data/dat/alphabet/d", "/sub/sub/d").unwrap(); + assert_eq!(dd.read_dir_recursive("/").count(), 4); + + dd.remove_dir_all("/sub").unwrap(); + assert_eq!(dd.read_dir_recursive("/").count(), 1); + assert!(&dd.read_file_bytes("/sub/b").is_err()); +} + +#[test] +fn test_dd_copy() { + + use tempdir::TempDir; + let tmp_dir = TempDir::new("geniza-test").unwrap(); + let mut dd = DatDrive::create(tmp_dir.path()).unwrap(); + + dd.import_file("test-data/dat/alphabet/a", "/a").unwrap(); + dd.copy("/a", "/c").unwrap(); + assert_eq!(dd.history(0).count(), 2); + assert!(&dd.read_file_bytes("/a").is_ok()); + assert!(&dd.read_file_bytes("/c").is_ok()); +} + +#[test] +fn test_dd_rename() { + + use tempdir::TempDir; + let tmp_dir = TempDir::new("geniza-test").unwrap(); + let mut dd = DatDrive::create(tmp_dir.path()).unwrap(); + + dd.import_file("test-data/dat/alphabet/a", "/a").unwrap(); + dd.rename("/a", "/c").unwrap(); + assert_eq!(dd.read_dir("/").count(), 1); + assert!(&dd.read_file_bytes("/a").is_err()); +} + #[derive(Debug)] pub struct DriveEntry { pub index: u64, @@ -701,9 +810,14 @@ impl<'a> ReadDriveDir<'a> { // start at the latest entry with the same path prefix match drive.get_nearest(path)? { Some(nearest) => { - let common_components = longest_common_prefix(path, nearest.path); - let list = nearest.children[(common_components - 1) as usize].clone(); - list.iter().map(|e| (common_components, *e)).collect() + if nearest.children.len() == 0 { + // Empty tree + vec![] + } else { + let common_components = longest_common_prefix(path, nearest.path); + let list = nearest.children[(common_components - 1) as usize].clone(); + list.iter().map(|e| (common_components, *e)).collect() + } }, None => vec![], } |