Commit e2d890af authored by David S. Miller's avatar David S. Miller

Merge branch 'selftests-xfail'

Jakub Kicinski says:

====================
selftests: kselftest_harness: support using xfail

When running selftests for our subsystem in our CI we'd like all
tests to pass. Currently some tests use SKIP for cases they
expect to fail, because the kselftest_harness limits the return
codes to pass/fail/skip. XFAIL which would be a great match
here cannot be used.

Remove the no_print handling and use vfork() to run the test in
a different process than the setup. This way we don't need to
pass "failing step" via the exit code. Further clean up the exit
codes so that we can use all KSFT_* values. Rewrite the result
printing to make handling XFAIL/XPASS easier. Support tests
declaring combinations of fixture + variant they expect to fail.

Merge plan is to put it on top of -rc6 and merge into net-next.
That way others should be able to pull the patches without
any networking changes.

v4:
 - rebase on top of Mickael's vfork() changes
v3: https://lore.kernel.org/all/20240220192235.2953484-1-kuba@kernel.org/
 - combine multiple series
 - change to "list of expected failures" rather than SKIP()-like handling
v2: https://lore.kernel.org/all/20240216002619.1999225-1-kuba@kernel.org/
 - fix alignment
follow up RFC: https://lore.kernel.org/all/20240216004122.2004689-1-kuba@kernel.org/
v1: https://lore.kernel.org/all/20240213154416.422739-1-kuba@kernel.org/
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 7779f268 c05bf0e9
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
* ksft_test_result_skip(fmt, ...); * ksft_test_result_skip(fmt, ...);
* ksft_test_result_xfail(fmt, ...); * ksft_test_result_xfail(fmt, ...);
* ksft_test_result_error(fmt, ...); * ksft_test_result_error(fmt, ...);
* ksft_test_result_code(exit_code, test_name, fmt, ...);
* *
* When all tests are finished, clean up and exit the program with one of: * When all tests are finished, clean up and exit the program with one of:
* *
...@@ -254,6 +255,50 @@ static inline __printf(1, 2) void ksft_test_result_error(const char *msg, ...) ...@@ -254,6 +255,50 @@ static inline __printf(1, 2) void ksft_test_result_error(const char *msg, ...)
va_end(args); va_end(args);
} }
static inline __printf(3, 4)
void ksft_test_result_code(int exit_code, const char *test_name,
const char *msg, ...)
{
const char *tap_code = "ok";
const char *directive = "";
int saved_errno = errno;
va_list args;
switch (exit_code) {
case KSFT_PASS:
ksft_cnt.ksft_pass++;
break;
case KSFT_XFAIL:
directive = " # XFAIL ";
ksft_cnt.ksft_xfail++;
break;
case KSFT_XPASS:
directive = " # XPASS ";
ksft_cnt.ksft_xpass++;
break;
case KSFT_SKIP:
directive = " # SKIP ";
ksft_cnt.ksft_xskip++;
break;
case KSFT_FAIL:
default:
tap_code = "not ok";
ksft_cnt.ksft_fail++;
break;
}
/* Docs seem to call for double space if directive is absent */
if (!directive[0] && msg[0])
directive = " # ";
va_start(args, msg);
printf("%s %u %s%s", tap_code, ksft_test_num(), test_name, directive);
errno = saved_errno;
vprintf(msg, args);
printf("\n");
va_end(args);
}
static inline int ksft_exit_pass(void) static inline int ksft_exit_pass(void)
{ {
ksft_print_cnts(); ksft_print_cnts();
......
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
#include <asm/types.h> #include <asm/types.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <limits.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
...@@ -95,14 +96,6 @@ ...@@ -95,14 +96,6 @@
* E.g., #define TH_LOG_ENABLED 1 * E.g., #define TH_LOG_ENABLED 1
* *
* If no definition is provided, logging is enabled by default. * If no definition is provided, logging is enabled by default.
*
* If there is no way to print an error message for the process running the
* test (e.g. not allowed to write to stderr), it is still possible to get the
* ASSERT_* number for which the test failed. This behavior can be enabled by
* writing `_metadata->no_print = true;` before the check sequence that is
* unable to print. When an error occur, instead of printing an error message
* and calling `abort(3)`, the test process call `_exit(2)` with the assert
* number as argument, which is then printed by the parent process.
*/ */
#define TH_LOG(fmt, ...) do { \ #define TH_LOG(fmt, ...) do { \
if (TH_LOG_ENABLED) \ if (TH_LOG_ENABLED) \
...@@ -135,8 +128,7 @@ ...@@ -135,8 +128,7 @@
fprintf(TH_LOG_STREAM, "# SKIP %s\n", \ fprintf(TH_LOG_STREAM, "# SKIP %s\n", \
_metadata->results->reason); \ _metadata->results->reason); \
} \ } \
_metadata->passed = 1; \ _metadata->exit_code = KSFT_SKIP; \
_metadata->skip = 1; \
_metadata->trigger = 0; \ _metadata->trigger = 0; \
statement; \ statement; \
} while (0) } while (0)
...@@ -363,6 +355,11 @@ ...@@ -363,6 +355,11 @@
* Defines a test that depends on a fixture (e.g., is part of a test case). * Defines a test that depends on a fixture (e.g., is part of a test case).
* Very similar to TEST() except that *self* is the setup instance of fixture's * Very similar to TEST() except that *self* is the setup instance of fixture's
* datatype exposed for use by the implementation. * datatype exposed for use by the implementation.
*
* The @test_name code is run in a separate process sharing the same memory
* (i.e. vfork), which means that the test process can update its privileges
* without impacting the related FIXTURE_TEARDOWN() (e.g. to remove files from
* a directory where write access was dropped).
*/ */
#define TEST_F(fixture_name, test_name) \ #define TEST_F(fixture_name, test_name) \
__TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT) __TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT)
...@@ -384,15 +381,28 @@ ...@@ -384,15 +381,28 @@
{ \ { \
/* fixture data is alloced, setup, and torn down per call. */ \ /* fixture data is alloced, setup, and torn down per call. */ \
FIXTURE_DATA(fixture_name) self; \ FIXTURE_DATA(fixture_name) self; \
pid_t child = 1; \
memset(&self, 0, sizeof(FIXTURE_DATA(fixture_name))); \ memset(&self, 0, sizeof(FIXTURE_DATA(fixture_name))); \
if (setjmp(_metadata->env) == 0) { \ if (setjmp(_metadata->env) == 0) { \
fixture_name##_setup(_metadata, &self, variant->data); \ fixture_name##_setup(_metadata, &self, variant->data); \
/* Let setup failure terminate early. */ \ /* Let setup failure terminate early. */ \
if (!_metadata->passed || _metadata->skip) \ if (_metadata->exit_code) \
return; \ return; \
_metadata->setup_completed = true; \ _metadata->setup_completed = true; \
fixture_name##_##test_name(_metadata, &self, variant->data); \ /* Use the same _metadata. */ \
child = vfork(); \
if (child == 0) { \
fixture_name##_##test_name(_metadata, &self, variant->data); \
_exit(0); \
} \
if (child < 0) { \
ksft_print_msg("ERROR SPAWNING TEST GRANDCHILD\n"); \
_metadata->exit_code = KSFT_FAIL; \
} \
} \ } \
if (child == 0) \
/* Child failed and updated the shared _metadata. */ \
_exit(0); \
if (_metadata->setup_completed) \ if (_metadata->setup_completed) \
fixture_name##_teardown(_metadata, &self, variant->data); \ fixture_name##_teardown(_metadata, &self, variant->data); \
__test_check_assert(_metadata); \ __test_check_assert(_metadata); \
...@@ -694,18 +704,12 @@ ...@@ -694,18 +704,12 @@
for (; _metadata->trigger; _metadata->trigger = \ for (; _metadata->trigger; _metadata->trigger = \
__bail(_assert, _metadata)) __bail(_assert, _metadata))
#define __INC_STEP(_metadata) \
/* Keep "step" below 255 (which is used for "SKIP" reporting). */ \
if (_metadata->passed && _metadata->step < 253) \
_metadata->step++;
#define is_signed_type(var) (!!(((__typeof__(var))(-1)) < (__typeof__(var))1)) #define is_signed_type(var) (!!(((__typeof__(var))(-1)) < (__typeof__(var))1))
#define __EXPECT(_expected, _expected_str, _seen, _seen_str, _t, _assert) do { \ #define __EXPECT(_expected, _expected_str, _seen, _seen_str, _t, _assert) do { \
/* Avoid multiple evaluation of the cases */ \ /* Avoid multiple evaluation of the cases */ \
__typeof__(_expected) __exp = (_expected); \ __typeof__(_expected) __exp = (_expected); \
__typeof__(_seen) __seen = (_seen); \ __typeof__(_seen) __seen = (_seen); \
if (_assert) __INC_STEP(_metadata); \
if (!(__exp _t __seen)) { \ if (!(__exp _t __seen)) { \
/* Report with actual signedness to avoid weird output. */ \ /* Report with actual signedness to avoid weird output. */ \
switch (is_signed_type(__exp) * 2 + is_signed_type(__seen)) { \ switch (is_signed_type(__exp) * 2 + is_signed_type(__seen)) { \
...@@ -742,7 +746,7 @@ ...@@ -742,7 +746,7 @@
break; \ break; \
} \ } \
} \ } \
_metadata->passed = 0; \ _metadata->exit_code = KSFT_FAIL; \
/* Ensure the optional handler is triggered */ \ /* Ensure the optional handler is triggered */ \
_metadata->trigger = 1; \ _metadata->trigger = 1; \
} \ } \
...@@ -751,10 +755,9 @@ ...@@ -751,10 +755,9 @@
#define __EXPECT_STR(_expected, _seen, _t, _assert) do { \ #define __EXPECT_STR(_expected, _seen, _t, _assert) do { \
const char *__exp = (_expected); \ const char *__exp = (_expected); \
const char *__seen = (_seen); \ const char *__seen = (_seen); \
if (_assert) __INC_STEP(_metadata); \
if (!(strcmp(__exp, __seen) _t 0)) { \ if (!(strcmp(__exp, __seen) _t 0)) { \
__TH_LOG("Expected '%s' %s '%s'.", __exp, #_t, __seen); \ __TH_LOG("Expected '%s' %s '%s'.", __exp, #_t, __seen); \
_metadata->passed = 0; \ _metadata->exit_code = KSFT_FAIL; \
_metadata->trigger = 1; \ _metadata->trigger = 1; \
} \ } \
} while (0); OPTIONAL_HANDLER(_assert) } while (0); OPTIONAL_HANDLER(_assert)
...@@ -800,6 +803,37 @@ struct __fixture_metadata { ...@@ -800,6 +803,37 @@ struct __fixture_metadata {
.prev = &_fixture_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;
};
/**
* XFAIL_ADD() - mark variant + test case combination as expected to fail
* @fixture_name: name of the fixture
* @variant_name: name of the variant
* @test_name: name of the test case
*
* Mark a combination of variant + test case for a given fixture as expected
* to fail. Tests marked this way will report XPASS / XFAIL return codes,
* instead of PASS / FAIL,and use respective counters.
*/
#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, \
.test = &_##fixture_name##_##test_name##_object, \
}; \
static void __attribute__((constructor)) \
_register_##fixture_name##_##variant_name##_##test_name##_xfail(void) \
{ \
__register_xfail(&_##fixture_name##_##variant_name##_##test_name##_xfail); \
}
static struct __fixture_metadata *__fixture_list = &_fixture_global; static struct __fixture_metadata *__fixture_list = &_fixture_global;
static int __constructor_order; static int __constructor_order;
...@@ -814,6 +848,7 @@ static inline void __register_fixture(struct __fixture_metadata *f) ...@@ -814,6 +848,7 @@ static inline void __register_fixture(struct __fixture_metadata *f)
struct __fixture_variant_metadata { struct __fixture_variant_metadata {
const char *name; const char *name;
const void *data; const void *data;
struct __test_xfail *xfails;
struct __fixture_variant_metadata *prev, *next; struct __fixture_variant_metadata *prev, *next;
}; };
...@@ -832,13 +867,10 @@ struct __test_metadata { ...@@ -832,13 +867,10 @@ struct __test_metadata {
pid_t pid; /* pid of test when being run */ pid_t pid; /* pid of test when being run */
struct __fixture_metadata *fixture; struct __fixture_metadata *fixture;
int termsig; int termsig;
int passed; int exit_code;
int skip; /* did SKIP get used? */
int trigger; /* extra handler after the evaluation */ int trigger; /* extra handler after the evaluation */
int timeout; /* seconds to wait for test timeout */ int timeout; /* seconds to wait for test timeout */
bool timed_out; /* did this test timeout instead of exiting? */ bool timed_out; /* did this test timeout instead of exiting? */
__u8 step;
bool no_print; /* manual trigger when TH_LOG_STREAM is not available */
bool aborted; /* stopped test due to failed ASSERT */ bool aborted; /* stopped test due to failed ASSERT */
bool setup_completed; /* did setup finish? */ bool setup_completed; /* did setup finish? */
jmp_buf env; /* for exiting out of test early */ jmp_buf env; /* for exiting out of test early */
...@@ -846,6 +878,12 @@ struct __test_metadata { ...@@ -846,6 +878,12 @@ struct __test_metadata {
struct __test_metadata *prev, *next; 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;
}
/* /*
* Since constructors are called in reverse order, reverse the test * Since constructors are called in reverse order, reverse the test
* list so tests are run in source declaration order. * list so tests are run in source declaration order.
...@@ -860,6 +898,11 @@ static inline void __register_test(struct __test_metadata *t) ...@@ -860,6 +898,11 @@ static inline void __register_test(struct __test_metadata *t)
__LIST_APPEND(t->fixture->tests, 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) static inline int __bail(int for_realz, struct __test_metadata *t)
{ {
/* if this is ASSERT, return immediately. */ /* if this is ASSERT, return immediately. */
...@@ -873,11 +916,8 @@ static inline int __bail(int for_realz, struct __test_metadata *t) ...@@ -873,11 +916,8 @@ static inline int __bail(int for_realz, struct __test_metadata *t)
static inline void __test_check_assert(struct __test_metadata *t) static inline void __test_check_assert(struct __test_metadata *t)
{ {
if (t->aborted) { if (t->aborted)
if (t->no_print)
_exit(t->step);
abort(); abort();
}
} }
struct __test_metadata *__active_test; struct __test_metadata *__active_test;
...@@ -913,7 +953,7 @@ void __wait_for_test(struct __test_metadata *t) ...@@ -913,7 +953,7 @@ void __wait_for_test(struct __test_metadata *t)
int status; int status;
if (sigaction(SIGALRM, &action, &saved_action)) { if (sigaction(SIGALRM, &action, &saved_action)) {
t->passed = 0; t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM, fprintf(TH_LOG_STREAM,
"# %s: unable to install SIGALRM handler\n", "# %s: unable to install SIGALRM handler\n",
t->name); t->name);
...@@ -925,7 +965,7 @@ void __wait_for_test(struct __test_metadata *t) ...@@ -925,7 +965,7 @@ void __wait_for_test(struct __test_metadata *t)
waitpid(t->pid, &status, 0); waitpid(t->pid, &status, 0);
alarm(0); alarm(0);
if (sigaction(SIGALRM, &saved_action, NULL)) { if (sigaction(SIGALRM, &saved_action, NULL)) {
t->passed = 0; t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM, fprintf(TH_LOG_STREAM,
"# %s: unable to uninstall SIGALRM handler\n", "# %s: unable to uninstall SIGALRM handler\n",
t->name); t->name);
...@@ -934,16 +974,16 @@ void __wait_for_test(struct __test_metadata *t) ...@@ -934,16 +974,16 @@ void __wait_for_test(struct __test_metadata *t)
__active_test = NULL; __active_test = NULL;
if (t->timed_out) { if (t->timed_out) {
t->passed = 0; t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM, fprintf(TH_LOG_STREAM,
"# %s: Test terminated by timeout\n", t->name); "# %s: Test terminated by timeout\n", t->name);
} else if (WIFEXITED(status)) { } else if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == 255) { if (WEXITSTATUS(status) == KSFT_SKIP ||
/* SKIP */ WEXITSTATUS(status) == KSFT_XPASS ||
t->passed = 1; WEXITSTATUS(status) == KSFT_XFAIL) {
t->skip = 1; t->exit_code = WEXITSTATUS(status);
} else if (t->termsig != -1) { } else if (t->termsig != -1) {
t->passed = 0; t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM, fprintf(TH_LOG_STREAM,
"# %s: Test exited normally instead of by signal (code: %d)\n", "# %s: Test exited normally instead of by signal (code: %d)\n",
t->name, t->name,
...@@ -951,26 +991,25 @@ void __wait_for_test(struct __test_metadata *t) ...@@ -951,26 +991,25 @@ void __wait_for_test(struct __test_metadata *t)
} else { } else {
switch (WEXITSTATUS(status)) { switch (WEXITSTATUS(status)) {
/* Success */ /* Success */
case 0: case KSFT_PASS:
t->passed = 1; t->exit_code = KSFT_PASS;
break; break;
/* Other failure, assume step report. */ /* Failure */
default: default:
t->passed = 0; t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM, fprintf(TH_LOG_STREAM,
"# %s: Test failed at step #%d\n", "# %s: Test failed\n",
t->name, t->name);
WEXITSTATUS(status));
} }
} }
} else if (WIFSIGNALED(status)) { } else if (WIFSIGNALED(status)) {
t->passed = 0; t->exit_code = KSFT_FAIL;
if (WTERMSIG(status) == SIGABRT) { if (WTERMSIG(status) == SIGABRT) {
fprintf(TH_LOG_STREAM, fprintf(TH_LOG_STREAM,
"# %s: Test terminated by assertion\n", "# %s: Test terminated by assertion\n",
t->name); t->name);
} else if (WTERMSIG(status) == t->termsig) { } else if (WTERMSIG(status) == t->termsig) {
t->passed = 1; t->exit_code = KSFT_PASS;
} else { } else {
fprintf(TH_LOG_STREAM, fprintf(TH_LOG_STREAM,
"# %s: Test terminated unexpectedly by signal %d\n", "# %s: Test terminated unexpectedly by signal %d\n",
...@@ -1110,16 +1149,19 @@ void __run_test(struct __fixture_metadata *f, ...@@ -1110,16 +1149,19 @@ void __run_test(struct __fixture_metadata *f,
struct __fixture_variant_metadata *variant, struct __fixture_variant_metadata *variant,
struct __test_metadata *t) struct __test_metadata *t)
{ {
struct __test_xfail *xfail;
char test_name[LINE_MAX];
const char *diagnostic;
/* reset test struct */ /* reset test struct */
t->passed = 1; t->exit_code = KSFT_PASS;
t->skip = 0;
t->trigger = 0; t->trigger = 0;
t->step = 1;
t->no_print = 0;
memset(t->results->reason, 0, sizeof(t->results->reason)); memset(t->results->reason, 0, sizeof(t->results->reason));
ksft_print_msg(" RUN %s%s%s.%s ...\n", snprintf(test_name, sizeof(test_name), "%s%s%s.%s",
f->name, variant->name[0] ? "." : "", variant->name, t->name); f->name, variant->name[0] ? "." : "", variant->name, t->name);
ksft_print_msg(" RUN %s ...\n", test_name);
/* Make sure output buffers are flushed before fork */ /* Make sure output buffers are flushed before fork */
fflush(stdout); fflush(stdout);
...@@ -1128,29 +1170,33 @@ void __run_test(struct __fixture_metadata *f, ...@@ -1128,29 +1170,33 @@ void __run_test(struct __fixture_metadata *f,
t->pid = fork(); t->pid = fork();
if (t->pid < 0) { if (t->pid < 0) {
ksft_print_msg("ERROR SPAWNING TEST CHILD\n"); ksft_print_msg("ERROR SPAWNING TEST CHILD\n");
t->passed = 0; t->exit_code = KSFT_FAIL;
} else if (t->pid == 0) { } else if (t->pid == 0) {
setpgrp(); setpgrp();
t->fn(t, variant); t->fn(t, variant);
if (t->skip) _exit(t->exit_code);
_exit(255);
/* Pass is exit 0 */
if (t->passed)
_exit(0);
/* Something else happened, report the step. */
_exit(t->step);
} else { } else {
__wait_for_test(t); __wait_for_test(t);
} }
ksft_print_msg(" %4s %s%s%s.%s\n", t->passed ? "OK" : "FAIL", ksft_print_msg(" %4s %s\n",
f->name, variant->name[0] ? "." : "", variant->name, t->name); __test_passed(t) ? "OK" : "FAIL", test_name);
if (t->skip) /* Check if we're expecting this test to fail */
ksft_test_result_skip("%s\n", t->results->reason[0] ? for (xfail = variant->xfails; xfail; xfail = xfail->next)
t->results->reason : "unknown"); 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 else
ksft_test_result(t->passed, "%s%s%s.%s\n", diagnostic = "unknown";
f->name, variant->name[0] ? "." : "", variant->name, t->name);
ksft_test_result_code(t->exit_code, test_name,
diagnostic ? "%s" : "", diagnostic);
} }
static int test_harness_run(int argc, char **argv) static int test_harness_run(int argc, char **argv)
...@@ -1198,7 +1244,7 @@ static int test_harness_run(int argc, char **argv) ...@@ -1198,7 +1244,7 @@ static int test_harness_run(int argc, char **argv)
t->results = results; t->results = results;
__run_test(f, v, t); __run_test(f, v, t);
t->results = NULL; t->results = NULL;
if (t->passed) if (__test_passed(t))
pass_count++; pass_count++;
else else
ret = 1; ret = 1;
......
...@@ -307,7 +307,7 @@ TEST(ruleset_fd_transfer) ...@@ -307,7 +307,7 @@ TEST(ruleset_fd_transfer)
dir_fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC); dir_fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
ASSERT_LE(0, dir_fd); ASSERT_LE(0, dir_fd);
ASSERT_EQ(0, close(dir_fd)); ASSERT_EQ(0, close(dir_fd));
_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); _exit(_metadata->exit_code);
return; return;
} }
......
...@@ -23,62 +23,8 @@ ...@@ -23,62 +23,8 @@
#define __maybe_unused __attribute__((__unused__)) #define __maybe_unused __attribute__((__unused__))
#endif #endif
/* /* TEST_F_FORK() should not be used for new tests. */
* TEST_F_FORK() is useful when a test drop privileges but the corresponding #define TEST_F_FORK(fixture_name, test_name) TEST_F(fixture_name, test_name)
* FIXTURE_TEARDOWN() requires them (e.g. to remove files from a directory
* where write actions are denied). For convenience, FIXTURE_TEARDOWN() is
* also called when the test failed, but not when FIXTURE_SETUP() failed. For
* this to be possible, we must not call abort() but instead exit smoothly
* (hence the step print).
*/
/* clang-format off */
#define TEST_F_FORK(fixture_name, test_name) \
static void fixture_name##_##test_name##_child( \
struct __test_metadata *_metadata, \
FIXTURE_DATA(fixture_name) *self, \
const FIXTURE_VARIANT(fixture_name) *variant); \
TEST_F(fixture_name, test_name) \
{ \
int status; \
const pid_t child = fork(); \
if (child < 0) \
abort(); \
if (child == 0) { \
_metadata->no_print = 1; \
fixture_name##_##test_name##_child(_metadata, self, variant); \
if (_metadata->skip) \
_exit(255); \
if (_metadata->passed) \
_exit(0); \
_exit(_metadata->step); \
} \
if (child != waitpid(child, &status, 0)) \
abort(); \
if (WIFSIGNALED(status) || !WIFEXITED(status)) { \
_metadata->passed = 0; \
_metadata->step = 1; \
return; \
} \
switch (WEXITSTATUS(status)) { \
case 0: \
_metadata->passed = 1; \
break; \
case 255: \
_metadata->passed = 1; \
_metadata->skip = 1; \
break; \
default: \
_metadata->passed = 0; \
_metadata->step = WEXITSTATUS(status); \
break; \
} \
} \
static void fixture_name##_##test_name##_child( \
struct __test_metadata __attribute__((unused)) *_metadata, \
FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
const FIXTURE_VARIANT(fixture_name) \
__attribute__((unused)) *variant)
/* clang-format on */
#ifndef landlock_create_ruleset #ifndef landlock_create_ruleset
static inline int static inline int
......
...@@ -1964,7 +1964,7 @@ static void test_execute(struct __test_metadata *const _metadata, const int err, ...@@ -1964,7 +1964,7 @@ static void test_execute(struct __test_metadata *const _metadata, const int err,
strerror(errno)); strerror(errno));
}; };
ASSERT_EQ(err, errno); ASSERT_EQ(err, errno);
_exit(_metadata->passed ? 2 : 1); _exit(__test_passed(_metadata) ? 2 : 1);
return; return;
} }
ASSERT_EQ(child, waitpid(child, &status, 0)); ASSERT_EQ(child, waitpid(child, &status, 0));
...@@ -3807,7 +3807,7 @@ TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) ...@@ -3807,7 +3807,7 @@ TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes)
ASSERT_EQ(0, close(socket_fds[0])); ASSERT_EQ(0, close(socket_fds[0]));
_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); _exit(_metadata->exit_code);
return; return;
} }
......
...@@ -539,7 +539,7 @@ static void test_bind_and_connect(struct __test_metadata *const _metadata, ...@@ -539,7 +539,7 @@ static void test_bind_and_connect(struct __test_metadata *const _metadata,
} }
EXPECT_EQ(0, close(connect_fd)); EXPECT_EQ(0, close(connect_fd));
_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); _exit(_metadata->exit_code);
return; return;
} }
...@@ -834,7 +834,7 @@ TEST_F(protocol, connect_unspec) ...@@ -834,7 +834,7 @@ TEST_F(protocol, connect_unspec)
} }
EXPECT_EQ(0, close(connect_fd)); EXPECT_EQ(0, close(connect_fd));
_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); _exit(_metadata->exit_code);
return; return;
} }
......
...@@ -314,7 +314,7 @@ TEST_F(hierarchy, trace) ...@@ -314,7 +314,7 @@ TEST_F(hierarchy, trace)
ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
if (variant->domain_both) { if (variant->domain_both) {
create_domain(_metadata); create_domain(_metadata);
if (!_metadata->passed) if (!__test_passed(_metadata))
/* Aborts before forking. */ /* Aborts before forking. */
return; return;
} }
...@@ -375,7 +375,7 @@ TEST_F(hierarchy, trace) ...@@ -375,7 +375,7 @@ TEST_F(hierarchy, trace)
/* Waits for the parent PTRACE_ATTACH test. */ /* Waits for the parent PTRACE_ATTACH test. */
ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); _exit(_metadata->exit_code);
return; return;
} }
...@@ -430,9 +430,10 @@ TEST_F(hierarchy, trace) ...@@ -430,9 +430,10 @@ TEST_F(hierarchy, trace)
/* Signals that the parent PTRACE_ATTACH test is done. */ /* Signals that the parent PTRACE_ATTACH test is done. */
ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
ASSERT_EQ(child, waitpid(child, &status, 0)); ASSERT_EQ(child, waitpid(child, &status, 0));
if (WIFSIGNALED(status) || !WIFEXITED(status) || if (WIFSIGNALED(status) || !WIFEXITED(status) ||
WEXITSTATUS(status) != EXIT_SUCCESS) WEXITSTATUS(status) != EXIT_SUCCESS)
_metadata->passed = 0; _metadata->exit_code = KSFT_FAIL;
} }
TEST_HARNESS_MAIN TEST_HARNESS_MAIN
...@@ -365,9 +365,6 @@ TEST_F(ip_local_port_range, late_bind) ...@@ -365,9 +365,6 @@ TEST_F(ip_local_port_range, late_bind)
__u32 range; __u32 range;
__u16 port; __u16 port;
if (variant->so_protocol == IPPROTO_SCTP)
SKIP(return, "SCTP doesn't support IP_BIND_ADDRESS_NO_PORT");
fd = socket(variant->so_domain, variant->so_type, 0); fd = socket(variant->so_domain, variant->so_type, 0);
ASSERT_GE(fd, 0) TH_LOG("socket failed"); ASSERT_GE(fd, 0) TH_LOG("socket failed");
...@@ -414,6 +411,9 @@ TEST_F(ip_local_port_range, late_bind) ...@@ -414,6 +411,9 @@ TEST_F(ip_local_port_range, late_bind)
ASSERT_TRUE(!err) TH_LOG("close failed"); ASSERT_TRUE(!err) TH_LOG("close failed");
} }
XFAIL_ADD(ip_local_port_range, ip4_stcp, late_bind);
XFAIL_ADD(ip_local_port_range, ip6_stcp, late_bind);
TEST_F(ip_local_port_range, get_port_range) TEST_F(ip_local_port_range, get_port_range)
{ {
__u16 lo, hi; __u16 lo, hi;
......
...@@ -1927,7 +1927,7 @@ TEST_F(tls_err, poll_partial_rec_async) ...@@ -1927,7 +1927,7 @@ TEST_F(tls_err, poll_partial_rec_async)
pfd.events = POLLIN; pfd.events = POLLIN;
EXPECT_EQ(poll(&pfd, 1, 20), 1); EXPECT_EQ(poll(&pfd, 1, 20), 1);
exit(!_metadata->passed); exit(!__test_passed(_metadata));
} }
} }
......
...@@ -1576,7 +1576,7 @@ void start_tracer(struct __test_metadata *_metadata, int fd, pid_t tracee, ...@@ -1576,7 +1576,7 @@ void start_tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
ASSERT_EQ(0, ret); ASSERT_EQ(0, ret);
} }
/* Directly report the status of our test harness results. */ /* Directly report the status of our test harness results. */
syscall(__NR_exit, _metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); syscall(__NR_exit, _metadata->exit_code);
} }
/* Common tracer setup/teardown functions. */ /* Common tracer setup/teardown functions. */
...@@ -1623,7 +1623,7 @@ void teardown_trace_fixture(struct __test_metadata *_metadata, ...@@ -1623,7 +1623,7 @@ void teardown_trace_fixture(struct __test_metadata *_metadata,
ASSERT_EQ(0, kill(tracer, SIGUSR1)); ASSERT_EQ(0, kill(tracer, SIGUSR1));
ASSERT_EQ(tracer, waitpid(tracer, &status, 0)); ASSERT_EQ(tracer, waitpid(tracer, &status, 0));
if (WEXITSTATUS(status)) if (WEXITSTATUS(status))
_metadata->passed = 0; _metadata->exit_code = KSFT_FAIL;
} }
} }
...@@ -3088,8 +3088,7 @@ TEST(syscall_restart) ...@@ -3088,8 +3088,7 @@ TEST(syscall_restart)
} }
/* Directly report the status of our test harness results. */ /* Directly report the status of our test harness results. */
syscall(__NR_exit, _metadata->passed ? EXIT_SUCCESS syscall(__NR_exit, _metadata->exit_code);
: EXIT_FAILURE);
} }
EXPECT_EQ(0, close(pipefd[0])); EXPECT_EQ(0, close(pipefd[0]));
...@@ -3174,7 +3173,7 @@ TEST(syscall_restart) ...@@ -3174,7 +3173,7 @@ TEST(syscall_restart)
ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
if (WIFSIGNALED(status) || WEXITSTATUS(status)) if (WIFSIGNALED(status) || WEXITSTATUS(status))
_metadata->passed = 0; _metadata->exit_code = KSFT_FAIL;
} }
TEST_SIGNAL(filter_flag_log, SIGSYS) TEST_SIGNAL(filter_flag_log, SIGSYS)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment