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