feat: decompress with count
[ouch.git] / tests / integration.rs
blobb67b3ee8481ef28dd60665aff62443ff25943807
1 #[macro_use]
2 mod utils;
4 use std::{
5     iter::once,
6     path::{Path, PathBuf},
7 };
9 use fs_err as fs;
10 use parse_display::Display;
11 use proptest::sample::size_range;
12 use rand::{rngs::SmallRng, Rng, SeedableRng};
13 use tempfile::tempdir;
14 use test_strategy::{proptest, Arbitrary};
16 use crate::utils::{assert_same_directory, write_random_content};
18 // tar and zip extensions
19 #[derive(Arbitrary, Debug, Display)]
20 #[display(style = "lowercase")]
21 enum DirectoryExtension {
22     Tar,
23     Tbz,
24     Tbz2,
25     Tgz,
26     Tlz4,
27     Tlzma,
28     Tsz,
29     Txz,
30     Tzst,
31     Zip,
34 // extensions of single file compression formats
35 #[derive(Arbitrary, Debug, Display)]
36 #[display(style = "lowercase")]
37 enum FileExtension {
38     Bz,
39     Bz2,
40     Gz,
41     Lz4,
42     Lzma,
43     Sz,
44     Xz,
45     Zst,
48 #[derive(Arbitrary, Debug, Display)]
49 #[display("{0}")]
50 enum Extension {
51     Directory(DirectoryExtension),
52     File(FileExtension),
55 // converts a list of extension structs to string
56 fn merge_extensions(ext: impl ToString, exts: Vec<FileExtension>) -> String {
57     once(ext.to_string())
58         .chain(exts.into_iter().map(|x| x.to_string()))
59         .collect::<Vec<_>>()
60         .join(".")
63 // create random nested directories and files under the specified directory
64 fn create_random_files(dir: impl Into<PathBuf>, depth: u8, rng: &mut SmallRng) {
65     if depth == 0 {
66         return;
67     }
69     let dir = &dir.into();
71     // create 0 to 4 random files
72     for _ in 0..rng.gen_range(0..=4u32) {
73         write_random_content(
74             &mut tempfile::Builder::new().tempfile_in(dir).unwrap().keep().unwrap().0,
75             rng,
76         );
77     }
79     // create more random files in 0 to 2 new directories
80     for _ in 0..rng.gen_range(0..=2u32) {
81         create_random_files(&tempfile::tempdir_in(dir).unwrap().into_path(), depth - 1, rng);
82     }
85 // compress and decompress a single empty file
86 #[proptest(cases = 200)]
87 fn single_empty_file(ext: Extension, #[any(size_range(0..8).lift())] exts: Vec<FileExtension>) {
88     let dir = tempdir().unwrap();
89     let dir = dir.path();
90     let before = &dir.join("before");
91     fs::create_dir(before).unwrap();
92     let before_file = &before.join("file");
93     let archive = &dir.join(format!("file.{}", merge_extensions(ext, exts)));
94     let after = &dir.join("after");
95     write_random_content(
96         &mut fs::File::create(before_file).unwrap(),
97         &mut SmallRng::from_entropy(),
98     );
99     ouch!("-A", "c", before_file, archive);
100     ouch!("-A", "d", archive, "-d", after);
101     assert_same_directory(before, after, false);
104 // compress and decompress a single file
105 #[proptest(cases = 250)]
106 fn single_file(
107     ext: Extension,
108     #[any(size_range(0..8).lift())] exts: Vec<FileExtension>,
109     #[strategy(proptest::option::of(0i16..12))] level: Option<i16>,
110 ) {
111     let dir = tempdir().unwrap();
112     let dir = dir.path();
113     let before = &dir.join("before");
114     fs::create_dir(before).unwrap();
115     let before_file = &before.join("file");
116     let archive = &dir.join(format!("file.{}", merge_extensions(ext, exts)));
117     let after = &dir.join("after");
118     fs::write(before_file, []).unwrap();
119     if let Some(level) = level {
120         ouch!("-A", "c", "-l", level.to_string(), before_file, archive);
121     } else {
122         ouch!("-A", "c", before_file, archive);
123     }
124     ouch!("-A", "d", archive, "-d", after);
125     assert_same_directory(before, after, false);
128 // compress and decompress a directory with random content generated with create_random_files
130 // this one runs only 50 times because there are only `.zip` and `.tar` to be tested, and
131 // single-file formats testing is done in the other test
132 #[proptest(cases = 50)]
133 fn multiple_files(
134     ext: DirectoryExtension,
135     #[any(size_range(0..5).lift())] exts: Vec<FileExtension>,
136     #[strategy(0u8..4)] depth: u8,
137 ) {
138     let dir = tempdir().unwrap();
139     let dir = dir.path();
140     let before = &dir.join("before");
141     let before_dir = &before.join("dir");
142     fs::create_dir_all(before_dir).unwrap();
143     let archive = &dir.join(format!("archive.{}", merge_extensions(&ext, exts)));
144     let after = &dir.join("after");
145     create_random_files(before_dir, depth, &mut SmallRng::from_entropy());
146     ouch!("-A", "c", before_dir, archive);
147     ouch!("-A", "d", archive, "-d", after);
148     assert_same_directory(before, after, !matches!(ext, DirectoryExtension::Zip));
151 // test .rar decompression
152 fn test_unpack_rar_single(input: &Path) -> Result<(), Box<dyn std::error::Error>> {
153     let dir = tempdir()?;
154     let dirpath = dir.path();
155     let unpacked_path = &dirpath.join("testfile.txt");
156     ouch!("-A", "d", input, "-d", dirpath);
157     let content = fs::read_to_string(unpacked_path)?;
158     assert_eq!(content, "Testing 123\n");
160     Ok(())
163 #[test]
164 fn unpack_rar() -> Result<(), Box<dyn std::error::Error>> {
165     let mut datadir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
166     datadir.push("tests/data");
167     ["testfile.rar3.rar.gz", "testfile.rar5.rar"]
168         .iter()
169         .try_for_each(|path| test_unpack_rar_single(&datadir.join(path)))?;
171     Ok(())