# Copyright 2019 The Pigweed Authors # # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy of # the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. import("//build_overrides/pigweed.gni") import("$dir_pw_build/python_action.gni") import("$dir_pw_build/target_types.gni") declare_args() { # Path to a test runner to automatically run unit tests after they are built. # # If set, the pw_test() template creates an action that invokes the test runner # on each test executable. If unset, the pw_test() template only creates a test # executable target. # # This should only be enabled for targets which support parallelized running of # unit tests, such as desktops with multiple cores. pw_unit_test_AUTOMATIC_RUNNER = "" # Additional dependencies required by all unit test targets. (For example, if # using a different test library like Googletest.) pw_unit_test_PUBLIC_DEPS = [] # Implementation of a main function for "pw_test" unit test binaries. pw_unit_test_MAIN = "$dir_pw_unit_test:simple_printing_main" } # Defines a target if enable_if is true. Otherwise, it defines that target as # .DISABLED and creates an empty group. This can be # used to conditionally create targets without having to conditionally add them # to groups. This results in simpler BUILD.gn files. template("_pw_disableable_target") { assert(defined(invoker.enable_if), "`enable_if` is required for _pw_disableable_target") assert(defined(invoker.target_type), "`target_type` is required for _pw_disableable_target") if (invoker.enable_if) { _actual_target_name = target_name } else { _actual_target_name = target_name + ".DISABLED" # If the target is disabled, create an empty target in its place. Use an # action with the original target's sources as inputs to ensure that # the source files exist (even if they don't compile). pw_python_action(target_name) { script = "$dir_pw_build/py/pw_build/nop.py" stamp = true inputs = [] if (defined(invoker.sources)) { inputs += invoker.sources } if (defined(invoker.public)) { inputs += invoker.public } } } target(invoker.target_type, _actual_target_name) { forward_variables_from(invoker, "*", [ "enable_if", "target_type", ]) # Remove "" from dependencies. This allows disabling targets if a variable # (e.g. a backend) is empty. if (defined(public_deps)) { public_deps += [ "" ] public_deps -= [ "" ] } if (defined(deps)) { deps += [ "" ] deps -= [ "" ] } } } # Creates a library and an executable target for a unit test. # # .lib contains the provided test sources as a library, which can # then be linked into a test executable. # is a standalone executable which contains only the test sources # specified in the pw_unit_test_template. # # If the pw_unit_test_AUTOMATIC_RUNNER variable is set, this template also creates a # "${test_name}.run" target which runs the unit test executable after building # it. # # Args: # - enable_if: (optional) Conditionally enables or disables this test. The test # target and *.run target do nothing when the test is disabled. The # disabled test can still be built and run with the # .DISABLED and .DISABLED.run targets. # Defaults to true (enable_if). # - All of the regular "executable" target args are accepted. template("pw_test") { # This is required in order to reference the pw_test template's target name # within the test_metadata of the metadata group below. The group() definition # creates a new scope where the "target_name" variable is set to its target, # shadowing the one in this scope. _test_target_name = target_name _test_is_enabled = !defined(invoker.enable_if) || invoker.enable_if # Always set the output_dir as pigweed is not compatible with shared # bin directories for tests. _test_output_dir = "${target_out_dir}/test" if (defined(invoker.output_dir)) { _test_output_dir = invoker.output_dir } _test_main = pw_unit_test_MAIN if (defined(invoker.test_main)) { _test_main = invoker.test_main } # The unit test code as a source_set. _pw_disableable_target("$target_name.lib") { target_type = "pw_source_set" enable_if = _test_is_enabled forward_variables_from(invoker, "*", [ "metadata" ]) if (!defined(public_deps)) { public_deps = [] } public_deps += pw_unit_test_PUBLIC_DEPS + [ dir_pw_unit_test ] } _pw_disableable_target(_test_target_name) { target_type = "pw_executable" enable_if = _test_is_enabled # Metadata for this test when used as part of a pw_test_group target. metadata = { tests = [ { type = "test" test_name = _test_target_name test_directory = rebase_path(_test_output_dir, root_build_dir) }, ] } deps = [ ":$_test_target_name.lib" ] if (_test_main != "") { deps += [ _test_main ] } output_dir = _test_output_dir } if (pw_unit_test_AUTOMATIC_RUNNER != "") { # When the automatic runner is set, create an action which runs the unit # test executable using the test runner script. if (_test_is_enabled) { _test_to_run = _test_target_name } else { # Create a run target for the .DISABLED version of the test. _test_to_run = _test_target_name + ".DISABLED" # Create a placeholder _run target for the regular version of the test. group(_test_target_name + ".run") { deps = [ ":$_test_target_name" ] } } pw_python_action(_test_to_run + ".run") { deps = [ ":$_test_target_name" ] inputs = [ pw_unit_test_AUTOMATIC_RUNNER ] script = "$dir_pw_unit_test/py/pw_unit_test/test_runner.py" python_deps = [ "$dir_pw_cli/py" ] args = [ "--runner", rebase_path(pw_unit_test_AUTOMATIC_RUNNER, root_build_dir), "--test", "", ] stamp = true } # TODO(frolv): Alias for the deprecated _run target. Remove when projects # are migrated. group(_test_to_run + "_run") { public_deps = [ ":$_test_to_run.run" ] } } else { group(_test_target_name + ".run") { } } } # Defines a related collection of unit tests. # # pw_test_group targets output a JSON metadata file for the Pigweed test runner. # # Args: # - tests: List of pw_test targets for each of the tests in the group. # - group_deps: (optional) pw_test_group targets on which this group depends. # - enable_if: (optional) Conditionally enables or disables this test group. # If false, an empty group is created. Defaults to true. template("pw_test_group") { _group_target = target_name _group_deps_metadata = [] if (defined(invoker.tests)) { _deps = invoker.tests } else { _deps = [] } _group_is_enabled = !defined(invoker.enable_if) || invoker.enable_if if (_group_is_enabled) { if (defined(invoker.group_deps)) { # If the group specified any other group dependencies, create a metadata # entry for each of them indicating that they are another group and a # group target to collect that metadata. foreach(dep, invoker.group_deps) { _group_deps_metadata += [ { type = "dep" group = get_label_info(dep, "label_no_toolchain") }, ] } _deps += invoker.group_deps } group(_group_target + ".lib") { deps = [] foreach(_target, _deps) { _dep_target = get_label_info(_target, "label_no_toolchain") _dep_toolchain = get_label_info(_target, "toolchain") deps += [ "$_dep_target.lib($_dep_toolchain)" ] } } _metadata_group_target = "${target_name}_pw_test_group_metadata" group(_metadata_group_target) { metadata = { group_deps = _group_deps_metadata self = [ { type = "self" name = get_label_info(":$_group_target", "label_no_toolchain") }, ] # Metadata from the group's own unit test targets is forwarded through # the group dependencies group. This entry is listed as a "walk_key" in # the generated file so that only test targets' metadata (not group # targets) appear in the output. if (defined(invoker.tests)) { propagate_metadata_from = invoker.tests } } deps = _deps } _test_group_deps = [ ":$_metadata_group_target" ] generated_file(_group_target) { outputs = [ "$target_out_dir/$target_name.testinfo.json" ] data_keys = [ "group_deps", "self", "tests", ] walk_keys = [ "propagate_metadata_from" ] output_conversion = "json" deps = _test_group_deps } # If automatic test running is enabled, create a *.run group that collects # all of the individual *.run targets and groups. if (pw_unit_test_AUTOMATIC_RUNNER != "") { group(_group_target + ".run") { deps = [ ":$_group_target" ] foreach(_target, _deps) { _dep_target = get_label_info(_target, "label_no_toolchain") _dep_toolchain = get_label_info(_target, "toolchain") deps += [ "$_dep_target.run($_dep_toolchain)" ] } } # TODO(frolv): Remove this deprecated alias. group(_group_target + "_run") { deps = [ ":$_group_target.run" ] } } } else { # _group_is_enabled # Create empty groups for the tests to avoid pulling in any dependencies. group(_group_target) { } group(_group_target + ".lib") { } if (pw_unit_test_AUTOMATIC_RUNNER != "") { group(_group_target + ".run") { } # TODO(frolv): Remove this deprecated alias. group(_group_target + "_run") { } } not_needed("*") not_needed(invoker, "*") } # All of the tests in this group and its dependencies bundled into a single # test binary. pw_test(_group_target + ".bundle") { deps = [ ":$_group_target.lib" ] enable_if = _group_is_enabled } }