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