This guide assumes that you have the proper setup and that you know how to build firmware. If this is not correct, follow the instructions at Build toolchains and SOF from sources first.
Cmocka is fetched and built automatically. For a successful compilation, it needs a toolchain thats supports C stdlib.
Configuring for unit tests¶
Unit tests are built from the same, top-level CMakeLists.txt as the firmware but with different CMake flags: -DBUILD_UNIT_TESTS=ON and a couple others.
Building unit tests can be more complex than building the firmware
because for the firmware the script
./xtensa-build-all.sh hides most
the CMake configuration. For unit tests you must find a working
combination of environment variables and CMake flags. Fortunately
./xtensa-build-all.sh logs some of its magic that you can “steal”
and re-use to build unit tests. Like this:
XTENSA_TOOLS_ROOTas you normally do when building the firmware.
Build the firmware using
./xtensa-build-all.shand take note of the following variables in the build log:
XTENSA_SYSTEMvalues found above.
Run cmake with
-DINIT_CONFIGand a new build directory
Build and run the tests with
Use -DTOOLCHAIN=xt option.
As of December 2021, -DTOOLCHAIN=xtensa-<platform_type>-elf is not supported. You can use a native toolchain, see below.
If you get this double
uintptr_t definition error:
[ 2%] Building C object test/cmocka/CMakeFiles/common_mock.dir/src/common_mocks.c.o In file included from sof/test/cmocka/src/common_mocks.c:29: sof/but/cmocka_git/src/cmocka_git/include/cmocka.h:132: error: redefinition of typedef ‘uintptr_t’ xcc/install/builds/RG-2017.8-linux/X4H3I16w2D48w3a_2017_8/xtensa-elf/include/stdint.h:252: error: previous declaration of ‘uintptr_t’ was here
… then append this to your cmake invocation:
Additional unit tests options can be found in CMake Arguments.
Example: Running tests for APL¶
mkdir build_ut && cd build_ut cmake -DBUILD_UNIT_TESTS=ON -DTOOLCHAIN=xt -DINIT_CONFIG=apollolake_defconfig \ -DROOT_DIR=/xcc/install/builds/RG-2017.8-linux/X4H3I16w2D48w3a_2017_8/xtensa-elf .. make -j4 && ctest -j8
Compiling unit tests without a cross-compilation toolchain¶
You can also compile and run unit tests with your native compiler:
rm -rf build_ut/ cmake -B build_ut/ -DBUILD_UNIT_TESTS_HOST=yes \ -DBUILD_UNIT_TESTS=ON -DINIT_CONFIG=something_defconfig make -C build_ut/ -j8 && make -C build_ut/ test
scripts/run-cmocks.sh script does all that and can also run unit
tests with valgrind.
Wrapping objects for unit tests¶
If you need to mock a symbol, define it in a unit test and include the .h file. There are two cases where this isn’t possible:
Static functions in headers (those most probably are inline short functions and don’t have to be mocked).
Static functions that are in the same file as tested functionality and are exceedingly large so they can’t be tested as one functionality.
Whatever the reason, mocking of those symbols can be done by using the –wrap linker functionality. To wrap the symbol follow these steps:
Create mocked symbol named __wrap_symbol_name
Pass instruction for the linker -Wl, –wrap=symbol_name during compilation.
Now every symbol calls to symbol_name will call __wrap_symbol_name.
Instructions can be passed to the linker in the SOF UT environment using CFLAGS; however, they should be passed in separate variables in the makefile.
# some tests before ... cmocka_test(pipeline_connect_upstream pipeline_connect_upstream.c ... ) target_link_libraries(pipeline_connect_upstream PRIVATE "-Wl,--wrap=symbol_name")
Full information about wrapping can be found here:
Use the ctest -j option while running tests that use xt-run (to speed up tests significantly) by running multiple instances of the xt-run simulator (it also speeds up the build if you have many unit tests).
ctest only runs unit tests; to rebuild them, you have to explicitly run make.