| # 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. | |
| # ============================================================================== | |
| # TEST SETUP | |
| # ============================================================================== | |
| import("//build/config/chromeos/args.gni") | |
| import("//build/config/chromeos/ui_mode.gni") | |
| import("//build/config/devtools.gni") | |
| import("//build/config/gclient_args.gni") | |
| import("//build/config/rts.gni") | |
| import("//build/rust/rust_static_library.gni") | |
| import("//build_overrides/build.gni") | |
| declare_args() { | |
| # Some component repos (e.g. ANGLE) import //testing but do not have | |
| # "location_tags.json", and so we don't want to try and upload the tags | |
| # for their tests. | |
| # And, some build configs may simply turn off generation altogether. | |
| tests_have_location_tags = generate_location_tags | |
| # Build individual_fuzztest_wrapper if use_fuzztest_wrapper is set. | |
| # Some projects doesn't have //base and cannot build | |
| # individual_fuzztest_wrapper. | |
| use_fuzztest_wrapper = true | |
| } | |
| # On Fuchsia, the test executable has a suffix and is a dependency of the | |
| # common |target_name| target. For `visibility`, the executable must be | |
| # specified. Cross-platform targets that include `test` targets in their | |
| # visibility lists, add `${exec_target_suffix}` immediately after the test | |
| # target name. This is not necessary when the target is a `source_set`. | |
| if (is_fuchsia) { | |
| exec_target_suffix = "__exec" | |
| } else { | |
| exec_target_suffix = "" | |
| } | |
| if (is_android) { | |
| import("//build/android/test_wrapper/logdog_wrapper.gni") | |
| import("//build/config/android/config.gni") | |
| import("//build/config/android/create_unwind_table.gni") | |
| import("//build/config/android/rules.gni") | |
| import("//build/config/sanitizers/sanitizers.gni") | |
| } else if (is_fuchsia) { | |
| import("//build/config/fuchsia/generate_runner_scripts.gni") | |
| import("//third_party/fuchsia-gn-sdk/src/cmc.gni") | |
| import("//third_party/fuchsia-gn-sdk/src/component.gni") | |
| import("//third_party/fuchsia-gn-sdk/src/package.gni") | |
| } else if (is_chromeos && is_chromeos_device) { | |
| import("//build/config/chromeos/rules.gni") | |
| import("//build/config/sanitizers/sanitizers.gni") | |
| import("//build/util/generate_wrapper.gni") | |
| } else if (is_ios) { | |
| import("//build/config/ios/ios_sdk.gni") | |
| import("//build/config/ios/ios_test_runner_wrapper.gni") | |
| import("//build/config/ios/rules.gni") | |
| } else { | |
| import("//build/config/sanitizers/sanitizers.gni") | |
| import("//build/util/generate_wrapper.gni") | |
| } | |
| # This template represents the core common functionality of a test target | |
| # on each platform. It includes: | |
| # * the ability to generate a rust library that includes all .rs files found | |
| # in sources and depends on that from the test target. | |
| # * the ability to recognize any declare fuzztests and build runners for them. | |
| template("mixed_test") { | |
| assert(defined(invoker.target_type) && invoker.target_type != "") | |
| # The crate_root variable would transform the target into a Rust binary | |
| # which is incorrect. To not use a generated crate root set: | |
| # ``` | |
| # test_crate_root = "path/to/root.rs" | |
| # ``` | |
| assert(!defined(invoker.crate_root)) | |
| _rs_vars = [ | |
| "sources", # We split this list into two. | |
| "crate_name", # Android test template overrides the crate name. | |
| ] | |
| if (defined(invoker.sources)) { | |
| _rs_sources = filter_include(invoker.sources, [ "*.rs" ]) | |
| _cc_sources = filter_exclude(invoker.sources, [ "*.rs" ]) | |
| } else { | |
| _rs_sources = [] | |
| _cc_sources = [] | |
| } | |
| if (_rs_sources != []) { | |
| # Note: as a weak convention, __ is usually used before a suffix for | |
| # internally-generated targets. However, rust_target requires a strict | |
| # snake_case name. | |
| if (defined(invoker.crate_name)) { | |
| _rust_target_name = "${invoker.crate_name}_rust_objects" | |
| } else { | |
| _rust_target_name = "${target_name}_rust_objects" | |
| } | |
| # We could automatically add `deps += [ "//testing/rust_gtest_interop" ]` | |
| # if `rs_sources` is non-empty. But we don't automatically provide | |
| # //testing/gtest either so it would be asymmetric and could break in that | |
| # case. So, we act instead as if //testing/rust_gtest_interop is part of | |
| # the //testing/gtest dependency. If you add one, and have `rs_sources` | |
| # listed, you get both. | |
| _gtest_is_in_deps = false | |
| if (defined(invoker.deps) && invoker.deps != []) { | |
| foreach(dep, invoker.deps) { | |
| if (get_label_info(dep, "label_no_toolchain") == | |
| "//testing/gtest:gtest") { | |
| _gtest_is_in_deps = true | |
| } | |
| } | |
| } | |
| # TODO(danakj): This could be a rust_source_set perhaps, the point being | |
| # that we need to link in all the .o object files inside the library, | |
| # instead of dropping unreachable ones during linking (which would drop the | |
| # tests). Alternatively we could use a special name suffix or other similar | |
| # trick perhaps to ensure that all object files are linked in here. | |
| rust_static_library(_rust_target_name) { | |
| forward_variables_from(invoker, | |
| TESTONLY_AND_VISIBILITY + [ | |
| "allow_unsafe", | |
| "deps", | |
| "generate_crate_root", | |
| "public_deps", | |
| ]) | |
| configs += [ "//build/rust:test" ] | |
| if (defined(invoker.test_crate_root)) { | |
| crate_root = invoker.test_crate_root | |
| } else { | |
| generate_crate_root = true | |
| } | |
| sources = _rs_sources | |
| is_gtest_unittests = true | |
| if (_gtest_is_in_deps) { | |
| deps += [ "//testing/rust_gtest_interop" ] | |
| } | |
| } | |
| } else { | |
| not_needed(invoker, _rs_vars) | |
| } | |
| if (invoker.target_type == "shared_library_with_jni") { | |
| # Needed for shared_library_with_jni. Keeping this import guarded so | |
| # that projects who import //testing but not //third_party/jni_zero | |
| # don't have issues. | |
| import("//third_party/jni_zero/jni_zero.gni") | |
| } | |
| _building_fuzztest_fuzzer = | |
| defined(invoker.fuzztests) && use_fuzzing_engine && (is_linux || is_mac) | |
| # Fuzz tests are small fuzzers that do not require particularly-powerful | |
| # machines to run, so we do not build them when `high_end_fuzzer_targets` | |
| # is true and we are building fuzztests in fuzzing mode. | |
| if (_building_fuzztest_fuzzer && high_end_fuzzer_targets) { | |
| not_needed(invoker, "*") | |
| not_needed("*") | |
| # We still want a reachable target, so make it a no-op empty group. This | |
| # will let the fuzzer builders crawl the build graph and invoke ninja in | |
| # the same way regardless of GN args. | |
| group(target_name) { | |
| } | |
| } else { | |
| target(invoker.target_type, target_name) { | |
| forward_variables_from(invoker, | |
| "*", | |
| TESTONLY_AND_VISIBILITY + _rs_vars + [ | |
| "fuzztests", | |
| "fuzztest_additional_args", | |
| "libfuzzer_options", | |
| "centipede_options", | |
| "asan_options", | |
| "msan_options", | |
| "ubsan_options", | |
| "environment_variables", | |
| "grammar_options", | |
| ]) | |
| forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) | |
| sources = _cc_sources | |
| if (!defined(deps)) { | |
| deps = [] | |
| } | |
| if (!defined(ldflags)) { | |
| ldflags = [] | |
| } | |
| if (_rs_sources != []) { | |
| deps += [ ":${_rust_target_name}" ] | |
| } | |
| if (defined(invoker.fuzztests)) { | |
| deps += [ | |
| "//third_party/fuzztest", | |
| # The following target contains a static initializer which | |
| # will ensure that the code in base/test:test_support | |
| # ends up calling through to fuzztest::InitFuzzTest. | |
| "//testing/libfuzzer:confirm_fuzztest_init", | |
| ] | |
| } | |
| } | |
| } | |
| if (_building_fuzztest_fuzzer && !high_end_fuzzer_targets) { | |
| # This test contains fuzztests. We want to package them up in a way | |
| # which ClusterFuzz knows how to extract. We need to: | |
| # 1) make an executable for each individual fuzz test; | |
| # 2) check that the fuzztests variable is correct. | |
| # At present, all this is likely to work only if invoker.target_type | |
| # is 'executable', since we generate a wrapper script that assumes so. | |
| # At the moment, we aim to fuzz these fuzztests only on Linux so that's | |
| # fine. In future we may need to broaden this. | |
| # It makes no sense to have an empty fuzztests variable, let's forbid it. | |
| assert(invoker.fuzztests != []) | |
| if (defined(invoker.output_name)) { | |
| _output_name = invoker.output_name | |
| } else { | |
| _output_name = target_name | |
| } | |
| _fuzzer_binary_extension = "" | |
| if (is_win) { | |
| _fuzzer_binary_extension = ".exe" | |
| } | |
| # This will be the actual name of the fuzzer binary generated by | |
| # `target_name`. | |
| _fuzzer_binary_name = _output_name + _fuzzer_binary_extension | |
| _fuzztest_target_name = target_name | |
| # Additional command-line arguments passed to fuzzer executables, common to | |
| # all fuzz tests in this executable. | |
| _common_fuzzer_args = [ "--corpus_database=" ] | |
| if (defined(invoker.fuzztest_additional_args)) { | |
| _common_fuzzer_args += invoker.fuzztest_additional_args | |
| } | |
| # Confirming that the "fuzztests =" directive is correct can only | |
| # be done on builds where we can confidently run the fuzzing binary. | |
| # Let's be conservative about that -- so long as any failures are | |
| # spotted by at least one CI bot we should be good. | |
| confirm_fuzztest_contents = is_asan || !using_sanitizer | |
| if (confirm_fuzztest_contents) { | |
| # Confirm that the fuzztests GN variable matches with the | |
| # actual fuzztests in the binary. The output of this action is unused. | |
| # It just exists to fail the build if there's an error. | |
| # We only do this on Linux, and not for any sanitizers other than | |
| # ASAN, because that's specific for CI to show problems and there | |
| # may be unknown problems running the fuzztest binary on other | |
| # platforms. | |
| _fuzztest_check_action = target_name + "__fuzztest_check" | |
| action(_fuzztest_check_action) { | |
| deps = [ ":" + _fuzztest_target_name ] | |
| testonly = true | |
| script = "//testing/libfuzzer/confirm_fuzztests.py" | |
| _output_name = "$target_gen_dir/${target_name}__checked.txt" | |
| outputs = [ _output_name ] | |
| args = [ | |
| "--executable", | |
| rebase_path( | |
| get_label_info(_fuzztest_target_name, "root_out_dir") + | |
| "/" + _fuzzer_binary_name), | |
| "--output", | |
| rebase_path(_output_name), | |
| "--fuzztests", | |
| ] + invoker.fuzztests | |
| } | |
| } | |
| # Make a wrapper executable for each individual fuzz test | |
| if (use_fuzztest_wrapper) { | |
| foreach(fuzztest_unit, invoker.fuzztests) { | |
| _fuzzer_name = target_name + "_" + | |
| string_replace(fuzztest_unit, ".", "_") + "_fuzzer" | |
| # We generate an actual executable because currently our fuzz | |
| # builder recipes use `gn refs --type=executable` to find things | |
| # to build. Otherwise we could use generate_wrapper or equivalent | |
| # to make a python script. We could alter the recipe, or rearrange | |
| # deps arragenements so that some other executable depends on these | |
| # scripts, but that seems worse. The executable might be more cross- | |
| # platform too. | |
| _fuzztest_generate_fuzzer = _fuzzer_name + "__generate" | |
| generated_file(_fuzztest_generate_fuzzer + "_constants") { | |
| outputs = [ "$target_gen_dir/$target_name/constants.cpp" ] | |
| _fuzzer_args = _common_fuzzer_args + [ "--fuzz=${fuzztest_unit}" ] | |
| fuzzer_args_joined = string_join(" ", _fuzzer_args) | |
| NEWLINE = "$0x0A" | |
| contents = | |
| "const char* kFuzzerArgs = \"${fuzzer_args_joined}\";" + NEWLINE + | |
| "const char* kFuzzerBinary = \"${_fuzzer_binary_name}\";" + | |
| NEWLINE | |
| } | |
| _additional_fuzz_deps = [] | |
| gen_fuzzer_config_args = [] | |
| if (defined(invoker.libfuzzer_options)) { | |
| gen_fuzzer_config_args += [ "--libfuzzer_options" ] | |
| gen_fuzzer_config_args += invoker.libfuzzer_options | |
| } | |
| if (defined(invoker.centipede_options)) { | |
| gen_fuzzer_config_args += [ "--centipede_options" ] | |
| gen_fuzzer_config_args += invoker.centipede_options | |
| } | |
| if (defined(invoker.asan_options)) { | |
| gen_fuzzer_config_args += [ "--asan_options" ] | |
| gen_fuzzer_config_args += invoker.asan_options | |
| } | |
| if (defined(invoker.msan_options)) { | |
| gen_fuzzer_config_args += [ "--msan_options" ] | |
| gen_fuzzer_config_args += invoker.msan_options | |
| } | |
| if (defined(invoker.ubsan_options)) { | |
| gen_fuzzer_config_args += [ "--ubsan_options" ] | |
| gen_fuzzer_config_args += invoker.ubsan_options | |
| } | |
| if (defined(invoker.environment_variables)) { | |
| gen_fuzzer_config_args += [ "--environment_variables" ] | |
| gen_fuzzer_config_args += invoker.environment_variables | |
| } | |
| if (defined(invoker.grammar_options)) { | |
| gen_fuzzer_config_args += [ "--grammar_options" ] | |
| gen_fuzzer_config_args += invoker.grammar_options | |
| } | |
| if (gen_fuzzer_config_args != []) { | |
| _config_file_name = _fuzzer_name + ".options" | |
| _additional_fuzz_deps += [ ":" + _config_file_name ] | |
| # Generate .options file. | |
| action(_config_file_name) { | |
| script = "//testing/libfuzzer/gen_fuzzer_config.py" | |
| args = [ | |
| "--config", | |
| rebase_path("$root_build_dir/" + _config_file_name, | |
| root_build_dir), | |
| ] | |
| args += gen_fuzzer_config_args | |
| outputs = [ "$root_build_dir/$_config_file_name" ] | |
| } | |
| } | |
| _fuzzer_target_name = target_name | |
| executable(_fuzztest_generate_fuzzer) { | |
| testonly = true | |
| data_deps = [ | |
| ":" + _fuzztest_target_name, | |
| ":" + _fuzzer_target_name, | |
| ] + _additional_fuzz_deps | |
| deps = [ | |
| "//testing/libfuzzer:individual_fuzztest_wrapper", | |
| ":" + _fuzztest_generate_fuzzer + "_constants", | |
| ] | |
| if (confirm_fuzztest_contents) { | |
| deps += [ ":" + _fuzztest_check_action ] | |
| } | |
| output_name = _fuzzer_name | |
| sources = | |
| get_target_outputs(":" + _fuzztest_generate_fuzzer + "_constants") | |
| write_runtime_deps = "$root_build_dir/${_fuzzer_name}.runtime_deps" | |
| } | |
| } | |
| } else { | |
| not_needed([ | |
| "_common_fuzzer_args", | |
| "_fuzzer_binary_name", | |
| "_fuzztest_target_name", | |
| ]) | |
| } | |
| } | |
| } | |
| # Define a test as an executable (or apk on Android) with the "testonly" flag | |
| # set. | |
| # Variable: | |
| # use_xvfb: (optional) whether to run the executable under Xvfb. | |
| # use_raw_android_executable: Use executable() rather than android_apk(). | |
| # use_native_activity: Test implements ANativeActivity_onCreate(). | |
| # test_runner_shard: (Fuchsia, optional): for CFv2 tests, use the given test | |
| # runner shard rather than the default shard for the ELF runner when | |
| # assembling the test component. This is useful, for example, to use the | |
| # elf_test_ambient_exec_runner for tests that require | |
| # job_policy_ambient_mark_vmo_exec. | |
| # fuchsia_package_deps: (Fuchsia, optional) List of fuchsia_component() | |
| # targets that this test package contains. | |
| # is_xctest: (iOS, optional) whether to build the executable as XCTest. | |
| # Similar to the GN arg 'enable_run_ios_unittests_with_xctest' but | |
| # for build targets. | |
| # allow_cleartext_traffic: (Android, optional) whether to allow cleartext | |
| # network requests during the test. | |
| # fuzztests: a list of instances of the FUZZ_TEST macro to | |
| # include fuzzing tests alongside unit tests. This introduces an | |
| # extra dependency and also creates additional metadata so that our | |
| # fuzzing infrastructure can find and run such tests. | |
| # This should be a list of the test names, for example | |
| # fuzztests = [ "MyTestClass.MyTestName" ] | |
| # fuzztest_additional_args: additional arguments to be forwarded to fuzztest. | |
| template("test") { | |
| # Ensures a test filter file exists and if not, creates a dummy file. | |
| # The Regression Test Selection (rts) flag is passed in mb.py and used | |
| # to filter out test cases. Flag ensures that a file exists. | |
| if (use_rts) { | |
| action("${target_name}__rts_filters") { | |
| script = "//build/add_rts_filters.py" | |
| rts_file = "${root_build_dir}/gen/rts/${invoker.target_name}.filter" | |
| args = [ rebase_path(rts_file, root_build_dir) ] | |
| outputs = [ rts_file ] | |
| } | |
| } | |
| testonly = true | |
| if (!is_ios) { | |
| assert(!defined(invoker.is_xctest) || !invoker.is_xctest, | |
| "is_xctest can be set only for iOS builds") | |
| } | |
| if (!is_android) { | |
| assert(!defined(invoker.allow_cleartext_traffic), | |
| "allow_cleartext_traffic can be set only for Android tests") | |
| } | |
| if (is_android) { | |
| assert(!defined(invoker.use_xvfb) || !invoker.use_xvfb) | |
| _use_default_launcher = | |
| !defined(invoker.use_default_launcher) || invoker.use_default_launcher | |
| if (!defined(invoker.use_raw_android_executable)) { | |
| # Checkouts where build_with_chromium == false often have a custom GN | |
| # template wrapper around test() which sets use_default_launcher == false. | |
| # Set the _use_raw_android_executable default so that test() targets which | |
| # do not use the custom wrapper | |
| # (1) Do not cause "gn gen" to fail | |
| # (2) Do not need to be moved into if(build_with_chromium) block. | |
| _use_raw_android_executable = | |
| !build_with_chromium && _use_default_launcher | |
| } else { | |
| not_needed([ "_use_default_launcher" ]) | |
| _use_raw_android_executable = invoker.use_raw_android_executable | |
| } | |
| # output_name is used to allow targets with the same name but in different | |
| # packages to still produce unique runner scripts. | |
| _output_name = invoker.target_name | |
| if (defined(invoker.output_name)) { | |
| _output_name = invoker.output_name | |
| } | |
| _test_runner_target = "${_output_name}__test_runner_script" | |
| _wrapper_script_vars = [ | |
| "additional_apks", | |
| "android_test_runner_script", | |
| "extra_args", | |
| "ignore_all_data_deps", | |
| "shard_timeout", | |
| ] | |
| assert(_use_raw_android_executable || enable_java_templates) | |
| if (_use_raw_android_executable) { | |
| not_needed(invoker, [ "add_unwind_tables_in_apk" ]) | |
| _exec_target = "${target_name}__exec" | |
| _dist_target = "${target_name}__dist" | |
| _exec_output = | |
| "$target_out_dir/${invoker.target_name}/${invoker.target_name}" | |
| _crate_name = "${target_name}" | |
| mixed_test(_exec_target) { | |
| target_type = "executable" | |
| # Use a crate name that avoids creating a warning due to double | |
| # underscore (ie. `__`). | |
| crate_name = _crate_name | |
| # Configs will always be defined since we set_defaults in | |
| # BUILDCONFIG.gn. | |
| configs = [] | |
| forward_variables_from( | |
| invoker, | |
| "*", | |
| TESTONLY_AND_VISIBILITY + _wrapper_script_vars + [ | |
| "data_deps", | |
| "extra_dist_files", | |
| ]) | |
| # Thanks to the set_defaults() for test(), configs are initialized with | |
| # the default shared_library configs rather than executable configs. | |
| configs -= [ | |
| "//build/config:shared_library_config", | |
| "//build/config/android:hide_all_but_jni", | |
| ] | |
| configs += [ "//build/config:executable_config" ] | |
| if (defined(invoker.data_deps)) { | |
| data_deps = invoker.data_deps | |
| } else { | |
| data_deps = [] | |
| } | |
| if (!defined(data)) { | |
| data = [] | |
| } | |
| if (tests_have_location_tags) { | |
| data += [ "//testing/location_tags.json" ] | |
| } | |
| if (!defined(deps)) { | |
| deps = [] | |
| } | |
| # Don't output to the root or else conflict with the group() below. | |
| output_name = rebase_path(_exec_output, root_out_dir) | |
| if (use_rts) { | |
| data_deps += [ ":${invoker.target_name}__rts_filters" ] | |
| } | |
| } | |
| create_native_executable_dist(_dist_target) { | |
| dist_dir = "$root_out_dir/$target_name" | |
| binary = _exec_output | |
| deps = [ ":$_exec_target" ] | |
| if (defined(invoker.extra_dist_files)) { | |
| extra_files = invoker.extra_dist_files | |
| } | |
| if (use_rts) { | |
| if (!defined(data_deps)) { | |
| data_deps = [] | |
| } | |
| data_deps += [ ":${invoker.target_name}__rts_filters" ] | |
| } | |
| } | |
| } else { | |
| _library_target_name = "${target_name}__library" | |
| _library_crate_name = "${target_name}_library" | |
| _apk_target_name = "${target_name}__apk" | |
| _apk_specific_vars = [ | |
| "allow_cleartext_traffic", | |
| "android_manifest", | |
| "android_manifest_dep", | |
| "android_manifest_template", | |
| "app_as_shared_lib", | |
| "keystore_name", | |
| "keystore_password", | |
| "keystore_path", | |
| "library_always_compress", | |
| "loadable_module_deps", | |
| "loadable_modules", | |
| "min_sdk_version", | |
| "product_config_java_packages", | |
| "proguard_configs", | |
| "proguard_enabled", | |
| "srcjar_deps", | |
| "target_sdk_version", | |
| "use_default_launcher", | |
| "use_native_activity", | |
| ] | |
| _add_unwind_tables_in_apk = | |
| defined(invoker.add_unwind_tables_in_apk) && | |
| invoker.add_unwind_tables_in_apk && target_cpu == "arm" | |
| # Adds the unwind tables from unstripped binary as an asset file in the | |
| # apk, if |add_unwind_tables_in_apk| is specified by the test. | |
| if (_add_unwind_tables_in_apk) { | |
| _unwind_table_v2_name = "${_library_target_name}_unwind_v2" | |
| unwind_table_v2(_unwind_table_v2_name) { | |
| library_target = ":$_library_target_name" | |
| } | |
| _unwind_table_asset_name = "${target_name}__unwind_assets" | |
| android_assets(_unwind_table_asset_name) { | |
| disable_compression = true | |
| sources = [ "$target_out_dir/$_unwind_table_v2_name/$unwind_table_asset_v2_filename" ] | |
| deps = [ ":$_unwind_table_v2_name" ] | |
| } | |
| } | |
| _generate_final_jni = | |
| !defined(invoker.generate_final_jni) || invoker.generate_final_jni | |
| mixed_test(_library_target_name) { | |
| if (_generate_final_jni) { | |
| target_type = "shared_library_with_jni" | |
| java_targets = [ ":$_apk_target_name" ] | |
| } else { | |
| target_type = "shared_library" | |
| } | |
| # Configs will always be defined since we set_defaults in | |
| # BUILDCONFIG.gn. | |
| configs = [] # Prevent list overwriting warning. | |
| configs = invoker.configs | |
| forward_variables_from( | |
| invoker, | |
| "*", | |
| [ | |
| "configs", | |
| "deps", | |
| ] + _apk_specific_vars + _wrapper_script_vars + | |
| TESTONLY_AND_VISIBILITY) | |
| # Use a crate name that avoids creating a warning due to double | |
| # underscore (ie. `__`). | |
| crate_name = _library_crate_name | |
| # Native targets do not need to depend on java targets. Filter them out | |
| # so that the shared library can be built without needing to wait for | |
| # dependent java targets. | |
| if (!defined(deps)) { | |
| deps = [] | |
| } | |
| if (defined(invoker.deps)) { | |
| deps += filter_exclude(invoker.deps, java_target_patterns) | |
| } | |
| if (_use_default_launcher) { | |
| deps += [ "//testing/android/native_test:native_test_native_code" ] | |
| } | |
| } | |
| if (defined(invoker.output_name)) { | |
| _apk_name = invoker.output_name | |
| } else { | |
| _apk_name = invoker.target_name | |
| } | |
| _final_apk_path = | |
| "$root_build_dir/${_apk_name}_apk/${_apk_name}-debug.apk" | |
| unittest_apk(_apk_target_name) { | |
| forward_variables_from(invoker, _apk_specific_vars) | |
| apk_name = _apk_name | |
| final_apk_path = _final_apk_path | |
| shared_library = ":$_library_target_name" | |
| if (_generate_final_jni) { | |
| srcjar_deps = [ "${shared_library}__jni_registration" ] | |
| } | |
| if (defined(invoker.deps)) { | |
| deps = invoker.deps | |
| } else { | |
| deps = [] | |
| } | |
| if (defined(loadable_module_deps)) { | |
| deps += loadable_module_deps | |
| } | |
| # Add the Java classes so that each target does not have to do it. | |
| if (_use_default_launcher) { | |
| deps += [ "//base/test:test_support_java" ] | |
| } | |
| if (defined(_unwind_table_asset_name)) { | |
| deps += [ ":${_unwind_table_asset_name}" ] | |
| } | |
| if (use_rts) { | |
| data_deps = [ ":${invoker.target_name}__rts_filters" ] | |
| } | |
| } | |
| } | |
| test_runner_script(_test_runner_target) { | |
| forward_variables_from(invoker, _wrapper_script_vars) | |
| if (_use_raw_android_executable) { | |
| executable_dist_dir = "$root_out_dir/$_dist_target" | |
| data_deps = [ ":$_exec_target" ] | |
| } else { | |
| apk_target = ":$_apk_target_name" | |
| incremental_apk = incremental_install | |
| # Dep needed for the test runner .runtime_deps file to pick up data | |
| # deps from the forward_variables_from(invoker, "*") on the library. | |
| data_deps = [ ":$_library_target_name" ] | |
| if (defined(invoker.proguard_enabled) && invoker.proguard_enabled) { | |
| proguard_mapping_path = "$_final_apk_path.mapping" | |
| } | |
| } | |
| test_name = _output_name | |
| test_suite = _output_name | |
| test_type = "gtest" | |
| if (use_rts) { | |
| data_deps += [ ":${invoker.target_name}__rts_filters" ] | |
| } | |
| } | |
| # Create a wrapper script rather than using a group() in order to ensure | |
| # "ninja $target_name" always works. If this was a group(), then GN would | |
| # not create a top-level alias for it if a target exists in another | |
| # directory with the same $target_name. | |
| # Also - bots run this script directly for "components_perftests". | |
| generate_wrapper(target_name) { | |
| forward_variables_from(invoker, [ "visibility" ]) | |
| executable = "$root_build_dir/bin/run_$_output_name" | |
| wrapper_script = "$root_build_dir/$_output_name" | |
| deps = [ ":$_test_runner_target" ] | |
| if (_use_raw_android_executable) { | |
| deps += [ ":$_dist_target" ] | |
| } else { | |
| # Dep needed for the swarming .isolate file to pick up data | |
| # deps from the forward_variables_from(invoker, "*") on the library. | |
| deps += [ | |
| ":$_apk_target_name", | |
| ":$_library_target_name", | |
| ] | |
| } | |
| if (defined(invoker.data_deps)) { | |
| data_deps = invoker.data_deps | |
| } else { | |
| data_deps = [] | |
| } | |
| data_deps += [ "//testing:test_scripts_shared" ] | |
| if (tests_have_location_tags) { | |
| data = [ "//testing/location_tags.json" ] | |
| } | |
| if (use_rts) { | |
| data_deps += [ ":${invoker.target_name}__rts_filters" ] | |
| } | |
| } | |
| } else if (is_fuchsia) { | |
| assert(!defined(invoker.use_xvfb) || !invoker.use_xvfb) | |
| _output_name = invoker.target_name | |
| _pkg_target = "${_output_name}_pkg" | |
| _exec_target = "${_output_name}__exec" | |
| _program_name = get_label_info(":${_exec_target}", "name") | |
| _crate_name = _output_name | |
| # Generate a CML fragment that provides the program name. | |
| _test_program_fragment_target = "${target_name}_program-fragment" | |
| _test_program_fragment = "${target_out_dir}/${target_name}_program.test-cml" | |
| generated_file(_test_program_fragment_target) { | |
| contents = { | |
| program = { | |
| binary = _program_name | |
| } | |
| } | |
| outputs = [ _test_program_fragment ] | |
| output_conversion = "json" | |
| } | |
| _test_runner_shard = | |
| "//build/config/fuchsia/test/elf_test_runner.shard.test-cml" | |
| if (defined(invoker.test_runner_shard)) { | |
| _test_runner_shard = invoker.test_runner_shard | |
| } | |
| # Collate the complete set of elements to include in the test component's | |
| # manifest. | |
| _manifest_fragments = [ | |
| _test_program_fragment, | |
| _test_runner_shard, | |
| ] | |
| _subpackages = [] | |
| # Select the Fuchsia test realm in which to run the test. | |
| if (defined(invoker.run_as_chromium_system_test) && | |
| invoker.run_as_chromium_system_test) { | |
| _manifest_fragments += [ | |
| "//build/config/fuchsia/test/chromium_system_test_facet.shard.test-cml", | |
| "//build/config/fuchsia/test/system_test_minimum.shard.test-cml", | |
| ] | |
| } else { | |
| _subpackages += [ | |
| "//third_party/fuchsia-sdk/sdk/packages/fake-build-info:fake-build-info", | |
| "//third_party/fuchsia-sdk/sdk/packages/intl_property_manager:intl_property_manager", | |
| ] | |
| _manifest_fragments += [ | |
| "//build/config/fuchsia/test/chromium_test_facet.shard.test-cml", | |
| "//build/config/fuchsia/test/minimum.shard.test-cml", | |
| ] | |
| } | |
| if (is_asan) { | |
| # TODO(crbug.com/40276216): Remove the extra cml segment for asan. | |
| _manifest_fragments += | |
| [ "//build/config/fuchsia/test/asan_options.shard.test-cml" ] | |
| } | |
| _test_component_manifest = "${target_out_dir}/${target_name}.cml" | |
| _merged_manifest_name = "${_output_name}.cml" | |
| if (defined(invoker.additional_manifest_fragments)) { | |
| _manifest_fragments += invoker.additional_manifest_fragments | |
| } | |
| # Generate the test component manifest from the specified elements. | |
| _test_component_manifest_target = "${target_name}_component-manifest" | |
| cmc_merge(_test_component_manifest_target) { | |
| sources = _manifest_fragments | |
| output_name = "${_merged_manifest_name}" | |
| deps = [ ":${_test_program_fragment_target}" ] | |
| } | |
| # Define the test component, dependent on the generated manifest, and the | |
| # test executable target. | |
| _test_component_target = "${target_name}_component" | |
| fuchsia_component(_test_component_target) { | |
| deps = [ ":$_test_component_manifest_target" ] | |
| data_deps = [ ":$_exec_target" ] | |
| manifest = _test_component_manifest | |
| visibility = [ ":*" ] | |
| } | |
| _test_component_targets = [ ":${_test_component_target}" ] | |
| # Define components for each entry in |additional_manifests|, if any. Since | |
| # manifests may themselves depend-on the outputs of |deps|, these components | |
| # must each individually depend on |invoker.deps|. | |
| if (defined(invoker.additional_manifests)) { | |
| foreach(filename, invoker.additional_manifests) { | |
| _additional_component_target = | |
| target_name + "_" + get_path_info(filename, "name") | |
| _test_component_targets += [ ":${_additional_component_target}" ] | |
| fuchsia_component(_additional_component_target) { | |
| forward_variables_from(invoker, [ "testonly" ]) | |
| data_deps = [ ":$_exec_target" ] | |
| visibility = [ ":*" ] | |
| manifest = filename | |
| # Depend on |invoker.deps|, in case it includes a dependency that | |
| # creates this additional component's manifest. | |
| if (defined(invoker.deps)) { | |
| deps = invoker.deps | |
| } | |
| } | |
| } | |
| } | |
| # Define the package target that will bundle the test and additional | |
| # components and their data. | |
| fuchsia_package(_pkg_target) { | |
| forward_variables_from(invoker, | |
| [ | |
| "excluded_files", | |
| "excluded_dirs", | |
| "excluded_paths", | |
| ]) | |
| package_name = _output_name | |
| deps = _test_component_targets | |
| if (defined(invoker.fuchsia_package_deps)) { | |
| deps += invoker.fuchsia_package_deps | |
| } | |
| if (!defined(excluded_paths)) { | |
| excluded_paths = [] | |
| } | |
| excluded_paths += [ | |
| "${devtools_root_location}/*", | |
| "*.git/*", | |
| "*.svn/*", | |
| "*.hg/*", | |
| ] | |
| if (devtools_root_location != "") { | |
| excluded_paths += [ "${devtools_root_location}/*" ] | |
| } | |
| subpackages = _subpackages | |
| } | |
| # |target_name| refers to the package-runner rule, so that building | |
| # "base_unittests" will build not only the executable, component, and | |
| # package, but also the script required to run them. | |
| fuchsia_test_runner(target_name) { | |
| forward_variables_from(invoker, | |
| [ | |
| "data", | |
| "data_deps", | |
| "package_deps", | |
| ]) | |
| is_test_exe = true | |
| package = ":$_pkg_target" | |
| package_name = _output_name | |
| if (!defined(deps)) { | |
| deps = [] | |
| } | |
| if (defined(invoker.deps)) { | |
| deps += invoker.deps | |
| } | |
| if (!defined(data)) { | |
| data = [] | |
| } | |
| if (tests_have_location_tags) { | |
| data += [ "//testing/location_tags.json" ] | |
| } | |
| if (!defined(data_deps)) { | |
| data_deps = [] | |
| } | |
| data_deps += [ "//testing:test_scripts_shared" ] | |
| } | |
| mixed_test(_exec_target) { | |
| target_type = "executable" | |
| forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) | |
| output_name = _exec_target | |
| if (!defined(deps)) { | |
| deps = [] | |
| } | |
| # Use a crate name that avoids creating a warning due to double | |
| # underscore (ie. `__`). | |
| crate_name = _crate_name | |
| } | |
| } else if (is_ios) { | |
| assert(!defined(invoker.use_xvfb) || !invoker.use_xvfb) | |
| _runtime_deps_file = "$root_out_dir/${target_name}.runtime_deps" | |
| declare_args() { | |
| # Keep the unittest-as-xctest functionality defaulted to off for | |
| # local builds and test executions. | |
| enable_run_ios_unittests_with_xctest = false | |
| } | |
| _test_target = target_name | |
| _wrapper_output_name = "run_${target_name}" | |
| ios_test_runner_wrapper(_wrapper_output_name) { | |
| forward_variables_from(invoker, | |
| [ | |
| "clones", | |
| "data", | |
| "deps", | |
| "executable_args", | |
| "retries", | |
| ]) | |
| _root_build_dir = rebase_path("${root_build_dir}", root_build_dir) | |
| if (!defined(executable_args)) { | |
| executable_args = [] | |
| } | |
| executable_args += [ | |
| "--app", | |
| "@WrappedPath(${_root_build_dir}/${_test_target}.app)", | |
| ] | |
| wrapper_output_name = "${_wrapper_output_name}" | |
| if (!defined(data)) { | |
| data = [] | |
| } | |
| if (tests_have_location_tags) { | |
| data += [ "//testing/location_tags.json" ] | |
| } | |
| } | |
| _resources_bundle_data = target_name + "_resources_bundle_data" | |
| bundle_data(_resources_bundle_data) { | |
| visibility = [ ":$_test_target" ] | |
| sources = [ "//testing/gtest_ios/Default.png" ] | |
| outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ] | |
| } | |
| force_xctest = enable_run_ios_unittests_with_xctest || | |
| (defined(invoker.is_xctest) && invoker.is_xctest) | |
| mixed_test(_test_target) { | |
| if (force_xctest) { | |
| target_type = "ios_xctest_test" | |
| } else { | |
| target_type = "ios_app_bundle" | |
| } | |
| testonly = true | |
| if (force_xctest && build_with_chromium) { | |
| xctest_module_target = "//base/test:google_test_runner" | |
| } | |
| forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) | |
| # Provide sensible defaults in case invoker did not define any of those | |
| # required variables. | |
| if (!defined(info_plist) && !defined(info_plist_target)) { | |
| info_plist = "//testing/gtest_ios/unittest-Info.plist" | |
| } | |
| bundle_identifier = shared_bundle_id_for_test_apps | |
| if (!defined(bundle_deps)) { | |
| bundle_deps = [] | |
| } | |
| bundle_deps += [ ":$_resources_bundle_data" ] | |
| if (!defined(data_deps)) { | |
| data_deps = [] | |
| } | |
| data_deps += [ "//testing:test_scripts_shared" ] | |
| # Include the generate_wrapper as part of data_deps | |
| data_deps += [ ":${_wrapper_output_name}" ] | |
| write_runtime_deps = _runtime_deps_file | |
| if (use_rts) { | |
| data_deps += [ ":${invoker.target_name}__rts_filters" ] | |
| } | |
| if (!defined(deps)) { | |
| deps = [] | |
| } | |
| } | |
| } else if (is_chromeos && cros_board != "") { | |
| assert(!defined(invoker.use_xvfb) || !invoker.use_xvfb) | |
| # Building for a cros board (ie: not linux-chromeos). | |
| _gen_runner_target = "${target_name}__runner" | |
| _runtime_deps_file = | |
| "$root_out_dir/gen.runtime/" + get_label_info(target_name, "dir") + | |
| "/" + get_label_info(target_name, "name") + ".runtime_deps" | |
| if (is_skylab && (defined(tast_attr_expr) || defined(tast_tests) || | |
| defined(tast_disabled_tests))) { | |
| generate_skylab_tast_filter(_gen_runner_target) { | |
| } | |
| } else { | |
| generate_runner_script(_gen_runner_target) { | |
| generated_script = "$root_build_dir/bin/run_" + invoker.target_name | |
| test_exe = invoker.target_name | |
| runtime_deps_file = _runtime_deps_file | |
| if (tests_have_location_tags) { | |
| data = [ "//testing/location_tags.json" ] | |
| } | |
| if (use_rts) { | |
| data_deps = [ ":${invoker.target_name}__rts_filters" ] | |
| } | |
| } | |
| } | |
| mixed_test(target_name) { | |
| target_type = "executable" | |
| forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) | |
| forward_variables_from(invoker, [ "visibility" ]) | |
| if (!defined(deps)) { | |
| deps = [] | |
| } | |
| if (!defined(data)) { | |
| data = [] | |
| } | |
| # We use a special trigger script for CrOS hardware tests. | |
| data += [ "//testing/trigger_scripts/chromeos_device_trigger.py" ] | |
| write_runtime_deps = _runtime_deps_file | |
| data += [ _runtime_deps_file ] | |
| deps += [ ":$_gen_runner_target" ] | |
| if (!defined(data_deps)) { | |
| data_deps = [] | |
| } | |
| data_deps += [ "//testing:test_scripts_shared" ] | |
| if (use_rts) { | |
| data_deps += [ ":${invoker.target_name}__rts_filters" ] | |
| } | |
| } | |
| } else { | |
| if (is_mac || is_win) { | |
| assert(!defined(invoker.use_xvfb) || !invoker.use_xvfb) | |
| } | |
| _runtime_deps_file = "$root_out_dir/${target_name}.runtime_deps" | |
| _executable = target_name | |
| _gen_runner_target = "${target_name}__runner" | |
| if ((is_linux || is_chromeos) && defined(invoker.use_xvfb)) { | |
| _use_xvfb = invoker.use_xvfb | |
| } else { | |
| _use_xvfb = false | |
| } | |
| generate_wrapper(_gen_runner_target) { | |
| wrapper_script = "$root_build_dir/bin/run_" + _executable | |
| data = [] | |
| data_deps = [ "//testing:test_scripts_shared" ] | |
| if (_use_xvfb) { | |
| executable = "//testing/xvfb.py" | |
| } else { | |
| executable = "//testing/test_env.py" | |
| } | |
| if (tests_have_location_tags) { | |
| data += [ "//testing/location_tags.json" ] | |
| } | |
| executable_args = [ | |
| "@WrappedPath(./${_executable})", | |
| "--test-launcher-bot-mode", | |
| ] | |
| if (is_asan) { | |
| executable_args += [ "--asan=1" ] | |
| } | |
| if (is_msan) { | |
| executable_args += [ "--msan=1" ] | |
| } | |
| if (is_tsan) { | |
| executable_args += [ "--tsan=1" ] | |
| } | |
| if (use_cfi_diag) { | |
| executable_args += [ "--cfi-diag=1" ] | |
| } | |
| if (fail_on_san_warnings) { | |
| executable_args += [ "--fail-san=1" ] | |
| } | |
| if (use_rts) { | |
| data_deps += [ ":${invoker.target_name}__rts_filters" ] | |
| } | |
| } | |
| mixed_test(target_name) { | |
| target_type = "executable" | |
| forward_variables_from(invoker, | |
| "*", | |
| TESTONLY_AND_VISIBILITY + [ "use_xvfb" ]) | |
| forward_variables_from(invoker, [ "visibility" ]) | |
| if (!defined(deps)) { | |
| deps = [] | |
| } | |
| deps += [ | |
| # Give tests the default manifest on Windows (a no-op elsewhere). | |
| "//build/win:default_exe_manifest", | |
| ] | |
| write_runtime_deps = _runtime_deps_file | |
| deps += [ ":$_gen_runner_target" ] | |
| if (!defined(data_deps)) { | |
| data_deps = [] | |
| } | |
| data_deps += [ "//testing:test_scripts_shared" ] | |
| if (use_rts) { | |
| data_deps += [ ":${invoker.target_name}__rts_filters" ] | |
| } | |
| } | |
| } | |
| } | |
| # Defines a type of test that invokes a script to run, rather than | |
| # invoking an executable. | |
| # | |
| # The script must implement the | |
| # [test executable API](//docs/testing/test_executable_api.md). | |
| # | |
| # The template must be passed the `script` parameter, which specifies the path | |
| # to the script to run. It may optionally be passed a `args` parameter, which | |
| # can be used to include a list of args to be specified by default. The | |
| # template will produce a `$root_build_dir/run_$target_name` wrapper and write | |
| # the runtime_deps for the target to | |
| # $root_build_dir/${target_name}.runtime_deps, as per the conventions listed in | |
| # the [test wrapper API](//docs/testing/test_wrapper_api.md). | |
| # | |
| # Variables: | |
| # script: Path to underlying test script. | |
| # args: Args to the test script. | |
| # enable_android_wrapper: (optional) If true, the test will get wrapped in the | |
| # logdog_wrapper on Android, which will stream logcats from the device. | |
| # enable_cros_wrapper: (optional) If true, the test will get wrapped by the | |
| # cros_test_wrapper on ChromeOS, which will prep the device for testing. | |
| template("script_test") { | |
| _enable_logdog_wrapper = defined(invoker.enable_android_wrapper) && | |
| invoker.enable_android_wrapper && is_android | |
| _enable_cros_wrapper = | |
| defined(invoker.enable_cros_wrapper) && invoker.enable_cros_wrapper && | |
| is_chromeos && is_chromeos_device | |
| assert(!(_enable_logdog_wrapper && _enable_cros_wrapper), | |
| "The logdog and cros_test wrappers are mutually exclusive") | |
| if (use_rts) { | |
| action("${target_name}__rts_filters") { | |
| script = "//build/add_rts_filters.py" | |
| rts_file = "${root_build_dir}/gen/rts/${invoker.target_name}.filter" | |
| args = [ rebase_path(rts_file, root_build_dir) ] | |
| outputs = [ rts_file ] | |
| } | |
| } | |
| _shared_data = [ invoker.script ] | |
| if (defined(invoker.data)) { | |
| _shared_data += invoker.data | |
| } | |
| if (tests_have_location_tags) { | |
| _shared_data += [ "//testing/location_tags.json" ] | |
| } | |
| _shared_data_deps = [ "//testing:test_scripts_shared" ] | |
| if (defined(invoker.data_deps)) { | |
| _shared_data_deps += invoker.data_deps | |
| } | |
| if (use_rts) { | |
| _shared_data_deps += [ ":${invoker.target_name}__rts_filters" ] | |
| } | |
| _wrapped_script = | |
| "@WrappedPath(" + rebase_path(invoker.script, root_build_dir) + ")" | |
| if (_enable_logdog_wrapper) { | |
| logdog_wrapper_script_test(target_name) { | |
| testonly = true | |
| args = [ _wrapped_script ] | |
| if (defined(invoker.args)) { | |
| args += invoker.args | |
| } | |
| data = _shared_data | |
| data_deps = _shared_data_deps | |
| forward_variables_from(invoker, | |
| "*", | |
| TESTONLY_AND_VISIBILITY + [ | |
| "args", | |
| "data", | |
| "data_deps", | |
| "script", | |
| ]) | |
| } | |
| } else if (_enable_cros_wrapper) { | |
| cros_test_wrapper_script_test(target_name) { | |
| args = [ _wrapped_script ] | |
| if (defined(invoker.args)) { | |
| args += invoker.args | |
| } | |
| data = _shared_data | |
| data_deps = _shared_data_deps | |
| forward_variables_from(invoker, | |
| "*", | |
| TESTONLY_AND_VISIBILITY + [ | |
| "args", | |
| "data", | |
| "data_deps", | |
| "script", | |
| ]) | |
| } | |
| } else { | |
| generate_wrapper(target_name) { | |
| testonly = true | |
| wrapper_script = "${root_build_dir}/bin/run_${target_name}" | |
| executable = "//testing/test_env.py" | |
| executable_args = [ _wrapped_script ] | |
| if (defined(invoker.args)) { | |
| executable_args += invoker.args | |
| } | |
| data = _shared_data | |
| data_deps = _shared_data_deps | |
| forward_variables_from(invoker, | |
| "*", | |
| TESTONLY_AND_VISIBILITY + [ | |
| "args", | |
| "data", | |
| "data_deps", | |
| "script", | |
| ]) | |
| forward_variables_from(invoker, [ "visibility" ]) | |
| write_runtime_deps = "${root_build_dir}/${target_name}.runtime_deps" | |
| } | |
| } | |
| } | |
| # Defines a test target that uses exit code for pass/fail. | |
| template("isolated_script_test") { | |
| script_test(target_name) { | |
| forward_variables_from(invoker, | |
| "*", | |
| TESTONLY_AND_VISIBILITY + [ | |
| "args", | |
| "data", | |
| "deps", | |
| "script", | |
| ]) | |
| forward_variables_from(invoker, [ "visibility" ]) | |
| deps = [ "//testing:run_isolated_script_test" ] | |
| if (defined(invoker.deps)) { | |
| deps += invoker.deps | |
| } | |
| script = "//testing/scripts/run_isolated_script_test.py" | |
| data = [ invoker.script ] | |
| if (defined(invoker.data)) { | |
| data += invoker.data | |
| } | |
| args = [ | |
| rebase_path(invoker.script, root_build_dir), | |
| "--script-type=bare", | |
| ] | |
| if (defined(invoker.args)) { | |
| args += invoker.args | |
| } | |
| } | |
| } | |
| # Test defaults. | |
| set_defaults("test") { | |
| if (is_android) { | |
| # Should be kept in sync with set_defaults("cronet_test") in | |
| # //components/cronet/android/cronet_test_templates.gni | |
| # LINT.IfChange | |
| configs = default_shared_library_configs | |
| configs -= [ "//build/config/android:hide_all_but_jni_onload" ] | |
| configs += [ "//build/config/android:hide_all_but_jni" ] | |
| # LINT.ThenChange(/components/cronet/android/cronet_test_templates.gni) | |
| } else { | |
| configs = default_executable_configs | |
| } | |
| } |