t9300: factor out portable "head -c" replacement
authorJeff King <peff@peff.net>
Thu, 30 Jun 2016 09:07:54 +0000 (05:07 -0400)
committerJunio C Hamano <gitster@pobox.com>
Fri, 1 Jul 2016 17:17:39 +0000 (10:17 -0700)
It is sometimes useful to be able to read exactly N bytes from a
pipe. Doing this portably turns out to be surprisingly difficult
in shell scripts.

We want a solution that:

- is portable

- never reads more than N bytes due to buffering (which
would mean those bytes are not available to the next
program to read from the same pipe)

- handles partial reads by looping until N bytes are read
(or we see EOF)

- is resilient to stray signals giving us EINTR while
trying to read (even though we don't send them, things
like SIGWINCH could cause apparently-random failures)

Some possible solutions are:

- "head -c" is not portable, and implementations may
buffer (though GNU head does not)

- "read -N" is a bash-ism, and thus not portable

- "dd bs=$n count=1" does not handle partial reads. GNU dd
has iflags=fullblock, but that is not portable

- "dd bs=1 count=$n" fixes the partial read problem (all
reads are 1-byte, so there can be no partial response).
It does make a lot of write() calls, but for our tests
that's unlikely to matter. It's fairly portable. We
already use it in our tests, and it's unlikely that
implementations would screw up any of our criteria. The
most unknown one would be signal handling.

- perl can do a sysread() loop pretty easily. On my Linux
system, at least, it seems to restart the read() call
automatically. If that turns out not to be portable,
though, it would be easy for us to handle it.

That makes the perl solution the least bad (because we
conveniently omitted "length of code" as a criterion).
It's also what t9300 is currently using, so we can just pull
the implementation from there.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
t/t9300-fast-import.sh
t/test-lib-functions.sh
index 4bca35c2594bbff4a659e1d6d1dcc5e746956a84..38a06aaafc72a337b1d25c9f18715dec4b2208f4 100755 (executable)
@@ -7,23 +7,6 @@ test_description='test git fast-import utility'
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
-# Print $1 bytes from stdin to stdout.
-#
-# This could be written as "head -c $1", but IRIX "head" does not
-# support the -c option.
-head_c () {
-       perl -e '
-               my $len = $ARGV[1];
-               while ($len > 0) {
-                       my $s;
-                       my $nread = sysread(STDIN, $s, $len);
-                       die "cannot read: $!" unless defined($nread);
-                       print $s;
-                       $len -= $nread;
-               }
-       ' - "$1"
-}
-
 verify_packs () {
        for p in .git/objects/pack/*.pack
        do
@@ -2480,7 +2463,7 @@ test_expect_success PIPE 'R: copy using cat-file' '
 
                read blob_id type size <&3 &&
                echo "$blob_id $type $size" >response &&
-               head_c $size >blob <&3 &&
+               test_copy_bytes $size >blob <&3 &&
                read newline <&3 &&
 
                cat <<-EOF &&
@@ -2523,7 +2506,7 @@ test_expect_success PIPE 'R: print blob mid-commit' '
                EOF
 
                read blob_id type size <&3 &&
-               head_c $size >actual <&3 &&
+               test_copy_bytes $size >actual <&3 &&
                read newline <&3 &&
 
                echo
@@ -2558,7 +2541,7 @@ test_expect_success PIPE 'R: print staged blob within commit' '
                echo "cat-blob $to_get" &&
 
                read blob_id type size <&3 &&
-               head_c $size >actual <&3 &&
+               test_copy_bytes $size >actual <&3 &&
                read newline <&3 &&
 
                echo deleteall
index 48884d520813998a735d89d406b45c63b0bf748d..90856d67e52dcdaabd13e78abd29c7c3d3949643 100644 (file)
@@ -961,3 +961,17 @@ test_env () {
                done
        )
 }
+
+# Read up to "$1" bytes (or to EOF) from stdin and write them to stdout.
+test_copy_bytes () {
+       perl -e '
+               my $len = $ARGV[1];
+               while ($len > 0) {
+                       my $s;
+                       my $nread = sysread(STDIN, $s, $len);
+                       die "cannot read: $!" unless defined($nread);
+                       print $s;
+                       $len -= $nread;
+               }
+       ' - "$1"
+}