Testing support

CantripOS testing support is a work in progress. Tests break down into the following areas:

  • unit tests: Rust cargo unit tests
  • sel4test: seL4's sel4test framework
  • shell tests: tests built into the CantripOS shell
  • application tests: test applications (mostly to exercise the SDK)
  • robot tests: automated tests that leverage the CantripOS shell

Unit tests

Unit tests excercise functional interfaces with tests that run on the build system (aka the “host”). These are all cargo-based and meant to be fast enough to run as part of a pre-submit process. The available tests can be found with the hmm command or using tab completion:

$ hmm cargo_test_<TAB>
cargo_test_cantrip                           cargo_test_cantrip_os_common_logger          cargo_test_cantrip_os_common_slot_allocator
cargo_test_cantrip_proc_interface            cargo_test_cantrip_proc_manager              cargo_test_debugconsole_zmodem
$ m cargo_test_cantrip
   ...
   Compiling memchr v2.5.0
   ...

running 5 tests
test tests::test_each_log_level_works ... ok
test tests::test_embedded_nul ... ok
test tests::test_formatting ... ok
test tests::test_max_log_level ... ok
test tests::test_too_long ... ok

test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
...

The main impediment to unit tests is structuring code so that platform-independent code can be exercised in an isolated/host environment. Expect the set of unit tests to grow as more code is structured with unit testing in mind.

sel4test

sel4test is the seL4 kernel test facility that substitutes a test harness for the usual rootserver and then automatically runs a suite of tests that exercises system call api's and checks operational correctness. This facility is supported with two make targets:

$ hmm sel4test

sel4test: (defined in build/platforms/shodan/sim_sel4test.mk)
 C-based libsel4 syscall api wrappers. The result is run under Renode.

$ hmm sel4test+wrapper

sel4test+wrapper: (defined in build/platforms/shodan/sim_sel4test.mk)
 crate wrapped with C shims. The result is run under Renode.

The first command runs the upstream seL4 test mechanism unchanged; this mostly verifies the CantripOS kernel (which follows upstream seL4 but has some non-trivial changes). The second command runs the upstream test mechanism but using the Rust sel4-sys crate with wrappers around the Rust implementations for use by C code; this is mostly intended to exercise sel4-sys.

Note the sel4test target uses a debug build; this is consistent with how upstream works. The sel4test+wrapper target however uses a release build of the user mode pieces to reduce the space overhead of the Rust wrappers.

Not all target platforms may support the above make targets.

Shell tests

Shell tests refers to builtin commands in the DebugConsole that exercise parts of the system. By convention these have a “test_” prefix; e.g.

CANTRIP> ?
...
test_alloc
test_alloc_error
test_mailbox
test_malloc
test_mfree
test_mlcancel
test_mlexecute
test_mlperiodic
test_obj_alloc
test_panic
test_timer_async
test_timer_blocking
test_timer_completed
CANTRIP> test_obj_alloc
32 bytes in-use, 63639264 bytes free, 32 bytes requested, 3342336 overhead
2 objs in-use, 2 objs requested
32 bytes in-use, 63639264 bytes free, 13648 bytes requested, 3342336 overhead
2 objs in-use, 11 objs requested
Batch alloc ok: ObjDescBundle { cnode: 43, depth: 7, objs: [ObjDesc { type_: seL4_TCBObject, count: 1, cptr: 0 }, ObjDesc { type_: seL4_EndpointObject, count: 2, cptr: 1 }, ObjDesc { type_: seL4_ReplyObject, count: 2, cptr: 3 }, ObjDesc { type_: seL4_SchedContextObject, count: 8, cptr: 5 }, ObjDesc { type_: seL4_RISCV_4K_Page, count: 10, cptr: 6 }] }
cantrip_object_alloc_in_cnode ok: ObjDescBundle { cnode: 43, depth: 5, objs: [ObjDesc { type_: seL4_TCBObject, count: 1, cptr: 0 }, ObjDesc { type_: seL4_EndpointObject, count: 1, cptr: 1 }, ObjDesc { type_: seL4_ReplyObject, count: 1, cptr: 2 }, ObjDesc { type_: seL4_SchedContextObject, count: 8, cptr: 3 }, ObjDesc { type_: seL4_RISCV_4K_Page, count: 2, cptr: 4 }] }
All tests passed!

