|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/http/mock_http_cache.h" | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/message_loop.h" | 
|  | #include "net/base/completion_callback.h" | 
|  | #include "net/base/net_errors.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // We can override the test mode for a given operation by setting this global | 
|  | // variable. | 
|  | int g_test_mode = 0; | 
|  |  | 
|  | int GetTestModeForEntry(const std::string& key) { | 
|  | // 'key' is prefixed with an identifier if it corresponds to a cached POST. | 
|  | // Skip past that to locate the actual URL. | 
|  | // | 
|  | // TODO(darin): It breaks the abstraction a bit that we assume 'key' is an | 
|  | // URL corresponding to a registered MockTransaction.  It would be good to | 
|  | // have another way to access the test_mode. | 
|  | GURL url; | 
|  | if (isdigit(key[0])) { | 
|  | size_t slash = key.find('/'); | 
|  | DCHECK(slash != std::string::npos); | 
|  | url = GURL(key.substr(slash + 1)); | 
|  | } else { | 
|  | url = GURL(key); | 
|  | } | 
|  | const MockTransaction* t = FindMockTransaction(url); | 
|  | DCHECK(t); | 
|  | return t->test_mode; | 
|  | } | 
|  |  | 
|  | void CallbackForwader(const net::CompletionCallback& callback, int result) { | 
|  | callback.Run(result); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  |  | 
|  | struct MockDiskEntry::CallbackInfo { | 
|  | scoped_refptr<MockDiskEntry> entry; | 
|  | net::CompletionCallback callback; | 
|  | int result; | 
|  | }; | 
|  |  | 
|  | MockDiskEntry::MockDiskEntry() | 
|  | : test_mode_(0), doomed_(false), sparse_(false), | 
|  | fail_requests_(false), busy_(false), delayed_(false) { | 
|  | } | 
|  |  | 
|  | MockDiskEntry::MockDiskEntry(const std::string& key) | 
|  | : key_(key), doomed_(false), sparse_(false), | 
|  | fail_requests_(false), busy_(false), delayed_(false) { | 
|  | test_mode_ = GetTestModeForEntry(key); | 
|  | } | 
|  |  | 
|  | void MockDiskEntry::Doom() { | 
|  | doomed_ = true; | 
|  | } | 
|  |  | 
|  | void MockDiskEntry::Close() { | 
|  | Release(); | 
|  | } | 
|  |  | 
|  | std::string MockDiskEntry::GetKey() const { | 
|  | return key_; | 
|  | } | 
|  |  | 
|  | base::Time MockDiskEntry::GetLastUsed() const { | 
|  | return base::Time::FromInternalValue(0); | 
|  | } | 
|  |  | 
|  | base::Time MockDiskEntry::GetLastModified() const { | 
|  | return base::Time::FromInternalValue(0); | 
|  | } | 
|  |  | 
|  | int32 MockDiskEntry::GetDataSize(int index) const { | 
|  | DCHECK(index >= 0 && index < kNumCacheEntryDataIndices); | 
|  | return static_cast<int32>(data_[index].size()); | 
|  | } | 
|  |  | 
|  | int MockDiskEntry::ReadData( | 
|  | int index, int offset, net::IOBuffer* buf, int buf_len, | 
|  | const net::CompletionCallback& callback) { | 
|  | DCHECK(index >= 0 && index < kNumCacheEntryDataIndices); | 
|  | DCHECK(!callback.is_null()); | 
|  |  | 
|  | if (fail_requests_) | 
|  | return net::ERR_CACHE_READ_FAILURE; | 
|  |  | 
|  | if (offset < 0 || offset > static_cast<int>(data_[index].size())) | 
|  | return net::ERR_FAILED; | 
|  | if (static_cast<size_t>(offset) == data_[index].size()) | 
|  | return 0; | 
|  |  | 
|  | int num = std::min(buf_len, static_cast<int>(data_[index].size()) - offset); | 
|  | memcpy(buf->data(), &data_[index][offset], num); | 
|  |  | 
|  | if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ) | 
|  | return num; | 
|  |  | 
|  | CallbackLater(callback, num); | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | int MockDiskEntry::WriteData( | 
|  | int index, int offset, net::IOBuffer* buf, int buf_len, | 
|  | const net::CompletionCallback& callback, bool truncate) { | 
|  | DCHECK(index >= 0 && index < kNumCacheEntryDataIndices); | 
|  | DCHECK(!callback.is_null()); | 
|  | DCHECK(truncate); | 
|  |  | 
|  | if (fail_requests_) { | 
|  | CallbackLater(callback, net::ERR_CACHE_READ_FAILURE); | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | if (offset < 0 || offset > static_cast<int>(data_[index].size())) | 
|  | return net::ERR_FAILED; | 
|  |  | 
|  | data_[index].resize(offset + buf_len); | 
|  | if (buf_len) | 
|  | memcpy(&data_[index][offset], buf->data(), buf_len); | 
|  |  | 
|  | if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE) | 
|  | return buf_len; | 
|  |  | 
|  | CallbackLater(callback, buf_len); | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | int MockDiskEntry::ReadSparseData(int64 offset, net::IOBuffer* buf, int buf_len, | 
|  | const net::CompletionCallback& callback) { | 
|  | DCHECK(!callback.is_null()); | 
|  | if (!sparse_ || busy_) | 
|  | return net::ERR_CACHE_OPERATION_NOT_SUPPORTED; | 
|  | if (offset < 0) | 
|  | return net::ERR_FAILED; | 
|  |  | 
|  | if (fail_requests_) | 
|  | return net::ERR_CACHE_READ_FAILURE; | 
|  |  | 
|  | DCHECK(offset < kint32max); | 
|  | int real_offset = static_cast<int>(offset); | 
|  | if (!buf_len) | 
|  | return 0; | 
|  |  | 
|  | int num = std::min(static_cast<int>(data_[1].size()) - real_offset, | 
|  | buf_len); | 
|  | memcpy(buf->data(), &data_[1][real_offset], num); | 
|  |  | 
|  | if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ) | 
|  | return num; | 
|  |  | 
|  | CallbackLater(callback, num); | 
|  | busy_ = true; | 
|  | delayed_ = false; | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | int MockDiskEntry::WriteSparseData(int64 offset, net::IOBuffer* buf, | 
|  | int buf_len, | 
|  | const net::CompletionCallback& callback) { | 
|  | DCHECK(!callback.is_null()); | 
|  | if (busy_) | 
|  | return net::ERR_CACHE_OPERATION_NOT_SUPPORTED; | 
|  | if (!sparse_) { | 
|  | if (data_[1].size()) | 
|  | return net::ERR_CACHE_OPERATION_NOT_SUPPORTED; | 
|  | sparse_ = true; | 
|  | } | 
|  | if (offset < 0) | 
|  | return net::ERR_FAILED; | 
|  | if (!buf_len) | 
|  | return 0; | 
|  |  | 
|  | if (fail_requests_) | 
|  | return net::ERR_CACHE_READ_FAILURE; | 
|  |  | 
|  | DCHECK(offset < kint32max); | 
|  | int real_offset = static_cast<int>(offset); | 
|  |  | 
|  | if (static_cast<int>(data_[1].size()) < real_offset + buf_len) | 
|  | data_[1].resize(real_offset + buf_len); | 
|  |  | 
|  | memcpy(&data_[1][real_offset], buf->data(), buf_len); | 
|  | if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE) | 
|  | return buf_len; | 
|  |  | 
|  | CallbackLater(callback, buf_len); | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | int MockDiskEntry::GetAvailableRange(int64 offset, int len, int64* start, | 
|  | const net::CompletionCallback& callback) { | 
|  | DCHECK(!callback.is_null()); | 
|  | if (!sparse_ || busy_) | 
|  | return net::ERR_CACHE_OPERATION_NOT_SUPPORTED; | 
|  | if (offset < 0) | 
|  | return net::ERR_FAILED; | 
|  |  | 
|  | if (fail_requests_) | 
|  | return net::ERR_CACHE_READ_FAILURE; | 
|  |  | 
|  | *start = offset; | 
|  | DCHECK(offset < kint32max); | 
|  | int real_offset = static_cast<int>(offset); | 
|  | if (static_cast<int>(data_[1].size()) < real_offset) | 
|  | return 0; | 
|  |  | 
|  | int num = std::min(static_cast<int>(data_[1].size()) - real_offset, len); | 
|  | int count = 0; | 
|  | for (; num > 0; num--, real_offset++) { | 
|  | if (!count) { | 
|  | if (data_[1][real_offset]) { | 
|  | count++; | 
|  | *start = real_offset; | 
|  | } | 
|  | } else { | 
|  | if (!data_[1][real_offset]) | 
|  | break; | 
|  | count++; | 
|  | } | 
|  | } | 
|  | if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE) | 
|  | return count; | 
|  |  | 
|  | CallbackLater(callback, count); | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | bool MockDiskEntry::CouldBeSparse() const { | 
|  | return sparse_; | 
|  | } | 
|  |  | 
|  | void MockDiskEntry::CancelSparseIO() { | 
|  | cancel_ = true; | 
|  | } | 
|  |  | 
|  | int MockDiskEntry::ReadyForSparseIO(const net::CompletionCallback& callback) { | 
|  | if (!cancel_) | 
|  | return net::OK; | 
|  |  | 
|  | cancel_ = false; | 
|  | DCHECK(!callback.is_null()); | 
|  | if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ) | 
|  | return net::OK; | 
|  |  | 
|  | // The pending operation is already in the message loop (and hopefully | 
|  | // already in the second pass).  Just notify the caller that it finished. | 
|  | CallbackLater(callback, 0); | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | // If |value| is true, don't deliver any completion callbacks until called | 
|  | // again with |value| set to false.  Caution: remember to enable callbacks | 
|  | // again or all subsequent tests will fail. | 
|  | // Static. | 
|  | void MockDiskEntry::IgnoreCallbacks(bool value) { | 
|  | if (ignore_callbacks_ == value) | 
|  | return; | 
|  | ignore_callbacks_ = value; | 
|  | if (!value) | 
|  | StoreAndDeliverCallbacks(false, NULL, net::CompletionCallback(), 0); | 
|  | } | 
|  |  | 
|  | MockDiskEntry::~MockDiskEntry() { | 
|  | } | 
|  |  | 
|  | // Unlike the callbacks for MockHttpTransaction, we want this one to run even | 
|  | // if the consumer called Close on the MockDiskEntry.  We achieve that by | 
|  | // leveraging the fact that this class is reference counted. | 
|  | void MockDiskEntry::CallbackLater(const net::CompletionCallback& callback, | 
|  | int result) { | 
|  | if (ignore_callbacks_) | 
|  | return StoreAndDeliverCallbacks(true, this, callback, result); | 
|  | MessageLoop::current()->PostTask(FROM_HERE, base::Bind( | 
|  | &MockDiskEntry::RunCallback, this, callback, result)); | 
|  | } | 
|  |  | 
|  | void MockDiskEntry::RunCallback( | 
|  | const net::CompletionCallback& callback, int result) { | 
|  | if (busy_) { | 
|  | // This is kind of hacky, but controlling the behavior of just this entry | 
|  | // from a test is sort of complicated.  What we really want to do is | 
|  | // delay the delivery of a sparse IO operation a little more so that the | 
|  | // request start operation (async) will finish without seeing the end of | 
|  | // this operation (already posted to the message loop)... and without | 
|  | // just delaying for n mS (which may cause trouble with slow bots).  So | 
|  | // we re-post this operation (all async sparse IO operations will take two | 
|  | // trips through the message loop instead of one). | 
|  | if (!delayed_) { | 
|  | delayed_ = true; | 
|  | return CallbackLater(callback, result); | 
|  | } | 
|  | } | 
|  | busy_ = false; | 
|  | callback.Run(result); | 
|  | } | 
|  |  | 
|  | // When |store| is true, stores the callback to be delivered later; otherwise | 
|  | // delivers any callback previously stored. | 
|  | // Static. | 
|  | void MockDiskEntry::StoreAndDeliverCallbacks( | 
|  | bool store, MockDiskEntry* entry, const net::CompletionCallback& callback, | 
|  | int result) { | 
|  | static std::vector<CallbackInfo> callback_list; | 
|  | if (store) { | 
|  | CallbackInfo c = {entry, callback, result}; | 
|  | callback_list.push_back(c); | 
|  | } else { | 
|  | for (size_t i = 0; i < callback_list.size(); i++) { | 
|  | CallbackInfo& c = callback_list[i]; | 
|  | c.entry->CallbackLater(c.callback, c.result); | 
|  | } | 
|  | callback_list.clear(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Statics. | 
|  | bool MockDiskEntry::cancel_ = false; | 
|  | bool MockDiskEntry::ignore_callbacks_ = false; | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  |  | 
|  | MockDiskCache::MockDiskCache() | 
|  | : open_count_(0), create_count_(0), fail_requests_(false), | 
|  | soft_failures_(false), double_create_check_(true) { | 
|  | } | 
|  |  | 
|  | MockDiskCache::~MockDiskCache() { | 
|  | ReleaseAll(); | 
|  | } | 
|  |  | 
|  | net::CacheType MockDiskCache::GetCacheType() const { | 
|  | return net::DISK_CACHE; | 
|  | } | 
|  |  | 
|  | int32 MockDiskCache::GetEntryCount() const { | 
|  | return static_cast<int32>(entries_.size()); | 
|  | } | 
|  |  | 
|  | int MockDiskCache::OpenEntry(const std::string& key, disk_cache::Entry** entry, | 
|  | const net::CompletionCallback& callback) { | 
|  | DCHECK(!callback.is_null()); | 
|  | if (fail_requests_) | 
|  | return net::ERR_CACHE_OPEN_FAILURE; | 
|  |  | 
|  | EntryMap::iterator it = entries_.find(key); | 
|  | if (it == entries_.end()) | 
|  | return net::ERR_CACHE_OPEN_FAILURE; | 
|  |  | 
|  | if (it->second->is_doomed()) { | 
|  | it->second->Release(); | 
|  | entries_.erase(it); | 
|  | return net::ERR_CACHE_OPEN_FAILURE; | 
|  | } | 
|  |  | 
|  | open_count_++; | 
|  |  | 
|  | it->second->AddRef(); | 
|  | *entry = it->second; | 
|  |  | 
|  | if (soft_failures_) | 
|  | it->second->set_fail_requests(); | 
|  |  | 
|  | if (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START) | 
|  | return net::OK; | 
|  |  | 
|  | CallbackLater(callback, net::OK); | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | int MockDiskCache::CreateEntry(const std::string& key, | 
|  | disk_cache::Entry** entry, | 
|  | const net::CompletionCallback& callback) { | 
|  | DCHECK(!callback.is_null()); | 
|  | if (fail_requests_) | 
|  | return net::ERR_CACHE_CREATE_FAILURE; | 
|  |  | 
|  | EntryMap::iterator it = entries_.find(key); | 
|  | if (it != entries_.end()) { | 
|  | if (!it->second->is_doomed()) { | 
|  | if (double_create_check_) | 
|  | NOTREACHED(); | 
|  | else | 
|  | return net::ERR_CACHE_CREATE_FAILURE; | 
|  | } | 
|  | it->second->Release(); | 
|  | entries_.erase(it); | 
|  | } | 
|  |  | 
|  | create_count_++; | 
|  |  | 
|  | MockDiskEntry* new_entry = new MockDiskEntry(key); | 
|  |  | 
|  | new_entry->AddRef(); | 
|  | entries_[key] = new_entry; | 
|  |  | 
|  | new_entry->AddRef(); | 
|  | *entry = new_entry; | 
|  |  | 
|  | if (soft_failures_) | 
|  | new_entry->set_fail_requests(); | 
|  |  | 
|  | if (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START) | 
|  | return net::OK; | 
|  |  | 
|  | CallbackLater(callback, net::OK); | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | int MockDiskCache::DoomEntry(const std::string& key, | 
|  | const net::CompletionCallback& callback) { | 
|  | DCHECK(!callback.is_null()); | 
|  | EntryMap::iterator it = entries_.find(key); | 
|  | if (it != entries_.end()) { | 
|  | it->second->Release(); | 
|  | entries_.erase(it); | 
|  | } | 
|  |  | 
|  | if (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START) | 
|  | return net::OK; | 
|  |  | 
|  | CallbackLater(callback, net::OK); | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | int MockDiskCache::DoomAllEntries(const net::CompletionCallback& callback) { | 
|  | return net::ERR_NOT_IMPLEMENTED; | 
|  | } | 
|  |  | 
|  | int MockDiskCache::DoomEntriesBetween(const base::Time initial_time, | 
|  | const base::Time end_time, | 
|  | const net::CompletionCallback& callback) { | 
|  | return net::ERR_NOT_IMPLEMENTED; | 
|  | } | 
|  |  | 
|  | int MockDiskCache::DoomEntriesSince(const base::Time initial_time, | 
|  | const net::CompletionCallback& callback) { | 
|  | return net::ERR_NOT_IMPLEMENTED; | 
|  | } | 
|  |  | 
|  | int MockDiskCache::OpenNextEntry(void** iter, disk_cache::Entry** next_entry, | 
|  | const net::CompletionCallback& callback) { | 
|  | return net::ERR_NOT_IMPLEMENTED; | 
|  | } | 
|  |  | 
|  | void MockDiskCache::EndEnumeration(void** iter) { | 
|  | } | 
|  |  | 
|  | void MockDiskCache::GetStats( | 
|  | std::vector<std::pair<std::string, std::string> >* stats) { | 
|  | } | 
|  |  | 
|  | void MockDiskCache::OnExternalCacheHit(const std::string& key) { | 
|  | } | 
|  |  | 
|  | void MockDiskCache::ReleaseAll() { | 
|  | EntryMap::iterator it = entries_.begin(); | 
|  | for (; it != entries_.end(); ++it) | 
|  | it->second->Release(); | 
|  | entries_.clear(); | 
|  | } | 
|  |  | 
|  | void MockDiskCache::CallbackLater(const net::CompletionCallback& callback, | 
|  | int result) { | 
|  | MessageLoop::current()->PostTask( | 
|  | FROM_HERE, base::Bind(&CallbackForwader, callback, result)); | 
|  | } | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  |  | 
|  | int MockBackendFactory::CreateBackend(net::NetLog* net_log, | 
|  | disk_cache::Backend** backend, | 
|  | const net::CompletionCallback& callback) { | 
|  | *backend = new MockDiskCache(); | 
|  | return net::OK; | 
|  | } | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  |  | 
|  | MockHttpCache::MockHttpCache() | 
|  | : http_cache_(new MockNetworkLayer(), NULL, new MockBackendFactory()) { | 
|  | } | 
|  |  | 
|  | MockHttpCache::MockHttpCache(net::HttpCache::BackendFactory* disk_cache_factory) | 
|  | : http_cache_(new MockNetworkLayer(), NULL, disk_cache_factory) { | 
|  | } | 
|  |  | 
|  | MockDiskCache* MockHttpCache::disk_cache() { | 
|  | net::TestCompletionCallback cb; | 
|  | disk_cache::Backend* backend; | 
|  | int rv = http_cache_.GetBackend(&backend, cb.callback()); | 
|  | rv = cb.GetResult(rv); | 
|  | return (rv == net::OK) ? static_cast<MockDiskCache*>(backend) : NULL; | 
|  | } | 
|  |  | 
|  | bool MockHttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry, | 
|  | net::HttpResponseInfo* response_info, | 
|  | bool* response_truncated) { | 
|  | int size = disk_entry->GetDataSize(0); | 
|  |  | 
|  | net::TestCompletionCallback cb; | 
|  | scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(size)); | 
|  | int rv = disk_entry->ReadData(0, 0, buffer, size, cb.callback()); | 
|  | rv = cb.GetResult(rv); | 
|  | EXPECT_EQ(size, rv); | 
|  |  | 
|  | return net::HttpCache::ParseResponseInfo(buffer->data(), size, | 
|  | response_info, | 
|  | response_truncated); | 
|  | } | 
|  |  | 
|  | bool MockHttpCache::WriteResponseInfo( | 
|  | disk_cache::Entry* disk_entry, const net::HttpResponseInfo* response_info, | 
|  | bool skip_transient_headers, bool response_truncated) { | 
|  | Pickle pickle; | 
|  | response_info->Persist( | 
|  | &pickle, skip_transient_headers, response_truncated); | 
|  |  | 
|  | net::TestCompletionCallback cb; | 
|  | scoped_refptr<net::WrappedIOBuffer> data(new net::WrappedIOBuffer( | 
|  | reinterpret_cast<const char*>(pickle.data()))); | 
|  | int len = static_cast<int>(pickle.size()); | 
|  |  | 
|  | int rv =  disk_entry->WriteData(0, 0, data, len, cb.callback(), true); | 
|  | rv = cb.GetResult(rv); | 
|  | return (rv == len); | 
|  | } | 
|  |  | 
|  | bool MockHttpCache::OpenBackendEntry(const std::string& key, | 
|  | disk_cache::Entry** entry) { | 
|  | net::TestCompletionCallback cb; | 
|  | if (!disk_cache()) return false; | 
|  | int rv = disk_cache()->OpenEntry(key, entry, cb.callback()); | 
|  | return (cb.GetResult(rv) == net::OK); | 
|  | } | 
|  |  | 
|  | bool MockHttpCache::CreateBackendEntry(const std::string& key, | 
|  | disk_cache::Entry** entry, | 
|  | net::NetLog* net_log) { | 
|  | net::TestCompletionCallback cb; | 
|  | if (!disk_cache()) return false; | 
|  | int rv = disk_cache()->CreateEntry(key, entry, cb.callback()); | 
|  | return (cb.GetResult(rv) == net::OK); | 
|  | } | 
|  |  | 
|  | // Static. | 
|  | int MockHttpCache::GetTestMode(int test_mode) { | 
|  | if (!g_test_mode) | 
|  | return test_mode; | 
|  |  | 
|  | return g_test_mode; | 
|  | } | 
|  |  | 
|  | // Static. | 
|  | void MockHttpCache::SetTestMode(int test_mode) { | 
|  | g_test_mode = test_mode; | 
|  | } | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  |  | 
|  | int MockDiskCacheNoCB::CreateEntry(const std::string& key, | 
|  | disk_cache::Entry** entry, | 
|  | const net::CompletionCallback& callback) { | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  |  | 
|  | int MockBackendNoCbFactory::CreateBackend( | 
|  | net::NetLog* net_log, disk_cache::Backend** backend, | 
|  | const net::CompletionCallback& callback) { | 
|  | *backend = new MockDiskCacheNoCB(); | 
|  | return net::OK; | 
|  | } | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  |  | 
|  | MockBlockingBackendFactory::MockBlockingBackendFactory() | 
|  | : backend_(NULL), | 
|  | block_(true), | 
|  | fail_(false) { | 
|  | } | 
|  |  | 
|  | MockBlockingBackendFactory::~MockBlockingBackendFactory() { | 
|  | } | 
|  |  | 
|  | int MockBlockingBackendFactory::CreateBackend( | 
|  | net::NetLog* net_log, disk_cache::Backend** backend, | 
|  | const net::CompletionCallback& callback) { | 
|  | if (!block_) { | 
|  | if (!fail_) | 
|  | *backend = new MockDiskCache(); | 
|  | return Result(); | 
|  | } | 
|  |  | 
|  | backend_ =  backend; | 
|  | callback_ = callback; | 
|  | return net::ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | void MockBlockingBackendFactory::FinishCreation() { | 
|  | block_ = false; | 
|  | if (!callback_.is_null()) { | 
|  | if (!fail_) | 
|  | *backend_ = new MockDiskCache(); | 
|  | net::CompletionCallback cb = callback_; | 
|  | callback_.Reset(); | 
|  | cb.Run(Result());  // This object can be deleted here. | 
|  | } | 
|  | } |