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>
This commit is contained in:
parent
7a230fa76a
commit
e66e2d4277
3
.gitignore
vendored
3
.gitignore
vendored
@ -32,3 +32,6 @@ po/stamp-po
|
||||
src/*.o
|
||||
src/.deps/
|
||||
src/calcurse
|
||||
|
||||
test/*.o
|
||||
test/run-test
|
||||
|
@ -2,7 +2,7 @@ AUTOMAKE_OPTIONS= foreign
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
SUBDIRS = po src
|
||||
SUBDIRS = po src test
|
||||
|
||||
if ENABLE_DOCS
|
||||
SUBDIRS += doc
|
||||
|
@ -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
|
||||
#-------------------------------------------------------------------------------
|
||||
|
6
test/Makefile.am
Normal file
6
test/Makefile.am
Normal file
@ -0,0 +1,6 @@
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
check_PROGRAMS = run-test
|
||||
|
||||
run_test_SOURCES = run-test.c
|
||||
|
241
test/run-test.c
Normal file
241
test/run-test.c
Normal file
@ -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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user