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 |