blob: 4f0594a6820b8bb32f1deb86d332db95bf55e591 [file] [log] [blame]
Imre Kisa21712e2019-10-08 12:56:59 +02001#
2# Copyright (c) 2019-2021, Arm Limited. All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6
Juan Pablo Condec51baad2023-06-21 11:28:21 -05007
Imre Kisa21712e2019-10-08 12:56:59 +02008#[===[.rst:
9UnitTest CMake module
10---------------------
11
12Control flow
13^^^^^^^^^^^^
14
151. Setting :cmake:variable:`CLANG_LIBRARY_PATH`
16
17 1. Using :cmake:variable:`CLANG_LIBRARY_PATH` CMake variable
18
19 2. Using ``CLANG_LIBRARY_PATH`` environment variable
20
21 3. Trying to find by ``find_package`` function which calls :cmake:module:`FindLibClang`.
22
232. Checking if ``c-picker`` command is available
24
25
26Variables
27^^^^^^^^^
28
29The module sets the following variables while it's checking its prerequisites.
30
31.. cmake:variable:: GIT_COMMAND
32
33Path of git executable.
34
35.. cmake:variable:: CLANG_LIBRARY_PATH
36
37c-picker uses libclang to parse the source files. If defined this variable
38specifies the path of the library.
39
40.. cmake:variable:: CPICKER_COMMAND
41
42Path of c-picker executable which is part of the c-picker pip package.
43
44.. cmake:variable:: UNIT_TEST_PROJECT_PATH
45
46Path of the project source directory. **This needs to be specified
47by the developer** to point to a suitable working copy of project to be tested.
48
49.. cmake:variable:: CPPUTEST_URL
50
51URL of the CppUTest git repository. By default it points to the official Github
52repository of CppUTest. It can be used to specify a different CppUTest mirror.
53
54.. cmake:variable:: CPPUTEST_REFSPEC
55
56CppUTest git refspec. The default value selects the latest release.
57
58.. cmake:variable:: CPPUTEST_INSTALL_PATH
59
60Temporary directory used during CppUTest build
61
62.. cmake:variable:: CPPUTEST_PACKAGE_PATH
63
64Path of the CppUTest CMake package directory
65
66.. cmake:variable:: CPICKER_CACHE_PATH
67
68Directory of c-picker generated files. Subdirectories are added according to
69the path of the original source file's path.
70
71.. cmake:variable:: UNIT_TEST_COMMON_SOURCES
72
73Lists of source files that are included in all test builds.
74
Imre Kis01ab6532021-03-12 12:42:40 +010075.. cmake:variable:: UNIT_TEST_COMMON_ARGS
76
77Common command line arguments for every test binary
78
Imre Kisa21712e2019-10-08 12:56:59 +020079
80Functions
81^^^^^^^^^
82
83#]===]
84
85include_guard(DIRECTORY)
86
87include(FetchContent)
88
89set(CLANG_LIBRARY_PATH_HELP "libclang directory for c-picker")
90
91set(CPPUTEST_URL "https://github.com/cpputest/cpputest.git" CACHE STRING "CppUTest repository URL")
92set(CPPUTEST_REFSPEC "v4.0" CACHE STRING "CppUTest git refspec")
93set(CPPUTEST_INSTALL_PATH ${CMAKE_CURRENT_BINARY_DIR}/CppUTest_install CACHE PATH "CppUTest installation directory")
94set(CPPUTEST_PACKAGE_PATH ${CPPUTEST_INSTALL_PATH}/lib/CppUTest/cmake CACHE PATH "CppUTest CMake package directory")
95
96set(CPICKER_CACHE_PATH ${CMAKE_CURRENT_BINARY_DIR}/cpicker_cache CACHE PATH "Directory of c-picker generated file")
97
98set(UNIT_TEST_COMMON_SOURCES ${CMAKE_CURRENT_LIST_DIR}/../common/main.cpp CACHE STRING "List of common test source files")
Imre Kis01ab6532021-03-12 12:42:40 +010099set(UNIT_TEST_COMMON_ARGS "" CACHE STRING "Common command line arguments for every test run")
Imre Kisa21712e2019-10-08 12:56:59 +0200100
Juan Pablo Condec51baad2023-06-21 11:28:21 -0500101if (POSITION_INDEPENDENT_CODE)
102 string(APPEND CPPUTEST_CXX_FLAGS -fPIC " " -pie)
103 string(APPEND CPPUTEST_C_FLAGS -fPIC " " -pie)
104 SET(TEST_COMPILE_OPTIONS ${TEST_COMPILE_OPTIONS} -fPIC -pie)
105 SET(TEST_LINK_OPTIONS ${TEST_LINK_OPTIONS} -fPIC -pie)
106else()
107 string(APPEND CPPUTEST_CXX_FLAGS -no-pie)
108 string(APPEND CPPUTEST_C_FLAGS -no-pie)
109 string(APPEND TEST_COMPILE_OPTIONS -no-pie)
110 string(APPEND TEST_LINK_OPTIONS -no-pie)
111endif()
112
Imre Kisa21712e2019-10-08 12:56:59 +0200113# Checking git
114find_program(GIT_COMMAND "git")
115if (NOT GIT_COMMAND)
116 message(FATAL_ERROR "Please install git")
117endif()
118
119if (DEFINED CLANG_LIBRARY_PATH)
120 message(STATUS "Using CLANG_LIBRARY_PATH from CMake variable (command line or cache)")
121
122 if (DEFINED ENV{CLANG_LIBRARY_PATH})
123 if (NOT (${CLANG_LIBRARY_PATH} STREQUAL $ENV{CLANG_LIBRARY_PATH}))
124 message(WARNING "Both CLANG_LIBRARY_PATH CMake and environment variables are set but have different values")
125 endif()
126 endif()
127else()
128 if (DEFINED ENV{CLANG_LIBRARY_PATH})
129 message(STATUS "Setting CLANG_LIBRARY_PATH based on environment variable")
130 set(CLANG_LIBRARY_PATH $ENV{CLANG_LIBRARY_PATH} CACHE PATH ${CLANG_LIBRARY_PATH_HELP})
131 else()
132 message(STATUS "Setting CLANG_LIBRARY_PATH based on find_package")
133 find_package(LibClang REQUIRED)
134 set(CLANG_LIBRARY_PATH ${LibClang_LIBRARY_DIRS} CACHE PATH ${CLANG_LIBRARY_PATH_HELP})
135 endif()
136endif()
137
138message(STATUS "CLANG_LIBRARY_PATH has been set to ${CLANG_LIBRARY_PATH}")
139
140# Checking c-picker
141find_program(CPICKER_COMMAND "c-picker")
142if (NOT CPICKER_COMMAND)
143 message(FATAL_ERROR "Please install c-picker using pip")
144endif()
145
146#[===[.rst:
147.. cmake:command:: unit_test_init_cpputest
148
149 .. code-block:: cmake
150
151 unit_test_init_cpputest()
152
153 The ``unit_test_init_cpputest`` CMake function fetches and build CppUTest unit testing framework.
154 It also enables linking the library to the test binaries.
155
156 Global dependencies:
157
158 ``CPPUTEST_URL``
159 Root directory of the c-picker generated files
160
161 ``CPPUTEST_REFSPEC``
162 Common source files for every test build
163
164#]===]
165function(unit_test_init_cpputest)
166 # Fetching CppUTest
167 FetchContent_Declare(
168 cpputest
169 GIT_REPOSITORY ${CPPUTEST_URL}
170 GIT_TAG ${CPPUTEST_REFSPEC}
171 GIT_SHALLOW TRUE
172 PATCH_COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/common/cpputest-cmake-fix.patch || true
173 )
174
175 # FetchContent_GetProperties exports cpputest_SOURCE_DIR and cpputest_BINARY_DIR variables
176 FetchContent_GetProperties(cpputest)
177 if(NOT cpputest_POPULATED)
178 message(STATUS "Fetching CppUTest")
179 FetchContent_Populate(cpputest)
180 endif()
181
182 # Build and install CppUTest in CMake time. This makes us able to use CppUTest as a CMake package later.
183 # Memory leak detection is turned off to avoid conflict with memcheck.
184 execute_process(COMMAND
185 ${CMAKE_COMMAND}
186 -DMEMORY_LEAK_DETECTION=OFF
187 -DLONGLONG=ON
188 -DC++11=ON
189 -DCMAKE_INSTALL_PREFIX=${CPPUTEST_INSTALL_PATH}
Juan Pablo Condec51baad2023-06-21 11:28:21 -0500190 -DCPPUTEST_CXX_FLAGS=${CPPUTEST_CXX_FLAGS}
191 -DCPPUTEST_C_FLAGS=${CPPUTEST_C_FLAGS}
Imre Kisa21712e2019-10-08 12:56:59 +0200192 -GUnix\ Makefiles
193 ${cpputest_SOURCE_DIR}
194 WORKING_DIRECTORY
195 ${cpputest_BINARY_DIR}
196 )
197 execute_process(COMMAND ${CMAKE_COMMAND} --build ${cpputest_BINARY_DIR} -- install -j)
198
199 # Finding CppUTest package. CMake will check [package name]_DIR variable.
200 set(CppUTest_DIR ${CPPUTEST_PACKAGE_PATH} CACHE PATH "Path of CppUTestConfig.cmake")
201 find_package(CppUTest CONFIG REQUIRED)
202
203 # find_package sets the CppUTest_INCLUDE_DIRS and CppUTest_LIBRARIES variables
204 include_directories(${CppUTest_INCLUDE_DIRS})
205 link_libraries(${CppUTest_LIBRARIES})
206endfunction()
207
208#[===[.rst:
209.. cmake:command:: unit_test_add_suite
210
211 .. code-block:: cmake
212
213 unit_test_add_suite(
214 NAME test_name
215 SOURCES source_files
216 INCLUDE_DIRECTORIES include_directories
217 COMPILE_DEFINITIONS defines
218 DEPENDS dependencies
Imre Kis01ab6532021-03-12 12:42:40 +0100219 ARGS arguments
Imre Kisa21712e2019-10-08 12:56:59 +0200220 )
221
222 The ``unit_test_add_suite`` CMake function provides a convenient interface for
223 defining unit test suites. Basically its input is the test source files, include
224 paths and macro definitions and it internally does all the necessary steps to
225 have the test binary registered in CTest as a result.
226
227 Control flow:
228
229 1. Adding new executable named ``NAME``
230
231 2. Iterating throught ``SOURCES``
232
233 1. If it's a normal source file add to the executable's source list
234 2. If it's a YAML file add as a c-picker custom command and add the generated
235 file to the executable's source list
236
237 3. Setting include directories
238
239 4. Setting defines
240
241 5. Adding extra dependencies of the test build
242
243 6. Adding executable to the system as a test
244
245 Inputs:
246
247 ``NAME``
248 Unique name of the test suite
249
250 ``SOURCES`` (multi, optional)
251 Source files
252
253 ``INCLUDE_DIRECTORIES`` (multi, optional)
254 Include directories
255
256 ``COMPILE_DEFINITIONS`` (multi, optional)
257 Defines
258
259 ``DEPENDS`` (multi, optional)
260 Extra targets as dependencies of the test build
261
Imre Kis01ab6532021-03-12 12:42:40 +0100262 ``ARGS`` (multi, optional)
263 Extra command line arguments for the test binary
264
Imre Kisa21712e2019-10-08 12:56:59 +0200265 Global dependencies:
266
267 ``UNIT_TEST_PROJECT_PATH``
268 Root directory of the project under test.
269
270 ``CPICKER_CACHE_PATH``
271 Root directory of the c-picker generated files
272
273 ``UNIT_TEST_COMMON_SOURCES``
274 Common source files for every test build
275
Imre Kis01ab6532021-03-12 12:42:40 +0100276 ``UNIT_TEST_COMMON_ARGS``
277 Common command line arguments for every test binary
278
Imre Kisa21712e2019-10-08 12:56:59 +0200279 ``CTest``
280 Built-in testing module of CMake
281
282#]===]
283function(unit_test_add_suite)
284 set(_OPTIONS_ARGS)
285 set(_ONE_VALUE_ARGS NAME)
Imre Kis01ab6532021-03-12 12:42:40 +0100286 set(_MULTI_VALUE_ARGS SOURCES INCLUDE_DIRECTORIES COMPILE_DEFINITIONS DEPENDS ARGS)
Imre Kisa21712e2019-10-08 12:56:59 +0200287 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
288
289 if(NOT DEFINED BUILD_TESTING)
290 message(FATAL_ERROR
291 "unit_test_add_suite(): "
292 "CTest module should be included in the root CMakeLists.txt before calling this function.")
293 endif()
294
295 if (NOT UNIT_TEST_PROJECT_PATH)
296 message(FATAL_ERROR "UNIT_TEST_PROJECT_PATH is not set")
297 endif()
298
299 set(TEST_NAME ${_MY_PARAMS_NAME})
300 set(TEST_INCLUDE_DIRECTORIES ${_MY_PARAMS_INCLUDE_DIRECTORIES})
301 set(TEST_COMPILE_DEFINITIONS ${_MY_PARAMS_COMPILE_DEFINITIONS})
302 set(TEST_DEPENDS ${_MY_PARAMS_DEPENDS})
Imre Kis01ab6532021-03-12 12:42:40 +0100303 set(TEST_ARGS ${_MY_PARAMS_ARGS})
Imre Kisa21712e2019-10-08 12:56:59 +0200304
305 add_executable(${TEST_NAME} ${UNIT_TEST_COMMON_SOURCES})
306
307 foreach(TEST_SOURCE ${_MY_PARAMS_SOURCES})
308 get_filename_component(TEST_SOURCE_EXTENSION ${TEST_SOURCE} EXT)
309
310 if (${TEST_SOURCE_EXTENSION} STREQUAL ".yml")
311 # Building output file name: tests/a/b/test.yml -> ${CPICKER_CACHE_PATH}/a/b/test.c
312 get_filename_component(TEST_SOURCE_DIR ${TEST_SOURCE} DIRECTORY)
313 file(RELATIVE_PATH CPICKER_SOURCE_DIR ${UNIT_TEST_PROJECT_PATH} ${TEST_SOURCE_DIR})
314 get_filename_component(TEST_SOURCE_NAME ${TEST_SOURCE} NAME_WE)
315 set(CPICKER_OUTPUT ${CPICKER_CACHE_PATH}/${TEST_NAME}/${CPICKER_SOURCE_DIR}/${TEST_SOURCE_NAME}.c)
316
317 # Creating output directory
318 get_filename_component(OUTPUT_DIRECTORY ${CPICKER_OUTPUT} DIRECTORY)
319 file(MAKE_DIRECTORY ${OUTPUT_DIRECTORY})
320
321 # Fetching referenced source files as the dependencies of the generated file
322 execute_process(
323 COMMAND
324 ${CMAKE_COMMAND} -E env CLANG_LIBRARY_PATH=${CLANG_LIBRARY_PATH}
325 ${CPICKER_COMMAND} --config ${TEST_SOURCE} --root ${UNIT_TEST_PROJECT_PATH} --print-dependencies
326 OUTPUT_VARIABLE CPICKER_DEPENDENCIES
327 )
328
329 # Adding custom command for invoking c-picker
330 add_custom_command(
331 OUTPUT ${CPICKER_OUTPUT}
332 COMMAND
333 ${CMAKE_COMMAND} -E env CLANG_LIBRARY_PATH=${CLANG_LIBRARY_PATH}
334 ${CPICKER_COMMAND} --config ${TEST_SOURCE} --root ${UNIT_TEST_PROJECT_PATH} > ${CPICKER_OUTPUT}
335 DEPENDS ${TEST_SOURCE} ${CPICKER_DEPENDENCIES}
336 COMMENT "Generating c-picker output ${CPICKER_OUTPUT}"
337 )
338 set(TEST_SOURCE ${CPICKER_OUTPUT})
339 endif()
340
341 target_sources(${TEST_NAME} PRIVATE ${TEST_SOURCE})
342 endforeach()
343
344 target_include_directories(${TEST_NAME} PRIVATE ${TEST_INCLUDE_DIRECTORIES})
345 target_compile_definitions(${TEST_NAME} PRIVATE ${TEST_COMPILE_DEFINITIONS})
Juan Pablo Condec51baad2023-06-21 11:28:21 -0500346 target_compile_options(${TEST_NAME} PRIVATE ${TEST_COMPILE_OPTIONS})
347 target_link_options(${TEST_NAME} PRIVATE ${TEST_LINK_OPTIONS})
Imre Kisa21712e2019-10-08 12:56:59 +0200348 if (TEST_DEPENDS)
349 add_dependencies(${TEST_NAME} ${TEST_DEPENDS})
350 endif()
Imre Kis01ab6532021-03-12 12:42:40 +0100351 add_test(${TEST_NAME} ${TEST_NAME} ${UNIT_TEST_COMMON_ARGS} ${TEST_ARGS})
Imre Kisa21712e2019-10-08 12:56:59 +0200352endfunction()