+/*
+ * Copyright (C) 2016 Julien Desfossez <jdesfossez@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <pthread.h>
#include <sys/mman.h>
#include <common/compat/time.h>
+#include <common/error.h>
#define BUF_SIZE 256
#define NB_FD 1
static volatile int stop_thread;
static int wait_fd;
+/* Used by logging utils. */
+int lttng_opt_quiet, lttng_opt_verbose, lttng_opt_mi;
+
+static void run_working_cases(FILE *validation_output_file);
+static void pselect_invalid_fd(FILE *validation_output_file);
+static void test_ppoll_big(FILE *validation_output_file);
+static void ppoll_fds_buffer_overflow(FILE *validation_output_file);
+static void pselect_invalid_pointer(FILE *validation_output_file);
+static void ppoll_fds_ulong_max(FILE *validation_output_file);
+static void epoll_pwait_invalid_pointer(FILE *validation_output_file);
+static void epoll_pwait_int_max(FILE *validation_output_file);
+static void ppoll_concurrent_write(FILE *validation_output_file);
+static void epoll_pwait_concurrent_munmap(FILE *validation_output_file);
+
+typedef void (*test_case_cb)(FILE *output_file);
+
+static const struct test_case {
+ test_case_cb run;
+ bool produces_validation_info;
+ int timeout;
+} test_cases [] =
+{
+ { .run = run_working_cases, .produces_validation_info = true, .timeout = -1 },
+ { .run = run_working_cases, .produces_validation_info = true, .timeout = 1 },
+ { .run = pselect_invalid_fd, .produces_validation_info = false },
+ { .run = test_ppoll_big, .produces_validation_info = false },
+ { .run = ppoll_fds_buffer_overflow, .produces_validation_info = false },
+ { .run = pselect_invalid_pointer, .produces_validation_info = false },
+ { .run = ppoll_fds_ulong_max, .produces_validation_info = false },
+ { .run = epoll_pwait_invalid_pointer, .produces_validation_info = true },
+ { .run = epoll_pwait_int_max, .produces_validation_info = true },
+ { .run = ppoll_concurrent_write, .produces_validation_info = false },
+ { .run = epoll_pwait_concurrent_munmap, .produces_validation_info = true },
+};
+
struct ppoll_thread_data {
struct pollfd *ufds;
int value;
};
+static
void test_select_big(void)
{
fd_set rfds, wfds, exfds;
fd2 = dup2(wait_fd, BIG_SELECT_FD);
if (fd2 < 0) {
- perror("dup2");
+ PERROR("dup2");
goto end;
}
FD_SET(fd2, &rfds);
}
if (ret == -1) {
- perror("select()");
+ PERROR("select()");
} else if (ret) {
- printf("# [select] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[select] read");
+ PERROR("[select] read");
}
- } else {
- printf("# [select] timeout\n");
}
ret = close(BIG_SELECT_FD);
if (ret) {
- perror("close");
+ PERROR("close");
}
end:
return;
}
+static
void test_pselect(void)
{
fd_set rfds;
}
if (ret == -1) {
- perror("pselect()");
+ PERROR("pselect()");
} else if (ret) {
- printf("# [pselect] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[pselect] read");
+ PERROR("[pselect] read");
}
- } else {
- printf("# [pselect] timeout\n");
}
-
}
+static
void test_select(void)
{
fd_set rfds;
}
if (ret == -1) {
- perror("select()");
+ PERROR("select()");
} else if (ret) {
- printf("# [select] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[select] read");
+ PERROR("[select] read");
}
- } else {
- printf("# [select] timeout\n");
}
-
}
+static
void test_poll(void)
{
struct pollfd ufds[NB_FD];
ret = poll(ufds, 1, timeout);
if (ret < 0) {
- perror("poll");
+ PERROR("poll");
} else if (ret > 0) {
- printf("# [poll] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[poll] read");
+ PERROR("[poll] read");
}
- } else {
- printf("# [poll] timeout\n");
}
}
+static
void test_ppoll(void)
{
struct pollfd ufds[NB_FD];
if (ret < 0) {
- perror("ppoll");
+ PERROR("ppoll");
} else if (ret > 0) {
- printf("# [ppoll] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[ppoll] read");
+ PERROR("[ppoll] read");
}
- } else {
- printf("# [ppoll] timeout\n");
}
}
-void test_ppoll_big(void)
+static
+void test_ppoll_big(FILE *validation_output_file)
{
struct pollfd ufds[MAX_FDS];
char buf[BUF_SIZE];
for (i = 0; i < MAX_FDS; i++) {
fds[i] = dup(wait_fd);
if (fds[i] < 0) {
- perror("dup");
+ PERROR("dup");
}
ufds[i].fd = fds[i];
ufds[i].events = POLLIN|POLLPRI;
ret = ppoll(ufds, MAX_FDS, NULL, NULL);
if (ret < 0) {
- perror("ppoll");
+ PERROR("ppoll");
} else if (ret > 0) {
- printf("# [ppoll] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[ppoll] read");
+ PERROR("[ppoll] read");
}
- } else {
- printf("# [ppoll] timeout\n");
}
for (i = 0; i < MAX_FDS; i++) {
ret = close(fds[i]);
if (ret != 0) {
- perror("close");
+ PERROR("close");
}
}
return;
}
-void test_epoll(void)
+static
+void test_epoll(FILE *validation_output_file)
{
int ret, epollfd;
char buf[BUF_SIZE];
epollfd = epoll_create(NB_FD);
if (epollfd < 0) {
- perror("[epoll] create");
+ PERROR("[epoll] create");
goto end;
}
+ ret = fprintf(validation_output_file,
+ ", \"epoll_wait_fd\": %i", epollfd);
+ if (ret < 0) {
+ PERROR("[epoll] Failed to write test validation output");
+ goto error;
+ }
+
epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET;
epoll_event.data.fd = wait_fd;
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event);
if (ret < 0) {
- perror("[epoll] add");
- goto end;
+ PERROR("[epoll] add");
+ goto error;
}
if (timeout > 0) {
}
if (ret == 1) {
- printf("# [epoll] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[epoll] read");
+ PERROR("[epoll] read");
}
- } else if (ret == 0) {
- printf("# [epoll] timeout\n");
- } else {
- perror("epoll_wait");
+ } else if (ret != 0) {
+ PERROR("epoll_wait");
}
+error:
+ ret = close(epollfd);
+ if (ret) {
+ PERROR("close");
+ }
end:
return;
}
-void test_pepoll(void)
+static
+void test_epoll_pwait(FILE *validation_output_file)
{
int ret, epollfd;
char buf[BUF_SIZE];
epollfd = epoll_create(NB_FD);
if (epollfd < 0) {
- perror("[eppoll] create");
+ PERROR("[epoll_pwait] create");
goto end;
}
+ ret = fprintf(validation_output_file,
+ ", \"epoll_pwait_fd\": %i", epollfd);
+ if (ret < 0) {
+ PERROR("[epoll_pwait] Failed to write test validation output");
+ goto error;
+ }
+
epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET;
epoll_event.data.fd = wait_fd;
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event);
if (ret < 0) {
- perror("[eppoll] add");
- goto end;
+ PERROR("[epoll_pwait] add");
+ goto error;
}
if (timeout > 0) {
}
if (ret == 1) {
- printf("# [eppoll] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[eppoll] read");
+ PERROR("[epoll_pwait] read");
}
- } else if (ret == 0) {
- printf("# [eppoll] timeout\n");
- } else {
- perror("epoll_pwait");
+ } else if (ret != 0) {
+ PERROR("epoll_pwait");
}
+error:
+ ret = close(epollfd);
+ if (ret) {
+ PERROR("close");
+ }
end:
return;
}
-void run_working_cases(void)
+static
+void run_working_cases(FILE *validation_output_file)
{
int ret;
int pipe_fds[2];
*/
ret = pipe(pipe_fds);
if (ret != 0) {
- perror("pipe");
+ PERROR("pipe");
goto end;
}
wait_fd = pipe_fds[0];
test_select_big();
test_poll();
test_ppoll();
- test_epoll();
- test_pepoll();
+
+ ret = fprintf(validation_output_file, "{ \"pid\": %i", getpid());
+ if (ret < 0) {
+ PERROR("Failed to write pid to test validation file");
+ goto end;
+ }
+
+ test_epoll(validation_output_file);
+ test_epoll_pwait(validation_output_file);
if (timeout > 0) {
ret = close(pipe_fds[0]);
if (ret) {
- perror("close");
+ PERROR("close");
}
ret = close(pipe_fds[1]);
if (ret) {
- perror("close");
+ PERROR("close");
}
}
+ ret = fputs(" }", validation_output_file);
+ if (ret < 0) {
+ PERROR("Failed to close JSON dictionary in test validation file");
+ goto end;
+ }
+
end:
return;
}
* segfault (eventually with a "*** stack smashing detected ***" message).
* The event should contain an array of 100 FDs filled with garbage.
*/
-void ppoll_fds_buffer_overflow(void)
+static
+void ppoll_fds_buffer_overflow(FILE *validation_output_file)
{
struct pollfd ufds[NB_FD];
char buf[BUF_SIZE];
ret = syscall(SYS_ppoll, ufds, 100, NULL, NULL);
if (ret < 0) {
- perror("ppoll");
+ PERROR("ppoll");
} else if (ret > 0) {
- printf("# [ppoll] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[ppoll] read");
+ PERROR("[ppoll] read");
}
- } else {
- printf("# [ppoll] timeout\n");
}
-
- return;
}
/*
* cleanly fail with a "Invalid argument".
* The event should contain an empty array of FDs and overflow = 1.
*/
-void ppoll_fds_ulong_max(void)
+static
+void ppoll_fds_ulong_max(FILE *validation_output_file)
{
struct pollfd ufds[NB_FD];
char buf[BUF_SIZE];
ufds[0].events = POLLIN|POLLPRI;
ret = syscall(SYS_ppoll, ufds, ULONG_MAX, NULL, NULL);
-
if (ret < 0) {
- perror("# ppoll");
+ /* Expected error. */
} else if (ret > 0) {
- printf("# [ppoll] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[ppoll] read");
+ PERROR("[ppoll] read");
}
- } else {
- printf("# [ppoll] timeout\n");
}
-
- return;
}
/*
* Pass an invalid file descriptor to pselect6(). The syscall should return
* -EBADF. The recorded event should contain a "ret = -EBADF (-9)".
*/
-void pselect_invalid_fd(void)
+static
+void pselect_invalid_fd(FILE *validation_output_file)
{
fd_set rfds;
int ret;
/*
* Open a file, close it and use the closed FD in the pselect6 call.
*/
-
fd = open("/dev/null", O_RDONLY);
if (fd == -1) {
- perror("open");
+ PERROR("open");
goto error;
}
ret = close(fd);
if (ret == -1) {
- perror("close");
+ PERROR("close");
goto error;
}
ret = syscall(SYS_pselect6, fd + 1, &rfds, NULL, NULL, NULL, NULL);
if (ret == -1) {
- perror("# pselect()");
+ /* Expected error. */
} else if (ret) {
- printf("# [pselect] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[pselect] read");
+ PERROR("[pselect] read");
}
- } else {
- printf("# [pselect] timeout\n");
}
error:
return;
* Invalid pointer as writefds, should output a ppoll event
* with 0 FDs.
*/
-void pselect_invalid_pointer(void)
+static
+void pselect_invalid_pointer(FILE *validation_output_file)
{
fd_set rfds;
int ret;
ret = syscall(SYS_pselect6, 1, &rfds, (fd_set *) invalid, NULL, NULL,
NULL);
-
if (ret == -1) {
- perror("# pselect()");
+ /* Expected error. */
} else if (ret) {
- printf("# [pselect] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[pselect] read");
+ PERROR("[pselect] read");
}
- } else {
- printf("# [pselect] timeout\n");
}
-
}
/*
* Pass an invalid pointer to epoll_pwait, should fail with
* "Bad address", the event returns 0 FDs.
*/
-void epoll_pwait_invalid_pointer(void)
+static
+void epoll_pwait_invalid_pointer(FILE *validation_output_file)
{
int ret, epollfd;
char buf[BUF_SIZE];
epollfd = epoll_create(NB_FD);
if (epollfd < 0) {
- perror("[eppoll] create");
+ PERROR("[epoll_pwait] create");
goto end;
}
+ ret = fprintf(validation_output_file,
+ "{ \"epollfd\": %i, \"pid\": %i }", epollfd,
+ getpid());
+ if (ret < 0) {
+ PERROR("[epoll_pwait] Failed to write test validation output");
+ goto error;
+ }
+
epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET;
epoll_event.data.fd = wait_fd;
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event);
if (ret < 0) {
- perror("[eppoll] add");
- goto end;
+ PERROR("[epoll_pwait] add");
+ goto error;
}
ret = syscall(SYS_epoll_pwait, epollfd,
(struct epoll_event *) invalid, 1, -1, NULL);
if (ret == 1) {
- printf("# [eppoll] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[eppoll] read");
+ PERROR("[epoll_pwait] read");
}
- } else if (ret == 0) {
- printf("# [eppoll] timeout\n");
- } else {
- perror("# epoll_pwait");
+ } else if (ret != 0) {
+ /* Expected error. */
}
+error:
+ ret = close(epollfd);
+ if (ret) {
+ PERROR("close");
+ }
end:
return;
}
* Set maxevents to INT_MAX, should output "Invalid argument"
* The event should return an empty array.
*/
-void epoll_pwait_int_max(void)
+static
+void epoll_pwait_int_max(FILE *validation_output_file)
{
int ret, epollfd;
char buf[BUF_SIZE];
epollfd = epoll_create(NB_FD);
if (epollfd < 0) {
- perror("[eppoll] create");
+ PERROR("[epoll_pwait] create");
goto end;
}
+ ret = fprintf(validation_output_file,
+ "{ \"epollfd\": %i, \"pid\": %i }", epollfd,
+ getpid());
+ if (ret < 0) {
+ PERROR("[epoll_pwait] Failed to write test validation output");
+ goto error;
+ }
+
epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET;
epoll_event.data.fd = wait_fd;
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event);
if (ret < 0) {
- perror("[eppoll] add");
- goto end;
+ PERROR("[epoll_pwait] add");
+ goto error;
}
ret = syscall(SYS_epoll_pwait, epollfd, &epoll_event, INT_MAX, -1,
NULL);
if (ret == 1) {
- printf("# [eppoll] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[eppoll] read");
+ PERROR("[epoll_pwait] read");
}
- } else if (ret == 0) {
- printf("# [eppoll] timeout\n");
- } else {
- perror("# epoll_pwait");
+ } else if (ret != 0) {
+ /* Expected error. */
}
+error:
+ ret = close(epollfd);
+ if (ret) {
+ PERROR("close");
+ }
end:
return;
}
+static
void *ppoll_writer(void *arg)
{
struct ppoll_thread_data *data = (struct ppoll_thread_data *) arg;
return NULL;
}
+static
void do_ppoll(int *fds, struct pollfd *ufds)
{
int i, ret;
ret = ppoll(ufds, MAX_FDS, &ts, NULL);
if (ret < 0) {
- perror("ppoll");
+ PERROR("ppoll");
} else if (ret > 0) {
- printf("# [ppoll] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[ppoll] read");
+ PERROR("[ppoll] read");
}
- } else {
- printf("# [ppoll] timeout\n");
}
}
+static
void stress_ppoll(int *fds, int value)
{
pthread_t writer;
*
* ppoll should work as expected and the trace should be readable at the end.
*/
-void ppoll_concurrent_write(void)
+static
+void ppoll_concurrent_write(FILE *validation_output_file)
{
int i, ret, fds[MAX_FDS];
for (i = 0; i < MAX_FDS; i++) {
fds[i] = dup(wait_fd);
if (fds[i] < 0) {
- perror("dup");
+ PERROR("dup");
}
}
for (i = 0; i < MAX_FDS; i++) {
ret = close(fds[i]);
if (ret != 0) {
- perror("close");
+ PERROR("close");
}
}
return;
}
+static
void *epoll_pwait_writer(void *addr)
{
srand(time(NULL));
* buffer allocated for the returned data. This should randomly segfault.
* The trace should be readable and no kernel OOPS should occur.
*/
-void epoll_pwait_concurrent_munmap(void)
+static
+void epoll_pwait_concurrent_munmap(FILE *validation_output_file)
{
int ret, epollfd, i, fds[MAX_FDS];
char buf[BUF_SIZE];
}
epollfd = epoll_create(MAX_FDS);
if (epollfd < 0) {
- perror("[eppoll] create");
+ PERROR("[epoll_pwait] create");
goto end;
}
- epoll_event = mmap(NULL, MAX_FDS * sizeof(struct epoll_event),
- PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
- -1, 0);
+ ret = fprintf(validation_output_file,
+ "{ \"epollfd\": %i, \"pid\": %i }", epollfd,
+ getpid());
+ if (ret < 0) {
+ PERROR("[epoll_pwait] Failed to write test validation output");
+ goto error;
+ }
+
+ epoll_event = (struct epoll_event *) mmap(NULL,
+ MAX_FDS * sizeof(struct epoll_event),
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1,
+ 0);
if (epoll_event == MAP_FAILED) {
- perror("mmap");
- goto end;
+ PERROR("mmap");
+ goto error;
}
for (i = 0; i < MAX_FDS; i++) {
fds[i] = dup(wait_fd);
if (fds[i] < 0) {
- perror("dup");
+ PERROR("dup");
}
epoll_event[i].events = EPOLLIN | EPOLLPRI | EPOLLET;
epoll_event[i].data.fd = fds[i];
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fds[i], epoll_event);
if (ret < 0) {
- perror("[eppoll] add");
- goto end_unmap;
+ PERROR("[epoll_pwait] add");
+ goto error_unmap;
}
}
stop_thread = 0;
(void *) epoll_event);
if (ret != 0) {
fprintf(stderr, "[error] pthread_create\n");
- goto end_unmap;
+ goto error_unmap;
}
ret = epoll_pwait(epollfd, epoll_event, 1, 1, NULL);
if (ret == 1) {
- printf("# [eppoll] data available\n");
ret = read(wait_fd, buf, BUF_SIZE);
if (ret < 0) {
- perror("[eppoll] read");
+ PERROR("[epoll_pwait] read");
}
- } else if (ret == 0) {
- printf("# [eppoll] timeout\n");
- } else {
- perror("# epoll_pwait");
+ } else if (ret != 0) {
+ /* Expected error. */
}
stop_thread = 1;
ret = pthread_join(writer, NULL);
if (ret) {
fprintf(stderr, "[error] pthread_join\n");
- goto end_unmap;
+ goto error_unmap;
}
-end_unmap:
+error_unmap:
for (i = 0; i < MAX_FDS; i++) {
ret = close(fds[i]);
if (ret != 0) {
- perror("close");
+ PERROR("close");
}
}
ret = munmap(epoll_event, MAX_FDS * sizeof(struct epoll_event));
if (ret != 0) {
- perror("munmap");
+ PERROR("munmap");
}
+error:
+ ret = close(epollfd);
+ if (ret) {
+ PERROR("close");
+ }
end:
return;
}
-void usage(poptContext optCon, int exitcode, char *error, char *addl)
-{
- poptPrintUsage(optCon, stderr, 0);
- if (error) {
- fprintf(stderr, "%s: %s\n", error, addl);
- }
- exit(exitcode);
-}
-
+static
void print_list(void)
{
fprintf(stderr, "Test list (-t X):\n");
int c, ret, test = -1;
poptContext optCon;
struct rlimit open_lim;
-
+ FILE *test_validation_output_file = NULL;
+ const char *test_validation_output_file_path = NULL;
struct poptOption optionsTable[] = {
{ "test", 't', POPT_ARG_INT, &test, 0,
"Test to run", NULL },
{ "list", 'l', 0, 0, 'l',
"List of tests (-t X)", NULL },
+ { "validation-file", 'o', POPT_ARG_STRING, &test_validation_output_file_path, 0,
+ "Test case output", NULL },
POPT_AUTOHELP
{ NULL, 0, 0, NULL, 0 }
};
+ const struct test_case *test_case;
optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
ret = 0;
while ((c = poptGetNextOpt(optCon)) >= 0) {
- switch(c) {
+ switch (c) {
case 'l':
print_list();
goto end;
}
}
+ if (!test_validation_output_file_path) {
+ fprintf(stderr, "A test validation file path is required (--validation-file/-o)\n");
+ ret = -1;
+ goto end;
+ }
+
+ test_validation_output_file = fopen(test_validation_output_file_path, "w+");
+ if (!test_validation_output_file) {
+ PERROR("Failed to create test validation output file at '%s'",
+ test_validation_output_file_path);
+ ret = -1;
+ goto end;
+ }
+
open_lim.rlim_cur = MAX_FDS + MIN_NR_FDS;
open_lim.rlim_max = MAX_FDS + MIN_NR_FDS;
ret = setrlimit(RLIMIT_NOFILE, &open_lim);
if (ret < 0) {
- perror("setrlimit");
+ PERROR("setrlimit");
goto end;
}
/*
* Some tests might segfault, but we need the getpid() to be output
- * for the validation, disabling the buffering on stdout works.
+ * for the validation, disabling the buffering on the validation file
+ * works.
*/
- setbuf(stdout, NULL);
- printf("%d\n", getpid());
-
+ setbuf(test_validation_output_file, NULL);
wait_fd = STDIN_FILENO;
- switch(test) {
- case 1:
- timeout = -1;
- run_working_cases();
- break;
- case 2:
- timeout = 1;
- run_working_cases();
- break;
- case 3:
- pselect_invalid_fd();
- break;
- case 4:
- test_ppoll_big();
- break;
- case 5:
- ppoll_fds_buffer_overflow();
- break;
- case 6:
- pselect_invalid_pointer();
- break;
- case 7:
- ppoll_fds_ulong_max();
- break;
- case 8:
- epoll_pwait_invalid_pointer();
- break;
- case 9:
- epoll_pwait_int_max();
- break;
- case 10:
- ppoll_concurrent_write();
- break;
- case 11:
- epoll_pwait_concurrent_munmap();
- break;
- default:
+ /* Test case id is 1-based. */
+ if (test < 1 || test > ARRAY_SIZE(test_cases)) {
poptPrintUsage(optCon, stderr, 0);
ret = -1;
- break;
}
+ test_case = &test_cases[test - 1];
+
+ timeout = test_case->timeout;
+ if (!test_case->produces_validation_info) {
+ /*
+ * All test cases need to provide, at minimum, the pid of the
+ * test application.
+ */
+ ret = fprintf(test_validation_output_file, "{ \"pid\": %i }", getpid());
+ if (ret < 0) {
+ PERROR("Failed to write application pid to test validation file");
+ goto end;
+ }
+ }
+
+ test_case->run(test_validation_output_file);
+
end:
+ if (test_validation_output_file) {
+ const int close_ret = fclose(test_validation_output_file);
+
+ if (close_ret) {
+ PERROR("Failed to close test output file");
+ }
+ }
poptFreeContext(optCon);
return ret;
}