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/test/test_timeouts.h"
      8 #include "media/base/mock_callback.h"
      9 #include "media/base/mock_filter_host.h"
     10 #include "media/base/mock_filters.h"
     11 #include "net/base/net_errors.h"
     12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h"
     13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h"
     14 #include "webkit/glue/media/buffered_data_source.h"
     15 #include "webkit/mocks/mock_webframe.h"
     16 
     17 using ::testing::_;
     18 using ::testing::Assign;
     19 using ::testing::AtLeast;
     20 using ::testing::DeleteArg;
     21 using ::testing::DoAll;
     22 using ::testing::InSequence;
     23 using ::testing::Invoke;
     24 using ::testing::InvokeWithoutArgs;
     25 using ::testing::NotNull;
     26 using ::testing::Return;
     27 using ::testing::ReturnRef;
     28 using ::testing::SetArgumentPointee;
     29 using ::testing::StrictMock;
     30 using ::testing::NiceMock;
     31 using ::testing::WithArgs;
     32 
     33 namespace webkit_glue {
     34 
     35 static const char* kHttpUrl = "http://test";
     36 static const char* kFileUrl = "file://test";
     37 static const int kDataSize = 1024;
     38 
     39 enum NetworkState {
     40   NONE,
     41   LOADED,
     42   LOADING
     43 };
     44 
     45 // A mock BufferedDataSource to inject mock BufferedResourceLoader through
     46 // CreateResourceLoader() method.
     47 class MockBufferedDataSource : public BufferedDataSource {
     48  public:
     49   MockBufferedDataSource(MessageLoop* message_loop, WebFrame* frame)
     50       : BufferedDataSource(message_loop, frame) {
     51   }
     52 
     53   virtual base::TimeDelta GetTimeoutMilliseconds() {
     54     return base::TimeDelta::FromMilliseconds(
     55                             TestTimeouts::tiny_timeout_ms());
     56   }
     57 
     58   MOCK_METHOD2(CreateResourceLoader,
     59                BufferedResourceLoader*(int64 first_position,
     60                                        int64 last_position));
     61 
     62  private:
     63   DISALLOW_COPY_AND_ASSIGN(MockBufferedDataSource);
     64 };
     65 
     66 class MockBufferedResourceLoader : public BufferedResourceLoader {
     67  public:
     68   MockBufferedResourceLoader() : BufferedResourceLoader(GURL(), 0, 0) {
     69   }
     70 
     71   MOCK_METHOD3(Start, void(net::CompletionCallback* read_callback,
     72                            NetworkEventCallback* network_callback,
     73                            WebFrame* frame));
     74   MOCK_METHOD0(Stop, void());
     75   MOCK_METHOD4(Read, void(int64 position, int read_size, uint8* buffer,
     76                           net::CompletionCallback* callback));
     77   MOCK_METHOD0(content_length, int64());
     78   MOCK_METHOD0(instance_size, int64());
     79   MOCK_METHOD0(range_supported, bool());
     80   MOCK_METHOD0(network_activity, bool());
     81   MOCK_METHOD0(url, const GURL&());
     82   MOCK_METHOD0(GetBufferedFirstBytePosition, int64());
     83   MOCK_METHOD0(GetBufferedLastBytePosition, int64());
     84 
     85  protected:
     86   ~MockBufferedResourceLoader() {}
     87 
     88   DISALLOW_COPY_AND_ASSIGN(MockBufferedResourceLoader);
     89 };
     90 
     91 class BufferedDataSourceTest : public testing::Test {
     92  public:
     93   BufferedDataSourceTest() {
     94     message_loop_ = MessageLoop::current();
     95 
     96     // Prepare test data.
     97     for (size_t i = 0; i < sizeof(data_); ++i) {
     98       data_[i] = i;
     99     }
    100   }
    101 
    102   virtual ~BufferedDataSourceTest() {
    103   }
    104 
    105   void ExpectCreateAndStartResourceLoader(int start_error) {
    106     EXPECT_CALL(*data_source_, CreateResourceLoader(_, _))
    107         .WillOnce(Return(loader_.get()));
    108 
    109     EXPECT_CALL(*loader_, Start(NotNull(), NotNull(), NotNull()))
    110         .WillOnce(
    111             DoAll(Assign(&error_, start_error),
    112                   Invoke(this,
    113                          &BufferedDataSourceTest::InvokeStartCallback)));
    114   }
    115 
    116   void InitializeDataSource(const char* url, int error,
    117                             bool partial_response, int64 instance_size,
    118                             NetworkState networkState) {
    119     // Saves the url first.
    120     gurl_ = GURL(url);
    121 
    122     frame_.reset(new NiceMock<MockWebFrame>());
    123 
    124     data_source_ = new MockBufferedDataSource(MessageLoop::current(),
    125                                               frame_.get());
    126     data_source_->set_host(&host_);
    127 
    128     scoped_refptr<NiceMock<MockBufferedResourceLoader> > first_loader(
    129         new NiceMock<MockBufferedResourceLoader>());
    130 
    131     // Creates the mock loader to be injected.
    132     loader_ = first_loader;
    133 
    134     bool initialized_ok = (error == net::OK);
    135     bool loaded = networkState == LOADED;
    136     {
    137       InSequence s;
    138       ExpectCreateAndStartResourceLoader(error);
    139 
    140       // In the case of an invalid partial response we expect a second loader
    141       // to be created.
    142       if (partial_response && (error == net::ERR_INVALID_RESPONSE)) {
    143         // Verify that the initial loader is stopped.
    144         EXPECT_CALL(*loader_, url())
    145             .WillRepeatedly(ReturnRef(gurl_));
    146         EXPECT_CALL(*loader_, Stop());
    147 
    148         // Replace loader_ with a new instance.
    149         loader_ = new NiceMock<MockBufferedResourceLoader>();
    150 
    151         // Create and start. Make sure Start() is called on the new loader.
    152         ExpectCreateAndStartResourceLoader(net::OK);
    153 
    154         // Update initialization variable since we know the second loader will
    155         // return OK.
    156         initialized_ok = true;
    157       }
    158     }
    159 
    160     // Attach a static function that deletes the memory referred by the
    161     // "callback" parameter.
    162     ON_CALL(*loader_, Read(_, _, _ , _))
    163         .WillByDefault(DeleteArg<3>());
    164 
    165     ON_CALL(*loader_, instance_size())
    166         .WillByDefault(Return(instance_size));
    167 
    168     // range_supported() return true if we expect to get a partial response.
    169     ON_CALL(*loader_, range_supported())
    170         .WillByDefault(Return(partial_response));
    171 
    172     ON_CALL(*loader_, url())
    173         .WillByDefault(ReturnRef(gurl_));
    174     media::PipelineStatus expected_init_status = media::PIPELINE_OK;
    175     if (initialized_ok) {
    176       // Expected loaded or not.
    177       EXPECT_CALL(host_, SetLoaded(loaded));
    178 
    179       // TODO(hclam): The condition for streaming needs to be adjusted.
    180       if (instance_size != -1 && (loaded || partial_response)) {
    181         EXPECT_CALL(host_, SetTotalBytes(instance_size));
    182         if (loaded)
    183           EXPECT_CALL(host_, SetBufferedBytes(instance_size));
    184         else
    185           EXPECT_CALL(host_, SetBufferedBytes(0));
    186       } else {
    187         EXPECT_CALL(host_, SetStreaming(true));
    188       }
    189     } else {
    190       expected_init_status = media::PIPELINE_ERROR_NETWORK;
    191       EXPECT_CALL(*loader_, Stop());
    192     }
    193 
    194     // Actual initialization of the data source.
    195     data_source_->Initialize(url,
    196         media::NewExpectedStatusCallback(expected_init_status));
    197     message_loop_->RunAllPending();
    198 
    199     if (initialized_ok) {
    200       // Verify the size of the data source.
    201       int64 size;
    202       if (instance_size != -1 && (loaded || partial_response)) {
    203         EXPECT_TRUE(data_source_->GetSize(&size));
    204         EXPECT_EQ(instance_size, size);
    205       } else {
    206         EXPECT_TRUE(data_source_->IsStreaming());
    207       }
    208     }
    209   }
    210 
    211   void StopDataSource() {
    212     if (loader_) {
    213       InSequence s;
    214       EXPECT_CALL(*loader_, Stop());
    215     }
    216 
    217     data_source_->Stop(media::NewExpectedCallback());
    218     message_loop_->RunAllPending();
    219   }
    220 
    221   void InvokeStartCallback(
    222       net::CompletionCallback* callback,
    223       BufferedResourceLoader::NetworkEventCallback* network_callback,
    224       WebFrame* frame) {
    225     callback->RunWithParams(Tuple1<int>(error_));
    226     delete callback;
    227     // TODO(hclam): Save this callback.
    228     delete network_callback;
    229   }
    230 
    231   void InvokeReadCallback(int64 position, int size, uint8* buffer,
    232                           net::CompletionCallback* callback) {
    233     if (error_ > 0)
    234       memcpy(buffer, data_ + static_cast<int>(position), error_);
    235     callback->RunWithParams(Tuple1<int>(error_));
    236     delete callback;
    237   }
    238 
    239   void ReadDataSourceHit(int64 position, int size, int read_size) {
    240     EXPECT_TRUE(loader_);
    241 
    242     InSequence s;
    243     // Expect the read is delegated to the resource loader.
    244     EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull()))
    245         .WillOnce(DoAll(Assign(&error_, read_size),
    246                         Invoke(this,
    247                                &BufferedDataSourceTest::InvokeReadCallback)));
    248 
    249     // The read has succeeded, so read callback will be called.
    250     EXPECT_CALL(*this, ReadCallback(read_size));
    251 
    252     data_source_->Read(
    253         position, size, buffer_,
    254         NewCallback(this, &BufferedDataSourceTest::ReadCallback));
    255     message_loop_->RunAllPending();
    256 
    257     // Make sure data is correct.
    258     EXPECT_EQ(0,
    259               memcmp(buffer_, data_ + static_cast<int>(position), read_size));
    260   }
    261 
    262   void ReadDataSourceHang(int64 position, int size) {
    263     EXPECT_TRUE(loader_);
    264 
    265     // Expect a call to read, but the call never returns.
    266     EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull()));
    267     data_source_->Read(
    268         position, size, buffer_,
    269         NewCallback(this, &BufferedDataSourceTest::ReadCallback));
    270     message_loop_->RunAllPending();
    271 
    272     // Now expect the read to return after aborting the data source.
    273     EXPECT_CALL(*this, ReadCallback(_));
    274     EXPECT_CALL(*loader_, Stop());
    275     data_source_->Abort();
    276     message_loop_->RunAllPending();
    277 
    278     // The loader has now been stopped. Set this to null so that when the
    279     // DataSource is stopped, it does not expect a call to stop the loader.
    280     loader_ = NULL;
    281   }
    282 
    283   void ReadDataSourceMiss(int64 position, int size, int start_error) {
    284     EXPECT_TRUE(loader_);
    285 
    286     // 1. Reply with a cache miss for the read.
    287     {
    288       InSequence s;
    289       EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull()))
    290           .WillOnce(DoAll(Assign(&error_, net::ERR_CACHE_MISS),
    291                           Invoke(this,
    292                                  &BufferedDataSourceTest::InvokeReadCallback)));
    293       EXPECT_CALL(*loader_, Stop());
    294     }
    295 
    296     // 2. Then the current loader will be stop and destroyed.
    297     NiceMock<MockBufferedResourceLoader> *new_loader =
    298         new NiceMock<MockBufferedResourceLoader>();
    299     EXPECT_CALL(*data_source_, CreateResourceLoader(position, -1))
    300         .WillOnce(Return(new_loader));
    301 
    302     // 3. Then the new loader will be started.
    303     EXPECT_CALL(*new_loader, Start(NotNull(), NotNull(), NotNull()))
    304         .WillOnce(DoAll(Assign(&error_, start_error),
    305                         Invoke(this,
    306                                &BufferedDataSourceTest::InvokeStartCallback)));
    307 
    308     if (start_error == net::OK) {
    309       EXPECT_CALL(*new_loader, range_supported())
    310           .WillRepeatedly(Return(loader_->range_supported()));
    311 
    312       // 4a. Then again a read request is made to the new loader.
    313       EXPECT_CALL(*new_loader, Read(position, size, NotNull(), NotNull()))
    314           .WillOnce(DoAll(Assign(&error_, size),
    315                           Invoke(this,
    316                                  &BufferedDataSourceTest::InvokeReadCallback)));
    317 
    318       EXPECT_CALL(*this, ReadCallback(size));
    319     } else {
    320       // 4b. The read callback is called with an error because Start() on the
    321       // new loader returned an error.
    322       EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
    323     }
    324 
    325     data_source_->Read(
    326         position, size, buffer_,
    327         NewCallback(this, &BufferedDataSourceTest::ReadCallback));
    328     message_loop_->RunAllPending();
    329 
    330     // Make sure data is correct.
    331     if (start_error == net::OK)
    332       EXPECT_EQ(0, memcmp(buffer_, data_ + static_cast<int>(position), size));
    333 
    334     loader_ = new_loader;
    335   }
    336 
    337   void ReadDataSourceFailed(int64 position, int size, int error) {
    338     EXPECT_TRUE(loader_);
    339 
    340     // 1. Expect the read is delegated to the resource loader.
    341     EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull()))
    342         .WillOnce(DoAll(Assign(&error_, error),
    343                         Invoke(this,
    344                                &BufferedDataSourceTest::InvokeReadCallback)));
    345 
    346     // 2. Host will then receive an error.
    347     EXPECT_CALL(*loader_, Stop());
    348 
    349     // 3. The read has failed, so read callback will be called.
    350     EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
    351 
    352     data_source_->Read(
    353         position, size, buffer_,
    354         NewCallback(this, &BufferedDataSourceTest::ReadCallback));
    355 
    356     message_loop_->RunAllPending();
    357   }
    358 
    359   void ReadDataSourceTimesOut(int64 position, int size) {
    360     // 1. Drop the request and let it times out.
    361     {
    362       InSequence s;
    363       EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull()))
    364           .WillOnce(DeleteArg<3>());
    365       EXPECT_CALL(*loader_, Stop());
    366     }
    367 
    368     // 2. Then the current loader will be stop and destroyed.
    369     NiceMock<MockBufferedResourceLoader> *new_loader =
    370         new NiceMock<MockBufferedResourceLoader>();
    371     EXPECT_CALL(*data_source_, CreateResourceLoader(position, -1))
    372         .WillOnce(Return(new_loader));
    373 
    374     // 3. Then the new loader will be started and respond to queries about
    375     //    whether this is a partial response using the value of the previous
    376     //    loader.
    377     EXPECT_CALL(*new_loader, Start(NotNull(), NotNull(), NotNull()))
    378         .WillOnce(DoAll(Assign(&error_, net::OK),
    379                         Invoke(this,
    380                                &BufferedDataSourceTest::InvokeStartCallback)));
    381     EXPECT_CALL(*new_loader, range_supported())
    382         .WillRepeatedly(Return(loader_->range_supported()));
    383 
    384     // 4. Then again a read request is made to the new loader.
    385     EXPECT_CALL(*new_loader, Read(position, size, NotNull(), NotNull()))
    386         .WillOnce(DoAll(Assign(&error_, size),
    387                         Invoke(this,
    388                                &BufferedDataSourceTest::InvokeReadCallback),
    389                         InvokeWithoutArgs(message_loop_,
    390                                           &MessageLoop::Quit)));
    391 
    392     EXPECT_CALL(*this, ReadCallback(size));
    393 
    394     data_source_->Read(
    395         position, size, buffer_,
    396         NewCallback(this, &BufferedDataSourceTest::ReadCallback));
    397 
    398     // This blocks the current thread until the watch task is executed and
    399     // triggers a read callback to quit this message loop.
    400     message_loop_->Run();
    401 
    402     // Make sure data is correct.
    403     EXPECT_EQ(0, memcmp(buffer_, data_ + static_cast<int>(position), size));
    404 
    405     loader_ = new_loader;
    406   }
    407 
    408   MOCK_METHOD1(ReadCallback, void(size_t size));
    409 
    410   scoped_refptr<NiceMock<MockBufferedResourceLoader> > loader_;
    411   scoped_refptr<MockBufferedDataSource> data_source_;
    412   scoped_ptr<NiceMock<MockWebFrame> > frame_;
    413 
    414   StrictMock<media::MockFilterHost> host_;
    415   GURL gurl_;
    416   MessageLoop* message_loop_;
    417 
    418   int error_;
    419   uint8 buffer_[1024];
    420   uint8 data_[1024];
    421 
    422  private:
    423   DISALLOW_COPY_AND_ASSIGN(BufferedDataSourceTest);
    424 };
    425 
    426 TEST_F(BufferedDataSourceTest, InitializationSuccess) {
    427   InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
    428   StopDataSource();
    429 }
    430 
    431 TEST_F(BufferedDataSourceTest, InitiailizationFailed) {
    432   InitializeDataSource(kHttpUrl, net::ERR_FILE_NOT_FOUND, false, 0, NONE);
    433   StopDataSource();
    434 }
    435 
    436 TEST_F(BufferedDataSourceTest, MissingContentLength) {
    437   InitializeDataSource(kHttpUrl, net::OK, true, -1, LOADING);
    438   StopDataSource();
    439 }
    440 
    441 TEST_F(BufferedDataSourceTest, RangeRequestNotSupported) {
    442   InitializeDataSource(kHttpUrl, net::OK, false, 1024, LOADING);
    443   StopDataSource();
    444 }
    445 
    446 // Test the case where we get a 206 response, but no Content-Range header.
    447 TEST_F(BufferedDataSourceTest, MissingContentRange) {
    448   InitializeDataSource(kHttpUrl, net::ERR_INVALID_RESPONSE, true, 1024,
    449                        LOADING);
    450   StopDataSource();
    451 }
    452 
    453 TEST_F(BufferedDataSourceTest,
    454        MissingContentLengthAndRangeRequestNotSupported) {
    455   InitializeDataSource(kHttpUrl, net::OK, false, -1, LOADING);
    456   StopDataSource();
    457 }
    458 
    459 TEST_F(BufferedDataSourceTest, ReadCacheHit) {
    460   InitializeDataSource(kHttpUrl, net::OK, true, 25, LOADING);
    461 
    462   // Performs read with cache hit.
    463   ReadDataSourceHit(10, 10, 10);
    464 
    465   // Performs read with cache hit but partially filled.
    466   ReadDataSourceHit(20, 10, 5);
    467 
    468   StopDataSource();
    469 }
    470 
    471 TEST_F(BufferedDataSourceTest, ReadCacheMiss) {
    472   InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
    473   ReadDataSourceMiss(1000, 10, net::OK);
    474   ReadDataSourceMiss(20, 10, net::OK);
    475   StopDataSource();
    476 }
    477 
    478 // Test the case where the initial response from the server indicates that
    479 // Range requests are supported, but a later request prove otherwise.
    480 TEST_F(BufferedDataSourceTest, ServerLiesAboutRangeSupport) {
    481   InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
    482   ReadDataSourceHit(10, 10, 10);
    483   ReadDataSourceMiss(1000, 10, net::ERR_INVALID_RESPONSE);
    484   StopDataSource();
    485 }
    486 
    487 TEST_F(BufferedDataSourceTest, ReadHang) {
    488   InitializeDataSource(kHttpUrl, net::OK, true, 25, LOADING);
    489   ReadDataSourceHang(10, 10);
    490   StopDataSource();
    491 }
    492 
    493 TEST_F(BufferedDataSourceTest, ReadFailed) {
    494   InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
    495   ReadDataSourceHit(10, 10, 10);
    496   ReadDataSourceFailed(10, 10, net::ERR_CONNECTION_RESET);
    497   StopDataSource();
    498 }
    499 
    500 TEST_F(BufferedDataSourceTest, ReadTimesOut) {
    501   InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
    502   ReadDataSourceTimesOut(20, 10);
    503   StopDataSource();
    504 }
    505 
    506 TEST_F(BufferedDataSourceTest, FileHasLoadedState) {
    507   InitializeDataSource(kFileUrl, net::OK, true, 1024, LOADED);
    508   ReadDataSourceTimesOut(20, 10);
    509   StopDataSource();
    510 }
    511 
    512 // This test makes sure that Stop() does not require a task to run on
    513 // |message_loop_| before it calls its callback. This prevents accidental
    514 // introduction of a pipeline teardown deadlock. The pipeline owner blocks
    515 // the render message loop while waiting for Stop() to complete. Since this
    516 // object runs on the render message loop, Stop() will not complete if it
    517 // requires a task to run on the the message loop that is being blocked.
    518 TEST_F(BufferedDataSourceTest, StopDoesNotUseMessageLoopForCallback) {
    519   InitializeDataSource(kFileUrl, net::OK, true, 1024, LOADED);
    520 
    521   // Create a callback that lets us verify that it was called before
    522   // Stop() returns. This is to make sure that the callback does not
    523   // require |message_loop_| to execute tasks before being called.
    524   media::MockCallback* stop_callback = media::NewExpectedCallback();
    525   bool stop_done_called = false;
    526   ON_CALL(*stop_callback, RunWithParams(_))
    527       .WillByDefault(Assign(&stop_done_called, true));
    528 
    529   // Stop() the data source like normal.
    530   data_source_->Stop(stop_callback);
    531 
    532   // Verify that the callback was called inside the Stop() call.
    533   EXPECT_TRUE(stop_done_called);
    534 
    535   message_loop_->RunAllPending();
    536 }
    537 
    538 TEST_F(BufferedDataSourceTest, AbortDuringPendingRead) {
    539   InitializeDataSource(kFileUrl, net::OK, true, 1024, LOADED);
    540 
    541   // Setup a way to verify that Read() is not called on the loader.
    542   // We are doing this to make sure that the ReadTask() is still on
    543   // the message loop queue when Abort() is called.
    544   bool read_called = false;
    545   ON_CALL(*loader_, Read(_, _, _ , _))
    546       .WillByDefault(DoAll(Assign(&read_called, true),
    547                            DeleteArg<3>()));
    548 
    549   // Initiate a Read() on the data source, but don't allow the
    550   // message loop to run.
    551   data_source_->Read(
    552       0, 10, buffer_,
    553       NewCallback(static_cast<BufferedDataSourceTest*>(this),
    554                   &BufferedDataSourceTest::ReadCallback));
    555 
    556   // Call Abort() with the read pending.
    557   EXPECT_CALL(*this, ReadCallback(-1));
    558   EXPECT_CALL(*loader_, Stop());
    559   data_source_->Abort();
    560 
    561   // Verify that Read()'s after the Abort() issue callback with an error.
    562   EXPECT_CALL(*this, ReadCallback(-1));
    563   data_source_->Read(
    564       0, 10, buffer_,
    565       NewCallback(static_cast<BufferedDataSourceTest*>(this),
    566                   &BufferedDataSourceTest::ReadCallback));
    567 
    568   // Stop() the data source like normal.
    569   data_source_->Stop(media::NewExpectedCallback());
    570 
    571   // Allow cleanup task to run.
    572   message_loop_->RunAllPending();
    573 
    574   // Verify that Read() was not called on the loader.
    575   EXPECT_FALSE(read_called);
    576 }
    577 
    578 }  // namespace webkit_glue
    579