feat(tests): Add support for coverage report
This patch enables support for coverage report on the
unit tests by using the gcovr tool.
The coverage report is stored in ${RMM_BUILD_DIR}/$<CONFIG>/coverage
Signed-off-by: Javier Almansa Sobrino <javier.almansasobrino@arm.com>
Change-Id: I5b6a2f3ebf13cbc036c0d64f422ea18f337dd8f5
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a793590..074b80c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -61,12 +61,18 @@
if(RMM_STATIC_ANALYSIS_CPPCHECK)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
endif()
+
#
# Include the platform makefile
#
include("cmake/Platforms.cmake")
#
+# Include Coverage report framework
+#
+include("cmake/CoverageReport.cmake")
+
+#
# Include the Unit Test Framework
#
include(UnitTestFramework)
diff --git a/cmake/CoverageReport.cmake b/cmake/CoverageReport.cmake
new file mode 100644
index 0000000..67c901d
--- /dev/null
+++ b/cmake/CoverageReport.cmake
@@ -0,0 +1,151 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+arm_config_option(
+ NAME RMM_COVERAGE
+ HELP "Enable coverage tests"
+ TYPE BOOL
+ DEFAULT "OFF")
+
+arm_config_option(
+ NAME RMM_HTML_COV_REPORT
+ HELP "Enable html coverage report"
+ TYPE BOOL
+ DEPENDS RMM_COVERAGE
+ DEFAULT "ON")
+
+arm_config_option(
+ NAME COVERAGE_REPORT_NAME
+ HELP "Canonical name for the coverage report"
+ TYPE STRING
+ DEPENDS RMM_COVERAGE
+ DEFAULT "tf-rmm-coverage"
+ ADVANCED)
+
+macro(check_and_prepare_c_coverage_flags)
+ # Store a copy of CMAKE_C_FLAGS
+ set(CMAKE_C_FLAGS_BACKUP "${CMAKE_C_FLAGS}")
+
+ foreach(flag ${ARGN})
+ string(REPLACE "-" "_" flag_no_hyphen ${flag})
+ check_c_compiler_flag("-${flag}" COVERAGE_C_FLAG_${flag_no_hyphen})
+ if (COVERAGE_C_FLAG_${flag_no_hyphen})
+ # Some of the coverage flags depend on the previous ones being
+ # enabled, so add them to the C Flags now for the next check.
+ string(APPEND CMAKE_C_FLAGS " -${flag}")
+ string(APPEND COVERAGE_C_FLAGS " -${flag}")
+ else()
+ set(COVERAGE_SUPPORTED "FALSE")
+ endif()
+ endforeach()
+
+ # Restore CMAKE_C_FLAGS
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_BACKUP}")
+endmacro(check_and_prepare_c_coverage_flags)
+
+macro(check_and_prepare_cxx_coverage_flags)
+ # Store a copy of CMAKE_CXX_FLAGS
+ set(CMAKE_CXX_FLAGS_BACKUP "${CMAKE_CXX_FLAGS}")
+
+ foreach(flag ${ARGN})
+ string(REPLACE "-" "_" flag_no_hyphen ${flag})
+ check_cxx_compiler_flag("-${flag}" COVERAGE_CXX_FLAG_${flag_no_hyphen})
+ if (COVERAGE_CXX_FLAG_${flag_no_hyphen})
+ # Some of the coverage flags depend on the previous ones being
+ # enabled, so add them to the CXX Flags now for the next check.
+ string(APPEND CMAKE_CXX_FLAGS " -${flag}")
+ string(APPEND COVERAGE_CXX_FLAGS " -${flag}")
+ endif()
+ endforeach()
+
+ # Restore CMAKE_CXX_FLAGS
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_BACKUP}")
+endmacro(check_and_prepare_cxx_coverage_flags)
+
+if(RMM_COVERAGE)
+
+ find_program(GCOVR_EXECUTABLE "gcovr" DOC "Path to gcovr")
+
+ if(${GCOVR_EXECUTABLE} STREQUAL "GCOVR_EXECUTABLE-NOTFOUND")
+ message (WARNING "gcovr executable not found. Coverage tests disabled")
+ return()
+ endif()
+
+ include(CheckCCompilerFlag)
+ include(CheckCXXCompilerFlag)
+
+ if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ # Using LLVM. Select the coverage flags to test
+ set(COVERAGE_FLAGS
+ ftest-coverage
+ fprofile-instr-generate
+ fprofile-arcs
+ fcoverage-mapping)
+
+ # Setup the right coverage tool if using llvm
+ set(GCOVR_EXE_OPTION --gcov-executable "llvm-cov gcov"
+ CACHE INTERNAL "GCOV_EXECUTABLE")
+
+ elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+ # Using GNU. Select the coverage flags to test
+ set(COVERAGE_FLAGS
+ -coverage)
+
+ # Flags needed to enable coverage testing
+ string(APPEND CMAKE_EXE_LINKER_FLAGS " -coverage -lgcov ")
+ else()
+ message(WARNING "Toolchain ${CMAKE_C_COMPILER_ID} does not support coverage")
+ return()
+ endif()
+
+ set(COVERAGE_SUPPORTED "TRUE")
+ check_and_prepare_c_coverage_flags(${COVERAGE_FLAGS})
+ check_and_prepare_cxx_coverage_flags(${COVERAGE_FLAGS})
+
+ # If coverage is not supported by the C compiler, or the C and C++
+ # compilers do not support the same set of coverage flags (which can
+ # lead to link problems), abort.
+ if ((COVERAGE_SUPPORTED STREQUAL "FALSE") OR
+ (NOT (COVERAGE_C_FLAGS STREQUAL COVERAGE_CXX_FLAGS)))
+ message (WARNING "Toolchain ${CMAKE_C_COMPILER_ID} does not support coverage")
+ return()
+ endif()
+
+ # Setup flags for coverage
+ foreach(language in ITEMS C CXX)
+ string(APPEND CMAKE_${language}_FLAGS
+ " ${COVERAGE_${language}_FLAGS} ")
+ endforeach()
+
+ # Directory where to store the results
+ set(COVERAGE_DIRECTORY
+ "${CMAKE_BINARY_DIR}/$<CONFIG>/coverage")
+
+ set(COVERAGE_OUTPUT "${COVERAGE_DIRECTORY}/${COVERAGE_REPORT_NAME}")
+
+ if(RMM_HTML_COV_REPORT)
+ set(HTML_REPORT --html-details ${COVERAGE_OUTPUT}.html)
+ endif()
+
+ #
+ # Rules for coverage report generation
+ #
+ add_custom_target(run-coverage
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${COVERAGE_DIRECTORY}"
+ COMMAND ${GCOVR_EXECUTABLE}
+ ${GCOVR_EXE_OPTION}
+ --exclude "'((.+)ext(.+))|((.+)CMakeFiles(.+)\..)|((.+)\.cpp)|((.+)test(.+))'"
+ -r ${CMAKE_SOURCE_DIR}
+ -x ${COVERAGE_OUTPUT}.xml
+ ${HTML_REPORT}
+ ${CMAKE_BINARY_DIR})
+ #
+ # Add dependency on unit test target if being invoked in a
+ # multi-target build command line.
+ #
+ if(RMM_UNITTESTS)
+ add_dependencies(run-coverage run-unittests)
+ endif()
+endif() # RMM_COVERAGE
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index d41a145..0db4cdc 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -162,7 +162,7 @@
cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
cmake --build ${RMM_BUILD_DIR} -- checkincludes-codebase
-14. Perform unit tests on development host:
+14. Perform unit tests on development host:
Build and run unit tests on host platform. It is recommended to do the Debug
build of RMM.
@@ -172,6 +172,17 @@
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
+Run coverage analysis on unit tests.
+
+.. code-block:: bash
+
+ cmake -DRMM_CONFIG=host_defcfg -DHOST_VARIANT=host_test -DRMM_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+ cmake --build ${RMM_BUILD_DIR} -- run-coverage
+
+The above commands will automatically generate the HTML coverage report in folder
+`build/Debug/coverage` within build directory. The HTML generation can be
+disabled by setting `RMM_HTML_COV_REPORT=OFF`.
+
.. _build_options_table:
###################
@@ -208,9 +219,9 @@
MBEDTLS_ECP_MAX_OPS ,248 - ,1000 ,"Number of max operations per ECC signing iteration"
RMM_FPU_USE_AT_REL2 ,ON | OFF ,OFF(fake_host) ON(aarch64),"Enable FPU/SIMD usage in RMM."
RMM_MAX_GRANULES , ,0 ,"Maximum number of memory granules available to the system"
- HOST_VARIANT ,host_build | host_test ,host_build , "Variant to build for the host platform. Only available when RMM_PLATFORM=host"
-
-
+ HOST_VARIANT ,host_build | host_test ,host_build ,"Variant to build for the host platform. Only available when RMM_PLATFORM=host"
+ RMM_COVERAGE ,ON | OFF ,OFF ,"Enable coverage analysis"
+ RMM_HTML_COV_REPORT ,ON | OFF ,ON ,"Enable HTML output report for coverage analysis"
.. _llvm_build:
diff --git a/docs/getting_started/getting-started.rst b/docs/getting_started/getting-started.rst
index 4e8e07b..88747c9 100644
--- a/docs/getting_started/getting-started.rst
+++ b/docs/getting_started/getting-started.rst
@@ -49,6 +49,7 @@
"Git",, "Firmware, Documentation"
"Graphviz dot",">v2.38.0","Documentation"
"docutils",">v2.38.0","Documentation"
+ "gcovr",">=v4.2","Tools(Coverage analysis)"
.. _getting_started_toolchain:
@@ -143,6 +144,30 @@
cd <rmm source folder>
pip3 install -r docs/requirements.txt
+############################################
+Install coverage tools analysis dependencies
+############################################
+
+.. note::
+
+ This is an optional step only needed if you intend to run coverage
+ analysis on the source code.
+
+On Ubuntu, ``gcovr`` tool can be installed in two different ways:
+
+Using the pagckage manager:
+
+.. code-block:: bash
+
+ sudo apt-get install gcovr
+
+The second (and recommended) way is install it with ``pip3``:
+
+.. code-block:: bash
+
+ pip3 install --upgrade pip
+ pip3 install gcovr
+
.. _getting_started_get_source:
#########################
diff --git a/plat/host/host_test/CMakeLists.txt b/plat/host/host_test/CMakeLists.txt
index c28fb13..0e477c6 100644
--- a/plat/host/host_test/CMakeLists.txt
+++ b/plat/host/host_test/CMakeLists.txt
@@ -9,6 +9,7 @@
arm_config_option(
NAME RMM_UNITTESTS
HELP "Enable Unitests for the build"
+ TYPE BOOL
DEFAULT "ON"
TYPE INTERNAL)