summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2023-01-05 11:56:59 +0100
committerWerner Koch <wk@gnupg.org>2023-01-05 11:57:48 +0100
commit72bd92465063875789a8e4995e0db2a8215bbdb6 (patch)
tree4b68075164560f2943c23af478428651c5ef37d5
parent35a7409dcf29009ed2cf365815c6abf02c94cb8f (diff)
downloadlibgcrypt-72bd92465063875789a8e4995e0db2a8215bbdb6.tar.gz
New test driver to allow for standalone regression tests.
* tests/testdrv.c: New. -- This driver is intended to run easily run the regression tests on the actual host platform where the software has been compiled for. In particular for Windows cross-building is mandatory and manually running tests is error prone. Some of the code has been taken from gnupg/common/exechelp*.c and re-licensed from (LGPL-3.0-or-later OR GPL-2.0-or-later) to LGPL-2.1-or-later. The original code has been written by g10 Code employees or contractors. Signed-off-by: Werner Koch <wk@gnupg.org> (cherry picked from commit b142da4c88deef4798ef96061dac399df3ddd73d) - Removed X448 related tests which are not supported by 1.8. - Removed SM3 from the long hash tests.
-rw-r--r--tests/testdrv.c885
1 files changed, 885 insertions, 0 deletions
diff --git a/tests/testdrv.c b/tests/testdrv.c
new file mode 100644
index 00000000..75be52e9
--- /dev/null
+++ b/tests/testdrv.c
@@ -0,0 +1,885 @@
+/* testdrv.c - Test driver to run all tests w/o using the Makefile.
+ * Copyright (C) 2021 g10 Code GmbH
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt 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.
+ *
+ * Libgcrypt 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#ifndef HAVE_W32_SYSTEM
+# include <unistd.h>
+# include <fcntl.h>
+# include <sys/wait.h>
+#endif
+#include <gpg-error.h> /* For some macros. */
+
+#include "stopwatch.h"
+
+#define PGM "testdrv"
+
+/* Flags for testpgms. */
+#define LONG_RUNNING 1
+
+/* This is our list of tests which are run in this order. */
+static struct {
+ const char *name;
+ const char *pgm;
+ const char *args;
+ unsigned int flags; /* e.g. LONG_RUNNING */
+} testpgms[] =
+ {
+ { "version" },
+ { "t-secmem" },
+ { "mpitests" },
+ { "t-sexp" },
+ { "t-convert" },
+ { "t-mpi-bit" },
+ { "t-mpi-point" },
+ { "curves" },
+ { "t-lock" },
+ { "prime" },
+ { "basic" },
+ { "basic-disable-all-hwf", "basic", "--disable-hwf all" },
+ { "keygen" },
+ { "pubkey" },
+ { "hmac" },
+ { "hashtest" },
+ { "t-kdf" },
+ { "keygrip" },
+ { "fips186-dsa" },
+ { "aeswrap" },
+ { "pkcs1v2" },
+ { "random" },
+ { "dsa-rfc6979" },
+ { "t-ed25519" },
+ { "t-cv25519" },
+ { "benchmark" },
+ { "bench-slope" },
+ { "hashtest-256g", "hashtest", "--gigs 256 SHA1 SHA256 SHA512",
+ LONG_RUNNING },
+ { NULL }
+ };
+
+/* Extra files needed for the above tests. */
+static const char *extratestfiles[] =
+ {
+ "t-ed25519.inp",
+ NULL
+ };
+
+
+/* A couple of useful macros. */
+#ifndef DIM
+# define DIM(v) (sizeof(v)/sizeof((v)[0]))
+#endif
+#define DIMof(type,member) DIM(((type *)0)->member)
+#define xfree(a) free ((a))
+#define spacep(p) (*(p) == ' ' || *(p) == '\t')
+
+/* If we have a decent libgpg-error we can use some gcc attributes. */
+#ifdef GPGRT_ATTR_NORETURN
+static void die (const char *format, ...)
+ GPGRT_ATTR_UNUSED GPGRT_ATTR_NR_PRINTF(1,2);
+static void fail (const char *format, ...)
+ GPGRT_ATTR_UNUSED GPGRT_ATTR_PRINTF(1,2);
+static void info (const char *format, ...) \
+ GPGRT_ATTR_UNUSED GPGRT_ATTR_PRINTF(1,2);
+static void printresult (const char *format, ...) \
+ GPGRT_ATTR_UNUSED GPGRT_ATTR_PRINTF(1,2);
+#endif /*GPGRT_ATTR_NORETURN*/
+
+
+#ifndef TESTDRV_EXEEXT
+# ifdef HAVE_W32_SYSTEM
+# define TESTDRV_EXEEXT ".exe"
+# else
+# define TESTDRV_EXEEXT ""
+# endif
+#endif
+#ifdef HAVE_W32_SYSTEM
+# define MYPID_T HANDLE
+# define MYINVALID_PID INVALID_HANDLE_VALUE
+#else
+# define MYPID_T pid_t
+# define MYINVALID_PID ((pid_t)(-1))
+#endif
+
+/* Standard global variables. */
+static int verbose;
+static int debug;
+static int error_count;
+static int die_on_error;
+static int long_running;
+static char **myenviron;
+static int testcount, failcount, passcount, skipcount;
+
+#ifdef HAVE_W32_SYSTEM
+static char *
+my_stpcpy (char *a, const char *b)
+{
+ while (*b)
+ *a++ = *b++;
+ *a = 0;
+
+ return a;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+/* Reporting functions. */
+static void
+die (const char *format, ...)
+{
+ va_list arg_ptr ;
+
+ /* Avoid warning. */
+ (void) debug;
+
+ fflush (stdout);
+#ifdef HAVE_FLOCKFILE
+ flockfile (stderr);
+#endif
+ fprintf (stderr, "%s: ", PGM);
+ va_start (arg_ptr, format) ;
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ if (*format && format[strlen(format)-1] != '\n')
+ putc ('\n', stderr);
+#ifdef HAVE_FLOCKFILE
+ funlockfile (stderr);
+#endif
+ exit (1);
+}
+
+static void *
+xmalloc (size_t n)
+{
+ char *p = malloc (n);
+ if (!p)
+ die ("malloc failed");
+ return p;
+}
+
+static void *
+xcalloc (size_t n, size_t m)
+{
+ char *p = calloc (n, m);
+ if (!p)
+ die ("calloc failed");
+ return p;
+}
+
+static char *
+xstrdup (const char *s)
+{
+ size_t n = strlen (s);
+ char *p = xmalloc (n+1);
+ strcpy (p, s);
+ return p;
+}
+
+
+static void
+fail (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ fflush (stdout);
+#ifdef HAVE_FLOCKFILE
+ flockfile (stderr);
+#endif
+ fprintf (stderr, "%s: ", PGM);
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ if (*format && format[strlen(format)-1] != '\n')
+ putc ('\n', stderr);
+#ifdef HAVE_FLOCKFILE
+ funlockfile (stderr);
+#endif
+ if (die_on_error)
+ exit (1);
+ error_count++;
+}
+
+
+static void
+info (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ if (!verbose)
+ return;
+ fflush (stdout);
+#ifdef HAVE_FLOCKFILE
+ flockfile (stderr);
+#endif
+ fprintf (stderr, "%s: ", PGM);
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ if (*format && format[strlen(format)-1] != '\n')
+ putc ('\n', stderr);
+ va_end (arg_ptr);
+#ifdef HAVE_FLOCKFILE
+ funlockfile (stderr);
+#endif
+}
+
+
+static void
+printresult (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ fflush (stdout);
+#ifdef HAVE_FLOCKFILE
+ flockfile (stdout);
+#endif
+ va_start (arg_ptr, format);
+ vfprintf (stdout, format, arg_ptr);
+ if (*format && format[strlen(format)-1] != '\n')
+ putc ('\n', stdout);
+ va_end (arg_ptr);
+ fflush (stdout);
+#ifdef HAVE_FLOCKFILE
+ funlockfile (stdout);
+#endif
+}
+
+
+/* Tokenize STRING using the set of delimiters in DELIM. Leading
+ * spaces and tabs are removed from all tokens. The caller must free
+ * the result. Returns a malloced and NULL delimited array with the
+ * tokens. */
+static char **
+strtokenize (const char *string, const char *delim)
+{
+ const char *s;
+ size_t fields;
+ size_t bytes, n;
+ char *buffer;
+ char *p, *px, *pend;
+ char **result;
+
+ /* Count the number of fields. */
+ for (fields = 1, s = strpbrk (string, delim); s; s = strpbrk (s + 1, delim))
+ fields++;
+ fields++; /* Add one for the terminating NULL. */
+
+ /* Allocate an array for all fields, a terminating NULL, and space
+ for a copy of the string. */
+ bytes = fields * sizeof *result;
+ if (bytes / sizeof *result != fields)
+ die ("integer overflow at %d\n", __LINE__);
+ n = strlen (string) + 1;
+ bytes += n;
+ if (bytes < n)
+ die ("integer overflow at %d\n", __LINE__);
+ result = xmalloc (bytes);
+ buffer = (char*)(result + fields);
+
+ /* Copy and parse the string. */
+ strcpy (buffer, string);
+ for (n = 0, p = buffer; (pend = strpbrk (p, delim)); p = pend + 1)
+ {
+ *pend = 0;
+ while (spacep (p))
+ p++;
+ for (px = pend - 1; px >= p && spacep (px); px--)
+ *px = 0;
+ result[n++] = p;
+ }
+ while (spacep (p))
+ p++;
+ for (px = p + strlen (p) - 1; px >= p && spacep (px); px--)
+ *px = 0;
+ result[n++] = p;
+ result[n] = NULL;
+
+ if (!((char*)(result + n + 1) == buffer))
+ die ("bug at %d\n", __LINE__);
+
+ return result;
+}
+
+
+#ifdef HAVE_W32_SYSTEM
+/* Helper functions for Windows. */
+static char *
+build_w32_commandline_copy (char *buffer, const char *string)
+{
+ char *p = buffer;
+ const char *s;
+
+ if (!*string) /* Empty string. */
+ p = my_stpcpy (p, "\"\"");
+ else if (strpbrk (string, " \t\n\v\f\""))
+ {
+ /* Need to do some kind of quoting. */
+ p = my_stpcpy (p, "\"");
+ for (s=string; *s; s++)
+ {
+ *p++ = *s;
+ if (*s == '\"')
+ *p++ = *s;
+ }
+ *p++ = '\"';
+ *p = 0;
+ }
+ else
+ p = my_stpcpy (p, string);
+
+ return p;
+}
+
+/* Build a command line for use with CreateProcess. This function
+ * either terminates the process or returns a malloced string. */
+static char *
+build_w32_commandline (const char *pgmname, char **argv)
+{
+ int i, n;
+ const char *s;
+ char *buf, *p;
+
+ s = pgmname;
+ n = strlen (s) + 1 + 2; /* (1 space, 2 quoting) */
+ for (; *s; s++)
+ if (*s == '\"')
+ n++; /* Account for to be doubled inner quotes. */
+ for (i=0; argv && (s=argv[i]); i++)
+ {
+ n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */
+ for (; *s; s++)
+ if (*s == '\"')
+ n++; /* For doubling inner quotes. */
+ }
+ n++; /* String terminator. */
+
+ buf = p = xmalloc (n);
+ p = build_w32_commandline_copy (p, pgmname);
+ for (i=0; argv && argv[i]; i++)
+ {
+ *p++ = ' ';
+ p = build_w32_commandline_copy (p, argv[i]);
+ }
+
+ return buf;
+}
+
+static HANDLE
+w32_open_null (int for_write)
+{
+ HANDLE hfile;
+
+ hfile = CreateFileW (L"nul",
+ for_write? GENERIC_WRITE : GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ if (hfile == INVALID_HANDLE_VALUE)
+ die ("can't open 'nul': ec=%lu\n", (unsigned long)GetLastError());
+ return hfile;
+}
+
+#endif /*HAVE_W32_SYSTEM*/
+
+
+/* Fork and exec the PGMNAME using ARGV as arguments (w/o pgmmname)
+ * and return the pid at PID. If ENVP is not NULL, add these strings
+ * as environment variables. Return -1 on severe errors. */
+static int
+my_spawn (const char *pgmname, char **argv, char **envp, MYPID_T *pid)
+{
+#ifdef HAVE_W32_SYSTEM
+ int rc;
+ SECURITY_ATTRIBUTES sec_attr;
+ PROCESS_INFORMATION pi = { NULL };
+ STARTUPINFO si;
+ char *cmdline;
+ char *pgmnamefull = NULL;
+ char **saveenviron = NULL;
+ int i;
+
+ /* Prepare security attributes. */
+ memset (&sec_attr, 0, sizeof sec_attr );
+ sec_attr.nLength = sizeof sec_attr;
+ sec_attr.bInheritHandle = FALSE;
+
+ if (!(strlen (pgmname) > 4 && !strcmp (pgmname+strlen(pgmname)-4, ".exe")))
+ {
+ pgmnamefull = xmalloc (strlen (pgmname) + 4 + 1);
+ strcpy (my_stpcpy (pgmnamefull, pgmname), ".exe");
+ pgmname = pgmnamefull;
+ }
+
+ /* Build the command line. */
+ cmdline = build_w32_commandline (pgmname, argv);
+
+ memset (&si, 0, sizeof si);
+ si.cb = sizeof (si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = w32_open_null (0);
+ if (verbose)
+ si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
+ else
+ si.hStdOutput = w32_open_null (1);
+ si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
+
+ if (envp)
+ {
+ for (i=0; envp[i]; i++)
+ ;
+ saveenviron = xcalloc (i+1, sizeof *saveenviron);
+ for (i=0; envp[i]; i++)
+ saveenviron[i] = xstrdup (envp[i]);
+ for (i=0; envp[i]; i++)
+ putenv (envp[i]);
+ }
+
+ if (debug)
+ info ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline);
+ if (!CreateProcess (pgmname, /* Program to start. */
+ cmdline, /* Command line arguments. */
+ &sec_attr, /* Process security attributes. */
+ &sec_attr, /* Thread security attributes. */
+ TRUE, /* Inherit handles. */
+ (CREATE_DEFAULT_ERROR_MODE
+ | GetPriorityClass (GetCurrentProcess ())
+ | CREATE_SUSPENDED | DETACHED_PROCESS),
+ NULL, /* Environment. */
+ NULL, /* Use current drive/directory. */
+ &si, /* Startup information. */
+ &pi /* Returns process information. */
+ ))
+ {
+ fail ("CreateProcess failed: ec=%lu\n", (unsigned long)GetLastError());
+ rc = -1;
+ }
+ else
+ rc = 0;
+
+ if (saveenviron)
+ {
+ for (i=0; saveenviron[i]; i++)
+ xfree (saveenviron[i]);
+ xfree (saveenviron);
+ }
+ xfree (cmdline);
+ CloseHandle (si.hStdInput);
+ if (!verbose)
+ CloseHandle (si.hStdOutput);
+ xfree (pgmnamefull); pgmname = NULL;
+ if (rc)
+ return rc;
+
+ if (debug)
+ info ("CreateProcess ready: hProcess=%p hThread=%p"
+ " dwProcessID=%d dwThreadId=%d\n",
+ pi.hProcess, pi.hThread,
+ (int) pi.dwProcessId, (int) pi.dwThreadId);
+
+ /* Process has been created suspended; resume it now. */
+ ResumeThread (pi.hThread);
+ CloseHandle (pi.hThread);
+
+ *pid = pi.hProcess;
+ return 0;
+
+#else /*!HAVE_W32_SYSTEM*/
+
+ char **arg_list;
+ int i, j;
+ int fd;
+
+ /* Create the command line argument array. */
+ i = 0;
+ if (argv)
+ while (argv[i])
+ i++;
+ arg_list = xcalloc (i+2, sizeof *arg_list);
+ arg_list[0] = strrchr (pgmname, '/');
+ if (arg_list[0])
+ arg_list[0]++;
+ else
+ arg_list[0] = xstrdup (pgmname);
+ if (argv)
+ for (i=0,j=1; argv[i]; i++, j++)
+ arg_list[j] = (char*)argv[i];
+
+
+ *pid = fork ();
+ if (*pid == MYINVALID_PID)
+ {
+ fail ("error forking process: %s\n", strerror (errno));
+ return -1;
+ }
+
+ if (!*pid)
+ {
+ /* This is the child. */
+ if (envp)
+ for (i=0; envp[i]; i++)
+ putenv (xstrdup (envp[i]));
+
+ /* Assign /dev/null to stdin. */
+ fd = open ("/dev/null", O_RDONLY);
+ if (fd == -1)
+ die ("failed to open '%s': %s\n", "/dev/null", strerror (errno));
+ if (fd != 0 && dup2 (fd, 0) == -1)
+ die ("dup2(%d,0) failed: %s\n", fd, strerror (errno));
+ /* Assign /dev/null to stdout unless in verbose mode. */
+ if (!verbose)
+ {
+ fd = open ("/dev/null", O_RDONLY);
+ if (fd == -1)
+ die ("failed to open '%s': %s\n", "/dev/null", strerror (errno));
+ if (fd != 1 && dup2 (fd, 1) == -1)
+ die ("dup2(%d,1) failed: %s\n", fd, strerror (errno));
+ }
+
+ /* Exec the program. */
+ execv (pgmname, arg_list);
+ info ("exec '%s' failed: %s\n", pgmname, strerror (errno));
+ _exit (127);
+ /*NOTREACHED*/
+ }
+
+ /* This is the parent. */
+ xfree (arg_list);
+ return 0;
+#endif /*!HAVE_W32_SYSTEM*/
+}
+
+
+/* Wait for PID and return its exitcode at R_EXITCODE. PGMNAME is
+ * only used for diagnostics. */
+static int
+my_wait (const char *pgmname, MYPID_T pid, int *r_exitcode)
+{
+ int rc = -1;
+
+#ifdef HAVE_W32_SYSTEM
+ HANDLE procs[1];
+ DWORD exc;
+ int code;
+
+ if (pid == MYINVALID_PID)
+ die ("invalid pid passed to my_wait\n");
+
+ procs[0] = (HANDLE)pid;
+ code = WaitForMultipleObjects (1, procs, TRUE, INFINITE);
+ switch (code)
+ {
+ case WAIT_TIMEOUT: /* Should not happen. */
+ fail ("waiting for process %p (%s) to terminate failed: timeout\n",
+ pid, pgmname);
+ break;
+
+ case WAIT_FAILED:
+ fail ("waiting for process %p (%s) to terminate failed: ec=%lu\n",
+ pid, pgmname, (unsigned long)GetLastError ());
+ break;
+
+ case WAIT_OBJECT_0:
+ if (!GetExitCodeProcess (procs[0], &exc))
+ {
+ fail ("error getting exit code for process %p (%s): ec=%lu\n",
+ pid, pgmname, (unsigned long)GetLastError ());
+ }
+ else
+ {
+ *r_exitcode = (int)exc;
+ rc = 0;
+ }
+ break;
+
+ default:
+ fail ("WaitForMultipleObjects returned unexpected code %d\n", code);
+ break;
+ }
+ CloseHandle ((HANDLE)pid);
+
+#else /*!HAVE_W32_SYSTEM*/
+
+ int i, status;
+
+ if (pid == MYINVALID_PID)
+ die ("invalid pid passed to my_wait\n");
+
+ while ((i=waitpid (pid, &status, 0)) == MYINVALID_PID
+ && errno == EINTR)
+ ;
+
+ if (i == MYINVALID_PID)
+ {
+ fail ("waiting for process %d (%s) to terminate failed: %s\n",
+ (int)pid, pgmname, strerror (errno));
+ }
+ else if (!i)
+ {
+ die ("waitpid returns unexpected code 0\n");
+ }
+ else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
+ {
+ fail ("error running '%s': probably not installed\n", pgmname);
+ }
+ else if (WIFEXITED (status) && WEXITSTATUS (status))
+ {
+ *r_exitcode = WEXITSTATUS (status);
+ rc = 0;
+ }
+ else if (!WIFEXITED (status))
+ {
+ info ("error running '%s': terminated\n", pgmname);
+ rc = 1;
+ }
+ else
+ {
+ *r_exitcode = 0;
+ rc = 0;
+ }
+
+#endif /*!HAVE_W32_SYSTEM*/
+
+ return rc;
+}
+
+
+static void
+run_one_test (int idx)
+{
+ MYPID_T pid;
+ int exitcode, rc;
+ const char *name = testpgms[idx].name;
+ const char *pgm = testpgms[idx].pgm;
+ char **args;
+
+ if (!pgm)
+ pgm = name;
+
+ testcount++;
+ if ((testpgms[idx].flags & LONG_RUNNING)
+ && !long_running)
+ {
+ printresult ("SKIP: %s\n", name);
+ skipcount++;
+ return;
+ }
+
+ args = testpgms[idx].args? strtokenize (testpgms[idx].args, " ") : NULL;
+ rc = my_spawn (pgm, args, myenviron, &pid);
+ xfree (args);
+ if (rc)
+ {
+ printresult ("FAIL: %s (error invoking test)\n", name);
+ failcount++;
+ return;
+ }
+ rc = my_wait (pgm, pid, &exitcode);
+ if (rc < 0)
+ {
+ printresult ("FAIL: %s (error running test)\n", name);
+ failcount++;
+ }
+ else if (rc)
+ {
+ printresult ("FAIL: %s (test crashed)\n", name);
+ failcount++;
+ }
+ else if (exitcode == 77)
+ {
+ printresult ("SKIP: %s\n", name);
+ skipcount++;
+ }
+ else if (exitcode == 1)
+ {
+ printresult ("FAIL: %s\n", name);
+ failcount++;
+ }
+ else if (exitcode)
+ {
+ printresult ("FAIL: %s (exit code %d)\n", name, exitcode);
+ failcount++;
+ }
+ else
+ {
+ printresult ("PASS: %s\n", name);
+ passcount++;
+ }
+}
+
+
+
+static void
+runtests (char **argv)
+{
+ int i;
+
+ if (argv && *argv)
+ {
+ for ( ; *argv; argv++)
+ {
+ for (i=0; testpgms[i].name; i++)
+ if (!strcmp (testpgms[i].name, *argv))
+ {
+ run_one_test (i);
+ break;
+ }
+ if (!testpgms[i].name)
+ {
+ fail ("requested test '%s' not found\n", *argv);
+ testcount++;
+ }
+ }
+ }
+ else /* Run all tests. */
+ {
+ for (i=0; testpgms[i].name; i++)
+ run_one_test (i);
+ }
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int last_argc = -1;
+ int listtests = 0;
+ int i;
+ const char *srcdir;
+
+ if (argc)
+ { argc--; argv++; }
+
+ while (argc && last_argc != argc )
+ {
+ last_argc = argc;
+ if (!strcmp (*argv, "--"))
+ {
+ argc--; argv++;
+ break;
+ }
+ else if (!strcmp (*argv, "--help"))
+ {
+ fputs ("usage: " PGM " [options] [tests_to_run]\n"
+ "Options:\n"
+ " --verbose print timings etc.\n"
+ " --debug flyswatter\n"
+ " --list list all tests\n"
+ " --files list all files\n"
+ " --long include long running tests\n"
+ , stdout);
+ exit (0);
+ }
+ else if (!strcmp (*argv, "--verbose"))
+ {
+ verbose++;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--debug"))
+ {
+ verbose += 2;
+ debug++;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--list"))
+ {
+ listtests = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--files"))
+ {
+ listtests = 2;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--long"))
+ {
+ long_running = 1;
+ argc--; argv++;
+ }
+ else if (!strncmp (*argv, "--", 2))
+ die ("unknown option '%s'", *argv);
+ }
+
+ srcdir = getenv ("srcdir");
+
+ myenviron = xcalloc (2, sizeof *myenviron);
+ myenviron[0] = xstrdup ("GCRYPT_IN_REGRESSION_TEST=1");
+#ifndef HAVE_W32_SYSTEM
+ if (!access ("libgcrypt-standalone-tests", F_OK))
+ myenviron[1] = xstrdup ("LD_LIBRARY_PATH=.");
+#endif
+
+ if (listtests == 1)
+ {
+ for (i=0; testpgms[i].name; i++)
+ {
+ printf ("%s", testpgms[i].name);
+ if (testpgms[i].pgm || testpgms[i].args)
+ printf (" (%s %s)",
+ testpgms[i].pgm? testpgms[i].pgm : testpgms[i].name,
+ testpgms[i].args? testpgms[i].args : "");
+ if (testpgms[i].flags)
+ {
+ putchar (' ');
+ putchar ('[');
+ if (testpgms[i].flags)
+ fputs ("long", stdout);
+ putchar (']');
+ }
+ putchar ('\n');
+ }
+ }
+ else if (listtests == 2)
+ {
+ for (i=0; testpgms[i].name; i++)
+ printf ("%s%s%s\n",
+ strcmp (TESTDRV_EXEEXT, ".exe")? "":".libs/",
+ testpgms[i].pgm? testpgms[i].pgm : testpgms[i].name,
+ TESTDRV_EXEEXT);
+ for (i=0; extratestfiles[i]; i++)
+ printf ("%s%s%s\n",
+ srcdir? srcdir :"",
+ srcdir? "/" :"",
+ extratestfiles[i]);
+ }
+ else
+ {
+ start_timer ();
+ runtests (argv);
+ stop_timer ();
+
+ printresult ("%d tests run, %d succeeded, %d failed, %d skipped.\n",
+ testcount-skipcount, passcount, failcount, skipcount);
+ if (testcount != passcount + failcount + skipcount)
+ printresult ("Warning: Execution of some tests failed\n");
+
+ info ("All tests completed in %s. Errors: %d\n",
+ elapsed_time (1), error_count + failcount);
+ }
+
+ for (i=0; myenviron[i]; i++)
+ xfree (myenviron[i]);
+ xfree (myenviron);
+
+ return !!error_count;
+}