bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

execute: Add tests


From: Bruno Haible
Subject: execute: Add tests
Date: Sun, 29 Nov 2020 21:35:25 +0100
User-agent: KMail/5.1.3 (Linux/4.4.0-193-generic; KDE/5.18.0; x86_64; ; )

This patch adds tests for the 'execute' module.


2020-11-29  Bruno Haible  <bruno@clisp.org>

        execute: Add tests.
        * tests/test-execute.sh: New file.
        * tests/test-execute-main.c: New file.
        * tests/test-execute-child.c: New file.
        * modules/execute-tests: New file.

============================ tests/test-execute.sh ============================
#!/bin/sh

st=0
for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do
  ${CHECKER} ./test-execute-main${EXEEXT} ./test-execute-child${EXEEXT} $i \
    || { echo test-execute.sh: test case $i failed >&2; st=1; }
done
exit $st
========================== tests/test-execute-main.c ==========================
/* Test of execute.
   Copyright (C) 2020 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, 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/>.  */

#include <config.h>

#include "execute.h"

#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "read-file.h"
#include "macros.h"

/* The name of the "always silent" device.  */
#if defined _WIN32 && ! defined __CYGWIN__
/* Native Windows API.  */
# define DEV_NULL "NUL"
#else
/* Unix API.  */
# define DEV_NULL "/dev/null"
#endif

#define BASE "test-execute"

