summaryrefslogtreecommitdiffstats
path: root/webkit/appcache/appcache_response_unittest.cc
diff options
context:
space:
mode:
authormichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-21 18:09:33 +0000
committermichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-21 18:09:33 +0000
commit073aabe9e48306216d979f03d6c2fc37d25d42e9 (patch)
treea9088a3c8be87fa57208074c7de65e998b865f94 /webkit/appcache/appcache_response_unittest.cc
parentc54d269e0b8a057533c1a1455a2acea0659629b9 (diff)
downloadchromium_src-073aabe9e48306216d979f03d6c2fc37d25d42e9.zip
chromium_src-073aabe9e48306216d979f03d6c2fc37d25d42e9.tar.gz
chromium_src-073aabe9e48306216d979f03d6c2fc37d25d42e9.tar.bz2
AppCacheResponse storage implementation
* classes AppCacheResponseReader and AppCacheResponseWriter * also implements AppCacheStorage.LoadResponseInfo() * using a memory-backed disk_cache for now, so not really on disk yet TEST=appcache_response_unittest.cc BUG=none Review URL: http://codereview.chromium.org/269062 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29670 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/appcache/appcache_response_unittest.cc')
-rw-r--r--webkit/appcache/appcache_response_unittest.cc716
1 files changed, 716 insertions, 0 deletions
diff --git a/webkit/appcache/appcache_response_unittest.cc b/webkit/appcache/appcache_response_unittest.cc
new file mode 100644
index 0000000..abc01a9
--- /dev/null
+++ b/webkit/appcache/appcache_response_unittest.cc
@@ -0,0 +1,716 @@
+// Copyright (c) 2009 The Chromium Authos. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/compiler_specific.h"
+#include "base/pickle.h"
+#include "base/thread.h"
+#include "base/waitable_event.h"
+#include "net/base/io_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/appcache/appcache_response.h"
+#include "webkit/appcache/mock_appcache_service.h"
+
+using net::IOBuffer;
+
+namespace appcache {
+
+class AppCacheResponseTest : public testing::Test {
+ public:
+
+ // Test Harness -------------------------------------------------------------
+
+ // Helper class used to verify test results
+ class MockStorageDelegate : public AppCacheStorage::Delegate {
+ public:
+ explicit MockStorageDelegate(AppCacheResponseTest* test)
+ : loaded_info_id_(0), test_(test) {
+ }
+
+ virtual void OnResponseInfoLoaded(AppCacheResponseInfo* info,
+ int64 response_id) {
+ loaded_info_ = info;
+ loaded_info_id_ = response_id;
+ test_->ScheduleNextTask();
+ }
+
+ scoped_refptr<AppCacheResponseInfo> loaded_info_;
+ int64 loaded_info_id_;
+ AppCacheResponseTest* test_;
+ };
+
+ // Helper class run a test on our io_thread. The io_thread
+ // is spun up once and reused for all tests.
+ template <class Method>
+ class WrapperTask : public Task {
+ public:
+ WrapperTask(AppCacheResponseTest* test, Method method)
+ : test_(test), method_(method) {
+ }
+
+ virtual void Run() {
+ test_->SetUpTest();
+ (test_->*method_)();
+ }
+
+ private:
+ AppCacheResponseTest* test_;
+ Method method_;
+ };
+
+ static void SetUpTestCase() {
+ io_thread_.reset(new base::Thread("AppCacheResponseTest Thread"));
+ base::Thread::Options options(MessageLoop::TYPE_IO, 0);
+ io_thread_->StartWithOptions(options);
+ }
+
+ static void TearDownTestCase() {
+ io_thread_.reset(NULL);
+ }
+
+ AppCacheResponseTest()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_(
+ this, &AppCacheResponseTest::OnReadComplete)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(read_info_callback_(
+ this, &AppCacheResponseTest::OnReadInfoComplete)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(write_callback_(
+ this, &AppCacheResponseTest::OnWriteComplete)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(write_info_callback_(
+ this, &AppCacheResponseTest::OnWriteInfoComplete)) {
+ }
+
+ template <class Method>
+ void RunTestOnIOThread(Method method) {
+ test_finished_event_ .reset(new base::WaitableEvent(false, false));
+ io_thread_->message_loop()->PostTask(
+ FROM_HERE, new WrapperTask<Method>(this, method));
+ test_finished_event_->Wait();
+ }
+
+ void SetUpTest() {
+ DCHECK(MessageLoop::current() == io_thread_->message_loop());
+ DCHECK(task_stack_.empty());
+ storage_delegate_.reset(new MockStorageDelegate(this));
+ service_.reset(new MockAppCacheService());
+ expected_read_result_ = 0;
+ expected_write_result_ = 0;
+ written_response_id_ = 0;
+ should_delete_reader_in_completion_callback_ = false;
+ should_delete_writer_in_completion_callback_ = false;
+ reader_deletion_count_down_ = 0;
+ writer_deletion_count_down_ = 0;
+ read_callback_was_called_ = false;
+ write_callback_was_called_ = false;
+ }
+
+ void TearDownTest() {
+ DCHECK(MessageLoop::current() == io_thread_->message_loop());
+ while (!task_stack_.empty()) {
+ delete task_stack_.top().first;
+ task_stack_.pop();
+ }
+ reader_.reset();
+ read_buffer_ = NULL;
+ read_info_buffer_ = NULL;
+ writer_.reset();
+ write_buffer_ = NULL;
+ write_info_buffer_ = NULL;
+ storage_delegate_.reset();
+ service_.reset();
+ }
+
+ void TestFinished() {
+ // We unwind the stack prior to finishing up to let stack
+ // based objects get deleted.
+ DCHECK(MessageLoop::current() == io_thread_->message_loop());
+ MessageLoop::current()->PostTask(FROM_HERE,
+ method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::TestFinishedUnwound));
+ }
+
+ void TestFinishedUnwound() {
+ TearDownTest();
+ test_finished_event_->Signal();
+ }
+
+ void PushNextTask(Task* task) {
+ task_stack_.push(std::pair<Task*, bool>(task, false));
+ }
+
+ void PushNextTaskAsImmediate(Task* task) {
+ task_stack_.push(std::pair<Task*, bool>(task, true));
+ }
+
+ void ScheduleNextTask() {
+ DCHECK(MessageLoop::current() == io_thread_->message_loop());
+ if (task_stack_.empty()) {
+ TestFinished();
+ return;
+ }
+ scoped_ptr<Task> task(task_stack_.top().first);
+ bool immediate = task_stack_.top().second;
+ task_stack_.pop();
+ if (immediate)
+ task->Run();
+ else
+ MessageLoop::current()->PostTask(FROM_HERE, task.release());
+ }
+
+ // Wrappers to call AppCacheResponseReader/Writer Read and Write methods
+
+ void WriteBasicResponse() {
+ static const char* kRawHttpHeaders =
+ "HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\n";
+ static const char* kRawHttpBody = "Hello";
+ WriteResponse(MakeHttpResponseInfo(kRawHttpHeaders), kRawHttpBody);
+ }
+
+ void WriteResponse(net::HttpResponseInfo* head, const char* body) {
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::WriteResponseBody,
+ new net::WrappedIOBuffer(body), strlen(body)));
+ WriteResponseHead(head);
+ }
+
+ void WriteResponseHead(net::HttpResponseInfo* head) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ expected_write_result_ = GetHttpResponseInfoSize(head);
+ write_info_buffer_ = new HttpResponseInfoIOBuffer(head);
+ writer_->WriteInfo(write_info_buffer_, &write_info_callback_);
+ }
+
+ void WriteResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ write_buffer_ = io_buffer;
+ expected_write_result_ = buf_len;
+ writer_->WriteData(
+ write_buffer_, buf_len, &write_callback_);
+ }
+
+ void ReadResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_buffer_ = io_buffer;
+ expected_read_result_ = buf_len;
+ reader_->ReadData(
+ read_buffer_, buf_len, &read_callback_);
+ }
+
+ // AppCacheResponseReader / Writer completion callbacks
+
+ void OnWriteInfoComplete(int result) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ EXPECT_EQ(expected_write_result_, result);
+ ScheduleNextTask();
+ }
+
+ void OnWriteComplete(int result) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ write_callback_was_called_ = true;
+ EXPECT_EQ(expected_write_result_, result);
+ if (should_delete_writer_in_completion_callback_ &&
+ --writer_deletion_count_down_ == 0) {
+ writer_.reset();
+ }
+ ScheduleNextTask();
+ }
+
+ void OnReadInfoComplete(int result) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ EXPECT_EQ(expected_read_result_, result);
+ ScheduleNextTask();
+ }
+
+ void OnReadComplete(int result) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_callback_was_called_ = true;
+ EXPECT_EQ(expected_read_result_, result);
+ if (should_delete_reader_in_completion_callback_ &&
+ --reader_deletion_count_down_ == 0) {
+ reader_.reset();
+ }
+ ScheduleNextTask();
+ }
+
+ // Helpers to work with HttpResponseInfo objects
+
+ net::HttpResponseInfo* MakeHttpResponseInfo(const char* raw_headers) {
+ net::HttpResponseInfo* info = new net::HttpResponseInfo;
+ info->request_time = base::Time::Now();
+ info->response_time = base::Time::Now();
+ info->was_cached = false;
+ info->headers = new net::HttpResponseHeaders(raw_headers);
+ return info;
+ }
+
+ int GetHttpResponseInfoSize(const net::HttpResponseInfo* info) {
+ Pickle pickle;
+ return PickleHttpResonseInfo(&pickle, info);
+ }
+
+ bool CompareHttpResponseInfos(const net::HttpResponseInfo* info1,
+ const net::HttpResponseInfo* info2) {
+ Pickle pickle1;
+ Pickle pickle2;
+ PickleHttpResonseInfo(&pickle1, info1);
+ PickleHttpResonseInfo(&pickle2, info2);
+ return (pickle1.size() == pickle2.size()) &&
+ (0 == memcmp(pickle1.data(), pickle2.data(), pickle1.size()));
+ }
+
+ int PickleHttpResonseInfo(Pickle* pickle, const net::HttpResponseInfo* info) {
+ const bool kSkipTransientHeaders = true;
+ const bool kTruncated = false;
+ info->Persist(pickle, kSkipTransientHeaders, kTruncated);
+ return pickle->size();
+ }
+
+ // Helpers to fill and verify blocks of memory with a value
+
+ void FillData(char value, char* data, int data_len) {
+ memset(data, value, data_len);
+ }
+
+ bool CheckData(char value, const char* data, int data_len) {
+ for (int i = 0; i < data_len; ++i, ++data) {
+ if (*data != value)
+ return false;
+ }
+ return true;
+ }
+
+ // Individual Tests ---------------------------------------------------------
+ // Most of the individual tests involve multiple async steps. Each test
+ // is delineated with a section header.
+
+ // DelegateReferences -------------------------------------------------------
+ // TODO(michaeln): maybe this one belongs in appcache_storage_unittest.cc
+ void DelegateReferences() {
+ typedef scoped_refptr<AppCacheStorage::DelegateReference>
+ ScopedDelegateReference;
+ MockStorageDelegate delegate(this);
+ ScopedDelegateReference delegate_reference1;
+ ScopedDelegateReference delegate_reference2;
+
+ EXPECT_FALSE(service_->storage()->GetDelegateReference(&delegate));
+
+ delegate_reference1 =
+ service_->storage()->GetOrCreateDelegateReference(&delegate);
+ EXPECT_TRUE(delegate_reference1.get());
+ EXPECT_TRUE(delegate_reference1->HasOneRef());
+ EXPECT_TRUE(service_->storage()->GetDelegateReference(&delegate));
+ EXPECT_EQ(&delegate,
+ service_->storage()->GetDelegateReference(&delegate)->delegate);
+ EXPECT_EQ(service_->storage()->GetDelegateReference(&delegate),
+ service_->storage()->GetOrCreateDelegateReference(&delegate));
+ delegate_reference1 = NULL;
+ EXPECT_FALSE(service_->storage()->GetDelegateReference(&delegate));
+
+ delegate_reference1 =
+ service_->storage()->GetOrCreateDelegateReference(&delegate);
+ service_->storage()->CancelDelegateCallbacks(&delegate);
+ EXPECT_TRUE(delegate_reference1.get());
+ EXPECT_TRUE(delegate_reference1->HasOneRef());
+ EXPECT_FALSE(delegate_reference1->delegate);
+ EXPECT_FALSE(service_->storage()->GetDelegateReference(&delegate));
+
+ delegate_reference2 =
+ service_->storage()->GetOrCreateDelegateReference(&delegate);
+ EXPECT_TRUE(delegate_reference2.get());
+ EXPECT_TRUE(delegate_reference2->HasOneRef());
+ EXPECT_EQ(&delegate, delegate_reference2->delegate);
+ EXPECT_NE(delegate_reference1.get(), delegate_reference2.get());
+
+ TestFinished();
+ }
+
+ // ReadNonExistentResponse -------------------------------------------
+ static const int64 kNoSuchResponseId = 123;
+
+ void ReadNonExistentResponse() {
+ // 1. Attempt to ReadInfo
+ // 2. Attempt to ReadData
+
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), kNoSuchResponseId));
+
+ // Push tasks in reverse order
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadNonExistentData));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadNonExistentInfo));
+ ScheduleNextTask();
+ }
+
+ void ReadNonExistentInfo() {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_info_buffer_ = new HttpResponseInfoIOBuffer();
+ reader_->ReadInfo(read_info_buffer_, &read_info_callback_);
+ EXPECT_TRUE(reader_->IsReadPending());
+ expected_read_result_ = net::ERR_CACHE_MISS;
+ }
+
+ void ReadNonExistentData() {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_buffer_ = new IOBuffer(kBlockSize);
+ reader_->ReadData(read_buffer_, kBlockSize, &read_callback_);
+ EXPECT_TRUE(reader_->IsReadPending());
+ expected_read_result_ = net::ERR_CACHE_MISS;
+ }
+
+ // LoadResponseInfo_Miss ----------------------------------------------------
+ void LoadResponseInfo_Miss() {
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::LoadResponseInfo_Miss_Verify));
+ service_->storage()->LoadResponseInfo(GURL(), kNoSuchResponseId,
+ storage_delegate_.get());
+ }
+
+ void LoadResponseInfo_Miss_Verify() {
+ EXPECT_EQ(kNoSuchResponseId, storage_delegate_->loaded_info_id_);
+ EXPECT_TRUE(!storage_delegate_->loaded_info_.get());
+ TestFinished();
+ }
+
+ // LoadResponseInfo_Hit ----------------------------------------------------
+ void LoadResponseInfo_Hit() {
+ // This tests involves multiple async steps.
+ // 1. Write a response headers and body to storage
+ // a. headers
+ // b. body
+ // 2. Use LoadResponseInfo to read the response headers back out
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::LoadResponseInfo_Hit_Step2));
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL()));
+ written_response_id_ = writer_->response_id();
+ WriteBasicResponse();
+ }
+
+ void LoadResponseInfo_Hit_Step2() {
+ writer_.reset();
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::LoadResponseInfo_Hit_Verify));
+ service_->storage()->LoadResponseInfo(GURL(), written_response_id_,
+ storage_delegate_.get());
+ }
+
+ void LoadResponseInfo_Hit_Verify() {
+ EXPECT_EQ(written_response_id_, storage_delegate_->loaded_info_id_);
+ EXPECT_TRUE(storage_delegate_->loaded_info_.get());
+ EXPECT_TRUE(CompareHttpResponseInfos(
+ write_info_buffer_->http_info.get(),
+ storage_delegate_->loaded_info_->http_response_info()));
+ TestFinished();
+ }
+
+ // WriteThenVariouslyReadResponse -------------------------------------------
+ static const int kNumBlocks = 4;
+ static const int kBlockSize = 1024;
+
+ void WriteThenVariouslyReadResponse() {
+ // This tests involves multiple async steps.
+ // 1. First, write a large body using multiple writes, we don't bother
+ // with a response head for this test.
+ // 2. Read the entire body, using multiple reads
+ // 3. Read the entire body, using one read.
+ // 4. Attempt to read beyond the EOF.
+ // 5. Read just a range.
+ // 6. Attempt to read beyond EOF of a range.
+
+ // Push tasks in reverse order
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadRangeFullyBeyondEOF));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadRangePartiallyBeyondEOF));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadPastEOF));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadRange));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadPastEOF));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadAllAtOnce));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadInBlocks));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::WriteOutBlocks));
+
+ // Get them going.
+ ScheduleNextTask();
+ }
+
+ void WriteOutBlocks() {
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL()));
+ written_response_id_ = writer_->response_id();
+ for (int i = 0; i < kNumBlocks; ++i) {
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::WriteOneBlock, kNumBlocks - i));
+ }
+ ScheduleNextTask();
+ }
+
+ void WriteOneBlock(int block_number) {
+ scoped_refptr<IOBuffer> io_buffer =
+ new IOBuffer(kBlockSize);
+ FillData(block_number, io_buffer->data(), kBlockSize);
+ WriteResponseBody(io_buffer, kBlockSize);
+ }
+
+ void ReadInBlocks() {
+ writer_.reset();
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), written_response_id_));
+ for (int i = 0; i < kNumBlocks; ++i) {
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadOneBlock, kNumBlocks - i));
+ }
+ ScheduleNextTask();
+ }
+
+ void ReadOneBlock(int block_number) {
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::VerifyOneBlock, block_number));
+ ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
+ }
+
+ void VerifyOneBlock(int block_number) {
+ EXPECT_TRUE(CheckData(block_number, read_buffer_->data(), kBlockSize));
+ ScheduleNextTask();
+ }
+
+ void ReadAllAtOnce() {
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::VerifyAllAtOnce));
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), written_response_id_));
+ int big_size = kNumBlocks * kBlockSize;
+ ReadResponseBody(new IOBuffer(big_size), big_size);
+ }
+
+ void VerifyAllAtOnce() {
+ char* p = read_buffer_->data();
+ for (int i = 0; i < kNumBlocks; ++i, p += kBlockSize)
+ EXPECT_TRUE(CheckData(i + 1, p, kBlockSize));
+ ScheduleNextTask();
+ }
+
+ void ReadPastEOF() {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_buffer_ = new IOBuffer(kBlockSize);
+ expected_read_result_ = 0;
+ reader_->ReadData(
+ read_buffer_, kBlockSize, &read_callback_);
+ }
+
+ void ReadRange() {
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::VerifyRange));
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), written_response_id_));
+ reader_->SetReadRange(kBlockSize, kBlockSize);
+ ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
+ }
+
+ void VerifyRange() {
+ EXPECT_TRUE(CheckData(2, read_buffer_->data(), kBlockSize));
+ ScheduleNextTask(); // ReadPastEOF is scheduled next
+ }
+
+ void ReadRangePartiallyBeyondEOF() {
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::VerifyRangeBeyondEOF));
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), written_response_id_));
+ reader_->SetReadRange(kBlockSize, kNumBlocks * kBlockSize);
+ ReadResponseBody(new IOBuffer(kNumBlocks * kBlockSize),
+ kNumBlocks * kBlockSize);
+ expected_read_result_ = (kNumBlocks - 1) * kBlockSize;
+ }
+
+ void VerifyRangeBeyondEOF() {
+ // Just verify the first 1k
+ VerifyRange();
+ }
+
+ void ReadRangeFullyBeyondEOF() {
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), written_response_id_));
+ reader_->SetReadRange((kNumBlocks * kBlockSize) + 1, kBlockSize);
+ ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
+ expected_read_result_ = 0;
+ }
+
+ // IOChaining -------------------------------------------
+ void IOChaining() {
+ // 1. Write several blocks out initiating the subsequent write
+ // from within the completion callback of the previous write.
+ // 2. Read and verify several blocks in similarly chaining reads.
+
+ // Push tasks in reverse order
+ PushNextTaskAsImmediate(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadInBlocksImmediately));
+ PushNextTaskAsImmediate(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::WriteOutBlocksImmediately));
+
+ // Get them going.
+ ScheduleNextTask();
+ }
+
+ void WriteOutBlocksImmediately() {
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL()));
+ written_response_id_ = writer_->response_id();
+ for (int i = 0; i < kNumBlocks; ++i) {
+ PushNextTaskAsImmediate(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::WriteOneBlock, kNumBlocks - i));
+ }
+ ScheduleNextTask();
+ }
+
+ void ReadInBlocksImmediately() {
+ writer_.reset();
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), written_response_id_));
+ for (int i = 0; i < kNumBlocks; ++i) {
+ PushNextTaskAsImmediate(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadOneBlockImmediately, kNumBlocks - i));
+ }
+ ScheduleNextTask();
+ }
+
+ void ReadOneBlockImmediately(int block_number) {
+ PushNextTaskAsImmediate(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::VerifyOneBlock, block_number));
+ ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
+ }
+
+ // DeleteWithinCallbacks -------------------------------------------
+ void DeleteWithinCallbacks() {
+ // 1. Write out a few blocks normally, and upon
+ // completion of the last write, delete the writer.
+ // 2. Read in a few blocks normally, and upon completion
+ // of the last read, delete the reader.
+
+ should_delete_reader_in_completion_callback_ = true;
+ reader_deletion_count_down_ = kNumBlocks;
+ should_delete_writer_in_completion_callback_ = true;
+ writer_deletion_count_down_ = kNumBlocks;
+
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadInBlocks));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::WriteOutBlocks));
+ ScheduleNextTask();
+ }
+
+ // DeleteWithIOPending -------------------------------------------
+ void DeleteWithIOPending() {
+ // 1. Write a few blocks normally.
+ // 2. Start a write, delete with it pending.
+ // 3. Start a read, delete with it pending.
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::ReadThenDelete));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::WriteThenDelete));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::WriteOutBlocks));
+ ScheduleNextTask();
+ }
+
+ void WriteThenDelete() {
+ write_callback_was_called_ = false;
+ WriteOneBlock(5);
+ EXPECT_TRUE(writer_->IsWritePending());
+ writer_.reset();
+ ScheduleNextTask();
+ }
+
+ void ReadThenDelete() {
+ read_callback_was_called_ = false;
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), written_response_id_));
+ ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
+ EXPECT_TRUE(reader_->IsReadPending());
+ reader_.reset();
+
+ // Wait a moment to verify no callbacks.
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ method_factory_.NewRunnableMethod(
+ &AppCacheResponseTest::VerifyNoCallbacks),
+ 10);
+ }
+
+ void VerifyNoCallbacks() {
+ EXPECT_TRUE(!write_callback_was_called_);
+ EXPECT_TRUE(!read_callback_was_called_);
+ TestFinished();
+ }
+
+ // Data members
+
+ ScopedRunnableMethodFactory<AppCacheResponseTest> method_factory_;
+ scoped_ptr<base::WaitableEvent> test_finished_event_;
+ scoped_ptr<MockStorageDelegate> storage_delegate_;
+ scoped_ptr<MockAppCacheService> service_;
+ std::stack<std::pair<Task*, bool> > task_stack_;
+
+ scoped_ptr<AppCacheResponseReader> reader_;
+ scoped_refptr<HttpResponseInfoIOBuffer> read_info_buffer_;
+ scoped_refptr<IOBuffer> read_buffer_;
+ int expected_read_result_;
+ net::CompletionCallbackImpl<AppCacheResponseTest> read_callback_;
+ net::CompletionCallbackImpl<AppCacheResponseTest> read_info_callback_;
+ bool should_delete_reader_in_completion_callback_;
+ int reader_deletion_count_down_;
+ bool read_callback_was_called_;
+
+ int64 written_response_id_;
+ scoped_ptr<AppCacheResponseWriter> writer_;
+ scoped_refptr<HttpResponseInfoIOBuffer> write_info_buffer_;
+ scoped_refptr<IOBuffer> write_buffer_;
+ int expected_write_result_;
+ net::CompletionCallbackImpl<AppCacheResponseTest> write_callback_;
+ net::CompletionCallbackImpl<AppCacheResponseTest> write_info_callback_;
+ bool should_delete_writer_in_completion_callback_;
+ int writer_deletion_count_down_;
+ bool write_callback_was_called_;
+
+ static scoped_ptr<base::Thread> io_thread_;
+};
+
+// static
+scoped_ptr<base::Thread> AppCacheResponseTest::io_thread_;
+
+TEST_F(AppCacheResponseTest, DelegateReferences) {
+ RunTestOnIOThread(&AppCacheResponseTest::DelegateReferences);
+}
+
+TEST_F(AppCacheResponseTest, ReadNonExistentResponse) {
+ RunTestOnIOThread(&AppCacheResponseTest::ReadNonExistentResponse);
+}
+
+TEST_F(AppCacheResponseTest, LoadResponseInfo_Miss) {
+ RunTestOnIOThread(&AppCacheResponseTest::LoadResponseInfo_Miss);
+}
+
+TEST_F(AppCacheResponseTest, LoadResponseInfo_Hit) {
+ RunTestOnIOThread(&AppCacheResponseTest::LoadResponseInfo_Hit);
+}
+
+TEST_F(AppCacheResponseTest, WriteThenVariouslyReadResponse) {
+ RunTestOnIOThread(&AppCacheResponseTest::WriteThenVariouslyReadResponse);
+}
+
+TEST_F(AppCacheResponseTest, IOChaining) {
+ RunTestOnIOThread(&AppCacheResponseTest::IOChaining);
+}
+
+TEST_F(AppCacheResponseTest, DeleteWithinCallbacks) {
+ RunTestOnIOThread(&AppCacheResponseTest::DeleteWithinCallbacks);
+}
+
+TEST_F(AppCacheResponseTest, DeleteWithIOPending) {
+ RunTestOnIOThread(&AppCacheResponseTest::DeleteWithIOPending);
+}
+
+} // namespace appcache
+