236 lines
5.2 KiB
C
236 lines
5.2 KiB
C
/*
|
|
* Calcurse - text-based organizer
|
|
*
|
|
* Copyright (c) 2004-2023 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 (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;
|
|
}
|