diff options
Diffstat (limited to 'test/run-test.c')
-rw-r--r-- | test/run-test.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/test/run-test.c b/test/run-test.c new file mode 100644 index 0000000..c779be7 --- /dev/null +++ b/test/run-test.c @@ -0,0 +1,229 @@ +/* + * 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; +} |