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