OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 <string> | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/file_path.h" | |
10 #include "base/memory/scoped_ptr.h" | |
11 #include "base/synchronization/waitable_event.h" | |
12 #include "chrome/common/automation_messages.h" | |
13 #include "chrome_frame/cfproxy_private.h" | |
14 #include "chrome_frame/test/chrome_frame_test_utils.h" | |
15 #include "chrome_frame/test/test_scrubber.h" | |
16 #include "testing/gtest/include/gtest/gtest.h" | |
17 #include "testing/gmock/include/gmock/gmock.h" | |
18 #include "testing/gmock_mutant.h" | |
19 | |
20 using testing::_; | |
21 using testing::DoAll; | |
22 using testing::NotNull; | |
23 using testing::Return; | |
24 using testing::StrictMock; | |
25 using testing::InvokeWithoutArgs; | |
26 using testing::WithoutArgs; | |
27 using testing::CreateFunctor; | |
28 using testing::StrEq; | |
29 using testing::Eq; | |
30 | |
31 // There is not much to test here since CFProxy is pretty dumb. | |
32 struct MockFactory : public ChromeProxyFactory { | |
33 MOCK_METHOD0(CreateProxy, ChromeProxy*()); | |
34 }; | |
35 | |
36 struct MockChromeProxyDelegate : public ChromeProxyDelegate { | |
37 MOCK_METHOD1(OnMessageReceived, bool(const IPC::Message& message)); | |
38 MOCK_METHOD1(Connected, void(ChromeProxy* proxy)); | |
39 MOCK_METHOD2(PeerLost, void(ChromeProxy*, enum DisconnectReason reason)); | |
40 MOCK_METHOD0(Disconnected, void()); | |
41 MOCK_METHOD0(tab_handle, int()); | |
42 | |
43 MOCK_METHOD5(Completed_CreateTab, void(bool success, HWND chrome_wnd, | |
44 HWND tab_window, int tab_handle, int session_id)); | |
45 MOCK_METHOD5(Completed_ConnectToTab, void(bool success, HWND chrome_window, | |
46 HWND tab_window, int tab_handle, int session_id)); | |
47 MOCK_METHOD2(Completed_Navigate, void(bool success, | |
48 enum AutomationMsg_NavigationResponseValues res)); | |
49 | |
50 // Network requests from Chrome. | |
51 MOCK_METHOD2(Network_Start, void(int request_id, | |
52 const AutomationURLRequest& request_info)); | |
53 MOCK_METHOD2(Network_Read, void(int request_id, int bytes_to_read)); | |
54 MOCK_METHOD2(Network_End, void(int request_id, | |
55 const net::URLRequestStatus& s)); | |
56 MOCK_METHOD1(Network_DownloadInHost, void(int request_id)); | |
57 MOCK_METHOD2(GetCookies, void(const GURL& url, int cookie_id)); | |
58 MOCK_METHOD2(SetCookie, void(const GURL& url, const std::string& cookie)); | |
59 | |
60 // Navigation progress notifications. | |
61 MOCK_METHOD2(NavigationStateChanged, void(int flags, | |
62 const NavigationInfo& nav_info)); | |
63 MOCK_METHOD1(UpdateTargetUrl, void(const std::wstring& url)); | |
64 MOCK_METHOD2(NavigationFailed, void(int error_code, const GURL& gurl)); | |
65 MOCK_METHOD1(DidNavigate, void(const NavigationInfo& navigation_info)); | |
66 MOCK_METHOD1(TabLoaded, void(const GURL& url)); | |
67 | |
68 // | |
69 MOCK_METHOD3(OpenURL, void(const GURL& url_to_open, const GURL& referrer, | |
70 int open_disposition)); | |
71 MOCK_METHOD1(GoToHistoryOffset, void(int offset)); | |
72 MOCK_METHOD3(MessageToHost, void(const std::string& message, | |
73 const std::string& origin, const std::string& target)); | |
74 | |
75 // Misc. UI. | |
76 MOCK_METHOD1(HandleAccelerator, void(const MSG& accel_message)); | |
77 MOCK_METHOD3(HandleContextMenu, void(HANDLE menu_handle, int align_flags, | |
78 const MiniContextMenuParams& params)); | |
79 MOCK_METHOD1(TabbedOut, void(bool reverse)); | |
80 | |
81 // | |
82 MOCK_METHOD0(TabClosed, void()); | |
83 MOCK_METHOD1(AttachTab, void(const AttachExternalTabParams& attach_params)); | |
84 }; | |
85 | |
86 struct MockSender : public IPC::Message::Sender { | |
87 MOCK_METHOD1(Send, bool(IPC::Message* m)); | |
88 }; | |
89 | |
90 struct MockCFProxyTraits : public CFProxyTraits { | |
91 MOCK_METHOD2(DoCreateChannel, IPC::Message::Sender*(const std::string& id, | |
92 IPC::Channel::Listener* l)); | |
93 MOCK_METHOD1(CloseChannel, void(IPC::Message::Sender* s)); | |
94 MOCK_METHOD1(LaunchApp, bool(const std::wstring& cmd_line)); | |
95 | |
96 // Forward the CreateChannel to DoCreateChannel, but save the ipc_thread | |
97 // and the listener (i.e. proxy implementation of Channel::Listener) | |
98 virtual IPC::Message::Sender* CreateChannel(const std::string& id, | |
99 IPC::Channel::Listener* l) { | |
100 ipc_loop = MessageLoop::current(); | |
101 listener = l; | |
102 return this->DoCreateChannel(id, l); | |
103 } | |
104 | |
105 // Simulate some activity in the IPC thread. | |
106 // You may find API_FIRE_XXXX macros (see below) handy instead. | |
107 void FireConnect(base::TimeDelta t) { | |
108 ASSERT_TRUE(ipc_loop != NULL); | |
109 ipc_loop->PostDelayedTask( | |
110 FROM_HERE, base::Bind(&IPC::Channel::Listener::OnChannelConnected, | |
111 base::Unretained(listener), 0), | |
112 t.InMilliseconds()); | |
113 } | |
114 | |
115 void FireError(base::TimeDelta t) { | |
116 ASSERT_TRUE(ipc_loop != NULL); | |
117 ipc_loop->PostDelayedTask( | |
118 FROM_HERE, base::Bind(&IPC::Channel::Listener::OnChannelError, | |
119 base::Unretained(listener)), | |
120 t.InMilliseconds()); | |
121 } | |
122 | |
123 void FireMessage(const IPC::Message& m, base::TimeDelta t) { | |
124 ASSERT_TRUE(ipc_loop != NULL); | |
125 ipc_loop->PostDelayedTask( | |
126 FROM_HERE, | |
127 base::Bind( | |
128 base::IgnoreResult(&IPC::Channel::Listener::OnMessageReceived), | |
129 base::Unretained(listener), m), | |
130 t.InMilliseconds()); | |
131 } | |
132 | |
133 MockCFProxyTraits() : ipc_loop(NULL) {} | |
134 MockSender sender; | |
135 private: | |
136 MessageLoop* ipc_loop; | |
137 IPC::Channel::Listener* listener; | |
138 }; | |
139 | |
140 // Handy macros when we want so simulate something on the IPC thread. | |
141 #define API_FIRE_CONNECT(api, t) InvokeWithoutArgs(CreateFunctor(&api, \ | |
142 &MockCFProxyTraits::FireConnect, t)) | |
143 #define API_FIRE_ERROR(api, t) InvokeWithoutArgs(CreateFunctor(&api, \ | |
144 &MockCFProxyTraits::FireError, t)) | |
145 #define API_FIRE_MESSAGE(api, t) InvokeWithoutArgs(CreateFunctor(&api, \ | |
146 &MockCFProxyTraits::FireMessage, t)) | |
147 | |
148 class ChromeProxyTest : public ::testing::Test { | |
149 protected: | |
150 virtual void SetUp() OVERRIDE { | |
151 chrome_frame_test::OverrideDataDirectoryForThisTest( | |
152 chrome_frame_test::GetProfilePath(L"").value()); | |
153 params_.profile = "Adam N. Epilinter"; | |
154 params_.timeout = base::TimeDelta::FromSeconds(4); | |
155 } | |
156 | |
157 ProxyParams params_; | |
158 }; | |
159 | |
160 TEST_F(ChromeProxyTest, DelegateAddRemove) { | |
161 StrictMock<MockCFProxyTraits> api; | |
162 StrictMock<MockChromeProxyDelegate> delegate; | |
163 StrictMock<MockFactory> factory; // to be destroyed before other mocks | |
164 CFProxy* proxy = new CFProxy(&api); | |
165 | |
166 EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy)); | |
167 EXPECT_CALL(api, DoCreateChannel(_, proxy)).WillOnce(Return(&api.sender)); | |
168 EXPECT_CALL(api, LaunchApp(_)).WillOnce(Return(true)); | |
169 EXPECT_CALL(api, CloseChannel(&api.sender)); | |
170 | |
171 EXPECT_CALL(delegate, tab_handle()).WillRepeatedly(Return(0)); | |
172 EXPECT_CALL(delegate, Disconnected()); | |
173 | |
174 factory.GetProxy(&delegate, params_); | |
175 factory.ReleaseProxy(&delegate, params_.profile); | |
176 } | |
177 | |
178 // Not very useful test. Just for illustration. :) | |
179 TEST_F(ChromeProxyTest, SharedProxy) { | |
180 base::WaitableEvent done1(false, false); | |
181 base::WaitableEvent done2(false, false); | |
182 StrictMock<MockCFProxyTraits> api; | |
183 StrictMock<MockChromeProxyDelegate> delegate1; | |
184 StrictMock<MockChromeProxyDelegate> delegate2; | |
185 StrictMock<MockFactory> factory; | |
186 CFProxy* proxy = new CFProxy(&api); | |
187 | |
188 EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy)); | |
189 EXPECT_CALL(api, DoCreateChannel(_, proxy)).WillOnce(Return(&api.sender)); | |
190 EXPECT_CALL(api, LaunchApp(_)).WillOnce(DoAll( | |
191 API_FIRE_CONNECT(api, base::TimeDelta::FromMilliseconds(150)), | |
192 Return(true))); | |
193 EXPECT_CALL(api, CloseChannel(&api.sender)); | |
194 | |
195 EXPECT_CALL(delegate1, tab_handle()).WillRepeatedly(Return(0)); | |
196 EXPECT_CALL(delegate2, tab_handle()).WillRepeatedly(Return(0)); | |
197 | |
198 EXPECT_CALL(delegate1, Connected(proxy)) | |
199 .WillOnce(InvokeWithoutArgs(&done1, &base::WaitableEvent::Signal)); | |
200 EXPECT_CALL(delegate2, Connected(proxy)) | |
201 .WillOnce(InvokeWithoutArgs(&done2, &base::WaitableEvent::Signal)); | |
202 | |
203 factory.GetProxy(&delegate1, params_); | |
204 params_.timeout = base::TimeDelta::FromSeconds(2); | |
205 factory.GetProxy(&delegate2, params_); | |
206 | |
207 EXPECT_TRUE(done1.TimedWait(base::TimeDelta::FromSeconds(1))); | |
208 EXPECT_TRUE(done2.TimedWait(base::TimeDelta::FromSeconds(1))); | |
209 | |
210 EXPECT_CALL(delegate2, Disconnected()); | |
211 EXPECT_CALL(delegate1, Disconnected()); | |
212 | |
213 factory.ReleaseProxy(&delegate2, params_.profile); | |
214 factory.ReleaseProxy(&delegate1, params_.profile); | |
215 } | |
216 | |
217 TEST_F(ChromeProxyTest, LaunchTimeout) { | |
218 base::WaitableEvent done(true, false); | |
219 StrictMock<MockCFProxyTraits> api; | |
220 StrictMock<MockChromeProxyDelegate> delegate; | |
221 StrictMock<MockFactory> factory; | |
222 CFProxy* proxy = new CFProxy(&api); | |
223 | |
224 EXPECT_CALL(delegate, tab_handle()).WillRepeatedly(Return(0)); | |
225 EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy)); | |
226 EXPECT_CALL(api, DoCreateChannel(_, proxy)).WillOnce(Return(&api.sender)); | |
227 EXPECT_CALL(api, LaunchApp(_)).WillOnce(Return(true)); | |
228 EXPECT_CALL(api, CloseChannel(&api.sender)); | |
229 | |
230 EXPECT_CALL(delegate, PeerLost(_, | |
231 ChromeProxyDelegate::CHROME_EXE_LAUNCH_TIMEOUT)) | |
232 .WillOnce(InvokeWithoutArgs(&done, &base::WaitableEvent::Signal)); | |
233 params_.timeout = base::TimeDelta::FromMilliseconds(300); | |
234 factory.GetProxy(&delegate, params_); | |
235 EXPECT_TRUE(done.TimedWait(base::TimeDelta::FromSeconds(1))); | |
236 | |
237 EXPECT_CALL(delegate, Disconnected()); | |
238 factory.ReleaseProxy(&delegate, params_.profile); | |
239 } | |
240 | |
241 TEST_F(ChromeProxyTest, LaunchChrome) { | |
242 base::WaitableEvent connected(false, false); | |
243 StrictMock<MockChromeProxyDelegate> delegate; | |
244 ChromeProxyFactory factory; | |
245 | |
246 params_.timeout = base::TimeDelta::FromSeconds(10); | |
247 | |
248 EXPECT_CALL(delegate, tab_handle()).WillRepeatedly(Return(0)); | |
249 EXPECT_CALL(delegate, Connected(NotNull())) | |
250 .WillOnce(InvokeWithoutArgs(&connected, &base::WaitableEvent::Signal)); | |
251 | |
252 factory.GetProxy(&delegate, params_); | |
253 EXPECT_TRUE(connected.TimedWait(base::TimeDelta::FromSeconds(15))); | |
254 | |
255 EXPECT_CALL(delegate, Disconnected()); | |
256 factory.ReleaseProxy(&delegate, params_.profile); | |
257 } | |
258 | |
259 // Test that a channel error results in Completed_XYZ(false, ) called if | |
260 // the synchronious XYZ message has been sent. | |
261 TEST_F(ChromeProxyTest, ChannelError) { | |
262 base::WaitableEvent connected(false, false); | |
263 StrictMock<MockCFProxyTraits> api; | |
264 StrictMock<MockChromeProxyDelegate> delegate; | |
265 StrictMock<MockFactory> factory; | |
266 CFProxy* proxy = new CFProxy(&api); | |
267 | |
268 params_.timeout = base::TimeDelta::FromMilliseconds(300); | |
269 | |
270 testing::InSequence s; | |
271 | |
272 EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy)); | |
273 EXPECT_CALL(api, DoCreateChannel(_, proxy)).WillOnce(Return(&api.sender)); | |
274 EXPECT_CALL(api, LaunchApp(_)).WillOnce(DoAll( | |
275 API_FIRE_CONNECT(api, base::TimeDelta::FromMilliseconds(10)), | |
276 Return(true))); | |
277 EXPECT_CALL(delegate, Connected(proxy)) | |
278 .WillOnce(DoAll( | |
279 InvokeWithoutArgs(CreateFunctor(proxy, &ChromeProxy::ConnectTab, | |
280 &delegate, HWND(6), 512)), | |
281 InvokeWithoutArgs(&connected, &base::WaitableEvent::Signal))); | |
282 | |
283 EXPECT_CALL(api.sender, Send(_)); | |
284 EXPECT_CALL(delegate, Completed_ConnectToTab(false, _, _, _, _)); | |
285 EXPECT_CALL(api, CloseChannel(&api.sender)); | |
286 EXPECT_CALL(delegate, PeerLost(_, ChromeProxyDelegate::CHANNEL_ERROR)); | |
287 | |
288 factory.GetProxy(&delegate, params_); | |
289 EXPECT_TRUE(connected.TimedWait(base::TimeDelta::FromSeconds(15))); | |
290 // Simulate a channel error. | |
291 api.FireError(base::TimeDelta::FromMilliseconds(0)); | |
292 | |
293 // Expectations when the Proxy is destroyed. | |
294 EXPECT_CALL(delegate, tab_handle()).WillOnce(Return(0)); | |
295 EXPECT_CALL(delegate, Disconnected()); | |
296 factory.ReleaseProxy(&delegate, params_.profile); | |
297 } | |
298 /////////////////////////////////////////////////////////////////////////////// | |
299 namespace { | |
300 template <typename M, typename A> | |
301 inline IPC::Message* CreateReply(M* m, const A& a) { | |
302 IPC::Message* r = IPC::SyncMessage::GenerateReply(m); | |
303 if (r) { | |
304 M::WriteReplyParams(r, a); | |
305 } | |
306 return r; | |
307 } | |
308 | |
309 template <typename M, typename A, typename B> | |
310 inline IPC::Message* CreateReply(M* m, const A& a, const B& b) { | |
311 IPC::Message* r = IPC::SyncMessage::GenerateReply(m); | |
312 if (r) { | |
313 M::WriteReplyParams(r, a, b); | |
314 } | |
315 return r; | |
316 } | |
317 | |
318 template <typename M, typename A, typename B, typename C> | |
319 inline IPC::Message* CreateReply(M* m, const A& a, const B& b, const C& c) { | |
320 IPC::Message* r = IPC::SyncMessage::GenerateReply(m); | |
321 if (r) { | |
322 M::WriteReplyParams(r, a, b, c); | |
323 } | |
324 return r; | |
325 } | |
326 | |
327 template <typename M, typename A, typename B, typename C, typename D> | |
328 inline IPC::Message* CreateReply(M* m, const A& a, const B& b, const C& c, | |
329 const D& d) { | |
330 IPC::Message* r = IPC::SyncMessage::GenerateReply(m); | |
331 if (r) { | |
332 M::WriteReplyParams(r, a, b, c, d); | |
333 } | |
334 return r; | |
335 }} // namespace | |
336 | |
337 TEST(SyncMsgSender, Deserialize) { | |
338 // Note the ipc thread is not actually needed, but we try to be close | |
339 // to real-world conditions - that SyncMsgSender works from multiple threads. | |
340 base::Thread ipc("ipc"); | |
341 ipc.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0)); | |
342 | |
343 StrictMock<MockChromeProxyDelegate> d1; | |
344 TabsMap tab2delegate; | |
345 SyncMsgSender queue(&tab2delegate); | |
346 | |
347 const int kTabHandle = 6; | |
348 const int kSessionId = 8; | |
349 | |
350 // Create a sync message and its reply. | |
351 AutomationMsg_CreateExternalTab m(ExternalTabSettings(), 0, 0, 0, 0); | |
352 scoped_ptr<IPC::Message> r(CreateReply(&m, (HWND)1, (HWND)2, kTabHandle, | |
353 kSessionId)); | |
354 | |
355 queue.QueueSyncMessage(&m, &d1, NULL); | |
356 | |
357 testing::InSequence s; | |
358 EXPECT_CALL(d1, Completed_CreateTab(true, (HWND)1, (HWND)2, kTabHandle, | |
359 kSessionId)); | |
360 | |
361 // Execute replies in a worker thread. | |
362 ipc.message_loop()->PostTask( | |
363 FROM_HERE, | |
364 base::Bind(base::IgnoreResult(&SyncMsgSender::OnReplyReceived), | |
365 base::Unretained(&queue), r.get())); | |
366 ipc.Stop(); | |
367 | |
368 // Expect that tab 6 has been associated with the delegate. | |
369 EXPECT_EQ(&d1, tab2delegate[6]); | |
370 } | |
371 | |
372 TEST(SyncMsgSender, OnChannelClosed) { | |
373 // TODO(stoyan): implement. | |
374 } | |
OLD | NEW |