# Copyright 2015 The Chromium Authors
# 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", root_build_dir),
"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", root_build_dir),
"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, root_build_dir),
"-t",
rebase_path(_shared_dir, root_build_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, root_build_dir),
"-d",
rebase_path(invoker.runtime_deps_path, root_build_dir),
"-t",
rebase_path(_shared_dir, root_build_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" ]
}
}
}
# This template creates a tar.gz file with test dependencies.
#
# Parameters
# output (required)
# The absolute path to the tar.gz file to be created.
#
# deps_list_path (required)
# Absolute path to runtime dependencies file. This has to point to the
# same file runtime_deps_path of cast_test_group_list is pointing to.
#
# exclude_deps (optional)
# gn list of paths to filter out from the dependencies list.
# This is used to remove folders with huge files that are not needed.
#
# additional_deps (optional)
# gn list of paths to be added to the list of dependencies
# before creating the tar.gz file.
#
# This template has to be used in combination with cast_test_group_list
# that generates the runtime dependencies file.
#
# Example usage:
#
# cast_test_group_list("chromecast_test_lists") {
# build_list_path = "$root_out_dir/tests/build_test_list.txt"
# runtime_deps_path = "$root_out_dir/tests/runtime_deps.json"
# run_list_path = "$root_out_dir/tests/run_test_list.txt"
# test_groups = [ ":chromecast_test_group" ]
# }
#
# cast_test_deps_archive("chromecast_test_deps_archive") {
# output = "$root_out_dir/test_deps.tar.gz"
# deps_list_path = "$root_out_dir/tests/runtime_deps.json"
# exclude_deps = [
# "exe.unstripped",
# "lib.unstripped"
# ]
# additional_deps = [
# "tests",
# ]
# deps = [
# ":chromecast_test_lists"
# ]
# }
#
template("cast_test_deps_archive") {
assert(defined(invoker.output), "$target_name needs 'output'")
assert(defined(invoker.deps_list_path), "$target_name needs 'deps_list_path'")
deps = invoker.deps
action(target_name) {
testonly = true
script = "//chromecast/tools/build/package_test_deps.py"
outputs = [ invoker.output ]
# Generate a comma separated list of filters.
_exclude_deps = ""
foreach(_exclude_dep, invoker.exclude_deps) {
_exclude_deps += "," + _exclude_dep
}
# Generate a comma separated list of includes.
_additional_deps = ""
foreach(_additional_dep, invoker.additional_deps) {
_additional_deps += "," + _additional_dep
}
args = [
"--output",
rebase_path(invoker.output, root_build_dir),
"--deps_list_path",
rebase_path(invoker.deps_list_path, root_build_dir),
"--exclude_deps",
_exclude_deps,
"--additional_deps",
_additional_deps,
]
}
}