Skip to content

Commit

Permalink
Fix multiple issues in previous commit
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewliebenow committed Oct 19, 2024
1 parent 8787882 commit 6ad5a48
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 35 deletions.
28 changes: 14 additions & 14 deletions src/uu/stty/src/stty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,16 @@ fn apply_flag<T: TermiosFlag>(
}

fn apply_baud_rate_flag(termios: &mut Termios, input: &str) -> ControlFlow<UResult<bool>> {
fn map_cfsetospeed_result(result: nix::Result<()>) -> UResult<bool> {
match result {
Ok(()) => Ok(true),
Err(er) => Err(USimpleError::new(
1,
format!("failed to set baud rate: errno {er}"),
)),
}
}

// BSDs use a u32 for the baud rate, so any decimal number applies.
#[cfg(any(
target_os = "freebsd",
Expand All @@ -530,14 +540,9 @@ fn apply_baud_rate_flag(termios: &mut Termios, input: &str) -> ControlFlow<UResu
target_os = "openbsd"
))]
if let Ok(n) = input.parse::<u32>() {
if let Err(er) = cfsetospeed(termios, n) {
return Err(USimpleError::new(
1,
format!("failed to set baud rate: errno {er}"),
));
}
let result = map_cfsetospeed_result(cfsetospeed(termios, n));

return ControlFlow::Break(Ok(true));
return ControlFlow::Break(result);
}

// Other platforms use an enum.
Expand All @@ -551,14 +556,9 @@ fn apply_baud_rate_flag(termios: &mut Termios, input: &str) -> ControlFlow<UResu
)))]
for (text, baud_rate) in BAUD_RATES {
if *text == input {
if let Err(er) = cfsetospeed(termios, *baud_rate) {
return ControlFlow::Break(Err(USimpleError::new(
1,
format!("failed to set baud rate: errno {er}"),
)));
}
let result = map_cfsetospeed_result(cfsetospeed(termios, *baud_rate));

return ControlFlow::Break(Ok(true));
return ControlFlow::Break(result);
}
}

Expand Down
151 changes: 130 additions & 21 deletions tests/by-util/test_stty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,76 @@

// spell-checker:ignore parenb parmrk ixany iuclc onlcr ofdel icanon noflsh ixon

use crate::common::util::TestScenario;
use crate::common::util::{TestScenario, UCommand};
use nix::{
fcntl::{self, OFlag},
sys::stat::Mode,
};
use once_cell::sync::OnceCell;
use regex::Regex;
use std::{io::Read, process::Stdio};

const DEV_TTY: &str = "/dev/tty";

fn get_print_first_line_regex() -> &'static Regex {
static ONCE_CELL: OnceCell<Regex> = OnceCell::<Regex>::new();

ONCE_CELL.get_or_init(|| {
// e.g.:
// speed 38400 baud; line = 0;
Regex::new("speed [0-9]+ baud; line = [0-9]+;").unwrap()
})
}

fn get_print_dash_a_first_line_regex() -> &'static Regex {
static ONCE_CELL: OnceCell<Regex> = OnceCell::<Regex>::new();

ONCE_CELL.get_or_init(|| {
// e.g.:
// speed 38400 baud; rows 54; columns 216; line = 0;
Regex::new("speed [0-9]+ baud; rows [0-9]+; columns [0-9]+; line = [0-9]+;").unwrap()
})
}

fn get_dev_tty_stdio() -> Stdio {
use std::os::fd::FromRawFd;

let dev_tty_raw_fd = fcntl::open(DEV_TTY, OFlag::O_NONBLOCK, Mode::empty()).unwrap();

// TODO
// Verify safety
unsafe { Stdio::from_raw_fd(dev_tty_raw_fd) }
}

impl UCommand {
fn set_stdin_to_dev_tty_stdio(&mut self) -> &mut Self {
self.set_stdin(get_dev_tty_stdio())
}
}

#[test]
#[cfg(not(target_os = "android"))]
fn test_invalid_arg() {
new_ucmd!()
.args(&["--file=/dev/tty", "--definitely-invalid"])
.arg("--definitely-invalid")
.set_stdin_to_dev_tty_stdio()
.fails()
.code_is(1);
}

#[test]
#[cfg(not(target_os = "android"))]
fn runs() {
new_ucmd!().arg("--file=/dev/tty").succeeds();
new_ucmd!().set_stdin_to_dev_tty_stdio().succeeds();
}