int
main (int argc, char *argv[])
{
  if (argc != 3)
    {
      fprintf (stderr, "%s: need 2 arguments\n", argv[0]);
      return 2;
    }
  char *prog_path = argv[1];
  const char *progname = "test-execute-child";
  int test = atoi (argv[2]);
  switch (test)
    {
    case 0:
      {
        /* Check an invocation without arguments.  Check the exit code.  */
        char *prog_argv[2] = { prog_path, NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, false, false, true, false, NULL);
        ASSERT (ret == 40);
      }
      break;
    case 1:
      {
        /* Check an invocation of a non-existent program.  */
        char *prog_argv[3] = { "./non-existent", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, false, false, true, false, NULL);
        ASSERT (ret == 127);
      }
      break;
    case 2:
      {
        /* Check argument passing.  */
        char *prog_argv[13] =
          {
            prog_path,
            (char *) "2",
            (char *) "abc def",
            (char *) "abc\"def\"ghi",
            (char *) "xyz\"",
            (char *) "abc\\def\\ghi",
            (char *) "xyz\\",
            (char *) "???",
            (char *) "***",
            (char *) "",
            (char *) "foo",
            (char *) "",
            NULL
          };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, false, false, true, false, NULL);
        ASSERT (ret == 0);
      }
      break;
    case 3:
      #if !(defined _WIN32 && !defined __CYGWIN__)
      {
        /* Check SIGPIPE handling with ignore_sigpipe = false.  */
        char *prog_argv[3] = { prog_path, (char *) "3", NULL };
        int termsig = 0xDEADBEEF;
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, false, false, true, false, &termsig);
        ASSERT (ret == 127);
        ASSERT (termsig == SIGPIPE);
      }
      #endif
      break;
    case 4:
      #if !(defined _WIN32 && !defined __CYGWIN__)
      {
        /* Check SIGPIPE handling with ignore_sigpipe = true.  */
        char *prog_argv[3] = { prog_path, (char *) "4", NULL };
        int termsig = 0xDEADBEEF;
        int ret = execute (progname, prog_argv[0], prog_argv,
                           true, false, false, false, true, false, &termsig);
        ASSERT (ret == 0);
        ASSERT (termsig == SIGPIPE);
      }
      #endif
      break;
    case 5:
      {
        /* Check other signal.  */
        char *prog_argv[3] = { prog_path, (char *) "5", NULL };
        int termsig = 0xDEADBEEF;
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, false, false, true, false, &termsig);
        #if defined _WIN32 && !defined __CYGWIN__
        ASSERT (ret == 3);
        ASSERT (termsig == 0);
        #else
        ASSERT (ret == 127);
        ASSERT (termsig == SIGINT);
        #endif
      }
      break;
    case 6:
      {
        /* Check stdin is inherited.  */
        FILE *fp = fopen (BASE ".tmp", "w");
        fputs ("Foo", fp);
        ASSERT (fclose (fp) == 0);

        fp = freopen (BASE ".tmp", "r", stdin);
        ASSERT (fp != NULL);

        char *prog_argv[3] = { prog_path, (char *) "6", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, false, false, true, false, NULL);
        ASSERT (ret == 0);

        ASSERT (fclose (stdin) == 0);
        ASSERT (remove (BASE ".tmp") == 0);
      }
      break;
    case 7:
      {
        /* Check null_stdin = true.  */
        FILE *fp = fopen (BASE ".tmp", "w");
        fputs ("Foo", fp);
        ASSERT (fclose (fp) == 0);

        fp = freopen (BASE ".tmp", "r", stdin);
        ASSERT (fp != NULL);

        char *prog_argv[3] = { prog_path, (char *) "7", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, true, false, false, true, false, NULL);
        ASSERT (ret == 0);

        ASSERT (fclose (stdin) == 0);
        ASSERT (remove (BASE ".tmp") == 0);
      }
      break;
    case 8:
      {
        /* Check stdout is inherited, part 1 (regular file).  */
        FILE *fp = freopen (BASE ".tmp", "w", stdout);
        ASSERT (fp != NULL);

        char *prog_argv[3] = { prog_path, (char *) "8", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, false, false, true, false, NULL);
        ASSERT (ret == 0);

        ASSERT (fclose (stdout) == 0);

        size_t length;
        char *contents = read_file (BASE ".tmp", 0, &length);
        ASSERT (length == 3 && memcmp (contents, "bar", 3) == 0);

        ASSERT (remove (BASE ".tmp") == 0);
      }
      break;
    case 9:
      {
        /* Check stdout is inherited, part 2 (device).  */
        FILE *fp = freopen (DEV_NULL, "w", stdout);
        ASSERT (fp != NULL);

        char *prog_argv[3] = { prog_path, (char *) "9", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, false, false, true, false, NULL);
        ASSERT (ret == 0);
      }
      break;
    case 10:
      {
        /* Check null_stdout = true.  */
        FILE *fp = freopen (BASE ".tmp", "w", stdout);
        ASSERT (fp != NULL);

        char *prog_argv[3] = { prog_path, (char *) "10", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, true, false, true, false, NULL);
        ASSERT (ret == 0);

        ASSERT (fclose (stdout) == 0);

        size_t length;
        (void) read_file (BASE ".tmp", 0, &length);
        ASSERT (length == 0);

        ASSERT (remove (BASE ".tmp") == 0);
      }
      break;
    case 11:
      {
        /* Check stderr is inherited, part 1 (regular file).  */
        FILE *fp = freopen (BASE ".tmp", "w", stderr);
        ASSERT (fp != NULL);

        char *prog_argv[3] = { prog_path, (char *) "11", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, false, false, true, false, NULL);
        ASSERT (ret == 0);

        ASSERT (fclose (stderr) == 0);

        size_t length;
        char *contents = read_file (BASE ".tmp", 0, &length);
        ASSERT (length == 3 && memcmp (contents, "bar", 3) == 0);

        ASSERT (remove (BASE ".tmp") == 0);
      }
      break;
    case 12:
      {
        /* Check stderr is inherited, part 2 (device).  */
        FILE *fp = freopen (DEV_NULL, "w", stderr);
        ASSERT (fp != NULL);

        char *prog_argv[3] = { prog_path, (char *) "12", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, false, false, true, false, NULL);
        ASSERT (ret == 0);
      }
      break;
    case 13:
      {
        /* Check null_stderr = true.  */
        FILE *fp = freopen (BASE ".tmp", "w", stderr);
        ASSERT (fp != NULL);

        char *prog_argv[3] = { prog_path, (char *) "13", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           false, false, false, true, true, false, NULL);
        ASSERT (ret == 0);

        ASSERT (fclose (stderr) == 0);

        size_t length;
        (void) read_file (BASE ".tmp", 0, &length);
        ASSERT (length == 0);

        ASSERT (remove (BASE ".tmp") == 0);
      }
      break;
    case 14:
      {
        /* Check file descriptors >= 3 can be inherited.  */
        ASSERT (dup2 (STDOUT_FILENO, 10) >= 0);
        char *prog_argv[3] = { prog_path, (char *) "14", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           true, false, false, false, true, false, NULL);
        ASSERT (ret == 0);
      }
      break;
    case 15:
      {
        /* Check file descriptors >= 3 can be inherited.  */
        ASSERT (fcntl (STDOUT_FILENO, F_DUPFD, 10) >= 0);
        char *prog_argv[3] = { prog_path, (char *) "15", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           true, false, false, false, true, false, NULL);
        ASSERT (ret == 0);
      }
      break;
    case 16:
      {
        /* Check file descriptors >= 3 with O_CLOEXEC bit are not inherited.  */
        ASSERT (fcntl (STDOUT_FILENO, F_DUPFD_CLOEXEC, 10) >= 0);
        char *prog_argv[3] = { prog_path, (char *) "16", NULL };
        int ret = execute (progname, prog_argv[0], prog_argv,
                           true, false, false, false, true, false, NULL);
        ASSERT (ret == 0);
      }
      break;
    default:
      ASSERT (false);
    }
  return 0;
}
========================== tests/test-execute-child.c ==========================
/* Child program invoked by test-execute-main.
   Copyright (C) 2009-2020 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, 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/>.  */

#include <config.h>

#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>

#if defined _WIN32 && ! defined __CYGWIN__
/* Get declarations of the native Windows API functions.  */
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
/* Get _get_osfhandle.  */
# include <io.h>
#endif

/* In this file, we use only system functions, no overrides from gnulib.  */
#undef atoi
#undef fcntl
#undef fflush
#undef fgetc
#undef fprintf
#undef fputs
#undef fstat
#undef raise
#undef sprintf
#undef stat
#undef strcmp
#undef strlen

