Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

290 lines
7.5 KiB
C

/*
* Copyright (c) 2009 Mark Heily <mark@heily.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <limits.h>
#if defined(__linux__) || defined(__FreeBSD__)
#include <sys/time.h>
#include <sys/resource.h>
#endif
#if defined(__linux__)
#include <sys/syscall.h>
#endif
#include "common.h"
unsigned int
get_fd_limit(void)
{
#ifdef _WIN32
/* actually windows should be able to hold
way more, as they use HANDLEs for everything.
Still this number should still be sufficient for
the provided number of kqueue fds.
*/
return 65536;
#else
struct rlimit rlim;
if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
perror("getrlimit(2)");
return (65536);
} else {
return (rlim.rlim_max);
}
#endif
}
unsigned int
print_fd_table(void)
{
unsigned int fd_max = get_fd_limit();
unsigned int i;
unsigned int used = 0;
int our_errno = errno; /* Preserve errno */
#ifdef __linux__
for (i = 0; i < fd_max; i++) {
if (fcntl(i, F_GETFD) == 0) {
printf("fd=%i used\n", i);
used++;
}
}
#endif
errno = our_errno;
return used;
}
void
run_iteration(struct test_context *ctx)
{
struct unit_test *test;
for (test = ctx->tests; test->ut_name != NULL; test++) {
if (test->ut_enabled) {
ctx->test = test; /* Record the current test */
test->ut_func(ctx);
}
}
free(ctx);
}
void
test_harness(struct unit_test tests[MAX_TESTS], int iterations)
{
int i, n, kqfd;
struct test_context *ctx;
printf("Running %d iterations\n", iterations);
testing_begin();
if ((kqfd = kqueue()) < 0)
die("kqueue()");
n = 0;
for (i = 0; i < iterations; i++) {
ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL)
abort();
ctx->iteration = n++;
ctx->kqfd = kqfd;
memcpy(&ctx->tests, tests, sizeof(ctx->tests));
ctx->iterations = iterations;
run_iteration(ctx);
}
testing_end();
close(kqfd);
}
void
usage(void)
{
printf("usage: [-hn] [testclass ...]\n"
" -h This message\n"
" -n Number of iterations (default: 1)\n"
" testclass[:<num>|:<start>-<end>] Tests suites to run:\n"
" ["
"kqueue "
"socket "
"signal "
"proc "
"timer "
"vnode "
"user "
"libkqueue]\n"
" All tests are run by default\n"
"\n"
);
exit(1);
}
int
main(int argc, char **argv)
{
struct unit_test tests[MAX_TESTS] = {
{ .ut_name = "kqueue",
.ut_enabled = 1,
.ut_func = test_kqueue,
.ut_end = INT_MAX },
{ .ut_name = "socket",
.ut_enabled = 1,
.ut_func = test_evfilt_read,
.ut_end = INT_MAX },
#if !defined(_WIN32) && !defined(__ANDROID__)
// XXX-FIXME -- BROKEN ON LINUX WHEN RUN IN A SEPARATE THREAD
{ .ut_name = "signal",
.ut_enabled = 1,
.ut_func = test_evfilt_signal,
.ut_end = INT_MAX },
#endif
{ .ut_name = "proc",
.ut_enabled = 1,
.ut_func = test_evfilt_proc,
.ut_end = INT_MAX },
{ .ut_name = "timer",
.ut_enabled = 1,
.ut_func = test_evfilt_timer,
.ut_end = INT_MAX },
#ifndef _WIN32
{ .ut_name = "vnode",
.ut_enabled = 1,
.ut_func = test_evfilt_vnode,
.ut_end = INT_MAX },
#endif
#ifdef EVFILT_USER
{ .ut_name = "user",
.ut_enabled = 1,
.ut_func = test_evfilt_user,
.ut_end = INT_MAX },
#endif
#ifdef EVFILT_LIBKQUEUE
{ .ut_name = "libkqueue",
.ut_enabled = 1,
.ut_func = test_evfilt_libkqueue,
.ut_end = INT_MAX },
{ NULL, 0, NULL },
#endif
};
struct unit_test *test;
int c, i, iterations;
char *arg;
int match;
#ifdef _WIN32
/* Initialize the Winsock library */
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
err(1, "WSAStartup failed");
#endif
iterations = 1;
/* Windows does not provide a POSIX-compatible getopt */
#ifndef _WIN32
while ((c = getopt (argc, argv, "hn:")) != -1) {
switch (c) {
case 'h':
usage();
break;
case 'n':
iterations = atoi(optarg);
break;
default:
usage();
}
}
/* If specific tests are requested, disable all tests by default */
if (optind < argc) {
for (test = tests; test->ut_name != NULL; test++) {
test->ut_enabled = 0;
}
}
for (i = optind; i < argc; i++) {
match = 0;
arg = argv[i];
for (test = tests; test->ut_name != NULL; test++) {
size_t namelen = strlen(test->ut_name);
char const *p;
char *q;
if (strncmp(arg, test->ut_name, strlen(test->ut_name)) == 0) {
test->ut_enabled = 1;
match = 1;
p = arg + namelen;
/*
* Test name includes a test range
*/
if (*p == ':') {
p++;
test->ut_start = strtoul(p, &q, 10);
if (p == q)
goto invalid_option;
p = q;
/*
* Range is in the format <start>-<end>
*/
if (*p == '-') {
p++;
test->ut_end = strtoul(p, &q, 10);
if (p == q)
goto invalid_option;
printf("enabled test: %s (%u-%u)\n", test->ut_name, test->ut_start, test->ut_end);
/*
* Range is in the format <num>
*/
} else if (*p == '\0') {
test->ut_end = test->ut_start;
printf("enabled test: %s (%u)\n", test->ut_name, test->ut_start);
/*
* Range is invalid
*/
} else
goto invalid_option;
/*
* Test name does not include a range
*/
} else if (*p == '\0') {
test->ut_start = 0;
test->ut_end = INT_MAX;
printf("enabled test: %s\n", test->ut_name);
} else
goto invalid_option;
break;
}
}
if (!match) {
invalid_option:
printf("ERROR: invalid option: %s\n", arg);
exit(1);
}
}
#endif
test_harness(tests, iterations);
return (0);
}