summaryrefslogtreecommitdiff
path: root/testing
diff options
context:
space:
mode:
Diffstat (limited to 'testing')
-rw-r--r--testing/multiprocess_func_list.cc55
-rw-r--r--testing/multiprocess_func_list.h72
-rw-r--r--testing/test.gni232
3 files changed, 359 insertions, 0 deletions
diff --git a/testing/multiprocess_func_list.cc b/testing/multiprocess_func_list.cc
new file mode 100644
index 0000000000..a400557445
--- /dev/null
+++ b/testing/multiprocess_func_list.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 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.
+
+#include "multiprocess_func_list.h"
+
+#include <map>
+
+// Helper functions to maintain mapping of "test name"->test func.
+// The information is accessed via a global map.
+namespace multi_process_function_list {
+
+namespace {
+
+struct ProcessFunctions {
+ ProcessFunctions() : main(NULL), setup(NULL) {}
+ ProcessFunctions(TestMainFunctionPtr main, SetupFunctionPtr setup)
+ : main(main), setup(setup) {}
+ TestMainFunctionPtr main;
+ SetupFunctionPtr setup;
+};
+
+typedef std::map<std::string, ProcessFunctions> MultiProcessTestMap;
+
+// Retrieve a reference to the global 'func name' -> func ptr map.
+MultiProcessTestMap& GetMultiprocessFuncMap() {
+ static MultiProcessTestMap test_name_to_func_ptr_map;
+ return test_name_to_func_ptr_map;
+}
+
+} // namespace
+
+AppendMultiProcessTest::AppendMultiProcessTest(
+ std::string test_name,
+ TestMainFunctionPtr main_func_ptr,
+ SetupFunctionPtr setup_func_ptr) {
+ GetMultiprocessFuncMap()[test_name] =
+ ProcessFunctions(main_func_ptr, setup_func_ptr);
+}
+
+int InvokeChildProcessTest(std::string test_name) {
+ MultiProcessTestMap& func_lookup_table = GetMultiprocessFuncMap();
+ MultiProcessTestMap::iterator it = func_lookup_table.find(test_name);
+ if (it != func_lookup_table.end()) {
+ const ProcessFunctions& process_functions = it->second;
+ if (process_functions.setup)
+ (*process_functions.setup)();
+ if (process_functions.main)
+ return (*process_functions.main)();
+ }
+
+ return -1;
+}
+
+} // namespace multi_process_function_list
diff --git a/testing/multiprocess_func_list.h b/testing/multiprocess_func_list.h
new file mode 100644
index 0000000000..2ec4d7d975
--- /dev/null
+++ b/testing/multiprocess_func_list.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 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.
+
+#ifndef TESTING_MULTIPROCESS_FUNC_LIST_H_
+#define TESTING_MULTIPROCESS_FUNC_LIST_H_
+
+#include <string>
+
+// This file provides the plumbing to register functions to be executed
+// as the main function of a child process in a multi-process test.
+// This complements the MultiProcessTest class which provides facilities
+// for launching such tests.
+//
+// The MULTIPROCESS_TEST_MAIN() macro registers a string -> func_ptr mapping
+// by creating a new global instance of the AppendMultiProcessTest() class
+// this means that by the time that we reach our main() function the mapping
+// is already in place.
+//
+// Example usage:
+// MULTIPROCESS_TEST_MAIN(a_test_func) {
+// // Code here runs in a child process.
+// return 0;
+// }
+//
+// The prototype of a_test_func is implicitly
+// int test_main_func_name();
+
+namespace multi_process_function_list {
+
+// Type for child process main functions.
+typedef int (*TestMainFunctionPtr)();
+
+// Type for child setup functions.
+typedef void (*SetupFunctionPtr)();
+
+// Helper class to append a test function to the global mapping.
+// Used by the MULTIPROCESS_TEST_MAIN macro.
+class AppendMultiProcessTest {
+ public:
+ // |main_func_ptr| is the main function that is run in the child process.
+ // |setup_func_ptr| is a function run when the global mapping is added.
+ AppendMultiProcessTest(std::string test_name,
+ TestMainFunctionPtr main_func_ptr,
+ SetupFunctionPtr setup_func_ptr);
+};
+
+// Invoke the main function of a test previously registered with
+// MULTIPROCESS_TEST_MAIN()
+int InvokeChildProcessTest(std::string test_name);
+
+// This macro creates a global MultiProcessTest::AppendMultiProcessTest object
+// whose constructor does the work of adding the global mapping.
+#define MULTIPROCESS_TEST_MAIN(test_main) \
+ MULTIPROCESS_TEST_MAIN_WITH_SETUP(test_main, NULL)
+
+// Same as above but lets callers specify a setup method that is run in the
+// child process, just before the main function is run. This facilitates
+// adding a generic one-time setup function for multiple tests.
+#define MULTIPROCESS_TEST_MAIN_WITH_SETUP(test_main, test_setup) \
+ int test_main(); \
+ namespace { \
+ multi_process_function_list::AppendMultiProcessTest \
+ AddMultiProcessTest##_##test_main(#test_main, \
+ (test_main), \
+ (test_setup)); \
+ } \
+ int test_main()
+
+} // namespace multi_process_function_list
+
+#endif // TESTING_MULTIPROCESS_FUNC_LIST_H_
diff --git a/testing/test.gni b/testing/test.gni
new file mode 100644
index 0000000000..13c434238a
--- /dev/null
+++ b/testing/test.gni
@@ -0,0 +1,232 @@
+# 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.
+
+# ==============================================================================
+# TEST SETUP
+# ==============================================================================
+
+# Define a test as an executable (or apk on Android) with the "testonly" flag
+# set.
+# Variable:
+# use_raw_android_executable: Use executable() rather than android_apk().
+template("test") {
+ if (is_android) {
+ import("//build/config/android/config.gni")
+ import("//build/config/android/rules.gni")
+
+ _use_raw_android_executable = defined(invoker.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
+ }
+
+ if (_use_raw_android_executable) {
+ _exec_target = "${target_name}__exec"
+ _dist_target = "${target_name}__dist"
+ _exec_output =
+ "$target_out_dir/${invoker.target_name}/${invoker.target_name}"
+
+ executable(_exec_target) {
+ # Configs will always be defined since we set_defaults in BUILDCONFIG.gn.
+ configs = []
+ data_deps = []
+ forward_variables_from(invoker, "*", [ "extra_dist_files" ])
+ testonly = true
+
+ # 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_native_jni_exports",
+ ]
+ configs += [ "//build/config:executable_config" ]
+
+ # Don't output to the root or else conflict with the group() below.
+ output_name = rebase_path(_exec_output, root_out_dir)
+ if (is_component_build || is_asan) {
+ data_deps += [ "//build/android:cpplib_stripped" ]
+ }
+ }
+
+ create_native_executable_dist(_dist_target) {
+ testonly = true
+ 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
+ }
+ }
+ } else {
+ _library_target = "_${target_name}__library"
+ _apk_target = "${target_name}_apk"
+ _apk_specific_vars = [
+ "android_manifest",
+ "enable_multidex",
+ "use_default_launcher",
+ "write_asset_list",
+ ]
+ shared_library(_library_target) {
+ # Configs will always be defined since we set_defaults in BUILDCONFIG.gn.
+ configs = [] # Prevent list overwriting warning.
+ configs = invoker.configs
+ testonly = true
+
+ deps = []
+ forward_variables_from(invoker,
+ "*",
+ _apk_specific_vars + [
+ "isolate_file",
+ "visibility",
+ ])
+
+ if (!defined(invoker.use_default_launcher) ||
+ invoker.use_default_launcher) {
+ deps += [ "//testing/android/native_test:native_test_native_code" ]
+ }
+ }
+ unittest_apk(_apk_target) {
+ forward_variables_from(invoker, _apk_specific_vars + [ "deps" ])
+ unittests_dep = ":$_library_target"
+ apk_name = invoker.target_name
+ if (defined(invoker.output_name)) {
+ apk_name = invoker.output_name
+ unittests_binary = "lib${apk_name}.so"
+ install_script_name = "install_${invoker.output_name}"
+ }
+ deps += [ ":$_library_target" ]
+ }
+
+ # Incremental test targets work only for .apks.
+ _incremental_test_runner_target =
+ "${_output_name}_incremental__test_runner_script"
+ test_runner_script(_incremental_test_runner_target) {
+ forward_variables_from(invoker, [ "isolate_file" ])
+ apk_target = ":$_apk_target"
+ test_name = "${_output_name}_incremental"
+ test_type = "gtest"
+ test_suite = _output_name
+ incremental_install = true
+ }
+ group("${target_name}_incremental") {
+ testonly = true
+ datadeps = [
+ ":$_incremental_test_runner_target",
+ ]
+ deps = [
+ ":${_apk_target}_incremental",
+ ]
+ }
+ }
+
+ _test_runner_target = "${_output_name}__test_runner_script"
+ test_runner_script(_test_runner_target) {
+ forward_variables_from(invoker, [ "isolate_file" ])
+ if (_use_raw_android_executable) {
+ executable_dist_dir = "$root_out_dir/$_dist_target"
+ } else {
+ apk_target = ":$_apk_target"
+ }
+ test_name = _output_name
+ test_type = "gtest"
+ test_suite = _output_name
+ }
+
+ group(target_name) {
+ testonly = true
+ deps = [
+ ":$_test_runner_target",
+ ]
+ if (_use_raw_android_executable) {
+ deps += [ ":$_dist_target" ]
+ } else {
+ deps += [ ":$_apk_target" ]
+ }
+ }
+
+ # TODO(GYP): Delete this after we've converted everything to GN.
+ # The _run targets exist only for compatibility w/ GYP.
+ group("${target_name}_apk_run") {
+ testonly = true
+ deps = [
+ ":${invoker.target_name}",
+ ]
+ }
+ } else if (is_ios) {
+ import("//build/config/ios/rules.gni")
+
+ _test_target = target_name
+ _resources_bundle_data = target_name + "_resources_bundle_data"
+
+ bundle_data(_resources_bundle_data) {
+ visibility = [
+ ":${_test_target}",
+ ":${_test_target}_generate_executable",
+ ]
+ sources = [
+ "//testing/gtest_ios/Default.png",
+ ]
+ outputs = [
+ "{{bundle_resources_dir}}/{{source_file_part}}",
+ ]
+ }
+
+ app(_test_target) {
+ # TODO(GYP): Make this configurable and only provide a default
+ # that can be overridden.
+ info_plist = "//testing/gtest_ios/unittest-Info.plist"
+ app_name = target_name
+ entitlements_path = "//testing/gtest_ios"
+ code_signing_identity = ""
+ testonly = true
+ extra_substitutions = [ "BUNDLE_ID_TEST_NAME=$app_name" ]
+
+ # See above call.
+ set_sources_assignment_filter([])
+
+ forward_variables_from(invoker, "*")
+
+ if (!defined(deps)) {
+ deps = []
+ }
+ deps += [
+ ":$_resources_bundle_data",
+
+ # All shared libraries must have the sanitizer deps to properly link in
+ # asan mode (this target will be empty in other cases).
+ "//build/config/sanitizers:deps",
+ ]
+ }
+ } else {
+ executable(target_name) {
+ deps = []
+ forward_variables_from(invoker, "*")
+
+ testonly = true
+ deps += [
+ # All shared libraries must have the sanitizer deps to properly link in
+ # asan mode (this target will be empty in other cases).
+ "//build/config/sanitizers:deps",
+
+ # Give tests the default manifest on Windows (a no-op elsewhere).
+ "//build/win:default_exe_manifest",
+ ]
+ }
+
+ # TODO(GYP): Delete this after we've converted everything to GN.
+ # The _run targets exist only for compatibility with GYP.
+ group("${target_name}_run") {
+ testonly = true
+ deps = [
+ ":${invoker.target_name}",
+ ]
+ }
+ }
+}