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