Skip to content

Commit d280f68

Browse files
committed
t0081 (line-buffer): add buffering tests
POSIX makes the behavior of read(2) from a pipe fairly clear: a read from an empty pipe will block until there is data available and any other read will not block, prefering to return a partial result. Likewise, fread(3) and fgets(3) are clearly specified to act as though implemented by calling fgetc(3) in a simple loop. But the buffering behavior of fgetc is less clear. Luckily, no sane platform is going to implement fgetc by calling the equivalent of read(2) more than once. fgetc has to be able to return without filling its buffer to preserve errno when errors are encountered anyway. So let's assume the simpler behavior (trust) but add some tests to catch insane platforms that violate that when they come (verify). First check that fread can handle a 0-length read from an empty fifo. Because open(O_RDONLY) blocks until the writing end is open, open the writing end of the fifo in advance in a subshell. Next try short inputs from a pipe that is not filled all the way. Lastly (two tests) try very large inputs from a pipe that will not fit in the relevant buffers. The first of these tests reads a little more than 8192 bytes, which is BUFSIZ (the size of stdio's buffers) on this Linux machine. The second reads a little over 64 KiB (the pipe capacity on Linux) and is not run unless requested by setting the GIT_REMOTE_SVN_TEST_BIG_FILES environment variable. Signed-off-by: Jonathan Nieder <[email protected]>
1 parent 7b990c9 commit d280f68

File tree

2 files changed

+128
-4
lines changed

2 files changed

+128
-4
lines changed

t/t0081-line-buffer.sh

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,76 @@
11
#!/bin/sh
22

33
test_description="Test the svn importer's input handling routines.
4+
5+
These tests exercise the line_buffer library, but their real purpose
6+
is to check the assumptions that library makes of the platform's input
7+
routines. Processes engaged in bi-directional communication would
8+
hang if fread or fgets is too greedy.
9+
10+
While at it, check that input of newlines and null bytes are handled
11+
correctly.
412
"
513
. ./test-lib.sh
614

7-
test_expect_success 'read greeting' '
15+
test -n "$GIT_REMOTE_SVN_TEST_BIG_FILES" && test_set_prereq EXPENSIVE
16+
17+
generate_tens_of_lines () {
18+
tens=$1 &&
19+
line=$2 &&
20+
21+
i=0 &&
22+
while test $i -lt "$tens"
23+
do
24+
for j in a b c d e f g h i j
25+
do
26+
echo "$line"
27+
done &&
28+
: $((i = $i + 1)) ||
29+
return
30+
done
31+
}
32+
33+
long_read_test () {
34+
: each line is 10 bytes, including newline &&
35+
line=abcdefghi &&
36+
echo "$line" >expect &&
37+
38+
if ! test_declared_prereq PIPE
39+
then
40+
echo >&4 "long_read_test: need to declare PIPE prerequisite"
41+
return 127
42+
fi &&
43+
tens_of_lines=$(($1 / 100 + 1)) &&
44+
lines=$(($tens_of_lines * 10)) &&
45+
readsize=$((($lines - 1) * 10 + 3)) &&
46+
copysize=7 &&
47+
rm -f input &&
48+
mkfifo input &&
49+
{
50+
{
51+
generate_tens_of_lines $tens_of_lines "$line" &&
52+
sleep 100
53+
} >input &
54+
} &&
55+
test-line-buffer input <<-EOF >output &&
56+
read $readsize
57+
copy $copysize
58+
EOF
59+
kill $! &&
60+
test_line_count = $lines output &&
61+
tail -n 1 <output >actual &&
62+
test_cmp expect actual
63+
}
64+
65+
test_expect_success 'setup: have pipes?' '
66+
rm -f frob &&
67+
if mkfifo frob
68+
then
69+
test_set_prereq PIPE
70+
fi
71+
'
72+
73+
test_expect_success 'hello world' '
874
echo HELLO >expect &&
975
test-line-buffer <<-\EOF >actual &&
1076
read 6
@@ -13,6 +79,21 @@ test_expect_success 'read greeting' '
1379
test_cmp expect actual
1480
'
1581

82+
test_expect_success PIPE '0-length read, no input available' '
83+
>expect &&
84+
rm -f input &&
85+
mkfifo input &&
86+
{
87+
sleep 100 >input &
88+
} &&
89+
test-line-buffer input <<-\EOF >actual &&
90+
read 0
91+
copy 0
92+
EOF
93+
kill $! &&
94+
test_cmp expect actual
95+
'
96+
1697
test_expect_success '0-length read, send along greeting' '
1798
echo HELLO >expect &&
1899
test-line-buffer <<-\EOF >actual &&
@@ -23,6 +104,33 @@ test_expect_success '0-length read, send along greeting' '
23104
test_cmp expect actual
24105
'
25106

107+
test_expect_success PIPE '1-byte read, no input available' '
108+
printf "%s" ab >expect &&
109+
rm -f input &&
110+
mkfifo input &&
111+
{
112+
{
113+
printf "%s" a &&
114+
printf "%s" b &&
115+
sleep 100
116+
} >input &
117+
} &&
118+
test-line-buffer input <<-\EOF >actual &&
119+
read 1
120+
copy 1
121+
EOF
122+
kill $! &&
123+
test_cmp expect actual
124+
'
125+
126+
test_expect_success PIPE 'long read (around 8192 bytes)' '
127+
long_read_test 8192
128+
'
129+
130+
test_expect_success PIPE,EXPENSIVE 'longer read (around 65536 bytes)' '
131+
long_read_test 65536
132+
'
133+
26134
test_expect_success 'buffer_read_string copes with null byte' '
27135
>expect &&
28136
q_to_nul <<-\EOF | test-line-buffer >actual &&

test-line-buffer.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,31 @@ static void handle_line(const char *line, struct line_buffer *stdin_buf)
4949
int main(int argc, char *argv[])
5050
{
5151
struct line_buffer stdin_buf = LINE_BUFFER_INIT;
52+
struct line_buffer file_buf = LINE_BUFFER_INIT;
53+
struct line_buffer *input = &stdin_buf;
54+
const char *filename;
5255
char *s;
5356

54-
if (argc != 1)
55-
usage("test-line-buffer < script");
57+
if (argc == 1)
58+
filename = NULL;
59+
else if (argc == 2)
60+
filename = argv[1];
61+
else
62+
usage("test-line-buffer [file] < script");
5663

5764
if (buffer_init(&stdin_buf, NULL))
5865
die_errno("open error");
66+
if (filename) {
67+
if (buffer_init(&file_buf, filename))
68+
die_errno("error opening %s", filename);
69+
input = &file_buf;
70+
}
71+
5972
while ((s = buffer_read_line(&stdin_buf)))
60-
handle_line(s, &stdin_buf);
73+
handle_line(s, input);
74+
75+
if (filename && buffer_deinit(&file_buf))
76+
die("error reading from %s", filename);
6177
if (buffer_deinit(&stdin_buf))
6278
die("input error");
6379
if (ferror(stdout))

0 commit comments

Comments
 (0)