2023-08-21 08:49:27 +00:00
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
2024-03-16 16:10:49 +00:00
// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, availible, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat abcdefghijklm abcdefghi nabcde nabcdefg abcdefg fifoname seekable
2021-07-23 21:53:24 +00:00
2023-07-22 09:26:56 +00:00
#[ cfg(unix) ]
use crate ::common ::util ::run_ucmd_as_root_with_stdin_stdout ;
2023-03-20 15:06:56 +00:00
use crate ::common ::util ::TestScenario ;
2023-05-08 05:59:56 +00:00
#[ cfg(all(not(windows), feature = " printf " )) ]
2023-03-20 15:06:56 +00:00
use crate ::common ::util ::{ UCommand , TESTS_BINARY } ;
2021-03-16 21:32:55 +00:00
dd: fix precision for display of total time spent
Improve the display of the total time spent transferring bytes so that
the number of seconds is displayed using the `%g` format specifier as
in `printf`. This matches the behavior of GNU `dd`.
Before this commit, the precision was always set to one digit after
the decimal point. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.2 s, 268.1 MB/s
After this commit, the precision increases dynamically as the duration
decreases. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.1019 s, 507 MB/s
$ dd count=1000 if=/dev/zero of=/dev/null
1000+0 records in
1000+0 records out
512000 bytes (512 kB, 500 KiB) copied, 0.002663 s, 256 MB/s
$ dd count=10 if=/dev/zero of=/dev/null
10+0 records in
10+0 records out
5120 bytes (5.1 kB, 5.0 KiB) copied, 0.000182 s, 5.1 MB/s
2023-03-12 03:37:12 +00:00
use regex ::Regex ;
2024-03-16 16:10:49 +00:00
use uucore ::io ::OwnedFileDescriptorOrHandle ;
dd: fix precision for display of total time spent
Improve the display of the total time spent transferring bytes so that
the number of seconds is displayed using the `%g` format specifier as
in `printf`. This matches the behavior of GNU `dd`.
Before this commit, the precision was always set to one digit after
the decimal point. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.2 s, 268.1 MB/s
After this commit, the precision increases dynamically as the duration
decreases. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.1019 s, 507 MB/s
$ dd count=1000 if=/dev/zero of=/dev/null
1000+0 records in
1000+0 records out
512000 bytes (512 kB, 500 KiB) copied, 0.002663 s, 256 MB/s
$ dd count=10 if=/dev/zero of=/dev/null
10+0 records in
10+0 records out
5120 bytes (5.1 kB, 5.0 KiB) copied, 0.000182 s, 5.1 MB/s
2023-03-12 03:37:12 +00:00
2021-07-06 18:52:48 +00:00
use std ::fs ::{ File , OpenOptions } ;
2021-06-30 21:47:48 +00:00
use std ::io ::{ BufReader , Read , Write } ;
2021-07-06 18:52:48 +00:00
use std ::path ::PathBuf ;
2023-12-01 09:03:44 +00:00
#[ cfg(all(
unix ,
not ( target_os = " macos " ) ,
not ( target_os = " freebsd " ) ,
feature = " printf "
) ) ]
2023-03-18 02:42:24 +00:00
use std ::process ::{ Command , Stdio } ;
2022-11-19 03:45:34 +00:00
#[ cfg(not(windows)) ]
use std ::thread ::sleep ;
#[ cfg(not(windows)) ]
use std ::time ::Duration ;
2021-06-30 21:47:48 +00:00
use tempfile ::tempfile ;
2021-07-06 18:52:48 +00:00
macro_rules ! inf {
( $fname :expr ) = > { {
2021-06-30 21:47:48 +00:00
& format! ( " if= {} " , $fname )
} } ;
}
2021-07-06 18:52:48 +00:00
macro_rules ! of {
( $fname :expr ) = > { {
2021-06-30 21:47:48 +00:00
& format! ( " of= {} " , $fname )
} } ;
}
2021-07-06 18:52:48 +00:00
macro_rules ! fixture_path {
( $fname :expr ) = > { {
2021-06-30 21:47:48 +00:00
PathBuf ::from ( format! ( " ./tests/fixtures/dd/ {} " , $fname ) )
} } ;
}
2021-07-06 18:52:48 +00:00
macro_rules ! assert_fixture_exists {
( $fname :expr ) = > { {
2021-06-30 21:47:48 +00:00
let fpath = fixture_path! ( $fname ) ;
2021-10-10 07:57:39 +00:00
assert! ( fpath . exists ( ) , " Fixture missing: {:?} " , fpath ) ;
2021-06-30 21:47:48 +00:00
} } ;
}
2022-02-09 18:08:28 +00:00
#[ cfg(any(target_os = " linux " , target_os = " android " )) ]
2021-07-06 18:52:48 +00:00
macro_rules ! assert_fixture_not_exists {
( $fname :expr ) = > { {
2021-06-30 21:47:48 +00:00
let fpath = PathBuf ::from ( format! ( " ./fixtures/dd/ {} " , $fname ) ) ;
2021-10-10 07:57:39 +00:00
assert! ( ! fpath . exists ( ) , " Fixture present: {:?} " , fpath ) ;
2021-06-30 21:47:48 +00:00
} } ;
}
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 ( ) ) ;
}
} ;
) ;
2021-07-06 18:52:48 +00:00
fn build_ascii_block ( n : usize ) -> Vec < u8 > {
( 0 ..= 127 ) . cycle ( ) . take ( n ) . collect ( )
2021-06-30 21:47:48 +00:00
}
2022-09-10 16:38:14 +00:00
#[ test ]
fn test_invalid_arg ( ) {
new_ucmd! ( ) . arg ( " --definitely-invalid " ) . fails ( ) . code_is ( 1 ) ;
}
2021-06-30 21:47:48 +00:00
// Sanity Tests
2021-06-21 18:17:47 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
fn version ( ) {
new_ucmd! ( ) . args ( & [ " --version " ] ) . succeeds ( ) ;
2021-06-21 18:17:47 +00:00
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn help ( ) {
new_ucmd! ( ) . args ( & [ " --help " ] ) . succeeds ( ) ;
2021-06-21 18:17:47 +00:00
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_stdin_stdout ( ) {
2021-06-21 18:17:47 +00:00
let input = build_ascii_block ( 521 ) ;
let output = String ::from_utf8 ( input . clone ( ) ) . unwrap ( ) ;
new_ucmd! ( )
. args ( & [ " status=none " ] )
. pipe_in ( input )
2021-06-30 21:47:48 +00:00
. run ( )
. no_stderr ( )
2021-06-21 18:17:47 +00:00
. stdout_only ( output ) ;
}
2021-06-30 21:47:48 +00:00
// Top-Level Items
// count=N, skip=N, status=LEVEL, conv=FLAG, *flag=FLAG
2021-06-21 18:17:47 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_stdin_stdout_count ( ) {
2021-06-21 18:17:47 +00:00
let input = build_ascii_block ( 521 ) ;
let mut output = String ::from_utf8 ( input . clone ( ) ) . unwrap ( ) ;
output . truncate ( 256 ) ;
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " status=none " , " count=2 " , " ibs=128 " ] )
2021-06-21 18:17:47 +00:00
. pipe_in ( input )
2021-06-30 21:47:48 +00:00
. run ( )
. no_stderr ( )
2021-06-21 18:17:47 +00:00
. stdout_only ( output ) ;
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_stdin_stdout_count_bytes ( ) {
2021-06-21 18:17:47 +00:00
let input = build_ascii_block ( 521 ) ;
let mut output = String ::from_utf8 ( input . clone ( ) ) . unwrap ( ) ;
output . truncate ( 256 ) ;
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " status=none " , " count=256 " , " iflag=count_bytes " ] )
2021-06-21 18:17:47 +00:00
. pipe_in ( input )
2021-06-30 21:47:48 +00:00
. run ( )
. no_stderr ( )
2021-06-21 18:17:47 +00:00
. stdout_only ( output ) ;
2021-03-16 21:32:55 +00:00
}
2021-06-21 18:17:47 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_stdin_stdout_skip ( ) {
2021-06-21 18:17:47 +00:00
let input = build_ascii_block ( 521 ) ;
let mut output = String ::from_utf8 ( input . clone ( ) ) . unwrap ( ) ;
let _ = output . drain ( .. 256 ) ;
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " status=none " , " skip=2 " , " ibs=128 " ] )
2021-06-21 18:17:47 +00:00
. pipe_in ( input )
2021-06-30 21:47:48 +00:00
. run ( )
. no_stderr ( )
2021-06-21 18:17:47 +00:00
. stdout_only ( output ) ;
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_stdin_stdout_skip_bytes ( ) {
2021-06-21 18:17:47 +00:00
let input = build_ascii_block ( 521 ) ;
let mut output = String ::from_utf8 ( input . clone ( ) ) . unwrap ( ) ;
let _ = output . drain ( .. 256 ) ;
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " status=none " , " skip=256 " , " ibs=128 " , " iflag=skip_bytes " ] )
2021-06-21 18:17:47 +00:00
. pipe_in ( input )
2021-06-30 21:47:48 +00:00
. run ( )
. no_stderr ( )
2021-06-21 18:17:47 +00:00
. stdout_only ( output ) ;
}
2021-06-30 21:47:48 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
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 ( ) ;
2021-06-30 21:47:48 +00:00
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " status=none " , " skip=5K " , " iflag=skip_bytes " ] )
2021-06-30 21:47:48 +00:00
. pipe_in ( input )
. run ( )
. no_stderr ( )
. stdout_is ( output )
. success ( ) ;
}
#[ test ]
2021-07-06 18:52:48 +00:00
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 ( ) ;
2021-06-30 21:47:48 +00:00
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " status=none " , " count=2KiB " , " iflag=count_bytes " ] )
2021-06-30 21:47:48 +00:00
. pipe_in ( input )
. run ( )
. no_stderr ( )
. stdout_is ( output )
. success ( ) ;
}
2022-02-02 03:53:19 +00:00
#[ test ]
fn test_b_multiplier ( ) {
// "2b" means 2 * 512, which is 1024.
new_ucmd! ( )
. args ( & [ " bs=2b " , " count=1 " ] )
. pipe_in ( " a " . repeat ( 1025 ) )
. succeeds ( )
. stdout_is ( " a " . repeat ( 1024 ) ) ;
}
#[ test ]
fn test_x_multiplier ( ) {
// "2x3" means 2 * 3, which is 6.
new_ucmd! ( )
. args ( & [ " bs=2x3 " , " count=1 " ] )
. pipe_in ( " abcdefghi " )
. succeeds ( )
. stdout_is ( " abcdef " ) ;
}
2022-02-02 04:22:01 +00:00
#[ test ]
fn test_zero_multiplier_warning ( ) {
for arg in [ " count " , " seek " , " skip " ] {
2022-02-13 03:34:39 +00:00
new_ucmd! ( )
2023-01-27 09:29:45 +00:00
. args ( & [ format! ( " {arg} =0 " ) . as_str ( ) , " status=none " ] )
2022-02-13 03:34:39 +00:00
. pipe_in ( " " )
. succeeds ( )
. no_stdout ( )
. no_stderr ( ) ;
2022-02-02 04:22:01 +00:00
new_ucmd! ( )
2023-01-27 09:29:45 +00:00
. args ( & [ format! ( " {arg} =00x1 " ) . as_str ( ) , " status=none " ] )
2022-02-02 04:22:01 +00:00
. pipe_in ( " " )
. succeeds ( )
. no_stdout ( )
. no_stderr ( ) ;
new_ucmd! ( )
2023-01-27 09:29:45 +00:00
. args ( & [ format! ( " {arg} =0x1 " ) . as_str ( ) , " status=none " ] )
2022-02-02 04:22:01 +00:00
. pipe_in ( " " )
. succeeds ( )
. no_stdout ( )
. stderr_contains ( " warning: '0x' is a zero multiplier; use '00x' if that is intended " ) ;
new_ucmd! ( )
2023-01-27 09:29:45 +00:00
. args ( & [ format! ( " {arg} =0x0x1 " ) . as_str ( ) , " status=none " ] )
2022-02-02 04:22:01 +00:00
. pipe_in ( " " )
. succeeds ( )
. no_stdout ( )
. stderr_is ( " dd: warning: '0x' is a zero multiplier; use '00x' if that is intended \n dd: warning: '0x' is a zero multiplier; use '00x' if that is intended \n " ) ;
new_ucmd! ( )
2023-01-27 09:29:45 +00:00
. args ( & [ format! ( " {arg} =1x0x1 " ) . as_str ( ) , " status=none " ] )
2022-02-02 04:22:01 +00:00
. pipe_in ( " " )
. succeeds ( )
. no_stdout ( )
. stderr_contains ( " warning: '0x' is a zero multiplier; use '00x' if that is intended " ) ;
}
}
2021-06-21 18:17:47 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_final_stats_noxfer ( ) {
2021-06-30 21:47:48 +00:00
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " status=noxfer " ] )
2021-06-21 18:17:47 +00:00
. succeeds ( )
2022-01-23 22:32:36 +00:00
. stderr_only ( " 0+0 records in \n 0+0 records out \n " ) ;
2021-06-21 18:17:47 +00:00
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_final_stats_unspec ( ) {
2022-06-12 02:41:54 +00:00
new_ucmd! ( )
. run ( )
dd: fix precision for display of total time spent
Improve the display of the total time spent transferring bytes so that
the number of seconds is displayed using the `%g` format specifier as
in `printf`. This matches the behavior of GNU `dd`.
Before this commit, the precision was always set to one digit after
the decimal point. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.2 s, 268.1 MB/s
After this commit, the precision increases dynamically as the duration
decreases. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.1019 s, 507 MB/s
$ dd count=1000 if=/dev/zero of=/dev/null
1000+0 records in
1000+0 records out
512000 bytes (512 kB, 500 KiB) copied, 0.002663 s, 256 MB/s
$ dd count=10 if=/dev/zero of=/dev/null
10+0 records in
10+0 records out
5120 bytes (5.1 kB, 5.0 KiB) copied, 0.000182 s, 5.1 MB/s
2023-03-12 03:37:12 +00:00
. stderr_contains ( " 0+0 records in \n 0+0 records out \n 0 bytes copied, " )
dd: fix flaky test_final_stats_unspec
If the first four decimal digits are zero, GNU dd elides them altogether.
Here's an execution on my PC:
```console
$ for i in $(seq 20000); do LC_ALL=C gnu_dd if=/dev/null of=/dev/null \
2>&1; done | grep copied | grep -E ' [0-9]e'
0 bytes copied, 1e-05 s, 0 B/s
0 bytes copied, 9e-06 s, 0.0 kB/s
```
Our implementation conforms to this, resulting in the following CI flake:
```
---- test_dd::test_final_stats_unspec stdout ----
run: D:\a\coreutils\coreutils\target\x86_64-pc-windows-gnu\debug\coreutils.exe dd
thread 'test_dd::test_final_stats_unspec' panicked at 'Stderr does not match regex:
0+0 records in
0+0 records out
0 bytes copied, 8e-05 s, 0.0 B/s
', tests\by-util\test_dd.rs:280:10
stack backtrace:
0: rust_begin_unwind
at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library\std\src/panicking.rs:578:5
```
Of course, this is just an overly strict regex in the test. This was a
one-in-tenthousand flaky test.
2024-02-23 05:20:11 +00:00
. stderr_matches ( & Regex ::new ( r "\d(\.\d+)?(e-\d\d)? s, " ) . unwrap ( ) )
dd: fix precision for display of total time spent
Improve the display of the total time spent transferring bytes so that
the number of seconds is displayed using the `%g` format specifier as
in `printf`. This matches the behavior of GNU `dd`.
Before this commit, the precision was always set to one digit after
the decimal point. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.2 s, 268.1 MB/s
After this commit, the precision increases dynamically as the duration
decreases. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.1019 s, 507 MB/s
$ dd count=1000 if=/dev/zero of=/dev/null
1000+0 records in
1000+0 records out
512000 bytes (512 kB, 500 KiB) copied, 0.002663 s, 256 MB/s
$ dd count=10 if=/dev/zero of=/dev/null
10+0 records in
10+0 records out
5120 bytes (5.1 kB, 5.0 KiB) copied, 0.000182 s, 5.1 MB/s
2023-03-12 03:37:12 +00:00
. stderr_contains ( " 0.0 B/s " )
2022-06-12 02:41:54 +00:00
. success ( ) ;
2021-06-21 18:17:47 +00:00
}
2022-02-09 18:08:28 +00:00
#[ cfg(any(target_os = " linux " , target_os = " android " )) ]
2021-06-21 18:17:47 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_excl_causes_failure_when_present ( ) {
2021-06-30 21:47:48 +00:00
let fname = " this-file-exists-excl.txt " ;
assert_fixture_exists! ( & fname ) ;
let ( _fix , mut ucmd ) = at_and_ucmd! ( ) ;
2021-07-06 18:52:48 +00:00
ucmd . args ( & [ " of=this-file-exists-excl.txt " , " conv=excl " ] )
2021-06-30 21:47:48 +00:00
. fails ( ) ;
}
2022-02-09 18:08:28 +00:00
#[ cfg(any(target_os = " linux " , target_os = " android " )) ]
2021-06-30 21:47:48 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_noatime_does_not_update_infile_atime ( ) {
2021-07-22 22:49:17 +00:00
// NOTE: Not all environments support tracking access time. If this
// test fails on some systems and passes on others, assume the functionality
// is not working and the systems that pass it simply don't update file access time.
2021-06-30 21:47:48 +00:00
let fname = " this-ifile-exists-noatime.txt " ;
assert_fixture_exists! ( & fname ) ;
let ( fix , mut ucmd ) = at_and_ucmd! ( ) ;
2021-07-06 18:52:48 +00:00
ucmd . args ( & [ " status=none " , " iflag=noatime " , inf! ( fname ) ] ) ;
2021-06-30 21:47:48 +00:00
2021-07-12 19:54:39 +00:00
let pre_atime = fix . metadata ( fname ) . accessed ( ) . unwrap ( ) ;
2021-06-30 21:47:48 +00:00
2021-07-06 18:52:48 +00:00
ucmd . run ( ) . no_stderr ( ) . success ( ) ;
2021-06-30 21:47:48 +00:00
2021-07-12 19:54:39 +00:00
let post_atime = fix . metadata ( fname ) . accessed ( ) . unwrap ( ) ;
2021-06-30 21:47:48 +00:00
assert_eq! ( pre_atime , post_atime ) ;
}
2022-02-09 18:08:28 +00:00
#[ cfg(any(target_os = " linux " , target_os = " android " )) ]
2021-06-30 21:47:48 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_noatime_does_not_update_ofile_atime ( ) {
2021-07-22 22:49:17 +00:00
// NOTE: Not all environments support tracking access time. If this
// test fails on some systems and passes on others, assume the functionality
// is not working and the systems that pass it simply don't update file access time.
2021-06-30 21:47:48 +00:00
let fname = " this-ofile-exists-noatime.txt " ;
assert_fixture_exists! ( & fname ) ;
let ( fix , mut ucmd ) = at_and_ucmd! ( ) ;
2021-07-06 18:52:48 +00:00
ucmd . args ( & [ " status=none " , " oflag=noatime " , of! ( fname ) ] ) ;
2021-06-30 21:47:48 +00:00
2021-07-12 19:54:39 +00:00
let pre_atime = fix . metadata ( fname ) . accessed ( ) . unwrap ( ) ;
2021-06-30 21:47:48 +00:00
2021-07-06 18:52:48 +00:00
ucmd . pipe_in ( " " ) . run ( ) . no_stderr ( ) . success ( ) ;
2021-06-30 21:47:48 +00:00
2021-07-12 19:54:39 +00:00
let post_atime = fix . metadata ( fname ) . accessed ( ) . unwrap ( ) ;
2021-06-30 21:47:48 +00:00
assert_eq! ( pre_atime , post_atime ) ;
}
2022-02-09 18:08:28 +00:00
#[ cfg(any(target_os = " linux " , target_os = " android " )) ]
2021-06-30 21:47:48 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_nocreat_causes_failure_when_outfile_not_present ( ) {
2021-06-30 21:47:48 +00:00
let fname = " this-file-does-not-exist.txt " ;
assert_fixture_not_exists! ( & fname ) ;
let ( fix , mut ucmd ) = at_and_ucmd! ( ) ;
2021-07-08 00:27:47 +00:00
ucmd . args ( & [ " conv=nocreat " , of! ( & fname ) ] )
. pipe_in ( " " )
. fails ( )
2021-12-29 14:13:52 +00:00
. stderr_only (
2023-01-05 20:09:15 +00:00
" dd: failed to open 'this-file-does-not-exist.txt': No such file or directory \n " ,
2021-12-29 14:13:52 +00:00
) ;
2021-07-12 19:54:39 +00:00
assert! ( ! fix . file_exists ( fname ) ) ;
2021-06-30 21:47:48 +00:00
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_notrunc_does_not_truncate ( ) {
2021-06-30 21:47:48 +00:00
// Set up test if needed (eg. after failure)
let fname = " this-file-exists-notrunc.txt " ;
let fpath = fixture_path! ( fname ) ;
2021-07-06 18:52:48 +00:00
match fpath . metadata ( ) {
Ok ( m ) if m . len ( ) = = 256 = > { }
_ = > build_test_file! ( & fpath , & build_ascii_block ( 256 ) ) ,
2021-06-30 21:47:48 +00:00
}
let ( fix , mut ucmd ) = at_and_ucmd! ( ) ;
2021-07-06 18:52:48 +00:00
ucmd . args ( & [ " status=none " , " conv=notrunc " , of! ( & fname ) , " if=null.txt " ] )
2021-06-30 21:47:48 +00:00
. run ( )
. no_stdout ( )
. no_stderr ( )
. success ( ) ;
2021-07-12 19:54:39 +00:00
assert_eq! ( 256 , fix . metadata ( fname ) . len ( ) ) ;
2021-06-30 21:47:48 +00:00
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_existing_file_truncated ( ) {
2021-06-30 21:47:48 +00:00
// Set up test if needed (eg. after failure)
let fname = " this-file-exists-truncated.txt " ;
let fpath = fixture_path! ( fname ) ;
2021-07-06 18:52:48 +00:00
match fpath . metadata ( ) {
Ok ( m ) if m . len ( ) = = 256 = > { }
_ = > build_test_file! ( & fpath , & vec! [ 0 ; 256 ] ) ,
2021-06-30 21:47:48 +00:00
}
let ( fix , mut ucmd ) = at_and_ucmd! ( ) ;
2021-07-06 18:52:48 +00:00
ucmd . args ( & [ " status=none " , " if=null.txt " , of! ( fname ) ] )
2021-06-30 21:47:48 +00:00
. run ( )
. no_stdout ( )
. no_stderr ( )
. success ( ) ;
2021-07-12 19:54:39 +00:00
assert_eq! ( 0 , fix . metadata ( fname ) . len ( ) ) ;
2021-06-30 21:47:48 +00:00
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_null_stats ( ) {
2021-06-21 18:17:47 +00:00
new_ucmd! ( )
dd: fix precision for display of total time spent
Improve the display of the total time spent transferring bytes so that
the number of seconds is displayed using the `%g` format specifier as
in `printf`. This matches the behavior of GNU `dd`.
Before this commit, the precision was always set to one digit after
the decimal point. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.2 s, 268.1 MB/s
After this commit, the precision increases dynamically as the duration
decreases. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.1019 s, 507 MB/s
$ dd count=1000 if=/dev/zero of=/dev/null
1000+0 records in
1000+0 records out
512000 bytes (512 kB, 500 KiB) copied, 0.002663 s, 256 MB/s
$ dd count=10 if=/dev/zero of=/dev/null
10+0 records in
10+0 records out
5120 bytes (5.1 kB, 5.0 KiB) copied, 0.000182 s, 5.1 MB/s
2023-03-12 03:37:12 +00:00
. arg ( " if=null.txt " )
2021-06-30 21:47:48 +00:00
. run ( )
dd: fix precision for display of total time spent
Improve the display of the total time spent transferring bytes so that
the number of seconds is displayed using the `%g` format specifier as
in `printf`. This matches the behavior of GNU `dd`.
Before this commit, the precision was always set to one digit after
the decimal point. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.2 s, 268.1 MB/s
After this commit, the precision increases dynamically as the duration
decreases. For example,
$ dd count=100000 if=/dev/zero of=/dev/null
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.1019 s, 507 MB/s
$ dd count=1000 if=/dev/zero of=/dev/null
1000+0 records in
1000+0 records out
512000 bytes (512 kB, 500 KiB) copied, 0.002663 s, 256 MB/s
$ dd count=10 if=/dev/zero of=/dev/null
10+0 records in
10+0 records out
5120 bytes (5.1 kB, 5.0 KiB) copied, 0.000182 s, 5.1 MB/s
2023-03-12 03:37:12 +00:00
. stderr_contains ( " 0+0 records in \n 0+0 records out \n 0 bytes copied, " )
. stderr_matches ( & Regex ::new ( r "\d\.\d+(e-\d\d)? s, " ) . unwrap ( ) )
. stderr_contains ( " 0.0 B/s " )
2021-06-30 21:47:48 +00:00
. success ( ) ;
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_null_fullblock ( ) {
2021-06-30 21:47:48 +00:00
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " if=null.txt " , " status=none " , " iflag=fullblock " ] )
2021-06-30 21:47:48 +00:00
. run ( )
. no_stdout ( )
. no_stderr ( )
. success ( ) ;
2021-06-21 18:17:47 +00:00
}
#[ cfg(unix) ]
2021-07-22 01:24:31 +00:00
#[ ignore ] // See note below before using this test.
2021-06-21 18:17:47 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_fullblock ( ) {
2021-06-30 21:47:48 +00:00
let tname = " fullblock-from-urand " ;
let tmp_fn = format! ( " TESTFILE- {} .tmp " , & tname ) ;
2021-07-01 21:33:30 +00:00
let exp_stats = vec! [
2021-06-30 21:47:48 +00:00
" 1+0 records in \n " ,
" 1+0 records out \n " ,
" 134217728 bytes (134 MB, 128 MiB) copied, " ,
2021-06-21 18:17:47 +00:00
] ;
2021-07-06 18:52:48 +00:00
let exp_stats = exp_stats . into_iter ( ) . fold ( Vec ::new ( ) , | mut acc , s | {
acc . extend ( s . bytes ( ) ) ;
acc
} ) ;
2021-06-30 21:47:48 +00:00
2021-07-01 21:33:30 +00:00
let ucmd = new_ucmd! ( )
2021-06-21 18:17:47 +00:00
. args ( & [
2021-06-30 21:47:48 +00:00
" if=/dev/urandom " ,
of! ( & tmp_fn ) ,
" bs=128M " ,
// Note: In order for this test to actually test iflag=fullblock, the bs=VALUE
2021-11-13 05:23:57 +00:00
// must be big enough to 'overwhelm' the urandom store of bytes.
2021-06-30 21:47:48 +00:00
// 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 " ,
2021-07-06 18:52:48 +00:00
] )
. run ( ) ;
2021-07-01 21:33:30 +00:00
ucmd . success ( ) ;
2021-06-30 21:47:48 +00:00
2021-07-01 21:33:30 +00:00
let run_stats = & ucmd . stderr ( ) [ .. exp_stats . len ( ) ] ;
assert_eq! ( exp_stats , run_stats ) ;
2021-06-21 18:17:47 +00:00
}
2021-06-30 21:47:48 +00:00
// Fileio
2021-06-21 18:17:47 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_ys_to_stdout ( ) {
let output : Vec < _ > = String ::from ( " y \n " ) . bytes ( ) . cycle ( ) . take ( 1024 ) . collect ( ) ;
2021-06-21 18:17:47 +00:00
let output = String ::from_utf8 ( output ) . unwrap ( ) ;
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " status=none " , " if=y-nl-1k.txt " ] )
2021-06-21 18:17:47 +00:00
. run ( )
2021-06-30 21:47:48 +00:00
. no_stderr ( )
. stdout_is ( output )
. success ( ) ;
2021-06-21 18:17:47 +00:00
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_zeros_to_stdout ( ) {
let output = vec! [ 0 ; 256 * 1024 ] ;
2021-06-21 18:17:47 +00:00
let output = String ::from_utf8 ( output ) . unwrap ( ) ;
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " status=none " , " if=zero-256k.txt " ] )
2021-06-21 18:17:47 +00:00
. run ( )
2021-06-30 21:47:48 +00:00
. no_stderr ( )
. stdout_is ( output )
. success ( ) ;
}
2022-02-22 19:58:41 +00:00
#[ cfg(target_pointer_width = " 32 " ) ]
#[ test ]
fn test_oversized_bs_32_bit ( ) {
2022-04-02 08:47:37 +00:00
for bs_param in [ " bs " , " ibs " , " obs " , " cbs " ] {
2022-02-22 19:58:41 +00:00
new_ucmd! ( )
. args ( & [ format! ( " {} =5GB " , bs_param ) ] )
. run ( )
. no_stdout ( )
. failure ( )
2023-01-01 14:33:02 +00:00
. code_is ( 1 )
2022-02-22 19:58:41 +00:00
. stderr_is ( format! ( " dd: {} =N cannot fit into memory \n " , bs_param ) ) ;
}
}
2021-06-30 21:47:48 +00:00
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_to_stdout_with_ibs_obs ( ) {
let output : Vec < _ > = String ::from ( " y \n " ) . bytes ( ) . cycle ( ) . take ( 1024 ) . collect ( ) ;
2021-06-30 21:47:48 +00:00
let output = String ::from_utf8 ( output ) . unwrap ( ) ;
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " status=none " , " if=y-nl-1k.txt " , " ibs=521 " , " obs=1031 " ] )
2021-06-30 21:47:48 +00:00
. run ( )
. no_stderr ( )
. stdout_is ( output )
. success ( ) ;
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_ascii_10k_to_stdout ( ) {
let output = build_ascii_block ( 1024 * 1024 ) ;
2021-06-30 21:47:48 +00:00
// build_test_file!("ascii-10k.txt", &output);
let output = String ::from_utf8 ( output ) . unwrap ( ) ;
new_ucmd! ( )
2021-07-06 18:52:48 +00:00
. args ( & [ " status=none " , " if=ascii-10k.txt " ] )
2021-06-30 21:47:48 +00:00
. run ( )
. no_stderr ( )
. stdout_is ( output )
. success ( ) ;
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_zeros_to_file ( ) {
2021-06-30 21:47:48 +00:00
let tname = " zero-256k " ;
2023-01-27 09:29:45 +00:00
let test_fn = format! ( " {tname} .txt " ) ;
2021-06-30 21:47:48 +00:00
let tmp_fn = format! ( " TESTFILE- {} .tmp " , & tname ) ;
2021-07-07 01:17:30 +00:00
assert_fixture_exists! ( test_fn ) ;
2021-06-30 21:47:48 +00:00
let ( fix , mut ucmd ) = at_and_ucmd! ( ) ;
2021-07-06 18:52:48 +00:00
ucmd . args ( & [ " status=none " , inf! ( test_fn ) , of! ( tmp_fn ) ] )
. run ( )
. no_stderr ( )
. no_stdout ( )
. success ( ) ;
2021-06-30 21:47:48 +00:00
2021-07-06 18:52:48 +00:00
cmp_file! (
File ::open ( fixture_path! ( & test_fn ) ) . unwrap ( ) ,
fix . open ( & tmp_fn )
) ;
2021-06-30 21:47:48 +00:00
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_to_file_with_ibs_obs ( ) {
2021-06-30 21:47:48 +00:00
let tname = " zero-256k " ;
2023-01-27 09:29:45 +00:00
let test_fn = format! ( " {tname} .txt " ) ;
2021-06-30 21:47:48 +00:00
let tmp_fn = format! ( " TESTFILE- {} .tmp " , & tname ) ;
2021-07-07 01:17:30 +00:00
assert_fixture_exists! ( test_fn ) ;
2021-06-30 21:47:48 +00:00
let ( fix , mut ucmd ) = at_and_ucmd! ( ) ;
ucmd . args ( & [
" status=none " ,
inf! ( test_fn ) ,
of! ( tmp_fn ) ,
" ibs=222 " ,
" obs=111 " ,
] )
2021-07-06 18:52:48 +00:00
. run ( )
. no_stderr ( )
. no_stdout ( )
. success ( ) ;
2021-06-30 21:47:48 +00:00
2021-07-06 18:52:48 +00:00
cmp_file! (
File ::open ( fixture_path! ( & test_fn ) ) . unwrap ( ) ,
fix . open ( & tmp_fn )
) ;
2021-06-30 21:47:48 +00:00
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_ascii_521k_to_file ( ) {
2021-06-30 21:47:48 +00:00
let tname = " ascii-521k " ;
2021-07-06 18:52:48 +00:00
let input = build_ascii_block ( 512 * 1024 ) ;
2021-06-30 21:47:48 +00:00
let tmp_fn = format! ( " TESTFILE- {} .tmp " , & tname ) ;
let ( fix , mut ucmd ) = at_and_ucmd! ( ) ;
2021-07-06 18:52:48 +00:00
ucmd . args ( & [ " status=none " , of! ( tmp_fn ) ] )
2021-06-30 21:47:48 +00:00
. pipe_in ( input . clone ( ) )
. run ( )
. no_stderr ( )
. no_stdout ( )
. success ( ) ;
2021-07-06 18:52:48 +00:00
assert_eq! ( 512 * 1024 , fix . metadata ( & tmp_fn ) . len ( ) ) ;
2021-06-30 21:47:48 +00:00
2021-07-06 18:52:48 +00:00
cmp_file! (
{
let mut input_f = tempfile ( ) . unwrap ( ) ;
2021-07-12 19:54:39 +00:00
input_f . write_all ( & input ) . unwrap ( ) ;
2021-07-06 18:52:48 +00:00
input_f
} ,
fix . open ( & tmp_fn )
) ;
2021-06-30 21:47:48 +00:00
}
#[ ignore ]
#[ cfg(unix) ]
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_ascii_5_gibi_to_file ( ) {
2021-06-30 21:47:48 +00:00
let tname = " ascii-5G " ;
let tmp_fn = format! ( " TESTFILE- {} .tmp " , & tname ) ;
let ( fix , mut ucmd ) = at_and_ucmd! ( ) ;
2021-07-12 17:13:47 +00:00
ucmd . args ( & [
" status=none " ,
" count=5G " ,
" iflag=count_bytes " ,
" if=/dev/zero " ,
2021-07-12 19:54:39 +00:00
of! ( tmp_fn ) ,
2021-07-12 17:13:47 +00:00
] )
2021-07-06 18:52:48 +00:00
. run ( )
. no_stderr ( )
. no_stdout ( )
. success ( ) ;
2021-06-30 21:47:48 +00:00
2021-07-06 18:52:48 +00:00
assert_eq! ( 5 * 1024 * 1024 * 1024 , fix . metadata ( & tmp_fn ) . len ( ) ) ;
2021-06-30 21:47:48 +00:00
}
#[ test ]
2021-07-06 18:52:48 +00:00
fn test_self_transfer ( ) {
2021-06-30 21:47:48 +00:00
let fname = " self-transfer-256k.txt " ;
2021-07-07 01:17:30 +00:00
assert_fixture_exists! ( fname ) ;
2021-06-30 21:47:48 +00:00
let ( fix , mut ucmd ) = at_and_ucmd! ( ) ;
2021-07-06 18:52:48 +00:00
ucmd . args ( & [ " status=none " , " conv=notrunc " , inf! ( fname ) , of! ( fname ) ] ) ;
2021-06-30 21:47:48 +00:00
assert! ( fix . file_exists ( fname ) ) ;
2021-07-06 18:52:48 +00:00
assert_eq! ( 256 * 1024 , fix . metadata ( fname ) . len ( ) ) ;
2021-06-30 21:47:48 +00:00
2021-07-06 18:52:48 +00:00
ucmd . run ( ) . no_stdout ( ) . no_stderr ( ) . success ( ) ;
2021-06-30 21:47:48 +00:00
assert! ( fix . file_exists ( fname ) ) ;
2021-07-06 18:52:48 +00:00
assert_eq! ( 256 * 1024 , fix . metadata ( fname ) . len ( ) ) ;
2021-06-21 18:17:47 +00:00
}
2021-07-07 01:17:30 +00:00
#[ test ]
fn test_unicode_filenames ( ) {
let tname = " 😎💚🦊 " ;
2023-01-27 09:29:45 +00:00
let test_fn = format! ( " {tname} .txt " ) ;
2021-07-07 01:17:30 +00:00
let tmp_fn = format! ( " TESTFILE- {} .tmp " , & tname ) ;
assert_fixture_exists! ( test_fn ) ;
let ( fix , mut ucmd ) = at_and_ucmd! ( ) ;
2021-07-08 16:14:06 +00:00
ucmd . args ( & [ " status=none " , inf! ( test_fn ) , of! ( tmp_fn ) ] )
. run ( )
. no_stderr ( )
. no_stdout ( )
. success ( ) ;
2021-07-07 01:17:30 +00:00
cmp_file! (
File ::open ( fixture_path! ( & test_fn ) ) . unwrap ( ) ,
fix . open ( & tmp_fn )
) ;
}
2022-01-23 02:01:16 +00:00
#[ test ]
fn test_conv_ascii_implies_unblock ( ) {
// 0x40 = 0o100 = 64, which gets converted to ' '
// 0xc1 = 0o301 = 193, which gets converted to 'A'
//
// `conv=ascii` implies `conv=unblock`, which means trailing paces
// are stripped and a newline is appended at the end of each
// block.
//
// `cbs=4` means use a conversion block size of 4 bytes per block.
new_ucmd! ( )
. args ( & [ " conv=ascii " , " cbs=4 " ] )
. pipe_in ( b " \x40 \xc1 \x40 \xc1 \x40 \xc1 \x40 \x40 " . to_vec ( ) )
. succeeds ( )
. stdout_is ( " A A \n A \n " ) ;
}
#[ test ]
fn test_conv_ebcdic_implies_block ( ) {
// 0x40 = 0o100 = 64, which is the result of converting from ' '
// 0xc1 = 0o301 = 193, which is the result of converting from 'A'
//
// `conv=ebcdic` implies `conv=block`, which means trailing spaces
// are added to pad each block.
//
// `cbs=4` means use a conversion block size of 4 bytes per block.
new_ucmd! ( )
. args ( & [ " conv=ebcdic " , " cbs=4 " ] )
. pipe_in ( " A A \n A \n " )
. succeeds ( )
. stdout_is_bytes ( b " \x40 \xc1 \x40 \xc1 \x40 \xc1 \x40 \x40 " ) ;
}
2022-01-23 15:45:17 +00:00
/// Test for seeking forward N bytes in the output file before copying.
#[ test ]
fn test_seek_bytes ( ) {
// Since the output file is stdout, seeking forward by eight bytes
// results in a prefix of eight null bytes.
new_ucmd! ( )
. args ( & [ " seek=8 " , " oflag=seek_bytes " ] )
. pipe_in ( " abcdefghijklm \n " )
. succeeds ( )
. stdout_is ( " \0 \0 \0 \0 \0 \0 \0 \0 abcdefghijklm \n " ) ;
}
2022-02-05 02:44:26 +00:00
/// Test for skipping beyond the number of bytes in a file.
#[ test ]
fn test_skip_beyond_file ( ) {
new_ucmd! ( )
. args ( & [ " bs=1 " , " skip=5 " , " count=0 " , " status=noxfer " ] )
. pipe_in ( " abcd " )
. succeeds ( )
. no_stdout ( )
. stderr_contains (
" 'standard input': cannot skip to specified offset \n 0+0 records in \n 0+0 records out \n " ,
) ;
}
2022-02-05 18:58:05 +00:00
#[ test ]
fn test_seek_do_not_overwrite ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
let mut outfile = at . make_file ( " outfile " ) ;
outfile . write_all ( b " abc " ) . unwrap ( ) ;
// Skip the first byte of the input, seek past the first byte of
// the output, and write only one byte to the output.
ucmd . args ( & [
" bs=1 " ,
" skip=1 " ,
" seek=1 " ,
" count=1 " ,
" status=noxfer " ,
" of=outfile " ,
] )
. pipe_in ( " 123 " )
. succeeds ( )
. stderr_is ( " 1+0 records in \n 1+0 records out \n " )
. no_stdout ( ) ;
assert_eq! ( at . read ( " outfile " ) , " a2 " ) ;
}
2022-02-13 03:38:25 +00:00
#[ test ]
fn test_partial_records_out ( ) {
new_ucmd! ( )
. args ( & [ " bs=2 " , " status=noxfer " ] )
. pipe_in ( " abc " )
. succeeds ( )
. stdout_is ( " abc " )
. stderr_is ( " 1+1 records in \n 1+1 records out \n " ) ;
}
2022-02-15 02:20:12 +00:00
#[ test ]
fn test_block_cbs16 ( ) {
new_ucmd! ( )
. args ( & [ " conv=block " , " cbs=16 " ] )
. pipe_in_fixture ( " dd-block-cbs16.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " dd-block-cbs16.spec " ) ;
}
#[ test ]
fn test_block_cbs16_as_cbs8 ( ) {
new_ucmd! ( )
. args ( & [ " conv=block " , " cbs=8 " ] )
. pipe_in_fixture ( " dd-block-cbs16.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " dd-block-cbs8.spec " ) ;
}
#[ test ]
fn test_block_consecutive_nl ( ) {
new_ucmd! ( )
. args ( & [ " conv=block " , " cbs=16 " ] )
. pipe_in_fixture ( " dd-block-consecutive-nl.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " dd-block-consecutive-nl-cbs16.spec " ) ;
}
#[ test ]
fn test_unblock_multi_16 ( ) {
new_ucmd! ( )
. args ( & [ " conv=unblock " , " cbs=16 " ] )
. pipe_in_fixture ( " dd-unblock-cbs16.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " dd-unblock-cbs16.spec " ) ;
}
#[ test ]
fn test_unblock_multi_16_as_8 ( ) {
new_ucmd! ( )
. args ( & [ " conv=unblock " , " cbs=8 " ] )
. pipe_in_fixture ( " dd-unblock-cbs16.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " dd-unblock-cbs8.spec " ) ;
}
#[ test ]
fn test_atoe_conv_spec_test ( ) {
new_ucmd! ( )
. args ( & [ " conv=ebcdic " ] )
. pipe_in_fixture ( " seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-conv-atoe-seq-byte-values.spec " ) ;
}
#[ test ]
fn test_etoa_conv_spec_test ( ) {
new_ucmd! ( )
. args ( & [ " conv=ascii " ] )
. pipe_in_fixture ( " seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-conv-etoa-seq-byte-values.spec " ) ;
}
#[ test ]
fn test_atoibm_conv_spec_test ( ) {
new_ucmd! ( )
. args ( & [ " conv=ibm " ] )
. pipe_in_fixture ( " seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-conv-atoibm-seq-byte-values.spec " ) ;
}
#[ test ]
fn test_lcase_ascii_to_ucase_ascii ( ) {
new_ucmd! ( )
. args ( & [ " conv=ucase " ] )
. pipe_in_fixture ( " lcase-ascii.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " ucase-ascii.test " ) ;
}
#[ test ]
fn test_ucase_ascii_to_lcase_ascii ( ) {
new_ucmd! ( )
. args ( & [ " conv=lcase " ] )
. pipe_in_fixture ( " ucase-ascii.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " lcase-ascii.test " ) ;
}
#[ test ]
fn test_atoe_and_ucase_conv_spec_test ( ) {
new_ucmd! ( )
. args ( & [ " conv=ebcdic,ucase " ] )
. pipe_in_fixture ( " seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " ucase-ebcdic.test " ) ;
}
#[ test ]
fn test_atoe_and_lcase_conv_spec_test ( ) {
new_ucmd! ( )
. args ( & [ " conv=ebcdic,lcase " ] )
. pipe_in_fixture ( " seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " lcase-ebcdic.test " ) ;
}
// TODO I think uppercase and lowercase are unintentionally swapped in
// the code that parses the command-line arguments. See this line from
// `parseargs.rs`:
//
// (ConvFlag::FmtAtoI, ConvFlag::UCase) => Some(&ASCII_TO_IBM_UCASE_TO_LCASE),
// (ConvFlag::FmtAtoI, ConvFlag::LCase) => Some(&ASCII_TO_IBM_LCASE_TO_UCASE),
//
// If my reading is correct and that is a typo, then the
// UCASE_TO_LCASE and LCASE_TO_UCASE in those lines should be swapped,
// and the expected output for the following two tests should be
// updated accordingly.
#[ test ]
fn test_atoibm_and_ucase_conv_spec_test ( ) {
new_ucmd! ( )
. args ( & [ " conv=ibm,ucase " ] )
. pipe_in_fixture ( " seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " lcase-ibm.test " ) ;
}
#[ test ]
fn test_atoibm_and_lcase_conv_spec_test ( ) {
new_ucmd! ( )
. args ( & [ " conv=ibm,lcase " ] )
. pipe_in_fixture ( " seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " ucase-ibm.test " ) ;
}
#[ test ]
fn test_swab_256_test ( ) {
new_ucmd! ( )
. args ( & [ " conv=swab " ] )
. pipe_in_fixture ( " seq-byte-values.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " seq-byte-values-swapped.test " ) ;
}
#[ test ]
fn test_swab_257_test ( ) {
new_ucmd! ( )
. args ( & [ " conv=swab " ] )
. pipe_in_fixture ( " seq-byte-values-odd.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " seq-byte-values-odd.spec " ) ;
}
2022-08-28 08:07:04 +00:00
#[ test ]
fn test_block_lower ( ) {
new_ucmd! ( )
. args ( & [ " conv=block,lcase " , " cbs=8 " ] )
. pipe_in_fixture ( " dd-block8-lowercase.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " dd-block8-lowercase.spec " ) ;
}
#[ test ]
fn test_lower_block ( ) {
new_ucmd! ( )
. args ( & [ " conv=lcase,block " , " cbs=8 " ] )
. pipe_in_fixture ( " dd-block8-lowercase.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " dd-block8-lowercase.spec " ) ;
}
#[ test ]
fn test_unblock_lower ( ) {
new_ucmd! ( )
. args ( & [ " conv=unblock,lcase " , " cbs=8 " ] )
. pipe_in_fixture ( " dd-unblock8-lowercase.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " dd-unblock8-lowercase.spec " ) ;
}
2022-02-15 02:20:12 +00:00
#[ test ]
fn test_zeros_4k_conv_sync_obs_gt_ibs ( ) {
new_ucmd! ( )
. args ( & [ " conv=sync " , " ibs=521 " , " obs=1031 " ] )
. pipe_in_fixture ( " zeros-620f0b67a91f7f74151bc5be745b7110.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-conv-sync-ibs-521-obs-1031-zeros.spec " ) ;
}
#[ test ]
fn test_zeros_4k_conv_sync_ibs_gt_obs ( ) {
new_ucmd! ( )
. args ( & [ " conv=sync " , " ibs=1031 " , " obs=521 " ] )
. pipe_in_fixture ( " zeros-620f0b67a91f7f74151bc5be745b7110.test " )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-conv-sync-ibs-1031-obs-521-zeros.spec " ) ;
}
#[ test ]
fn test_deadbeef_32k_conv_sync_obs_gt_ibs ( ) {
new_ucmd! ( )
. args ( & [
" conv=sync " ,
" ibs=521 " ,
" obs=1031 " ,
" if=deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-conv-sync-ibs-521-obs-1031-deadbeef.spec " ) ;
}
#[ test ]
fn test_deadbeef_32k_conv_sync_ibs_gt_obs ( ) {
new_ucmd! ( )
. args ( & [
" conv=sync " ,
" ibs=1031 " ,
" obs=521 " ,
" if=deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-conv-sync-ibs-1031-obs-521-deadbeef.spec " ) ;
}
#[ test ]
fn test_random_73k_test_bs_prime_obs_gt_ibs_sync ( ) {
new_ucmd! ( )
. args ( & [
" conv=sync " ,
" ibs=521 " ,
" obs=1031 " ,
" if=random-5828891cb1230748e146f34223bbd3b5.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-conv-sync-ibs-521-obs-1031-random.spec " ) ;
}
#[ test ]
fn test_random_73k_test_bs_prime_ibs_gt_obs_sync ( ) {
new_ucmd! ( )
. args ( & [
" conv=sync " ,
" ibs=1031 " ,
" obs=521 " ,
" if=random-5828891cb1230748e146f34223bbd3b5.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-conv-sync-ibs-1031-obs-521-random.spec " ) ;
}
#[ test ]
fn test_identity ( ) {
new_ucmd! ( )
. args ( & [ " if=zeros-620f0b67a91f7f74151bc5be745b7110.test " ] )
. succeeds ( )
. stdout_is_fixture_bytes ( " zeros-620f0b67a91f7f74151bc5be745b7110.test " ) ;
new_ucmd! ( )
. args ( & [ " if=ones-6ae59e64850377ee5470c854761551ea.test " ] )
. succeeds ( )
. stdout_is_fixture_bytes ( " ones-6ae59e64850377ee5470c854761551ea.test " ) ;
new_ucmd! ( )
. args ( & [ " if=deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test " ] )
. succeeds ( )
. stdout_is_fixture_bytes ( " deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test " ) ;
new_ucmd! ( )
. args ( & [ " if=random-5828891cb1230748e146f34223bbd3b5.test " ] )
. succeeds ( )
. stdout_is_fixture_bytes ( " random-5828891cb1230748e146f34223bbd3b5.test " ) ;
}
#[ test ]
fn test_random_73k_test_not_a_multiple_obs_gt_ibs ( ) {
new_ucmd! ( )
. args ( & [
" ibs=521 " ,
" obs=1031 " ,
" if=random-5828891cb1230748e146f34223bbd3b5.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " random-5828891cb1230748e146f34223bbd3b5.test " ) ;
}
#[ test ]
fn test_random_73k_test_obs_lt_not_a_multiple_ibs ( ) {
new_ucmd! ( )
. args ( & [
" ibs=1031 " ,
" obs=521 " ,
" if=random-5828891cb1230748e146f34223bbd3b5.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " random-5828891cb1230748e146f34223bbd3b5.test " ) ;
}
2022-11-19 03:45:34 +00:00
#[ cfg(not(windows)) ]
#[ test ]
fn test_random_73k_test_lazy_fullblock ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . mkfifo ( " fifo " ) ;
let child = ucmd
. args ( & [
" ibs=521 " ,
" obs=1031 " ,
" iflag=fullblock " ,
" if=fifo " ,
" status=noxfer " ,
] )
. run_no_wait ( ) ;
let data = at . read_bytes ( " random-5828891cb1230748e146f34223bbd3b5.test " ) ;
{
let mut fifo = OpenOptions ::new ( )
. write ( true )
. open ( at . plus ( " fifo " ) )
. unwrap ( ) ;
for chunk in data . chunks ( 521 / 2 ) {
fifo . write_all ( chunk ) . unwrap ( ) ;
sleep ( Duration ::from_millis ( 10 ) ) ;
}
}
2022-11-18 00:25:43 +00:00
child
. wait ( )
. unwrap ( )
. success ( )
. stdout_is_bytes ( & data )
. stderr_is ( " 142+1 records in \n 72+1 records out \n " ) ;
2022-11-19 03:45:34 +00:00
}
2022-02-15 02:20:12 +00:00
#[ test ]
fn test_deadbeef_all_32k_test_count_reads ( ) {
new_ucmd! ( )
. args ( & [
" bs=1024 " ,
" count=32 " ,
" if=deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test " ) ;
}
#[ test ]
fn test_deadbeef_all_32k_test_count_bytes ( ) {
new_ucmd! ( )
. args ( & [
" ibs=531 " ,
" obs=1031 " ,
" count=32x1024 " ,
" oflag=count_bytes " ,
" if=deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test " ) ;
}
#[ test ]
fn test_deadbeef_32k_to_16k_test_count_reads ( ) {
new_ucmd! ( )
. args ( & [
" ibs=1024 " ,
" obs=1031 " ,
" count=16 " ,
" if=deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-deadbeef-first-16k.spec " ) ;
}
#[ test ]
fn test_deadbeef_32k_to_12345_test_count_bytes ( ) {
new_ucmd! ( )
. args ( & [
" ibs=531 " ,
" obs=1031 " ,
" count=12345 " ,
" iflag=count_bytes " ,
" if=deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-deadbeef-first-12345.spec " ) ;
}
#[ test ]
fn test_random_73k_test_count_reads ( ) {
new_ucmd! ( )
. args ( & [
" bs=1024 " ,
" count=32 " ,
" if=random-5828891cb1230748e146f34223bbd3b5.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-random-first-32k.spec " ) ;
}
#[ test ]
fn test_random_73k_test_count_bytes ( ) {
new_ucmd! ( )
. args ( & [
" ibs=521 " ,
" obs=1031 " ,
" count=32x1024 " ,
" iflag=count_bytes " ,
" if=random-5828891cb1230748e146f34223bbd3b5.test " ,
] )
. succeeds ( )
. stdout_is_fixture_bytes ( " gnudd-random-first-32k.spec " ) ;
}
#[ test ]
fn test_all_valid_ascii_ebcdic_ascii_roundtrip_conv_test ( ) {
let tmp = new_ucmd! ( )
. args ( & [ " ibs=128 " , " obs=1024 " , " conv=ebcdic " ] )
. pipe_in_fixture ( " all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test " )
. succeeds ( )
. stdout_move_bytes ( ) ;
new_ucmd! ( )
. args ( & [ " ibs=256 " , " obs=1024 " , " conv=ascii " ] )
. pipe_in ( tmp )
. succeeds ( )
. stdout_is_fixture_bytes ( " all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test " ) ;
}
2022-02-13 03:34:39 +00:00
#[ test ]
fn test_skip_zero ( ) {
new_ucmd! ( )
. args ( & [ " skip=0 " , " status=noxfer " ] )
. succeeds ( )
. no_stdout ( )
. stderr_is ( " 0+0 records in \n 0+0 records out \n " ) ;
}
2022-02-18 03:35:30 +00:00
#[ test ]
fn test_truncated_record ( ) {
new_ucmd! ( )
. args ( & [ " cbs=1 " , " conv=block " , " status=noxfer " ] )
. pipe_in ( " ab " )
. succeeds ( )
. stdout_is ( " a " )
. stderr_is ( " 0+1 records in \n 0+1 records out \n 1 truncated record \n " ) ;
new_ucmd! ( )
. args ( & [ " cbs=1 " , " conv=block " , " status=noxfer " ] )
. pipe_in ( " ab \n cd \n " )
. succeeds ( )
. stdout_is ( " ac " )
. stderr_is ( " 0+1 records in \n 0+1 records out \n 2 truncated records \n " ) ;
}
2022-02-17 02:32:38 +00:00
/// Test that the output file can be `/dev/null`.
#[ cfg(unix) ]
#[ test ]
fn test_outfile_dev_null ( ) {
new_ucmd! ( ) . arg ( " of=/dev/null " ) . succeeds ( ) . no_stdout ( ) ;
}
2022-02-17 23:58:25 +00:00
#[ test ]
fn test_block_sync ( ) {
new_ucmd! ( )
. args ( & [ " ibs=5 " , " cbs=5 " , " conv=block,sync " , " status=noxfer " ] )
. pipe_in ( " 012 \n abcde \n " )
. succeeds ( )
// blocks: 1 2
. stdout_is ( " 012 abcde " )
. stderr_is ( " 2+0 records in \n 0+1 records out \n " ) ;
// It seems that a partial record in is represented as an
// all-spaces block at the end of the output. The "1 truncated
// record" line is present in the status report due to the line
// "abcdefg\n" being truncated to "abcde".
new_ucmd! ( )
. args ( & [ " ibs=5 " , " cbs=5 " , " conv=block,sync " , " status=noxfer " ] )
. pipe_in ( " 012 \n abcdefg \n " )
. succeeds ( )
// blocks: 1 2 3
. stdout_is ( " 012 abcde " )
. stderr_is ( " 2+1 records in \n 0+1 records out \n 1 truncated record \n " ) ;
}
2022-03-13 21:39:10 +00:00
#[ test ]
2022-03-18 01:16:41 +00:00
fn test_bytes_iseek_bytes_iflag ( ) {
2022-03-13 21:39:10 +00:00
new_ucmd! ( )
2022-03-18 01:16:41 +00:00
. args ( & [ " iseek=10 " , " iflag=skip_bytes " , " bs=2 " ] )
2022-03-13 21:39:10 +00:00
. pipe_in ( " 0123456789abcdefghijklm " )
. succeeds ( )
. stdout_is ( " abcdefghijklm " ) ;
}
#[ test ]
2022-09-12 14:06:31 +00:00
fn test_bytes_iseek_skip_not_additive ( ) {
2022-03-13 21:39:10 +00:00
new_ucmd! ( )
2022-09-12 14:06:31 +00:00
. args ( & [ " iseek=4 " , " skip=4 " , " iflag=skip_bytes " , " bs=2 " ] )
2022-03-13 21:39:10 +00:00
. pipe_in ( " 0123456789abcdefghijklm " )
. succeeds ( )
2022-09-12 14:06:31 +00:00
. stdout_is ( " 456789abcdefghijklm " ) ;
2022-03-13 21:39:10 +00:00
}
#[ test ]
fn test_bytes_oseek_bytes_oflag ( ) {
new_ucmd! ( )
2022-03-18 01:16:41 +00:00
. args ( & [ " oseek=8 " , " oflag=seek_bytes " , " bs=2 " ] )
2022-03-13 21:39:10 +00:00
. pipe_in ( " abcdefghijklm " )
. succeeds ( )
. stdout_is_fixture_bytes ( " dd-bytes-alphabet-null.spec " ) ;
}
#[ test ]
fn test_bytes_oseek_bytes_trunc_oflag ( ) {
new_ucmd! ( )
2022-03-18 01:16:41 +00:00
. args ( & [ " oseek=8 " , " oflag=seek_bytes " , " bs=2 " , " count=0 " ] )
2022-03-13 21:39:10 +00:00
. pipe_in ( " abcdefghijklm " )
2022-08-08 19:44:59 +00:00
. ignore_stdin_write_error ( )
2022-03-13 21:39:10 +00:00
. succeeds ( )
. stdout_is_fixture_bytes ( " dd-bytes-null-trunc.spec " ) ;
}
2022-03-18 01:16:41 +00:00
#[ test ]
2022-09-12 14:06:31 +00:00
fn test_bytes_oseek_seek_not_additive ( ) {
2022-03-18 01:16:41 +00:00
new_ucmd! ( )
2022-09-12 14:06:31 +00:00
. args ( & [ " oseek=8 " , " seek=8 " , " oflag=seek_bytes " , " bs=2 " ] )
2022-03-18 01:16:41 +00:00
. pipe_in ( " abcdefghijklm " )
. succeeds ( )
. stdout_is_fixture_bytes ( " dd-bytes-alphabet-null.spec " ) ;
}
2022-06-12 02:41:54 +00:00
#[ test ]
dd: correct rendering of SI and IEC byte counts
Adjust the rendering of the concise byte counts in both SI and IEC
units to better match the behavior of GNU dd.
Before this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1 KB, 1024 B) copied, 0.0 s, 1.0 MB/s
After this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.0 s, 1.0 MB/s
For comparison, GNU dd produces the following:
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.000332864 s, 3.1 MB/s
2022-06-14 02:00:58 +00:00
fn test_final_stats_less_than_one_kb_si ( ) {
2022-06-12 02:41:54 +00:00
let result = new_ucmd! ( ) . pipe_in ( " 0 " . repeat ( 999 ) ) . succeeds ( ) ;
let s = result . stderr_str ( ) ;
assert! ( s . starts_with ( " 1+1 records in \n 1+1 records out \n 999 bytes copied, " ) ) ;
dd: correct rendering of SI and IEC byte counts
Adjust the rendering of the concise byte counts in both SI and IEC
units to better match the behavior of GNU dd.
Before this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1 KB, 1024 B) copied, 0.0 s, 1.0 MB/s
After this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.0 s, 1.0 MB/s
For comparison, GNU dd produces the following:
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.000332864 s, 3.1 MB/s
2022-06-14 02:00:58 +00:00
}
2022-06-12 02:41:54 +00:00
dd: correct rendering of SI and IEC byte counts
Adjust the rendering of the concise byte counts in both SI and IEC
units to better match the behavior of GNU dd.
Before this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1 KB, 1024 B) copied, 0.0 s, 1.0 MB/s
After this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.0 s, 1.0 MB/s
For comparison, GNU dd produces the following:
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.000332864 s, 3.1 MB/s
2022-06-14 02:00:58 +00:00
#[ test ]
fn test_final_stats_less_than_one_kb_iec ( ) {
2022-06-12 02:41:54 +00:00
let result = new_ucmd! ( ) . pipe_in ( " 0 " . repeat ( 1000 ) ) . succeeds ( ) ;
let s = result . stderr_str ( ) ;
dd: correct rendering of SI and IEC byte counts
Adjust the rendering of the concise byte counts in both SI and IEC
units to better match the behavior of GNU dd.
Before this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1 KB, 1024 B) copied, 0.0 s, 1.0 MB/s
After this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.0 s, 1.0 MB/s
For comparison, GNU dd produces the following:
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.000332864 s, 3.1 MB/s
2022-06-14 02:00:58 +00:00
assert! ( s . starts_with ( " 1+1 records in \n 1+1 records out \n 1000 bytes (1.0 kB) copied, " ) ) ;
2022-06-12 02:41:54 +00:00
let result = new_ucmd! ( ) . pipe_in ( " 0 " . repeat ( 1023 ) ) . succeeds ( ) ;
let s = result . stderr_str ( ) ;
dd: correct rendering of SI and IEC byte counts
Adjust the rendering of the concise byte counts in both SI and IEC
units to better match the behavior of GNU dd.
Before this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1 KB, 1024 B) copied, 0.0 s, 1.0 MB/s
After this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.0 s, 1.0 MB/s
For comparison, GNU dd produces the following:
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.000332864 s, 3.1 MB/s
2022-06-14 02:00:58 +00:00
assert! ( s . starts_with ( " 1+1 records in \n 1+1 records out \n 1023 bytes (1.0 kB) copied, " ) ) ;
}
2022-06-12 02:41:54 +00:00
dd: correct rendering of SI and IEC byte counts
Adjust the rendering of the concise byte counts in both SI and IEC
units to better match the behavior of GNU dd.
Before this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1 KB, 1024 B) copied, 0.0 s, 1.0 MB/s
After this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.0 s, 1.0 MB/s
For comparison, GNU dd produces the following:
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.000332864 s, 3.1 MB/s
2022-06-14 02:00:58 +00:00
#[ test ]
fn test_final_stats_more_than_one_kb ( ) {
2022-06-12 02:41:54 +00:00
let result = new_ucmd! ( ) . pipe_in ( " 0 " . repeat ( 1024 ) ) . succeeds ( ) ;
let s = result . stderr_str ( ) ;
dd: correct rendering of SI and IEC byte counts
Adjust the rendering of the concise byte counts in both SI and IEC
units to better match the behavior of GNU dd.
Before this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1 KB, 1024 B) copied, 0.0 s, 1.0 MB/s
After this commit,
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.0 s, 1.0 MB/s
For comparison, GNU dd produces the following:
$ head -c 1024 /dev/zero | dd > /dev/null
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.000332864 s, 3.1 MB/s
2022-06-14 02:00:58 +00:00
assert! ( s . starts_with ( " 2+0 records in \n 2+0 records out \n 1024 bytes (1.0 kB, 1.0 KiB) copied, " ) ) ;
}
#[ test ]
fn test_final_stats_three_char_limit ( ) {
let result = new_ucmd! ( ) . pipe_in ( " 0 " . repeat ( 10_000 ) ) . succeeds ( ) ;
let s = result . stderr_str ( ) ;
assert! (
s . starts_with ( " 19+1 records in \n 19+1 records out \n 10000 bytes (10 kB, 9.8 KiB) copied, " )
) ;
let result = new_ucmd! ( ) . pipe_in ( " 0 " . repeat ( 100_000 ) ) . succeeds ( ) ;
let s = result . stderr_str ( ) ;
assert! (
s . starts_with ( " 195+1 records in \n 195+1 records out \n 100000 bytes (100 kB, 98 KiB) copied, " )
) ;
2022-06-12 02:41:54 +00:00
}
2022-08-17 09:40:42 +00:00
#[ test ]
fn test_invalid_number_arg_gnu_compatibility ( ) {
let commands = vec! [ " bs " , " cbs " , " count " , " ibs " , " obs " , " seek " , " skip " ] ;
for command in commands {
new_ucmd! ( )
2023-01-27 09:29:45 +00:00
. args ( & [ format! ( " {command} = " ) ] )
2022-08-17 09:40:42 +00:00
. fails ( )
2023-01-05 20:09:15 +00:00
. stderr_is ( " dd: invalid number: ‘ ’ \n " ) ;
2022-08-17 09:40:42 +00:00
new_ucmd! ( )
2023-01-27 09:29:45 +00:00
. args ( & [ format! ( " {command} =29d " ) ] )
2022-08-17 09:40:42 +00:00
. fails ( )
2023-01-05 20:09:15 +00:00
. stderr_is ( " dd: invalid number: ‘ 29d’ \n " ) ;
2022-08-17 09:40:42 +00:00
}
}
#[ test ]
fn test_invalid_flag_arg_gnu_compatibility ( ) {
let commands = vec! [ " iflag " , " oflag " ] ;
for command in commands {
new_ucmd! ( )
2023-01-27 09:29:45 +00:00
. args ( & [ format! ( " {command} = " ) ] )
2022-08-17 09:40:42 +00:00
. fails ( )
2023-01-08 18:39:49 +00:00
. usage_error ( " invalid input flag: ‘ ’ " ) ;
2022-08-17 09:40:42 +00:00
new_ucmd! ( )
2023-01-27 09:29:45 +00:00
. args ( & [ format! ( " {command} =29d " ) ] )
2022-08-17 09:40:42 +00:00
. fails ( )
2023-01-08 18:39:49 +00:00
. usage_error ( " invalid input flag: ‘ 29d’ " ) ;
2022-08-17 09:40:42 +00:00
}
}
#[ test ]
fn test_invalid_file_arg_gnu_compatibility ( ) {
new_ucmd! ( )
. args ( & [ " if= " ] )
. fails ( )
2023-01-05 20:09:15 +00:00
. stderr_is ( " dd: failed to open '': No such file or directory \n " ) ;
2022-08-17 09:40:42 +00:00
new_ucmd! ( )
. args ( & [ " if=81as9bn8as9g302az8ns9.pdf.zip.pl.com " ] )
. fails ( )
. stderr_is (
2023-01-05 20:09:15 +00:00
" dd: failed to open '81as9bn8as9g302az8ns9.pdf.zip.pl.com': No such file or directory \n " ,
2022-08-17 09:40:42 +00:00
) ;
new_ucmd! ( )
. args ( & [ " of= " ] )
. fails ( )
2023-01-05 20:09:15 +00:00
. stderr_is ( " dd: failed to open '': No such file or directory \n " ) ;
2022-08-17 09:40:42 +00:00
new_ucmd! ( )
. args ( & [ " of=81as9bn8as9g302az8ns9.pdf.zip.pl.com " ] )
. pipe_in ( " " )
. succeeds ( ) ;
}
2022-09-12 14:06:31 +00:00
#[ test ]
fn test_ucase_lcase ( ) {
new_ucmd! ( )
. arg ( " conv=ucase,lcase " )
. fails ( )
. stderr_contains ( " lcase " )
. stderr_contains ( " ucase " ) ;
}
#[ test ]
fn test_big_multiplication ( ) {
new_ucmd! ( )
. arg ( " ibs=10x10x10x10x10x10x10x10x10x10x10x10x10x10x10x10x10x10x10x10x10x10x10 " )
. fails ( )
. stderr_contains ( " invalid number " ) ;
}
2022-11-19 01:27:42 +00:00
/// Test for count, seek, and skip given in units of bytes.
#[ test ]
fn test_bytes_suffix ( ) {
new_ucmd! ( )
. args ( & [ " count=3B " , " status=none " ] )
. pipe_in ( " abcdef " )
. succeeds ( )
. stdout_only ( " abc " ) ;
new_ucmd! ( )
. args ( & [ " skip=3B " , " status=none " ] )
. pipe_in ( " abcdef " )
. succeeds ( )
. stdout_only ( " def " ) ;
new_ucmd! ( )
. args ( & [ " iseek=3B " , " status=none " ] )
. pipe_in ( " abcdef " )
. succeeds ( )
. stdout_only ( " def " ) ;
new_ucmd! ( )
. args ( & [ " seek=3B " , " status=none " ] )
. pipe_in ( " abcdef " )
. succeeds ( )
. stdout_only ( " \0 \0 \0 abcdef " ) ;
new_ucmd! ( )
. args ( & [ " oseek=3B " , " status=none " ] )
. pipe_in ( " abcdef " )
. succeeds ( )
. stdout_only ( " \0 \0 \0 abcdef " ) ;
}
2022-11-19 03:45:34 +00:00
2024-03-09 20:08:50 +00:00
#[ test ]
// the recursive nature of the suffix allows any string with a 'B' in it treated as bytes.
fn test_bytes_suffix_recursive ( ) {
new_ucmd! ( )
. args ( & [ " count=2Bx2 " , " status=none " ] )
. pipe_in ( " abcdef " )
. succeeds ( )
. stdout_only ( " abcd " ) ;
new_ucmd! ( )
. args ( & [ " skip=2Bx2 " , " status=none " ] )
. pipe_in ( " abcdef " )
. succeeds ( )
. stdout_only ( " ef " ) ;
new_ucmd! ( )
. args ( & [ " iseek=2Bx2 " , " status=none " ] )
. pipe_in ( " abcdef " )
. succeeds ( )
. stdout_only ( " ef " ) ;
new_ucmd! ( )
. args ( & [ " seek=2Bx2 " , " status=none " ] )
. pipe_in ( " abcdef " )
. succeeds ( )
. stdout_only ( " \0 \0 \0 \0 abcdef " ) ;
new_ucmd! ( )
. args ( & [ " oseek=2Bx2 " , " status=none " ] )
. pipe_in ( " abcdef " )
. succeeds ( )
. stdout_only ( " \0 \0 \0 \0 abcdef " ) ;
}
2022-11-19 03:45:34 +00:00
/// Test for "conv=sync" with a slow reader.
#[ cfg(not(windows)) ]
#[ test ]
fn test_sync_delayed_reader ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . mkfifo ( " fifo " ) ;
let child = ucmd
. args ( & [ " ibs=16 " , " obs=32 " , " conv=sync " , " if=fifo " , " status=noxfer " ] )
. run_no_wait ( ) ;
{
let mut fifo = OpenOptions ::new ( )
. write ( true )
. open ( at . plus ( " fifo " ) )
. unwrap ( ) ;
for _ in 0 .. 8 {
fifo . write_all ( & [ 0xF ; 8 ] ) . unwrap ( ) ;
sleep ( Duration ::from_millis ( 10 ) ) ;
}
}
// Expected output is 0xFFFFFFFF00000000FFFFFFFF00000000...
let mut expected : [ u8 ; 8 * 16 ] = [ 0 ; 8 * 16 ] ;
for i in 0 .. 8 {
for j in 0 .. 8 {
expected [ 16 * i + j ] = 0xF ;
}
}
2022-11-18 00:25:43 +00:00
child
. wait ( )
. unwrap ( )
. success ( )
. stdout_is_bytes ( expected )
. stderr_is ( " 0+8 records in \n 4+0 records out \n " ) ;
2022-11-19 03:45:34 +00:00
}
2022-11-28 02:01:25 +00:00
/// Test for making a sparse copy of the input file.
#[ test ]
fn test_sparse ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
// Create a file and make it a large sparse file.
//
// On common Linux filesystems, setting the length to one megabyte
// should cause the file to become a sparse file, but it depends
// on the system.
std ::fs ::File ::create ( at . plus ( " infile " ) )
. unwrap ( )
. set_len ( 1024 * 1024 )
. unwrap ( ) ;
// Perform a sparse copy.
ucmd . args ( & [ " bs=32K " , " if=infile " , " of=outfile " , " conv=sparse " ] )
. succeeds ( ) ;
// The number of bytes in the file should be accurate though the
// number of blocks stored on disk may be zero.
assert_eq! ( at . metadata ( " infile " ) . len ( ) , at . metadata ( " outfile " ) . len ( ) ) ;
}
2022-11-13 13:46:01 +00:00
/// Test that a seek on an output FIFO results in a read.
#[ test ]
2023-10-22 03:23:28 +00:00
#[ cfg(unix) ]
2022-11-13 13:46:01 +00:00
fn test_seek_output_fifo ( ) {
let ts = TestScenario ::new ( util_name! ( ) ) ;
let at = & ts . fixtures ;
at . mkfifo ( " fifo " ) ;
2023-10-22 03:23:28 +00:00
let mut ucmd = ts . ucmd ( ) ;
let child = ucmd
2022-11-13 13:46:01 +00:00
. args ( & [ " count=0 " , " seek=1 " , " of=fifo " , " status=noxfer " ] )
2023-10-22 03:23:28 +00:00
. run_no_wait ( ) ;
2022-11-13 13:46:01 +00:00
2023-11-16 15:02:38 +00:00
std ::fs ::write ( at . plus ( " fifo " ) , vec! [ 0 ; 512 ] ) . unwrap ( ) ;
2023-10-22 03:23:28 +00:00
child
. wait ( )
. unwrap ( )
. success ( )
. stderr_only ( " 0+0 records in \n 0+0 records out \n " ) ;
2022-11-13 13:46:01 +00:00
}
2022-11-19 16:36:08 +00:00
/// Test that a skip on an input FIFO results in a read.
#[ test ]
2023-10-22 03:23:28 +00:00
#[ cfg(unix) ]
2022-11-19 16:36:08 +00:00
fn test_skip_input_fifo ( ) {
let ts = TestScenario ::new ( util_name! ( ) ) ;
let at = & ts . fixtures ;
at . mkfifo ( " fifo " ) ;
2023-10-22 03:23:28 +00:00
let mut ucmd = ts . ucmd ( ) ;
let child = ucmd
2022-11-19 16:36:08 +00:00
. args ( & [ " count=0 " , " skip=1 " , " if=fifo " , " status=noxfer " ] )
2023-10-22 03:23:28 +00:00
. run_no_wait ( ) ;
2023-11-16 15:02:38 +00:00
std ::fs ::write ( at . plus ( " fifo " ) , vec! [ 0 ; 512 ] ) . unwrap ( ) ;
2022-11-19 16:36:08 +00:00
2023-10-22 03:23:28 +00:00
child
. wait ( )
. unwrap ( )
. success ( )
. stderr_only ( " 0+0 records in \n 0+0 records out \n " ) ;
2022-11-19 16:36:08 +00:00
}
2022-11-27 22:14:02 +00:00
/// Test for reading part of stdin from each of two child processes.
#[ cfg(all(not(windows), feature = " printf " )) ]
#[ test ]
fn test_multiple_processes_reading_stdin ( ) {
// TODO Investigate if this is possible on Windows.
let printf = format! ( " {TESTS_BINARY} printf 'abcdef \n ' " ) ;
let dd_skip = format! ( " {TESTS_BINARY} dd bs=1 skip=3 count=0 " ) ;
let dd = format! ( " {TESTS_BINARY} dd " ) ;
UCommand ::new ( )
. arg ( format! ( " {printf} | ( {dd_skip} && {dd} ) 2> /dev/null " ) )
. succeeds ( )
. stdout_only ( " def \n " ) ;
}
2023-02-24 03:05:47 +00:00
/// Test that discarding system file cache fails for stdin.
#[ test ]
#[ cfg(target_os = " linux " ) ]
fn test_nocache_stdin_error ( ) {
2023-05-07 20:33:21 +00:00
#[ cfg(not(target_env = " musl " )) ]
let detail = " Illegal seek " ;
#[ cfg(target_env = " musl " ) ]
let detail = " Invalid seek " ;
2023-02-24 03:05:47 +00:00
new_ucmd! ( )
. args ( & [ " iflag=nocache " , " count=0 " , " status=noxfer " ] )
. fails ( )
. code_is ( 1 )
2023-05-07 20:33:21 +00:00
. stderr_only ( format! ( " dd: failed to discard cache for: 'standard input': {detail} \n 0+0 records in \n 0+0 records out \n " ) ) ;
2023-02-24 03:05:47 +00:00
}
2024-01-30 14:51:25 +00:00
/// Test that dd fails when no number in count.
#[ test ]
fn test_empty_count_number ( ) {
new_ucmd! ( )
. args ( & [ " count=B " ] )
. fails ( )
. code_is ( 1 )
. stderr_only ( " dd: invalid number: ‘ B’ \n " ) ;
}
2023-02-24 03:05:47 +00:00
/// Test for discarding system file cache.
#[ test ]
#[ cfg(target_os = " linux " ) ]
fn test_nocache_file ( ) {
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
at . write_bytes ( " f " , b " a " . repeat ( 1 < < 20 ) . as_slice ( ) ) ;
ucmd . args ( & [ " if=f " , " of=/dev/null " , " iflag=nocache " , " status=noxfer " ] )
. succeeds ( )
. stderr_only ( " 2048+0 records in \n 2048+0 records out \n " ) ;
}
2023-07-22 09:26:56 +00:00
#[ test ]
#[ cfg(unix) ]
fn test_skip_past_dev ( ) {
// NOTE: This test intends to trigger code which can only be reached with root permissions.
let ts = TestScenario ::new ( util_name! ( ) ) ;
if let Ok ( result ) = run_ucmd_as_root_with_stdin_stdout (
& ts ,
& [ " bs=1 " , " skip=10000000000000000 " , " count=0 " , " status=noxfer " ] ,
Some ( " /dev/sda1 " ) ,
None ,
) {
result . stderr_contains ( " dd: 'standard input': cannot skip: Invalid argument " ) ;
result . stderr_contains ( " 0+0 records in " ) ;
result . stderr_contains ( " 0+0 records out " ) ;
result . code_is ( 1 ) ;
} else {
2023-12-27 23:42:36 +00:00
print! ( " Test skipped; requires root user " ) ;
2023-07-22 09:26:56 +00:00
}
}
#[ test ]
#[ cfg(unix) ]
fn test_seek_past_dev ( ) {
// NOTE: This test intends to trigger code which can only be reached with root permissions.
let ts = TestScenario ::new ( util_name! ( ) ) ;
if let Ok ( result ) = run_ucmd_as_root_with_stdin_stdout (
& ts ,
& [ " bs=1 " , " seek=10000000000000000 " , " count=0 " , " status=noxfer " ] ,
None ,
Some ( " /dev/sda1 " ) ,
) {
result . stderr_contains ( " dd: 'standard output': cannot seek: Invalid argument " ) ;
result . stderr_contains ( " 0+0 records in " ) ;
result . stderr_contains ( " 0+0 records out " ) ;
result . code_is ( 1 ) ;
} else {
2023-12-27 23:42:36 +00:00
print! ( " Test skipped; requires root user " ) ;
2023-07-22 09:26:56 +00:00
}
}
2023-03-18 02:42:24 +00:00
#[ test ]
2023-12-01 09:03:44 +00:00
#[ cfg(all(
unix ,
not ( target_os = " macos " ) ,
not ( target_os = " freebsd " ) ,
feature = " printf "
) ) ]
2023-03-18 02:42:24 +00:00
fn test_reading_partial_blocks_from_fifo ( ) {
// Create the FIFO.
let ts = TestScenario ::new ( util_name! ( ) ) ;
2024-03-22 09:38:54 +00:00
let at = & ts . fixtures ;
2023-03-18 02:42:24 +00:00
at . mkfifo ( " fifo " ) ;
let fifoname = at . plus_as_string ( " fifo " ) ;
// Start a `dd` process that reads from the fifo (so it will wait
// until the writer process starts).
let mut reader_command = Command ::new ( TESTS_BINARY ) ;
let child = reader_command
. args ( [ " dd " , " ibs=3 " , " obs=3 " , & format! ( " if= {} " , fifoname ) ] )
. stdout ( Stdio ::piped ( ) )
. stderr ( Stdio ::piped ( ) )
. spawn ( )
. unwrap ( ) ;
// Start different processes to write to the FIFO, with a small
// pause in between.
let mut writer_command = Command ::new ( " sh " ) ;
writer_command
. args ( [
" -c " ,
& format! ( " (printf \" ab \" ; sleep 0.1; printf \" cd \" ) > {} " , fifoname ) ,
] )
. spawn ( )
. unwrap ( ) ;
let output = child . wait_with_output ( ) . unwrap ( ) ;
assert_eq! ( output . stdout , b " abcd " ) ;
let expected = b " 0+2 records in \n 1+1 records out \n 4 bytes copied " ;
assert! ( output . stderr . starts_with ( expected ) ) ;
}
#[ test ]
2023-12-01 09:03:44 +00:00
#[ cfg(all(
unix ,
not ( target_os = " macos " ) ,
not ( target_os = " freebsd " ) ,
feature = " printf "
) ) ]
2023-03-18 02:42:24 +00:00
fn test_reading_partial_blocks_from_fifo_unbuffered ( ) {
// Create the FIFO.
let ts = TestScenario ::new ( util_name! ( ) ) ;
2024-03-22 09:38:54 +00:00
let at = ts . fixtures ;
2023-03-18 02:42:24 +00:00
at . mkfifo ( " fifo " ) ;
let fifoname = at . plus_as_string ( " fifo " ) ;
// Start a `dd` process that reads from the fifo (so it will wait
// until the writer process starts).
//
// `bs=N` takes precedence over `ibs=N` and `obs=N`.
let mut reader_command = Command ::new ( TESTS_BINARY ) ;
let child = reader_command
. args ( [ " dd " , " bs=3 " , " ibs=1 " , " obs=1 " , & format! ( " if= {} " , fifoname ) ] )
. stdout ( Stdio ::piped ( ) )
. stderr ( Stdio ::piped ( ) )
. spawn ( )
. unwrap ( ) ;
// Start different processes to write to the FIFO, with a small
// pause in between.
let mut writer_command = Command ::new ( " sh " ) ;
writer_command
. args ( [
" -c " ,
& format! ( " (printf \" ab \" ; sleep 0.1; printf \" cd \" ) > {} " , fifoname ) ,
] )
. spawn ( )
. unwrap ( ) ;
let output = child . wait_with_output ( ) . unwrap ( ) ;
assert_eq! ( output . stdout , b " abcd " ) ;
let expected = b " 0+2 records in \n 0+2 records out \n 4 bytes copied " ;
assert! ( output . stderr . starts_with ( expected ) ) ;
}
2024-03-16 16:10:49 +00:00
2024-03-20 05:55:54 +00:00
#[ test ]
#[ cfg(any(target_os = " linux " , target_os = " android " )) ]
fn test_iflag_directory_fails_when_file_is_passed_via_std_in ( ) {
let ts = TestScenario ::new ( util_name! ( ) ) ;
let at = & ts . fixtures ;
at . make_file ( " input " ) ;
let filename = at . plus_as_string ( " input " ) ;
new_ucmd! ( )
. args ( & [ " iflag=directory " , " count=0 " ] )
. set_stdin ( std ::process ::Stdio ::from ( File ::open ( filename ) . unwrap ( ) ) )
. fails ( )
. stderr_contains ( " standard input: not a directory " ) ;
}
2024-03-16 16:10:49 +00:00
#[ test ]
fn test_stdin_stdout_not_rewound_even_when_connected_to_seekable_file ( ) {
use std ::process ::Stdio ;
let ts = TestScenario ::new ( util_name! ( ) ) ;
let at = & ts . fixtures ;
at . write ( " in " , " abcde " ) ;
let stdin = OwnedFileDescriptorOrHandle ::open_file (
OpenOptions ::new ( ) . read ( true ) ,
at . plus ( " in " ) . as_path ( ) ,
)
. unwrap ( ) ;
let stdout = OwnedFileDescriptorOrHandle ::open_file (
OpenOptions ::new ( ) . create ( true ) . write ( true ) ,
at . plus ( " out " ) . as_path ( ) ,
)
. unwrap ( ) ;
let stderr = OwnedFileDescriptorOrHandle ::open_file (
OpenOptions ::new ( ) . create ( true ) . write ( true ) ,
at . plus ( " err " ) . as_path ( ) ,
)
. unwrap ( ) ;
ts . ucmd ( )
. args ( & [ " bs=1 " , " skip=1 " , " count=1 " ] )
. set_stdin ( Stdio ::from ( stdin . try_clone ( ) . unwrap ( ) ) )
. set_stdout ( Stdio ::from ( stdout . try_clone ( ) . unwrap ( ) ) )
. set_stderr ( Stdio ::from ( stderr . try_clone ( ) . unwrap ( ) ) )
. succeeds ( ) ;
ts . ucmd ( )
. args ( & [ " bs=1 " , " skip=1 " ] )
. set_stdin ( stdin )
. set_stdout ( stdout )
. set_stderr ( stderr )
. succeeds ( ) ;
let err_file_content = std ::fs ::read_to_string ( at . plus_as_string ( " err " ) ) . unwrap ( ) ;
println! ( " stderr: \n {} " , err_file_content ) ;
let out_file_content = std ::fs ::read_to_string ( at . plus_as_string ( " out " ) ) . unwrap ( ) ;
println! ( " stdout: \n {} " , out_file_content ) ;
assert_eq! ( out_file_content , " bde " ) ;
}