ouch/tests/integration.rs
2023-11-25 21:33:22 -03:00

175 lines
5.0 KiB
Rust

#[macro_use]
mod utils;
use std::{
iter::once,
path::{Path, PathBuf},
};
use fs_err as fs;
use parse_display::Display;
use proptest::sample::size_range;
use rand::{rngs::SmallRng, Rng, SeedableRng};
use tempfile::tempdir;
use test_strategy::{proptest, Arbitrary};
use crate::utils::{assert_same_directory, write_random_content};
// tar and zip extensions
#[derive(Arbitrary, Debug, Display)]
#[display(style = "lowercase")]
enum DirectoryExtension {
#[display("7z")]
SevenZ,
Tar,
Tbz,
Tbz2,
Tgz,
Tlz4,
Tlzma,
Tsz,
Txz,
Tzst,
Zip,
}
// extensions of single file compression formats
#[derive(Arbitrary, Debug, Display)]
#[display(style = "lowercase")]
enum FileExtension {
Bz,
Bz2,
Gz,
Lz4,
Lzma,
Sz,
Xz,
Zst,
}
#[derive(Arbitrary, Debug, Display)]
#[display("{0}")]
enum Extension {
Directory(DirectoryExtension),
File(FileExtension),
}
// converts a list of extension structs to string
fn merge_extensions(ext: impl ToString, exts: Vec<FileExtension>) -> String {
once(ext.to_string())
.chain(exts.into_iter().map(|x| x.to_string()))
.collect::<Vec<_>>()
.join(".")
}
// create random nested directories and files under the specified directory
fn create_random_files(dir: impl Into<PathBuf>, depth: u8, rng: &mut SmallRng) {
if depth == 0 {
return;
}
let dir = &dir.into();
// create 0 to 4 random files
for _ in 0..rng.gen_range(0..=4u32) {
write_random_content(
&mut tempfile::Builder::new().tempfile_in(dir).unwrap().keep().unwrap().0,
rng,
);
}
// create more random files in 0 to 2 new directories
for _ in 0..rng.gen_range(0..=2u32) {
create_random_files(&tempfile::tempdir_in(dir).unwrap().into_path(), depth - 1, rng);
}
}
// compress and decompress a single empty file
#[proptest(cases = 200)]
fn single_empty_file(ext: Extension, #[any(size_range(0..8).lift())] exts: Vec<FileExtension>) {
let dir = tempdir().unwrap();
let dir = dir.path();
let before = &dir.join("before");
fs::create_dir(before).unwrap();
let before_file = &before.join("file");
let archive = &dir.join(format!("file.{}", merge_extensions(ext, exts)));
let after = &dir.join("after");
write_random_content(
&mut fs::File::create(before_file).unwrap(),
&mut SmallRng::from_entropy(),
);
ouch!("-A", "c", before_file, archive);
ouch!("-A", "d", archive, "-d", after);
assert_same_directory(before, after, false);
}
// compress and decompress a single file
#[proptest(cases = 250)]
fn single_file(
ext: Extension,
#[any(size_range(0..8).lift())] exts: Vec<FileExtension>,
#[strategy(proptest::option::of(0i16..12))] level: Option<i16>,
) {
let dir = tempdir().unwrap();
let dir = dir.path();
let before = &dir.join("before");
fs::create_dir(before).unwrap();
let before_file = &before.join("file");
let archive = &dir.join(format!("file.{}", merge_extensions(ext, exts)));
let after = &dir.join("after");
fs::write(before_file, []).unwrap();
if let Some(level) = level {
ouch!("-A", "c", "-l", level.to_string(), before_file, archive);
} else {
ouch!("-A", "c", before_file, archive);
}
ouch!("-A", "d", archive, "-d", after);
assert_same_directory(before, after, false);
}
// compress and decompress a directory with random content generated with create_random_files
//
// this one runs only 50 times because there are only `.zip` and `.tar` to be tested, and
// single-file formats testing is done in the other test
#[proptest(cases = 50)]
fn multiple_files(
ext: DirectoryExtension,
#[any(size_range(0..5).lift())] exts: Vec<FileExtension>,
#[strategy(0u8..4)] depth: u8,
) {
let dir = tempdir().unwrap();
let dir = dir.path();
let before = &dir.join("before");
let before_dir = &before.join("dir");
fs::create_dir_all(before_dir).unwrap();
let archive = &dir.join(format!("archive.{}", merge_extensions(&ext, exts)));
let after = &dir.join("after");
create_random_files(before_dir, depth, &mut SmallRng::from_entropy());
ouch!("-A", "c", before_dir, archive);
ouch!("-A", "d", archive, "-d", after);
assert_same_directory(before, after, !matches!(ext, DirectoryExtension::Zip));
}
// test .rar decompression
fn test_unpack_rar_single(input: &Path) -> Result<(), Box<dyn std::error::Error>> {
let dir = tempdir()?;
let dirpath = dir.path();
let unpacked_path = &dirpath.join("testfile.txt");
ouch!("-A", "d", input, "-d", dirpath);
let content = fs::read_to_string(unpacked_path)?;
assert_eq!(content, "Testing 123\n");
Ok(())
}
#[test]
fn unpack_rar() -> Result<(), Box<dyn std::error::Error>> {
let mut datadir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
datadir.push("tests/data");
["testfile.rar3.rar.gz", "testfile.rar5.rar"]
.iter()
.try_for_each(|path| test_unpack_rar_single(&datadir.join(path)))?;
Ok(())
}