1 // Copyright 2013 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.
6 #include "base/message_loop/message_loop.h"
7 #include "content/renderer/media/buffered_data_source.h"
8 #include "content/renderer/media/test_response_generator.h"
9 #include "content/test/mock_webframeclient.h"
10 #include "content/test/mock_weburlloader.h"
11 #include "media/base/media_log.h"
12 #include "media/base/mock_data_source_host.h"
13 #include "media/base/mock_filters.h"
14 #include "media/base/test_helpers.h"
15 #include "third_party/WebKit/public/platform/WebURLResponse.h"
16 #include "third_party/WebKit/public/web/WebView.h"
19 using ::testing::Assign;
20 using ::testing::Invoke;
21 using ::testing::InSequence;
22 using ::testing::NiceMock;
23 using ::testing::StrictMock;
25 using WebKit::WebFrame;
26 using WebKit::WebString;
27 using WebKit::WebURLLoader;
28 using WebKit::WebURLResponse;
29 using WebKit::WebView;
33 // Overrides CreateResourceLoader() to permit injecting a MockWebURLLoader.
34 // Also keeps track of whether said MockWebURLLoader is actively loading.
35 class MockBufferedDataSource : public BufferedDataSource {
37 MockBufferedDataSource(
38 const scoped_refptr<base::MessageLoopProxy>& message_loop,
40 : BufferedDataSource(message_loop, frame, new media::MediaLog(),
41 base::Bind(&MockBufferedDataSource::set_downloading,
42 base::Unretained(this))),
46 virtual ~MockBufferedDataSource() {}
48 MOCK_METHOD2(CreateResourceLoader, BufferedResourceLoader*(int64, int64));
49 BufferedResourceLoader* CreateMockResourceLoader(int64 first_byte_position,
50 int64 last_byte_position) {
51 CHECK(!loading_) << "Previous resource load wasn't cancelled";
53 BufferedResourceLoader* loader =
54 BufferedDataSource::CreateResourceLoader(first_byte_position,
57 // Keep track of active loading state via loadAsynchronously() and cancel().
58 NiceMock<MockWebURLLoader>* url_loader = new NiceMock<MockWebURLLoader>();
59 ON_CALL(*url_loader, loadAsynchronously(_, _))
60 .WillByDefault(Assign(&loading_, true));
61 ON_CALL(*url_loader, cancel())
62 .WillByDefault(Assign(&loading_, false));
64 // |test_loader_| will be used when Start() is called.
65 loader->test_loader_ = scoped_ptr<WebURLLoader>(url_loader);
69 bool loading() { return loading_; }
70 void set_loading(bool loading) { loading_ = loading; }
71 bool downloading() { return downloading_; }
72 void set_downloading(bool downloading) { downloading_ = downloading; }
75 // Whether the resource is downloading or deferred.
78 // Whether the resource load has starting loading but yet to been cancelled.
81 DISALLOW_COPY_AND_ASSIGN(MockBufferedDataSource);
84 static const int64 kFileSize = 5000000;
85 static const int64 kFarReadPosition = 4000000;
86 static const int kDataSize = 1024;
88 static const char kHttpUrl[] = "http://localhost/foo.webm";
89 static const char kFileUrl[] = "file:///tmp/bar.webm";
91 class BufferedDataSourceTest : public testing::Test {
93 BufferedDataSourceTest()
94 : view_(WebView::create(NULL)) {
95 view_->initializeMainFrame(&client_);
97 data_source_.reset(new MockBufferedDataSource(
98 message_loop_.message_loop_proxy(), view_->mainFrame()));
99 data_source_->set_host(&host_);
102 virtual ~BufferedDataSourceTest() {
106 MOCK_METHOD1(OnInitialize, void(bool));
108 void Initialize(const char* url, bool expected) {
110 response_generator_.reset(new TestResponseGenerator(gurl, kFileSize));
112 ExpectCreateResourceLoader();
113 EXPECT_CALL(*this, OnInitialize(expected));
114 data_source_->Initialize(
115 gurl, BufferedResourceLoader::kUnspecified, base::Bind(
116 &BufferedDataSourceTest::OnInitialize, base::Unretained(this)));
117 message_loop_.RunUntilIdle();
119 bool is_http = gurl.SchemeIs(kHttpScheme) || gurl.SchemeIs(kHttpsScheme);
120 EXPECT_EQ(data_source_->downloading(), is_http);
123 // Helper to initialize tests with a valid 206 response.
124 void InitializeWith206Response() {
125 Initialize(kHttpUrl, true);
127 EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length()));
128 Respond(response_generator_->Generate206(0));
131 // Helper to initialize tests with a valid file:// response.
132 void InitializeWithFileResponse() {
133 Initialize(kFileUrl, true);
135 EXPECT_CALL(host_, SetTotalBytes(kFileSize));
136 EXPECT_CALL(host_, AddBufferedByteRange(0, kFileSize));
137 Respond(response_generator_->GenerateFileResponse(0));
140 // Stops any active loaders and shuts down the data source.
142 // This typically happens when the page is closed and for our purposes is
143 // appropriate to do when tearing down a test.
145 if (data_source_->loading()) {
146 loader()->didFail(url_loader(), response_generator_->GenerateError());
147 message_loop_.RunUntilIdle();
150 data_source_->Stop(media::NewExpectedClosure());
151 message_loop_.RunUntilIdle();
154 void ExpectCreateResourceLoader() {
155 EXPECT_CALL(*data_source_, CreateResourceLoader(_, _))
156 .WillOnce(Invoke(data_source_.get(),
157 &MockBufferedDataSource::CreateMockResourceLoader));
158 message_loop_.RunUntilIdle();
161 void Respond(const WebURLResponse& response) {
162 loader()->didReceiveResponse(url_loader(), response);
163 message_loop_.RunUntilIdle();
166 void ReceiveData(int size) {
167 scoped_ptr<char[]> data(new char[size]);
168 memset(data.get(), 0xA5, size); // Arbitrary non-zero value.
170 loader()->didReceiveData(url_loader(), data.get(), size, size);
171 message_loop_.RunUntilIdle();
174 void FinishLoading() {
175 data_source_->set_loading(false);
176 loader()->didFinishLoading(url_loader(), 0);
177 message_loop_.RunUntilIdle();
180 MOCK_METHOD1(ReadCallback, void(int size));
182 void ReadAt(int64 position) {
183 data_source_->Read(position, kDataSize, buffer_,
184 base::Bind(&BufferedDataSourceTest::ReadCallback,
185 base::Unretained(this)));
186 message_loop_.RunUntilIdle();
189 // Accessors for private variables on |data_source_|.
190 BufferedResourceLoader* loader() {
191 return data_source_->loader_.get();
193 WebURLLoader* url_loader() {
194 return loader()->active_loader_->loader_.get();
197 Preload preload() { return data_source_->preload_; }
198 BufferedResourceLoader::DeferStrategy defer_strategy() {
199 return loader()->defer_strategy_;
201 int data_source_bitrate() { return data_source_->bitrate_; }
202 int data_source_playback_rate() { return data_source_->playback_rate_; }
203 int loader_bitrate() { return loader()->bitrate_; }
204 int loader_playback_rate() { return loader()->playback_rate_; }
206 scoped_ptr<MockBufferedDataSource> data_source_;
208 scoped_ptr<TestResponseGenerator> response_generator_;
209 MockWebFrameClient client_;
212 StrictMock<media::MockDataSourceHost> host_;
213 base::MessageLoop message_loop_;
216 // Used for calling BufferedDataSource::Read().
217 uint8 buffer_[kDataSize];
219 DISALLOW_COPY_AND_ASSIGN(BufferedDataSourceTest);
222 TEST_F(BufferedDataSourceTest, Range_Supported) {
223 Initialize(kHttpUrl, true);
225 EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length()));
226 Respond(response_generator_->Generate206(0));
228 EXPECT_TRUE(data_source_->loading());
229 EXPECT_FALSE(data_source_->IsStreaming());
233 TEST_F(BufferedDataSourceTest, Range_InstanceSizeUnknown) {
234 Initialize(kHttpUrl, true);
236 Respond(response_generator_->Generate206(
237 0, TestResponseGenerator::kNoContentRangeInstanceSize));
239 EXPECT_TRUE(data_source_->loading());
240 EXPECT_TRUE(data_source_->IsStreaming());
244 TEST_F(BufferedDataSourceTest, Range_NotFound) {
245 Initialize(kHttpUrl, false);
246 Respond(response_generator_->Generate404());
248 EXPECT_FALSE(data_source_->loading());
252 TEST_F(BufferedDataSourceTest, Range_NotSupported) {
253 Initialize(kHttpUrl, true);
254 EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length()));
255 Respond(response_generator_->Generate200());
257 EXPECT_TRUE(data_source_->loading());
258 EXPECT_TRUE(data_source_->IsStreaming());
262 // Special carve-out for Apache versions that choose to return a 200 for
263 // Range:0- ("because it's more efficient" than a 206)
264 TEST_F(BufferedDataSourceTest, Range_SupportedButReturned200) {
265 Initialize(kHttpUrl, true);
266 EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length()));
267 WebURLResponse response = response_generator_->Generate200();
268 response.setHTTPHeaderField(WebString::fromUTF8("Accept-Ranges"),
269 WebString::fromUTF8("bytes"));
272 EXPECT_TRUE(data_source_->loading());
273 EXPECT_FALSE(data_source_->IsStreaming());
277 TEST_F(BufferedDataSourceTest, Range_MissingContentRange) {
278 Initialize(kHttpUrl, false);
279 Respond(response_generator_->Generate206(
280 0, TestResponseGenerator::kNoContentRange));
282 EXPECT_FALSE(data_source_->loading());
286 TEST_F(BufferedDataSourceTest, Range_MissingContentLength) {
287 Initialize(kHttpUrl, true);
289 // It'll manage without a Content-Length response.
290 EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length()));
291 Respond(response_generator_->Generate206(
292 0, TestResponseGenerator::kNoContentLength));
294 EXPECT_TRUE(data_source_->loading());
295 EXPECT_FALSE(data_source_->IsStreaming());
299 TEST_F(BufferedDataSourceTest, Range_WrongContentRange) {
300 Initialize(kHttpUrl, false);
302 // Now it's done and will fail.
303 Respond(response_generator_->Generate206(1337));
305 EXPECT_FALSE(data_source_->loading());
309 // Test the case where the initial response from the server indicates that
310 // Range requests are supported, but a later request prove otherwise.
311 TEST_F(BufferedDataSourceTest, Range_ServerLied) {
312 InitializeWith206Response();
314 // Read causing a new request to be made -- we'll expect it to error.
315 ExpectCreateResourceLoader();
316 ReadAt(kFarReadPosition);
318 // Return a 200 in response to a range request.
319 EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
320 Respond(response_generator_->Generate200());
322 EXPECT_FALSE(data_source_->loading());
326 TEST_F(BufferedDataSourceTest, Http_AbortWhileReading) {
327 InitializeWith206Response();
329 // Make sure there's a pending read -- we'll expect it to error.
333 EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
334 data_source_->Abort();
335 message_loop_.RunUntilIdle();
337 EXPECT_FALSE(data_source_->loading());
341 TEST_F(BufferedDataSourceTest, File_AbortWhileReading) {
342 InitializeWithFileResponse();
344 // Make sure there's a pending read -- we'll expect it to error.
348 EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
349 data_source_->Abort();
350 message_loop_.RunUntilIdle();
352 EXPECT_FALSE(data_source_->loading());
356 TEST_F(BufferedDataSourceTest, Http_Retry) {
357 InitializeWith206Response();
359 // Read to advance our position.
360 EXPECT_CALL(*this, ReadCallback(kDataSize));
361 EXPECT_CALL(host_, AddBufferedByteRange(0, kDataSize - 1));
363 ReceiveData(kDataSize);
365 // Issue a pending read but terminate the connection to force a retry.
367 ExpectCreateResourceLoader();
369 Respond(response_generator_->Generate206(kDataSize));
371 // Complete the read.
372 EXPECT_CALL(*this, ReadCallback(kDataSize));
373 EXPECT_CALL(host_, AddBufferedByteRange(kDataSize, (kDataSize * 2) - 1));
374 ReceiveData(kDataSize);
376 EXPECT_TRUE(data_source_->loading());
380 TEST_F(BufferedDataSourceTest, File_Retry) {
381 InitializeWithFileResponse();
383 // Read to advance our position.
384 EXPECT_CALL(*this, ReadCallback(kDataSize));
386 ReceiveData(kDataSize);
388 // Issue a pending read but terminate the connection to force a retry.
390 ExpectCreateResourceLoader();
392 Respond(response_generator_->GenerateFileResponse(kDataSize));
394 // Complete the read.
395 EXPECT_CALL(*this, ReadCallback(kDataSize));
396 ReceiveData(kDataSize);
398 EXPECT_TRUE(data_source_->loading());
402 TEST_F(BufferedDataSourceTest, Http_TooManyRetries) {
403 InitializeWith206Response();
405 // Make sure there's a pending read -- we'll expect it to error.
408 // It'll try three times.
409 ExpectCreateResourceLoader();
411 Respond(response_generator_->Generate206(0));
413 ExpectCreateResourceLoader();
415 Respond(response_generator_->Generate206(0));
417 ExpectCreateResourceLoader();
419 Respond(response_generator_->Generate206(0));
421 // It'll error after this.
422 EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
425 EXPECT_FALSE(data_source_->loading());
429 TEST_F(BufferedDataSourceTest, File_TooManyRetries) {
430 InitializeWithFileResponse();
432 // Make sure there's a pending read -- we'll expect it to error.
435 // It'll try three times.
436 ExpectCreateResourceLoader();
438 Respond(response_generator_->GenerateFileResponse(0));
440 ExpectCreateResourceLoader();
442 Respond(response_generator_->GenerateFileResponse(0));
444 ExpectCreateResourceLoader();
446 Respond(response_generator_->GenerateFileResponse(0));
448 // It'll error after this.
449 EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
452 EXPECT_FALSE(data_source_->loading());
456 TEST_F(BufferedDataSourceTest, File_InstanceSizeUnknown) {
457 Initialize(kFileUrl, false);
458 EXPECT_FALSE(data_source_->downloading());
460 Respond(response_generator_->GenerateFileResponse(-1));
462 EXPECT_FALSE(data_source_->loading());
466 TEST_F(BufferedDataSourceTest, File_Successful) {
467 InitializeWithFileResponse();
469 EXPECT_TRUE(data_source_->loading());
470 EXPECT_FALSE(data_source_->IsStreaming());
474 static void SetTrue(bool* value) {
478 // This test makes sure that Stop() does not require a task to run on
479 // |message_loop_| before it calls its callback. This prevents accidental
480 // introduction of a pipeline teardown deadlock. The pipeline owner blocks
481 // the render message loop while waiting for Stop() to complete. Since this
482 // object runs on the render message loop, Stop() will not complete if it
483 // requires a task to run on the the message loop that is being blocked.
484 TEST_F(BufferedDataSourceTest, StopDoesNotUseMessageLoopForCallback) {
485 InitializeWith206Response();
487 // Stop() the data source, using a callback that lets us verify that it was
488 // called before Stop() returns. This is to make sure that the callback does
489 // not require |message_loop_| to execute tasks before being called.
490 bool stop_done_called = false;
491 EXPECT_TRUE(data_source_->loading());
492 data_source_->Stop(base::Bind(&SetTrue, &stop_done_called));
494 // Verify that the callback was called inside the Stop() call.
495 EXPECT_TRUE(stop_done_called);
496 message_loop_.RunUntilIdle();
499 TEST_F(BufferedDataSourceTest, StopDuringRead) {
500 InitializeWith206Response();
503 data_source_->Read(0, arraysize(buffer), buffer, base::Bind(
504 &BufferedDataSourceTest::ReadCallback, base::Unretained(this)));
506 // The outstanding read should fail before the stop callback runs.
509 EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
510 data_source_->Stop(media::NewExpectedClosure());
512 message_loop_.RunUntilIdle();
515 TEST_F(BufferedDataSourceTest, DefaultValues) {
516 InitializeWith206Response();
518 // Ensure we have sane values for default loading scenario.
519 EXPECT_EQ(AUTO, preload());
520 EXPECT_EQ(BufferedResourceLoader::kCapacityDefer, defer_strategy());
522 EXPECT_EQ(0, data_source_bitrate());
523 EXPECT_EQ(0.0f, data_source_playback_rate());
524 EXPECT_EQ(0, loader_bitrate());
525 EXPECT_EQ(0.0f, loader_playback_rate());
527 EXPECT_TRUE(data_source_->loading());
531 TEST_F(BufferedDataSourceTest, SetBitrate) {
532 InitializeWith206Response();
534 data_source_->SetBitrate(1234);
535 message_loop_.RunUntilIdle();
536 EXPECT_EQ(1234, data_source_bitrate());
537 EXPECT_EQ(1234, loader_bitrate());
539 // Read so far ahead to cause the loader to get recreated.
540 BufferedResourceLoader* old_loader = loader();
541 ExpectCreateResourceLoader();
542 ReadAt(kFarReadPosition);
543 Respond(response_generator_->Generate206(kFarReadPosition));
545 // Verify loader changed but still has same bitrate.
546 EXPECT_NE(old_loader, loader());
547 EXPECT_EQ(1234, loader_bitrate());
549 EXPECT_TRUE(data_source_->loading());
550 EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
554 TEST_F(BufferedDataSourceTest, MediaPlaybackRateChanged) {
555 InitializeWith206Response();
557 data_source_->MediaPlaybackRateChanged(2.0f);
558 message_loop_.RunUntilIdle();
559 EXPECT_EQ(2.0f, data_source_playback_rate());
560 EXPECT_EQ(2.0f, loader_playback_rate());
562 // Read so far ahead to cause the loader to get recreated.
563 BufferedResourceLoader* old_loader = loader();
564 ExpectCreateResourceLoader();
565 ReadAt(kFarReadPosition);
566 Respond(response_generator_->Generate206(kFarReadPosition));
568 // Verify loader changed but still has same playback rate.
569 EXPECT_NE(old_loader, loader());
571 EXPECT_TRUE(data_source_->loading());
572 EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
576 TEST_F(BufferedDataSourceTest, Http_Read) {
577 InitializeWith206Response();
581 // Receive first half of the read.
582 EXPECT_CALL(host_, AddBufferedByteRange(0, (kDataSize / 2) - 1));
583 ReceiveData(kDataSize / 2);
585 // Receive last half of the read.
586 EXPECT_CALL(*this, ReadCallback(kDataSize));
587 EXPECT_CALL(host_, AddBufferedByteRange(0, kDataSize - 1));
588 ReceiveData(kDataSize / 2);
590 EXPECT_TRUE(data_source_->downloading());
594 TEST_F(BufferedDataSourceTest, Http_Read_Seek) {
595 InitializeWith206Response();
597 // Read a bit from the beginning.
599 EXPECT_CALL(*this, ReadCallback(kDataSize));
600 EXPECT_CALL(host_, AddBufferedByteRange(0, kDataSize - 1));
601 ReceiveData(kDataSize);
603 // Simulate a seek by reading a bit beyond kDataSize.
604 ReadAt(kDataSize * 2);
606 // We receive data leading up to but not including our read.
607 EXPECT_CALL(host_, AddBufferedByteRange(0, kDataSize * 2 - 1));
608 ReceiveData(kDataSize);
610 // We now receive the rest of the data for our read.
611 EXPECT_CALL(*this, ReadCallback(kDataSize));
612 EXPECT_CALL(host_, AddBufferedByteRange(0, kDataSize * 3 - 1));
613 ReceiveData(kDataSize);
615 EXPECT_TRUE(data_source_->downloading());
619 TEST_F(BufferedDataSourceTest, File_Read) {
620 InitializeWithFileResponse();
624 // Receive first half of the read but no buffering update.
625 ReceiveData(kDataSize / 2);
627 // Receive last half of the read but no buffering update.
628 EXPECT_CALL(*this, ReadCallback(kDataSize));
629 ReceiveData(kDataSize / 2);
634 TEST_F(BufferedDataSourceTest, Http_FinishLoading) {
635 InitializeWith206Response();
637 EXPECT_TRUE(data_source_->downloading());
639 EXPECT_FALSE(data_source_->downloading());
644 TEST_F(BufferedDataSourceTest, File_FinishLoading) {
645 InitializeWithFileResponse();
647 EXPECT_FALSE(data_source_->downloading());
649 EXPECT_FALSE(data_source_->downloading());
654 } // namespace content