1 # Copyright 2019 The Pigweed Authors
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
7 # https://www.apache.org/licenses/LICENSE-2.0
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
15 import("//build_overrides/pigweed.gni")
17 import("$dir_pw_build/python_action.gni")
18 import("$dir_pw_build/target_types.gni")
21 # Path to a test runner to automatically run unit tests after they are built.
23 # If set, the pw_test() template creates an action that invokes the test runner
24 # on each test executable. If unset, the pw_test() template only creates a test
27 # This should only be enabled for targets which support parallelized running of
28 # unit tests, such as desktops with multiple cores.
29 pw_unit_test_AUTOMATIC_RUNNER = ""
31 # Additional dependencies required by all unit test targets. (For example, if
32 # using a different test library like Googletest.)
33 pw_unit_test_PUBLIC_DEPS = []
35 # Implementation of a main function for "pw_test" unit test binaries.
36 pw_unit_test_MAIN = "$dir_pw_unit_test:simple_printing_main"
39 # Defines a target if enable_if is true. Otherwise, it defines that target as
40 # <target_name>.DISABLED and creates an empty <target_name> group. This can be
41 # used to conditionally create targets without having to conditionally add them
42 # to groups. This results in simpler BUILD.gn files.
43 template("_pw_disableable_target") {
44 assert(defined(invoker.enable_if),
45 "`enable_if` is required for _pw_disableable_target")
46 assert(defined(invoker.target_type),
47 "`target_type` is required for _pw_disableable_target")
49 if (invoker.enable_if) {
50 _actual_target_name = target_name
52 _actual_target_name = target_name + ".DISABLED"
54 # If the target is disabled, create an empty target in its place. Use an
55 # action with the original target's sources as inputs to ensure that
56 # the source files exist (even if they don't compile).
57 pw_python_action(target_name) {
58 script = "$dir_pw_build/py/pw_build/nop.py"
62 if (defined(invoker.sources)) {
63 inputs += invoker.sources
65 if (defined(invoker.public)) {
66 inputs += invoker.public
71 target(invoker.target_type, _actual_target_name) {
72 forward_variables_from(invoker,
79 # Remove "" from dependencies. This allows disabling targets if a variable
80 # (e.g. a backend) is empty.
81 if (defined(public_deps)) {
92 # Creates a library and an executable target for a unit test.
94 # <target_name>.lib contains the provided test sources as a library, which can
95 # then be linked into a test executable.
96 # <target_name> is a standalone executable which contains only the test sources
97 # specified in the pw_unit_test_template.
99 # If the pw_unit_test_AUTOMATIC_RUNNER variable is set, this template also creates a
100 # "${test_name}.run" target which runs the unit test executable after building
104 # - enable_if: (optional) Conditionally enables or disables this test. The test
105 # target and *.run target do nothing when the test is disabled. The
106 # disabled test can still be built and run with the
107 # <target_name>.DISABLED and <target_name>.DISABLED.run targets.
108 # Defaults to true (enable_if).
109 # - All of the regular "executable" target args are accepted.
110 template("pw_test") {
111 # This is required in order to reference the pw_test template's target name
112 # within the test_metadata of the metadata group below. The group() definition
113 # creates a new scope where the "target_name" variable is set to its target,
114 # shadowing the one in this scope.
115 _test_target_name = target_name
117 _test_is_enabled = !defined(invoker.enable_if) || invoker.enable_if
119 # Always set the output_dir as pigweed is not compatible with shared
120 # bin directories for tests.
121 _test_output_dir = "${target_out_dir}/test"
122 if (defined(invoker.output_dir)) {
123 _test_output_dir = invoker.output_dir
126 _test_main = pw_unit_test_MAIN
127 if (defined(invoker.test_main)) {
128 _test_main = invoker.test_main
131 # The unit test code as a source_set.
132 _pw_disableable_target("$target_name.lib") {
133 target_type = "pw_source_set"
134 enable_if = _test_is_enabled
135 forward_variables_from(invoker, "*", [ "metadata" ])
137 if (!defined(public_deps)) {
140 public_deps += pw_unit_test_PUBLIC_DEPS + [ dir_pw_unit_test ]
143 _pw_disableable_target(_test_target_name) {
144 target_type = "pw_executable"
145 enable_if = _test_is_enabled
147 # Metadata for this test when used as part of a pw_test_group target.
152 test_name = _test_target_name
153 test_directory = rebase_path(_test_output_dir, root_build_dir)
158 deps = [ ":$_test_target_name.lib" ]
159 if (_test_main != "") {
160 deps += [ _test_main ]
163 output_dir = _test_output_dir
166 if (pw_unit_test_AUTOMATIC_RUNNER != "") {
167 # When the automatic runner is set, create an action which runs the unit
168 # test executable using the test runner script.
169 if (_test_is_enabled) {
170 _test_to_run = _test_target_name
172 # Create a run target for the .DISABLED version of the test.
173 _test_to_run = _test_target_name + ".DISABLED"
175 # Create a dummy _run target for the regular version of the test.
176 group(_test_target_name + ".run") {
177 deps = [ ":$_test_target_name" ]
181 pw_python_action(_test_to_run + ".run") {
182 deps = [ ":$_test_target_name" ]
183 inputs = [ pw_unit_test_AUTOMATIC_RUNNER ]
184 script = "$dir_pw_unit_test/py/pw_unit_test/test_runner.py"
185 python_deps = [ "$dir_pw_cli/py" ]
188 rebase_path(pw_unit_test_AUTOMATIC_RUNNER),
190 "<TARGET_FILE(:$_test_to_run)>",
195 # TODO(frolv): Alias for the deprecated _run target. Remove when projects
197 group(_test_to_run + "_run") {
198 public_deps = [ ":$_test_to_run.run" ]
201 group(_test_target_name + ".run") {
206 # Defines a related collection of unit tests.
208 # pw_test_group targets output a JSON metadata file for the Pigweed test runner.
211 # - tests: List of pw_test targets for each of the tests in the group.
212 # - group_deps: (optional) pw_test_group targets on which this group depends.
213 # - enable_if: (optional) Conditionally enables or disables this test group.
214 # If false, an empty group is created. Defaults to true.
215 template("pw_test_group") {
216 _group_target = target_name
217 _group_deps_metadata = []
218 if (defined(invoker.tests)) {
219 _deps = invoker.tests
224 _group_is_enabled = !defined(invoker.enable_if) || invoker.enable_if
226 if (_group_is_enabled) {
227 if (defined(invoker.group_deps)) {
228 # If the group specified any other group dependencies, create a metadata
229 # entry for each of them indicating that they are another group and a
230 # group target to collect that metadata.
231 foreach(dep, invoker.group_deps) {
232 _group_deps_metadata += [
235 group = get_label_info(dep, "label_no_toolchain")
240 _deps += invoker.group_deps
243 group(_group_target + ".lib") {
245 foreach(_target, _deps) {
246 _dep_target = get_label_info(_target, "label_no_toolchain")
247 _dep_toolchain = get_label_info(_target, "toolchain")
248 deps += [ "$_dep_target.lib($_dep_toolchain)" ]
252 _metadata_group_target = "${target_name}_pw_test_group_metadata"
253 group(_metadata_group_target) {
255 group_deps = _group_deps_metadata
259 name = get_label_info(":$_group_target", "label_no_toolchain")
263 # Metadata from the group's own unit test targets is forwarded through
264 # the group dependencies group. This entry is listed as a "walk_key" in
265 # the generated file so that only test targets' metadata (not group
266 # targets) appear in the output.
267 if (defined(invoker.tests)) {
268 propagate_metadata_from = invoker.tests
274 _test_group_deps = [ ":$_metadata_group_target" ]
276 generated_file(_group_target) {
277 outputs = [ "$target_out_dir/$target_name.testinfo.json" ]
283 walk_keys = [ "propagate_metadata_from" ]
284 output_conversion = "json"
285 deps = _test_group_deps
288 # If automatic test running is enabled, create a *.run group that collects
289 # all of the individual *.run targets and groups.
290 if (pw_unit_test_AUTOMATIC_RUNNER != "") {
291 group(_group_target + ".run") {
292 deps = [ ":$_group_target" ]
293 foreach(_target, _deps) {
294 _dep_target = get_label_info(_target, "label_no_toolchain")
295 _dep_toolchain = get_label_info(_target, "toolchain")
296 deps += [ "$_dep_target.run($_dep_toolchain)" ]
300 # TODO(frolv): Remove this deprecated alias.
301 group(_group_target + "_run") {
302 deps = [ ":$_group_target.run" ]
305 } else { # _group_is_enabled
306 # Create empty groups for the tests to avoid pulling in any dependencies.
307 group(_group_target) {
309 group(_group_target + ".lib") {
312 if (pw_unit_test_AUTOMATIC_RUNNER != "") {
313 group(_group_target + ".run") {
316 # TODO(frolv): Remove this deprecated alias.
317 group(_group_target + "_run") {
322 not_needed(invoker, "*")
325 # All of the tests in this group and its dependencies bundled into a single
327 pw_test(_group_target + ".bundle") {
328 deps = [ ":$_group_target.lib" ]
329 enable_if = _group_is_enabled