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