KTEST(9) Kernel Concepts KTEST(9)

ktestkernel test facility

The ktest facility provides the means for writing and executing kernel test modules. A kernel test module is a kernel module that contains test functions designed to execute and verify some aspect of the kernel. You can test any aspect of the kernel for which you can create the requisite context, whether that be its local arguments or shared state.

The key design idea behind ktest is to put the test in the same context as the code it's testing: kernel context. By writing tests as kernel functions it allows one to more thoroughly test particular aspects of the kernel. The ktest facility complements user-space testing by allowing targeted testing of the kernel that is not as easily exercised via user-space.

The ktest facility provides a kernel API for creating tests as well as a character device for interacting with the facility from user-space. The latter of which should be done by way of the ktest(8) command.

This page concerns itself with the various kernel-side concepts of the ktest facility and acts as a guide to the various 9F APIs.

A test is uniquely identified by its test triple. The triple is a three-level namespace comprising of the module name, suite name, and test name. Each part of the namespace is separated by the ':' character.

<module>:<suite>:<test>
module
The top of the namespace. If the test module is primarily focused on testing one kernel module, then its module name is typically named after the module-under-test. For example, a test module designed to test all aspects of the kernel module might use "mac" as the module name. This is merely a convention; at the end of the day the module name is simply a string meant to represent the general theme of its underlying tests.
suite
Each module consists of one or more suites. The suite name provides a second level of organization for grouping related tests. For example, your "mac" test module may have several tests for checksum-related routines. In that case it might help to organize all those tests under the "checksum" suite.
test
The name of the test. This can be any name you find descriptive of the test. For tests that focus on a single function you could use the name of the function-under-test with the "_test" suffix attached. For tests that exercise many functions do your best to make the name descriptive. The test functions are typically suffixed with "_test" to visually separate them from test-helper functions; and the test name is often the same as the test function.

For a name to be considered valid it must meet the following criteria.

  1. It must be 63 characters or less (the 64th byte must hold the character).
  2. It must contain only the following ASCII characters: 'A-Z', 'a-z', '0-9', '.', '_'.

The context handle provides the means through which the test communicates with the ktest facility. All test functions must conform to the following prototype.

typedef void (*ktest_fn_t)(ktest_ctx_hdl_t *ctx);

The test interacts with ctx through various ktest routines documented in their requisite sections.

The ultimate goal of a test function is to return a result. See ktest_result_pass(9f) for the complete guide to setting test results.

Some tests require an input stream to test against. The input stream is provided at runtime by the user. The user may run the same test against one or more different input streams.

This is useful when a test applies to many different scenarios with the same general logic. For example, a test that verifies a checksum routine applies to any byte stream; or a test that verifies TCP header parsing applies to any byte stream as well.

Writing a test against an input stream provides several benefits.

  1. It allows one test to verify innumerable scenarios, reducing the need to replicate test logic.
  2. It avoids the need to hard-code test input in the test module itself.
  3. With the ability to change the input stream at runtime it allows testing new scenarios on the spot, avoiding the need for writing a new test.

A test that requires input must specify the flag when calling ktest_add_test(9f). To retrieve the input stream the test uses the ktest_get_input(9f) function.

A test facility that can't test private ( static ) functions is going to be pretty limited. Some of the most useful functions to test are those that act as "helpers" to other, larger functions. These functions are often declared static and not directly accessible to the test module which is built as its own compilation unit.

One solution would be to alter ktest to also allow declaring test functions inside the regular kernel modules themselves, but it currently does not offer that capability. Instead, ktest provides the ktest_get_fn(9f) function for obtaining a handle to private functions. See that page for more detail.

Only registered tests are accessible to the user. See ktest_create_module(9f) for more information.

There are several conventions to follow when creating a new test module.

  1. The test module should be a miscellaneous-type module using the modlmisc(9S) linkage structure.
  2. Test functions are prefixed with their test module name.
  3. Test functions are suffixed with "_test" to distinguish them from test helper functions.
  4. Test registration should happen in _init(9E).
  5. Test deregistration should happen in _fini(9E).

KT_ASSERT(9F), KT_ERROR(9F), KT_FAIL(9F), KT_PASS(9F), KT_SKIP(9F), ktest_add_suite(9F), ktest_add_test(9F), ktest_create_module(9F), ktest_get_fn(9F), ktest_get_input(9F), ktest_hold_mod(9F), ktest_msg_clear(9F), ktest_msg_prepend(9F), ktest_register_module(9F), ktest_release_mod(9F), ktest_unregister_module(9F), modlmisc(9S)

February 17, 2023 OmniOS