diff options
Diffstat (limited to 'base/test')
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 : ∅ - - 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) { |