xref: /aosp_15_r20/external/pigweed/pw_unit_test/test.cmake (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1# Copyright 2022 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14include_guard(GLOBAL)
15
16include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
17
18set(pw_unit_test_ENABLE_PW_ADD_TEST ON CACHE BOOL
19    "Enable or disable pw_add_test calls. This is useful if you would like to \
20     disable test generation when adding Pigweed to an existing project. Set to \
21     OFF before the add_subdirectory(third_party/pigweed) call to prevent tests \
22     from being generated.")
23
24set(pw_unit_test_BACKEND pw_unit_test.light CACHE STRING
25    "CMake target which implements GoogleTest, by default pw_unit_test.light \
26     is used. You could, for example, point this at pw_unit_test.googletest \
27     if using upstream GoogleTest directly on your host for GoogleMock.")
28
29set(pw_unit_test_GOOGLETEST_BACKEND "" CACHE STRING
30    "Warning: Deprecated build argument. When using googletest backend set
31    pw_unit_test_BACKEND to pw_unit_test.googletest instead.")
32
33# TODO(ewout): Remove the default.
34set(pw_unit_test_ADD_EXECUTABLE_FUNCTION "pw_add_test_executable" CACHE STRING
35    "The name of the CMake function used to instantiate pw_unit_test \
36     executables")
37
38# TODO(ewout): Remove the default.
39set(pw_unit_test_ADD_EXECUTABLE_FUNCTION_FILE
40    "$ENV{PW_ROOT}/targets/host/pw_add_test_executable.cmake" CACHE STRING
41    "The path to the .cmake file that defines \
42     pw_unit_test_ADD_EXECUTABLE_FUNCTION.")
43
44# TODO(ewout): Remove the default to match GN and support Windows.
45set(pw_unit_test_AUTOMATIC_RUNNER "$ENV{PW_ROOT}/targets/host/run_test" CACHE
46    STRING
47    "Path to a test runner to automatically run unit tests after they are \
48     built. \
49     If set, a pw_add_test's {NAME}.run action will invoke the test runner \
50     specified by this variable, passing the path to the unit test to run. If \
51     set to an empty string, the {NAME}.run step will fail to build.")
52
53set(pw_unit_test_AUTOMATIC_RUNNER_TIMEOUT_SECONDS "" CACHE STRING
54    "Optional timeout to apply when running tests via the automatic runner. \
55     Timeout is in seconds. Defaults to empty which means no timeout.")
56
57set(pw_unit_test_AUTOMATIC_RUNNER_ARGS "" CACHE STRING
58    "Optional list of arguments to forward to the automatic runner")
59
60# pw_add_test: Declares a single unit test suite with Pigweed naming rules and
61#              compiler warning options.
62#
63#   {NAME} depends on ${NAME}.run if pw_unit_test_AUTOMATIC_RUNNER is set, else
64#          it depends on ${NAME}.bin
65#   {NAME}.lib contains the provided test sources as a library target, which can
66#              then be linked into a test executable.
67#   {NAME}.bin is a standalone executable which contains only the test sources
68#              specified in the pw_unit_test_template.
69#   {NAME}.run which runs the unit test executable after building it if
70#              pw_unit_test_AUTOMATIC_RUNNER is set, else it fails to build.
71#
72# Required Arguments:
73#
74#   NAME: name to use for the produced test targets specified above
75#
76# Optional Arguments:
77#
78#   SOURCES - source files for this library
79#   HEADERS - header files for this library
80#   PRIVATE_DEPS - private pw_target_link_targets arguments
81#   PRIVATE_INCLUDES - public target_include_directories argument
82#   PRIVATE_DEFINES - private target_compile_definitions arguments
83#   PRIVATE_COMPILE_OPTIONS - private target_compile_options arguments
84#   PRIVATE_LINK_OPTIONS - private target_link_options arguments
85#
86#  TODO(ewout, hepler): Deprecate the following legacy arguments
87#   GROUPS - groups to which to add this test.
88#
89function(pw_add_test NAME)
90if("${pw_unit_test_ENABLE_PW_ADD_TEST}")
91  pw_parse_arguments(
92    NUM_POSITIONAL_ARGS
93      1
94    MULTI_VALUE_ARGS
95      SOURCES HEADERS PRIVATE_DEPS PRIVATE_INCLUDES
96      PRIVATE_DEFINES PRIVATE_COMPILE_OPTIONS
97      PRIVATE_LINK_OPTIONS GROUPS
98  )
99
100  _pw_check_name_is_relative_to_root("${NAME}" "$ENV{PW_ROOT}"
101    REMAP_PREFIXES
102      third_party pw_third_party
103  )
104
105  pw_add_test_generic(${NAME}
106    SOURCES
107      ${arg_SOURCES}
108    HEADERS
109      ${arg_HEADERS}
110    PRIVATE_DEPS
111      # TODO: b/232141950 - Apply compilation options that affect ABI
112      # globally in the CMake build instead of injecting them into libraries.
113      pw_build
114      ${arg_PRIVATE_DEPS}
115    PRIVATE_INCLUDES
116      ${arg_PRIVATE_INCLUDES}
117    PRIVATE_DEFINES
118      ${arg_PRIVATE_DEFINES}
119    PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE
120      pw_build.warnings
121    PRIVATE_COMPILE_OPTIONS
122      ${arg_PRIVATE_COMPILE_OPTIONS}
123    PRIVATE_LINK_OPTIONS
124      ${arg_PRIVATE_LINK_OPTIONS}
125    GROUPS
126      ${arg_GROUPS}
127  )
128endif()
129endfunction()
130
131# pw_add_test_generic: Declares a single unit test suite.
132#
133#   {NAME} depends on ${NAME}.run if pw_unit_test_AUTOMATIC_RUNNER is set, else
134#          it depends on ${NAME}.bin
135#   {NAME}.lib contains the provided test sources as a library target, which can
136#              then be linked into a test executable.
137#   {NAME}.bin is a standalone executable which contains only the test sources
138#              specified in the pw_unit_test_template.
139#   {NAME}.run which runs the unit test executable after building it if
140#              pw_unit_test_AUTOMATIC_RUNNER is set, else it fails to build.
141#
142# Required Arguments:
143#
144#   NAME: name to use for the produced test targets specified above
145#
146# Optional Arguments:
147#
148#   SOURCES - source files for this library
149#   HEADERS - header files for this library
150#   PRIVATE_DEPS - private pw_target_link_targets arguments
151#   PRIVATE_INCLUDES - public target_include_directories argument
152#   PRIVATE_DEFINES - private target_compile_definitions arguments
153#   PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE - private target_compile_options BEFORE
154#     arguments from the specified deps's INTERFACE_COMPILE_OPTIONS. Note that
155#     these deps are not pulled in as target_link_libraries. This should not be
156#     exposed by the non-generic API.
157#   PRIVATE_COMPILE_OPTIONS - private target_compile_options arguments
158#   PRIVATE_LINK_OPTIONS - private target_link_options arguments
159#   TEST_MAIN - overrides the default test main dependency
160#
161#  TODO(ewout, hepler): Deprecate the following legacy arguments
162#   GROUPS - groups to which to add this test.
163#
164function(pw_add_test_generic NAME)
165  pw_parse_arguments(
166    NUM_POSITIONAL_ARGS
167      1
168    ONE_VALUE_ARGS
169      TEST_MAIN
170    MULTI_VALUE_ARGS
171      SOURCES HEADERS PRIVATE_DEPS PRIVATE_INCLUDES
172      PRIVATE_DEFINES
173      PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE PRIVATE_COMPILE_OPTIONS
174      PRIVATE_LINK_OPTIONS GROUPS
175  )
176
177  # Add the library target under "${NAME}.lib".
178  # OBJECT libraries require at least one source file.
179  if("${arg_SOURCES}" STREQUAL "")
180    set(lib_type "INTERFACE")
181  else()
182    set(lib_type "OBJECT")
183  endif()
184  pw_add_library_generic("${NAME}.lib" ${lib_type}
185    SOURCES
186      ${arg_SOURCES}
187    HEADERS
188      ${arg_HEADERS}
189    PRIVATE_DEPS
190      pw_unit_test
191      ${arg_PRIVATE_DEPS}
192    PRIVATE_INCLUDES
193      ${arg_PRIVATE_INCLUDES}
194    PRIVATE_DEFINES
195      ${arg_PRIVATE_DEFINES}
196    PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE
197      ${arg_PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE}
198    PRIVATE_COMPILE_OPTIONS
199      ${arg_PRIVATE_COMPILE_OPTIONS}
200    PRIVATE_LINK_OPTIONS
201      ${arg_PRIVATE_LINK_OPTIONS}
202  )
203
204  # Add the executable target under "${NAME}.bin".
205  if(("${pw_unit_test_ADD_EXECUTABLE_FUNCTION}" STREQUAL "") OR
206     ("${pw_unit_test_ADD_EXECUTABLE_FUNCTION_FILE}" STREQUAL ""))
207    pw_add_error_target("${NAME}.bin"
208      MESSAGE
209        "Attempted to build the ${NAME}.bin without enabling the unit "
210        "test executable function via pw_unit_test_ADD_EXECUTABLE_FUNCTION "
211        "and pw_unit_test_ADD_EXECUTABLE_FUNCTION_FILE. "
212        "See https://pigweed.dev/pw_unit_test for more details."
213    )
214  else()
215    include("${pw_unit_test_ADD_EXECUTABLE_FUNCTION_FILE}")
216    if ("${arg_TEST_MAIN}" STREQUAL "")
217      cmake_language(CALL "${pw_unit_test_ADD_EXECUTABLE_FUNCTION}"
218                    "${NAME}.bin" "${NAME}.lib")
219    else()
220      cmake_language(CALL "${pw_unit_test_ADD_EXECUTABLE_FUNCTION}_with_main"
221                    "${NAME}.bin" "${NAME}.lib" "${arg_TEST_MAIN}")
222    endif()
223  endif()
224
225  # Add the ${NAME} target and optionally the run target under ${NAME}.run.
226  add_custom_target("${NAME}")
227  if("${pw_unit_test_AUTOMATIC_RUNNER}" STREQUAL "")
228    # Test runner is not provided, only build the executable.
229    add_dependencies("${NAME}" "${NAME}.bin")
230
231    pw_add_error_target("${NAME}.run"
232      MESSAGE
233        "Attempted to build ${NAME}.run which is not available because "
234        "pw_unit_test_AUTOMATIC_RUNNER has not been configured. "
235        "See https://pigweed.dev/pw_unit_test."
236    )
237  else()  # pw_unit_test_AUTOMATIC_RUNNER is provided.
238    # Define a target for running the test. The target creates a stamp file to
239    # indicate successful test completion. This allows running tests in parallel
240    # with Ninja's full dependency resolution.
241    if(NOT "${pw_unit_test_AUTOMATIC_RUNNER_TIMEOUT_SECONDS}" STREQUAL "")
242      set(optional_timeout_arg
243          "--timeout" "${pw_unit_test_AUTOMATIC_RUNNER_TIMEOUT_SECONDS}")
244    endif()
245    if(NOT "${pw_unit_test_AUTOMATIC_RUNNER_ARGS}" STREQUAL "")
246      set(optional_runner_args "--" "${pw_unit_test_AUTOMATIC_RUNNER_ARGS}")
247    endif()
248    add_custom_command(
249      COMMAND
250        python3 -m pw_unit_test.test_runner
251        --runner "${pw_unit_test_AUTOMATIC_RUNNER}"
252        --test "$<TARGET_FILE:${NAME}.bin>"
253        ${optional_timeout_arg}
254        ${optional_runner_args}
255      COMMAND
256        "${CMAKE_COMMAND}" -E touch "${NAME}.stamp"
257      DEPENDS
258        "${NAME}.bin"
259      OUTPUT
260        "${NAME}.stamp"
261    )
262    add_custom_target("${NAME}.run" DEPENDS "${NAME}.stamp")
263    add_dependencies("${NAME}" "${NAME}.run")
264  endif()
265
266  if(arg_GROUPS)
267    pw_add_test_to_groups("${NAME}" ${arg_GROUPS})
268  endif()
269endfunction(pw_add_test_generic)
270
271# pw_add_test_group: Defines a collection of tests or other test groups.
272#
273# Creates the following targets:
274#
275#   {NAME} depends on ${NAME}.run if pw_unit_test_AUTOMATIC_RUNNER is set, else
276#          it depends on ${NAME}.bin
277#   {NAME}.bundle depends on ${NAME}.bundle.run if pw_unit_test_AUTOMATIC_RUNNER
278#                 is set, else it depends on ${NAME}.bundle.bin
279#   {NAME}.lib depends on ${NAME}.bundle.lib.
280#   {NAME}.bin depends on the provided TESTS's <test_dep>.bin targets.
281#   {NAME}.run depends on the provided TESTS's <test_dep>.run targets if
282#              pw_unit_test_AUTOMATIC_RUNNER is set, else it fails to build.
283#   {NAME}.bundle.lib contains the provided tests bundled as a library target,
284#                     which can then be linked into a test executable.
285#   {NAME}.bundle.bin standalone executable which contains the bundled tests.
286#   {NAME}.bundle.run runs the {NAME}.bundle.bin test bundle executable after
287#                     building it if pw_unit_test_AUTOMATIC_RUNNER is set, else
288#                     it fails to build.
289#
290# Required Arguments:
291#
292#   NAME - The name of the executable target to be created.
293#   TESTS - pw_add_test targets and pw_add_test_group bundles to be included in
294#           this test bundle
295#
296function(pw_add_test_group NAME)
297  pw_parse_arguments(
298    NUM_POSITIONAL_ARGMENTS
299      1
300    MULTI_VALUE_ARGS
301      TESTS
302    REQUIRED_ARGS
303      TESTS
304  )
305
306  set(test_lib_targets "")
307  set(test_bin_targets "")
308  set(test_run_targets "")
309  foreach(test IN LISTS arg_TESTS)
310    list(APPEND test_lib_targets "${test}.lib")
311    list(APPEND test_bin_targets "${test}.bin")
312    list(APPEND test_run_targets "${test}.run")
313  endforeach()
314
315  # This produces ${NAME}.bundle, ${NAME}.bundle.lib, ${NAME}.bundle.bin, and
316  # ${NAME}.bundle.run.
317  pw_add_test("${NAME}.bundle"
318    PRIVATE_DEPS
319      ${test_lib_targets}
320  )
321
322  # Produce ${NAME}.lib.
323  pw_add_library_generic("${NAME}.lib" INTERFACE
324    PUBLIC_DEPS
325      ${NAME}.bundle.lib
326  )
327
328  # Produce ${NAME}.bin.
329  add_custom_target("${NAME}.bin")
330  add_dependencies("${NAME}.bin" ${test_bin_targets})
331
332  # Produce ${NAME} and ${NAME}.run.
333  add_custom_target("${NAME}")
334  if("${pw_unit_test_AUTOMATIC_RUNNER}" STREQUAL "")
335    # Test runner is not provided, only build the executable.
336    add_dependencies("${NAME}" "${NAME}.bin")
337
338    pw_add_error_target("${NAME}.run"
339      MESSAGE
340        "Attempted to build ${NAME}.run which is not available because "
341        "pw_unit_test_AUTOMATIC_RUNNER has not been configured. "
342        "See https://pigweed.dev/pw_unit_test."
343    )
344  else()  # pw_unit_test_AUTOMATIC_RUNNER is provided, build and run the test.
345    add_custom_target("${NAME}.run")
346    add_dependencies("${NAME}.run" ${test_run_targets})
347
348    add_dependencies("${NAME}" "${NAME}.run")
349  endif()
350endfunction(pw_add_test_group)
351
352# Adds a test target to the specified test groups. Test groups can be built with
353# the pw_tests_GROUP_NAME target or executed with the pw_run_tests_GROUP_NAME
354# target.
355function(pw_add_test_to_groups TEST_NAME)
356  foreach(group IN LISTS ARGN)
357    if(NOT TARGET "pw_tests.${group}")
358      add_custom_target("pw_tests.${group}")
359      add_custom_target("pw_run_tests.${group}")
360    endif()
361
362    add_dependencies("pw_tests.${group}" "${TEST_NAME}.bin")
363    add_dependencies("pw_run_tests.${group}" "${TEST_NAME}.run")
364  endforeach()
365endfunction(pw_add_test_to_groups)
366