diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 16:58:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 16:58:41 +0000 |
commit | e1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe (patch) | |
tree | f5cc731bedcac0fb7fe14d952e4581e749f8bb87 /tests/misc | |
parent | Initial commit. (diff) | |
download | coreutils-upstream.tar.xz coreutils-upstream.zip |
Adding upstream version 9.4.upstream/9.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/misc')
39 files changed, 5104 insertions, 0 deletions
diff --git a/tests/misc/arch.sh b/tests/misc/arch.sh new file mode 100755 index 0000000..ec240d1 --- /dev/null +++ b/tests/misc/arch.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# Ensure that arch output is equal to uname -m + +# Copyright (C) 2007-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ arch + +arch > out || fail=1 +uname -m > exp || fail=1 + +compare exp out || fail=1 + +Exit $fail diff --git a/tests/misc/base64.pl b/tests/misc/base64.pl new file mode 100755 index 0000000..63e6c6b --- /dev/null +++ b/tests/misc/base64.pl @@ -0,0 +1,214 @@ +#!/usr/bin/perl +# Exercise base{32,64}. + +# Copyright (C) 2006-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; + +(my $program_name = $0) =~ s|.*/||; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +# Return the encoding of a string of N 'a's. +sub enc64($) +{ + my ($n) = @_; + my %remainder = ( 0 => '', 1 => 'YQ==', 2 => 'YWE=' ); + return 'YWFh' x ($n / 3) . $remainder{$n % 3}; +} + +sub enc32($) +{ + my ($n) = @_; + my %remainder = ( 0 => '', 1 => 'ME======', 2 => 'MFQQ====', + 3 => 'MFQWC===', 4 => 'MFQWCYI='); + return 'MFQWCYLB' x ($n / 5) . $remainder{$n % 5}; +} + +# Function reference to appropriate encoder +my $enc; + +# An encoded string of length 4KB, using 3K "a"s. +my $a3k; +my @a3k_nl; + +# Return a copy of S, with newlines inserted every WIDTH bytes. +# Ensure that the result (if not the empty string) is newline-terminated. +sub wrap($$) +{ + my ($s, $width) = @_; + $s =~ s/(.{$width})/$1\n/g; + substr ($s, -1, 1) ne "\n" + and $s .= "\n"; + return $s; +} + +my @Tests; + +sub gen_tests($) +{ + my ($prog) = @_; + my $try_help = "Try '$prog --help' for more information.\n"; + @Tests= + ( + ['empty', {IN=>''}, {OUT=>""}], + ['inout1', {IN=>'a'x1}, {OUT=>&$enc(1)."\n"}], + ['inout2', {IN=>'a'x2}, {OUT=>&$enc(2)."\n"}], + ['inout3', {IN=>'a'x3}, {OUT=>&$enc(3)."\n"}], + ['inout4', {IN=>'a'x4}, {OUT=>&$enc(4)."\n"}], + ['inout5', {IN=>'a'x5}, {OUT=>&$enc(5)."\n"}], + ['wrap', '--wrap 0', {IN=>'a'}, {OUT=>&$enc(1)}], + ['wrap-zero', '--wrap 08', {IN=>'a'}, {OUT=>&$enc(1)."\n"}], + ['wrap5-39', '--wrap=5', {IN=>'a' x 39}, {OUT=>wrap &$enc(39),5}], + ['wrap5-40', '--wrap=5', {IN=>'a' x 40}, {OUT=>wrap &$enc(40),5}], + ['wrap5-41', '--wrap=5', {IN=>'a' x 41}, {OUT=>wrap &$enc(41),5}], + ['wrap5-42', '--wrap=5', {IN=>'a' x 42}, {OUT=>wrap &$enc(42),5}], + ['wrap5-43', '--wrap=5', {IN=>'a' x 43}, {OUT=>wrap &$enc(43),5}], + ['wrap5-44', '--wrap=5', {IN=>'a' x 44}, {OUT=>wrap &$enc(44),5}], + ['wrap5-45', '--wrap=5', {IN=>'a' x 45}, {OUT=>wrap &$enc(45),5}], + ['wrap5-46', '--wrap=5', {IN=>'a' x 46}, {OUT=>wrap &$enc(46),5}], + + ['wrap-bad-1', '-w0x0', {IN=>''}, {OUT=>""}, + {ERR_SUBST => 's/base..:/base..:/'}, + {ERR => "base..: invalid wrap size: '0x0'\n"}, {EXIT => 1}], + ['wrap-bad-2', '-w1k', {IN=>''}, {OUT=>""}, + {ERR_SUBST => 's/base..:/base..:/'}, + {ERR => "base..: invalid wrap size: '1k'\n"}, {EXIT => 1}], + ['wrap-bad-3', '-w-1', {IN=>''}, {OUT=>""}, + {ERR_SUBST => 's/base..:/base..:/'}, + {ERR => "base..: invalid wrap size: '-1'\n"}, {EXIT => 1}], + + ['buf-1', '--decode', {IN=>&$enc(1)}, {OUT=>'a' x 1}], + ['buf-2', '--decode', {IN=>&$enc(2)}, {OUT=>'a' x 2}], + ['buf-3', '--decode', {IN=>&$enc(3)}, {OUT=>'a' x 3}], + ['buf-4', '--decode', {IN=>&$enc(4)}, {OUT=>'a' x 4}], + # 4KB worth of input. + ['buf-4k0', '--decode', {IN=>&$enc(3072+0)}, {OUT=>'a' x (3072+0)}], + ['buf-4k1', '--decode', {IN=>&$enc(3072+1)}, {OUT=>'a' x (3072+1)}], + ['buf-4k2', '--decode', {IN=>&$enc(3072+2)}, {OUT=>'a' x (3072+2)}], + ['buf-4k3', '--decode', {IN=>&$enc(3072+3)}, {OUT=>'a' x (3072+3)}], + ['buf-4km1','--decode', {IN=>&$enc(3072-1)}, {OUT=>'a' x (3072-1)}], + ['buf-4km2','--decode', {IN=>&$enc(3072-2)}, {OUT=>'a' x (3072-2)}], + ['buf-4km3','--decode', {IN=>&$enc(3072-3)}, {OUT=>'a' x (3072-3)}], + ['buf-4km4','--decode', {IN=>&$enc(3072-4)}, {OUT=>'a' x (3072-4)}], + + # Exercise the case in which the final base-64 byte is + # in a buffer all by itself. + ['b4k-1', '--decode', {IN=>$a3k_nl[1]}, {OUT=>'a' x (3072+0)}], + ['b4k-2', '--decode', {IN=>$a3k_nl[2]}, {OUT=>'a' x (3072+0)}], + ['b4k-3', '--decode', {IN=>$a3k_nl[3]}, {OUT=>'a' x (3072+0)}], + + ['ext-op1', 'a b', {IN=>''}, {EXIT=>1}, + {ERR => "$prog: extra operand 'b'\n" . $try_help}], + # Again, with more option arguments + ['ext-op2', '-di --wrap=40 a b', {IN=>''}, {EXIT=>1}, + {ERR => "$prog: extra operand 'b'\n" . $try_help}], + ); + + if ($prog eq "base64") + { + push @Tests, ( + ['baddecode', '--decode', {IN=>'a'}, {OUT=>""}, + {ERR_SUBST => 's/.*: invalid input//'}, {ERR => "\n"}, {EXIT => 1}], + ['baddecode2', '--decode', {IN=>'ab'}, {OUT=>"i"}, + {ERR_SUBST => 's/.*: invalid input//'}, {ERR => "\n"}, {EXIT => 1}], + ['baddecode3', '--decode', {IN=>'Zzz'}, {OUT=>"g<"}, + {ERR_SUBST => 's/.*: invalid input//'}, {ERR => "\n"}, {EXIT => 1}], + ['baddecode4', '--decode', {IN=>'Zz='}, {OUT=>"g"}, + {ERR_SUBST => 's/.*: invalid input//'}, {ERR => "\n"}, {EXIT => 1}], + ['baddecode5', '--decode', {IN=>'Z==='}, {OUT=>""}, + {ERR_SUBST => 's/.*: invalid input//'}, {ERR => "\n"}, {EXIT => 1}] + ); + } + + # For each non-failing test, create a --decode test using the + # expected output as input. Also, add tests inserting newlines. + my @new; + foreach my $t (@Tests) + { + my $exit_val; + my $in; + my @out; + + # If the test has a single option of "--decode", then skip it. + !ref $t->[1] && $t->[1] eq '--decode' + and next; + + foreach my $e (@$t) + { + ref $e && ref $e eq 'HASH' + or next; + defined $e->{EXIT} + and $exit_val = $e->{EXIT}; + defined $e->{IN} + and $in = $e->{IN}; + if (defined $e->{OUT}) + { + my $t = $e->{OUT}; + push @out, $t; + my $len = length $t; + foreach my $i (0..$len) + { + my $u = $t; + substr ($u, $i, 0) = "\n"; + push @out, $u; + 10 <= $i + and last; + } + } + } + $exit_val + and next; + + my $i = 0; + foreach my $o (@out) + { + push @new, ["d$i-$t->[0]", '--decode', {IN => $o}, {OUT => $in}]; + ++$i; + } + } + push @Tests, @new; +} + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $fail = 0; +foreach my $prog (qw(base32 base64)) + { + $enc = $prog eq "base32" ? \&enc32 : \&enc64; + + # Construct an encoded string of length 4KB, using 3K "a"s. + $a3k = &$enc(3072); + @a3k_nl = (); + # A few copies, each with different number of newlines at the start. + for my $k (0..3) + { + (my $t = $a3k) =~ s/^/"\n"x $k/e; + push @a3k_nl, $t; + } + + gen_tests($prog); + + $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); + if ($fail != 0) + { + last; + } + } + +exit $fail; diff --git a/tests/misc/basename.pl b/tests/misc/basename.pl new file mode 100755 index 0000000..4f76a21 --- /dev/null +++ b/tests/misc/basename.pl @@ -0,0 +1,92 @@ +#!/usr/bin/perl +# Test basename. +# Copyright (C) 2006-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; +use File::stat; + +(my $program_name = $0) =~ s|.*/||; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my $stat_single = stat('/'); +my $stat_double = stat('//'); +my $double_slash = ($stat_single->dev == $stat_double->dev + && $stat_single->ino == $stat_double->ino) ? '/' : '//'; + +my $prog = 'basename'; + +my @Tests = + ( + ['fail-1', {ERR => "$prog: missing operand\n" + . "Try '$prog --help' for more information.\n"}, {EXIT => '1'}], + ['fail-2', qw(a b c), {ERR => "$prog: extra operand 'c'\n" + . "Try '$prog --help' for more information.\n"}, {EXIT => '1'}], + + ['a', qw(d/f), {OUT => 'f'}], + ['b', qw(/d/f), {OUT => 'f'}], + ['c', qw(d/f/), {OUT => 'f'}], + ['d', qw(d/f//), {OUT => 'f'}], + ['e', qw(f), {OUT => 'f'}], + ['f', qw(/), {OUT => '/'}], + ['g', qw(//), {OUT => "$double_slash"}], + ['h', qw(///), {OUT => '/'}], + ['i', qw(///a///), {OUT => 'a'}], + ['j', qw(''), {OUT => ''}], + ['k', qw(aa a), {OUT => 'a'}], + ['l', qw(-a a b), {OUT => "a\nb"}], + ['m', qw(-s a aa ba ab), {OUT => "a\nb\nab"}], + ['n', qw(a-a -a), {OUT => 'a'}], + ['1', qw(f.s .s), {OUT => 'f'}], + ['2', qw(fs s), {OUT => 'f'}], + ['3', qw(fs fs), {OUT => 'fs'}], + ['4', qw(fs/ s), {OUT => 'f'}], + ['5', qw(dir/file.suf .suf), {OUT => 'file'}], + ['6', qw(// /), {OUT => "$double_slash"}], + ['7', qw(// //), {OUT => "$double_slash"}], + ['8', qw(fs x), {OUT => 'fs'}], + ['9', qw(fs ''), {OUT => 'fs'}], + ['10', qw(fs/ s/), {OUT => 'fs'}], + + # Exercise -z option. + ['z0', qw(-z a), {OUT => "a\0"}], + ['z1', qw(--zero a), {OUT => "a\0"}], + ['z2', qw(-za a b), {OUT => "a\0b\0"}], + ['z3', qw(-z ba a), {OUT => "b\0"}], + ['z4', qw(-z -s a ba), {OUT => "b\0"}], + ); + +# Append a newline to end of each expected 'OUT' string. +# Skip -z tests, i.e., those whose 'OUT' string has a trailing '\0'. +my $t; +foreach $t (@Tests) + { + my $arg1 = $t->[1]; + my $e; + foreach $e (@$t) + { + $e->{OUT} = "$e->{OUT}\n" + if ref $e eq 'HASH' and exists $e->{OUT} + and not $e->{OUT} =~ /\0$/; + } + } + +my $save_temps = $ENV{SAVE_TEMPS}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/basenc.pl b/tests/misc/basenc.pl new file mode 100755 index 0000000..de20d2d --- /dev/null +++ b/tests/misc/basenc.pl @@ -0,0 +1,292 @@ +#!/usr/bin/perl +# Exercise basenc. + +# Copyright (C) 2006-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +# This test exercises the various encoding (other than base64/32). +# It also does not test the general options (e.g. --wrap), as that code is +# shared and tested in base64. + +use strict; + +(my $program_name = $0) =~ s|.*/||; +my $prog = 'basenc'; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + + +my $base64_in = "\x54\x0f\xdc\xf0\x0f\xaf\x4a"; +my $base64_out = "VA/c8A+vSg=="; +my $base64url_out = $base64_out; +$base64url_out =~ y|+/|-_|; +my $base64url_out_nl = $base64url_out; +$base64url_out_nl =~ s/(..)/\1\n/g; # add newline every two characters + + +# Bug 49741: +# The input is 'abc' in base64, in an 8K buffer (larger than 1024*5, +# the buffer size which caused the bug). +my $base64_bug49741_in = "YWJj" x 2000 ; +my $base64_bug49741_out = "abc" x 2000 ; + + +my $base32_in = "\xfd\xd8\x07\xd1\xa5"; +my $base32_out = "7XMAPUNF"; +my $x = $base32_out; +$x =~ y|ABCDEFGHIJKLMNOPQRSTUVWXYZ234567|0123456789ABCDEFGHIJKLMNOPQRSTUV|; +my $base32hex_out = $x; + +# base32 with padding and newline +my $base32_in2 = "\xFF\x00"; +my $base32_out2 = "74AA===="; +$x = $base32_out2; +$x =~ y|ABCDEFGHIJKLMNOPQRSTUVWXYZ234567|0123456789ABCDEFGHIJKLMNOPQRSTUV|; +my $base32hex_out2 = $x; +my $base32hex_out2_nl = $x; +$base32hex_out2_nl =~ s/(...)/\1\n/g; # Add newline every 3 characters + +my $base16_in = "\xfd\xd8\x07\xd1\xa5"; +my $base16_out = "FDD807D1A5"; + +my $z85_in = "\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B"; +my $z85_out = 'HelloWorld'; + +my $base2lsbf_ab = "1000011001000110"; +my $base2lsbf_ab_nl = $base2lsbf_ab; +$base2lsbf_ab_nl =~ s/(...)/\1\n/g; # Add newline every 3 characters +my $base2msbf_ab = "0110000101100010"; +my $base2msbf_ab_nl = $base2msbf_ab; +$base2msbf_ab_nl =~ s/(...)/\1\n/g; # Add newline every 3 characters + +my $try_help = "Try '$prog --help' for more information.\n"; + +my @Tests = +( + # These are mainly for higher coverage + ['help', '--help', {IN=>''}, {OUT=>""}, {OUT_SUBST=>'s/.*//sm'}], + + # Typical message is " unrecognized option '--foobar'", but on + # Open/NetBSD it is " unknown option -- foobar". + ['error', '--foobar', {IN=>''}, {OUT=>""}, {EXIT=>1}, + {ERR=>"$prog: foobar\n" . $try_help }, + {ERR_SUBST=>"s/(unrecognized|unknown) option [-' ]*foobar[' ]*/foobar/"}], + + ['noenc', '', {IN=>''}, {EXIT=>1}, + {ERR=>"$prog: missing encoding type\n" . $try_help }], + + ['extra', '--base64 A B', {IN=>''}, {EXIT=>1}, + {ERR=>"$prog: extra operand 'B'\n" . $try_help}], + + + ['empty1', '--base64', {IN=>''}, {OUT=>""}], + ['empty2', '--base64url', {IN=>''}, {OUT=>""}], + ['empty3', '--base32', {IN=>''}, {OUT=>""}], + ['empty4', '--base32hex', {IN=>''}, {OUT=>""}], + ['empty5', '--base16', {IN=>''}, {OUT=>""}], + ['empty6', '--base2msbf', {IN=>''}, {OUT=>""}], + ['empty7', '--base2lsbf', {IN=>''}, {OUT=>""}], + ['empty8', '--z85', {IN=>''}, {OUT=>""}], + + + + + ['b64_1', '--base64', {IN=>$base64_in}, {OUT=>$base64_out}], + ['b64_2', '--base64 -d', {IN=>$base64_out}, {OUT=>$base64_in}], + ['b64_3', '--base64 -d -i', {IN=>'&'.$base64_out},{OUT=>$base64_in}], + + ['b64u_1', '--base64url', {IN=>$base64_in}, {OUT=>$base64url_out}], + ['b64u_2', '--base64url -d', {IN=>$base64url_out}, {OUT=>$base64_in}], + ['b64u_3', '--base64url -di', {IN=>'&'.$base64url_out} , {OUT=>$base64_in}], + ['b64u_4', '--base64url -di', {IN=>'/'.$base64url_out.'+'},{OUT=>$base64_in}], + ['b64u_5', '--base64url -d', {IN=>$base64url_out_nl}, {OUT=>$base64_in}], + ['b64u_6', '--base64url -di', {IN=>$base64url_out_nl}, {OUT=>$base64_in}], + # ensure base64url fails to decode base64 input with "+" and "/" + ['b64u_7', '--base64url -d', {IN=>$base64_out}, + {EXIT=>1}, {ERR=>"$prog: invalid input\n"}], + + ['b64_bug49741', '--base64 -d', {IN=>$base64_bug49741_in}, + {OUT=>$base64_bug49741_out}], + + + + ['b32_1', '--base32', {IN=>$base32_in}, {OUT=>$base32_out}], + ['b32_2', '--base32 -d', {IN=>$base32_out}, {OUT=>$base32_in}], + ['b32_3', '--base32 -d -i', {IN=>'&'.$base32_out},{OUT=>$base32_in}], + ['b32_4', '--base32', {IN=>$base32_in2}, {OUT=>$base32_out2}], + ['b32_5', '--base32 -d', {IN=>$base32_out2}, {OUT=>$base32_in2}], + ['b32_6', '--base32 -d -i', {IN=>$base32_out2}, {OUT=>$base32_in2}], + + + + ['b32h_1', '--base32hex', {IN=>$base32_in}, {OUT=>$base32hex_out}], + ['b32h_2', '--base32hex -d', {IN=>$base32hex_out}, {OUT=>$base32_in}], + ['b32h_3', '--base32hex -d -i', {IN=>'/'.$base32hex_out}, {OUT=>$base32_in}], + ['b32h_4', '--base32hex -d -i', {IN=>'W'.$base32hex_out}, {OUT=>$base32_in}], + ['b32h_5', '--base32hex -d', {IN=>$base32hex_out.'W'}, , {OUT=>$base32_in}, + {EXIT=>1}, {ERR=>"$prog: invalid input\n"}], + ['b32h_6', '--base32hex -d', {IN=>$base32hex_out.'/'}, {OUT=>$base32_in}, + {EXIT=>1}, {ERR=>"$prog: invalid input\n"}], + ['b32h_7', '--base32hex', {IN=>$base32_in2}, {OUT=>$base32hex_out2}], + ['b32h_8', '--base32hex -d', {IN=>$base32hex_out2}, {OUT=>$base32_in2}], + ['b32h_9', '--base32hex -di', {IN=>$base32hex_out2}, {OUT=>$base32_in2}], + ['b32h_10', '--base32hex -d', {IN=>$base32hex_out2_nl}, {OUT=>$base32_in2}], + ['b32h_11', '--base32hex -di', {IN=>$base32hex_out2_nl}, {OUT=>$base32_in2}], + + + + ['b16_1', '--base16', {IN=>$base16_in}, {OUT=>$base16_out}], + ['b16_2', '--base16 -d', {IN=>$base16_out}, {OUT=>$base16_in}], + ['b16_3', '--base16 -d -i', {IN=>'&'. $base16_out}, {OUT=>$base16_in}], + ['b16_4', '--base16 -d -i', {IN=>$base16_out.'G'}, {OUT=>$base16_in}], + ['b16_5', '--base16 -d', {IN=>'.'}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b16_6', '--base16 -d', {IN=>'='}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b16_7', '--base16 -d', {IN=>'G'}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b16_8', '--base16 -d', {IN=>"AB\nCD"}, {OUT=>"\xAB\xCD"}], + + + + ['b2m_1', '--base2m', {IN=>"\xC1"}, {OUT=>"11000001"}], + ['b2m_2', '--base2m -d', {IN=>'11000001'}, {OUT=>"\xC1"}], + ['b2m_3', '--base2m -d', {IN=>"110\n00001"}, {OUT=>"\xC1"}], + ['b2m_4', '--base2m -di', {IN=>"110x00001"}, {OUT=>"\xC1"}], + ['b2m_5', '--base2m -d', {IN=>"110x00001"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b2m_6', '--base2m -d', {IN=>"11000001x"}, {OUT=>"\xC1"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b2m_7', '--base2m -d', {IN=>"1"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b2m_8', '--base2m -d', {IN=>"1000100"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b2m_9', '--base2m -d', {IN=>"100010000000000"}, {OUT=>"\x88"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b2m_10','--base2m', {IN=>"ab"}, {OUT=>$base2msbf_ab}], + ['b2m_11','--base2m -d', {IN=>$base2msbf_ab}, {OUT=>"ab"}], + ['b2m_12','--base2m -d', {IN=>$base2msbf_ab_nl}, {OUT=>"ab"}], + + + ['b2l_1', '--base2l', {IN=>"\x83"}, {OUT=>"11000001"}], + ['b2l_2', '--base2l -d', {IN=>'11000001'}, {OUT=>"\x83"}], + ['b2l_3', '--base2l -d', {IN=>"110\n00001"}, {OUT=>"\x83"}], + ['b2l_4', '--base2l -di', {IN=>"110x00001"}, {OUT=>"\x83"}], + ['b2l_5', '--base2l -d', {IN=>"110x00001"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b2l_6', '--base2l -d', {IN=>"11000001x"}, {OUT=>"\x83"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b2l_7', '--base2l -d', {IN=>"1"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b2l_8', '--base2l -d', {IN=>"1000100"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b2l_9', '--base2l -d', {IN=>"100010000000000"}, {OUT=>"\x11"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['b2l_10','--base2l', {IN=>"ab"}, {OUT=>$base2lsbf_ab}], + ['b2l_11','--base2l -d', {IN=>$base2lsbf_ab}, {OUT=>"ab"}], + ['b2l_12','--base2l -d', {IN=>$base2lsbf_ab_nl}, {OUT=>"ab"}], + + + + + + ['z85_1', '--z85', {IN=>$z85_in}, {OUT=>$z85_out}], + ['z85_2', '--z85 -d', {IN=>$z85_out}, {OUT=>$z85_in}], + ['z85_3', '--z85 -d -i', {IN=>'~'. $z85_out}, {OUT=>$z85_in}], + ['z85_4', '--z85 -d -i', {IN=>' '. $z85_out}, {OUT=>$z85_in}], + ['z85_5', '--z85 -d', {IN=>'%j$qP'}, {OUT=>"\xFF\xDD\xBB\x99"}], + ['z85_6', '--z85 -d -i', {IN=>'%j~$qP'}, {OUT=>"\xFF\xDD\xBB\x99"}], + + # z85 encoding require input to be multiple of 5 octets + ['z85_7', '--z85 -d', {IN=>'hello'}, {OUT=>"5jXu"}], + ['z85_8', '--z85 -d', {IN=>'helloX'}, {OUT=>"5jXu"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_9', '--z85 -d', {IN=>"he\nl\nlo"}, {OUT=>"5jXu"}], + + # Invalid input characters (space ~ ") + ['z85_10', '--z85 -d', {IN=>' j$qP'}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_11', '--z85 -d', {IN=>'%j$q~'}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_12', '--z85 -d', {IN=>'%j$"P'}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + + # Invalid length (binary input must be a multiple of 4 octets, + # z85-encoded input must be a multiple of 5 octets) + ['z85_20', '--z85', {IN=>'A'}, {EXIT=>1}, + {ERR=>"$prog: invalid input (length must be multiple of 4 characters)\n"}], + ['z85_21', '--z85', {IN=>'AB'}, {EXIT=>1}, + {ERR=>"$prog: invalid input (length must be multiple of 4 characters)\n"}], + ['z85_22', '--z85', {IN=>'ABC'}, {EXIT=>1}, + {ERR=>"$prog: invalid input (length must be multiple of 4 characters)\n"}], + ['z85_23', '--z85', {IN=>'ABCD'}, {OUT=>'k%^}b'}], + ['z85_24', '--z85', {IN=>'ABCDE'}, {EXIT=>1}, + {ERR=>"$prog: invalid input (length must be multiple of 4 characters)\n"}], + + ['z85_30', '--z85 -d', {IN=>'A'}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_31', '--z85 -d', {IN=>'AB'}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_32', '--z85 -d', {IN=>'ABC'}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_33', '--z85 -d', {IN=>'ABCD'}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_34', '--z85 -d', {IN=>'ABCDE'}, {OUT=>"\x71\x61\x9e\xb6"}], + ['z85_35', '--z85 -d', {IN=>'ABCDEF'},{OUT=>"\x71\x61\x9e\xb6"}, + {EXIT=>1}, {ERR=>"$prog: invalid input\n"}], + + # largest possible value + ['z85_40', '--z85', {IN=>"\xFF\xFF\xFF\xFF"},{OUT=>"%nSc0"}], + ['z85_41', '--z85 -d', {IN=>"%nSc0"}, {OUT=>"\xFF\xFF\xFF\xFF"}], + # Invalid encoded data - will decode to more than 0xFFFFFFFF + ['z85_42', '--z85 -d', {IN=>"%nSc1"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_43', '--z85 -d', {IN=>"%nSd0"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_44', '--z85 -d', {IN=>"%nTc0"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_45', '--z85 -d', {IN=>"%oSc0"}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_46', '--z85 -d', {IN=>'$nSc0'}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], + ['z85_47', '--z85 -d', {IN=>'#0000'}, {EXIT=>1}, + {ERR=>"$prog: invalid input\n"}], +); + +# Prepend the command line argument and append a newline to end +# of each expected 'OUT' string. +my $t; + +Test: +foreach $t (@Tests) + { + foreach my $e (@$t) + { + ref $e && ref $e eq 'HASH' && defined $e->{OUT_SUBST} + and next Test; + } + + push @$t, {OUT_SUBST=>'s/\n$//s'}; + } + + + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); + +exit $fail; diff --git a/tests/misc/close-stdout.sh b/tests/misc/close-stdout.sh new file mode 100755 index 0000000..4bff9e2 --- /dev/null +++ b/tests/misc/close-stdout.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# Ensure that several programs work fine, even with stdout initially closed. +# This is effectively a test of closeout.c's close_stdout function. + +# Copyright (C) 2004-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ rm + +p=$abs_top_builddir + + +# Ensure these exit successfully, even though stdout is closed, +# because they generate no output. +touch a +cp a b >&- || fail=1 +test -f b || fail=1 +chmod o-w . >&- || fail=1 +ln a c >&- || fail=1 +rm c >&- || fail=1 +mkdir d >&- || fail=1 +mv d e >&- || fail=1 +rmdir e >&- || fail=1 +touch e >&- || fail=1 +sleep 0 >&- || fail=1 +"$p/src/true" >&- || fail=1 +"$p/src/printf" '' >&- || fail=1 + +# If >&- works, ensure these fail, because stdout is closed and they +# *do* generate output. >&- apparently does not work in HP-UX 11.23. +# This test is ineffective unless /dev/stdout also works. +if "$p/src/test" -w /dev/stdout >/dev/null && + "$p/src/test" ! -w /dev/stdout >&-; then + returns_ 1 "$p/src/printf" 'foo' >&- 2>/dev/null || fail=1 + returns_ 1 cp --verbose a b >&- 2>/dev/null || fail=1 + rm -Rf tmpfile-?????? || fail=1 + returns_ 1 mktemp tmpfile-XXXXXX >&- 2>/dev/null || fail=1 + returns_ 1 mktemp tmpfile-XXXXXX -q >&- 2>/dev/null || fail=1 + case $(echo tmpfile-??????) in 'tmpfile-??????') ;; *) fail=1 ;; esac +fi + +# Likewise for /dev/full, if /dev/full works. +if test -w /dev/full && test -c /dev/full; then + returns_ 1 "$p/src/printf" 'foo' >/dev/full 2>/dev/null || fail=1 + returns_ 1 cp --verbose a b >/dev/full 2>/dev/null || fail=1 + rm -Rf tmpdir-?????? || fail=1 + returns_ 1 mktemp -d tmpdir-XXXXXX >/dev/full 2>/dev/null || fail=1 + returns_ 1 mktemp -d -q tmpdir-XXXXXX >/dev/full 2>/dev/null || fail=1 + case $(echo tmpfile-??????) in 'tmpfile-??????') ;; *) fail=1 ;; esac +fi + +Exit $fail diff --git a/tests/misc/comm.pl b/tests/misc/comm.pl new file mode 100755 index 0000000..aaaf465 --- /dev/null +++ b/tests/misc/comm.pl @@ -0,0 +1,186 @@ +#!/usr/bin/perl +# -*- perl -*- +# Test comm + +# Copyright (C) 2008-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +require 5.003; +use strict; + +(my $program_name = $0) =~ s|.*/||; + +my $prog = 'comm'; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my @inputs = ({IN=>{a=>"1\n3\n3\n3"}}, {IN=>{b=>"2\n2\n3\n3\n3"}}); +my @zinputs = ({IN=>{za=>"1\0003\0003\0003"}}, + {IN=>{zb=>"2\0002\0003\0003\0003"}}); + +my @Tests = + ( + # basic operation + ['basic', @inputs, {OUT=>"1\n\t2\n\t2\n\t\t3\n\t\t3\n\t\t3\n"} ], + ['zbasic', '-z', @zinputs, {OUT=>"1\0\t2\0\t2\0\t\t3\0\t\t3\0\t\t3\0"} ], + + # suppress lines unique to file 1 + ['opt-1', '-1', @inputs, {OUT=>"2\n2\n\t3\n\t3\n\t3\n"} ], + ['zopt-1', '-z', '-1', @zinputs, {OUT=>"2\0002\000\t3\000\t3\000\t3\000"} ], + + # suppress lines unique to file 2 + ['opt-2', '-2', @inputs, {OUT=>"1\n\t3\n\t3\n\t3\n"} ], + ['zopt-2', '-z', '-2', @zinputs, {OUT=>"1\000\t3\000\t3\000\t3\000"} ], + + # suppress lines that appear in both files + ['opt-3', '-3', @inputs, {OUT=>"1\n\t2\n\t2\n"} ], + ['zopt-3', '-z', '-3', @zinputs, {OUT=>"1\000\t2\000\t2\000"} ], + + # suppress lines unique to file 1 and lines unique to file 2 + ['opt-12', '-1', '-2', @inputs, {OUT=>"3\n3\n3\n"} ], + ['zopt-12', '-12z', @zinputs, {OUT=>"3\0003\0003\000"} ], + + # suppress lines unique to file 1 and those that appear in both files + ['opt-13', '-1', '-3', @inputs, {OUT=>"2\n2\n"} ], + ['zopt-13', '-13z', @zinputs, {OUT=>"2\0002\000"} ], + + # suppress lines unique to file 2 and those that appear in both files + ['opt-23', '-2', '-3', @inputs, {OUT=>"1\n"} ], + ['zopt-23', '-23z', @zinputs, {OUT=>"1\000"} ], + + # suppress all output + ['opt-123', '-1', '-2', '-3', @inputs, {OUT=>""} ], + + # show summary: 1 only in file1, 2 only in file2, 3 in both files + ['total-all', '--total', @inputs, {OUT=>"1\n\t2\n\t2\n\t\t3\n\t\t3\n\t\t3\n" + . "1\t2\t3\ttotal\n"} ], + + # show summary only, suppressing regular output + ['total-123', '--total', '-123', @inputs, {OUT=>"1\t2\t3\ttotal\n"} ], + + # invalid missing command line argument (1) + ['missing-arg1', $inputs[0], {EXIT=>1}, + {ERR => "$prog: missing operand after 'a'\n" + . "Try '$prog --help' for more information.\n"}], + + # invalid missing command line argument (both) + ['missing-arg2', {EXIT=>1}, + {ERR => "$prog: missing operand\n" + . "Try '$prog --help' for more information.\n"}], + + # invalid extra command line argument + ['extra-arg', @inputs, 'no-such', {EXIT=>1}, + {ERR => "$prog: extra operand 'no-such'\n" + . "Try '$prog --help' for more information.\n"}], + + # out-of-order input + ['ooo', {IN=>{a=>"1\n3"}}, {IN=>{b=>"3\n2"}}, {EXIT=>1}, + {OUT => "1\n\t\t3\n\t2\n"}, + {ERR => "$prog: file 2 is not in sorted order\n" + . "$prog: input is not in sorted order\n"}], + + # out-of-order input, fatal + ['ooo2', '--check-order', {IN=>{a=>"1\n3"}}, {IN=>{b=>"3\n2"}}, {EXIT=>1}, + {OUT => "1\n\t\t3\n"}, + {ERR => "$prog: file 2 is not in sorted order\n"}], + + # out-of-order input, ignored + ['ooo3', '--nocheck-order', {IN=>{a=>"1\n3"}}, {IN=>{b=>"3\n2"}}, + {OUT => "1\n\t\t3\n\t2\n"}], + + # both inputs out-of-order + ['ooo4', {IN=>{a=>"3\n1\n0"}}, {IN=>{b=>"3\n2\n0"}}, {EXIT=>1}, + {OUT => "\t\t3\n1\n0\n\t2\n\t0\n"}, + {ERR => "$prog: file 1 is not in sorted order\n". + "$prog: file 2 is not in sorted order\n" + . "$prog: input is not in sorted order\n"}], + + # both inputs out-of-order on last pair + ['ooo5', {IN=>{a=>"3\n1"}}, {IN=>{b=>"3\n2"}}, {EXIT=>1}, + {OUT => "\t\t3\n1\n\t2\n"}, + {ERR => "$prog: file 1 is not in sorted order\n". + "$prog: file 2 is not in sorted order\n" + . "$prog: input is not in sorted order\n"}], + + # first input out-of-order extended + ['ooo5b', {IN=>{a=>"0\n3\n1"}}, {IN=>{b=>"2\n3"}}, {EXIT=>1}, + {OUT => "0\n\t2\n\t\t3\n1\n"}, + {ERR => "$prog: file 1 is not in sorted order\n" + . "$prog: input is not in sorted order\n"}], + + # second input out-of-order extended + ['ooo5c', {IN=>{a=>"0\n3"}}, {IN=>{b=>"2\n3\n1"}}, {EXIT=>1}, + {OUT => "0\n\t2\n\t\t3\n\t1\n"}, + {ERR => "$prog: file 2 is not in sorted order\n" + . "$prog: input is not in sorted order\n"}], + + # both inputs out-of-order, but fully pairable + ['ooo6', {IN=>{a=>"2\n1\n0"}}, {IN=>{b=>"2\n1\n0"}}, {EXIT=>0}, + {OUT => "\t\t2\n\t\t1\n\t\t0\n"}], + + # both inputs out-of-order, fully pairable, but forced to fail + ['ooo7', '--check-order', {IN=>{a=>"2\n1\n0"}}, {IN=>{b=>"2\n1\n0"}}, + {EXIT=>1}, + {OUT => "\t\t2\n"}, + {ERR => "$prog: file 1 is not in sorted order\n"}], + + # out-of-order, line 2 is a prefix of line 1 + # until coreutils-7.2, this test would fail -- no disorder detected + ['ooo-prefix', '--check-order', {IN=>{a=>"Xa\nX\n"}}, {IN=>{b=>""}}, + {EXIT=>1}, + {OUT => "Xa\n"}, + {ERR => "$prog: file 1 is not in sorted order\n"}], + + # alternate delimiter: ',' + ['delim-comma', '--output-delimiter=,', @inputs, + {OUT=>"1\n,2\n,2\n,,3\n,,3\n,,3\n"} ], + + # two-character alternate delimiter: '++' + ['delim-2char', '--output-delimiter=++', @inputs, + {OUT=>"1\n++2\n++2\n++++3\n++++3\n++++3\n"} ], + + # NUL delimiter + ['delim-empty', '--output-delimiter=', @inputs, + {OUT=>"1\n\0002\n\0002\n\000\0003\n\000\0003\n\000\0003\n"} ], + ['zdelim-empty', '-z', '-z --output-delimiter=', @zinputs, + {OUT=>"1\000\0002\000\0002\000\000\0003\000\000\0003\000\000\0003\000"} ], + ['total-delim-empty', '--total --output-delimiter=', @inputs, + {OUT=>"1\n\0002\n\0002\n\000\0003\n\000\0003\n\000\0003\n" + . "1\0002\0003\000total\n"} ], + + # invalid dual delimiter + ['delim-dual', '--output-delimiter=,', '--output-delimiter=+', @inputs, + {EXIT=>1}, {ERR => "$prog: multiple output delimiters specified\n"}], + + # valid dual delimiter specification + ['delim-dual2', '--output-delimiter=,', '--output-delimiter=,', @inputs, + {OUT=>"1\n,2\n,2\n,,3\n,,3\n,,3\n"} ], + + # show summary, zero-terminated + ['totalz-all', '--total', '-z', @zinputs, + {OUT=>"1\000\t2\000\t2\000\t\t3\000\t\t3\000\t\t3\000" + . "1\t2\t3\ttotal\000"} ], + + # show summary only (-123), zero-terminated and with ',' as delimiter + ['totalz-123', '--total', '-z123', '--output-delimiter=,', @zinputs, + {OUT=>"1,2,3,total\000"} ], + ); + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/coreutils.sh b/tests/misc/coreutils.sh new file mode 100755 index 0000000..ffcce84 --- /dev/null +++ b/tests/misc/coreutils.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# Verify behavior of separate coreutils multicall binary + +# Copyright (C) 2014-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ coreutils + +test -s "$abs_top_builddir/src/coreutils.h" \ + || skip_ "multicall binary is disabled" + +# Yes outputs all its params so is good to verify argv manipulations +echo 'y' > exp +coreutils --coreutils-prog=yes | head -n10 | uniq > out +compare exp out || fail=1 + +# Ensure if incorrect program passed, we diagnose +echo "coreutils: unknown program 'blah'" > exp +coreutils --coreutils-prog='blah' --help 2>err && fail=1 +compare exp err || fail=1 + +Exit $fail diff --git a/tests/misc/dircolors.pl b/tests/misc/dircolors.pl new file mode 100755 index 0000000..f33e893 --- /dev/null +++ b/tests/misc/dircolors.pl @@ -0,0 +1,76 @@ +#!/usr/bin/perl +# Simple dircolors tests. + +# Copyright (C) 1998-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; + +(my $program_name = $0) =~ s|.*/||; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my @Tests = + ( + ['a', '-b', {IN => {k => "exec\n"}}, + {ERR => "dircolors: k:1: invalid line; missing second token\n"}, + {EXIT => 1}], + ['quote', '-b', {IN => "exec 'echo Hello;:'\n"}, + {OUT => "LS_COLORS='ex='\\''echo Hello;\\:'\\'':';\n" + . "export LS_COLORS\n"}], + ['other-wr', '-b', {IN => "owt 40;33\n"}, + {OUT => "LS_COLORS='tw=40;33:';\nexport LS_COLORS\n"}], + ['term-1', '-b', {IN => "TERM none\nowt 40;33\n"}, + {OUT => "LS_COLORS='tw=40;33:';\nexport LS_COLORS\n"}], + ['term-2', '-b', {IN => "TERM non*\nowt 40;33\n"}, + {OUT => "LS_COLORS='tw=40;33:';\nexport LS_COLORS\n"}], + ['term-3', '-b', {IN => "TERM nomatch\nowt 40;33\n"}, + {OUT => "LS_COLORS='';\nexport LS_COLORS\n"}], + ['term-4', '-b', {IN => "TERM N*match\nowt 40;33\n"}, + {OUT => "LS_COLORS='';\nexport LS_COLORS\n"}], + + ['colorterm-1', '-b', {ENV => 'COLORTERM=any'}, + {IN => "COLORTERM ?*\nowt 40;33\n"}, + {OUT => "LS_COLORS='tw=40;33:';\nexport LS_COLORS\n"}], + + ['colorterm-2', '-b', {ENV => 'COLORTERM='}, + {IN => "COLORTERM ?*\nowt 40;33\n"}, + {OUT => "LS_COLORS='';\nexport LS_COLORS\n"}], + + ['print-clash1', '-p', '--print-ls', + {ERR => "dircolors: options --print-database and --print-ls-colors " . + "are mutually exclusive\n" . + "Try 'dircolors --help' for more information.\n"}, + {EXIT => 1}], + ['print-clash2', '-b', '--print-database', + {ERR => "dircolors: the options to output non shell syntax,\n" . + "and to select a shell syntax are mutually exclusive\n" . + "Try 'dircolors --help' for more information.\n"}, + {EXIT => 1}], + + ['print-ls-colors', '--print-ls-colors', {IN => "OWT 40;33\n"}, + {OUT => "\x1B[40;33mtw\t40;33\x1B[0m\n"}], + + # CAREFUL: always specify the -b option, unless explicitly testing + # for csh syntax output, or --print-ls-color output. + ); + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $prog = 'dircolors'; +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/dirname.pl b/tests/misc/dirname.pl new file mode 100755 index 0000000..e509c6b --- /dev/null +++ b/tests/misc/dirname.pl @@ -0,0 +1,72 @@ +#!/usr/bin/perl +# Test "dirname". + +# Copyright (C) 2006-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; +use File::stat; + +(my $program_name = $0) =~ s|.*/||; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my $stat_single = stat('/'); +my $stat_double = stat('//'); +my $double_slash = ($stat_single->dev == $stat_double->dev + && $stat_single->ino == $stat_double->ino) ? '/' : '//'; + +my $prog = 'dirname'; + +my @Tests = + ( + ['fail-1', {ERR => "$prog: missing operand\n" + . "Try '$prog --help' for more information.\n"}, {EXIT => '1'}], + + ['a', qw(d/f), {OUT => 'd'}], + ['b', qw(/d/f), {OUT => '/d'}], + ['c', qw(d/f/), {OUT => 'd'}], + ['d', qw(d/f//), {OUT => 'd'}], + ['e', qw(f), {OUT => '.'}], + ['f', qw(/), {OUT => '/'}], + ['g', qw(//), {OUT => "$double_slash"}], + ['h', qw(///), {OUT => '/'}], + ['i', qw(//a//), {OUT => "$double_slash"}], + ['j', qw(///a///), {OUT => '/'}], + ['k', qw(///a///b), {OUT => '///a'}], + ['l', qw(///a//b/), {OUT => '///a'}], + ['m', qw(''), {OUT => '.'}], + ['n', qw(a/b c/d), {OUT => "a\nc"}], + ); + +# Append a newline to end of each expected 'OUT' string. +my $t; +foreach $t (@Tests) + { + my $arg1 = $t->[1]; + my $e; + foreach $e (@$t) + { + $e->{OUT} = "$e->{OUT}\n" + if ref $e eq 'HASH' and exists $e->{OUT}; + } + } + +my $save_temps = $ENV{SAVE_TEMPS}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/echo.sh b/tests/misc/echo.sh new file mode 100755 index 0000000..1cd9231 --- /dev/null +++ b/tests/misc/echo.sh @@ -0,0 +1,99 @@ +#!/bin/sh +# basic tests for echo + +# Copyright (C) 2018-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +prog='env echo' + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ echo + + +# Verify the methods of specifying "Escape": +# Note 4 octal digits are allowed (unlike printf which uses up to 3) +printf '%s\n' . . . . . | tr . '\033' > exp +$prog -n -e '\x1b\n\e\n\33\n\033\n\0033\n' > out || fail=1 +compare exp out || fail=1 + +# Incomplete hex escapes are output as is (unlike printf) +printf '\\x\n' > exp +$prog -n -e '\x\n' > out || fail=1 +compare exp out || fail=1 + +# Always output -- (unlike printf) +$prog -- 'foo' > out || fail=1 +$prog -n -e -- 'foo\n' >> out || fail=1 +cat <<\EOF > exp +-- foo +-- foo +EOF +compare exp out || fail=1 + +# Ensure \c stops processing +$prog -e 'foo\n\cbar' > out || fail=1 +printf 'foo\n' > exp +compare exp out || fail=1 + +# With POSIXLY_CORRECT: +# only -n as the first (separate) option enables option processing +# -E is ignored +# escapes are processed by default +POSIXLY_CORRECT=1 $prog -n -E 'foo\n' > out || fail=1 +POSIXLY_CORRECT=1 $prog -nE 'foo' >> out || fail=1 +POSIXLY_CORRECT=1 $prog -E -n 'foo' >> out || fail=1 +POSIXLY_CORRECT=1 $prog --version >> out || fail=1 +cat <<\EOF > exp +foo +-nE foo +-E -n foo +--version +EOF +compare exp out || fail=1 + +# Further test coverage. +# Output a literal '-' (on a line itself). +$prog - > out || fail=1 +# Output a literal backslash '\', no newline. +$prog -n -e '\\' >> out || fail=1 +# Output an empty line (merely to have a newline after the previous test). +$prog >> out || fail=1 +# Test other characters escaped by a backslash: +# \a hex 07 alert, bell +# \b hex 08 backspace +# \e hex 1b escape +# \f hex 0c form feed +# \n hex 0a new line +# \r hex 0d carriage return +# \t hex 09 horizontal tab +# \v hex 0b vertical tab +# Convert output, yet checking the exit status of $prog. +{ $prog -n -e '\a\b\e\f\n\r\t\v' || touch fail; } | od -tx1 >> out || fail=1 +test '!' -f fail || fail=1 +# Output hex values which contain hexadecimal characters to test hextobin(). +# Hex values 4a through 4f are ASCII "JKLMNO". +$prog -n -e '\x4a\x4b\x4c\x4d\x4e\x4f\x4A\x4B\x4C\x4D\x4E\x4F' >> out || fail=1 +# Output another newline. +$prog >> out || fail=1 +cat <<\EOF > exp +- +\ +0000000 07 08 1b 0c 0a 0d 09 0b +0000010 +JKLMNOJKLMNO +EOF +compare exp out || fail=1 + +Exit $fail diff --git a/tests/misc/expand.pl b/tests/misc/expand.pl new file mode 100755 index 0000000..06261ac --- /dev/null +++ b/tests/misc/expand.pl @@ -0,0 +1,191 @@ +#!/usr/bin/perl +# Exercise expand. + +# Copyright (C) 2004-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; + +my $limits = getlimits (); +my $UINTMAX_OFLOW = $limits->{UINTMAX_OFLOW}; + +(my $program_name = $0) =~ s|.*/||; +my $prog = 'expand'; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my @Tests = + ( + ['t1', '--tabs=3', {IN=>"a\tb"}, {OUT=>"a b"}], + ['t2', '--tabs=3,6,9', {IN=>"a\tb\tc\td\te"}, {OUT=>"a b c d e"}], + ['t3', '--tabs="3 6 9"', {IN=>"a\tb\tc\td\te"}, {OUT=>"a b c d e"}], + # Leading space/commas are silently ignored; Mixing space/commas is allowed. + # (a side-effect of allowing direct "-3,9" parameter). + ['t4', '--tabs=", 3,6 9"', {IN=>"a\tb\tc\td\te"}, {OUT=>"a b c d e"}], + # tab stops parameter without values + ['t5', '--tabs=""', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], + ['t6', '--tabs=","', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], + ['t7', '--tabs=" "', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], + ['t8', '--tabs="/"', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], + + # Input field wider than the specified tab list + ['if', '--tabs=6,9', {IN=>"a\tbbbbbbbbbbbbb\tc"}, + {OUT=>"a bbbbbbbbbbbbb c"}], + + ['i1', '--tabs=3 -i', {IN=>"\ta\tb"}, {OUT=>" a\tb"}], + ['i2', '--tabs=3 -i', {IN=>" \ta\tb"}, {OUT=>" a\tb"}], + + # Undocumented feature: + # treat "expand -7" as "expand --tabs 7" , + # and "expand -90" as "expand --tabs 90", + ['u1', '-3', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], + ['u2', '-4 -9', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], + ['u3', '-11', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], + # Test all digits (for full code coverage) + ['u4', '-2 -6', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], + ['u5', '-7', {IN=>"a\tb"}, {OUT=>"a b"}], + ['u6', '-8', {IN=>"a\tb"}, {OUT=>"a b"}], + # This syntax is handled internally as "-3, -9" + ['u7', '-3,9', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], + + # Multiple non-empty files + ['f1', '--tabs=4', + {IN=>{"in1" => "a\tb\n"}}, {IN=>{"in2" => "c\td\n"}}, + {OUT=>"a b\nc d\n"}], + # Multiple files, first file is empty + ['f2', '--tabs=4', + {IN=>{"in1" => ""}}, {IN=>{"in2" => "c\td\n"}}, + {OUT=>"c d\n"}], + # Multiple files, second file is empty + ['f3', '--tabs=4', + {IN=>{"in1" => "a\tb\n"}}, {IN=>{"in2" => ""}}, + {OUT=>"a b\n"}], + + + # Test '\b' (back-space) - subtract one column. + # + # Note: + # In a terminal window, 'expand' will appear to erase the 'a' characters + # due to overwriting them with spaces: + # + # $ printf 'aaa\b\b\bc\td\n' + # caa d + # $ printf 'aaa\b\b\bc\td\n' | expand + # c d + # + # However the characters are all printed: + # + # $ printf 'aaa\b\b\bc\td\n' | expand | od -An -ta + # a a a bs bs bs c sp sp sp sp sp sp sp d nl + # + # If users ever report a problem with these tests and just + # copy&paste from the terminal, their report will be confusing + # (the 'a' will not appear). + # + # To see an example, enable the 'b-confusing' test, and examine the + # reported log: + # + # expand.pl: test b-confusing: stdout mismatch + # *** b-confusing.2 Fri Jun 24 15:43:21 2016 + # --- b-confusing.O Fri Jun 24 15:43:21 2016 + # *************** + # *** 1 **** + # ! c d + # --- 1 ---- + # ! c d + # + # ['b-confusing','', {IN=>"aaa\b\b\bc\td\n"}, {OUT=>"c d\n"}], + + ['b1','', {IN=>"aaa\b\b\bc\td\n"}, {OUT=>"aaa\b\b\bc d\n"}], + + # \b as first character, when column is zero + ['b2','', {IN=>"\bc\td"}, {OUT=>"\bc d"}], + + # Testing tab list adjusted due to backspaces + # ('b3' is the baseline without backspaces). + ['b3','--tabs 2,4,6,10', + {IN=>"1\t2\t3\t4\t5\n" . + "a\tb\tc\td\te\n"}, + {OUT=>"1 2 3 4 5\n" . + "a b c d e\n"}], + + # On screen this will appear the same as 'b3' + ['b4','--tabs 2,4,6,10', + {IN=>"1\t2\t3\t4\t5\n" . + "a\tbHELLO\b\b\b\b\b\tc\td\te\n"}, + {OUT=>"1 2 3 4 5\n" . + "a bHELLO\b\b\b\b\b c d e\n"}], + + # On screen on 'bHE' will appear (LLO overwritten by spaces), + # 'c' should align with 4, 'd' with 5: + # 1 2 3 4 5 + # a bHE c d e + ['b5','--tabs 2,4,6,10', + {IN=>"1\t2\t3\t4\t5\n" . + "a\tbHELLO\b\b\b\tc\td\te\n"}, + {OUT=>"1 2 3 4 5\n" . + "a bHELLO\b\b\b c d e\n"}], + + # Test the trailing '/' feature which specifies the + # tab size to use after the last specified stop + ['trail1', '--tabs=1,/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], + ['trail2', '--tabs=2,/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], + ['trail3', '--tabs=1,2,/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], + ['trail4', '--tabs=/5', {IN=>"\ta\tb"}, {OUT=>" a b"}], + ['trail5', '--tabs=//5', {IN=>"\ta\tb"}, {OUT=>" a b"}], + ['trail5a','--tabs=+/5', {IN=>"\ta\tb"}, {OUT=>" a b"}], + ['trail6', '--tabs=/,/5', {IN=>"\ta\tb"}, {OUT=>" a b"}], + ['trail7', '--tabs=,/5', {IN=>"\ta\tb"}, {OUT=>" a b"}], + ['trail8', '--tabs=1 -t/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], + ['trail9', '--tab=1,2 -t/5',{IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], + + # Test incremental trailing '+' feature which specifies that + # tab stops should continue every increment + ['incre0', '--tab=1,+5', {IN=>"+\t\ta\tb"}, {OUT=>"+ a b"}], + ['incre1', '--tabs=1,+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], + ['incre2', '--tabs=2,+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], + ['incre3', '--tabs=1,2,+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], + ['incre4', '--tabs=+5', {IN=>"\ta\tb"}, {OUT=>" a b"}], + ['incre5', '--tabs=++5', {IN=>"\ta\tb"}, {OUT=>" a b"}], + ['incre5a','--tabs=/+5', {IN=>"\ta\tb"}, {OUT=>" a b"}], + ['incre6', '--tabs=+,+5', {IN=>"\ta\tb"}, {OUT=>" a b"}], + ['incre7', '--tabs=,+5', {IN=>"\ta\tb"}, {OUT=>" a b"}], + ['incre8', '--tabs=1 -t+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], + ['incre9', '--tab=1,2 -t+5',{IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], + + + # Test errors + ['e1', '--tabs="a"', {IN=>''}, {OUT=>''}, {EXIT=>1}, + {ERR => "$prog: tab size contains invalid character(s): 'a'\n"}], + ['e2', "-t $UINTMAX_OFLOW", {IN=>''}, {OUT=>''}, {EXIT=>1}, + {ERR => "$prog: tab stop is too large '$UINTMAX_OFLOW'\n"}], + ['e3', '--tabs=0', {IN=>''}, {OUT=>''}, {EXIT=>1}, + {ERR => "$prog: tab size cannot be 0\n"}], + ['e4', '--tabs=3,3', {IN=>''}, {OUT=>''}, {EXIT=>1}, + {ERR => "$prog: tab sizes must be ascending\n"}], + ['e5', '--tabs=/3,6,8', {IN=>''}, {OUT=>''}, {EXIT=>1}, + {ERR => "$prog: '/' specifier only allowed with the last value\n"}], + ['e6', '-t/3 -t/6', {IN=>''}, {OUT=>''}, {EXIT=>1}, + {ERR => "$prog: '/' specifier only allowed with the last value\n"}], + ['e7', '--tabs=3/', {IN=>''}, {OUT=>''}, {EXIT=>1}, + {ERR => "$prog: '/' specifier not at start of number: '/'\n"}], + ); + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/false-status.sh b/tests/misc/false-status.sh new file mode 100755 index 0000000..7c4f8e2 --- /dev/null +++ b/tests/misc/false-status.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# ensure that false exits nonzero even with --help or --version +# and ensure that true exits nonzero when it can't write --help or --version + +# Copyright (C) 2003-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ false true + +returns_ 1 env false --version > /dev/null || fail=1 +returns_ 1 env false --help > /dev/null || fail=1 + +if test -w /dev/full && test -c /dev/full; then + returns_ 1 env true --version > /dev/full || fail=1 + returns_ 1 env true --help > /dev/full || fail=1 +fi + +Exit $fail diff --git a/tests/misc/fold.pl b/tests/misc/fold.pl new file mode 100755 index 0000000..a94072f --- /dev/null +++ b/tests/misc/fold.pl @@ -0,0 +1,39 @@ +#!/usr/bin/perl +# Exercise fold. + +# Copyright (C) 2003-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; + +(my $program_name = $0) =~ s|.*/||; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my @Tests = + ( + ['s1', '-w2 -s', {IN=>"a\t"}, {OUT=>"a\n\t"}], + ['s2', '-w4 -s', {IN=>"abcdef d\n"}, {OUT=>"abcd\nef d\n"}], + ['s3', '-w4 -s', {IN=>"a cd fgh\n"}, {OUT=>"a \ncd \nfgh\n"}], + ['s4', '-w4 -s', {IN=>"abc ef\n"}, {OUT=>"abc \nef\n"}], + ); + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $prog = 'fold'; +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/invalid-opt.pl b/tests/misc/invalid-opt.pl new file mode 100755 index 0000000..872cbf8 --- /dev/null +++ b/tests/misc/invalid-opt.pl @@ -0,0 +1,103 @@ +#!/usr/bin/perl +# exercise the 'invalid option' handling code in each program + +# Copyright (C) 2008-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +require 5.003; +use strict; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my %exit_status = + ( + ls => 2, + dir => 2, + vdir => 2, + test => 2, + chroot => 125, + echo => 0, + env => 125, + expr => 0, + nice => 125, + nohup => 125, + runcon => 125, + sort => 2, + stdbuf => 125, + test => 0, + timeout => 125, + true => 0, + tty => 2, + printf => 0, + printenv => 2, + ); + +my %expected_out = + ( + echo => "-/\n", + expr => "-/\n", + printf => "-/", + ); + +my %expected_err = + ( + false => '', + stty => '', + ); + +my $fail = 0; +my @built_programs = split ' ', $ENV{built_programs}; +foreach my $prog (@built_programs) + { + $prog eq '[' + and next; + + my $try = "Try '$prog --help' for more information.\n"; + my $x = $exit_status{$prog}; + defined $x + or $x = 1; + + my $out = $expected_out{$prog}; + defined $out + or $out = ''; + + my $err = $expected_err{$prog}; + defined $err + or $err = $x == 0 ? '' : "$prog: invalid option -- /\n$try"; + + # Accommodate different syntax in glibc's getopt + # diagnostics by filtering out single quotes. + # Also accommodate BSD getopt. + my $err_subst = "s,'/',/,; s,unknown,invalid,"; + + # Depending on how this script is run, stty emits different + # diagnostics. Don't bother checking them. + $prog eq 'stty' + and $err_subst = 's/(.|\n)*//ms'; + + my @Tests = (["$prog-invalid-opt", '-/', {OUT=>$out}, + {ERR_SUBST => $err_subst}, + {EXIT=>$x}, {ERR=>$err}]); + + my $save_temps = $ENV{DEBUG}; + my $verbose = $ENV{VERBOSE}; + + my $f = run_tests ($prog, \$prog, \@Tests, $save_temps, $verbose); + $f + and $fail = 1; + } + +exit $fail; diff --git a/tests/misc/join.pl b/tests/misc/join.pl new file mode 100755 index 0000000..2ca8567 --- /dev/null +++ b/tests/misc/join.pl @@ -0,0 +1,342 @@ +#!/usr/bin/perl +# Test join. + +# Copyright (C) 2008-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; + +my $limits = getlimits (); + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my $prog = 'join'; + +my $delim = chr 0247; +sub t_subst ($) +{ + (my $s = $_[0]) =~ s/:/$delim/g; + return $s; +} + +my @tv = ( +# test name +# flags file-1 file-2 expected output expected return code +# +['1a', '-a1', ["a 1\n", "b\n"], "a 1\n", 0], +['1b', '-a2', ["a 1\n", "b\n"], "b\n", 0], # Got "\n" +['1c', '-a1 -a2', ["a 1\n", "b\n"], "a 1\nb\n", 0], # Got "a 1\n\n" +['1d', '-a1', ["a 1\nb\n", "b\n"], "a 1\nb\n", 0], +['1e', '-a2', ["a 1\nb\n", "b\n"], "b\n", 0], +['1f', '-a2', ["b\n", "a\nb\n"], "a\nb\n", 0], + +['2a', '-a1 -e .', ["a\nb\nc\n", "a x y\nb\nc\n"], "a x y\nb\nc\n", 0], +['2b', '-a1 -e . -o 2.1,2.2,2.3', ["a\nb\nc\n", "a x y\nb\nc\n"], + "a x y\nb . .\nc . .\n", 0], +['2c', '-a1 -e . -o 2.1,2.2,2.3', ["a\nb\nc\nd\n", "a x y\nb\nc\n"], + "a x y\nb . .\nc . .\n. . .\n", 0], + +['3a', '-t:', ["a:1\nb:1\n", "a:2:\nb:2:\n"], "a:1:2:\nb:1:2:\n", 0], + +# operate on whole line (as sort does by default) +['3b', '-t ""', ["a 1\nb 1\n", "a 1\nb 2\n"], "a 1\n", 0], +# use NUL as the field delimiter +['3c', '-t "\\0"', ["a\0a\n", "a\0b\n"], "a\0a\0b\n", 0], + +# Just like -a1 and -a2 when there are no pairable lines +['4a', '-v 1', ["a 1\n", "b\n"], "a 1\n", 0], +['4b', '-v 2', ["a 1\n", "b\n"], "b\n", 0], + +['4c', '-v 1', ["a 1\nb\n", "b\n"], "a 1\n", 0], +['4d', '-v 2', ["a 1\nb\n", "b\n"], "", 0], +['4e', '-v 2', ["b\n", "a 1\nb\n"], "a 1\n", 0], +['5a', '-a1 -e - -o 1.1,2.2', + ["a 1\nb 2\n", "a 11\nb\n"], "a 11\nb -\n", 0], +['5b', '-a1 -e - -o 1.1,2.2', + ["apr 15\naug 20\ndec 18\nfeb 05\n", "apr 06\naug 14\ndate\nfeb 15"], + "apr 06\naug 14\ndec -\nfeb 15\n", 0], +['5c', '-a1 -e - -o 1.1,2.2', + ["aug 20\ndec 18\n", "aug 14\ndate\nfeb 15"], + "aug 14\ndec -\n", 0], +['5d', '-a1 -e - -o 1.1,2.2', + ["dec 18\n", ""], "dec -\n", 0], +['5e', '-a2 -e - -o 1.1,2.2', + ["apr 15\naug 20\ndec 18\nfeb 05\n", "apr 06\naug 14\ndate\nfeb 15\n"], + "apr 06\naug 14\n- -\nfeb 15\n", 0], +['5f', '-a2 -e - -o 2.2,1.1', + ["apr 15\naug 20\ndec 18\nfeb 05\n", "apr 06\naug 14\ndate\nfeb 15\n"], + "06 apr\n14 aug\n- -\n15 feb\n", 0], +['5g', '-a1 -e - -o 2.2,1.1', + ["apr 15\naug 20\ndec 18\nfeb 05\n", "apr 06\naug 14\ndate\nfeb 15\n"], + "06 apr\n14 aug\n- dec\n15 feb\n", 0], + +['5h', '-a1 -e - -o 2.2,1.1', + ["apr 15\naug 20\ndec 18\nfeb 05\n", "apr 06\naug 14\ndate\n"], + "06 apr\n14 aug\n- dec\n- feb\n", 0], +['5i', '-a1 -e - -o 1.1,2.2', + ["apr 15\naug 20\ndec 18\nfeb 05\n", "apr 06\naug 14\ndate\n"], + "apr 06\naug 14\ndec -\nfeb -\n", 0], + +['5j', '-a2 -e - -o 2.2,1.1', + ["apr 15\naug 20\ndec 18\nfeb 05\n", "apr 06\naug 14\ndate\n"], + "06 apr\n14 aug\n- -\n", 0], +['5k', '-a2 -e - -o 2.2,1.1', + ["apr 15\naug 20\ndec 18\nfeb 05\n", "apr 06\naug 14\ndate\n"], + "06 apr\n14 aug\n- -\n", 0], + +['5l', '-a1 -e - -o 2.2,1.1', + ["apr 15\naug 20\ndec 18\n", "apr 06\naug 14\ndate\nfeb 15\n"], + "06 apr\n14 aug\n- dec\n", 0], +['5m', '-a2 -e - -o 2.2,1.1', + ["apr 15\naug 20\ndec 18\n", "apr 06\naug 14\ndate\nfeb 15\n"], + "06 apr\n14 aug\n- -\n15 -\n", 0], + +['6a', '-e -', + ["a 1\nb 2\nd 4\n", "a 21\nb 22\nc 23\nf 26\n"], + "a 1 21\nb 2 22\n", 0], +['6b', '-a1 -e -', + ["a 1\nb 2\nd 4\n", "a 21\nb 22\nc 23\nf 26\n"], + "a 1 21\nb 2 22\nd 4\n", 0], +['6c', '-a1 -e -', + ["a 21\nb 22\nc 23\nf 26\n", "a 1\nb 2\nd 4\n"], + "a 21 1\nb 22 2\nc 23\nf 26\n", 0], + +['7a', '-a1 -e . -o 2.7', + ["a\nb\nc\n", "a x y\nb\nc\n"], ".\n.\n.\n", 0], + +['8a', '-a1 -e . -o 0,1.2', + ["a\nb\nc\nd G\n", "a x y\nb\nc\ne\n"], + "a .\nb .\nc .\nd G\n", 0], +['8b', '-a1 -a2 -e . -o 0,1.2', + ["a\nb\nc\nd G\n", "a x y\nb\nc\ne\n"], + "a .\nb .\nc .\nd G\ne .\n", 0], + +# From David Dyck +['9a', '', [" a 1\n b 2\n", " a Y\n b Z\n"], "a 1 Y\nb 2 Z\n", 0], + +# -o 'auto' +['10a', '-a1 -a2 -e . -o auto', + ["a 1 2\nb 1\nd 1 2\n", "a 3 4\nb 3 4\nc 3 4\n"], + "a 1 2 3 4\nb 1 . 3 4\nc . . 3 4\nd 1 2 . .\n", 0], +['10b', '-a1 -a2 -j3 -e . -o auto', + ["a 1 2\nb 1\nd 1 2\n", "a 3 4\nb 3 4\nc 3 4\n"], + "2 a 1 . .\n. b 1 . .\n2 d 1 . .\n4 . . a 3\n4 . . b 3\n4 . . c 3\n"], +['10c', '-a1 -1 1 -2 4 -e. -o auto', + ["a 1 2\nb 1\nd 1 2\n", "a 3 4\nb 3 4\nc 3 4\n"], + "a 1 2 . . .\nb 1 . . . .\nd 1 2 . . .\n"], +['10d', '-a2 -1 1 -2 4 -e. -o auto', + ["a 1 2\nb 1\nd 1 2\n", "a 3 4\nb 3 4\nc 3 4\n"], + ". . . a 3 4\n. . . b 3 4\n. . . c 3 4\n"], +['10e', '-o auto', + ["a 1 2\nb 1 2 discard\n", "a 3 4\nb 3 4 discard\n"], + "a 1 2 3 4\nb 1 2 3 4\n"], +['10f', '-t, -o auto', + ["a,1,,2\nb,1,2\n", "a,3,4\nb,3,4\n"], + "a,1,,2,3,4\nb,1,2,,3,4\n"], + +# For -v2, print the match field correctly with the default output format, +# when that match field is different between file 1 and file 2. Fixed in 8.10 +['v2-order', '-v2 -2 2', ["", "2 1\n"], "1 2\n", 0], + +# From Tim Smithers: fixed in 1.22l +['trailing-sp', '-t: -1 1 -2 1', ["a:x \n", "a:y \n"], "a:x :y \n", 0], + +# From Paul Eggert: fixed in 1.22n +['sp-vs-blank', '', ["\f 1\n", "\f 2\n"], "\f 1 2\n", 0], + +# From Paul Eggert: fixed in 1.22n (this would fail on Solaris7, +# with LC_ALL set to en_US). +# Unfortunately, that Solaris7's en_US locale folds case (making +# the first input file sorted) is not portable, so this test would +# fail on e.g. Linux systems, because the input to join isn't sorted. +# ['lc-collate', '', ["a 1a\nB 1B\n", "B 2B\n"], "B 1B 2B\n", 0], + +# Based on a report from Antonio Rendas. Fixed in 2.0.9. +['8-bit-t', t_subst "-t:", + [t_subst "a:1\nb:1\n", t_subst "a:2:\nb:2:\n"], + t_subst "a:1:2:\nb:1:2:\n", 0], + +# fields > SIZE_MAX are silently interpreted as SIZE_MAX +['bigfield1', "-1 $limits->{UINTMAX_OFLOW} -2 2", + ["a\n", "b\n"], " a b\n", 0], +['bigfield2', "-1 $limits->{SIZE_OFLOW} -2 2", + ["a\n", "b\n"], " a b\n", 0], + +# FIXME: change this to ensure the diagnostic makes sense +['invalid-j', '-j x', ["", ""], "", 1, + "$prog: invalid field number: 'x'\n"], + +# With ordering check, inputs in order +['chkodr-1', '--check-order', + [" a 1\n b 2\n", " a Y\n b Z\n"], "a 1 Y\nb 2 Z\n", 0], + +# Without check, inputs in order +['chkodr-2', '--nocheck-order', + [" a 1\n b 2\n", " a Y\n b Z\n"], "a 1 Y\nb 2 Z\n", 0], + +# Without check, both inputs out of order (in fact, in reverse order) +# but all pairable. Support for this is a GNU extension. +['chkodr-3', '--nocheck-order', + [" b 1\n a 2\n", " b Y\n a Z\n"], "b 1 Y\na 2 Z\n", 0], + +# The extension should work without --nocheck-order, since that is the +# default. +['chkodr-4', '', + [" b 1\n a 2\n", " b Y\n a Z\n"], "b 1 Y\na 2 Z\n", 0], + +# With check, both inputs out of order (in fact, in reverse order) +['chkodr-5', '--check-order', + [" b 1\n a 2\n", " b Y\n a Z\n"], "", 1, + "$prog: chkodr-5.1:2: is not sorted: a 2\n"], + +# Similar, but with only file 2 not sorted. +['chkodr-5b', '--check-order', + [" a 2\n b 1\n", " b Y\n a Z\n"], "", 1, + "$prog: chkodr-5b.2:2: is not sorted: a Z\n"], + +# Similar, but with the offending line having length 0 (excluding newline). +['chkodr-5c', '--check-order', + [" a 2\n b 1\n", " b Y\n\n"], "", 1, + "$prog: chkodr-5c.2:2: is not sorted: \n"], + +# Similar, but elicit a warning for each input file (without --check-order). +['chkodr-5d', '', + ["a\nx\n\n", "b\ny\n\n"], "", 1, + "$prog: chkodr-5d.1:3: is not sorted: \n" . + "$prog: chkodr-5d.2:3: is not sorted: \n" . + "$prog: input is not in sorted order\n" + ], + +# Similar, but make it so each offending line has no newline. +['chkodr-5e', '', + ["a\nx\no", "b\ny\np"], "", 1, + "$prog: chkodr-5e.1:3: is not sorted: o\n" . + "$prog: chkodr-5e.2:3: is not sorted: p\n" . + "$prog: input is not in sorted order\n" + ], + +# Without order check, both inputs out of order and some lines +# unpairable. This is NOT supported by the GNU extension. All that +# we really care about for this test is that the return status is +# zero, since that is the only way to actually verify that the +# --nocheck-order option had any effect. We don't actually want to +# guarantee that join produces this output on stdout. +['chkodr-6', '--nocheck-order', + [" b 1\n a 2\n", " b Y\n c Z\n"], "b 1 Y\n", 0], + +# Before 6.10.143, this would mistakenly fail with the diagnostic: +# join: File 1 is not in sorted order +['chkodr-7', '-12', ["2 a\n1 b\n", "2 c\n1 d"], "", 0], + +# After 8.9, join doesn't report disorder by default +# when comparing against an empty input file. +['chkodr-8', '', ["2 a\n1 b\n", ""], "", 0], + +# Test '--header' feature +['header-1', '--header', + [ "ID Name\n1 A\n2 B\n", "ID Color\n1 red\n"], "ID Name Color\n1 A red\n", 0], + +# '--header' with '--check-order' : The header line is out-of-order but the +# actual data is in order. This join should succeed. +['header-2', '--header --check-order', + ["ID Name\n1 A\n2 B\n", "ID Color\n2 green\n"], + "ID Name Color\n2 B green\n", 0], + +# '--header' with '--check-order' : The header line is out-of-order AND the +# actual data out-of-order. This join should fail. +['header-3', '--header --check-order', + ["ID Name\n2 B\n1 A\n", "ID Color\n2 blue\n"], "ID Name Color\n", 1, + "$prog: header-3.1:3: is not sorted: 1 A\n"], + +# '--header' with specific output format '-o'. +# output header line should respect the requested format +['header-4', '--header -o "0,1.3,2.2"', + ["ID Group Name\n1 Foo A\n2 Bar B\n", "ID Color\n2 blue\n"], + "ID Name Color\n2 B blue\n", 0], + +# '--header' always outputs headers from the first file +# even if the headers from the second file don't match +['header-5', '--header', + [ "ID1 Name\n1 A\n2 B\n", "ID2 Color\n1 red\n"], + "ID1 Name Color\n1 A red\n", 0], + +# '--header' doesn't check order of a header +# even if there is no header in the second file +['header-6', '--header -a1', + [ "ID1 Name\n1 A\n", ""], + "ID1 Name\n1 A\n", 0], + +# Zero-terminated lines +['z1', '-z', + ["a\0c\0e\0", "a\0b\0c\0"], "a\0c\0", 0], + +# not zero-terminated, but related to the code change: +# the old readlinebuffer() auto-added '\n' to the last line. +# the new readlinebuffer_delim() does not. +# Ensure it doesn't matter. +['z2', '', + ["a\nc\ne\n", "a\nb\nc"], "a\nc\n", 0], +['z3', '', + ["a\nc\ne", "a\nb\nc"], "a\nc\n", 0], +# missing last NUL at the end of the last line (=end of file) +['z4', '-z', + ["a\0c\0e", "a\0b\0c"], "a\0c\0", 0], +# With -z, embedded newlines are treated as field separators. +# Note '\n' are converted to ' ' in this case. +['z5', '-z -a1 -a2', + ["a\n\n1\0c 3\0", "a 2\0b\n8\0c 9\0"], "a 1 2\0b 8\0c 3 9\0"], +# One can avoid field processing like: +['z6', '-z -t ""', + ["a\n1\n\0", "a\n1\n\0"], "a\n1\n\0"], + +); + +# Convert the above old-style test vectors to the newer +# format used by Coreutils.pm. + +my @Tests; +foreach my $t (@tv) + { + my ($test_name, $flags, $in, $exp, $ret, $err_msg) = @$t; + my $new_ent = [$test_name, $flags]; + if (!ref $in) + { + push @$new_ent, {IN=>$in}; + } + elsif (ref $in eq 'HASH') + { + # ignore + } + else + { + foreach my $e (@$in) + { + push @$new_ent, {IN=>$e}; + } + } + push @$new_ent, {OUT=>$exp}; + $ret + and push @$new_ent, {EXIT=>$ret}, {ERR=>$err_msg}; + push @Tests, $new_ent; + } + +@Tests = triple_test \@Tests; + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($prog, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/kill.sh b/tests/misc/kill.sh new file mode 100755 index 0000000..34d6578 --- /dev/null +++ b/tests/misc/kill.sh @@ -0,0 +1,63 @@ +#!/bin/sh +# Validate kill operation + +# Copyright (C) 2015-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ kill + +# params required +returns_ 1 env kill || fail=1 +returns_ 1 env kill -TERM || fail=1 + +# Invalid combinations +returns_ 1 env kill -l -l || fail=1 +returns_ 1 env kill -l -t || fail=1 +returns_ 1 env kill -l -s 1 || fail=1 +returns_ 1 env kill -t -s 1 || fail=1 + +# signal sending +returns_ 1 env kill -0 no_pid || fail=1 +env kill -0 $$ || fail=1 +env kill -s0 $$ || fail=1 +env kill -n0 $$ || fail=1 # bash compat +env kill -CONT $$ || fail=1 +env kill -Cont $$ || fail=1 +returns_ 1 env kill -cont $$ || fail=1 +env kill -0 -1 || fail=1 # to group + +# table listing +env kill -l || fail=1 +env kill -t || fail=1 +env kill -L || fail=1 # bash compat +env kill -t TERM HUP || fail=1 + +# Verify (multi) name to signal number and vice versa +SIGTERM=$(env kill -l HUP TERM | tail -n1) || fail=1 +test $(env kill -l "$SIGTERM") = TERM || fail=1 + +# Verify we only consider the lower "signal" bits, +# to support ksh which just adds 256 to the signal value +STD_TERM_STATUS=$(expr "$SIGTERM" + 128) +KSH_TERM_STATUS=$(expr "$SIGTERM" + 256) +test $(env kill -l $STD_TERM_STATUS $KSH_TERM_STATUS | uniq) = TERM || fail=1 + +# Verify invalid signal spec is diagnosed +returns_ 1 env kill -l -1 || fail=1 +returns_ 1 env kill -l -1 0 || fail=1 +returns_ 1 env kill -l INVALID TERM || fail=1 + +Exit $fail diff --git a/tests/misc/mknod.sh b/tests/misc/mknod.sh new file mode 100755 index 0000000..de8d4f6 --- /dev/null +++ b/tests/misc/mknod.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# Ensure that mknod, mkfifo, mkdir -m MODE work with a restrictive umask + +# Copyright (C) 2004-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ mknod + +# Ensure fifos are supported +mkfifo_or_skip_ fifo + +umask 777 + +mknod -m 734 f1 p || fail=1 +mode=$(ls -dgo f1|cut -b-10) +test $mode = prwx-wxr-- || fail=1 + +mkfifo -m 734 f2 || fail=1 +mode=$(ls -dgo f2|cut -b-10) +test $mode = prwx-wxr-- || fail=1 + +mkdir -m 734 f3 || fail=1 +mode=$(ls -dgo f3|cut -b-10) +test $mode = drwx-wxr-- || test $mode = drwx-wsr-- || fail=1 + +Exit $fail diff --git a/tests/misc/mktemp.pl b/tests/misc/mktemp.pl new file mode 100755 index 0000000..01dcf61 --- /dev/null +++ b/tests/misc/mktemp.pl @@ -0,0 +1,206 @@ +#!/usr/bin/perl +# Test "mktemp". + +# Copyright (C) 2007-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; + +(my $ME = $0) =~ s|.*/||; + +sub check_tmp($$) +{ + my ($file, $file_or_dir) = @_; + + my (undef, undef, $mode, undef) = stat $file + or die "$ME: failed to stat $file: $!\n"; + my $required_mode; + if ($file_or_dir eq 'D') { + -d $file or die "$ME: $file isn't a directory\n"; + -x $file or die "$ME: $file isn't owner-searchable\n"; + $required_mode = 0700; + } elsif ($file_or_dir eq 'F') { + -f $file or die "$ME: $file isn't a regular file\n"; + $required_mode = 0600; + } + -r $file or die "$ME: $file isn't owner-readable\n"; + -w $file or die "$ME: $file isn't owner-writable\n"; + ($mode & 0777) == $required_mode + or die "$ME: $file doesn't have required permissions\n"; + + $file_or_dir eq 'D' + and do { rmdir $file or die "$ME: failed to rmdir $file: $!\n" }; + $file_or_dir eq 'F' + and do { unlink $file or die "$ME: failed to unlink $file: $!\n" }; +} + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; +my $prog = 'mktemp'; +my $bad_dir = 'no/such/dir'; + +my @Tests = + ( + # test-name, [option, option, ...] {OUT=>"expected-output"} + # + ['too-many', '-q a b', + {ERR=>"$prog: too many templates\n" + . "Try '$prog --help' for more information.\n"}, {EXIT => 1} ], + + ['too-few-x', '-q foo.XX', {EXIT => 1}, + {ERR=>"$prog: too few X's in template 'foo.XX'\n"}], + + ['1f', 'bar.XXXX', {OUT => "bar.ZZZZ\n"}, + {OUT_SUBST => 's,\.....$,.ZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }} + ], + + ['2f', '-- -XXXX', {OUT => "-ZZZZ\n"}, + {OUT_SUBST => 's,-....$,-ZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }} + ], + + # Create a temporary directory. + ['1d', '-d f.XXXX', {OUT => "f.ZZZZ\n"}, + {OUT_SUBST => 's,\.....$,.ZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'D'; }} + ], + + # Use a template consisting solely of X's + ['1d-allX', '-d XXXX', {OUT => "ZZZZ\n"}, + {PRE => sub {mkdir 'XXXX',0755 or die "XXXX: $!\n"}}, + {OUT_SUBST => 's,^....$,ZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'D'; rmdir 'XXXX' or die "rmdir XXXX: $!\n"; }} + ], + + # Test -u + ['uf', '-u f.XXXX', {OUT => "f.ZZZZ\n"}, + {OUT_SUBST => 's,\.....$,.ZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + -e $f and die "dry-run created file"; }}], + ['ud', '-d --dry-run d.XXXX', {OUT => "d.ZZZZ\n"}, + {OUT_SUBST => 's,\.....$,.ZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + -e $f and die "dry-run created directory"; }}], + + # Test bad templates + ['invalid-tl', '-t a/bXXXX', + {ERR=>"$prog: invalid template, 'a/bXXXX', " + . "contains directory separator\n"}, {EXIT => 1} ], + + ['invalid-t2', '--tmpdir=a /bXXXX', + {ERR=>"$prog: invalid template, '/bXXXX'; " + . "with --tmpdir, it may not be absolute\n"}, {EXIT => 1} ], + + # Suffix after X. + ['suffix1f', 'aXXXXb', {OUT=>"aZZZZb\n"}, + {OUT_SUBST=>'s,a....b,aZZZZb,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + ['suffix1d', '-d aXXXXb', {OUT=>"aZZZZb\n"}, + {OUT_SUBST=>'s,a....b,aZZZZb,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'D'; }}], + ['suffix1u', '-u aXXXXb', {OUT=>"aZZZZb\n"}, + {OUT_SUBST=>'s,a....b,aZZZZb,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + -e $f and die "dry-run created file"; }}], + + ['suffix2f', 'aXXXXaaXXXXa', {OUT=>"aXXXXaaZZZZa\n"}, + {OUT_SUBST=>'s,a....a$,aZZZZa,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + ['suffix2d', '-d --suffix= aXXXXaaXXXX', {OUT=>"aXXXXaaZZZZ\n"}, + {OUT_SUBST=>'s,a....$,aZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'D'; }}], + + ['suffix3f', '--suffix=b aXXXX', {OUT=>"aZZZZb\n"}, + {OUT_SUBST=>'s,a....b,aZZZZb,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + + ['suffix4f', '--suffix=X aXXXX', {OUT=>"aZZZZX\n"}, + {OUT_SUBST=>'s,^a....,aZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + + ['suffix5f', '--suffix /b aXXXX', {EXIT=>1}, + {ERR=>"$prog: invalid suffix '/b', contains directory separator\n"}], + + ['suffix6f', 'aXXXX/b', {EXIT=>1}, + {ERR=>"$prog: invalid suffix '/b', contains directory separator\n"}], + + ['suffix7f', '--suffix= aXXXXb', {EXIT=>1}, + {ERR=>"$prog: with --suffix, template 'aXXXXb' must end in X\n"}], + ['suffix7d', '-d --suffix=aXXXXb ""', {EXIT=>1}, + {ERR=>"$prog: with --suffix, template '' must end in X\n"}], + + ['suffix8f', 'aXXXX --suffix=b', {OUT=>"aZZZZb\n"}, + {OUT_SUBST=>'s,^a....,aZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + + ['suffix9f', 'aXXXX --suffix=b', {EXIT=>1}, + {ENV=>"POSIXLY_CORRECT=1"}, + {ERR=>"$prog: too many templates\n" + . "Try '$prog --help' for more information.\n"}], + + ['suffix10f', 'aXXb', {EXIT => 1}, + {ERR=>"$prog: too few X's in template 'aXXb'\n"}], + ['suffix10d', '-d --suffix=X aXX', {EXIT => 1}, + {ERR=>"$prog: too few X's in template 'aXXX'\n"}], + + ['suffix11f', '--suffix=.txt', {OUT=>"./tmp.ZZZZZZZZZZ.txt\n"}, + {ENV=>"TMPDIR=."}, + {OUT_SUBST=>'s,\..{10}\.,.ZZZZZZZZZZ.,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + + + # Test template with subdirectory + ['tmp-w-slash', '--tmpdir=. a/bXXXX', + {PRE => sub {mkdir 'a',0755 or die "a: $!\n"}}, + {OUT_SUBST => 's,b....$,bZZZZ,'}, + {OUT => "./a/bZZZZ\n"}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; unlink $f; rmdir 'a' or die "rmdir a: $!\n" }} + ], + + ['priority-t-tmpdir', "-t -p $bad_dir foo.XXX", + {ENV=>"TMPDIR=."}, + {OUT_SUBST => 's,....$,.ZZZ,'}, + {OUT => "./foo.ZZZ\n"}, + ], + + ['pipe-bad-tmpdir', + {ENV => "TMPDIR=$bad_dir"}, + {ERR_SUBST => "s,($bad_dir/)[^']+': .*,\$1...,"}, + {ERR => "$prog: failed to create file via template '$bad_dir/...\n"}, + {EXIT => 1}], + ['pipe-bad-tmpdir-u', '-u', {OUT => "$bad_dir/tmp.ZZZZZZZZZZ\n"}, + {ENV => "TMPDIR=$bad_dir"}, + {OUT_SUBST => 's,\..{10}$,.ZZZZZZZZZZ,'}], + ); + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($ME, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/nl.sh b/tests/misc/nl.sh new file mode 100755 index 0000000..a1e9480 --- /dev/null +++ b/tests/misc/nl.sh @@ -0,0 +1,113 @@ +#!/bin/sh +# exercise nl functionality + +# Copyright (C) 2002-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ nl +getlimits_ + +echo a | nl > out || fail=1 +echo b | nl -s%n >> out || fail=1 +echo c | nl -n ln >> out || fail=1 +echo d | nl -n rn >> out || fail=1 +echo e | nl -n rz >> out || fail=1 +echo === >> out +printf 'a\n\n' | nl > t || fail=1; cat -A t >> out +cat <<\EOF > exp + 1 a + 1%nb +1 c + 1 d +000001 e +=== + 1^Ia$ + $ +EOF +compare exp out || fail=1 + +# Ensure numbering reset at each delimiter. +# coreutils <= v8.25 only reset at a page header. +printf '%s\n' '\:\:\:' a '\:\:' b '\:' c > in.txt || framework_failure_ +nl -ha -fa in.txt > out.tmp || fail=1 +nl -p -ha -fa in.txt >> out.tmp || fail=1 +sed '/^$/d' < out.tmp > out || framework_failure_ +cat <<\EOF > exp + 1 a + 1 b + 1 c + 1 a + 2 b + 3 c +EOF +compare exp out || fail=1 + +# Ensure we only indicate overflow when needing to output overflowed numbers +returns_ 1 nl -v$INTMAX_OFLOW /dev/null || fail=1 +printf '%s\n' a \\:\\: b > in.txt || framework_failure_ +nl -v$INTMAX_MAX in.txt > out || fail=1 +cat <<EOF > exp +$INTMAX_MAX a + +$INTMAX_MAX b +EOF +compare exp out || fail=1 +returns_ 1 nl -p -v$INTMAX_MAX in.txt > out || fail=1 + +# Test negative iteration +returns_ 1 nl -i$INTMAX_UFLOW /dev/null || fail=1 +printf '%s\n' a b > in.txt || framework_failure_ +nl -v$INTMAX_MAX -i$INTMAX_MIN in.txt > out || fail=1 +cat <<EOF > exp +$INTMAX_MAX a + -1 b +EOF +compare exp out || fail=1 +printf '%s\n' a b c > in.txt || framework_failure_ +returns_ 1 nl -v$INTMAX_MAX -i$INTMAX_MIN in.txt > out || fail=1 + +# Test GNU extension to --section-delimiter, of disabling section matching +printf '%s\n' a '\:\:' c > in.txt || framework_failure_ +nl -d '' in.txt > out || fail=1 +cat <<\EOF > exp + 1 a + 2 \:\: + 3 c +EOF +compare exp out || fail=1 + +# Test GNU extension to --section-delimiter, of supporting strings longer than 2 +printf '%s\n' a foofoo c > in.txt || framework_failure_ +nl -d 'foo' in.txt > out || fail=1 +cat <<EOF > exp + 1 a + + 1 c +EOF +compare exp out || fail=1 + +# Ensure single char delimiters assume a following ':' character (as per POSIX) +# coreutils <= v8.32 didn't match single char delimiters at all +printf '%s\n' a x:x: c > in.txt || framework_failure_ +nl -d 'x' in.txt > out || fail=1 +cat <<EOF > exp + 1 a + + 1 c +EOF +compare exp out || fail=1 + +Exit $fail diff --git a/tests/misc/nohup.sh b/tests/misc/nohup.sh new file mode 100755 index 0000000..3a598af --- /dev/null +++ b/tests/misc/nohup.sh @@ -0,0 +1,125 @@ +#!/bin/sh +# test nohup + +# Copyright (C) 2003-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ nohup + + +nohup sh -c 'echo stdout; echo stderr 1>&2' 2>err || fail=1 + +# Be careful. The results of the above nohup command +# change depending on whether stdin and stdout are redirected. +if test -t 1; then + test "$(cat nohup.out)" = stdout || fail=1 + if test -t 0; then + echo 'nohup: ignoring input and appending output to 'nohup.out'\' + else + echo 'nohup: appending output to 'nohup.out'\' + fi >exp || framework_failure_ +else + # Here it should not even exist. + test -f nohup.out && fail=1 + if test -t 0; then + echo 'nohup: ignoring input' >exp + else + rm -f exp + fi || framework_failure_ +fi +echo 'stderr' >> exp || framework_failure_ + +compare exp err || fail=1 +rm -f nohup.out err exp +# ---------------------- + +# Be careful. The results of the following nohup command +# change depending on whether stderr is redirected. +nohup sh -c 'echo stdout; echo stderr 1>&2' >out || fail=1 +if test -t 2; then + test "$(cat out|tr '\n' -)" = stdout-stderr- || fail=1 +else + test "$(cat out|tr '\n' -)" = stdout- || fail=1 +fi +# It must *not* exist. +test -f nohup.out && fail=1 +rm -f nohup.out err +# ---------------------- + +# Bug present through coreutils 8.0: failure to print advisory message +# to stderr must be fatal. Requires stdout to be terminal. +if test -w /dev/full && test -c /dev/full; then +( + # POSIX shells immediately exit the subshell on exec error. + # So check we can write to /dev/tty before the exec, which + # isn't possible if we've no controlling tty for example. + test -c /dev/tty && >/dev/tty || exit 0 + + exec >/dev/tty + test -t 1 || exit 0 + returns_ 125 nohup echo hi 2> /dev/full || fail=1 + test -f nohup.out || fail=1 + compare /dev/null nohup.out || fail=1 + rm -f nohup.out + exit $fail +) || fail=1 +fi + +nohup no-such-command 2> err +errno=$? +if test -t 1; then + test $errno = 127 || fail=1 + # It must exist. + test -f nohup.out || fail=1 + # It must be empty. + compare /dev/null nohup.out || fail=1 +fi + +cat <<\EOF > exp || framework_failure_ +nohup: appending output to 'nohup.out' +nohup: cannot run command 'no-such-command': No such file or directory +EOF +# Disable these comparisons. Too much variation in 2nd line. +# compare exp err || fail=1 +rm -f nohup.out err exp +# ---------------------- + +touch k; chmod 0 k +nohup ./k 2> err +errno=$? +test $errno = 126 || fail=1 +if test -t 1; then + # It must exist. + test -f nohup.out || fail=1 + # It must be empty. + compare /dev/null nohup.out || fail=1 +fi + +cat <<\EOF > exp || framework_failure_ +nohup: appending output to 'nohup.out' +nohup: cannot run command './k': Permission denied +EOF +# Disable these comparisons. Too much variation in 2nd line. +# compare exp err || fail=1 + +# Make sure it fails with exit status of 125 when given too few arguments, +# except that POSIX requires 127 in this case. +returns_ 125 nohup >/dev/null 2>&1 || fail=1 +export POSIXLY_CORRECT=1 +returns_ 127 nohup >/dev/null 2>&1 || fail=1 +unset POSIXLY_CORRECT + +Exit $fail diff --git a/tests/misc/numfmt.pl b/tests/misc/numfmt.pl new file mode 100755 index 0000000..86fb78f --- /dev/null +++ b/tests/misc/numfmt.pl @@ -0,0 +1,1125 @@ +#!/usr/bin/perl +# Basic tests for "numfmt". + +# Copyright (C) 2012-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; + +(my $program_name = $0) =~ s|.*/||; +my $prog = 'numfmt'; + +my $limits = getlimits (); + +# TODO: add localization tests with "grouping" +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my $locale = $ENV{LOCALE_FR_UTF8}; +! defined $locale || $locale eq 'none' + and $locale = 'C'; + +my $try = "Try '$prog --help' for more information.\n"; + +my @Tests = + ( + ['1', '1234', {OUT => "1234"}], + ['2', '--from=si 1K', {OUT => "1000"}], + ['3', '--from=iec 1K', {OUT => "1024"}], + ['4', '--from=auto 1K', {OUT => "1000"}], + ['5', '--from=auto 1Ki', {OUT => "1024"}], + ['5.1', '--from=iec-i 1Ki', {OUT => "1024"}], + + ['6', {IN_PIPE => "1234\n"}, {OUT => "1234"}], + ['7', '--from=si', {IN_PIPE => "2K\n"}, {OUT => "2000"}], + ['7a', '--invalid=fail', {IN_PIPE => "no_NL"}, {OUT => "no_NL"}, + {ERR => "$prog: invalid number: 'no_NL'\n"}, + {EXIT => '2'}], + + ['8', '--to=si 2000', {OUT => "2.0K"}], + ['9', '--to=si 2001', {OUT => "2.1K"}], + ['10', '--to=si 1999', {OUT => "2.0K"}], + ['11', '--to=si --round=down 2001', {OUT => "2.0K"}], + ['12', '--to=si --round=down 1999', {OUT => "1.9K"}], + ['13', '--to=si --round=up 1901', {OUT => "2.0K"}], + ['14', '--to=si --round=down 1901', {OUT => "1.9K"}], + ['15', '--to=si --round=nearest 1901', {OUT => "1.9K"}], + ['16', '--to=si --round=nearest 1945', {OUT => "1.9K"}], + ['17', '--to=si --round=nearest 1955', {OUT => "2.0K"}], + + ['18', '--to=iec 2048', {OUT => "2.0K"}], + ['19', '--to=iec 2049', {OUT => "2.1K"}], + ['20', '--to=iec 2047', {OUT => "2.0K"}], + ['21', '--to=iec --round=down 2049', {OUT => "2.0K"}], + ['22', '--to=iec --round=down 2047', {OUT => "1.9K"}], + ['23', '--to=iec --round=up 2040', {OUT => "2.0K"}], + ['24', '--to=iec --round=down 2040', {OUT => "1.9K"}], + ['25', '--to=iec --round=nearest 1996', {OUT => "1.9K"}], + ['26', '--to=iec --round=nearest 1997', {OUT => "2.0K"}], + ['27', '--to=iec-i 2048', {OUT => "2.0Ki"}], + + ['neg-1', '-- -1234', {OUT => "-1234"}], + ['neg-2', '--padding=5 -- -1234', {OUT => "-1234"}], + ['neg-3', '--padding=6 -- -1234', {OUT => " -1234"}], + ['neg-4', '--to=iec -- 9100 -9100', {OUT => "8.9K\n-8.9K"}], + ['neg-5', '-- -0.1', {OUT => "-0.1"}], + ['neg-6', '-- -0', {OUT => "0"}], + ['neg-7', '-- -0.-1', + {ERR => "$prog: invalid number: '-0.-1'\n"}, + {EXIT => '2'}], + + ['float-1', '1.1', {OUT => "1.1"}], + ['float-2', '1.22', {OUT => "1.22"}], + ['float-3', '1.22.', + {ERR => "$prog: invalid suffix in input: '1.22.'\n"}, + {EXIT => '2'}], + + ['unit-1', '--from-unit=512 4', {OUT => "2048"}], + ['unit-2', '--to-unit=512 2048', {OUT => "4"}], + ['unit-3', '--from-unit=512 --from=si 4M', {OUT => "2048000000"}], + ['unit-4', '--from-unit=512 --from=iec --to=iec 4M', {OUT => "2.0G"}], + ['unit-5', '--from-unit=AA --from=iec --to=iec 4M', + {ERR => "$prog: invalid unit size: 'AA'\n"}, + {EXIT => '1'}], + ['unit-6', '--from-unit=54W --from=iec --to=iec 4M', + {ERR => "$prog: invalid unit size: '54W'\n"}, + {EXIT => '1'}], + ['unit-7', '--from-unit=K 30', {OUT=>"30000"}], + ['unit-7.1', '--from-unit=Ki 30', {OUT=>"30720"}], + ['unit-7.2', '--from-unit=i 0', + {ERR => "$prog: invalid unit size: 'i'\n"}, + {EXIT => '1'}], + ['unit-7.3', '--from-unit=1i 0', + {ERR => "$prog: invalid unit size: '1i'\n"}, + {EXIT => '1'}], + ['unit-8', '--from-unit='.$limits->{UINTMAX_OFLOW}.' --to=iec 30', + {ERR => "$prog: invalid unit size: '$limits->{UINTMAX_OFLOW}'\n"}, + {EXIT => '1'}], + ['unit-9', '--from-unit=0 1', + {ERR => "$prog: invalid unit size: '0'\n"}, + {EXIT => '1'}], + ['unit-10', '--to-unit=0 1', + {ERR => "$prog: invalid unit size: '0'\n"}, + {EXIT => '1'}], + + # Test Suffix logic + ['suf-1', '4000', {OUT=>'4000'}], + ['suf-2', '4J', + {ERR => "$prog: invalid suffix in input: '4J'\n"}, + {EXIT => '2'}], + ['suf-2.1', '4M', + {ERR => "$prog: rejecting suffix " . + "in input: '4M' (consider using --from)\n"}, + {EXIT => '2'}], + ['suf-3', '--from=si 4M', {OUT=>'4000000'}], + ['suf-4', '--from=si 4J', + {ERR => "$prog: invalid suffix in input: '4J'\n"}, + {EXIT => '2'}], + ['suf-5', '--from=si 4MJ', + {ERR => "$prog: invalid suffix in input '4MJ': 'J'\n"}, + {EXIT => '2'}], + + ['suf-6', '--from=iec 4M', {OUT=>'4194304'}], + ['suf-7', '--from=auto 4M', {OUT=>'4000000'}], + ['suf-8', '--from=auto 4Mi', {OUT=>'4194304'}], + ['suf-9', '--from=auto 4MiJ', + {ERR => "$prog: invalid suffix in input '4MiJ': 'J'\n"}, + {EXIT => '2'}], + ['suf-10', '--from=auto 4JiJ', + {ERR => "$prog: invalid suffix in input: '4JiJ'\n"}, + {EXIT => '2'}], + + # characters after a white space are OK - printed as-is + ['suf-11', '"4 M"', {OUT=>'4 M'}], + + # Custom suffix + ['suf-12', '--suffix=Foo 70Foo', {OUT=>'70Foo'}], + ['suf-13', '--suffix=Foo 70', {OUT=>'70Foo'}], + ['suf-14', '--suffix=Foo --from=si 70K', {OUT=>'70000Foo'}], + ['suf-15', '--suffix=Foo --from=si 70KFoo', {OUT=>'70000Foo'}], + ['suf-16', '--suffix=Foo --to=si 7000Foo', {OUT=>'7.0KFoo'}], + ['suf-17', '--suffix=Foo --to=si 7000Bar', + {ERR => "$prog: invalid suffix in input: '7000Bar'\n"}, + {EXIT => '2'}], + ['suf-18', '--suffix=Foo --to=si 7000FooF', + {ERR => "$prog: invalid suffix in input: '7000FooF'\n"}, + {EXIT => '2'}], + # space(s) between number and suffix. Note only field 1 is used + # by default so specify the NUL delimiter to consider the whole "line". + ['suf-19', "-d '' --from=si '4.0 K'", {OUT => "4000"}], + + ## GROUPING + + # "C" locale - no grouping (locale-specific tests, below) + ['grp-1', '--from=si --grouping 7M', {OUT=>'7000000'}], + ['grp-2', '--from=si --to=si --grouping 7M', + {ERR => "$prog: grouping cannot be combined with --to\n"}, + {EXIT => '1'}], + + + ## Padding + ['pad-1', '--padding=10 5', {OUT=>' 5'}], + ['pad-2', '--padding=-10 5', {OUT=>'5 '}], + ['pad-3', '--padding=A 5', + {ERR => "$prog: invalid padding value 'A'\n"}, + {EXIT => '1'}], + ['pad-3.1', '--padding=0 5', + {ERR => "$prog: invalid padding value '0'\n"}, + {EXIT => '1'}], + ['pad-3.2', "--padding=$limits->{LONG_MIN} 0", + {ERR => "$prog: invalid padding value '$limits->{LONG_MIN}'\n"}, + {EXIT => '1'}], + ['pad-4', '--padding=10 --to=si 50000', {OUT=>' 50K'}], + ['pad-5', '--padding=-10 --to=si 50000', {OUT=>'50K '}], + + # padding too narrow + ['pad-6', '--padding=2 --to=si 1000', {OUT=>'1.0K'}], + + + # Padding + suffix + ['pad-7', '--padding=10 --suffix=foo --to=si 50000', + {OUT=>' 50Kfoo'}], + ['pad-8', '--padding=-10 --suffix=foo --to=si 50000', + {OUT=>'50Kfoo '}], + + + # Delimiters + ['delim-1', '--delimiter=: --from=auto 40M:', {OUT=>'40000000:'}], + ['delim-2', '--delimiter="" --from=auto "40 M"',{OUT=>'40000000'}], + ['delim-3', '--delimiter=" " --from=auto "40M Foo"',{OUT=>'40000000 Foo'}], + ['delim-4', '--delimiter=: --from=auto 40M:60M', {OUT=>'40000000:60M'}], + ['delim-5', '-d: --field=2 --from=auto :40M:60M', {OUT=>':40000000:60M'}], + ['delim-6', '-d: --field 3 --from=auto 40M:60M', {OUT=>"40M:60M"}], + ['delim-err-1', '-d,, --to=si 1', {EXIT=>1}, + {ERR => "$prog: the delimiter must be a single character\n"}], + + #Fields + ['field-1', '--field A', + {ERR => "$prog: invalid field value 'A'\n$try"}, + {EXIT => '1'}], + ['field-2', '--field 2 --from=auto "Hello 40M World 90G"', + {OUT=>'Hello 40000000 World 90G'}], + ['field-3', '--field 3 --from=auto "Hello 40M World 90G"', + {OUT=>"Hello 40M "}, + {ERR=>"$prog: invalid number: 'World'\n"}, + {EXIT => 2},], + # Last field - no text after number + ['field-4', '--field 4 --from=auto "Hello 40M World 90G"', + {OUT=>"Hello 40M World 90000000000"}], + # Last field - a delimiter after the number + ['field-5', '--field 4 --from=auto "Hello 40M World 90G "', + {OUT=>"Hello 40M World 90000000000 "}], + + # Mix Fields + Delimiters + ['field-6', '--delimiter=: --field 2 --from=auto "Hello:40M:World:90G"', + {OUT=>"Hello:40000000:World:90G"}], + + # not enough fields + ['field-8', '--field 3 --to=si "Hello World"', {OUT=>"Hello World"}], + + # Multiple fields + ['field-range-1', '--field 2,4 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1000 2.0K 3000 4.0K 5000"}], + + ['field-range-2', '--field 2-4 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1000 2.0K 3.0K 4.0K 5000"}], + + ['field-range-3', '--field 1,2,3-5 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3.0K 4.0K 5.0K"}], + + ['field-range-4', '--field 1-5 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3.0K 4.0K 5.0K"}], + + ['field-range-5', '--field 1-3,5 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3.0K 4000 5.0K"}], + + ['field-range-6', '--field 3- --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1000 2000 3.0K 4.0K 5.0K"}], + + ['field-range-7', '--field -3 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3.0K 4000 5000"}], + + ['field-range-8', '--field 1-2,4-5 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3000 4.0K 5.0K"}], + ['field-range-9', '--field 4-5,1-2 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3000 4.0K 5.0K"}], + + ['field-range-10','--field 1-3,2-4 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3.0K 4.0K 5000"}], + ['field-range-11','--field 2-4,1-3 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3.0K 4.0K 5000"}], + + ['field-range-12','--field 1-1,3-3 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2000 3.0K 4000 5000"}], + + ['field-range-13', '--field 1,-2 --to=si "1000 2000 3000"', + {OUT=>"1.0K 2.0K 3000"}], + + ['field-range-14', '--field -2,4- --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3000 4.0K 5.0K"}], + ['field-range-15', '--field -2,-4 --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3.0K 4.0K 5000"}], + ['field-range-16', '--field 2-,4- --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1000 2.0K 3.0K 4.0K 5.0K"}], + ['field-range-17', '--field 4-,2- --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1000 2.0K 3.0K 4.0K 5.0K"}], + + # white space are valid field separators + # (undocumented? but works in cut as well). + ['field-range-18', '--field "1,2 4" --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3000 4.0K 5000"}], + + # Unlike 'cut', a lone '-' means 'all fields', even as part of a list + # of fields. + ['field-range-19','--field 3,- --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3.0K 4.0K 5.0K"}], + + ['all-fields-1', '--field=- --to=si "1000 2000 3000 4000 5000"', + {OUT=>"1.0K 2.0K 3.0K 4.0K 5.0K"}], + + ['field-range-err-1', '--field -foo --to=si 10', + {EXIT=>1}, {ERR=>"$prog: invalid field value 'foo'\n$try"}], + ['field-range-err-2', '--field --3 --to=si 10', + {EXIT=>1}, {ERR=>"$prog: invalid field range\n$try"}], + ['field-range-err-3', '--field 0 --to=si 10', + {EXIT=>1}, {ERR=>"$prog: fields are numbered from 1\n$try"}], + ['field-range-err-4', '--field 3-2 --to=si 10', + {EXIT=>1}, {ERR=>"$prog: invalid decreasing range\n$try"}], + ['field-range-err-6', '--field - --field 1- --to=si 10', + {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}], + ['field-range-err-7', '--field -1 --field 1- --to=si 10', + {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}], + ['field-range-err-8', '--field -1 --field 1,2,3 --to=si 10', + {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}], + ['field-range-err-9', '--field 1- --field 1,2,3 --to=si 10', + {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}], + ['field-range-err-10','--field 1,2,3 --field 1- --to=si 10', + {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}], + ['field-range-err-11','--field 1-2-3 --to=si 10', + {EXIT=>1}, {ERR=>"$prog: invalid field range\n$try"}], + ['field-range-err-12','--field 0-1 --to=si 10', + {EXIT=>1}, {ERR=>"$prog: fields are numbered from 1\n$try"}], + ['field-range-err-13','--field '.$limits->{UINTMAX_MAX}.',22 --to=si 10', + {EXIT=>1}, {ERR=>"$prog: field number " . + "'".$limits->{UINTMAX_MAX}."' is too large\n$try"}], + + # Auto-consume white-space, setup auto-padding + ['whitespace-1', '--to=si --field 2 "A 500 B"', {OUT=>"A 500 B"}], + ['whitespace-2', '--to=si --field 2 "A 5000 B"', {OUT=>"A 5.0K B"}], + ['whitespace-3', '--to=si " 500"', {OUT=>" 500"}], + ['whitespace-4', '--to=si " 6500"', {OUT=>" 6.5K"}], + # NOTE: auto-padding is not enabled if the value is on the first + # field and there's no white-space before it. + ['whitespace-5', '--to=si "6000000"', {OUT=>"6.0M"}], + # but if there is whitespace, assume auto-padding is desired. + ['whitespace-6', '--to=si " 6000000"', {OUT=>" 6.0M"}], + + # auto-padding - lines have same padding-width + # (padding_buffer will be alloc'd just once) + ['whitespace-7', '--to=si --field 2', + {IN_PIPE=>"rootfs 100000\n" . + "udevxx 2000000\n"}, + {OUT =>"rootfs 100K\n" . + "udevxx 2.0M"}], + # auto-padding - second line requires a + # larger padding (padding-buffer needs to be realloc'd) + ['whitespace-8', '--to=si --field 2', + {IN_PIPE=>"rootfs 100000\n" . + "udev 20000000\n"}, + {OUT =>"rootfs 100K\n" . + "udev 20M"}], + + + # Corner-cases: + # weird mix of identical suffix,delimiters + # The priority is: + # 1. delimiters (and fields) are parsed (in process_line() + # 2. optional custom suffix is removed (in process_suffixed_number()) + # 3. Remaining suffixes must be valid SI/IEC (in human_xstrtol()) + + # custom suffix comes BEFORE SI/IEC suffix, + # so these are 40 of "M", not 40,000,000. + ['mix-1', '--suffix=M --from=si 40M', {OUT=>"40M"}], + + # These are forty-million Ms . + ['mix-2', '--suffix=M --from=si 40MM', {OUT=>"40000000M"}], + + ['mix-3', '--suffix=M --from=auto 40MM', {OUT=>"40000000M"}], + ['mix-4', '--suffix=M --from=auto 40MiM', {OUT=>"41943040M"}], + ['mix-5', '--suffix=M --to=si --from=si 4MM', {OUT=>"4.0MM"}], + + # This might be confusing to the user, but it's legit: + # The M in the output is the custom suffix, not Mega. + ['mix-6', '--suffix=M 40', {OUT=>"40M"}], + ['mix-7', '--suffix=M 4000000', {OUT=>"4000000M"}], + ['mix-8', '--suffix=M --to=si 4000000', {OUT=>"4.0MM"}], + + # The output 'M' is the custom suffix. + ['mix-10', '--delimiter=M --suffix=M 40', {OUT=>"40M"}], + + # The INPUT 'M' is a delimiter (delimiters are top priority) + # The output contains one M for custom suffix, and one 'M' delimiter. + ['mix-11', '--delimiter=M --suffix=M 40M', {OUT=>"40MM"}], + + # Same as above, the "M" is NOT treated as a mega SI prefix, + ['mix-12', '--delimiter=M --from=si --suffix=M 40M', {OUT=>"40MM"}], + + # The 'M' is treated as a delimiter, and so the input value is '4000' + ['mix-13', '--delimiter=M --to=si --from=auto 4000M5000M9000', + {OUT=>"4.0KM5000M9000"}], + # 'M' is the delimiter, so the second input field is '5000' + ['mix-14', '--delimiter=M --field 2 --from=auto --to=si 4000M5000M9000', + {OUT=>"4000M5.0KM9000"}], + + + + ## Header testing + + # header - silently ignored with command line parameters + ['header-1', '--header --to=iec 4096', {OUT=>"4.0K"}], + + # header warning with --debug + ['header-2', '--debug --header --to=iec 4096', {OUT=>"4.0K"}, + {ERR=>"$prog: --header ignored with command-line input\n"}], + + ['header-3', '--header=A', + {ERR=>"$prog: invalid header value 'A'\n"}, + {EXIT => 1},], + ['header-4', '--header=0', + {ERR=>"$prog: invalid header value '0'\n"}, + {EXIT => 1},], + ['header-5', '--header=-6', + {ERR=>"$prog: invalid header value '-6'\n"}, + {EXIT => 1},], + ['header-6', '--debug --header --to=iec', + {IN_PIPE=>"size\n5000\n90000\n"}, + {OUT=>"size\n4.9K\n88K"}], + ['header-7', '--debug --header=3 --to=iec', + {IN_PIPE=>"hello\nworld\nsize\n5000\n90000\n"}, + {OUT=>"hello\nworld\nsize\n4.9K\n88K"}], + # header, but no actual content + ['header-8', '--header=2 --to=iec', + {IN_PIPE=>"hello\nworld\n"}, + {OUT=>"hello\nworld"}], + # not enough header lines + ['header-9', '--header=3 --to=iec', + {IN_PIPE=>"hello\nworld\n"}, + {OUT=>"hello\nworld"}], + + + ## human_strtod testing + + # NO_DIGITS_FOUND + ['strtod-1', '--from=si "foo"', + {ERR=>"$prog: invalid number: 'foo'\n"}, + {EXIT=> 2}], + ['strtod-2', '--from=si ""', + {ERR=>"$prog: invalid number: ''\n"}, + {EXIT=> 2}], + + # FRACTION_NO_DIGITS_FOUND + ['strtod-5', '--from=si 12.', + {ERR=>"$prog: invalid number: '12.'\n"}, + {EXIT=>2}], + ['strtod-6', '--from=si 12.K', + {ERR=>"$prog: invalid number: '12.K'\n"}, + {EXIT=>2}], + + # whitespace is not allowed after decimal-point + ['strtod-6.1', '--from=si --delimiter=, "12. 2"', + {ERR=>"$prog: invalid number: '12. 2'\n"}, + {EXIT=>2}], + + # INVALID_SUFFIX + ['strtod-9', '--from=si 12.2J', + {ERR=>"$prog: invalid suffix in input: '12.2J'\n"}, + {EXIT=>2}], + + # VALID_BUT_FORBIDDEN_SUFFIX + ['strtod-10', '12M', + {ERR => "$prog: rejecting suffix " . + "in input: '12M' (consider using --from)\n"}, + {EXIT=>2}], + + # MISSING_I_SUFFIX + ['strtod-11', '--from=iec-i 12M', + {ERR => "$prog: missing 'i' suffix in input: " . + "'12M' (e.g Ki/Mi/Gi)\n"}, + {EXIT=>2}], + + # + # Test double_to_human() + # + + # 1K and smaller + ['dbl-to-human-1','--to=si 800', {OUT=>"800"}], + ['dbl-to-human-2','--to=si 0', {OUT=>"0"}], + ['dbl-to-human-2.1','--to=si 999', {OUT=>"999"}], + ['dbl-to-human-2.2','--to=si 1000', {OUT=>"1.0K"}], + #NOTE: the following are consistent with "ls -lh" output + ['dbl-to-human-2.3','--to=iec 999', {OUT=>"999"}], + ['dbl-to-human-2.4','--to=iec 1023', {OUT=>"1023"}], + ['dbl-to-human-2.5','--to=iec 1024', {OUT=>"1.0K"}], + ['dbl-to-human-2.6','--to=iec 1025', {OUT=>"1.1K"}], + ['dbl-to-human-2.7','--to=iec 0', {OUT=>"0"}], + # no "i" suffix if output has no suffix + ['dbl-to-human-2.8','--to=iec-i 0', {OUT=>"0"}], + + # values resulting in "N.Nx" output + ['dbl-to-human-3','--to=si 8000', {OUT=>"8.0K"}], + ['dbl-to-human-3.1','--to=si 8001', {OUT=>"8.1K"}], + ['dbl-to-human-4','--to=si --round=down 8001', {OUT=>"8.0K"}], + + ['dbl-to-human-5','--to=si --round=down 3500', {OUT=>"3.5K"}], + ['dbl-to-human-6','--to=si --round=nearest 3500', {OUT=>"3.5K"}], + ['dbl-to-human-7','--to=si --round=up 3500', {OUT=>"3.5K"}], + + ['dbl-to-human-8','--to=si --round=down 3501', {OUT=>"3.5K"}], + ['dbl-to-human-9','--to=si --round=nearest 3501', {OUT=>"3.5K"}], + ['dbl-to-human-10','--to=si --round=up 3501', {OUT=>"3.6K"}], + + ['dbl-to-human-11','--to=si --round=nearest 3550', {OUT=>"3.6K"}], + ['dbl-to-human-12','--to=si --from=si 999.89K', {OUT=>"1.0M"}], + ['dbl-to-human-13','--to=si --from=si 9.9K', {OUT=>"9.9K"}], + ['dbl-to-human-14','--to=si 9900', {OUT=>"9.9K"}], + ['dbl-to-human-15','--to=iec --from=si 3.3K', {OUT=>"3.3K"}], + ['dbl-to-human-16','--to=iec --round=down --from=si 3.3K', {OUT=>"3.2K"}], + + # values resulting in 'NNx' output + ['dbl-to-human-17','--to=si 9999', {OUT=>"10K"}], + ['dbl-to-human-18','--to=si --round=down 35000', {OUT=>"35K"}], + ['dbl-to-human-19','--to=iec 35000', {OUT=>"35K"}], + ['dbl-to-human-20','--to=iec --round=down 35000', {OUT=>"34K"}], + ['dbl-to-human-21','--to=iec 35000000', {OUT=>"34M"}], + ['dbl-to-human-22','--to=iec --round=down 35000000', {OUT=>"33M"}], + ['dbl-to-human-23','--to=si 35000001', {OUT=>"36M"}], + ['dbl-to-human-24','--to=si --from=si 9.99M', {OUT=>"10M"}], + ['dbl-to-human-25','--to=si --from=iec 9.99M', {OUT=>"11M"}], + ['dbl-to-human-25.1','--to=iec 99999', {OUT=>"98K"}], + + # values resulting in 'NNNx' output + ['dbl-to-human-26','--to=si 999000000000', {OUT=>"999G"}], + ['dbl-to-human-27','--to=iec 999000000000', {OUT=>"931G"}], + ['dbl-to-human-28','--to=si 123600000000000', {OUT=>"124T"}], + ['dbl-to-human-29','--to=si 998123', {OUT=>"999K"}], + ['dbl-to-human-30','--to=si --round=nearest 998123', {OUT=>"998K"}], + ['dbl-to-human-31','--to=si 99999', {OUT=>"100K"}], + ['dbl-to-human-32','--to=iec 102399', {OUT=>"100K"}], + ['dbl-to-human-33','--to=iec-i 102399', {OUT=>"100Ki"}], + + + # Default --round=from-zero + ['round-1','--to-unit=1024 -- 6000 -6000', + {OUT=>"6\n-6"}], + ['round-2','--to-unit=1024 -- 6000.0 -6000.0', + {OUT=>"5.9\n-5.9"}], + ['round-3','--to-unit=1024 -- 6000.00 -6000.00', + {OUT=>"5.86\n-5.86"}], + ['round-4','--to-unit=1024 -- 6000.000 -6000.000', + {OUT=>"5.860\n-5.860"}], + ['round-5','--to-unit=1024 -- 6000.0000 -6000.0000', + {OUT=>"5.8594\n-5.8594"}], + # --round=up + ['round-1-up','--round=up --to-unit=1024 -- 6000 -6000', + {OUT=>"6\n-5"}], + ['round-2-up','--round=up --to-unit=1024 -- 6000.0 -6000.0', + {OUT=>"5.9\n-5.8"}], + ['round-3-up','--round=up --to-unit=1024 -- 6000.00 -6000.00', + {OUT=>"5.86\n-5.85"}], + ['round-4-up','--round=up --to-unit=1024 -- 6000.000 -6000.000', + {OUT=>"5.860\n-5.859"}], + ['round-5-up','--round=up --to-unit=1024 -- 6000.0000 -6000.0000', + {OUT=>"5.8594\n-5.8593"}], + # --round=down + ['round-1-down','--round=down --to-unit=1024 -- 6000 -6000', + {OUT=>"5\n-6"}], + ['round-2-down','--round=down --to-unit=1024 -- 6000.0 -6000.0', + {OUT=>"5.8\n-5.9"}], + ['round-3-down','--round=down --to-unit=1024 -- 6000.00 -6000.00', + {OUT=>"5.85\n-5.86"}], + ['round-4-down','--round=down --to-unit=1024 -- 6000.000 -6000.000', + {OUT=>"5.859\n-5.860"}], + ['round-5-down','--round=down --to-unit=1024 -- 6000.0000 -6000.0000', + {OUT=>"5.8593\n-5.8594"}], + # --round=towards-zero + ['round-1-to-zero','--ro=towards-zero --to-u=1024 -- 6000 -6000', + {OUT=>"5\n-5"}], + ['round-2-to-zero','--ro=towards-zero --to-u=1024 -- 6000.0 -6000.0', + {OUT=>"5.8\n-5.8"}], + ['round-3-to-zero','--ro=towards-zero --to-u=1024 -- 6000.00 -6000.00', + {OUT=>"5.85\n-5.85"}], + ['round-4-to-zero','--ro=towards-zero --to-u=1024 -- 6000.000 -6000.000', + {OUT=>"5.859\n-5.859"}], + ['round-5-to-zero','--ro=towards-zero --to-u=1024 -- 6000.0000 -6000.0000', + {OUT=>"5.8593\n-5.8593"}], + # --round=nearest + ['round-1-near','--ro=nearest --to-u=1024 -- 6000 -6000', + {OUT=>"6\n-6"}], + ['round-2-near','--ro=nearest --to-u=1024 -- 6000.0 -6000.0', + {OUT=>"5.9\n-5.9"}], + ['round-3-near','--ro=nearest --to-u=1024 -- 6000.00 -6000.00', + {OUT=>"5.86\n-5.86"}], + ['round-4-near','--ro=nearest --to-u=1024 -- 6000.000 -6000.000', + {OUT=>"5.859\n-5.859"}], + ['round-5-near','--ro=nearest --to-u=1024 -- 6000.0000 -6000.0000', + {OUT=>"5.8594\n-5.8594"}], + + + # Leading zeros weren't handled appropriately before 8.24 + ['leading-1','0000000000000000000000000001', {OUT=>"1"}], + ['leading-2','.1', {OUT=>"0.1"}], + ['leading-3','bad.1', + {ERR => "$prog: invalid number: 'bad.1'\n"}, + {EXIT => 2}], + ['leading-4','..1', + {ERR => "$prog: invalid suffix in input: '..1'\n"}, + {EXIT => 2}], + ['leading-5','1.', + {ERR => "$prog: invalid number: '1.'\n"}, + {EXIT => 2}], + + # precision override + ['precision-1','--format=%.4f 9991239123 --to=si', {OUT=>"9.9913G"}], + ['precision-2','--format=%.1f 9991239123 --to=si', {OUT=>"10.0G"}], + ['precision-3','--format=%.1f 1', {OUT=>"1.0"}], + ['precision-4','--format=%.1f 1.12', {OUT=>"1.2"}], + ['precision-5','--format=%.1f 9991239123 --to-unit=G', {OUT=>"10.0"}], + ['precision-6','--format="% .1f" 9991239123 --to-unit=G', {OUT=>"10.0"}], + ['precision-7','--format=%.-1f 1.1', + {ERR => "$prog: invalid precision in format '%.-1f'\n"}, + {EXIT => 1}], + ['precision-8','--format=%.+1f 1.1', + {ERR => "$prog: invalid precision in format '%.+1f'\n"}, + {EXIT => 1}], + ['precision-9','--format="%. 1f" 1.1', + {ERR => "$prog: invalid precision in format '%. 1f'\n"}, + {EXIT => 1}], + + # debug warnings + ['debug-1', '--debug 4096', {OUT=>"4096"}, + {ERR=>"$prog: no conversion option specified\n"}], + # '--padding' is a valid conversion option - no warning should be printed + ['debug-1.1', '--debug --padding 10 4096', {OUT=>" 4096"}], + ['debug-2', '--debug --grouping --from=si 4.0K', {OUT=>"4000"}, + {ERR=>"$prog: grouping has no effect in this locale\n"}], + + # dev-debug messages - the actual messages don't matter + # just ensure the program works, and for code coverage testing. + ['devdebug-1', '---debug --from=si 4.9K', {OUT=>"4900"}, + {ERR=>""}, + {ERR_SUBST=>"s/.*//msg"}], + ['devdebug-2', '---debug 4900', {OUT=>"4900"}, + {ERR=>""}, + {ERR_SUBST=>"s/.*//msg"}], + ['devdebug-3', '---debug --from=auto 4Mi', {OUT=>"4194304"}, + {ERR=>""}, + {ERR_SUBST=>"s/.*//msg"}], + ['devdebug-4', '---debug --to=si 4000000', {OUT=>"4.0M"}, + {ERR=>""}, + {ERR_SUBST=>"s/.*//msg"}], + ['devdebug-5', '---debug --to=si --padding=5 4000000', {OUT=>" 4.0M"}, + {ERR=>""}, + {ERR_SUBST=>"s/.*//msg"}], + ['devdebug-6', '---debug --suffix=Foo 1234Foo', {OUT=>"1234Foo"}, + {ERR=>""}, + {ERR_SUBST=>"s/.*//msg"}], + ['devdebug-7', '---debug --suffix=Foo 1234', {OUT=>"1234Foo"}, + {ERR=>""}, + {ERR_SUBST=>"s/.*//msg"}], + ['devdebug-9', '---debug --grouping 10000', {OUT=>"10000"}, + {ERR=>""}, + {ERR_SUBST=>"s/.*//msg"}], + ['devdebug-10', '---debug --format %f 10000', {OUT=>"10000"}, + {ERR=>""}, + {ERR_SUBST=>"s/.*//msg"}], + ['devdebug-11', '---debug --format "%\'-10f" 10000',{OUT=>"10000 "}, + {ERR=>""}, + {ERR_SUBST=>"s/.*//msg"}], + + # Invalid parameters + ['help-1', '--foobar', + {ERR=>"$prog: unrecognized option\n$try"}, + {ERR_SUBST=>"s/option.*/option/; s/unknown/unrecognized/"}, + {EXIT=>1}], + + ## Format string - check error detection + ['fmt-err-1', '--format ""', + {ERR=>"$prog: format '' has no % directive\n"}, + {EXIT=>1}], + ['fmt-err-2', '--format "hello"', + {ERR=>"$prog: format 'hello' has no % directive\n"}, + {EXIT=>1}], + ['fmt-err-3', '--format "hello%"', + {ERR=>"$prog: format 'hello%' ends in %\n"}, + {EXIT=>1}], + ['fmt-err-4', '--format "%d"', + {ERR=>"$prog: invalid format '%d', " . + "directive must be %[0]['][-][N][.][N]f\n"}, + {EXIT=>1}], + ['fmt-err-5', '--format "% -43 f"', + {ERR=>"$prog: invalid format '% -43 f', " . + "directive must be %[0]['][-][N][.][N]f\n"}, + {EXIT=>1}], + ['fmt-err-6', '--format "%f %f"', + {ERR=>"$prog: format '%f %f' has too many % directives\n"}, + {EXIT=>1}], + ['fmt-err-7', '--format "%'.$limits->{LONG_OFLOW}.'f"', + {ERR=>"$prog: invalid format '%$limits->{LONG_OFLOW}f'". + " (width overflow)\n"}, + {EXIT=>1}], + ['fmt-err-9', '--format "%f" --grouping', + {ERR=>"$prog: --grouping cannot be combined with --format\n"}, + {EXIT=>1}], + ['fmt-err-10', '--format "%\'f" --to=si', + {ERR=>"$prog: grouping cannot be combined with --to\n"}, + {EXIT=>1}], + ['fmt-err-11', '--debug --format "%\'f" 5000', {OUT=>"5000"}, + {ERR=>"$prog: grouping has no effect in this locale\n"}], + + ## Format string - check some corner cases + ['fmt-1', '--format "%% %f" 5000', {OUT=>"%%5000"}], + ['fmt-2', '--format "%f %%" 5000', {OUT=>"5000 %%"}], + + ['fmt-3', '--format "--%f--" 5000000', {OUT=>"--5000000--"}], + ['fmt-4', '--format "--%f--" --to=si 5000000', {OUT=>"--5.0M--"}], + + ['fmt-5', '--format "--%10f--" --to=si 5000000',{OUT=>"-- 5.0M--"}], + ['fmt-6', '--format "--%-10f--" --to=si 5000000',{OUT=>"--5.0M --"}], + ['fmt-7', '--format "--%10f--" 5000000',{OUT=>"-- 5000000--"}], + ['fmt-8', '--format "--%-10f--" 5000000',{OUT=>"--5000000 --"}], + + # too-short width + ['fmt-9', '--format "--%5f--" 5000000',{OUT=>"--5000000--"}], + + # Format + Suffix + ['fmt-10', '--format "--%10f--" --suffix Foo 50', {OUT=>"-- 50Foo--"}], + ['fmt-11', '--format "--%-10f--" --suffix Foo 50',{OUT=>"--50Foo --"}], + + # Grouping in C locale - no grouping effect + ['fmt-12', '--format "%\'f" 50000',{OUT=>"50000"}], + ['fmt-13', '--format "%\'10f" 50000', {OUT=>" 50000"}], + ['fmt-14', '--format "%\'-10f" 50000',{OUT=>"50000 "}], + + # Very large format strings + ['fmt-15', '--format "--%100000f--" --to=si 4200', + {OUT=>"--" . " " x 99996 . "4.2K--" }], + + # --format padding overrides --padding + ['fmt-16', '--format="%6f" --padding=66 1234',{OUT=>" 1234"}], + + # zero padding + ['fmt-17', '--format="%06f" 1234',{OUT=>"001234"}], + # also support spaces (which are ignored as spacing is handled separately) + ['fmt-18', '--format="%0 6f" 1234',{OUT=>"001234"}], + # handle generic padding in combination + ['fmt-22', '--format="%06f" --padding=7 1234',{OUT=>" 001234"}], + ['fmt-23', '--format="%06f" --padding=-7 1234',{OUT=>"001234 "}], + + + ## Check all errors again, this time with --invalid=fail + ## Input will be printed without conversion, + ## and exit code will be 2 + ['ign-err-1', '--invalid=fail 4J', + {ERR => "$prog: invalid suffix in input: '4J'\n"}, + {OUT => "4J\n"}, + {EXIT => 2}], + ['ign-err-2', '--invalid=fail 4M', + {ERR => "$prog: rejecting suffix " . + "in input: '4M' (consider using --from)\n"}, + {OUT => "4M\n"}, + {EXIT => 2}], + ['ign-err-3', '--invalid=fail --from=si 4MJ', + {ERR => "$prog: invalid suffix in input '4MJ': 'J'\n"}, + {OUT => "4MJ\n"}, + {EXIT => 2}], + ['ign-err-4', '--invalid=fail --suffix=Foo --to=si 7000FooF', + {ERR => "$prog: invalid suffix in input: '7000FooF'\n"}, + {OUT => "7000FooF\n"}, + {EXIT => 2}], + ['ign-err-5','--invalid=fail --field 3 --from=auto "Hello 40M World 90G"', + {ERR => "$prog: invalid number: 'World'\n"}, + {OUT => "Hello 40M World 90G\n"}, + {EXIT => 2}], + ['ign-err-7', '--invalid=fail --from=si "foo"', + {ERR => "$prog: invalid number: 'foo'\n"}, + {OUT => "foo\n"}, + {EXIT=> 2}], + ['ign-err-8', '--invalid=fail 12M', + {ERR => "$prog: rejecting suffix " . + "in input: '12M' (consider using --from)\n"}, + {OUT => "12M\n"}, + {EXIT => 2}], + ['ign-err-9', '--invalid=fail --from=iec-i 12M', + {ERR => "$prog: missing 'i' suffix in input: " . + "'12M' (e.g Ki/Mi/Gi)\n"}, + {OUT => "12M\n"}, + {EXIT=>2}], + + ## Ignore Errors with multiple conversions + ['ign-err-m1', '--invalid=ignore --to=si 1000 2000 bad 3000', + {OUT => "1.0K\n2.0K\nbad\n3.0K"}, + {EXIT => 0}], + ['ign-err-m1.1', '--invalid=ignore --to=si', + {IN_PIPE => "1000\n2000\nbad\n3000\n"}, + {OUT => "1.0K\n2.0K\nbad\n3.0K"}, + {EXIT => 0}], + ['ign-err-m1.3', '--invalid=fail --debug --to=si 1000 2000 3000', + {OUT => "1.0K\n2.0K\n3.0K"}, + {EXIT => 0}], + ['ign-err-m2', '--invalid=fail --to=si 1000 Foo 3000', + {OUT => "1.0K\nFoo\n3.0K\n"}, + {ERR => "$prog: invalid number: 'Foo'\n"}, + {EXIT => 2}], + ['ign-err-m2.1', '--invalid=warn --to=si', + {IN_PIPE => "1000\nFoo\n3000\n"}, + {OUT => "1.0K\nFoo\n3.0K"}, + {ERR => "$prog: invalid number: 'Foo'\n"}, + {EXIT => 0}], + + # --debug will trigger a final warning at EOF + ['ign-err-m2.2', '--invalid=fail --debug --to=si 1000 Foo 3000', + {OUT => "1.0K\nFoo\n3.0K\n"}, + {ERR => "$prog: invalid number: 'Foo'\n" . + "$prog: failed to convert some of the input numbers\n"}, + {EXIT => 2}], + + ['ign-err-m3', '--invalid=fail --field 2 --from=si --to=iec', + {IN_PIPE => "A 1K x\nB 2M y\nC 3G z\n"}, + {OUT => "A 1000 x\nB 2.0M y\nC 2.8G z"}, + {EXIT => 0}], + # invalid input on one of the fields + ['ign-err-m3.1', '--invalid=fail --field 2 --from=si --to=iec', + {IN_PIPE => "A 1K x\nB Foo y\nC 3G z\n"}, + {OUT => "A 1000 x\nB Foo y\nC 2.8G z\n"}, + {ERR => "$prog: invalid number: 'Foo'\n"}, + {EXIT => 2}], + ); + +# test null-terminated lines +my @NullDelim_Tests = + ( + # Input from STDIN + ['z1', '-z --to=iec', + {IN_PIPE => "1025\x002048\x00"}, {OUT=>"1.1K\x002.0K\x00"}], + + # Input from the commandline - terminated by NULL vs NL + ['z3', ' --to=iec 1024', {OUT=>"1.0K\n"}], + ['z2', '-z --to=iec 1024', {OUT=>"1.0K\x00"}], + + # Input from STDIN, with fields + ['z4', '-z --field=3 --to=si', + {IN_PIPE => "A B 1001 C\x00" . + "D E 2002 F\x00"}, + {OUT => "A B 1.1K C\x00" . + "D E 2.1K F\x00"}], + + # Input from STDIN, with fields and embedded NL + ['z5', '-z --field=3 --to=si', + {IN_PIPE => "A\nB 1001 C\x00" . + "D E\n2002 F\x00"}, + {OUT => "A B 1.1K C\x00" . + "D E 2.1K F\x00"}], + ); + +my @Limit_Tests = + ( + # Large Values + ['large-1','1000000000000000', {OUT=>"1000000000000000"}], + # 18 digits is OK + ['large-2','1000000000000000000', {OUT=>"1000000000000000000"}], + # 19 digits is too much (without output scaling) + ['large-3','10000000000000000000', + {ERR => "$prog: value too large to be printed: '1e+19' " . + "(consider using --to)\n"}, + {EXIT=>2}], + ['large-4','1000000000000000000.0', + {ERR => "$prog: value/precision too large to be printed: " . + "'1e+18/1' (consider using --to)\n"}, + {EXIT=>2}], + + + # Test input: + # Up to 33 digits is OK. + ['large-3.1', '--to=si 1', {OUT=> "1"}], + ['large-3.2', '--to=si 10', {OUT=> "10"}], + ['large-3.3', '--to=si 100', {OUT=> "100"}], + ['large-3.4', '--to=si 1000', {OUT=>"1.0K"}], + ['large-3.5', '--to=si 10000', {OUT=> "10K"}], + ['large-3.6', '--to=si 100000', {OUT=>"100K"}], + ['large-3.7', '--to=si 1000000', {OUT=>"1.0M"}], + ['large-3.8', '--to=si 10000000', {OUT=> "10M"}], + ['large-3.9', '--to=si 100000000', {OUT=>"100M"}], + ['large-3.10','--to=si 1000000000', {OUT=>"1.0G"}], + ['large-3.11','--to=si 10000000000', {OUT=> "10G"}], + ['large-3.12','--to=si 100000000000', {OUT=>"100G"}], + ['large-3.13','--to=si 1000000000000', {OUT=>"1.0T"}], + ['large-3.14','--to=si 10000000000000', {OUT=> "10T"}], + ['large-3.15','--to=si 100000000000000', {OUT=>"100T"}], + ['large-3.16','--to=si 1000000000000000', {OUT=>"1.0P"}], + ['large-3.17','--to=si 10000000000000000', {OUT=> "10P"}], + ['large-3.18','--to=si 100000000000000000', {OUT=>"100P"}], + ['large-3.19','--to=si 1000000000000000000', {OUT=>"1.0E"}], + ['large-3.20','--to=si 10000000000000000000', {OUT=> "10E"}], + ['large-3.21','--to=si 210000000000000000000', {OUT=>"210E"}], + ['large-3.22','--to=si 3210000000000000000000', {OUT=>"3.3Z"}], + ['large-3.23','--to=si 43210000000000000000000', {OUT=> "44Z"}], + ['large-3.24','--to=si 543210000000000000000000', {OUT=>"544Z"}], + ['large-3.25','--to=si 6543210000000000000000000', {OUT=>"6.6Y"}], + ['large-3.26','--to=si 76543210000000000000000000', {OUT=> "77Y"}], + ['large-3.27','--to=si 876543210000000000000000000', {OUT=>"877Y"}], + ['large-3.28','--to=si 9876543210000000000000000000', {OUT=>"9.9R"}], + ['large-3.29','--to=si 19876543210000000000000000000', {OUT=> "20R"}], + ['large-3.30','--to=si 219876543210000000000000000000', {OUT=>"220R"}], + ['large-3.31','--to=si 3219876543210000000000000000000', {OUT=>"3.3Q"}], + ['large-3.32','--to=si 43219876543210000000000000000000', {OUT=> "44Q"}], + ['large-3.33','--to=si 543219876543210000000000000000000', {OUT=>"544Q"}], + + # More than 33 digits is not OK + ['large-3.34','--to=si 6543219876543210000000000000000000', + {ERR => "$prog: value too large to be converted: " . + "'6543219876543210000000000000000000'\n"}, + {EXIT => 2}], + + # Test Output + ['large-4.1', '--from=si 9.7M', {OUT=>"9700000"}], + ['large-4.2', '--from=si 10M', {OUT =>"10000000"}], + ['large-4.3', '--from=si 200M', {OUT =>"200000000"}], + ['large-4.4', '--from=si 3G', {OUT =>"3000000000"}], + ['large-4.5', '--from=si 40G', {OUT =>"40000000000"}], + ['large-4.6', '--from=si 500G', {OUT =>"500000000000"}], + ['large-4.7', '--from=si 6T', {OUT =>"6000000000000"}], + ['large-4.8', '--from=si 70T', {OUT =>"70000000000000"}], + ['large-4.9', '--from=si 800T', {OUT =>"800000000000000"}], + ['large-4.10','--from=si 9P', {OUT =>"9000000000000000"}], + ['large-4.11','--from=si 10P', {OUT =>"10000000000000000"}], + ['large-4.12','--from=si 200P', {OUT =>"200000000000000000"}], + ['large-4.13','--from=si 3E', {OUT =>"3000000000000000000"}], + + # More than 18 digits of output without scaling - no good. + ['large-4.14','--from=si 40E', + {ERR => "$prog: value too large to be printed: '4e+19' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.15','--from=si 500E', + {ERR => "$prog: value too large to be printed: '5e+20' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.16','--from=si 6Z', + {ERR => "$prog: value too large to be printed: '6e+21' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.17','--from=si 70Z', + {ERR => "$prog: value too large to be printed: '7e+22' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.18','--from=si 800Z', + {ERR => "$prog: value too large to be printed: '8e+23' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.19','--from=si 9Y', + {ERR => "$prog: value too large to be printed: '9e+24' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.20','--from=si 10Y', + {ERR => "$prog: value too large to be printed: '1e+25' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-4.21','--from=si 200Y', + {ERR => "$prog: value too large to be printed: '2e+26' " . + "(consider using --to)\n"}, + {EXIT => 2}], + + ['large-5.1','--to=si 1000000000000000000', {OUT=>"1.0E"}], + ['large-5','--from=si --to=si 2E', {OUT=>"2.0E"}], + ['large-6','--from=si --to=si 3.4Z', {OUT=>"3.4Z"}], + ['large-7','--from=si --to=si 80Y', {OUT=>"80Y"}], + ['large-8','--from=si --to=si 9000Z', {OUT=>"9.0Y"}], + + ['large-10','--from=si --to=si 999Q', {OUT=>"999Q"}], + ['large-11','--from=si --to=iec 999Q', {OUT=>"789Q"}], + ['large-12','--from=si --round=down --to=iec 999Q', {OUT=>"788Q"}], + + # units can also affect the output + ['large-13','--from=si --from-unit=1000000 9P', + {ERR => "$prog: value too large to be printed: '9e+21' " . + "(consider using --to)\n"}, + {EXIT => 2}], + ['large-13.1','--from=si --from-unit=1000000 --to=si 9P', {OUT=>"9.0Z"}], + + # Numbers>999Q are never acceptable, regardless of scaling + ['large-14','--from=si --to=si 999Q', {OUT=>"999Q"}], + ['large-14.1','--from=si --to=si 1000Q', + {ERR => "$prog: value too large to be printed: '1e+33' " . + "(cannot handle values > 999Q)\n"}, + {EXIT => 2}], + ['large-14.2','--from=si --to=si --from-unit=10000 1Q', + {ERR => "$prog: value too large to be printed: '1e+34' " . + "(cannot handle values > 999Q)\n"}, + {EXIT => 2}], + + # intmax_t overflow when rounding caused this to fail before 8.24 + ['large-15',$limits->{INTMAX_OFLOW}, {OUT=>$limits->{INTMAX_OFLOW}}], + ['large-16','9.300000000000000000', {OUT=>'9.300000000000000000'}], + + # INTEGRAL_OVERFLOW + ['strtod-3', '--from=si "1234567890123456789012345678901234567890'. + '1234567890123456789012345678901234567890"', + {ERR=>"$prog: value too large to be converted: '" . + "1234567890123456789012345678901234567890" . + "1234567890123456789012345678901234567890'\n", + }, + {EXIT=> 2}], + + # FRACTION_OVERFLOW + ['strtod-7', '--from=si "12.1234567890123456789012345678901234567890'. + '1234567890123456789012345678901234567890"', + {ERR=>"$prog: value too large to be converted: '" . + "12.1234567890123456789012345678901234567890" . + "1234567890123456789012345678901234567890'\n", + }, + {EXIT=> 2}], + + ['debug-4', '--to=si --debug 12345678901234567890', + {OUT=>"13E"}, + {ERR=>"$prog: large input value '12345678901234567890':" . + " possible precision loss\n"}], + ['debug-5', '--to=si --from=si --debug 1.12345678901234567890Y', + {OUT=>"1.2Y"}, + {ERR=>"$prog: large input value '1.12345678901234567890Y':" . + " possible precision loss\n"}], + + ['ign-err-10','--invalid=fail 10000000000000000000', + {ERR => "$prog: value too large to be printed: '1e+19' " . + "(consider using --to)\n"}, + {OUT => "10000000000000000000\n"}, + {EXIT=>2}], + ['ign-err-11','--invalid=fail --to=si 6543219876543210000000000000000000', + {ERR => "$prog: value too large to be converted: " . + "'6543219876543210000000000000000000'\n"}, + {OUT => "6543219876543210000000000000000000\n"}, + {EXIT => 2}], + ); +# Restrict these tests to systems with LDBL_DIG == 18 +(system "$prog ---debug 1 2>&1|grep 'MAX_UNSCALED_DIGITS: 18' > /dev/null") == 0 + and push @Tests, @Limit_Tests; + +my @Locale_Tests = + ( + # Locale that supports grouping, but without '--grouping' parameter + ['lcl-grp-1', '--from=si 7M', {OUT=>"7000000"}, + {ENV=>"LC_ALL=$locale"}], + + # Locale with grouping + ['lcl-grp-2', '--from=si --grouping 7M', {OUT=>"7 000 000"}, + {ENV=>"LC_ALL=$locale"}], + + # Locale with grouping and debug - no debug warning message + ['lcl-grp-3', '--from=si --debug --grouping 7M', {OUT=>"7 000 000"}, + {ENV=>"LC_ALL=$locale"}], + + # Input with locale'd decimal-point + ['lcl-stdtod-1', '--from=si 12,2K', {OUT=>"12200"}, + {ENV=>"LC_ALL=$locale"}], + + ['lcl-dbl-to-human-1', '--to=si 1100', {OUT=>"1,1K"}, + {ENV=>"LC_ALL=$locale"}], + + # Format + Grouping + ['lcl-fmt-1', '--format "%\'f" 50000',{OUT=>"50 000"}, + {ENV=>"LC_ALL=$locale"}], + ['lcl-fmt-2', '--format "--%\'10f--" 50000', {OUT=>"-- 50 000--"}, + {ENV=>"LC_ALL=$locale"}], + ['lcl-fmt-3', '--format "--%\'-10f--" 50000',{OUT=>"--50 000 --"}, + {ENV=>"LC_ALL=$locale"}], + ['lcl-fmt-4', '--format "--%-10f--" --to=si 5000000', + {OUT=>"--5,0M --"}, + {ENV=>"LC_ALL=$locale"}], + # handle zero/grouping in combination + ['lcl-fmt-5', '--format="%\'06f" 1234',{OUT=>"01 234"}, + {ENV=>"LC_ALL=$locale"}], + ['lcl-fmt-6', '--format="%0\'6f" 1234',{OUT=>"01 234"}, + {ENV=>"LC_ALL=$locale"}], + ['lcl-fmt-7', '--format="%0\'\'6f" 1234',{OUT=>"01 234"}, + {ENV=>"LC_ALL=$locale"}], + + ); +if ($locale ne 'C') + { + # Reset locale to 'C' if LOCALE_FR_UTF8 doesn't output as expected + # as determined by the separate printf program. + open(LOC_NUM, "env LC_ALL=$locale printf \"%'d\" 1234|") + or die "Can't fork command: $!"; + my $loc_num = <LOC_NUM>; + close(LOC_NUM) || die "Failed to read grouped number from printf"; + if ($loc_num ne '1 234') + { + warn "skipping locale grouping tests as 1234 groups like $loc_num\n"; + $locale = 'C'; + } + } +push @Tests, @Locale_Tests if $locale ne 'C'; + +## Check all valid/invalid suffixes +foreach my $suf ( 'A' .. 'Z', 'a' .. 'z' ) { + if ( $suf =~ /^[KMGTPEZYRQ]$/ ) + { + push @Tests, ["auto-suf-si-$suf","--from=si --to=si 1$suf", + {OUT=>"1.0$suf"}]; + push @Tests, ["auto-suf-iec-$suf","--from=iec --to=iec 1$suf", + {OUT=>"1.0$suf"}]; + push @Tests, ["auto-suf-auto-$suf","--from=auto --to=iec 1${suf}i", + {OUT=>"1.0$suf"}]; + push @Tests, ["auto-suf-iec-to-ieci-$suf","--from=iec --to=iec-i 1${suf}", + {OUT=>"1.0${suf}i"}]; + push @Tests, ["auto-suf-ieci-to-iec-$suf", + "--from=iec-i --to=iec 1${suf}i",{OUT=>"1.0${suf}"}]; + } + else + { + push @Tests, ["auto-suf-si-$suf","--from=si --to=si 1$suf", + {ERR=>"$prog: invalid suffix in input: '1${suf}'\n"}, + {EXIT=>2}]; + } +} + +# Prepend the command line argument and append a newline to end +# of each expected 'OUT' string. +my $t; + +Test: +foreach $t (@Tests) + { + # Don't fiddle with expected OUT string if there's a nonzero exit status. + foreach my $e (@$t) + { + ref $e eq 'HASH' && exists $e->{EXIT} && $e->{EXIT} + and next Test; + } + + foreach my $e (@$t) + { + ref $e eq 'HASH' && exists $e->{OUT} + and $e->{OUT} .= "\n" + } + } + +# Add test for null-terminated lines (after adjusting the OUT string, above). +push @Tests, @NullDelim_Tests; + +my $save_temps = $ENV{SAVE_TEMPS}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/paste.pl b/tests/misc/paste.pl new file mode 100755 index 0000000..e14ef1c --- /dev/null +++ b/tests/misc/paste.pl @@ -0,0 +1,74 @@ +#!/usr/bin/perl +# Test paste. + +# Copyright (C) 2003-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; + +(my $program_name = $0) =~ s|.*/||; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my $prog = 'paste'; +my $msg = "$prog: delimiter list ends with an unescaped backslash: "; + +my @Tests = + ( + # Ensure that paste properly handles files lacking a final newline. + ['no-nl-1', {IN=>"a"}, {IN=>"b"}, {OUT=>"a\tb\n"}], + ['no-nl-2', {IN=>"a\n"}, {IN=>"b"}, {OUT=>"a\tb\n"}], + ['no-nl-3', {IN=>"a"}, {IN=>"b\n"}, {OUT=>"a\tb\n"}], + ['no-nl-4', {IN=>"a\n"}, {IN=>"b\n"}, {OUT=>"a\tb\n"}], + + ['zno-nl-1', '-z', {IN=>"a"}, {IN=>"b"}, {OUT=>"a\tb\0"}], + ['zno-nl-2', '-z', {IN=>"a\0"}, {IN=>"b"}, {OUT=>"a\tb\0"}], + ['zno-nl-3', '-z', {IN=>"a"}, {IN=>"b\0"}, {OUT=>"a\tb\0"}], + ['zno-nl-4', '-z', {IN=>"a\0"}, {IN=>"b\0"}, {OUT=>"a\tb\0"}], + + # Same as above, but with a two lines in each input file and + # the addition of the -d option to make SPACE be the output delimiter. + ['no-nla1', '-d" "', {IN=>"1\na"}, {IN=>"2\nb"}, {OUT=>"1 2\na b\n"}], + ['no-nla2', '-d" "', {IN=>"1\na\n"}, {IN=>"2\nb"}, {OUT=>"1 2\na b\n"}], + ['no-nla3', '-d" "', {IN=>"1\na"}, {IN=>"2\nb\n"}, {OUT=>"1 2\na b\n"}], + ['no-nla4', '-d" "', {IN=>"1\na\n"}, {IN=>"2\nb\n"}, {OUT=>"1 2\na b\n"}], + + ['zno-nla1', '-zd" "', {IN=>"1\0a"}, {IN=>"2\0b"}, {OUT=>"1 2\0a b\0"}], + ['zno-nla2', '-zd" "', {IN=>"1\0a\0"}, {IN=>"2\0b"}, {OUT=>"1 2\0a b\0"}], + ['zno-nla3', '-zd" "', {IN=>"1\0a"}, {IN=>"2\0b\0"}, {OUT=>"1 2\0a b\0"}], + ['zno-nla4', '-zd" "', {IN=>"1\0a\0"}, {IN=>"2\0b\0"}, {OUT=>"1 2\0a b\0"}], + + # Specifying a delimiter with a trailing backslash would overrun a + # malloc'd buffer. + ['delim-bs1', q!-d'\'!, {IN=>{'a'x50=>''}}, {EXIT => 1}, + # We print a single backslash into the expected output + {ERR => $msg . q!\\! . "\n"} ], + + # Prior to coreutils-5.1.2, this sort of abuse would make paste + # scribble on command-line arguments. With paste from coreutils-5.1.0, + # this example would mangle the first file name argument, if it contains + # accepted backslash-escapes: + # $ paste -d\\ '123\b\b\b.....@' 2>&1 |cat -A + # paste: 23^H^H^H.....@...@: No such file or directory$ + ['delim-bs2', q!-d'\'!, {IN=>{'123\b\b\b.....@'=>''}}, {EXIT => 1}, + {ERR => $msg . q!\\! . "\n"} ], + ); + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/pathchk.sh b/tests/misc/pathchk.sh new file mode 100755 index 0000000..b0892ac --- /dev/null +++ b/tests/misc/pathchk.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# pathchk tests + +# Copyright (C) 2002-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ pathchk +skip_if_root_ + +touch file || framework_failure_ + + +# This should exit nonzero. Before 2.0.13, it gave a diagnostic, +# but exited successfully. +returns_ 1 pathchk file/x > /dev/null 2>&1 || fail=1 + +# This should exit nonzero. Through 5.3.0 it exited with status zero. +returns_ 1 pathchk -p '' > /dev/null 2>&1 || fail=1 + +# This tests the new -P option. +returns_ 1 pathchk -P '' > /dev/null 2>&1 || fail=1 +returns_ 1 pathchk -P -- - > /dev/null 2>&1 || fail=1 +returns_ 1 pathchk -p -P x/- > /dev/null 2>&1 || fail=1 + +Exit $fail diff --git a/tests/misc/printenv.sh b/tests/misc/printenv.sh new file mode 100755 index 0000000..708f92b --- /dev/null +++ b/tests/misc/printenv.sh @@ -0,0 +1,83 @@ +#!/bin/sh +# Verify behavior of printenv. + +# Copyright (C) 2009-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ printenv + +# Without arguments, printenv behaves like env. Some shells provide +# printenv as a builtin, so we must invoke it via "env". +# But beware of $_, set by many shells to the last command run. +# Also, filter out LD_PRELOAD, which is set when running under valgrind. +# Note the apparently redundant "env env": this is to ensure to get +# env's output the same way as that of printenv and works around a bug +# on aarch64 at least where libc's execvp reverses the order of the +# output. +env -- env | grep -Ev '^(_|LD_PRELOAD)=' > exp || framework_failure_ +env -- printenv | grep -Ev '^(_|LD_PRELOAD)=' > out || fail=1 +compare exp out || fail=1 + +# POSIX is clear that environ may, but need not be, sorted. +# Environment variable values may contain newlines, which cannot be +# observed by merely inspecting output from printenv. +if env -- printenv | grep '^ENV_TEST' >/dev/null ; then + skip_ "environment has potential interference from ENV_TEST*" +fi + +# Printing a single variable's value. +returns_ 1 env -- printenv ENV_TEST > out || fail=1 +compare /dev/null out || fail=1 +echo a > exp || framework_failure_ +ENV_TEST=a env -- printenv ENV_TEST > out || fail=1 +compare exp out || fail=1 + +# Printing multiple variables. Order follows command line. +ENV_TEST1=a ENV_TEST2=b env -- printenv ENV_TEST2 ENV_TEST1 ENV_TEST2 > out \ + || fail=1 +ENV_TEST1=a ENV_TEST2=b env -- printenv ENV_TEST1 ENV_TEST2 >> out || fail=1 +cat <<EOF > exp || framework_failure_ +b +a +b +a +b +EOF +compare exp out || fail=1 + +# Exit status reflects missing variable, but remaining arguments processed. +export ENV_TEST1=a +returns_ 1 env -- printenv ENV_TEST2 ENV_TEST1 > out || fail=1 +returns_ 1 env -- printenv ENV_TEST1 ENV_TEST2 >> out || fail=1 +unset ENV_TEST1 +cat <<EOF > exp || framework_failure_ +a +a +EOF +compare exp out || fail=1 + +# Non-standard environment variable name. Shells won't create it, but +# env can, and printenv must be able to deal with it. +echo b > exp || framework_failure_ +env -- -a=b printenv -- -a > out || fail=1 +compare exp out || fail=1 + +# Silently reject invalid env-var names. +# Bug present through coreutils 8.0. +returns_ 1 env a=b=c printenv a=b > out || fail=1 +compare /dev/null out || fail=1 + +Exit $fail diff --git a/tests/misc/read-errors.sh b/tests/misc/read-errors.sh new file mode 100755 index 0000000..7f395bb --- /dev/null +++ b/tests/misc/read-errors.sh @@ -0,0 +1,95 @@ +#!/bin/sh +# Make sure all of these programs diagnose read errors + +# Copyright (C) 2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src + +! cat . >/dev/null 2>&1 || skip_ "Need unreadable directories" + +echo "\ +basenc --base32 . +basenc -d --base64 . +cat . +cksum -a blake2b . +cksum -a bsd . +cksum -a crc . +cksum -a md5 . +cksum -a sha1 . +cksum -a sha224 . +cksum -a sha256 . +cksum -a sha384 . +cksum -a sha512 . +cksum -a sm3 . +cksum -a sysv . +comm . . +csplit . 1 +cut -c1 . +cut -f1 . +date -f . +dd if=. +dircolors . +expand . +factor < . +fmt . +fold . +head -n1 . +head -n-1 . +head -c1 . +head -c-1 . +join . . +nl . +numfmt < . +od . +paste . +pr . +ptx . +shuf -r . +shuf -n1 . +sort . +split -l1 . +split -b1 . +split -C1 . +split -n1 . +split -nl/1 . +split -nr/1 . +tac . +tail -n1 . +tail -c1 . +tail -n+1 . +tail -c+1 . +tee < . +tr 1 1 < . +tsort . +unexpand . +uniq . +uniq -c . +wc . +wc -c . +wc -l . +" | +sort -k 1b,1 > all_readers || framework_failure_ + +printf '%s\n' $built_programs | +sort -k 1b,1 > built_programs || framework_failure_ + +join all_readers built_programs > built_readers || framework_failure_ + +while read reader; do + eval $reader >/dev/null && { fail=1; echo "$reader: exited with 0" >&2; } +done < built_readers + +Exit $fail diff --git a/tests/misc/realpath.sh b/tests/misc/realpath.sh new file mode 100755 index 0000000..251ea19 --- /dev/null +++ b/tests/misc/realpath.sh @@ -0,0 +1,111 @@ +#!/bin/sh +# Validate realpath operation + +# Copyright (C) 2011-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ realpath + +stat_single=$(stat -c %d:%i /) || framework_failure_ +stat_double=$(stat -c %d:%i //) || framework_failure_ +double_slash=// +if test x"$stat_single" = x"$stat_double"; then + double_slash=/ +fi +nl=' +' + +test -d /dev || framework_failure_ + +# Setup dir, file, symlink structure + +mkdir -p dir1/dir2 || framework_failure_ +ln -s dir1/dir2 ldir2 || framework_failure_ +touch dir1/f dir1/dir2/f || framework_failure_ +ln -s / one || framework_failure_ +ln -s // two || framework_failure_ +ln -s /// three || framework_failure_ + +# Basic operation +realpath -Pqz . >/dev/null || fail=1 +# Operand is required +returns_ 1 realpath >/dev/null || fail=1 +returns_ 1 realpath --relative-base . --relative-to . || fail=1 +returns_ 1 realpath --relative-base . || fail=1 + +# -e --relative-* require directories +returns_ 1 realpath -e --relative-to=dir1/f --relative-base=. . || fail=1 +realpath -e --relative-to=dir1/ --relative-base=. . || fail=1 + +# Note NUL params are unconditionally rejected by canonicalize_filename_mode +returns_ 1 realpath -m '' || fail=1 +returns_ 1 realpath --relative-base= --relative-to=. . || fail=1 + +# symlink resolution +this=$(realpath .) +test "$(realpath ldir2/..)" = "$this/dir1" || fail=1 +test "$(realpath -L ldir2/..)" = "$this" || fail=1 +test "$(realpath -s ldir2)" = "$this/ldir2" || fail=1 + +# relative string handling +test $(realpath -m --relative-to=prefix prefixed/1) = '../prefixed/1' || fail=1 +test $(realpath -m --relative-to=prefixed prefix/1) = '../prefix/1' || fail=1 +test $(realpath -m --relative-to=prefixed prefixed/1) = '1' || fail=1 + +# Ensure no redundant trailing '/' present, as was the case in v8.15 +test $(realpath -sm --relative-to=/usr /) = '..' || fail=1 +# Ensure no redundant leading '../' present, as was the case in v8.15 +test $(realpath -sm --relative-to=/ /usr) = 'usr' || fail=1 + +# Ensure --relative-base works +out=$(realpath -sm --relative-base=/usr --relative-to=/usr /tmp /usr) || fail=1 +test "$out" = "/tmp$nl." || fail=1 +out=$(realpath -sm --relative-base=/ --relative-to=/ / /usr) || fail=1 +test "$out" = ".${nl}usr" || fail=1 +# --relative-to defaults to the value of --relative-base +out=$(realpath -sm --relative-base=/usr /tmp /usr) || fail=1 +test "$out" = "/tmp$nl." || fail=1 +out=$(realpath -sm --relative-base=/ / /usr) || fail=1 +test "$out" = ".${nl}usr" || fail=1 +# For now, --relative-base must be a prefix of --relative-to, or all output +# will be absolute (compare to MacOS 'relpath -d dir start end'). +out=$(realpath -sm --relative-base=/usr/local --relative-to=/usr \ + /usr /usr/local) || fail=1 +test "$out" = "/usr${nl}/usr/local" || fail=1 + +# Ensure // is handled correctly. +test "$(realpath / // ///)" = "/$nl$double_slash$nl/" || fail=1 +test "$(realpath one two three)" = "/$nl$double_slash$nl/" || fail=1 +out=$(realpath -sm --relative-to=/ / // /dev //dev) || fail=1 +if test $double_slash = //; then + test "$out" = ".$nl//${nl}dev$nl//dev" || fail=1 +else + test "$out" = ".$nl.${nl}dev${nl}dev" || fail=1 +fi +out=$(realpath -sm --relative-to=// / // /dev //dev) || fail=1 +if test $double_slash = //; then + test "$out" = "/$nl.$nl/dev${nl}dev" || fail=1 +else + test "$out" = ".$nl.${nl}dev${nl}dev" || fail=1 +fi +out=$(realpath --relative-base=/ --relative-to=// / //) || fail=1 +if test $double_slash = //; then + test "$out" = "/$nl//" || fail=1 +else + test "$out" = ".$nl." || fail=1 +fi + +Exit $fail diff --git a/tests/misc/selinux.sh b/tests/misc/selinux.sh new file mode 100755 index 0000000..35ad5f8 --- /dev/null +++ b/tests/misc/selinux.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# Test SELinux-related options. + +# Copyright (C) 2007-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ chcon cp ls mv stat + +require_root_ +require_selinux_ +skip_if_mcstransd_is_running_ + +# Create a regular file, dir, fifo. +touch f || framework_failure_ +mkdir d s1 s2 || framework_failure_ +mkfifo_or_skip_ p + + +# special context that works both with and without mcstransd +ctx='root:object_r:tmp_t' +mls_enabled_ && ctx="$ctx:s0" + +chcon $ctx f d p || skip_ "Failed to set context: $ctx" + +# inspect that context with both ls -Z and stat. +for i in d f p; do + c=$(ls -dogZ $i|cut -d' ' -f3); test x$c = x$ctx || fail=1 + c=$(stat --printf %C $i); test x$c = x$ctx || fail=1 +done + +# ensure that ls -l output includes the ".". +c=$(ls -l f|cut -c11); test "$c" = . || fail=1 + +# Copy with an invalid context and ensure it fails +# Note this may succeed when root and selinux is in permissive mode +if test "$(getenforce)" = Enforcing; then + returns_ 1 cp --context='invalid-selinux-context' f f.cp || fail=1 +fi + +# Copy each to a new directory and ensure that context is preserved. +cp -r --preserve=all d f p s1 || fail=1 +for i in d f p; do + c=$(stat --printf %C s1/$i); test x$c = x$ctx || fail=1 +done + +# Now, move each to a new directory and ensure that context is preserved. +mv d f p s2 || fail=1 +for i in d f p; do + c=$(stat --printf %C s2/$i); test x$c = x$ctx || fail=1 +done + +Exit $fail diff --git a/tests/misc/sleep.sh b/tests/misc/sleep.sh new file mode 100755 index 0000000..9082853 --- /dev/null +++ b/tests/misc/sleep.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# Validate sleep parameters + +# Copyright (C) 2016-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ sleep printf +getlimits_ + +# invalid timeouts +returns_ 1 timeout 10 sleep invalid || fail=1 +returns_ 1 timeout 10 sleep -- -1 || fail=1 +returns_ 1 timeout 10 sleep 42D || fail=1 +returns_ 1 timeout 10 sleep 42d 42day || fail=1 +returns_ 1 timeout 10 sleep nan || fail=1 +returns_ 1 timeout 10 sleep '' || fail=1 +returns_ 1 timeout 10 sleep || fail=1 + +# subsecond actual sleep +timeout 10 sleep 0.001 || fail=1 +timeout 10 sleep 0x.002p1 || fail=1 + +# Using small timeouts for larger sleeps is racy, +# but false positives should be avoided on most systems +returns_ 124 timeout 0.1 sleep 1d 2h 3m 4s || fail=1 +returns_ 124 timeout 0.1 sleep inf || fail=1 +returns_ 124 timeout 0.1 sleep $LDBL_MAX || fail=1 + +# Test locale decimal handling for printf, sleep, timeout +: ${LOCALE_FR_UTF8=none} +if test "$LOCALE_FR_UTF8" != "none"; then + f=$LOCALE_FR_UTF8 + locale_decimal=$(LC_ALL=$f env printf '%0.3f' 0.001) || fail=1 + locale_decimal=$(LC_ALL=$f env printf '%0.3f' "$locale_decimal") || fail=1 + case "$locale_decimal" in + 0?001) + LC_ALL=$f timeout 1$locale_decimal sleep "$locale_decimal" || fail=1 ;; + *) fail=1 ;; + esac +fi + +Exit $fail diff --git a/tests/misc/stdbuf.sh b/tests/misc/stdbuf.sh new file mode 100755 index 0000000..c4369f7 --- /dev/null +++ b/tests/misc/stdbuf.sh @@ -0,0 +1,125 @@ +#!/bin/sh +# Exercise stdbuf functionality + +# Copyright (C) 2009-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ stdbuf env + +getlimits_ + +# stdbuf fails when the absolute top build dir name contains e.g., +# space, TAB, NL +lf=' +' +case $abs_top_builddir in + *[\\\"\#\$\&\'\`$lf\ \ ]*) + skip_ "unsafe absolute build directory name: $abs_top_builddir";; +esac + +# Use a fifo rather than a pipe in the tests below +# so that the producer (uniq) will wait until the +# consumer (dd) opens the fifo therefore increasing +# the chance that dd will read the data from each +# write separately. +mkfifo_or_skip_ fifo + + +# Verify input parameter checking +stdbuf -o1 true || fail=1 # verify size syntax +stdbuf -oK true || fail=1 # verify size syntax +stdbuf -o0 true || fail=1 # verify unbuffered syntax +stdbuf -oL true || fail=1 # verify line buffered syntax + +# Capital 'L' required +# Internal error is a particular status +returns_ 125 stdbuf -ol true || fail=1 + +returns_ 125 stdbuf -o$SIZE_OFLOW true || fail=1 # size too large +returns_ 125 stdbuf -iL true || fail=1 # line buffering stdin disallowed +returns_ 125 stdbuf true || fail=1 # a buffering mode must be specified +stdbuf -i0 -o0 -e0 true || fail=1 #check all files +returns_ 126 env . && { returns_ 126 stdbuf -o1 . || fail=1; } # invalid command +returns_ 127 stdbuf -o1 no_such || fail=1 # no such command + +# Terminate any background processes +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + +# Ensure line buffering stdout takes effect +stdbuf_linebuffer() +{ + local delay="$1" + + printf '1\n' > exp + > out || framework_failure_ + dd count=1 if=fifo > out 2> err & pid=$! + (printf '1\n'; sleep $delay; printf '2\n') | stdbuf -oL uniq > fifo + wait $pid + compare exp out +} + +retry_delay_ stdbuf_linebuffer .1 6 || fail=1 + +stdbuf_unbuffer() +{ + local delay="$1" + + # Ensure un buffering stdout takes effect + printf '1\n' > exp + > out || framework_failure_ + dd count=1 if=fifo > out 2> err & pid=$! + (printf '1\n'; sleep $delay; printf '2\n') | stdbuf -o0 uniq > fifo + wait $pid + compare exp out +} + +retry_delay_ stdbuf_unbuffer .1 6 || fail=1 + +# Ensure un buffering stdin takes effect +# The following works for me, but is racy. I.e., we're depending +# on dd to run and close the fifo before the second write by uniq. +# If we add a sleep, then we're just testing -oL + # printf '3\n' > exp + # dd count=1 if=fifo > /dev/null 2> err & + # printf '1\n\2\n3\n' | (stdbuf -i0 -oL uniq > fifo; cat) > out + # wait # for dd to complete + # compare exp out || fail=1 +# One could remove the need for dd (used to close the fifo to get uniq to quit +# early), if head -n1 read stdin char by char. Note uniq | head -c2 doesn't +# suffice due to the buffering implicit in the pipe. sed currently does read +# stdin char by char, so we can test with 'sed 1q'. However I'm wary about +# adding this dependency on a program outside of coreutils. + # printf '2\n' > exp + # printf '1\n2\n' | (stdbuf -i0 sed 1q >/dev/null; cat) > out + # compare exp out || fail=1 + +# Ensure block buffering stdout takes effect +# We don't currently test block buffering failures as +# this doesn't work on GLIBC-2.7 or GLIBC-2.9 at least. + # stdbuf_blockbuffer() + # { + # local delay="$1" + # + # printf '1\n2\n' > exp + # dd count=1 if=fifo > out 2> err & + # (printf '1\n'; sleep $delay; printf '2\n') | stdbuf -o4 uniq > fifo + # wait # for dd to complete + # compare exp out + # } + # + # retry_delay_ stdbuf_blockbuffer .1 6 || fail=1 + +Exit $fail diff --git a/tests/misc/sync.sh b/tests/misc/sync.sh new file mode 100755 index 0000000..daedf41 --- /dev/null +++ b/tests/misc/sync.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# Test various sync(1) operations + +# Copyright (C) 2015-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ sync + +touch file || framework_failure_ + +# fdatasync+syncfs is nonsensical +returns_ 1 sync --data --file-system || fail=1 + +# fdatasync needs an operand +returns_ 1 sync -d || fail=1 + +# Test syncing of file (fsync) (little side effects) +sync file || fail=1 + +# Test syncing of write-only file - which failed since adding argument +# support to sync in coreutils-8.24. +chmod 0200 file || framework_failure_ +sync file || fail=1 + +# Ensure multiple args are processed and diagnosed +returns_ 1 sync file nofile || fail=1 + +# Ensure inaccessible dirs give an appropriate error +mkdir norw || framework_failure_ +chmod 0 norw || framework_failure_ +if ! test -r norw; then + returns_ 1 sync norw 2>errt || fail=1 + # AIX gives "Is a directory" + sed 's/Is a directory/Permission denied/' <errt >err || framework_failure_ + printf "sync: error opening 'norw': Permission denied\n" >exp + compare exp err || fail=1 +fi + +if test "$fail" != '1'; then + # Ensure a fifo doesn't block + mkfifo_or_skip_ fifo + returns_ 124 timeout 10 sync fifo && fail=1 +fi + +Exit $fail diff --git a/tests/misc/tee.sh b/tests/misc/tee.sh new file mode 100755 index 0000000..3daf610 --- /dev/null +++ b/tests/misc/tee.sh @@ -0,0 +1,145 @@ +#!/bin/sh +# test for basic tee functionality. + +# Copyright (C) 2005-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ tee + +echo line >sample || framework_failure_ + +# POSIX says: "Processing of at least 13 file operands shall be supported." +for n in 0 1 2 12 13; do + files=$(seq $n) + rm -f $files + tee $files <sample >out || fail=1 + for f in out $files; do + compare sample $f || fail=1 + done +done + +# Ensure tee treats '-' as the name of a file, as mandated by POSIX. +# Between v5.3.0 and v8.23, a '-' argument caused tee to send another +# copy of input to standard output. +tee - <sample >out 2>err || fail=1 +compare sample ./- || fail=1 +compare sample out || fail=1 +compare /dev/null err || fail=1 + +# Ensure tee exits early if no more writable outputs +if test -w /dev/full && test -c /dev/full; then + yes | returns_ 1 timeout 10 tee /dev/full 2>err >/dev/full || fail=1 + # Ensure an error for each of the 2 outputs + # (and no redundant errors for stdout). + test $(wc -l < err) = 2 || { cat err; fail=1; } + + + # Ensure we continue with outputs that are OK + seq 10000 > multi_read || framework_failure_ + + returns_ 1 tee /dev/full out2 2>err >out1 <multi_read || fail=1 + cmp multi_read out1 || fail=1 + cmp multi_read out2 || fail=1 + # Ensure an error for failing output + test $(wc -l < err) = 1 || { cat err; fail=1; } + + returns_ 1 tee out1 out2 2>err >/dev/full <multi_read || fail=1 + cmp multi_read out1 || fail=1 + cmp multi_read out2 || fail=1 + # Ensure an error for failing output + test $(wc -l < err) = 1 || { cat err; fail=1; } +fi + +case $host_triplet in + *aix*) echo 'avoiding due to no way to detect closed outputs on AIX' ;; + *) +# Test iopoll-powered early exit for closed pipes +tee_exited() { sleep $1; test -f tee.exited; } +# Currently this functionality is most useful with +# intermittent input from a terminal, but here we +# use an input pipe that doesn't write anything +# but will exit as soon as tee does, or it times out +retry_delay_ tee_exited .1 7 | # 12.7s (Must be > following timeout) +{ timeout 10 tee -p 2>err && touch tee.exited; } | : +test $(wc -l < err) = 0 || { cat err; fail=1; } +test -f tee.exited || fail=1 ;; +esac + +# Test with unwritable files +if ! uid_is_privileged_; then # root does not get EPERM. + touch file.ro || framework_failure_ + chmod a-w file.ro || framework_failure_ + returns_ 1 tee -p </dev/null file.ro || fail=1 +fi + +mkfifo_or_skip_ fifo + +# Ensure tee handles nonblocking output correctly +# Terminate any background processes +cleanup_() { kill $pid 2>/dev/null && wait $pid; } +read_fifo_delayed() { + { sleep .1; timeout 10 dd of=/dev/null status=none; } <fifo +} +read_fifo_delayed & pid=$! +dd count=20 bs=100K if=/dev/zero status=none | +{ + dd count=0 oflag=nonblock status=none + tee || { cleanup_; touch tee.fail; } +} >fifo +test -f tee.fail && fail=1 || cleanup_ + +# Ensure tee honors --output-error modes +read_fifo() { timeout 10 dd count=1 if=fifo of=/dev/null status=none & } + +# Determine platform sigpipe exit status +read_fifo +yes >fifo +pipe_status=$? + +# Default operation is to continue on output errors but exit silently on SIGPIPE +read_fifo +yes | returns_ $pipe_status timeout 10 tee ./e/noent 2>err >fifo || fail=1 +test $(wc -l < err) = 1 || { cat err; fail=1; } + +# With -p, SIGPIPE is suppressed, exit 0 for EPIPE when all outputs finished +read_fifo +yes | timeout 10 tee -p 2>err >fifo || fail=1 +test $(wc -l < err) = 0 || { cat err; fail=1; } + +# With --output-error=warn, exit 1 for EPIPE when all outputs finished +read_fifo +yes | returns_ 1 timeout 10 tee --output-error=warn 2>err >fifo || fail=1 +test $(wc -l < err) = 1 || { cat err; fail=1; } + +# With --output-error=exit, exit 1 immediately for EPIPE +read_fifo +yes | returns_ 1 timeout 10 tee --output-error=exit /dev/null 2>err >fifo \ + || fail=1 +test $(wc -l < err) = 1 || { cat err; fail=1; } + +# With --output-error=exit, exit 1 immediately on output error +read_fifo +yes | returns_ 1 timeout 10 tee --output-error=exit ./e/noent 2>err >fifo \ + || fail=1 +test $(wc -l < err) = 1 || { cat err; fail=1; } + +# With --output-error=exit-nopipe, exit 0 for EPIPE +read_fifo +yes | timeout 10 tee --output-error=exit-nopipe 2>err >fifo || fail=1 +test $(wc -l < err) = 0 || { cat err; fail=1; } + +wait +Exit $fail diff --git a/tests/misc/time-style.sh b/tests/misc/time-style.sh new file mode 100755 index 0000000..3433836 --- /dev/null +++ b/tests/misc/time-style.sh @@ -0,0 +1,119 @@ +#!/bin/sh +# Test --time-style in programs like 'ls'. + +# Copyright (C) 2016-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ du +print_ver_ ls +print_ver_ pr + +echo hello >a || framework_failure_ + +# The tests assume this is an old timestamp in northern hemisphere summer. +TZ=UTC0 touch -d '1970-07-08 09:10:11' a || framework_failure_ + +for tz in UTC0 PST8 PST8PDT,M3.2.0,M11.1.0 XXXYYY-12:30; do + for style in full-iso long-iso iso locale '+%Y-%m-%d %H:%M:%S %z (%Z)' \ + +%%b%b%%b%b; do + test "$style" = locale || + TZ=$tz LC_ALL=C du --time --time-style="$style" a >>duout 2>>err || fail=1 + TZ=$tz LC_ALL=C ls -no --time-style="$style" a >>lsout 2>>err || fail=1 + case $style in + (+*) TZ=$tz LC_ALL=C pr -D"$style" a >>prout 2>>err || fail=1 ;; + esac + done +done + +sed 's/[^ ]* //' duout >dued || framework_failure_ +sed 's/[^ ]* *[^ ]* *[^ ]* *[^ ]* *//' lsout >lsed || framework_failure_ +sed '/^$/d' prout >pred || framework_failure_ + +cat <<\EOF > duexp || framework_failure_ +1970-07-08 09:10:11.000000000 +0000 a +1970-07-08 09:10 a +1970-07-08 a +1970-07-08 09:10:11 +0000 (UTC) a +%bJul%bJul a +1970-07-08 01:10:11.000000000 -0800 a +1970-07-08 01:10 a +1970-07-08 a +1970-07-08 01:10:11 -0800 (PST) a +%bJul%bJul a +1970-07-08 02:10:11.000000000 -0700 a +1970-07-08 02:10 a +1970-07-08 a +1970-07-08 02:10:11 -0700 (PDT) a +%bJul%bJul a +1970-07-08 21:40:11.000000000 +1230 a +1970-07-08 21:40 a +1970-07-08 a +1970-07-08 21:40:11 +1230 (XXXYYY) a +%bJul%bJul a +EOF + +cat <<\EOF > lsexp || framework_failure_ +1970-07-08 09:10:11.000000000 +0000 a +1970-07-08 09:10 a +1970-07-08 a +Jul 8 1970 a +1970-07-08 09:10:11 +0000 (UTC) a +%bJul%bJul a +1970-07-08 01:10:11.000000000 -0800 a +1970-07-08 01:10 a +1970-07-08 a +Jul 8 1970 a +1970-07-08 01:10:11 -0800 (PST) a +%bJul%bJul a +1970-07-08 02:10:11.000000000 -0700 a +1970-07-08 02:10 a +1970-07-08 a +Jul 8 1970 a +1970-07-08 02:10:11 -0700 (PDT) a +%bJul%bJul a +1970-07-08 21:40:11.000000000 +1230 a +1970-07-08 21:40 a +1970-07-08 a +Jul 8 1970 a +1970-07-08 21:40:11 +1230 (XXXYYY) a +%bJul%bJul a +EOF + +cat <<\EOF > prexp || framework_failure_ ++1970-07-08 09:10:11 +0000 (UTC) a Page 1 +hello ++%bJul%bJul a Page 1 +hello ++1970-07-08 01:10:11 -0800 (PST) a Page 1 +hello ++%bJul%bJul a Page 1 +hello ++1970-07-08 02:10:11 -0700 (PDT) a Page 1 +hello ++%bJul%bJul a Page 1 +hello ++1970-07-08 21:40:11 +1230 (XXXYYY) a Page 1 +hello ++%bJul%bJul a Page 1 +hello +EOF + +compare duexp dued || fail=1 +compare lsexp lsed || fail=1 +compare prexp pred || fail=1 +compare /dev/null err || fail=1 + +Exit $fail diff --git a/tests/misc/tsort.pl b/tests/misc/tsort.pl new file mode 100755 index 0000000..ca3aa3b --- /dev/null +++ b/tests/misc/tsort.pl @@ -0,0 +1,66 @@ +#!/usr/bin/perl +# Test "tsort". + +# Copyright (C) 1999-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; + +(my $program_name = $0) =~ s|.*/||; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my @Tests = + ( + ['cycle-1', {IN => {f => "t b\nt s\ns t\n"}}, {OUT => "s\nt\nb\n"}, + {EXIT => 1}, + {ERR => "tsort: f: input contains a loop:\ntsort: s\ntsort: t\n"} ], + ['cycle-2', {IN => {f => "t x\nt s\ns t\n"}}, {OUT => "s\nt\nx\n"}, + {EXIT => 1}, + {ERR => "tsort: f: input contains a loop:\ntsort: s\ntsort: t\n"} ], + + ['posix-1', {IN => "a b c c d e\ng g\nf g e f\nh h\n"}, + {OUT => "a\nc\nd\nh\nb\ne\nf\ng\n"}], + ['posix-2', {IN => "b a\nd c\nz h x h r h\n"}, + {OUT => "b\nd\nr\nx\nz\na\nc\nh\n"}], + + ['linear-1', {IN => "a b b c c d d e e f f g\n"}, + {OUT => "a\nb\nc\nd\ne\nf\ng\n"}], + + ['tree-1', {IN => "a b b c c d d e e f f g\nc x x y y z\n"}, + {OUT => "a\nb\nc\nx\nd\ny\ne\nz\nf\ng\n"}], + ['tree-2', {IN => "a b b c c d d e e f f g\nc x x y y z\nf r r s s t\n"}, + {OUT => "a\nb\nc\nx\nd\ny\ne\nz\nf\nr\ng\ns\nt\n"}], + + # Before coreutils-5.0.1, given an odd number of input tokens, + # tsort would accept that and treat the input as if an additional + # copy of the final token were appended. + ['odd', {IN => "a\n"}, + {EXIT => 1}, + {ERR => "tsort: odd.1: input contains an odd number of tokens\n"}], + + ['only-one', {IN => {f => ""}}, {IN => {g => ""}}, + {EXIT => 1}, + {ERR => "tsort: extra operand 'g'\n" + . "Try 'tsort --help' for more information.\n"}], + ); + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $prog = 'tsort'; +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/unexpand.pl b/tests/misc/unexpand.pl new file mode 100755 index 0000000..d78a1bc --- /dev/null +++ b/tests/misc/unexpand.pl @@ -0,0 +1,135 @@ +#!/usr/bin/perl +# Test "unexpand". + +# Copyright (C) 2000-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; + +my $limits = getlimits (); + +(my $program_name = $0) =~ s|.*/||; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my $prog = 'unexpand'; + +my @Tests = + ( + ['a1', {IN=> ' 'x 1 ."y\n"}, {OUT=> ' 'x 1 ."y\n"}], + ['a2', {IN=> ' 'x 2 ."y\n"}, {OUT=> ' 'x 2 ."y\n"}], + ['a3', {IN=> ' 'x 3 ."y\n"}, {OUT=> ' 'x 3 ."y\n"}], + ['a4', {IN=> ' 'x 4 ."y\n"}, {OUT=> ' 'x 4 ."y\n"}], + ['a5', {IN=> ' 'x 5 ."y\n"}, {OUT=> ' 'x 5 ."y\n"}], + ['a6', {IN=> ' 'x 6 ."y\n"}, {OUT=> ' 'x 6 ."y\n"}], + ['a7', {IN=> ' 'x 7 ."y\n"}, {OUT=> ' 'x 7 ."y\n"}], + ['a8', {IN=> ' 'x 8 ."y\n"}, {OUT=> "\ty\n"}], + + ['aa-1', '-a', {IN=> 'w'.' 'x 1 ."y\n"}, {OUT=> 'w'.' 'x 1 ."y\n"}], + ['aa-2', '-a', {IN=> 'w'.' 'x 2 ."y\n"}, {OUT=> 'w'.' 'x 2 ."y\n"}], + ['aa-3', '-a', {IN=> 'w'.' 'x 3 ."y\n"}, {OUT=> 'w'.' 'x 3 ."y\n"}], + ['aa-4', '-a', {IN=> 'w'.' 'x 4 ."y\n"}, {OUT=> 'w'.' 'x 4 ."y\n"}], + ['aa-5', '-a', {IN=> 'w'.' 'x 5 ."y\n"}, {OUT=> 'w'.' 'x 5 ."y\n"}], + ['aa-6', '-a', {IN=> 'w'.' 'x 6 ."y\n"}, {OUT=> 'w'.' 'x 6 ."y\n"}], + ['aa-7', '-a', {IN=> 'w'.' 'x 7 ."y\n"}, {OUT=> "w\ty\n"}], + ['aa-8', '-a', {IN=> 'w'.' 'x 8 ."y\n"}, {OUT=> "w\t y\n"}], + + ['b-1', '-t', '2,4', {IN=> " ."}, {OUT=>"\t\t ."}], + # These would infloop prior to textutils-2.0d. + + ['infloop-1', '-t', '1,2', {IN=> " \t\t .\n"}, {OUT=>"\t\t\t .\n"}], + ['infloop-2', '-t', '4,5', {IN=> ' 'x4 . "\t\t \n"}, {OUT=>"\t\t\t \n"}], + ['infloop-3', '-t', '2,3', {IN=> "x \t\t \n"}, {OUT=>"x\t\t\t \n"}], + ['infloop-4', '-t', '1,2', {IN=> " \t\t \n"}, {OUT=>"\t\t\t \n"}], + ['c-1', '-t', '1,2', {IN=> "x\t\t .\n"}, {OUT=>"x\t\t .\n"}], + + # -t implies -a + # Feature addition (--first-only) prompted by a report from Jie Xu. + ['tabs-1', qw(-t 3), {IN=> " a b\n"}, {OUT=>"\ta\tb\n"}], + ['tabs-2', qw(-t 3 --first-only), {IN=> " a b\n"}, {OUT=>"\ta b\n"}], + + # blanks + ['blanks-1', qw(-t 1), {IN=> " b c d\n"}, {OUT=> "\tb\t\tc\t\t\td\n"}], + ['blanks-2', qw(-t 1), {IN=> "a \n"}, {OUT=> "a \n"}], + ['blanks-3', qw(-t 1), {IN=> "a \n"}, {OUT=> "a\t\t\n"}], + ['blanks-4', qw(-t 1), {IN=> "a \n"}, {OUT=> "a\t\t\t\n"}], + ['blanks-5', qw(-t 1), {IN=> "a "}, {OUT=> "a "}], + ['blanks-6', qw(-t 1), {IN=> "a "}, {OUT=> "a\t\t"}], + ['blanks-7', qw(-t 1), {IN=> "a "}, {OUT=> "a\t\t\t"}], + ['blanks-8', qw(-t 1), {IN=> " a a a\n"}, {OUT=> "\ta a\t\ta\n"}], + ['blanks-9', qw(-t 2), {IN=> " a a a\n"}, {OUT=> "\t a\ta\t a\n"}], + ['blanks-10', '-t', '3,4', {IN=> "0 2 4 6\t8\n"}, {OUT=> "0 2 4 6\t8\n"}], + ['blanks-11', '-t', '3,4', {IN=> " 4\n"}, {OUT=> "\t\t4\n"}], + ['blanks-12', '-t', '3,4', {IN=> "01 4\n"}, {OUT=> "01\t\t4\n"}], + ['blanks-13', '-t', '3,4', {IN=> "0 4\n"}, {OUT=> "0\t\t4\n"}], + + # POSIX says spaces should only follow tabs. Also a single + # trailing space is not converted to a tab, when before + # a field starting with non blanks + ['posix-1', '-a', {IN=> "1234567 \t1\n"}, {OUT=>"1234567\t\t1\n"}], + ['posix-2', '-a', {IN=> "1234567 \t1\n"}, {OUT=>"1234567\t\t1\n"}], + ['posix-3', '-a', {IN=> "1234567 \t1\n"}, {OUT=>"1234567\t\t1\n"}], + ['posix-4', '-a', {IN=> "1234567\t1\n"}, {OUT=>"1234567\t1\n"}], + ['posix-5', '-a', {IN=> "1234567 1\n"}, {OUT=>"1234567\t 1\n"}], + ['posix-6', '-a', {IN=> "1234567 1\n"}, {OUT=>"1234567 1\n"}], + + # It is debatable whether this test should require an environment + # setting of e.g., _POSIX2_VERSION=1. + ['obs-ovflo', "-$limits->{UINTMAX_OFLOW}", {IN=>''}, {OUT=>''}, + {EXIT => 1}, {ERR => "$prog: tab stop value is too large\n"}], + + + # Test input with backspaces '\b' ('bs1' is the baseline, without \b) + # Note: If users report errors in these tests, copy&pasting results from + # their terminate output might be confusing due to '\b' overriding + # characters. For details see '\b' tests in 'expand.pl'. + ['bs1', '-a -t4', {IN=>"aa c\n"}, {OUT=>"aa\tc\n"}], + ['bs2', '-a -t4', {IN=>"aa\b c\n"}, {OUT=>"aa\b c\n"}], + ['bs3', '-a -t4', {IN=>"aa\b c\n"}, {OUT=>"aa\b\tc\n"}], + ['bs4', '-a -t3', {IN=>"aa\b c\n"}, {OUT=>"aa\b\tc\n"}], + + # Undocumented feature: + # treat "unexpand -7" as "unexpand --first-only --tabs 7" , + # and "unexpand -90" as "unexpand --first-only --tabs 90", + ['u1', '-a -3', {IN=>"a b c"}, {OUT=>"a\tb\tc"}], + ['u2', '-a -4,9', {IN=>"a b c"}, {OUT=>"a\tb\tc"}], + ['u3', '-a -11', {IN=>"a b"}, {OUT=>"a\tb"}], + # Test all digits (for full code coverage) + ['u4', '-a -2,6', {IN=>"a b c"}, {OUT=>"a b\tc"}], + ['u5', '-a -7', {IN=>"a b"}, {OUT=>"a\tb"}], + ['u6', '-a -8', {IN=>"a b"}, {OUT=>"a\tb"}], + # This syntax is handled internally as "-3, -9" + ['u7', '-a -3,9', {IN=>"a b c"}, {OUT=>"a\tb\tc"}], + # Default (without -a) is --first-only: + ['u8', '-3', {IN=>" a b"}, {OUT=>"\ta b"}], + + # Arguably this should minimize translation as is done on Solaris. + # I.e., not modify the input. But since the result is equivalent, + # and to be consistent in output with older versions, we output + # a '\t' rather than a space for the second tab position. + # For more detailed comparison with other implementations see: + # https://lists.gnu.org/r/coreutils/2016-06/msg00015.html + # https://lists.gnu.org/r/coreutils/2016-07/msg00011.html + ['ts1', '-t8,9', {IN=>"x\t \t y\n"}, {OUT=>"x\t\t\t y\n"}], + # There is no ambiguity here. This should always be the output. + ['ts2', '-t5,8', {IN=>"x\t \t y\n"}, {OUT=>"x\t\t y\n"}], + ); + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/usage_vs_getopt.sh b/tests/misc/usage_vs_getopt.sh new file mode 100755 index 0000000..f4f1213 --- /dev/null +++ b/tests/misc/usage_vs_getopt.sh @@ -0,0 +1,96 @@ +#!/bin/sh +# Verify that all options mentioned in usage are recognized by getopt. + +# Copyright (C) 2017-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src + +checkprg () { + prg="$1" + + # Get stderr output for unrecognized options for later use as a pattern. + # Also consider the expected exit status of each program. + rcexp=1 + case "$prg" in + dir | ls | printenv | sort | tty | vdir ) rcexp=2 ;; + env | chroot | nice | nohup | runcon | stdbuf | timeout ) rcexp=125 ;; + esac + # Write the pattern for a long, unknown option into a pattern file. + o='thisoptiondoesnotexist' + returns_ $rcexp $prg --$o >/dev/null 2> err || fail=1 + grep -F "$o" err || framework_failure_ + sed -n "1s/--$o/OPT/p" < err > pat || framework_failure_ + + # Append the pattern for a short unknown option. + returns_ $rcexp $prg -/ >/dev/null 2> err || fail=1 + grep " '*/'*" err || framework_failure_ + # The following only handles the common case that has single quotes, + # as it simplifies to identify missing options only on common systems. + sed -n "1s/'\/'/'OPT'/p" < err >> pat || framework_failure_ + + # Get output for --help. + $prg --help > help || fail=1 + + # Extract all options mention in the above --help output. + nl=" + " + sed -n -e '/--version/q' \ + -e 's/^ \{2,6\}-/-/; s/ .*//; s/[=[].*//; s/, /\'"$nl"'/g; s/^-/-/p' help \ + > opts || framework_failure_ + cat opts # for debugging purposes + + # Test all options mentioned in usage (but --version). + while read opt; do + test "x$opt" = 'x--help' \ + && continue + # Append --help to be on the safe side: the option under test either + # requires a further argument, or --help triggers usage(); so $prg should + # exit without performing its regular operation. + # Add a 2nd --help for the 'env -u --help' case. + $prg "$opt" --help --help </dev/null >out 2>err1 + rc=$? + # In the --help case, i.e., when the option under test has been accepted, + # the exit code should be Zero. + if [ $rc = 0 ]; then + compare help out || fail=1 + else + # Else $prg should have complained about a missing argument. + # Catch false positives. + case "$prg/$opt" in + 'pr/-COLUMN') continue;; + esac + # Replace $opt in stderr output by the neutral placeholder. + # Handle both long and short option error messages. + sed -e "s/$opt/OPT/" -e "s/'.'/'OPT'/" < err1 > err || framework_failure_ + # Fail if the stderr output matches the above provoked error. + grep -Ff pat err && { fail=1; cat err1; } + fi + done < opts +} + +for prg in $built_programs; do + case "$prg" in + # Skip utilities entirely which have special option parsing. + '[' | expr | stty ) + continue;; + # Wrap some utilities known by the shell by env. + echo | false | kill | printf | pwd | sleep | test | true ) + prg="env $prg";; + esac + checkprg $prg +done + +Exit $fail diff --git a/tests/misc/write-errors.sh b/tests/misc/write-errors.sh new file mode 100755 index 0000000..4f17028 --- /dev/null +++ b/tests/misc/write-errors.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# Make sure all of these programs promptly diagnose write errors. + +# Copyright (C) 2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ timeout + +if ! test -w /dev/full || ! test -c /dev/full; then + skip_ '/dev/full is required' +fi + +# Writers that may output data indefinitely +# First word in command line is checked against built programs +echo "\ +cat /dev/zero +comm -z /dev/zero /dev/zero +cut -z -c1- /dev/zero +cut -z -f1- /dev/zero +dd if=/dev/zero +expand /dev/zero +factor --version; yes 1 | factor +# TODO: fmt /dev/zero +# TODO: fold -b /dev/zero +head -z -n-1 /dev/zero +join -a 1 -z /dev/zero /dev/null +# TODO: nl --version; yes | nl +# TODO: numfmt --version; yes 1 | numfmt +od -v /dev/zero +paste /dev/zero +# TODO: pr /dev/zero +seq inf +tail -n+1 -z /dev/zero +tee < /dev/zero +tr . . < /dev/zero +unexpand /dev/zero +uniq -z -D /dev/zero +yes +" | +sort -k 1b,1 > all_writers || framework_failure_ + +printf '%s\n' $built_programs | +sort -k 1b,1 > built_programs || framework_failure_ + +join all_writers built_programs > built_writers || framework_failure_ + +while read writer; do + timeout 10 $SHELL -c "$writer > /dev/full" + test $? = 124 && { fail=1; echo "$writer: failed to exit" >&2; } +done < built_writers + +Exit $fail diff --git a/tests/misc/xattr.sh b/tests/misc/xattr.sh new file mode 100755 index 0000000..1416578 --- /dev/null +++ b/tests/misc/xattr.sh @@ -0,0 +1,130 @@ +#!/bin/sh +# Ensure that cp --preserve=xattr, cp --preserve=all and mv preserve extended +# attributes and install does not preserve extended attributes. +# cp -a should preserve xattr, error diagnostics should not be displayed + +# Copyright (C) 2009-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ cp mv ginstall + +# Skip this test if cp was built without xattr support: +touch src dest || framework_failure_ +cp --preserve=xattr -n src dest \ + || skip_ "coreutils built without xattr support" + +# this code was taken from test mv/backup-is-src +cleanup_() { rm -rf "$other_partition_tmpdir"; } +. "$abs_srcdir/tests/other-fs-tmpdir" +b_other="$other_partition_tmpdir/b" +rm -f "$b_other" || framework_failure_ + +# testing xattr name-value pair +xattr_name="user.foo" +xattr_value="bar" +xattr_pair="$xattr_name=\"$xattr_value\"" + +# create new file and check its xattrs +touch a || framework_failure_ +getfattr -d a >out_a || skip_ "failed to get xattr of file" +grep -F "$xattr_pair" out_a && framework_failure_ + +# try to set user xattr on file +setfattr -n "$xattr_name" -v "$xattr_value" a >out_a \ + || skip_ "failed to set xattr of file" +getfattr -d a >out_a || skip_ "failed to get xattr of file" +grep -F "$xattr_pair" out_a \ + || skip_ "failed to set xattr of file" + + +# cp should not preserve xattr by default +cp a b || fail=1 +getfattr -d b >out_b || skip_ "failed to get xattr of file" +grep -F "$xattr_pair" out_b && fail=1 + +# test if --preserve=xattr option works +cp --preserve=xattr a b || fail=1 +getfattr -d b >out_b || skip_ "failed to get xattr of file" +grep -F "$xattr_pair" out_b || fail=1 + +# test if --preserve=all option works +cp --preserve=all a c || fail=1 +getfattr -d c >out_c || skip_ "failed to get xattr of file" +grep -F "$xattr_pair" out_c || fail=1 + +# cp's -a option must produce no diagnostics. +cp -a a d 2>err && { compare /dev/null err || fail=1; } +getfattr -d d >out_d || skip_ "failed to get xattr of file" +grep -F "$xattr_pair" out_d || fail=1 + +# test if --preserve=xattr works even for files without write access +chmod a-w a || framework_failure_ +rm -f e +cp --preserve=xattr a e || fail=1 +getfattr -d e >out_e || skip_ "failed to get xattr of file" +grep -F "$xattr_pair" out_e || fail=1 + +# Ensure that permission bits are preserved, too. +src_perm=$(stat --format=%a a) +dst_perm=$(stat --format=%a e) +test "$dst_perm" = "$src_perm" || fail=1 + +chmod u+w a || framework_failure_ + +rm b || framework_failure_ + +# install should never preserve xattr +ginstall a b || fail=1 +getfattr -d b >out_b || skip_ "failed to get xattr of file" +grep -F "$xattr_pair" out_b && fail=1 + +# mv should preserve xattr when renaming within a file system. +# This is implicitly done by rename () and doesn't need explicit +# xattr support in mv. +mv a b || fail=1 +getfattr -d b >out_b || skip_ "failed to get xattr of file" +grep -F "$xattr_pair" out_b || cat >&2 <<EOF +================================================================= +$0: WARNING!!! +rename () does not preserve extended attributes +================================================================= +EOF + +# try to set user xattr on file on other partition +test_mv=1 +touch "$b_other" || framework_failure_ +setfattr -n "$xattr_name" -v "$xattr_value" "$b_other" >out_a \ + || test_mv=0 +getfattr -d "$b_other" >out_b || test_mv=0 +grep -F "$xattr_pair" out_b || test_mv=0 +rm -f "$b_other" || framework_failure_ + +if test $test_mv -eq 1; then + # mv should preserve xattr when copying content from one partition to another + mv b "$b_other" || fail=1 + getfattr -d "$b_other" >out_b || + skip_ "failed to get xattr of file" + grep -F "$xattr_pair" out_b || fail=1 +else + cat >&2 <<EOF +================================================================= +$0: WARNING!!! +failed to set xattr of file $b_other +================================================================= +EOF +fi + +Exit $fail diff --git a/tests/misc/xstrtol.pl b/tests/misc/xstrtol.pl new file mode 100755 index 0000000..dfdaee0 --- /dev/null +++ b/tests/misc/xstrtol.pl @@ -0,0 +1,49 @@ +#!/usr/bin/perl +# exercise xstrtol's diagnostics via pr + +# Copyright (C) 2007-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +use strict; + +(my $ME = $0) =~ s|.*/||; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my $prog = 'pr'; +my $too_big = '9' x 81; # Big enough to overflow a 256-bit integer. +my @Tests = + ( + # test-name, [option, option, ...] {OUT=>"expected-output"} + + ['inval-suffix', "--pages=${too_big}h", {EXIT => 1}, + {ERR=>"$prog: invalid suffix in --pages argument '${too_big}h'\n"}], + + ['too-big', "--pages=$too_big", {EXIT => 1}, + {ERR=>"$prog: --pages argument '$too_big' too large\n"}], + + ['simply-inval', "--pages=x", {EXIT => 1}, + {ERR=>"$prog: invalid --pages argument 'x'\n"}], + + ['inv-pg-range', "--pages=9x", {EXIT => 1}, + {ERR=>"$prog: invalid page range '9x'\n"}], + ); + +my $save_temps = $ENV{DEBUG}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($ME, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/misc/yes.sh b/tests/misc/yes.sh new file mode 100755 index 0000000..9cd07d4 --- /dev/null +++ b/tests/misc/yes.sh @@ -0,0 +1,59 @@ +#!/bin/sh +# Validate yes buffer handling + +# Copyright (C) 2015-2023 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ yes + +# Check basic operation +test "$(yes | head -n1)" = 'y' || fail=1 + +# Check various single item sizes, with the most important +# size being BUFSIZ used for the local buffer to yes(1). +# Note a \n is added, so actual sizes required internally +# are 1 more than the size used here. +for size in 1 1999 4095 4096 8191 8192 16383 16384; do + printf "%${size}s\n" '' > out.1 + yes "$(printf %${size}s '')" | head -n2 | uniq > out.2 + compare out.1 out.2 || fail=1 +done + +# Check the many small items case, +# both fitting and overflowing the internal buffer. +# First check that 4000 arguments supported. +if test 4000 -eq $(sh -c 'echo $#' 0 $(seq 4000)); then + for i in 100 4000; do + seq $i | paste -s -d ' ' | sed p > out.1 + yes $(seq $i) | head -n2 > out.2 + compare out.1 out.2 || fail=1 + done +fi + +# Check a single appropriate diagnostic is output on write error +if test -w /dev/full && test -c /dev/full; then + # The single output diagnostic expected, + # (without the possibly varying :strerror(ENOSPC) suffix). + printf '%s\n' "yes: standard output" > exp + + for size in 1 16384; do + returns_ 1 yes "$(printf %${size}s '')" >/dev/full 2>errt || fail=1 + sed 's/\(yes:.*\):.*/\1/' errt > err + compare exp err || fail=1 + done +fi + +Exit $fail |