diff options
author | Lukas Fleischer <calcurse@cryptocrack.de> | 2012-01-20 16:08:45 +0100 |
---|---|---|
committer | Lukas Fleischer <calcurse@cryptocrack.de> | 2012-01-21 23:06:32 +0100 |
commit | e66e2d4277998094ee58ebd7c9455507df303939 (patch) | |
tree | 3015583bd679bf0c044a72af72c7deef5fd9ffd7 /test | |
parent | 7a230fa76a5a0f5ada7b6afd2c42b2f99a549569 (diff) | |
download | calcurse-e66e2d4277998094ee58ebd7c9455507df303939.zip |
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 <calcurse@cryptocrack.de>
Diffstat (limited to 'test')
-rw-r--r-- | test/Makefile.am | 6 | ||||
-rw-r--r-- | test/run-test.c | 241 |
2 files changed, 247 insertions, 0 deletions
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 <misc@calcurse.org> + * 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 <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/wait.h> + +/* + * 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] <test>...\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; +} |