From e66e2d4277998094ee58ebd7c9455507df303939 Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Fri, 20 Jan 2012 16:08:45 +0100 Subject: Add a minimal test suite Introduce a new "test/" sub-directory that contains tests for calcurse. Right now, it only includes the quick-and-dirty "run-test" helper that can be used to run and verify tests: $ ./run-test test-1 test-2 test-3 test-4 Running test-1... ok Running test-2... ok Running test-3... FAIL Each argument passed to run-test must be a test script located in the current directory. run-test invokes each script twice and passes the command line argument "expected" and "actual", respectively. A test case succeeds if both "expected" and "actual" instances return with a zero exit status and produce exactly the same output. It fails otherwise. run-test terminates with a non-zero exit status as soon as one of the test fails. Signed-off-by: Lukas Fleischer --- .gitignore | 3 + Makefile.am | 2 +- configure.ac | 3 +- test/Makefile.am | 6 ++ test/run-test.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 253 insertions(+), 2 deletions(-) create mode 100644 test/Makefile.am create mode 100644 test/run-test.c diff --git a/.gitignore b/.gitignore index 24a28f1..d298069 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ po/stamp-po src/*.o src/.deps/ src/calcurse + +test/*.o +test/run-test diff --git a/Makefile.am b/Makefile.am index 5dd1ed9..b36524d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ AUTOMAKE_OPTIONS= foreign ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = po src +SUBDIRS = po src test if ENABLE_DOCS SUBDIRS += doc diff --git a/configure.ac b/configure.ac index debd8b8..0500722 100644 --- a/configure.ac +++ b/configure.ac @@ -145,7 +145,8 @@ AM_XGETTEXT_OPTION([--no-location --keyword=_ --keyword=N_]) #------------------------------------------------------------------------------- # Create Makefiles #------------------------------------------------------------------------------- -AC_OUTPUT(Makefile doc/Makefile src/Makefile po/Makefile.in po/Makefile) +AC_OUTPUT(Makefile doc/Makefile src/Makefile test/Makefile po/Makefile.in \ + po/Makefile) #------------------------------------------------------------------------------- # Summary #------------------------------------------------------------------------------- diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..8b04cb8 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,6 @@ +AUTOMAKE_OPTIONS = foreign + +check_PROGRAMS = run-test + +run_test_SOURCES = run-test.c + diff --git a/test/run-test.c b/test/run-test.c new file mode 100644 index 0000000..31fda0c --- /dev/null +++ b/test/run-test.c @@ -0,0 +1,241 @@ +/* + * Calcurse - text-based organizer + * + * Copyright (c) 2004-2011 calcurse Development Team + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Send your feedback or comments to : misc@calcurse.org + * Calcurse home page : http://calcurse.org + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Fork and execute an external process. + * + * If pfdin and/or pfdout point to a valid address, a pipe is created and the + * appropriate file descriptors are written to pfdin/pfdout. + */ +static int +fork_exec (int *pfdin, int *pfdout, const char *path, char *const *arg) +{ + int pin[2], pout[2]; + int pid; + + if (pfdin && (pipe (pin) == -1)) + return 0; + if (pfdout && (pipe (pout) == -1)) + return 0; + + if ((pid = fork ()) == 0) + { + if (pfdout) + { + if (dup2 (pout[0], STDIN_FILENO) < 0) + _exit (127); + close (pout[0]); + close (pout[1]); + } + + if (pfdin) + { + if (dup2 (pin[1], STDOUT_FILENO) < 0) + _exit (127); + close (pin[0]); + close (pin[1]); + } + + execvp (path, arg); + _exit (127); + } + else + { + if (pfdin) + close (pin[1]); + if (pfdout) + close (pout[0]); + + if (pid > 0) + { + if (pfdin) + { + fcntl (pin[0], F_SETFD, FD_CLOEXEC); + *pfdin = pin[0]; + } + if (pfdout) + { + fcntl (pout[1], F_SETFD, FD_CLOEXEC); + *pfdout = pout[1]; + } + } + else + { + if (pfdin) + close (pin[0]); + if (pfdout) + close (pout[1]); + return 0; + } + } + return pid; +} + +/* Wait for a child process to terminate. */ +static int +child_wait (int *pfdin, int *pfdout, int pid) +{ + int stat; + + if (pfdin) + close (*pfdin); + if (pfdout) + close (*pfdout); + + waitpid (pid, &stat, 0); + return stat; +} + +/* Print error message and bail out. */ +static void +die (const char *format, ...) +{ + va_list arg; + + va_start (arg, format); + fprintf (stderr, "error: "); + vfprintf (stderr, format, arg); + va_end (arg); + + exit (1); +} + +/* Print usage message. */ +static void +usage (void) +{ + printf ("usage: run-test [-h|--help] ...\n"); +} + +/* Run test with a specific name. */ +static int +run_test (const char *name) +{ + char filename[BUFSIZ]; + char *arg1[3], *arg2[3]; + int pid1 = -1, pin1, pid2 = -1, pin2; + FILE *fpin1 = NULL, *fpin2 = NULL; + char buf1[BUFSIZ], buf2[BUFSIZ]; + int ret = 1; + + if (snprintf (filename, BUFSIZ, "./%s", name) >= BUFSIZ) + die ("file name too long\n"); + + if (access (filename, F_OK) != 0) + { + if (snprintf (filename, BUFSIZ, "./%s.sh", name) >= BUFSIZ) + die ("file name too long\n"); + + if (access (filename, F_OK) != 0) + die ("test not found: %s\n", name); + } + + if (access (filename, X_OK) != 0) + die ("script is not executable: %s\n", filename); + + arg1[0] = arg2[0] = filename; + arg1[1] = "expected"; + arg2[1] = "actual"; + arg1[2] = arg2[2] = NULL; + + printf ("Running %s...", name); + + if ((pid1 = fork_exec (&pin1, NULL, *arg1, arg1)) < 0) + die("failed to execute %s: %s\n", filename, strerror (errno)); + + if ((pid2 = fork_exec (&pin2, NULL, *arg2, arg2)) < 0) + die("failed to execute %s: %s\n", filename, strerror (errno)); + + fpin1 = fdopen (pin1, "r"); + fpin2 = fdopen (pin2, "r"); + + while (fgets (buf1, BUFSIZ, fpin1) > 0) + { + if (fgets (buf2, BUFSIZ, fpin2) <= 0 || strcmp (buf1, buf2) != 0) + { + ret = 0; + break; + } + } + + if (fpin1) + fclose (fpin1); + if (fpin2) + fclose (fpin2); + + if (child_wait (&pin1, NULL, pid1) != 0) + ret = 0; + if (child_wait (&pin2, NULL, pid2) != 0) + ret = 0; + + if (ret == 1) + printf (" ok\n"); + else + printf (" FAIL\n"); + + return ret; +} + +int +main (int argc, char **argv) +{ + int i; + + if (!argv[1]) + die ("no tests specified, bailing out\n"); + else if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) + { + usage(); + return 0; + } + + for (i = 1; i < argc; i++) + { + if (!run_test (argv[i])) + return 1; + } + + return 0; +} -- cgit v1.2.3-70-g09d2