diff --git a/Cargo.lock b/Cargo.lock index 0e9bd8847..314c7559e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,16 +98,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814" dependencies = [ "byte-tools", - "generic-array 0.8.4", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array 0.14.4", + "generic-array", ] [[package]] @@ -569,16 +560,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" dependencies = [ - "generic-array 0.8.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.4", + "generic-array", ] [[package]] @@ -655,16 +637,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "generic-array" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getopts" version = "0.2.21" @@ -682,7 +654,18 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", ] [[package]] @@ -718,12 +701,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" -[[package]] -name = "hex-literal" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5af1f635ef1bc545d78392b136bfe1c9809e029023c84a3638a864a10b8819c8" - [[package]] name = "hostname" version = "0.3.1" @@ -832,17 +809,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -[[package]] -name = "md-5" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "md5" version = "0.3.8" @@ -973,12 +939,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "paste" version = "0.1.18" @@ -1115,14 +1075,26 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", "libc", - "rand_chacha", + "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc", + "rand_hc 0.2.0", "rand_pcg", ] +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.3", + "rand_hc 0.3.1", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -1133,6 +1105,16 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -1154,7 +1136,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.3", ] [[package]] @@ -1166,6 +1157,15 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.3", +] + [[package]] name = "rand_pcg" version = "0.2.1" @@ -1366,11 +1366,11 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d963c78ce367df26d7ea8b8cc655c651b42e8a1e584e869c1e17dae3ccb116a" dependencies = [ - "block-buffer 0.2.0", + "block-buffer", "byte-tools", - "digest 0.6.2", + "digest", "fake-simd", - "generic-array 0.8.4", + "generic-array", ] [[package]] @@ -1379,10 +1379,10 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26405905b6a56a94c60109cfda62610507ac14a65be531f5767dec5c5a8dd6a0" dependencies = [ - "block-buffer 0.2.0", + "block-buffer", "byte-tools", - "digest 0.6.2", - "generic-array 0.8.4", + "digest", + "generic-array", ] [[package]] @@ -1759,10 +1759,10 @@ dependencies = [ "debug_print", "gcd", "getopts", - "hex-literal", "libc", - "md-5", + "rand 0.8.4", "signal-hook", + "tempfile", "uucore", "uucore_procs", ] @@ -1854,7 +1854,7 @@ dependencies = [ "paste", "quickcheck", "rand 0.7.3", - "rand_chacha", + "rand_chacha 0.2.2", "smallvec", "uucore", "uucore_procs", @@ -1902,7 +1902,7 @@ version = "0.0.4" dependencies = [ "blake2-rfc", "clap", - "digest 0.6.2", + "digest", "hex", "libc", "md5", @@ -2621,12 +2621,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - [[package]] name = "void" version = "1.0.2" @@ -2650,6 +2644,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasm-bindgen" version = "0.2.71" diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index e10fd7f07..df7dbf483 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -17,7 +17,7 @@ path = "src/dd.rs" [dependencies] byte-unit = "4.0" debug_print = "1.0" -# Probably best to keep this identical to the version of getopts in the uucore crate +# Probably best to keep the getopts version identical to the version of getopts in the uucore crate getopts = "<= 0.2.21" gcd = "2.0" libc = "0.2" @@ -26,8 +26,8 @@ uucore = { version=">=0.0.7", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } [dev-dependencies] -md-5 = "0.9" -hex-literal = "0.3" +rand = "0.8" +tempfile = "^3" [[bin]] name = "dd" diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 459c66e54..74ee24c46 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -361,19 +361,32 @@ impl Read for Input { fn read(&mut self, mut buf: &mut [u8]) -> io::Result { - match self.src.read(&mut buf) + let mut base_idx = 0; + let tlen = buf.len(); + loop { - Ok(len) => - Ok(len), - Err(e) => - if !self.cflags.noerror + match self.src.read(&mut buf[base_idx..]) + { + Ok(0) => + return Ok(base_idx), + Ok(rlen) if self.iflags.fullblock => { - Err(e) - } - else - { - Ok(0) + base_idx += rlen; + + if base_idx >= tlen + { + return Ok(tlen) + } }, + Ok(len) => + return Ok(len), + Err(e) if e.kind() == io::ErrorKind::Interrupted => + continue, + Err(_) if self.cflags.noerror => + return Ok(base_idx), + Err(e) => + return Err(e), + } } } } @@ -529,6 +542,11 @@ fn make_unix_oflags(oflags: &OFlags) -> Option { let mut flag = 0; + // oflag=FLAG + if oflags.append + { + flag |= libc::O_APPEND; + } if oflags.direct { flag |= libc::O_DIRECT; @@ -585,9 +603,11 @@ impl Output { let mut dst = { let mut opts = OpenOptions::new(); opts.write(true) + .create(true) + .truncate(!cflags.notrunc) .append(oflags.append) - .create_new(cflags.excl || !cflags.nocreat) - .truncate(!cflags.notrunc); + // 'create_new' overrides 'create' + .create_new(cflags.excl && !cflags.nocreat); if cfg!(unix) { @@ -1484,47 +1504,57 @@ fn append_dashes_if_not_present(mut acc: Vec, s: &String) -> Vec } } +macro_rules! unpack_or_rtn ( + ($i:expr, $o:expr) => + {{ + match ($i, $o) + { + (Ok(i), Ok(o)) => + (i,o), + (Err(e), _) => + { + eprintln!("dd Error: {}", e); + return RTN_FAILURE; + }, + (_, Err(e)) => + { + eprintln!("dd Error: {}", e); + return RTN_FAILURE; + }, + } + }}; +); + pub fn uumain(args: impl uucore::Args) -> i32 { let dashed_args = args.collect_str() .iter() .fold(Vec::new(), append_dashes_if_not_present); let matches = build_app!().parse(dashed_args); + let result = match (matches.opt_present("if"), matches.opt_present("of")) { (true, true) => { - let i = Input::::new(&matches) - .expect("TODO: Return correct error code"); - let o = Output::::new(&matches) - .expect("TODO: Return correct error code"); + let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); + + dd_fileout(i,o) + }, + (false, true) => + { + let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); dd_fileout(i,o) }, (true, false) => { - let i = Input::::new(&matches) - .expect("TODO: Return correct error code"); - let o = Output::::new(&matches) - .expect("TODO: Return correct error code"); + let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); dd_stdout(i,o) }, - (false, true) => + (false, false) => { - let i = Input::::new(&matches) - .expect("TODO: Return correct error code"); - let o = Output::::new(&matches) - .expect("TODO: Return correct error code"); - - dd_fileout(i,o) - }, - (false, false) => - { - let i = Input::::new(&matches) - .expect("TODO: Return correct error code"); - let o = Output::::new(&matches) - .expect("TODO: Return correct error code"); + let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); dd_stdout(i,o) }, diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index 959cb53fd..2195b1b84 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -1,19 +1,5 @@ use super::*; -struct LazyReader -{ - src: R, -} - -impl Read for LazyReader -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result - { - let reduced = cmp::max(buf.len() / 2, 1); - self.src.read(&mut buf[..reduced]) - } -} - macro_rules! make_sync_test ( ( $test_id:ident, $test_name:expr, $src:expr, $sync:expr, $ibs:expr, $obs:expr, $spec:expr ) => { diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index eb8af60df..019943e59 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -5,14 +5,10 @@ mod conversion_tests; mod block_unblock_tests; mod conv_sync_tests; +use rand::prelude::*; use std::io::prelude::*; use std::io::BufReader; use std::fs; -use md5::{ Md5, Digest, }; -use hex_literal::hex; - -// use tempfile::tempfile; -// TODO: (Maybe) Use tempfiles in the tests. const DEFAULT_CFO: OConvFlags = OConvFlags { sparse: false, @@ -60,6 +56,40 @@ const DEFAULT_OFLAGS: OFlags = OFlags { seek_bytes: false, }; +struct LazyReader +{ + src: R, +} + +impl Read for LazyReader +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result + { + let reduced = cmp::max(buf.len() / 2, 1); + self.src.read(&mut buf[..reduced]) + } +} + +struct FickleReader +{ + src: R, +} + +impl Read for FickleReader +{ + fn read(&mut self, mut buf: &mut [u8]) -> io::Result + { + if rand::random() + { + self.src.read(&mut buf) + } + else + { + Ok(0) + } + } +} + #[macro_export] macro_rules! icf ( () => diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index e9923b4b1..8920ac04d 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -212,6 +212,86 @@ make_io_test!( File::open("./test-resources/gnudd-random-first-32k.spec").unwrap() ); +make_io_test!( + random_73k_test_lazy_fullblock, + "random-73k-test-lazy-fullblock", + Input { + src: LazyReader { + src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() + }, + non_ascii: false, + ibs: 521, + xfer_stats: None, + count: None, + cflags: icf!(), + iflags: IFlags { + fullblock: true, + cio: false, + direct: false, + directory: false, + dsync: false, + sync: false, + nocache: false, + nonblock: false, + noatime: false, + noctty: false, + nofollow: false, + nolinks: false, + binary: false, + text: false, + count_bytes: false, + skip_bytes: false, + }, + }, + Output { + dst: DST_PLACEHOLDER, + obs: 1031, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() +); + +make_io_test!( + random_73k_test_fickle_fullblock, + "random-73k-test-fickle-fullblock", + Input { + src: FickleReader { + src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() + }, + non_ascii: false, + ibs: 521, + xfer_stats: None, + count: None, + cflags: icf!(), + iflags: IFlags { + fullblock: true, + cio: false, + direct: false, + directory: false, + dsync: false, + sync: false, + nocache: false, + nonblock: false, + noatime: false, + noctty: false, + nofollow: false, + nolinks: false, + binary: false, + text: false, + count_bytes: false, + skip_bytes: false, + }, + }, + Output { + dst: DST_PLACEHOLDER, + obs: 1031, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() +); + // Test internal buffer size fn #[test] fn bsize_test_primes() diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index b4ee0efb2..a8ec0527c 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -33,7 +33,53 @@ pub enum ParseError impl std::fmt::Display for ParseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "dd-args: Parse Error") + match self + { + Self::MultipleFmtTable => + { + write!(f, "Only one of conv=ascii conv=ebcdic or conv=ibm may be specified") + }, + Self::MultipleUCaseLCase => + { + write!(f, "Only one of conv=lcase or conv=ucase may be specified") + }, + Self::MultipleBlockUnblock => + { + write!(f, "Only one of conv=block or conv=unblock may be specified") + }, + Self::MultipleExclNoCreat => + { + write!(f, "Only one ov conv=excl or conv=nocreat may be specified") + }, + Self::FlagNoMatch(arg) => + { + write!(f, "Unrecognized iflag=FLAG or oflag=FLAG -> {}", arg) + }, + Self::ConvFlagNoMatch(arg) => + { + write!(f, "Unrecognized conv=CONV -> {}", arg) + }, + Self::NoMatchingMultiplier(arg) => + { + write!(f, "Unrecognized byte multiplier -> {}", arg) + }, + Self::ByteStringContainsNoValue(arg) => + { + write!(f, "Unrecognized byte value -> {}", arg) + }, + Self::MultiplierStringWouldOverflow(arg) => + { + write!(f, "Multiplier string would overflow on current system -> {}", arg) + }, + Self::BlockUnblockWithoutCBS => + { + write!(f, "conv=block or conv=unblock specified without cbs=N") + }, + Self::StatusLevelNotRecognized(arg) => + { + write!(f, "status=LEVEL not recognized -> {}", arg) + }, + } } } @@ -270,23 +316,24 @@ fn parse_bytes_only(s: &str) -> Result fn parse_bytes_with_opt_multiplier(s: String) -> Result { - if let Some(idx) = s.find(char::is_alphabetic) + match s.find(char::is_alphabetic) { - let base = parse_bytes_only(&s[..idx])?; - let mult = parse_multiplier(&s[idx..])?; + Some(idx) => + { + let base = parse_bytes_only(&s[..idx])?; + let mult = parse_multiplier(&s[idx..])?; - if let Some(bytes) = base.checked_mul(mult) - { - Ok(bytes) + if let Some(bytes) = base.checked_mul(mult) + { + Ok(bytes) + } + else + { + Err(ParseError::MultiplierStringWouldOverflow(s)) + } } - else - { - Err(ParseError::MultiplierStringWouldOverflow(s)) - } - } - else - { - parse_bytes_only(&s) + _ => + parse_bytes_only(&s), } } diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index b9eb7f3ac..cf8be384c 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -1,5 +1,107 @@ use crate::common::util::*; +use std::io::{BufReader, Read, Write}; +use std::path::{Path, PathBuf}; +use std::fs::{self, OpenOptions, File}; +use std::time::{Duration, SystemTime}; +use tempfile::tempfile; + +macro_rules! inf +{ + ($fname:expr) => + {{ + &format!("if={}", $fname) + }}; +} + +macro_rules! of +{ + ($fname:expr) => + {{ + &format!("of={}", $fname) + }}; +} + +macro_rules! fixture_path +{ + ($fname:expr) => + {{ + PathBuf::from(format!("./tests/fixtures/dd/{}", $fname)) + }}; +} + +macro_rules! assert_fixture_exists +{ + ($fname:expr) => + {{ + let fpath = fixture_path!($fname); + if !fpath.exists() + { + panic!("Fixture missing: {:?}", fpath); + } + }}; +} + +macro_rules! assert_fixture_not_exists +{ + ($fname:expr) => + {{ + let fpath = PathBuf::from(format!("./fixtures/dd/{}", $fname)); + if fpath.exists() + { + panic!("Fixture present: {:?}", fpath); + } + }}; +} + +macro_rules! build_test_file { + ($fp:expr, $data:expr) => {{ + OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open($fp) + .unwrap() + .write_all($data) + .unwrap() + }}; +} + +macro_rules! cmp_file ( + ($spec:expr, $test:expr) => + { + let specfile_len = $spec.metadata().unwrap().len(); + let testfile_len = $test.metadata().unwrap().len(); + assert_eq!(testfile_len, specfile_len); + + let spec = BufReader::new($spec); + let test = BufReader::new($test); + + for (b_spec, b_test) in spec.bytes().zip(test.bytes()) + { + assert_eq!(b_spec.unwrap(), + b_test.unwrap()); + } + }; +); + +fn build_ascii_block(n: usize) -> Vec +{ + (0..=127) + .cycle() + .take(n) + .collect() +} + +fn build_ebcdic_block(n: usize) -> Vec +{ + (0..=255) + .cycle() + .take(n) + .collect() +} + +// Sanity Tests #[test] fn version() { @@ -16,16 +118,6 @@ fn help() .succeeds(); } -fn build_ascii_block(n: usize) -> Vec -{ - vec!['a', 'b', 'c', 'd', 'e', 'f'] - .into_iter() - .map(|c| c as u8) - .cycle() - .take(n) - .collect() -} - #[test] fn test_stdin_stdout() { @@ -34,10 +126,13 @@ fn test_stdin_stdout() new_ucmd!() .args(&["status=none"]) .pipe_in(input) - .succeeds() + .run() + .no_stderr() .stdout_only(output); } +// Top-Level Items +// count=N, skip=N, status=LEVEL, conv=FLAG, *flag=FLAG #[test] fn test_stdin_stdout_count() { @@ -51,7 +146,8 @@ fn test_stdin_stdout_count() "ibs=128", ]) .pipe_in(input) - .succeeds() + .run() + .no_stderr() .stdout_only(output); } @@ -68,7 +164,8 @@ fn test_stdin_stdout_count_bytes() "iflag=count_bytes", ]) .pipe_in(input) - .succeeds() + .run() + .no_stderr() .stdout_only(output); } @@ -85,7 +182,8 @@ fn test_stdin_stdout_skip() "ibs=128", ]) .pipe_in(input) - .succeeds() + .run() + .no_stderr() .stdout_only(output); } @@ -103,14 +201,51 @@ fn test_stdin_stdout_skip_bytes() "iflag=skip_bytes", ]) .pipe_in(input) - .succeeds() + .run() + .no_stderr() .stdout_only(output); } +#[test] +fn test_stdin_stdout_skip_w_multiplier() +{ + let input = build_ascii_block(10*1024); + let output = String::from_utf8(input[5*1024..].to_vec()).unwrap(); + new_ucmd!() + .args(&[ + "status=none", + "skip=5K", + "iflag=skip_bytes" + ]) + .pipe_in(input) + .run() + .no_stderr() + .stdout_is(output) + .success(); +} + +#[test] +fn test_stdin_stdout_count_w_multiplier() +{ + let input = build_ascii_block(5*1024); + let output = String::from_utf8(input[..2*1024].to_vec()).unwrap(); + new_ucmd!() + .args(&[ + "status=none", + "count=2KiB", + "iflag=count_bytes", + ]) + .pipe_in(input) + .run() + .no_stderr() + .stdout_is(output) + .success(); +} + #[test] fn test_final_stats_noxfer() { - new_ucmd!() + new_ucmd!() .args(&[ "status=noxfer", ]) @@ -133,49 +268,246 @@ fn test_final_stats_unspec() acc }); new_ucmd!() - .succeeds() - .stderr_only(&output); + .run() + .stderr_only(&output) + .success(); } #[test] -fn test_self_transfer() +fn test_excl_causes_failure_when_present() { - panic!(); - // TODO: Make new copy per-test - new_ucmd!() - .args(&[ - "conv=notruc", - "if=../fixtures/dd/zero-256k.copy", - "of=../fixtures/dd/zero-256k.copy", - ]) - .succeeds(); - assert!(false/* Must check that zero256k.copy still == zero-256k.txt */) + let fname = "this-file-exists-excl.txt"; + assert_fixture_exists!(&fname); + + let (_fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "of=this-file-exists-excl.txt", + "conv=excl", + ]) + .fails(); } -#[cfg(unix)] #[test] -fn test_null() +fn test_atime_updated() +{ + let fname = "this-file-exists-no-noatime.txt"; + assert_fixture_exists!(&fname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + inf!(fname), + ]); + + let pre_atime = fix.metadata(&fname).accessed().unwrap(); + + ucmd.pipe_in("") + .run() + .no_stderr() + .success(); + + let post_atime = fix.metadata(&fname).accessed().unwrap(); + assert!(pre_atime != post_atime); +} + +#[test] +fn test_noatime_does_not_update_infile_atime() +{ + let fname = "this-ifile-exists-noatime.txt"; + assert_fixture_exists!(&fname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "iflag=noatime", + inf!(fname), + ]); + + let pre_atime = fix.metadata(&fname).accessed().unwrap(); + + ucmd.run() + .no_stderr() + .success(); + + let post_atime = fix.metadata(&fname).accessed().unwrap(); + assert_eq!(pre_atime, post_atime); +} + +#[test] +fn test_noatime_does_not_update_ofile_atime() +{ + let fname = "this-ofile-exists-noatime.txt"; + assert_fixture_exists!(&fname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "oflag=noatime", + of!(fname), + ]); + + let pre_atime = fix.metadata(&fname).accessed().unwrap(); + + ucmd.pipe_in("") + .run() + .no_stderr() + .success(); + + let post_atime = fix.metadata(&fname).accessed().unwrap(); + assert_eq!(pre_atime, post_atime); +} + +#[test] +fn test_nocreat_causes_failure_when_not_present() +{ + let fname = "this-file-does-not-exist.txt"; + assert_fixture_not_exists!(&fname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "conv=nocreat", + of!(&fname), + ]) + .run(); + + assert!(!fix.file_exists(&fname)); + + ucmd.fails(); +} + +#[test] +fn test_notrunc_does_not_truncate() +{ + // Set up test if needed (eg. after failure) + let fname = "this-file-exists-notrunc.txt"; + let fpath = fixture_path!(fname); + match fpath.metadata() + { + Ok(m) if m.len() == 256 => {}, + _ => + build_test_file!(&fpath, &build_ascii_block(256)), + } + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "conv=notrunc", + of!(&fname), + "if=null.txt", + ]) + .run() + .no_stdout() + .no_stderr() + .success(); + + assert_eq!(256, fix.metadata(&fname).len()); +} + +#[test] +fn test_existing_file_truncated() +{ + // Set up test if needed (eg. after failure) + let fname = "this-file-exists-truncated.txt"; + let fpath = fixture_path!(fname); + match fpath.metadata() + { + Ok(m) if m.len() == 256 => {}, + _ => + build_test_file!(&fpath, &vec![0; 256]), + } + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "if=null.txt", + of!(fname), + ]) + .run() + .no_stdout() + .no_stderr() + .success(); + + assert_eq!(0, fix.metadata(&fname).len()); +} + +#[test] +fn test_null_stats() { let stats = vec![ - "0+0 records in", - "0+0 records out", - "0 bytes (0 B, 0 B) copied, 0.0 s, 0 B/s", + "0+0 records in\n", + "0+0 records out\n", + "0 bytes (0 B, 0 B) copied, 0.0 s, 0 B/s\n", ]; let stats = stats.into_iter() .fold(String::new(), | mut acc, s | { acc.push_str(s); - acc.push('\n'); acc }); + new_ucmd!() .args(&[ - "if=/dev/null", + "if=null.txt", ]) - .succeeds() + .run() .stderr_only(stats) - .stdout_only(""); + .success(); } +#[test] +fn test_null_fullblock() +{ + new_ucmd!() + .args(&[ + "if=null.txt", + "status=none", + "iflag=fullblock", + ]) + .run() + .no_stdout() + .no_stderr() + .success(); +} + +#[cfg(unix)] +// #[ignore] // See note below before running this test! +#[test] +fn test_fullblock() +{ + let tname = "fullblock-from-urand"; + let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + let stats = vec![ + "1+0 records in\n", + "1+0 records out\n", + "134217728 bytes (134 MB, 128 MiB) copied,", + ]; + let stats = stats.into_iter() + .fold(String::new(), | mut acc, s | { + acc.push_str(s); + acc + }); + + let res = new_ucmd!() + .args(&[ + "if=/dev/urandom", + of!(&tmp_fn), + "bs=128M", + // Note: In order for this test to actually test iflag=fullblock, the bs=VALUE + // must be big enough to 'overwhelm' urandom's store of bytes. + // Try executing 'dd if=/dev/urandom bs=128M count=1' (i.e without iflag=fullblock). + // The stats should contain the line: '0+1 records in' indicating a partial read. + // Since my system only copies 32 MiB without fullblock, I expect 128 MiB to be + // a reasonable value for testing most systems. + "count=1", + "iflag=fullblock", + ]) + .run(); + + res.success(); + let res_stats = &res.stderr[..stats.len()]; + assert_eq!(&stats, res_stats); +} + +// Fileio #[test] fn test_ys_to_stdout() { @@ -188,10 +520,13 @@ fn test_ys_to_stdout() new_ucmd!() .args(&[ - "if=../fixtures/dd/y-nl-1k.txt", + "status=none", + "if=y-nl-1k.txt", ]) .run() - .stdout_only(output); + .no_stderr() + .stdout_is(output) + .success(); } #[test] @@ -201,9 +536,176 @@ fn test_zeros_to_stdout() let output = String::from_utf8(output).unwrap(); new_ucmd!() .args(&[ - "if=../fixtures/dd/zero-256k.txt", + "status=none", + "if=zero-256k.txt", ]) .run() - .stdout_only(output); + .no_stderr() + .stdout_is(output) + .success(); } +#[test] +fn test_to_stdout_with_ibs_obs() +{ + let output: Vec<_> = String::from("y\n") + .bytes() + .cycle() + .take(1024) + .collect(); + let output = String::from_utf8(output).unwrap(); + + new_ucmd!() + .args(&[ + "status=none", + "if=y-nl-1k.txt", + "ibs=521", + "obs=1031", + ]) + .run() + .no_stderr() + .stdout_is(output) + .success(); +} + +#[test] +fn test_ascii_10k_to_stdout() +{ + let output = build_ascii_block(1024*1024); + // build_test_file!("ascii-10k.txt", &output); + let output = String::from_utf8(output).unwrap(); + + new_ucmd!() + .args(&[ + "status=none", + "if=ascii-10k.txt", + ]) + .run() + .no_stderr() + .stdout_is(output) + .success(); +} + +#[test] +fn test_zeros_to_file() +{ + let tname = "zero-256k"; + let test_fn = format!("{}.txt", tname); + let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + inf!(test_fn), + of!(tmp_fn), + ]) + .run() + .no_stderr() + .no_stdout() + .success(); + + cmp_file!(File::open(fixture_path!(&test_fn)).unwrap(), + fix.open(&tmp_fn)); +} + +#[test] +fn test_to_file_with_ibs_obs() +{ + let tname = "zero-256k"; + let test_fn = format!("{}.txt", tname); + let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + inf!(test_fn), + of!(tmp_fn), + "ibs=222", + "obs=111", + ]) + .run() + .no_stderr() + .no_stdout() + .success(); + + cmp_file!(File::open(fixture_path!(&test_fn)).unwrap(), + fix.open(&tmp_fn)); +} + +#[test] +fn test_ascii_521k_to_file() +{ + let tname = "ascii-521k"; + let input = build_ascii_block(512*1024); + let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + of!(tmp_fn), + ]) + .pipe_in(input.clone()) + .run() + .no_stderr() + .no_stdout() + .success(); + + assert_eq!(512*1024, fix.metadata(&tmp_fn).len()); + + cmp_file!({ let mut input_f = tempfile().unwrap(); + input_f.write(&input).unwrap(); + input_f }, + fix.open(&tmp_fn)); +} + +#[ignore] +#[cfg(unix)] +#[test] +fn test_ascii_5_gibi_to_file() +{ + let tname = "ascii-5G"; + let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "count=5G", + "iflag=count_bytes", + "if=/dev/zero", + of!(tmp_fn), + ]) + .run() + .no_stderr() + .no_stdout() + .success(); + + assert_eq!(5*1024*1024*1024, fix.metadata(&tmp_fn).len()); +} + +#[test] +fn test_self_transfer() +{ + let fname = "self-transfer-256k.txt"; + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "conv=notrunc", + inf!(fname), + of!(fname), + ]); + + assert!(fix.file_exists(fname)); + assert_eq!(256*1024, fix.metadata(fname).len()); + + ucmd.run() + .no_stdout() + .no_stderr() + .success(); + + assert!(fix.file_exists(fname)); + assert_eq!(256*1024, fix.metadata(fname).len()); +} + +// conv=[ascii,ebcdic,ibm], conv=[ucase,lcase], conv=[block,unblock], conv=sync +// TODO: Move conv tests from unit test module diff --git a/tests/fixtures/dd/ascii-10k.txt b/tests/fixtures/dd/ascii-10k.txt new file mode 100644 index 000000000..5e72e94d4 Binary files /dev/null and b/tests/fixtures/dd/ascii-10k.txt differ diff --git a/tests/fixtures/dd/gnudd-conv-atoe-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-atoe-seq-byte-values.spec new file mode 100644 index 000000000..8701c67fd Binary files /dev/null and b/tests/fixtures/dd/gnudd-conv-atoe-seq-byte-values.spec differ diff --git a/tests/fixtures/dd/gnudd-conv-atoibm-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-atoibm-seq-byte-values.spec new file mode 100644 index 000000000..3835d57cf Binary files /dev/null and b/tests/fixtures/dd/gnudd-conv-atoibm-seq-byte-values.spec differ diff --git a/tests/fixtures/dd/gnudd-conv-ebcdic-ltou-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-ebcdic-ltou-seq-byte-values.spec new file mode 100644 index 000000000..ed0686639 Binary files /dev/null and b/tests/fixtures/dd/gnudd-conv-ebcdic-ltou-seq-byte-values.spec differ diff --git a/tests/fixtures/dd/gnudd-conv-ebcdic-utol-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-ebcdic-utol-seq-byte-values.spec new file mode 100644 index 000000000..13c82e5e3 Binary files /dev/null and b/tests/fixtures/dd/gnudd-conv-ebcdic-utol-seq-byte-values.spec differ diff --git a/tests/fixtures/dd/gnudd-conv-etoa-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-etoa-seq-byte-values.spec new file mode 100644 index 000000000..433b1f600 Binary files /dev/null and b/tests/fixtures/dd/gnudd-conv-etoa-seq-byte-values.spec differ diff --git a/tests/fixtures/dd/gnudd-conv-ibm-ltou-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-ibm-ltou-seq-byte-values.spec new file mode 100644 index 000000000..d425e7d01 Binary files /dev/null and b/tests/fixtures/dd/gnudd-conv-ibm-ltou-seq-byte-values.spec differ diff --git a/tests/fixtures/dd/gnudd-conv-ibm-utol-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-ibm-utol-seq-byte-values.spec new file mode 100644 index 000000000..1dcaccbcd Binary files /dev/null and b/tests/fixtures/dd/gnudd-conv-ibm-utol-seq-byte-values.spec differ diff --git a/tests/fixtures/dd/gnudd-conv-ltou-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-ltou-seq-byte-values.spec new file mode 100644 index 000000000..5aacec0cd Binary files /dev/null and b/tests/fixtures/dd/gnudd-conv-ltou-seq-byte-values.spec differ diff --git a/tests/fixtures/dd/gnudd-conv-utol-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-utol-seq-byte-values.spec new file mode 100644 index 000000000..d205368d5 Binary files /dev/null and b/tests/fixtures/dd/gnudd-conv-utol-seq-byte-values.spec differ diff --git a/tests/fixtures/dd/null.txt b/tests/fixtures/dd/null.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/fixtures/dd/self-transfer-256k.txt b/tests/fixtures/dd/self-transfer-256k.txt new file mode 100644 index 000000000..6d23118f0 Binary files /dev/null and b/tests/fixtures/dd/self-transfer-256k.txt differ diff --git a/tests/fixtures/dd/seq-byte-values.test b/tests/fixtures/dd/seq-byte-values.test new file mode 100644 index 000000000..c86626638 Binary files /dev/null and b/tests/fixtures/dd/seq-byte-values.test differ diff --git a/tests/fixtures/dd/this-file-exists-excl.txt b/tests/fixtures/dd/this-file-exists-excl.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/fixtures/dd/this-file-exists-no-noatime.txt b/tests/fixtures/dd/this-file-exists-no-noatime.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/fixtures/dd/this-file-exists-notrunc.txt b/tests/fixtures/dd/this-file-exists-notrunc.txt new file mode 100644 index 000000000..5223e3549 --- /dev/null +++ b/tests/fixtures/dd/this-file-exists-notrunc.txt @@ -0,0 +1 @@ +abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd \ No newline at end of file diff --git a/tests/fixtures/dd/this-file-exists-truncated.txt b/tests/fixtures/dd/this-file-exists-truncated.txt new file mode 100644 index 000000000..5223e3549 --- /dev/null +++ b/tests/fixtures/dd/this-file-exists-truncated.txt @@ -0,0 +1 @@ +abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd \ No newline at end of file diff --git a/tests/fixtures/dd/this-ifile-exists-noatime.txt b/tests/fixtures/dd/this-ifile-exists-noatime.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/fixtures/dd/this-ofile-exists-noatime.txt b/tests/fixtures/dd/this-ofile-exists-noatime.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/fixtures/dd/y-nl-1k.txt b/tests/fixtures/dd/y-nl-1k.txt new file mode 100644 index 000000000..e75c50796 --- /dev/null +++ b/tests/fixtures/dd/y-nl-1k.txt @@ -0,0 +1,512 @@ +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y +y diff --git a/tests/fixtures/dd/zero-256k.txt b/tests/fixtures/dd/zero-256k.txt new file mode 100644 index 000000000..6d23118f0 Binary files /dev/null and b/tests/fixtures/dd/zero-256k.txt differ