1 // Copyright 2014 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 <stack> 6 #include <string> 7 #include <utility> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/callback.h" 12 #include "base/compiler_specific.h" 13 #include "base/pickle.h" 14 #include "base/synchronization/waitable_event.h" 15 #include "base/threading/thread.h" 16 #include "content/browser/appcache/appcache_response.h" 17 #include "content/browser/appcache/mock_appcache_service.h" 18 #include "net/base/io_buffer.h" 19 #include "net/base/net_errors.h" 20 #include "net/http/http_response_headers.h" 21 #include "testing/gtest/include/gtest/gtest.h" 22 23 using net::IOBuffer; 24 using net::WrappedIOBuffer; 25 26 namespace content { 27 28 static const int kNumBlocks = 4; 29 static const int kBlockSize = 1024; 30 static const int kNoSuchResponseId = 123; 31 32 class AppCacheResponseTest : public testing::Test { 33 public: 34 // Test Harness ------------------------------------------------------------- 35 36 // Helper class used to verify test results 37 class MockStorageDelegate : public AppCacheStorage::Delegate { 38 public: 39 explicit MockStorageDelegate(AppCacheResponseTest* test) 40 : loaded_info_id_(0), test_(test) { 41 } 42 43 virtual void OnResponseInfoLoaded(AppCacheResponseInfo* info, 44 int64 response_id) OVERRIDE { 45 loaded_info_ = info; 46 loaded_info_id_ = response_id; 47 test_->ScheduleNextTask(); 48 } 49 50 scoped_refptr<AppCacheResponseInfo> loaded_info_; 51 int64 loaded_info_id_; 52 AppCacheResponseTest* test_; 53 }; 54 55 // Helper callback to run a test on our io_thread. The io_thread is spun up 56 // once and reused for all tests. 57 template <class Method> 58 void MethodWrapper(Method method) { 59 SetUpTest(); 60 (this->*method)(); 61 } 62 63 static void SetUpTestCase() { 64 io_thread_.reset(new base::Thread("AppCacheResponseTest Thread")); 65 base::Thread::Options options(base::MessageLoop::TYPE_IO, 0); 66 io_thread_->StartWithOptions(options); 67 } 68 69 static void TearDownTestCase() { 70 io_thread_.reset(NULL); 71 } 72 73 AppCacheResponseTest() {} 74 75 template <class Method> 76 void RunTestOnIOThread(Method method) { 77 test_finished_event_ .reset(new base::WaitableEvent(false, false)); 78 io_thread_->message_loop()->PostTask( 79 FROM_HERE, base::Bind(&AppCacheResponseTest::MethodWrapper<Method>, 80 base::Unretained(this), method)); 81 test_finished_event_->Wait(); 82 } 83 84 void SetUpTest() { 85 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 86 DCHECK(task_stack_.empty()); 87 storage_delegate_.reset(new MockStorageDelegate(this)); 88 service_.reset(new MockAppCacheService()); 89 expected_read_result_ = 0; 90 expected_write_result_ = 0; 91 written_response_id_ = 0; 92 should_delete_reader_in_completion_callback_ = false; 93 should_delete_writer_in_completion_callback_ = false; 94 reader_deletion_count_down_ = 0; 95 writer_deletion_count_down_ = 0; 96 read_callback_was_called_ = false; 97 write_callback_was_called_ = false; 98 } 99 100 void TearDownTest() { 101 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 102 while (!task_stack_.empty()) 103 task_stack_.pop(); 104 105 reader_.reset(); 106 read_buffer_ = NULL; 107 read_info_buffer_ = NULL; 108 writer_.reset(); 109 write_buffer_ = NULL; 110 write_info_buffer_ = NULL; 111 storage_delegate_.reset(); 112 service_.reset(); 113 } 114 115 void TestFinished() { 116 // We unwind the stack prior to finishing up to let stack 117 // based objects get deleted. 118 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 119 base::MessageLoop::current()->PostTask( 120 FROM_HERE, base::Bind(&AppCacheResponseTest::TestFinishedUnwound, 121 base::Unretained(this))); 122 } 123 124 void TestFinishedUnwound() { 125 TearDownTest(); 126 test_finished_event_->Signal(); 127 } 128 129 void PushNextTask(const base::Closure& task) { 130 task_stack_.push(std::pair<base::Closure, bool>(task, false)); 131 } 132 133 void PushNextTaskAsImmediate(const base::Closure& task) { 134 task_stack_.push(std::pair<base::Closure, bool>(task, true)); 135 } 136 137 void ScheduleNextTask() { 138 DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); 139 if (task_stack_.empty()) { 140 TestFinished(); 141 return; 142 } 143 base::Closure task = task_stack_.top().first; 144 bool immediate = task_stack_.top().second; 145 task_stack_.pop(); 146 if (immediate) 147 task.Run(); 148 else 149 base::MessageLoop::current()->PostTask(FROM_HERE, task); 150 } 151 152 // Wrappers to call AppCacheResponseReader/Writer Read and Write methods 153 154 void WriteBasicResponse() { 155 static const char kHttpHeaders[] = 156 "HTTP/1.0 200 OK\0Content-Length: 5\0\0"; 157 static const char kHttpBody[] = "Hello"; 158 scoped_refptr<IOBuffer> body(new WrappedIOBuffer(kHttpBody)); 159 std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders)); 160 WriteResponse( 161 MakeHttpResponseInfo(raw_headers), body.get(), strlen(kHttpBody)); 162 } 163 164 int basic_response_size() { return 5; } // should match kHttpBody above 165 166 void WriteResponse(net::HttpResponseInfo* head, 167 IOBuffer* body, int body_len) { 168 DCHECK(body); 169 scoped_refptr<IOBuffer> body_ref(body); 170 PushNextTask(base::Bind(&AppCacheResponseTest::WriteResponseBody, 171 base::Unretained(this), body_ref, body_len)); 172 WriteResponseHead(head); 173 } 174 175 void WriteResponseHead(net::HttpResponseInfo* head) { 176 EXPECT_FALSE(writer_->IsWritePending()); 177 expected_write_result_ = GetHttpResponseInfoSize(head); 178 write_info_buffer_ = new HttpResponseInfoIOBuffer(head); 179 writer_->WriteInfo(write_info_buffer_.get(), 180 base::Bind(&AppCacheResponseTest::OnWriteInfoComplete, 181 base::Unretained(this))); 182 } 183 184 void WriteResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) { 185 EXPECT_FALSE(writer_->IsWritePending()); 186 write_buffer_ = io_buffer; 187 expected_write_result_ = buf_len; 188 writer_->WriteData(write_buffer_.get(), 189 buf_len, 190 base::Bind(&AppCacheResponseTest::OnWriteComplete, 191 base::Unretained(this))); 192 } 193 194 void ReadResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) { 195 EXPECT_FALSE(reader_->IsReadPending()); 196 read_buffer_ = io_buffer; 197 expected_read_result_ = buf_len; 198 reader_->ReadData(read_buffer_.get(), 199 buf_len, 200 base::Bind(&AppCacheResponseTest::OnReadComplete, 201 base::Unretained(this))); 202 } 203 204 // AppCacheResponseReader / Writer completion callbacks 205 206 void OnWriteInfoComplete(int result) { 207 EXPECT_FALSE(writer_->IsWritePending()); 208 EXPECT_EQ(expected_write_result_, result); 209 ScheduleNextTask(); 210 } 211 212 void OnWriteComplete(int result) { 213 EXPECT_FALSE(writer_->IsWritePending()); 214 write_callback_was_called_ = true; 215 EXPECT_EQ(expected_write_result_, result); 216 if (should_delete_writer_in_completion_callback_ && 217 --writer_deletion_count_down_ == 0) { 218 writer_.reset(); 219 } 220 ScheduleNextTask(); 221 } 222 223 void OnReadInfoComplete(int result) { 224 EXPECT_FALSE(reader_->IsReadPending()); 225 EXPECT_EQ(expected_read_result_, result); 226 ScheduleNextTask(); 227 } 228 229 void OnReadComplete(int result) { 230 EXPECT_FALSE(reader_->IsReadPending()); 231 read_callback_was_called_ = true; 232 EXPECT_EQ(expected_read_result_, result); 233 if (should_delete_reader_in_completion_callback_ && 234 --reader_deletion_count_down_ == 0) { 235 reader_.reset(); 236 } 237 ScheduleNextTask(); 238 } 239 240 // Helpers to work with HttpResponseInfo objects 241 242 net::HttpResponseInfo* MakeHttpResponseInfo(const std::string& raw_headers) { 243 net::HttpResponseInfo* info = new net::HttpResponseInfo; 244 info->request_time = base::Time::Now(); 245 info->response_time = base::Time::Now(); 246 info->was_cached = false; 247 info->headers = new net::HttpResponseHeaders(raw_headers); 248 return info; 249 } 250 251 int GetHttpResponseInfoSize(const net::HttpResponseInfo* info) { 252 Pickle pickle; 253 return PickleHttpResonseInfo(&pickle, info); 254 } 255 256 bool CompareHttpResponseInfos(const net::HttpResponseInfo* info1, 257 const net::HttpResponseInfo* info2) { 258 Pickle pickle1; 259 Pickle pickle2; 260 PickleHttpResonseInfo(&pickle1, info1); 261 PickleHttpResonseInfo(&pickle2, info2); 262 return (pickle1.size() == pickle2.size()) && 263 (0 == memcmp(pickle1.data(), pickle2.data(), pickle1.size())); 264 } 265 266 int PickleHttpResonseInfo(Pickle* pickle, const net::HttpResponseInfo* info) { 267 const bool kSkipTransientHeaders = true; 268 const bool kTruncated = false; 269 info->Persist(pickle, kSkipTransientHeaders, kTruncated); 270 return pickle->size(); 271 } 272 273 // Helpers to fill and verify blocks of memory with a value 274 275 void FillData(char value, char* data, int data_len) { 276 memset(data, value, data_len); 277 } 278 279 bool CheckData(char value, const char* data, int data_len) { 280 for (int i = 0; i < data_len; ++i, ++data) { 281 if (*data != value) 282 return false; 283 } 284 return true; 285 } 286 287 // Individual Tests --------------------------------------------------------- 288 // Most of the individual tests involve multiple async steps. Each test 289 // is delineated with a section header. 290 291 292 // ReadNonExistentResponse ------------------------------------------- 293 void ReadNonExistentResponse() { 294 // 1. Attempt to ReadInfo 295 // 2. Attempt to ReadData 296 297 reader_.reset(service_->storage()->CreateResponseReader( 298 GURL(), 0, kNoSuchResponseId)); 299 300 // Push tasks in reverse order 301 PushNextTask(base::Bind(&AppCacheResponseTest::ReadNonExistentData, 302 base::Unretained(this))); 303 PushNextTask(base::Bind(&AppCacheResponseTest::ReadNonExistentInfo, 304 base::Unretained(this))); 305 ScheduleNextTask(); 306 } 307 308 void ReadNonExistentInfo() { 309 EXPECT_FALSE(reader_->IsReadPending()); 310 read_info_buffer_ = new HttpResponseInfoIOBuffer(); 311 reader_->ReadInfo(read_info_buffer_.get(), 312 base::Bind(&AppCacheResponseTest::OnReadInfoComplete, 313 base::Unretained(this))); 314 EXPECT_TRUE(reader_->IsReadPending()); 315 expected_read_result_ = net::ERR_CACHE_MISS; 316 } 317 318 void ReadNonExistentData() { 319 EXPECT_FALSE(reader_->IsReadPending()); 320 read_buffer_ = new IOBuffer(kBlockSize); 321 reader_->ReadData(read_buffer_.get(), 322 kBlockSize, 323 base::Bind(&AppCacheResponseTest::OnReadComplete, 324 base::Unretained(this))); 325 EXPECT_TRUE(reader_->IsReadPending()); 326 expected_read_result_ = net::ERR_CACHE_MISS; 327 } 328 329 // LoadResponseInfo_Miss ---------------------------------------------------- 330 void LoadResponseInfo_Miss() { 331 PushNextTask(base::Bind(&AppCacheResponseTest::LoadResponseInfo_Miss_Verify, 332 base::Unretained(this))); 333 service_->storage()->LoadResponseInfo(GURL(), 0, kNoSuchResponseId, 334 storage_delegate_.get()); 335 } 336 337 void LoadResponseInfo_Miss_Verify() { 338 EXPECT_EQ(kNoSuchResponseId, storage_delegate_->loaded_info_id_); 339 EXPECT_TRUE(!storage_delegate_->loaded_info_.get()); 340 TestFinished(); 341 } 342 343 // LoadResponseInfo_Hit ---------------------------------------------------- 344 void LoadResponseInfo_Hit() { 345 // This tests involves multiple async steps. 346 // 1. Write a response headers and body to storage 347 // a. headers 348 // b. body 349 // 2. Use LoadResponseInfo to read the response headers back out 350 PushNextTask(base::Bind(&AppCacheResponseTest::LoadResponseInfo_Hit_Step2, 351 base::Unretained(this))); 352 writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); 353 written_response_id_ = writer_->response_id(); 354 WriteBasicResponse(); 355 } 356 357 void LoadResponseInfo_Hit_Step2() { 358 writer_.reset(); 359 PushNextTask(base::Bind(&AppCacheResponseTest::LoadResponseInfo_Hit_Verify, 360 base::Unretained(this))); 361 service_->storage()->LoadResponseInfo(GURL(), 0, written_response_id_, 362 storage_delegate_.get()); 363 } 364 365 void LoadResponseInfo_Hit_Verify() { 366 EXPECT_EQ(written_response_id_, storage_delegate_->loaded_info_id_); 367 EXPECT_TRUE(storage_delegate_->loaded_info_.get()); 368 EXPECT_TRUE(CompareHttpResponseInfos( 369 write_info_buffer_->http_info.get(), 370 storage_delegate_->loaded_info_->http_response_info())); 371 EXPECT_EQ(basic_response_size(), 372 storage_delegate_->loaded_info_->response_data_size()); 373 TestFinished(); 374 } 375 376 // AmountWritten ---------------------------------------------------- 377 378 void AmountWritten() { 379 static const char kHttpHeaders[] = "HTTP/1.0 200 OK\0\0"; 380 std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders)); 381 net::HttpResponseInfo* head = MakeHttpResponseInfo(raw_headers); 382 int expected_amount_written = 383 GetHttpResponseInfoSize(head) + kNumBlocks * kBlockSize; 384 385 // Push tasks in reverse order. 386 PushNextTask(base::Bind(&AppCacheResponseTest::Verify_AmountWritten, 387 base::Unretained(this), expected_amount_written)); 388 for (int i = 0; i < kNumBlocks; ++i) { 389 PushNextTask(base::Bind(&AppCacheResponseTest::WriteOneBlock, 390 base::Unretained(this), kNumBlocks - i)); 391 } 392 PushNextTask(base::Bind(&AppCacheResponseTest::WriteResponseHead, 393 base::Unretained(this), head)); 394 395 writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); 396 written_response_id_ = writer_->response_id(); 397 ScheduleNextTask(); 398 } 399 400 void Verify_AmountWritten(int expected_amount_written) { 401 EXPECT_EQ(expected_amount_written, writer_->amount_written()); 402 TestFinished(); 403 } 404 405 406 // WriteThenVariouslyReadResponse ------------------------------------------- 407 408 void WriteThenVariouslyReadResponse() { 409 // This tests involves multiple async steps. 410 // 1. First, write a large body using multiple writes, we don't bother 411 // with a response head for this test. 412 // 2. Read the entire body, using multiple reads 413 // 3. Read the entire body, using one read. 414 // 4. Attempt to read beyond the EOF. 415 // 5. Read just a range. 416 // 6. Attempt to read beyond EOF of a range. 417 418 // Push tasks in reverse order 419 PushNextTask(base::Bind(&AppCacheResponseTest::ReadRangeFullyBeyondEOF, 420 base::Unretained(this))); 421 PushNextTask(base::Bind(&AppCacheResponseTest::ReadRangePartiallyBeyondEOF, 422 base::Unretained(this))); 423 PushNextTask(base::Bind(&AppCacheResponseTest::ReadPastEOF, 424 base::Unretained(this))); 425 PushNextTask(base::Bind(&AppCacheResponseTest::ReadRange, 426 base::Unretained(this))); 427 PushNextTask(base::Bind(&AppCacheResponseTest::ReadPastEOF, 428 base::Unretained(this))); 429 PushNextTask(base::Bind(&AppCacheResponseTest::ReadAllAtOnce, 430 base::Unretained(this))); 431 PushNextTask(base::Bind(&AppCacheResponseTest::ReadInBlocks, 432 base::Unretained(this))); 433 PushNextTask(base::Bind(&AppCacheResponseTest::WriteOutBlocks, 434 base::Unretained(this))); 435 436 // Get them going. 437 ScheduleNextTask(); 438 } 439 440 void WriteOutBlocks() { 441 writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); 442 written_response_id_ = writer_->response_id(); 443 for (int i = 0; i < kNumBlocks; ++i) { 444 PushNextTask(base::Bind(&AppCacheResponseTest::WriteOneBlock, 445 base::Unretained(this), kNumBlocks - i)); 446 } 447 ScheduleNextTask(); 448 } 449 450 void WriteOneBlock(int block_number) { 451 scoped_refptr<IOBuffer> io_buffer( 452 new IOBuffer(kBlockSize)); 453 FillData(block_number, io_buffer->data(), kBlockSize); 454 WriteResponseBody(io_buffer, kBlockSize); 455 } 456 457 void ReadInBlocks() { 458 writer_.reset(); 459 reader_.reset(service_->storage()->CreateResponseReader( 460 GURL(), 0, written_response_id_)); 461 for (int i = 0; i < kNumBlocks; ++i) { 462 PushNextTask(base::Bind(&AppCacheResponseTest::ReadOneBlock, 463 base::Unretained(this), kNumBlocks - i)); 464 } 465 ScheduleNextTask(); 466 } 467 468 void ReadOneBlock(int block_number) { 469 PushNextTask(base::Bind(&AppCacheResponseTest::VerifyOneBlock, 470 base::Unretained(this), block_number)); 471 ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize); 472 } 473 474 void VerifyOneBlock(int block_number) { 475 EXPECT_TRUE(CheckData(block_number, read_buffer_->data(), kBlockSize)); 476 ScheduleNextTask(); 477 } 478 479 void ReadAllAtOnce() { 480 PushNextTask(base::Bind(&AppCacheResponseTest::VerifyAllAtOnce, 481 base::Unretained(this))); 482 reader_.reset(service_->storage()->CreateResponseReader( 483 GURL(), 0, written_response_id_)); 484 int big_size = kNumBlocks * kBlockSize; 485 ReadResponseBody(new IOBuffer(big_size), big_size); 486 } 487 488 void VerifyAllAtOnce() { 489 char* p = read_buffer_->data(); 490 for (int i = 0; i < kNumBlocks; ++i, p += kBlockSize) 491 EXPECT_TRUE(CheckData(i + 1, p, kBlockSize)); 492 ScheduleNextTask(); 493 } 494 495 void ReadPastEOF() { 496 EXPECT_FALSE(reader_->IsReadPending()); 497 read_buffer_ = new IOBuffer(kBlockSize); 498 expected_read_result_ = 0; 499 reader_->ReadData(read_buffer_.get(), 500 kBlockSize, 501 base::Bind(&AppCacheResponseTest::OnReadComplete, 502 base::Unretained(this))); 503 } 504 505 void ReadRange() { 506 PushNextTask(base::Bind(&AppCacheResponseTest::VerifyRange, 507 base::Unretained(this))); 508 reader_.reset(service_->storage()->CreateResponseReader( 509 GURL(), 0, written_response_id_)); 510 reader_->SetReadRange(kBlockSize, kBlockSize); 511 ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize); 512 } 513 514 void VerifyRange() { 515 EXPECT_TRUE(CheckData(2, read_buffer_->data(), kBlockSize)); 516 ScheduleNextTask(); // ReadPastEOF is scheduled next 517 } 518 519 void ReadRangePartiallyBeyondEOF() { 520 PushNextTask(base::Bind(&AppCacheResponseTest::VerifyRangeBeyondEOF, 521 base::Unretained(this))); 522 reader_.reset(service_->storage()->CreateResponseReader( 523 GURL(), 0, written_response_id_)); 524 reader_->SetReadRange(kBlockSize, kNumBlocks * kBlockSize); 525 ReadResponseBody(new IOBuffer(kNumBlocks * kBlockSize), 526 kNumBlocks * kBlockSize); 527 expected_read_result_ = (kNumBlocks - 1) * kBlockSize; 528 } 529 530 void VerifyRangeBeyondEOF() { 531 // Just verify the first 1k 532 VerifyRange(); 533 } 534 535 void ReadRangeFullyBeyondEOF() { 536 reader_.reset(service_->storage()->CreateResponseReader( 537 GURL(), 0, written_response_id_)); 538 reader_->SetReadRange((kNumBlocks * kBlockSize) + 1, kBlockSize); 539 ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize); 540 expected_read_result_ = 0; 541 } 542 543 // IOChaining ------------------------------------------- 544 void IOChaining() { 545 // 1. Write several blocks out initiating the subsequent write 546 // from within the completion callback of the previous write. 547 // 2. Read and verify several blocks in similarly chaining reads. 548 549 // Push tasks in reverse order 550 PushNextTaskAsImmediate( 551 base::Bind(&AppCacheResponseTest::ReadInBlocksImmediately, 552 base::Unretained(this))); 553 PushNextTaskAsImmediate( 554 base::Bind(&AppCacheResponseTest::WriteOutBlocksImmediately, 555 base::Unretained(this))); 556 557 // Get them going. 558 ScheduleNextTask(); 559 } 560 561 void WriteOutBlocksImmediately() { 562 writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); 563 written_response_id_ = writer_->response_id(); 564 for (int i = 0; i < kNumBlocks; ++i) { 565 PushNextTaskAsImmediate( 566 base::Bind(&AppCacheResponseTest::WriteOneBlock, 567 base::Unretained(this), kNumBlocks - i)); 568 } 569 ScheduleNextTask(); 570 } 571 572 void ReadInBlocksImmediately() { 573 writer_.reset(); 574 reader_.reset(service_->storage()->CreateResponseReader( 575 GURL(), 0, written_response_id_)); 576 for (int i = 0; i < kNumBlocks; ++i) { 577 PushNextTaskAsImmediate( 578 base::Bind(&AppCacheResponseTest::ReadOneBlockImmediately, 579 base::Unretained(this), 580 kNumBlocks - i)); 581 } 582 ScheduleNextTask(); 583 } 584 585 void ReadOneBlockImmediately(int block_number) { 586 PushNextTaskAsImmediate(base::Bind(&AppCacheResponseTest::VerifyOneBlock, 587 base::Unretained(this), block_number)); 588 ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize); 589 } 590 591 // DeleteWithinCallbacks ------------------------------------------- 592 void DeleteWithinCallbacks() { 593 // 1. Write out a few blocks normally, and upon 594 // completion of the last write, delete the writer. 595 // 2. Read in a few blocks normally, and upon completion 596 // of the last read, delete the reader. 597 598 should_delete_reader_in_completion_callback_ = true; 599 reader_deletion_count_down_ = kNumBlocks; 600 should_delete_writer_in_completion_callback_ = true; 601 writer_deletion_count_down_ = kNumBlocks; 602 603 PushNextTask(base::Bind(&AppCacheResponseTest::ReadInBlocks, 604 base::Unretained(this))); 605 PushNextTask(base::Bind(&AppCacheResponseTest::WriteOutBlocks, 606 base::Unretained(this))); 607 ScheduleNextTask(); 608 } 609 610 // DeleteWithIOPending ------------------------------------------- 611 void DeleteWithIOPending() { 612 // 1. Write a few blocks normally. 613 // 2. Start a write, delete with it pending. 614 // 3. Start a read, delete with it pending. 615 PushNextTask(base::Bind(&AppCacheResponseTest::ReadThenDelete, 616 base::Unretained(this))); 617 PushNextTask(base::Bind(&AppCacheResponseTest::WriteThenDelete, 618 base::Unretained(this))); 619 PushNextTask(base::Bind(&AppCacheResponseTest::WriteOutBlocks, 620 base::Unretained(this))); 621 ScheduleNextTask(); 622 } 623 624 void WriteThenDelete() { 625 write_callback_was_called_ = false; 626 WriteOneBlock(5); 627 EXPECT_TRUE(writer_->IsWritePending()); 628 writer_.reset(); 629 ScheduleNextTask(); 630 } 631 632 void ReadThenDelete() { 633 read_callback_was_called_ = false; 634 reader_.reset(service_->storage()->CreateResponseReader( 635 GURL(), 0, written_response_id_)); 636 ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize); 637 EXPECT_TRUE(reader_->IsReadPending()); 638 reader_.reset(); 639 640 // Wait a moment to verify no callbacks. 641 base::MessageLoop::current()->PostDelayedTask( 642 FROM_HERE, base::Bind(&AppCacheResponseTest::VerifyNoCallbacks, 643 base::Unretained(this)), 644 base::TimeDelta::FromMilliseconds(10)); 645 } 646 647 void VerifyNoCallbacks() { 648 EXPECT_TRUE(!write_callback_was_called_); 649 EXPECT_TRUE(!read_callback_was_called_); 650 TestFinished(); 651 } 652 653 // Data members 654 655 scoped_ptr<base::WaitableEvent> test_finished_event_; 656 scoped_ptr<MockStorageDelegate> storage_delegate_; 657 scoped_ptr<MockAppCacheService> service_; 658 std::stack<std::pair<base::Closure, bool> > task_stack_; 659 660 scoped_ptr<AppCacheResponseReader> reader_; 661 scoped_refptr<HttpResponseInfoIOBuffer> read_info_buffer_; 662 scoped_refptr<IOBuffer> read_buffer_; 663 int expected_read_result_; 664 bool should_delete_reader_in_completion_callback_; 665 int reader_deletion_count_down_; 666 bool read_callback_was_called_; 667 668 int64 written_response_id_; 669 scoped_ptr<AppCacheResponseWriter> writer_; 670 scoped_refptr<HttpResponseInfoIOBuffer> write_info_buffer_; 671 scoped_refptr<IOBuffer> write_buffer_; 672 int expected_write_result_; 673 bool should_delete_writer_in_completion_callback_; 674 int writer_deletion_count_down_; 675 bool write_callback_was_called_; 676 677 static scoped_ptr<base::Thread> io_thread_; 678 }; 679 680 // static 681 scoped_ptr<base::Thread> AppCacheResponseTest::io_thread_; 682 683 TEST_F(AppCacheResponseTest, ReadNonExistentResponse) { 684 RunTestOnIOThread(&AppCacheResponseTest::ReadNonExistentResponse); 685 } 686 687 TEST_F(AppCacheResponseTest, LoadResponseInfo_Miss) { 688 RunTestOnIOThread(&AppCacheResponseTest::LoadResponseInfo_Miss); 689 } 690 691 TEST_F(AppCacheResponseTest, LoadResponseInfo_Hit) { 692 RunTestOnIOThread(&AppCacheResponseTest::LoadResponseInfo_Hit); 693 } 694 695 TEST_F(AppCacheResponseTest, AmountWritten) { 696 RunTestOnIOThread(&AppCacheResponseTest::AmountWritten); 697 } 698 699 TEST_F(AppCacheResponseTest, WriteThenVariouslyReadResponse) { 700 RunTestOnIOThread(&AppCacheResponseTest::WriteThenVariouslyReadResponse); 701 } 702 703 TEST_F(AppCacheResponseTest, IOChaining) { 704 RunTestOnIOThread(&AppCacheResponseTest::IOChaining); 705 } 706 707 TEST_F(AppCacheResponseTest, DeleteWithinCallbacks) { 708 RunTestOnIOThread(&AppCacheResponseTest::DeleteWithinCallbacks); 709 } 710 711 TEST_F(AppCacheResponseTest, DeleteWithIOPending) { 712 RunTestOnIOThread(&AppCacheResponseTest::DeleteWithIOPending); 713 } 714 715 } // namespace content 716