1# Copyright 2019 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. 14 15import("//build_overrides/pigweed.gni") 16 17import("$dir_pw_build/python_action.gni") 18import("$dir_pw_build/target_types.gni") 19 20declare_args() { 21 # Path to a test runner to automatically run unit tests after they are built. 22 # 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 25 # executable target. 26 # 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 = "" 30 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 = [] 34 35 # Implementation of a main function for "pw_test" unit test binaries. 36 pw_unit_test_MAIN = "$dir_pw_unit_test:simple_printing_main" 37} 38 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. 43template("_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") 48 49 if (invoker.enable_if) { 50 _actual_target_name = target_name 51 } else { 52 _actual_target_name = target_name + ".DISABLED" 53 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" 59 stamp = true 60 61 inputs = [] 62 if (defined(invoker.sources)) { 63 inputs += invoker.sources 64 } 65 if (defined(invoker.public)) { 66 inputs += invoker.public 67 } 68 } 69 } 70 71 target(invoker.target_type, _actual_target_name) { 72 forward_variables_from(invoker, 73 "*", 74 [ 75 "enable_if", 76 "target_type", 77 ]) 78 79 # Remove "" from dependencies. This allows disabling targets if a variable 80 # (e.g. a backend) is empty. 81 if (defined(public_deps)) { 82 public_deps += [ "" ] 83 public_deps -= [ "" ] 84 } 85 if (defined(deps)) { 86 deps += [ "" ] 87 deps -= [ "" ] 88 } 89 } 90} 91 92# Creates a library and an executable target for a unit test. 93# 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. 98# 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 101# it. 102# 103# Args: 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. 110template("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 116 117 _test_is_enabled = !defined(invoker.enable_if) || invoker.enable_if 118 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 124 } 125 126 _test_main = pw_unit_test_MAIN 127 if (defined(invoker.test_main)) { 128 _test_main = invoker.test_main 129 } 130 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" ]) 136 137 if (!defined(public_deps)) { 138 public_deps = [] 139 } 140 public_deps += pw_unit_test_PUBLIC_DEPS + [ dir_pw_unit_test ] 141 } 142 143 _pw_disableable_target(_test_target_name) { 144 target_type = "pw_executable" 145 enable_if = _test_is_enabled 146 147 # Metadata for this test when used as part of a pw_test_group target. 148 metadata = { 149 tests = [ 150 { 151 type = "test" 152 test_name = _test_target_name 153 test_directory = rebase_path(_test_output_dir, root_build_dir) 154 }, 155 ] 156 } 157 158 deps = [ ":$_test_target_name.lib" ] 159 if (_test_main != "") { 160 deps += [ _test_main ] 161 } 162 163 output_dir = _test_output_dir 164 } 165 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 171 } else { 172 # Create a run target for the .DISABLED version of the test. 173 _test_to_run = _test_target_name + ".DISABLED" 174 175 # Create a dummy _run target for the regular version of the test. 176 group(_test_target_name + ".run") { 177 deps = [ ":$_test_target_name" ] 178 } 179 } 180 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" ] 186 args = [ 187 "--runner", 188 rebase_path(pw_unit_test_AUTOMATIC_RUNNER), 189 "--test", 190 "<TARGET_FILE(:$_test_to_run)>", 191 ] 192 stamp = true 193 } 194 195 # TODO(frolv): Alias for the deprecated _run target. Remove when projects 196 # are migrated. 197 group(_test_to_run + "_run") { 198 public_deps = [ ":$_test_to_run.run" ] 199 } 200 } else { 201 group(_test_target_name + ".run") { 202 } 203 } 204} 205 206# Defines a related collection of unit tests. 207# 208# pw_test_group targets output a JSON metadata file for the Pigweed test runner. 209# 210# Args: 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. 215template("pw_test_group") { 216 _group_target = target_name 217 _group_deps_metadata = [] 218 if (defined(invoker.tests)) { 219 _deps = invoker.tests 220 } else { 221 _deps = [] 222 } 223 224 _group_is_enabled = !defined(invoker.enable_if) || invoker.enable_if 225 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 += [ 233 { 234 type = "dep" 235 group = get_label_info(dep, "label_no_toolchain") 236 }, 237 ] 238 } 239 240 _deps += invoker.group_deps 241 } 242 243 group(_group_target + ".lib") { 244 deps = [] 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)" ] 249 } 250 } 251 252 _metadata_group_target = "${target_name}_pw_test_group_metadata" 253 group(_metadata_group_target) { 254 metadata = { 255 group_deps = _group_deps_metadata 256 self = [ 257 { 258 type = "self" 259 name = get_label_info(":$_group_target", "label_no_toolchain") 260 }, 261 ] 262 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 269 } 270 } 271 deps = _deps 272 } 273 274 _test_group_deps = [ ":$_metadata_group_target" ] 275 276 generated_file(_group_target) { 277 outputs = [ "$target_out_dir/$target_name.testinfo.json" ] 278 data_keys = [ 279 "group_deps", 280 "self", 281 "tests", 282 ] 283 walk_keys = [ "propagate_metadata_from" ] 284 output_conversion = "json" 285 deps = _test_group_deps 286 } 287 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)" ] 297 } 298 } 299 300 # TODO(frolv): Remove this deprecated alias. 301 group(_group_target + "_run") { 302 deps = [ ":$_group_target.run" ] 303 } 304 } 305 } else { # _group_is_enabled 306 # Create empty groups for the tests to avoid pulling in any dependencies. 307 group(_group_target) { 308 } 309 group(_group_target + ".lib") { 310 } 311 312 if (pw_unit_test_AUTOMATIC_RUNNER != "") { 313 group(_group_target + ".run") { 314 } 315 316 # TODO(frolv): Remove this deprecated alias. 317 group(_group_target + "_run") { 318 } 319 } 320 321 not_needed("*") 322 not_needed(invoker, "*") 323 } 324 325 # All of the tests in this group and its dependencies bundled into a single 326 # test binary. 327 pw_test(_group_target + ".bundle") { 328 deps = [ ":$_group_target.lib" ] 329 enable_if = _group_is_enabled 330 } 331} 332