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