#[test]
#[cfg(not(target_os = "android"))]
fn print_all() {
let cmd_result = new_ucmd!().args(&["--file=/dev/tty", "-a"]).succeeds();
let cmd_result = new_ucmd!()
.arg("-a")
.set_stdin_to_dev_tty_stdio()
.succeeds();

// "iuclc" removed due to this comment in stty.rs:
//
Expand Down Expand Up @@ -73,9 +124,11 @@ fn save_and_all() {

// Make sure the "allow_hyphen_values" clap function has been called with true
#[test]
#[cfg(not(target_os = "android"))]
fn negation() {
new_ucmd!()
.args(&["--file=/dev/tty", "-ixon"])
.arg("-ixon")
.set_stdin_to_dev_tty_stdio()
.succeeds()
.stdout_is_bytes([])
.stderr_is_bytes([]);
Expand All @@ -84,6 +137,7 @@ fn negation() {
fn succeeds_test_with_regex(args: &[&str], stdout_regex: &Regex) {
new_ucmd!()
.args(args)
.set_stdin_to_dev_tty_stdio()
.succeeds()
.stdout_str_check(|st| {
let Some(str) = st.lines().next() else {
Expand All @@ -97,32 +151,87 @@ fn succeeds_test_with_regex(args: &[&str], stdout_regex: &Regex) {

// The end of options delimiter ("--") and everything after must be ignored
#[test]
#[cfg(not(target_os = "android"))]
fn ignore_end_of_options_and_after() {
{
// e.g.:
// speed 38400 baud; rows 54; columns 216; line = 0;
let regex =
Regex::new("speed [0-9]+ baud; rows [0-9]+; columns [0-9]+; line = [0-9]+;").unwrap();

// "stty -a -- -ixon" should behave like "stty -a"
// Should not abort with an error complaining about passing both "-a" and "-ixon" (since "-ixon" is after "--")
succeeds_test_with_regex(&["--file=/dev/tty", "-a", "--", "-ixon"], &regex);
succeeds_test_with_regex(&["-a", "--", "-ixon"], get_print_dash_a_first_line_regex());
}

{
// e.g.:
// speed 38400 baud; line = 0;
let regex = Regex::new("speed [0-9]+ baud; line = [0-9]+;").unwrap();

// "stty -- non-existent-option-that-must-be-ignore" should behave like "stty"
// Should not abort with an error complaining about an invalid argument, since the invalid argument is after "--"
succeeds_test_with_regex(
&[
"--file=/dev/tty",
"--",
"non-existent-option-that-must-be-ignored",
],
&regex,
&["--", "non-existent-option-that-must-be-ignored"],
get_print_first_line_regex(),
);
}
}

#[test]
fn f_file_option() {
for st in ["-F", "--file"] {
for bo in [false, true] {
let (args, regex): (&[&str], &'static Regex) = if bo {
(&[st, DEV_TTY, "-a"], get_print_dash_a_first_line_regex())
} else {
(&[st, DEV_TTY], get_print_first_line_regex())
};

new_ucmd!()
.args(args)
.set_stdin_to_dev_tty_stdio()
.succeeds()
.stdout_str_check(|st| {
let Some(str) = st.lines().next() else {
return false;
};

regex.is_match(str)
})
.no_stderr();
}
}
}

// Make sure stty is using stdin to look up terminal attributes, not stdout
#[test]
fn correct_file_descriptor_output_piped() {
const PIPE_STDOUT_TO: &str = "pipe_stdout_to";
const PIPE_STDERR_TO: &str = "pipe_stderr_to";

let test_scenario = TestScenario::new(util_name!());

let at_path = &test_scenario.fixtures;

let stdout_file = at_path.make_file(PIPE_STDOUT_TO);
let stderr_file = at_path.make_file(PIPE_STDERR_TO);

test_scenario
.ucmd()
.set_stdin_to_dev_tty_stdio()
.set_stdout(Stdio::from(stdout_file))
.set_stderr(Stdio::from(stderr_file))
.succeeds();

let mut read_to_string_buffer = String::new();

at_path
.open(PIPE_STDOUT_TO)
.read_to_string(&mut read_to_string_buffer)
.unwrap();

let stdout_first_line = read_to_string_buffer.lines().next().unwrap();

assert!(get_print_first_line_regex().is_match(stdout_first_line));

read_to_string_buffer.clear();

at_path
.open(PIPE_STDERR_TO)
.read_to_string(&mut read_to_string_buffer)
.unwrap();

assert!(read_to_string_buffer.is_empty());
}

0 comments on commit 6ad5a48

Please sign in to comment.