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 "chrome_frame/test/automation_client_mock.h" 6 7 #include "base/bind.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" 15 16 #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING 17 #include "testing/gmock_mutant.h" 18 19 using testing::_; 20 using testing::CreateFunctor; 21 using testing::Return; 22 23 namespace { 24 25 #ifndef NDEBUG 26 const base::TimeDelta kChromeLaunchTimeout = base::TimeDelta::FromSeconds(15); 27 #else 28 const base::TimeDelta kChromeLaunchTimeout = base::TimeDelta::FromSeconds(10); 29 #endif 30 31 const int kSaneAutomationTimeoutMs = 10 * 1000; 32 33 } // namespace 34 35 MATCHER_P(LaunchParamProfileEq, profile_name, "Check for profile name") { 36 return arg->profile_name().compare(profile_name) == 0; 37 } 38 39 void MockProxyFactory::GetServerImpl(ChromeFrameAutomationProxy* pxy, 40 void* proxy_id, 41 AutomationLaunchResult result, 42 LaunchDelegate* d, 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); 50 } 51 52 void CFACMockTest::SetAutomationServerOk(int times) { 53 EXPECT_CALL(factory_, GetAutomationServer(testing::NotNull(), 54 LaunchParamProfileEq(profile_path_.BaseName().value()), 55 testing::NotNull())) 56 .Times(times) 57 .WillRepeatedly(testing::Invoke(CreateFunctor(&factory_, 58 &MockProxyFactory::GetServerImpl, get_proxy(), id_, 59 AUTOMATION_SUCCESS))); 60 61 EXPECT_CALL(factory_, 62 ReleaseAutomationServer(testing::Eq(id_), testing::NotNull())) 63 .Times(times); 64 } 65 66 void CFACMockTest::Set_CFD_LaunchFailed(AutomationLaunchResult result) { 67 EXPECT_CALL(cfd_, OnAutomationServerLaunchFailed(testing::Eq(result), 68 testing::_)) 69 .Times(1) 70 .WillOnce(QUIT_LOOP(loop_)); 71 } 72 73 MATCHER_P(MsgType, msg_type, "IPC::Message::type()") { 74 const IPC::Message& m = arg; 75 return (m.type() == msg_type); 76 } 77 78 MATCHER_P(EqNavigationInfoUrl, url, "IPC::NavigationInfo matcher") { 79 if (url.is_valid() && url != arg.url) 80 return false; 81 // TODO(stevet): other members 82 return true; 83 } 84 85 // Could be implemented as MockAutomationProxy member (we have WithArgs<>!) 86 ACTION_P4(HandleCreateTab, tab_handle, external_tab_container, tab_wnd, 87 session_id) { 88 // arg0 - message 89 // arg1 - callback 90 // arg2 - key 91 CreateExternalTabContext::output_type input_args(tab_wnd, 92 external_tab_container, 93 tab_handle, 94 session_id); 95 CreateExternalTabContext* context = 96 reinterpret_cast<CreateExternalTabContext*>(arg1); 97 DispatchToMethod(context, &CreateExternalTabContext::Completed, input_args); 98 delete context; 99 } 100 101 ACTION_P4(InitiateNavigation, client, url, referrer, constraints) { 102 client->InitiateNavigation(url, referrer, constraints); 103 } 104 105 // ChromeFrameAutomationClient tests that launch Chrome. 106 class CFACWithChrome : public testing::Test { 107 protected: 108 static void SetUpTestCase(); 109 static void TearDownTestCase(); 110 111 virtual void SetUp() OVERRIDE; 112 virtual void TearDown() OVERRIDE; 113 114 static base::FilePath profile_path_; 115 MockCFDelegate cfd_; 116 scoped_refptr<ChromeFrameAutomationClient> client_; 117 scoped_refptr<ChromeFrameLaunchParams> launch_params_; 118 chrome_frame_test::TimedMsgLoop loop_; 119 }; 120 121 // static 122 base::FilePath CFACWithChrome::profile_path_; 123 124 // static 125 void CFACWithChrome::SetUpTestCase() { 126 GetChromeFrameProfilePath(L"Adam.N.Epilinter", &profile_path_); 127 } 128 129 // static 130 void CFACWithChrome::TearDownTestCase() { 131 profile_path_.clear(); 132 } 133 134 void CFACWithChrome::SetUp() { 135 chrome_frame_test::OverrideDataDirectoryForThisTest(profile_path_.value()); 136 client_ = new ChromeFrameAutomationClient(); 137 GURL empty; 138 launch_params_ = new ChromeFrameLaunchParams( 139 empty, empty, profile_path_, profile_path_.BaseName().value(), L"", 140 false, false, false); 141 launch_params_->set_version_check(false); 142 launch_params_->set_launch_timeout(kSaneAutomationTimeoutMs); 143 } 144 145 void CFACWithChrome::TearDown() { 146 client_->Uninitialize(); 147 } 148 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. 152 153 EXPECT_CALL(cfd_, OnAutomationServerLaunchFailed(AUTOMATION_TIMEOUT, _)) 154 .WillOnce(QUIT_LOOP(loop_)); 155 156 launch_params_->set_launch_timeout(timeout); 157 EXPECT_TRUE(client_->Initialize(&cfd_, launch_params_)); 158 loop_.RunFor(kChromeLaunchTimeout); 159 } 160 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_)); 168 169 EXPECT_CALL(cfd_, OnAutomationServerLaunchFailed(_, _)) 170 .Times(0); 171 172 EXPECT_TRUE(client_->Initialize(&cfd_, launch_params_)); 173 174 loop_.RunFor(kChromeLaunchTimeout); 175 } 176 177 TEST_F(CFACWithChrome, NavigateOk) { 178 NavigationConstraintsImpl navigation_constraints; 179 180 const std::string url = "about:version"; 181 182 EXPECT_CALL(cfd_, OnAutomationServerReady()) 183 .WillOnce(InitiateNavigation(client_.get(), url, std::string(), 184 &navigation_constraints)); 185 186 EXPECT_CALL(cfd_, GetBounds(_)).Times(testing::AnyNumber()); 187 188 EXPECT_CALL(cfd_, OnNavigationStateChanged(_)) 189 .Times(testing::AnyNumber()); 190 191 { 192 testing::InSequence s; 193 194 EXPECT_CALL(cfd_, OnDidNavigate(EqNavigationInfoUrl(GURL()))) 195 .Times(1); 196 197 EXPECT_CALL(cfd_, OnUpdateTargetUrl(_)).Times(testing::AtMost(1)); 198 199 EXPECT_CALL(cfd_, OnLoad(_)) 200 .WillOnce(QUIT_LOOP(loop_)); 201 } 202 203 EXPECT_TRUE(client_->Initialize(&cfd_, launch_params_)); 204 loop_.RunFor(kChromeLaunchTimeout); 205 } 206 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); 212 213 cfd_.SetRequestDelegate(client_); 214 215 EXPECT_CALL(cfd_, OnAutomationServerReady()) 216 .WillOnce(testing::IgnoreResult(testing::InvokeWithoutArgs(CreateFunctor( 217 client_.get(), &ChromeFrameAutomationClient::InitiateNavigation, 218 url, std::string(), &navigation_constraints)))); 219 220 EXPECT_CALL(cfd_, GetBounds(_)).Times(testing::AnyNumber()); 221 EXPECT_CALL(cfd_, OnNavigationStateChanged(_)).Times(testing::AnyNumber()); 222 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)))); 228 229 EXPECT_CALL(cfd_, OnUpdateTargetUrl(_)).Times(testing::AnyNumber()); 230 EXPECT_CALL(cfd_, OnLoad(_)).Times(testing::AtMost(1)); 231 232 EXPECT_CALL(cfd_, OnNavigationFailed(_, GURL(url))) 233 .Times(1) 234 .WillOnce(QUIT_LOOP_SOON(loop_, base::TimeDelta::FromSeconds(2))); 235 236 EXPECT_TRUE(client_->Initialize(&cfd_, launch_params_)); 237 238 loop_.RunFor(kChromeLaunchTimeout); 239 } 240 241 TEST_F(CFACMockTest, MockedCreateTabOk) { 242 int timeout = 500; 243 CreateTab(); 244 SetAutomationServerOk(1); 245 246 EXPECT_CALL(mock_proxy_, server_version()).Times(testing::AnyNumber()) 247 .WillRepeatedly(Return("")); 248 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)); 256 257 EXPECT_CALL(mock_proxy_, CreateTabProxy(testing::Eq(tab_handle_))) 258 .WillOnce(Return(tab_)); 259 260 EXPECT_CALL(cfd_, OnAutomationServerReady()) 261 .WillOnce(QUIT_LOOP(loop_)); 262 263 EXPECT_CALL(mock_proxy_, CancelAsync(_)).Times(testing::AnyNumber()); 264 265 // Here we go! 266 GURL empty; 267 scoped_refptr<ChromeFrameLaunchParams> clp(new ChromeFrameLaunchParams( 268 empty, empty, profile_path_, profile_path_.BaseName().value(), L"", 269 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)); 274 275 EXPECT_CALL(mock_proxy_, ReleaseTabProxy(testing::Eq(tab_handle_))).Times(1); 276 client_->Uninitialize(); 277 } 278 279 TEST_F(CFACMockTest, MockedCreateTabFailed) { 280 HWND null_wnd = NULL; 281 SetAutomationServerOk(1); 282 283 EXPECT_CALL(mock_proxy_, server_version()).Times(testing::AnyNumber()) 284 .WillRepeatedly(Return("")); 285 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, 290 99)); 291 292 EXPECT_CALL(mock_proxy_, CreateTabProxy(_)).Times(0); 293 294 EXPECT_CALL(mock_proxy_, CancelAsync(_)).Times(testing::AnyNumber()); 295 296 Set_CFD_LaunchFailed(AUTOMATION_CREATE_TAB_FAILED); 297 298 // Here we go! 299 GURL empty; 300 scoped_refptr<ChromeFrameLaunchParams> clp(new ChromeFrameLaunchParams( 301 empty, empty, profile_path_, profile_path_.BaseName().value(), L"", 302 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(); 308 } 309 310 class TestChromeFrameAutomationProxyImpl 311 : public ChromeFrameAutomationProxyImpl { 312 public: 313 TestChromeFrameAutomationProxyImpl() 314 // 1 is an unneeded timeout. 315 : ChromeFrameAutomationProxyImpl( 316 NULL, 317 AutomationProxy::GenerateChannelID(), 318 base::TimeDelta::FromMilliseconds(1)) { 319 } 320 MOCK_METHOD3( 321 SendAsAsync, 322 void(IPC::SyncMessage* msg, 323 SyncMessageReplyDispatcher::SyncMessageCallContext* context, 324 void* key)); 325 void FakeChannelError() { 326 reinterpret_cast<IPC::ChannelProxy::MessageFilter*>(message_filter_.get())-> 327 OnChannelError(); 328 } 329 }; 330 331 TEST_F(CFACMockTest, OnChannelErrorEmpty) { 332 TestChromeFrameAutomationProxyImpl proxy; 333 334 // No tabs should do nothing yet still not fail either. 335 proxy.FakeChannelError(); 336 } 337 338 TEST_F(CFACMockTest, OnChannelError) { 339 const base::TimeDelta loop_duration = base::TimeDelta::FromSeconds(11); 340 TestChromeFrameAutomationProxyImpl proxy; 341 returned_proxy_ = &proxy; 342 343 GURL empty; 344 scoped_refptr<ChromeFrameLaunchParams> clp(new ChromeFrameLaunchParams( 345 empty, empty, profile_path_, profile_path_.BaseName().value(), L"", 346 false, false, false)); 347 clp->set_launch_timeout(1); // Unneeded timeout, but can't be 0. 348 clp->set_version_check(false); 349 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)); 358 359 SetAutomationServerOk(3); 360 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_); 367 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); 372 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); 377 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_); 383 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); 388 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); 394 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_); 399 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); 404 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); 411 412 // Cleanup. 413 client1->Uninitialize(); 414 client2->Uninitialize(); 415 client3->Uninitialize(); 416 client1 = NULL; 417 client2 = NULL; 418 client3 = NULL; 419 } 420 421 TEST_F(CFACMockTest, NavigateTwiceAfterInitToSameUrl) { 422 int timeout = 500; 423 NavigationConstraintsImpl navigation_constraints; 424 425 CreateTab(); 426 SetAutomationServerOk(1); 427 428 EXPECT_CALL(mock_proxy_, server_version()).Times(testing::AnyNumber()) 429 .WillRepeatedly(Return("")); 430 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)); 438 439 EXPECT_CALL(mock_proxy_, CreateTabProxy(testing::Eq(tab_handle_))) 440 .WillOnce(Return(tab_)); 441 442 EXPECT_CALL(cfd_, OnAutomationServerReady()) 443 .WillOnce(InitiateNavigation(client_.get(), 444 std::string("http://www.nonexistent.com"), 445 std::string(), &navigation_constraints)); 446 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_)); 451 452 EXPECT_CALL(mock_proxy_, CancelAsync(_)).Times(testing::AnyNumber()); 453 454 EXPECT_CALL(mock_proxy_, Send( 455 testing::Property(&IPC::Message::type, AutomationMsg_TabReposition::ID))) 456 .Times(1) 457 .WillOnce(Return(true)); 458 459 EXPECT_CALL(cfd_, GetBounds(_)).Times(1); 460 461 // Here we go! 462 GURL empty; 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)); 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)); 471 472 EXPECT_CALL(mock_proxy_, ReleaseTabProxy(testing::Eq(tab_handle_))).Times(1); 473 client_->Uninitialize(); 474 } 475