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
17using ::testing::_;
18using ::testing::Assign;
19using ::testing::AtLeast;
20using ::testing::DeleteArg;
21using ::testing::DoAll;
22using ::testing::InSequence;
23using ::testing::Invoke;
24using ::testing::InvokeWithoutArgs;
25using ::testing::NotNull;
26using ::testing::Return;
27using ::testing::ReturnRef;
28using ::testing::SetArgumentPointee;
29using ::testing::StrictMock;
30using ::testing::NiceMock;
31using ::testing::WithArgs;
32
33namespace webkit_glue {
34
35static const char* kHttpUrl = "http://test";
36static const char* kFileUrl = "file://test";
37static const int kDataSize = 1024;
38
39enum NetworkState {
40  NONE,
41  LOADED,
42  LOADING
43};
44
45// A mock BufferedDataSource to inject mock BufferedResourceLoader through
46// CreateResourceLoader() method.
47class 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
66class 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
91class 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
426TEST_F(BufferedDataSourceTest, InitializationSuccess) {
427  InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
428  StopDataSource();
429}
430
431TEST_F(BufferedDataSourceTest, InitiailizationFailed) {
432  InitializeDataSource(kHttpUrl, net::ERR_FILE_NOT_FOUND, false, 0, NONE);
433  StopDataSource();
434}
435
436TEST_F(BufferedDataSourceTest, MissingContentLength) {
437  InitializeDataSource(kHttpUrl, net::OK, true, -1, LOADING);
438  StopDataSource();
439}
440
441TEST_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.
447TEST_F(BufferedDataSourceTest, MissingContentRange) {
448  InitializeDataSource(kHttpUrl, net::ERR_INVALID_RESPONSE, true, 1024,
449                       LOADING);
450  StopDataSource();
451}
452
453TEST_F(BufferedDataSourceTest,
454       MissingContentLengthAndRangeRequestNotSupported) {
455  InitializeDataSource(kHttpUrl, net::OK, false, -1, LOADING);
456  StopDataSource();
457}
458
459TEST_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
471TEST_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.
480TEST_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
487TEST_F(BufferedDataSourceTest, ReadHang) {
488  InitializeDataSource(kHttpUrl, net::OK, true, 25, LOADING);
489  ReadDataSourceHang(10, 10);
490  StopDataSource();
491}
492
493TEST_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
500TEST_F(BufferedDataSourceTest, ReadTimesOut) {
501  InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
502  ReadDataSourceTimesOut(20, 10);
503  StopDataSource();
504}
505
506TEST_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.
518TEST_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
538TEST_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