|
4 | 4 | // file that was distributed with this source code. |
5 | 5 | // spell-checker:ignore lmnop xlmnop |
6 | 6 | use crate::common::util::TestScenario; |
7 | | -use std::process::Stdio; |
8 | 7 |
|
9 | 8 | #[test] |
10 | 9 | fn test_invalid_arg() { |
@@ -566,51 +565,52 @@ fn test_width_floats() { |
566 | 565 | .stdout_only("09.0\n10.0\n"); |
567 | 566 | } |
568 | 567 |
|
569 | | -// TODO This is duplicated from `test_yes.rs`; refactor them. |
570 | | -/// Run `seq`, capture some of the output, close the pipe, and verify it. |
571 | | -fn run(args: &[&str], expected: &[u8]) { |
572 | | - let mut cmd = new_ucmd!(); |
573 | | - let mut child = cmd.args(args).set_stdout(Stdio::piped()).run_no_wait(); |
574 | | - let buf = child.stdout_exact_bytes(expected.len()); |
575 | | - child.close_stdout(); |
576 | | - child.wait().unwrap().success(); |
577 | | - assert_eq!(buf.as_slice(), expected); |
578 | | -} |
579 | | - |
580 | 568 | #[test] |
581 | 569 | fn test_neg_inf() { |
582 | | - run(&["--", "-inf", "0"], b"-inf\n-inf\n-inf\n"); |
| 570 | + new_ucmd!() |
| 571 | + .args(&["--", "-inf", "0"]) |
| 572 | + .run_stdout_starts_with(b"-inf\n-inf\n-inf\n") |
| 573 | + .success(); |
583 | 574 | } |
584 | 575 |
|
585 | 576 | #[test] |
586 | 577 | fn test_neg_infinity() { |
587 | | - run(&["--", "-infinity", "0"], b"-inf\n-inf\n-inf\n"); |
| 578 | + new_ucmd!() |
| 579 | + .args(&["--", "-infinity", "0"]) |
| 580 | + .run_stdout_starts_with(b"-inf\n-inf\n-inf\n") |
| 581 | + .success(); |
588 | 582 | } |
589 | 583 |
|
590 | 584 | #[test] |
591 | 585 | fn test_inf() { |
592 | | - run(&["inf"], b"1\n2\n3\n"); |
| 586 | + new_ucmd!() |
| 587 | + .args(&["inf"]) |
| 588 | + .run_stdout_starts_with(b"1\n2\n3\n") |
| 589 | + .success(); |
593 | 590 | } |
594 | 591 |
|
595 | 592 | #[test] |
596 | 593 | fn test_infinity() { |
597 | | - run(&["infinity"], b"1\n2\n3\n"); |
| 594 | + new_ucmd!() |
| 595 | + .args(&["infinity"]) |
| 596 | + .run_stdout_starts_with(b"1\n2\n3\n") |
| 597 | + .success(); |
598 | 598 | } |
599 | 599 |
|
600 | 600 | #[test] |
601 | 601 | fn test_inf_width() { |
602 | | - run( |
603 | | - &["-w", "1.000", "inf", "inf"], |
604 | | - b"1.000\n inf\n inf\n inf\n", |
605 | | - ); |
| 602 | + new_ucmd!() |
| 603 | + .args(&["-w", "1.000", "inf", "inf"]) |
| 604 | + .run_stdout_starts_with(b"1.000\n inf\n inf\n inf\n") |
| 605 | + .success(); |
606 | 606 | } |
607 | 607 |
|
608 | 608 | #[test] |
609 | 609 | fn test_neg_inf_width() { |
610 | | - run( |
611 | | - &["-w", "1.000", "-inf", "-inf"], |
612 | | - b"1.000\n -inf\n -inf\n -inf\n", |
613 | | - ); |
| 610 | + new_ucmd!() |
| 611 | + .args(&["-w", "1.000", "-inf", "-inf"]) |
| 612 | + .run_stdout_starts_with(b"1.000\n -inf\n -inf\n -inf\n") |
| 613 | + .success(); |
614 | 614 | } |
615 | 615 |
|
616 | 616 | #[test] |
@@ -936,3 +936,111 @@ fn test_parse_valid_hexadecimal_float_format_issues() { |
936 | 936 | .succeeds() |
937 | 937 | .stdout_only("9.92804e-09\n1\n"); |
938 | 938 | } |
| 939 | + |
| 940 | +// GNU `seq` manual states that, when the parameters "all use a fixed point |
| 941 | +// decimal representation", the format should be `%.pf`, where the precision |
| 942 | +// is inferred from parameters. Else, `%g` is used. |
| 943 | +// |
| 944 | +// This is understandable, as translating hexadecimal precision to decimal precision |
| 945 | +// is not straightforward or intuitive to the user. There are some exceptions though, |
| 946 | +// if a mix of hexadecimal _integers_ and decimal floats are provided. |
| 947 | +// |
| 948 | +// The way precision is inferred is said to be able to "represent the output numbers |
| 949 | +// exactly". In practice, this means that trailing zeros in first/increment number are |
| 950 | +// considered, but not in the last number. This makes sense if we take that last number |
| 951 | +// as a "bound", and not really part of input/output. |
| 952 | +#[test] |
| 953 | +fn test_precision_corner_cases() { |
| 954 | + // Mixed input with integer hex still uses precision in decimal float |
| 955 | + new_ucmd!() |
| 956 | + .args(&["0x1", "0.90", "3"]) |
| 957 | + .succeeds() |
| 958 | + .stdout_is("1.00\n1.90\n2.80\n"); |
| 959 | + |
| 960 | + // Mixed input with hex float reverts to %g |
| 961 | + new_ucmd!() |
| 962 | + .args(&["0x1.00", "0.90", "3"]) |
| 963 | + .succeeds() |
| 964 | + .stdout_is("1\n1.9\n2.8\n"); |
| 965 | + |
| 966 | + // Even if it's the last number. |
| 967 | + new_ucmd!() |
| 968 | + .args(&["1", "1.20", "0x3.000000"]) |
| 969 | + .succeeds() |
| 970 | + .stdout_is("1\n2.2\n"); |
| 971 | + |
| 972 | + // Otherwise, precision in last number is ignored. |
| 973 | + new_ucmd!() |
| 974 | + .args(&["1", "1.20", "3.000000"]) |
| 975 | + .succeeds() |
| 976 | + .stdout_is("1.00\n2.20\n"); |
| 977 | + |
| 978 | + // Infinity is ignored |
| 979 | + new_ucmd!() |
| 980 | + .args(&["1", "1.2", "inf"]) |
| 981 | + .run_stdout_starts_with(b"1.0\n2.2\n3.4\n") |
| 982 | + .success(); |
| 983 | +} |
| 984 | + |
| 985 | +// GNU `seq` manual only makes guarantees about `-w` working if the |
| 986 | +// provided numbers "all use a fixed point decimal representation", |
| 987 | +// and guides the user to use `-f` for other cases. |
| 988 | +#[test] |
| 989 | +fn test_equalize_widths_corner_cases() { |
| 990 | + // Mixed input with hexadecimal does behave as expected |
| 991 | + new_ucmd!() |
| 992 | + .args(&["-w", "0x1", "5.2", "9"]) |
| 993 | + .succeeds() |
| 994 | + .stdout_is("1.0\n6.2\n"); |
| 995 | + |
| 996 | + // Confusingly, the number of integral digits in the last number is |
| 997 | + // used to pad the output numbers, while it is ignored for precision |
| 998 | + // computation. |
| 999 | + // |
| 1000 | + // This problem has been reported on list here: |
| 1001 | + // "bug#77179: seq incorrectly(?) pads output when last parameter magnitude" |
| 1002 | + // https://lists.gnu.org/archive/html/bug-coreutils/2025-03/msg00028.html |
| 1003 | + // |
| 1004 | + // TODO: This case could be handled correctly, consider fixing this in |
| 1005 | + // `uutils` implementation. Output should probably be "1.0\n6.2\n". |
| 1006 | + new_ucmd!() |
| 1007 | + .args(&["-w", "0x1", "5.2", "10.0000"]) |
| 1008 | + .succeeds() |
| 1009 | + .stdout_is("01.0\n06.2\n"); |
| 1010 | + |
| 1011 | + // But if we fixed the case above, we need to make sure we still pad |
| 1012 | + // if the last number in the output requires an extra digit. |
| 1013 | + new_ucmd!() |
| 1014 | + .args(&["-w", "0x1", "5.2", "15.0000"]) |
| 1015 | + .succeeds() |
| 1016 | + .stdout_is("01.0\n06.2\n11.4\n"); |
| 1017 | + |
| 1018 | + // GNU `seq` bails out if any hex float is in the output. |
| 1019 | + // Unlike the precision corner cases above, it is harder to justify |
| 1020 | + // this behavior for hexadecimal float inputs, as it is always be |
| 1021 | + // possible to output numbers with a fixed width. |
| 1022 | + // |
| 1023 | + // This problem has been reported on list here: |
| 1024 | + // "bug#76070: Subject: seq, hexadecimal args and equal width" |
| 1025 | + // https://lists.gnu.org/archive/html/bug-coreutils/2025-02/msg00007.html |
| 1026 | + // |
| 1027 | + // TODO: These cases could be handled correctly, consider fixing this in |
| 1028 | + // `uutils` implementation. |
| 1029 | + // If we ignore hexadecimal precision, the output should be "1.0\n6.2\n". |
| 1030 | + new_ucmd!() |
| 1031 | + .args(&["-w", "0x1.0000", "5.2", "10"]) |
| 1032 | + .succeeds() |
| 1033 | + .stdout_is("1\n6.2\n"); |
| 1034 | + // The equivalent `seq -w 1.0625 1.00002 3` correctly pads the first number: "1.06250\n2.06252\n" |
| 1035 | + new_ucmd!() |
| 1036 | + .args(&["-w", "0x1.1", "1.00002", "3"]) |
| 1037 | + .succeeds() |
| 1038 | + .stdout_is("1.0625\n2.06252\n"); |
| 1039 | + |
| 1040 | + // We can't really pad with infinite number of zeros, so `-w` is ignored. |
| 1041 | + // (there is another test with infinity as an increment above) |
| 1042 | + new_ucmd!() |
| 1043 | + .args(&["-w", "1", "1.2", "inf"]) |
| 1044 | + .run_stdout_starts_with(b"1.0\n2.2\n3.4\n") |
| 1045 | + .success(); |
| 1046 | +} |
0 commit comments