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 <atlbase.h>
      6 #include <atlcom.h>
      7 
      8 #include "base/bind.h"
      9 #include "base/threading/thread.h"
     10 #include "base/win/scoped_comptr.h"
     11 #include "base/win/scoped_handle.h"
     12 #include "chrome_frame/bho.h"
     13 //#include "chrome_frame/urlmon_moniker.h"
     14 #include "chrome_frame/test/chrome_frame_test_utils.h"
     15 #include "chrome_frame/test/test_server.h"
     16 #include "chrome_frame/test/urlmon_moniker_tests.h"
     17 #include "gmock/gmock.h"
     18 #include "gtest/gtest.h"
     19 
     20 #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING
     21 #include "testing/gmock_mutant.h"
     22 
     23 using testing::_;
     24 using testing::CreateFunctor;
     25 using testing::Eq;
     26 using testing::Invoke;
     27 using testing::SetArgumentPointee;
     28 using testing::StrEq;
     29 using testing::Return;
     30 using testing::DoAll;
     31 using testing::WithArgs;
     32 
     33 
     34 static const base::TimeDelta kUrlmonMonikerTimeout =
     35      base::TimeDelta::FromSeconds(5);
     36 
     37 namespace {
     38 const char kTestContent[] = "<html><head>"
     39     "<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\" />"
     40     "</head><body>Test HTML content</body></html>";
     41 }  // end namespace
     42 
     43 class UrlmonMonikerTest : public testing::Test {
     44  protected:
     45   UrlmonMonikerTest() {
     46   }
     47 };
     48 
     49 TEST_F(UrlmonMonikerTest, MonikerPatch) {
     50   EXPECT_TRUE(MonikerPatch::Initialize());
     51   EXPECT_TRUE(MonikerPatch::Initialize());  // Should be ok to call twice.
     52   MonikerPatch::Uninitialize();
     53 }
     54 
     55 // Runs an HTTP server on a worker thread that has a message loop.
     56 class RunTestServer : public base::Thread {
     57  public:
     58   RunTestServer()
     59       : base::Thread("TestServer"),
     60       default_response_("/", kTestContent),
     61       ready_(::CreateEvent(NULL, TRUE, FALSE, NULL)) {
     62   }
     63 
     64   ~RunTestServer() {
     65     Stop();
     66   }
     67 
     68   bool Start() {
     69     bool ret = StartWithOptions(Options(base::MessageLoop::TYPE_UI, 0));
     70     if (ret) {
     71       message_loop()->PostTask(FROM_HERE,
     72                                base::Bind(&RunTestServer::StartServer, this));
     73       wait_until_ready();
     74     }
     75     return ret;
     76   }
     77 
     78   static void StartServer(RunTestServer* me) {
     79     me->server_.reset(new test_server::SimpleWebServer(43210));
     80     me->server_->AddResponse(&me->default_response_);
     81     ::SetEvent(me->ready_);
     82   }
     83 
     84   bool wait_until_ready() {
     85     return ::WaitForSingleObject(ready_, kUrlmonMonikerTimeout.InMilliseconds())
     86            == WAIT_OBJECT_0;
     87   }
     88 
     89  protected:
     90   scoped_ptr<test_server::SimpleWebServer> server_;
     91   test_server::SimpleResponse default_response_;
     92   base::win::ScopedHandle ready_;
     93 };
     94 
     95 // Helper class for running tests that rely on the NavigationManager.
     96 class UrlmonMonikerTestManager {
     97  public:
     98   explicit UrlmonMonikerTestManager(const wchar_t* test_url) {
     99     EXPECT_TRUE(MonikerPatch::Initialize());
    100   }
    101 
    102   ~UrlmonMonikerTestManager() {
    103     MonikerPatch::Uninitialize();
    104   }
    105 
    106   chrome_frame_test::TimedMsgLoop& loop() {
    107     return loop_;
    108   }
    109 
    110  protected:
    111   chrome_frame_test::TimedMsgLoop loop_;
    112 };
    113 
    114 ACTION_P(SetBindInfo, is_async) {
    115   DWORD* flags = arg0;
    116   BINDINFO* bind_info = arg1;
    117 
    118   DCHECK(flags);
    119   DCHECK(bind_info);
    120   DCHECK(bind_info->cbSize >= sizeof(BINDINFO));
    121 
    122   *flags = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
    123   if (is_async)
    124     *flags |= BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE;
    125 
    126   bind_info->dwBindVerb = BINDVERB_GET;
    127   memset(&bind_info->stgmedData, 0, sizeof(STGMEDIUM));
    128   bind_info->grfBindInfoF = 0;
    129   bind_info->szCustomVerb = NULL;
    130 }
    131 
    132 // Wraps the MockBindStatusCallbackImpl mock object and allows the user
    133 // to specify expectations on the callback object.
    134 class UrlmonMonikerTestCallback {
    135  public:
    136   explicit UrlmonMonikerTestCallback(UrlmonMonikerTestManager* mgr)
    137       : mgr_(mgr), clip_format_(0) {
    138   }
    139 
    140   ~UrlmonMonikerTestCallback() {
    141   }
    142 
    143   typedef enum GetBindInfoExpectations {
    144     EXPECT_NO_CALL,
    145     REQUEST_SYNCHRONOUS,
    146     REQUEST_ASYNCHRONOUS,
    147   } GET_BIND_INFO_EXPECTATION;
    148 
    149   // Sets gmock expectations for the IBindStatusCallback mock object.
    150   void SetCallbackExpectations(GetBindInfoExpectations bind_info_handling,
    151                                HRESULT data_available_response,
    152                                bool quit_loop_on_stop) {
    153     EXPECT_CALL(callback_, OnProgress(_, _, _, _))
    154         .WillRepeatedly(Return(S_OK));
    155 
    156     if (bind_info_handling == REQUEST_ASYNCHRONOUS) {
    157       EXPECT_CALL(callback_, GetBindInfo(_, _))
    158           .WillOnce(DoAll(SetBindInfo(true), Return(S_OK)));
    159     } else if (bind_info_handling == REQUEST_SYNCHRONOUS) {
    160       EXPECT_CALL(callback_, GetBindInfo(_, _))
    161           .WillOnce(DoAll(SetBindInfo(false), Return(S_OK)));
    162     } else {
    163       DCHECK(bind_info_handling == EXPECT_NO_CALL);
    164     }
    165 
    166     EXPECT_CALL(callback_, OnStartBinding(_, _))
    167         .WillOnce(Return(S_OK));
    168 
    169     EXPECT_CALL(callback_, OnDataAvailable(_, _, _, _))
    170         .WillRepeatedly(Return(data_available_response));
    171 
    172     if (quit_loop_on_stop) {
    173       // When expecting asynchronous
    174       EXPECT_CALL(callback_, OnStopBinding(data_available_response, _))
    175           .WillOnce(DoAll(QUIT_LOOP(mgr_->loop()), Return(S_OK)));
    176     } else {
    177       EXPECT_CALL(callback_, OnStopBinding(data_available_response, _))
    178           .WillOnce(Return(S_OK));
    179     }
    180   }
    181 
    182   HRESULT CreateUrlMonikerAndBindToStorage(const wchar_t* url,
    183                                            IBindCtx** bind_ctx) {
    184     base::win::ScopedComPtr<IMoniker> moniker;
    185     HRESULT hr = CreateURLMoniker(NULL, url, moniker.Receive());
    186     EXPECT_TRUE(moniker != NULL);
    187     if (moniker) {
    188       base::win::ScopedComPtr<IBindCtx> context;
    189       ::CreateAsyncBindCtx(0, callback(), NULL, context.Receive());
    190       DCHECK(context);
    191       base::win::ScopedComPtr<IStream> stream;
    192       hr = moniker->BindToStorage(context, NULL, IID_IStream,
    193           reinterpret_cast<void**>(stream.Receive()));
    194       if (SUCCEEDED(hr) && bind_ctx)
    195         *bind_ctx = context.Detach();
    196     }
    197     return hr;
    198   }
    199 
    200   IBindStatusCallback* callback() {
    201     return &callback_;
    202   }
    203 
    204  protected:
    205   CComObjectStackEx<MockBindStatusCallbackImpl> callback_;
    206   UrlmonMonikerTestManager* mgr_;
    207   CLIPFORMAT clip_format_;
    208 };
    209 
    210 /*
    211 
    212 // Tests synchronously binding to a moniker and downloading the target.
    213 TEST_F(UrlmonMonikerTest, BindToStorageSynchronous) {
    214   const wchar_t test_url[] = L"http://localhost:43210/";
    215   UrlmonMonikerTestManager test(test_url);
    216   UrlmonMonikerTestCallback callback(&test);
    217 
    218   RunTestServer server_thread;
    219   EXPECT_TRUE(server_thread.Start());
    220 
    221   callback.SetCallbackExpectations(
    222       UrlmonMonikerTestCallback::REQUEST_SYNCHRONOUS, S_OK, false);
    223 
    224   base::win::ScopedComPtr<IBindCtx> bind_ctx;
    225   HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url,
    226                                                          bind_ctx.Receive());
    227   // The download should have happened synchronously, so we don't expect
    228   // MK_S_ASYNCHRONOUS or any errors.
    229   EXPECT_EQ(S_OK, hr);
    230 
    231   IBindCtx* release = bind_ctx.Detach();
    232   EXPECT_EQ(0, release->Release());
    233 
    234   server_thread.Stop();
    235 }
    236 
    237 // Tests asynchronously binding to a moniker and downloading the target.
    238 TEST_F(UrlmonMonikerTest, BindToStorageAsynchronous) {
    239   const wchar_t test_url[] = L"http://localhost:43210/";
    240   UrlmonMonikerTestManager test(test_url);
    241   UrlmonMonikerTestCallback callback(&test);
    242 
    243   test_server::SimpleWebServer server(43210);
    244   test_server::SimpleResponse default_response("/", kTestContent);
    245   server.AddResponse(&default_response);
    246 
    247   callback.SetCallbackExpectations(
    248       UrlmonMonikerTestCallback::REQUEST_ASYNCHRONOUS, S_OK, true);
    249 
    250   base::win::ScopedComPtr<IBindCtx> bind_ctx;
    251   HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url,
    252                                                          bind_ctx.Receive());
    253   EXPECT_EQ(MK_S_ASYNCHRONOUS, hr);
    254   test.loop().RunFor(kUrlmonMonikerTimeout);
    255 
    256   IBindCtx* release = bind_ctx.Detach();
    257   EXPECT_EQ(0, release->Release());
    258 }
    259 
    260 // Responds with the Chrome mime type.
    261 class ResponseWithContentType : public test_server::SimpleResponse {
    262  public:
    263   ResponseWithContentType(const char* request_path,
    264                           const std::string& contents)
    265     : test_server::SimpleResponse(request_path, contents) {
    266   }
    267   virtual bool GetContentType(std::string* content_type) const {
    268     *content_type = WideToASCII(kChromeMimeType);
    269     return true;
    270   }
    271 };
    272 
    273 // Downloads a document asynchronously and then verifies that the downloaded
    274 // contents were cached and the cache contents are correct.
    275 // TODO(tommi): Fix and re-enable.
    276 //  http://code.google.com/p/chromium/issues/detail?id=39415
    277 TEST_F(UrlmonMonikerTest, BindToStorageSwitchContent) {
    278   const wchar_t test_url[] = L"http://localhost:43210/";
    279   UrlmonMonikerTestManager test(test_url);
    280   UrlmonMonikerTestCallback callback(&test);
    281 
    282   test_server::SimpleWebServer server(43210);
    283   ResponseWithContentType default_response("/", kTestContent);
    284   server.AddResponse(&default_response);
    285 
    286   callback.SetCallbackExpectations(
    287       UrlmonMonikerTestCallback::REQUEST_ASYNCHRONOUS, INET_E_TERMINATED_BIND,
    288       true);
    289 
    290   HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url, NULL);
    291   EXPECT_EQ(MK_S_ASYNCHRONOUS, hr);
    292   test.loop().RunFor(kUrlmonMonikerTimeout);
    293 
    294   scoped_refptr<RequestData> request_data(
    295       test.nav_manager().GetActiveRequestData(test_url));
    296   EXPECT_TRUE(request_data != NULL);
    297 
    298   if (request_data) {
    299     EXPECT_EQ(request_data->GetCachedContentSize(),
    300               arraysize(kTestContent) - 1);
    301     base::win::ScopedComPtr<IStream> stream;
    302     request_data->GetResetCachedContentStream(stream.Receive());
    303     EXPECT_TRUE(stream != NULL);
    304     if (stream) {
    305       char buffer[0xffff];
    306       DWORD read = 0;
    307       stream->Read(buffer, sizeof(buffer), &read);
    308       EXPECT_EQ(read, arraysize(kTestContent) - 1);
    309       EXPECT_EQ(0, memcmp(buffer, kTestContent, read));
    310     }
    311   }
    312 }
    313 
    314 // Fetches content asynchronously first to cache it and then
    315 // verifies that fetching the cached content the same way works as expected
    316 // and happens synchronously.
    317 TEST_F(UrlmonMonikerTest, BindToStorageCachedContent) {
    318   const wchar_t test_url[] = L"http://localhost:43210/";
    319   UrlmonMonikerTestManager test(test_url);
    320   UrlmonMonikerTestCallback callback(&test);
    321 
    322   test_server::SimpleWebServer server(43210);
    323   ResponseWithContentType default_response("/", kTestContent);
    324   server.AddResponse(&default_response);
    325 
    326   // First set of expectations.  Download the contents
    327   // asynchronously.  This should populate the cache so that
    328   // the second request should be served synchronously without
    329   // going to the server.
    330   callback.SetCallbackExpectations(
    331       UrlmonMonikerTestCallback::REQUEST_ASYNCHRONOUS, INET_E_TERMINATED_BIND,
    332       true);
    333 
    334   HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url, NULL);
    335   EXPECT_EQ(MK_S_ASYNCHRONOUS, hr);
    336   test.loop().RunFor(kUrlmonMonikerTimeout);
    337 
    338   scoped_refptr<RequestData> request_data(
    339       test.nav_manager().GetActiveRequestData(test_url));
    340   EXPECT_TRUE(request_data != NULL);
    341 
    342   if (request_data) {
    343     // This time, just accept the content as normal.
    344     UrlmonMonikerTestCallback callback2(&test);
    345     callback2.SetCallbackExpectations(
    346         UrlmonMonikerTestCallback::EXPECT_NO_CALL, S_OK, false);
    347     hr = callback2.CreateUrlMonikerAndBindToStorage(test_url, NULL);
    348     // S_OK means that the operation completed synchronously.
    349     // Otherwise we'd get MK_S_ASYNCHRONOUS.
    350     EXPECT_EQ(S_OK, hr);
    351   }
    352 }
    353 
    354 */
    355