rewrite tests

This commit is contained in:
figsoda 2021-11-04 09:28:19 -04:00
parent 739a52f00e
commit 35df50857d
8 changed files with 477 additions and 322 deletions

3
.gitignore vendored
View File

@ -6,3 +6,6 @@ makeshift_testing.py
# These are backup files generated by rustfmt
**/*.rs.bk
# crash logs generated by proptest
*.proptest-regressions

284
Cargo.lock generated
View File

@ -8,6 +8,29 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "assert_cmd"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e996dc7940838b7ef1096b882e29ec30a3149a3a443cdc8dba19ed382eca1fe2"
dependencies = [
"bstr",
"doc-comment",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "atty"
version = "0.2.14"
@ -25,12 +48,38 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bit-set"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
]
[[package]]
name = "byteorder"
version = "1.4.3"
@ -132,6 +181,33 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "difflib"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "dir-diff"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2860407d7d7e2e004bb2128510ad9e8d669e76fa005ccf567977b5d71b8b4a0b"
dependencies = [
"walkdir",
]
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "filetime"
version = "0.2.15"
@ -157,6 +233,12 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fs-err"
version = "2.6.0"
@ -217,6 +299,15 @@ dependencies = [
"cfb",
]
[[package]]
name = "itertools"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
dependencies = [
"either",
]
[[package]]
name = "jobserver"
version = "0.1.24"
@ -291,6 +382,15 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.8.0"
@ -310,10 +410,12 @@ dependencies = [
name = "ouch"
version = "0.3.1"
dependencies = [
"assert_cmd",
"atty",
"bzip2",
"clap",
"clap_generate",
"dir-diff",
"flate2",
"fs-err",
"infer",
@ -321,15 +423,44 @@ dependencies = [
"linked-hash-map",
"lz4_flex",
"once_cell",
"parse-display",
"proptest",
"rand",
"tar",
"tempfile",
"test-strategy",
"walkdir",
"xz2",
"zip",
"zstd",
]
[[package]]
name = "parse-display"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "898bf4c2a569dedbfd4e6c3f0bbd0ae825e5b6b0b69bae3e3c1000158689334a"
dependencies = [
"once_cell",
"parse-display-derive",
"regex",
]
[[package]]
name = "parse-display-derive"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1779d1e28ab04568223744c2af4aa4e642e67b92c76bdce0929a6d2c36267199"
dependencies = [
"once_cell",
"proc-macro2",
"quote",
"regex",
"regex-syntax",
"structmeta",
"syn",
]
[[package]]
name = "pkg-config"
version = "0.3.22"
@ -342,6 +473,33 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
[[package]]
name = "predicates"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6ce811d0b2e103743eec01db1c50612221f173084ce2f7941053e94b6bb474"
dependencies = [
"difflib",
"itertools",
"predicates-core",
]
[[package]]
name = "predicates-core"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
[[package]]
name = "predicates-tree"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "338c7be2905b732ae3984a2f40032b5e94fd8f52505b186c7d4d68d193445df7"
dependencies = [
"predicates-core",
"termtree",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -375,6 +533,38 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "proptest"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5"
dependencies = [
"bit-set",
"bitflags",
"byteorder",
"lazy_static",
"num-traits",
"quick-error 2.0.1",
"rand",
"rand_chacha",
"rand_xorshift",
"regex-syntax",
"rusty-fork",
"tempfile",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
version = "1.0.10"
@ -424,6 +614,15 @@ dependencies = [
"rand_core",
]
[[package]]
name = "rand_xorshift"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
@ -433,6 +632,29 @@ dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
@ -442,6 +664,18 @@ dependencies = [
"winapi",
]
[[package]]
name = "rusty-fork"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
dependencies = [
"fnv",
"quick-error 1.2.3",
"tempfile",
"wait-timeout",
]
[[package]]
name = "same-file"
version = "1.0.6"
@ -463,6 +697,29 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "structmeta"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59915b528a896f2e3bfa1a6ace65f7bb0ff9f9863de6213b0c01cb6fd3c3ac71"
dependencies = [
"proc-macro2",
"quote",
"structmeta-derive",
"syn",
]
[[package]]
name = "structmeta-derive"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b73800bcca56045d5ab138a48cd28a96093335335deaa916f22b5749c4150c79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.81"
@ -508,6 +765,24 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "termtree"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13a4ec180a2de59b57434704ccfad967f789b12737738798fa08798cd5824c16"
[[package]]
name = "test-strategy"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22c726321a7c108ca1de4ed2e6a362ead7193ecfbe0b326c5dff602b65a09e6a"
dependencies = [
"proc-macro2",
"quote",
"structmeta",
"syn",
]
[[package]]
name = "textwrap"
version = "0.14.2"
@ -592,6 +867,15 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]]
name = "walkdir"
version = "2.3.2"

View File

@ -33,9 +33,14 @@ clap = "=3.0.0-beta.5"
clap_generate = "=3.0.0-beta.5"
[dev-dependencies]
tempfile = "3.2.0"
assert_cmd = "2.0.2"
dir-diff = "0.3.2"
infer = "0.5.0"
parse-display = "0.5.3"
proptest = "1.0.0"
rand = { version = "0.8.3", default-features = false, features = ["small_rng", "std"] }
tempfile = "3.2.0"
test-strategy = "0.1.2"
[profile.release]
lto = true

View File

@ -1,215 +0,0 @@
mod utils;
use std::{
env,
io::prelude::*,
path::{Path, PathBuf},
time::Duration,
};
use fs_err as fs;
use ouch::{commands::run, Opts, QuestionPolicy, Subcommand};
use rand::{rngs::SmallRng, RngCore, SeedableRng};
use tempfile::NamedTempFile;
use utils::*;
#[test]
/// Makes sure that the files ouch produces are what they claim to be, checking their
/// types through MIME sniffing.
fn sanity_check_through_mime() {
// Somehow this test causes test failures when run in parallel with test_each_format
// This is a temporary hack that should allow the tests to pass while this bug isn't solved.
std::thread::sleep(Duration::from_secs(2));
let temp_dir = tempfile::tempdir().expect("to build a temporary directory");
let mut test_file = NamedTempFile::new_in(temp_dir.path()).expect("to be able to build a temporary file");
let bytes = generate_random_file_content(&mut SmallRng::from_entropy());
test_file.write_all(&bytes).expect("to successfully write bytes to the file");
let formats = [
"tar", "zip", "tar.gz", "tgz", "tbz", "tbz2", "txz", "tlz", "tlzma", "tzst", "tar.bz", "tar.bz2", "tar.lzma",
"tar.xz", "tar.zst",
];
let expected_mimes = [
"application/x-tar",
"application/zip",
"application/gzip",
"application/gzip",
"application/x-bzip2",
"application/x-bzip2",
"application/x-xz",
"application/x-xz",
"application/x-xz",
"application/zstd",
"application/x-bzip2",
"application/x-bzip2",
"application/x-xz",
"application/x-xz",
"application/zstd",
];
assert_eq!(formats.len(), expected_mimes.len());
for (format, expected_mime) in formats.iter().zip(expected_mimes.iter()) {
let temp_dir_path = temp_dir.path();
let paths_to_compress = &[test_file.path().into()];
let compressed_file_path = compress_files(temp_dir_path, paths_to_compress, format);
let sniffed =
infer::get_from_path(compressed_file_path).expect("the file to be read").expect("the MIME to be found");
assert_eq!(&sniffed.mime_type(), expected_mime);
}
}
#[test]
/// Tests each format that supports multiple files with random input.
/// TODO: test the remaining formats.
fn test_each_format() {
test_compressing_and_decompressing_archive("tar");
test_compressing_and_decompressing_archive("tar.gz");
test_compressing_and_decompressing_archive("tar.bz");
test_compressing_and_decompressing_archive("tar.bz2");
test_compressing_and_decompressing_archive("tar.xz");
test_compressing_and_decompressing_archive("tar.lz");
test_compressing_and_decompressing_archive("tar.lz4");
test_compressing_and_decompressing_archive("tar.lzma");
test_compressing_and_decompressing_archive("tar.zst");
test_compressing_and_decompressing_archive("tgz");
test_compressing_and_decompressing_archive("tbz");
test_compressing_and_decompressing_archive("tbz2");
test_compressing_and_decompressing_archive("txz");
test_compressing_and_decompressing_archive("tlz4");
test_compressing_and_decompressing_archive("tlz");
test_compressing_and_decompressing_archive("tlzma");
test_compressing_and_decompressing_archive("tzst");
test_compressing_and_decompressing_archive("zip");
test_compressing_and_decompressing_archive("zip.gz");
test_compressing_and_decompressing_archive("zip.bz");
test_compressing_and_decompressing_archive("zip.bz2");
test_compressing_and_decompressing_archive("zip.xz");
test_compressing_and_decompressing_archive("zip.lz");
test_compressing_and_decompressing_archive("zip.lzma");
// Why not
test_compressing_and_decompressing_archive(
"tar.gz.gz.gz.gz.gz.gz.gz.gz.zst.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",
);
}
type FileContent = Vec<u8>;
/// Compress and decompresses random files to archive formats, checks if contents match
fn test_compressing_and_decompressing_archive(format: &str) {
// System temporary directory depends on the platform, for linux it's /tmp
let system_tmp = env::temp_dir();
// Create a temporary testing folder that will be deleted on scope drop
let testing_dir =
tempfile::Builder::new().prefix("ouch-testing").tempdir_in(system_tmp).expect("Could not create testing_dir");
let testing_dir_path = testing_dir.path();
// Quantity of compressed files vary from 1 to 10
let mut rng = SmallRng::from_entropy();
let quantity_of_files = rng.next_u32() % 10 + 1;
let contents_of_files: Vec<FileContent> =
(0..quantity_of_files).map(|_| generate_random_file_content(&mut rng)).collect();
// Create them
let mut file_paths = create_files(testing_dir_path, &contents_of_files);
// Compress them
let compressed_archive_path = compress_files(testing_dir_path, &file_paths, format);
// Decompress them
let mut extracted_paths = extract_files(&compressed_archive_path);
// // DEBUG UTIL:
// // Uncomment line below to freeze the code and see compressed and extracted files in
// // the temporary directory before their auto-destruction.
// std::thread::sleep(std::time::Duration::from_secs(1_000_000));
file_paths.sort();
extracted_paths.sort();
assert_correct_paths(&file_paths, &extracted_paths, format);
compare_file_contents(&extracted_paths, &contents_of_files, format);
}
/// Crate file contents from 1024 up to 8192 random bytes
fn generate_random_file_content(rng: &mut impl RngCore) -> FileContent {
let quantity = 1024 + rng.next_u32() % (8192 - 1024);
let mut vec = vec![0; quantity as usize];
rng.fill_bytes(&mut vec);
vec
}
/// Create files using the indexes as file names (eg. 0, 1, 2 and 3)
///
/// Return the path to each one.
fn create_files(at: &Path, contents: &[FileContent]) -> Vec<PathBuf> {
contents
.iter()
.enumerate()
.map(|(i, content)| {
let path = at.join(i.to_string());
let mut file = fs::File::create(&path).expect("Could not create dummy test file");
file.write_all(content).expect("Could not write to dummy test file");
path
})
.collect()
}
fn extract_files(archive_path: &Path) -> Vec<PathBuf> {
// We will extract in the same folder as the archive
// If the archive is at:
// /tmp/ouch-testing-tar.Rbq4DusBrtF8/archive.tar
// Then the extraction_output_folder will be:
// /tmp/ouch-testing-tar.Rbq4DusBrtF8/extraction_results/
let mut extraction_output_folder = archive_path.to_path_buf();
// Remove the name of the extracted archive
assert!(extraction_output_folder.pop());
// Add the suffix "results"
extraction_output_folder.push("extraction_results");
let command = Opts {
yes: false,
no: false,
cmd: Subcommand::Decompress {
files: vec![archive_path.to_owned()],
output_dir: Some(extraction_output_folder.clone()),
},
};
run(command, QuestionPolicy::Ask).expect("Failed to extract");
fs::read_dir(extraction_output_folder).unwrap().map(Result::unwrap).map(|entry| entry.path()).collect()
}
fn assert_correct_paths(original: &[PathBuf], extracted: &[PathBuf], format: &str) {
assert_eq!(
original.len(),
extracted.len(),
"Number of compressed files does not match number of decompressed when testing archive format '{:?}'.",
format
);
for (original, extracted) in original.iter().zip(extracted) {
assert_eq!(original.file_name(), extracted.file_name(), "");
}
}
fn compare_file_contents(extracted: &[PathBuf], contents: &[FileContent], format: &str) {
extracted.iter().zip(contents).for_each(|(extracted_path, expected_content)| {
let actual_content = fs::read(extracted_path).unwrap();
assert_eq!(
expected_content,
actual_content.as_slice(),
"Contents of file with path '{:?}' does not match after compression and decompression while testing archive format '{:?}.'",
extracted_path.canonicalize().unwrap(),
format
);
});
}

View File

@ -1,42 +0,0 @@
mod utils;
use std::{env, path::PathBuf};
use utils::*;
#[test]
fn test_each_format() {
test_compress_decompress_with_empty_dir("tar");
test_compress_decompress_with_empty_dir("zip");
}
fn test_compress_decompress_with_empty_dir(format: &str) {
// System temporary directory depends on the platform, for linux it's /tmp
let system_tmp = env::temp_dir();
// Create a temporary testing folder that will be deleted on scope drop
let testing_dir =
tempfile::Builder::new().prefix("ouch-testing").tempdir_in(system_tmp).expect("Could not create testing_dir");
let testing_dir_path = testing_dir.path();
let empty_dir_path: PathBuf = create_empty_dir(testing_dir_path, "dummy_empty_dir_name");
let mut file_paths: Vec<PathBuf> = vec![empty_dir_path];
let compressed_archive_path: PathBuf = compress_files(testing_dir_path, &file_paths, format);
let mut extracted_paths = extract_files(&compressed_archive_path);
// // DEBUG UTIL:
// // Uncomment line below to freeze the code and see compressed and extracted files in
// // the temporary directory before their auto-destruction.
// std::thread::sleep(std::time::Duration::from_secs(10));
// no need to sort a unitary value vector but i will keep this
// for retrocompatibility, for now.
file_paths.sort();
extracted_paths.sort();
assert_correct_paths(&file_paths, &extracted_paths, format);
}

114
tests/integration.rs Normal file
View File

@ -0,0 +1,114 @@
#[macro_use]
mod utils;
use std::{iter::once, path::PathBuf};
use fs_err as fs;
use parse_display::Display;
use proptest::sample::size_range;
use rand::{rngs::SmallRng, RngCore, SeedableRng};
use tempfile::tempdir;
use test_strategy::{proptest, Arbitrary};
use crate::utils::create_file_random;
#[derive(Arbitrary, Debug, Display)]
#[display(style = "snake_case")]
enum DirectoryExtension {
Tar,
Tbz,
Tgz,
Tlz4,
Tlz,
Tlzma,
Txz,
Zip,
}
#[derive(Arbitrary, Debug, Display)]
#[display(style = "snake_case")]
enum FileExtension {
Bz,
Bz2,
Gz,
// Lz4, // broken
Lz,
Lzma,
Xz,
}
#[derive(Arbitrary, Debug, Display)]
#[display("{0}")]
enum Extension {
Directory(DirectoryExtension),
File(FileExtension),
}
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(".")
}
fn create_random_files(dir: impl Into<PathBuf>, depth: u8, rng: &mut SmallRng) {
if depth == 0 {
return;
}
let dir = &dir.into();
for _ in 0..rng.next_u32() % 8 {
create_file_random(&mut tempfile::Builder::new().tempfile_in(dir).unwrap().keep().unwrap().0, rng);
}
for _ in 0..rng.next_u32() % 4 {
create_random_files(&tempfile::tempdir_in(dir).unwrap().into_path(), depth - 1, rng);
}
}
#[proptest(cases = 512)]
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");
create_file_random(&mut fs::File::create(before_file).unwrap(), &mut SmallRng::from_entropy());
ouch!("c", before_file, archive);
ouch!("d", archive, "-d", after);
assert!(!dir_diff::is_different(before, after).unwrap());
}
#[proptest(cases = 512)]
fn single_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");
fs::write(before_file, []).unwrap();
ouch!("c", before_file, archive);
ouch!("d", archive, "-d", after);
assert!(!dir_diff::is_different(before, after).unwrap());
}
#[proptest(cases = 512)]
fn multiple_files(
ext: DirectoryExtension,
#[any(size_range(0..8).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!("c", before_dir, archive);
ouch!("d", archive, "-d", after);
assert!(!dir_diff::is_different(before, after).unwrap());
}

55
tests/mime.rs Normal file
View File

@ -0,0 +1,55 @@
#[macro_use]
mod utils;
use rand::{rngs::SmallRng, SeedableRng};
use tempfile::NamedTempFile;
use crate::utils::create_file_random;
#[test]
/// Makes sure that the files ouch produces are what they claim to be, checking their
/// types through MIME sniffing.
fn sanity_check_through_mime() {
let temp_dir = tempfile::tempdir().expect("to build a temporary directory");
let temp_dir_path = temp_dir.path();
let test_file = &mut NamedTempFile::new_in(temp_dir_path).expect("to be able to build a temporary file");
create_file_random(test_file, &mut SmallRng::from_entropy());
let formats = [
"tar", "zip", "tar.gz", "tgz", "tbz", "tbz2", "txz", "tlz", "tlzma", "tzst", "tar.bz", "tar.bz2", "tar.lzma",
"tar.xz", "tar.zst",
];
let expected_mimes = [
"application/x-tar",
"application/zip",
"application/gzip",
"application/gzip",
"application/x-bzip2",
"application/x-bzip2",
"application/x-xz",
"application/x-xz",
"application/x-xz",
"application/zstd",
"application/x-bzip2",
"application/x-bzip2",
"application/x-xz",
"application/x-xz",
"application/zstd",
];
assert_eq!(formats.len(), expected_mimes.len());
for (format, expected_mime) in formats.iter().zip(expected_mimes.iter()) {
let path_to_compress = test_file.path();
let compressed_file_path = &format!("{}.{}", path_to_compress.display(), format);
ouch!("c", path_to_compress, compressed_file_path);
let sniffed =
infer::get_from_path(compressed_file_path).expect("the file to be read").expect("the MIME to be found");
assert_eq!(&sniffed.mime_type(), expected_mime);
}
}

View File

@ -1,68 +1,19 @@
//! Files in common between one or more integration tests
use std::io::Write;
#![allow(dead_code)]
use rand::RngCore;
use std::path::{Path, PathBuf};
use fs_err as fs;
use ouch::{commands::run, Opts, QuestionPolicy, Subcommand};
pub fn create_empty_dir(at: &Path, filename: &str) -> PathBuf {
let dirname = Path::new(filename);
let full_path = at.join(dirname);
fs::create_dir(&full_path).expect("Failed to create an empty directory");
full_path
}
pub fn compress_files(at: &Path, paths_to_compress: &[PathBuf], format: &str) -> PathBuf {
let archive_path = String::from("archive.") + format;
let archive_path = at.join(archive_path);
let command = Opts {
yes: false,
no: false,
cmd: Subcommand::Compress { files: paths_to_compress.to_vec(), output: archive_path.clone() },
};
run(command, QuestionPolicy::Ask).expect("Failed to compress test dummy files");
archive_path
}
pub fn extract_files(archive_path: &Path) -> Vec<PathBuf> {
// We will extract in the same folder as the archive
// If the archive is at:
// /tmp/ouch-testing-tar.Rbq4DusBrtF8/archive.tar
// Then the extraction_output_folder will be:
// /tmp/ouch-testing-tar.Rbq4DusBrtF8/extraction_results/
let mut extraction_output_folder = archive_path.to_path_buf();
// Remove the name of the extracted archive
assert!(extraction_output_folder.pop());
// Add the suffix "results"
extraction_output_folder.push("extraction_results");
let command = Opts {
yes: false,
no: false,
cmd: Subcommand::Decompress {
files: vec![archive_path.to_owned()],
output_dir: Some(extraction_output_folder.clone()),
},
};
run(command, QuestionPolicy::Ask).expect("Failed to extract");
fs::read_dir(extraction_output_folder).unwrap().map(Result::unwrap).map(|entry| entry.path()).collect()
}
pub fn assert_correct_paths(original: &[PathBuf], extracted: &[PathBuf], format: &str) {
assert_eq!(
original.len(),
extracted.len(),
"Number of compressed files does not match number of decompressed when testing archive format '{:?}'.",
format
);
for (original, extracted) in original.iter().zip(extracted) {
assert_eq!(original.file_name(), extracted.file_name());
#[macro_export]
macro_rules! ouch {
($($e:expr),*) => {
::assert_cmd::Command::cargo_bin("ouch")
.expect("Failed to find ouch executable")
$(.arg($e))*
.unwrap();
}
}
pub fn create_file_random(file: &mut impl Write, rng: &mut impl RngCore) {
let data = &mut Vec::with_capacity((rng.next_u32() % 8192) as usize);
rng.fill_bytes(data);
file.write_all(data).unwrap();
}