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/mock_ie_event_sink_test.h"
      6 
      7 #include <sstream>
      8 
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "base/win/scoped_variant.h"
     11 #include "chrome_frame/test/mock_ie_event_sink_actions.h"
     12 
     13 // Needed for CreateFunctor.
     14 #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING
     15 #include "testing/gmock_mutant.h"
     16 
     17 using testing::_;
     18 using testing::Cardinality;
     19 using testing::Exactly;
     20 using testing::ExpectationSet;
     21 using testing::InSequence;
     22 using testing::StrCaseEq;
     23 
     24 namespace chrome_frame_test {
     25 
     26 // MockIEEventSink methods
     27 void MockIEEventSink::OnDocumentComplete(IDispatch* dispatch, VARIANT* url) {
     28   if (!event_sink_->IsCFRendering()) {
     29     HWND renderer_window = event_sink_->GetRendererWindowSafe();
     30     if (renderer_window) {
     31       ::NotifyWinEvent(IA2_EVENT_DOCUMENT_LOAD_COMPLETE,
     32                        renderer_window,
     33                        OBJID_CLIENT, 0L);
     34     } else {
     35       VLOG(1) << "Browser does not have renderer window";
     36     }
     37     OnLoad(IN_IE, V_BSTR(url));
     38   }
     39 }
     40 
     41 ExpectationSet MockIEEventSink::ExpectNavigationCardinality(
     42     const std::wstring& url, Cardinality before_cardinality,
     43     Cardinality complete_cardinality) {
     44   ExpectationSet navigation;
     45   if (url.empty()) {
     46     navigation += EXPECT_CALL(*this, OnBeforeNavigate2(_, _, _, _, _, _, _))
     47         .Times(before_cardinality).RetiresOnSaturation();
     48   } else {
     49     navigation += EXPECT_CALL(*this, OnBeforeNavigate2(_,
     50                               testing::Field(&VARIANT::bstrVal,
     51                               StrCaseEq(url)), _, _, _, _, _))
     52         .Times(before_cardinality).RetiresOnSaturation();
     53   }
     54 
     55   // Hack: OnFileDownload may occur zero or once (for reasons not understood)
     56   // before each OnNavigateComplete2 which causes problems for tests expecting
     57   // things in sequence. To redress this, expectations which allow multiple
     58   // calls are split into expect statements expecting exactly one call or at
     59   // most one call.
     60   // TODO(kkania): Consider avoiding this problem by creating a mock without
     61   // the OnFileDownload call or by removing the dependency of some tests on
     62   // InSequence.
     63   LOG_IF(WARNING, complete_cardinality.ConservativeUpperBound() > 1000)
     64       << "Cardinality upper bound may be too great to be split up into single "
     65          "expect statements. If you do not require this navigation to be in "
     66          "sequence, do not call this method.";
     67   int call_count = 0;
     68   InSequence expect_in_sequence_for_scope;
     69   while (!complete_cardinality.IsSaturatedByCallCount(call_count)) {
     70     navigation += EXPECT_CALL(*this, OnFileDownload(_, _))
     71         .Times(testing::AtMost(1))
     72         .WillOnce(testing::SetArgumentPointee<1>(VARIANT_TRUE))
     73         .RetiresOnSaturation();
     74 
     75     Cardinality split_complete_cardinality = testing::Exactly(1);
     76     if (complete_cardinality.IsSatisfiedByCallCount(call_count))
     77       split_complete_cardinality = testing::AtMost(1);
     78 
     79     if (url.empty()) {
     80       navigation += EXPECT_CALL(*this, OnNavigateComplete2(_, _))
     81           .Times(split_complete_cardinality)
     82           .RetiresOnSaturation();
     83     } else {
     84       navigation += EXPECT_CALL(*this, OnNavigateComplete2(_,
     85                                 testing::Field(&VARIANT::bstrVal,
     86                                 StrCaseEq(url))))
     87           .Times(split_complete_cardinality)
     88           .RetiresOnSaturation();
     89     }
     90     call_count++;
     91   }
     92   return navigation;
     93 }
     94 
     95 void MockIEEventSink::ExpectNavigation(bool is_cf, const std::wstring& url) {
     96   InSequence expect_in_sequence_for_scope;
     97   if (is_cf || GetInstalledIEVersion() == IE_9) {
     98     ExpectNavigationCardinality(url, Exactly(1), testing::Between(1, 2));
     99   } else {
    100     ExpectNavigationCardinality(url, Exactly(1), Exactly(1));
    101   }
    102 }
    103 
    104 void MockIEEventSink::ExpectNavigationOptionalBefore(bool is_cf,
    105                                                      const std::wstring& url) {
    106   InSequence expect_in_sequence_for_scope;
    107   if (is_cf && GetInstalledIEVersion() == IE_6) {
    108     ExpectNavigationCardinality(url, testing::AtMost(1),
    109                                 testing::Between(1, 2));
    110   } else {
    111     ExpectNavigation(is_cf, url);
    112   }
    113 }
    114 
    115 void MockIEEventSink::ExpectJavascriptWindowOpenNavigation(
    116     bool parent_cf, bool new_window_cf, const std::wstring& url) {
    117   if (parent_cf) {
    118     InSequence expect_in_sequence_for_scope;
    119     ExpectNavigation(IN_CF, L"");
    120     ExpectNavigationCardinality(L"", testing::AtMost(1),
    121                                 testing::Between(1, 2));
    122   } else {
    123     if (new_window_cf) {
    124       ExpectNavigationCardinality(url, testing::AtMost(1), testing::AtMost(1));
    125       // Sometimes an extra load occurs here for some reason.
    126       EXPECT_CALL(*this, OnLoad(IN_IE, StrCaseEq(url)))
    127           .Times(testing::AtMost(1));
    128       ExpectNavigationCardinality(url, testing::AtMost(1),
    129                                   testing::Between(1, 2));
    130     } else {
    131       ExpectNavigation(IN_IE, url);
    132     }
    133   }
    134 }
    135 
    136 void MockIEEventSink::ExpectNewWindow(MockIEEventSink* new_window_mock) {
    137   DCHECK(new_window_mock);
    138 
    139   // IE8 seems to fire one of these events based on version.
    140   EXPECT_CALL(*this, OnNewWindow2(_, _))
    141       .Times(testing::AtMost(1));
    142   EXPECT_CALL(*this, OnNewWindow3(_, _, _, _, _))
    143       .Times(testing::AtMost(1));
    144 
    145   EXPECT_CALL(*this, OnNewBrowserWindow(_, _))
    146       .WillOnce(testing::WithArgs<0>(testing::Invoke(testing::CreateFunctor(
    147           new_window_mock, &MockIEEventSink::Attach))));
    148 }
    149 
    150 void MockIEEventSink::ExpectAnyNavigations() {
    151   EXPECT_CALL(*this, OnBeforeNavigate2(_, _, _, _, _, _, _))
    152       .Times(testing::AnyNumber());
    153   EXPECT_CALL(*this, OnFileDownload(VARIANT_TRUE, _))
    154       .Times(testing::AnyNumber());
    155   EXPECT_CALL(*this, OnNavigateComplete2(_, _))
    156       .Times(testing::AnyNumber());
    157 }
    158 
    159 void MockIEEventSink::ExpectDocumentReadystate(int ready_state) {
    160   base::win::ScopedComPtr<IWebBrowser2> browser(event_sink_->web_browser2());
    161   EXPECT_TRUE(browser != NULL);
    162   if (browser) {
    163     base::win::ScopedComPtr<IDispatch> document;
    164     browser->get_Document(document.Receive());
    165     EXPECT_TRUE(document != NULL);
    166     if (document) {
    167       DISPPARAMS params = { 0 };
    168       base::win::ScopedVariant result;
    169       EXPECT_HRESULT_SUCCEEDED(document->Invoke(DISPID_READYSTATE, IID_NULL,
    170           LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &params,
    171           result.Receive(), NULL, NULL));
    172       EXPECT_EQ(VT_I4, result.type());
    173       if (result.type() == VT_I4) {
    174         EXPECT_EQ(ready_state, static_cast<int>(V_I4(&result)));
    175       }
    176     }
    177   }
    178 }
    179 
    180 // MockIEEventSinkTest methods
    181 MockIEEventSinkTest::MockIEEventSinkTest()
    182     : server_mock_(1337, ASCIIToWide(chrome_frame_test::GetLocalIPv4Address()),
    183                    GetTestDataFolder()) {
    184   loop_.set_snapshot_on_timeout(true);
    185   EXPECT_CALL(server_mock_, Get(_, StrCaseEq(L"/favicon.ico"), _))
    186       .WillRepeatedly(SendFast("HTTP/1.1 404 Not Found", ""));
    187 }
    188 
    189 MockIEEventSinkTest::MockIEEventSinkTest(int port, const std::wstring& address,
    190                                          const base::FilePath& root_dir)
    191     : server_mock_(port, address, root_dir) {
    192   loop_.set_snapshot_on_timeout(true);
    193   EXPECT_CALL(server_mock_, Get(_, StrCaseEq(L"/favicon.ico"), _))
    194       .WillRepeatedly(SendFast("HTTP/1.1 404 Not Found", ""));
    195 }
    196 
    197 void MockIEEventSinkTest::LaunchIEAndNavigate(const std::wstring& url) {
    198   LaunchIENavigateAndLoop(url, kChromeFrameLongNavigationTimeout);
    199 }
    200 
    201 void MockIEEventSinkTest::LaunchIENavigateAndLoop(const std::wstring& url,
    202                                                   base::TimeDelta timeout) {
    203   if (GetInstalledIEVersion() >= IE_8) {
    204     chrome_frame_test::ClearIESessionHistory();
    205   }
    206   hung_call_detector_ = HungCOMCallDetector::Setup(ceil(timeout.InSecondsF()));
    207   EXPECT_TRUE(hung_call_detector_ != NULL);
    208 
    209   IEEventSink::SetAbnormalShutdown(false);
    210 
    211   EXPECT_CALL(ie_mock_, OnQuit())
    212       .WillOnce(QUIT_LOOP(loop_));
    213 
    214   HRESULT hr = ie_mock_.event_sink()->LaunchIEAndNavigate(url, &ie_mock_);
    215   ASSERT_HRESULT_SUCCEEDED(hr);
    216   if (hr != S_FALSE) {
    217     ASSERT_TRUE(ie_mock_.event_sink()->web_browser2() != NULL);
    218     loop_.RunFor(timeout);
    219   }
    220 
    221   if (hung_call_detector_) {
    222     IEEventSink::SetAbnormalShutdown(hung_call_detector_->is_hung());
    223     hung_call_detector_->TearDown();
    224   }
    225 }
    226 
    227 base::FilePath MockIEEventSinkTest::GetTestFilePath(
    228     const std::wstring& relative_path) {
    229   return server_mock_.root_dir().Append(relative_path);
    230 }
    231 
    232 std::wstring MockIEEventSinkTest::GetTestUrl(
    233     const std::wstring& relative_path) {
    234   return server_mock_.Resolve(relative_path.c_str());
    235 }
    236 
    237 }  // namespace chrome_frame_test
    238