2021-06-03 22:49:06 +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.
2022-01-17 15:18:04 +00:00
// spell-checker:ignore (ToDO) abcdefghijklmnopqrstuvwxyz efghijklmnopqrstuvwxyz vwxyz emptyfile bogusfile
2021-06-03 22:49:06 +00:00
2020-05-04 06:25:36 +00:00
extern crate tail ;
2015-12-12 21:24:48 +00:00
2020-05-25 17:05:26 +00:00
use crate ::common ::util ::* ;
2016-05-22 07:46:54 +00:00
use std ::char ::from_digit ;
2016-12-20 01:47:40 +00:00
use std ::io ::Write ;
2015-12-12 21:24:48 +00:00
2021-05-29 12:32:35 +00:00
static FOOBAR_TXT : & str = " foobar.txt " ;
static FOOBAR_2_TXT : & str = " foobar2.txt " ;
static FOOBAR_WITH_NULL_TXT : & str = " foobar_with_null.txt " ;
2015-12-12 21:24:48 +00:00
#[ test ]
fn test_stdin_default ( ) {
2020-04-13 18:36:03 +00:00
new_ucmd! ( )
. pipe_in_fixture ( FOOBAR_TXT )
. run ( )
. stdout_is_fixture ( " foobar_stdin_default.expected " ) ;
2021-07-12 23:46:43 +00:00
}
#[ test ]
fn test_stdin_explicit ( ) {
new_ucmd! ( )
. pipe_in_fixture ( FOOBAR_TXT )
. arg ( " - " )
. run ( )
. stdout_is_fixture ( " foobar_stdin_default.expected " ) ;
2015-12-12 21:24:48 +00:00
}
#[ test ]
fn test_single_default ( ) {
2020-04-13 18:36:03 +00:00
new_ucmd! ( )
. arg ( FOOBAR_TXT )
. run ( )
. stdout_is_fixture ( " foobar_single_default.expected " ) ;
2015-12-12 21:24:48 +00:00
}
2016-03-20 05:25:53 +00:00
2016-03-27 21:42:45 +00:00
#[ test ]
fn test_n_greater_than_number_of_lines ( ) {
2020-04-13 18:36:03 +00:00
new_ucmd! ( )
. arg ( " -n " )
. arg ( " 99999999 " )
. arg ( FOOBAR_TXT )
. run ( )
2016-07-29 21:26:32 +00:00
. stdout_is_fixture ( FOOBAR_TXT ) ;
2016-03-27 21:42:45 +00:00
}
2016-04-02 10:32:33 +00:00
#[ test ]
fn test_null_default ( ) {
2020-04-13 18:36:03 +00:00
new_ucmd! ( )
. arg ( " -z " )
. arg ( FOOBAR_WITH_NULL_TXT )
. run ( )
. stdout_is_fixture ( " foobar_with_null_default.expected " ) ;
2016-04-02 10:32:33 +00:00
}
2016-04-02 21:09:20 +00:00
#[ test ]
fn test_follow ( ) {
2016-08-23 11:52:43 +00:00
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
2016-04-02 21:09:20 +00:00
let mut child = ucmd . arg ( " -f " ) . arg ( FOOBAR_TXT ) . run_no_wait ( ) ;
let expected = at . read ( " foobar_single_default.expected " ) ;
assert_eq! ( read_size ( & mut child , expected . len ( ) ) , expected ) ;
// We write in a temporary copy of foobar.txt
let expected = " line1 \n line2 \n " ;
at . append ( FOOBAR_TXT , expected ) ;
assert_eq! ( read_size ( & mut child , expected . len ( ) ) , expected ) ;
child . kill ( ) . unwrap ( ) ;
}
2016-05-30 20:34:53 +00:00
#[ test ]
fn test_follow_multiple ( ) {
2016-08-23 11:52:43 +00:00
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
2020-04-13 18:36:03 +00:00
let mut child = ucmd
. arg ( " -f " )
. arg ( FOOBAR_TXT )
. arg ( FOOBAR_2_TXT )
. run_no_wait ( ) ;
2016-05-30 20:34:53 +00:00
let expected = at . read ( " foobar_follow_multiple.expected " ) ;
assert_eq! ( read_size ( & mut child , expected . len ( ) ) , expected ) ;
let first_append = " trois \n " ;
at . append ( FOOBAR_2_TXT , first_append ) ;
assert_eq! ( read_size ( & mut child , first_append . len ( ) ) , first_append ) ;
2021-05-30 05:10:54 +00:00
let second_append = " twenty \n thirty \n " ;
2016-05-30 20:34:53 +00:00
let expected = at . read ( " foobar_follow_multiple_appended.expected " ) ;
at . append ( FOOBAR_TXT , second_append ) ;
assert_eq! ( read_size ( & mut child , expected . len ( ) ) , expected ) ;
child . kill ( ) . unwrap ( ) ;
}
2016-05-31 20:40:06 +00:00
#[ test ]
fn test_follow_stdin ( ) {
2020-04-13 18:36:03 +00:00
new_ucmd! ( )
. arg ( " -f " )
. pipe_in_fixture ( FOOBAR_TXT )
. run ( )
. stdout_is_fixture ( " follow_stdin.expected " ) ;
2016-05-31 20:40:06 +00:00
}
2021-05-30 05:10:54 +00:00
// FixME: test PASSES for usual windows builds, but fails for coverage testing builds (likely related to the specific RUSTFLAGS '-Zpanic_abort_tests -Cpanic=abort') This test also breaks tty settings under bash requiring a 'stty sane' or reset. // spell-checker:disable-line
2021-02-23 09:25:06 +00:00
#[ cfg(disable_until_fixed) ]
2016-12-20 01:47:40 +00:00
#[ test ]
fn test_follow_with_pid ( ) {
2020-08-10 05:15:26 +00:00
use std ::process ::{ Command , Stdio } ;
use std ::thread ::sleep ;
use std ::time ::Duration ;
2016-12-20 01:47:40 +00:00
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
#[ cfg(unix) ]
let dummy_cmd = " sh " ;
#[ cfg(windows) ]
let dummy_cmd = " cmd " ;
2020-04-13 18:36:03 +00:00
let mut dummy = Command ::new ( dummy_cmd )
. stdout ( Stdio ::null ( ) )
. spawn ( )
. unwrap ( ) ;
2016-12-20 01:47:40 +00:00
let pid = dummy . id ( ) ;
2020-04-13 18:36:03 +00:00
let mut child = ucmd
. arg ( " -f " )
. arg ( format! ( " --pid= {} " , pid ) )
. arg ( FOOBAR_TXT )
. arg ( FOOBAR_2_TXT )
. run_no_wait ( ) ;
2016-12-20 01:47:40 +00:00
let expected = at . read ( " foobar_follow_multiple.expected " ) ;
assert_eq! ( read_size ( & mut child , expected . len ( ) ) , expected ) ;
let first_append = " trois \n " ;
at . append ( FOOBAR_2_TXT , first_append ) ;
assert_eq! ( read_size ( & mut child , first_append . len ( ) ) , first_append ) ;
2021-05-30 05:10:54 +00:00
let second_append = " twenty \n thirty \n " ;
2016-12-20 01:47:40 +00:00
let expected = at . read ( " foobar_follow_multiple_appended.expected " ) ;
at . append ( FOOBAR_TXT , second_append ) ;
assert_eq! ( read_size ( & mut child , expected . len ( ) ) , expected ) ;
// kill the dummy process and give tail time to notice this
dummy . kill ( ) . unwrap ( ) ;
let _ = dummy . wait ( ) ;
sleep ( Duration ::from_secs ( 1 ) ) ;
let third_append = " should \n be \n ignored \n " ;
at . append ( FOOBAR_TXT , third_append ) ;
assert_eq! ( read_size ( & mut child , 1 ) , " \u{0} " ) ;
// On Unix, trying to kill a process that's already dead is fine; on Windows it's an error.
#[ cfg(unix) ]
child . kill ( ) . unwrap ( ) ;
#[ cfg(windows) ]
assert_eq! ( child . kill ( ) . is_err ( ) , true ) ;
}
2016-03-26 19:36:15 +00:00
#[ test ]
fn test_single_big_args ( ) {
2021-05-29 12:32:35 +00:00
const FILE : & str = " single_big_args.txt " ;
const EXPECTED_FILE : & str = " single_big_args_expected.txt " ;
2016-03-26 19:36:15 +00:00
const LINES : usize = 1_000_000 ;
const N_ARG : usize = 100_000 ;
2016-03-20 05:25:53 +00:00
2016-08-23 11:52:43 +00:00
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
2016-03-20 05:25:53 +00:00
2016-05-22 19:29:23 +00:00
let mut big_input = at . make_file ( FILE ) ;
2016-03-26 19:36:15 +00:00
for i in 0 .. LINES {
2021-05-29 12:32:35 +00:00
writeln! ( & mut big_input , " Line {} " , i ) . expect ( " Could not write to FILE " ) ;
2016-03-20 05:25:53 +00:00
}
2016-03-26 19:36:15 +00:00
big_input . flush ( ) . expect ( " Could not flush FILE " ) ;
2016-03-20 05:25:53 +00:00
2016-05-22 19:29:23 +00:00
let mut big_expected = at . make_file ( EXPECTED_FILE ) ;
2016-03-26 19:36:15 +00:00
for i in ( LINES - N_ARG ) .. LINES {
2021-05-29 12:32:35 +00:00
writeln! ( & mut big_expected , " Line {} " , i ) . expect ( " Could not write to EXPECTED_FILE " ) ;
2016-03-26 19:36:15 +00:00
}
big_expected . flush ( ) . expect ( " Could not flush EXPECTED_FILE " ) ;
2016-03-20 05:25:53 +00:00
2020-04-13 18:36:03 +00:00
ucmd . arg ( FILE )
. arg ( " -n " )
. arg ( format! ( " {} " , N_ARG ) )
. run ( )
. stdout_is ( at . read ( EXPECTED_FILE ) ) ;
2016-03-20 05:25:53 +00:00
}
2016-03-26 18:52:10 +00:00
#[ test ]
fn test_bytes_single ( ) {
2020-04-13 18:36:03 +00:00
new_ucmd! ( )
. arg ( " -c " )
. arg ( " 10 " )
. arg ( FOOBAR_TXT )
. run ( )
2016-07-29 21:26:32 +00:00
. stdout_is_fixture ( " foobar_bytes_single.expected " ) ;
2016-03-26 18:52:10 +00:00
}
2016-03-26 18:56:00 +00:00
#[ test ]
fn test_bytes_stdin ( ) {
2020-04-13 18:36:03 +00:00
new_ucmd! ( )
. arg ( " -c " )
. arg ( " 13 " )
. pipe_in_fixture ( FOOBAR_TXT )
. run ( )
. stdout_is_fixture ( " foobar_bytes_stdin.expected " ) ;
2016-03-26 18:56:00 +00:00
}
2016-03-27 21:33:55 +00:00
#[ test ]
fn test_bytes_big ( ) {
2021-05-29 12:32:35 +00:00
const FILE : & str = " test_bytes_big.txt " ;
const EXPECTED_FILE : & str = " test_bytes_big_expected.txt " ;
2016-03-27 21:33:55 +00:00
const BYTES : usize = 1_000_000 ;
const N_ARG : usize = 100_000 ;
2016-08-23 11:52:43 +00:00
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
2016-03-27 21:33:55 +00:00
2016-05-22 19:29:23 +00:00
let mut big_input = at . make_file ( FILE ) ;
2016-03-27 21:33:55 +00:00
for i in 0 .. BYTES {
2016-05-22 07:46:54 +00:00
let digit = from_digit ( ( i % 10 ) as u32 , 10 ) . unwrap ( ) ;
2016-03-27 21:33:55 +00:00
write! ( & mut big_input , " {} " , digit ) . expect ( " Could not write to FILE " ) ;
}
big_input . flush ( ) . expect ( " Could not flush FILE " ) ;
2016-05-22 19:29:23 +00:00
let mut big_expected = at . make_file ( EXPECTED_FILE ) ;
2016-03-27 21:33:55 +00:00
for i in ( BYTES - N_ARG ) .. BYTES {
2016-05-22 07:46:54 +00:00
let digit = from_digit ( ( i % 10 ) as u32 , 10 ) . unwrap ( ) ;
2016-03-27 21:33:55 +00:00
write! ( & mut big_expected , " {} " , digit ) . expect ( " Could not write to EXPECTED_FILE " ) ;
}
big_expected . flush ( ) . expect ( " Could not flush EXPECTED_FILE " ) ;
2020-04-13 18:36:03 +00:00
let result = ucmd
. arg ( FILE )
. arg ( " -c " )
. arg ( format! ( " {} " , N_ARG ) )
2021-04-17 12:22:20 +00:00
. succeeds ( )
. stdout_move_str ( ) ;
2016-03-27 21:33:55 +00:00
let expected = at . read ( EXPECTED_FILE ) ;
assert_eq! ( result . len ( ) , expected . len ( ) ) ;
for ( actual_char , expected_char ) in result . chars ( ) . zip ( expected . chars ( ) ) {
assert_eq! ( actual_char , expected_char ) ;
}
}
2016-04-02 21:16:11 +00:00
#[ test ]
fn test_lines_with_size_suffix ( ) {
2021-05-29 12:32:35 +00:00
const FILE : & str = " test_lines_with_size_suffix.txt " ;
const EXPECTED_FILE : & str = " test_lines_with_size_suffix_expected.txt " ;
2016-04-02 21:16:11 +00:00
const LINES : usize = 3_000 ;
const N_ARG : usize = 2 * 1024 ;
2016-08-23 11:52:43 +00:00
let ( at , mut ucmd ) = at_and_ucmd! ( ) ;
2016-04-02 21:16:11 +00:00
2016-05-22 19:29:23 +00:00
let mut big_input = at . make_file ( FILE ) ;
2016-04-02 21:16:11 +00:00
for i in 0 .. LINES {
writeln! ( & mut big_input , " Line {} " , i ) . expect ( " Could not write to FILE " ) ;
}
big_input . flush ( ) . expect ( " Could not flush FILE " ) ;
2016-05-22 19:29:23 +00:00
let mut big_expected = at . make_file ( EXPECTED_FILE ) ;
2016-04-02 21:16:11 +00:00
for i in ( LINES - N_ARG ) .. LINES {
writeln! ( & mut big_expected , " Line {} " , i ) . expect ( " Could not write to EXPECTED_FILE " ) ;
}
big_expected . flush ( ) . expect ( " Could not flush EXPECTED_FILE " ) ;
2020-04-13 18:36:03 +00:00
ucmd . arg ( FILE )
. arg ( " -n " )
. arg ( " 2K " )
. run ( )
. stdout_is_fixture ( EXPECTED_FILE ) ;
2016-04-02 21:16:11 +00:00
}
2017-10-05 19:25:21 +00:00
#[ test ]
fn test_multiple_input_files ( ) {
2020-04-13 18:36:03 +00:00
new_ucmd! ( )
. arg ( FOOBAR_TXT )
. arg ( FOOBAR_2_TXT )
. run ( )
. stdout_is_fixture ( " foobar_follow_multiple.expected " ) ;
2017-10-05 19:25:21 +00:00
}
#[ test ]
fn test_multiple_input_files_with_suppressed_headers ( ) {
2020-04-13 18:36:03 +00:00
new_ucmd! ( )
. arg ( FOOBAR_TXT )
. arg ( FOOBAR_2_TXT )
. arg ( " -q " )
. run ( )
. stdout_is_fixture ( " foobar_multiple_quiet.expected " ) ;
2017-10-05 19:25:21 +00:00
}
#[ test ]
fn test_multiple_input_quiet_flag_overrides_verbose_flag_for_suppressing_headers ( ) {
2020-04-13 18:36:03 +00:00
new_ucmd! ( )
. arg ( FOOBAR_TXT )
. arg ( FOOBAR_2_TXT )
. arg ( " -v " )
2021-06-03 18:37:29 +00:00
. arg ( " -q " )
2020-04-13 18:36:03 +00:00
. run ( )
. stdout_is_fixture ( " foobar_multiple_quiet.expected " ) ;
2017-10-05 19:25:21 +00:00
}
2021-03-22 09:01:54 +00:00
#[ test ]
fn test_negative_indexing ( ) {
let positive_lines_index = new_ucmd! ( ) . arg ( " -n " ) . arg ( " 5 " ) . arg ( FOOBAR_TXT ) . run ( ) ;
let negative_lines_index = new_ucmd! ( ) . arg ( " -n " ) . arg ( " -5 " ) . arg ( FOOBAR_TXT ) . run ( ) ;
let positive_bytes_index = new_ucmd! ( ) . arg ( " -c " ) . arg ( " 20 " ) . arg ( FOOBAR_TXT ) . run ( ) ;
let negative_bytes_index = new_ucmd! ( ) . arg ( " -c " ) . arg ( " -20 " ) . arg ( FOOBAR_TXT ) . run ( ) ;
2021-04-17 12:22:20 +00:00
assert_eq! ( positive_lines_index . stdout ( ) , negative_lines_index . stdout ( ) ) ;
assert_eq! ( positive_bytes_index . stdout ( ) , negative_bytes_index . stdout ( ) ) ;
2021-03-22 09:01:54 +00:00
}
2021-04-22 15:10:08 +00:00
#[ test ]
fn test_sleep_interval ( ) {
2021-04-24 10:46:06 +00:00
new_ucmd! ( ) . arg ( " -s " ) . arg ( " 10 " ) . arg ( FOOBAR_TXT ) . succeeds ( ) ;
2021-04-22 15:10:08 +00:00
}
2021-05-17 23:33:49 +00:00
/// Test for reading all but the first NUM bytes: `tail -c +3`.
#[ test ]
fn test_positive_bytes ( ) {
new_ucmd! ( )
. args ( & [ " -c " , " +3 " ] )
. pipe_in ( " abcde " )
. succeeds ( )
. stdout_is ( " cde " ) ;
}
/// Test for reading all bytes, specified by `tail -c +0`.
#[ test ]
fn test_positive_zero_bytes ( ) {
new_ucmd! ( )
. args ( & [ " -c " , " +0 " ] )
. pipe_in ( " abcde " )
. succeeds ( )
. stdout_is ( " abcde " ) ;
}
/// Test for reading all but the first NUM lines: `tail -n +3`.
#[ test ]
fn test_positive_lines ( ) {
new_ucmd! ( )
. args ( & [ " -n " , " +3 " ] )
. pipe_in ( " a \n b \n c \n d \n e \n " )
. succeeds ( )
. stdout_is ( " c \n d \n e \n " ) ;
}
2021-11-19 20:37:47 +00:00
/// Test for reading all but the first NUM lines: `tail -3`.
#[ test ]
fn test_obsolete_syntax_positive_lines ( ) {
new_ucmd! ( )
. args ( & [ " -3 " ] )
. pipe_in ( " a \n b \n c \n d \n e \n " )
. succeeds ( )
. stdout_is ( " c \n d \n e \n " ) ;
}
/// Test for reading all but the first NUM lines: `tail -n -10`.
#[ test ]
fn test_small_file ( ) {
new_ucmd! ( )
. args ( & [ " -n -10 " ] )
. pipe_in ( " a \n b \n c \n d \n e \n " )
. succeeds ( )
. stdout_is ( " a \n b \n c \n d \n e \n " ) ;
}
/// Test for reading all but the first NUM lines: `tail -10`.
#[ test ]
fn test_obsolete_syntax_small_file ( ) {
new_ucmd! ( )
. args ( & [ " -10 " ] )
. pipe_in ( " a \n b \n c \n d \n e \n " )
. succeeds ( )
. stdout_is ( " a \n b \n c \n d \n e \n " ) ;
}
2021-05-17 23:33:49 +00:00
/// Test for reading all lines, specified by `tail -n +0`.
#[ test ]
fn test_positive_zero_lines ( ) {
new_ucmd! ( )
. args ( & [ " -n " , " +0 " ] )
. pipe_in ( " a \n b \n c \n d \n e \n " )
. succeeds ( )
. stdout_is ( " a \n b \n c \n d \n e \n " ) ;
}
2021-06-01 10:17:11 +00:00
#[ test ]
fn test_tail_invalid_num ( ) {
new_ucmd! ( )
. args ( & [ " -c " , " 1024R " , " emptyfile.txt " ] )
. fails ( )
2021-06-21 22:22:30 +00:00
. stderr_is ( " tail: invalid number of bytes: '1024R' " ) ;
2021-06-01 10:17:11 +00:00
new_ucmd! ( )
. args ( & [ " -n " , " 1024R " , " emptyfile.txt " ] )
. fails ( )
2021-06-21 22:22:30 +00:00
. stderr_is ( " tail: invalid number of lines: '1024R' " ) ;
2021-06-01 10:17:11 +00:00
#[ cfg(not(target_pointer_width = " 128 " )) ]
new_ucmd! ( )
. args ( & [ " -c " , " 1Y " , " emptyfile.txt " ] )
. fails ( )
2021-06-21 22:22:30 +00:00
. stderr_is ( " tail: invalid number of bytes: '1Y': Value too large for defined data type " ) ;
2021-06-01 10:17:11 +00:00
#[ cfg(not(target_pointer_width = " 128 " )) ]
new_ucmd! ( )
. args ( & [ " -n " , " 1Y " , " emptyfile.txt " ] )
. fails ( )
2021-06-21 22:22:30 +00:00
. stderr_is ( " tail: invalid number of lines: '1Y': Value too large for defined data type " ) ;
2021-06-03 18:37:29 +00:00
#[ cfg(target_pointer_width = " 32 " ) ]
{
let sizes = [ " 1000G " , " 10T " ] ;
for size in & sizes {
new_ucmd! ( )
. args ( & [ " -c " , size ] )
. fails ( )
. code_is ( 1 )
. stderr_only ( format! (
2021-06-21 22:22:30 +00:00
" tail: invalid number of bytes: '{}': Value too large for defined data type " ,
2021-06-03 18:37:29 +00:00
size
) ) ;
}
}
2021-06-01 10:17:11 +00:00
}
#[ test ]
fn test_tail_num_with_undocumented_sign_bytes ( ) {
// tail: '-' is not documented (8.32 man pages)
// head: '+' is not documented (8.32 man pages)
const ALPHABET : & str = " abcdefghijklmnopqrstuvwxyz " ;
new_ucmd! ( )
. args ( & [ " -c " , " 5 " ] )
. pipe_in ( ALPHABET )
. succeeds ( )
. stdout_is ( " vwxyz " ) ;
new_ucmd! ( )
. args ( & [ " -c " , " -5 " ] )
. pipe_in ( ALPHABET )
. succeeds ( )
. stdout_is ( " vwxyz " ) ;
new_ucmd! ( )
. args ( & [ " -c " , " +5 " ] )
. pipe_in ( ALPHABET )
. succeeds ( )
. stdout_is ( " efghijklmnopqrstuvwxyz " ) ;
}
2021-09-08 00:09:09 +00:00
#[ test ]
#[ cfg(unix) ]
fn test_tail_bytes_for_funny_files ( ) {
// gnu/tests/tail-2/tail-c.sh
let ts = TestScenario ::new ( util_name! ( ) ) ;
let at = & ts . fixtures ;
for & file in & [ " /proc/version " , " /sys/kernel/profiling " ] {
if ! at . file_exists ( file ) {
continue ;
}
let args = [ " --bytes " , " 1 " , file ] ;
let result = ts . ucmd ( ) . args ( & args ) . run ( ) ;
let exp_result = unwrap_or_return! ( expected_result ( & ts , & args ) ) ;
result
. stdout_is ( exp_result . stdout_str ( ) )
. stderr_is ( exp_result . stderr_str ( ) )
. code_is ( exp_result . code ( ) ) ;
}
}
2022-01-17 15:18:04 +00:00
#[ test ]
fn test_no_such_file ( ) {
new_ucmd! ( )
. arg ( " bogusfile " )
. fails ( )
. no_stdout ( )
. stderr_contains ( " cannot open 'bogusfile' for reading: No such file or directory " ) ;
}