# Copyright 2015 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # This file contains templates which are meant to simplify building and # running test binaries with the Chromecast build infrastructure. See # documentation above each template for specific use. # # Example Usage # # # This is a standard test() template from //testing/test.gni. This generates # # a binary called foo_unittests. # test("foo_unittests") { # sources = [ "foo_unittest.cc" ] # # deps = [ # ":foo", # "//testing/gtest", # "//testing/gmock", # ] # } # # # And another standard test() target, which generates bar_unittests # test("bar_unittests") { # sources = [ "bar_unittest.cc" ] # # deps = [ ... ] # } # # # This is an organizational target. This cannot be built directly. # cast_test_group("cast_tests") { # tests = [ # ":bar_unittests", # "//path/to:foo_unittests", # ] # } # # # Here is another cast_test_group target which builds a bunch of other # # test binaries, and wants to apply some filters. # cast_test_group("external_tests") { # filters = [] # tests = [ # "//path/to/widget:widget_unittests", # "//even/more/foo:foo_unittests", # ] # # widget_unittests_filter = { # test_name = "widgets_unittests" # gtest_includes = [ "WidgetTest.*" ] # gtest_excludes = [ "WidgetTest.TestToBeFilteredOut", # "WidgetTest.MoreTestsToFilterOut" ] # args = [ "--extra_arg", # "--another_arg", ] # } # filters += [ widget_unittests_filter ] # } # # # Build this to create build and run lists for bar and foo tests. # cast_test_group_list("cast_test_lists") { # test_groups = [ # ":cast_tests", # ":external_tests", # ] # # build_list_paths = "$root_out_dir/path/to/test/build_list.txt" # run_list_path = "$root_out_dir/path/to/list/run_list.txt" # runtime_deps_path = "$root_out_dir/path/to/list/runtime_deps.json" # } import("//chromecast/chromecast.gni") import("//testing/test.gni") declare_args() { # Set this true to build all tests in a cast_test_group_list by default. This # can be overriden in each cast_test_group_list instance by setting # |build_tests|. build_cast_tests_by_default = true } # Do not allow mixing of gtests and junit tests. All gtests must go into one # directory, while all junit tests must go into another directory. _gtest_gen_dir = "$root_gen_dir/chromecast/tests" _junit_gen_dir = "$root_gen_dir/chromecast/junit" # A group of test executables. Including test targets in this group makes it # possible for Chromecast build infrastructure to build and run them with # filters. To accomplish this, it defines two actions, one which generates a # build list of all |tests|, and one which generates a run list of all |tests|. # It also creates a group with dependencies on each test, to ensure that they # are valid targets. Do not build these targets. Build cast_test_group_list # instead. # # Parameters # tests (required) # A list of test targets included in the assembly. Do not include any # other type of target. Each target's name must match the name of the # executable it builds. # # filters (optional) # A list of scopes, where each scope has the has the follow variables: # test_name (required): The name of the test executable. This must # correspond to a test in |tests|. # gtest_includes (optional): A list of gtest filter pattern strings. # Only tests that match one of the positive patterns (if provided)\ # will be run. # Example: gtest_includes = [ "Foo.*", "Bar.Test1", "Bar.Test2" ] # will get translated to "--gtest_filter=Foo.*:Bar.Test1:Bar.Test2" # gtest_excludes (optional): A list of gtest filter pattern strings. # Only tests that do not match any of the negative patterns # (if provided) will be run. # Example: gtest_excludes = [ "Baz.*", "Biz.Test" ] # will get translated to "--gtest_filter=-Baz.*:Biz.Test1" # args (optional): A list of additional args to pass to the test. # Please see //chromecast/tools/build/generate_test_lists.py for more # information. # If |filters| is not defined, no filters are applied. # # priority (optional) # A string which takes any single-digit integer bewtween "1" and "9", # inclusive. Assign this to prioritize filters applied by other # cast_test_groups, where a higher number trumps a lower number. # If not assigned, priority defaults to "1", the lowest priority. # # test_type (optional) # A string, which must be either "junit" or "gtest". If not defined, # defaults to "gtest". # template("cast_test_group") { assert(defined(invoker.tests), "$target_name needs 'tests' listing the test() targets") _test_type = "gtest" if (defined(invoker.test_type)) { assert(invoker.test_type == "gtest" || invoker.test_type == "junit") _test_type = invoker.test_type } if (_test_type == "gtest") { _shared_dir = _gtest_gen_dir } else if (_test_type == "junit") { _shared_dir = _junit_gen_dir } # If a set of filters has not been defined, use the empty list. _filters = [] if (defined(invoker.filters)) { foreach(filter, invoker.filters) { assert(defined(filter.test_name), "A filter must have a test_name.") _test_name = filter.test_name _gtest_include_filter = "" if (defined(filter.gtest_includes)) { foreach(include, filter.gtest_includes) { _gtest_include_filter = "${_gtest_include_filter}:${include}" } } _gtest_exclude_filter = "" if (defined(filter.gtest_excludes)) { foreach(exclude, filter.gtest_excludes) { _gtest_exclude_filter = "${_gtest_exclude_filter}:${exclude}" } } _gtest_filter = "" if (_gtest_include_filter != "" || _gtest_exclude_filter != "") { _gtest_filter += "--gtest_filter=" if (_gtest_include_filter != "") { _gtest_filter += _gtest_include_filter } if (_gtest_exclude_filter != "") { _gtest_filter += "-" + _gtest_exclude_filter } } _args = "" if (defined(filter.args)) { foreach(arg, filter.args) { _args = "${_args} ${arg}" } } # Only add filters that actually do something, otherwise the # generate script will get confused. if (_args != "" || _gtest_filter != "") { _filters += [ "${_test_name} ${_args} ${_gtest_filter}" ] } } } # If priority has not been set, set the priority to "1", the lowest priority. _priority = "1" if (defined(invoker.priority)) { _priority = invoker.priority } # Assert that |_priority| is an integer between "1" and "9", inclusive. assert(_priority == "1" || _priority == "2" || _priority == "3" || _priority == "4" || _priority == "5" || _priority == "6" || _priority == "7" || _priority == "8" || _priority == "9") # This will be the prefix of each output file. _output_prefix = "$_shared_dir/$_priority-$target_name" # Create a list of all the target names. These must correspond to the name of # the test binary. _test_names = [] foreach(_test, invoker.tests) { _test_names += [ get_label_info(_test, "name") ] } # Create a dummy target so that we can get the runtime deps for each test # and output them into a file. The runtime deps include all of the data files, # data directories, and shared libraries that a test needs in order to run. foreach(_test, invoker.tests) { _test_name = get_label_info(_test, "name") group(_test_name + "_cast_runtime_deps") { testonly = true data_deps = [ _test, ] write_runtime_deps = "${_shared_dir}/runtime_deps/${_test_name}_runtime_deps.txt" } } # This action generates a list of target names to build and run. It will be # depended upon by the "pack_build" action of the cast_test_group_list # instance which depends on this cast_test_group. action(target_name + "_create_list") { script = "//chromecast/tools/build/generate_test_lists.py" outputs = [ "$_output_prefix.tests", ] args = [ "-o", rebase_path("$_output_prefix.tests"), "create_list", ] args += _test_names deps = [] if (defined(invoker.deps)) { foreach(_dep, invoker.deps) { deps += [ _dep + "_create_list" ] } } } # This action generates a list of test filters, which will have a priority # [1-9]. This will be depended upon by the "pack_run" action of the # cast_test_group_list which depends on this group. action(target_name + "_filters") { script = "//chromecast/tools/build/generate_test_lists.py" outputs = [ "$_output_prefix.filters", ] args = [ "-o", rebase_path("$_output_prefix.filters"), "create_list", ] args += _filters deps = [] if (defined(invoker.deps)) { foreach(_dep, invoker.deps) { deps += [ _dep + "_filters" ] } } } # This target allows us to reference each test as a fully-qualified GN path, # to ensure that each path is correct. If a test does not exist, gives a # helpful error message at the line it is included. Do not build this target # directly. group(target_name + "_build_tests") { testonly = true deps = invoker.tests if (defined(invoker.deps)) { foreach(_dep, invoker.deps) { deps += [ _dep + "_build_tests" ] } } } } # This template runs a script which generates lists of test to be built and run. # # Parameters # test_groups (required) # The cast_test_group() targets for which this binary is to be created. # The targets referenced here must be cast_test_group targets, or buiding # this target will fail. # # build_list_path (required) # The absolute filepath of the output file which will hold the list of # tests to be built. # # run_list_path (required) # The absolute filepath of the output file which will hold the list of # tests to be run, each with filters assigned by cast_groups. # # runtime_deps_path (required) # The absolute filepath of the output file which will hold the json dict # of runtime dependencies for each test to be run. # # additional_options (optional) # Options which are passed to the python script, and applied to every test # # build_tests (optional) # If true, all of the tests included in |test_groups| will be built as # dependencies of this target. If false, only the lists will be generated. # If not explicitly set, this defaults to the value of # |build_cast_tests_by_default|. Note that if this is set to true, # the test targets will be built after all the lists are generated. # # test_type (optional) # A string, which must be either "junit" or "gtest". If not defined, # defaults to "gtest". # template("cast_test_group_list") { assert(defined(invoker.test_groups), "$target_name needs 'test_groups'") assert(defined(invoker.run_list_path), "$target_name needs 'run_list_path'") assert(defined(invoker.build_list_path), "$target_name needs 'build_list_path'") assert(defined(invoker.runtime_deps_path), "$target_name needs 'runtime_deps_path'") _pack_build_action = target_name + "_pack_build" _test_type = "gtest" if (defined(invoker.test_type)) { assert(invoker.test_type == "gtest" || invoker.test_type == "junit") _test_type = invoker.test_type } if (_test_type == "gtest") { _shared_dir = _gtest_gen_dir } else if (_test_type == "junit") { _shared_dir = _junit_gen_dir } # Add the run list to runtime data dependencies data = [ invoker.run_list_path, invoker.runtime_deps_path, ] # Generate a list of the "create_list" actions for each group. These will be # depended upon to ensure they're run before the "pack_build" step. _build_actions = [] foreach(_test_group, invoker.test_groups) { _build_actions += [ _test_group + "_create_list" ] } # Generate a list of the "filter" actions for each group. These will be # depended upon to ensure they're run before the "pack_run" step. _filter_actions = [] foreach(_test_group, invoker.test_groups) { _filter_actions += [ _test_group + "_filters" ] } # Decide whether tests are built as dependencies. _build_cast_tests = build_cast_tests_by_default if (defined(invoker.build_tests)) { _build_cast_tests = invoker.build_tests } # Generate a list of the groups of targets, so that they can be depended upon # by the "pack_run" step and built when this target is invoked. if (_build_cast_tests) { _build_test_targets = [] foreach(_test_group, invoker.test_groups) { _build_test_targets += [ _test_group + "_build_tests" ] } } # The "pack_build" step. This step looks in the common folder for files with # the ".tests" extenstion, collecting these and packing them into an output # file. The steps which create these files are depeneded upon, to ensure # they're run before this step. Do not invoke this target directly. action(_pack_build_action) { script = "//chromecast/tools/build/generate_test_lists.py" outputs = [ invoker.build_list_path, ] args = [ "-o", rebase_path(invoker.build_list_path), "-t", rebase_path(_shared_dir), "pack_build", ] deps = _build_actions } # The "pack_run" step. This step looks in the common folder for files with # the ".tests" and ".filters" extensions, creating a script of tests to run, # with filters and priorities. See # //chromecast/tools/build/generate_test_lists.py for more information. # Note that this target takes the name of the invoker, such that invoking the # target runs this step. action(target_name) { testonly = true script = "//chromecast/tools/build/generate_test_lists.py" outputs = [ invoker.run_list_path, invoker.runtime_deps_path, ] args = [ "-o", rebase_path(invoker.run_list_path), "-d", rebase_path(invoker.runtime_deps_path), "-t", rebase_path(_shared_dir), "pack_run", ] # Add addtional options if they have been set. if (defined(invoker.additional_options)) { args += [ "-a" ] args += invoker.additional_options } # Depend first on the "pack_build" step, so that the build lists are created. deps = [ ":$_pack_build_action", ] # Next, depend on the filter steps, such that they are created before this # script executes. deps += _filter_actions # If |_build_cast_tests| has been set to true, depend on the testing targets # so that the tests are built. if (_build_cast_tests) { deps += _build_test_targets } if (chromecast_branding != "public") { deps += [ "//chromecast/internal:cast_test_checker" ] } } }