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

Side by Side 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: Created 5 years, 10 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/app/chrome_watcher_client_win.h"
6
7 #include <windows.h>
8 #include <string>
9 #include "base/base_switches.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/process/process_handle.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "base/test/multiprocess_test.h"
18 #include "base/threading/simple_thread.h"
19 #include "base/time/time.h"
20 #include "base/win/scoped_handle.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "testing/multiprocess_func_list.h"
23
24 namespace {
25
26 const char kParentHandle[] = "parent-handle";
27 const char kEventHandle[] = "event-handle";
28
29 const base::char16 kExitEventName[] = L"ChromeWatcherClientTestFailEvent";
30 const base::char16 kInitializeEventName[] = L"ChromeWatcherClientTestFailEvent";
31
32 base::win::ScopedHandle InterpretHandleSwitch(base::CommandLine& cmd_line,
33 const char* switch_name) {
34 std::string str_handle =
35 cmd_line.GetSwitchValueASCII(switch_name);
36 if (str_handle.empty()) {
37 LOG(ERROR) << "Switch " << switch_name << " unexpectedly absent.";
38 return base::win::ScopedHandle();
39 }
40
41 unsigned int_handle = 0;
42 if (!base::StringToUint(str_handle, &int_handle)) {
43 LOG(ERROR) << "Switch " << switch_name << " has invalid value "
44 << str_handle;
45 return base::win::ScopedHandle();
46 }
47
48 return base::win::ScopedHandle(
49 reinterpret_cast<base::ProcessHandle>(int_handle));
50 }
51
52 // Simulates a Chrome watcher process. Exits when the global event
53 // kExitEventName is signaled. Signals the "on initialized" event (passed on the
54 // command-line) when the global event kInitializeEventName is signaled.
55 MULTIPROCESS_TEST_MAIN(ChromeWatcherClientTestProcess) {
56 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
57 base::win::ScopedHandle exit_event(
58 ::CreateEvent(NULL, FALSE, FALSE, kExitEventName));
Sigurður Ásgeirsson 2015/01/29 16:59:56 I think this will create flakes on parallel tests.
erikwright (departed) 2015/01/30 20:39:57 Done.
59 if (!exit_event.IsValid()) {
60 LOG(ERROR) << "Failed to create event named " << kExitEventName;
61 return 1;
62 }
63
64 base::win::ScopedHandle initialize_event(
65 ::CreateEvent(NULL, FALSE, FALSE, kInitializeEventName));
Sigurður Ásgeirsson 2015/01/29 16:59:56 ditto
erikwright (departed) 2015/01/30 20:39:57 Done.
66 if (!initialize_event.IsValid()) {
67 LOG(ERROR) << "Failed to create event named " << kInitializeEventName;
68 return 1;
69 }
70
71 base::win::ScopedHandle parent_process(
72 InterpretHandleSwitch(*cmd_line, kParentHandle));
73 if (!parent_process.IsValid())
74 return 1;
75
76 base::win::ScopedHandle on_initialized_event(
77 InterpretHandleSwitch(*cmd_line, kEventHandle));
78 if (!on_initialized_event.IsValid())
79 return 1;
80
81 while (true) {
82 // We loop as a convenient way to continue waiting for the exit_event after
83 // the initialize_event is signaled. We expect to get initialize_event zero
84 // or one times before exit_event, never more.
85 HANDLE handles[] = {exit_event.Get(), initialize_event.Get()};
86 DWORD result =
87 ::WaitForMultipleObjects(arraysize(handles), handles, FALSE, INFINITE);
88 switch (result) {
89 case WAIT_OBJECT_0:
90 // exit_event
91 return 0;
92 case WAIT_OBJECT_0 + 1:
93 // initialize_event
94 ::SetEvent(on_initialized_event.Get());
95 break;
96 case WAIT_FAILED:
97 PLOG(ERROR) << "Unexpected failure in WaitForMultipleObjects.";
98 return 1;
99 default:
100 NOTREACHED() << "Unexpected result from WaitForMultipleObjects: "
101 << result;
102 return 1;
103 }
104 }
105 }
106
107 // Returns a command line to launch back into ChromeWatcherClientTestProcess.
108 base::CommandLine GenerateCommandLine(HANDLE on_initialized_event,
109 HANDLE parent_handle) {
110 base::CommandLine ret = base::GetMultiProcessTestChildBaseCommandLine();
111
112 ret.AppendSwitchASCII(switches::kTestChildProcess, "VerifyParentHandle");
113 ret.AppendSwitchASCII(
114 kEventHandle,
115 base::UintToString(reinterpret_cast<unsigned int>(parent_handle)));
116 ret.AppendSwitchASCII(
117 kParentHandle,
118 base::UintToString(reinterpret_cast<unsigned int>(parent_handle)));
119 return ret;
120 }
121
122 // Implements a thread to launch the ChromeWatcherClient and block on
123 // WaitForInitialization. Provides various helpers to interact with the
124 // ChromeWatcherClient.
125 class ChromeWatcherClientThread : public base::SimpleThread {
126 public:
127 ChromeWatcherClientThread()
128 : client_(base::Bind(&GenerateCommandLine)),
129 complete_(false, false),
130 result_(false),
131 SimpleThread("ChromeWatcherClientTest thread") {}
132
133 // Waits up to |timeout| for the call to WaitForInitialization to complete. If
134 // it does, sets |result| to the return value of WaitForInitialization and
135 // returns true. Otherwise returns false.
136 bool WaitForResultWithTimeout(const base::TimeDelta& timeout, bool* result) {
137 if (!complete_.TimedWait(timeout))
138 return false;
139 *result = result_;
140 return true;
141 }
142
143 // Waits indefinitely for the call to WaitForInitialization to complete.
144 // Returns the return value of WaitForInitialization.
145 bool WaitForResult() {
146 complete_.Wait();
147 return result_;
148 }
149
150 ChromeWatcherClient& client() { return client_; }
151
152 // base::SimpleThread implementation.
153 void Run() override {
154 result_ = client_.LaunchWatcher();
155 if (result_)
156 result_ = client_.WaitForInitialization();
157 complete_.Signal();
158 }
159
160 private:
161 // The instance under test.
162 ChromeWatcherClient client_;
163 // Signaled when WaitForInitialization returns.
164 base::WaitableEvent complete_;
165 // The return value of WaitForInitialization.
166 bool result_;
167
168 DISALLOW_COPY_AND_ASSIGN(ChromeWatcherClientThread);
169 };
170
171 } // namespace
172
173 class ChromeWatcherClientTest : public testing::Test {
174 protected:
175 // Sends a signal to the simulated watcher process to exit. Returns true if
176 // successful.
177 bool SignalExit() { return FALSE != ::SetEvent(exit_event_.Get()); }
178
179 // Sends a signal to the simulated watcher process to signal its
180 // "initialization". Returns true if successful.
181 bool SignalInitialize() {
182 return FALSE != ::SetEvent(initialize_event_.Get());
183 }
184
185 // The helper thread, which also provides access to the ChromeWatcherClient.
186 ChromeWatcherClientThread& thread() { return thread_; }
187
188 // testing::Test implementation.
189 void SetUp() override {
190 exit_event_.Set(::CreateEvent(NULL, FALSE, FALSE, kExitEventName));
191 ASSERT_TRUE(exit_event_.IsValid());
192 initialize_event_.Set(
193 ::CreateEvent(NULL, FALSE, FALSE, kInitializeEventName));
194 ASSERT_TRUE(initialize_event_.IsValid());
195 }
196
197 void TearDown() override {
198 if (thread_.client().launched())
199 thread_.client().TerminateWatcher(99);
200 thread_.Join();
201 }
202
203 private:
204 // Used to launch and block on the Chrome watcher process in a background
205 // thread.
206 ChromeWatcherClientThread thread_;
207 // Used to signal the Chrome watcher process to exit.
208 base::win::ScopedHandle exit_event_;
209 // Used to signal the Chrome watcher process to signal its own
210 // initialization..
211 base::win::ScopedHandle initialize_event_;
212 };
213
214 TEST_F(ChromeWatcherClientTest, SuccessTest) {
215 thread().Start();
216 bool result = false;
217 // Give a broken implementation a chance to exit unexpectedly.
218 ASSERT_FALSE(thread().WaitForResultWithTimeout(
219 base::TimeDelta::FromMilliseconds(100), &result));
220 ASSERT_TRUE(SignalInitialize());
221 ASSERT_TRUE(thread().WaitForResult());
222 // The watcher should still be running. Give a broken implementation a chance
223 // to exit unexpectedly, then kill it. That the returned exit code is what we
224 // requested proves that the process was still running.
225 ASSERT_FALSE(thread().WaitForResultWithTimeout(
226 base::TimeDelta::FromMilliseconds(100), &result));
227 ASSERT_EQ(99, thread().client().TerminateWatcher(99));
228 }
229
230 TEST_F(ChromeWatcherClientTest, FailureTest) {
231 thread().Start();
232 bool result = false;
233 // Give a broken implementation a chance to exit unexpectedly.
234 ASSERT_FALSE(thread().WaitForResultWithTimeout(
235 base::TimeDelta::FromMilliseconds(100), &result));
236 ASSERT_TRUE(SignalExit());
237 ASSERT_FALSE(thread().WaitForResult());
238 // The exit code will not be as requested since the process exited "normally".
239 ASSERT_EQ(0, thread().client().TerminateWatcher(99));
240 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698