buffered_resource_loader_unittest.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2010 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/format_macros.h" 8#include "base/stringprintf.h" 9#include "net/base/net_errors.h" 10#include "net/http/http_util.h" 11#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" 12#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrameClient.h" 13#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h" 14#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h" 15#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" 16#include "webkit/glue/media/buffered_resource_loader.h" 17#include "webkit/mocks/mock_webframe.h" 18#include "webkit/mocks/mock_weburlloader.h" 19 20using ::testing::_; 21using ::testing::Assign; 22using ::testing::AtLeast; 23using ::testing::DeleteArg; 24using ::testing::DoAll; 25using ::testing::InSequence; 26using ::testing::Invoke; 27using ::testing::InvokeWithoutArgs; 28using ::testing::NotNull; 29using ::testing::Return; 30using ::testing::ReturnRef; 31using ::testing::SetArgumentPointee; 32using ::testing::StrictMock; 33using ::testing::NiceMock; 34using ::testing::WithArgs; 35 36using WebKit::WebURLError; 37using WebKit::WebFrameClient; 38using WebKit::WebURLResponse; 39using WebKit::WebView; 40 41namespace { 42 43const char* kHttpUrl = "http://test"; 44const char kHttpRedirectToSameDomainUrl1[] = "http://test/ing"; 45const char kHttpRedirectToSameDomainUrl2[] = "http://test/ing2"; 46const char kHttpRedirectToDifferentDomainUrl1[] = "http://test2"; 47const char kHttpRedirectToDifferentDomainUrl2[] = "http://test2/ing"; 48 49const int kDataSize = 1024; 50const int kHttpOK = 200; 51const int kHttpPartialContent = 206; 52 53enum NetworkState { 54 NONE, 55 LOADED, 56 LOADING 57}; 58 59} // namespace 60 61namespace webkit_glue { 62 63// Submit a request completed event to the resource loader due to request 64// being canceled. Pretending the event is from external. 65ACTION_P(RequestCanceled, loader) { 66 WebURLError error; 67 error.reason = net::ERR_ABORTED; 68 error.domain = WebString::fromUTF8(net::kErrorDomain); 69 loader->didFail(NULL, error); 70} 71 72class BufferedResourceLoaderTest : public testing::Test { 73 public: 74 BufferedResourceLoaderTest() { 75 for (int i = 0; i < kDataSize; ++i) 76 data_[i] = i; 77 } 78 79 virtual ~BufferedResourceLoaderTest() { 80 } 81 82 void Initialize(const char* url, int first_position, int last_position) { 83 gurl_ = GURL(url); 84 first_position_ = first_position; 85 last_position_ = last_position; 86 87 frame_.reset(new NiceMock<MockWebFrame>()); 88 89 url_loader_ = new NiceMock<MockWebURLLoader>(); 90 loader_ = new BufferedResourceLoader(gurl_, 91 first_position_, last_position_); 92 loader_->SetURLLoaderForTest(url_loader_); 93 } 94 95 void SetLoaderBuffer(size_t forward_capacity, size_t backward_capacity) { 96 loader_->buffer_.reset( 97 new media::SeekableBuffer(backward_capacity, forward_capacity)); 98 } 99 100 void Start() { 101 InSequence s; 102 EXPECT_CALL(*url_loader_, loadAsynchronously(_, loader_.get())); 103 loader_->Start( 104 NewCallback(this, &BufferedResourceLoaderTest::StartCallback), 105 NewCallback(this, &BufferedResourceLoaderTest::NetworkCallback), 106 frame_.get()); 107 } 108 109 void FullResponse(int64 instance_size) { 110 EXPECT_CALL(*this, StartCallback(net::OK)); 111 112 WebURLResponse response(gurl_); 113 response.setHTTPHeaderField(WebString::fromUTF8("Content-Length"), 114 WebString::fromUTF8(base::StringPrintf("%" 115 PRId64, instance_size))); 116 response.setExpectedContentLength(instance_size); 117 response.setHTTPStatusCode(kHttpOK); 118 loader_->didReceiveResponse(url_loader_, response); 119 EXPECT_EQ(instance_size, loader_->content_length()); 120 EXPECT_EQ(instance_size, loader_->instance_size()); 121 EXPECT_FALSE(loader_->partial_response()); 122 } 123 124 void PartialResponse(int64 first_position, int64 last_position, 125 int64 instance_size) { 126 EXPECT_CALL(*this, StartCallback(net::OK)); 127 int64 content_length = last_position - first_position + 1; 128 129 WebURLResponse response(gurl_); 130 response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"), 131 WebString::fromUTF8(base::StringPrintf("bytes " 132 "%" PRId64 "-%" PRId64 "/%" PRId64, 133 first_position, 134 last_position, 135 instance_size))); 136 response.setExpectedContentLength(content_length); 137 response.setHTTPStatusCode(kHttpPartialContent); 138 loader_->didReceiveResponse(url_loader_, response); 139 EXPECT_EQ(content_length, loader_->content_length()); 140 EXPECT_EQ(instance_size, loader_->instance_size()); 141 EXPECT_TRUE(loader_->partial_response()); 142 } 143 144 void Redirect(const char* url) { 145 GURL redirectUrl(url); 146 WebKit::WebURLRequest newRequest(redirectUrl); 147 WebKit::WebURLResponse redirectResponse(gurl_); 148 149 loader_->willSendRequest(url_loader_, newRequest, redirectResponse); 150 151 MessageLoop::current()->RunAllPending(); 152 } 153 154 void StopWhenLoad() { 155 InSequence s; 156 EXPECT_CALL(*url_loader_, cancel()) 157 .WillOnce(RequestCanceled(loader_)); 158 loader_->Stop(); 159 loader_ = NULL; 160 } 161 162 // Helper method to write to |loader_| from |data_|. 163 void WriteLoader(int position, int size) { 164 EXPECT_CALL(*this, NetworkCallback()) 165 .RetiresOnSaturation(); 166 loader_->didReceiveData(url_loader_, 167 reinterpret_cast<char*>(data_ + position), size); 168 } 169 170 // Helper method to read from |loader_|. 171 void ReadLoader(int64 position, int size, uint8* buffer) { 172 loader_->Read(position, size, buffer, 173 NewCallback(this, &BufferedResourceLoaderTest::ReadCallback)); 174 } 175 176 // Verifis that data in buffer[0...size] is equal to data_[pos...pos+size]. 177 void VerifyBuffer(uint8* buffer, int pos, int size) { 178 EXPECT_EQ(0, memcmp(buffer, data_ + pos, size)); 179 } 180 181 // Helper method to disallow deferring in |loader_|. 182 void DisallowLoaderDefer() { 183 if (loader_->deferred_) { 184 EXPECT_CALL(*url_loader_, setDefersLoading(false)); 185 EXPECT_CALL(*this, NetworkCallback()); 186 } 187 loader_->SetAllowDefer(false); 188 } 189 190 // Helper method to allow deferring in |loader_|. 191 void AllowLoaderDefer() { 192 loader_->SetAllowDefer(true); 193 } 194 195 MOCK_METHOD1(StartCallback, void(int error)); 196 MOCK_METHOD1(ReadCallback, void(int error)); 197 MOCK_METHOD0(NetworkCallback, void()); 198 199 protected: 200 GURL gurl_; 201 int64 first_position_; 202 int64 last_position_; 203 204 scoped_refptr<BufferedResourceLoader> loader_; 205 NiceMock<MockWebURLLoader>* url_loader_; 206 scoped_ptr<NiceMock<MockWebFrame> > frame_; 207 208 uint8 data_[kDataSize]; 209 210 private: 211 DISALLOW_COPY_AND_ASSIGN(BufferedResourceLoaderTest); 212}; 213 214TEST_F(BufferedResourceLoaderTest, StartStop) { 215 Initialize(kHttpUrl, -1, -1); 216 Start(); 217 StopWhenLoad(); 218} 219 220// Tests that a bad HTTP response is recived, e.g. file not found. 221TEST_F(BufferedResourceLoaderTest, BadHttpResponse) { 222 Initialize(kHttpUrl, -1, -1); 223 Start(); 224 225 EXPECT_CALL(*this, StartCallback(net::ERR_FAILED)); 226 EXPECT_CALL(*url_loader_, cancel()) 227 .WillOnce(RequestCanceled(loader_)); 228 229 WebURLResponse response(gurl_); 230 response.setHTTPStatusCode(404); 231 response.setHTTPStatusText("Not Found\n"); 232 loader_->didReceiveResponse(url_loader_, response); 233} 234 235// Tests that partial content is requested but not fulfilled. 236TEST_F(BufferedResourceLoaderTest, NotPartialResponse) { 237 Initialize(kHttpUrl, 100, -1); 238 Start(); 239 FullResponse(1024); 240 StopWhenLoad(); 241} 242 243// Tests that a 200 response is received. 244TEST_F(BufferedResourceLoaderTest, FullResponse) { 245 Initialize(kHttpUrl, -1, -1); 246 Start(); 247 FullResponse(1024); 248 StopWhenLoad(); 249} 250 251// Tests that a partial content response is received. 252TEST_F(BufferedResourceLoaderTest, PartialResponse) { 253 Initialize(kHttpUrl, 100, 200); 254 Start(); 255 PartialResponse(100, 200, 1024); 256 StopWhenLoad(); 257} 258 259// Tests that an invalid partial response is received. 260TEST_F(BufferedResourceLoaderTest, InvalidPartialResponse) { 261 Initialize(kHttpUrl, 0, 10); 262 Start(); 263 264 EXPECT_CALL(*this, StartCallback(net::ERR_INVALID_RESPONSE)); 265 EXPECT_CALL(*url_loader_, cancel()) 266 .WillOnce(RequestCanceled(loader_)); 267 268 WebURLResponse response(gurl_); 269 response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"), 270 WebString::fromUTF8(base::StringPrintf("bytes " 271 "%d-%d/%d", 1, 10, 1024))); 272 response.setExpectedContentLength(10); 273 response.setHTTPStatusCode(kHttpPartialContent); 274 loader_->didReceiveResponse(url_loader_, response); 275} 276 277// Tests the logic of sliding window for data buffering and reading. 278TEST_F(BufferedResourceLoaderTest, BufferAndRead) { 279 Initialize(kHttpUrl, 10, 29); 280 Start(); 281 PartialResponse(10, 29, 30); 282 283 uint8 buffer[10]; 284 InSequence s; 285 286 // Writes 10 bytes and read them back. 287 WriteLoader(10, 10); 288 EXPECT_CALL(*this, ReadCallback(10)); 289 ReadLoader(10, 10, buffer); 290 VerifyBuffer(buffer, 10, 10); 291 292 // Writes 10 bytes and read 2 times. 293 WriteLoader(20, 10); 294 EXPECT_CALL(*this, ReadCallback(5)); 295 ReadLoader(20, 5, buffer); 296 VerifyBuffer(buffer, 20, 5); 297 EXPECT_CALL(*this, ReadCallback(5)); 298 ReadLoader(25, 5, buffer); 299 VerifyBuffer(buffer, 25, 5); 300 301 // Read backward within buffer. 302 EXPECT_CALL(*this, ReadCallback(10)); 303 ReadLoader(10, 10, buffer); 304 VerifyBuffer(buffer, 10, 10); 305 306 // Read backward outside buffer. 307 EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS)); 308 ReadLoader(9, 10, buffer); 309 310 // Response has completed. 311 EXPECT_CALL(*this, NetworkCallback()); 312 loader_->didFinishLoading(url_loader_, 0); 313 314 // Try to read 10 from position 25 will just return with 5 bytes. 315 EXPECT_CALL(*this, ReadCallback(5)); 316 ReadLoader(25, 10, buffer); 317 VerifyBuffer(buffer, 25, 5); 318 319 // Try to read outside buffered range after request has completed. 320 EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS)); 321 ReadLoader(5, 10, buffer); 322 323 // Try to read beyond the instance size. 324 EXPECT_CALL(*this, ReadCallback(0)); 325 ReadLoader(30, 10, buffer); 326} 327 328TEST_F(BufferedResourceLoaderTest, ReadOutsideBuffer) { 329 Initialize(kHttpUrl, 10, 0x00FFFFFF); 330 Start(); 331 PartialResponse(10, 0x00FFFFFF, 0x01000000); 332 333 uint8 buffer[10]; 334 InSequence s; 335 336 // Read very far aheard will get a cache miss. 337 EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS)); 338 ReadLoader(0x00FFFFFF, 1, buffer); 339 340 // The following call will not call ReadCallback() because it is waiting for 341 // data to arrive. 342 ReadLoader(10, 10, buffer); 343 344 // Writing to loader will fulfill the read request. 345 EXPECT_CALL(*this, ReadCallback(10)); 346 WriteLoader(10, 20); 347 VerifyBuffer(buffer, 10, 10); 348 349 // The following call cannot be fulfilled now. 350 ReadLoader(25, 10, buffer); 351 352 EXPECT_CALL(*this, ReadCallback(5)); 353 EXPECT_CALL(*this, NetworkCallback()); 354 loader_->didFinishLoading(url_loader_, 0); 355} 356 357TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) { 358 Initialize(kHttpUrl, 10, 29); 359 Start(); 360 PartialResponse(10, 29, 30); 361 362 uint8 buffer[10]; 363 InSequence s; 364 365 ReadLoader(10, 10, buffer); 366 EXPECT_CALL(*this, ReadCallback(net::ERR_FAILED)); 367 EXPECT_CALL(*this, NetworkCallback()); 368 WebURLError error; 369 error.reason = net::ERR_FAILED; 370 loader_->didFail(url_loader_, error); 371} 372 373// Tests the logic of caching data to disk when media is paused. 374TEST_F(BufferedResourceLoaderTest, AllowDefer_NoDataReceived) { 375 Initialize(kHttpUrl, 10, 99); 376 SetLoaderBuffer(10, 20); 377 Start(); 378 PartialResponse(10, 99, 100); 379 380 // Start in undeferred state, then disallow defer, then allow defer 381 // without receiving data in between. 382 DisallowLoaderDefer(); 383 AllowLoaderDefer(); 384 StopWhenLoad(); 385} 386 387TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadSameWindow) { 388 Initialize(kHttpUrl, 10, 99); 389 SetLoaderBuffer(10, 20); 390 Start(); 391 PartialResponse(10, 99, 100); 392 393 uint8 buffer[10]; 394 395 // Start in undeferred state, disallow defer, receive data but don't shift 396 // buffer window, then allow defer and read. 397 DisallowLoaderDefer(); 398 WriteLoader(10, 10); 399 AllowLoaderDefer(); 400 401 EXPECT_CALL(*this, ReadCallback(10)); 402 ReadLoader(10, 10, buffer); 403 VerifyBuffer(buffer, 10, 10); 404 StopWhenLoad(); 405} 406 407TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadPastWindow) { 408 Initialize(kHttpUrl, 10, 99); 409 SetLoaderBuffer(10, 20); 410 Start(); 411 PartialResponse(10, 99, 100); 412 413 uint8 buffer[10]; 414 415 // Not deferred, disallow defer, received data and shift buffer window, 416 // allow defer, then read in area outside of buffer window. 417 DisallowLoaderDefer(); 418 WriteLoader(10, 10); 419 WriteLoader(20, 50); 420 AllowLoaderDefer(); 421 422 EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS)); 423 ReadLoader(10, 10, buffer); 424 StopWhenLoad(); 425} 426 427TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredNoDataReceived) { 428 Initialize(kHttpUrl, 10, 99); 429 SetLoaderBuffer(10, 20); 430 Start(); 431 PartialResponse(10, 99, 100); 432 433 uint8 buffer[10]; 434 435 // Start in deferred state, then disallow defer, receive no data, and 436 // allow defer and read. 437 EXPECT_CALL(*url_loader_, setDefersLoading(true)); 438 EXPECT_CALL(*this, NetworkCallback()); 439 WriteLoader(10, 40); 440 441 DisallowLoaderDefer(); 442 AllowLoaderDefer(); 443 444 EXPECT_CALL(*this, ReadCallback(10)); 445 ReadLoader(20, 10, buffer); 446 VerifyBuffer(buffer, 20, 10); 447 StopWhenLoad(); 448} 449 450TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadSameWindow) { 451 Initialize(kHttpUrl, 10, 99); 452 SetLoaderBuffer(10, 20); 453 Start(); 454 PartialResponse(10, 99, 100); 455 456 uint8 buffer[10]; 457 458 // Start in deferred state, disallow defer, receive data and shift buffer 459 // window, allow defer, and read in a place that's still in the window. 460 EXPECT_CALL(*url_loader_, setDefersLoading(true)); 461 EXPECT_CALL(*this, NetworkCallback()); 462 WriteLoader(10, 30); 463 464 DisallowLoaderDefer(); 465 WriteLoader(40, 5); 466 AllowLoaderDefer(); 467 468 EXPECT_CALL(*this, ReadCallback(10)); 469 ReadLoader(20, 10, buffer); 470 VerifyBuffer(buffer, 20, 10); 471 StopWhenLoad(); 472} 473 474TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadPastWindow) { 475 Initialize(kHttpUrl, 10, 99); 476 SetLoaderBuffer(10, 20); 477 Start(); 478 PartialResponse(10, 99, 100); 479 480 uint8 buffer[10]; 481 482 // Start in deferred state, disallow defer, receive data and shift buffer 483 // window, allow defer, and read outside of the buffer window. 484 EXPECT_CALL(*url_loader_, setDefersLoading(true)); 485 EXPECT_CALL(*this, NetworkCallback()); 486 WriteLoader(10, 40); 487 488 DisallowLoaderDefer(); 489 WriteLoader(50, 20); 490 WriteLoader(70, 40); 491 AllowLoaderDefer(); 492 493 EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS)); 494 ReadLoader(20, 5, buffer); 495 StopWhenLoad(); 496} 497 498// NOTE: This test will need to be reworked a little once 499// http://code.google.com/p/chromium/issues/detail?id=72578 500// is fixed. 501TEST_F(BufferedResourceLoaderTest, HasSingleOrigin) { 502 // Make sure no redirect case works as expected. 503 Initialize(kHttpUrl, -1, -1); 504 Start(); 505 FullResponse(1024); 506 EXPECT_TRUE(loader_->HasSingleOrigin()); 507 StopWhenLoad(); 508 509 // Test redirect to the same domain. 510 Initialize(kHttpUrl, -1, -1); 511 Start(); 512 Redirect(kHttpRedirectToSameDomainUrl1); 513 FullResponse(1024); 514 EXPECT_TRUE(loader_->HasSingleOrigin()); 515 StopWhenLoad(); 516 517 // Test redirect twice to the same domain. 518 Initialize(kHttpUrl, -1, -1); 519 Start(); 520 Redirect(kHttpRedirectToSameDomainUrl1); 521 Redirect(kHttpRedirectToSameDomainUrl2); 522 FullResponse(1024); 523 EXPECT_TRUE(loader_->HasSingleOrigin()); 524 StopWhenLoad(); 525 526 // Test redirect to a different domain. 527 Initialize(kHttpUrl, -1, -1); 528 Start(); 529 Redirect(kHttpRedirectToDifferentDomainUrl1); 530 FullResponse(1024); 531 EXPECT_FALSE(loader_->HasSingleOrigin()); 532 StopWhenLoad(); 533 534 // Test redirect twice to a different domain. 535 Initialize(kHttpUrl, -1, -1); 536 Start(); 537 Redirect(kHttpRedirectToDifferentDomainUrl1); 538 Redirect(kHttpRedirectToDifferentDomainUrl2); 539 FullResponse(1024); 540 EXPECT_FALSE(loader_->HasSingleOrigin()); 541 StopWhenLoad(); 542 543 // Test to a different domain and then back to the same domain. 544 // NOTE: A different origin was encountered at least once so that 545 // makes HasSingleOrigin() become false. 546 Initialize(kHttpUrl, -1, -1); 547 Start(); 548 Redirect(kHttpRedirectToDifferentDomainUrl1); 549 Redirect(kHttpRedirectToSameDomainUrl1); 550 FullResponse(1024); 551 EXPECT_FALSE(loader_->HasSingleOrigin()); 552 StopWhenLoad(); 553} 554 555// TODO(hclam): add unit test for defer loading. 556 557} // namespace webkit_glue 558