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.
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"
20 #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING
21 #include "testing/gmock_mutant.h"
24 using testing::CreateFunctor
;
26 using testing::Invoke
;
27 using testing::SetArgumentPointee
;
29 using testing::Return
;
31 using testing::WithArgs
;
34 static const base::TimeDelta kUrlmonMonikerTimeout
=
35 base::TimeDelta::FromSeconds(5);
38 const char kTestContent
[] = "<html><head>"
39 "<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\" />"
40 "</head><body>Test HTML content</body></html>";
43 class UrlmonMonikerTest
: public testing::Test
{
49 TEST_F(UrlmonMonikerTest
, MonikerPatch
) {
50 EXPECT_TRUE(MonikerPatch::Initialize());
51 EXPECT_TRUE(MonikerPatch::Initialize()); // Should be ok to call twice.
52 MonikerPatch::Uninitialize();
55 // Runs an HTTP server on a worker thread that has a message loop.
56 class RunTestServer
: public base::Thread
{
59 : base::Thread("TestServer"),
60 default_response_("/", kTestContent
),
61 ready_(::CreateEvent(NULL
, TRUE
, FALSE
, NULL
)) {
69 bool ret
= StartWithOptions(Options(MessageLoop::TYPE_UI
, 0));
71 message_loop()->PostTask(FROM_HERE
,
72 base::Bind(&RunTestServer::StartServer
, this));
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_
);
84 bool wait_until_ready() {
85 return ::WaitForSingleObject(ready_
, kUrlmonMonikerTimeout
.InMilliseconds())
90 scoped_ptr
<test_server::SimpleWebServer
> server_
;
91 test_server::SimpleResponse default_response_
;
92 base::win::ScopedHandle ready_
;
95 // Helper class for running tests that rely on the NavigationManager.
96 class UrlmonMonikerTestManager
{
98 explicit UrlmonMonikerTestManager(const wchar_t* test_url
) {
99 EXPECT_TRUE(MonikerPatch::Initialize());
102 ~UrlmonMonikerTestManager() {
103 MonikerPatch::Uninitialize();
106 chrome_frame_test::TimedMsgLoop
& loop() {
111 chrome_frame_test::TimedMsgLoop loop_
;
114 ACTION_P(SetBindInfo
, is_async
) {
116 BINDINFO
* bind_info
= arg1
;
120 DCHECK(bind_info
->cbSize
>= sizeof(BINDINFO
));
122 *flags
= BINDF_ASYNCHRONOUS
| BINDF_ASYNCSTORAGE
| BINDF_PULLDATA
;
124 *flags
|= BINDF_ASYNCHRONOUS
| BINDF_ASYNCSTORAGE
;
126 bind_info
->dwBindVerb
= BINDVERB_GET
;
127 memset(&bind_info
->stgmedData
, 0, sizeof(STGMEDIUM
));
128 bind_info
->grfBindInfoF
= 0;
129 bind_info
->szCustomVerb
= NULL
;
132 // Wraps the MockBindStatusCallbackImpl mock object and allows the user
133 // to specify expectations on the callback object.
134 class UrlmonMonikerTestCallback
{
136 explicit UrlmonMonikerTestCallback(UrlmonMonikerTestManager
* mgr
)
137 : mgr_(mgr
), clip_format_(0) {
140 ~UrlmonMonikerTestCallback() {
143 typedef enum GetBindInfoExpectations
{
146 REQUEST_ASYNCHRONOUS
,
147 } GET_BIND_INFO_EXPECTATION
;
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
));
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
)));
163 DCHECK(bind_info_handling
== EXPECT_NO_CALL
);
166 EXPECT_CALL(callback_
, OnStartBinding(_
, _
))
167 .WillOnce(Return(S_OK
));
169 EXPECT_CALL(callback_
, OnDataAvailable(_
, _
, _
, _
))
170 .WillRepeatedly(Return(data_available_response
));
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
)));
177 EXPECT_CALL(callback_
, OnStopBinding(data_available_response
, _
))
178 .WillOnce(Return(S_OK
));
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
);
188 base::win::ScopedComPtr
<IBindCtx
> context
;
189 ::CreateAsyncBindCtx(0, callback(), NULL
, context
.Receive());
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();
200 IBindStatusCallback
* callback() {
205 CComObjectStackEx
<MockBindStatusCallbackImpl
> callback_
;
206 UrlmonMonikerTestManager
* mgr_
;
207 CLIPFORMAT clip_format_
;
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);
218 RunTestServer server_thread;
219 EXPECT_TRUE(server_thread.Start());
221 callback.SetCallbackExpectations(
222 UrlmonMonikerTestCallback::REQUEST_SYNCHRONOUS, S_OK, false);
224 base::win::ScopedComPtr<IBindCtx> bind_ctx;
225 HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url,
227 // The download should have happened synchronously, so we don't expect
228 // MK_S_ASYNCHRONOUS or any errors.
231 IBindCtx* release = bind_ctx.Detach();
232 EXPECT_EQ(0, release->Release());
234 server_thread.Stop();
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);
243 test_server::SimpleWebServer server(43210);
244 test_server::SimpleResponse default_response("/", kTestContent);
245 server.AddResponse(&default_response);
247 callback.SetCallbackExpectations(
248 UrlmonMonikerTestCallback::REQUEST_ASYNCHRONOUS, S_OK, true);
250 base::win::ScopedComPtr<IBindCtx> bind_ctx;
251 HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url,
253 EXPECT_EQ(MK_S_ASYNCHRONOUS, hr);
254 test.loop().RunFor(kUrlmonMonikerTimeout);
256 IBindCtx* release = bind_ctx.Detach();
257 EXPECT_EQ(0, release->Release());
260 // Responds with the Chrome mime type.
261 class ResponseWithContentType : public test_server::SimpleResponse {
263 ResponseWithContentType(const char* request_path,
264 const std::string& contents)
265 : test_server::SimpleResponse(request_path, contents) {
267 virtual bool GetContentType(std::string* content_type) const {
268 *content_type = WideToASCII(kChromeMimeType);
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);
282 test_server::SimpleWebServer server(43210);
283 ResponseWithContentType default_response("/", kTestContent);
284 server.AddResponse(&default_response);
286 callback.SetCallbackExpectations(
287 UrlmonMonikerTestCallback::REQUEST_ASYNCHRONOUS, INET_E_TERMINATED_BIND,
290 HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url, NULL);
291 EXPECT_EQ(MK_S_ASYNCHRONOUS, hr);
292 test.loop().RunFor(kUrlmonMonikerTimeout);
294 scoped_refptr<RequestData> request_data(
295 test.nav_manager().GetActiveRequestData(test_url));
296 EXPECT_TRUE(request_data != NULL);
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);
307 stream->Read(buffer, sizeof(buffer), &read);
308 EXPECT_EQ(read, arraysize(kTestContent) - 1);
309 EXPECT_EQ(0, memcmp(buffer, kTestContent, read));
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);
322 test_server::SimpleWebServer server(43210);
323 ResponseWithContentType default_response("/", kTestContent);
324 server.AddResponse(&default_response);
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,
334 HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url, NULL);
335 EXPECT_EQ(MK_S_ASYNCHRONOUS, hr);
336 test.loop().RunFor(kUrlmonMonikerTimeout);
338 scoped_refptr<RequestData> request_data(
339 test.nav_manager().GetActiveRequestData(test_url));
340 EXPECT_TRUE(request_data != NULL);
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.