coreutils/tests/by-util/test_seq.rs
2025-07-01 03:36:46 +02:00

1078 lines
28 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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.
// spell-checker:ignore lmnop xlmnop
use uutests::new_ucmd;
#[test]
fn test_invalid_arg() {
new_ucmd!().arg("--definitely-invalid").fails_with_code(1);
}
#[test]
fn test_no_args() {
new_ucmd!()
.fails_with_code(1)
.stderr_contains("missing operand");
}
#[test]
fn test_format_and_equal_width() {
new_ucmd!()
.args(&["-w", "-f", "%f", "1"])
.fails_with_code(1)
.stderr_contains("format string may not be specified");
}
#[test]
fn test_hex_rejects_sign_after_identifier() {
new_ucmd!()
.args(&["0x-123ABC"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '0x-123ABC'");
new_ucmd!()
.args(&["0x+123ABC"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '0x+123ABC'");
new_ucmd!()
.args(&["--", "-0x-123ABC"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '-0x-123ABC'");
new_ucmd!()
.args(&["--", "-0x+123ABC"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '-0x+123ABC'");
// test without "--" => argument parsed as (invalid) flag
new_ucmd!()
.args(&["-0x-123ABC"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '-0x-123ABC'");
new_ucmd!()
.args(&["-0x+123ABC"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '-0x+123ABC'");
}
#[test]
fn test_hex_lowercase_uppercase() {
new_ucmd!()
.args(&["0xa", "0xA"])
.succeeds()
.stdout_is("10\n");
new_ucmd!()
.args(&["0Xa", "0XA"])
.succeeds()
.stdout_is("10\n");
}
#[test]
fn test_hex_big_number() {
new_ucmd!()
.args(&[
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"0x100000000000000000000000000000000",
])
.succeeds()
.stdout_is(
"340282366920938463463374607431768211455\n340282366920938463463374607431768211456\n",
);
}
#[test]
fn test_hex_identifier_in_wrong_place() {
new_ucmd!()
.args(&["1234ABCD0x"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '1234ABCD0x'");
}
#[test]
fn test_rejects_nan() {
new_ucmd!()
.arg("NaN")
.fails()
.usage_error("invalid 'not-a-number' argument: 'NaN'");
}
#[test]
fn test_rejects_non_floats() {
new_ucmd!()
.arg("foo")
.fails()
.usage_error("invalid floating point argument: 'foo'");
}
#[test]
fn test_accepts_option_argument_directly() {
new_ucmd!()
.arg("-s,")
.arg("2")
.succeeds()
.stdout_is("1,2\n");
}
#[test]
fn test_option_with_detected_negative_argument() {
new_ucmd!()
.arg("-s,")
.args(&["-1", "2"])
.succeeds()
.stdout_is("-1,0,1,2\n");
}
#[test]
fn test_negative_number_as_separator() {
new_ucmd!()
.arg("-s")
.args(&["-1", "2"])
.succeeds()
.stdout_is("1-12\n");
}
#[test]
fn test_invalid_float() {
new_ucmd!()
.args(&["1e2.3"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '1e2.3'");
new_ucmd!()
.args(&["1e2.3", "2"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '1e2.3'");
new_ucmd!()
.args(&["1", "1e2.3"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '1e2.3'");
new_ucmd!()
.args(&["1e2.3", "2", "3"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '1e2.3'");
new_ucmd!()
.args(&["1", "1e2.3", "3"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '1e2.3'");
new_ucmd!()
.args(&["1", "2", "1e2.3"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '1e2.3'");
}
#[test]
fn test_width_invalid_float() {
new_ucmd!()
.args(&["-w", "1e2.3"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '1e2.3'");
}
// ---- Tests for the big integer based path ----
#[test]
fn test_count_up() {
new_ucmd!()
.args(&["10"])
.succeeds()
.stdout_is("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n");
}
#[test]
fn test_count_down() {
new_ucmd!()
.args(&["--", "5", "-1", "1"])
.succeeds()
.stdout_is("5\n4\n3\n2\n1\n");
new_ucmd!()
.args(&["5", "-1", "1"])
.succeeds()
.stdout_is("5\n4\n3\n2\n1\n");
}
#[test]
fn test_separator_and_terminator() {
new_ucmd!()
.args(&["-s", ",", "-t", "!", "2", "6"])
.succeeds()
.stdout_is("2,3,4,5,6!");
new_ucmd!()
.args(&["-s", ",", "2", "6"])
.succeeds()
.stdout_is("2,3,4,5,6\n");
new_ucmd!()
.args(&["-s", "", "2", "6"])
.succeeds()
.stdout_is("23456\n");
new_ucmd!()
.args(&["-s", "\n", "2", "6"])
.succeeds()
.stdout_is("2\n3\n4\n5\n6\n");
new_ucmd!()
.args(&["-s", "\\n", "2", "6"])
.succeeds()
.stdout_is("2\\n3\\n4\\n5\\n6\n");
}
#[test]
fn test_equalize_widths() {
let args = ["-w", "--equal-width"];
for arg in args {
new_ucmd!()
.args(&[arg, "5", "10"])
.succeeds()
.stdout_is("05\n06\n07\n08\n09\n10\n");
}
}
#[test]
fn test_seq_wrong_arg() {
new_ucmd!().args(&["-w", "5", "10", "33", "32"]).fails();
}
#[test]
fn test_zero_step() {
new_ucmd!().args(&["10", "0", "32"]).fails();
}
#[test]
fn test_big_numbers() {
new_ucmd!()
.args(&[
"1000000000000000000000000000",
"1000000000000000000000000001",
])
.succeeds()
.stdout_only("1000000000000000000000000000\n1000000000000000000000000001\n");
}
// ---- Tests for the floating point based path ----
#[test]
fn test_count_up_floats() {
new_ucmd!()
.args(&["10.0"])
.succeeds()
.stdout_is("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n");
}
#[test]
fn test_count_down_floats() {
new_ucmd!()
.args(&["--", "5", "-1.0", "1"])
.succeeds()
.stdout_is("5.0\n4.0\n3.0\n2.0\n1.0\n");
new_ucmd!()
.args(&["5", "-1", "1.0"])
.succeeds()
.stdout_is("5\n4\n3\n2\n1\n");
}
#[test]
fn test_separator_and_terminator_floats() {
new_ucmd!()
.args(&["-s", ",", "-t", "!", "2.0", "6"])
.succeeds()
.stdout_is("2.0,3.0,4.0,5.0,6.0!");
new_ucmd!()
.args(&["-s", "", "-t", "!", "2.0", "6"])
.succeeds()
.stdout_is("2.03.04.05.06.0!");
}
#[test]
fn test_equalize_widths_floats() {
new_ucmd!()
.args(&["-w", "5", "10.0"])
.succeeds()
.stdout_is("05\n06\n07\n08\n09\n10\n");
}
#[test]
fn test_seq_wrong_arg_floats() {
new_ucmd!().args(&["-w", "5", "10.0", "33", "32"]).fails();
}
#[test]
fn test_zero_step_floats() {
new_ucmd!().args(&["10.0", "0", "32"]).fails();
}
#[test]
fn test_preserve_negative_zero_start() {
new_ucmd!()
.args(&["-0", "1"])
.succeeds()
.stdout_only("-0\n1\n");
new_ucmd!()
.args(&["-0", "1", "2"])
.succeeds()
.stdout_only("-0\n1\n2\n");
new_ucmd!()
.args(&["-0", "1", "2.0"])
.succeeds()
.stdout_only("-0\n1\n2\n");
}
#[test]
fn test_drop_negative_zero_end() {
new_ucmd!()
.args(&["1", "-1", "-0"])
.succeeds()
.stdout_only("1\n0\n");
}
#[test]
fn test_width_scientific_notation() {
new_ucmd!()
.args(&["-w", "999", "1e3"])
.succeeds()
.stdout_only("0999\n1000\n");
new_ucmd!()
.args(&["-w", "999", "1E3"])
.succeeds()
.stdout_only("0999\n1000\n");
}
#[test]
fn test_width_negative_zero() {
new_ucmd!()
.args(&["-w", "-0", "1"])
.succeeds()
.stdout_only("-0\n01\n");
new_ucmd!()
.args(&["-w", "-0", "1", "2"])
.succeeds()
.stdout_only("-0\n01\n02\n");
new_ucmd!()
.args(&["-w", "-0", "1", "2.0"])
.succeeds()
.stdout_only("-0\n01\n02\n");
}
#[test]
fn test_width_negative_zero_decimal_notation() {
new_ucmd!()
.args(&["-w", "-0.0", "1"])
.succeeds()
.stdout_only("-0.0\n01.0\n");
new_ucmd!()
.args(&["-w", "-0.0", "1.0"])
.succeeds()
.stdout_only("-0.0\n01.0\n");
new_ucmd!()
.args(&["-w", "-0.0", "1", "2"])
.succeeds()
.stdout_only("-0.0\n01.0\n02.0\n");
new_ucmd!()
.args(&["-w", "-0.0", "1", "2.0"])
.succeeds()
.stdout_only("-0.0\n01.0\n02.0\n");
new_ucmd!()
.args(&["-w", "-0.0", "1.0", "2"])
.succeeds()
.stdout_only("-0.0\n01.0\n02.0\n");
new_ucmd!()
.args(&["-w", "-0.0", "1.0", "2.0"])
.succeeds()
.stdout_only("-0.0\n01.0\n02.0\n");
}
#[test]
fn test_width_negative_zero_scientific_notation() {
new_ucmd!()
.args(&["-w", "-0e0", "1"])
.succeeds()
.stdout_only("-0\n01\n");
new_ucmd!()
.args(&["-w", "-0e0", "1", "2"])
.succeeds()
.stdout_only("-0\n01\n02\n");
new_ucmd!()
.args(&["-w", "-0e0", "1", "2.0"])
.succeeds()
.stdout_only("-0\n01\n02\n");
new_ucmd!()
.args(&["-w", "-0e+1", "1"])
.succeeds()
.stdout_only("-00\n001\n");
new_ucmd!()
.args(&["-w", "-0e+1", "1", "2"])
.succeeds()
.stdout_only("-00\n001\n002\n");
new_ucmd!()
.args(&["-w", "-0e+1", "1", "2.0"])
.succeeds()
.stdout_only("-00\n001\n002\n");
new_ucmd!()
.args(&["-w", "-0.000e0", "1"])
.succeeds()
.stdout_only("-0.000\n01.000\n");
new_ucmd!()
.args(&["-w", "-0.000e0", "1", "2"])
.succeeds()
.stdout_only("-0.000\n01.000\n02.000\n");
new_ucmd!()
.args(&["-w", "-0.000e0", "1", "2.0"])
.succeeds()
.stdout_only("-0.000\n01.000\n02.000\n");
new_ucmd!()
.args(&["-w", "-0.000e-2", "1"])
.succeeds()
.stdout_only("-0.00000\n01.00000\n");
new_ucmd!()
.args(&["-w", "-0.000e-2", "1", "2"])
.succeeds()
.stdout_only("-0.00000\n01.00000\n02.00000\n");
new_ucmd!()
.args(&["-w", "-0.000e-2", "1", "2.0"])
.succeeds()
.stdout_only("-0.00000\n01.00000\n02.00000\n");
new_ucmd!()
.args(&["-w", "-0.000e5", "1"])
.succeeds()
.stdout_only("-000000\n0000001\n");
new_ucmd!()
.args(&["-w", "-0.000e5", "1", "2"])
.succeeds()
.stdout_only("-000000\n0000001\n0000002\n");
new_ucmd!()
.args(&["-w", "-0.000e5", "1", "2.0"])
.succeeds()
.stdout_only("-000000\n0000001\n0000002\n");
new_ucmd!()
.args(&["-w", "-0.000e5", "1"])
.succeeds()
.stdout_only("-000000\n0000001\n");
new_ucmd!()
.args(&["-w", "-0.000e5", "1", "2"])
.succeeds()
.stdout_only("-000000\n0000001\n0000002\n");
new_ucmd!()
.args(&["-w", "-0.000e5", "1", "2.0"])
.succeeds()
.stdout_only("-000000\n0000001\n0000002\n");
}
#[test]
fn test_width_decimal_scientific_notation_increment() {
new_ucmd!()
.args(&["-w", ".1", "1e-2", ".11"])
.succeeds()
.stdout_only("0.10\n0.11\n");
new_ucmd!()
.args(&["-w", ".0", "1.500e-1", ".2"])
.succeeds()
.stdout_only("0.0000\n0.1500\n");
}
/// Test that trailing zeros in the start argument contribute to precision.
#[test]
fn test_width_decimal_scientific_notation_trailing_zeros_start() {
new_ucmd!()
.args(&["-w", ".1000", "1e-2", ".11"])
.succeeds()
.stdout_only("0.1000\n0.1100\n");
}
/// Test that trailing zeros in the increment argument contribute to precision.
#[test]
fn test_width_decimal_scientific_notation_trailing_zeros_increment() {
new_ucmd!()
.args(&["-w", "1e-1", "0.0100", ".11"])
.succeeds()
.stdout_only("0.1000\n0.1100\n");
}
#[test]
fn test_width_negative_decimal_notation() {
new_ucmd!()
.args(&["-w", "-.1", ".1", ".11"])
.succeeds()
.stdout_only("-0.1\n00.0\n00.1\n");
}
#[test]
fn test_width_negative_scientific_notation() {
new_ucmd!()
.args(&["-w", "-1e-3", "1"])
.succeeds()
.stdout_only("-0.001\n00.999\n");
new_ucmd!()
.args(&["-w", "-1.e-3", "1"])
.succeeds()
.stdout_only("-0.001\n00.999\n");
new_ucmd!()
.args(&["-w", "-1.0e-4", "1"])
.succeeds()
.stdout_only("-0.00010\n00.99990\n");
new_ucmd!()
.args(&["-w", "-.1e2", "10", "100"])
.succeeds()
.stdout_only(
"-010
0000
0010
0020
0030
0040
0050
0060
0070
0080
0090
0100
",
);
new_ucmd!()
.args(&["-w", "-0.1e2", "10", "100"])
.succeeds()
.stdout_only(
"-010
0000
0010
0020
0030
0040
0050
0060
0070
0080
0090
0100
",
);
}
/// Test that trailing zeros in the end argument do not contribute to width.
#[test]
fn test_width_decimal_scientific_notation_trailing_zeros_end() {
new_ucmd!()
.args(&["-w", "1e-1", "1e-2", ".1100"])
.succeeds()
.stdout_only("0.10\n0.11\n");
}
#[test]
fn test_width_floats() {
new_ucmd!()
.args(&["-w", "9.0", "10.0"])
.succeeds()
.stdout_only("09.0\n10.0\n");
}
#[test]
fn test_neg_inf() {
new_ucmd!()
.args(&["--", "-inf", "0"])
.run_stdout_starts_with(b"-inf\n-inf\n-inf\n")
.success();
}
#[test]
fn test_neg_infinity() {
new_ucmd!()
.args(&["--", "-infinity", "0"])
.run_stdout_starts_with(b"-inf\n-inf\n-inf\n")
.success();
}
#[test]
fn test_inf() {
new_ucmd!()
.args(&["inf"])
.run_stdout_starts_with(b"1\n2\n3\n")
.success();
}
#[test]
fn test_infinity() {
new_ucmd!()
.args(&["infinity"])
.run_stdout_starts_with(b"1\n2\n3\n")
.success();
}
#[test]
fn test_inf_width() {
new_ucmd!()
.args(&["-w", "1.000", "inf", "inf"])
.run_stdout_starts_with(b"1.000\n inf\n inf\n inf\n")
.success();
}
#[test]
fn test_neg_inf_width() {
new_ucmd!()
.args(&["-w", "1.000", "-inf", "-inf"])
.run_stdout_starts_with(b"1.000\n -inf\n -inf\n -inf\n")
.success();
}
#[test]
fn test_ignore_leading_whitespace() {
new_ucmd!().arg(" 1").succeeds().stdout_only("1\n");
}
#[test]
fn test_trailing_whitespace_error() {
// In some locales, the GNU error message has curly quotes ()
// instead of straight quotes ('). We just test the straight single
// quotes.
new_ucmd!()
.arg("1 ")
.fails()
.usage_error("invalid floating point argument: '1 '");
}
#[test]
fn test_negative_zero_int_start_float_increment() {
new_ucmd!()
.args(&["-0", "0.1", "0.1"])
.succeeds()
.stdout_only("-0.0\n0.1\n");
}
#[test]
fn test_float_precision_increment() {
new_ucmd!()
.args(&["999", "0.1", "1000.1"])
.succeeds()
.stdout_only(
"999.0
999.1
999.2
999.3
999.4
999.5
999.6
999.7
999.8
999.9
1000.0
1000.1
",
);
}
/// Test for floating point precision issues.
#[test]
fn test_negative_increment_decimal() {
new_ucmd!()
.args(&["0.1", "-0.1", "-0.2"])
.succeeds()
.stdout_only("0.1\n0.0\n-0.1\n-0.2\n");
}
#[test]
fn test_zero_not_first() {
new_ucmd!()
.args(&["-w", "-0.1", "0.1", "0.1"])
.succeeds()
.stdout_only("-0.1\n00.0\n00.1\n");
}
#[test]
fn test_rounding_end() {
new_ucmd!()
.args(&["1", "-1", "0.1"])
.succeeds()
.stdout_only("1\n");
}
#[test]
fn test_parse_error_float() {
new_ucmd!()
.arg("lmnop")
.fails()
.usage_error("invalid floating point argument: 'lmnop'");
}
#[test]
fn test_parse_error_hex() {
new_ucmd!()
.arg("0xlmnop")
.fails()
.usage_error("invalid floating point argument: '0xlmnop'");
}
#[test]
fn test_format_option() {
new_ucmd!()
.args(&["-f", "%.2f", "0.0", "0.1", "0.5"])
.succeeds()
.stdout_only("0.00\n0.10\n0.20\n0.30\n0.40\n0.50\n");
}
#[test]
fn test_format_option_default_precision() {
new_ucmd!()
.args(&["-f", "%f", "0", "0.7", "2"])
.succeeds()
.stdout_only("0.000000\n0.700000\n1.400000\n");
}
#[test]
fn test_format_option_default_precision_short() {
new_ucmd!()
.args(&["-f", "%g", "0", "0.987654321", "2"])
.succeeds()
.stdout_only("0\n0.987654\n1.97531\n");
}
#[test]
fn test_format_option_default_precision_scientific() {
new_ucmd!()
.args(&["-f", "%E", "0", "0.7", "2"])
.succeeds()
.stdout_only("0.000000E+00\n7.000000E-01\n1.400000E+00\n");
}
#[test]
fn test_auto_precision() {
new_ucmd!()
.args(&["1", "0x1p-1", "2"])
.succeeds()
.stdout_only("1\n1.5\n2\n");
}
#[test]
fn test_undefined() {
new_ucmd!()
.args(&["1e-9223372036854775808"])
.succeeds()
.no_output();
}
#[test]
fn test_invalid_float_point_fail_properly() {
// Note that we support arguments that are much bigger than what GNU coreutils supports.
// Tests below use exponents larger than we support (i64)
new_ucmd!()
.args(&["66000e0000000000000000000000000000000000000000000000000000092233720368547758070"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '66000e0000000000000000000000000000000000000000000000000000092233720368547758070'");
new_ucmd!()
.args(&["-1.1e92233720368547758070"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '-1.1e92233720368547758070'");
new_ucmd!()
.args(&["-.1e92233720368547758070"])
.fails()
.no_stdout()
.usage_error("invalid floating point argument: '-.1e92233720368547758070'");
}
#[test]
fn test_invalid_zero_increment_value() {
new_ucmd!()
.args(&["0", "0", "1"])
.fails()
.no_stdout()
.usage_error("invalid Zero increment value: '0'");
}
#[test]
fn test_power_of_ten_display() {
new_ucmd!()
.args(&["-f", "%.2g", "10", "10"])
.succeeds()
.stdout_only("10\n");
}
#[test]
fn test_default_g_precision() {
new_ucmd!()
.args(&["-f", "%010g", "1e5", "1e5"])
.succeeds()
.stdout_only("0000100000\n");
new_ucmd!()
.args(&["-f", "%010g", "1e6", "1e6"])
.succeeds()
.stdout_only("000001e+06\n");
}
#[test]
fn test_invalid_format() {
new_ucmd!()
.args(&["-f", "%%g", "1"])
.fails()
.no_stdout()
.stderr_contains("format '%%g' has no % directive");
new_ucmd!()
.args(&["-f", "%g%g", "1"])
.fails()
.no_stdout()
.stderr_contains("format '%g%g' has too many % directives");
new_ucmd!()
.args(&["-f", "%g%", "1"])
.fails()
.no_stdout()
.stderr_contains("format '%g%' has too many % directives");
new_ucmd!()
.args(&["-f", "%", "1"])
.fails()
.no_stdout()
.stderr_contains("format '%' ends in %");
}
#[test]
fn test_parse_scientific_zero() {
new_ucmd!()
.args(&["0e15", "1"])
.succeeds()
.stdout_only("0\n1\n");
new_ucmd!()
.args(&["0.0e15", "1"])
.succeeds()
.stdout_only("0\n1\n");
new_ucmd!()
.args(&["0", "1"])
.succeeds()
.stdout_only("0\n1\n");
new_ucmd!()
.args(&["-w", "0e15", "1"])
.succeeds()
.stdout_only("0000000000000000\n0000000000000001\n");
new_ucmd!()
.args(&["-w", "0.0e15", "1"])
.succeeds()
.stdout_only("0000000000000000\n0000000000000001\n");
new_ucmd!()
.args(&["-w", "0", "1"])
.succeeds()
.stdout_only("0\n1\n");
}
#[test]
fn test_parse_valid_hexadecimal_float_two_args() {
let test_cases = [
(["0x1p-1", "2"], "0.5\n1.5\n"),
(["0x.8p16", "32768"], "32768\n"),
(["0xffff.4p-4", "4096"], "4095.95\n"),
(["0xA.A9p-1", "6"], "5.33008\n"),
(["0xa.a9p-1", "6"], "5.33008\n"),
(["0xffffffffffp-30", "1024"], "1024\n"), // spell-checker:disable-line
([" 0XA.A9P-1", "6"], "5.33008\n"),
([" 0xee.", " 0xef."], "238\n239\n"),
];
for (input_arguments, expected_output) in &test_cases {
new_ucmd!()
.args(input_arguments)
.succeeds()
.stdout_only(expected_output);
}
}
#[test]
fn test_parse_valid_hexadecimal_float_three_args() {
let test_cases = [
(["0x3.4p-1", "0x4p-1", "4"], "1.625\n3.625\n"),
(
["-0x.ep-3", "-0x.1p-3", "-0x.fp-3"],
"-0.109375\n-0.117188\n",
),
];
for (input_arguments, expected_output) in &test_cases {
new_ucmd!()
.args(input_arguments)
.succeeds()
.stdout_only(expected_output);
}
}
#[test]
fn test_parse_float_gnu_coreutils() {
// some values from GNU coreutils tests
new_ucmd!()
.args(&[".89999", "1e-7", ".8999901"])
.succeeds()
.stdout_only("0.8999900\n0.8999901\n");
new_ucmd!()
.args(&["0", "0.000001", "0.000003"])
.succeeds()
.stdout_only("0.000000\n0.000001\n0.000002\n0.000003\n");
}
#[test]
fn test_parse_out_of_bounds_exponents() {
// The value 1e-9223372036854775808 is used in GNU Coreutils and BigDecimal tests to verify
// overflows and undefined behavior. Let's test the value too.
new_ucmd!()
.args(&["1e-9223372036854775808"])
.succeeds()
.stdout_only("");
// GNU seq supports arbitrarily small exponents (and treats the value as 0).
new_ucmd!()
.args(&["1e-922337203685477580800000000", "1"])
.succeeds()
.stdout_only("0\n1\n");
// Check we can also underflow to -0.0.
new_ucmd!()
.args(&["-1e-922337203685477580800000000", "1"])
.succeeds()
.stdout_only("-0\n1\n");
}
#[ignore = ""]
#[test]
fn test_parse_valid_hexadecimal_float_format_issues() {
// These tests detect differences in the representation of floating-point values with GNU seq.
// There are two key areas to investigate:
//
// 1. GNU seq uses long double (80-bit) types for values, while the current implementation
// relies on f64 (64-bit). This can lead to differences due to varying precision. However, it's
// likely not the primary cause, as even double (64-bit) values can differ when compared to
// f64.
//
// 2. GNU seq uses the %Lg format specifier for printing (see the "get_default_format" function
// ). It appears that Rust lacks a direct equivalent for this format. Additionally, %Lg
// can use %f (floating) or %e (scientific) depending on the precision. There also seem to be
// some differences in the behavior of C and Rust when displaying floating-point or scientific
// notation, at least without additional configuration.
//
// It makes sense to begin by experimenting with formats and attempting to replicate
// the printf("%Lg",...) behavior. Another area worth investigating is glibc, as reviewing its
// code may help uncover additional corner cases or test data that could reveal more issues.
//Test output: 0.00000000992804416455328464508056640625
new_ucmd!()
.args(&["0xa.a9p-30", "1"])
.succeeds()
.stdout_only("9.92804e-09\n1\n");
}
// GNU `seq` manual states that, when the parameters "all use a fixed point
// decimal representation", the format should be `%.pf`, where the precision
// is inferred from parameters. Else, `%g` is used.
//
// This is understandable, as translating hexadecimal precision to decimal precision
// is not straightforward or intuitive to the user. There are some exceptions though,
// if a mix of hexadecimal _integers_ and decimal floats are provided.
//
// The way precision is inferred is said to be able to "represent the output numbers
// exactly". In practice, this means that trailing zeros in first/increment number are
// considered, but not in the last number. This makes sense if we take that last number
// as a "bound", and not really part of input/output.
#[test]
fn test_precision_corner_cases() {
// Mixed input with integer hex still uses precision in decimal float
new_ucmd!()
.args(&["0x1", "0.90", "3"])
.succeeds()
.stdout_is("1.00\n1.90\n2.80\n");
// Mixed input with hex float reverts to %g
new_ucmd!()
.args(&["0x1.00", "0.90", "3"])
.succeeds()
.stdout_is("1\n1.9\n2.8\n");
// Even if it's the last number.
new_ucmd!()
.args(&["1", "1.20", "0x3.000000"])
.succeeds()
.stdout_is("1\n2.2\n");
// Otherwise, precision in last number is ignored.
new_ucmd!()
.args(&["1", "1.20", "3.000000"])
.succeeds()
.stdout_is("1.00\n2.20\n");
// Infinity is ignored
new_ucmd!()
.args(&["1", "1.2", "inf"])
.run_stdout_starts_with(b"1.0\n2.2\n3.4\n")
.success();
}
// GNU `seq` manual only makes guarantees about `-w` working if the
// provided numbers "all use a fixed point decimal representation",
// and guides the user to use `-f` for other cases.
#[test]
fn test_equalize_widths_corner_cases() {
// Mixed input with hexadecimal does behave as expected
new_ucmd!()
.args(&["-w", "0x1", "5.2", "9"])
.succeeds()
.stdout_is("1.0\n6.2\n");
// Confusingly, the number of integral digits in the last number is
// used to pad the output numbers, while it is ignored for precision
// computation.
//
// This problem has been reported on list here:
// "bug#77179: seq incorrectly(?) pads output when last parameter magnitude"
// https://lists.gnu.org/archive/html/bug-coreutils/2025-03/msg00028.html
//
// TODO: This case could be handled correctly, consider fixing this in
// `uutils` implementation. Output should probably be "1.0\n6.2\n".
new_ucmd!()
.args(&["-w", "0x1", "5.2", "10.0000"])
.succeeds()
.stdout_is("01.0\n06.2\n");
// But if we fixed the case above, we need to make sure we still pad
// if the last number in the output requires an extra digit.
new_ucmd!()
.args(&["-w", "0x1", "5.2", "15.0000"])
.succeeds()
.stdout_is("01.0\n06.2\n11.4\n");
// GNU `seq` bails out if any hex float is in the output.
// Unlike the precision corner cases above, it is harder to justify
// this behavior for hexadecimal float inputs, as it is always be
// possible to output numbers with a fixed width.
//
// This problem has been reported on list here:
// "bug#76070: Subject: seq, hexadecimal args and equal width"
// https://lists.gnu.org/archive/html/bug-coreutils/2025-02/msg00007.html
//
// TODO: These cases could be handled correctly, consider fixing this in
// `uutils` implementation.
// If we ignore hexadecimal precision, the output should be "1.0\n6.2\n".
new_ucmd!()
.args(&["-w", "0x1.0000", "5.2", "10"])
.succeeds()
.stdout_is("1\n6.2\n");
// The equivalent `seq -w 1.0625 1.00002 3` correctly pads the first number: "1.06250\n2.06252\n"
new_ucmd!()
.args(&["-w", "0x1.1", "1.00002", "3"])
.succeeds()
.stdout_is("1.0625\n2.06252\n");
// We can't really pad with infinite number of zeros, so `-w` is ignored.
// (there is another test with infinity as an increment above)
new_ucmd!()
.args(&["-w", "1", "1.2", "inf"])
.run_stdout_starts_with(b"1.0\n2.2\n3.4\n")
.success();
}