From 4431e8e0476e4eb8da5652dba4dfbeff2bbf6d30 Mon Sep 17 00:00:00 2001 From: Gabriel Simonetto Date: Fri, 1 Oct 2021 23:10:07 -0300 Subject: [PATCH 1/4] Create failing test --- tests/compress_empty_dir.rs | 92 +++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 tests/compress_empty_dir.rs diff --git a/tests/compress_empty_dir.rs b/tests/compress_empty_dir.rs new file mode 100644 index 000000000..438565a21 --- /dev/null +++ b/tests/compress_empty_dir.rs @@ -0,0 +1,92 @@ +use std::{ + env, fs, + path::{Path, PathBuf}, +}; + +use ouch::{cli::Command, commands::run, oof}; + +#[test] +fn test_compress_decompress_with_empty_dir() { + // System temporary directory depends on the platform, for linux it's /tmp + let system_tmp = env::temp_dir(); + + // Create a temporary testing folder that will be deleted on scope drop + let testing_dir = + tempfile::Builder::new().prefix("ouch-testing").tempdir_in(system_tmp).expect("Could not create testing_dir"); + + let testing_dir_path = testing_dir.path(); + + let empty_dir_path: PathBuf = create_empty_dir(&testing_dir_path, "dummy_empty_dir_name"); + + let mut file_paths: Vec = vec![empty_dir_path]; + + let format = "zip"; + + let compressed_archive_path: PathBuf = compress_files(&testing_dir_path, &file_paths, &format); + + let mut extracted_paths = extract_files(&compressed_archive_path); + + // // DEBUG UTIL: + // // Uncomment line below to freeze the code and see compressed and extracted files in + // // the temporary directory before their auto-destruction. + // std::thread::sleep(std::time::Duration::from_secs(10)); + + // no need to sort a unitary value vector but i will keep this + // for retrocompatibility, for now. + file_paths.sort(); + extracted_paths.sort(); + + assert_correct_paths(&file_paths, &extracted_paths, format); +} + +fn create_empty_dir(at: &Path, filename: &str) -> PathBuf { + let dirname = Path::new(filename); + let full_path = at.join(dirname); + + fs::create_dir(&full_path).expect("Failed to create an empty directory"); + + full_path +} + +fn compress_files(at: &Path, paths_to_compress: &[PathBuf], format: &str) -> PathBuf { + let archive_path = String::from("archive.") + format; + let archive_path = at.join(archive_path); + + let command = Command::Compress { files: paths_to_compress.to_vec(), output_path: archive_path.to_path_buf() }; + run(command, &oof::Flags::default()).expect("Failed to compress test dummy files"); + + archive_path +} + +fn extract_files(archive_path: &Path) -> Vec { + // We will extract in the same folder as the archive + // If the archive is at: + // /tmp/ouch-testing-tar.Rbq4DusBrtF8/archive.tar + // Then the extraction_output_folder will be: + // /tmp/ouch-testing-tar.Rbq4DusBrtF8/extraction_results/ + let mut extraction_output_folder = archive_path.to_path_buf(); + // Remove the name of the extracted archive + assert!(extraction_output_folder.pop()); + // Add the suffix "results" + extraction_output_folder.push("extraction_results"); + + let command = Command::Decompress { + files: vec![archive_path.to_owned()], + output_folder: Some(extraction_output_folder.clone()), + }; + run(command, &oof::Flags::default()).expect("Failed to extract"); + + fs::read_dir(extraction_output_folder).unwrap().map(Result::unwrap).map(|entry| entry.path()).collect() +} + +fn assert_correct_paths(original: &[PathBuf], extracted: &[PathBuf], format: &str) { + assert_eq!( + original.len(), + extracted.len(), + "Number of compressed files does not match number of decompressed when testing archive format '{:?}'.", + format + ); + for (original, extracted) in original.iter().zip(extracted) { + assert_eq!(original.file_name(), extracted.file_name(), ""); + } +} From d43a93d8bb739919575d46f9f6feafe3d17859d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20M=2E=20Bezerra?= Date: Sat, 2 Oct 2021 00:35:51 -0300 Subject: [PATCH 2/4] Moving some tests utilities to tests/utils.rs --- tests/compress_and_decompress.rs | 13 ++----- tests/compress_empty_dir.rs | 61 +++----------------------------- tests/utils.rs | 59 ++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 67 deletions(-) create mode 100644 tests/utils.rs diff --git a/tests/compress_and_decompress.rs b/tests/compress_and_decompress.rs index fa07d13b6..cb207ad71 100644 --- a/tests/compress_and_decompress.rs +++ b/tests/compress_and_decompress.rs @@ -1,3 +1,5 @@ +mod utils; + use std::{ env, fs, io::prelude::*, @@ -6,6 +8,7 @@ use std::{ use ouch::{cli::Command, commands::run, oof}; use rand::{rngs::SmallRng, RngCore, SeedableRng}; +use utils::*; #[test] /// Tests each format that supports multiple files with random input. @@ -94,16 +97,6 @@ fn create_files(at: &Path, contents: &[FileContent]) -> Vec { .collect() } -fn compress_files(at: &Path, paths_to_compress: &[PathBuf], format: &str) -> PathBuf { - let archive_path = String::from("archive.") + format; - let archive_path = at.join(archive_path); - - let command = Command::Compress { files: paths_to_compress.to_vec(), output_path: archive_path.to_path_buf() }; - run(command, &oof::Flags::default()).expect("Failed to compress test dummy files"); - - archive_path -} - fn extract_files(archive_path: &Path) -> Vec { // We will extract in the same folder as the archive // If the archive is at: diff --git a/tests/compress_empty_dir.rs b/tests/compress_empty_dir.rs index 438565a21..4a7c108c5 100644 --- a/tests/compress_empty_dir.rs +++ b/tests/compress_empty_dir.rs @@ -1,9 +1,8 @@ -use std::{ - env, fs, - path::{Path, PathBuf}, -}; +mod utils; -use ouch::{cli::Command, commands::run, oof}; +use std::{env, path::PathBuf}; + +use utils::*; #[test] fn test_compress_decompress_with_empty_dir() { @@ -38,55 +37,3 @@ fn test_compress_decompress_with_empty_dir() { assert_correct_paths(&file_paths, &extracted_paths, format); } - -fn create_empty_dir(at: &Path, filename: &str) -> PathBuf { - let dirname = Path::new(filename); - let full_path = at.join(dirname); - - fs::create_dir(&full_path).expect("Failed to create an empty directory"); - - full_path -} - -fn compress_files(at: &Path, paths_to_compress: &[PathBuf], format: &str) -> PathBuf { - let archive_path = String::from("archive.") + format; - let archive_path = at.join(archive_path); - - let command = Command::Compress { files: paths_to_compress.to_vec(), output_path: archive_path.to_path_buf() }; - run(command, &oof::Flags::default()).expect("Failed to compress test dummy files"); - - archive_path -} - -fn extract_files(archive_path: &Path) -> Vec { - // We will extract in the same folder as the archive - // If the archive is at: - // /tmp/ouch-testing-tar.Rbq4DusBrtF8/archive.tar - // Then the extraction_output_folder will be: - // /tmp/ouch-testing-tar.Rbq4DusBrtF8/extraction_results/ - let mut extraction_output_folder = archive_path.to_path_buf(); - // Remove the name of the extracted archive - assert!(extraction_output_folder.pop()); - // Add the suffix "results" - extraction_output_folder.push("extraction_results"); - - let command = Command::Decompress { - files: vec![archive_path.to_owned()], - output_folder: Some(extraction_output_folder.clone()), - }; - run(command, &oof::Flags::default()).expect("Failed to extract"); - - fs::read_dir(extraction_output_folder).unwrap().map(Result::unwrap).map(|entry| entry.path()).collect() -} - -fn assert_correct_paths(original: &[PathBuf], extracted: &[PathBuf], format: &str) { - assert_eq!( - original.len(), - extracted.len(), - "Number of compressed files does not match number of decompressed when testing archive format '{:?}'.", - format - ); - for (original, extracted) in original.iter().zip(extracted) { - assert_eq!(original.file_name(), extracted.file_name(), ""); - } -} diff --git a/tests/utils.rs b/tests/utils.rs new file mode 100644 index 000000000..9152e05fd --- /dev/null +++ b/tests/utils.rs @@ -0,0 +1,59 @@ +#[allow(dead_code)] +use std::{ + fs, + path::{Path, PathBuf}, +}; + +use ouch::{cli::Command, commands::run, oof}; + +pub fn create_empty_dir(at: &Path, filename: &str) -> PathBuf { + let dirname = Path::new(filename); + let full_path = at.join(dirname); + + fs::create_dir(&full_path).expect("Failed to create an empty directory"); + + full_path +} + +pub fn compress_files(at: &Path, paths_to_compress: &[PathBuf], format: &str) -> PathBuf { + let archive_path = String::from("archive.") + format; + let archive_path = at.join(archive_path); + + let command = Command::Compress { files: paths_to_compress.to_vec(), output_path: archive_path.to_path_buf() }; + run(command, &oof::Flags::default()).expect("Failed to compress test dummy files"); + + archive_path +} + +pub fn extract_files(archive_path: &Path) -> Vec { + // We will extract in the same folder as the archive + // If the archive is at: + // /tmp/ouch-testing-tar.Rbq4DusBrtF8/archive.tar + // Then the extraction_output_folder will be: + // /tmp/ouch-testing-tar.Rbq4DusBrtF8/extraction_results/ + let mut extraction_output_folder = archive_path.to_path_buf(); + // Remove the name of the extracted archive + assert!(extraction_output_folder.pop()); + // Add the suffix "results" + extraction_output_folder.push("extraction_results"); + + let command = Command::Decompress { + files: vec![archive_path.to_owned()], + output_folder: Some(extraction_output_folder.clone()), + }; + run(command, &oof::Flags::default()).expect("Failed to extract"); + + fs::read_dir(extraction_output_folder).unwrap().map(Result::unwrap).map(|entry| entry.path()).collect() +} + +pub fn assert_correct_paths(original: &[PathBuf], extracted: &[PathBuf], format: &str) { + assert_eq!( + original.len(), + extracted.len(), + "Number of compressed files does not match number of decompressed when testing archive format '{:?}'.", + format + ); + for (original, extracted) in original.iter().zip(extracted) { + assert_eq!(original.file_name(), extracted.file_name()); + } +} From 0c65fbd2d4a505b9e72bf96cc52c610ee19b9dc6 Mon Sep 17 00:00:00 2001 From: Gabriel Simonetto Date: Sat, 2 Oct 2021 17:10:42 -0300 Subject: [PATCH 3/4] Allow empty dirs on zip --- src/archive/zip.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/archive/zip.rs b/src/archive/zip.rs index 6db5d008f..e25e7f5ff 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -88,14 +88,16 @@ where let path = entry.path(); if path.is_dir() { - continue; + if dir_is_empty(path)? { + writer.add_directory(path.to_str().unwrap().to_owned(), options)?; + } + // If a dir has files, the files are responsible for creating them. + } else { + writer.start_file(path.to_str().unwrap().to_owned(), options)?; + // TODO: better error messages + let file_bytes = fs::read(entry.path())?; + writer.write_all(&*file_bytes)?; } - - writer.start_file(path.to_str().unwrap().to_owned(), options)?; - - // TODO: better error messages - let file_bytes = fs::read(entry.path())?; - writer.write_all(&*file_bytes)?; } env::set_current_dir(previous_location)?; @@ -112,6 +114,10 @@ fn check_for_comments(file: &ZipFile) { } } +fn dir_is_empty(dir_path: &Path) -> crate::Result { + Ok(dir_path.read_dir()?.next().is_none()) +} + #[cfg(unix)] fn __unix_set_permissions(file_path: &Path, file: &ZipFile) -> crate::Result<()> { use std::os::unix::fs::PermissionsExt; From e352f8bc931cf0f0b35c8b93a22d396d5cb273f1 Mon Sep 17 00:00:00 2001 From: Gabriel Simonetto Date: Sat, 2 Oct 2021 17:33:16 -0300 Subject: [PATCH 4/4] Allow empty dirs on tar --- src/archive/tar.rs | 4 +++- tests/compress_empty_dir.rs | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/archive/tar.rs b/src/archive/tar.rs index 010bb45ef..8dc5d1e40 100644 --- a/src/archive/tar.rs +++ b/src/archive/tar.rs @@ -51,7 +51,9 @@ where let path = entry.path(); println!("Compressing '{}'.", utils::to_utf(path)); - if !path.is_dir() { + if path.is_dir() { + builder.append_dir(path, path)?; + } else { let mut file = fs::File::open(path)?; builder.append_file(path, &mut file)?; } diff --git a/tests/compress_empty_dir.rs b/tests/compress_empty_dir.rs index 4a7c108c5..95f334899 100644 --- a/tests/compress_empty_dir.rs +++ b/tests/compress_empty_dir.rs @@ -5,7 +5,12 @@ use std::{env, path::PathBuf}; use utils::*; #[test] -fn test_compress_decompress_with_empty_dir() { +fn test_each_format() { + test_compress_decompress_with_empty_dir("tar"); + test_compress_decompress_with_empty_dir("zip"); +} + +fn test_compress_decompress_with_empty_dir(format: &str) { // System temporary directory depends on the platform, for linux it's /tmp let system_tmp = env::temp_dir(); @@ -19,8 +24,6 @@ fn test_compress_decompress_with_empty_dir() { let mut file_paths: Vec = vec![empty_dir_path]; - let format = "zip"; - let compressed_archive_path: PathBuf = compress_files(&testing_dir_path, &file_paths, &format); let mut extracted_paths = extract_files(&compressed_archive_path);