Add initial version of firmware-test-builder

Introduce the following features to firmware-test-builder:

* Testing on the host machine (PC)
* Provides CMake functions for building/running/evaluating tests
  * Checking prerequisites (git, c-picker, libclang, etc.)
  * Fetching and building CppUTest
  * Defining and building unit test suites (i.e. separate binaries)
  * Handling c-picker based extraction of code snippets
  * Registering test suites to CTest (CMake's test system) which runs
    all the test binaries
* Generating coverage report
* Documentation of the system

Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: Ic0a1af55bef07c6e76071193caa94a9a48f9041f
diff --git a/cmake/Coverage.cmake b/cmake/Coverage.cmake
new file mode 100644
index 0000000..b7a6439
--- /dev/null
+++ b/cmake/Coverage.cmake
@@ -0,0 +1,286 @@
+#
+# Copyright (c) 2020-2021, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+#[===[.rst:
+Coverage CMake module
+---------------------
+
+Control flow
+^^^^^^^^^^^^
+
+Using the code coverage feature of the system starts with including
+``Coverage`` module. This will implicitly check if all the requirements for
+generating coverage are fulfilled. This includes checking the following
+conditions.
+
+- Compiler is GCC
+- lcov executables exist
+- ``c-picker-coverage-mapper`` is available
+
+As the next step it sets the compiler flags to make GCC to generate binaries
+with coverage information.
+
+
+Variables
+^^^^^^^^^
+
+The module sets the following variables while it's checking its prerequisites.
+
+.. cmake:variable:: LCOV_COMMAND
+
+Path of lcov executable
+
+.. cmake:variable:: GENHTML_COMMAND
+
+Path of genhtml executable which is part of the lcov package.
+
+.. cmake:variable:: CPICKER_COVERAGE_MAPPER_COMMAND
+
+Path of ``c-picker-coverage-mapper`` executable which is provided by c-picker
+pip package.
+
+
+Functions
+^^^^^^^^^
+
+The module also contains functions for setting up the coverage feature.
+
+#]===]
+
+include_guard(DIRECTORY)
+
+# Checking GCC
+if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+	message(FATAL_ERROR "Coverage measurement is only supported when using GCC")
+endif()
+
+# Checking lcov
+find_program(LCOV_COMMAND "lcov")
+if (NOT LCOV_COMMAND)
+	message(FATAL_ERROR "Please install lcov")
+endif()
+
+# Checking c-picker-coverage-mapper
+find_program(CPICKER_COVERAGE_MAPPER_COMMAND "c-picker-coverage-mapper")
+if (NOT CPICKER_COVERAGE_MAPPER_COMMAND)
+	message(FATAL_ERROR "Please install c-picker-coverage-mapper using pip (part of c-picker)")
+endif()
+
+# Checking genhtml
+find_program(GENHTML_COMMAND "genhtml")
+if (NOT GENHTML_COMMAND)
+	message(FATAL_ERROR "Please install genhtml with genhtml (part of lcov)")
+endif()
+
+# Including this file enables code coverage measurement by adding the necessary compiler and
+# linker flags.
+set(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
+set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage -fno-exceptions")
+set(CMAKE_EXE_LINKER_FLAGS "-fprofile-arcs -ftest-coverage")
+
+# Adding custom targets
+add_custom_target(coverage)
+add_custom_target(coverage_report)
+
+# Adds a file to the dependency list of the target by inserting an accessory
+# custom target. The name of the custom target is properly escaped.
+function(add_coverage_dependency)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS TARGET DEPENDS)
+	set(_MULTI_VALUE_ARGS)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	set(TARGET ${_MY_PARAMS_TARGET})
+	set(DEPENDS ${_MY_PARAMS_DEPENDS})
+
+	string(REGEX REPLACE "\\/" "_" CUSTOM_TARGET_SUFFIX ${DEPENDS})
+
+	add_custom_target(${TARGET}_target_${CUSTOM_TARGET_SUFFIX} DEPENDS ${DEPENDS})
+	add_dependencies(${TARGET} ${TARGET}_target_${CUSTOM_TARGET_SUFFIX})
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: coverage_generate
+
+  .. code-block:: cmake
+
+    coverage_generate(
+    	NAME test_name
+    	SOURCE_DIR source_directory
+    	BINARY_DIR binary_directory
+    	OUTPUT_FILE output_file
+    )
+
+  The function outputs an lcov info file for further processing. It also handles
+  the remapping of the coverage of the c-picker generated files.
+
+  Control flow:
+
+  1. Running the ``lcov`` command for collecting the coverage data from the
+     available ``.gcda`` and ``.gcno`` files in the ``BINARY_DIR``.
+
+  2. The output of previous step is processed by ``c-picker-coverage-mapper``.
+     This will remap the coverage of files in ``CPICKER_CACHE_PATH`` to the
+     original source files.
+
+  3. Adds the output file to the ``coverage`` target's dependency list.
+
+  Inputs:
+
+  ``NAME``
+    Test name included in lcov info file
+
+  ``SOURCE_DIR``
+    Directory of source files
+
+  ``BINARY_DIR``
+    Directory of the ``.gcda`` and ``.gcno`` files
+
+  ``OUTPUT_FILE``
+    Output lcov coverage info file
+
+  Global dependencies:
+
+  ``CPICKER_CACHE_PATH``
+    Root directory of the c-picker generated files
+
+
+#]===]
+function(coverage_generate)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS NAME SOURCE_DIR BINARY_DIR OUTPUT_FILE)
+	set(_MULTI_VALUE_ARGS)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	set(TEST_NAME ${_MY_PARAMS_NAME})
+	set(SOURCE_DIR ${_MY_PARAMS_SOURCE_DIR})
+	set(BINARY_DIR ${_MY_PARAMS_BINARY_DIR})
+	set(TEMP_FILE ${_MY_PARAMS_OUTPUT_FILE}_temp)
+	set(OUTPUT_FILE ${_MY_PARAMS_OUTPUT_FILE})
+
+	# Collecting information from .gcda and .gcno files into an lcov .info file
+	# Mapping c-picker generated files' coverage info to the original source lines
+	add_custom_command(
+		OUTPUT ${TEMP_FILE} ${OUTPUT_FILE}
+		COMMAND ${LCOV_COMMAND}
+			--capture
+			--test-name ${TEST_NAME}
+			--directory ${BINARY_DIR}
+			--base-directory ${SOURCE_DIR}
+			--output-file ${TEMP_FILE}
+		COMMAND ${CPICKER_COVERAGE_MAPPER_COMMAND}
+			--input ${TEMP_FILE}
+			--output ${OUTPUT_FILE}
+			--mapping-path ${CPICKER_CACHE_PATH}
+	)
+
+	add_coverage_dependency(
+		TARGET coverage
+		DEPENDS ${OUTPUT_FILE}
+	)
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: coverage_filter
+
+  .. code-block:: cmake
+
+    coverage_filter(
+    	INPUT_FILE input_file
+    	OUTPUT_FILE output_file
+    	INCLUDE_DIRECTORY include_directory
+    )
+
+  The function filters the coverage data by including only the coverage of the
+  files of ``INCLUDE_DIRECTORY`` or its subdirectories. It adds the filtered
+  output file to the ``coverage`` target's dependency list.
+
+  Inputs:
+
+  ``INPUT_FILE``
+    Input lcov coverage info file
+
+  ``OUTPUT_FILE``
+    Output lcov coverage info file
+
+  ``INCLUDE_DIRECTORY``
+    Root directory of included files
+
+#]===]
+function(coverage_filter)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS INPUT_FILE OUTPUT_FILE INCLUDE_DIRECTORY)
+	set(_MULTI_VALUE_ARGS)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	set(INPUT_FILE ${_MY_PARAMS_INPUT_FILE})
+	set(OUTPUT_FILE ${_MY_PARAMS_OUTPUT_FILE})
+	set(INCLUDE_DIRECTORY ${_MY_PARAMS_INCLUDE_DIRECTORY})
+
+	# The pattern must be an absolute path ending with an asterisk
+	get_filename_component(INCLUDE_DIRECTORY_ABSPATH "${INCLUDE_DIRECTORY}" ABSOLUTE)
+	set(INCLUDE_DIRECTORY_ABSPATH "${INCLUDE_DIRECTORY_ABSPATH}/*")
+
+	add_custom_command(
+		OUTPUT ${OUTPUT_FILE}
+		COMMAND ${LCOV_COMMAND}
+			--extract ${INPUT_FILE} \"${INCLUDE_DIRECTORY_ABSPATH}\"
+			--output-file ${OUTPUT_FILE}
+		DEPENDS ${INPUT_FILE}
+	)
+
+	add_coverage_dependency(
+		TARGET coverage
+		DEPENDS ${OUTPUT_FILE}
+	)
+endfunction()
+
+#[===[.rst:
+.. cmake:command:: coverage_generate_report
+
+  .. code-block:: cmake
+
+    coverage_generate_report(
+    	INPUT_FILE input_file
+    	OUTPUT_DIRECTORY output_directory
+    )
+
+  The function generates a HTML coverage report from the lcov info file into
+  the ``OUTPUT_DIRECTORY``. It adds the output directory to the
+  ``coverage_report`` target's dependency list.
+
+  Inputs:
+
+  ``INPUT_FILE``
+    Input lcov coverage info file
+
+  ``OUTPUT_DIRECTORY``
+    Output directory of the coverage report where the ``index.html`` is the
+    root file of the report.
+
+#]===]
+function(coverage_generate_report)
+	set(_OPTIONS_ARGS)
+	set(_ONE_VALUE_ARGS INPUT_FILE OUTPUT_DIRECTORY)
+	set(_MULTI_VALUE_ARGS)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	set(INPUT_FILE ${_MY_PARAMS_INPUT_FILE})
+	set(OUTPUT_DIRECTORY ${_MY_PARAMS_OUTPUT_DIRECTORY})
+
+	add_custom_command(
+		OUTPUT ${OUTPUT_DIRECTORY}
+		COMMAND genhtml ${INPUT_FILE}
+			--show-details
+			--output-directory ${OUTPUT_DIRECTORY}
+		DEPENDS ${INPUT_FILE}
+	)
+
+	add_coverage_dependency(
+		TARGET coverage_report
+		DEPENDS ${OUTPUT_DIRECTORY}
+	)
+endfunction()