use uutests::util::{expected_result, pty_path};
use uutests::{at_and_ts, new_ucmd, unwrap_or_return};
fn normalize_stderr(stderr: &str) -> String {
let re = regex::Regex::new(r"Try '[^']*(?:g)?stty --help'").unwrap();
re.replace_all(stderr, "Try 'stty --help'").to_string()
}
#[test]
fn test_invalid_arg() {
new_ucmd!()
.arg("--definitely-invalid")
.fails_with_code(1)
.stderr_contains("invalid argument")
.stderr_contains("--definitely-invalid");
}
#[test]
#[cfg(unix)]
fn test_basic() {
let (path, _controller, _replica) = pty_path();
new_ucmd!()
.args(&["--file", &path])
.succeeds()
.stdout_contains("speed");
}
#[test]
#[cfg(unix)]
fn test_all_flag() {
let (path, _controller, _replica) = pty_path();
let result = new_ucmd!().args(&["--all", "--file", &path]).succeeds();
for flag in ["parenb", "parmrk", "ixany", "onlcr", "icanon", "noflsh"] {
result.stdout_contains(flag);
}
}
#[test]
#[cfg(unix)]
fn test_sane() {
let (path, _controller, _replica) = pty_path();
new_ucmd!()
.args(&["--file", &path, "intr", "^A"])
.succeeds();
new_ucmd!()
.args(&["--file", &path])
.succeeds()
.stdout_contains("intr = ^A");
new_ucmd!().args(&["--file", &path, "sane"]).succeeds();
new_ucmd!()
.args(&["--file", &path])
.succeeds()
.stdout_str_check(|s| !s.contains("intr = ^A"));
}
#[test]
fn save_and_setting() {
new_ucmd!()
.args(&["--save", "nl0"])
.fails()
.stderr_contains("when specifying an output style, modes may not be set");
}
#[test]
fn all_and_setting() {
new_ucmd!()
.args(&["--all", "nl0"])
.fails()
.stderr_contains("when specifying an output style, modes may not be set");
}
#[test]
fn all_and_print_setting() {
new_ucmd!()
.args(&["--all", "size"])
.fails()
.stderr_contains("when specifying an output style, modes may not be set");
}
#[test]
fn save_and_all() {
new_ucmd!()
.args(&["--save", "--all"])
.fails()
.stderr_contains(
"the options for verbose and stty-readable output styles are mutually exclusive",
);
new_ucmd!()
.args(&["--all", "--save"])
.fails()
.stderr_contains(
"the options for verbose and stty-readable output styles are mutually exclusive",
);
}
#[test]
fn no_mapping() {
new_ucmd!()
.args(&["intr"])
.fails()
.stderr_contains("missing argument to 'intr'");
}
#[test]
fn invalid_mapping() {
new_ucmd!()
.args(&["intr", "cc"])
.fails()
.stderr_contains("invalid integer argument: 'cc'");
new_ucmd!()
.args(&["intr", "256"])
.fails()
.stderr_contains("invalid integer argument: '256': Value too large for defined data type");
new_ucmd!()
.args(&["intr", "0x100"])
.fails()
.stderr_contains(
"invalid integer argument: '0x100': Value too large for defined data type",
);
new_ucmd!()
.args(&["intr", "0400"])
.fails()
.stderr_contains("invalid integer argument: '0400': Value too large for defined data type");
}
#[test]
fn invalid_setting() {
new_ucmd!()
.args(&["-econl"])
.fails()
.stderr_contains("invalid argument '-econl'");
new_ucmd!()
.args(&["igpar"])
.fails()
.stderr_contains("invalid argument 'igpar'");
}
#[test]
fn invalid_baud_setting() {
#[cfg(not(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)))]
new_ucmd!()
.args(&["100"])
.fails()
.stderr_contains("invalid argument '100'");
new_ucmd!()
.args(&["-1"])
.fails()
.stderr_contains("invalid argument '-1'");
new_ucmd!()
.args(&["ispeed"])
.fails()
.stderr_contains("missing argument to 'ispeed'");
new_ucmd!()
.args(&["ospeed"])
.fails()
.stderr_contains("missing argument to 'ospeed'");
#[cfg(not(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)))]
new_ucmd!()
.args(&["ispeed", "995"])
.fails()
.stderr_contains("invalid ispeed '995'");
#[cfg(not(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)))]
new_ucmd!()
.args(&["ospeed", "995"])
.fails()
.stderr_contains("invalid ospeed '995'");
for speed in &[
"9599..", "9600..", "9600.5.", "9600.50.", "9600.0.", "++9600", "0x2580", "96E2", "9600,0",
"9600.0 ",
] {
new_ucmd!().args(&["ispeed", speed]).fails();
}
}
#[test]
#[cfg(unix)]
fn valid_baud_formats() {
let (path, _controller, _replica) = pty_path();
for speed in &[" +9600", "9600.49", "9600.50", "9599.51", " 9600."] {
new_ucmd!()
.args(&["--file", &path, "ispeed", speed])
.succeeds();
}
}
#[test]
#[ignore = "Fails because cargo test does not run in a tty"]
fn set_mapping() {
new_ucmd!().args(&["intr", "'"]).succeeds();
new_ucmd!()
.args(&["--all"])
.succeeds()
.stdout_contains("intr = '");
new_ucmd!().args(&["intr", "undef"]).succeeds();
new_ucmd!()
.args(&["--all"])
.succeeds()
.stdout_contains("intr = <undef>");
new_ucmd!().args(&["intr", "^-"]).succeeds();
new_ucmd!()
.args(&["--all"])
.succeeds()
.stdout_contains("intr = <undef>");
new_ucmd!().args(&["intr", ""]).succeeds();
new_ucmd!()
.args(&["--all"])
.succeeds()
.stdout_contains("intr = <undef>");
new_ucmd!().args(&["intr", "^C"]).succeeds();
new_ucmd!()
.args(&["--all"])
.succeeds()
.stdout_contains("intr = ^C");
}
#[test]
fn row_column_sizes() {
new_ucmd!()
.args(&["rows", "-1"])
.fails()
.stderr_contains("invalid integer argument: '-1'");
new_ucmd!()
.args(&["columns", "-1"])
.fails()
.stderr_contains("invalid integer argument: '-1'");
new_ucmd!()
.args(&["cols", "4294967296"])
.fails()
.stderr_contains("invalid integer argument: '4294967296'");
new_ucmd!()
.args(&["rows", ""])
.fails()
.stderr_contains("invalid integer argument: ''");
new_ucmd!()
.args(&["columns"])
.fails()
.stderr_contains("missing argument to 'columns'");
new_ucmd!()
.args(&["rows"])
.fails()
.stderr_contains("missing argument to 'rows'");
}
#[test]
#[cfg(unix)]
fn test_row_column_hex_octal() {
let (path, _controller, _replica) = pty_path();
let (_at, ts) = at_and_ts!();
let test_cases = [
("rows", "0x1E"), ("rows", "0x1e"), ("rows", "0X1e"), ("rows", "036"), ("cols", "0X1E"), ("columns", "30"), ("rows", "0"), ];
for (setting, value) in test_cases {
let result = ts.ucmd().args(&["--file", &path, setting, value]).run();
let exp_result =
unwrap_or_return!(expected_result(&ts, &["--file", &path, setting, value]));
let normalized_stderr = normalize_stderr(result.stderr_str());
result
.stdout_is(exp_result.stdout_str())
.code_is(exp_result.code());
assert_eq!(normalized_stderr, exp_result.stderr_str());
}
}
#[test]
#[cfg(any(target_os = "linux", target_os = "android"))]
fn line() {
new_ucmd!()
.args(&["line"])
.fails()
.stderr_contains("missing argument to 'line'");
new_ucmd!()
.args(&["line", "-1"])
.fails()
.stderr_contains("invalid integer argument: '-1'");
new_ucmd!()
.args(&["line", "256"])
.fails()
.stderr_contains("invalid integer argument: '256'");
}
#[test]
fn min_and_time() {
new_ucmd!()
.args(&["min"])
.fails()
.stderr_contains("missing argument to 'min'");
new_ucmd!()
.args(&["time"])
.fails()
.stderr_contains("missing argument to 'time'");
new_ucmd!()
.args(&["min", "-1"])
.fails()
.stderr_contains("invalid integer argument: '-1'");
new_ucmd!()
.args(&["time", "-1"])
.fails()
.stderr_contains("invalid integer argument: '-1'");
new_ucmd!()
.args(&["min", "256"])
.fails()
.stderr_contains("invalid integer argument: '256': Value too large for defined data type");
new_ucmd!()
.args(&["time", "256"])
.fails()
.stderr_contains("invalid integer argument: '256': Value too large for defined data type");
}
#[test]
fn non_negatable_combo() {
new_ucmd!()
.args(&["-dec"])
.fails()
.stderr_contains("invalid argument '-dec'");
new_ucmd!()
.args(&["-crt"])
.fails()
.stderr_contains("invalid argument '-crt'");
new_ucmd!()
.args(&["-ek"])
.fails()
.stderr_contains("invalid argument '-ek'");
}
#[test]
fn help_output() {
new_ucmd!()
.arg("--help")
.succeeds()
.stdout_contains("Usage:")
.stdout_contains("stty");
}
#[test]
fn version_output() {
new_ucmd!()
.arg("--version")
.succeeds()
.stdout_contains("stty");
}
#[test]
fn invalid_control_char_names() {
new_ucmd!()
.args(&["notachar", "^C"])
.fails()
.stderr_contains("invalid argument 'notachar'");
}
#[test]
fn control_char_overflow_hex() {
new_ucmd!()
.args(&["erase", "0xFFF"])
.fails()
.stderr_contains("Value too large for defined data type");
}
#[test]
fn control_char_overflow_octal() {
new_ucmd!()
.args(&["kill", "0777"])
.fails()
.stderr_contains("Value too large for defined data type");
}
#[test]
fn multiple_invalid_args() {
new_ucmd!()
.args(&["invalid1", "invalid2"])
.fails()
.stderr_contains("invalid argument");
}
#[test]
#[ignore = "Fails because cargo test does not run in a tty"]
fn negatable_combo_settings() {
new_ucmd!().args(&["-cbreak"]).fails();
new_ucmd!().args(&["-evenp"]).fails();
new_ucmd!().args(&["-oddp"]).fails();
}
#[test]
fn grouped_flag_removal() {
new_ucmd!()
.args(&["-cs7"])
.fails()
.stderr_contains("invalid argument '-cs7'");
new_ucmd!()
.args(&["-cs8"])
.fails()
.stderr_contains("invalid argument '-cs8'");
}
#[test]
#[ignore = "Fails because cargo test does not run in a tty"]
fn baud_rate_validation() {
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
{
new_ucmd!().args(&["9600"]).fails(); }
new_ucmd!().args(&["ispeed", "9600"]).fails(); new_ucmd!().args(&["ospeed", "115200"]).fails(); }
#[test]
#[ignore = "Fails because cargo test does not run in a tty"]
fn combination_setting_validation() {
new_ucmd!().args(&["sane"]).fails(); new_ucmd!().args(&["raw"]).fails();
new_ucmd!().args(&["cooked"]).fails();
new_ucmd!().args(&["cbreak"]).fails();
}
#[test]
#[ignore = "Fails because cargo test does not run in a tty"]
fn control_char_hat_notation() {
new_ucmd!().args(&["intr", "^?"]).fails(); new_ucmd!().args(&["quit", "^\\"]).fails();
new_ucmd!().args(&["erase", "^H"]).fails();
}
#[test]
#[ignore = "Fails because cargo test does not run in a tty"]
fn special_settings() {
new_ucmd!().args(&["speed"]).fails();
new_ucmd!().args(&["size"]).fails(); }
#[test]
fn file_argument() {
new_ucmd!()
.args(&["--file", "/nonexistent/device"])
.fails()
.stderr_contains("No such file or directory");
}
#[test]
fn conflicting_print_modes() {
new_ucmd!()
.args(&["--save", "speed"])
.fails()
.stderr_contains("when specifying an output style, modes may not be set");
new_ucmd!()
.args(&["--all", "speed"])
.fails()
.stderr_contains("when specifying an output style, modes may not be set");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_save_format() {
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["--save"])
.succeeds();
result.stdout_contains(":");
let stdout = result.stdout_str();
assert!(
stdout.split(':').count() > 1,
"Save format should have multiple colon-separated fields"
);
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_set_control_flags() {
new_ucmd!()
.terminal_simulation(true)
.args(&["parenb"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("parenb");
new_ucmd!()
.terminal_simulation(true)
.args(&["-parenb"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("-parenb");
new_ucmd!()
.terminal_simulation(true)
.args(&["parodd"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("parodd");
new_ucmd!()
.terminal_simulation(true)
.args(&["cstopb"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("cstopb");
}
#[test]
#[cfg(unix)]
fn test_save_and_restore() {
let (path, _controller, _replica) = pty_path();
let saved = new_ucmd!()
.args(&["--save", "--file", &path])
.succeeds()
.stdout_move_str();
let saved = saved.trim();
assert!(saved.contains(':'));
new_ucmd!().args(&["--file", &path, saved]).succeeds();
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_set_input_flags() {
new_ucmd!()
.terminal_simulation(true)
.args(&["ignbrk"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("ignbrk");
new_ucmd!()
.terminal_simulation(true)
.args(&["brkint"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("brkint");
new_ucmd!()
.terminal_simulation(true)
.args(&["ignpar"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("ignpar");
}
#[test]
#[cfg(unix)]
fn test_save_with_g_flag() {
let (path, _controller, _replica) = pty_path();
let saved = new_ucmd!()
.args(&["-g", "--file", &path])
.succeeds()
.stdout_move_str();
let saved = saved.trim();
assert!(saved.contains(':'));
new_ucmd!().args(&["--file", &path, saved]).succeeds();
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_set_output_flags() {
new_ucmd!()
.terminal_simulation(true)
.args(&["opost"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("opost");
new_ucmd!()
.terminal_simulation(true)
.args(&["-opost"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("-opost");
new_ucmd!()
.terminal_simulation(true)
.args(&["onlcr"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("onlcr");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_set_local_flags() {
new_ucmd!()
.terminal_simulation(true)
.args(&["isig"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("isig");
new_ucmd!()
.terminal_simulation(true)
.args(&["icanon"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("icanon");
new_ucmd!()
.terminal_simulation(true)
.args(&["echo"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("echo");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_combo_cbreak() {
new_ucmd!()
.terminal_simulation(true)
.args(&["cbreak"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("-icanon");
new_ucmd!()
.terminal_simulation(true)
.args(&["-cbreak"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("icanon");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_combo_nl() {
new_ucmd!()
.terminal_simulation(true)
.args(&["nl"])
.succeeds();
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds();
result.stdout_contains("-icrnl");
result.stdout_contains("-onlcr");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_combo_ek() {
new_ucmd!()
.terminal_simulation(true)
.args(&["ek"])
.succeeds();
let result = new_ucmd!().terminal_simulation(true).succeeds();
result.stdout_contains("erase");
result.stdout_contains("kill");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_combo_litout() {
new_ucmd!()
.terminal_simulation(true)
.args(&["litout"])
.succeeds();
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds();
result.stdout_contains("-parenb");
result.stdout_contains("-istrip");
result.stdout_contains("-opost");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_combo_pass8() {
new_ucmd!()
.terminal_simulation(true)
.args(&["pass8"])
.succeeds();
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds();
result.stdout_contains("-parenb");
result.stdout_contains("-istrip");
result.stdout_contains("cs8");
}
#[test]
#[cfg(unix)]
fn test_save_restore_after_change() {
let (path, _controller, _replica) = pty_path();
let saved = new_ucmd!()
.args(&["--save", "--file", &path])
.succeeds()
.stdout_move_str();
let saved = saved.trim();
new_ucmd!()
.args(&["--file", &path, "intr", "^A"])
.succeeds();
new_ucmd!().args(&["--file", &path, saved]).succeeds();
new_ucmd!()
.args(&["--file", &path])
.succeeds()
.stdout_str_check(|s| !s.contains("intr = ^A"));
}
#[test]
#[cfg(unix)]
fn test_saved_state_valid_formats() {
let (path, _controller, _replica) = pty_path();
let (_at, ts) = at_and_ts!();
let saved = unwrap_or_return!(expected_result(&ts, &["-g", "--file", &path])).stdout_move_str();
let saved = saved.trim();
let result = ts.ucmd().args(&["--file", &path, saved]).run();
result.success().no_stderr();
let exp_result = unwrap_or_return!(expected_result(&ts, &["--file", &path, saved]));
let normalized_stderr = normalize_stderr(result.stderr_str());
result
.stdout_is(exp_result.stdout_str())
.code_is(exp_result.code());
assert_eq!(normalized_stderr, exp_result.stderr_str());
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_combo_decctlq() {
new_ucmd!()
.terminal_simulation(true)
.args(&["decctlq"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("ixany");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_combo_dec() {
new_ucmd!()
.terminal_simulation(true)
.args(&["dec"])
.succeeds();
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds();
result.stdout_contains("echoe");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_combo_crt() {
new_ucmd!()
.terminal_simulation(true)
.args(&["crt"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("echoe");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_multiple_settings() {
new_ucmd!()
.terminal_simulation(true)
.args(&["parenb", "parodd", "cs7"])
.succeeds();
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds();
result.stdout_contains("parenb");
result.stdout_contains("parodd");
result.stdout_contains("cs7");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_set_all_control_chars() {
new_ucmd!()
.terminal_simulation(true)
.args(&["intr", "^C"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.succeeds()
.stdout_contains("intr = ^C");
new_ucmd!()
.terminal_simulation(true)
.args(&["quit", "^\\"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.succeeds()
.stdout_contains("quit = ^\\");
new_ucmd!()
.terminal_simulation(true)
.args(&["erase", "^?"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.succeeds()
.stdout_contains("erase = ^?");
new_ucmd!()
.terminal_simulation(true)
.args(&["kill", "^U"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.succeeds()
.stdout_contains("kill = ^U");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_print_size() {
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["size"])
.succeeds();
result.stdout_contains("rows");
result.stdout_contains("columns");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_print_speed() {
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["speed"])
.succeeds();
let stdout = result.stdout_str();
assert!(
stdout.trim().parse::<u32>().is_ok(),
"Speed should be a numeric value"
);
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_set_rows_cols() {
new_ucmd!()
.terminal_simulation(true)
.args(&["rows", "24"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["size"])
.succeeds()
.stdout_contains("rows 24");
new_ucmd!()
.terminal_simulation(true)
.args(&["cols", "80"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["size"])
.succeeds()
.stdout_contains("columns 80");
new_ucmd!()
.terminal_simulation(true)
.args(&["rows", "50", "cols", "100"])
.succeeds();
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["size"])
.succeeds();
result.stdout_contains("rows 50");
result.stdout_contains("columns 100");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_character_size_settings() {
new_ucmd!()
.terminal_simulation(true)
.args(&["cs5"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("cs5");
new_ucmd!()
.terminal_simulation(true)
.args(&["cs7"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("cs7");
new_ucmd!()
.terminal_simulation(true)
.args(&["cs8"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("cs8");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_baud_rate_settings() {
new_ucmd!()
.terminal_simulation(true)
.args(&["ispeed", "9600"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["speed"])
.succeeds()
.stdout_contains("9600");
new_ucmd!()
.terminal_simulation(true)
.args(&["ispeed", "38400", "ospeed", "38400"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["speed"])
.succeeds()
.stdout_contains("38400");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_min_time_settings() {
new_ucmd!()
.terminal_simulation(true)
.args(&["min", "1"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("min = 1");
new_ucmd!()
.terminal_simulation(true)
.args(&["time", "10"])
.succeeds();
new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds()
.stdout_contains("time = 10");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_complex_scenario() {
new_ucmd!()
.terminal_simulation(true)
.args(&["sane", "rows", "24", "cols", "80", "intr", "^C"])
.succeeds();
let size_result = new_ucmd!()
.terminal_simulation(true)
.args(&["size"])
.succeeds();
size_result.stdout_contains("rows 24");
size_result.stdout_contains("columns 80");
let result = new_ucmd!().terminal_simulation(true).succeeds();
result.stdout_contains("intr = ^C");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_raw_mode() {
new_ucmd!()
.terminal_simulation(true)
.args(&["raw"])
.succeeds();
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds();
result.stdout_contains("-icanon");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_cooked_mode() {
new_ucmd!()
.terminal_simulation(true)
.args(&["cooked"])
.succeeds();
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds();
result.stdout_contains("icanon");
}
#[test]
#[cfg(unix)]
fn test_saved_state_invalid_formats() {
let (path, _controller, _replica) = pty_path();
let (_at, ts) = at_and_ts!();
let num_cc = libc::NCCS;
let cc_zeros = vec!["0"; num_cc].join(":");
let cc_with_invalid = if num_cc > 0 {
let mut parts = vec!["1c"; num_cc];
parts[0] = "100"; parts.join(":")
} else {
String::new()
};
let cc_with_space = if num_cc > 0 {
let mut parts = vec!["1c"; num_cc];
parts[0] = "1c "; parts.join(":")
} else {
String::new()
};
let cc_with_nonhex = if num_cc > 0 {
let mut parts = vec!["1c"; num_cc];
parts[0] = "xyz"; parts.join(":")
} else {
String::new()
};
let cc_with_empty = if num_cc > 0 {
let mut parts = vec!["1c"; num_cc];
parts[0] = ""; parts.join(":")
} else {
String::new()
};
let invalid_states = vec![
"500:5:4bf".to_string(), "500:5:4bf:8a3b".to_string(), format!("500:5:{}:8a3b:{}", cc_zeros, "extra"), format!("500::4bf:8a3b:{}", cc_zeros), format!("500:5:4bf:8a3b:{}", cc_with_empty), format!("500:5:4bf:8a3b:{}", cc_with_nonhex), format!("500:5:4bf:8a3b:{}", cc_with_space), format!("500:5:4bf:8a3b:{}", cc_with_invalid), ];
for state in &invalid_states {
let result = ts.ucmd().args(&["--file", &path, state]).run();
result.failure().stderr_contains("invalid argument");
let exp_result = unwrap_or_return!(expected_result(&ts, &["--file", &path, state]));
let normalized_stderr = normalize_stderr(result.stderr_str());
let exp_normalized_stderr = normalize_stderr(exp_result.stderr_str());
result
.stdout_is(exp_result.stdout_str())
.code_is(exp_result.code());
assert_eq!(normalized_stderr, exp_normalized_stderr);
}
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because cargo test does not run in a tty"]
fn test_parity_settings() {
new_ucmd!()
.terminal_simulation(true)
.args(&["evenp"])
.succeeds();
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds();
result.stdout_contains("parenb");
result.stdout_contains("cs7");
new_ucmd!()
.terminal_simulation(true)
.args(&["oddp"])
.succeeds();
let result = new_ucmd!()
.terminal_simulation(true)
.args(&["--all"])
.succeeds();
result.stdout_contains("parenb");
result.stdout_contains("parodd");
result.stdout_contains("cs7");
}
#[test]
fn missing_arg_ispeed() {
new_ucmd!()
.args(&["ispeed"])
.fails()
.stderr_contains("missing argument")
.stderr_contains("ispeed");
}
#[test]
fn missing_arg_ospeed() {
new_ucmd!()
.args(&["ospeed"])
.fails()
.stderr_contains("missing argument")
.stderr_contains("ospeed");
}
#[test]
fn missing_arg_line() {
new_ucmd!()
.args(&["line"])
.fails()
.stderr_contains("missing argument")
.stderr_contains("line");
}
#[test]
fn missing_arg_min() {
new_ucmd!()
.args(&["min"])
.fails()
.stderr_contains("missing argument")
.stderr_contains("min");
}
#[test]
fn missing_arg_time() {
new_ucmd!()
.args(&["time"])
.fails()
.stderr_contains("missing argument")
.stderr_contains("time");
}
#[test]
fn missing_arg_rows() {
new_ucmd!()
.args(&["rows"])
.fails()
.stderr_contains("missing argument")
.stderr_contains("rows");
}
#[test]
fn missing_arg_cols() {
new_ucmd!()
.args(&["cols"])
.fails()
.stderr_contains("missing argument")
.stderr_contains("cols");
}
#[test]
fn missing_arg_columns() {
new_ucmd!()
.args(&["columns"])
.fails()
.stderr_contains("missing argument")
.stderr_contains("columns");
}
#[test]
fn missing_arg_control_char() {
new_ucmd!()
.args(&["intr"])
.fails()
.stderr_contains("missing argument")
.stderr_contains("intr");
new_ucmd!()
.args(&["erase"])
.fails()
.stderr_contains("missing argument")
.stderr_contains("erase");
}
#[test]
fn invalid_integer_rows() {
new_ucmd!()
.args(&["rows", "abc"])
.fails()
.stderr_contains("invalid integer argument");
new_ucmd!()
.args(&["rows", "-1"])
.fails()
.stderr_contains("invalid integer argument");
}
#[test]
fn invalid_integer_cols() {
new_ucmd!()
.args(&["cols", "xyz"])
.fails()
.stderr_contains("invalid integer argument");
new_ucmd!()
.args(&["columns", "12.5"])
.fails()
.stderr_contains("invalid integer argument");
}
#[test]
fn invalid_min_value() {
new_ucmd!()
.args(&["min", "256"])
.fails()
.stderr_contains("Value too large");
new_ucmd!()
.args(&["min", "-1"])
.fails()
.stderr_contains("invalid integer argument");
}
#[test]
fn invalid_time_value() {
new_ucmd!()
.args(&["time", "1000"])
.fails()
.stderr_contains("Value too large");
new_ucmd!()
.args(&["time", "abc"])
.fails()
.stderr_contains("invalid integer argument");
}
#[test]
fn invalid_baud_rate() {
new_ucmd!()
.args(&["ispeed", "notabaud"])
.fails()
.stderr_contains("invalid ispeed");
#[cfg(not(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)))]
{
new_ucmd!()
.args(&["ospeed", "999999999"])
.fails()
.stderr_contains("invalid ospeed");
}
}
#[test]
fn control_char_multiple_chars_error() {
new_ucmd!()
.args(&["intr", "ABC"])
.fails()
.stderr_contains("invalid integer argument");
}
#[test]
fn control_char_decimal_overflow() {
new_ucmd!()
.args(&["quit", "256"])
.fails()
.stderr_contains("Value too large");
new_ucmd!()
.args(&["susp", "1000"])
.fails()
.stderr_contains("Value too large");
}
#[test]
#[cfg(unix)]
#[ignore = "Fails because the implementation of print state is not correctly printing flags on certain platforms"]
fn test_saved_state_with_control_chars() {
let (path, _controller, _replica) = pty_path();
let (_at, ts) = at_and_ts!();
let num_cc = libc::NCCS;
let cc_values: Vec<String> = (1..=num_cc).map(|_| format!("{:x}", 0)).collect();
let saved_state = format!("500:5:4bf:8a3b:{}", cc_values.join(":"));
ts.ucmd().args(&["--file", &path, &saved_state]).succeeds();
let result = ts.ucmd().args(&["-g", "--file", &path]).run();
result.success().stdout_contains(":");
let exp_result = unwrap_or_return!(expected_result(&ts, &["-g", "--file", &path]));
result
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str())
.code_is(exp_result.code());
}
#[test]
#[cfg(unix)]
fn test_stdin_not_tty_fails() {
#[cfg(target_os = "android")]
let expected_error = "standard input: Not a typewriter";
#[cfg(all(not(target_os = "android"), target_env = "musl"))]
let expected_error = "standard input: Not a tty";
#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
let expected_error = "standard input: Inappropriate ioctl for device";
new_ucmd!()
.pipe_in("")
.fails()
.stderr_contains(expected_error);
}
#[test]
#[cfg(unix)]
fn test_stty_uses_stdin() {
use std::fs::File;
use std::process::Stdio;
let (path, _controller, _replica) = pty_path();
let stdin = File::open(&path).unwrap();
new_ucmd!()
.set_stdin(stdin)
.set_stdout(Stdio::piped())
.succeeds()
.stdout_contains("speed");
let stdin = File::open(&path).unwrap();
let saved = new_ucmd!()
.arg("-g")
.set_stdin(stdin)
.set_stdout(Stdio::piped())
.succeeds()
.stdout_str()
.trim()
.to_string();
assert!(saved.contains(':'), "Expected colon-separated saved state");
let stdin = File::open(&path).unwrap();
new_ucmd!().arg(&saved).set_stdin(stdin).succeeds();
let stdin = File::open(&path).unwrap();
new_ucmd!()
.args(&["rows", "30", "cols", "100"])
.set_stdin(stdin)
.succeeds();
let stdin = File::open(&path).unwrap();
new_ucmd!()
.arg("--all")
.set_stdin(stdin)
.succeeds()
.stdout_contains("rows 30")
.stdout_contains("columns 100");
}
#[test]
#[cfg(unix)]
fn test_ispeed_ospeed_valid_speeds() {
let (path, _controller, _replica) = pty_path();
let (_at, ts) = at_and_ts!();
let test_cases = [
("ispeed", "50"),
("ispeed", "9600"),
("ispeed", "19200"),
("ospeed", "1200"),
("ospeed", "9600"),
("ospeed", "38400"),
];
for (arg, speed) in test_cases {
let result = ts.ucmd().args(&["--file", &path, arg, speed]).run();
let exp_result = unwrap_or_return!(expected_result(&ts, &["--file", &path, arg, speed]));
let normalized_stderr = normalize_stderr(result.stderr_str());
result
.stdout_is(exp_result.stdout_str())
.code_is(exp_result.code());
assert_eq!(normalized_stderr, exp_result.stderr_str());
}
}
#[test]
#[cfg(all(
unix,
not(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))
))]
#[ignore = "Issue: #9547"]
fn test_ispeed_ospeed_invalid_speeds() {
let (path, _controller, _replica) = pty_path();
let (_at, ts) = at_and_ts!();
let test_cases = [
("ispeed", "12345"),
("ospeed", "99999"),
("ispeed", "abc"),
("ospeed", "xyz"),
];
for (arg, speed) in test_cases {
let result = ts.ucmd().args(&["--file", &path, arg, speed]).run();
let exp_result = unwrap_or_return!(expected_result(&ts, &["--file", &path, arg, speed]));
let normalized_stderr = normalize_stderr(result.stderr_str());
result
.stdout_is(exp_result.stdout_str())
.code_is(exp_result.code());
assert_eq!(normalized_stderr, exp_result.stderr_str());
}
}
#[test]
#[cfg(unix)]
fn test_columns_env_wrapping() {
use std::process::Stdio;
let (path, _controller, _replica) = pty_path();
for (columns, max_len) in [(20, 20), (40, 40), (50, 50)] {
let result = new_ucmd!()
.args(&["--all", "--file", &path])
.env("COLUMNS", columns.to_string())
.set_stdout(Stdio::piped())
.succeeds();
for line in result.stdout_str().lines() {
assert!(
line.len() <= max_len,
"Line exceeds COLUMNS={columns}: '{line}'"
);
}
}
let result = new_ucmd!()
.args(&["--all", "--file", &path])
.env("COLUMNS", "200")
.set_stdout(Stdio::piped())
.succeeds();
let has_long_line = result.stdout_str().lines().any(|line| line.len() > 80);
assert!(
has_long_line,
"Expected at least one line longer than 80 chars with COLUMNS=200"
);
for invalid in ["invalid", "0", "-10"] {
new_ucmd!()
.args(&["--all", "--file", &path])
.env("COLUMNS", invalid)
.set_stdout(Stdio::piped())
.succeeds();
}
let result = new_ucmd!()
.args(&["--file", &path])
.env("COLUMNS", "30")
.set_stdout(Stdio::piped())
.succeeds();
for line in result.stdout_str().lines() {
assert!(
line.len() <= 30,
"Line exceeds COLUMNS=30 without --all: '{line}'"
);
}
}