| OLD | NEW |
| (Empty) |
| 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 */ | |
| OLD | NEW |