KTEST_RESULT_PASS(9F) Kernel Functions for Drivers KTEST_RESULT_PASS(9F)
NAME
KT_PASS,
KT_FAIL,
KT_ERROR,
KT_SKIP,
KT_ASSERT,
KT_ASSERT0,
KT_ASSERT3S,
KT_ASSERT3U,
KT_ASSERT3P,
KT_ASSERTG,
KT_ASSERT0G,
KT_ASSERT3SG,
KT_ASSERT3UG,
KT_ASSERT3PG,
ktest_result_pass,
ktest_result_fail,
ktest_result_error,
ktest_result_skip,
ktest_msg_prepend,
ktest_msg_clear - set test result, assert test
conditions, add failure context
SYNOPSIS
#include <sys/ktest.h> void ktest_result_pass(
ktest_ctx_hdl_t *ctx,
int line);
void ktest_result_fail(
ktest_ctx_hdl_t *ctx,
int line,
const char *msg,
...);
void ktest_result_error(
ktest_ctx_hdl_t *ctx,
int line,
const char *msg,
...);
void ktest_result_skip(
ktest_ctx_hdl_t *ctx,
int line,
const char *msg,
...);
void ktest_msg_prepend(
ktest_ctx_hdl_t *ctx,
const char *msg,
...);
void ktest_msg_clear(
ktest_ctx_hdl_t *ctx);
void KT_PASS(
ktest_ctx_hdl_t *ctx);
void KT_FAIL(
ktest_ctx_hdl_t *ctx,
const char *msg,
...);
void KT_ERROR(
ktest_ctx_hdl_t *ctx,
const char *msg,
...);
void KT_SKIP(
ktest_ctx_hdl_t *ctx,
const char *msg,
...);
void KT_ASSERT(
exp,
ktest_ctx_hdl_t *ctx);
void KT_ASSERT0(
exp,
ktest_ctx_hdl_t *ctx);
void KT_ASSERT3S(
int64_t left,
op,
int64_t right,
ktest_ctx_hdl_t *ctx);
void KT_ASSERT3U(
uint64_t left,
op,
uint64_t right,
ktest_ctx_hdl_t *ctx);
void KT_ASSERT3P(
uintptr_t left,
op,
uintptr_t right,
ktest_ctx_hdl_t *ctx);
void KT_ASSERTG(
exp,
ktest_ctx_hdl_t *ctx,
label);
void KT_ASSERT0G(
exp,
ktest_ctx_hdl_t *ctx,
label);
void KT_ASSERT3SG(
int64_t left,
op,
int64_t right,
ktest_ctx_hdl_t *ctx,
label);
void KT_ASSERT3UG(
uint64_t left,
op,
uint64_t right,
ktest_ctx_hdl_t *ctx,
label);
void KT_ASSERT3PG(
uintptr_t left,
op,
uintptr_t right,
ktest_ctx_hdl_t *ctx,
label);
INTERFACE LEVEL
Volatile - This interface is still evolving in illumos. API and ABI
stability is not guaranteed.
PARAMETERS
ctx A handle to the test context. This handle is passed as
argument to the test function by the ktest facility.
exp A test condition expression.
left The left side value of the test condition. This may be
an expression.
right The right side value of the test condition. This may be
an expression.
op The operator used to compare the
left and
right side
values.
label The source code label to jump to if the test condition is
false.
line The source line number where the result is set. This
should always be the
__LINE__ macro.
msg A message giving additional context on why a test did not
pass.
DESCRIPTION
These functions and macros are used to set the result of a test
function.
Result Macros
These are convenience macros for setting the test result, providing an
alternative to the verbose result functions. In general, you should
only need to use the
KT_PASS() and
KT_SKIP() macros. For most test
assertions it's more convenient to use the "KTest ASSERT Macros"
described below. These macros do not cause a
return.
KT_PASS()
Set a passing result.
KT_FAIL()
Set a failure result along with the failure message.
KT_ERROR()
Set an error result along with the error message.
KT_SKIP()
Set a skip result along with the skip message.
KTest ASSERT Macros
These macros evaluate their test condition expression and verify it's
true. They take care of building a failure message based on the
expression and calling the
ktest_result_fail() function with the
appropriate line number. They provide a convenient way to express test
conditions and automatically build failure messages on the caller's
behalf. They are essentially the same as the traditional
ASSERT3 family of macros with three exceptions:
1. They all require the additional
ctx argument in order to set the
failure result when the assert trips.
2. They do not panic but instead build a failure message, call
ktest_result_fail(), and cause an immediate return of the test
function.
3. The "goto" variations of these macros provide the ability to
cleanup test state instead of returning immediately.
There are two variations of these macros.
KT_ASSERT* Essentially the same as the traditional
ASSERT3 family of
macros, with the exception that they all take the
ctx as an
additional argument. This assert returns from the test
function.
KT_ASSERT*G Assert the condition or
goto label.
KTest Error ASSERT Macros
These macros are the same as the
KT_ASSERT*() macros with the only
exception being that they call the
ktest_result_error() function to
indicate an error condition. These macros use the same names as the
KT_ASSERT*() macros but prefixed with the character 'E', like so:
KT_EASSERT*(). This is a convenience for checking conditions which are
indicative of a test error rather than failure. For example, for most
tests a failure to acquire memory is considered an error, not a test
failure. In that case one could use the following assert to raise a
test error.
mblk_t *mp = allocb(len, 0);
KT_EASSERT(mp != NULL, ctx);
Additional Message Context
Sometimes the failure message generated by the
KT_ASSERT*() macros is
not enough. You may find the need to prepend additional information to
the message to disambiguate the reason for failure. For example, you
might find yourself asserting an invariant against an array of values
and in order to disambiguate the failure you need to know the index of
the value which tripped the assert. The
ktest_msg_prepend() function
provides this ability.
ktest_msg_prepend Append the given format string to the failure message. This
overwrites the prepended string of any previous call making it
more convenient to use in a
for loop.
ktest_msg_clear Clear the prepend buffer. This is equivalent to
ktest_msg_prepend("").
Multiple Results
Given the nature of ktest's design it is trivial to accidentally write
a test that can produce multiple results for a given execution of its
code. For example, placing the
KT_PASS call in a cleanup label would
overwrite a failure result with a pass result. This is the unavoidable
nature of ktest's implementation: unlike typical testing frameworks we
are executing in kernel context and it would be annoying if test
failure was reported by way of host panic.
To avoid incorrect test results the ktest facility itself checks for
this scenario. For each call to the result API, ktest first checks if
a result has already been set. If no result is present, then it stores
the result along with its line number. However, if a result already
exists, it generates an error result describing the line number of the
overriding result along with the line number of the original result.
EXAMPLES
Test Without Cleanup
This example shows the basic skeleton of a contrived test for a
fictional
object_t type. As there is no allocation or resource
acquisition, there is no need for cleanup.
void
no_cleanup_test(ktest_ctx_hdl_t *ctx)
{
object_t obj;
obj.obj_value = 7777;
obj.obj_state = OBJ_STATE_FIRST;
if (!check_for_condition_x()) {
KT_SKIP(ctx, "condition X was not met");
return;
}
KT_ASSERT3U(obj.obj_state, ==, OBJ_STATE_FIRST, ctx);
next_state(&obj);
KT_ASSERT3U(obj.obj_state, ==, OBJ_STATE_SECOND, ctx);
<... more obj manipulation and assertions ...>
KT_PASS(ctx);
}
Test With Cleanup
It's more likely that your test will require some amount of allocation
and thus will need to make use of the
KT_ASSERTG*() macros. In this
scenario
KT_PASS() must come before the
cleanup label. Calling it
after the
cleanup label produces a multiple-result bug when one of the
assertions trips. The ktest facility automatically catches this type
of bug as explained in the "Multiple Results" section.
void
test_with_cleanup(ktest_ctx_hdl_t *ctx)
{
mblk_t *mp = allocb(74, 0);
/*
* Failure to allocate is an error, not a test failure.
*/
KT_EASSERT(mp != NULL, ctx);
/*
* If any of these assertions trips, a failure result is set
* and execution jumps to the 'cleanup' label.
*/
KT_ASSERT3UG(msgsize(mp), ==, 0, ctx, cleanup);
KT_ASSERT3PG(mp->b_rptr, ==, mp->b_wptr, ctx, cleanup);
KT_ASSERT3PG(mp->b_next, ==, NULL, ctx, cleanup);
KT_ASSERT3PG(mp->b_cont, ==, NULL, ctx, cleanup);
/*
* All assertions passed; mark the test a success and let
* execution fall into the 'cleanup' label.
*/
KT_PASS(ctx);
cleanup:
freeb(mp);
}
Additional Failure Context
This example shows how to prepend additional context to the failure
message. The
ktest_msg_clear() call after the loop is important;
otherwise any subsequent assert failure would pick up the prepended
message from the last iteration of the loop.
for (uint_t i = 0; i < num_objs; i++) {
obj_t *obj = &objs[i];
ktest_msg_prepend(ctx, "objs[%d]: ", i);
KT_ASSERT3U(obj->o_state, ==, EXPECTED_STATE, ctx);
}
ktest_msg_clear(ctx);
illumos February 15, 2023 illumos