diff options
author | michaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-21 18:09:33 +0000 |
---|---|---|
committer | michaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-21 18:09:33 +0000 |
commit | 073aabe9e48306216d979f03d6c2fc37d25d42e9 (patch) | |
tree | a9088a3c8be87fa57208074c7de65e998b865f94 /webkit/appcache/appcache_response_unittest.cc | |
parent | c54d269e0b8a057533c1a1455a2acea0659629b9 (diff) | |
download | chromium_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.cc | 716 |
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 + |