git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 8/8] t0081 (line-buffer): add buffering tests

From
Jonathan Nieder <jrnieder@gmail.com>
Date
Jan 3, 2011, 01:07 UTC
Message-ID
<20110103010716.GE30506@burratino>
In-Reply-To
<20110103004900.GA30506@burratino>

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. The writing end of the fifo is opened in advance in a subshell since even 0-length reads are allowed to block when the writing end of a pipe is not open.

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 <jrnieder@gmail.com>
---
 t/t0081-line-buffer.sh |  110 +++++++++++++++++++++++++++++++++++++++++++++++-
 test-line-buffer.c     |   22 ++++++++-
 2 files changed, 128 insertions(+), 4 deletions(-)
diff --git a/t/t0081-line-buffer.sh b/t/t0081-line-buffer.sh
index 68d6163..33a728e 100755
--- a/t/t0081-line-buffer.sh
+++ b/t/t0081-line-buffer.sh
@@ -1,10 +1,76 @@
 #!/bin/sh
 
 test_description="Test the svn importer's input handling routines.
+
+These tests exercise the line_buffer library, but their real purpose
+is to check the assumptions that library makes of the platform's input
+routines.  Processes engaged in bi-directional communication would
+hang if fread or fgets is too greedy.
+
+While at it, check that input of newlines and null bytes are handled
+correctly.
 "
 . ./test-lib.sh
 
