/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. * * kselftest_harness.h: simple C unit test helper. * * See documentation in Documentation/dev-tools/kselftest.rst * * API inspired by code.google.com/p/googletest
*/
/** * TH_LOG() * * @fmt: format string * @...: optional arguments * * .. code-block:: c * * TH_LOG(format, ...) * * Optional debug logging function available for use in tests. * Logging may be enabled or disabled by defining TH_LOG_ENABLED. * E.g., #define TH_LOG_ENABLED 1 * * If no definition is provided, logging is enabled by default.
*/ #define TH_LOG(fmt, ...) do { \ if (TH_LOG_ENABLED) \
__TH_LOG(fmt, ##__VA_ARGS__); \
} while (0)
/** * SKIP() * * @statement: statement to run after reporting SKIP * @fmt: format string * @...: optional arguments * * .. code-block:: c * * SKIP(statement, fmt, ...); * * This forces a "pass" after reporting why something is being skipped * and runs "statement", which is usually "return" or "goto skip".
*/ #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)
/** * TEST() - Defines the test function and creates the registration * stub * * @test_name: test name * * .. code-block:: c * * TEST(name) { implementation } * * Defines a test by name. * Names must be unique and tests must not be run in parallel. The * implementation containing block is a function and scoping should be treated * as such. Returning early may be performed with a bare "return;" statement. * * EXPECT_* and ASSERT_* are valid in a TEST() { } context.
*/ #define TEST(test_name) __TEST_IMPL(test_name, -1)
/** * TEST_SIGNAL() * * @test_name: test name * @signal: signal number * * .. code-block:: c * * TEST_SIGNAL(name, signal) { implementation } * * Defines a test by name and the expected term signal. * Names must be unique and tests must not be run in parallel. The * implementation containing block is a function and scoping should be treated * as such. Returning early may be performed with a bare "return;" statement. * * EXPECT_* and ASSERT_* are valid in a TEST() { } context.
*/ #define TEST_SIGNAL(test_name, signal) __TEST_IMPL(test_name, signal)
/** * FIXTURE_DATA() - Wraps the struct name so we have one less * argument to pass around * * @datatype_name: datatype name * * .. code-block:: c * * FIXTURE_DATA(datatype_name) * * Almost always, you want just FIXTURE() instead (see below). * This call may be used when the type of the fixture data * is needed. In general, this should not be needed unless * the *self* is being passed to a helper directly.
*/ #define FIXTURE_DATA(datatype_name) struct _test_data_##datatype_name
/** * FIXTURE() - Called once per fixture to setup the data and * register * * @fixture_name: fixture name * * .. code-block:: c * * FIXTURE(fixture_name) { * type property1; * ... * }; * * Defines the data provided to TEST_F()-defined tests as *self*. It should be * populated and cleaned up using FIXTURE_SETUP() and FIXTURE_TEARDOWN().
*/ #define FIXTURE(fixture_name) \
FIXTURE_VARIANT(fixture_name); \ staticstruct __fixture_metadata _##fixture_name##_fixture_object = \
{ .name = #fixture_name, }; \ staticvoid __attribute__((constructor)) \
_register_##fixture_name##_data(void) \
{ \
__register_fixture(&_##fixture_name##_fixture_object); \
} \
FIXTURE_DATA(fixture_name)
/** * FIXTURE_SETUP() - Prepares the setup function for the fixture. * *_metadata* is included so that EXPECT_*, ASSERT_* etc. work correctly. * * @fixture_name: fixture name * * .. code-block:: c * * FIXTURE_SETUP(fixture_name) { implementation } * * Populates the required "setup" function for a fixture. An instance of the * datatype defined with FIXTURE_DATA() will be exposed as *self* for the * implementation. * * ASSERT_* are valid for use in this context and will prempt the execution * of any dependent fixture tests. * * A bare "return;" statement may be used to return early.
*/ #define FIXTURE_SETUP(fixture_name) \ staticvoid fixture_name##_setup( \ struct __test_metadata __attribute__((unused)) *_metadata, \
FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \ const FIXTURE_VARIANT(fixture_name) \
__attribute__((unused)) *variant)
/** * FIXTURE_TEARDOWN() * *_metadata* is included so that EXPECT_*, ASSERT_* etc. work correctly. * * @fixture_name: fixture name * * .. code-block:: c * * FIXTURE_TEARDOWN(fixture_name) { implementation } * * Populates the required "teardown" function for a fixture. An instance of the * datatype defined with FIXTURE_DATA() will be exposed as *self* for the * implementation to clean up. * * A bare "return;" statement may be used to return early.
*/ #define FIXTURE_TEARDOWN(fixture_name) \ staticconstbool fixture_name##_teardown_parent; \
__FIXTURE_TEARDOWN(fixture_name)
/** * FIXTURE_TEARDOWN_PARENT() * *_metadata* is included so that EXPECT_*, ASSERT_* etc. work correctly. * * @fixture_name: fixture name * * .. code-block:: c * * FIXTURE_TEARDOWN_PARENT(fixture_name) { implementation } * * Same as FIXTURE_TEARDOWN() but run this code in a parent process. This * enables the test process to drop its privileges without impacting the * related FIXTURE_TEARDOWN_PARENT() (e.g. to remove files from a directory * where write access was dropped). * * To make it possible for the parent process to use *self*, share (MAP_SHARED) * the fixture data between all forked processes.
*/ #define FIXTURE_TEARDOWN_PARENT(fixture_name) \ staticconstbool fixture_name##_teardown_parent = true; \
__FIXTURE_TEARDOWN(fixture_name)
/** * FIXTURE_VARIANT() - Optionally called once per fixture * to declare fixture variant * * @fixture_name: fixture name * * .. code-block:: c * * FIXTURE_VARIANT(fixture_name) { * type property1; * ... * }; * * Defines type of constant parameters provided to FIXTURE_SETUP(), TEST_F() and * FIXTURE_TEARDOWN as *variant*. Variants allow the same tests to be run with * different arguments.
*/ #define FIXTURE_VARIANT(fixture_name) struct _fixture_variant_##fixture_name
/** * FIXTURE_VARIANT_ADD() - Called once per fixture * variant to setup and register the data * * @fixture_name: fixture name * @variant_name: name of the parameter set * * .. code-block:: c * * FIXTURE_VARIANT_ADD(fixture_name, variant_name) { * .property1 = val1, * ... * }; * * Defines a variant of the test fixture, provided to FIXTURE_SETUP() and * TEST_F() as *variant*. Tests of each fixture will be run once for each * variant.
*/ #define FIXTURE_VARIANT_ADD(fixture_name, variant_name) \ externconst FIXTURE_VARIANT(fixture_name) \
_##fixture_name##_##variant_name##_variant; \ staticstruct __fixture_variant_metadata \
_##fixture_name##_##variant_name##_object = \
{ .name = #variant_name, \
.data = &_##fixture_name##_##variant_name##_variant}; \ staticvoid __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 =
/** * TEST_F() - Emits test registration and helpers for * fixture-based test cases * * @fixture_name: fixture name * @test_name: test name * * .. code-block:: c * * TEST_F(fixture, name) { implementation } * * 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 * datatype exposed for use by the implementation. * * The _metadata object is shared (MAP_SHARED) with all the potential forked * processes, which enables them to use EXCEPT_*() and ASSERT_*(). * * The *self* object is only shared with the potential forked processes if * FIXTURE_TEARDOWN_PARENT() is used instead of FIXTURE_TEARDOWN().
*/ #define TEST_F(fixture_name, test_name) \
__TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT)
/** * TEST_HARNESS_MAIN - Simple wrapper to run the test harness * * .. code-block:: c * * TEST_HARNESS_MAIN * * Use once to append a main() to the test file.
*/ #define TEST_HARNESS_MAIN \ int main(int argc, char **argv) { \ return test_harness_run(argc, argv); \
}
/** * DOC: operators * * Operators for use in TEST() and TEST_F(). * ASSERT_* calls will stop test execution immediately. * EXPECT_* calls will emit a failure warning, note it, and continue.
*/
/* Support an optional handler after and ASSERT_* or EXPECT_*. The approach is * not thread-safe, but it should be fine in most sane test scenarios. * * Using __bail(), which optionally abort()s, is the easiest way to early * return while still providing an optional block to the API consumer.
*/ #define OPTIONAL_HANDLER(_assert) \ for (; _metadata->trigger; _metadata->trigger = \
__bail(_assert, _metadata))
/** * 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) \ staticstruct __test_xfail \
_##fixture_name##_##variant_name##_##test_name##_xfail = \
{ \
.fixture = &_##fixture_name##_fixture_object, \
.variant = &_##fixture_name##_##variant_name##_object, \
}; \ staticvoid __attribute__((constructor)) \
_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); \
}
/* Contains all the information for test execution and status checking. */ struct __test_metadata { constchar *name; void (*fn)(struct __test_metadata *, struct __fixture_variant_metadata *);
pid_t pid; /* pid of test when being run */ struct __fixture_metadata *fixture; void (*teardown_fn)(bool in_parent, struct __test_metadata *_metadata, void *self, constvoid *variant); int termsig; int exit_code; int trigger; /* extra handler after the evaluation */ int timeout; /* seconds to wait for test timeout */ bool aborted; /* stopped test due to failed ASSERT */ bool *no_teardown; /* fixture needs teardown */ void *self; constvoid *variant; struct __test_results *results; struct __test_metadata *prev, *next;
};
/* * Since constructors are called in reverse order, reverse the test * list so tests are run in source declaration order. * https://gcc.gnu.org/onlinedocs/gccint/Initialization.html * However, it seems not all toolchains do this correctly, so use * __constructor_order_foward to detect which direction is called first * and adjust list building logic to get things running in the right * direction.
*/ staticinlinevoid __register_test(struct __test_metadata *t)
{
__LIST_APPEND(t->fixture->tests, t);
}
staticinlineint __bail(int for_realz, struct __test_metadata *t)
{ /* if this is ASSERT, return immediately. */ if (for_realz) { if (t->teardown_fn)
t->teardown_fn(false, t, t->self, t->variant);
abort();
} /* otherwise, end the for loop and continue. */ return 0;
}
staticvoid __wait_for_test(struct __test_metadata *t)
{ /* * Sets status so that WIFEXITED(status) returns true and * WEXITSTATUS(status) returns KSFT_FAIL. This safe default value * should never be evaluated because of the waitpid(2) check and * timeout handling.
*/ 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;
} elseif (ret == 0) {
timed_out = true; /* signal process group */
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);
} elseif (WIFEXITED(status)) { if (WEXITSTATUS(status) == KSFT_SKIP ||
WEXITSTATUS(status) == KSFT_XPASS ||
WEXITSTATUS(status) == KSFT_XFAIL) {
t->exit_code = WEXITSTATUS(status);
} elseif (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)) { /* Success */ case KSFT_PASS:
t->exit_code = KSFT_PASS; break; /* Failure */ default:
t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM, "# %s: Test failed\n",
t->name);
}
}
} elseif (WIFSIGNALED(status)) {
t->exit_code = KSFT_FAIL; if (WTERMSIG(status) == SIGABRT) {
fprintf(TH_LOG_STREAM, "# %s: Test terminated by assertion\n",
t->name);
} elseif (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);
}
}
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);
}
}
staticint test_harness_argv_check(int argc, char **argv)
{ int opt;
while ((opt = getopt(argc, argv, "hlF: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'h': default:
fprintf(stderr, "Usage: %s [-h|-l] [-t|-T|-v|-V|-f|-F|-r name]\n" "\t-h print help\n" "\t-l list all tests\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;
}
}
/* Check if we're expecting this test to fail */ 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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.