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/bind_helpers.h"
10#include "chrome/common/automation_messages.h"
11#include "chrome_frame/test/chrome_frame_test_utils.h"
12#include "chrome_frame/test/test_server.h"
13#include "chrome_frame/test/test_with_web_server.h"
14#include "chrome_frame/urlmon_url_request.h"
15#include "chrome_frame/urlmon_url_request_private.h"
16#include "testing/gmock/include/gmock/gmock.h"
17#include "testing/gmock_mutant.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20using testing::CreateFunctor;
21
22using chrome_frame_test::kChromeFrameLongNavigationTimeout;
23
24static void AppendToStream(IStream* s, void* buffer, ULONG cb) {
25  ULONG bytes_written;
26  LARGE_INTEGER current_pos;
27  LARGE_INTEGER zero = {0};
28  // Remember current position.
29  ASSERT_HRESULT_SUCCEEDED(s->Seek(zero, STREAM_SEEK_CUR,
30      reinterpret_cast<ULARGE_INTEGER*>(&current_pos)));
31  // Seek to the end.
32  ASSERT_HRESULT_SUCCEEDED(s->Seek(zero, STREAM_SEEK_END, NULL));
33  ASSERT_HRESULT_SUCCEEDED(s->Write(buffer, cb, &bytes_written));
34  ASSERT_EQ(cb, bytes_written);
35  // Seek to original position.
36  ASSERT_HRESULT_SUCCEEDED(s->Seek(current_pos, STREAM_SEEK_SET, NULL));
37}
38
39class MockUrlDelegate : public PluginUrlRequestDelegate {
40 public:
41  MOCK_METHOD9(OnResponseStarted, void(int request_id, const char* mime_type,
42      const char* headers, int size, base::Time last_modified,
43      const std::string& redirect_url, int redirect_status,
44      const net::HostPortPair& socket_address, uint64 upload_size));
45  MOCK_METHOD2(OnReadComplete, void(int request_id, const std::string& data));
46  MOCK_METHOD2(OnResponseEnd, void(int request_id,
47                                   const net::URLRequestStatus& status));
48
49  void PostponeReadRequest(chrome_frame_test::TimedMsgLoop* loop,
50                   UrlmonUrlRequest* request, int bytes_to_read) {
51    loop->PostTask(FROM_HERE,
52                   base::Bind(&MockUrlDelegate::RequestRead,
53                              base::Unretained(this), request, bytes_to_read));
54  }
55
56 private:
57  void RequestRead(UrlmonUrlRequest* request, int bytes_to_read) {
58    request->Read(bytes_to_read);
59  }
60};
61
62// Simplest UrlmonUrlRequest. Retrieve a file from local web server.
63TEST(UrlmonUrlRequestTest, Simple1) {
64  MockUrlDelegate mock;
65  chrome_frame_test::TimedMsgLoop loop;
66
67  testing::StrictMock<MockWebServer> mock_server(1337,
68      ASCIIToWide(chrome_frame_test::GetLocalIPv4Address()),
69      chrome_frame_test::GetTestDataFolder());
70  mock_server.ExpectAndServeAnyRequests(CFInvocation(CFInvocation::NONE));
71
72  CComObjectStackEx<UrlmonUrlRequest> request;
73
74  request.AddRef();
75  request.Initialize(&mock, 1,  // request_id
76      WideToUTF8(mock_server.Resolve(L"chrome_frame_window_open.html")),
77      "get",
78      "",      // referrer
79      "",      // extra request
80      NULL,    // upload data
81      ResourceType::MAIN_FRAME,  // resource type
82      true,
83      0);   // frame busting
84
85  testing::InSequence s;
86  EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_,
87                                      testing::_, testing::_, testing::_,
88                                      testing::_, testing::_))
89    .Times(1)
90    .WillOnce(testing::IgnoreResult(testing::InvokeWithoutArgs(CreateFunctor(
91        &request, &UrlmonUrlRequest::Read, 512))));
92
93
94  EXPECT_CALL(mock, OnReadComplete(1, testing::Property(&std::string::size,
95                                                        testing::Gt(0u))))
96    .Times(testing::AtLeast(1))
97    .WillRepeatedly(testing::InvokeWithoutArgs(CreateFunctor(&mock,
98        &MockUrlDelegate::PostponeReadRequest, &loop, &request, 64)));
99
100  EXPECT_CALL(mock, OnResponseEnd(1, testing::_))
101    .Times(1)
102    .WillOnce(QUIT_LOOP_SOON(loop, base::TimeDelta::FromSeconds(2)));
103
104  request.Start();
105  loop.RunFor(kChromeFrameLongNavigationTimeout);
106  request.Release();
107}
108
109// Same as Simple1 except we use the HEAD verb to fetch only the headers
110// from the server.
111TEST(UrlmonUrlRequestTest, Head) {
112  MockUrlDelegate mock;
113  chrome_frame_test::TimedMsgLoop loop;
114  // Use SimpleWebServer instead of the python server to support HEAD
115  // requests.
116  test_server::SimpleWebServer server(13337);
117  test_server::SimpleResponse head_response("/head", "");
118  server.AddResponse(&head_response);
119
120  CComObjectStackEx<UrlmonUrlRequest> request;
121
122  request.AddRef();
123  request.Initialize(&mock, 1,  // request_id
124      base::StringPrintf("http://%s:13337/head", server.host().c_str()),
125      "head",
126      "",      // referrer
127      "",      // extra request
128      NULL,    // upload data
129      ResourceType::MAIN_FRAME,  // resource type
130      true,
131      0);   // frame busting
132
133  testing::InSequence s;
134  EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_,
135                                      testing::_, testing::_, testing::_,
136                                      testing::_, testing::_))
137    .Times(1)
138    .WillOnce(testing::IgnoreResult(testing::InvokeWithoutArgs(CreateFunctor(
139        &request, &UrlmonUrlRequest::Read, 512))));
140
141  // For HEAD requests we don't expect content reads.
142  EXPECT_CALL(mock, OnReadComplete(1, testing::_)).Times(0);
143
144  EXPECT_CALL(mock, OnResponseEnd(1, testing::_))
145    .Times(1)
146    .WillOnce(QUIT_LOOP_SOON(loop, base::TimeDelta::FromSeconds(2)));
147
148  request.Start();
149  loop.RunFor(kChromeFrameLongNavigationTimeout);
150  request.Release();
151}
152
153TEST(UrlmonUrlRequestTest, UnreachableUrl) {
154  MockUrlDelegate mock;
155  chrome_frame_test::TimedMsgLoop loop;
156  CComObjectStackEx<UrlmonUrlRequest> request;
157
158  testing::StrictMock<MockWebServer> mock_server(1337,
159      ASCIIToWide(chrome_frame_test::GetLocalIPv4Address()),
160      chrome_frame_test::GetTestDataFolder());
161  mock_server.ExpectAndServeAnyRequests(CFInvocation(CFInvocation::NONE));
162
163  GURL unreachable(WideToUTF8(mock_server.Resolve(
164      L"non_existing.html")));
165
166  request.AddRef();
167  request.Initialize(&mock, 1,  // request_id
168      unreachable.spec(), "get",
169      "",      // referrer
170      "",      // extra request
171      NULL,    // upload data
172      ResourceType::MAIN_FRAME,  // resource type
173      true,
174      0);   // frame busting
175
176  // Expect headers
177  EXPECT_CALL(mock, OnResponseStarted(1, testing::_,
178                                      testing::StartsWith("HTTP/1.1 404"),
179                                      testing::_, testing::_, testing::_,
180                                      testing::_, testing::_, testing::_))
181    .Times(1)
182    .WillOnce(QUIT_LOOP_SOON(loop, base::TimeDelta::FromSeconds(2)));
183
184  EXPECT_CALL(mock, OnResponseEnd(1, testing::Property(
185              &net::URLRequestStatus::error,
186              net::ERR_TUNNEL_CONNECTION_FAILED)))
187    .Times(testing::AtMost(1));
188
189  request.Start();
190  loop.RunFor(kChromeFrameLongNavigationTimeout);
191  request.Release();
192}
193
194TEST(UrlmonUrlRequestTest, ZeroLengthResponse) {
195  MockUrlDelegate mock;
196  chrome_frame_test::TimedMsgLoop loop;
197
198  testing::StrictMock<MockWebServer> mock_server(1337,
199      ASCIIToWide(chrome_frame_test::GetLocalIPv4Address()),
200      chrome_frame_test::GetTestDataFolder());
201  mock_server.ExpectAndServeAnyRequests(CFInvocation(CFInvocation::NONE));
202
203  CComObjectStackEx<UrlmonUrlRequest> request;
204
205  request.AddRef();
206  request.Initialize(&mock, 1,  // request_id
207      WideToUTF8(mock_server.Resolve(L"empty.html")), "get",
208      "",      // referrer
209      "",      // extra request
210      NULL,    // upload data
211      ResourceType::MAIN_FRAME,  // resource type
212      true,
213      0);   // frame busting
214
215  // Expect headers
216  EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_,
217                                      testing::_, testing::_, testing::_,
218                                      testing::_, testing::_))
219    .Times(1)
220    .WillOnce(QUIT_LOOP(loop));
221
222  request.Start();
223  loop.RunFor(kChromeFrameLongNavigationTimeout);
224  EXPECT_FALSE(loop.WasTimedOut());
225
226  // Should stay quiet, since we do not ask for anything for awhile.
227  EXPECT_CALL(mock, OnResponseEnd(1, testing::_)).Times(0);
228  loop.RunFor(base::TimeDelta::FromSeconds(3));
229
230  // Invoke read. Only now the response end ("server closed the connection")
231  // is supposed to be delivered.
232  EXPECT_CALL(mock, OnResponseEnd(1, testing::Property(
233      &net::URLRequestStatus::is_success, true))).Times(1);
234  request.Read(512);
235  request.Release();
236}
237
238ACTION_P4(ManagerRead, loop, mgr, request_id, bytes_to_read) {
239  loop->PostTask(FROM_HERE,
240                 base::Bind(&UrlmonUrlRequestManager::ReadUrlRequest,
241                            base::Unretained(mgr), request_id, bytes_to_read));
242}
243ACTION_P3(ManagerEndRequest, loop, mgr, request_id) {
244  loop->PostTask(FROM_HERE, base::Bind(&UrlmonUrlRequestManager::EndUrlRequest,
245                                       base::Unretained(mgr), request_id,
246                                       net::URLRequestStatus()));
247}
248
249// Simplest test - retrieve file from local web server.
250TEST(UrlmonUrlRequestManagerTest, Simple1) {
251  MockUrlDelegate mock;
252  chrome_frame_test::TimedMsgLoop loop;
253
254  testing::StrictMock<MockWebServer> mock_server(1337,
255      ASCIIToWide(chrome_frame_test::GetLocalIPv4Address()),
256      chrome_frame_test::GetTestDataFolder());
257  mock_server.ExpectAndServeAnyRequests(CFInvocation(CFInvocation::NONE));
258
259  scoped_ptr<UrlmonUrlRequestManager> mgr(new UrlmonUrlRequestManager());
260  mgr->set_delegate(&mock);
261  AutomationURLRequest r1;
262  r1.url =  WideToUTF8(mock_server.Resolve(L"chrome_frame_window_open.html"));
263  r1.method = "get";
264  r1.resource_type = 0;
265  r1.load_flags = 0;
266
267  EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_,
268                             testing::_, testing::_, testing::_, testing::_,
269                             testing::_))
270      .Times(1)
271      .WillOnce(ManagerRead(&loop, mgr.get(), 1, 512));
272
273  EXPECT_CALL(mock, OnReadComplete(1, testing::Property(&std::string::size,
274                                                        testing::Gt(0u))))
275    .Times(testing::AtLeast(1))
276    .WillRepeatedly(ManagerRead(&loop, mgr.get(), 1, 2));
277
278  EXPECT_CALL(mock, OnResponseEnd(1, testing::_))
279    .Times(1)
280    .WillOnce(QUIT_LOOP_SOON(loop, base::TimeDelta::FromSeconds(2)));
281
282  mgr->StartUrlRequest(1, r1);
283  loop.RunFor(kChromeFrameLongNavigationTimeout);
284  mgr.reset();
285}
286
287TEST(UrlmonUrlRequestManagerTest, Abort1) {
288  MockUrlDelegate mock;
289  chrome_frame_test::TimedMsgLoop loop;
290
291  testing::StrictMock<MockWebServer> mock_server(1337,
292      ASCIIToWide(chrome_frame_test::GetLocalIPv4Address()),
293      chrome_frame_test::GetTestDataFolder());
294  mock_server.ExpectAndServeAnyRequests(CFInvocation(CFInvocation::NONE));
295
296  scoped_ptr<UrlmonUrlRequestManager> mgr(new UrlmonUrlRequestManager());
297  mgr->set_delegate(&mock);
298  AutomationURLRequest r1;
299  r1.url = WideToUTF8(mock_server.Resolve(L"chrome_frame_window_open.html"));
300  r1.method = "get";
301  r1.resource_type = 0;
302  r1.load_flags = 0;
303
304  EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_,
305                               testing::_, testing::_, testing::_, testing::_,
306                               testing::_))
307    .Times(1)
308    .WillOnce(testing::DoAll(
309        ManagerEndRequest(&loop, mgr.get(), 1),
310        QUIT_LOOP_SOON(loop, base::TimeDelta::FromSeconds(3))));
311
312  EXPECT_CALL(mock, OnReadComplete(1, testing::_))
313    .Times(0);
314
315  EXPECT_CALL(mock, OnResponseEnd(1, testing::_))
316    .Times(0);
317
318  mgr->StartUrlRequest(1, r1);
319  loop.RunFor(kChromeFrameLongNavigationTimeout);
320  mgr.reset();
321}
322