Index: net/http/http_cache_unittest.cc |
=================================================================== |
--- net/http/http_cache_unittest.cc (revision 28049) |
+++ net/http/http_cache_unittest.cc (working copy) |
@@ -32,11 +32,13 @@ |
public base::RefCounted<MockDiskEntry> { |
public: |
MockDiskEntry() |
- : test_mode_(0), doomed_(false), sparse_(false), fail_requests_(false) { |
+ : test_mode_(0), doomed_(false), sparse_(false), fail_requests_(false), |
+ busy_(false), delayed_(false) { |
} |
explicit MockDiskEntry(const std::string& key) |
- : key_(key), doomed_(false), sparse_(false), fail_requests_(false) { |
+ : key_(key), doomed_(false), sparse_(false), fail_requests_(false), |
+ busy_(false), delayed_(false) { |
// |
// 'key' is prefixed with an identifier if it corresponds to a cached POST. |
// Skip past that to locate the actual URL. |
@@ -131,7 +133,7 @@ |
virtual int ReadSparseData(int64 offset, net::IOBuffer* buf, int buf_len, |
net::CompletionCallback* completion_callback) { |
- if (!sparse_) |
+ if (!sparse_ || busy_) |
return net::ERR_CACHE_OPERATION_NOT_SUPPORTED; |
if (offset < 0) |
return net::ERR_FAILED; |
@@ -152,11 +154,15 @@ |
return num; |
CallbackLater(completion_callback, num); |
+ busy_ = true; |
+ delayed_ = false; |
return net::ERR_IO_PENDING; |
} |
virtual int WriteSparseData(int64 offset, net::IOBuffer* buf, int buf_len, |
net::CompletionCallback* completion_callback) { |
+ if (busy_) |
+ return net::ERR_CACHE_OPERATION_NOT_SUPPORTED; |
if (!sparse_) { |
if (data_[1].size()) |
return net::ERR_CACHE_OPERATION_NOT_SUPPORTED; |
@@ -181,7 +187,7 @@ |
} |
virtual int GetAvailableRange(int64 offset, int len, int64* start) { |
- if (!sparse_) |
+ if (!sparse_ || busy_) |
return net::ERR_CACHE_OPERATION_NOT_SUPPORTED; |
if (offset < 0) |
return net::ERR_FAILED; |
@@ -212,6 +218,23 @@ |
return count; |
} |
+ virtual void CancelSparseIO() { cancel_ = true; } |
+ |
+ virtual int ReadyForSparseIO(net::CompletionCallback* completion_callback) { |
+ if (!cancel_) |
+ return net::OK; |
+ |
+ cancel_ = false; |
+ DCHECK(completion_callback); |
+ if (test_mode_ & TEST_MODE_SYNC_CACHE_READ) |
+ return net::OK; |
+ |
+ // The pending operation is already in the message loop (and hopefuly |
+ // already in the second pass). Just notify the caller that it finished. |
+ CallbackLater(completion_callback, 0); |
+ return net::ERR_IO_PENDING; |
+ } |
+ |
// Fail most subsequent requests. |
void set_fail_requests() { fail_requests_ = true; } |
@@ -224,6 +247,21 @@ |
&MockDiskEntry::RunCallback, callback, result)); |
} |
void RunCallback(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 trhough the message loop instead of one). |
+ if (!delayed_) { |
+ delayed_ = true; |
+ return CallbackLater(callback, result); |
+ } |
+ } |
+ busy_ = false; |
callback->Run(result); |
} |
@@ -233,8 +271,14 @@ |
bool doomed_; |
bool sparse_; |
bool fail_requests_; |
+ bool busy_; |
+ bool delayed_; |
+ static bool cancel_; |
}; |
+// Static. |
+bool MockDiskEntry::cancel_ = false; |
+ |
class MockDiskCache : public disk_cache::Backend { |
public: |
MockDiskCache() |
@@ -2344,6 +2388,52 @@ |
RemoveMockTransaction(&kRangeGET_TransactionOK); |
} |
+// Tests that we don't delete a sparse entry when we start a new request after |
+// cancelling the previous one. |
+TEST(HttpCache, RangeGET_Cancel2) { |
+ MockHttpCache cache; |
+ cache.http_cache()->set_enable_range_support(true); |
+ AddMockTransaction(&kRangeGET_TransactionOK); |
+ |
+ RunTransactionTest(cache.http_cache(), kRangeGET_TransactionOK); |
+ MockHttpRequest request(kRangeGET_TransactionOK); |
+ |
+ Context* c = new Context(); |
+ int rv = cache.http_cache()->CreateTransaction(&c->trans); |
+ EXPECT_EQ(net::OK, rv); |
+ |
+ rv = c->trans->Start(&request, &c->callback, NULL); |
+ if (rv == net::ERR_IO_PENDING) |
+ rv = c->callback.WaitForResult(); |
+ |
+ EXPECT_EQ(2, cache.network_layer()->transaction_count()); |
+ EXPECT_EQ(1, cache.disk_cache()->open_count()); |
+ EXPECT_EQ(1, cache.disk_cache()->create_count()); |
+ |
+ // Make sure that we revalidate the entry and read from the cache (a single |
+ // read will return while waiting for the network). |
+ scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(5); |
+ rv = c->trans->Read(buf, buf->size(), &c->callback); |
+ EXPECT_EQ(net::ERR_IO_PENDING, rv); |
+ rv = c->callback.WaitForResult(); |
+ rv = c->trans->Read(buf, buf->size(), &c->callback); |
+ EXPECT_EQ(net::ERR_IO_PENDING, rv); |
+ |
+ // Destroy the transaction before completing the read. |
+ delete c; |
+ |
+ // We have the read and the delete (OnProcessPendingQueue) waiting on the |
+ // message loop. This means that a new transaction will just reuse the same |
+ // active entry (no open or create). |
+ |
+ RunTransactionTest(cache.http_cache(), kRangeGET_TransactionOK); |
+ |
+ EXPECT_EQ(3, cache.network_layer()->transaction_count()); |
+ EXPECT_EQ(1, cache.disk_cache()->open_count()); |
+ EXPECT_EQ(1, cache.disk_cache()->create_count()); |
+ RemoveMockTransaction(&kRangeGET_TransactionOK); |
+} |
+ |
#ifdef NDEBUG |
// This test hits a NOTREACHED so it is a release mode only test. |
TEST(HttpCache, RangeGET_OK_LoadOnlyFromCache) { |