[PATCH v2 1/8] stdio: Add more setvbuf tests

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Wed Jun 18 19:43:39 GMT 2025



On 18/06/25 11:33, Jovan Dmitrovic wrote:
> From: DJ Delorie <dj@redhat.com>
> 
> Signed-off-by: Jovan Dmitrović <jovan.dmitrovic@htecgroup.com>

I think this patch are not related to this set, maybe it was sent
wrongly (as it does not apply)?

> ---
>  stdio-common/Makefile           |   10 +-
>  stdio-common/tst-setvbuf2-ind.c |    2 +
>  stdio-common/tst-setvbuf2.c     | 1030 +++++++++++++++++++++++++++++++
>  3 files changed, 1041 insertions(+), 1 deletion(-)
>  create mode 100644 stdio-common/tst-setvbuf2-ind.c
>  create mode 100644 stdio-common/tst-setvbuf2.c
> 
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index 31f40cf57c..3fd33b836d 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -378,7 +378,9 @@ endif
>  endif
>  
>  tests-container += \
> -  tst-popen3
> +  tst-popen3 \
> +  tst-setvbuf2 \
> +  tst-setvbuf2-ind
>    # tests-container
>  
>  generated += \
> @@ -390,6 +392,8 @@ generated += \
>  
>  tests-internal = \
>    tst-grouping_iterator \
> +  tst-setvbuf2 \
> +  tst-setvbuf2-ind \
>    # tests-internal
>  
>  test-srcs = \
> @@ -762,6 +766,10 @@ $(objpfx)tst-setvbuf1-cmp.out: tst-setvbuf1.expect $(objpfx)tst-setvbuf1.out
>  	cmp $^ > $@; \
>  	$(evaluate-test)
>  
> +CFLAGS-tst-setvbuf2.c += -DIND_PROC=\"$(objpfx)tst-setvbuf2-ind\"
> +$(objpfx)tst-setvbuf2-ind : $(objpfx)tst-setvbuf2-ind.o
> +$(objpfx)tst-setvbuf2.out: $(objpfx)tst-setvbuf2-ind
> +
>  $(objpfx)tst-printf-round: $(libm)
>  $(objpfx)tst-scanf-round: $(libm)
>  
> diff --git a/stdio-common/tst-setvbuf2-ind.c b/stdio-common/tst-setvbuf2-ind.c
> new file mode 100644
> index 0000000000..fda2942c24
> --- /dev/null
> +++ b/stdio-common/tst-setvbuf2-ind.c
> @@ -0,0 +1,2 @@
> +#define INDEPENDENT_PART 1
> +#include "tst-setvbuf2.c"
> diff --git a/stdio-common/tst-setvbuf2.c b/stdio-common/tst-setvbuf2.c
> new file mode 100644
> index 0000000000..6cc83355f3
> --- /dev/null
> +++ b/stdio-common/tst-setvbuf2.c
> @@ -0,0 +1,1030 @@
> +/* Test setvbuf under various conditions.
> +   Copyright (C) 2025 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +/* This file is used twice, once as the test itself (where do_test
> +   is defined) and once as a subprocess we spawn to test stdin et all
> +   (where main is defined).  INDEPENDENT_PART is defined for the
> +   latter.
> +
> +   Note also that the purpose of this test is to test setvbuf, not the
> +   underlying buffering code.  */
> +
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdio_ext.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <signal.h>
> +#include <libio.h>
> +#include <termios.h>
> +
> +#include <support/support.h>
> +#include <support/check.h>
> +#include <support/temp_file.h>
> +#include <support/xstdio.h>
> +#include <support/xunistd.h>
> +#include <support/xthread.h>
> +#include <support/tty.h>
> +
> +/* Dear future developer: If you are reading this, you are likely
> +   trying to change or understand this test.  In that case, these
> +   debug/dump macros will be helpful.  */
> +#if 0
> +# define debug printf ("\033[3%dm%s:%d\033[0m\n", \
> +		       (__LINE__ % 6) + 1, __FUNCTION__, __LINE__);
> +
> +static void
> +dumpfp (FILE *fp)
> +{
> +  char f[10], *p=f;
> +
> +  if (fp->_flags & _IO_UNBUFFERED)
> +    *p++ = 'N';
> +  if (fp->_flags & _IO_LINE_BUF)
> +    *p++ = 'L';
> +  if (p == f)
> +    *p++ = 'B';
> +  *p = 0;
> +
> +  printf ("FILE %p flags %s"
> +	  " read %p \033[%dm%+ld \033[%dm%+ld\033[0m"
> +	  " write %p \033[%dm%+ld \033[%dm%+ld\033[0m %ld"
> +	  " buf %p \033[%dm%+ld\033[0m  sz %ld pend %ld\n",
> +	  fp, f,
> +
> +	  fp->_IO_read_base,
> +	  fp->_IO_read_ptr == fp->_IO_read_base ? 33 : 32,
> +	  fp->_IO_read_ptr - fp->_IO_read_base,
> +	  fp->_IO_read_end == fp->_IO_read_base ? 33 : 36,
> +	  fp->_IO_read_end - fp->_IO_read_base,
> +
> +	  fp->_IO_write_base,
> +	  fp->_IO_write_ptr == fp->_IO_write_base ? 33 : 32,
> +	  fp->_IO_write_ptr - fp->_IO_write_base,
> +	  fp->_IO_write_end == fp->_IO_write_base ? 33 : 36,
> +	  fp->_IO_write_end - fp->_IO_write_base,
> +	  fp->_IO_write_end - fp->_IO_write_base,
> +
> +	  fp->_IO_buf_base,
> +	  fp->_IO_buf_end == fp->_IO_buf_base ? 33 : 35,
> +	  fp->_IO_buf_end - fp->_IO_buf_base,
> +	  __fbufsize (fp), __fpending (fp)
> +	  );
> +}
> +#else
> +# define debug
> +# define dumpfp(FP)
> +#endif
> +
> +#ifndef INDEPENDENT_PART
> +/* st_blksize value for that file, or BUFSIZ if out of range.  */
> +static int blksize = BUFSIZ;
> +#endif
> +
> +/* Our test buffer.  */
> +#define TEST_BUFSIZE 42
> +static int bufsize = TEST_BUFSIZE < BUFSIZ ? TEST_BUFSIZE : BUFSIZ;
> +static char *buffer;
> +
> +/* Test data, both written to that file and used as an in-memory
> +   stream.  */
> +char test_data[2 * BUFSIZ];
> +
> +#define TEST_STRING "abcdef\n"
> +
> +enum test_source_case
> +  {
> +    test_source_file,
> +    test_source_pipe,
> +    test_source_fifo,
> +    test_source_pseudo_terminal,
> +    test_source_dev_null,
> +    test_source_count,
> +  };
> +
> +static const char *const test_source_name[test_source_count] =
> +  {
> +    "regular file",
> +    "pipe",
> +    "fifo",
> +    "pseudo_terminal",
> +    "dev_null"
> +  };
> +
> +enum test_stream_case
> +  {
> +    test_stream_stdin,
> +    test_stream_stdout,
> +    test_stream_stderr,
> +    test_stream_fopen_r,
> +    test_stream_fdopen_r,
> +    test_stream_fopen_w,
> +    test_stream_fdopen_w,
> +    test_stream_count
> +  };
> +
> +static bool test_stream_reads[test_stream_count] =
> +  {
> +    true,
> +    false,
> +    false,
> +    true,
> +    true,
> +    false,
> +    false
> +  };
> +
> +static const char *const test_stream_name[test_stream_count] =
> +  {
> +    "stdin",
> +    "stdout",
> +    "stderr",
> +    "fopen (read)",
> +    "fdopen (read)",
> +    "fopen (write)",
> +    "fdopen (write)"
> +  };
> +
> +enum test_config_case
> +  {
> +    test_config_none,
> +    test_config_unbuffered,
> +    test_config_line,
> +    test_config_fully,
> +    test_config_count
> +  };
> +
> +static const char *const test_config_name[test_config_count] =
> +  {
> +    "no change",
> +    "unbuffered",
> +    "line buffered",
> +    "fully buffered"
> +  };
> +
> +FILE *test_stream;
> +
> +char *test_file_name = NULL;
> +int pty_fd;
> +char *test_pipe_name = NULL;
> +int test_pipe[2];
> +
> +/* This is either -1 or represents a pre-opened file descriptor for
> +   the test as returned by prepare_test_file.  */
> +int test_fd;
> +
> +/*------------------------------------------------------------*/
> +
> +/* Note that throughout this test we reopen, remove, and change
> +   to/from a fifo, the test file.  This would normally cause a race
> +   condition, except that we're in a test container.  No other process
> +   can run in the test container simultaneously.  */
> +
> +void
> +prepare_test_data (void)
> +{
> +  buffer = (char *) xmalloc (bufsize);
> +
> +#ifndef INDEPENDENT_PART
> +  /* Both file and pipe need this.  */
> +  if (test_file_name == NULL)
> +    {
> +      debug;
> +      int fd = create_temp_file ("tst-setvbuf2", &test_file_name);
> +      TEST_VERIFY_EXIT (fd != -1);
> +      struct stat64 st;
> +      xfstat64 (fd, &st);
> +      if (st.st_blksize > 0 && st.st_blksize < BUFSIZ)
> +	blksize = st.st_blksize;
> +      xclose (fd);
> +    }
> +#endif
> +
> +  for (size_t i = 0; i < 2 * BUFSIZ; i++)
> +    {
> +      unsigned char c = TEST_STRING[i % strlen (TEST_STRING)];
> +      test_data[i] = c;
> +    }
> +}
> +
> +#ifndef INDEPENDENT_PART
> +
> +/* These functions provide a source/sink for the "other" side of any
> +   pipe-style descriptor we're using for test.  */
> +
> +static pthread_t writer_thread_tid = 0;
> +static pthread_t reader_thread_tid = 0;
> +
> +typedef struct {
> +  int fd;
> +  const char *fname;
> +} ThreadData;
> +/* It's OK if this is static, we only run one at a time.  */
> +ThreadData thread_data;
> +
> +static void *
> +writer_thread_proc (void *closure)
> +{
> +  ThreadData *td = (ThreadData *) closure;
> +  int fd;
> +  int i;
> +  ssize_t wn;
> +  debug;
> +
> +  if (td->fname)
> +    td->fd = xopen (td->fname, O_WRONLY, 0777);
> +  fd = td->fd;
> +
> +  while (1)
> +    {
> +      i = 0;
> +      while (i < BUFSIZ)
> +	{
> +	  wn = write (fd, test_data + i, BUFSIZ - i);
> +	  if (wn <= 0)
> +	    break;
> +	  i += wn;
> +	}
> +    }
> +  return NULL;
> +}
> +
> +static void *
> +reader_thread_proc (void *closure)
> +{
> +  ThreadData *td = (ThreadData *) closure;
> +  int fd;
> +  ssize_t rn;
> +  int n = 0;
> +  debug;
> +
> +  if (td->fname)
> +    td->fd = xopen (td->fname, O_RDONLY, 0777);
> +  fd = td->fd;
> +
> +  while (1)
> +    {
> +      char buf[BUFSIZ];
> +      rn = read (fd, buf, BUFSIZ);
> +      if (rn <= 0)
> +	break;
> +      TEST_COMPARE_BLOB (buf, rn, test_data+n, rn);
> +      n += rn;
> +    }
> +  return NULL;
> +}
> +
> +static void
> +start_writer_thread (int fd)
> +{
> +  debug;
> +  thread_data.fd = fd;
> +  thread_data.fname = NULL;
> +  writer_thread_tid = xpthread_create (NULL, writer_thread_proc,
> +				       (void *)&thread_data);
> +}
> +
> +static void
> +start_writer_thread_n (const char *fname)
> +{
> +  debug;
> +  thread_data.fd = 0;
> +  thread_data.fname = fname;
> +  writer_thread_tid = xpthread_create (NULL, writer_thread_proc,
> +				       (void *)&thread_data);
> +}
> +
> +static void
> +end_writer_thread (void)
> +{
> +  debug;
> +  if (writer_thread_tid)
> +    {
> +      pthread_cancel (writer_thread_tid);
> +      xpthread_join (writer_thread_tid);
> +      xclose (thread_data.fd);
> +      writer_thread_tid = 0;
> +    }
> +}
> +
> +static void
> +start_reader_thread (int fd)
> +{
> +  debug;
> +  thread_data.fd = fd;
> +  thread_data.fname = NULL;
> +  reader_thread_tid = xpthread_create (NULL, reader_thread_proc,
> +				       (void *)&thread_data);
> +}
> +
> +static void
> +start_reader_thread_n (const char *fname)
> +{
> +  debug;
> +  thread_data.fd = 0;
> +  thread_data.fname = fname;
> +  reader_thread_tid = xpthread_create (NULL, reader_thread_proc,
> +				       (void *)&thread_data);
> +}
> +
> +static void
> +end_reader_thread (void)
> +{
> +  debug;
> +  if (reader_thread_tid)
> +    {
> +      pthread_cancel (reader_thread_tid);
> +      xpthread_join (reader_thread_tid);
> +      xclose (thread_data.fd);
> +      reader_thread_tid = 0;
> +    }
> +}
> +
> +/*------------------------------------------------------------*/
> +
> +/* These two functions are reponsible for choosing a file to be tested
> +   against, typically by returning a filename but in a few cases also
> +   providing a file descriptor (i.e. for fdopen).  */
> +
> +static const char *
> +prepare_test_file (enum test_source_case f, enum test_stream_case s)
> +{
> +  debug;
> +
> +  test_fd = -1;
> +
> +  switch (f)
> +    {
> +    case test_source_file:
> +      {
> +	if (test_stream_reads[f])
> +	  {
> +	    debug;
> +	    FILE *fp = xfopen (test_file_name, "w");
> +	    TEST_VERIFY_EXIT (fwrite (test_data, 1, 2 * BUFSIZ, fp)
> +			      == 2 * BUFSIZ);
> +	    xfclose (fp);
> +	  }
> +	debug;
> +	return test_file_name;
> +      }
> +
> +    case test_source_pipe:
> +      {
> +	debug;
> +	xpipe (test_pipe);
> +	if (test_stream_reads[s])
> +	  {
> +	    start_writer_thread (test_pipe[1]);
> +	    test_fd = test_pipe[0];
> +	  }
> +	else
> +	  {
> +	    start_reader_thread (test_pipe[0]);
> +	    test_fd = test_pipe[1];
> +	  }
> +	test_pipe_name = xasprintf ("/proc/self/fd/%d", test_fd);
> +	debug;
> +	return test_pipe_name;
> +      }
> +
> +    case test_source_fifo:
> +      {
> +	/* We do not want to fail/exit if the file doesn't exist.  */
> +	unlink (test_file_name);
> +	xmkfifo (test_file_name, 0600);
> +	debug;
> +	if (test_stream_reads[s])
> +	  start_writer_thread_n (test_file_name);
> +	else
> +	  start_reader_thread_n (test_file_name);
> +	debug;
> +	return test_file_name;
> +      }
> +
> +    case test_source_pseudo_terminal:
> +      {
> +	support_openpty (&pty_fd, &test_fd, &test_pipe_name, NULL, NULL);
> +
> +	debug;
> +	if (test_stream_reads[s])
> +	  start_writer_thread (pty_fd);
> +	else
> +	  start_reader_thread (pty_fd);
> +
> +	debug;
> +	return test_pipe_name;
> +      }
> +
> +    case test_source_dev_null:
> +	debug;
> +      return "/dev/null";
> +
> +    default:
> +      abort ();
> +    }
> +}
> +
> +static void
> +unprepare_test_file (FILE *fp,
> +		     enum test_source_case f,
> +		     enum test_stream_case s)
> +{
> +  debug;
> +  switch (f)
> +    {
> +    case test_source_file:
> +      break;
> +
> +    case test_source_pipe:
> +      free (test_pipe_name);
> +      if (test_stream_reads[s])
> +	end_writer_thread ();
> +      else
> +	end_reader_thread ();
> +      break;
> +
> +    case test_source_fifo:
> +      if (test_stream_reads[s])
> +	end_writer_thread ();
> +      else
> +	end_reader_thread ();
> +      unlink (test_file_name);
> +      break;
> +
> +    case test_source_pseudo_terminal:
> +      free (test_pipe_name);
> +      if (test_stream_reads[s])
> +	end_writer_thread ();
> +      else
> +	end_reader_thread ();
> +      break;
> +
> +    case test_source_dev_null:
> +      break;
> +
> +    default:
> +      abort ();
> +    }
> +  debug;
> +}
> +
> +/*------------------------------------------------------------*/
> +
> +/* This function takes a filename and returns a file descriptor,
> +   opened according to the method requested.  */
> +
> +static FILE *
> +open_test_stream (enum test_source_case f, enum test_stream_case s)
> +{
> +  int fd;
> +  FILE *fp;
> +  const char *fname;
> +
> +  debug;
> +  fname = prepare_test_file (f, s);
> +  if (fname == NULL)
> +    return NULL;
> +
> +  switch (s)
> +    {
> +    case test_stream_stdin:
> +      fp = xfopen (fname, "r");
> +      break;
> +
> +    case test_stream_stdout:
> +      fp = xfopen (fname, "w");
> +      break;
> +
> +    case test_stream_stderr:
> +      fp = xfopen (fname, "w");
> +      break;
> +
> +    case test_stream_fopen_r:
> +      fp = xfopen (fname, "r");
> +      break;
> +
> +    case test_stream_fdopen_r:
> +      if (test_fd == -1)
> +	fd = xopen (fname, O_RDONLY, 0);
> +      else
> +	fd = test_fd;
> +      fp = fdopen (fd, "r");
> +      break;
> +
> +    case test_stream_fopen_w:
> +      fp = xfopen (fname, "w");
> +      break;
> +
> +    case test_stream_fdopen_w:
> +      fd = xopen (fname, O_WRONLY|O_CREAT|O_TRUNC, 0777);
> +      fp = fdopen (fd, "w");
> +      break;
> +
> +    default:
> +      abort ();
> +    }
> +  TEST_VERIFY_EXIT (fp != NULL);
> +
> +  if (f == test_source_pseudo_terminal)
> +    {
> +      struct termios t;
> +      /* We disable the NL to CR-LF conversion so that we can compare
> +	 data without having to remove the extra CRs.  */
> +      if (tcgetattr (fileno (fp), &t) < 0)
> +	FAIL_EXIT1 ("tcgetattr failed: %m");
> +      t.c_oflag &= ~ONLCR;
> +      if (tcsetattr (fileno (fp), TCSANOW, &t) < 0)
> +	FAIL_EXIT1 ("tcsetattr failed: %m");
> +    }
> +
> +  debug;
> +  printf ("source %s stream %s file %s fd %d\n",
> +	  test_source_name[f],
> +	  test_stream_name[s], fname, fileno (fp));
> +  return fp;
> +}
> +
> +#endif
> +
> +/*------------------------------------------------------------*/
> +
> +/* These functions do the actual testing - setting various buffering
> +   options and verifying that they buffer as expected.  */
> +
> +static void
> +test_put_string (FILE *fp, const char *s, int count)
> +{
> +  while (*s && count--)
> +    {
> +      fputc (*s++, fp);
> +      TEST_VERIFY_EXIT (!ferror (fp));
> +    }
> +}
> +
> +int
> +verify_fully_buffered (FILE *fp,
> +		       enum test_source_case f,
> +		       enum test_stream_case s,
> +		       enum test_config_case c)
> +{
> +  debug;
> +  if (test_stream_reads[s])
> +    {
> +      char buf[10];
> +      dumpfp (fp);
> +      size_t fc = fread (buf, 1, 10 - 1, fp);
> +      dumpfp (fp);
> +
> +      ssize_t count = fp->_IO_read_ptr - fp->_IO_read_base;
> +
> +      TEST_VERIFY (fp->_IO_read_base != NULL);
> +      if (f == test_source_dev_null)
> +	{
> +	  TEST_VERIFY (fc == 0);
> +	  TEST_VERIFY (count == 0);
> +	}
> +      else if (f == test_source_pseudo_terminal)
> +	{
> +	  TEST_VERIFY (fc == 9);
> +	  TEST_VERIFY (count == 3 || count == 10);
> +	}
> +      else
> +	{
> +	  TEST_VERIFY (fc == 9);
> +	  TEST_VERIFY (count == 10);
> +	}
> +
> +      /* We already checked for the first character being 'a'.  */
> +      if (count > 1)
> +	{
> +	  TEST_COMPARE_BLOB (buf, count - 1, test_data + 1, count - 1);
> +	  TEST_COMPARE_BLOB (fp->_IO_read_base, count, test_data, count);
> +	}
> +    }
> +  else
> +    {
> +      dumpfp (fp);
> +      test_put_string (fp, test_data + 1, 10 - 1);
> +      dumpfp (fp);
> +      TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 10);
> +      TEST_COMPARE_BLOB (fp->_IO_write_base, 10, test_data, 10);
> +    }
> +
> +  TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)), 0);
> +  if (c != test_config_none)
> +    TEST_COMPARE (__fbufsize (fp), bufsize);
> +  return 0;
> +}
> +
> +int
> +verify_line_buffered (FILE *fp,
> +		      enum test_source_case f,
> +		      enum test_stream_case s,
> +		      enum test_config_case c)
> +{
> +  debug;
> +  /* "line buffered" for inputs is not really defined; what you really
> +     want here is to control the device providing input.  For GLIBC a
> +     line-buffered input is treated as fully buffered.  */
> +  if (test_stream_reads[s])
> +    {
> +      char buf[10];
> +      dumpfp (fp);
> +      size_t fc = fread (buf, 1, 10 - 1, fp);
> +      dumpfp (fp);
> +
> +      ssize_t count = fp->_IO_read_ptr - fp->_IO_read_base;
> +
> +      TEST_VERIFY (fp->_IO_read_base != NULL);
> +      if (f == test_source_dev_null)
> +	{
> +	  TEST_VERIFY (fc == 0);
> +	  TEST_VERIFY (count == 0);
> +	}
> +      else if (f == test_source_pseudo_terminal)
> +	{
> +	  TEST_VERIFY (fc == 9);
> +	  TEST_VERIFY (count == 3 || count == 10);
> +	}
> +      else
> +	{
> +	  TEST_VERIFY (fc == 9);
> +	  TEST_VERIFY (count == 10);
> +	}
> +
> +      /* We already checked for the first character being 'a'.  */
> +      if (count > 1)
> +	{
> +	  TEST_COMPARE_BLOB (buf, count - 1, test_data + 1, count - 1);
> +	  TEST_COMPARE_BLOB (fp->_IO_read_base, count, test_data, count);
> +	}
> +    }
> +  else
> +    {
> +      dumpfp (fp);
> +      test_put_string (fp, test_data + 1, 10 - 1);
> +      dumpfp (fp);
> +      TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 3);
> +      /* The first "abcdef\n" got flushed, leaving "abc".  */
> +      TEST_COMPARE_BLOB (fp->_IO_write_base, 3, test_data + 7, 3);
> +    }
> +
> +  TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)), _IO_LINE_BUF);
> +  if (c != test_config_none)
> +    TEST_COMPARE (__fbufsize (fp), bufsize);
> +  return 0;
> +}
> +
> +int
> +verify_unbuffered (FILE *fp,
> +		   enum test_source_case f,
> +		   enum test_stream_case s,
> +		   enum test_config_case c)
> +{
> +  debug;
> +  if (test_stream_reads[s])
> +    {
> +      /* We've already read one byte.  */
> +      dumpfp (fp);
> +      TEST_VERIFY (fp->_IO_read_base != NULL);
> +      if (f == test_source_dev_null)
> +	TEST_COMPARE (fp->_IO_read_ptr - fp->_IO_read_base, 0);
> +      else
> +	{
> +	  TEST_COMPARE (fp->_IO_read_ptr - fp->_IO_read_base, 1);
> +	  TEST_COMPARE (fp->_IO_read_base[0], test_data[0]);
> +	  TEST_VERIFY (fp->_IO_read_ptr == fp->_IO_read_end);
> +	}
> +    }
> +  else
> +    {
> +      dumpfp (fp);
> +      fputc (test_data[1], fp);
> +      dumpfp (fp);
> +      TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 0);
> +      TEST_COMPARE (fp->_IO_write_base[0], test_data[1]);
> +      TEST_VERIFY (fp->_IO_write_end == fp->_IO_write_base);
> +    }
> +  TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)),
> +	       _IO_UNBUFFERED);
> +  TEST_COMPARE (__fbufsize (fp), 1);
> +  return 0;
> +}
> +
> +static int
> +do_setvbuf (FILE *fp, void *buf, int flags, int size,
> +	    enum test_stream_case s)
> +{
> +  if (s != test_stream_stdout)
> +    printf ("SETVBUF %p %p %s %d\n",
> +	    fp, buf,
> +	    flags == _IONBF ? "_IONBF"
> +	    : flags == _IOLBF ? "_IOLBF"
> +	    : flags == _IOFBF ? "_IOFBF"
> +	    : "???", size);
> +  if (setvbuf (fp, buf, flags, size))
> +    {
> +      perror ("setvbuf");
> +      return 1;
> +    }
> +  return 0;
> +}
> +
> +int
> +do_second_part (FILE *fp,
> +		enum test_source_case f,
> +		enum test_stream_case s,
> +		enum test_config_case c)
> +{
> +  /* At this point, FP is the stream to test according to the other
> +     parameters.  */
> +
> +  int rv = 0;
> +  int flags_before;
> +  int flags_after;
> +
> +  debug;
> +
> +  flags_before = fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF);
> +
> +  /* This is where we do the thing we're testing for.  */
> +  switch (c)
> +    {
> +    case test_config_none:
> +      /* Buffering is unchanged.  */
> +      break;
> +
> +    case test_config_unbuffered:
> +      do_setvbuf (fp, NULL, _IONBF, 0, s);
> +      break;
> +
> +    case test_config_line:
> +      do_setvbuf (fp, buffer, _IOLBF, bufsize, s);
> +      break;
> +
> +    case test_config_fully:
> +      do_setvbuf (fp, buffer, _IOFBF, bufsize, s);
> +      break;
> +
> +    default:
> +      abort ();
> +    }
> +
> +  flags_after = fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF);
> +
> +  /* Check the buffer mode after we touch it, if we touched it.  */
> +  switch (c)
> +    {
> +    case test_config_none:
> +      /* Buffering is unchanged, but may change on the first read/write.  */
> +      TEST_COMPARE (flags_after, flags_before);
> +      break;
> +
> +    case test_config_unbuffered:
> +      TEST_COMPARE (flags_after, _IO_UNBUFFERED);
> +      break;
> +
> +    case test_config_line:
> +      TEST_COMPARE (flags_after, _IO_LINE_BUF);
> +      break;
> +
> +    case test_config_fully:
> +      TEST_COMPARE (flags_after, 0);
> +      break;
> +
> +    default:
> +      abort ();
> +    }
> +
> +  /* Glibc defers calculating the appropriate buffering mechanism
> +     until it reads from or writes to the device.  So we read one
> +     character here, and account for that in the tests.  */
> +  if (test_stream_reads[s])
> +    {
> +      dumpfp (fp);
> +      int c = fgetc (fp);
> +      if (c != TEST_STRING[0] && f != test_source_dev_null)
> +	FAIL ("first char read is %c not %c", c, TEST_STRING[0]);
> +      dumpfp (fp);
> +    }
> +  else
> +    {
> +      dumpfp (fp);
> +      fputc (TEST_STRING[0], fp);
> +      dumpfp (fp);
> +    }
> +
> +  switch (fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF))
> +    {
> +    case _IO_LINE_BUF:
> +      rv += verify_line_buffered (fp, f, s, c);
> +      break;
> +
> +    case _IO_UNBUFFERED:
> +      rv += verify_unbuffered (fp, f, s, c);
> +      break;
> +
> +    case 0: /* Fully buffered.  */
> +      rv += verify_fully_buffered (fp, f, s, c);
> +      break;
> +
> +    default:
> +      abort ();
> +    }
> +
> +
> +  fclose (fp);
> +  return rv;
> +}
> +
> +/*------------------------------------------------------------*/
> +
> +#ifdef INDEPENDENT_PART
> +/* This part is the independent sub-process we call to test stdin et
> +   al.  */
> +
> +int
> +main (int argc, char **argv)
> +{
> +  /* This is one of the subprocesses we created to test stdin et
> +     al.  */
> +  FILE *fp;
> +
> +  /* If we're called as a regular test, instead of as a sub-process,
> +     don't complain.  */
> +  if (argc == 1)
> +    return 0;
> +
> +  if (argc != 4)
> +    {
> +      int i;
> +      for (i = 0; i <= argc; i ++)
> +	printf ("argv[%d] = `%s'\n", i, argv[i] ?: "(null)");
> +      FAIL_EXIT1 ("sub-process called wrong");
> +    }
> +
> +  prepare_test_data ();
> +
> +  enum test_source_case f = atoi (argv[1]);
> +  enum test_stream_case s = atoi (argv[2]);
> +  enum test_config_case c = atoi (argv[3]);
> +
> +  if (s != test_stream_stdout)
> +    printf ("\n\033[41mRunning test %s : %s : %s\033[0m\n",
> +	    test_source_name[f],
> +	    test_stream_name[s],
> +	    test_config_name[c]);
> +
> +  switch (s)
> +    {
> +    case test_stream_stdin:
> +      fp = stdin;
> +      break;
> +    case test_stream_stdout:
> +      fp = stdout;
> +      break;
> +    case test_stream_stderr:
> +      fp = stderr;
> +      break;
> +    default:
> +      abort ();
> +    }
> +
> +  return do_second_part (fp, f, s, c);
> +}
> +
> +#else
> +/* This part is the standard test process.  */
> +
> +/* Spawn an independent sub-process with std* redirected.  */
> +int
> +recurse (FILE *fp,
> +	 enum test_source_case f,
> +	 enum test_stream_case s,
> +	 enum test_config_case c)
> +{
> +  /* We need to test stdin, stdout, or stderr, which means creating a
> +     subprocess with one of those redirected from FP.  */
> +  debug;
> +
> +  pid_t pid;
> +  int status;
> +
> +  pid = fork ();
> +
> +  switch (pid)
> +    {
> +    case -1: /* error */
> +      perror ("fork");
> +      return 1;
> +      break;
> +
> +    default: /* parent */
> +      fclose (fp);
> +      xwaitpid (pid, &status, 0);
> +      if (WIFEXITED (status)
> +	  && WEXITSTATUS (status) == 0)
> +	return 0;
> +      return 1;
> +
> +    case 0: /* child */
> +      switch (s)
> +	{
> +	case test_stream_stdin:
> +	  xclose (0);
> +	  dup2 (fileno (fp), 0);
> +	  break;
> +	case test_stream_stdout:
> +	  xclose (1);
> +	  dup2 (fileno (fp), 1);
> +	  break;
> +	case test_stream_stderr:
> +	  xclose (2);
> +	  dup2 (fileno (fp), 2);
> +	  break;
> +	default:
> +	  abort ();
> +	}
> +      fclose (fp);
> +
> +      /* At this point, we have to run a program... which is tricky to
> +	 properly support for remote targets or crosses, because of
> +	 glibc versions etc.  Hence we run in a test-container.  */
> +
> +      char fs[10], ss[10], cs[10];
> +      sprintf (fs, "%d", f);
> +      sprintf (ss, "%d", s);
> +      sprintf (cs, "%d", c);
> +      execl (IND_PROC, IND_PROC, fs, ss, cs, NULL);
> +      if (s == test_stream_stdout)
> +	fprintf (stderr, "execl (%s) failed, ", IND_PROC);
> +      else
> +	printf ("execl (%s) failed, ", IND_PROC);
> +      perror ("The error was");
> +      exit (1);
> +      break;
> +    }
> +
> +  return 0;
> +}
> +
> +int
> +do_test (void)
> +{
> +  int rv = 0;
> +
> +  signal (SIGPIPE, SIG_IGN);
> +
> +  prepare_test_data ();
> +
> +  for (enum test_source_case f = 0; f < test_source_count; ++f)
> +    for (enum test_stream_case s = 0; s < test_stream_count; ++s)
> +      for (enum test_config_case c = 0; c < test_config_count; ++c)
> +	{
> +	  printf ("\n\033[43mRunning test %s : %s : %s\033[0m\n",
> +		  test_source_name[f],
> +		  test_stream_name[s],
> +		  test_config_name[c]);
> +
> +	  FILE *fp = open_test_stream (f, s);
> +
> +	  if (fp)
> +	    {
> +
> +	      if (s <= test_stream_stderr)
> +		rv += recurse (fp, f, s, c);
> +	      else
> +		rv += do_second_part (fp, f, s, c);
> +
> +	      unprepare_test_file (fp, f, s);
> +	    }
> +	}
> +
> +  free (buffer);
> +
> +  printf ("return %d\n", rv);
> +  return rv;
> +}
> +
> +# include <support/test-driver.c>
> +#endif
> +



More information about the Libc-alpha mailing list