#ifndef __KSELFTEST_HARNESS_H
#define __KSELFTEST_HARNESS_H
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <asm/types.h>
#include <ctype.h>
#include <errno.h>
#include <linux/unistd.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "kselftest.h"
static inline void __kselftest_memset_safe(void *s, int c, size_t n)
{
if (n > 0)
memset(s, c, n);
}
#define KSELFTEST_PRIO_TEST_F 20000
#define KSELFTEST_PRIO_XFAIL 20001
#define TEST_TIMEOUT_DEFAULT 30
#ifndef TH_LOG_STREAM
# define TH_LOG_STREAM stderr
#endif
#ifndef TH_LOG_ENABLED
# define TH_LOG_ENABLED 1
#endif
#define TH_LOG(fmt, ...) do { \
if (TH_LOG_ENABLED) \
__TH_LOG(fmt, ##__VA_ARGS__); \
} while (0)
#define __TH_LOG(fmt, ...) \
fprintf(TH_LOG_STREAM, "# %s:%d:%s:" fmt "\n", \
__FILE__, __LINE__, _metadata->name, ##__VA_ARGS__)
#define SKIP(statement, fmt, ...) do { \
snprintf(_metadata->results->reason, \
sizeof(_metadata->results->reason), fmt, ##__VA_ARGS__); \
if (TH_LOG_ENABLED) { \
fprintf(TH_LOG_STREAM, "# SKIP %s\n", \
_metadata->results->reason); \
} \
_metadata->exit_code = KSFT_SKIP; \
_metadata->trigger = 0; \
statement; \
} while (0)
#define TEST(test_name) __TEST_IMPL(test_name, -1)
#define TEST_SIGNAL(test_name, signal) __TEST_IMPL(test_name, signal)
#define __TEST_IMPL(test_name, _signal) \
static void test_name(struct __test_metadata *_metadata); \
static void wrapper_##test_name( \
struct __test_metadata *_metadata, \
struct __fixture_variant_metadata __attribute__((unused)) *variant) \
{ \
test_name(_metadata); \
} \
static struct __test_metadata _##test_name##_object = \
{ .name = #test_name, \
.fn = &wrapper_##test_name, \
.fixture = &_fixture_global, \
.termsig = _signal, \
.timeout = TEST_TIMEOUT_DEFAULT, }; \
static void __attribute__((constructor)) _register_##test_name(void) \
{ \
__register_test(&_##test_name##_object); \
} \
static void test_name( \
struct __test_metadata __attribute__((unused)) *_metadata)
#define FIXTURE_DATA(datatype_name) struct _test_data_##datatype_name
#define FIXTURE(fixture_name) \
FIXTURE_VARIANT(fixture_name); \
static struct __fixture_metadata _##fixture_name##_fixture_object = \
{ .name = #fixture_name, }; \
static void __attribute__((constructor)) \
_register_##fixture_name##_data(void) \
{ \
__register_fixture(&_##fixture_name##_fixture_object); \
} \
FIXTURE_DATA(fixture_name)
#define FIXTURE_SETUP(fixture_name) \
static void fixture_name##_setup( \
struct __test_metadata __attribute__((unused)) *_metadata, \
FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
const FIXTURE_VARIANT(fixture_name) \
__attribute__((unused)) *variant)
#define FIXTURE_TEARDOWN(fixture_name) \
static const bool fixture_name##_teardown_parent; \
__FIXTURE_TEARDOWN(fixture_name)
#define FIXTURE_TEARDOWN_PARENT(fixture_name) \
static const bool fixture_name##_teardown_parent = true; \
__FIXTURE_TEARDOWN(fixture_name)
#define __FIXTURE_TEARDOWN(fixture_name) \
static void fixture_name##_teardown( \
struct __test_metadata __attribute__((unused)) *_metadata, \
FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
const FIXTURE_VARIANT(fixture_name) \
__attribute__((unused)) *variant)
#define FIXTURE_VARIANT(fixture_name) struct _fixture_variant_##fixture_name
#define FIXTURE_VARIANT_ADD(fixture_name, variant_name) \
extern const FIXTURE_VARIANT(fixture_name) \
_##fixture_name##_##variant_name##_variant; \
static struct __fixture_variant_metadata \
_##fixture_name##_##variant_name##_object = \
{ .name = #variant_name, \
.data = &_##fixture_name##_##variant_name##_variant}; \
static void __attribute__((constructor)) \
_register_##fixture_name##_##variant_name(void) \
{ \
__register_fixture_variant(&_##fixture_name##_fixture_object, \
&_##fixture_name##_##variant_name##_object); \
} \
const FIXTURE_VARIANT(fixture_name) \
_##fixture_name##_##variant_name##_variant =
#define TEST_F(fixture_name, test_name) \
__TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT)
#define TEST_F_SIGNAL(fixture_name, test_name, signal) \
__TEST_F_IMPL(fixture_name, test_name, signal, TEST_TIMEOUT_DEFAULT)
#define TEST_F_TIMEOUT(fixture_name, test_name, timeout) \
__TEST_F_IMPL(fixture_name, test_name, -1, timeout)
#define __TEST_F_IMPL(fixture_name, test_name, signal, tmout) \
static void fixture_name##_##test_name( \
struct __test_metadata *_metadata, \
FIXTURE_DATA(fixture_name) *self, \
const FIXTURE_VARIANT(fixture_name) *variant); \
static void wrapper_##fixture_name##_##test_name( \
struct __test_metadata *_metadata, \
struct __fixture_variant_metadata *variant) \
{ \
\
FIXTURE_DATA(fixture_name) self_private, *self = NULL; \
pid_t child = 1; \
int status = 0; \
\
_metadata->no_teardown = mmap(NULL, sizeof(*_metadata->no_teardown), \
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); \
*_metadata->no_teardown = true; \
if (sizeof(*self) > 0) { \
if (fixture_name##_teardown_parent) { \
self = mmap(NULL, sizeof(*self), PROT_READ | PROT_WRITE, \
MAP_SHARED | MAP_ANONYMOUS, -1, 0); \
} else { \
__kselftest_memset_safe(&self_private, 0, sizeof(self_private)); \
self = &self_private; \
} \
} \
_metadata->variant = variant->data; \
_metadata->self = self; \
\
child = fork(); \
if (child == 0) { \
fixture_name##_setup(_metadata, self, variant->data); \
\
if (_metadata->exit_code) \
_exit(0); \
*_metadata->no_teardown = false; \
fixture_name##_##test_name(_metadata, self, variant->data); \
_metadata->teardown_fn(false, _metadata, self, variant->data); \
_exit(0); \
} else if (child < 0 || child != waitpid(child, &status, 0)) { \
ksft_print_msg("ERROR SPAWNING TEST GRANDCHILD\n"); \
_metadata->exit_code = KSFT_FAIL; \
} \
_metadata->teardown_fn(true, _metadata, self, variant->data); \
munmap(_metadata->no_teardown, sizeof(*_metadata->no_teardown)); \
_metadata->no_teardown = NULL; \
if (self && fixture_name##_teardown_parent) \
munmap(self, sizeof(*self)); \
if (WIFEXITED(status)) { \
if (WEXITSTATUS(status)) \
_metadata->exit_code = WEXITSTATUS(status); \
} else if (WIFSIGNALED(status)) { \
\
kill(getpid(), WTERMSIG(status)); \
} \
} \
static void wrapper_##fixture_name##_##test_name##_teardown( \
bool in_parent, struct __test_metadata *_metadata, \
void *self, const void *variant) \
{ \
if (fixture_name##_teardown_parent == in_parent && \
!__atomic_test_and_set(_metadata->no_teardown, __ATOMIC_RELAXED)) \
fixture_name##_teardown(_metadata, self, variant); \
} \
static struct __test_metadata *_##fixture_name##_##test_name##_object; \
static void __attribute__((constructor(KSELFTEST_PRIO_TEST_F))) \
_register_##fixture_name##_##test_name(void) \
{ \
struct __test_metadata *object = mmap(NULL, sizeof(*object), \
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); \
object->name = #test_name; \
object->fn = &wrapper_##fixture_name##_##test_name; \
object->fixture = &_##fixture_name##_fixture_object; \
object->teardown_fn = &wrapper_##fixture_name##_##test_name##_teardown; \
object->termsig = signal; \
object->timeout = tmout; \
_##fixture_name##_##test_name##_object = object; \
__register_test(object); \
} \
static void fixture_name##_##test_name( \
struct __test_metadata __attribute__((unused)) *_metadata, \
FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
const FIXTURE_VARIANT(fixture_name) \
__attribute__((unused)) *variant)
#define TEST_HARNESS_MAIN \
int main(int argc, char **argv) { \
return test_harness_run(argc, argv); \
}
#define ASSERT_EQ(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, ==, 1)
#define ASSERT_NE(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, !=, 1)
#define ASSERT_LT(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, <, 1)
#define ASSERT_LE(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, <=, 1)
#define ASSERT_GT(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, >, 1)
#define ASSERT_GE(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, >=, 1)
#define ASSERT_NULL(seen) \
__EXPECT(NULL, "NULL", seen, #seen, ==, 1)
#define ASSERT_TRUE(seen) \
__EXPECT(0, "0", seen, #seen, !=, 1)
#define ASSERT_FALSE(seen) \
__EXPECT(0, "0", seen, #seen, ==, 1)
#define ASSERT_STREQ(expected, seen) \
__EXPECT_STR(expected, seen, ==, 1)
#define ASSERT_STRNE(expected, seen) \
__EXPECT_STR(expected, seen, !=, 1)
#define EXPECT_EQ(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, ==, 0)
#define EXPECT_NE(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, !=, 0)
#define EXPECT_LT(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, <, 0)
#define EXPECT_LE(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, <=, 0)
#define EXPECT_GT(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, >, 0)
#define EXPECT_GE(expected, seen) \
__EXPECT(expected, #expected, seen, #seen, >=, 0)
#define EXPECT_NULL(seen) \
__EXPECT(NULL, "NULL", seen, #seen, ==, 0)
#define EXPECT_TRUE(seen) \
__EXPECT(0, "0", seen, #seen, !=, 0)
#define EXPECT_FALSE(seen) \
__EXPECT(0, "0", seen, #seen, ==, 0)
#define EXPECT_STREQ(expected, seen) \
__EXPECT_STR(expected, seen, ==, 0)
#define EXPECT_STRNE(expected, seen) \
__EXPECT_STR(expected, seen, !=, 0)
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#endif
#define OPTIONAL_HANDLER(_assert) \
for (; _metadata->trigger; _metadata->trigger = \
__bail(_assert, _metadata))
#define is_signed_var(var) (!!(((__typeof__(var))(-1)) < (__typeof__(var))1))
#define __EXPECT(_expected, _expected_str, _seen, _seen_str, _t, _assert) do { \
\
__typeof__(_expected) __exp = (_expected); \
__typeof__(_seen) __seen = (_seen); \
if (!(__exp _t __seen)) { \
\
switch (is_signed_var(__exp) * 2 + is_signed_var(__seen)) { \
case 0: { \
uintmax_t __exp_print = (uintmax_t)__exp; \
uintmax_t __seen_print = (uintmax_t)__seen; \
__TH_LOG("Expected %s (%ju) %s %s (%ju)", \
_expected_str, __exp_print, #_t, \
_seen_str, __seen_print); \
break; \
} \
case 1: { \
uintmax_t __exp_print = (uintmax_t)__exp; \
intmax_t __seen_print = (intmax_t)__seen; \
__TH_LOG("Expected %s (%ju) %s %s (%jd)", \
_expected_str, __exp_print, #_t, \
_seen_str, __seen_print); \
break; \
} \
case 2: { \
intmax_t __exp_print = (intmax_t)__exp; \
uintmax_t __seen_print = (uintmax_t)__seen; \
__TH_LOG("Expected %s (%jd) %s %s (%ju)", \
_expected_str, __exp_print, #_t, \
_seen_str, __seen_print); \
break; \
} \
case 3: { \
intmax_t __exp_print = (intmax_t)__exp; \
intmax_t __seen_print = (intmax_t)__seen; \
__TH_LOG("Expected %s (%jd) %s %s (%jd)", \
_expected_str, __exp_print, #_t, \
_seen_str, __seen_print); \
break; \
} \
} \
_metadata->exit_code = KSFT_FAIL; \
\
_metadata->trigger = 1; \
} \
} while (0); OPTIONAL_HANDLER(_assert)
#define __EXPECT_STR(_expected, _seen, _t, _assert) do { \
const char *__exp = (_expected); \
const char *__seen = (_seen); \
if (!(strcmp(__exp, __seen) _t 0)) { \
__TH_LOG("Expected '%s' %s '%s'.", __exp, #_t, __seen); \
_metadata->exit_code = KSFT_FAIL; \
_metadata->trigger = 1; \
} \
} while (0); OPTIONAL_HANDLER(_assert)
#define __LIST_APPEND(head, item) \
{ \
\
if (head == NULL) { \
head = item; \
item->next = NULL; \
item->prev = item; \
return; \
} \
if (__constructor_order_forward) { \
item->next = NULL; \
item->prev = head->prev; \
item->prev->next = item; \
head->prev = item; \
} else { \
item->next = head; \
item->next->prev = item; \
item->prev = item; \
head = item; \
} \
}
struct __test_results {
char reason[1024];
};
struct __test_metadata;
struct __fixture_variant_metadata;
struct __fixture_metadata {
const char *name;
struct __test_metadata *tests;
struct __fixture_variant_metadata *variant;
struct __fixture_metadata *prev, *next;
} _fixture_global __attribute__((unused)) = {
.name = "global",
.prev = &_fixture_global,
};
struct __test_xfail {
struct __fixture_metadata *fixture;
struct __fixture_variant_metadata *variant;
struct __test_metadata *test;
struct __test_xfail *prev, *next;
};
#define XFAIL_ADD(fixture_name, variant_name, test_name) \
static struct __test_xfail \
_##fixture_name##_##variant_name##_##test_name##_xfail = \
{ \
.fixture = &_##fixture_name##_fixture_object, \
.variant = &_##fixture_name##_##variant_name##_object, \
}; \
static void __attribute__((constructor(KSELFTEST_PRIO_XFAIL))) \
_register_##fixture_name##_##variant_name##_##test_name##_xfail(void) \
{ \
_##fixture_name##_##variant_name##_##test_name##_xfail.test = \
_##fixture_name##_##test_name##_object; \
__register_xfail(&_##fixture_name##_##variant_name##_##test_name##_xfail); \
}
static struct __fixture_metadata *__fixture_list = &_fixture_global;
static bool __constructor_order_forward;
static inline void __register_fixture(struct __fixture_metadata *f)
{
__LIST_APPEND(__fixture_list, f);
}
struct __fixture_variant_metadata {
const char *name;
const void *data;
struct __test_xfail *xfails;
struct __fixture_variant_metadata *prev, *next;
};
static inline void
__register_fixture_variant(struct __fixture_metadata *f,
struct __fixture_variant_metadata *variant)
{
__LIST_APPEND(f->variant, variant);
}
struct __test_metadata {
const char *name;
void (*fn)(struct __test_metadata *,
struct __fixture_variant_metadata *);
pid_t pid;
struct __fixture_metadata *fixture;
void (*teardown_fn)(bool in_parent, struct __test_metadata *_metadata,
void *self, const void *variant);
int termsig;
int exit_code;
int trigger;
int timeout;
bool aborted;
bool *no_teardown;
void *self;
const void *variant;
struct __test_results *results;
struct __test_metadata *prev, *next;
};
static inline bool __test_passed(struct __test_metadata *metadata)
{
return metadata->exit_code != KSFT_FAIL &&
metadata->exit_code <= KSFT_SKIP;
}
static inline void __register_test(struct __test_metadata *t)
{
__LIST_APPEND(t->fixture->tests, t);
}
static inline void __register_xfail(struct __test_xfail *xf)
{
__LIST_APPEND(xf->variant->xfails, xf);
}
static inline int __bail(int for_realz, struct __test_metadata *t)
{
if (for_realz) {
if (t->teardown_fn)
t->teardown_fn(false, t, t->self, t->variant);
abort();
}
return 0;
}
static void __wait_for_test(struct __test_metadata *t)
{
int status = KSFT_FAIL << 8;
struct pollfd poll_child;
int ret, child, childfd;
bool timed_out = false;
childfd = syscall(__NR_pidfd_open, t->pid, 0);
if (childfd == -1) {
t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM,
"# %s: unable to open pidfd\n",
t->name);
return;
}
poll_child.fd = childfd;
poll_child.events = POLLIN;
ret = poll(&poll_child, 1, t->timeout * 1000);
if (ret == -1) {
t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM,
"# %s: unable to wait on child pidfd\n",
t->name);
return;
} else if (ret == 0) {
timed_out = true;
kill(-(t->pid), SIGKILL);
}
child = waitpid(t->pid, &status, WNOHANG);
if (child == -1 && errno != EINTR) {
t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM,
"# %s: Failed to wait for PID %d (errno: %d)\n",
t->name, t->pid, errno);
return;
}
if (timed_out) {
t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM,
"# %s: Test terminated by timeout\n", t->name);
} else if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == KSFT_SKIP ||
WEXITSTATUS(status) == KSFT_XPASS ||
WEXITSTATUS(status) == KSFT_XFAIL) {
t->exit_code = WEXITSTATUS(status);
} else if (t->termsig != -1) {
t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM,
"# %s: Test exited normally instead of by signal (code: %d)\n",
t->name,
WEXITSTATUS(status));
} else {
switch (WEXITSTATUS(status)) {
case KSFT_PASS:
t->exit_code = KSFT_PASS;
break;
default:
t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM,
"# %s: Test failed\n",
t->name);
}
}
} else if (WIFSIGNALED(status)) {
t->exit_code = KSFT_FAIL;
if (WTERMSIG(status) == SIGABRT) {
fprintf(TH_LOG_STREAM,
"# %s: Test terminated by assertion\n",
t->name);
} else if (WTERMSIG(status) == t->termsig) {
t->exit_code = KSFT_PASS;
} else {
fprintf(TH_LOG_STREAM,
"# %s: Test terminated unexpectedly by signal %d\n",
t->name,
WTERMSIG(status));
}
} else {
t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM,
"# %s: Test ended in some other way [%u]\n",
t->name,
status);
}
}
static void test_harness_list_tests(void)
{
struct __fixture_variant_metadata *v;
struct __fixture_metadata *f;
struct __test_metadata *t;
for (f = __fixture_list; f; f = f->next) {
v = f->variant;
t = f->tests;
if (f == __fixture_list)
fprintf(stderr, "%-20s %-25s %s\n",
"# FIXTURE", "VARIANT", "TEST");
else
fprintf(stderr, "--------------------------------------------------------------------------------\n");
do {
fprintf(stderr, "%-20s %-25s %s\n",
t == f->tests ? f->name : "",
v ? v->name : "",
t ? t->name : "");
v = v ? v->next : NULL;
t = t ? t->next : NULL;
} while (v || t);
}
}
static int test_harness_argv_check(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "dhlF:f:V:v:t:T:r:")) != -1) {
switch (opt) {
case 'f':
case 'F':
case 'v':
case 'V':
case 't':
case 'T':
case 'r':
break;
case 'l':
test_harness_list_tests();
return KSFT_SKIP;
case 'd':
ksft_debug_enabled = true;
break;
case 'h':
default:
fprintf(stderr,
"Usage: %s [-h|-l|-d] [-t|-T|-v|-V|-f|-F|-r name]\n"
"\t-h print help\n"
"\t-l list all tests\n"
"\t-d enable debug prints\n"
"\n"
"\t-t name include test\n"
"\t-T name exclude test\n"
"\t-v name include variant\n"
"\t-V name exclude variant\n"
"\t-f name include fixture\n"
"\t-F name exclude fixture\n"
"\t-r name run specified test\n"
"\n"
"Test filter options can be specified "
"multiple times. The filtering stops\n"
"at the first match. For example to "
"include all tests from variant 'bla'\n"
"but not test 'foo' specify '-T foo -v bla'.\n"
"", argv[0]);
return opt == 'h' ? KSFT_SKIP : KSFT_FAIL;
}
}
return KSFT_PASS;
}
static bool test_enabled(int argc, char **argv,
struct __fixture_metadata *f,
struct __fixture_variant_metadata *v,
struct __test_metadata *t)
{
unsigned int flen = 0, vlen = 0, tlen = 0;
bool has_positive = false;
int opt;
optind = 1;
while ((opt = getopt(argc, argv, "dF:f:V:v:t:T:r:")) != -1) {
if (opt != 'd')
has_positive |= islower(opt);
switch (tolower(opt)) {
case 't':
if (!strcmp(t->name, optarg))
return islower(opt);
break;
case 'f':
if (!strcmp(f->name, optarg))
return islower(opt);
break;
case 'v':
if (!strcmp(v->name, optarg))
return islower(opt);
break;
case 'r':
if (!tlen) {
flen = strlen(f->name);
vlen = strlen(v->name);
tlen = strlen(t->name);
}
if (strlen(optarg) == flen + 1 + vlen + !!vlen + tlen &&
!strncmp(f->name, &optarg[0], flen) &&
!strncmp(v->name, &optarg[flen + 1], vlen) &&
!strncmp(t->name, &optarg[flen + 1 + vlen + !!vlen], tlen))
return true;
break;
}
}
return !has_positive;
}
static void __run_test(struct __fixture_metadata *f,
struct __fixture_variant_metadata *variant,
struct __test_metadata *t)
{
struct __test_xfail *xfail;
char test_name[1024];
const char *diagnostic;
int child;
t->exit_code = KSFT_PASS;
t->trigger = 0;
t->aborted = false;
t->no_teardown = NULL;
memset(t->results->reason, 0, sizeof(t->results->reason));
snprintf(test_name, sizeof(test_name), "%s%s%s.%s",
f->name, variant->name[0] ? "." : "", variant->name, t->name);
ksft_print_msg(" RUN %s ...\n", test_name);
fflush(stdout);
fflush(stderr);
child = fork();
if (child < 0) {
ksft_print_msg("ERROR SPAWNING TEST CHILD\n");
t->exit_code = KSFT_FAIL;
} else if (child == 0) {
setpgrp();
t->fn(t, variant);
_exit(t->exit_code);
} else {
t->pid = child;
__wait_for_test(t);
}
ksft_print_msg(" %4s %s\n",
__test_passed(t) ? "OK" : "FAIL", test_name);
for (xfail = variant->xfails; xfail; xfail = xfail->next)
if (xfail->test == t)
break;
if (xfail)
t->exit_code = __test_passed(t) ? KSFT_XPASS : KSFT_XFAIL;
if (t->results->reason[0])
diagnostic = t->results->reason;
else if (t->exit_code == KSFT_PASS || t->exit_code == KSFT_FAIL)
diagnostic = NULL;
else
diagnostic = "unknown";
ksft_test_result_code(t->exit_code, test_name,
diagnostic ? "%s" : NULL, diagnostic);
}
static int test_harness_run(int argc, char **argv)
{
struct __fixture_variant_metadata no_variant = { .name = "", };
struct __fixture_variant_metadata *v;
struct __fixture_metadata *f;
struct __test_results *results;
struct __test_metadata *t;
int ret;
unsigned int case_count = 0, test_count = 0;
unsigned int count = 0;
unsigned int pass_count = 0;
ret = test_harness_argv_check(argc, argv);
if (ret != KSFT_PASS)
return ret;
for (f = __fixture_list; f; f = f->next) {
for (v = f->variant ?: &no_variant; v; v = v->next) {
unsigned int old_tests = test_count;
for (t = f->tests; t; t = t->next)
if (test_enabled(argc, argv, f, v, t))
test_count++;
if (old_tests != test_count)
case_count++;
}
}
results = mmap(NULL, sizeof(*results), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
ksft_print_header();
ksft_set_plan(test_count);
ksft_print_msg("Starting %u tests from %u test cases.\n",
test_count, case_count);
for (f = __fixture_list; f; f = f->next) {
for (v = f->variant ?: &no_variant; v; v = v->next) {
for (t = f->tests; t; t = t->next) {
if (!test_enabled(argc, argv, f, v, t))
continue;
count++;
t->results = results;
__run_test(f, v, t);
t->results = NULL;
if (__test_passed(t))
pass_count++;
else
ret = 1;
}
}
}
munmap(results, sizeof(*results));
ksft_print_msg("%s: %u / %u tests passed.\n", ret ? "FAILED" : "PASSED",
pass_count, count);
ksft_exit(ret == 0);
return KSFT_FAIL;
}
static void __attribute__((constructor)) __constructor_order_first(void)
{
__constructor_order_forward = true;
}
#endif