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/csplit | |
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/csplit')
-rwxr-xr-x | tests/csplit/csplit-1000.sh | 29 | ||||
-rwxr-xr-x | tests/csplit/csplit-heap.sh | 32 | ||||
-rwxr-xr-x | tests/csplit/csplit-io-err.sh | 92 | ||||
-rwxr-xr-x | tests/csplit/csplit-suppress-matched.pl | 218 | ||||
-rwxr-xr-x | tests/csplit/csplit.sh | 103 |
5 files changed, 474 insertions, 0 deletions
diff --git a/tests/csplit/csplit-1000.sh b/tests/csplit/csplit-1000.sh new file mode 100755 index 0000000..bc34a24 --- /dev/null +++ b/tests/csplit/csplit-1000.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# cause a 1-byte heap buffer overrun + +# Copyright (C) 2010-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_ csplit + +# Before coreutils-8.7, this would overrun the 6-byte filename_space buffer. +# It's hard to detect that without using valgrind, so here, we simply +# run the demonstrator. +seq 1000 | csplit - '/./' '{*}' || fail=1 +test -f xx1000 || fail=1 +test -f xx1001 && fail=1 + +Exit $fail diff --git a/tests/csplit/csplit-heap.sh b/tests/csplit/csplit-heap.sh new file mode 100755 index 0000000..36b286b --- /dev/null +++ b/tests/csplit/csplit-heap.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# ensure that csplit uses a bounded amount of memory + +# Copyright (C) 2010-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_ csplit + +# Determine basic amount of memory needed. +{ echo y; echo n; } > f || framework_failure_ +vm=$(get_min_ulimit_v_ csplit -z f %n%1) \ + || skip_ "this shell lacks ulimit support" + +( + ulimit -v $(($vm + 4000)) \ + && { yes | head -n2500000; echo n; } | csplit -z - %n%1 +) || fail=1 + +Exit $fail diff --git a/tests/csplit/csplit-io-err.sh b/tests/csplit/csplit-io-err.sh new file mode 100755 index 0000000..04c0ce0 --- /dev/null +++ b/tests/csplit/csplit-io-err.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# Ensure we handle i/o errors correctly in csplit + +# 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_ csplit +require_gcc_shared_ + +if ! test -w /dev/full || ! test -c /dev/full; then + skip_ '/dev/full is required' +fi + +# Ensure error messages are in English +LC_ALL=C +export LC_ALL + +# Replace fwrite and ferror, always returning an error +cat > k.c <<'EOF' || framework_failure_ +#include <stdio.h> +#include <errno.h> + +#undef fwrite +#undef fwrite_unlocked + +size_t +fwrite (const void *ptr, size_t size, size_t nitems, FILE *stream) +{ + if (stream == stderr) + { + /* Perform the normal operation of fwrite. */ + const char *p = ptr; + size_t count = size * nitems; + size_t i; + for (i = 0; i < count; i++) + if (putc ((unsigned char) *p++, stream) == EOF) + break; + return i / size; + } + else + { + fclose (fopen ("preloaded","w")); /* marker for preloaded interception */ + errno = ENOSPC; + return 0; + } +} + +size_t +fwrite_unlocked (const void *ptr, size_t size, size_t nitems, FILE *stream) +{ + return fwrite (ptr, size, nitems, stream); +} +EOF + +# Get the wording of the OS-dependent ENOSPC message +returns_ 1 seq 1 >/dev/full 2>msgt || framework_failure_ +sed 's/seq: write error: //' msgt > msg || framework_failure_ + +# Create the expected error message +{ printf "%s" "csplit: write error for 'xx01': " ; cat msg ; } > exp \ + || framework_failure_ + +# compile/link the interception shared library: +gcc_shared_ k.c k.so \ + || skip_ 'failed to build forced-fwrite-failure shared library' + +# Split the input, and force fwrite() failure - +# the 'csplit' command should fail with exit code 1 +# (checked with 'returns_ 1 ... || fail=1') +seq 10 | +(export LD_PRELOAD=$LD_PRELOAD:./k.so + returns_ 1 csplit - 1 4 2>out) || fail=1 + +test -e preloaded || skip_ 'LD_PRELOAD interception failed' + +# Ensure we got the expected error message +compare exp out || fail=1 + +Exit $fail diff --git a/tests/csplit/csplit-suppress-matched.pl b/tests/csplit/csplit-suppress-matched.pl new file mode 100755 index 0000000..80db7fc --- /dev/null +++ b/tests/csplit/csplit-suppress-matched.pl @@ -0,0 +1,218 @@ +#!/usr/bin/perl + +# Copyright (C) 2013-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 $prog = 'csplit'; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +# Input from 'seq 6' +my $IN_SEQ_6 =<<EOF; +1 +2 +3 +4 +5 +6 +EOF + +# Input from a possible run of 'uniq --group' +# (groups separated by empty lines) +my $IN_UNIQ =<<EOF; +a +a +YY + +XX +b +b +YY + +XX +c +YY + +XX +d +d +d +EOF + +# Standard Coreutils::run_tests() structure, except the addition of +# "OUTPUTS" array, containing the expected content of the output files. +# See code below for conversion into PRE/CMP/POST checks. +my @csplit_tests = +( + # without --suppress-matched, + # the newline (matched line) appears in the output files + ["re-base", "-q - '/^\$/' '{*}'", {IN_PIPE => $IN_UNIQ}, + {OUTPUTS => [ "a\na\nYY\n", "\nXX\nb\nb\nYY\n","\nXX\nc\nYY\n", + "\nXX\nd\nd\nd\n" ] }], + + # the newline (matched line) does not appear in the output files + ["re-1", " --suppress-matched -q - '/^\$/' '{*}'", {IN_PIPE => $IN_UNIQ}, + {OUTPUTS => ["a\na\nYY\n", "XX\nb\nb\nYY\n", "XX\nc\nYY\n", + "XX\nd\nd\nd\n"]}], + + # the 'XX' (matched line + offset 1) does not appear in the output files. + # the newline appears in the files (before each split, at the end of the file) + ["re-2", "--suppress-matched -q - '/^\$/1' '{*}'", {IN_PIPE => $IN_UNIQ}, + {OUTPUTS => ["a\na\nYY\n\n","b\nb\nYY\n\n","c\nYY\n\n","d\nd\nd\n"]}], + + # the 'YY' (matched line + offset of -1) does not appear in the output files + # the newline appears in the files (as the first line of the new split) + ["re-3", " --suppress-matched -q - '/^\$/-1' '{*}'", {IN_PIPE => $IN_UNIQ}, + {OUTPUTS => ["a\na\n", "\nXX\nb\nb\n", "\nXX\nc\n", "\nXX\nd\nd\nd\n"]}], + + # the last matched line for a non infinite match repetition is suppressed. + # Up to and including coreutils 8.32, the last match was output. + ["re-4", " --suppress-matched -q - '/^\$/' '{2}'", {IN_PIPE => $IN_UNIQ}, + {OUTPUTS => ["a\na\nYY\n", "XX\nb\nb\nYY\n", "XX\nc\nYY\n", + "XX\nd\nd\nd\n"]}], + + # Test two consecutive matched lines + # without suppress-matched, the second file should contain a single newline. + ["re-4.1", "-q - '/^\$/' '{*}'", {IN_PIPE => "a\n\n\nb\n"}, + {OUTPUTS => [ "a\n", "\n", "\nb\n" ]}], + # suppress-matched will cause the second file to be empty. + ["re-4.2", "--suppress-match -q - '/^\$/' '{*}'", {IN_PIPE => "a\n\n\nb\n"}, + {OUTPUTS => [ "a\n", "", "b\n" ]}], + # suppress-matched + elide-empty should output just two files. + ["re-4.3", "--suppress-match -zq - '/^\$/' '{*}'", {IN_PIPE => "a\n\n\nb\n"}, + {OUTPUTS => [ "a\n", "b\n" ]}], + + + # Test a matched-line as the last line + # default: last file with newline should be created. + ["re-5.1", "-q - '/^\$/' '{*}'", {IN_PIPE => "a\n\nb\n\n"}, + {OUTPUTS => [ "a\n", "\nb\n", "\n" ]}], + # suppress-matched - last empty files should be created. + ["re-5.2", "--suppress-match -q - '/^\$/' '{*}'", {IN_PIPE => "a\n\nb\n\n"}, + {OUTPUTS => [ "a\n", "b\n", "" ]}], + # suppress-matched + elide-empty: just two files should be created. + ["re-5.3", "--suppress-match -zq - '/^\$/' '{*}'", {IN_PIPE => "a\n\nb\n\n"}, + {OUTPUTS => [ "a\n", "b\n" ]}], + + # without suppress-matched, + # the matched lines (2/4/6) appears in the output files + ["int-base", '-q - 2 4 6', {IN_PIPE => $IN_SEQ_6}, + {OUTPUTS => [ "1\n", "2\n3\n", "4\n5\n", "6\n" ]}], + # suppress matched - the matching lines (2/4/6) should not appear. + ["int-1", '--suppress-matched -q - 2 4 6', {IN_PIPE => $IN_SEQ_6}, + {OUTPUTS => [ "1\n", "3\n", "5\n", "" ]}], + # suppress matched + elide-empty + ["int-2", '--suppress-matched -zq - 2 4 6', {IN_PIPE => $IN_SEQ_6}, + {OUTPUTS => [ "1\n", "3\n", "5\n" ]}], +); + + + +=pod +The following loop translate the above @Tests to a Coreutils::run_tests() +compatible structure. It converts "OUTPUTS" key into "CMP" + "POST" keys: +1. Each element in the OUTPUTS key is expected to be an output file + from csplit (named xx00, xx01, xx02...) + create a "CMP" key for each one, with the output and the filename. +2. Add a "POST" key, ensuring no extra files have been created. + (e.g. if there are 4 expected outputs, xx00 to xx03, + ensure xx04 doesn't exist). +3. Add a "PRE" key, deleting all existing 'xx*' files. + +Example: + +Before conversion: + my @csplit_tests = + ( + ["1", '-z -q - 2 4 6', + {IN_PIPE => "1\n2\n3\n4\n5\n6\n"}, + {OUTPUTS => [ "1\n", "2\n3\n", "4\n5\n", "6\n" ], + ] + ) + +After conversion: + + my @csplit_tests = + ( + ["1", '-z -q - 2 4 6', + {IN_PIPE => "1\n2\n3\n4\n5\n6\n"}, + {PRE => sub { unlink glob './xx??' ; }}, + {CMP => ["1\n", {'xx00'=> undef}]}, + {CMP => ["2\n3\n", {'xx01'=> undef}]}, + {CMP => ["4\n5\n", {'xx02'=> undef}]}, + {CMP => ["6\n", {'xx03'=> undef}]}, + {POST => sub { die "extra file" if -e 'xx04'}}, + ], + ); +=cut +my @Tests; +foreach my $t (@csplit_tests) + { + my ($test_name, $cmdline, @others) = @$t; + my $new_ent = [$test_name, $cmdline]; + + my $out_file_num = 0 ; + + foreach my $e (@others) + { + die "Internal error: expecting a hash (e.g. IN_PIPE/OUTPUTS/ERR)" . + "in test '$test_name', got $e" + unless ref $e && (ref $e eq 'HASH'); + + my ($key, $value) = each %$e; + if ($key eq 'OUTPUTS') + { + # Convert each expected OUTPUT to a 'CMP' key. + foreach my $output (@$value) + { + my $filename = sprintf("xx%02d",$out_file_num++); + my $cmp = {CMP => [ $output, { $filename => undef}]}; + push @$new_ent, $cmp; + } + + # Add a 'POST' check + # Ensure no extra files have been created. + my $filename = sprintf("xx%02d",$out_file_num++); + my $post = { POST => sub { die "Test failed: an extraneous file " . + "'$filename' has been created\n" + if -e $filename; } } ; + push @$new_ent, $post; + + # before running each test, cleanup the 'xx00' files + # from previous runs. + my $pre = { PRE => sub { unlink glob "./xx??"; } }; + push @$new_ent, $pre; + } + else + { + # pass other entities as-is (e.g. OUT, ERR, OUT_SUBST, EXIT) + # run_tests() will know how to handle them. + push @$new_ent, $e; + } + } + + push @Tests, $new_ent; + } + +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/csplit/csplit.sh b/tests/csplit/csplit.sh new file mode 100755 index 0000000..a27f61e --- /dev/null +++ b/tests/csplit/csplit.sh @@ -0,0 +1,103 @@ +#!/bin/sh +# various csplit tests + +# Copyright (C) 2001-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_ csplit + + +# csplit could get a failed assertion to 2.0.17 +(echo a; echo; echo) > in +csplit in '/^$/' 2 > out || fail=1 +cat <<EOF > exp +2 +0 +2 +EOF +compare exp out || fail=1 +rm -f in out exp + +# Ensure that xx02 contains just two newlines. +# This would fail due to reading from freed buffer with coreutils-5.0.91. +printf '\n\n' > exp +cp xx02 out || fail=1 +compare exp out || fail=1 +rm -f in out exp + +# csplit would infloop +(echo; echo a) > in +csplit in '/a/-1' '{*}' > out || fail=1 +cat <<EOF > exp +0 +3 +EOF +compare exp out || fail=1 +rm -f in out exp + +# 'echo |csplit - 1 1' used to abort. +echo > in +csplit in 1 1 > out 2> err || fail=1 +cat <<EOF > exp +0 +0 +1 +EOF +compare exp out || fail=1 +cat <<\EOF > experr +csplit: warning: line number '1' is the same as preceding line number +EOF +compare experr err || fail=1 +rm -f in out exp err experr + +# 'echo | csplit -b '%0#6.3x' - 1' incorrectly warned about the format +# up through coreutils 8.6. +echo > in +csplit -b '%0#6.3x' in 1 > out 2> err || fail=1 +cat <<EOF > exp +0 +1 +EOF +compare exp out || fail=1 +touch experr +compare experr err || fail=1 +compare 'xx 000' experr || fail=1 +compare 'xx 0x001' in || fail=1 +rm -f in out exp err experr xx* + +# make sure 'csplit FILE 0' fails. +echo > in +csplit in 0 > out 2> err && fail=1 +csplit in 2 1 > out 2>> err && fail=1 +csplit in 3 3 > out 2>> err && fail=1 +cat <<\EOF > experr +csplit: 0: line number must be greater than zero +csplit: line number '1' is smaller than preceding line number, 2 +csplit: warning: line number '3' is the same as preceding line number +csplit: '3': line number out of range +EOF +compare experr err || fail=1 + +# Ensure that lines longer than the initial buffer length don't cause +# trouble (e.g. reading from freed memory, resulting in corrupt output). +# This test failed at least in coreutils-5.2.1 and 5.3.0, and was fixed +# in 5.3.1. +rm -f in out exp err experr xx?? +printf 'x%8199s\nx\n%8199s\nx\n' x x > in +csplit in '/x\{1\}/' '{*}' > /dev/null || fail=1 +cat xx?? | compare - in || fail=1 + +Exit $fail |