4 use std::{iter::once, path::PathBuf};
7 use parse_display::Display;
8 use proptest::sample::size_range;
9 use rand::{rngs::SmallRng, Rng, SeedableRng};
10 use tempfile::tempdir;
11 use test_strategy::{proptest, Arbitrary};
13 use crate::utils::{assert_same_directory, write_random_content};
15 // tar and zip extensions
16 #[derive(Arbitrary, Debug, Display)]
17 #[display(style = "lowercase")]
18 enum DirectoryExtension {
31 // extensions of single file compression formats
32 #[derive(Arbitrary, Debug, Display)]
33 #[display(style = "lowercase")]
45 #[derive(Arbitrary, Debug, Display)]
48 Directory(DirectoryExtension),
52 // converts a list of extension structs to string
53 fn merge_extensions(ext: impl ToString, exts: Vec<FileExtension>) -> String {
55 .chain(exts.into_iter().map(|x| x.to_string()))
60 // create random nested directories and files under the specified directory
61 fn create_random_files(dir: impl Into<PathBuf>, depth: u8, rng: &mut SmallRng) {
66 let dir = &dir.into();
68 // create 0 to 7 random files
69 for _ in 0..rng.gen_range(0..8u32) {
71 &mut tempfile::Builder::new().tempfile_in(dir).unwrap().keep().unwrap().0,
76 // create more random files in 0 to 3 new directories
77 for _ in 0..rng.gen_range(0..4u32) {
78 create_random_files(&tempfile::tempdir_in(dir).unwrap().into_path(), depth - 1, rng);
82 // compress and decompress a single empty file
83 #[proptest(cases = 512)]
84 fn single_empty_file(ext: Extension, #[any(size_range(0..8).lift())] exts: Vec<FileExtension>) {
85 let dir = tempdir().unwrap();
87 let before = &dir.join("before");
88 fs::create_dir(before).unwrap();
89 let before_file = &before.join("file");
90 let archive = &dir.join(format!("file.{}", merge_extensions(ext, exts)));
91 let after = &dir.join("after");
93 &mut fs::File::create(before_file).unwrap(),
94 &mut SmallRng::from_entropy(),
96 ouch!("-A", "c", before_file, archive);
97 ouch!("-A", "d", archive, "-d", after);
98 assert_same_directory(before, after, false);
101 // compress and decompress a single file
102 #[proptest(cases = 512)]
103 fn single_file(ext: Extension, #[any(size_range(0..8).lift())] exts: Vec<FileExtension>) {
104 let dir = tempdir().unwrap();
105 let dir = dir.path();
106 let before = &dir.join("before");
107 fs::create_dir(before).unwrap();
108 let before_file = &before.join("file");
109 let archive = &dir.join(format!("file.{}", merge_extensions(ext, exts)));
110 let after = &dir.join("after");
111 fs::write(before_file, []).unwrap();
112 ouch!("-A", "c", before_file, archive);
113 ouch!("-A", "d", archive, "-d", after);
114 assert_same_directory(before, after, false);
117 // compress and decompress a directory with random content generated with create_random_files
118 #[proptest(cases = 512)]
120 ext: DirectoryExtension,
121 #[any(size_range(0..8).lift())] exts: Vec<FileExtension>,
122 #[strategy(0u8..4)] depth: u8,
124 let dir = tempdir().unwrap();
125 let dir = dir.path();
126 let before = &dir.join("before");
127 let before_dir = &before.join("dir");
128 fs::create_dir_all(before_dir).unwrap();
129 let archive = &dir.join(format!("archive.{}", merge_extensions(&ext, exts)));
130 let after = &dir.join("after");
131 create_random_files(before_dir, depth, &mut SmallRng::from_entropy());
132 ouch!("-A", "c", before_dir, archive);
133 ouch!("-A", "d", archive, "-d", after);
134 assert_same_directory(before, after, !matches!(ext, DirectoryExtension::Zip));