/* * Calcurse - text-based organizer * * Copyright (c) 2004-2017 calcurse Development Team * 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 #include #include #include #include #include #include #include /* * 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] ...\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 (fgets(buf2, BUFSIZ, fpin2)) ret = 0; 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; }