Shell commands bloat a system image so are conditionally compiled in (in particular release builds do not include any test commands). Check DebugConsole/cantrip-shell/Cargo.toml for features named “TEST_*”. These control the set of test commands, some of which are platform-dependent. Beware that some builtin tests may generate assertions that will kill the console shell; e.g.

CANTRIP> test_panic
panic::panicked at 'testing', cantrip-shell/src/test_panic.rs:34:5

Application tests

There are several applications designed to exercise/test the SDKRuntime. These are typically included in the builtins archive baked into a system image. For example,

CANTRIP> builtins
fibonacci.app 30852
hello.app 580
keyval.app 31040
logtest.app 26100
mltest.app 29832
mobilenet_v1_emitc_static.model 1001090
panic.app 24812
suicide.app 551
timer.app 3121
CANTRIP> install logtest.app
Collected 26100 bytes of data, crc32 8673c4b7
Application "logtest" installed
CANTRIP> start logtest
Bundle "logtest" started.
CANTRIP> [logtest]::logtest::ping!
[logtest]::DONE
stop logtest
Bundle "logtest" stopped.
CANTRIP> uninstall logtest
Bundle "logtest" uninstalled.

Unlike a shell builtin an application that dies can just be stopped and unloaded. Note multiple applications can be run simultaneously (depending on available resources) to exercise concurrent use of the SDKRuntime. For example,

CANTRIP> install timer.app
Collected 31212 bytes of data, crc32 8d3381c0
Application "timer" installed
CANTRIP> start fibonacci
Bundle "fibonacci" started.
CANTRIP> [fibonacci]::fibonacci::[ 0]                    0  0
[fibonacci]::fibonacci::[ 1]                    1  100
[fibonacci]::fibonacci::[ 2]                    1  200
s[fibonacci]::fibonacci::[ 3]                    2  300
t[fibonacci]::fibonacci::[ 4]                    3  400
ar[fibonacci]::fibonacci::[ 5]                    5  500
t [fibonacci]::fibonacci::[ 6]                    8  600
tim[fibonacci]::fibonacci::[ 7]                   13  700
er[fibonacci]::fibonacci::[ 8]                   21  800

[fibonacci]::fibonacci::[ 9]                   34  900
...
Bundle "timer" started.
CANTRIP> [fibonacci]::fibonacci::[26]               121393  2600
[timer]::timer::sdk_timer_cancel returned Err(SDKInvalidTimer) with nothing running
[timer]::timer::sdk_timer_poll returned Ok(0) with nothing running
[timer]::timer::sdk_timer_oneshot returned Err(SDKNoSuchTimer) with an invalid timer id
[timer]::timer::Timer 0 started
[fibonacci]::fibonacci::[27]               196418  2700
[fibonacci]::fibonacci::[28]               317811  2800
[timer]::timer::Timer 0 completed
[timer]::timer::Timer 1 started
[fibonacci]::fibonacci::[29]               514229  2900

By default, platforms without an interactive shell include an autostart.repl script in their builtins bundle that runs the available applications. Systems that have an interactive command line have a builtins.repl file that does the same thing and can be run with the “source” command; e.g.

CANTRIP> builtins
builtins.repl 642
...
CANTRIP> source builtins.repl
CANTRIP> install hello.app
Collected 640 bytes of data, crc32 877a95c1
Application "hello" installed
...

Robot tests

Robot tests refer to system-level tests that treat the system as a black box. Typically these are automated and used for regression testing. The sel4test mechanism can be used for this purpose as can many of the shell and application tests described above. The scripts we use with Renode's Robot framework are included in the sim/tests directory.

Next Section: Memory Footprint