diff options
38 files changed, 2799 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..fa1c6b58c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,102 @@ +# +# Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + + +# References: +# [LCS] Linux Coding Style +# (https://www.kernel.org/doc/html/v4.10/process/coding-style.html) +# [PEP8] Style Guide for Python Code +# (https://www.python.org/dev/peps/pep-0008) + +root = true + +################################ +# Default settings for all files +[*] +# Windows .bat files may have trouble with utf8 and will fail with lf line ends. +# Currently no plans to add .bat files, but this can be an issue in the future. +charset = uft-8 +end_of_line = lf +trim_trailing_whitespace = true +indent_size = 4 +indent_style = tab + +################################ +#C and C++, follow LCS +[*.{c,h,cpp,hpp}] + +# [LCS] Chapter 1: Indentation +# "Tabs are 8 characters" +tab_width = 8 + +# [LCS] Chapter 1: Indentation +# "and thus indentations are also 8 characters" +indent_size = 8 + +# [LCS] Chapter 1: Indentation +# "Outside of comments,...spaces are never used for indentation" +indent_style = tab + +# [LCS] Chapter 2: Breaking long lines and strings +# "Statements may be up to 100 columns when appropriate." +# This is a "soft" requirement for Arm-TF, and should not be the sole +# reason for changes. +max_line_length = 100 + +# [LCS] Chapter 1: Indentation +# "Get a decent editor and don't leave whitespace at the end of lines." +# [LCS] Chapter 3.1: Spaces +# "Do not leave trailing whitespace at the ends of lines." +trim_trailing_whitespace = true + + +################################ +#CMake specific settings +[{CMakeLists.txt,*.cmake}] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = tab +insert_final_newline = false +max_line_length = 128 +trim_trailing_whitespace = true + +################################ +#Documentation +[*.{rst,md}] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = false +max_line_length = 128 +tab_width = 4 +trim_trailing_whitespace = true + +################################ +# Python code +[*.py] +# [PEP8] Indentation +# "Use 4 spaces per indentation level." +indent_size = 4 +indent_style = space +# [PEP8] Maximum Line Length +# "Limit all lines to a maximum of 79 characters." +max_line_length = 79 + +################################ +# Makefiles +[{Makefile,*.mk}] +indent_style = tab +indent_size = 4 + +################################ +# json,yaml and xml files +[{*.json,*.yaml,*.xml}] +indent_style = space +indent_size = 4 +tab_width = 4 +trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..af93826a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# +# Copyright (c) 2019-2021, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +#Ignore all build directoryes. +*build*/ + +##Python specific intermediate files +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +#Ignore Eclipse project files +.project +.cproject +.settings +.pydevproject diff --git a/cmake/Coverage.cmake b/cmake/Coverage.cmake new file mode 100644 index 000000000..a66649c8e --- /dev/null +++ b/cmake/Coverage.cmake @@ -0,0 +1,289 @@ +# +# Copyright (c) 2020-2022, 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} + --rc lcov_branch_coverage=1 + 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} + --rc lcov_branch_coverage=1 + 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 + --branch-coverage + --output-directory ${OUTPUT_DIRECTORY} + DEPENDS ${INPUT_FILE} + ) + + add_coverage_dependency( + TARGET coverage_report + DEPENDS ${OUTPUT_DIRECTORY} + ) +endfunction() diff --git a/cmake/FindLibClang.cmake b/cmake/FindLibClang.cmake new file mode 100644 index 000000000..00b0e658f --- /dev/null +++ b/cmake/FindLibClang.cmake @@ -0,0 +1,92 @@ +# +# Copyright (c) 2019-2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +#[=======================================================================[.rst: +FindLibClang +------------ + +CMake module for finding the LibClang library. + +Control flow +^^^^^^^^^^^^ + +1. Running ``llvm-config`` if exists +2. Searching for library at common places +3. Searching in Windows registry if available + + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``LibClang`` + The Clang library + + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``LibClang_FOUND`` + True if the system has the Clang library. +``LibClang_LIBRARY_DIRS`` + Libraries needed to link to Clang. + +#]=======================================================================] + + +# 1. Use llvm-config +find_program(_LLVM_CONFIG_COMMAND "llvm-config") + +if (_LLVM_CONFIG_COMMAND) + message(STATUS "Setting LibClang_LIBRARY_DIRS using ${_LLVM_CONFIG_COMMAND}") + + execute_process( + COMMAND ${_LLVM_CONFIG_COMMAND} --libdir + OUTPUT_VARIABLE _LLVM_CONFIG_OUTPUT + ) + + # Stripping newline + string(STRIP ${_LLVM_CONFIG_OUTPUT} LibClang_LIBRARY_DIRS) +endif() + +# 2. Try to find as library +if (NOT LibClang_LIBRARY_DIRS) + message(STATUS "Setting LibClang_LIBRARY_DIRS based on common directories list") + + set(LIBCLANG_COMMON_PATHS + /usr/lib/llvm-14/lib + /usr/lib/llvm-10/lib + /usr/lib/llvm-9/lib + /usr/lib/llvm-8/lib + /usr/lib/llvm-7/lib + /usr/lib/llvm-6.0/lib) + + if (WIN32) + set(CMAKE_FIND_LIBRARY_PREFIXES "lib") + set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll") + + get_filename_component(LLVM_PATH_FROM_REGISTRY [HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\LLVM\\LLVM] ABSOLUTE) + list(APPEND LIBCLANG_COMMON_PATHS "${LLVM_PATH_FROM_REGISTRY}/bin") + endif() + + find_library(_LIBCLANG_PATH + NAMES clang + HINTS ${LIBCLANG_COMMON_PATHS} + ) + + if (_LIBCLANG_PATH) + get_filename_component(LibClang_LIBRARY_DIRS ${_LIBCLANG_PATH} DIRECTORY) + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibClang + "Please install llvm-config or set LibClang path manually" + LibClang_LIBRARY_DIRS +) diff --git a/cmake/UnitTest.cmake b/cmake/UnitTest.cmake new file mode 100644 index 000000000..27ce31df3 --- /dev/null +++ b/cmake/UnitTest.cmake @@ -0,0 +1,339 @@ +# +# Copyright (c) 2019-2021, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + + +#[===[.rst: +UnitTest CMake module +--------------------- + +Control flow +^^^^^^^^^^^^ + +1. Setting :cmake:variable:`CLANG_LIBRARY_PATH` + + 1. Using :cmake:variable:`CLANG_LIBRARY_PATH` CMake variable + + 2. Using ``CLANG_LIBRARY_PATH`` environment variable + + 3. Trying to find by ``find_package`` function which calls :cmake:module:`FindLibClang`. + +2. Checking if ``c-picker`` command is available + + +Variables +^^^^^^^^^ + +The module sets the following variables while it's checking its prerequisites. + +.. cmake:variable:: GIT_COMMAND + +Path of git executable. + +.. cmake:variable:: CLANG_LIBRARY_PATH + +c-picker uses libclang to parse the source files. If defined this variable +specifies the path of the library. + +.. cmake:variable:: CPICKER_COMMAND + +Path of c-picker executable which is part of the c-picker pip package. + +.. cmake:variable:: UNIT_TEST_PROJECT_PATH + +Path of the project source directory. **This needs to be specified +by the developer** to point to a suitable working copy of project to be tested. + +.. cmake:variable:: CPPUTEST_URL + +URL of the CppUTest git repository. By default it points to the official Github +repository of CppUTest. It can be used to specify a different CppUTest mirror. + +.. cmake:variable:: CPPUTEST_REFSPEC + +CppUTest git refspec. The default value selects the latest release. + +.. cmake:variable:: CPPUTEST_INSTALL_PATH + +Temporary directory used during CppUTest build + +.. cmake:variable:: CPPUTEST_PACKAGE_PATH + +Path of the CppUTest CMake package directory + +.. cmake:variable:: CPICKER_CACHE_PATH + +Directory of c-picker generated files. Subdirectories are added according to +the path of the original source file's path. + +.. cmake:variable:: UNIT_TEST_COMMON_SOURCES + +Lists of source files that are included in all test builds. + + +Functions +^^^^^^^^^ + +#]===] + +include_guard(DIRECTORY) + +include(FetchContent) + +set(CLANG_LIBRARY_PATH_HELP "libclang directory for c-picker") + +set(CPPUTEST_URL "https://github.com/cpputest/cpputest.git" CACHE STRING "CppUTest repository URL") +set(CPPUTEST_REFSPEC "v4.0" CACHE STRING "CppUTest git refspec") +set(CPPUTEST_INSTALL_PATH ${CMAKE_CURRENT_BINARY_DIR}/CppUTest_install CACHE PATH "CppUTest installation directory") +set(CPPUTEST_PACKAGE_PATH ${CPPUTEST_INSTALL_PATH}/lib/CppUTest/cmake CACHE PATH "CppUTest CMake package directory") + +set(CPICKER_CACHE_PATH ${CMAKE_CURRENT_BINARY_DIR}/cpicker_cache CACHE PATH "Directory of c-picker generated file") + +set(UNIT_TEST_COMMON_SOURCES ${CMAKE_CURRENT_LIST_DIR}/../common/main.cpp CACHE STRING "List of common test source files") + +if (POSITION_INDEPENDENT_CODE) + string(APPEND CPPUTEST_CXX_FLAGS -fPIC " " -pie) + string(APPEND CPPUTEST_C_FLAGS -fPIC " " -pie) + SET(TEST_COMPILE_OPTIONS ${TEST_COMPILE_OPTIONS} -fPIC -pie) + SET(TEST_LINK_OPTIONS ${TEST_LINK_OPTIONS} -fPIC -pie) +else() + string(APPEND CPPUTEST_CXX_FLAGS -no-pie) + string(APPEND CPPUTEST_C_FLAGS -no-pie) + string(APPEND TEST_COMPILE_OPTIONS -no-pie) + string(APPEND TEST_LINK_OPTIONS -no-pie) +endif() + +# Checking git +find_program(GIT_COMMAND "git") +if (NOT GIT_COMMAND) + message(FATAL_ERROR "Please install git") +endif() + +if (DEFINED CLANG_LIBRARY_PATH) + message(STATUS "Using CLANG_LIBRARY_PATH from CMake variable (command line or cache)") + + if (DEFINED ENV{CLANG_LIBRARY_PATH}) + if (NOT (${CLANG_LIBRARY_PATH} STREQUAL $ENV{CLANG_LIBRARY_PATH})) + message(WARNING "Both CLANG_LIBRARY_PATH CMake and environment variables are set but have different values") + endif() + endif() +else() + if (DEFINED ENV{CLANG_LIBRARY_PATH}) + message(STATUS "Setting CLANG_LIBRARY_PATH based on environment variable") + set(CLANG_LIBRARY_PATH $ENV{CLANG_LIBRARY_PATH} CACHE PATH ${CLANG_LIBRARY_PATH_HELP}) + else() + message(STATUS "Setting CLANG_LIBRARY_PATH based on find_package") + find_package(LibClang REQUIRED) + set(CLANG_LIBRARY_PATH ${LibClang_LIBRARY_DIRS} CACHE PATH ${CLANG_LIBRARY_PATH_HELP}) + endif() +endif() + +message(STATUS "CLANG_LIBRARY_PATH has been set to ${CLANG_LIBRARY_PATH}") + +# Checking c-picker +find_program(CPICKER_COMMAND "c-picker") +if (NOT CPICKER_COMMAND) + message(FATAL_ERROR "Please install c-picker using pip") +endif() + +#[===[.rst: +.. cmake:command:: unit_test_init_cpputest + + .. code-block:: cmake + + unit_test_init_cpputest() + + The ``unit_test_init_cpputest`` CMake function fetches and build CppUTest unit testing framework. + It also enables linking the library to the test binaries. + + Global dependencies: + + ``CPPUTEST_URL`` + Root directory of the c-picker generated files + + ``CPPUTEST_REFSPEC`` + Common source files for every test build + +#]===] +function(unit_test_init_cpputest) + # Fetching CppUTest + FetchContent_Declare( + cpputest + GIT_REPOSITORY ${CPPUTEST_URL} + GIT_TAG ${CPPUTEST_REFSPEC} + GIT_SHALLOW TRUE + PATCH_COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/common/cpputest-cmake-fix.patch || true + ) + + # FetchContent_GetProperties exports cpputest_SOURCE_DIR and cpputest_BINARY_DIR variables + FetchContent_GetProperties(cpputest) + if(NOT cpputest_POPULATED) + message(STATUS "Fetching CppUTest") + FetchContent_Populate(cpputest) + endif() + + # Build and install CppUTest in CMake time. This makes us able to use CppUTest as a CMake package later. + # Memory leak detection is turned off to avoid conflict with memcheck. + execute_process(COMMAND + ${CMAKE_COMMAND} + -DMEMORY_LEAK_DETECTION=OFF + -DLONGLONG=ON + -DC++11=ON + -DCMAKE_INSTALL_PREFIX=${CPPUTEST_INSTALL_PATH} + -DCPPUTEST_CXX_FLAGS=${CPPUTEST_CXX_FLAGS} + -DCPPUTEST_C_FLAGS=${CPPUTEST_C_FLAGS} + -GUnix\ Makefiles + ${cpputest_SOURCE_DIR} + WORKING_DIRECTORY + ${cpputest_BINARY_DIR} + ) + execute_process(COMMAND ${CMAKE_COMMAND} --build ${cpputest_BINARY_DIR} -- install -j) + + # Finding CppUTest package. CMake will check [package name]_DIR variable. + set(CppUTest_DIR ${CPPUTEST_PACKAGE_PATH} CACHE PATH "Path of CppUTestConfig.cmake") + find_package(CppUTest CONFIG REQUIRED) + + # find_package sets the CppUTest_INCLUDE_DIRS and CppUTest_LIBRARIES variables + include_directories(${CppUTest_INCLUDE_DIRS}) + link_libraries(${CppUTest_LIBRARIES}) +endfunction() + +#[===[.rst: +.. cmake:command:: unit_test_add_suite + + .. code-block:: cmake + + unit_test_add_suite( + NAME test_name + SOURCES source_files + INCLUDE_DIRECTORIES include_directories + COMPILE_DEFINITIONS defines + DEPENDS dependencies + ) + + The ``unit_test_add_suite`` CMake function provides a convenient interface for + defining unit test suites. Basically its input is the test source files, include + paths and macro definitions and it internally does all the necessary steps to + have the test binary registered in CTest as a result. + + Control flow: + + 1. Adding new executable named ``NAME`` + + 2. Iterating throught ``SOURCES`` + + 1. If it's a normal source file add to the executable's source list + 2. If it's a YAML file add as a c-picker custom command and add the generated + file to the executable's source list + + 3. Setting include directories + + 4. Setting defines + + 5. Adding extra dependencies of the test build + + 6. Adding executable to the system as a test + + Inputs: + + ``NAME`` + Unique name of the test suite + + ``SOURCES`` (multi, optional) + Source files + + ``INCLUDE_DIRECTORIES`` (multi, optional) + Include directories + + ``COMPILE_DEFINITIONS`` (multi, optional) + Defines + + ``DEPENDS`` (multi, optional) + Extra targets as dependencies of the test build + + Global dependencies: + + ``UNIT_TEST_PROJECT_PATH`` + Root directory of the project under test. + + ``CPICKER_CACHE_PATH`` + Root directory of the c-picker generated files + + ``UNIT_TEST_COMMON_SOURCES`` + Common source files for every test build + + ``CTest`` + Built-in testing module of CMake + +#]===] +function(unit_test_add_suite) + set(_OPTIONS_ARGS) + set(_ONE_VALUE_ARGS NAME) + set(_MULTI_VALUE_ARGS SOURCES INCLUDE_DIRECTORIES COMPILE_DEFINITIONS DEPENDS) + cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN}) + + if(NOT DEFINED BUILD_TESTING) + message(FATAL_ERROR + "unit_test_add_suite(): " + "CTest module should be included in the root CMakeLists.txt before calling this function.") + endif() + + if (NOT UNIT_TEST_PROJECT_PATH) + message(FATAL_ERROR "UNIT_TEST_PROJECT_PATH is not set") + endif() + + set(TEST_NAME ${_MY_PARAMS_NAME}) + set(TEST_INCLUDE_DIRECTORIES ${_MY_PARAMS_INCLUDE_DIRECTORIES}) + set(TEST_COMPILE_DEFINITIONS ${_MY_PARAMS_COMPILE_DEFINITIONS}) + set(TEST_DEPENDS ${_MY_PARAMS_DEPENDS}) + + add_executable(${TEST_NAME} ${UNIT_TEST_COMMON_SOURCES}) + + foreach(TEST_SOURCE ${_MY_PARAMS_SOURCES}) + get_filename_component(TEST_SOURCE_EXTENSION ${TEST_SOURCE} EXT) + + if (${TEST_SOURCE_EXTENSION} STREQUAL ".yml") + # Building output file name: tests/a/b/test.yml -> ${CPICKER_CACHE_PATH}/a/b/test.c + get_filename_component(TEST_SOURCE_DIR ${TEST_SOURCE} DIRECTORY) + file(RELATIVE_PATH CPICKER_SOURCE_DIR ${UNIT_TEST_PROJECT_PATH} ${TEST_SOURCE_DIR}) + get_filename_component(TEST_SOURCE_NAME ${TEST_SOURCE} NAME_WE) + set(CPICKER_OUTPUT ${CPICKER_CACHE_PATH}/${TEST_NAME}/${CPICKER_SOURCE_DIR}/${TEST_SOURCE_NAME}.c) + + # Creating output directory + get_filename_component(OUTPUT_DIRECTORY ${CPICKER_OUTPUT} DIRECTORY) + file(MAKE_DIRECTORY ${OUTPUT_DIRECTORY}) + + # Fetching referenced source files as the dependencies of the generated file + execute_process( + COMMAND + ${CMAKE_COMMAND} -E env CLANG_LIBRARY_PATH=${CLANG_LIBRARY_PATH} + ${CPICKER_COMMAND} --config ${TEST_SOURCE} --root ${UNIT_TEST_PROJECT_PATH} --print-dependencies + OUTPUT_VARIABLE CPICKER_DEPENDENCIES + ) + + # Adding custom command for invoking c-picker + add_custom_command( + OUTPUT ${CPICKER_OUTPUT} + COMMAND + ${CMAKE_COMMAND} -E env CLANG_LIBRARY_PATH=${CLANG_LIBRARY_PATH} + ${CPICKER_COMMAND} --config ${TEST_SOURCE} --root ${UNIT_TEST_PROJECT_PATH} > ${CPICKER_OUTPUT} + DEPENDS ${TEST_SOURCE} ${CPICKER_DEPENDENCIES} + COMMENT "Generating c-picker output ${CPICKER_OUTPUT}" + ) + set(TEST_SOURCE ${CPICKER_OUTPUT}) + endif() + + target_sources(${TEST_NAME} PRIVATE ${TEST_SOURCE}) + endforeach() + + target_include_directories(${TEST_NAME} PRIVATE ${TEST_INCLUDE_DIRECTORIES}) + target_compile_definitions(${TEST_NAME} PRIVATE ${TEST_COMPILE_DEFINITIONS}) + target_compile_options(${TEST_NAME} PRIVATE ${TEST_COMPILE_OPTIONS}) + target_link_options(${TEST_NAME} PRIVATE ${TEST_LINK_OPTIONS}) + if (TEST_DEPENDS) + add_dependencies(${TEST_NAME} ${TEST_DEPENDS}) + endif() + add_test(${TEST_NAME} ${TEST_NAME}) +endfunction() diff --git a/common/cpputest-cmake-fix.patch b/common/cpputest-cmake-fix.patch new file mode 100644 index 000000000..a46bde350 --- /dev/null +++ b/common/cpputest-cmake-fix.patch @@ -0,0 +1,13 @@ +diff --git a/src/CppUTest/CMakeLists.txt b/src/CppUTest/CMakeLists.txt +index 81eda28..4f1db8a 100644 +--- a/src/CppUTest/CMakeLists.txt ++++ b/src/CppUTest/CMakeLists.txt +@@ -69,7 +69,7 @@ set_target_properties(CppUTest PROPERTIES + PUBLIC_HEADER "${CppUTest_headers}") + + if (WIN32) +- target_link_libraries(CppUTest winmm.lib) ++ target_link_libraries(CppUTest winmm) + endif (WIN32) + install(TARGETS CppUTest + EXPORT CppUTestTargets diff --git a/common/main.cpp b/common/main.cpp new file mode 100644 index 000000000..732751f0f --- /dev/null +++ b/common/main.cpp @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2019-2021, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "CppUTest/CommandLineTestRunner.h" + +int main(int argc, char *argv[]) +{ + return RUN_ALL_TESTS(argc, argv); +} diff --git a/dco.txt b/dco.txt new file mode 100644 index 000000000..8201f9921 --- /dev/null +++ b/dco.txt @@ -0,0 +1,37 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..04e3a70b8 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,25 @@ +# +# Copyright (c) 2019-2021, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/SphinxBuild.cmake b/docs/SphinxBuild.cmake new file mode 100644 index 000000000..b364813e0 --- /dev/null +++ b/docs/SphinxBuild.cmake @@ -0,0 +1,18 @@ +# +# Copyright (c) 2019-2021, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# Minimal cmake script for running sphinx. Use as: +# cmake -P SphinxBuild.cmake + +# Inputs: +# SPHINXOPTS : extra options for sphinx + +set(SPHINXBUILD "sphinx-build" CACHE PATH "Location of sphinx-build executable.") +set(SPHNIX_BUILDDIR "_build" CACHE PATH "Directory to place sphinx outpot to.") + +exec_program(${SPHINXBUILD} ./ + ARGS -M html ${CMAKE_CURRENT_LIST_DIR} ${SPHNIX_BUILDDIR} ${SPHINXOPTS} + ) diff --git a/docs/_static/TrustedFirmware-Logo_standard-white.png b/docs/_static/TrustedFirmware-Logo_standard-white.png Binary files differnew file mode 100644 index 000000000..e7bff7128 --- /dev/null +++ b/docs/_static/TrustedFirmware-Logo_standard-white.png diff --git a/docs/_templates/.keep_me b/docs/_templates/.keep_me new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/docs/_templates/.keep_me diff --git a/docs/components/build_system.rst b/docs/components/build_system.rst new file mode 100644 index 000000000..4c26b325d --- /dev/null +++ b/docs/components/build_system.rst @@ -0,0 +1,22 @@ +Build system +============ + +.. cmake-module:: ../../cmake/UnitTest.cmake + +.. cmake-module:: ../../cmake/FindLibClang.cmake + +Generating documentation +------------------------ + +``sphinx-builder`` is used for building the documentation which is required to be installed on the machine. The documentation +can be built by executing the following commands. + +:: + + cd docs + make html + + +-------------- + +*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* diff --git a/docs/components/c_picker.rst b/docs/components/c_picker.rst new file mode 100644 index 000000000..da89520a3 --- /dev/null +++ b/docs/components/c_picker.rst @@ -0,0 +1,61 @@ +c-picker +======== + +c-picker uses ``libclang``'s Python interface for parsing source files. + +Command line options +-------------------- + +- ``-h, --help`` - Showing help message +- ``--root ROOT`` - Root source directory +- ``--config CONFIG`` - Configuration file (``.json|.yml``) +- ``--output OUTPUT`` - Output file +- ``--print-dependencies`` - Prints the dependencies +- ``--version`` - Shows the program's version number and exit +- ``--args ...`` - clang arguments + + +Configuration file format +------------------------- + +c-picker configuration can be described in ``JSON`` or ``YAML`` format using the same object structure. + +- ``elements`` - List of elements to pick from the original code + + - ``name`` - Name of the element + - ``type`` - Type of the element: ``include``, ``function``, ``variable`` + - ``args`` - Arguments for clang used during picking the element + - ``options`` - Currenly the only options is ``remove_static`` which removes + the ``static`` qualifier from the element's definition. + +- ``options`` - Global options for all elements +- ``args`` - Global clang arguments for all elements + +YAML example +------------ + +YAML format is preferred because it can contain metadata like comments and licence information. + +.. code-block:: YAML + + elements: + - file: bl1/bl1_fwu.c + type: variable + name: bl1_fwu_loaded_ids + options: [remove_static] + - file: bl1/bl1_fwu.c + type: function + name: bl1_fwu_add_loaded_id + options: [remove_static] + - file: bl1/bl1_fwu.c + type: function + name: bl1_fwu_remove_loaded_id + options: [remove_static] + args: + - -DFWU_MAX_SIMULTANEOUS_IMAGES=10 + - -DINVALID_IMAGE_ID=0xffffffff + + +-------------- + +*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* diff --git a/docs/components/code_coverage.rst b/docs/components/code_coverage.rst new file mode 100644 index 000000000..3936e4f03 --- /dev/null +++ b/docs/components/code_coverage.rst @@ -0,0 +1,27 @@ +Code coverage +============= + +Coverage processing flow +------------------------ + +1. Prerequisites + + 1. Building all or selected test binaries with coverage enabled + + 2. Running all or selected test binaries + +2. Collecting coverage data from ``.gcda`` and ``.gcno`` file into ``lcov`` coverage info file + +3. Mapping c-picker generated files' coverage to the original source lines + +4. Filtering coverage data for separating the coverage of the code under tests and the coverage of the test code + +5. Generating HTML coverage report from the filtered lcov info files + + +.. cmake-module:: ../../cmake/Coverage.cmake + + +-------------- + +*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* diff --git a/docs/components/cppumock.rst b/docs/components/cppumock.rst new file mode 100644 index 000000000..fd82d95f2 --- /dev/null +++ b/docs/components/cppumock.rst @@ -0,0 +1,197 @@ +CppUMock +======== + +CppUMock is the built-in mocking system of CppUTest. This chapter describes the basic features of the system. For details please +refer the `official CppUMock manual`_. + +System functions +---------------- + +Checking expectations +^^^^^^^^^^^^^^^^^^^^^ + +After defining expected calls an invoking actual call the test should check if all the expected calls have happened. This can be +done by calling ``mock().checkExpectations()``. The common place to put this function call is the ``TEST_TEARDOWN`` function of +the test group. + + +Resetting mocking system +^^^^^^^^^^^^^^^^^^^^^^^^ + +After the last interaction with the mocking system the developer should reset the state of the system by calling +``mock().clear()``. The common place to put this function call is the ``TEST_TEARDOWN`` function of the test group after calling +``mock().checkExpectations()``. + + +Namespaces +^^^^^^^^^^ + +All interactions with the mocking system start by calling ``mock()``. This function has an option ``name`` string parameter +which can be used for limiting the scope of the mocking operation. + +.. code-block:: C++ + + mock("bl1").expectOneCall("bl1_main"); + + +Enable/disable +^^^^^^^^^^^^^^ + +The mocking system can be enabled/disabled runtime using the functions below. This causes call expected and actual call to be +ignored. Default settings are restored by ``mock().clear()``. + +- ``enable()`` +- ``disable()`` + +.. code-block:: C++ + + mock().disable(); + // All CppUMock calls are ignored after this point + mock().enable(); + // CppUMock calls are working again + + mock().disable(); + // All CppUMock calls are ignored after this point + [...] + + mock().clear(); + // Default settings are restored + + +String order +^^^^^^^^^^^^ + +After defining multiple expected function call the mocking system always The mocking system always uses the next matching +function when an actual call happens but by default it doesn't check if different function calls happen in the order of +definition. This behaviour can be turned on using the ``mock().strictOrder()`` function. This option is also set to default by +the ``mock().clear()`` function. + +.. code-block:: C++ + + mock().expectOneCall("A"); + mock().expectOneCall("B"); + + mock().actualCall("B"); + mock().actualCall("A"); + // No error + + mock().strictOrder(); + mock().expectOneCall("A"); + mock().expectOneCall("B"); + + mock().actualCall("B"); // Error generated here + mock().actualCall("A"); + + +Ignoring unspecified calls +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If there are addition actual calls happening in the test case which are unrelated to the test case (i.e. log or other messages) +the unspecified calls can be ignored by adding ``mock().ignoreOtherCalls()`` to the test case. This function should be used +carefully because it can hide unexpected call which are indicating errors in the code. The affect of this call ends by calling +``mock().clear()``. + + +Specifying object +----------------- + +In object oriented environment member function should validate if the function call happened on the suitable object. This is +done by adding the following function to the mock specification. + +- ``onObject(objectPtr)`` + + +Validating parameters +--------------------- + +Each supported parameter type has a corresponding function. These are the same +in the expected and actual calls. + +- ``withBoolParameter(name, bool value)`` +- ``withIntParameter(name, int value)`` +- ``withUnsignedIntParameter(name, unsigned int value)`` +- ``withLongIntParameter(name, long int value)`` +- ``withUnsignedLongIntParameter(name, unsigned long int value)`` +- ``withDoubleParameter(name, double value)`` +- ``withStringParameter(name, const char* value)`` +- ``withPointerParameter(name, void* value)`` +- ``withFunctionPointerParameter(name, void (*value)())`` +- ``withConstPointerParameter(name, const void* value)`` +- ``withMemoryBufferParameter(name, const unsigned char* value, size_t size)`` + +If custum types are defined and copier/comparator objects were installed the following function can handle these parameters. + +- ``withParameterOfType(typeName, name, value)`` + +There's an option to copying data from the test environment into the mock function. When setting expectations the following +function can be used to set the pointer and the address of the data. **The mocking system will not create a copy of this data** +so the original data should be kept intact until the actual call happens. + +- ``withOutputParameterReturning(name, value, size)`` +- ``withOutputParameterOfTypeReturning(typeName, name, value)`` + +In the actual call the pair of these function are shown below. + +- ``withOutputParameter(name, output)`` +- ``withOutputParameterOfType(typeName, name, output)`` + + +Ignoring parameters +^^^^^^^^^^^^^^^^^^^ + +There are cases when the developer doesn't want to specify all parameters. The following function can set this behaviour in the +expected call. + +- ``ignoreOtherParameters()`` + + +Specifying return values +------------------------ + +Using function name overloading the return values are specified by calling ``andReturnValue`` and the parameter type will +determine the exact function. + +- ``andReturnValue(bool value)`` +- ``andReturnValue(int value)`` +- ``andReturnValue(unsigned int value)`` +- ``andReturnValue(long int value)`` +- ``andReturnValue(unsigned long int value)`` +- ``andReturnValue(double value)`` +- ``andReturnValue(const char* value)`` +- ``andReturnValue(void* value)`` +- ``andReturnValue(const void* value)`` +- ``andReturnValue(void (*value)())`` + + +Returning value in actual calls +------------------------------- + +All of these function have version with ``OrDefault(type default_value)`` suffix. These version return a default value if the +return value was not specified in the expected call. + +- ``bool returnBoolValue()`` +- ``int returnIntValue()`` +- ``unsigned int returnUnsignedIntValue()`` +- ``long int returnLongIntValue()`` +- ``unsigned long int returnUnsignedLongIntValue()`` +- ``double returnDoubleValue()`` +- ``const char * returnStringValue()`` +- ``void * returnPointerValue()`` +- ``const void * returnConstPointerValue()`` +- ``void (*returnFunctionPointerValue())()`` + + +Debugging CppUMock errors +------------------------- + +Debugging CppUMock errors can be hard unlike assertion errors because a mocking failure can happen multiple layers of function +calls under the test case. The mocking system has a very similar feature to CppUTest's ``UT_CRASH()`` which is +``mock().crashOnFailure()``. By enabling this feature the code will crash on mocking errors and the developer could easily catch +it with the debugger. + + +-------------- + +*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* + +.. _`official CppUMock manual`: https://cpputest.github.io/mocking_manual.html diff --git a/docs/components/cpputest.rst b/docs/components/cpputest.rst new file mode 100644 index 000000000..e1c34a9e1 --- /dev/null +++ b/docs/components/cpputest.rst @@ -0,0 +1,281 @@ +CppUTest +======== + +This document is based on CppUTest v3.8. CppUtest is a unit testing framework for testing C and C++ code. This document +introduces the basic features of the framework. For further information check the `official manual of CppUTest`_. + + +Why CppUTest? +------------- + +First of all it was not our goal to develop a new unit testing framework while plenty of open source solutions are already +available. There were no special requirements agains the unit testing framework that would rule out all existing frameworks so +we only had to choose a suitable one for our current and possible future needs. + +We ended up selecting CppUTest because of its small footprint and easy portability. It also goes along with the standard xUnit +frameworks' principles and provides a standard interface to the outside world. Some details are listed below. + +- C/C++ support +- Small footprint (compared to Google Test) +- Easy to use on embedded systems +- Built-in mocking system (CppUMock) +- Implements four-phase testing pattern (setup, exercise, verify, teardown) +- Selective run of test cases +- Standard output format (JUnit, TeamCity) + + +Assertions +---------- + +Generally is a good practice to use more specific assertions because it can output more informative error messages in case of a +failure. The generic form or assertions is ``assert(expected, actual)``. Each assert type has a _TEXT variant for user defined +error messages as last parameter. + +- Boolean + + - ``CHECK(condition)`` - Same as ``CHECK_TRUE`` + - ``CHECK_TRUE(condition)`` + - ``CHECK_FALSE(condition)`` + +- C string + + - ``STRCMP_EQUAL(expected, actual)`` + - ``STRNCMP_EQUAL(expected, actual, length)`` + - ``STRCMP_NOCASE_EQUAL(expected, actual)`` + - ``STRCMP_CONTAINS(expected, actual)`` + - ``STRCMP_NOCASE_CONTAINS(expected, actual)`` + +- Integer + + - ``LONGS_EQUAL(expected, actual)`` + - ``UNSIGNED_LONGS_EQUAL(expected, actual)`` + - ``LONGLONGS_EQUAL(expected, actual)`` + - ``UNSIGNED_LONGLONGS_EQUAL(expected, actual)`` + - ``BYTES_EQUAL(expected, actual)`` + - ``SIGNED_BYTES_EQUAL(expected, actual)`` + - ``POINTERS_EQUAL(expected, actual)`` + - ``FUNCTIONPOINTERS_EQUAL(expected, actual)`` + +- Enums + + - ``ENUMS_EQUAL_INT(expected, actual)`` + - ``ENUMS_EQUAL_TYPE(underlying_type, expected, actual)`` + +- Other assertions + + - ``CHECK_EQUAL(expected, actual)`` - Requires ``operator=`` and ``StringFrom(type)`` to be implemented + - ``CHECK_COMPARE(first, relop, second)`` - Same as ``CHECK_EQUAL`` but with any type of compare + - ``DOUBLES_EQUAL(expected, actual, threshold)`` + - ``MEMCMP_EQUAL(expected, actual, size)`` + - ``BITS_EQUAL(expected, actual, mask)`` + - ``FAIL()`` or ``FAIL_TEST()`` - Test case fails if called + - ``CHECK_THROWS(expected, expression)`` - Catching C++ exceptions + +- Miscellaneous macros + + - ``IGNORE_TEST`` - Same as ``TEST`` but it’s not called + - ``TEST_EXIT`` - Exists test + - ``UT_CRASH()`` - Crashes the test which is easy to catch with debugger + - ``UT_PRINT(text)`` - Generic print function + + +Test runner +----------- + +Test cases are collected automatically. Under the hood the ``TEST`` macros are creating global instances of classes and their +constructor registers the test cases into the test registry. This happens before entering the ``main`` function. In the ``main`` +function only the ``RUN_ALL_TESTS`` macro needs to be placed with the command line arguments passed to it. On executing the +binary the command line arguments will control the behaviour of the test process. + +.. code-block:: C++ + + #include "CppUTest/CommandLineTestRunner.h" + + int main(int argc, char* argv[]) { + return RUN_ALL_TESTS(argc, argv); + } + +The default ``main`` implementation is added to all unit test suites by the +build system. + + +Command line options +-------------------- + +Command line options are available mainly for configuring the output format of +the test binary and for filtering test groups or cases. + +- Output + + - ``-v`` - Prints each test name before running them + - ``-c`` - Colorized output + - ``-o{normal, junit, teamcity}`` - Output format, junit can be processed by + most CIs + - ``-k packageName`` - Package name for junit output + - ``-lg`` - List test groups + - ``-ln`` - List test cases + +- Other + + - ``-p`` - Runs each test case in separate processes + - ``-ri`` - Runs ignored test cases + - ``-r#`` - Repeats testing ``#`` times + - ``-s seed`` - Shuffles tests + +- Filtering test cases + + - ``"TEST(groupName, testName)"`` - Running single test + - ``"IGNORE_TEST(groupName, testName)"`` -- Running single ignored test + - ``-g text`` - Runing groups containing text + - ``-n text`` - Runing tests containing text + - ``-sg text`` - Runing groups matching text + - ``-sn text`` - Runing tests matching text + - ``-xg text`` - Excluding groups containing text + - ``-xn text`` - Excluding tests containing text + - ``-xsg text`` - Excluding groups matching text + - ``-xsn text`` - Excluding tests matching text + + +Troubleshooting +--------------- + +Output messages +^^^^^^^^^^^^^^^ + +When one of tests fails the first step is to run it separately and check its +output message. Usually it shows the exact line of the file where the error +happened. + +:: + + test_memcmp.cpp:17: error: Failure in TEST(memcmp, empty) + expected <1 0x1> + but was <0 0x0> + +The executed tests can be followed by adding ``-v`` command line option. + +:: + + ./memcmp -v + TEST(memcmp, different) - 0 ms + TEST(memcmp, same) - 0 ms + TEST(memcmp, empty) - 0 ms + + OK (3 tests, 3 ran, 1 checks, 0 ignored, 0 filtered out, 0 ms) + + +Catching failure with debugger +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If a failure happens in a helper function or in a loop where the assertion +is called multiple times it is harder to get the exact environment of a failure. +In this case it's a good practice to put a ``UT_CRASH()`` call into a +conditional block which hits when the failure happens. This way the debugger can +stop on failure because the code emits a signal. + +.. code-block:: C++ + + TEST(magic, iterate) { + int result; + + for(int i = 0; i < 1000; i++) { + result = magic_function(i); + + // Debug code + if (result) { + UT_CRASH(); + } + + LONGS_EQUAL(0, result); + } + } + + +Using ``FAIL`` macro +^^^^^^^^^^^^^^^^^^^^ + +It's recommended to use ``FAIL`` macro in conditions that should never occur in +tests. For example if a test case loads test data from an external file but the +file could not be opened the ``FAIL`` macro should be used with an informative +message. + +.. code-block:: C++ + + fd = open("test.bin", O_RDONLY); + if (fd < 0) { + FAIL("test.bin open failed"); + } + + +Interference between test cases +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Test cases can interfere if there's a global resource which was not restored to +its original state after leaving a test case. This can be hard to find but at +least the it's easy to make sure that this is root case of an error. Let's +assume there's a global variable which is set during the test case but it +original value is not restore at the end. CppUTest has an command line option +for running each test case in separate process. This makes the global variable +to have its original value at the beginning of the test cases. Basically if the +test works by passing argument ``-p`` when running but fails without it, there's +a good chance for having an interference between test cases. + +.. code-block:: C++ + + int x = 0; + + TEST_GROUP(crosstalk) { + }; + + TEST(crosstalk, a) { + LONGS_EQUAL(0, x); + x = 1; + } + + TEST(crosstalk, b) { + LONGS_EQUAL(0, x); + x = 1; + } + + TEST(crosstalk, c) { + LONGS_EQUAL(0, x); + x = 1; + } + +By running the test executable with different command line arguments it produces +a different result. + +.. code-block:: + + ./crosstalk -v + + TEST(crosstalk, c) - 0 ms + TEST(crosstalk, b) + test_crosstalk.cpp:37: error: + Failure in TEST(crosstalk, b) + expected <0 0x0> + but was <1 0x1> + + - 0 ms + TEST(crosstalk, a) + test_crosstalk.cpp:32: error: Failure in TEST(crosstalk, a) + expected <0 0x0> + but was <1 0x1> + + - 0 ms + + Errors (2 failures, 3 tests, 3 ran, 3 checks, 0 ignored, 0 filtered out, 0 ms) + + ./crosstalk -v -p + TEST(crosstalk, c) - 1 ms + TEST(crosstalk, b) - 0 ms + TEST(crosstalk, a) - 0 ms + + OK (3 tests, 0 ran, 0 checks, 0 ignored, 0 filtered out, 2 ms) + + +-------------- + +*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* + +.. _`official manual of CppUTest`: https://cpputest.github.io/ diff --git a/docs/components/index.rst b/docs/components/index.rst new file mode 100644 index 000000000..301cf9f99 --- /dev/null +++ b/docs/components/index.rst @@ -0,0 +1,14 @@ +Component user manuals +====================== + +.. toctree:: + cpputest + cppumock + c_picker + build_system + code_coverage + + +-------------- + +*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 000000000..283be8674 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019-2021, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# +# Configuration file for the Sphinx documentation builder. +# +# See the options documentation at http://www.sphinx-doc.org/en/master/config + +# -- Metadata about this file ------------------------------------------------ +__copyright__ = "Copyright (c) 2019-2021 Arm Limited" +__license__ = "SPDX-License-Identifier: BSD-3-Clause" + +# Configuration file for the Sphinx documentation builder. + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +# -- Project information ----------------------------------------------------- +project = 'Firmware Test Builder' +copyright = 'Copyright (c) 2019-2021 Arm Limited' +author = 'Arm Limited' + +# The full version, including alpha/beta/rc tags +with open('../version.txt', 'r') as f: + release = f.read() + f.close() +version = release + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.plantuml', + 'sphinxcontrib.moderncmakedomain'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# Load the contents of the global substitutions file into the 'rst_prolog' +# variable. This ensures that the substitutions are all inserted into each page. +with open('global_substitutions.txt', 'r') as subs: + rst_prolog = subs.read() +rst_prolog += "\n .. |RELEASE_NUMBER| replace:: %s" % (version) +# Minimum version of sphinx required +needs_sphinx = '2.0' + +# -- Options for HTML output ------------------------------------------------- + +# Don't show the "Built with Sphinx" footer +html_show_sphinx = False + +# Don't show copyright info in the footer (we have this content in the page) +html_show_copyright = False + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = "sphinx_rtd_theme" + +# The logo to display in the sidebar +html_logo = 'resources/TrustedFirmware-Logo_standard-white.png' + +# Options for the "sphinx-rtd-theme" theme +html_theme_options = { + 'collapse_navigation': False, # Can expand and collapse sidebar entries + 'prev_next_buttons_location': 'both', # Top and bottom of the page + 'style_external_links': True # Display an icon next to external links +} + +# -- Options for autosectionlabel -------------------------------------------- + +# Only generate automatic section labels for document titles +autosectionlabel_maxdepth = 1 + +# -- Options for plantuml ---------------------------------------------------- + +plantuml_output_format = 'svg_img' diff --git a/docs/global_substitutions.txt b/docs/global_substitutions.txt new file mode 100644 index 000000000..095c68c25 --- /dev/null +++ b/docs/global_substitutions.txt @@ -0,0 +1,26 @@ +.. |FTB| replace:: :term:`FTB` +.. |CppUMock| replace:: :term:`CppUMock` +.. |CppUTest| replace:: :term:`CppUTest` +.. |Test case| replace:: :term:`Test case` +.. |Test group| replace:: :term:`Test group` +.. |Test suite| replace:: :term:`Test suite` +.. |TFA_MAIL_LIST| replace:: `TF-A development`_ +.. |TS_MAIL_LIST| replace:: `TS Mailing List`_ +.. |TS_REPO| replace:: `TS repository`_ +.. |LCS| replace:: :term:`LCS` +.. |FTB_REPO| replace:: `FTB repository`_ +.. |REST| replace:: reST +.. |SEMVER| replace:: `Semantic Versioning`_ + +.. _`TF-A development`: https://lists.trustedfirmware.org/pipermail/tf-a/ +.. _`TS Mailing List`: https://lists.trustedfirmware.org/mailman/listinfo/trusted-services +.. _`FTB repository`: https://review.trustedfirmware.org/admin/repos/shared/firmware-test-builder +.. _`TS repository`: https://review.trustedfirmware.org/admin/repos/TS/trusted-services +.. _`Semantic Versioning`: https://semver.org/spec/v2.0.0.html + +.. + -------------- + + *Copyright (c) 2019-2021, Arm Limited. All rights reserved.* + + SPDX-License-Identifier: BSD-3-Clause diff --git a/docs/implementing_tests.rst b/docs/implementing_tests.rst new file mode 100644 index 000000000..fe3cb4a3e --- /dev/null +++ b/docs/implementing_tests.rst @@ -0,0 +1,331 @@ +Implementing tests +================== + +Concept of unit testing +----------------------- + +First of all unit tests exercise the C code on a function level. The tests should call functions directly from the code under +tests and verify if their return values are matching the expected ones and the functions are behaving according to the +specification. + +Because of the function level testing the dependencies of the tested functions should be detached. This is done by mocking the +underlying layer. This provides an additional advantage of controlling and verifying all the call to the lower layer. + + +Adding new unit test suite +-------------------------- + +The first step is to define a new unit test suite. If a completely new module is being test the test suite definition should be +created in a separate ``.cmake`` file which is placed in the test files' directory. Otherwise the test definition can be added +to an existing ``.cmake`` file. These files should be included in the root ``CMakeLists.txt``. + +The ``UnitTest`` CMake module defines the ``unit_test_add_suite`` function so before using this function the module must be +included in the ``.cmake`` file. The function first requires a unique test name which will be test binary's name. The test +sources, include directories and macro definition are passed to the function in the matching arguments. CMake variables can be +used to reference files relative to common directories: + +- ``CMAKE_CURRENT_LIST_DIR`` - Relative to the ``.cmake`` file +- :cmake:variable:`UNIT_TEST_PROJECT_PATH` - Relative to the project's root directory + +.. code-block:: cmake + + # tests/new_module/new_test_suite.cmake + include(UnitTest) + + unit_test_add_suite( + NAME [unique test name] + SOURCES + [source files] + INCLUDE_DIRECTORIES + [include directories] + COMPILE_DEFINITIONS + [defines] + ) + +.. code-block:: cmake + + # Root CMakeLists.txt + include(tests/new_module/new_test_suite.cmake) + +Example test definition +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: cmake + + unit_test_add_suite( + NAME memcmp + SOURCES + ${CMAKE_CURRENT_LIST_DIR}/test_memcmp.cpp + ${CMAKE_CURRENT_LIST_DIR}/memcmp.yml + INCLUDE_DIRECTORIES + ${UNIT_TEST_PROJECT_PATH}/include + ${UNIT_TEST_PROJECT_PATH}/include/lib/libc/aarch64/ + ) + + +Using c-picker +-------------- + +c-picker is a simple tool used for detaching dependencies of the code under test. It can copy elements (i.e. functions, +variables, etc.) from the original source code into generated files. This way the developer can pick functions from compilation +units and surround them with a mocked environment. + +If a ``.yml`` file listed among source files the build system invokes c-picker and the generated ``.c`` file is implicitly added +to the source file list. + +Example .yml file +^^^^^^^^^^^^^^^^^ + +In this simple example c-picker is instructed to copy the include directives and the ``memcmp`` function from the +``lib/libc/memcmp.c`` file. The root directory of the source files referenced by c-picker is the project's root directory. + +.. code-block:: yaml + + elements: + - file: lib/libc/memcmp.c + type: include + - file: lib/libc/memcmp.c + type: function + name: memcmp + + +Writing unit tests +------------------ + +Unit test code should be placed in ``.cpp`` files. + +Four-phase test pattern +^^^^^^^^^^^^^^^^^^^^^^^ + +All tests cases should follow the four-phase test pattern. This consists of four simple steps that altogether ensure the +isolation between test cases. These steps follows below. + +- Setup +- Exercise +- Verify +- Teardown + +After the teardown step all global states should be the same as they were at the beginning of the setup step. + +CppUTest +^^^^^^^^ + +CppUTest is an open source unit testing framework for C/C++. It is written in C++ so all the useful features of the language is +available while testing. It automatically collects and runs the defined ``TEST_GROUPS`` and provides an interface for +implementing the four-phase test pattern. Furthermore the framework has assertion macros for many variable types and test +scenarios. + +Include +''''''' + +The unit test source files should include the CppUTest header after all other headers to avoid conflicts. + +.. code-block:: C++ + + // Other headers + // [...] + + #include "CppUTest/TestHarness.h" + +Test group +'''''''''' + +The next step is to define a test group. When multiple tests cases are written around testing the same function or couple +related functions these tests cases should be part of the same test group. Basically test cases in a test group share have same +setup/teardown sequence. In CppUTest the ``TEST_GROUP`` macro defines a new class which can contain member variables and +functions. Special setup/teardown function are defined using ``TEST_SETUP`` and ``TEST_TEARDOWN`` macros. These functions are +called before/after running each test case of the group so all the common initilization and cleanup code should go into these +functions. + +.. code-block:: C++ + + TEST_GROUP(List) { + TEST_SETUP() { + list = list_alloc(); + } + + TEST_TEARDOWN() { + list_cleanup(list); + } + + bool has_element(int value) { + for (int i = 0; i < list_count (list); i++) { + if (list_get(i) == value) { return true; } + } + return false; + } + + List* list; + }; + + +Test case +''''''''' + +Test cases are defined by the ``TEST`` macro. This macro defines a child class of the test group's class so it can access the +member functions and variables of the test group. The test case's block itself is the body of the function of the child class. + +.. code-block:: C++ + + TEST(List, add_one) { + // Exercise + const int test_value = 5; + list_add(list, test_value); + + // Verify using CHECK_TRUE assertion and TEST_GROUP member function + CHECK_TRUE(has_element(test_value)); + } + + TEST(List, add_two) { + // Exercise + const int test_value1 = 5; + const int test_value2 = 6; + list_add(list, test_value1); + list_add(list, test_value2); + + // Verify + CHECK_TRUE(has_element(test_value1)); + CHECK_TRUE(has_element(test_value2)); + } + +CppUMock +^^^^^^^^ + +During unit testing the dependencies of the tested functions should be replaced by stubs or mocks. When using mocks the +developer would usually like to check if the function was called with corrent parameters and would like to return controlled +values from the function. When a mocked function is called multiple times from the tested function maybe it should check or +return different values on each call. This is where CppUMock comes handy. + +All CppUMock interactions start with calling the ``mock()`` function. This returs a reference to the mocking system. At this +point the developer either wants to define expected or actual calls. This is achiveable by calling +``expectOneCall(functionName)`` or ``expectNCalls(amount, functionName)`` or ``actualCall(functionName)`` member functions of +``mock()`` call's return value. Registering expected calls are done in the test case before exercising the code and actual calls +happen from the mocked functions. + +After this point the following functions can be chained: + +- ``onObject(object)`` - In C++ it is usually the ``this`` pointer but it can be + useful in C too. +- ``with[type]Parameter(name, value)`` - Specifying and checking call parameters +- ``andReturnValue(result)`` - Specifying return value when defining expected + call +- ``return[type]Value()`` - Returning value from function + +The mocking system has two important functions. ``mock().checkExpectation()`` checks if all the expected calls have been +fulfilled and and the ``mock().clear()`` removes all the expected calls from CppUMock's registry. These two functions are +usually called from the ``TEST_TEARDOWN`` function because there should not be any crosstalk between test cases through the +mocking system. + +CppUMock's typical use-case is shown below by a simple example of the ``print_to_eeprom`` function. + +.. code-block:: C++ + + int eeprom_write(const char* str); /* From eeprom.h */ + + int printf_to_eeprom(const char* format, ...) { + char buffer[256]; + int length, written_bytes = 0, eeprom_write_result; + va_list args; + + va_start(args, format); + length = vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + + if (length < 0) { + return length; + } + + while(written_bytes < length) { + eeprom_write_result = eeprom_write(&buffer[written_bytes]); + if (eeprom_write_result < 0) { + return eeprom_write_result; + } + written_bytes += eeprom_write_result; + } + + return written_bytes; + } + +Having the code snipped above a real life usage of the function would look like something shown in the following sequence +diagram. + +.. uml:: resources/sequence_print_to_eeprom.puml + +It would be really hard to test unit this whole system so all the lower layers should be separated and mock on the first +possible level. In the following example the ``print_to_eeprom`` function is being tested and the ``eeprom_write`` function is +mocked. In test cases where ``eeprom_write`` function is expected to be called the test case should first call the +``expect_write`` function. This registers an expected call to CppUMocks internal database and when the call actually happens it +matches the call parameters with the entry in the database. It also returns the previously specified value. + +.. code-block:: C++ + + TEST_GROUP(printf_to_eeprom) { + TEST_TEARDOWN() { + mock().checkExpectations(); + mock().clear(); + } + + void expect_write(const char* str, int result) { + mock().expectOneCall("eeprom_write").withStringParameter("str", str). + andReturnValue(result); + } + }; + + /* Mocked function */ + int eeprom_write(const char* str) { + return mock().actualCall("eeprom_write").withStringParameter("str", str). + returnIntValue(); + } + + TEST(printf_to_eeprom, empty) { + LONGS_EQUAL(0, printf_to_eeprom("")) + } + + TEST(printf_to_eeprom, two_writes) { + expect_write("hello1hello2", 6); + expect_write("hello2", 6); + LONGS_EQUAL(12, printf_to_eeprom("hello%dhello%d", 1, 2)) + } + + TEST(printf_to_eeprom, error) { + expect_write("hello", -1); + LONGS_EQUAL(-1, printf_to_eeprom("hello")) + } + +This how the ``printf_to_eeprom/two_writes`` test case's sequence diagram looks like after mocking ``eeprom_write``. The test +case became able to check the parameters of multiple calls and it could return controlled values. + +.. uml:: resources/sequence_print_to_eeprom_mock.puml + + +Analyzing code coverage +----------------------- + +The code coverage reports can be easily used for finding untested parts of the code. The two main parts of the coverage report +are the line coverage and the branch coverage. Line coverage shows that how many times the tests ran the given line of the +source code. It is beneficial to increase the line coverage however 100% line coverage is still not enough to consider the code +fully tested. + +Let's have a look on the following example. + +.. code-block:: C++ + + void set_pointer_value(unsigned int id, unsigned int value) { + unsigned int *pointer; + + if (id < MAX_ID) { + pointer = get_pointer(id); + } + + *pointer = value; + } + +The 100% line coverage is achievable by testing the function with an ``id`` value smaller than ``MAX_ID``. However if an ``id`` +larger than or equal to ``MAX_ID`` is used as a parameter of this function it will try to write to a memory address pointed by +an uninitialized variable. To catch untested conditions like this the branch coverage comes handy. It will show that only one +branch of the ``if`` statement has been tested as the condition was always true in the tests. + + +-------------- + +*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 000000000..32b066a1d --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,44 @@ +Welcome to the Firmware Test Builds's documentation! +==================================================== + +.. toctree:: + :maxdepth: 1 + :hidden: + :numbered: + + Home<self> + user_guide + implementing_tests + components/index + project/index + +This repository contains the unit testing framework. + +These tests are meant to run on host machine and used to cover platform independent code on the unit test level. In this case a +unit is considered to be a C function or couple related functions. Each unit test suite compiles into a binary which can be run +and debugged as any ordinary executable. + +The system uses CppUTest as unit testing framework. The tests are written in C++ in order to be able to use CppUTests' all +useful features like the automatic collection of test cases and the CppUMock mocking system. + +Separating dependencies apart from the code under test is a crutial step in unit testing systems. In many cases this can be +easily done by linking mocked functions to the tested code but sometimes it's difficult like when the code under test and its +dependencies are in the same compilation unit. For separating the code under test from its dependencies a tool called c-picker +can be used. It can pick pieces of code (functions, variables, etc.) based on descriptor files. + +The build system is based on CMake. The repository contains CMake modules for defining unit test suites. It also invokes +c-picker if a descriptor file is listed among the test sources. CMake has a built in test driver system called ctest. It runs +all the test binaries and produces an well structured output. Test filtering and parallel test run is also available. + +For measuring unit test coverage lcov is utilized. The coverage of c-picker generated sources is mapped to the original sources +files. Coverage currently only works with GCC. + +As a next step start with reading the :ref:`User guide` and the :ref:`Implementing tests` section of this manual. For detailed +description of the components check the :ref:`Component user manuals` section. + + +-------------- + +*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* + +SPDX-License-Identifier: BSD-3-Clause diff --git a/docs/project/change-log.rst b/docs/project/change-log.rst new file mode 100644 index 000000000..037f6dace --- /dev/null +++ b/docs/project/change-log.rst @@ -0,0 +1,29 @@ +Change Log & Release Notes +========================== + +This document contains a summary of the new features, changes, fixes and known issues in each release of Trusted Services. + +Version 1.0.0 +------------- + +New Features +^^^^^^^^^^^^ +First release. + +Changes +^^^^^^^ +None. + +Resolved Issues +^^^^^^^^^^^^^^^ +None. + +Deprecations +^^^^^^^^^^^^ +None. + +-------------- + +*Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.* + +SPDX-License-Identifier: BSD-3-Clause diff --git a/docs/project/coding-guidelines.rst b/docs/project/coding-guidelines.rst new file mode 100644 index 000000000..1f96c5731 --- /dev/null +++ b/docs/project/coding-guidelines.rst @@ -0,0 +1,69 @@ +Coding Style & Guidelines +========================= + +The following sections contain |FTB| coding guidelines. They are continually evolving and should not be considered "set +in stone". Feel free to question them and provide feedback. + +The |FTB| project uses multiple "domains" (textual content types, like programming languages) and each defines its own +rules. + +To help configuring text editors the project comes with "`EditorConfig`_" file(s). (:download:`../../.editorconfig`). + +Shared rules +------------ + +The following rules are common for all domains, except where noted otherwise: + +#. Files shall be **UTF-8** encoded. +#. Use **Unix** style line endings (``LF`` character) +#. The primary language of the project is English. All comments and documentation must be in this language. +#. Trailing whitespace is not welcome, please trim these. + +C/C++ Domain +------------ + +C source code rules are base on the *Linux Coding Style* (See: |LCS|). The following deviations apply: + +5. C code in |FTB| follows *ISO/IEC 9899:1999* standard. +#. C++ code in |FTB| follows *ISO/IEC 14882:2011* standard (C++ 11) +#. Line length shall not exceed 100 characters. +#. Use `snake_case`_ for function, variable and file names. +#. Each file shall be "self contained" and include header files with external dependencies. No file shall depend on + headers included by other files. +#. Include ordering: please include project specific headers first and then system includes. Please order the files + alphabetically in the above two groups. +#. All variables must be initialized. + +CMake domain +------------ + +11. CMake file names use `CamelCase`_ style. +#. Indent with tabs and otherwise use spaces. Use 4 spaces for tab size. +#. Use LF as line end in CMake files. +#. Remove trailing whitespace. +#. Maximum line length is 128 characters. +#. When complicated functionality is needed prefer CMake scripting over other languages. +#. Prefix local variables with `_`. +#. Use functions to prevent global name-space pollution. +#. Use `snake_case`_ for function and variable names. +#. Use the ``include_guard()`` CMake function when creating new modules, to prevent multiple inclusion. +#. Use self contained modules, i.e. include direct dependencies of the module. +#. Use the Sphinx CMake domain for in-line documentation of CMake scripts. For details please refer to the + `CMake Documentation`_. + +Restructured Text Domain +------------------------ + +Please refer to :ref:`Writing documentation`. + +-------------- + +.. _`CamelCase`: https://hu.wikipedia.org/wiki/CamelCase +.. _`snake_case`: https://en.wikipedia.org/wiki/Snake_case +.. _`CMake Documentation`: https://github.com/Kitware/CMake/blob/master/Help/dev/documentation.rst +.. _`EditorConfig`: https://editorconfig.org/ +.. _`Uncrustify`: https://github.com/uncrustify/uncrustify + +*Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.* + +SPDX-License-Identifier: BSD-3-Clause diff --git a/docs/project/contributing.rst b/docs/project/contributing.rst new file mode 100644 index 000000000..7eefb2e85 --- /dev/null +++ b/docs/project/contributing.rst @@ -0,0 +1,83 @@ +Contributing +============ + + +Getting Started +--------------- + +- Make sure you have a GitHub account and you are logged on `developer.trustedfirmware.org`_. +- Send an email to the |TS_MAIL_LIST| about your work. This gives everyone visibility of whether others are working on something + similar. +- Clone the |FTB_REPO| on your own machine. +- Create a local topic branch based on ``main`` branch of the |FTB_REPO|. + +Making Changes +-------------- + +- Make commits of logical units. See these general `Git guidelines`_ for contributing to a project. +- Follow the :ref:`Coding Style & Guidelines`. +- Keep the commits on topic. If you need to fix another bug or make another enhancement, please create a separate change. +- Avoid long commit series. If you do have a long series, consider whether some commits should be squashed together or + addressed in a separate topic. +- Make sure your commit messages are in the proper format. Please keel the 50/72 rule (for details see `Tim Popes blog entry`_.) +- Where appropriate, please update the documentation. + + - Consider whether the this document or other in-source documentation needs updating. + + - Ensure that each changed file has the correct copyright and license information. Files that entirely consist of + contributions to this project should have a copyright notice and BSD-3-Clause SPDX license identifier of the form as shown + in :ref:`license`. Files that contain changes to imported Third Party IP files should retain their original copyright and + license notices. For significant contributions you may add your own copyright notice in following format:: + + Portions copyright (c) [XXXX-]YYYY, <OWNER>. All rights reserved. + + where XXXX is the year of first contribution (if different to YYYY) and YYYY is the year of most recent contribution. + *<OWNER>* is your name or your company name. + - If you are submitting new files that you intend to be the technical sub-maintainer for (for example, a new platform port), + then also update the :ref:`maintainers` file. + - For topics with multiple commits, you should make all documentation changes (and nothing else) in the last commit of the + series. Otherwise, include the documentation changes within the single commit. + +- Please test your changes. + +Submitting Changes +------------------ + +- Ensure that each commit in the series has at least one ``Signed-off-by:``line, using your real name and email address. The + names in the ``Signed-off-by:`` and ``Author:`` lines must match. If anyone else contributes to the commit, they must also add + their own ``Signed-off-by:`` line. By adding this line the contributor certifies the contribution is made under the terms of + the :download:`Developer Certificate of Origin <../../dco.txt>`. + + More details may be found in the `Gerrit Signed-off-by Lines guidelines`_. + +- Ensure that each commit also has a unique ``Change-Id:`` line. If you have cloned the repository with the + "`Clone with commit-msg hook`" clone method, this should already be the case. + + More details may be found in the `Gerrit Change-Ids documentation`_. + +- Submit your changes for review at https://review.trustedfirmware.org targeting the ``main`` branch. + +- The changes will then undergo further review and testing by the :ref:`maintainers`. Any review comments will be made + directly on your patch. This may require you to do some rework. + + Refer to the `Gerrit Uploading Changes documentation`_ for more details. + +- When the changes are accepted, the :ref:`maintainers` will integrate them. + + - Typically, the :ref:`maintainers` will merge the changes into the ``main`` branch. + - If the changes are not based on a sufficiently-recent commit, or if they cannot be automatically rebased, then the + :ref:`maintainers` may rebase it on the ``main`` branch or ask you to do so. + +-------------- + +.. _developer.trustedfirmware.org: https://developer.trustedfirmware.org +.. _Git guidelines: http://git-scm.com/book/ch5-2.html +.. _Gerrit Uploading Changes documentation: https://review.trustedfirmware.org/Documentation/user-upload.html +.. _Gerrit Signed-off-by Lines guidelines: https://review.trustedfirmware.org/Documentation/user-signedoffby.html +.. _Gerrit Change-Ids documentation: https://review.trustedfirmware.org/Documentation/user-changeid.html +.. _`Tim Popes blog entry`: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html + + +*Copyright (c) 2019-2021, Arm Limited and Contributors. All rights reserved.* + +SPDX-License-Identifier: BSD-3-Clause diff --git a/docs/project/glossary.rst b/docs/project/glossary.rst new file mode 100644 index 000000000..7f9c4020e --- /dev/null +++ b/docs/project/glossary.rst @@ -0,0 +1,38 @@ +Glossary +======== + +This glossary provides definitions for terms and abbreviations used in the unit testing framework's documentation. + +You can find additional definitions in the `Arm Glossary`_. + +.. glossary:: + :sorted: + + CppUMock + Built-in mocking system of CppUTest. + + CppUTest + Open source C/C++ unit testing framework. + + FTB + Firmware Test Builder + + LCS + `Linux Coding Style`_ + + Test case + Single use case tested. Defined by ``TEST`` macro of CppUTest. + + Test group + Multiple test cases with common setup/teardown steps and helper functions and variables. Defined by ``TEST_GROUP`` macro + of CppUTest. + + Test suite + Test binary which contains one or more test groups. Defined by :cmake:command:`unit_test_add_suite` CMake function. + +-------------- + +*Copyright (c) 2020-2021, Arm Limited. All rights reserved.* + +.. _`Arm Glossary`: https://developer.arm.com/support/arm-glossary +.. _`Linux Coding Style`: https://www.kernel.org/doc/html/v4.10/process/coding-style.html diff --git a/docs/project/index.rst b/docs/project/index.rst new file mode 100644 index 000000000..9934ff69f --- /dev/null +++ b/docs/project/index.rst @@ -0,0 +1,22 @@ +About the project +================= + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + change-log + coding-guidelines + contributing + glossary + license + maintainers + versioning_policy + writing-documentation + + +-------------- + +*Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.* + +SPDX-License-Identifier: BSD-3-Clause
\ No newline at end of file diff --git a/docs/project/license.rst b/docs/project/license.rst new file mode 100644 index 000000000..ac98f00eb --- /dev/null +++ b/docs/project/license.rst @@ -0,0 +1,37 @@ +License +======= + +Copyright (c) 2019-2021, Arm Limited and Contributors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +- Neither the name of Arm nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------- + +*Note*: +Individual files contain the following tag instead of the full license text. + + SPDX-License-Identifier: BSD-3-Clause + +This enables machine processing of license information based on the SPDX +License Identifiers that are here available: http://spdx.org/licenses/ diff --git a/docs/project/maintainers.rst b/docs/project/maintainers.rst new file mode 100644 index 000000000..9cd0f4ada --- /dev/null +++ b/docs/project/maintainers.rst @@ -0,0 +1,41 @@ +Maintainers +=========== + +|FTB| is a trustedfirmware.org maintained component used by multiple trustedfirmware.org projects. All contributions are +ultimately merged by the maintainers listed below. Technical ownership of some parts of the code-base is delegated to the code +owners listed below. An acknowledgment from these code maintainers may be required before the maintainers merge a contribution. + +More details may be found in the `Project Maintenance Process`_ document. + +This file follows the format of the Linux Maintainers file. For details on the meaning of tags below please refer to the +`Linux Maintainers file`_. + +Main maintainers +---------------- +:M: György Szing <gyorgy.szing@arm.com> +:G: `gyuri-szing`_ +:L: |TS_MAIL_LIST| +:M: Imre Kis <imre.kis@arm.com> +:G: `imre-kis-arm`_ +:M: Lauren Wehrmeister <Lauren.Wehrmeister@arm.com> +:G: `laurenw-arm`_ +:L: |TFA_MAIL_LIST| + +Code owners +-------------------- + +Cyrrently there are no code owners. + +-------------- + +.. _danh-arm: https://github.com/danh-arm +.. _gyuri-szing: https://github.com/gyuri-szing +.. _imre-kis-arm: https://github.com/imre-kis-arm +.. _laurenw-arm: https://github.com/laurenw-arm + +.. _`Linux Maintainers file`: https://github.com/torvalds/linux/blob/master/MAINTAINERS#L80 +.. _Project Maintenance Process: https://developer.trustedfirmware.org/w/collaboration/project-maintenance-process/ + +*Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.* + +SPDX-License-Identifier: BSD-3-Clause diff --git a/docs/project/versioning_policy.rst b/docs/project/versioning_policy.rst new file mode 100644 index 000000000..90d54f097 --- /dev/null +++ b/docs/project/versioning_policy.rst @@ -0,0 +1,42 @@ +Versioning policy +================== + +This document captures information about the version identifier used by the project. It tells the meaning of each part, where +the version information is captured and how it is managed. + +Summary +------- + +The version identifier identifies the feature set supported by a specific release, and captures compatibility information to +other releases. + +This project uses "Semantic Versioning", for details please refer to |SEMVER|. + +In general the version number is constructed from three numbers. The `MAJOR` number is changed when incompatible API changes are +introduced, the `MINOR` version when you functionality is added in a backward compatible manner, and the `PATCH` version when +backwards compatible bug fixes are added. + +Each release will get a unique release id assigned. When a release is made, the version number will get incremented in +accordance with the compatibility rules mentioned above. + +This project is only using the core version and will not use pre-release or build specific metadata extension. + +Storage and format +------------------ + +The version number of each release will be stored at two locations: + #. In a tag of the version control system in the form of "vX.Y.Z" where X Y and Z are the major, minor and patch version + numbers. + #. In a file called version.txt. This file uses ASCII encoding and will contain the version number as "X.Y.Z" where X Y and + Z are the major, minor and patch version numbers. + +.. note:: The version id is independent from version identifiers of the + versioning system used to store the |FTB| (i.e. git). + +-------------- + +.. _`Semantic Versioning`: https://semver.org/spec/v2.0.0.html + +*Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.* + +SPDX-License-Identifier: BSD-3-Clause diff --git a/docs/project/writing-documentation.rst b/docs/project/writing-documentation.rst new file mode 100644 index 000000000..ca55b3219 --- /dev/null +++ b/docs/project/writing-documentation.rst @@ -0,0 +1,52 @@ +Writing documentation +===================== + +|FTB| is documented using `Sphinx`_, which in turn uses Docutils and `Restructured Text`_ (|REST| hereafter). + +The source files for the documents are in the *docs* directory of the |FTB_REPO|. + +The preferred output format is *HTML*, and other formats may or may not work. + + +Section Headings +---------------- + +In order to avoid problems if documents include each other, it is important to follow a consistent section heading +style. Please use at most five heading levels. Please use the following style:: + + First-Level Title + ================= + + Second-Level Title + ------------------ + + Third-Level Title + ''''''''''''''''' + + Forth-level Title + """"""""""""""""" + + Fifth-level Title + ~~~~~~~~~~~~~~~~~ + + +Inline documentation +-------------------- + +To get all information integrated into a single document the project uses Sphinx extensions to allow capturing inline +documentation into this manual. + + +CMake +''''' + +Apologies, this section is not ready yet. Please check existing cmake and |REST| files to see how documentation can be added. + +-------------- + +.. _`Restructured Text`: https://docutils.sourceforge.io/rst.html +.. _`Sphinx`: https://www.sphinx-doc.org + +*Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.* + +SPDX-License-Identifier: BSD-3-Clause diff --git a/docs/resources/TrustedFirmware-Logo_standard-white.png b/docs/resources/TrustedFirmware-Logo_standard-white.png Binary files differnew file mode 100644 index 000000000..e7bff7128 --- /dev/null +++ b/docs/resources/TrustedFirmware-Logo_standard-white.png diff --git a/docs/resources/sequence_print_to_eeprom.puml b/docs/resources/sequence_print_to_eeprom.puml new file mode 100644 index 000000000..8ba719063 --- /dev/null +++ b/docs/resources/sequence_print_to_eeprom.puml @@ -0,0 +1,46 @@ +'------------------------------------------------------------------------------- +' Copyright (c) 2019-2021, Arm Limited and Contributors. All rights reserved. +' +' SPDX-License-Identifier: BSD-3-Clause +' +'------------------------------------------------------------------------------- + +@startuml +participant Application as APP + +activate APP +APP -> Print: print_to_eeprom(fmt, ...) +activate Print + + Print -> EEPROM: eeprom_write(buffer) + activate EEPROM + + EEPROM -> I2C: i2c_write(address, buffer) + activate I2C + + I2C -> Hardware: I2C transaction + activate Hardware + Hardware --> I2C: ack/nack + deactivate Hardware + + I2C --> EEPROM: result + deactivate I2C + + EEPROM -> I2C: i2c_write(address, buffer+ result) + activate I2C + + I2C -> Hardware: I2C transaction + activate Hardware + Hardware --> I2C: ack/nack + deactivate Hardware + + I2C --> EEPROM: result + deactivate I2C + + EEPROM --> Print: result + deactivate EEPROM + +Print -> APP: result +deactivate Print + +@enduml
\ No newline at end of file diff --git a/docs/resources/sequence_print_to_eeprom_mock.puml b/docs/resources/sequence_print_to_eeprom_mock.puml new file mode 100644 index 000000000..ce394d465 --- /dev/null +++ b/docs/resources/sequence_print_to_eeprom_mock.puml @@ -0,0 +1,48 @@ +'------------------------------------------------------------------------------- +' Copyright (c) 2019-2021, Arm Limited and Contributors. All rights reserved. +' +' SPDX-License-Identifier: BSD-3-Clause +' +'------------------------------------------------------------------------------- + +@startuml +participant "Test case" as TC +participant "Code under test" as CUT + +group Setting expectations + activate TC + TC -> Mock: expect_write("hello1hello2", 6) + activate Mock + deactivate Mock + TC -> Mock: expect_write("hello2", 6) + activate Mock + deactivate Mock +end + +TC -> CUT: print_to_eeprom("hello%dhello%d", 1, 2) +activate CUT + + group Using mocked interface + CUT -> Mock: eeprom_write("hello1hello2") + activate Mock + Mock --> CUT: 6 + deactivate Mock + + CUT -> Mock: eeprom_write("hello2") + activate Mock + Mock --> CUT: 6 + deactivate Mock + end + +CUT --> TC: 6 + 6 = 12 +deactivate CUT + +TC -> Mock: mock().checkExpectation() +activate Mock +deactivate Mock + +TC -> Mock: mock().clear() +activate Mock +deactivate Mock + +@enduml
\ No newline at end of file diff --git a/docs/user_guide.rst b/docs/user_guide.rst new file mode 100644 index 000000000..4c214f43c --- /dev/null +++ b/docs/user_guide.rst @@ -0,0 +1,201 @@ +User guide +========== + +This page describes how to get started with compiling and running unit tests on the host machine. + +Host machine requirements +------------------------- + +The system has been successfully tested on the following platforms: + +- Ubuntu 19.04 +- Ubuntu 18.04 +- Arch Linux +- MSYS2 MinGW64 + +Tools +----- + +The following applications are expected to be installed in the build machine: + +- CMake >=3.11 + +- Python >=3.4 + +- c-picker + + - pyyaml + + - clang (pip package if not included in libclang) + + - libclang + +- git + +- Toolchain + +- Native build system + +Ubuntu 19.04 +^^^^^^^^^^^^ + +On Ubuntu 19.04 use the following commands to install the required tools: + +:: + + sudo apt-get install cmake git python3 python3-pip libclang-dev build-essential + sudo pip3 install git+[TBD] + +Ubuntu 18.04 +^^^^^^^^^^^^ + +The official Ubuntu 18.04 package repository only contains CMake 3.10 which not satisfies the requirements for building unit +tests. Fortunately there's an official CMake APT repository provided by Kitware: https://apt.kitware.com/ By adding this server +to the repository list an up-to-date version of CMake can be installed on Ubuntu 18.04. + +:: + + wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc \ + 2>/dev/null | sudo apt-key add - + sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main' + + sudo apt-get install cmake git python3 python3-pip libclang-dev build-essential + sudo pip3 install git+[TBD] + +Arch Linux +^^^^^^^^^^ + +On Arch Linux use the following commands to install the required tools: + +:: + + sudo pacman -Sy cmake git python python-pip python-yaml make gcc clang + sudo pip install git+[TBD] + +MSYS2 MinGW64 +^^^^^^^^^^^^^ + +:: + + pacman -Sy mingw-w64-x86_64-cmake git mingw-w64-x86_64-python3 \ + mingw-w64-x86_64-python3-pip make mingw-w64-x86_64-gcc mingw-w64-x86_64-clang + pip install git+[TBD]] + + +Integrating firmware test builder into a project +------------------------------------------------ + +The firmware test builder can be simply integrated into a CMake based project by adding the lines below to its CMakeLists.txt. +This example shows how to fetch the firmware test builder files on demand using CMake's FetchContent module. + +:: + + FetchContent_Declare( + firmware_test_builder + GIT_REPOSITORY ${FIRMWARE_TEST_BUILDER_URL} + GIT_TAG ${FIRMWARE_TEST_BUILDER_REFSPEC} + GIT_SHALLOW TRUE + ) + + FetchContent_GetProperties(firmware_test_builder) + if(NOT firmware_test_builder_POPULATED) + message(STATUS "Fetching Firmware Test Builder") + FetchContent_Populate(firmware_test_builder) + endif() + + # Appending Firmware Test Builder's cmake directory to CMake module path + list(APPEND CMAKE_MODULE_PATH ${firmware_test_builder_SOURCE_DIR}/cmake) + +For more details on adding tests see :ref:`Implementing tests` chapter. + + +Building unit tests +------------------- + +Building unit tests start with running CMake which will check all the prerequisites and generate the native build system's input +files. This example uses Unix Makefiles. Unit tests exercise the code directly by compiling it into the same binary as the test +code. + +:: + + cd project + mkdir build + cd build + cmake -G"Unix Makefiles" .. + +After running the previous steps the makefiles are generated into the build directory so make can build unit tests. During unit +test development if only the source files have changed it's not necessary to re-run cmake it's only needed to run make as shown +below. + +:: + + make -j + +For building single unit test suites the test's name can be used as a makefile target. Let's assume there's a test suite called +best_unit_tests. + +:: + + make best_unit_tests + + +Running unit tests +------------------ + +CMake provides a built-in tool called ctest for running all the tests using a single command. It is also able to filter tests or +run them in parallel for speeding up the tests process. Run all the tests using the following command: + +:: + + ctest + +Each unit test suite has its own executable. The easiest way of running single test suite is running it as a simple executable. + +:: + + ./best_unit_tests + + +Debugging unit tests +-------------------- + +As it was mentioned in the previous section test suites are basically separate executables so they can be debugged as any other +native applications on the host machine. In a Linux environment gdb or IDE's built-in debugger can be utilized for debugging. + +:: + + gdb ./best_unit_tests + + +Measuring code coverage +----------------------- + +Inspecting code coverage is a useful method for detecting parts of the code which is not exercised by tests. The build system +includes an option for generating code coverage report of the unit tests. The coverage is processed by ``lcov`` which needs to +be installed for this feature. Also the coverage measurement in only available when GCC is used as a compiler. + +The methods for enabling coverage measurement is project dependent. + +Before collecting coverage info and generating reports the tests must be run as the coverage is a runtime measurement. See +section `Running unit tests`_ for more information about running unit tests. + +In case of enabled coverage report the system adds two new build targets called ``coverage`` and ``coverage_report``. They can +be used simply by running the following commands if ``make`` is used as a build system. + +:: + + make coverage + make coverage_report + +The ``coverage`` target generates lcov info files for further processing. If there are coverage files available from different +sources (i.e. coverages of other tests) they can be merged with the unit test coverage file and evaluated together. + +The ``coverage_report`` target generates a HTML report from the coverage info files. The coverage reports can be found in the +build directory. The report shows the directory structure of the code and each file can be inspected individually. Line, +function and branch coverage is included. + + +-------------- + +*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* +SPDX-License-Identifier: BSD-3-Clause diff --git a/license.rst b/license.rst new file mode 100644 index 000000000..3c59fe94e --- /dev/null +++ b/license.rst @@ -0,0 +1,7 @@ +See `license.rst <./docs/project/license.rst>`_ + +-------------- + +*Copyright (c) 2019-2021, Arm Limited and Contributors. All rights reserved.* + +SPDX-License-Identifier: BSD-3-Clause diff --git a/readme.rst b/readme.rst new file mode 100644 index 000000000..eaffb56ca --- /dev/null +++ b/readme.rst @@ -0,0 +1,24 @@ +Firmware Test Builder +===================== + +This repository holds the unit testing framework for testing C code. + +The full documentation of this project is `Sphinx`_ based, lives in the *docs* +sub-directory and is captured in reStructuredText_ format. + +For licensing information please refer to `license.rst`_ + +Contributed content is accepted under the `Developer Certificate of Origin (DCO)`_ +and commit messages shall follow specific formatting rules (for details +please refer to the *Contributing* sections of the full documentation). + +.. _reStructuredText: http://docutils.sourceforge.net/rst.html +.. _Sphinx: http://www.sphinx-doc.org/en/master/ +.. _`license.rst`: ./license.rst +.. _`Developer Certificate of Origin (DCO)`: ./dco.txt + +-------------- + +*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* + +SPDX-License-Identifier: BSD-3-Clause diff --git a/version.txt b/version.txt new file mode 100644 index 000000000..afaf360d3 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +1.0.0
\ No newline at end of file |