blob: d557eb01a28d580814f72f88f21d1b881e8d6180 [file] [log] [blame]
#
# Copyright (c) 2019-2020, 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:: CLANG_LIBRARY_PATH
libclang directory for c-picker
.. cmake:variable:: CPICKER_COMMAND
Path of c-picker executable which is part of the c-picker pip package.
Functions
^^^^^^^^^
#]===]
include_guard(DIRECTORY)
set(CLANG_LIBRARY_PATH_HELP "libclang directory for c-picker")
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_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:
``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
"add_unit_test_suite(): "
"CTest module should be included in the root CMakeLists.txt before calling this function.")
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 ${CMAKE_CURRENT_SOURCE_DIR}/tests ${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 ${TF_A_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 ${TF_A_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})
if (TEST_DEPENDS)
add_dependencies(${TEST_NAME} ${TEST_DEPENDS})
endif()
add_test(${TEST_NAME} ${TEST_NAME})
endfunction()