/*
* Calcurse - text-based organizer
*
* Copyright (c) 2004-2012 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, int expect_failure)
{
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))
{
if (!fgets (buf2, BUFSIZ, fpin2) || 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 (expect_failure)
ret = 1 - ret;
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 (*argv[i] == '!')
{
if (!run_test (argv[i] + 1, 1))
return 1;
}
else
{
if (!run_test (argv[i], 0))
return 1;
}
}
return 0;
}