-test_expect_success 'read greeting' '
+test -n "$GIT_REMOTE_SVN_TEST_BIG_FILES" && test_set_prereq EXPENSIVE
+
+generate_tens_of_lines () {
+	tens=$1 &&
+	line=$2 &&
+
+	i=0 &&
+	while test $i -lt "$tens"
+	do
+		for j in a b c d e f g h i j
+		do
+			echo "$line"
+		done &&
+		: $((i = $i + 1)) ||
+		return
+	done
+}
+
+long_read_test () {
+	: each line is 10 bytes, including newline &&
+	line=abcdefghi &&
+	echo "$line" >expect &&
+
+	if ! test_declared_prereq PIPE
+	then
+		echo >&4 "long_read_test: need to declare PIPE prerequisite"
+		return 127
+	fi &&
+	tens_of_lines=$(($1 / 100 + 1)) &&
+	lines=$(($tens_of_lines * 10)) &&
+	readsize=$((($lines - 1) * 10 + 3)) &&
+	copysize=7 &&
+	rm -f input &&
+	mkfifo input &&
+	{
+		{
+			generate_tens_of_lines $tens_of_lines "$line" &&
+			sleep 100
+		} >input &
+	} &&
+	test-line-buffer input <<-EOF >output &&
+	read $readsize
+	copy $copysize
+	EOF
+	kill $! &&
+	test_line_count = $lines output &&
+	tail -n 1 <output >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'setup: have pipes?' '
+      rm -f frob &&
+      if mkfifo frob
+      then
+		test_set_prereq PIPE
+      fi
+'
+
+test_expect_success 'hello world' '
 	echo HELLO >expect &&
 	test-line-buffer <<-\EOF >actual &&
 	read 6
@@ -13,6 +79,21 @@ test_expect_success 'read greeting' '
 	test_cmp expect actual
 '
 
+test_expect_success PIPE '0-length read, no input available' '
+	>expect &&
+	rm -f input &&
+	mkfifo input &&
+	{
+		sleep 100 >input &
+	} &&
+	test-line-buffer input <<-\EOF >actual &&
+	read 0
+	copy 0
+	EOF
+	kill $! &&
+	test_cmp expect actual
+'
+
 test_expect_success '0-length read, send along greeting' '
 	echo HELLO >expect &&
 	test-line-buffer <<-\EOF >actual &&
@@ -23,6 +104,33 @@ test_expect_success '0-length read, send along greeting' '
 	test_cmp expect actual
 '
 
+test_expect_success PIPE '1-byte read, no input available' '
+	printf "%s" ab >expect &&
+	rm -f input &&
+	mkfifo input &&
+	{
+		{
+			printf "%s" a &&
+			printf "%s" b &&
+			sleep 100
+		} >input &
+	} &&
+	test-line-buffer input <<-\EOF >actual &&
+	read 1
+	copy 1
+	EOF
+	kill $! &&
+	test_cmp expect actual
+'
+
+test_expect_success PIPE 'long read (around 8192 bytes)' '
+	long_read_test 8192
+'
+
+test_expect_success PIPE,EXPENSIVE 'longer read (around 65536 bytes)' '
+	long_read_test 65536
+'
+
 test_expect_success 'buffer_read_string copes with null byte' '
 	>expect &&
 	q_to_nul <<-\EOF | test-line-buffer >actual &&
diff --git a/test-line-buffer.c b/test-line-buffer.c
index da0bc65..ec19b13 100644
--- a/test-line-buffer.c
+++ b/test-line-buffer.c
@@ -49,15 +49,31 @@ static void handle_line(const char *line, struct line_buffer *stdin_buf)
 int main(int argc, char *argv[])
 {
 	struct line_buffer stdin_buf = LINE_BUFFER_INIT;
+	struct line_buffer file_buf = LINE_BUFFER_INIT;
+	struct line_buffer *input = &stdin_buf;
+	const char *filename;
 	char *s;
 
-	if (argc != 1)
-		usage("test-line-buffer < script");
+	if (argc == 1)
+		filename = NULL;
+	else if (argc == 2)
+		filename = argv[1];
+	else
+		usage("test-line-buffer [file] < script");
 
 	if (buffer_init(&stdin_buf, NULL))
 		die_errno("open error");
+	if (filename) {
+		if (buffer_init(&file_buf, filename))
+			die_errno("error opening %s", filename);
+		input = &file_buf;
+	}
+
 	while ((s = buffer_read_line(&stdin_buf)))
-		handle_line(s, &stdin_buf);
+		handle_line(s, input);
+
+	if (filename && buffer_deinit(&file_buf))
+		die("error reading from %s", filename);
 	if (buffer_deinit(&stdin_buf))
 		die("input error");
 	if (ferror(stdout))
-- 
1.7.4.rc0.580.g89dc.dirty
Previous: Jonathan NiederNext: Jonathan Nieder
Message 10 of 25 in “teach vcs-svn/line_buffer to handle multiple input files”
  1. 0/4 teach vcs-svn/line_buffer to handle multiple input filesJonathan Nieder, Dec 24, 2010
  2. 1/4 vcs-svn: eliminate global byte_bufferJonathan Nieder, Dec 24, 2010
  3. 2/4 vcs-svn: replace buffer_read_string memory pool with a strbufJonathan Nieder, Dec 24, 2010
  4. 3/4 vcs-svn: collect line_buffer data in a structJonathan Nieder, Dec 24, 2010
  5. 4/4 vcs-svn: teach line_buffer to handle multiple input filesJonathan Nieder, Dec 24, 2010
  6. Jonathan NiederJan 3, 2011
  7. 5/8 vcs-svn: make test-line-buffer input format more flexibleJonathan Nieder, Jan 3, 2011
  8. 6/8 tests: give vcs-svn/line_buffer its own test scriptJonathan Nieder, Jan 3, 2011
  9. 7/8 vcs-svn: tweak test-line-buffer to not assume line-oriented inputJonathan Nieder, Jan 3, 2011
  10. 8/8 t0081 (line-buffer): add buffering testsJonathan Nieder, Jan 3, 2011
  11. Jonathan NiederJan 3, 2011
  12. 12/12 line_buffer: more wrappers around stdio functionsJonathan Nieder, Jan 3, 2011
  13. 09/12 vcs-svn: add binary-safe read functionJonathan Nieder, Jan 3, 2011
  14. 10/12 vcs-svn: allow character-oriented inputJonathan Nieder, Jan 3, 2011
  15. 11/12 vcs-svn: allow input from file descriptorJonathan Nieder, Jan 3, 2011
  16. 12/12 vcs-svn: teach line_buffer about temporary filesJonathan Nieder, Jan 3, 2011
  17. vcs-svn: give control over temporary file namesJonathan Nieder, Jan 22, 2011
  18. [PULL svn-fe] fast-import 'ls', line-buffer changesJonathan Nieder, Feb 26, 2011
  19. David Michael BarrFeb 26, 2011
  20. Junio C HamanoFeb 28, 2011
  21. fast-import: make code "-Wpointer-arith" cleanJonathan Nieder, Feb 28, 2011
  22. Sverre RabbelierFeb 28, 2011
  23. Junio C HamanoFeb 28, 2011
  24. Jonathan NiederFeb 28, 2011
  25. Junio C HamanoMar 1, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.