aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukas Fleischer <calcurse@cryptocrack.de>2012-01-20 16:08:45 +0100
committerLukas Fleischer <calcurse@cryptocrack.de>2012-01-21 23:06:32 +0100
commite66e2d4277998094ee58ebd7c9455507df303939 (patch)
tree3015583bd679bf0c044a72af72c7deef5fd9ffd7
parent7a230fa76a5a0f5ada7b6afd2c42b2f99a549569 (diff)
downloadcalcurse-e66e2d4277998094ee58ebd7c9455507df303939.tar.gz
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>
-rw-r--r--.gitignore3
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac3
-rw-r--r--test/Makefile.am6
-rw-r--r--test/run-test.c241
5 files changed, 253 insertions, 2 deletions
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 <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;
+}