1 // Copyright (c) 2011 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.
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 "testing/gtest/include/gtest/gtest.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gmock_mutant.h"
20 using testing::NotNull
;
21 using testing::Return
;
22 using testing::StrictMock
;
23 using testing::InvokeWithoutArgs
;
24 using testing::WithoutArgs
;
25 using testing::CreateFunctor
;
29 // There is not much to test here since CFProxy is pretty dumb.
30 struct MockFactory
: public ChromeProxyFactory
{
31 MOCK_METHOD0(CreateProxy
, ChromeProxy
*());
34 struct MockChromeProxyDelegate
: public ChromeProxyDelegate
{
35 MOCK_METHOD1(OnMessageReceived
, bool(const IPC::Message
& message
));
36 MOCK_METHOD1(Connected
, void(ChromeProxy
* proxy
));
37 MOCK_METHOD2(PeerLost
, void(ChromeProxy
*, enum DisconnectReason reason
));
38 MOCK_METHOD0(Disconnected
, void());
39 MOCK_METHOD0(tab_handle
, int());
41 MOCK_METHOD5(Completed_CreateTab
, void(bool success
, HWND chrome_wnd
,
42 HWND tab_window
, int tab_handle
, int session_id
));
43 MOCK_METHOD5(Completed_ConnectToTab
, void(bool success
, HWND chrome_window
,
44 HWND tab_window
, int tab_handle
, int session_id
));
45 MOCK_METHOD2(Completed_Navigate
, void(bool success
,
46 enum AutomationMsg_NavigationResponseValues res
));
48 // Network requests from Chrome.
49 MOCK_METHOD2(Network_Start
, void(int request_id
,
50 const AutomationURLRequest
& request_info
));
51 MOCK_METHOD2(Network_Read
, void(int request_id
, int bytes_to_read
));
52 MOCK_METHOD2(Network_End
, void(int request_id
,
53 const net::URLRequestStatus
& s
));
54 MOCK_METHOD1(Network_DownloadInHost
, void(int request_id
));
55 MOCK_METHOD2(GetCookies
, void(const GURL
& url
, int cookie_id
));
56 MOCK_METHOD2(SetCookie
, void(const GURL
& url
, const std::string
& cookie
));
58 // Navigation progress notifications.
59 MOCK_METHOD2(NavigationStateChanged
, void(int flags
,
60 const NavigationInfo
& nav_info
));
61 MOCK_METHOD1(UpdateTargetUrl
, void(const std::wstring
& url
));
62 MOCK_METHOD2(NavigationFailed
, void(int error_code
, const GURL
& gurl
));
63 MOCK_METHOD1(DidNavigate
, void(const NavigationInfo
& navigation_info
));
64 MOCK_METHOD1(TabLoaded
, void(const GURL
& url
));
67 MOCK_METHOD3(OpenURL
, void(const GURL
& url_to_open
, const GURL
& referrer
,
68 int open_disposition
));
69 MOCK_METHOD1(GoToHistoryOffset
, void(int offset
));
70 MOCK_METHOD3(MessageToHost
, void(const std::string
& message
,
71 const std::string
& origin
, const std::string
& target
));
74 MOCK_METHOD1(HandleAccelerator
, void(const MSG
& accel_message
));
75 MOCK_METHOD3(HandleContextMenu
, void(HANDLE menu_handle
, int align_flags
,
76 const MiniContextMenuParams
& params
));
77 MOCK_METHOD1(TabbedOut
, void(bool reverse
));
80 MOCK_METHOD0(TabClosed
, void());
81 MOCK_METHOD1(AttachTab
, void(const AttachExternalTabParams
& attach_params
));
84 struct MockSender
: public IPC::Message::Sender
{
85 MOCK_METHOD1(Send
, bool(IPC::Message
* m
));
88 struct MockCFProxyTraits
: public CFProxyTraits
{
89 MOCK_METHOD2(DoCreateChannel
, IPC::Message::Sender
*(const std::string
& id
,
90 IPC::Channel::Listener
* l
));
91 MOCK_METHOD1(CloseChannel
, void(IPC::Message::Sender
* s
));
92 MOCK_METHOD1(LaunchApp
, bool(const std::wstring
& cmd_line
));
94 // Forward the CreateChannel to DoCreateChannel, but save the ipc_thread
95 // and the listener (i.e. proxy implementation of Channel::Listener)
96 virtual IPC::Message::Sender
* CreateChannel(const std::string
& id
,
97 IPC::Channel::Listener
* l
) {
98 ipc_loop
= MessageLoop::current();
100 return this->DoCreateChannel(id
, l
);
103 // Simulate some activity in the IPC thread.
104 // You may find API_FIRE_XXXX macros (see below) handy instead.
105 void FireConnect(base::TimeDelta t
) {
106 ASSERT_TRUE(ipc_loop
!= NULL
);
107 ipc_loop
->PostDelayedTask(
108 FROM_HERE
, base::Bind(&IPC::Channel::Listener::OnChannelConnected
,
109 base::Unretained(listener
), 0),
113 void FireError(base::TimeDelta t
) {
114 ASSERT_TRUE(ipc_loop
!= NULL
);
115 ipc_loop
->PostDelayedTask(
116 FROM_HERE
, base::Bind(&IPC::Channel::Listener::OnChannelError
,
117 base::Unretained(listener
)),
121 void FireMessage(const IPC::Message
& m
, base::TimeDelta t
) {
122 ASSERT_TRUE(ipc_loop
!= NULL
);
123 ipc_loop
->PostDelayedTask(
126 base::IgnoreResult(&IPC::Channel::Listener::OnMessageReceived
),
127 base::Unretained(listener
), m
),
131 MockCFProxyTraits() : ipc_loop(NULL
) {}
134 MessageLoop
* ipc_loop
;
135 IPC::Channel::Listener
* listener
;
138 // Handy macros when we want so simulate something on the IPC thread.
139 #define API_FIRE_CONNECT(api, t) InvokeWithoutArgs(CreateFunctor(&api, \
140 &MockCFProxyTraits::FireConnect, t))
141 #define API_FIRE_ERROR(api, t) InvokeWithoutArgs(CreateFunctor(&api, \
142 &MockCFProxyTraits::FireError, t))
143 #define API_FIRE_MESSAGE(api, t) InvokeWithoutArgs(CreateFunctor(&api, \
144 &MockCFProxyTraits::FireMessage, t))
146 TEST(ChromeProxy
, DelegateAddRemove
) {
147 StrictMock
<MockCFProxyTraits
> api
;
148 StrictMock
<MockChromeProxyDelegate
> delegate
;
149 StrictMock
<MockFactory
> factory
; // to be destroyed before other mocks
150 CFProxy
* proxy
= new CFProxy(&api
);
152 EXPECT_CALL(factory
, CreateProxy()).WillOnce(Return(proxy
));
153 EXPECT_CALL(api
, DoCreateChannel(_
, proxy
)).WillOnce(Return(&api
.sender
));
154 EXPECT_CALL(api
, LaunchApp(_
)).WillOnce(Return(true));
155 EXPECT_CALL(api
, CloseChannel(&api
.sender
));
157 EXPECT_CALL(delegate
, tab_handle()).WillRepeatedly(Return(0));
158 EXPECT_CALL(delegate
, Disconnected());
161 params
.profile
= "Adam N. Epilinter";
162 params
.timeout
= base::TimeDelta::FromSeconds(4);
163 factory
.GetProxy(&delegate
, params
);
164 factory
.ReleaseProxy(&delegate
, params
.profile
);
167 // Not very useful test. Just for illustration. :)
168 TEST(ChromeProxy
, SharedProxy
) {
169 base::WaitableEvent
done1(false, false);
170 base::WaitableEvent
done2(false, false);
171 StrictMock
<MockCFProxyTraits
> api
;
172 StrictMock
<MockChromeProxyDelegate
> delegate1
;
173 StrictMock
<MockChromeProxyDelegate
> delegate2
;
174 StrictMock
<MockFactory
> factory
;
175 CFProxy
* proxy
= new CFProxy(&api
);
177 EXPECT_CALL(factory
, CreateProxy()).WillOnce(Return(proxy
));
178 EXPECT_CALL(api
, DoCreateChannel(_
, proxy
)).WillOnce(Return(&api
.sender
));
179 EXPECT_CALL(api
, LaunchApp(_
)).WillOnce(DoAll(
180 API_FIRE_CONNECT(api
, base::TimeDelta::FromMilliseconds(150)),
182 EXPECT_CALL(api
, CloseChannel(&api
.sender
));
184 EXPECT_CALL(delegate1
, tab_handle()).WillRepeatedly(Return(0));
185 EXPECT_CALL(delegate2
, tab_handle()).WillRepeatedly(Return(0));
187 EXPECT_CALL(delegate1
, Connected(proxy
))
188 .WillOnce(InvokeWithoutArgs(&done1
, &base::WaitableEvent::Signal
));
189 EXPECT_CALL(delegate2
, Connected(proxy
))
190 .WillOnce(InvokeWithoutArgs(&done2
, &base::WaitableEvent::Signal
));
193 params
.profile
= "Adam N. Epilinter";
194 params
.timeout
= base::TimeDelta::FromSeconds(4);
196 factory
.GetProxy(&delegate1
, params
);
197 params
.timeout
= base::TimeDelta::FromSeconds(2);
198 factory
.GetProxy(&delegate2
, params
);
200 EXPECT_TRUE(done1
.TimedWait(base::TimeDelta::FromSeconds(1)));
201 EXPECT_TRUE(done2
.TimedWait(base::TimeDelta::FromSeconds(1)));
203 EXPECT_CALL(delegate2
, Disconnected());
204 EXPECT_CALL(delegate1
, Disconnected());
206 factory
.ReleaseProxy(&delegate2
, params
.profile
);
207 factory
.ReleaseProxy(&delegate1
, params
.profile
);
210 TEST(ChromeProxy
, LaunchTimeout
) {
211 base::WaitableEvent
done(true, false);
212 StrictMock
<MockCFProxyTraits
> api
;
213 StrictMock
<MockChromeProxyDelegate
> delegate
;
214 StrictMock
<MockFactory
> factory
;
215 CFProxy
* proxy
= new CFProxy(&api
);
217 EXPECT_CALL(delegate
, tab_handle()).WillRepeatedly(Return(0));
218 EXPECT_CALL(factory
, CreateProxy()).WillOnce(Return(proxy
));
219 EXPECT_CALL(api
, DoCreateChannel(_
, proxy
)).WillOnce(Return(&api
.sender
));
220 EXPECT_CALL(api
, LaunchApp(_
)).WillOnce(Return(true));
221 EXPECT_CALL(api
, CloseChannel(&api
.sender
));
223 EXPECT_CALL(delegate
, PeerLost(_
,
224 ChromeProxyDelegate::CHROME_EXE_LAUNCH_TIMEOUT
))
225 .WillOnce(InvokeWithoutArgs(&done
, &base::WaitableEvent::Signal
));
227 params
.profile
= "Adam N. Epilinter";
228 params
.timeout
= base::TimeDelta::FromMilliseconds(300);
229 factory
.GetProxy(&delegate
, params
);
230 EXPECT_TRUE(done
.TimedWait(base::TimeDelta::FromSeconds(1)));
232 EXPECT_CALL(delegate
, Disconnected());
233 factory
.ReleaseProxy(&delegate
, params
.profile
);
236 TEST(ChromeProxy
, LaunchChrome
) {
237 base::WaitableEvent
connected(false, false);
238 StrictMock
<MockChromeProxyDelegate
> delegate
;
239 ChromeProxyFactory factory
;
242 params
.profile
= "Adam N. Epilinter";
243 params
.timeout
= base::TimeDelta::FromSeconds(10);
245 EXPECT_CALL(delegate
, tab_handle()).WillRepeatedly(Return(0));
246 EXPECT_CALL(delegate
, Connected(NotNull()))
247 .WillOnce(InvokeWithoutArgs(&connected
, &base::WaitableEvent::Signal
));
249 factory
.GetProxy(&delegate
, params
);
250 EXPECT_TRUE(connected
.TimedWait(base::TimeDelta::FromSeconds(15)));
252 EXPECT_CALL(delegate
, Disconnected());
253 factory
.ReleaseProxy(&delegate
, params
.profile
);
256 // Test that a channel error results in Completed_XYZ(false, ) called if
257 // the synchronious XYZ message has been sent.
258 TEST(ChromeProxy
, ChannelError
) {
259 base::WaitableEvent
connected(false, false);
260 StrictMock
<MockCFProxyTraits
> api
;
261 StrictMock
<MockChromeProxyDelegate
> delegate
;
262 StrictMock
<MockFactory
> factory
;
263 CFProxy
* proxy
= new CFProxy(&api
);
266 params
.profile
= "Adam N. Epilinter";
267 params
.timeout
= base::TimeDelta::FromMilliseconds(300);
269 testing::InSequence s
;
271 EXPECT_CALL(factory
, CreateProxy()).WillOnce(Return(proxy
));
272 EXPECT_CALL(api
, DoCreateChannel(_
, proxy
)).WillOnce(Return(&api
.sender
));
273 EXPECT_CALL(api
, LaunchApp(_
)).WillOnce(DoAll(
274 API_FIRE_CONNECT(api
, base::TimeDelta::FromMilliseconds(10)),
276 EXPECT_CALL(delegate
, Connected(proxy
))
278 InvokeWithoutArgs(CreateFunctor(proxy
, &ChromeProxy::ConnectTab
,
279 &delegate
, HWND(6), 512)),
280 InvokeWithoutArgs(&connected
, &base::WaitableEvent::Signal
)));
282 EXPECT_CALL(api
.sender
, Send(_
));
283 EXPECT_CALL(delegate
, Completed_ConnectToTab(false, _
, _
, _
, _
));
284 EXPECT_CALL(api
, CloseChannel(&api
.sender
));
285 EXPECT_CALL(delegate
, PeerLost(_
, ChromeProxyDelegate::CHANNEL_ERROR
));
287 factory
.GetProxy(&delegate
, params
);
288 EXPECT_TRUE(connected
.TimedWait(base::TimeDelta::FromSeconds(15)));
289 // Simulate a channel error.
290 api
.FireError(base::TimeDelta::FromMilliseconds(0));
292 // Expectations when the Proxy is destroyed.
293 EXPECT_CALL(delegate
, tab_handle()).WillOnce(Return(0));
294 EXPECT_CALL(delegate
, Disconnected());
295 factory
.ReleaseProxy(&delegate
, params
.profile
);
297 ///////////////////////////////////////////////////////////////////////////////
299 template <typename M
, typename A
>
300 inline IPC::Message
* CreateReply(M
* m
, const A
& a
) {
301 IPC::Message
* r
= IPC::SyncMessage::GenerateReply(m
);
303 M::WriteReplyParams(r
, a
);
308 template <typename M
, typename A
, typename B
>
309 inline IPC::Message
* CreateReply(M
* m
, const A
& a
, const B
& b
) {
310 IPC::Message
* r
= IPC::SyncMessage::GenerateReply(m
);
312 M::WriteReplyParams(r
, a
, b
);
317 template <typename M
, typename A
, typename B
, typename C
>
318 inline IPC::Message
* CreateReply(M
* m
, const A
& a
, const B
& b
, const C
& c
) {
319 IPC::Message
* r
= IPC::SyncMessage::GenerateReply(m
);
321 M::WriteReplyParams(r
, a
, b
, c
);
326 template <typename M
, typename A
, typename B
, typename C
, typename D
>
327 inline IPC::Message
* CreateReply(M
* m
, const A
& a
, const B
& b
, const C
& c
,
329 IPC::Message
* r
= IPC::SyncMessage::GenerateReply(m
);
331 M::WriteReplyParams(r
, a
, b
, c
, d
);
336 TEST(SyncMsgSender
, Deserialize
) {
337 // Note the ipc thread is not actually needed, but we try to be close
338 // to real-world conditions - that SyncMsgSender works from multiple threads.
339 base::Thread
ipc("ipc");
340 ipc
.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO
, 0));
342 StrictMock
<MockChromeProxyDelegate
> d1
;
343 TabsMap tab2delegate
;
344 SyncMsgSender
queue(&tab2delegate
);
346 const int kTabHandle
= 6;
347 const int kSessionId
= 8;
349 // Create a sync message and its reply.
350 AutomationMsg_CreateExternalTab
m(ExternalTabSettings(), 0, 0, 0, 0);
351 scoped_ptr
<IPC::Message
> r(CreateReply(&m
, (HWND
)1, (HWND
)2, kTabHandle
,
354 queue
.QueueSyncMessage(&m
, &d1
, NULL
);
356 testing::InSequence s
;
357 EXPECT_CALL(d1
, Completed_CreateTab(true, (HWND
)1, (HWND
)2, kTabHandle
,
360 // Execute replies in a worker thread.
361 ipc
.message_loop()->PostTask(
363 base::Bind(base::IgnoreResult(&SyncMsgSender::OnReplyReceived
),
364 base::Unretained(&queue
), r
.get()));
367 // Expect that tab 6 has been associated with the delegate.
368 EXPECT_EQ(&d1
, tab2delegate
[6]);
371 TEST(SyncMsgSender
, OnChannelClosed
) {
372 // TODO(stoyan): implement.