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.
5 #include "chrome_frame/test/automation_client_mock.h"
8 #include "base/bind_helpers.h"
9 #include "chrome/common/automation_messages.h"
10 #include "chrome_frame/custom_sync_call_context.h"
11 #include "chrome_frame/navigation_constraints.h"
12 #include "chrome_frame/test/chrome_frame_test_utils.h"
13 #include "chrome_frame/test/test_scrubber.h"
14 #include "net/base/net_errors.h"
16 #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING
17 #include "testing/gmock_mutant.h"
20 using testing::CreateFunctor
;
21 using testing::Return
;
26 const base::TimeDelta kChromeLaunchTimeout
= base::TimeDelta::FromSeconds(15);
28 const base::TimeDelta kChromeLaunchTimeout
= base::TimeDelta::FromSeconds(10);
31 const int kSaneAutomationTimeoutMs
= 10 * 1000;
35 MATCHER_P(LaunchParamProfileEq
, profile_name
, "Check for profile name") {
36 return arg
->profile_name().compare(profile_name
) == 0;
39 void MockProxyFactory::GetServerImpl(ChromeFrameAutomationProxy
* pxy
,
41 AutomationLaunchResult result
,
43 ChromeFrameLaunchParams
* params
,
44 void** automation_server_id
) {
45 *automation_server_id
= proxy_id
;
46 loop_
->PostDelayedTask(FROM_HERE
,
47 base::Bind(&LaunchDelegate::LaunchComplete
,
48 base::Unretained(d
), pxy
, result
),
49 base::TimeDelta::FromMilliseconds(params
->launch_timeout()) / 2);
52 void CFACMockTest::SetAutomationServerOk(int times
) {
53 EXPECT_CALL(factory_
, GetAutomationServer(testing::NotNull(),
54 LaunchParamProfileEq(profile_path_
.BaseName().value()),
57 .WillRepeatedly(testing::Invoke(CreateFunctor(&factory_
,
58 &MockProxyFactory::GetServerImpl
, get_proxy(), id_
,
59 AUTOMATION_SUCCESS
)));
62 ReleaseAutomationServer(testing::Eq(id_
), testing::NotNull()))
66 void CFACMockTest::Set_CFD_LaunchFailed(AutomationLaunchResult result
) {
67 EXPECT_CALL(cfd_
, OnAutomationServerLaunchFailed(testing::Eq(result
),
70 .WillOnce(QUIT_LOOP(loop_
));
73 MATCHER_P(MsgType
, msg_type
, "IPC::Message::type()") {
74 const IPC::Message
& m
= arg
;
75 return (m
.type() == msg_type
);
78 MATCHER_P(EqNavigationInfoUrl
, url
, "IPC::NavigationInfo matcher") {
79 if (url
.is_valid() && url
!= arg
.url
)
81 // TODO(stevet): other members
85 // Could be implemented as MockAutomationProxy member (we have WithArgs<>!)
86 ACTION_P4(HandleCreateTab
, tab_handle
, external_tab_container
, tab_wnd
,
91 CreateExternalTabContext::output_type
input_args(tab_wnd
,
92 external_tab_container
,
95 CreateExternalTabContext
* context
=
96 reinterpret_cast<CreateExternalTabContext
*>(arg1
);
97 DispatchToMethod(context
, &CreateExternalTabContext::Completed
, input_args
);
101 ACTION_P4(InitiateNavigation
, client
, url
, referrer
, constraints
) {
102 client
->InitiateNavigation(url
, referrer
, constraints
);
105 // ChromeFrameAutomationClient tests that launch Chrome.
106 class CFACWithChrome
: public testing::Test
{
108 static void SetUpTestCase();
109 static void TearDownTestCase();
111 virtual void SetUp() OVERRIDE
;
112 virtual void TearDown() OVERRIDE
;
114 static base::FilePath profile_path_
;
116 scoped_refptr
<ChromeFrameAutomationClient
> client_
;
117 scoped_refptr
<ChromeFrameLaunchParams
> launch_params_
;
118 chrome_frame_test::TimedMsgLoop loop_
;
122 base::FilePath
CFACWithChrome::profile_path_
;
125 void CFACWithChrome::SetUpTestCase() {
126 GetChromeFrameProfilePath(L
"Adam.N.Epilinter", &profile_path_
);
130 void CFACWithChrome::TearDownTestCase() {
131 profile_path_
.clear();
134 void CFACWithChrome::SetUp() {
135 chrome_frame_test::OverrideDataDirectoryForThisTest(profile_path_
.value());
136 client_
= new ChromeFrameAutomationClient();
138 launch_params_
= new ChromeFrameLaunchParams(
139 empty
, empty
, profile_path_
, profile_path_
.BaseName().value(), L
"",
140 false, false, false, false);
141 launch_params_
->set_version_check(false);
142 launch_params_
->set_launch_timeout(kSaneAutomationTimeoutMs
);
145 void CFACWithChrome::TearDown() {
146 client_
->Uninitialize();
149 // We mock ChromeFrameDelegate only. The rest is with real AutomationProxy
150 TEST_F(CFACWithChrome
, CreateTooFast
) {
151 int timeout
= 0; // Chrome cannot send Hello message so fast.
153 EXPECT_CALL(cfd_
, OnAutomationServerLaunchFailed(AUTOMATION_TIMEOUT
, _
))
154 .WillOnce(QUIT_LOOP(loop_
));
156 launch_params_
->set_launch_timeout(timeout
);
157 EXPECT_TRUE(client_
->Initialize(&cfd_
, launch_params_
));
158 loop_
.RunFor(kChromeLaunchTimeout
);
161 // This test may fail if Chrome take more that 10 seconds (timeout var) to
162 // launch. In this case GMock shall print something like "unexpected call to
163 // OnAutomationServerLaunchFailed". I'm yet to find out how to specify
164 // that this is an unexpected call, and still to execute an action.
165 TEST_F(CFACWithChrome
, CreateNotSoFast
) {
166 EXPECT_CALL(cfd_
, OnAutomationServerReady())
167 .WillOnce(QUIT_LOOP(loop_
));
169 EXPECT_CALL(cfd_
, OnAutomationServerLaunchFailed(_
, _
))
172 EXPECT_TRUE(client_
->Initialize(&cfd_
, launch_params_
));
174 loop_
.RunFor(kChromeLaunchTimeout
);
177 TEST_F(CFACWithChrome
, NavigateOk
) {
178 NavigationConstraintsImpl navigation_constraints
;
180 const std::string url
= "about:version";
182 EXPECT_CALL(cfd_
, OnAutomationServerReady())
183 .WillOnce(InitiateNavigation(client_
.get(), url
, std::string(),
184 &navigation_constraints
));
186 EXPECT_CALL(cfd_
, GetBounds(_
)).Times(testing::AnyNumber());
188 EXPECT_CALL(cfd_
, OnNavigationStateChanged(_
))
189 .Times(testing::AnyNumber());
192 testing::InSequence s
;
194 EXPECT_CALL(cfd_
, OnDidNavigate(EqNavigationInfoUrl(GURL())))
197 EXPECT_CALL(cfd_
, OnUpdateTargetUrl(_
)).Times(testing::AtMost(1));
199 EXPECT_CALL(cfd_
, OnLoad(_
))
200 .WillOnce(QUIT_LOOP(loop_
));
203 EXPECT_TRUE(client_
->Initialize(&cfd_
, launch_params_
));
204 loop_
.RunFor(kChromeLaunchTimeout
);
207 TEST_F(CFACWithChrome
, NavigateFailed
) {
208 NavigationConstraintsImpl navigation_constraints
;
209 const std::string url
= "http://127.0.0.3:65412/";
210 const net::URLRequestStatus
connection_failed(net::URLRequestStatus::FAILED
,
211 net::ERR_INVALID_URL
);
213 cfd_
.SetRequestDelegate(client_
);
215 EXPECT_CALL(cfd_
, OnAutomationServerReady())
216 .WillOnce(testing::IgnoreResult(testing::InvokeWithoutArgs(CreateFunctor(
217 client_
.get(), &ChromeFrameAutomationClient::InitiateNavigation
,
218 url
, std::string(), &navigation_constraints
))));
220 EXPECT_CALL(cfd_
, GetBounds(_
)).Times(testing::AnyNumber());
221 EXPECT_CALL(cfd_
, OnNavigationStateChanged(_
)).Times(testing::AnyNumber());
223 EXPECT_CALL(cfd_
, OnRequestStart(_
, _
))
224 // Often there's another request for the error page
225 .Times(testing::Between(1, 2))
226 .WillRepeatedly(testing::WithArgs
<0>(testing::Invoke(CreateFunctor(&cfd_
,
227 &MockCFDelegate::Reply
, connection_failed
))));
229 EXPECT_CALL(cfd_
, OnUpdateTargetUrl(_
)).Times(testing::AnyNumber());
230 EXPECT_CALL(cfd_
, OnLoad(_
)).Times(testing::AtMost(1));
232 EXPECT_CALL(cfd_
, OnNavigationFailed(_
, GURL(url
)))
234 .WillOnce(QUIT_LOOP_SOON(loop_
, base::TimeDelta::FromSeconds(2)));
236 EXPECT_TRUE(client_
->Initialize(&cfd_
, launch_params_
));
238 loop_
.RunFor(kChromeLaunchTimeout
);
241 TEST_F(CFACMockTest
, MockedCreateTabOk
) {
244 SetAutomationServerOk(1);
246 EXPECT_CALL(mock_proxy_
, server_version()).Times(testing::AnyNumber())
247 .WillRepeatedly(Return(""));
249 // We need some valid HWNDs, when responding to CreateExternalTab
250 HWND h1
= ::GetDesktopWindow();
251 HWND h2
= ::GetDesktopWindow();
252 EXPECT_CALL(mock_proxy_
, SendAsAsync(testing::Property(
253 &IPC::SyncMessage::type
, AutomationMsg_CreateExternalTab::ID
),
254 testing::NotNull(), _
))
255 .Times(1).WillOnce(HandleCreateTab(tab_handle_
, h1
, h2
, 99));
257 EXPECT_CALL(mock_proxy_
, CreateTabProxy(testing::Eq(tab_handle_
)))
258 .WillOnce(Return(tab_
));
260 EXPECT_CALL(cfd_
, OnAutomationServerReady())
261 .WillOnce(QUIT_LOOP(loop_
));
263 EXPECT_CALL(mock_proxy_
, CancelAsync(_
)).Times(testing::AnyNumber());
267 scoped_refptr
<ChromeFrameLaunchParams
> clp(new ChromeFrameLaunchParams(
268 empty
, empty
, profile_path_
, profile_path_
.BaseName().value(), L
"",
269 false, false, false, false));
270 clp
->set_launch_timeout(timeout
);
271 clp
->set_version_check(false);
272 EXPECT_TRUE(client_
->Initialize(&cfd_
, clp
));
273 loop_
.RunFor(base::TimeDelta::FromSeconds(10));
275 EXPECT_CALL(mock_proxy_
, ReleaseTabProxy(testing::Eq(tab_handle_
))).Times(1);
276 client_
->Uninitialize();
279 TEST_F(CFACMockTest
, MockedCreateTabFailed
) {
280 HWND null_wnd
= NULL
;
281 SetAutomationServerOk(1);
283 EXPECT_CALL(mock_proxy_
, server_version()).Times(testing::AnyNumber())
284 .WillRepeatedly(Return(""));
286 EXPECT_CALL(mock_proxy_
, SendAsAsync(testing::Property(
287 &IPC::SyncMessage::type
, AutomationMsg_CreateExternalTab::ID
),
288 testing::NotNull(), _
))
289 .Times(1).WillOnce(HandleCreateTab(tab_handle_
, null_wnd
, null_wnd
,
292 EXPECT_CALL(mock_proxy_
, CreateTabProxy(_
)).Times(0);
294 EXPECT_CALL(mock_proxy_
, CancelAsync(_
)).Times(testing::AnyNumber());
296 Set_CFD_LaunchFailed(AUTOMATION_CREATE_TAB_FAILED
);
300 scoped_refptr
<ChromeFrameLaunchParams
> clp(new ChromeFrameLaunchParams(
301 empty
, empty
, profile_path_
, profile_path_
.BaseName().value(), L
"",
302 false, false, false, false));
303 clp
->set_launch_timeout(timeout_
);
304 clp
->set_version_check(false);
305 EXPECT_TRUE(client_
->Initialize(&cfd_
, clp
));
306 loop_
.RunFor(base::TimeDelta::FromSeconds(4));
307 client_
->Uninitialize();
310 class TestChromeFrameAutomationProxyImpl
311 : public ChromeFrameAutomationProxyImpl
{
313 TestChromeFrameAutomationProxyImpl()
314 // 1 is an unneeded timeout.
315 : ChromeFrameAutomationProxyImpl(
317 AutomationProxy::GenerateChannelID(),
318 base::TimeDelta::FromMilliseconds(1)) {
322 void(IPC::SyncMessage
* msg
,
323 SyncMessageReplyDispatcher::SyncMessageCallContext
* context
,
325 void FakeChannelError() {
326 reinterpret_cast<IPC::ChannelProxy::MessageFilter
*>(message_filter_
.get())->
331 TEST_F(CFACMockTest
, OnChannelErrorEmpty
) {
332 TestChromeFrameAutomationProxyImpl proxy
;
334 // No tabs should do nothing yet still not fail either.
335 proxy
.FakeChannelError();
338 TEST_F(CFACMockTest
, OnChannelError
) {
339 const base::TimeDelta loop_duration
= base::TimeDelta::FromSeconds(11);
340 TestChromeFrameAutomationProxyImpl proxy
;
341 returned_proxy_
= &proxy
;
344 scoped_refptr
<ChromeFrameLaunchParams
> clp(new ChromeFrameLaunchParams(
345 empty
, empty
, profile_path_
, profile_path_
.BaseName().value(), L
"",
346 false, false, false, false));
347 clp
->set_launch_timeout(1); // Unneeded timeout, but can't be 0.
348 clp
->set_version_check(false);
350 HWND h1
= ::GetDesktopWindow();
351 HWND h2
= ::GetDesktopWindow();
352 EXPECT_CALL(proxy
, SendAsAsync(testing::Property(
353 &IPC::SyncMessage::type
, AutomationMsg_CreateExternalTab::ID
),
354 testing::NotNull(), _
)).Times(3)
355 .WillOnce(HandleCreateTab(tab_handle_
, h1
, h2
, 99))
356 .WillOnce(HandleCreateTab(tab_handle_
* 2, h1
, h2
, 100))
357 .WillOnce(HandleCreateTab(tab_handle_
* 3, h1
, h2
, 101));
359 SetAutomationServerOk(3);
361 // First, try a single tab and make sure the notification find its way to the
362 // Chrome Frame Delegate.
363 StrictMock
<MockCFDelegate
> cfd1
;
364 scoped_refptr
<ChromeFrameAutomationClient
> client1
;
365 client1
= new ChromeFrameAutomationClient
;
366 client1
->set_proxy_factory(&factory_
);
368 EXPECT_CALL(cfd1
, OnAutomationServerReady()).WillOnce(QUIT_LOOP(loop_
));
369 EXPECT_TRUE(client1
->Initialize(&cfd1
, clp
));
370 // Wait for OnAutomationServerReady to be called in the UI thread.
371 loop_
.RunFor(loop_duration
);
373 proxy
.FakeChannelError();
374 EXPECT_CALL(cfd1
, OnChannelError()).WillOnce(QUIT_LOOP(loop_
));
375 // Wait for OnChannelError to be propagated to delegate from the UI thread.
376 loop_
.RunFor(loop_duration
);
378 // Add a second tab using a different delegate.
379 StrictMock
<MockCFDelegate
> cfd2
;
380 scoped_refptr
<ChromeFrameAutomationClient
> client2
;
381 client2
= new ChromeFrameAutomationClient
;
382 client2
->set_proxy_factory(&factory_
);
384 EXPECT_CALL(cfd2
, OnAutomationServerReady()).WillOnce(QUIT_LOOP(loop_
));
385 EXPECT_TRUE(client2
->Initialize(&cfd2
, clp
));
386 // Wait for OnAutomationServerReady to be called in the UI thread.
387 loop_
.RunFor(loop_duration
);
389 EXPECT_CALL(cfd1
, OnChannelError()).Times(1);
390 EXPECT_CALL(cfd2
, OnChannelError()).WillOnce(QUIT_LOOP(loop_
));
391 proxy
.FakeChannelError();
392 // Wait for OnChannelError to be propagated to delegate from the UI thread.
393 loop_
.RunFor(loop_duration
);
395 // And now a 3rd tab using the first delegate.
396 scoped_refptr
<ChromeFrameAutomationClient
> client3
;
397 client3
= new ChromeFrameAutomationClient
;
398 client3
->set_proxy_factory(&factory_
);
400 EXPECT_CALL(cfd1
, OnAutomationServerReady()).WillOnce(QUIT_LOOP(loop_
));
401 EXPECT_TRUE(client3
->Initialize(&cfd1
, clp
));
402 // Wait for OnAutomationServerReady to be called in the UI thread.
403 loop_
.RunFor(loop_duration
);
405 EXPECT_CALL(cfd2
, OnChannelError()).Times(1);
406 EXPECT_CALL(cfd1
, OnChannelError()).Times(2).WillOnce(Return())
407 .WillOnce(QUIT_LOOP(loop_
));
408 proxy
.FakeChannelError();
409 // Wait for OnChannelError to be propagated to delegate from the UI thread.
410 loop_
.RunFor(loop_duration
);
413 client1
->Uninitialize();
414 client2
->Uninitialize();
415 client3
->Uninitialize();
421 TEST_F(CFACMockTest
, NavigateTwiceAfterInitToSameUrl
) {
423 NavigationConstraintsImpl navigation_constraints
;
426 SetAutomationServerOk(1);
428 EXPECT_CALL(mock_proxy_
, server_version()).Times(testing::AnyNumber())
429 .WillRepeatedly(Return(""));
431 // We need some valid HWNDs, when responding to CreateExternalTab
432 HWND h1
= ::GetDesktopWindow();
433 HWND h2
= ::GetDesktopWindow();
434 EXPECT_CALL(mock_proxy_
, SendAsAsync(testing::Property(
435 &IPC::SyncMessage::type
, AutomationMsg_CreateExternalTab::ID
),
436 testing::NotNull(), _
))
437 .Times(1).WillOnce(HandleCreateTab(tab_handle_
, h1
, h2
, 99));
439 EXPECT_CALL(mock_proxy_
, CreateTabProxy(testing::Eq(tab_handle_
)))
440 .WillOnce(Return(tab_
));
442 EXPECT_CALL(cfd_
, OnAutomationServerReady())
443 .WillOnce(InitiateNavigation(client_
.get(),
444 std::string("http://www.nonexistent.com"),
445 std::string(), &navigation_constraints
));
447 EXPECT_CALL(mock_proxy_
, SendAsAsync(testing::Property(
448 &IPC::SyncMessage::type
, AutomationMsg_NavigateInExternalTab::ID
),
449 testing::NotNull(), _
))
450 .Times(1).WillOnce(QUIT_LOOP(loop_
));
452 EXPECT_CALL(mock_proxy_
, CancelAsync(_
)).Times(testing::AnyNumber());
454 EXPECT_CALL(mock_proxy_
, Send(
455 testing::Property(&IPC::Message::type
, AutomationMsg_TabReposition::ID
)))
457 .WillOnce(Return(true));
459 EXPECT_CALL(cfd_
, GetBounds(_
)).Times(1);
463 scoped_refptr
<ChromeFrameLaunchParams
> launch_params(
464 new ChromeFrameLaunchParams(
465 GURL("http://www.nonexistent.com"), empty
, profile_path_
,
466 profile_path_
.BaseName().value(), L
"", false, false, false, false));
467 launch_params
->set_launch_timeout(timeout
);
468 launch_params
->set_version_check(false);
469 EXPECT_TRUE(client_
->Initialize(&cfd_
, launch_params
));
470 loop_
.RunFor(base::TimeDelta::FromSeconds(10));
472 EXPECT_CALL(mock_proxy_
, ReleaseTabProxy(testing::Eq(tab_handle_
))).Times(1);
473 client_
->Uninitialize();