KTEST_RESULT_PASS(9F) | Kernel Functions for Drivers | KTEST_RESULT_PASS(9F) |
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
#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);
Volatile - This interface is still evolving in illumos. API and ABI stability is not guaranteed.
These functions and macros are used to set the result of a test function.
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.
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:
ktest_result_fail
(), and cause an immediate return
of the test function.There are two variations of these 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);
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
ktest_msg_clear
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.
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); }
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); }
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);
February 15, 2023 | OmniOS |