summaryrefslogtreecommitdiff
path: root/base/test
diff options
context:
space:
mode:
Diffstat (limited to 'base/test')
-rw-r--r--base/test/BUILD.gn104
-rw-r--r--base/test/gtest_util.cc112
-rw-r--r--base/test/gtest_util.h102
-rw-r--r--base/test/mock_entropy_provider.cc20
-rw-r--r--base/test/mock_entropy_provider.h32
-rw-r--r--base/test/multiprocess_test.cc16
-rw-r--r--base/test/multiprocess_test.h35
-rw-r--r--base/test/multiprocess_test_android.cc476
-rw-r--r--base/test/opaque_ref_counted.cc30
-rw-r--r--base/test/opaque_ref_counted.h5
-rw-r--r--base/test/scoped_feature_list.cc74
-rw-r--r--base/test/scoped_feature_list.h59
-rw-r--r--base/test/sequenced_worker_pool_owner.cc8
-rw-r--r--base/test/sequenced_worker_pool_owner.h2
-rw-r--r--base/test/test_file_util.h21
-rw-r--r--base/test/test_io_thread.cc23
-rw-r--r--base/test/test_io_thread.h16
-rw-r--r--base/test/test_mock_time_task_runner.cc321
-rw-r--r--base/test/test_mock_time_task_runner.h223
-rw-r--r--base/test/test_pending_task.cc4
-rw-r--r--base/test/test_pending_task.h9
-rw-r--r--base/test/test_simple_task_runner.cc62
-rw-r--r--base/test/test_simple_task_runner.h32
-rw-r--r--base/test/test_timeouts.cc8
-rw-r--r--base/test/test_timeouts.h3
-rw-r--r--base/test/trace_event_analyzer.cc4
-rw-r--r--base/test/trace_event_analyzer_unittest.cc2
27 files changed, 1273 insertions, 530 deletions
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 51863a2a0c..844707ebd1 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -22,11 +22,16 @@ static_library("test_config") {
]
}
-# GYP: //base/base.gyp:test_support_base
static_library("test_support") {
testonly = true
sources = [
"../trace_event/trace_config_memory_test_util.h",
+ "android/java_handler_thread_for_testing.cc",
+ "android/java_handler_thread_for_testing.h",
+ "android/test_system_message_handler_link_android.cc",
+ "android/test_system_message_handler_link_android.h",
+ "fuzzed_data_provider.cc",
+ "fuzzed_data_provider.h",
"gtest_util.cc",
"gtest_util.h",
"gtest_xml_unittest_result_printer.cc",
@@ -35,12 +40,15 @@ static_library("test_support") {
"gtest_xml_util.h",
"histogram_tester.cc",
"histogram_tester.h",
+ "icu_test_util.cc",
+ "icu_test_util.h",
"ios/wait_util.h",
"ios/wait_util.mm",
"launcher/test_result.cc",
"launcher/test_result.h",
"launcher/test_results_tracker.h",
"launcher/unit_test_launcher.h",
+ "mock_callback.h",
"mock_chrome_application_mac.h",
"mock_chrome_application_mac.mm",
"mock_devices_changed_observer.cc",
@@ -49,7 +57,9 @@ static_library("test_support") {
"mock_entropy_provider.h",
"mock_log.cc",
"mock_log.h",
+ "multiprocess_test.cc",
"multiprocess_test.h",
+ "multiprocess_test_android.cc",
"null_task_runner.cc",
"null_task_runner.h",
"opaque_ref_counted.cc",
@@ -62,12 +72,20 @@ static_library("test_support") {
"perf_time_logger.h",
"power_monitor_test_base.cc",
"power_monitor_test_base.h",
+ "scoped_async_task_scheduler.cc",
+ "scoped_async_task_scheduler.h",
"scoped_command_line.cc",
"scoped_command_line.h",
+ "scoped_feature_list.cc",
+ "scoped_feature_list.h",
"scoped_locale.cc",
"scoped_locale.h",
+ "scoped_mock_time_message_loop_task_runner.cc",
+ "scoped_mock_time_message_loop_task_runner.h",
"scoped_path_override.cc",
"scoped_path_override.h",
+ "scoped_task_scheduler.cc",
+ "scoped_task_scheduler.h",
"sequenced_task_runner_test_template.cc",
"sequenced_task_runner_test_template.h",
"sequenced_worker_pool_owner.cc",
@@ -133,8 +151,6 @@ static_library("test_support") {
"launcher/test_launcher_tracer.h",
"launcher/test_results_tracker.cc",
"launcher/unit_test_launcher.cc",
- "multiprocess_test.cc",
- "multiprocess_test_android.cc",
]
}
@@ -178,7 +194,11 @@ static_library("test_support") {
}
if (is_android) {
- deps += [ ":base_unittests_jni_headers" ]
+ deps += [
+ ":base_unittests_jni_headers",
+ ":test_support_jni_headers",
+ ]
+ public_deps += [ ":test_support_java" ]
}
if (is_nacl_nonsfi) {
@@ -191,6 +211,8 @@ static_library("test_support") {
sources -= [
"gtest_xml_util.cc",
"gtest_xml_util.h",
+ "icu_test_util.cc",
+ "icu_test_util.h",
"perf_test_suite.cc",
"perf_test_suite.h",
"scoped_path_override.cc",
@@ -255,6 +277,41 @@ static_library("run_all_unittests") {
]
}
+# These sources are linked into both the base_unittests binary and the test
+# shared library target below.
+source_set("native_library_test_utils") {
+ testonly = true
+ sources = [
+ "native_library_test_utils.cc",
+ "native_library_test_utils.h",
+ ]
+}
+
+# This shared library is dynamically loaded by NativeLibrary unittests.
+shared_library("test_shared_library") {
+ testonly = true
+ sources = [
+ "test_shared_library.cc",
+ ]
+
+ deps = [
+ ":native_library_test_utils",
+ ]
+}
+
+static_library("run_all_base_unittests") {
+ # Only targets in base should depend on this, targets outside base
+ # should depend on run_all_unittests above.
+ visibility = [ "//base/*" ]
+ testonly = true
+ sources = [
+ "run_all_base_unittests.cc",
+ ]
+ deps = [
+ ":test_support",
+ ]
+}
+
if (is_linux) {
shared_library("malloc_wrapper") {
testonly = true
@@ -272,8 +329,47 @@ if (is_android) {
generate_jni("base_unittests_jni_headers") {
sources = [
"android/java/src/org/chromium/base/ContentUriTestUtils.java",
+ "android/java/src/org/chromium/base/TestSystemMessageHandler.java",
"android/java/src/org/chromium/base/TestUiThread.java",
]
jni_package = "base"
}
+
+ generate_jni("test_support_jni_headers") {
+ sources = [
+ "android/java/src/org/chromium/base/MainReturnCodeResult.java",
+ "android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java",
+ ]
+ jni_package = "base"
+ }
+
+ android_library("test_support_java") {
+ testonly = true
+ deps = [
+ "//base:base_java",
+ "//testing/android/native_test:native_main_runner_java",
+ "//third_party/android_tools:android_support_annotations_java",
+ "//third_party/jsr-305:jsr_305_javalib",
+ ]
+ srcjar_deps = [ ":test_support_java_aidl" ]
+ java_files = [
+ "android/java/src/org/chromium/base/FileDescriptorInfo.java",
+ "android/java/src/org/chromium/base/MainReturnCodeResult.java",
+ "android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java",
+ "android/java/src/org/chromium/base/MultiprocessTestClientService.java",
+ "android/java/src/org/chromium/base/MultiprocessTestClientService0.java",
+ "android/java/src/org/chromium/base/MultiprocessTestClientService1.java",
+ "android/java/src/org/chromium/base/MultiprocessTestClientService2.java",
+ "android/java/src/org/chromium/base/MultiprocessTestClientService3.java",
+ "android/java/src/org/chromium/base/MultiprocessTestClientService4.java",
+ ]
+ }
+
+ android_aidl("test_support_java_aidl") {
+ testonly = true
+ import_include = [ "android/java/src" ]
+ sources = [
+ "android/java/src/org/chromium/base/ITestClient.aidl",
+ ]
+ }
}
diff --git a/base/test/gtest_util.cc b/base/test/gtest_util.cc
new file mode 100644
index 0000000000..6da902da2e
--- /dev/null
+++ b/base/test/gtest_util.cc
@@ -0,0 +1,112 @@
+// Copyright 2014 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 "base/test/gtest_util.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TestIdentifier::TestIdentifier() {
+}
+
+TestIdentifier::TestIdentifier(const TestIdentifier& other) = default;
+
+std::string FormatFullTestName(const std::string& test_case_name,
+ const std::string& test_name) {
+ return test_case_name + "." + test_name;
+}
+
+std::string TestNameWithoutDisabledPrefix(const std::string& full_test_name) {
+ std::string test_name_no_disabled(full_test_name);
+ ReplaceSubstringsAfterOffset(&test_name_no_disabled, 0, "DISABLED_", "");
+ return test_name_no_disabled;
+}
+
+std::vector<TestIdentifier> GetCompiledInTests() {
+ testing::UnitTest* const unit_test = testing::UnitTest::GetInstance();
+
+ std::vector<TestIdentifier> tests;
+ for (int i = 0; i < unit_test->total_test_case_count(); ++i) {
+ const testing::TestCase* test_case = unit_test->GetTestCase(i);
+ for (int j = 0; j < test_case->total_test_count(); ++j) {
+ const testing::TestInfo* test_info = test_case->GetTestInfo(j);
+ TestIdentifier test_data;
+ test_data.test_case_name = test_case->name();
+ test_data.test_name = test_info->name();
+ test_data.file = test_info->file();
+ test_data.line = test_info->line();
+ tests.push_back(test_data);
+ }
+ }
+ return tests;
+}
+
+bool WriteCompiledInTestsToFile(const FilePath& path) {
+ std::vector<TestIdentifier> tests(GetCompiledInTests());
+
+ ListValue root;
+ for (size_t i = 0; i < tests.size(); ++i) {
+ std::unique_ptr<DictionaryValue> test_info(new DictionaryValue);
+ test_info->SetString("test_case_name", tests[i].test_case_name);
+ test_info->SetString("test_name", tests[i].test_name);
+ test_info->SetString("file", tests[i].file);
+ test_info->SetInteger("line", tests[i].line);
+ root.Append(std::move(test_info));
+ }
+
+ JSONFileValueSerializer serializer(path);
+ return serializer.Serialize(root);
+}
+
+bool ReadTestNamesFromFile(const FilePath& path,
+ std::vector<TestIdentifier>* output) {
+ JSONFileValueDeserializer deserializer(path);
+ int error_code = 0;
+ std::string error_message;
+ std::unique_ptr<base::Value> value =
+ deserializer.Deserialize(&error_code, &error_message);
+ if (!value.get())
+ return false;
+
+ base::ListValue* tests = nullptr;
+ if (!value->GetAsList(&tests))
+ return false;
+
+ std::vector<base::TestIdentifier> result;
+ for (base::ListValue::iterator i = tests->begin(); i != tests->end(); ++i) {
+ base::DictionaryValue* test = nullptr;
+ if (!(*i)->GetAsDictionary(&test))
+ return false;
+
+ TestIdentifier test_data;
+
+ if (!test->GetStringASCII("test_case_name", &test_data.test_case_name))
+ return false;
+
+ if (!test->GetStringASCII("test_name", &test_data.test_name))
+ return false;
+
+ if (!test->GetStringASCII("file", &test_data.file))
+ return false;
+
+ if (!test->GetInteger("line", &test_data.line))
+ return false;
+
+ result.push_back(test_data);
+ }
+
+ output->swap(result);
+ return true;
+}
+
+} // namespace base
diff --git a/base/test/gtest_util.h b/base/test/gtest_util.h
new file mode 100644
index 0000000000..8dfb1f236f
--- /dev/null
+++ b/base/test/gtest_util.h
@@ -0,0 +1,102 @@
+// Copyright 2014 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 BASE_TEST_GTEST_UTIL_H_
+#define BASE_TEST_GTEST_UTIL_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// EXPECT/ASSERT_DCHECK_DEATH is intended to replace EXPECT/ASSERT_DEBUG_DEATH
+// when the death is expected to be caused by a DCHECK. Contrary to
+// EXPECT/ASSERT_DEBUG_DEATH however, it doesn't execute the statement in non-
+// dcheck builds as DCHECKs are intended to catch things that should never
+// happen and as such executing the statement results in undefined behavior
+// (|statement| is compiled in unsupported configurations nonetheless).
+// Death tests misbehave on Android.
+#if DCHECK_IS_ON() && defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
+
+// EXPECT/ASSERT_DCHECK_DEATH tests verify that a DCHECK is hit ("Check failed"
+// is part of the error message), but intentionally do not expose the gtest
+// death test's full |regex| parameter to avoid users having to verify the exact
+// syntax of the error message produced by the DCHECK.
+#define EXPECT_DCHECK_DEATH(statement) EXPECT_DEATH(statement, "Check failed")
+#define ASSERT_DCHECK_DEATH(statement) ASSERT_DEATH(statement, "Check failed")
+
+#else
+// DCHECK_IS_ON() && defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
+
+// Macro copied from gtest-death-test-internal.h as it's (1) internal for now
+// and (2) only defined if !GTEST_HAS_DEATH_TEST which is only a subset of the
+// conditions in which it's needed here.
+// TODO(gab): Expose macro in upstream gtest repo for consumers like us that
+// want more specific death tests and remove this hack.
+# define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ GTEST_LOG_(WARNING) \
+ << "Death tests are not supported on this platform.\n" \
+ << "Statement '" #statement "' cannot be verified."; \
+ } else if (::testing::internal::AlwaysFalse()) { \
+ ::testing::internal::RE::PartialMatch(".*", (regex)); \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ terminator; \
+ } else \
+ ::testing::Message()
+
+#define EXPECT_DCHECK_DEATH(statement) \
+ GTEST_UNSUPPORTED_DEATH_TEST(statement, "Check failed", )
+#define ASSERT_DCHECK_DEATH(statement) \
+ GTEST_UNSUPPORTED_DEATH_TEST(statement, "Check failed", return)
+
+#endif
+// DCHECK_IS_ON() && defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
+
+namespace base {
+
+class FilePath;
+
+struct TestIdentifier {
+ TestIdentifier();
+ TestIdentifier(const TestIdentifier& other);
+
+ std::string test_case_name;
+ std::string test_name;
+ std::string file;
+ int line;
+};
+
+// Constructs a full test name given a test case name and a test name,
+// e.g. for test case "A" and test name "B" returns "A.B".
+std::string FormatFullTestName(const std::string& test_case_name,
+ const std::string& test_name);
+
+// Returns the full test name with the "DISABLED_" prefix stripped out.
+// e.g. for the full test names "A.DISABLED_B", "DISABLED_A.B", and
+// "DISABLED_A.DISABLED_B", returns "A.B".
+std::string TestNameWithoutDisabledPrefix(const std::string& full_test_name);
+
+// Returns a vector of gtest-based tests compiled into
+// current executable.
+std::vector<TestIdentifier> GetCompiledInTests();
+
+// Writes the list of gtest-based tests compiled into
+// current executable as a JSON file. Returns true on success.
+bool WriteCompiledInTestsToFile(const FilePath& path) WARN_UNUSED_RESULT;
+
+// Reads the list of gtest-based tests from |path| into |output|.
+// Returns true on success.
+bool ReadTestNamesFromFile(
+ const FilePath& path,
+ std::vector<TestIdentifier>* output) WARN_UNUSED_RESULT;
+
+} // namespace base
+
+#endif // BASE_TEST_GTEST_UTIL_H_
diff --git a/base/test/mock_entropy_provider.cc b/base/test/mock_entropy_provider.cc
new file mode 100644
index 0000000000..5ebf19a7c7
--- /dev/null
+++ b/base/test/mock_entropy_provider.cc
@@ -0,0 +1,20 @@
+// 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.
+
+#include "base/test/mock_entropy_provider.h"
+
+namespace base {
+
+MockEntropyProvider::MockEntropyProvider() : entropy_value_(0.5) {}
+MockEntropyProvider::MockEntropyProvider(double entropy_value)
+ : entropy_value_(entropy_value) {}
+MockEntropyProvider::~MockEntropyProvider() {}
+
+double MockEntropyProvider::GetEntropyForTrial(
+ const std::string& trial_name,
+ uint32_t randomization_seed) const {
+ return entropy_value_;
+}
+
+} // namespace base
diff --git a/base/test/mock_entropy_provider.h b/base/test/mock_entropy_provider.h
new file mode 100644
index 0000000000..ca2b4bc8fe
--- /dev/null
+++ b/base/test/mock_entropy_provider.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef BASE_TEST_MOCK_ENTROPY_PROVIDER_H_
+#define BASE_TEST_MOCK_ENTROPY_PROVIDER_H_
+
+#include <stdint.h>
+
+#include "base/metrics/field_trial.h"
+
+namespace base {
+
+class MockEntropyProvider : public base::FieldTrial::EntropyProvider {
+ public:
+ MockEntropyProvider();
+ explicit MockEntropyProvider(double entropy_value);
+ ~MockEntropyProvider() override;
+
+ // base::FieldTrial::EntropyProvider:
+ double GetEntropyForTrial(const std::string& trial_name,
+ uint32_t randomization_seed) const override;
+
+ private:
+ double entropy_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockEntropyProvider);
+};
+
+} // namespace base
+
+#endif // BASE_TEST_MOCK_ENTROPY_PROVIDER_H_
diff --git a/base/test/multiprocess_test.cc b/base/test/multiprocess_test.cc
index de56e7f6be..fcc4d123ed 100644
--- a/base/test/multiprocess_test.cc
+++ b/base/test/multiprocess_test.cc
@@ -26,6 +26,19 @@ Process SpawnMultiProcessTestChild(
return LaunchProcess(command_line, options);
}
+
+bool WaitForMultiprocessTestChildExit(const Process& process,
+ TimeDelta timeout,
+ int* exit_code) {
+ return process.WaitForExitWithTimeout(timeout, exit_code);
+}
+
+bool TerminateMultiProcessTestChild(const Process& process,
+ int exit_code,
+ bool wait) {
+ return process.Terminate(exit_code, wait);
+}
+
#endif // !OS_ANDROID && !__ANDROID__ && !__ANDROID_HOST__
CommandLine GetMultiProcessTestChildBaseCommandLine() {
@@ -39,6 +52,8 @@ CommandLine GetMultiProcessTestChildBaseCommandLine() {
MultiProcessTest::MultiProcessTest() {
}
+// Don't compile on Arc++.
+#if 0
Process MultiProcessTest::SpawnChild(const std::string& procname) {
LaunchOptions options;
#if defined(OS_WIN)
@@ -52,6 +67,7 @@ Process MultiProcessTest::SpawnChildWithOptions(
const LaunchOptions& options) {
return SpawnMultiProcessTestChild(procname, MakeCmdLine(procname), options);
}
+#endif
CommandLine MultiProcessTest::MakeCmdLine(const std::string& procname) {
CommandLine command_line = GetMultiProcessTestChildBaseCommandLine();
diff --git a/base/test/multiprocess_test.h b/base/test/multiprocess_test.h
index ae4c3eb2ef..bf9663759e 100644
--- a/base/test/multiprocess_test.h
+++ b/base/test/multiprocess_test.h
@@ -40,7 +40,7 @@ class CommandLine;
// // Do stuff involving |test_child_process| and the child process....
//
// int rv = -1;
-// ASSERT_TRUE(test_child_process.WaitForExitWithTimeout(
+// ASSERT_TRUE(base::WaitForMultiprocessTestChildExit(test_child_process,
// TestTimeouts::action_timeout(), &rv));
// EXPECT_EQ(0, rv);
// }
@@ -51,6 +51,10 @@ class CommandLine;
// // Code here runs in a child process....
// return 0;
// }
+//
+// If you need to terminate the child process, use the
+// TerminateMultiProcessTestChild method to ensure that test will work on
+// Android.
// Spawns a child process and executes the function |procname| declared using
// |MULTIPROCESS_TEST_MAIN()| or |MULTIPROCESS_TEST_MAIN_WITH_SETUP()|.
@@ -66,24 +70,17 @@ Process SpawnMultiProcessTestChild(
// may add any flags needed for your child process.
CommandLine GetMultiProcessTestChildBaseCommandLine();
-#if defined(OS_ANDROID)
-
-// Enable the alternate test child implementation which support spawning a child
-// after threads have been created. If used, this MUST be the first line of
-// main(). The main function is passed in to avoid a link-time dependency in
-// component builds.
-void InitAndroidMultiProcessTestHelper(int (*main)(int, char**));
-
-// Returns true if the current process is a test child.
-bool AndroidIsChildProcess();
-
-// Wait for a test child to exit if the alternate test child implementation is
-// being used.
-bool AndroidWaitForChildExitWithTimeout(
- const Process& process, TimeDelta timeout, int* exit_code)
- WARN_UNUSED_RESULT;
-
-#endif // defined(OS_ANDROID)
+// Waits for the child process to exit. Returns true if the process exited
+// within |timeout| and sets |exit_code| if non null.
+bool WaitForMultiprocessTestChildExit(const Process& process,
+ TimeDelta timeout,
+ int* exit_code);
+
+// Terminates |process| with |exit_code|. If |wait| is true, this call blocks
+// until the process actually terminates.
+bool TerminateMultiProcessTestChild(const Process& process,
+ int exit_code,
+ bool wait);
// MultiProcessTest ------------------------------------------------------------
diff --git a/base/test/multiprocess_test_android.cc b/base/test/multiprocess_test_android.cc
index f58b452d1c..c74f013da1 100644
--- a/base/test/multiprocess_test_android.cc
+++ b/base/test/multiprocess_test_android.cc
@@ -4,451 +4,87 @@
#include "base/test/multiprocess_test.h"
-#include <errno.h>
#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include <memory>
-#include <utility>
#include <vector>
+#include "base/android/context_utils.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/scoped_java_ref.h"
#include "base/base_switches.h"
#include "base/command_line.h"
-#include "base/containers/hash_tables.h"
-#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/macros.h"
-#include "base/pickle.h"
-#include "base/posix/global_descriptors.h"
-#include "base/posix/unix_domain_socket_linux.h"
-#include "testing/multiprocess_func_list.h"
+#include "jni/MainReturnCodeResult_jni.h"
+#include "jni/MultiprocessTestClientLauncher_jni.h"
namespace base {
-namespace {
-
-const int kMaxMessageSize = 1024 * 1024;
-const int kFragmentSize = 4096;
-
-// Message sent between parent process and helper child process.
-enum class MessageType : uint32_t {
- START_REQUEST,
- START_RESPONSE,
- WAIT_REQUEST,
- WAIT_RESPONSE,
-};
-
-struct MessageHeader {
- uint32_t size;
- MessageType type;
-};
-
-struct StartProcessRequest {
- MessageHeader header =
- {sizeof(StartProcessRequest), MessageType::START_REQUEST};
-
- uint32_t num_args = 0;
- uint32_t num_fds = 0;
-};
-
-struct StartProcessResponse {
- MessageHeader header =
- {sizeof(StartProcessResponse), MessageType::START_RESPONSE};
-
- pid_t child_pid;
-};
-
-struct WaitProcessRequest {
- MessageHeader header =
- {sizeof(WaitProcessRequest), MessageType::WAIT_REQUEST};
-
- pid_t pid;
- uint64_t timeout_ms;
-};
-
-struct WaitProcessResponse {
- MessageHeader header =
- {sizeof(WaitProcessResponse), MessageType::WAIT_RESPONSE};
-
- bool success = false;
- int32_t exit_code = 0;
-};
-
-// Helper class that implements an alternate test child launcher for
-// multi-process tests. The default implementation doesn't work if the child is
-// launched after starting threads. However, for some tests (i.e. Mojo), this
-// is necessary. This implementation works around that issue by forking a helper
-// process very early in main(), before any real work is done. Then, when a
-// child needs to be spawned, a message is sent to that helper process, which
-// then forks and returns the result to the parent. The forked child then calls
-// main() and things look as though a brand new process has been fork/exec'd.
-class LaunchHelper {
- public:
- using MainFunction = int (*)(int, char**);
-
- LaunchHelper() {}
-
- // Initialise the alternate test child implementation.
- void Init(MainFunction main);
-
- // Starts a child test helper process.
- Process StartChildTestHelper(const std::string& procname,
- const CommandLine& base_command_line,
- const LaunchOptions& options);
-
- // Waits for a child test helper process.
- bool WaitForChildExitWithTimeout(const Process& process, TimeDelta timeout,
- int* exit_code);
-
- bool IsReady() const { return child_fd_ != -1; }
- bool IsChild() const { return is_child_; }
-
- private:
- // Wrappers around sendmsg/recvmsg that supports message fragmentation.
- void Send(int fd, const MessageHeader* msg, const std::vector<int>& fds);
- ssize_t Recv(int fd, void* buf, std::vector<ScopedFD>* fds);
-
- // Parent process implementation.
- void DoParent(int fd);
- // Helper process implementation.
- void DoHelper(int fd);
-
- void StartProcessInHelper(const StartProcessRequest* request,
- std::vector<ScopedFD> fds);
- void WaitForChildInHelper(const WaitProcessRequest* request);
-
- bool is_child_ = false;
-
- // Parent vars.
- int child_fd_ = -1;
-
- // Helper vars.
- int parent_fd_ = -1;
- MainFunction main_ = nullptr;
-
- DISALLOW_COPY_AND_ASSIGN(LaunchHelper);
-};
-
-void LaunchHelper::Init(MainFunction main) {
- main_ = main;
-
- // Create a communication channel between the parent and child launch helper.
- // fd[0] belongs to the parent, fd[1] belongs to the child.
- int fds[2] = {-1, -1};
- int rv = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds);
- PCHECK(rv == 0);
- CHECK_NE(-1, fds[0]);
- CHECK_NE(-1, fds[1]);
-
- pid_t pid = fork();
- PCHECK(pid >= 0) << "Fork failed";
- if (pid) {
- // Parent.
- rv = close(fds[1]);
- PCHECK(rv == 0);
- DoParent(fds[0]);
- } else {
- // Helper.
- rv = close(fds[0]);
- PCHECK(rv == 0);
- DoHelper(fds[1]);
- NOTREACHED();
- _exit(0);
- }
-}
-
-void LaunchHelper::Send(
- int fd, const MessageHeader* msg, const std::vector<int>& fds) {
- uint32_t bytes_remaining = msg->size;
- const char* buf = reinterpret_cast<const char*>(msg);
- while (bytes_remaining) {
- size_t send_size =
- (bytes_remaining > kFragmentSize) ? kFragmentSize : bytes_remaining;
- bool success = UnixDomainSocket::SendMsg(
- fd, buf, send_size,
- (bytes_remaining == msg->size) ? fds : std::vector<int>());
- CHECK(success);
- bytes_remaining -= send_size;
- buf += send_size;
- }
-}
-
-ssize_t LaunchHelper::Recv(int fd, void* buf, std::vector<ScopedFD>* fds) {
- ssize_t size = UnixDomainSocket::RecvMsg(fd, buf, kFragmentSize, fds);
- if (size <= 0)
- return size;
-
- const MessageHeader* header = reinterpret_cast<const MessageHeader*>(buf);
- CHECK(header->size < kMaxMessageSize);
- uint32_t bytes_remaining = header->size - size;
- char* buffer = reinterpret_cast<char*>(buf);
- buffer += size;
- while (bytes_remaining) {
- std::vector<ScopedFD> dummy_fds;
- size = UnixDomainSocket::RecvMsg(fd, buffer, kFragmentSize, &dummy_fds);
- if (size <= 0)
- return size;
-
- CHECK(dummy_fds.empty());
- CHECK(size == kFragmentSize ||
- static_cast<size_t>(size) == bytes_remaining);
- bytes_remaining -= size;
- buffer += size;
- }
- return header->size;
-}
-
-void LaunchHelper::DoParent(int fd) {
- child_fd_ = fd;
-}
-
-void LaunchHelper::DoHelper(int fd) {
- parent_fd_ = fd;
- is_child_ = true;
- std::unique_ptr<char[]> buf(new char[kMaxMessageSize]);
- while (true) {
- // Wait for a message from the parent.
- std::vector<ScopedFD> fds;
- ssize_t size = Recv(parent_fd_, buf.get(), &fds);
- if (size == 0 || (size < 0 && errno == ECONNRESET)) {
- _exit(0);
- }
- PCHECK(size > 0);
-
- const MessageHeader* header =
- reinterpret_cast<const MessageHeader*>(buf.get());
- CHECK_EQ(static_cast<ssize_t>(header->size), size);
- switch (header->type) {
- case MessageType::START_REQUEST:
- StartProcessInHelper(
- reinterpret_cast<const StartProcessRequest*>(buf.get()),
- std::move(fds));
- break;
- case MessageType::WAIT_REQUEST:
- WaitForChildInHelper(
- reinterpret_cast<const WaitProcessRequest*>(buf.get()));
- break;
- default:
- LOG(FATAL) << "Unsupported message type: "
- << static_cast<uint32_t>(header->type);
- }
- }
-}
-
-void LaunchHelper::StartProcessInHelper(const StartProcessRequest* request,
- std::vector<ScopedFD> fds) {
- pid_t pid = fork();
- PCHECK(pid >= 0) << "Fork failed";
- if (pid) {
- // Helper.
- StartProcessResponse resp;
- resp.child_pid = pid;
- Send(parent_fd_, reinterpret_cast<const MessageHeader*>(&resp),
- std::vector<int>());
- } else {
- // Child.
- PCHECK(close(parent_fd_) == 0);
- parent_fd_ = -1;
- CommandLine::Reset();
-
- Pickle serialised_extra(reinterpret_cast<const char*>(request + 1),
- request->header.size - sizeof(StartProcessRequest));
- PickleIterator iter(serialised_extra);
- std::vector<std::string> args;
- for (size_t i = 0; i < request->num_args; i++) {
- std::string arg;
- CHECK(iter.ReadString(&arg));
- args.push_back(std::move(arg));
- }
-
- CHECK_EQ(request->num_fds, fds.size());
- for (size_t i = 0; i < request->num_fds; i++) {
- int new_fd;
- CHECK(iter.ReadInt(&new_fd));
- int old_fd = fds[i].release();
- if (new_fd != old_fd) {
- if (dup2(old_fd, new_fd) < 0) {
- PLOG(FATAL) << "dup2";
- }
- PCHECK(close(old_fd) == 0);
- }
- }
+// A very basic implementation for Android. On Android tests can run in an APK
+// and we don't have an executable to exec*. This implementation does the bare
+// minimum to execute the method specified by procname (in the child process).
+// - All options except |fds_to_remap| are ignored.
+//
+// NOTE: This MUST NOT run on the main thread of the NativeTest application.
+Process SpawnMultiProcessTestChild(const std::string& procname,
+ const CommandLine& base_command_line,
+ const LaunchOptions& options) {
+ JNIEnv* env = android::AttachCurrentThread();
+ DCHECK(env);
- // argv has argc+1 elements, where the last element is NULL.
- std::unique_ptr<char*[]> argv(new char*[args.size() + 1]);
- for (size_t i = 0; i < args.size(); i++) {
- argv[i] = const_cast<char*>(args[i].c_str());
+ std::vector<int> fd_keys;
+ std::vector<int> fd_fds;
+ if (options.fds_to_remap) {
+ for (auto& iter : *options.fds_to_remap) {
+ fd_keys.push_back(iter.second);
+ fd_fds.push_back(iter.first);
}
- argv[args.size()] = nullptr;
- _exit(main_(args.size(), argv.get()));
- NOTREACHED();
}
-}
-
-void LaunchHelper::WaitForChildInHelper(const WaitProcessRequest* request) {
- Process process(request->pid);
- TimeDelta timeout = TimeDelta::FromMilliseconds(request->timeout_ms);
- int exit_code = -1;
- bool success = process.WaitForExitWithTimeout(timeout, &exit_code);
-
- WaitProcessResponse resp;
- resp.exit_code = exit_code;
- resp.success = success;
- Send(parent_fd_, reinterpret_cast<const MessageHeader*>(&resp),
- std::vector<int>());
-}
-Process LaunchHelper::StartChildTestHelper(const std::string& procname,
- const CommandLine& base_command_line,
- const LaunchOptions& options) {
+ android::ScopedJavaLocalRef<jobjectArray> fds =
+ android::Java_MultiprocessTestClientLauncher_makeFdInfoArray(
+ env, base::android::ToJavaIntArray(env, fd_keys),
+ base::android::ToJavaIntArray(env, fd_fds));
CommandLine command_line(base_command_line);
- if (!command_line.HasSwitch(switches::kTestChildProcess))
+ if (!command_line.HasSwitch(switches::kTestChildProcess)) {
command_line.AppendSwitchASCII(switches::kTestChildProcess, procname);
-
- StartProcessRequest request;
- Pickle serialised_extra;
- const CommandLine::StringVector& argv = command_line.argv();
- for (const auto& arg : argv)
- CHECK(serialised_extra.WriteString(arg));
- request.num_args = argv.size();
-
- std::vector<int> fds_to_send;
- if (options.fds_to_remap) {
- for (auto p : *options.fds_to_remap) {
- CHECK(serialised_extra.WriteInt(p.second));
- fds_to_send.push_back(p.first);
- }
- request.num_fds = options.fds_to_remap->size();
}
- size_t buf_size = sizeof(StartProcessRequest) + serialised_extra.size();
- request.header.size = buf_size;
- std::unique_ptr<char[]> buffer(new char[buf_size]);
- memcpy(buffer.get(), &request, sizeof(StartProcessRequest));
- memcpy(buffer.get() + sizeof(StartProcessRequest), serialised_extra.data(),
- serialised_extra.size());
-
- // Send start message.
- Send(child_fd_, reinterpret_cast<const MessageHeader*>(buffer.get()),
- fds_to_send);
-
- // Synchronously get response.
- StartProcessResponse response;
- std::vector<ScopedFD> recv_fds;
- ssize_t resp_size = Recv(child_fd_, &response, &recv_fds);
- PCHECK(resp_size == sizeof(StartProcessResponse));
-
- return Process(response.child_pid);
+ android::ScopedJavaLocalRef<jobjectArray> j_argv =
+ android::ToJavaArrayOfStrings(env, command_line.argv());
+ jint pid = android::Java_MultiprocessTestClientLauncher_launchClient(
+ env, android::GetApplicationContext(), j_argv, fds);
+ return Process(pid);
}
-bool LaunchHelper::WaitForChildExitWithTimeout(
- const Process& process, TimeDelta timeout, int* exit_code) {
-
- WaitProcessRequest request;
- request.pid = process.Handle();
- request.timeout_ms = timeout.InMilliseconds();
-
- Send(child_fd_, reinterpret_cast<const MessageHeader*>(&request),
- std::vector<int>());
-
- WaitProcessResponse response;
- std::vector<ScopedFD> recv_fds;
- ssize_t resp_size = Recv(child_fd_, &response, &recv_fds);
- PCHECK(resp_size == sizeof(WaitProcessResponse));
-
- if (!response.success)
+bool WaitForMultiprocessTestChildExit(const Process& process,
+ TimeDelta timeout,
+ int* exit_code) {
+ JNIEnv* env = android::AttachCurrentThread();
+ DCHECK(env);
+
+ base::android::ScopedJavaLocalRef<jobject> result_code =
+ android::Java_MultiprocessTestClientLauncher_waitForMainToReturn(
+ env, android::GetApplicationContext(), process.Pid(),
+ static_cast<int32_t>(timeout.InMilliseconds()));
+ if (result_code.is_null() ||
+ Java_MainReturnCodeResult_hasTimedOut(env, result_code)) {
return false;
-
- *exit_code = response.exit_code;
+ }
+ if (exit_code) {
+ *exit_code = Java_MainReturnCodeResult_getReturnCode(env, result_code);
+ }
return true;
}
-LazyInstance<LaunchHelper>::Leaky g_launch_helper;
-
-} // namespace
-
-void InitAndroidMultiProcessTestHelper(int (*main)(int, char**)) {
- DCHECK(main);
- // Don't allow child processes to themselves create new child processes.
- if (g_launch_helper.Get().IsChild())
- return;
- g_launch_helper.Get().Init(main);
-}
-
-bool AndroidIsChildProcess() {
- return g_launch_helper.Get().IsChild();
-}
-
-bool AndroidWaitForChildExitWithTimeout(
- const Process& process, TimeDelta timeout, int* exit_code) {
- CHECK(g_launch_helper.Get().IsReady());
- return g_launch_helper.Get().WaitForChildExitWithTimeout(
- process, timeout, exit_code);
-}
-
-// A very basic implementation for Android. On Android tests can run in an APK
-// and we don't have an executable to exec*. This implementation does the bare
-// minimum to execute the method specified by procname (in the child process).
-// - All options except |fds_to_remap| are ignored.
-Process SpawnMultiProcessTestChild(const std::string& procname,
- const CommandLine& base_command_line,
- const LaunchOptions& options) {
- if (g_launch_helper.Get().IsReady()) {
- return g_launch_helper.Get().StartChildTestHelper(
- procname, base_command_line, options);
- }
-
- // TODO(viettrungluu): The FD-remapping done below is wrong in the presence of
- // cycles (e.g., fd1 -> fd2, fd2 -> fd1). crbug.com/326576
- FileHandleMappingVector empty;
- const FileHandleMappingVector* fds_to_remap =
- options.fds_to_remap ? options.fds_to_remap : &empty;
-
- pid_t pid = fork();
-
- if (pid < 0) {
- PLOG(ERROR) << "fork";
- return Process();
- }
- if (pid > 0) {
- // Parent process.
- return Process(pid);
- }
- // Child process.
- base::hash_set<int> fds_to_keep_open;
- for (FileHandleMappingVector::const_iterator it = fds_to_remap->begin();
- it != fds_to_remap->end(); ++it) {
- fds_to_keep_open.insert(it->first);
- }
- // Keep standard FDs (stdin, stdout, stderr, etc.) open since this
- // is not meant to spawn a daemon.
- int base = GlobalDescriptors::kBaseDescriptor;
- for (int fd = base; fd < sysconf(_SC_OPEN_MAX); ++fd) {
- if (fds_to_keep_open.find(fd) == fds_to_keep_open.end()) {
- close(fd);
- }
- }
- for (FileHandleMappingVector::const_iterator it = fds_to_remap->begin();
- it != fds_to_remap->end(); ++it) {
- int old_fd = it->first;
- int new_fd = it->second;
- if (dup2(old_fd, new_fd) < 0) {
- PLOG(FATAL) << "dup2";
- }
- close(old_fd);
- }
- CommandLine::Reset();
- CommandLine::Init(0, nullptr);
- CommandLine* command_line = CommandLine::ForCurrentProcess();
- command_line->InitFromArgv(base_command_line.argv());
- if (!command_line->HasSwitch(switches::kTestChildProcess))
- command_line->AppendSwitchASCII(switches::kTestChildProcess, procname);
+bool TerminateMultiProcessTestChild(const Process& process,
+ int exit_code,
+ bool wait) {
+ JNIEnv* env = android::AttachCurrentThread();
+ DCHECK(env);
- _exit(multi_process_function_list::InvokeChildProcessTest(procname));
- return Process();
+ return android::Java_MultiprocessTestClientLauncher_terminate(
+ env, android::GetApplicationContext(), process.Pid(), exit_code, wait);
}
} // namespace base
diff --git a/base/test/opaque_ref_counted.cc b/base/test/opaque_ref_counted.cc
index ed6c36f1a2..36253e5ef9 100644
--- a/base/test/opaque_ref_counted.cc
+++ b/base/test/opaque_ref_counted.cc
@@ -11,17 +11,31 @@ namespace base {
class OpaqueRefCounted : public RefCounted<OpaqueRefCounted> {
public:
- OpaqueRefCounted() {}
+ OpaqueRefCounted() = default;
int Return42() { return 42; }
private:
- virtual ~OpaqueRefCounted() {}
+ friend class RefCounted<OpaqueRefCounted>;
+ ~OpaqueRefCounted() = default;
- friend RefCounted<OpaqueRefCounted>;
DISALLOW_COPY_AND_ASSIGN(OpaqueRefCounted);
};
+class OpaqueRefCountedThreadSafe
+ : public RefCounted<OpaqueRefCountedThreadSafe> {
+ public:
+ OpaqueRefCountedThreadSafe() = default;
+
+ int Return42() { return 42; }
+
+ private:
+ friend class RefCounted<OpaqueRefCountedThreadSafe>;
+ ~OpaqueRefCountedThreadSafe() = default;
+
+ DISALLOW_COPY_AND_ASSIGN(OpaqueRefCountedThreadSafe);
+};
+
scoped_refptr<OpaqueRefCounted> MakeOpaqueRefCounted() {
return new OpaqueRefCounted();
}
@@ -30,6 +44,16 @@ void TestOpaqueRefCounted(scoped_refptr<OpaqueRefCounted> p) {
EXPECT_EQ(42, p->Return42());
}
+scoped_refptr<OpaqueRefCountedThreadSafe> MakeOpaqueRefCountedThreadSafe() {
+ return new OpaqueRefCountedThreadSafe();
+}
+
+void TestOpaqueRefCountedThreadSafe(
+ scoped_refptr<OpaqueRefCountedThreadSafe> p) {
+ EXPECT_EQ(42, p->Return42());
+}
+
} // namespace base
template class scoped_refptr<base::OpaqueRefCounted>;
+template class scoped_refptr<base::OpaqueRefCountedThreadSafe>;
diff --git a/base/test/opaque_ref_counted.h b/base/test/opaque_ref_counted.h
index faf6a650fd..c0ddc87fe1 100644
--- a/base/test/opaque_ref_counted.h
+++ b/base/test/opaque_ref_counted.h
@@ -12,13 +12,18 @@ namespace base {
// OpaqueRefCounted is a test class for scoped_refptr to ensure it still works
// when the pointed-to type is opaque (i.e., incomplete).
class OpaqueRefCounted;
+class OpaqueRefCountedThreadSafe;
// Test functions that return and accept scoped_refptr<OpaqueRefCounted> values.
scoped_refptr<OpaqueRefCounted> MakeOpaqueRefCounted();
void TestOpaqueRefCounted(scoped_refptr<OpaqueRefCounted> p);
+scoped_refptr<OpaqueRefCountedThreadSafe> MakeOpaqueRefCountedThreadSafe();
+void TestOpaqueRefCountedThreadSafe(
+ scoped_refptr<OpaqueRefCountedThreadSafe> p);
} // namespace base
extern template class scoped_refptr<base::OpaqueRefCounted>;
+extern template class scoped_refptr<base::OpaqueRefCountedThreadSafe>;
#endif // BASE_TEST_OPAQUE_REF_COUNTED_H_
diff --git a/base/test/scoped_feature_list.cc b/base/test/scoped_feature_list.cc
new file mode 100644
index 0000000000..f0f3f4edfb
--- /dev/null
+++ b/base/test/scoped_feature_list.cc
@@ -0,0 +1,74 @@
+// Copyright 2016 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 "base/test/scoped_feature_list.h"
+
+#include <string>
+
+namespace base {
+namespace test {
+
+namespace {
+
+static std::string GetFeatureString(
+ const std::initializer_list<base::Feature>& features) {
+ std::string output;
+ for (const base::Feature& feature : features) {
+ if (!output.empty())
+ output += ",";
+ output += feature.name;
+ }
+ return output;
+}
+
+} // namespace
+
+ScopedFeatureList::ScopedFeatureList() {}
+
+ScopedFeatureList::~ScopedFeatureList() {
+ if (original_feature_list_) {
+ base::FeatureList::ClearInstanceForTesting();
+ base::FeatureList::RestoreInstanceForTesting(
+ std::move(original_feature_list_));
+ }
+}
+
+void ScopedFeatureList::Init() {
+ std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+ feature_list->InitializeFromCommandLine(std::string(), std::string());
+ InitWithFeatureList(std::move(feature_list));
+}
+
+void ScopedFeatureList::InitWithFeatures(
+ const std::initializer_list<base::Feature>& enabled_features,
+ const std::initializer_list<base::Feature>& disabled_features) {
+ InitFromCommandLine(GetFeatureString(enabled_features),
+ GetFeatureString(disabled_features));
+}
+
+void ScopedFeatureList::InitWithFeatureList(
+ std::unique_ptr<FeatureList> feature_list) {
+ DCHECK(!original_feature_list_);
+ original_feature_list_ = base::FeatureList::ClearInstanceForTesting();
+ base::FeatureList::SetInstance(std::move(feature_list));
+}
+
+void ScopedFeatureList::InitFromCommandLine(
+ const std::string& enable_features,
+ const std::string& disable_features) {
+ std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+ feature_list->InitializeFromCommandLine(enable_features, disable_features);
+ InitWithFeatureList(std::move(feature_list));
+}
+
+void ScopedFeatureList::InitAndEnableFeature(const base::Feature& feature) {
+ InitFromCommandLine(feature.name, std::string());
+}
+
+void ScopedFeatureList::InitAndDisableFeature(const base::Feature& feature) {
+ InitFromCommandLine(std::string(), feature.name);
+}
+
+} // namespace test
+} // namespace base
diff --git a/base/test/scoped_feature_list.h b/base/test/scoped_feature_list.h
new file mode 100644
index 0000000000..99e07f5374
--- /dev/null
+++ b/base/test/scoped_feature_list.h
@@ -0,0 +1,59 @@
+// Copyright 2016 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 BASE_TEST_SCOPED_FEATURE_LIST_H_
+#define BASE_TEST_SCOPED_FEATURE_LIST_H_
+
+#include <initializer_list>
+
+#include "base/feature_list.h"
+
+namespace base {
+namespace test {
+
+// ScopedFeatureList resets the global FeatureList instance to a new empty
+// instance and restores the original instance upon destruction.
+// Note: Re-using the same object is not allowed. To reset the feature
+// list and initialize it anew, destroy an existing scoped list and init
+// a new one.
+class ScopedFeatureList final {
+ public:
+ ScopedFeatureList();
+ ~ScopedFeatureList();
+
+ // Initializes and registers a FeatureList instance with no overrides.
+ void Init();
+
+ // Initializes and registers the given FeatureList instance.
+ void InitWithFeatureList(std::unique_ptr<FeatureList> feature_list);
+
+ // Initializes and registers a FeatureList instance with the given enabled
+ // and disabled features.
+ void InitWithFeatures(
+ const std::initializer_list<base::Feature>& enabled_features,
+ const std::initializer_list<base::Feature>& disabled_features);
+
+ // Initializes and registers a FeatureList instance with the given
+ // enabled and disabled features (comma-separated names).
+ void InitFromCommandLine(const std::string& enable_features,
+ const std::string& disable_features);
+
+ // Initializes and registers a FeatureList instance enabling a single
+ // feature.
+ void InitAndEnableFeature(const base::Feature& feature);
+
+ // Initializes and registers a FeatureList instance disabling a single
+ // feature.
+ void InitAndDisableFeature(const base::Feature& feature);
+
+ private:
+ std::unique_ptr<FeatureList> original_feature_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFeatureList);
+};
+
+} // namespace test
+} // namespace base
+
+#endif // BASE_TEST_SCOPED_FEATURE_LIST_H_
diff --git a/base/test/sequenced_worker_pool_owner.cc b/base/test/sequenced_worker_pool_owner.cc
index 8781495d7d..324d071352 100644
--- a/base/test/sequenced_worker_pool_owner.cc
+++ b/base/test/sequenced_worker_pool_owner.cc
@@ -14,7 +14,10 @@ SequencedWorkerPoolOwner::SequencedWorkerPoolOwner(
size_t max_threads,
const std::string& thread_name_prefix)
: constructor_message_loop_(MessageLoop::current()),
- pool_(new SequencedWorkerPool(max_threads, thread_name_prefix, this)),
+ pool_(new SequencedWorkerPool(max_threads,
+ thread_name_prefix,
+ TaskPriority::USER_VISIBLE,
+ this)),
has_work_call_count_(0) {}
SequencedWorkerPoolOwner::~SequencedWorkerPoolOwner() {
@@ -25,7 +28,8 @@ SequencedWorkerPoolOwner::~SequencedWorkerPoolOwner() {
exit_loop_.Run();
}
-const scoped_refptr<SequencedWorkerPool>& SequencedWorkerPoolOwner::pool() {
+const scoped_refptr<SequencedWorkerPool>& SequencedWorkerPoolOwner::pool()
+ const {
return pool_;
}
diff --git a/base/test/sequenced_worker_pool_owner.h b/base/test/sequenced_worker_pool_owner.h
index 05fc7505fb..28a6cf070a 100644
--- a/base/test/sequenced_worker_pool_owner.h
+++ b/base/test/sequenced_worker_pool_owner.h
@@ -39,7 +39,7 @@ class SequencedWorkerPoolOwner : public SequencedWorkerPool::TestingObserver {
~SequencedWorkerPoolOwner() override;
// Don't change the returned pool's testing observer.
- const scoped_refptr<SequencedWorkerPool>& pool();
+ const scoped_refptr<SequencedWorkerPool>& pool() const;
// The given callback will be called on WillWaitForShutdown().
void SetWillWaitForShutdownCallback(const Closure& callback);
diff --git a/base/test/test_file_util.h b/base/test/test_file_util.h
index 7042e48484..d9172d757e 100644
--- a/base/test/test_file_util.h
+++ b/base/test/test_file_util.h
@@ -20,6 +20,10 @@
#include <jni.h>
#endif
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
namespace base {
class FilePath;
@@ -40,14 +44,12 @@ bool DieFileDie(const FilePath& file, bool recurse);
bool EvictFileFromSystemCache(const FilePath& file);
#if defined(OS_WIN)
-// Returns true if the volume supports Alternate Data Streams.
-bool VolumeSupportsADS(const FilePath& path);
-
-// Returns true if the ZoneIdentifier is correctly set to "Internet" (3).
-// Note that this function must be called from the same process as
-// the one that set the zone identifier. I.e. don't use it in UI/automation
-// based tests.
-bool HasInternetZoneIdentifier(const FilePath& full_path);
+// Deny |permission| on the file |path| for the current user. |permission| is an
+// ACCESS_MASK structure which is defined in
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa374892.aspx
+// Refer to https://msdn.microsoft.com/en-us/library/aa822867.aspx for a list of
+// possible values.
+bool DenyFilePermission(const FilePath& path, DWORD permission);
#endif // defined(OS_WIN)
// For testing, make the file unreadable or unwritable.
@@ -70,9 +72,6 @@ class FilePermissionRestorer {
};
#if defined(OS_ANDROID)
-// Register the ContentUriTestUrils JNI bindings.
-bool RegisterContentUriTestUtils(JNIEnv* env);
-
// Insert an image file into the MediaStore, and retrieve the content URI for
// testing purpose.
FilePath InsertImageIntoMediaStore(const FilePath& path);
diff --git a/base/test/test_io_thread.cc b/base/test/test_io_thread.cc
index 1fa041251c..ce4a8d10de 100644
--- a/base/test/test_io_thread.cc
+++ b/base/test/test_io_thread.cc
@@ -4,19 +4,7 @@
#include "base/test/test_io_thread.h"
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/synchronization/waitable_event.h"
-
-namespace {
-
-void PostTaskAndWaitHelper(base::WaitableEvent* event,
- const base::Closure& task) {
- task.Run();
- event->Signal();
-}
-
-} // namespace
+#include "base/logging.h"
namespace base {
@@ -54,13 +42,4 @@ void TestIOThread::PostTask(const tracked_objects::Location& from_here,
task_runner()->PostTask(from_here, task);
}
-void TestIOThread::PostTaskAndWait(const tracked_objects::Location& from_here,
- const base::Closure& task) {
- base::WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
- WaitableEvent::InitialState::NOT_SIGNALED);
- task_runner()->PostTask(from_here,
- base::Bind(&PostTaskAndWaitHelper, &event, task));
- event.Wait();
-}
-
} // namespace base
diff --git a/base/test/test_io_thread.h b/base/test/test_io_thread.h
index c2ed1878d1..5d3885e81c 100644
--- a/base/test/test_io_thread.h
+++ b/base/test/test_io_thread.h
@@ -18,6 +18,13 @@ namespace base {
// Create and run an IO thread with a MessageLoop, and
// making the MessageLoop accessible from its client.
// It also provides some ideomatic API like PostTaskAndWait().
+//
+// This API is not thread-safe:
+// - Start()/Stop() should only be called from the main (creation) thread.
+// - PostTask()/message_loop()/task_runner() are also safe to call from the
+// underlying thread itself (to post tasks from other threads: get the
+// task_runner() from the main thread first, it is then safe to pass _it_
+// around).
class TestIOThread {
public:
enum Mode { kAutoStart, kManualStart };
@@ -25,19 +32,14 @@ class TestIOThread {
// Stops the I/O thread if necessary.
~TestIOThread();
- // |Start()|/|Stop()| should only be called from the main (creation) thread.
- // After |Stop()|, |Start()| may be called again to start a new I/O thread.
- // |Stop()| may be called even when the I/O thread is not started.
+ // After Stop(), Start() may be called again to start a new I/O thread.
+ // Stop() may be called even when the I/O thread is not started.
void Start();
void Stop();
// Post |task| to the IO thread.
void PostTask(const tracked_objects::Location& from_here,
const base::Closure& task);
- // Posts |task| to the IO-thread with an WaitableEvent associated blocks on
- // it until the posted |task| is executed, then returns.
- void PostTaskAndWait(const tracked_objects::Location& from_here,
- const base::Closure& task);
base::MessageLoopForIO* message_loop() {
return static_cast<base::MessageLoopForIO*>(io_thread_.message_loop());
diff --git a/base/test/test_mock_time_task_runner.cc b/base/test/test_mock_time_task_runner.cc
new file mode 100644
index 0000000000..f4bd7244b4
--- /dev/null
+++ b/base/test/test_mock_time_task_runner.cc
@@ -0,0 +1,321 @@
+// 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.
+
+#include "base/test/test_mock_time_task_runner.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
+#include "base/time/tick_clock.h"
+
+namespace base {
+
+namespace {
+
+// MockTickClock --------------------------------------------------------------
+
+// TickClock that always returns the then-current mock time ticks of
+// |task_runner| as the current time ticks.
+class MockTickClock : public TickClock {
+ public:
+ explicit MockTickClock(
+ scoped_refptr<const TestMockTimeTaskRunner> task_runner);
+
+ // TickClock:
+ TimeTicks NowTicks() override;
+
+ private:
+ scoped_refptr<const TestMockTimeTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockTickClock);
+};
+
+MockTickClock::MockTickClock(
+ scoped_refptr<const TestMockTimeTaskRunner> task_runner)
+ : task_runner_(task_runner) {
+}
+
+TimeTicks MockTickClock::NowTicks() {
+ return task_runner_->NowTicks();
+}
+
+// MockClock ------------------------------------------------------------------
+
+// Clock that always returns the then-current mock time of |task_runner| as the
+// current time.
+class MockClock : public Clock {
+ public:
+ explicit MockClock(scoped_refptr<const TestMockTimeTaskRunner> task_runner);
+
+ // Clock:
+ Time Now() override;
+
+ private:
+ scoped_refptr<const TestMockTimeTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockClock);
+};
+
+MockClock::MockClock(scoped_refptr<const TestMockTimeTaskRunner> task_runner)
+ : task_runner_(task_runner) {
+}
+
+Time MockClock::Now() {
+ return task_runner_->Now();
+}
+
+} // namespace
+
+// TestMockTimeTaskRunner::TestOrderedPendingTask -----------------------------
+
+// Subclass of TestPendingTask which has a strictly monotonically increasing ID
+// for every task, so that tasks posted with the same 'time to run' can be run
+// in the order of being posted.
+struct TestMockTimeTaskRunner::TestOrderedPendingTask
+ : public base::TestPendingTask {
+ TestOrderedPendingTask();
+ TestOrderedPendingTask(const tracked_objects::Location& location,
+ const Closure& task,
+ TimeTicks post_time,
+ TimeDelta delay,
+ size_t ordinal,
+ TestNestability nestability);
+ TestOrderedPendingTask(TestOrderedPendingTask&&);
+ ~TestOrderedPendingTask();
+
+ TestOrderedPendingTask& operator=(TestOrderedPendingTask&&);
+
+ size_t ordinal;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestOrderedPendingTask);
+};
+
+TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask()
+ : ordinal(0) {
+}
+
+TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
+ TestOrderedPendingTask&&) = default;
+
+TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
+ const tracked_objects::Location& location,
+ const Closure& task,
+ TimeTicks post_time,
+ TimeDelta delay,
+ size_t ordinal,
+ TestNestability nestability)
+ : base::TestPendingTask(location, task, post_time, delay, nestability),
+ ordinal(ordinal) {}
+
+TestMockTimeTaskRunner::TestOrderedPendingTask::~TestOrderedPendingTask() {
+}
+
+TestMockTimeTaskRunner::TestOrderedPendingTask&
+TestMockTimeTaskRunner::TestOrderedPendingTask::operator=(
+ TestOrderedPendingTask&&) = default;
+
+// TestMockTimeTaskRunner -----------------------------------------------------
+
+// TODO(gab): This should also set the SequenceToken for the current thread.
+// Ref. TestMockTimeTaskRunner::RunsTasksOnCurrentThread().
+TestMockTimeTaskRunner::ScopedContext::ScopedContext(
+ scoped_refptr<TestMockTimeTaskRunner> scope)
+ : on_destroy_(ThreadTaskRunnerHandle::OverrideForTesting(scope)) {
+ scope->RunUntilIdle();
+}
+
+TestMockTimeTaskRunner::ScopedContext::~ScopedContext() = default;
+
+bool TestMockTimeTaskRunner::TemporalOrder::operator()(
+ const TestOrderedPendingTask& first_task,
+ const TestOrderedPendingTask& second_task) const {
+ if (first_task.GetTimeToRun() == second_task.GetTimeToRun())
+ return first_task.ordinal > second_task.ordinal;
+ return first_task.GetTimeToRun() > second_task.GetTimeToRun();
+}
+
+TestMockTimeTaskRunner::TestMockTimeTaskRunner()
+ : now_(Time::UnixEpoch()), next_task_ordinal_(0) {
+}
+
+TestMockTimeTaskRunner::TestMockTimeTaskRunner(Time start_time,
+ TimeTicks start_ticks)
+ : now_(Time::UnixEpoch()), now_ticks_(start_ticks), next_task_ordinal_(0) {}
+
+TestMockTimeTaskRunner::~TestMockTimeTaskRunner() {
+}
+
+void TestMockTimeTaskRunner::FastForwardBy(TimeDelta delta) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_GE(delta, TimeDelta());
+
+ const TimeTicks original_now_ticks = now_ticks_;
+ ProcessAllTasksNoLaterThan(delta);
+ ForwardClocksUntilTickTime(original_now_ticks + delta);
+}
+
+void TestMockTimeTaskRunner::RunUntilIdle() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ProcessAllTasksNoLaterThan(TimeDelta());
+}
+
+void TestMockTimeTaskRunner::FastForwardUntilNoTasksRemain() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ProcessAllTasksNoLaterThan(TimeDelta::Max());
+}
+
+void TestMockTimeTaskRunner::ClearPendingTasks() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ AutoLock scoped_lock(tasks_lock_);
+ while (!tasks_.empty())
+ tasks_.pop();
+}
+
+Time TestMockTimeTaskRunner::Now() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return now_;
+}
+
+TimeTicks TestMockTimeTaskRunner::NowTicks() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return now_ticks_;
+}
+
+std::unique_ptr<Clock> TestMockTimeTaskRunner::GetMockClock() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return MakeUnique<MockClock>(this);
+}
+
+std::unique_ptr<TickClock> TestMockTimeTaskRunner::GetMockTickClock() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return MakeUnique<MockTickClock>(this);
+}
+
+std::deque<TestPendingTask> TestMockTimeTaskRunner::TakePendingTasks() {
+ AutoLock scoped_lock(tasks_lock_);
+ std::deque<TestPendingTask> tasks;
+ while (!tasks_.empty()) {
+ // It's safe to remove const and consume |task| here, since |task| is not
+ // used for ordering the item.
+ tasks.push_back(
+ std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
+ tasks_.pop();
+ }
+ return tasks;
+}
+
+bool TestMockTimeTaskRunner::HasPendingTask() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return !tasks_.empty();
+}
+
+size_t TestMockTimeTaskRunner::GetPendingTaskCount() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return tasks_.size();
+}
+
+TimeDelta TestMockTimeTaskRunner::NextPendingTaskDelay() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return tasks_.empty() ? TimeDelta::Max()
+ : tasks_.top().GetTimeToRun() - now_ticks_;
+}
+
+// TODO(gab): Combine |thread_checker_| with a SequenceToken to differentiate
+// between tasks running in the scope of this TestMockTimeTaskRunner and other
+// task runners sharing this thread. http://crbug.com/631186
+bool TestMockTimeTaskRunner::RunsTasksOnCurrentThread() const {
+ return thread_checker_.CalledOnValidThread();
+}
+
+bool TestMockTimeTaskRunner::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ AutoLock scoped_lock(tasks_lock_);
+ tasks_.push(TestOrderedPendingTask(from_here, task, now_ticks_, delay,
+ next_task_ordinal_++,
+ TestPendingTask::NESTABLE));
+ return true;
+}
+
+bool TestMockTimeTaskRunner::PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ return PostDelayedTask(from_here, task, delay);
+}
+
+bool TestMockTimeTaskRunner::IsElapsingStopped() {
+ return false;
+}
+
+void TestMockTimeTaskRunner::OnBeforeSelectingTask() {
+ // Empty default implementation.
+}
+
+void TestMockTimeTaskRunner::OnAfterTimePassed() {
+ // Empty default implementation.
+}
+
+void TestMockTimeTaskRunner::OnAfterTaskRun() {
+ // Empty default implementation.
+}
+
+void TestMockTimeTaskRunner::ProcessAllTasksNoLaterThan(TimeDelta max_delta) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_GE(max_delta, TimeDelta());
+
+ // Multiple test task runners can share the same thread for determinism in
+ // unit tests. Make sure this TestMockTimeTaskRunner's tasks run in its scope.
+ ScopedClosureRunner undo_override;
+ if (!ThreadTaskRunnerHandle::IsSet() ||
+ ThreadTaskRunnerHandle::Get() != this) {
+ undo_override = ThreadTaskRunnerHandle::OverrideForTesting(this);
+ }
+
+ const TimeTicks original_now_ticks = now_ticks_;
+ while (!IsElapsingStopped()) {
+ OnBeforeSelectingTask();
+ TestPendingTask task_info;
+ if (!DequeueNextTask(original_now_ticks, max_delta, &task_info))
+ break;
+ // If tasks were posted with a negative delay, task_info.GetTimeToRun() will
+ // be less than |now_ticks_|. ForwardClocksUntilTickTime() takes care of not
+ // moving the clock backwards in this case.
+ ForwardClocksUntilTickTime(task_info.GetTimeToRun());
+ std::move(task_info.task).Run();
+ OnAfterTaskRun();
+ }
+}
+
+void TestMockTimeTaskRunner::ForwardClocksUntilTickTime(TimeTicks later_ticks) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (later_ticks <= now_ticks_)
+ return;
+
+ now_ += later_ticks - now_ticks_;
+ now_ticks_ = later_ticks;
+ OnAfterTimePassed();
+}
+
+bool TestMockTimeTaskRunner::DequeueNextTask(const TimeTicks& reference,
+ const TimeDelta& max_delta,
+ TestPendingTask* next_task) {
+ AutoLock scoped_lock(tasks_lock_);
+ if (!tasks_.empty() &&
+ (tasks_.top().GetTimeToRun() - reference) <= max_delta) {
+ // It's safe to remove const and consume |task| here, since |task| is not
+ // used for ordering the item.
+ *next_task = std::move(const_cast<TestOrderedPendingTask&>(tasks_.top()));
+ tasks_.pop();
+ return true;
+ }
+ return false;
+}
+
+} // namespace base
diff --git a/base/test/test_mock_time_task_runner.h b/base/test/test_mock_time_task_runner.h
new file mode 100644
index 0000000000..54ebbdb7a8
--- /dev/null
+++ b/base/test/test_mock_time_task_runner.h
@@ -0,0 +1,223 @@
+// 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.
+
+#ifndef BASE_TEST_TEST_MOCK_TIME_TASK_RUNNER_H_
+#define BASE_TEST_TEST_MOCK_TIME_TASK_RUNNER_H_
+
+#include <stddef.h>
+
+#include <deque>
+#include <memory>
+#include <queue>
+#include <vector>
+
+#include "base/callback_helpers.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/test/test_pending_task.h"
+#include "base/threading/thread_checker_impl.h"
+#include "base/time/time.h"
+
+namespace base {
+
+class Clock;
+class TickClock;
+
+// Runs pending tasks in the order of the tasks' post time + delay, and keeps
+// track of a mock (virtual) tick clock time that can be fast-forwarded.
+//
+// TestMockTimeTaskRunner has the following properties:
+//
+// - Methods RunsTasksOnCurrentThread() and Post[Delayed]Task() can be called
+// from any thread, but the rest of the methods must be called on the same
+// thread the TaskRunner was created on.
+// - It allows for reentrancy, in that it handles the running of tasks that in
+// turn call back into it (e.g., to post more tasks).
+// - Tasks are stored in a priority queue, and executed in the increasing
+// order of post time + delay, but ignoring nestability.
+// - It does not check for overflow when doing time arithmetic. A sufficient
+// condition for preventing overflows is to make sure that the sum of all
+// posted task delays and fast-forward increments is still representable by
+// a TimeDelta, and that adding this delta to the starting values of Time
+// and TickTime is still within their respective range.
+// - Tasks aren't guaranteed to be destroyed immediately after they're run.
+//
+// This is a slightly more sophisticated version of TestSimpleTaskRunner, in
+// that it supports running delayed tasks in the correct temporal order.
+class TestMockTimeTaskRunner : public SingleThreadTaskRunner {
+ public:
+ // Everything that is executed in the scope of a ScopedContext will behave as
+ // though it ran under |scope| (i.e. ThreadTaskRunnerHandle,
+ // RunsTasksOnCurrentThread, etc.). This allows the test body to be all in one
+ // block when multiple TestMockTimeTaskRunners share the main thread. For
+ // example:
+ //
+ // class ExampleFixture {
+ // protected:
+ // DoBarOnFoo() {
+ // DCHECK(foo_task_runner_->RunsOnCurrentThread());
+ // EXPECT_EQ(foo_task_runner_, ThreadTaskRunnerHandle::Get());
+ // DoBar();
+ // }
+ //
+ // // Mock main task runner.
+ // base::MessageLoop message_loop_;
+ // base::ScopedMockTimeMessageLoopTaskRunner main_task_runner_;
+ //
+ // // Mock foo task runner.
+ // scoped_refptr<TestMockTimeTaskRunner> foo_task_runner_ =
+ // new TestMockTimeTaskRunner();
+ // };
+ //
+ // TEST_F(ExampleFixture, DoBarOnFoo) {
+ // DoThingsOnMain();
+ // {
+ // TestMockTimeTaskRunner::ScopedContext scoped_context(
+ // foo_task_runner_.get());
+ // DoBarOnFoo();
+ // }
+ // DoMoreThingsOnMain();
+ // }
+ //
+ class ScopedContext {
+ public:
+ // Note: |scope| is ran until idle as part of this constructor to ensure
+ // that anything which runs in the underlying scope runs after any already
+ // pending tasks (the contrary would break the SequencedTraskRunner
+ // contract).
+ explicit ScopedContext(scoped_refptr<TestMockTimeTaskRunner> scope);
+ ~ScopedContext();
+
+ private:
+ ScopedClosureRunner on_destroy_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedContext);
+ };
+
+ // Constructs an instance whose virtual time will start at the Unix epoch, and
+ // whose time ticks will start at zero.
+ TestMockTimeTaskRunner();
+
+ // Constructs an instance starting at the given virtual time and time ticks.
+ TestMockTimeTaskRunner(Time start_time, TimeTicks start_ticks);
+
+ // Fast-forwards virtual time by |delta|, causing all tasks with a remaining
+ // delay less than or equal to |delta| to be executed. |delta| must be
+ // non-negative.
+ void FastForwardBy(TimeDelta delta);
+
+ // Fast-forwards virtual time just until all tasks are executed.
+ void FastForwardUntilNoTasksRemain();
+
+ // Executes all tasks that have no remaining delay. Tasks with a remaining
+ // delay greater than zero will remain enqueued, and no virtual time will
+ // elapse.
+ void RunUntilIdle();
+
+ // Clears the queue of pending tasks without running them.
+ void ClearPendingTasks();
+
+ // Returns the current virtual time (initially starting at the Unix epoch).
+ Time Now() const;
+
+ // Returns the current virtual tick time (initially starting at 0).
+ TimeTicks NowTicks() const;
+
+ // Returns a Clock that uses the virtual time of |this| as its time source.
+ // The returned Clock will hold a reference to |this|.
+ std::unique_ptr<Clock> GetMockClock() const;
+
+ // Returns a TickClock that uses the virtual time ticks of |this| as its tick
+ // source. The returned TickClock will hold a reference to |this|.
+ std::unique_ptr<TickClock> GetMockTickClock() const;
+
+ std::deque<TestPendingTask> TakePendingTasks();
+ bool HasPendingTask() const;
+ size_t GetPendingTaskCount() const;
+ TimeDelta NextPendingTaskDelay() const;
+
+ // SingleThreadTaskRunner:
+ bool RunsTasksOnCurrentThread() const override;
+ bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) override;
+ bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) override;
+
+ protected:
+ ~TestMockTimeTaskRunner() override;
+
+ // Whether the elapsing of virtual time is stopped or not. Subclasses can
+ // override this method to perform early exits from a running task runner.
+ // Defaults to always return false.
+ virtual bool IsElapsingStopped();
+
+ // Called before the next task to run is selected, so that subclasses have a
+ // last chance to make sure all tasks are posted.
+ virtual void OnBeforeSelectingTask();
+
+ // Called after the current mock time has been incremented so that subclasses
+ // can react to the passing of time.
+ virtual void OnAfterTimePassed();
+
+ // Called after each task is run so that subclasses may perform additional
+ // activities, e.g., pump additional task runners.
+ virtual void OnAfterTaskRun();
+
+ private:
+ struct TestOrderedPendingTask;
+
+ // Predicate that defines a strict weak temporal ordering of tasks.
+ class TemporalOrder {
+ public:
+ bool operator()(const TestOrderedPendingTask& first_task,
+ const TestOrderedPendingTask& second_task) const;
+ };
+
+ typedef std::priority_queue<TestOrderedPendingTask,
+ std::vector<TestOrderedPendingTask>,
+ TemporalOrder> TaskPriorityQueue;
+
+ // Core of the implementation for all flavors of fast-forward methods. Given a
+ // non-negative |max_delta|, runs all tasks with a remaining delay less than
+ // or equal to |max_delta|, and moves virtual time forward as needed for each
+ // processed task. Pass in TimeDelta::Max() as |max_delta| to run all tasks.
+ void ProcessAllTasksNoLaterThan(TimeDelta max_delta);
+
+ // Forwards |now_ticks_| until it equals |later_ticks|, and forwards |now_| by
+ // the same amount. Calls OnAfterTimePassed() if |later_ticks| > |now_ticks_|.
+ // Does nothing if |later_ticks| <= |now_ticks_|.
+ void ForwardClocksUntilTickTime(TimeTicks later_ticks);
+
+ // Returns the |next_task| to run if there is any with a running time that is
+ // at most |reference| + |max_delta|. This additional complexity is required
+ // so that |max_delta| == TimeDelta::Max() can be supported.
+ bool DequeueNextTask(const TimeTicks& reference,
+ const TimeDelta& max_delta,
+ TestPendingTask* next_task);
+
+ // Also used for non-dcheck logic (RunsTasksOnCurrentThread()) and as such
+ // needs to be a ThreadCheckerImpl.
+ ThreadCheckerImpl thread_checker_;
+
+ Time now_;
+ TimeTicks now_ticks_;
+
+ // Temporally ordered heap of pending tasks. Must only be accessed while the
+ // |tasks_lock_| is held.
+ TaskPriorityQueue tasks_;
+
+ // The ordinal to use for the next task. Must only be accessed while the
+ // |tasks_lock_| is held.
+ size_t next_task_ordinal_;
+
+ Lock tasks_lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMockTimeTaskRunner);
+};
+
+} // namespace base
+
+#endif // BASE_TEST_TEST_MOCK_TIME_TASK_RUNNER_H_
diff --git a/base/test/test_pending_task.cc b/base/test/test_pending_task.cc
index 87b107e838..98bc0179b8 100644
--- a/base/test/test_pending_task.cc
+++ b/base/test/test_pending_task.cc
@@ -22,7 +22,9 @@ TestPendingTask::TestPendingTask(
delay(delay),
nestability(nestability) {}
-TestPendingTask::TestPendingTask(const TestPendingTask& other) = default;
+TestPendingTask::TestPendingTask(TestPendingTask&& other) = default;
+
+TestPendingTask& TestPendingTask::operator=(TestPendingTask&& other) = default;
TimeTicks TestPendingTask::GetTimeToRun() const {
return post_time + delay;
diff --git a/base/test/test_pending_task.h b/base/test/test_pending_task.h
index 2dbdb7eecc..42f3f42c7b 100644
--- a/base/test/test_pending_task.h
+++ b/base/test/test_pending_task.h
@@ -21,7 +21,7 @@ struct TestPendingTask {
enum TestNestability { NESTABLE, NON_NESTABLE };
TestPendingTask();
- TestPendingTask(const TestPendingTask& other);
+ TestPendingTask(TestPendingTask&& other);
TestPendingTask(const tracked_objects::Location& location,
const Closure& task,
TimeTicks post_time,
@@ -29,6 +29,8 @@ struct TestPendingTask {
TestNestability nestability);
~TestPendingTask();
+ TestPendingTask& operator=(TestPendingTask&& other);
+
// Returns post_time + delay.
TimeTicks GetTimeToRun() const;
@@ -51,7 +53,7 @@ struct TestPendingTask {
bool ShouldRunBefore(const TestPendingTask& other) const;
tracked_objects::Location location;
- Closure task;
+ OnceClosure task;
TimeTicks post_time;
TimeDelta delay;
TestNestability nestability;
@@ -61,6 +63,9 @@ struct TestPendingTask {
void AsValueInto(base::trace_event::TracedValue* state) const;
std::unique_ptr<base::trace_event::ConvertableToTraceFormat> AsValue() const;
std::string ToString() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestPendingTask);
};
// gtest helpers which allow pretty printing of the tasks, very useful in unit
diff --git a/base/test/test_simple_task_runner.cc b/base/test/test_simple_task_runner.cc
index cc39fab85a..090a72e96a 100644
--- a/base/test/test_simple_task_runner.cc
+++ b/base/test/test_simple_task_runner.cc
@@ -5,20 +5,20 @@
#include "base/test/test_simple_task_runner.h"
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/thread_task_runner_handle.h"
namespace base {
-TestSimpleTaskRunner::TestSimpleTaskRunner() {}
+TestSimpleTaskRunner::TestSimpleTaskRunner() = default;
-TestSimpleTaskRunner::~TestSimpleTaskRunner() {
- DCHECK(thread_checker_.CalledOnValidThread());
-}
+TestSimpleTaskRunner::~TestSimpleTaskRunner() = default;
bool TestSimpleTaskRunner::PostDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ AutoLock auto_lock(lock_);
pending_tasks_.push_back(
TestPendingTask(from_here, task, TimeTicks(), delay,
TestPendingTask::NESTABLE));
@@ -29,48 +29,70 @@ bool TestSimpleTaskRunner::PostNonNestableDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ AutoLock auto_lock(lock_);
pending_tasks_.push_back(
TestPendingTask(from_here, task, TimeTicks(), delay,
TestPendingTask::NON_NESTABLE));
return true;
}
+// TODO(gab): Use SequenceToken here to differentiate between tasks running in
+// the scope of this TestSimpleTaskRunner and other task runners sharing this
+// thread. http://crbug.com/631186
bool TestSimpleTaskRunner::RunsTasksOnCurrentThread() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return true;
+ return thread_ref_ == PlatformThread::CurrentRef();
}
-const std::deque<TestPendingTask>&
-TestSimpleTaskRunner::GetPendingTasks() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return pending_tasks_;
+std::deque<TestPendingTask> TestSimpleTaskRunner::TakePendingTasks() {
+ AutoLock auto_lock(lock_);
+ return std::move(pending_tasks_);
+}
+
+size_t TestSimpleTaskRunner::NumPendingTasks() const {
+ AutoLock auto_lock(lock_);
+ return pending_tasks_.size();
}
bool TestSimpleTaskRunner::HasPendingTask() const {
- DCHECK(thread_checker_.CalledOnValidThread());
+ AutoLock auto_lock(lock_);
return !pending_tasks_.empty();
}
base::TimeDelta TestSimpleTaskRunner::NextPendingTaskDelay() const {
- DCHECK(thread_checker_.CalledOnValidThread());
+ AutoLock auto_lock(lock_);
return pending_tasks_.front().GetTimeToRun() - base::TimeTicks();
}
+base::TimeDelta TestSimpleTaskRunner::FinalPendingTaskDelay() const {
+ AutoLock auto_lock(lock_);
+ return pending_tasks_.back().GetTimeToRun() - base::TimeTicks();
+}
+
void TestSimpleTaskRunner::ClearPendingTasks() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ AutoLock auto_lock(lock_);
pending_tasks_.clear();
}
void TestSimpleTaskRunner::RunPendingTasks() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(RunsTasksOnCurrentThread());
+
// Swap with a local variable to avoid re-entrancy problems.
std::deque<TestPendingTask> tasks_to_run;
- tasks_to_run.swap(pending_tasks_);
- for (std::deque<TestPendingTask>::iterator it = tasks_to_run.begin();
- it != tasks_to_run.end(); ++it) {
- it->task.Run();
+ {
+ AutoLock auto_lock(lock_);
+ tasks_to_run.swap(pending_tasks_);
}
+
+ // Multiple test task runners can share the same thread for determinism in
+ // unit tests. Make sure this TestSimpleTaskRunner's tasks run in its scope.
+ ScopedClosureRunner undo_override;
+ if (!ThreadTaskRunnerHandle::IsSet() ||
+ ThreadTaskRunnerHandle::Get() != this) {
+ undo_override = ThreadTaskRunnerHandle::OverrideForTesting(this);
+ }
+
+ for (auto& task : tasks_to_run)
+ std::move(task.task).Run();
}
void TestSimpleTaskRunner::RunUntilIdle() {
diff --git a/base/test/test_simple_task_runner.h b/base/test/test_simple_task_runner.h
index 338c634c8d..d089ba8a0b 100644
--- a/base/test/test_simple_task_runner.h
+++ b/base/test/test_simple_task_runner.h
@@ -10,8 +10,9 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
#include "base/test/test_pending_task.h"
-#include "base/threading/thread_checker.h"
+#include "base/threading/platform_thread.h"
namespace base {
@@ -25,8 +26,6 @@ class TimeDelta;
//
// TestSimpleTaskRunner has the following properties which make it simple:
//
-// - It is non-thread safe; all member functions must be called on
-// the same thread.
// - Tasks are simply stored in a queue in FIFO order, ignoring delay
// and nestability.
// - Tasks aren't guaranteed to be destroyed immediately after
@@ -36,10 +35,6 @@ class TimeDelta;
// handles the running of tasks that in turn call back into itself
// (e.g., to post more tasks).
//
-// If you need more complicated properties, consider using this class
-// as a template for writing a test TaskRunner implementation using
-// TestPendingTask.
-//
// Note that, like any TaskRunner, TestSimpleTaskRunner is
// ref-counted.
class TestSimpleTaskRunner : public SingleThreadTaskRunner {
@@ -56,27 +51,36 @@ class TestSimpleTaskRunner : public SingleThreadTaskRunner {
bool RunsTasksOnCurrentThread() const override;
- const std::deque<TestPendingTask>& GetPendingTasks() const;
+ std::deque<TestPendingTask> TakePendingTasks();
+ size_t NumPendingTasks() const;
bool HasPendingTask() const;
base::TimeDelta NextPendingTaskDelay() const;
+ base::TimeDelta FinalPendingTaskDelay() const;
// Clears the queue of pending tasks without running them.
void ClearPendingTasks();
- // Runs each current pending task in order and clears the queue.
- // Any tasks posted by the tasks are not run.
- virtual void RunPendingTasks();
+ // Runs each current pending task in order and clears the queue. Tasks posted
+ // by the tasks that run within this call do not run within this call. Can
+ // only be called on the thread that created this TestSimpleTaskRunner.
+ void RunPendingTasks();
- // Runs pending tasks until the queue is empty.
+ // Runs pending tasks until the queue is empty. Can only be called on the
+ // thread that created this TestSimpleTaskRunner.
void RunUntilIdle();
protected:
~TestSimpleTaskRunner() override;
+ private:
+ // Thread on which this was instantiated.
+ const PlatformThreadRef thread_ref_ = PlatformThread::CurrentRef();
+
+ // Synchronizes access to |pending_tasks_|.
+ mutable Lock lock_;
+
std::deque<TestPendingTask> pending_tasks_;
- ThreadChecker thread_checker_;
- private:
DISALLOW_COPY_AND_ASSIGN(TestSimpleTaskRunner);
};
diff --git a/base/test/test_timeouts.cc b/base/test/test_timeouts.cc
index 55e9a79861..0dc0f49dee 100644
--- a/base/test/test_timeouts.cc
+++ b/base/test/test_timeouts.cc
@@ -46,7 +46,10 @@ void InitializeTimeout(const char* switch_name, int min_value, int* value) {
std::string string_value(base::CommandLine::ForCurrentProcess()->
GetSwitchValueASCII(switch_name));
int timeout;
- base::StringToInt(string_value, &timeout);
+ if (string_value == TestTimeouts::kNoTimeoutSwitchValue)
+ timeout = kAlmostInfiniteTimeoutMs;
+ else
+ base::StringToInt(string_value, &timeout);
*value = std::max(*value, timeout);
}
*value *= kTimeoutMultiplier;
@@ -65,6 +68,9 @@ void InitializeTimeout(const char* switch_name, int* value) {
} // namespace
// static
+constexpr const char TestTimeouts::kNoTimeoutSwitchValue[];
+
+// static
bool TestTimeouts::initialized_ = false;
// The timeout values should increase in the order they appear in this block.
diff --git a/base/test/test_timeouts.h b/base/test/test_timeouts.h
index ddaf05b5e0..9d42eb91fe 100644
--- a/base/test/test_timeouts.h
+++ b/base/test/test_timeouts.h
@@ -13,6 +13,9 @@
// the timeouts for different environments (like Valgrind).
class TestTimeouts {
public:
+ // Argument that can be passed on the command line to indicate "no timeout".
+ static constexpr const char kNoTimeoutSwitchValue[] = "-1";
+
// Initializes the timeouts. Non thread-safe. Should be called exactly once
// by the test suite.
static void Initialize();
diff --git a/base/test/trace_event_analyzer.cc b/base/test/trace_event_analyzer.cc
index 55a349acf2..e61337cccb 100644
--- a/base/test/trace_event_analyzer.cc
+++ b/base/test/trace_event_analyzer.cc
@@ -34,8 +34,8 @@ TraceEvent::~TraceEvent() {
TraceEvent& TraceEvent::operator=(TraceEvent&& rhs) = default;
bool TraceEvent::SetFromJSON(const base::Value* event_value) {
- if (event_value->GetType() != base::Value::TYPE_DICTIONARY) {
- LOG(ERROR) << "Value must be TYPE_DICTIONARY";
+ if (event_value->GetType() != base::Value::Type::DICTIONARY) {
+ LOG(ERROR) << "Value must be Type::DICTIONARY";
return false;
}
const base::DictionaryValue* dictionary =
diff --git a/base/test/trace_event_analyzer_unittest.cc b/base/test/trace_event_analyzer_unittest.cc
index b4f0950793..ce7bce22a0 100644
--- a/base/test/trace_event_analyzer_unittest.cc
+++ b/base/test/trace_event_analyzer_unittest.cc
@@ -123,7 +123,7 @@ TEST_F(TraceEventAnalyzerTest, TraceEvent) {
std::unique_ptr<base::Value> arg;
EXPECT_TRUE(event.GetArgAsValue("dict", &arg));
- EXPECT_EQ(base::Value::TYPE_DICTIONARY, arg->GetType());
+ EXPECT_EQ(base::Value::Type::DICTIONARY, arg->GetType());
}
TEST_F(TraceEventAnalyzerTest, QueryEventMember) {