From 07e8f4c7a5d7bf94aaf4f49cf86813a012fb8dcd Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Thu, 15 Feb 2024 21:19:41 +0100 Subject: [PATCH 1/3] shuf: include all echo args, not just the last --- src/uu/shuf/src/shuf.rs | 1 + tests/by-util/test_shuf.rs | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/uu/shuf/src/shuf.rs b/src/uu/shuf/src/shuf.rs index d7ce8049d..4052af49e 100644 --- a/src/uu/shuf/src/shuf.rs +++ b/src/uu/shuf/src/shuf.rs @@ -129,6 +129,7 @@ pub fn uu_app() -> Command { .help("treat each ARG as an input line") .use_value_delimiter(false) .num_args(0..) + .action(clap::ArgAction::Append) .conflicts_with(options::INPUT_RANGE), ) .arg( diff --git a/tests/by-util/test_shuf.rs b/tests/by-util/test_shuf.rs index c506bc51a..eca914f9f 100644 --- a/tests/by-util/test_shuf.rs +++ b/tests/by-util/test_shuf.rs @@ -79,6 +79,27 @@ fn test_echo() { assert_eq!(result_seq, input_seq, "Output is not a permutation"); } +#[test] +fn test_echo_multi() { + let result = new_ucmd!() + .arg("-e") + .arg("a") + .arg("b") + .arg("-e") + .arg("c") + .succeeds(); + result.no_stderr(); + + let mut result_seq: Vec = result + .stdout_str() + .split('\n') + .filter(|x| !x.is_empty()) + .map(|x| x.into()) + .collect(); + result_seq.sort_unstable(); + assert_eq!(result_seq, ["a", "b", "c"], "Output is not a permutation"); +} + #[test] fn test_head_count() { let repeat_limit = 5; From 69f23c25214f8e42c9556c868fdeed0361a8c00c Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Thu, 15 Feb 2024 22:03:21 +0100 Subject: [PATCH 2/3] shuf: obey all headcount args, not just the last --- src/uu/shuf/src/shuf.rs | 1 + tests/by-util/test_shuf.rs | 66 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/src/uu/shuf/src/shuf.rs b/src/uu/shuf/src/shuf.rs index 4052af49e..7df7f1e44 100644 --- a/src/uu/shuf/src/shuf.rs +++ b/src/uu/shuf/src/shuf.rs @@ -145,6 +145,7 @@ pub fn uu_app() -> Command { .short('n') .long(options::HEAD_COUNT) .value_name("COUNT") + .action(clap::ArgAction::Append) .help("output at most COUNT lines"), ) .arg( diff --git a/tests/by-util/test_shuf.rs b/tests/by-util/test_shuf.rs index eca914f9f..91167b36e 100644 --- a/tests/by-util/test_shuf.rs +++ b/tests/by-util/test_shuf.rs @@ -131,6 +131,72 @@ fn test_head_count() { ); } +#[test] +fn test_head_count_multi_big_then_small() { + let repeat_limit = 5; + let input_seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let input = input_seq + .iter() + .map(ToString::to_string) + .collect::>() + .join("\n"); + + let result = new_ucmd!() + .arg("-n") + .arg(&(repeat_limit + 1).to_string()) + .arg("-n") + .arg(&repeat_limit.to_string()) + .pipe_in(input.as_bytes()) + .succeeds(); + result.no_stderr(); + + let result_seq: Vec = result + .stdout_str() + .split('\n') + .filter(|x| !x.is_empty()) + .map(|x| x.parse().unwrap()) + .collect(); + assert_eq!(result_seq.len(), repeat_limit, "Output is not limited"); + assert!( + result_seq.iter().all(|x| input_seq.contains(x)), + "Output includes element not from input: {}", + result.stdout_str() + ); +} + +#[test] +fn test_head_count_multi_small_then_big() { + let repeat_limit = 5; + let input_seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let input = input_seq + .iter() + .map(ToString::to_string) + .collect::>() + .join("\n"); + + let result = new_ucmd!() + .arg("-n") + .arg(&repeat_limit.to_string()) + .arg("-n") + .arg(&(repeat_limit + 1).to_string()) + .pipe_in(input.as_bytes()) + .succeeds(); + result.no_stderr(); + + let result_seq: Vec = result + .stdout_str() + .split('\n') + .filter(|x| !x.is_empty()) + .map(|x| x.parse().unwrap()) + .collect(); + assert_eq!(result_seq.len(), repeat_limit, "Output is not limited"); + assert!( + result_seq.iter().all(|x| input_seq.contains(x)), + "Output includes element not from input: {}", + result.stdout_str() + ); +} + #[test] fn test_repeat() { let repeat_limit = 15000; From b091911aae1e6289f855f6612925cf53838bda4b Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Fri, 16 Feb 2024 21:14:56 +0100 Subject: [PATCH 3/3] shuf: refuse multiple input ranges and multiple output files --- src/uu/shuf/src/shuf.rs | 7 ++-- tests/by-util/test_shuf.rs | 73 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/uu/shuf/src/shuf.rs b/src/uu/shuf/src/shuf.rs index 7df7f1e44..091024ec4 100644 --- a/src/uu/shuf/src/shuf.rs +++ b/src/uu/shuf/src/shuf.rs @@ -120,7 +120,6 @@ pub fn uu_app() -> Command { .version(crate_version!()) .override_usage(format_usage(USAGE)) .infer_long_args(true) - .args_override_self(true) .arg( Arg::new(options::ECHO) .short('e') @@ -168,14 +167,16 @@ pub fn uu_app() -> Command { .short('r') .long(options::REPEAT) .help("output lines can be repeated") - .action(ArgAction::SetTrue), + .action(ArgAction::SetTrue) + .overrides_with(options::REPEAT), ) .arg( Arg::new(options::ZERO_TERMINATED) .short('z') .long(options::ZERO_TERMINATED) .help("line delimiter is NUL, not newline") - .action(ArgAction::SetTrue), + .action(ArgAction::SetTrue) + .overrides_with(options::ZERO_TERMINATED), ) .arg(Arg::new(options::FILE).value_hint(clap::ValueHint::FilePath)) } diff --git a/tests/by-util/test_shuf.rs b/tests/by-util/test_shuf.rs index 91167b36e..335af7909 100644 --- a/tests/by-util/test_shuf.rs +++ b/tests/by-util/test_shuf.rs @@ -48,6 +48,22 @@ fn test_zero_termination() { assert_eq!(result_seq, input_seq, "Output is not a permutation"); } +#[test] +fn test_zero_termination_multi() { + let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let result = new_ucmd!().arg("-z").arg("-z").arg("-i1-10").succeeds(); + result.no_stderr(); + + let mut result_seq: Vec = result + .stdout_str() + .split('\0') + .filter(|x| !x.is_empty()) + .map(|x| x.parse().unwrap()) + .collect(); + result_seq.sort_unstable(); + assert_eq!(result_seq, input_seq, "Output is not a permutation"); +} + #[test] fn test_empty_input() { let result = new_ucmd!().pipe_in(vec![]).succeeds(); @@ -235,6 +251,45 @@ fn test_repeat() { ); } +#[test] +fn test_repeat_multi() { + let repeat_limit = 15000; + let input_seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let input = input_seq + .iter() + .map(ToString::to_string) + .collect::>() + .join("\n"); + + let result = new_ucmd!() + .arg("-r") + .arg("-r") // The only difference to test_repeat() + .args(&["-n", &repeat_limit.to_string()]) + .pipe_in(input.as_bytes()) + .succeeds(); + result.no_stderr(); + + let result_seq: Vec = result + .stdout_str() + .split('\n') + .filter(|x| !x.is_empty()) + .map(|x| x.parse().unwrap()) + .collect(); + assert_eq!( + result_seq.len(), + repeat_limit, + "Output is not repeating forever" + ); + assert!( + result_seq.iter().all(|x| input_seq.contains(x)), + "Output includes element not from input: {:?}", + result_seq + .iter() + .filter(|x| !input_seq.contains(x)) + .collect::>() + ); +} + #[test] fn test_file_input() { let expected_seq = vec![11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; @@ -292,6 +347,24 @@ fn test_shuf_invalid_input_range_three() { .stderr_contains("invalid input range: 'b'"); } +#[test] +fn test_shuf_multiple_input_ranges() { + new_ucmd!() + .args(&["-i", "2-9", "-i", "2-9"]) + .fails() + .stderr_contains("--input-range") + .stderr_contains("cannot be used multiple times"); +} + +#[test] +fn test_shuf_multiple_outputs() { + new_ucmd!() + .args(&["-o", "file_a", "-o", "file_b"]) + .fails() + .stderr_contains("--output") + .stderr_contains("cannot be used multiple times"); +} + #[test] fn test_shuf_invalid_input_line_count() { new_ucmd!()