Files
Cả thế giới là Rust 1eea517d37 test(more): Fix test_from_line_option race condition by increasing PTY delay (#9629)
* test(more): Fix test_from_line_option race condition by increasing PTY delay

Increased the delay in run_more_with_pty() from 100ms to 500ms to allow
more time to fully render output before the test reads from the PTY.

The test was failing because it was reading too early, before more could
initialize the terminal and render the file content. The -F flag itself
works correctly.

* test_more: decrease the delay

---------

Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
2025-12-31 20:20:50 +01:00

256 lines
6.8 KiB
Rust

// 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.
#[cfg(unix)]
use nix::unistd::{read, write};
#[cfg(unix)]
use std::fs::File;
#[cfg(unix)]
use std::fs::{Permissions, set_permissions};
#[cfg(target_os = "linux")]
use std::os::unix::ffi::OsStrExt;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
#[cfg(unix)]
use uutests::util::pty_path;
#[cfg(unix)]
use uutests::{at_and_ucmd, new_ucmd};
#[cfg(unix)]
fn run_more_with_pty(
args: &[&str],
file: &str,
content: &str,
) -> (uutests::util::UChild, std::os::fd::OwnedFd, String) {
let (path, controller, _replica) = pty_path();
let (at, mut ucmd) = at_and_ucmd!();
at.write(file, content);
let mut child = ucmd
.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.args(args)
.arg(file)
.run_no_wait();
child.delay(200);
let mut output = vec![0u8; 1024];
let n = read(&controller, &mut output).unwrap();
let output_str = String::from_utf8_lossy(&output[..n]).to_string();
(child, controller, output_str)
}
#[cfg(unix)]
fn quit_more(controller: &std::os::fd::OwnedFd, mut child: uutests::util::UChild) {
write(controller, b"q").unwrap();
child.delay(50);
}
#[cfg(unix)]
#[test]
fn test_no_arg() {
let (path, _controller, _replica) = pty_path();
new_ucmd!()
.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.fails()
.stderr_contains("more: bad usage");
}
#[test]
#[cfg(unix)]
fn test_valid_arg() {
let args_list: Vec<&[&str]> = vec![
&["-c"],
&["--clean-print"],
&["-p"],
&["--print-over"],
&["-s"],
&["--squeeze"],
&["-u"],
&["--plain"],
&["-n", "10"],
&["--lines", "0"],
&["--number", "0"],
&["-F", "10"],
&["--from-line", "0"],
&["-P", "something"],
&["--pattern", "-1"],
];
for args in args_list {
test_alive(args);
}
}
#[cfg(unix)]
fn test_alive(args: &[&str]) {
let (at, mut ucmd) = at_and_ucmd!();
let (path, controller, _replica) = pty_path();
let content = "test content";
let file = "test_file";
at.write(file, content);
let mut child = ucmd
.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.args(args)
.arg(file)
.run_no_wait();
// wait for more to start and display the file
child.delay(100);
assert!(child.is_alive(), "Command should still be alive");
// cleanup
write(&controller, b"q").unwrap();
child.delay(50);
}
#[test]
#[cfg(unix)]
fn test_invalid_arg() {
let (path, _controller, _replica) = pty_path();
new_ucmd!()
.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.arg("--invalid")
.fails();
let (path, _controller, _replica) = pty_path();
new_ucmd!()
.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.arg("--lines")
.arg("-10")
.fails();
let (path, _controller, _replica) = pty_path();
new_ucmd!()
.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.arg("--from-line")
.arg("-10")
.fails();
}
#[test]
#[cfg(unix)]
fn test_file_arg() {
// Directory as argument
let (path, _controller, _replica) = pty_path();
new_ucmd!()
.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.arg(".")
.succeeds()
.stderr_contains("'.' is a directory.");
// Single argument errors
let (path, _controller, _replica) = pty_path();
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir_all("folder");
ucmd.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.arg("folder")
.succeeds()
.stderr_contains("is a directory");
let (path, _controller, _replica) = pty_path();
new_ucmd!()
.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.arg("nonexistent_file")
.succeeds()
.stderr_contains("No such file or directory");
// Multiple nonexistent files
let (path, _controller, _replica) = pty_path();
new_ucmd!()
.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.arg("file2")
.arg("file3")
.succeeds()
.stderr_contains("file2")
.stderr_contains("file3");
}
#[test]
#[cfg(unix)]
fn test_invalid_file_perms() {
let (path, _controller, _replica) = pty_path();
let (at, mut ucmd) = at_and_ucmd!();
let permissions = Permissions::from_mode(0o244);
at.make_file("invalid-perms.txt");
set_permissions(at.plus("invalid-perms.txt"), permissions).unwrap();
ucmd.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.arg("invalid-perms.txt")
.succeeds()
.stderr_contains("permission denied");
}
#[test]
#[cfg(target_os = "linux")]
fn test_more_non_utf8_paths() {
let (path, _controller, _replica) = pty_path();
let (at, mut ucmd) = at_and_ucmd!();
let file_name = std::ffi::OsStr::from_bytes(b"test_\xFF\xFE.txt");
// Create test file with normal name first
at.write(
&file_name.to_string_lossy(),
"test content for non-UTF-8 file",
);
// Test that more can handle non-UTF-8 filenames without crashing
ucmd.set_stdin(File::open(&path).unwrap())
.set_stdout(File::create(&path).unwrap())
.arg(file_name)
.succeeds();
}
#[test]
#[cfg(unix)]
fn test_basic_display() {
let (child, controller, output) = run_more_with_pty(&[], "test.txt", "line1\nline2\nline3\n");
assert!(output.contains("line1"));
quit_more(&controller, child);
}
#[test]
#[cfg(unix)]
fn test_squeeze_blank_lines() {
let (child, controller, output) =
run_more_with_pty(&["-s"], "test.txt", "line1\n\n\n\nline2\n");
assert!(output.contains("line1"));
quit_more(&controller, child);
}
#[test]
#[cfg(unix)]
fn test_pattern_search() {
let (child, controller, output) = run_more_with_pty(
&["-P", "target"],
"test.txt",
"foo\nbar\nbaz\ntarget\nend\n",
);
assert!(output.contains("target"));
assert!(!output.contains("foo"));
quit_more(&controller, child);
}
#[test]
#[cfg(unix)]
fn test_from_line_option() {
let (child, controller, output) =
run_more_with_pty(&["-F", "2"], "test.txt", "line1\nline2\nline3\nline4\n");
assert!(output.contains("line2"));
assert!(!output.contains("line1"));
quit_more(&controller, child);
}