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:
MockBufferedDataSource(MessageLoop * message_loop,WebFrame * frame)49 MockBufferedDataSource(MessageLoop* message_loop, WebFrame* frame)
50 : BufferedDataSource(message_loop, frame) {
51 }
52
GetTimeoutMilliseconds()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:
MockBufferedResourceLoader()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:
~MockBufferedResourceLoader()86 ~MockBufferedResourceLoader() {}
87
88 DISALLOW_COPY_AND_ASSIGN(MockBufferedResourceLoader);
89 };
90
91 class BufferedDataSourceTest : public testing::Test {
92 public:
BufferedDataSourceTest()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
~BufferedDataSourceTest()102 virtual ~BufferedDataSourceTest() {
103 }
104
ExpectCreateAndStartResourceLoader(int start_error)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
InitializeDataSource(const char * url,int error,bool partial_response,int64 instance_size,NetworkState networkState)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
StopDataSource()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
InvokeStartCallback(net::CompletionCallback * callback,BufferedResourceLoader::NetworkEventCallback * network_callback,WebFrame * frame)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
InvokeReadCallback(int64 position,int size,uint8 * buffer,net::CompletionCallback * callback)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
ReadDataSourceHit(int64 position,int size,int read_size)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
ReadDataSourceHang(int64 position,int size)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
ReadDataSourceMiss(int64 position,int size,int start_error)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
ReadDataSourceFailed(int64 position,int size,int error)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
ReadDataSourceTimesOut(int64 position,int size)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
TEST_F(BufferedDataSourceTest,InitializationSuccess)426 TEST_F(BufferedDataSourceTest, InitializationSuccess) {
427 InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
428 StopDataSource();
429 }
430
TEST_F(BufferedDataSourceTest,InitiailizationFailed)431 TEST_F(BufferedDataSourceTest, InitiailizationFailed) {
432 InitializeDataSource(kHttpUrl, net::ERR_FILE_NOT_FOUND, false, 0, NONE);
433 StopDataSource();
434 }
435
TEST_F(BufferedDataSourceTest,MissingContentLength)436 TEST_F(BufferedDataSourceTest, MissingContentLength) {
437 InitializeDataSource(kHttpUrl, net::OK, true, -1, LOADING);
438 StopDataSource();
439 }
440
TEST_F(BufferedDataSourceTest,RangeRequestNotSupported)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.
TEST_F(BufferedDataSourceTest,MissingContentRange)447 TEST_F(BufferedDataSourceTest, MissingContentRange) {
448 InitializeDataSource(kHttpUrl, net::ERR_INVALID_RESPONSE, true, 1024,
449 LOADING);
450 StopDataSource();
451 }
452
TEST_F(BufferedDataSourceTest,MissingContentLengthAndRangeRequestNotSupported)453 TEST_F(BufferedDataSourceTest,
454 MissingContentLengthAndRangeRequestNotSupported) {
455 InitializeDataSource(kHttpUrl, net::OK, false, -1, LOADING);
456 StopDataSource();
457 }
458
TEST_F(BufferedDataSourceTest,ReadCacheHit)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
TEST_F(BufferedDataSourceTest,ReadCacheMiss)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.
TEST_F(BufferedDataSourceTest,ServerLiesAboutRangeSupport)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
TEST_F(BufferedDataSourceTest,ReadHang)487 TEST_F(BufferedDataSourceTest, ReadHang) {
488 InitializeDataSource(kHttpUrl, net::OK, true, 25, LOADING);
489 ReadDataSourceHang(10, 10);
490 StopDataSource();
491 }
492
TEST_F(BufferedDataSourceTest,ReadFailed)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
TEST_F(BufferedDataSourceTest,ReadTimesOut)500 TEST_F(BufferedDataSourceTest, ReadTimesOut) {
501 InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
502 ReadDataSourceTimesOut(20, 10);
503 StopDataSource();
504 }
505
TEST_F(BufferedDataSourceTest,FileHasLoadedState)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.
TEST_F(BufferedDataSourceTest,StopDoesNotUseMessageLoopForCallback)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
TEST_F(BufferedDataSourceTest,AbortDuringPendingRead)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