Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(395)

Unified Diff: chrome/app/chrome_watcher_client_unittest_win.cc

Issue 886613002: Introduce the ability to wait for the watcher process to initialize. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add some comments. Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/app/chrome_watcher_client_unittest_win.cc
diff --git a/chrome/app/chrome_watcher_client_unittest_win.cc b/chrome/app/chrome_watcher_client_unittest_win.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7489113047e13b9173f07c3a8d9c69a95b7a7a39
--- /dev/null
+++ b/chrome/app/chrome_watcher_client_unittest_win.cc
@@ -0,0 +1,270 @@
+// 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 "chrome/app/chrome_watcher_client_win.h"
+
+#include <windows.h>
+#include <string>
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/process/process_handle.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/multiprocess_test.h"
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "base/win/scoped_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace {
+
+const char kParentHandle[] = "parent-handle";
+const char kEventHandle[] = "event-handle";
+const char kNamedEventSuffix[] = "named-event-suffix";
+
+const base::char16 kExitEventBaseName[] = L"ChromeWatcherClientTestExitEvent_";
+const base::char16 kInitializeEventBaseName[] =
+ L"ChromeWatcherClientTestInitializeEvent_";
+
+base::win::ScopedHandle InterpretHandleSwitch(base::CommandLine& cmd_line,
+ const char* switch_name) {
+ std::string str_handle =
+ cmd_line.GetSwitchValueASCII(switch_name);
+ if (str_handle.empty()) {
+ LOG(ERROR) << "Switch " << switch_name << " unexpectedly absent.";
+ return base::win::ScopedHandle();
+ }
+
+ unsigned int_handle = 0;
+ if (!base::StringToUint(str_handle, &int_handle)) {
+ LOG(ERROR) << "Switch " << switch_name << " has invalid value "
+ << str_handle;
+ return base::win::ScopedHandle();
+ }
+
+ return base::win::ScopedHandle(
+ reinterpret_cast<base::ProcessHandle>(int_handle));
+}
+
+// Simulates a Chrome watcher process. Exits when the global exit event is
+// signaled. Signals the "on initialized" event (passed on the command-line)
+// when the global initialization event is signaled.
+MULTIPROCESS_TEST_MAIN(ChromeWatcherClientTestProcess) {
+ base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+
+ base::string16 named_event_suffix =
+ base::ASCIIToUTF16(cmd_line->GetSwitchValueASCII(kNamedEventSuffix));
+ if (named_event_suffix.empty()) {
+ LOG(ERROR) << "Switch " << kNamedEventSuffix << " unexpectedly absent.";
+ return 1;
+ }
+
+ base::win::ScopedHandle exit_event(::CreateEvent(
+ NULL, FALSE, FALSE, (kExitEventBaseName + named_event_suffix).c_str()));
+ if (!exit_event.IsValid()) {
+ LOG(ERROR) << "Failed to create event named "
+ << kExitEventBaseName + named_event_suffix;
+ return 1;
+ }
+
+ base::win::ScopedHandle initialize_event(
+ ::CreateEvent(NULL, FALSE, FALSE,
+ (kInitializeEventBaseName + named_event_suffix).c_str()));
+ if (!initialize_event.IsValid()) {
+ LOG(ERROR) << "Failed to create event named "
+ << kInitializeEventBaseName + named_event_suffix;
+ return 1;
+ }
+
+ base::win::ScopedHandle parent_process(
+ InterpretHandleSwitch(*cmd_line, kParentHandle));
+ if (!parent_process.IsValid())
+ return 1;
+
+ base::win::ScopedHandle on_initialized_event(
+ InterpretHandleSwitch(*cmd_line, kEventHandle));
+ if (!on_initialized_event.IsValid())
+ return 1;
+
+ while (true) {
+ // We loop as a convenient way to continue waiting for the exit_event after
+ // the initialize_event is signaled. We expect to get initialize_event zero
+ // or one times before exit_event, never more.
+ HANDLE handles[] = {exit_event.Get(), initialize_event.Get()};
+ DWORD result =
+ ::WaitForMultipleObjects(arraysize(handles), handles, FALSE, INFINITE);
+ switch (result) {
+ case WAIT_OBJECT_0:
+ // exit_event
+ return 0;
+ case WAIT_OBJECT_0 + 1:
+ // initialize_event
+ ::SetEvent(on_initialized_event.Get());
+ break;
+ case WAIT_FAILED:
+ PLOG(ERROR) << "Unexpected failure in WaitForMultipleObjects.";
+ return 1;
+ default:
+ NOTREACHED() << "Unexpected result from WaitForMultipleObjects: "
+ << result;
+ return 1;
+ }
+ }
+}
+
+// Implements a thread to launch the ChromeWatcherClient and block on
+// EnsureInitialized. Provides various helpers to interact with the
+// ChromeWatcherClient.
+class ChromeWatcherClientThread : public base::SimpleThread {
+ public:
+ ChromeWatcherClientThread()
+ : client_(base::Bind(&ChromeWatcherClientThread::GenerateCommandLine,
+ base::Unretained(this))),
+ complete_(false, false),
+ result_(false),
+ SimpleThread("ChromeWatcherClientTest thread") {}
+
+ // Waits up to |timeout| for the call to EnsureInitialized to complete. If it
+ // does, sets |result| to the return value of EnsureInitialized and returns
+ // true. Otherwise returns false.
+ bool WaitForResultWithTimeout(const base::TimeDelta& timeout, bool* result) {
+ if (!complete_.TimedWait(timeout))
+ return false;
+ *result = result_;
+ return true;
+ }
+
+ // Waits indefinitely for the call to WaitForInitialization to complete.
+ // Returns the return value of WaitForInitialization.
+ bool WaitForResult() {
+ complete_.Wait();
+ return result_;
+ }
+
+ ChromeWatcherClient& client() { return client_; }
+
+ base::string16 NamedEventSuffix() {
+ return base::UintToString16(base::GetCurrentProcId());
+ }
+
+ // base::SimpleThread implementation.
+ void Run() override {
+ result_ = client_.LaunchWatcher();
+ if (result_)
+ result_ = client_.EnsureInitialized();
+ complete_.Signal();
+ }
+
+ private:
+ // Returns a command line to launch back into ChromeWatcherClientTestProcess.
+ base::CommandLine GenerateCommandLine(HANDLE on_initialized_event,
+ HANDLE parent_handle) {
+ base::CommandLine ret = base::GetMultiProcessTestChildBaseCommandLine();
+ ret.AppendSwitchASCII(switches::kTestChildProcess,
+ "ChromeWatcherClientTestProcess");
+ ret.AppendSwitchASCII(kEventHandle,
+ base::UintToString(reinterpret_cast<unsigned int>(
+ on_initialized_event)));
+ ret.AppendSwitchASCII(
+ kParentHandle,
+ base::UintToString(reinterpret_cast<unsigned int>(parent_handle)));
+ ret.AppendSwitchASCII(kNamedEventSuffix,
+ base::UTF16ToASCII(NamedEventSuffix()));
+ return ret;
+ }
+
+ // The instance under test.
+ ChromeWatcherClient client_;
+ // Signaled when WaitForInitialization returns.
+ base::WaitableEvent complete_;
+ // The return value of WaitForInitialization.
+ bool result_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeWatcherClientThread);
+};
+
+} // namespace
+
+class ChromeWatcherClientTest : public testing::Test {
+ protected:
+ // Sends a signal to the simulated watcher process to exit. Returns true if
+ // successful.
+ bool SignalExit() { return FALSE != ::SetEvent(exit_event_.Get()); }
+
+ // Sends a signal to the simulated watcher process to signal its
+ // "initialization". Returns true if successful.
+ bool SignalInitialize() {
+ return FALSE != ::SetEvent(initialize_event_.Get());
+ }
+
+ // The helper thread, which also provides access to the ChromeWatcherClient.
+ ChromeWatcherClientThread& thread() { return thread_; }
+
+ // testing::Test implementation.
+ void SetUp() override {
+ exit_event_.Set(::CreateEvent(
+ NULL, FALSE, FALSE,
+ (kExitEventBaseName + thread_.NamedEventSuffix()).c_str()));
+ ASSERT_TRUE(exit_event_.IsValid());
+ initialize_event_.Set(::CreateEvent(
+ NULL, FALSE, FALSE,
+ (kInitializeEventBaseName + thread_.NamedEventSuffix()).c_str()));
+ ASSERT_TRUE(initialize_event_.IsValid());
+ }
+
+ void TearDown() override {
+ // Even if we never launched, the following is harmless.
+ SignalExit();
+ int exit_code = 0;
+ thread_.client().WaitForExit(&exit_code);
+ thread_.Join();
+ }
+
+ private:
+ // Used to launch and block on the Chrome watcher process in a background
+ // thread.
+ ChromeWatcherClientThread thread_;
+ // Used to signal the Chrome watcher process to exit.
+ base::win::ScopedHandle exit_event_;
+ // Used to signal the Chrome watcher process to signal its own
+ // initialization..
+ base::win::ScopedHandle initialize_event_;
+};
+
+TEST_F(ChromeWatcherClientTest, SuccessTest) {
+ thread().Start();
+ bool result = false;
+ // Give a broken implementation a chance to exit unexpectedly.
+ ASSERT_FALSE(thread().WaitForResultWithTimeout(
+ base::TimeDelta::FromMilliseconds(100), &result));
+ ASSERT_TRUE(SignalInitialize());
+ ASSERT_TRUE(thread().WaitForResult());
+ // The watcher should still be running. Give a broken implementation a chance
+ // to exit unexpectedly, then signal it to exit.
+ int exit_code = 0;
+ ASSERT_FALSE(thread().client().WaitForExitWithTimeout(
+ base::TimeDelta::FromMilliseconds(100), &exit_code));
+ SignalExit();
+ ASSERT_TRUE(thread().client().WaitForExit(&exit_code));
+ ASSERT_EQ(0, exit_code);
+}
+
+TEST_F(ChromeWatcherClientTest, FailureTest) {
+ thread().Start();
+ bool result = false;
+ // Give a broken implementation a chance to exit unexpectedly.
+ ASSERT_FALSE(thread().WaitForResultWithTimeout(
+ base::TimeDelta::FromMilliseconds(100), &result));
+ ASSERT_TRUE(SignalExit());
+ ASSERT_FALSE(thread().WaitForResult());
+ int exit_code = 0;
+ ASSERT_TRUE(
+ thread().client().WaitForExitWithTimeout(base::TimeDelta(), &exit_code));
+ ASSERT_EQ(0, exit_code);
+}

Powered by Google App Engine
This is Rietveld 408576698