Home | History | Annotate | Download | only in test
      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