buffered_resource_loader_unittest.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2010 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 <algorithm>
6
7#include "base/format_macros.h"
8#include "base/stringprintf.h"
9#include "net/base/net_errors.h"
10#include "net/http/http_util.h"
11#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
12#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrameClient.h"
13#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h"
14#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h"
15#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
16#include "webkit/glue/media/buffered_resource_loader.h"
17#include "webkit/mocks/mock_webframe.h"
18#include "webkit/mocks/mock_weburlloader.h"
19
20using ::testing::_;
21using ::testing::Assign;
22using ::testing::AtLeast;
23using ::testing::DeleteArg;
24using ::testing::DoAll;
25using ::testing::InSequence;
26using ::testing::Invoke;
27using ::testing::InvokeWithoutArgs;
28using ::testing::NotNull;
29using ::testing::Return;
30using ::testing::ReturnRef;
31using ::testing::SetArgumentPointee;
32using ::testing::StrictMock;
33using ::testing::NiceMock;
34using ::testing::WithArgs;
35
36using WebKit::WebURLError;
37using WebKit::WebFrameClient;
38using WebKit::WebURLResponse;
39using WebKit::WebView;
40
41namespace {
42
43const char* kHttpUrl = "http://test";
44const char kHttpRedirectToSameDomainUrl1[] = "http://test/ing";
45const char kHttpRedirectToSameDomainUrl2[] = "http://test/ing2";
46const char kHttpRedirectToDifferentDomainUrl1[] = "http://test2";
47const char kHttpRedirectToDifferentDomainUrl2[] = "http://test2/ing";
48
49const int kDataSize = 1024;
50const int kHttpOK = 200;
51const int kHttpPartialContent = 206;
52
53enum NetworkState {
54  NONE,
55  LOADED,
56  LOADING
57};
58
59}  // namespace
60
61namespace webkit_glue {
62
63// Submit a request completed event to the resource loader due to request
64// being canceled. Pretending the event is from external.
65ACTION_P(RequestCanceled, loader) {
66  WebURLError error;
67  error.reason = net::ERR_ABORTED;
68  error.domain = WebString::fromUTF8(net::kErrorDomain);
69  loader->didFail(NULL, error);
70}
71
72class BufferedResourceLoaderTest : public testing::Test {
73 public:
74  BufferedResourceLoaderTest() {
75    for (int i = 0; i < kDataSize; ++i)
76      data_[i] = i;
77  }
78
79  virtual ~BufferedResourceLoaderTest() {
80  }
81
82  void Initialize(const char* url, int first_position, int last_position) {
83    gurl_ = GURL(url);
84    first_position_ = first_position;
85    last_position_ = last_position;
86
87    frame_.reset(new NiceMock<MockWebFrame>());
88
89    url_loader_ = new NiceMock<MockWebURLLoader>();
90    loader_ = new BufferedResourceLoader(gurl_,
91                                         first_position_, last_position_);
92    loader_->SetURLLoaderForTest(url_loader_);
93  }
94
95  void SetLoaderBuffer(size_t forward_capacity, size_t backward_capacity) {
96    loader_->buffer_.reset(
97        new media::SeekableBuffer(backward_capacity, forward_capacity));
98  }
99
100  void Start() {
101    InSequence s;
102    EXPECT_CALL(*url_loader_, loadAsynchronously(_, loader_.get()));
103    loader_->Start(
104        NewCallback(this, &BufferedResourceLoaderTest::StartCallback),
105        NewCallback(this, &BufferedResourceLoaderTest::NetworkCallback),
106        frame_.get());
107  }
108
109  void FullResponse(int64 instance_size) {
110    EXPECT_CALL(*this, StartCallback(net::OK));
111
112    WebURLResponse response(gurl_);
113    response.setHTTPHeaderField(WebString::fromUTF8("Content-Length"),
114                                WebString::fromUTF8(base::StringPrintf("%"
115                                    PRId64, instance_size)));
116    response.setExpectedContentLength(instance_size);
117    response.setHTTPStatusCode(kHttpOK);
118    loader_->didReceiveResponse(url_loader_, response);
119    EXPECT_EQ(instance_size, loader_->content_length());
120    EXPECT_EQ(instance_size, loader_->instance_size());
121    EXPECT_FALSE(loader_->partial_response());
122  }
123
124  void PartialResponse(int64 first_position, int64 last_position,
125                       int64 instance_size) {
126    EXPECT_CALL(*this, StartCallback(net::OK));
127    int64 content_length = last_position - first_position + 1;
128
129    WebURLResponse response(gurl_);
130    response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"),
131                                WebString::fromUTF8(base::StringPrintf("bytes "
132                                            "%" PRId64 "-%" PRId64 "/%" PRId64,
133                                            first_position,
134                                            last_position,
135                                            instance_size)));
136    response.setExpectedContentLength(content_length);
137    response.setHTTPStatusCode(kHttpPartialContent);
138    loader_->didReceiveResponse(url_loader_, response);
139    EXPECT_EQ(content_length, loader_->content_length());
140    EXPECT_EQ(instance_size, loader_->instance_size());
141    EXPECT_TRUE(loader_->partial_response());
142  }
143
144  void Redirect(const char* url) {
145    GURL redirectUrl(url);
146    WebKit::WebURLRequest newRequest(redirectUrl);
147    WebKit::WebURLResponse redirectResponse(gurl_);
148
149    loader_->willSendRequest(url_loader_, newRequest, redirectResponse);
150
151    MessageLoop::current()->RunAllPending();
152  }
153
154  void StopWhenLoad() {
155    InSequence s;
156    EXPECT_CALL(*url_loader_, cancel())
157        .WillOnce(RequestCanceled(loader_));
158    loader_->Stop();
159    loader_ = NULL;
160  }
161
162  // Helper method to write to |loader_| from |data_|.
163  void WriteLoader(int position, int size) {
164    EXPECT_CALL(*this, NetworkCallback())
165        .RetiresOnSaturation();
166    loader_->didReceiveData(url_loader_,
167                            reinterpret_cast<char*>(data_ + position), size);
168  }
169
170  // Helper method to read from |loader_|.
171  void ReadLoader(int64 position, int size, uint8* buffer) {
172    loader_->Read(position, size, buffer,
173                  NewCallback(this, &BufferedResourceLoaderTest::ReadCallback));
174  }
175
176  // Verifis that data in buffer[0...size] is equal to data_[pos...pos+size].
177  void VerifyBuffer(uint8* buffer, int pos, int size) {
178    EXPECT_EQ(0, memcmp(buffer, data_ + pos, size));
179  }
180
181  // Helper method to disallow deferring in |loader_|.
182  void DisallowLoaderDefer() {
183    if (loader_->deferred_) {
184      EXPECT_CALL(*url_loader_, setDefersLoading(false));
185      EXPECT_CALL(*this, NetworkCallback());
186    }
187    loader_->SetAllowDefer(false);
188  }
189
190  // Helper method to allow deferring in |loader_|.
191  void AllowLoaderDefer() {
192    loader_->SetAllowDefer(true);
193  }
194
195  MOCK_METHOD1(StartCallback, void(int error));
196  MOCK_METHOD1(ReadCallback, void(int error));
197  MOCK_METHOD0(NetworkCallback, void());
198
199 protected:
200  GURL gurl_;
201  int64 first_position_;
202  int64 last_position_;
203
204  scoped_refptr<BufferedResourceLoader> loader_;
205  NiceMock<MockWebURLLoader>* url_loader_;
206  scoped_ptr<NiceMock<MockWebFrame> > frame_;
207
208  uint8 data_[kDataSize];
209
210 private:
211  DISALLOW_COPY_AND_ASSIGN(BufferedResourceLoaderTest);
212};
213
214TEST_F(BufferedResourceLoaderTest, StartStop) {
215  Initialize(kHttpUrl, -1, -1);
216  Start();
217  StopWhenLoad();
218}
219
220// Tests that a bad HTTP response is recived, e.g. file not found.
221TEST_F(BufferedResourceLoaderTest, BadHttpResponse) {
222  Initialize(kHttpUrl, -1, -1);
223  Start();
224
225  EXPECT_CALL(*this, StartCallback(net::ERR_FAILED));
226  EXPECT_CALL(*url_loader_, cancel())
227      .WillOnce(RequestCanceled(loader_));
228
229  WebURLResponse response(gurl_);
230  response.setHTTPStatusCode(404);
231  response.setHTTPStatusText("Not Found\n");
232  loader_->didReceiveResponse(url_loader_, response);
233}
234
235// Tests that partial content is requested but not fulfilled.
236TEST_F(BufferedResourceLoaderTest, NotPartialResponse) {
237  Initialize(kHttpUrl, 100, -1);
238  Start();
239  FullResponse(1024);
240  StopWhenLoad();
241}
242
243// Tests that a 200 response is received.
244TEST_F(BufferedResourceLoaderTest, FullResponse) {
245  Initialize(kHttpUrl, -1, -1);
246  Start();
247  FullResponse(1024);
248  StopWhenLoad();
249}
250
251// Tests that a partial content response is received.
252TEST_F(BufferedResourceLoaderTest, PartialResponse) {
253  Initialize(kHttpUrl, 100, 200);
254  Start();
255  PartialResponse(100, 200, 1024);
256  StopWhenLoad();
257}
258
259// Tests that an invalid partial response is received.
260TEST_F(BufferedResourceLoaderTest, InvalidPartialResponse) {
261  Initialize(kHttpUrl, 0, 10);
262  Start();
263
264  EXPECT_CALL(*this, StartCallback(net::ERR_INVALID_RESPONSE));
265  EXPECT_CALL(*url_loader_, cancel())
266      .WillOnce(RequestCanceled(loader_));
267
268  WebURLResponse response(gurl_);
269  response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"),
270                              WebString::fromUTF8(base::StringPrintf("bytes "
271                                  "%d-%d/%d", 1, 10, 1024)));
272  response.setExpectedContentLength(10);
273  response.setHTTPStatusCode(kHttpPartialContent);
274  loader_->didReceiveResponse(url_loader_, response);
275}
276
277// Tests the logic of sliding window for data buffering and reading.
278TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
279  Initialize(kHttpUrl, 10, 29);
280  Start();
281  PartialResponse(10, 29, 30);
282
283  uint8 buffer[10];
284  InSequence s;
285
286  // Writes 10 bytes and read them back.
287  WriteLoader(10, 10);
288  EXPECT_CALL(*this, ReadCallback(10));
289  ReadLoader(10, 10, buffer);
290  VerifyBuffer(buffer, 10, 10);
291
292  // Writes 10 bytes and read 2 times.
293  WriteLoader(20, 10);
294  EXPECT_CALL(*this, ReadCallback(5));
295  ReadLoader(20, 5, buffer);
296  VerifyBuffer(buffer, 20, 5);
297  EXPECT_CALL(*this, ReadCallback(5));
298  ReadLoader(25, 5, buffer);
299  VerifyBuffer(buffer, 25, 5);
300
301  // Read backward within buffer.
302  EXPECT_CALL(*this, ReadCallback(10));
303  ReadLoader(10, 10, buffer);
304  VerifyBuffer(buffer, 10, 10);
305
306  // Read backward outside buffer.
307  EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
308  ReadLoader(9, 10, buffer);
309
310  // Response has completed.
311  EXPECT_CALL(*this, NetworkCallback());
312  loader_->didFinishLoading(url_loader_, 0);
313
314  // Try to read 10 from position 25 will just return with 5 bytes.
315  EXPECT_CALL(*this, ReadCallback(5));
316  ReadLoader(25, 10, buffer);
317  VerifyBuffer(buffer, 25, 5);
318
319  // Try to read outside buffered range after request has completed.
320  EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
321  ReadLoader(5, 10, buffer);
322
323  // Try to read beyond the instance size.
324  EXPECT_CALL(*this, ReadCallback(0));
325  ReadLoader(30, 10, buffer);
326}
327
328TEST_F(BufferedResourceLoaderTest, ReadOutsideBuffer) {
329  Initialize(kHttpUrl, 10, 0x00FFFFFF);
330  Start();
331  PartialResponse(10, 0x00FFFFFF, 0x01000000);
332
333  uint8 buffer[10];
334  InSequence s;
335
336  // Read very far aheard will get a cache miss.
337  EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
338  ReadLoader(0x00FFFFFF, 1, buffer);
339
340  // The following call will not call ReadCallback() because it is waiting for
341  // data to arrive.
342  ReadLoader(10, 10, buffer);
343
344  // Writing to loader will fulfill the read request.
345  EXPECT_CALL(*this, ReadCallback(10));
346  WriteLoader(10, 20);
347  VerifyBuffer(buffer, 10, 10);
348
349  // The following call cannot be fulfilled now.
350  ReadLoader(25, 10, buffer);
351
352  EXPECT_CALL(*this, ReadCallback(5));
353  EXPECT_CALL(*this, NetworkCallback());
354  loader_->didFinishLoading(url_loader_, 0);
355}
356
357TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) {
358  Initialize(kHttpUrl, 10, 29);
359  Start();
360  PartialResponse(10, 29, 30);
361
362  uint8 buffer[10];
363  InSequence s;
364
365  ReadLoader(10, 10, buffer);
366  EXPECT_CALL(*this, ReadCallback(net::ERR_FAILED));
367  EXPECT_CALL(*this, NetworkCallback());
368  WebURLError error;
369  error.reason = net::ERR_FAILED;
370  loader_->didFail(url_loader_, error);
371}
372
373// Tests the logic of caching data to disk when media is paused.
374TEST_F(BufferedResourceLoaderTest, AllowDefer_NoDataReceived) {
375  Initialize(kHttpUrl, 10, 99);
376  SetLoaderBuffer(10, 20);
377  Start();
378  PartialResponse(10, 99, 100);
379
380  // Start in undeferred state, then disallow defer, then allow defer
381  // without receiving data in between.
382  DisallowLoaderDefer();
383  AllowLoaderDefer();
384  StopWhenLoad();
385}
386
387TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadSameWindow) {
388  Initialize(kHttpUrl, 10, 99);
389  SetLoaderBuffer(10, 20);
390  Start();
391  PartialResponse(10, 99, 100);
392
393  uint8 buffer[10];
394
395  // Start in undeferred state, disallow defer, receive data but don't shift
396  // buffer window, then allow defer and read.
397  DisallowLoaderDefer();
398  WriteLoader(10, 10);
399  AllowLoaderDefer();
400
401  EXPECT_CALL(*this, ReadCallback(10));
402  ReadLoader(10, 10, buffer);
403  VerifyBuffer(buffer, 10, 10);
404  StopWhenLoad();
405}
406
407TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadPastWindow) {
408  Initialize(kHttpUrl, 10, 99);
409  SetLoaderBuffer(10, 20);
410  Start();
411  PartialResponse(10, 99, 100);
412
413  uint8 buffer[10];
414
415  // Not deferred, disallow defer, received data and shift buffer window,
416  // allow defer, then read in area outside of buffer window.
417  DisallowLoaderDefer();
418  WriteLoader(10, 10);
419  WriteLoader(20, 50);
420  AllowLoaderDefer();
421
422  EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
423  ReadLoader(10, 10, buffer);
424  StopWhenLoad();
425}
426
427TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredNoDataReceived) {
428  Initialize(kHttpUrl, 10, 99);
429  SetLoaderBuffer(10, 20);
430  Start();
431  PartialResponse(10, 99, 100);
432
433  uint8 buffer[10];
434
435  // Start in deferred state, then disallow defer, receive no data, and
436  // allow defer and read.
437  EXPECT_CALL(*url_loader_, setDefersLoading(true));
438  EXPECT_CALL(*this, NetworkCallback());
439  WriteLoader(10, 40);
440
441  DisallowLoaderDefer();
442  AllowLoaderDefer();
443
444  EXPECT_CALL(*this, ReadCallback(10));
445  ReadLoader(20, 10, buffer);
446  VerifyBuffer(buffer, 20, 10);
447  StopWhenLoad();
448}
449
450TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadSameWindow) {
451  Initialize(kHttpUrl, 10, 99);
452  SetLoaderBuffer(10, 20);
453  Start();
454  PartialResponse(10, 99, 100);
455
456  uint8 buffer[10];
457
458  // Start in deferred state, disallow defer, receive data and shift buffer
459  // window, allow defer, and read in a place that's still in the window.
460  EXPECT_CALL(*url_loader_, setDefersLoading(true));
461  EXPECT_CALL(*this, NetworkCallback());
462  WriteLoader(10, 30);
463
464  DisallowLoaderDefer();
465  WriteLoader(40, 5);
466  AllowLoaderDefer();
467
468  EXPECT_CALL(*this, ReadCallback(10));
469  ReadLoader(20, 10, buffer);
470  VerifyBuffer(buffer, 20, 10);
471  StopWhenLoad();
472}
473
474TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadPastWindow) {
475  Initialize(kHttpUrl, 10, 99);
476  SetLoaderBuffer(10, 20);
477  Start();
478  PartialResponse(10, 99, 100);
479
480  uint8 buffer[10];
481
482  // Start in deferred state, disallow defer, receive data and shift buffer
483  // window, allow defer, and read outside of the buffer window.
484  EXPECT_CALL(*url_loader_, setDefersLoading(true));
485  EXPECT_CALL(*this, NetworkCallback());
486  WriteLoader(10, 40);
487
488  DisallowLoaderDefer();
489  WriteLoader(50, 20);
490  WriteLoader(70, 40);
491  AllowLoaderDefer();
492
493  EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
494  ReadLoader(20, 5, buffer);
495  StopWhenLoad();
496}
497
498// NOTE: This test will need to be reworked a little once
499// http://code.google.com/p/chromium/issues/detail?id=72578
500// is fixed.
501TEST_F(BufferedResourceLoaderTest, HasSingleOrigin) {
502  // Make sure no redirect case works as expected.
503  Initialize(kHttpUrl, -1, -1);
504  Start();
505  FullResponse(1024);
506  EXPECT_TRUE(loader_->HasSingleOrigin());
507  StopWhenLoad();
508
509  // Test redirect to the same domain.
510  Initialize(kHttpUrl, -1, -1);
511  Start();
512  Redirect(kHttpRedirectToSameDomainUrl1);
513  FullResponse(1024);
514  EXPECT_TRUE(loader_->HasSingleOrigin());
515  StopWhenLoad();
516
517  // Test redirect twice to the same domain.
518  Initialize(kHttpUrl, -1, -1);
519  Start();
520  Redirect(kHttpRedirectToSameDomainUrl1);
521  Redirect(kHttpRedirectToSameDomainUrl2);
522  FullResponse(1024);
523  EXPECT_TRUE(loader_->HasSingleOrigin());
524  StopWhenLoad();
525
526  // Test redirect to a different domain.
527  Initialize(kHttpUrl, -1, -1);
528  Start();
529  Redirect(kHttpRedirectToDifferentDomainUrl1);
530  FullResponse(1024);
531  EXPECT_FALSE(loader_->HasSingleOrigin());
532  StopWhenLoad();
533
534  // Test redirect twice to a different domain.
535  Initialize(kHttpUrl, -1, -1);
536  Start();
537  Redirect(kHttpRedirectToDifferentDomainUrl1);
538  Redirect(kHttpRedirectToDifferentDomainUrl2);
539  FullResponse(1024);
540  EXPECT_FALSE(loader_->HasSingleOrigin());
541  StopWhenLoad();
542
543  // Test to a different domain and then back to the same domain.
544  // NOTE: A different origin was encountered at least once so that
545  //       makes HasSingleOrigin() become false.
546  Initialize(kHttpUrl, -1, -1);
547  Start();
548  Redirect(kHttpRedirectToDifferentDomainUrl1);
549  Redirect(kHttpRedirectToSameDomainUrl1);
550  FullResponse(1024);
551  EXPECT_FALSE(loader_->HasSingleOrigin());
552  StopWhenLoad();
553}
554
555// TODO(hclam): add unit test for defer loading.
556
557}  // namespace webkit_glue
558