4 use std::{iter::once, path::PathBuf};
7 use parse_display::Display;
8 use proptest::sample::size_range;
9 use rand::{rngs::SmallRng, RngCore, 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 {
29 // extensions of single file compression formats
30 #[derive(Arbitrary, Debug, Display)]
31 #[display(style = "lowercase")]
41 #[derive(Arbitrary, Debug, Display)]
44 Directory(DirectoryExtension),
48 // converts a list of extension structs to string
49 fn merge_extensions(ext: impl ToString, exts: Vec<FileExtension>) -> String {
50 once(ext.to_string()).chain(exts.into_iter().map(|x| x.to_string())).collect::<Vec<_>>().join(".")
53 // create random nested directories and files under the specified directory
54 fn create_random_files(dir: impl Into<PathBuf>, depth: u8, rng: &mut SmallRng) {
59 let dir = &dir.into();
61 // create 0 to 7 random files
62 for _ in 0..rng.next_u32() % 8 {
63 write_random_content(&mut tempfile::Builder::new().tempfile_in(dir).unwrap().keep().unwrap().0, rng);
66 // create more random files in 0 to 3 new directories
67 for _ in 0..rng.next_u32() % 4 {
68 create_random_files(&tempfile::tempdir_in(dir).unwrap().into_path(), depth - 1, rng);
72 // compress and decompress a single empty file
73 #[proptest(cases = 512)]
74 fn single_empty_file(ext: Extension, #[any(size_range(0..8).lift())] exts: Vec<FileExtension>) {
75 let dir = tempdir().unwrap();
77 let before = &dir.join("before");
78 fs::create_dir(before).unwrap();
79 let before_file = &before.join("file");
80 let archive = &dir.join(format!("file.{}", merge_extensions(ext, exts)));
81 let after = &dir.join("after");
82 write_random_content(&mut fs::File::create(before_file).unwrap(), &mut SmallRng::from_entropy());
83 ouch!("c", before_file, archive);
84 ouch!("d", archive, "-d", after);
85 assert_same_directory(before, after, false);
88 // compress and decompress a single file
89 #[proptest(cases = 512)]
90 fn single_file(ext: Extension, #[any(size_range(0..8).lift())] exts: Vec<FileExtension>) {
91 let dir = tempdir().unwrap();
93 let before = &dir.join("before");
94 fs::create_dir(before).unwrap();
95 let before_file = &before.join("file");
96 let archive = &dir.join(format!("file.{}", merge_extensions(ext, exts)));
97 let after = &dir.join("after");
98 fs::write(before_file, []).unwrap();
99 ouch!("c", before_file, archive);
100 ouch!("d", archive, "-d", after);
101 assert_same_directory(before, after, false);
104 // compress and decompress a directory with random content generated with create_random_files
105 #[proptest(cases = 512)]
107 ext: DirectoryExtension,
108 #[any(size_range(0..8).lift())] exts: Vec<FileExtension>,
109 #[strategy(0u8..4)] depth: u8,
111 let dir = tempdir().unwrap();
112 let dir = dir.path();
113 let before = &dir.join("before");
114 let before_dir = &before.join("dir");
115 fs::create_dir_all(before_dir).unwrap();
116 let archive = &dir.join(format!("archive.{}", merge_extensions(&ext, exts)));
117 let after = &dir.join("after");
118 create_random_files(before_dir, depth, &mut SmallRng::from_entropy());
119 ouch!("c", before_dir, archive);
120 ouch!("d", archive, "-d", after);
121 assert_same_directory(before, after, !matches!(ext, DirectoryExtension::Zip));