mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
Head: ensure stdin input stream is correct on exit
Fix issue #7028 Head tool now ensures that stdin is set to the last character that was output by the tool. This ensures that if any subsequent tools are run from the same input stream they will start at the correct point in the stream.
This commit is contained in:
parent
604cc404ff
commit
ad20cb35a0
2 changed files with 325 additions and 60 deletions
|
@ -7,6 +7,14 @@
|
|||
// spell-checker:ignore (words) seekable
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
#[cfg(all(
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "macos"),
|
||||
not(target_os = "android"),
|
||||
not(target_os = "freebsd"),
|
||||
not(target_os = "openbsd")
|
||||
))]
|
||||
use std::io::Read;
|
||||
|
||||
static INPUT: &str = "lorem_ipsum.txt";
|
||||
|
||||
|
@ -400,51 +408,51 @@ fn test_all_but_last_bytes_large_file_piped() {
|
|||
let fixtures = &scene.fixtures;
|
||||
|
||||
// First, create all our fixtures.
|
||||
let seq_30000_file_name = "seq_30000";
|
||||
let seq_29000_file_name = "seq_29000";
|
||||
let seq_29001_30000_file_name = "seq_29001_30000";
|
||||
let seq_20000_file_name = "seq_20000";
|
||||
let seq_19000_file_name = "seq_19000";
|
||||
let seq_19001_20000_file_name = "seq_19001_20000";
|
||||
scene
|
||||
.cmd("seq")
|
||||
.arg("30000")
|
||||
.set_stdout(fixtures.make_file(seq_30000_file_name))
|
||||
.arg("20000")
|
||||
.set_stdout(fixtures.make_file(seq_20000_file_name))
|
||||
.succeeds();
|
||||
scene
|
||||
.cmd("seq")
|
||||
.arg("29000")
|
||||
.set_stdout(fixtures.make_file(seq_29000_file_name))
|
||||
.arg("19000")
|
||||
.set_stdout(fixtures.make_file(seq_19000_file_name))
|
||||
.succeeds();
|
||||
scene
|
||||
.cmd("seq")
|
||||
.args(&["29001", "30000"])
|
||||
.set_stdout(fixtures.make_file(seq_29001_30000_file_name))
|
||||
.args(&["19001", "20000"])
|
||||
.set_stdout(fixtures.make_file(seq_19001_20000_file_name))
|
||||
.succeeds();
|
||||
|
||||
let seq_29001_30000_file_length = fixtures
|
||||
.open(seq_29001_30000_file_name)
|
||||
let seq_19001_20000_file_length = fixtures
|
||||
.open(seq_19001_20000_file_name)
|
||||
.metadata()
|
||||
.unwrap()
|
||||
.len();
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-c", &format!("-{}", seq_29001_30000_file_length)])
|
||||
.pipe_in_fixture(seq_30000_file_name)
|
||||
.args(&["-c", &format!("-{}", seq_19001_20000_file_length)])
|
||||
.pipe_in_fixture(seq_20000_file_name)
|
||||
.succeeds()
|
||||
.stdout_only_fixture(seq_29000_file_name);
|
||||
.stdout_only_fixture(seq_19000_file_name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_backwards_lines_large_file() {
|
||||
fn test_all_but_last_lines_large_file() {
|
||||
// Create our fixtures on the fly. We need the input file to be at least double
|
||||
// the size of BUF_SIZE as specified in head.rs. Go for something a bit bigger
|
||||
// than that.
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let fixtures = &scene.fixtures;
|
||||
let seq_30000_file_name = "seq_30000";
|
||||
let seq_20000_file_name = "seq_20000";
|
||||
let seq_1000_file_name = "seq_1000";
|
||||
scene
|
||||
.cmd("seq")
|
||||
.arg("30000")
|
||||
.set_stdout(fixtures.make_file(seq_30000_file_name))
|
||||
.arg("20000")
|
||||
.set_stdout(fixtures.make_file(seq_20000_file_name))
|
||||
.succeeds();
|
||||
scene
|
||||
.cmd("seq")
|
||||
|
@ -455,21 +463,246 @@ fn test_read_backwards_lines_large_file() {
|
|||
// Now run our tests.
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-n", "-29000", "seq_30000"])
|
||||
.args(&["-n", "-19000", seq_20000_file_name])
|
||||
.succeeds()
|
||||
.stdout_is_fixture("seq_1000");
|
||||
.stdout_only_fixture("seq_1000");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-n", "-30000", "seq_30000"])
|
||||
.run()
|
||||
.stdout_is_fixture("emptyfile.txt");
|
||||
.args(&["-n", "-20000", seq_20000_file_name])
|
||||
.succeeds()
|
||||
.stdout_only_fixture("emptyfile.txt");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-n", "-30001", "seq_30000"])
|
||||
.run()
|
||||
.stdout_is_fixture("emptyfile.txt");
|
||||
.args(&["-n", "-20001", seq_20000_file_name])
|
||||
.succeeds()
|
||||
.stdout_only_fixture("emptyfile.txt");
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "macos"),
|
||||
not(target_os = "android"),
|
||||
not(target_os = "freebsd"),
|
||||
not(target_os = "openbsd")
|
||||
))]
|
||||
#[test]
|
||||
fn test_validate_stdin_offset_lines() {
|
||||
// A handful of unix-only tests to validate behavior when reading from stdin on a seekable
|
||||
// file. GNU-compatibility requires that the stdin file be left such that if another
|
||||
// process is invoked on the same stdin file after head has run, the subsequent file should
|
||||
// start reading from the byte after the last byte printed by head.
|
||||
// Since this is unix-only requirement, keep this as a separate test rather than adding a
|
||||
// conditionally-compiled segment to multiple tests.
|
||||
//
|
||||
// Test scenarios...
|
||||
// 1 - Print the first n lines
|
||||
// 2 - Print all-but the last n lines
|
||||
// 3 - Print all but the last n lines, large file.
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let fixtures = &scene.fixtures;
|
||||
|
||||
// Test 1 - Print the first n lines
|
||||
fixtures.write("f1", "a\nb\nc\n");
|
||||
let file = fixtures.open("f1");
|
||||
let mut file_shadow = file.try_clone().unwrap();
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-n", "1"])
|
||||
.set_stdin(file)
|
||||
.succeeds()
|
||||
.stdout_only("a\n");
|
||||
let mut bytes_remaining_in_stdin = vec![];
|
||||
assert_eq!(
|
||||
file_shadow
|
||||
.read_to_end(&mut bytes_remaining_in_stdin)
|
||||
.unwrap(),
|
||||
4
|
||||
);
|
||||
assert_eq!(
|
||||
String::from_utf8(bytes_remaining_in_stdin).unwrap(),
|
||||
"b\nc\n"
|
||||
);
|
||||
|
||||
// Test 2 - Print all-but the last n lines
|
||||
fixtures.write("f2", "a\nb\nc\n");
|
||||
let file = fixtures.open("f2");
|
||||
let mut file_shadow = file.try_clone().unwrap();
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-n", "-1"])
|
||||
.set_stdin(file)
|
||||
.succeeds()
|
||||
.stdout_only("a\nb\n");
|
||||
let mut bytes_remaining_in_stdin = vec![];
|
||||
assert_eq!(
|
||||
file_shadow
|
||||
.read_to_end(&mut bytes_remaining_in_stdin)
|
||||
.unwrap(),
|
||||
2
|
||||
);
|
||||
assert_eq!(String::from_utf8(bytes_remaining_in_stdin).unwrap(), "c\n");
|
||||
|
||||
// Test 3 - Print all but the last n lines, large input file.
|
||||
// First, create all our fixtures.
|
||||
let seq_20000_file_name = "seq_20000";
|
||||
let seq_1000_file_name = "seq_1000";
|
||||
let seq_1001_20000_file_name = "seq_1001_20000";
|
||||
scene
|
||||
.cmd("seq")
|
||||
.arg("20000")
|
||||
.set_stdout(fixtures.make_file(seq_20000_file_name))
|
||||
.succeeds();
|
||||
scene
|
||||
.cmd("seq")
|
||||
.arg("1000")
|
||||
.set_stdout(fixtures.make_file(seq_1000_file_name))
|
||||
.succeeds();
|
||||
scene
|
||||
.cmd("seq")
|
||||
.args(&["1001", "20000"])
|
||||
.set_stdout(fixtures.make_file(seq_1001_20000_file_name))
|
||||
.succeeds();
|
||||
|
||||
let file = fixtures.open(seq_20000_file_name);
|
||||
let file_shadow = file.try_clone().unwrap();
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-n", "-19000"])
|
||||
.set_stdin(file)
|
||||
.succeeds()
|
||||
.stdout_only_fixture(seq_1000_file_name);
|
||||
scene
|
||||
.cmd("cat")
|
||||
.set_stdin(file_shadow)
|
||||
.succeeds()
|
||||
.stdout_only_fixture(seq_1001_20000_file_name);
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "macos"),
|
||||
not(target_os = "android"),
|
||||
not(target_os = "freebsd"),
|
||||
not(target_os = "openbsd")
|
||||
))]
|
||||
#[test]
|
||||
fn test_validate_stdin_offset_bytes() {
|
||||
// A handful of unix-only tests to validate behavior when reading from stdin on a seekable
|
||||
// file. GNU-compatibility requires that the stdin file be left such that if another
|
||||
// process is invoked on the same stdin file after head has run, the subsequent file should
|
||||
// start reading from the byte after the last byte printed by head.
|
||||
// Since this is unix-only requirement, keep this as a separate test rather than adding a
|
||||
// conditionally-compiled segment to multiple tests.
|
||||
//
|
||||
// Test scenarios...
|
||||
// 1 - Print the first n bytes
|
||||
// 2 - Print all-but the last n bytes
|
||||
// 3 - Print all-but the last n bytes, with n=0 (i.e. print everything)
|
||||
// 4 - Print all but the last n bytes, large file.
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let fixtures = &scene.fixtures;
|
||||
|
||||
// Test 1 - Print the first n bytes
|
||||
fixtures.write("f1", "abc\ndef\n");
|
||||
let file = fixtures.open("f1");
|
||||
let mut file_shadow = file.try_clone().unwrap();
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-c", "2"])
|
||||
.set_stdin(file)
|
||||
.succeeds()
|
||||
.stdout_only("ab");
|
||||
let mut bytes_remaining_in_stdin = vec![];
|
||||
assert_eq!(
|
||||
file_shadow
|
||||
.read_to_end(&mut bytes_remaining_in_stdin)
|
||||
.unwrap(),
|
||||
6
|
||||
);
|
||||
assert_eq!(
|
||||
String::from_utf8(bytes_remaining_in_stdin).unwrap(),
|
||||
"c\ndef\n"
|
||||
);
|
||||
|
||||
// Test 2 - Print all-but the last n bytes
|
||||
fixtures.write("f2", "abc\ndef\n");
|
||||
let file = fixtures.open("f2");
|
||||
let mut file_shadow = file.try_clone().unwrap();
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-c", "-3"])
|
||||
.set_stdin(file)
|
||||
.succeeds()
|
||||
.stdout_only("abc\nd");
|
||||
let mut bytes_remaining_in_stdin = vec![];
|
||||
assert_eq!(
|
||||
file_shadow
|
||||
.read_to_end(&mut bytes_remaining_in_stdin)
|
||||
.unwrap(),
|
||||
3
|
||||
);
|
||||
assert_eq!(String::from_utf8(bytes_remaining_in_stdin).unwrap(), "ef\n");
|
||||
|
||||
// Test 3 - Print all-but the last n bytes, n=0 (i.e. print everything)
|
||||
fixtures.write("f3", "abc\ndef\n");
|
||||
let file = fixtures.open("f3");
|
||||
let mut file_shadow = file.try_clone().unwrap();
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-c", "-0"])
|
||||
.set_stdin(file)
|
||||
.succeeds()
|
||||
.stdout_only("abc\ndef\n");
|
||||
let mut bytes_remaining_in_stdin = vec![];
|
||||
assert_eq!(
|
||||
file_shadow
|
||||
.read_to_end(&mut bytes_remaining_in_stdin)
|
||||
.unwrap(),
|
||||
0
|
||||
);
|
||||
assert_eq!(String::from_utf8(bytes_remaining_in_stdin).unwrap(), "");
|
||||
|
||||
// Test 4 - Print all but the last n bytes, large input file.
|
||||
// First, create all our fixtures.
|
||||
let seq_20000_file_name = "seq_20000";
|
||||
let seq_19000_file_name = "seq_19000";
|
||||
let seq_19001_20000_file_name = "seq_19001_20000";
|
||||
scene
|
||||
.cmd("seq")
|
||||
.arg("20000")
|
||||
.set_stdout(fixtures.make_file(seq_20000_file_name))
|
||||
.succeeds();
|
||||
scene
|
||||
.cmd("seq")
|
||||
.arg("19000")
|
||||
.set_stdout(fixtures.make_file(seq_19000_file_name))
|
||||
.succeeds();
|
||||
scene
|
||||
.cmd("seq")
|
||||
.args(&["19001", "20000"])
|
||||
.set_stdout(fixtures.make_file(seq_19001_20000_file_name))
|
||||
.succeeds();
|
||||
|
||||
let file = fixtures.open(seq_20000_file_name);
|
||||
let file_shadow = file.try_clone().unwrap();
|
||||
let seq_19001_20000_file_length = fixtures
|
||||
.open(seq_19001_20000_file_name)
|
||||
.metadata()
|
||||
.unwrap()
|
||||
.len();
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-c", &format!("-{}", seq_19001_20000_file_length)])
|
||||
.set_stdin(file)
|
||||
.succeeds()
|
||||
.stdout_only_fixture(seq_19000_file_name);
|
||||
scene
|
||||
.cmd("cat")
|
||||
.set_stdin(file_shadow)
|
||||
.succeeds()
|
||||
.stdout_only_fixture(seq_19001_20000_file_name);
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue