Files

280 lines
8.0 KiB
Rust
Raw Permalink Normal View History

2023-08-21 10:49:27 +02: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.
2025-09-09 11:16:39 +02:00
// spell-checker:ignore readelf
2025-03-28 09:22:03 +01:00
use uutests::util::TestScenario;
#[cfg(unix)]
use std::os::unix::fs::symlink as symlink_file;
2025-03-28 09:22:03 +01:00
use std::env;
pub const TESTS_BINARY: &str = env!("CARGO_BIN_EXE_coreutils");
// Set the environment variable for any tests
// Use the ctor attribute to run this function before any tests
2026-05-03 13:56:09 +02:00
#[ctor::ctor(unsafe)]
2025-03-28 09:22:03 +01:00
fn init() {
unsafe {
env::set_var("UUTESTS_BINARY_PATH", TESTS_BINARY);
2025-03-28 09:22:03 +01:00
}
// Print for debugging
2025-04-07 22:56:21 -04:00
eprintln!("Setting UUTESTS_BINARY_PATH={TESTS_BINARY}");
2025-03-28 09:22:03 +01:00
}
#[test]
#[cfg(all(
feature = "env",
any(target_os = "linux", target_os = "android"),
not(target_env = "musl")
))]
fn binary_name_protection() {
let ts = TestScenario::new("env");
let bin = ts.bin_path.clone();
ts.ucmd()
.arg("-a")
.arg("hijacked")
.arg(&bin)
.arg("--version")
.succeeds()
.stdout_contains("coreutils");
}
2026-03-17 22:35:34 +09:00
#[test]
fn test_coreutils_help_ignore_args() {
let scenario = TestScenario::new("help_ignoring_args");
let output = std::process::Command::new(&scenario.bin_path)
.arg("--help")
.arg("---")
.output()
.unwrap();
assert_eq!(output.status.code(), Some(0));
}
#[test]
#[cfg(feature = "ls")]
fn execution_phrase_double() {
use std::process::Command;
let scenario = TestScenario::new("ls");
2025-03-28 09:22:03 +01:00
if !scenario.bin_path.exists() {
println!("Skipping test: Binary not found at {:?}", scenario.bin_path);
return;
}
let output = Command::new(&scenario.bin_path)
.arg("ls")
.arg("--some-invalid-arg")
.env("LANG", "en_US.UTF-8")
.output()
.unwrap();
2025-03-24 21:04:32 +01:00
assert!(
String::from_utf8(output.stderr)
.unwrap()
.contains(&"Usage: ls".to_string())
2025-03-24 21:04:32 +01:00
);
}
#[test]
2025-07-24 13:46:27 +02:00
#[ignore = "Test assumes error upon non-UTF8"]
#[cfg(feature = "sort")]
fn util_name_double() {
use std::{
io::Write,
process::{Command, Stdio},
};
let scenario = TestScenario::new("sort");
2025-03-28 09:22:03 +01:00
if !scenario.bin_path.exists() {
println!("Skipping test: Binary not found at {:?}", scenario.bin_path);
return;
}
let mut child = Command::new(&scenario.bin_path)
.arg("sort")
.stdin(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
// input invalid utf8 to cause an error
child.stdin.take().unwrap().write_all(&[255]).unwrap();
let output = child.wait_with_output().unwrap();
assert!(String::from_utf8(output.stderr).unwrap().contains("sort: "));
}
#[test]
2025-07-24 13:46:27 +02:00
#[ignore = "Test assumes error upon non-UTF8"]
#[cfg(feature = "sort")]
2025-03-28 09:22:03 +01:00
#[cfg(unix)]
fn util_name_single() {
use std::{
io::Write,
process::{Command, Stdio},
};
let scenario = TestScenario::new("sort");
2025-03-28 09:22:03 +01:00
if !scenario.bin_path.exists() {
println!("Skipping test: Binary not found at {:?}", scenario.bin_path);
return;
}
symlink_file(&scenario.bin_path, scenario.fixtures.plus("uu-sort")).unwrap();
let mut child = Command::new(scenario.fixtures.plus("uu-sort"))
.stdin(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
// input invalid utf8 to cause an error
child.stdin.take().unwrap().write_all(&[255]).unwrap();
let output = child.wait_with_output().unwrap();
assert!(String::from_utf8(output.stderr).unwrap().contains(&format!(
"{}: ",
scenario.fixtures.plus("uu-sort").display()
)));
}
2024-04-06 23:25:30 +02:00
#[test]
2025-03-28 09:22:03 +01:00
#[cfg(unix)]
2024-04-06 23:25:30 +02:00
fn util_invalid_name_help() {
use std::process::Command;
2024-04-06 23:25:30 +02:00
let scenario = TestScenario::new("invalid_name");
2025-03-28 09:22:03 +01:00
if !scenario.bin_path.exists() {
println!("Skipping test: Binary not found at {:?}", scenario.bin_path);
return;
}
symlink_file(&scenario.bin_path, scenario.fixtures.plus("invalid_name")).unwrap();
let code = Command::new(scenario.fixtures.plus("invalid_name"))
2024-04-06 23:25:30 +02:00
.arg("--help")
.status()
.unwrap()
.code();
assert_eq!(code, Some(1));
2024-04-06 23:25:30 +02:00
}
#[test]
// The exact set of permitted filenames depends on many factors. Non-UTF-8 strings
// work on very few platforms, but linux works, especially because it also increases
// the likelihood that a filesystem is being used that supports non-UTF-8 filenames.
#[cfg(target_os = "linux")]
fn util_non_utf8_name_help() {
// Make sure we don't crash even if the util name is invalid UTF-8.
use std::{
ffi::OsStr,
os::unix::ffi::OsStrExt,
process::{Command, Stdio},
};
let scenario = TestScenario::new("invalid_name");
let non_utf8_path = scenario.fixtures.plus(OsStr::from_bytes(b"\xff"));
2025-03-28 09:22:03 +01:00
if !scenario.bin_path.exists() {
println!("Skipping test: Binary not found at {:?}", scenario.bin_path);
return;
}
symlink_file(&scenario.bin_path, &non_utf8_path).unwrap();
2024-04-06 23:25:30 +02:00
let child = Command::new(&non_utf8_path)
.arg("--help")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
let output = child.wait_with_output().unwrap();
assert_eq!(output.status.code(), Some(0));
assert_eq!(output.stderr, b"");
let output_str = String::from_utf8(output.stdout).unwrap();
2024-05-25 09:02:54 +02:00
assert!(output_str.contains("(multi-call binary)"), "{output_str:?}");
2024-04-06 23:25:30 +02:00
assert!(
output_str.contains("Usage: <unknown binary name> [function "),
2024-05-25 09:02:54 +02:00
"{output_str:?}"
2024-04-06 23:25:30 +02:00
);
}
#[test]
2025-03-28 09:22:03 +01:00
#[cfg(unix)]
2024-04-06 23:25:30 +02:00
fn util_invalid_name_invalid_command() {
use std::process::Command;
2024-04-06 23:25:30 +02:00
let scenario = TestScenario::new("invalid_name");
symlink_file(&scenario.bin_path, scenario.fixtures.plus("invalid_name")).unwrap();
2025-03-28 09:22:03 +01:00
if !scenario.bin_path.exists() {
println!("Skipping test: Binary not found at {:?}", scenario.bin_path);
return;
}
let code = Command::new(scenario.fixtures.plus("invalid_name"))
.status()
.unwrap()
.code();
assert_eq!(code, Some(1));
2024-04-06 23:25:30 +02:00
}
#[test]
fn util_version() {
use std::process::{Command, Stdio};
let scenario = TestScenario::new("--version");
if !scenario.bin_path.exists() {
println!("Skipping test: Binary not found at {:?}", scenario.bin_path);
return;
}
for arg in ["-V", "--version"] {
let child = Command::new(&scenario.bin_path)
.arg(arg)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
let output = child.wait_with_output().unwrap();
assert_eq!(output.status.code(), Some(0));
assert_eq!(output.stderr, b"");
let output_str = String::from_utf8(output.stdout).unwrap();
let ver = env::var("CARGO_PKG_VERSION").unwrap();
assert_eq!(format!("coreutils {ver} (multi-call binary)\n"), output_str);
}
}
2025-09-09 11:16:39 +02:00
#[test]
#[cfg(target_env = "musl")]
fn test_musl_no_dynamic_deps() {
use std::process::Command;
let scenario = TestScenario::new("test_musl_no_dynamic_deps");
if !scenario.bin_path.exists() {
println!("Skipping test: Binary not found at {:?}", scenario.bin_path);
return;
}
let output = Command::new("readelf")
.arg("-d")
.arg(&scenario.bin_path)
.output()
.expect("Failed to run readelf");
let stdout = String::from_utf8_lossy(&output.stdout);
// Static binaries should have no NEEDED entries (dynamic library dependencies)
assert!(
!stdout.contains("NEEDED"),
"Found dynamic dependencies in musl binary:\n{}",
stdout
);
}
#[test]
fn test_sorted_utils() {
let s = TestScenario::new("list_sorted");
let out = String::from_utf8(
std::process::Command::new(&s.bin_path)
.arg("--list")
.output()
.unwrap()
.stdout,
)
.unwrap();
assert!(out.lines().filter(|s| !s.is_empty()).is_sorted());
}