| Writing tests for OpenSBI | |
| ========================= | |
| SBIUnit | |
| ------- | |
| SBIUnit is a set of macros and functions which simplify the test development and | |
| automate the test execution and evaluation. All of the SBIUnit definitions are | |
| in the `include/sbi/sbi_unit_test.h` header file, and implementations are | |
| available in `lib/sbi/tests/sbi_unit_test.c`. | |
| Simple SBIUnit test | |
| ------------------- | |
| For instance, we would like to test the following function from | |
| `lib/sbi/sbi_string.c`: | |
| ```c | |
| size_t sbi_strlen(const char *str) | |
| { | |
| unsigned long ret = 0; | |
| while (*str != '\0') { | |
| ret++; | |
| str++; | |
| } | |
| return ret; | |
| } | |
| ``` | |
| which calculates the string length. | |
| Create the file `lib/sbi/tests/sbi_string_test.c` with the following content: | |
| ```c | |
| #include <sbi/sbi_unit_test.h> | |
| #include <sbi/sbi_string.h> | |
| static void strlen_test(struct sbiunit_test_case *test) | |
| { | |
| SBIUNIT_EXPECT_EQ(test, sbi_strlen("Hello"), 5); | |
| SBIUNIT_EXPECT_EQ(test, sbi_strlen("Hell\0o"), 4); | |
| } | |
| static struct sbiunit_test_case string_test_cases[] = { | |
| SBIUNIT_TEST_CASE(strlen_test), | |
| SBIUNIT_END_CASE, | |
| }; | |
| SBIUNIT_TEST_SUITE(string_test_suite, string_test_cases); | |
| ``` | |
| Then, add the corresponding Makefile entries to `lib/sbi/tests/objects.mk`: | |
| ```lang-makefile | |
| ... | |
| carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += string_test_suite | |
| libsbi-objs-$(CONFIG_SBIUNIT) += tests/sbi_string_test.o | |
| ``` | |
| Now, run `make clean` in order to regenerate the carray-related files. | |
| Recompile OpenSBI with the CONFIG_SBIUNIT option enabled and run it in QEMU. | |
| You will see something like this: | |
| ``` | |
| # make PLATFORM=generic run | |
| ... | |
| # Running SBIUNIT tests # | |
| ... | |
| ## Running test suite: string_test_suite | |
| [PASSED] strlen_test | |
| 1 PASSED / 0 FAILED / 1 TOTAL | |
| ``` | |
| Now let's try to change this test in the way that it will fail: | |
| ```c | |
| - SBIUNIT_EXPECT_EQ(test, sbi_strlen("Hello"), 5); | |
| + SBIUNIT_EXPECT_EQ(test, sbi_strlen("Hello"), 100); | |
| ``` | |
| `make all` and `make run` it again: | |
| ``` | |
| ... | |
| # Running SBIUNIT tests # | |
| ... | |
| ## Running test suite: string_test_suite | |
| [SBIUnit] [.../opensbi/lib/sbi/tests/sbi_string_test.c:6]: strlen_test: Condition "(sbi_strlen("Hello")) == (100)" expected to be true! | |
| [FAILED] strlen_test | |
| 0 PASSED / 1 FAILED / 1 TOTAL | |
| ``` | |
| Covering the static functions / using the static definitions | |
| ------------------------------------------------------------ | |
| SBIUnit also allows you to test static functions. In order to do so, simply | |
| include your test source in the file you would like to test. Complementing the | |
| example above, just add this to the `lib/sbi/sbi_string.c` file: | |
| ```c | |
| #ifdef CONFIG_SBIUNIT | |
| #include "tests/sbi_string_test.c" | |
| #endif | |
| ``` | |
| In this case you should only add a new carray entry pointing to the test suite | |
| to `lib/sbi/tests/objects.mk`: | |
| ```lang-makefile | |
| ... | |
| carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += string_test_suite | |
| ``` | |
| You don't have to compile the `sbi_string_test.o` separately, because the | |
| test code will be included into the `sbi_string` object file. | |
| "Mocking" the structures | |
| ------------------------ | |
| See the example of structure "mocking" in `lib/sbi/tests/sbi_console_test.c`, | |
| where the sbi_console_device structure was mocked to be used in various | |
| console-related functions in order to test them. | |
| API Reference | |
| ------------- | |
| All of the `SBIUNIT_EXPECT_*` macros will cause a test case to fail if the | |
| corresponding conditions are not met, however, the execution of a particular | |
| test case will not be stopped. | |
| All of the `SBIUNIT_ASSERT_*` macros will cause a test case to fail and stop | |
| immediately, triggering a panic. |