Rework `FinalError::display` into a `FinalError::Display` impl
[ouch.git] / tests / compress_and_decompress.rs
blobfa07d13b6d6cd630f8882fe9365d739dacbba475
1 use std::{
2     env, fs,
3     io::prelude::*,
4     path::{Path, PathBuf},
5 };
7 use ouch::{cli::Command, commands::run, oof};
8 use rand::{rngs::SmallRng, RngCore, SeedableRng};
10 #[test]
11 /// Tests each format that supports multiple files with random input.
12 /// TODO: test the remaining formats.
13 fn test_each_format() {
14     test_compressing_and_decompressing_archive("tar");
15     test_compressing_and_decompressing_archive("tar.gz");
16     test_compressing_and_decompressing_archive("tar.bz");
17     test_compressing_and_decompressing_archive("tar.bz2");
18     test_compressing_and_decompressing_archive("tar.xz");
19     test_compressing_and_decompressing_archive("tar.lz");
20     test_compressing_and_decompressing_archive("tar.lzma");
21     test_compressing_and_decompressing_archive("zip");
22     test_compressing_and_decompressing_archive("zip.gz");
23     test_compressing_and_decompressing_archive("zip.bz");
24     test_compressing_and_decompressing_archive("zip.bz2");
25     test_compressing_and_decompressing_archive("zip.xz");
26     test_compressing_and_decompressing_archive("zip.lz");
27     test_compressing_and_decompressing_archive("zip.lzma");
29     // Why not
30     test_compressing_and_decompressing_archive(
31         "tar.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz.lz.lz.lz.lz.lz.lz.lz.lz.lz.lz.bz.bz.bz.bz.bz.bz.bz",
32     );
35 type FileContent = Vec<u8>;
37 /// Compress and decompresses random files to archive formats, checks if contents match
38 fn test_compressing_and_decompressing_archive(format: &str) {
39     // System temporary directory depends on the platform, for linux it's /tmp
40     let system_tmp = env::temp_dir();
42     // Create a temporary testing folder that will be deleted on scope drop
43     let testing_dir =
44         tempfile::Builder::new().prefix("ouch-testing").tempdir_in(system_tmp).expect("Could not create testing_dir");
45     let testing_dir_path = testing_dir.path();
47     // Quantity of compressed files vary from 1 to 10
48     let mut rng = SmallRng::from_entropy();
49     let quantity_of_files = rng.next_u32() % 10 + 1;
51     let contents_of_files: Vec<FileContent> =
52         (0..quantity_of_files).map(|_| generate_random_file_content(&mut rng)).collect();
54     // Create them
55     let mut file_paths = create_files(&testing_dir_path, &contents_of_files);
56     // Compress them
57     let compressed_archive_path = compress_files(&testing_dir_path, &file_paths, &format);
58     // Decompress them
59     let mut extracted_paths = extract_files(&compressed_archive_path);
61     // // DEBUG UTIL:
62     // // Uncomment line below to freeze the code and see compressed and extracted files in
63     // // the temporary directory before their auto-destruction.
64     // std::thread::sleep(std::time::Duration::from_secs(1_000_000));
66     file_paths.sort();
67     extracted_paths.sort();
69     assert_correct_paths(&file_paths, &extracted_paths, format);
70     compare_file_contents(&extracted_paths, &contents_of_files, format);
73 /// Crate file contents from 1024 up to 8192 random bytes
74 fn generate_random_file_content(rng: &mut impl RngCore) -> FileContent {
75     let quantity = 1024 + rng.next_u32() % (8192 - 1024);
76     let mut vec = vec![0; quantity as usize];
77     rng.fill_bytes(&mut vec);
78     vec
81 /// Create files using the indexes as file names (eg. 0, 1, 2 and 3)
82 ///
83 /// Return the path to each one.
84 fn create_files(at: &Path, contents: &[FileContent]) -> Vec<PathBuf> {
85     contents
86         .iter()
87         .enumerate()
88         .map(|(i, content)| {
89             let path = at.join(i.to_string());
90             let mut file = fs::File::create(&path).expect("Could not create dummy test file");
91             file.write_all(content).expect("Could not write to dummy test file");
92             path
93         })
94         .collect()
97 fn compress_files(at: &Path, paths_to_compress: &[PathBuf], format: &str) -> PathBuf {
98     let archive_path = String::from("archive.") + format;
99     let archive_path = at.join(archive_path);
101     let command = Command::Compress { files: paths_to_compress.to_vec(), output_path: archive_path.to_path_buf() };
102     run(command, &oof::Flags::default()).expect("Failed to compress test dummy files");
104     archive_path
107 fn extract_files(archive_path: &Path) -> Vec<PathBuf> {
108     // We will extract in the same folder as the archive
109     // If the archive is at:
110     //   /tmp/ouch-testing-tar.Rbq4DusBrtF8/archive.tar
111     // Then the extraction_output_folder will be:
112     //   /tmp/ouch-testing-tar.Rbq4DusBrtF8/extraction_results/
113     let mut extraction_output_folder = archive_path.to_path_buf();
114     // Remove the name of the extracted archive
115     assert!(extraction_output_folder.pop());
116     // Add the suffix "results"
117     extraction_output_folder.push("extraction_results");
119     let command = Command::Decompress {
120         files: vec![archive_path.to_owned()],
121         output_folder: Some(extraction_output_folder.clone()),
122     };
123     run(command, &oof::Flags::default()).expect("Failed to extract");
125     fs::read_dir(extraction_output_folder).unwrap().map(Result::unwrap).map(|entry| entry.path()).collect()
128 fn assert_correct_paths(original: &[PathBuf], extracted: &[PathBuf], format: &str) {
129     assert_eq!(
130         original.len(),
131         extracted.len(),
132         "Number of compressed files does not match number of decompressed when testing archive format '{:?}'.",
133         format
134     );
135     for (original, extracted) in original.iter().zip(extracted) {
136         assert_eq!(original.file_name(), extracted.file_name(), "");
137     }
140 fn compare_file_contents(extracted: &[PathBuf], contents: &[FileContent], format: &str) {
141     extracted.iter().zip(contents).for_each(|(extracted_path, expected_content)| {
142         let actual_content = fs::read(extracted_path).unwrap();
144         assert_eq!(
145             expected_content,
146             actual_content.as_slice(),
147             "Contents of file with path '{:?}' does not match after compression and decompression while testing archive format '{:?}.'",
148             extracted_path.canonicalize().unwrap(),
149             format
150         );
151     });