#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
static void __cdecl
gl_msvc_invalid_parameter_handler (const wchar_t *expression,
                                   const wchar_t *function,
                                   const wchar_t *file,
                                   unsigned int line,
                                   uintptr_t dummy)
{
}
#endif

/* Return non-zero if FD is open.  */
static int
is_open (int fd)
{
#if defined _WIN32 && ! defined __CYGWIN__
  /* On native Windows, the initial state of unassigned standard file
     descriptors is that they are open but point to an
     INVALID_HANDLE_VALUE, and there is no fcntl.  */
  return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE;
#else
# ifndef F_GETFL
#  error Please port fcntl to your platform
# endif
  return 0 <= fcntl (fd, F_GETFL);
#endif
}

int
main (int argc, char *argv[])
{
  if (argc == 1)
    /* Check an invocation without arguments.  Check the exit code.  */
    return 40;

  int test = atoi (argv[1]);
  switch (test)
    {
    case 2:
      /* Check argument passing.  */
      return !(argc == 12
               && strcmp (argv[2], "abc def") == 0
               && strcmp (argv[3], "abc\"def\"ghi") == 0
               && strcmp (argv[4], "xyz\"") == 0
               && strcmp (argv[5], "abc\\def\\ghi") == 0
               && strcmp (argv[6], "xyz\\") == 0
               && strcmp (argv[7], "???") == 0
               && strcmp (argv[8], "***") == 0
               && strcmp (argv[9], "") == 0
               && strcmp (argv[10], "foo") == 0
               && strcmp (argv[11], "") == 0);
    #if !(defined _WIN32 && !defined __CYGWIN__)
    case 3:
      /* Check SIGPIPE handling with ignore_sigpipe = false.  */
    case 4:
      /* Check SIGPIPE handling with ignore_sigpipe = true.  */
      raise (SIGPIPE);
      return 71;
    #endif
    case 5:
      /* Check other signal.  */
      raise (SIGINT);
      return 71;
    case 6:
      /* Check stdin is inherited.  */
      return !(fgetc (stdin) == 'F' && fgetc (stdin) == 'o');
    case 7:
      /* Check null_stdin = true.  */
      return !(fgetc (stdin) == EOF);
    case 8:
      /* Check stdout is inherited, part 1 (regular file).  */
      return !(fputs ("bar", stdout) != EOF && fflush (stdout) == 0);
    case 9:
      /* Check stdout is inherited, part 2 (device).  */
    case 10:
      /* Check null_stdout = true.  */
      {
        struct stat st;
        return !(fstat (STDOUT_FILENO, &st) >= 0 && !S_ISREG (st.st_mode));
      }
    case 11:
      /* Check stderr is inherited, part 1 (regular file).  */
      return !(fputs ("bar", stderr) != EOF && fflush (stderr) == 0);
    case 12:
      /* Check stderr is inherited, part 2 (device).  */
    case 13:
      /* Check null_stderr = true.  */
      {
        struct stat st;
        return !(fstat (STDERR_FILENO, &st) >= 0 && !S_ISREG (st.st_mode));
      }
    case 14:
    case 15:
      /* Check file descriptors >= 3 can be inherited.  */
    case 16:
      /* Check file descriptors >= 3 with O_CLOEXEC bit are not inherited.  */
      #if HAVE_MSVC_INVALID_PARAMETER_HANDLER
      /* Avoid exceptions from within _get_osfhandle.  */
      _set_invalid_parameter_handler (gl_msvc_invalid_parameter_handler);
      #endif
      {
        char buf[300];
        buf[0] = '\0';
        char *p = buf;
        int fd;
        for (fd = 0; fd < 20; fd++)
          #ifdef __NetBSD__
          if (fd != 3)
          #endif
            if (is_open (fd))
              {
                sprintf (p, "%d ", fd);
                p += strlen (p);
              }
        const char *expected = (test < 16 ? "0 1 2 10 " : "0 1 2 ");
        if (strcmp (buf, expected) == 0)
          return 0;
        else
          {
            fprintf (stderr, "Test case %d: %s\n", test, buf); fflush (stderr);
            return 1;
          }
      }
    default:
      abort ();
    }
}
============================ modules/execute-tests ============================
Files:
tests/test-execute.sh
tests/test-execute-main.c
tests/test-execute-child.c
tests/macros.h

Depends-on:
dup2
fcntl
msvc-inval
read-file
stdint
unistd

configure.ac:

Makefile.am:
TESTS += test-execute.sh
check_PROGRAMS += test-execute-main test-execute-child
test_execute_main_LDADD = $(LDADD) @LIBINTL@ $(LIBTHREAD)
# The test-execute-child program must be a real executable, not a libtool
# wrapper script, and should link against as few libraries as possible.
# Therefore don't link it against any libraries other than -lc.
test_execute_child_LDADD =




reply via email to

[Prev in Thread] Current Thread [Next in Thread]