feat(unittests): add a unit test framework.
This patch adds all the infrastructure needed to run unit tests for
RMM, including a new variant for platform host, called `host_test`.
To build and run the tests:
cmake -DRMM_CONFIG=host_defcfg -DHOST_VARIANT=host_test \
-DCMAKE_BUILD_TYPE=Debug -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
cmake --build ${RMM_BUILD_DIR} -- run-unittests
Signed-off-by: Javier Almansa Sobrino <javier.almansasobrino@arm.com>
Signed-off-by: Soby Mathew <soby.mathew@arm.com>
Change-Id: If16686e111d91c563f8e7281d4ee7ca2864125ae
diff --git a/cmake/BuildCppUTest.cmake b/cmake/BuildCppUTest.cmake
new file mode 100644
index 0000000..df87e3c
--- /dev/null
+++ b/cmake/BuildCppUTest.cmake
@@ -0,0 +1,17 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# Remove Werror from CXXFLAGS else CppUTest compiler checks will fail.
+# This will affect CMAKE_CXX_FLAG in the current scope and parent scope
+# is unaffected.
+#
+string(REPLACE "-Werror" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
+
+# Additional CXXFLAGS to get CppUTest to compile.
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++98-compat-pedantic ")
+
+add_subdirectory("ext/cpputest")
+
diff --git a/cmake/CommonConfigs.cmake b/cmake/CommonConfigs.cmake
index 96dc650..6f743ca 100644
--- a/cmake/CommonConfigs.cmake
+++ b/cmake/CommonConfigs.cmake
@@ -51,7 +51,7 @@
endif()
target_compile_definitions(rmm-common
- INTERFACE "MAX_CPUS=U(${MAX_CPUS})")
+ INTERFACE "MAX_CPUS=${MAX_CPUS}U")
if(NOT(GRANULE_SIZE EQUAL 4096))
message(FATAL_ERROR "GRANULE_SIZE is not initialized correctly")
diff --git a/cmake/Modules/UnitTestFramework.cmake b/cmake/Modules/UnitTestFramework.cmake
new file mode 100644
index 0000000..758bce9
--- /dev/null
+++ b/cmake/Modules/UnitTestFramework.cmake
@@ -0,0 +1,138 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#[=======================================================================[.rst:
+rmm_build_unittest
+------------------
+
+.. default-domain:: unit tests
+
+.. command:: rmm_build_unittest
+
+Build a unit test group for a given target
+
+.. code:: cmake
+
+ rmm_build_unittest(NAME <name> TARGET <target> SOURCES <sources>
+ [RUN_ISOLATED_TESTS <LIST of tests to run>]
+ [LIBRARIES <libraries_to_link>]
+ [ITERATIONS <iterations>])
+
+This helper function simplifies the mechanics to setup and enable an unit test.
+
+Basics
+^^^^^^
+
+Every unit test configuration has the following parameters (defined as
+strings):
+
+- ``NAME`` Name of the test. It must match the name of the CppUtest test group.
+- ``TARGET`` Target where the tests will be linked against.
+- ``SOURCES`` Source files for the tests. This is usually a single C++ file.
+- ``RUN_ISOLATED_TESTS`` Optional parameter that specifies a list of tests
+ implemented within ``SOURCES`` to be run. When this
+ list is specified, the binary is re-spawned for each
+ test and only executed once (``ITERATIONS`` is
+ ignored). Any test not included on the list will be
+ ignored.
+ If this parameter is not used, all the tests included
+ in the group will be run automatically by CppUTest,
+ the number of times specified by ``ITERATIONS``
+- ``LIBRARIES`` Optional parameter to define libraries to link against
+ the tests.
+- ``ITERATIONS`` Optional parameter that defines how many times the test will
+ run. By default it is 1 times.
+ This option is ignored when using ``RUN_ISOLATED_TESTS``
+
+#]=======================================================================]
+
+if(RMM_UNITTESTS)
+ include("${CMAKE_SOURCE_DIR}/cmake/BuildCppUTest.cmake")
+
+ # Clean ${IMPORT_TEST_GROUPS}, used to generate test_groups.h later.
+ SET(IMPORT_TEST_GROUPS "" CACHE INTERNAL "IMPORT_TEST_GROUP List")
+
+ # Generate an empty test_groups.h, needed if we don't have unittests
+ configure_file(${CMAKE_SOURCE_DIR}/plat/host/host_test/src/test_groups.h.in
+ ${CMAKE_BINARY_DIR}/plat/host/host_test/src/test_groups.h
+ @ONLY)
+
+ # Include CTest for unittests
+ include(CTest)
+
+ # Custom target to run the unit tests
+ add_custom_target(run-unittests
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ COMMAND ctest -C "$<CONFIG>"
+ DEPENDS rmm.elf rmm.map
+ )
+endif()
+
+function(rmm_build_unittest)
+ if(RMM_UNITTESTS)
+ set(_options "")
+ set(_multi_args "SOURCES;LIBRARIES;RUN_ISOLATED_TESTS")
+ set(_single_args "NAME;TARGET;ITERATIONS")
+
+ cmake_parse_arguments(
+ arg "${_options}" "${_single_args}" "${_multi_args}" ${ARGN})
+
+ if("NAME" IN_LIST arg_KEYWORDS_MISSING_VALUES OR
+ NOT DEFINED arg_NAME)
+ message(FATAL_ERROR "Missing unit test name")
+ endif()
+
+ if("TARGET" IN_LIST arg_KEYWORDS_MISSING_VALUES OR
+ NOT DEFINED arg_TARGET)
+ message(FATAL_ERROR "Missing test target")
+ endif()
+
+ if("SOURCES" IN_LIST arg_KEYWORDS_MISSING_VALUES OR
+ NOT DEFINED arg_SOURCES)
+ message(FATAL_ERROR "Missing test sources")
+ endif()
+
+ if("ITERATIONS" IN_LIST arg_KEYWORDS_MISSING_VALUES OR
+ NOT DEFINED arg_ITERATIONS)
+ set(arg_ITERATIONS "1")
+ endif()
+
+ target_sources("${arg_TARGET}"
+ PRIVATE ${arg_SOURCES})
+
+ target_link_libraries("${arg_TARGET}"
+ PRIVATE CppUTest ${arg_LIBRARIES})
+
+ # Add the test to the CMake test builder, so we can automate
+ # the test run process.
+ if("RUN_ISOLATED_TESTS" IN_LIST arg_KEYWORDS_MISSING_VALUES OR
+ NOT DEFINED arg_RUN_ISOLATED_TESTS)
+ # Run all tests at once
+ add_test(NAME "${arg_NAME}"
+ COMMAND ${CMAKE_BINARY_DIR}/rmm.elf
+ -g${arg_NAME}
+ -r${arg_ITERATIONS})
+ else()
+ # Register a test for each test case, so each one on them can
+ # run on isolation.
+ foreach(TEST IN LISTS arg_RUN_ISOLATED_TESTS)
+ add_test(NAME "${arg_NAME}::${TEST}"
+ COMMAND ${CMAKE_BINARY_DIR}/rmm.elf
+ -sg${arg_NAME}
+ -sn${TEST})
+ endforeach()
+ endif()
+
+ # Use CppUtest IMPORT_TEST_GROUP macro to explicitly include the new test
+ # group. This is needed as otherwise the linker will ignore the test code.
+ SET(IMPORT_TEST_GROUPS "${IMPORT_TEST_GROUPS} IMPORT_TEST_GROUP(${arg_NAME});"
+ CACHE INTERNAL "IMPORT_TEST_GROUP List")
+
+ # Generate the test_groups.h
+ configure_file(${CMAKE_SOURCE_DIR}/plat/host/host_test/src/test_groups.h.in
+ ${CMAKE_BINARY_DIR}/plat/host/host_test/src/test_groups.h
+ @ONLY)
+ endif()
+endfunction()