| Index: net/http/http_cache_unittest.cc
 | 
| diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
 | 
| index 13182e43f640f9b903d42de3ea9fb53c4c9481f6..62269b7952c8b89bc3d66dc773b42c22f62311fb 100644
 | 
| --- a/net/http/http_cache_unittest.cc
 | 
| +++ b/net/http/http_cache_unittest.cc
 | 
| @@ -147,6 +147,10 @@ void TestLoadTimingCachedResponse(const LoadTimingInfo& load_timing_info) {
 | 
|    EXPECT_TRUE(load_timing_info.receive_headers_end.is_null());
 | 
|  }
 | 
|  
 | 
| +void DeferNetworkStart(bool* defer) {
 | 
| +  *defer = true;
 | 
| +}
 | 
| +
 | 
|  class DeleteCacheCompletionCallback : public TestCompletionCallbackBase {
 | 
|   public:
 | 
|    explicit DeleteCacheCompletionCallback(MockHttpCache* cache)
 | 
| @@ -182,6 +186,17 @@ void ReadAndVerifyTransaction(HttpTransaction* trans,
 | 
|    EXPECT_EQ(expected, content);
 | 
|  }
 | 
|  
 | 
| +void ReadRemainingAndVerifyTransaction(HttpTransaction* trans,
 | 
| +                                       std::string& already_read,
 | 
| +                                       const MockTransaction& trans_info) {
 | 
| +  std::string content;
 | 
| +  int rv = ReadTransaction(trans, &content);
 | 
| +  EXPECT_THAT(rv, IsOk());
 | 
| +
 | 
| +  std::string expected(trans_info.data);
 | 
| +  EXPECT_EQ(expected, already_read + content);
 | 
| +}
 | 
| +
 | 
|  void RunTransactionTestBase(HttpCache* cache,
 | 
|                              const MockTransaction& trans_info,
 | 
|                              const MockHttpRequest& request,
 | 
| @@ -1176,180 +1191,1036 @@ TEST(HttpCache, SimpleGET_LoadBypassCache_Implicit) {
 | 
|    MockTransaction transaction(kSimpleGET_Transaction);
 | 
|    transaction.request_headers = "pragma: no-cache\r\n";
 | 
|  
 | 
| -  RunTransactionTest(cache.http_cache(), transaction);
 | 
| +  RunTransactionTest(cache.http_cache(), transaction);
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(2, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +TEST(HttpCache, SimpleGET_LoadBypassCache_Implicit2) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  // write to the cache
 | 
| +  RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
 | 
| +
 | 
| +  // force this transaction to write to the cache again
 | 
| +  MockTransaction transaction(kSimpleGET_Transaction);
 | 
| +  transaction.request_headers = "cache-control: no-cache\r\n";
 | 
| +
 | 
| +  RunTransactionTest(cache.http_cache(), transaction);
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(2, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +TEST(HttpCache, SimpleGET_LoadValidateCache) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  // Write to the cache.
 | 
| +  RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
 | 
| +
 | 
| +  // Read from the cache.
 | 
| +  RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
 | 
| +
 | 
| +  // Force this transaction to validate the cache.
 | 
| +  MockTransaction transaction(kSimpleGET_Transaction);
 | 
| +  transaction.load_flags |= LOAD_VALIDATE_CACHE;
 | 
| +
 | 
| +  HttpResponseInfo response_info;
 | 
| +  BoundTestNetLog log;
 | 
| +  LoadTimingInfo load_timing_info;
 | 
| +  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| +      cache.http_cache(), transaction, &response_info, log.bound(),
 | 
| +      &load_timing_info);
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +  EXPECT_TRUE(response_info.network_accessed);
 | 
| +  TestLoadTimingNetworkRequest(load_timing_info);
 | 
| +}
 | 
| +
 | 
| +TEST(HttpCache, SimpleGET_LoadValidateCache_Implicit) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  // write to the cache
 | 
| +  RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
 | 
| +
 | 
| +  // read from the cache
 | 
| +  RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
 | 
| +
 | 
| +  // force this transaction to validate the cache
 | 
| +  MockTransaction transaction(kSimpleGET_Transaction);
 | 
| +  transaction.request_headers = "cache-control: max-age=0\r\n";
 | 
| +
 | 
| +  RunTransactionTest(cache.http_cache(), transaction);
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +// Tests that |unused_since_prefetch| is updated accordingly (e.g. it is set to
 | 
| +// true after a prefetch and set back to false when the prefetch is used).
 | 
| +TEST(HttpCache, SimpleGET_UnusedSincePrefetch) {
 | 
| +  MockHttpCache cache;
 | 
| +  HttpResponseInfo response_info;
 | 
| +
 | 
| +  // A normal load does not have |unused_since_prefetch| set.
 | 
| +  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| +      cache.http_cache(), kSimpleGET_Transaction, &response_info,
 | 
| +      BoundTestNetLog().bound(), nullptr);
 | 
| +  EXPECT_FALSE(response_info.unused_since_prefetch);
 | 
| +  EXPECT_FALSE(response_info.was_cached);
 | 
| +
 | 
| +  // The prefetch itself does not have |unused_since_prefetch| set.
 | 
| +  MockTransaction prefetch_transaction(kSimpleGET_Transaction);
 | 
| +  prefetch_transaction.load_flags |= LOAD_PREFETCH;
 | 
| +  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| +      cache.http_cache(), prefetch_transaction, &response_info,
 | 
| +      BoundTestNetLog().bound(), nullptr);
 | 
| +  EXPECT_FALSE(response_info.unused_since_prefetch);
 | 
| +  EXPECT_TRUE(response_info.was_cached);
 | 
| +
 | 
| +  // A duplicated prefetch has |unused_since_prefetch| set.
 | 
| +  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| +      cache.http_cache(), prefetch_transaction, &response_info,
 | 
| +      BoundTestNetLog().bound(), nullptr);
 | 
| +  EXPECT_TRUE(response_info.unused_since_prefetch);
 | 
| +  EXPECT_TRUE(response_info.was_cached);
 | 
| +
 | 
| +  // |unused_since_prefetch| is still true after two prefetches in a row.
 | 
| +  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| +      cache.http_cache(), kSimpleGET_Transaction, &response_info,
 | 
| +      BoundTestNetLog().bound(), nullptr);
 | 
| +  EXPECT_TRUE(response_info.unused_since_prefetch);
 | 
| +  EXPECT_TRUE(response_info.was_cached);
 | 
| +
 | 
| +  // The resource has now been used, back to normal behavior.
 | 
| +  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| +      cache.http_cache(), kSimpleGET_Transaction, &response_info,
 | 
| +      BoundTestNetLog().bound(), nullptr);
 | 
| +  EXPECT_FALSE(response_info.unused_since_prefetch);
 | 
| +  EXPECT_TRUE(response_info.was_cached);
 | 
| +}
 | 
| +
 | 
| +static void PreserveRequestHeaders_Handler(const HttpRequestInfo* request,
 | 
| +                                           std::string* response_status,
 | 
| +                                           std::string* response_headers,
 | 
| +                                           std::string* response_data) {
 | 
| +  EXPECT_TRUE(request->extra_headers.HasHeader(kExtraHeaderKey));
 | 
| +}
 | 
| +
 | 
| +// Tests that we don't remove extra headers for simple requests.
 | 
| +TEST(HttpCache, SimpleGET_PreserveRequestHeaders) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  MockTransaction transaction(kSimpleGET_Transaction);
 | 
| +  transaction.handler = PreserveRequestHeaders_Handler;
 | 
| +  transaction.request_headers = EXTRA_HEADER;
 | 
| +  transaction.response_headers = "Cache-Control: max-age=0\n";
 | 
| +  AddMockTransaction(&transaction);
 | 
| +
 | 
| +  // Write, then revalidate the entry.
 | 
| +  RunTransactionTest(cache.http_cache(), transaction);
 | 
| +  RunTransactionTest(cache.http_cache(), transaction);
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +  RemoveMockTransaction(&transaction);
 | 
| +}
 | 
| +
 | 
| +// Tests that we don't remove extra headers for conditionalized requests.
 | 
| +TEST(HttpCache, ConditionalizedGET_PreserveRequestHeaders) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  // Write to the cache.
 | 
| +  RunTransactionTest(cache.http_cache(), kETagGET_Transaction);
 | 
| +
 | 
| +  MockTransaction transaction(kETagGET_Transaction);
 | 
| +  transaction.handler = PreserveRequestHeaders_Handler;
 | 
| +  transaction.request_headers = "If-None-Match: \"foopy\"\r\n" EXTRA_HEADER;
 | 
| +  AddMockTransaction(&transaction);
 | 
| +
 | 
| +  RunTransactionTest(cache.http_cache(), transaction);
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +  RemoveMockTransaction(&transaction);
 | 
| +}
 | 
| +
 | 
| +TEST(HttpCache, SimpleGET_ManyReaders) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  MockHttpRequest request(kSimpleGET_Transaction);
 | 
| +
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
| +  const int kNumTransactions = 5;
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +    auto& c = context_list[i];
 | 
| +
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
 | 
| +
 | 
| +    c->result =
 | 
| +        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
 | 
| +  }
 | 
| +
 | 
| +  // All requests are waiting for the active entry.
 | 
| +  for (auto& context : context_list) {
 | 
| +    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  // Allow all requests to move from the Create queue to the active entry.
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  // The first request should be a writer at this point, and the subsequent
 | 
| +  // requests should have passed the validation phase and waiting for the
 | 
| +  // response to be written to the cache before they can read.
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_EQ(kNumTransactions - 1,
 | 
| +            cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  EXPECT_EQ(1, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  // All requests depend on the writer, and the writer is between Start and
 | 
| +  // Read, i.e. idle.
 | 
| +  for (auto& context : context_list) {
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    auto& c = context_list[i];
 | 
| +    if (c->result == ERR_IO_PENDING)
 | 
| +      c->result = c->callback.WaitForResult();
 | 
| +
 | 
| +    if (i > 0) {
 | 
| +      EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +      EXPECT_EQ(kNumTransactions - i,
 | 
| +                cache.GetCountReaders(kSimpleGET_Transaction.url));
 | 
| +    }
 | 
| +
 | 
| +    ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
 | 
| +  }
 | 
| +
 | 
| +  // We should not have had to re-open the disk entry
 | 
| +
 | 
| +  EXPECT_EQ(1, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +// Tests that we can have parallel validation on range requests.
 | 
| +TEST(HttpCache, RangeGET_ParallelValidationNoMatch) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  ScopedMockTransaction transaction(kRangeGET_TransactionOK);
 | 
| +  MockHttpRequest request(transaction);
 | 
| +
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
| +  const int kNumTransactions = 5;
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +    auto& c = context_list[i];
 | 
| +
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
 | 
| +
 | 
| +    c->result =
 | 
| +        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
 | 
| +  }
 | 
| +
 | 
| +  // All requests are waiting for the active entry.
 | 
| +  for (auto& context : context_list) {
 | 
| +    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  // Allow all requests to move from the Create queue to the active entry.
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  // The first entry should have been doomed. Since the 1st transaction has not
 | 
| +  // started writing to the cache, MockDiskEntry::CouldBeSparse() returns false
 | 
| +  // leading to restarting the dooming the entry and restarting the second
 | 
| +  // transaction.
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
 | 
| +  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
 | 
| +
 | 
| +  EXPECT_EQ(5, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(5, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  for (auto& context : context_list) {
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    auto& c = context_list[i];
 | 
| +    if (c->result == ERR_IO_PENDING)
 | 
| +      c->result = c->callback.WaitForResult();
 | 
| +
 | 
| +    ReadAndVerifyTransaction(c->trans.get(), kRangeGET_TransactionOK);
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(5, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(5, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +// Tests parallel validation on range requests with non-overlapping ranges.
 | 
| +TEST(HttpCache, RangeGET_ParallelValidationDifferentRanges) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  ScopedMockTransaction transaction(kRangeGET_TransactionOK);
 | 
| +
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
| +  const int kNumTransactions = 2;
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +  }
 | 
| +
 | 
| +  // Let 1st transaction complete headers phase for ranges 40-49.
 | 
| +  std::string first_read;
 | 
| +  {
 | 
| +    MockHttpRequest request(transaction);
 | 
| +    auto& c = context_list[0];
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
 | 
| +
 | 
| +    c->result =
 | 
| +        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
 | 
| +    base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +    // Start writing to the cache so that MockDiskEntry::CouldBeSparse() returns
 | 
| +    // true.
 | 
| +    const int kBufferSize = 5;
 | 
| +    scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize));
 | 
| +    ReleaseBufferCompletionCallback cb(buffer.get());
 | 
| +    c->result = c->trans->Read(buffer.get(), kBufferSize, cb.callback());
 | 
| +    EXPECT_EQ(kBufferSize, cb.GetResult(c->result));
 | 
| +
 | 
| +    std::string data_read(buffer->data(), kBufferSize);
 | 
| +    first_read = data_read;
 | 
| +
 | 
| +    EXPECT_EQ(LOAD_STATE_READING_RESPONSE, c->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  // 2nd transaction requests ranges 30-39.
 | 
| +  {
 | 
| +    transaction.request_headers = "Range: bytes = 30-39\r\n" EXTRA_HEADER;
 | 
| +    MockHttpRequest request(transaction);
 | 
| +    auto& c = context_list[1];
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
 | 
| +
 | 
| +    c->result =
 | 
| +        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
 | 
| +    base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
 | 
| +  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    auto& c = context_list[i];
 | 
| +    if (c->result == ERR_IO_PENDING)
 | 
| +      c->result = c->callback.WaitForResult();
 | 
| +
 | 
| +    if (i == 0) {
 | 
| +      ReadRemainingAndVerifyTransaction(c->trans.get(), first_read,
 | 
| +                                        transaction);
 | 
| +      continue;
 | 
| +    }
 | 
| +
 | 
| +    transaction.data = "rg: 30-39 ";
 | 
| +    ReadAndVerifyTransaction(c->trans.get(), transaction);
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  // Fetch from the cache to check that ranges 30-49 have been successfully
 | 
| +  // cached.
 | 
| +  {
 | 
| +    MockTransaction transaction(kRangeGET_TransactionOK);
 | 
| +    transaction.request_headers = "Range: bytes = 30-49\r\n" EXTRA_HEADER;
 | 
| +    transaction.data = "rg: 30-39 rg: 40-49 ";
 | 
| +    std::string headers;
 | 
| +    RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 | 
| +    Verify206Response(headers, 30, 49);
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +// Tests parallel validation on range requests with overlapping ranges.
 | 
| +TEST(HttpCache, RangeGET_ParallelValidationOverlappingRanges) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  ScopedMockTransaction transaction(kRangeGET_TransactionOK);
 | 
| +
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
| +  const int kNumTransactions = 2;
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +  }
 | 
| +
 | 
| +  // Let 1st transaction complete headers phase for ranges 40-49.
 | 
| +  std::string first_read;
 | 
| +  {
 | 
| +    MockHttpRequest request(transaction);
 | 
| +    auto& c = context_list[0];
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
 | 
| +
 | 
| +    c->result =
 | 
| +        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
 | 
| +    base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +    // Start writing to the cache so that MockDiskEntry::CouldBeSparse() returns
 | 
| +    // true.
 | 
| +    const int kBufferSize = 5;
 | 
| +    scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize));
 | 
| +    ReleaseBufferCompletionCallback cb(buffer.get());
 | 
| +    c->result = c->trans->Read(buffer.get(), kBufferSize, cb.callback());
 | 
| +    EXPECT_EQ(kBufferSize, cb.GetResult(c->result));
 | 
| +
 | 
| +    std::string data_read(buffer->data(), kBufferSize);
 | 
| +    first_read = data_read;
 | 
| +
 | 
| +    EXPECT_EQ(LOAD_STATE_READING_RESPONSE, c->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  // 2nd transaction requests ranges 30-49.
 | 
| +  {
 | 
| +    transaction.request_headers = "Range: bytes = 30-49\r\n" EXTRA_HEADER;
 | 
| +    MockHttpRequest request(transaction);
 | 
| +    auto& c = context_list[1];
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
 | 
| +
 | 
| +    c->result =
 | 
| +        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
 | 
| +    base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
 | 
| +  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
 | 
| +
 | 
| +  // Should have created another transaction for the uncached range.
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    auto& c = context_list[i];
 | 
| +    if (c->result == ERR_IO_PENDING)
 | 
| +      c->result = c->callback.WaitForResult();
 | 
| +
 | 
| +    if (i == 0) {
 | 
| +      ReadRemainingAndVerifyTransaction(c->trans.get(), first_read,
 | 
| +                                        transaction);
 | 
| +      continue;
 | 
| +    }
 | 
| +
 | 
| +    transaction.data = "rg: 30-39 rg: 40-49 ";
 | 
| +    ReadAndVerifyTransaction(c->trans.get(), transaction);
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  // Fetch from the cache to check that ranges 30-49 have been successfully
 | 
| +  // cached.
 | 
| +  {
 | 
| +    MockTransaction transaction(kRangeGET_TransactionOK);
 | 
| +    transaction.request_headers = "Range: bytes = 30-49\r\n" EXTRA_HEADER;
 | 
| +    transaction.data = "rg: 30-39 rg: 40-49 ";
 | 
| +    std::string headers;
 | 
| +    RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 | 
| +    Verify206Response(headers, 30, 49);
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +// Parallel validation results in 200.
 | 
| +TEST(HttpCache, SimpleGET_ParallelValidationNoMatch) {
 | 
| +  MockHttpCache cache;
 | 
| +  MockHttpRequest request(kSimpleGET_Transaction);
 | 
| +  request.load_flags |= LOAD_VALIDATE_CACHE;
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
| +  const int kNumTransactions = 5;
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +    auto& c = context_list[i];
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
 | 
| +    c->result =
 | 
| +        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
 | 
| +  }
 | 
| +
 | 
| +  // All requests are waiting for the active entry.
 | 
| +  for (auto& context : context_list) {
 | 
| +    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  // Allow all requests to move from the Create queue to the active entry.
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  // The first request should be a writer at this point, and the subsequent
 | 
| +  // requests should have passed the validation phase and created their own
 | 
| +  // entries since none of them matched the headers of the earlier one.
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Note that there are only 3 entries created and not 5 since every other
 | 
| +  // transaction would have gone to the network.
 | 
| +  EXPECT_EQ(5, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(3, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  // All requests depend on the writer, and the writer is between Start and
 | 
| +  // Read, i.e. idle.
 | 
| +  for (auto& context : context_list) {
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  for (auto& context : context_list) {
 | 
| +    if (context->result == ERR_IO_PENDING)
 | 
| +      context->result = context->callback.WaitForResult();
 | 
| +    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(5, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(3, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +// Tests that a GET followed by a DELETE results in DELETE immediately starting
 | 
| +// the headers phase and the entry is doomed.
 | 
| +TEST(HttpCache, SimpleGET_ParallelValidationDelete) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  MockHttpRequest request(kSimpleGET_Transaction);
 | 
| +  request.load_flags |= LOAD_VALIDATE_CACHE;
 | 
| +
 | 
| +  MockHttpRequest delete_request(kSimpleGET_Transaction);
 | 
| +  delete_request.method = "DELETE";
 | 
| +
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
| +  const int kNumTransactions = 2;
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +    auto& c = context_list[i];
 | 
| +
 | 
| +    MockHttpRequest* this_request = &request;
 | 
| +    if (i == 1)
 | 
| +      this_request = &delete_request;
 | 
| +
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
 | 
| +
 | 
| +    c->result = c->trans->Start(this_request, c->callback.callback(),
 | 
| +                                NetLogWithSource());
 | 
| +  }
 | 
| +
 | 
| +  // All requests are waiting for the active entry.
 | 
| +  for (auto& context : context_list) {
 | 
| +    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  // Allow all requests to move from the Create queue to the active entry.
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  // The first request should be a writer at this point, and the subsequent
 | 
| +  // request should have passed the validation phase and doomed the existing
 | 
| +  // entry.
 | 
| +  EXPECT_TRUE(
 | 
| +      cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  // All requests depend on the writer, and the writer is between Start and
 | 
| +  // Read, i.e. idle.
 | 
| +  for (auto& context : context_list) {
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  for (auto& context : context_list) {
 | 
| +    if (context->result == ERR_IO_PENDING)
 | 
| +      context->result = context->callback.WaitForResult();
 | 
| +    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +// Tests that a transaction which is in validated queue can be destroyed without
 | 
| +// any impact to other transactions.
 | 
| +TEST(HttpCache, SimpleGET_ParallelValidationCancelValidated) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  MockHttpRequest request(kSimpleGET_Transaction);
 | 
| +
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
| +  const int kNumTransactions = 2;
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +    auto& c = context_list[i];
 | 
| +
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +
 | 
| +    c->result =
 | 
| +        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
 | 
| +  }
 | 
| +
 | 
| +  // Allow all requests to move from the Create queue to the active entry.
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  // The first request should be a writer at this point, and the subsequent
 | 
| +  // requests should have completed validation.
 | 
| +
 | 
| +  EXPECT_EQ(1, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  context_list[1].reset();
 | 
| +
 | 
| +  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Complete the rest of the transactions.
 | 
| +  for (auto& context : context_list) {
 | 
| +    if (!context)
 | 
| +      continue;
 | 
| +    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(1, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +// Tests that a transaction which is in readers can be destroyed without
 | 
| +// any impact to other transactions.
 | 
| +TEST(HttpCache, SimpleGET_ParallelValidationCancelReader) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  MockHttpRequest request(kSimpleGET_Transaction);
 | 
| +
 | 
| +  MockTransaction transaction(kSimpleGET_Transaction);
 | 
| +  transaction.load_flags |= LOAD_VALIDATE_CACHE;
 | 
| +  MockHttpRequest validate_request(transaction);
 | 
| +
 | 
| +  int kNumTransactions = 4;
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +    auto& c = context_list[i];
 | 
| +
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +
 | 
| +    MockHttpRequest* this_request = &request;
 | 
| +    if (i == 3) {
 | 
| +      this_request = &validate_request;
 | 
| +      c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
 | 
| +    }
 | 
| +
 | 
| +    c->result = c->trans->Start(this_request, c->callback.callback(),
 | 
| +                                NetLogWithSource());
 | 
| +  }
 | 
| +
 | 
| +  // Allow all requests to move from the Create queue to the active entry.
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_EQ(2, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Complete the response body.
 | 
| +  auto& c = context_list[0];
 | 
| +  ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
 | 
| +
 | 
| +  // Rest of the transactions should move to readers.
 | 
| +  EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_EQ(2, cache.GetCountReaders(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Add 2 new transactions.
 | 
| +  kNumTransactions = 6;
 | 
| +
 | 
| +  for (int i = 4; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +    auto& c = context_list[i];
 | 
| +
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +
 | 
| +    c->result =
 | 
| +        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Delete a reader.
 | 
| +  context_list[1].reset();
 | 
| +
 | 
| +  // Deleting the reader did not impact any other transaction.
 | 
| +  EXPECT_EQ(1, cache.GetCountReaders(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Resume network start for headers_transaction. It will doom the entry as it
 | 
| +  // will be a 200 and will go to network for the response body.
 | 
| +  auto& context = context_list[3];
 | 
| +  context->trans->ResumeNetworkStart();
 | 
| +
 | 
| +  // The pending transactions will be added to a new entry.
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Complete the rest of the transactions.
 | 
| +  for (int i = 2; i < kNumTransactions; ++i) {
 | 
| +    auto& c = context_list[i];
 | 
| +    ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(3, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(2, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +// Tests that a transaction is in validated queue and writer is destroyed
 | 
| +// leading to restarting the validated transaction.
 | 
| +TEST(HttpCache, SimpleGET_ParallelValidationCancelWriter) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  MockHttpRequest request(kSimpleGET_Transaction);
 | 
| +
 | 
| +  MockTransaction transaction(kSimpleGET_Transaction);
 | 
| +  transaction.load_flags |= LOAD_VALIDATE_CACHE;
 | 
| +  MockHttpRequest validate_request(transaction);
 | 
| +
 | 
| +  const int kNumTransactions = 3;
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +    auto& c = context_list[i];
 | 
| +
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +
 | 
| +    MockHttpRequest* this_request = &request;
 | 
| +    if (i == 2) {
 | 
| +      this_request = &validate_request;
 | 
| +      c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
 | 
| +    }
 | 
| +
 | 
| +    c->result = c->trans->Start(this_request, c->callback.callback(),
 | 
| +                                NetLogWithSource());
 | 
| +  }
 | 
| +
 | 
| +  // Allow all requests to move from the Create queue to the active entry.
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Deleting the writer at this point will lead to destroying the entry and
 | 
| +  // restarting the remaining transactions which will then create a new entry.
 | 
| +  context_list[0].reset();
 | 
| +
 | 
| +  // Resume network start for headers_transaction. It should be restarted due to
 | 
| +  // writer cancellation.
 | 
| +  auto& c = context_list[2];
 | 
| +  c->trans->ResumeNetworkStart();
 | 
| +
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Resume network start for the transaction the second time.
 | 
| +  c->trans->ResumeNetworkStart();
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  // Headers transaction would have doomed the new entry created.
 | 
| +  EXPECT_TRUE(
 | 
| +      cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Complete the rest of the transactions.
 | 
| +  for (auto& context : context_list) {
 | 
| +    if (!context)
 | 
| +      continue;
 | 
| +    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(4, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(2, cache.disk_cache()->create_count());
 | 
| +}
 | 
| +
 | 
| +// Tests when a writer is destroyed mid-read and entry is marked as truncated,
 | 
| +// it should lead to restarting the dependent transactions.
 | 
| +TEST(HttpCache, SimpleGET_ParallelValidationCancelWriterTruncateEntry) {
 | 
| +  MockHttpCache cache;
 | 
| +
 | 
| +  ScopedMockTransaction transaction(kSimpleGET_Transaction);
 | 
| +  transaction.response_headers =
 | 
| +      "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n"
 | 
| +      "Content-Length: 22\n"
 | 
| +      "Etag: \"foopy\"\n";
 | 
| +  MockHttpRequest request(transaction);
 | 
| +
 | 
| +  const int kNumTransactions = 3;
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
| +
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +    auto& c = context_list[i];
 | 
| +
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
| +
 | 
| +    c->result =
 | 
| +        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
 | 
| +  }
 | 
| +
 | 
| +  // Allow all requests to move from the Create queue to the active entry.
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  EXPECT_EQ(1, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_EQ(2, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Let first transaction read some bytes, so that entry can be marked as
 | 
| +  // truncated.
 | 
| +  {
 | 
| +    auto& c = context_list[0];
 | 
| +    const int kBufferSize = 5;
 | 
| +    scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize));
 | 
| +    ReleaseBufferCompletionCallback cb(buffer.get());
 | 
| +    c->result = c->trans->Read(buffer.get(), kBufferSize, cb.callback());
 | 
| +    EXPECT_EQ(kBufferSize, cb.GetResult(c->result));
 | 
| +  }
 | 
| +
 | 
| +  // Deleting the writer at this point will lead to restarting the remaining
 | 
| +  // transactions which will then be able to read the truncated status of the
 | 
| +  // entry.
 | 
| +  context_list[0].reset();
 | 
| +
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
|  
 | 
|    EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
|    EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| -  EXPECT_EQ(2, cache.disk_cache()->create_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +
 | 
| +  // Complete the rest of the transactions.
 | 
| +  for (auto& context : context_list) {
 | 
| +    if (!context)
 | 
| +      continue;
 | 
| +    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
 | 
| +  }
 | 
|  }
 | 
|  
 | 
| -TEST(HttpCache, SimpleGET_LoadBypassCache_Implicit2) {
 | 
| +// Tests that when StopCaching is invoked on a writer, dependent transactions
 | 
| +// are restarted.
 | 
| +TEST(HttpCache, SimpleGET_ParallelValidationStopCaching) {
 | 
|    MockHttpCache cache;
 | 
|  
 | 
| -  // write to the cache
 | 
| -  RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
 | 
| +  MockHttpRequest request(kSimpleGET_Transaction);
 | 
|  
 | 
| -  // force this transaction to write to the cache again
 | 
|    MockTransaction transaction(kSimpleGET_Transaction);
 | 
| -  transaction.request_headers = "cache-control: no-cache\r\n";
 | 
| -
 | 
| -  RunTransactionTest(cache.http_cache(), transaction);
 | 
| +  transaction.load_flags |= LOAD_VALIDATE_CACHE;
 | 
| +  MockHttpRequest validate_request(transaction);
 | 
|  
 | 
| -  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| -  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| -  EXPECT_EQ(2, cache.disk_cache()->create_count());
 | 
| -}
 | 
| +  const int kNumTransactions = 3;
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
|  
 | 
| -TEST(HttpCache, SimpleGET_LoadValidateCache) {
 | 
| -  MockHttpCache cache;
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +    auto& c = context_list[i];
 | 
|  
 | 
| -  // Write to the cache.
 | 
| -  RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
|  
 | 
| -  // Read from the cache.
 | 
| -  RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
 | 
| +    MockHttpRequest* this_request = &request;
 | 
| +    if (i == 2) {
 | 
| +      this_request = &validate_request;
 | 
| +      c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
 | 
| +    }
 | 
|  
 | 
| -  // Force this transaction to validate the cache.
 | 
| -  MockTransaction transaction(kSimpleGET_Transaction);
 | 
| -  transaction.load_flags |= LOAD_VALIDATE_CACHE;
 | 
| +    c->result = c->trans->Start(this_request, c->callback.callback(),
 | 
| +                                NetLogWithSource());
 | 
| +  }
 | 
|  
 | 
| -  HttpResponseInfo response_info;
 | 
| -  BoundTestNetLog log;
 | 
| -  LoadTimingInfo load_timing_info;
 | 
| -  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| -      cache.http_cache(), transaction, &response_info, log.bound(),
 | 
| -      &load_timing_info);
 | 
| +  // Allow all requests to move from the Create queue to the active entry.
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
|  
 | 
|    EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| -  EXPECT_EQ(1, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
|    EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| -  EXPECT_TRUE(response_info.network_accessed);
 | 
| -  TestLoadTimingNetworkRequest(load_timing_info);
 | 
| -}
 | 
|  
 | 
| -TEST(HttpCache, SimpleGET_LoadValidateCache_Implicit) {
 | 
| -  MockHttpCache cache;
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
 | 
|  
 | 
| -  // write to the cache
 | 
| -  RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
 | 
| +  // Invoking StopCaching on the writer will lead to dooming the entry and
 | 
| +  // restarting the remaining transactions which will then create a new entry.
 | 
| +  context_list[0]->trans->StopCaching();
 | 
|  
 | 
| -  // read from the cache
 | 
| -  RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
 | 
| +  // Resume network start for headers_transaction. It should be restarted due to
 | 
| +  // writer cancellation.
 | 
| +  auto& c = context_list[2];
 | 
| +  c->trans->ResumeNetworkStart();
 | 
|  
 | 
| -  // force this transaction to validate the cache
 | 
| -  MockTransaction transaction(kSimpleGET_Transaction);
 | 
| -  transaction.request_headers = "cache-control: max-age=0\r\n";
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
|  
 | 
| -  RunTransactionTest(cache.http_cache(), transaction);
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
 | 
|  
 | 
| -  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| -  EXPECT_EQ(1, cache.disk_cache()->open_count());
 | 
| -  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +  // Resume network start for the transaction the second time.
 | 
| +  c->trans->ResumeNetworkStart();
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  // Headers transaction would have doomed the new entry created.
 | 
| +  EXPECT_TRUE(
 | 
| +      cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // Complete the rest of the transactions.
 | 
| +  for (auto& context : context_list) {
 | 
| +    if (!context)
 | 
| +      continue;
 | 
| +    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
 | 
| +  }
 | 
| +
 | 
| +  EXPECT_EQ(4, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(2, cache.disk_cache()->create_count());
 | 
|  }
 | 
|  
 | 
| -// Tests that |unused_since_prefetch| is updated accordingly (e.g. it is set to
 | 
| -// true after a prefetch and set back to false when the prefetch is used).
 | 
| -TEST(HttpCache, SimpleGET_UnusedSincePrefetch) {
 | 
| +// Tests that a transaction is currently in headers phase and is destroyed
 | 
| +// leading to destroying the entry.
 | 
| +TEST(HttpCache, SimpleGET_ParallelValidationCancelHeaders) {
 | 
|    MockHttpCache cache;
 | 
| -  HttpResponseInfo response_info;
 | 
| -
 | 
| -  // A normal load does not have |unused_since_prefetch| set.
 | 
| -  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| -      cache.http_cache(), kSimpleGET_Transaction, &response_info,
 | 
| -      BoundTestNetLog().bound(), nullptr);
 | 
| -  EXPECT_FALSE(response_info.unused_since_prefetch);
 | 
| -  EXPECT_FALSE(response_info.was_cached);
 | 
|  
 | 
| -  // The prefetch itself does not have |unused_since_prefetch| set.
 | 
| -  MockTransaction prefetch_transaction(kSimpleGET_Transaction);
 | 
| -  prefetch_transaction.load_flags |= LOAD_PREFETCH;
 | 
| -  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| -      cache.http_cache(), prefetch_transaction, &response_info,
 | 
| -      BoundTestNetLog().bound(), nullptr);
 | 
| -  EXPECT_FALSE(response_info.unused_since_prefetch);
 | 
| -  EXPECT_TRUE(response_info.was_cached);
 | 
| +  MockHttpRequest request(kSimpleGET_Transaction);
 | 
|  
 | 
| -  // A duplicated prefetch has |unused_since_prefetch| set.
 | 
| -  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| -      cache.http_cache(), prefetch_transaction, &response_info,
 | 
| -      BoundTestNetLog().bound(), nullptr);
 | 
| -  EXPECT_TRUE(response_info.unused_since_prefetch);
 | 
| -  EXPECT_TRUE(response_info.was_cached);
 | 
| +  const int kNumTransactions = 2;
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
|  
 | 
| -  // |unused_since_prefetch| is still true after two prefetches in a row.
 | 
| -  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| -      cache.http_cache(), kSimpleGET_Transaction, &response_info,
 | 
| -      BoundTestNetLog().bound(), nullptr);
 | 
| -  EXPECT_TRUE(response_info.unused_since_prefetch);
 | 
| -  EXPECT_TRUE(response_info.was_cached);
 | 
| +  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| +    context_list.push_back(base::MakeUnique<Context>());
 | 
| +    auto& c = context_list[i];
 | 
|  
 | 
| -  // The resource has now been used, back to normal behavior.
 | 
| -  RunTransactionTestWithResponseInfoAndGetTiming(
 | 
| -      cache.http_cache(), kSimpleGET_Transaction, &response_info,
 | 
| -      BoundTestNetLog().bound(), nullptr);
 | 
| -  EXPECT_FALSE(response_info.unused_since_prefetch);
 | 
| -  EXPECT_TRUE(response_info.was_cached);
 | 
| -}
 | 
| +    c->result = cache.CreateTransaction(&c->trans);
 | 
| +    ASSERT_THAT(c->result, IsOk());
 | 
|  
 | 
| -static void PreserveRequestHeaders_Handler(const HttpRequestInfo* request,
 | 
| -                                           std::string* response_status,
 | 
| -                                           std::string* response_headers,
 | 
| -                                           std::string* response_data) {
 | 
| -  EXPECT_TRUE(request->extra_headers.HasHeader(kExtraHeaderKey));
 | 
| -}
 | 
| +    if (i == 0)
 | 
| +      c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
 | 
|  
 | 
| -// Tests that we don't remove extra headers for simple requests.
 | 
| -TEST(HttpCache, SimpleGET_PreserveRequestHeaders) {
 | 
| -  MockHttpCache cache;
 | 
| +    c->result =
 | 
| +        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
 | 
| +  }
 | 
|  
 | 
| -  MockTransaction transaction(kSimpleGET_Transaction);
 | 
| -  transaction.handler = PreserveRequestHeaders_Handler;
 | 
| -  transaction.request_headers = EXTRA_HEADER;
 | 
| -  transaction.response_headers = "Cache-Control: max-age=0\n";
 | 
| -  AddMockTransaction(&transaction);
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
|  
 | 
| -  // Write, then revalidate the entry.
 | 
| -  RunTransactionTest(cache.http_cache(), transaction);
 | 
| -  RunTransactionTest(cache.http_cache(), transaction);
 | 
| +  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_EQ(1, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
 | 
|  
 | 
| -  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| -  EXPECT_EQ(1, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(1, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
|    EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| -  RemoveMockTransaction(&transaction);
 | 
| -}
 | 
| -
 | 
| -// Tests that we don't remove extra headers for conditionalized requests.
 | 
| -TEST(HttpCache, ConditionalizedGET_PreserveRequestHeaders) {
 | 
| -  MockHttpCache cache;
 | 
|  
 | 
| -  // Write to the cache.
 | 
| -  RunTransactionTest(cache.http_cache(), kETagGET_Transaction);
 | 
| +  // Delete the headers transaction.
 | 
| +  context_list[0].reset();
 | 
|  
 | 
| -  MockTransaction transaction(kETagGET_Transaction);
 | 
| -  transaction.handler = PreserveRequestHeaders_Handler;
 | 
| -  transaction.request_headers = "If-None-Match: \"foopy\"\r\n"
 | 
| -                                EXTRA_HEADER;
 | 
| -  AddMockTransaction(&transaction);
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
|  
 | 
| -  RunTransactionTest(cache.http_cache(), transaction);
 | 
| +  // Complete the rest of the transactions.
 | 
| +  for (auto& context : context_list) {
 | 
| +    if (!context)
 | 
| +      continue;
 | 
| +    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
 | 
| +  }
 | 
|  
 | 
|    EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
| -  EXPECT_EQ(1, cache.disk_cache()->open_count());
 | 
| -  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| -  RemoveMockTransaction(&transaction);
 | 
| +  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(2, cache.disk_cache()->create_count());
 | 
|  }
 | 
|  
 | 
| -TEST(HttpCache, SimpleGET_ManyReaders) {
 | 
| +// Similar to the above test, except here cache write fails and the
 | 
| +// validated transactions should be restarted.
 | 
| +TEST(HttpCache, SimpleGET_ParallelValidationFailWrite) {
 | 
|    MockHttpCache cache;
 | 
|  
 | 
|    MockHttpRequest request(kSimpleGET_Transaction);
 | 
|  
 | 
| -  std::vector<std::unique_ptr<Context>> context_list;
 | 
|    const int kNumTransactions = 5;
 | 
| +  std::vector<std::unique_ptr<Context>> context_list;
 | 
|  
 | 
|    for (int i = 0; i < kNumTransactions; ++i) {
 | 
|      context_list.push_back(base::MakeUnique<Context>());
 | 
| -    Context* c = context_list[i].get();
 | 
| +    auto& c = context_list[i];
 | 
|  
 | 
|      c->result = cache.CreateTransaction(&c->trans);
 | 
|      ASSERT_THAT(c->result, IsOk());
 | 
| @@ -1360,40 +2231,57 @@ TEST(HttpCache, SimpleGET_ManyReaders) {
 | 
|    }
 | 
|  
 | 
|    // All requests are waiting for the active entry.
 | 
| -  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| -    Context* c = context_list[i].get();
 | 
| -    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
 | 
| +  for (auto& context : context_list) {
 | 
| +    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
 | 
|    }
 | 
|  
 | 
|    // Allow all requests to move from the Create queue to the active entry.
 | 
|    base::RunLoop().RunUntilIdle();
 | 
|  
 | 
|    // The first request should be a writer at this point, and the subsequent
 | 
| -  // requests should be pending.
 | 
| +  // requests should have passed the validation phase and waiting for the
 | 
| +  // response to be written to the cache before they can read.
 | 
| +  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
 | 
| +  EXPECT_EQ(4, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
 | 
| +
 | 
| +  // All requests depend on the writer, and the writer is between Start and
 | 
| +  // Read, i.e. idle.
 | 
| +  for (auto& context : context_list) {
 | 
| +    EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
 | 
| +  }
 | 
| +
 | 
| +  // The first request should be a writer at this point, and the subsequent
 | 
| +  // requests should have passed the validation phase and waiting for the
 | 
| +  // response to be written to the cache before they can read.
 | 
|  
 | 
|    EXPECT_EQ(1, cache.network_layer()->transaction_count());
 | 
|    EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
|    EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
|  
 | 
| -  // All requests depend on the writer, and the writer is between Start and
 | 
| -  // Read, i.e. idle.
 | 
| -  for (int i = 0; i < kNumTransactions; ++i) {
 | 
| -    Context* c = context_list[i].get();
 | 
| -    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
 | 
| -  }
 | 
| +  // Fail the request.
 | 
| +  cache.disk_cache()->set_soft_failures(true);
 | 
| +  // We have to open the entry again to propagate the failure flag.
 | 
| +  disk_cache::Entry* en;
 | 
| +  cache.OpenBackendEntry(kSimpleGET_Transaction.url, &en);
 | 
| +  en->Close();
 | 
|  
 | 
|    for (int i = 0; i < kNumTransactions; ++i) {
 | 
| -    Context* c = context_list[i].get();
 | 
| +    auto& c = context_list[i];
 | 
|      if (c->result == ERR_IO_PENDING)
 | 
|        c->result = c->callback.WaitForResult();
 | 
| +    if (i == 1) {
 | 
| +      // The earlier entry must be destroyed and its disk entry doomed.
 | 
| +      EXPECT_TRUE(
 | 
| +          cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
 | 
| +    }
 | 
|      ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
 | 
|    }
 | 
|  
 | 
| -  // We should not have had to re-open the disk entry
 | 
| -
 | 
| -  EXPECT_EQ(1, cache.network_layer()->transaction_count());
 | 
| -  EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| -  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +  // Since validated transactions were restarted and new entry read/write
 | 
| +  // operations would also fail, all requests would have gone to the network.
 | 
| +  EXPECT_EQ(5, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->open_count());
 | 
| +  EXPECT_EQ(5, cache.disk_cache()->create_count());
 | 
|  }
 | 
|  
 | 
|  // This is a test for http://code.google.com/p/chromium/issues/detail?id=4769.
 | 
| @@ -1440,11 +2328,12 @@ TEST(HttpCache, SimpleGET_RacingReaders) {
 | 
|    c->result = c->callback.WaitForResult();
 | 
|    ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
 | 
|  
 | 
| -  // Now we have 2 active readers and two queued transactions.
 | 
| -
 | 
| +  // Now all transactions should be waiting for read to be invoked. Two readers
 | 
| +  // are because of the load flags and remaining two transactions were converted
 | 
| +  // to readers after skipping validation. Note that the remaining two went on
 | 
| +  // to process the headers in parallel with readers present on the entry.
 | 
|    EXPECT_EQ(LOAD_STATE_IDLE, context_list[2]->trans->GetLoadState());
 | 
| -  EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE,
 | 
| -            context_list[3]->trans->GetLoadState());
 | 
| +  EXPECT_EQ(LOAD_STATE_IDLE, context_list[3]->trans->GetLoadState());
 | 
|  
 | 
|    c = context_list[1].get();
 | 
|    ASSERT_THAT(c->result, IsError(ERR_IO_PENDING));
 | 
| @@ -1504,12 +2393,16 @@ TEST(HttpCache, SimpleGET_DoomWithPending) {
 | 
|                                  NetLogWithSource());
 | 
|    }
 | 
|  
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
|    // The first request should be a writer at this point, and the two subsequent
 | 
|    // requests should be pending. The last request doomed the first entry.
 | 
|  
 | 
|    EXPECT_EQ(2, cache.network_layer()->transaction_count());
 | 
|  
 | 
| -  // Cancel the first queued transaction.
 | 
| +  // Cancel the second transaction. Note that this and the 3rd transactions
 | 
| +  // would have completed their headers phase and would be waiting in the
 | 
| +  // done_headers_queue when the 2nd transaction is cancelled.
 | 
|    context_list[1].reset();
 | 
|  
 | 
|    for (int i = 0; i < kNumTransactions; ++i) {
 | 
| @@ -1551,11 +2444,12 @@ TEST(HttpCache, FastNoStoreGET_DoneWithPending) {
 | 
|    base::RunLoop().RunUntilIdle();
 | 
|  
 | 
|    // The first request should be a writer at this point, and the subsequent
 | 
| -  // requests should be pending.
 | 
| +  // requests should have completed validation. Since the validation does not
 | 
| +  // result in a match, a new entry would be created.
 | 
|  
 | 
| -  EXPECT_EQ(1, cache.network_layer()->transaction_count());
 | 
| +  EXPECT_EQ(3, cache.network_layer()->transaction_count());
 | 
|    EXPECT_EQ(0, cache.disk_cache()->open_count());
 | 
| -  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
| +  EXPECT_EQ(2, cache.disk_cache()->create_count());
 | 
|  
 | 
|    // Now, make sure that the second request asks for the entry not to be stored.
 | 
|    request_handler.set_no_store(true);
 | 
| @@ -1609,6 +2503,9 @@ TEST(HttpCache, SimpleGET_ManyWriters_CancelFirst) {
 | 
|      if (c->result == ERR_IO_PENDING)
 | 
|        c->result = c->callback.WaitForResult();
 | 
|      // Destroy only the first transaction.
 | 
| +    // This should lead to all transactions to restart, even those that have
 | 
| +    // validated themselves and were waiting for the writer transaction to
 | 
| +    // complete writing to the cache.
 | 
|      if (i == 0) {
 | 
|        context_list[i].reset();
 | 
|      }
 | 
| @@ -2497,6 +3394,8 @@ TEST(HttpCache, ETagGET_ConditionalRequest_304_NoStore) {
 | 
|  //     be returned.
 | 
|  // (4) loads |kUrl| from cache only -- expects |cached_response_2| to be
 | 
|  //     returned.
 | 
| +// The entry will be created once and will be opened for the 3 subsequent
 | 
| +// requests.
 | 
|  static void ConditionalizedRequestUpdatesCacheHelper(
 | 
|      const Response& net_response_1,
 | 
|      const Response& net_response_2,
 | 
| @@ -6069,13 +6968,14 @@ TEST(HttpCache, GET_IncompleteResource_Cancel) {
 | 
|    rv = c->trans->Read(buf.get(), buf->size(), c->callback.callback());
 | 
|    EXPECT_EQ(5, c->callback.GetResult(rv));
 | 
|  
 | 
| -  // Cancel the requests.
 | 
| +  // Since |pending| is currently validating the already written headers
 | 
| +  // it will be restarted as well.
 | 
|    c.reset();
 | 
|    pending.reset();
 | 
|  
 | 
|    EXPECT_EQ(1, cache.network_layer()->transaction_count());
 | 
|    EXPECT_EQ(1, cache.disk_cache()->open_count());
 | 
| -  EXPECT_EQ(2, cache.disk_cache()->create_count());
 | 
| +  EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
|  
 | 
|    base::RunLoop().RunUntilIdle();
 | 
|    RemoveMockTransaction(&transaction);
 | 
| @@ -6800,46 +7700,6 @@ TEST(HttpCache, WriteMetadata_Fail) {
 | 
|    EXPECT_EQ(1, cache.disk_cache()->create_count());
 | 
|  }
 | 
|  
 | 
| -// Tests that if a metadata writer transaction hits cache lock timeout, it will
 | 
| -// error out.
 | 
| -TEST(HttpCache, WriteMetadata_CacheLockTimeout) {
 | 
| -  MockHttpCache cache;
 | 
| -
 | 
| -  // Write to the cache
 | 
| -  HttpResponseInfo response;
 | 
| -  RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
 | 
| -                                     &response);
 | 
| -  EXPECT_FALSE(response.metadata.get());
 | 
| -
 | 
| -  MockHttpRequest request(kSimpleGET_Transaction);
 | 
| -  Context c1;
 | 
| -  ASSERT_THAT(cache.CreateTransaction(&c1.trans), IsOk());
 | 
| -  ASSERT_EQ(ERR_IO_PENDING, c1.trans->Start(&request, c1.callback.callback(),
 | 
| -                                            NetLogWithSource()));
 | 
| -
 | 
| -  cache.SimulateCacheLockTimeout();
 | 
| -
 | 
| -  // Write meta data to the same entry.
 | 
| -  scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(50));
 | 
| -  memset(buf->data(), 0, buf->size());
 | 
| -  base::strlcpy(buf->data(), "Hi there", buf->size());
 | 
| -  cache.http_cache()->WriteMetadata(GURL(kSimpleGET_Transaction.url),
 | 
| -                                    DEFAULT_PRIORITY, response.response_time,
 | 
| -                                    buf.get(), buf->size());
 | 
| -
 | 
| -  // Release the buffer before the operation takes place.
 | 
| -  buf = NULL;
 | 
| -
 | 
| -  // Makes sure we finish pending operations.
 | 
| -  base::RunLoop().RunUntilIdle();
 | 
| -
 | 
| -  RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
 | 
| -                                     &response);
 | 
| -
 | 
| -  // The writer transaction should fail due to cache lock timeout.
 | 
| -  ASSERT_FALSE(response.metadata.get());
 | 
| -}
 | 
| -
 | 
|  // Tests that we ignore VARY checks when writing metadata since the request
 | 
|  // headers for the WriteMetadata transaction are made up.
 | 
|  TEST(HttpCache, WriteMetadata_IgnoreVary) {
 | 
| @@ -7167,10 +8027,6 @@ TEST(HttpCache, StopCachingWithAuthDeletesEntry) {
 | 
|      EXPECT_THAT(callback.GetResult(rv), IsOk());
 | 
|  
 | 
|      trans->StopCaching();
 | 
| -
 | 
| -    scoped_refptr<IOBuffer> buf(new IOBuffer(256));
 | 
| -    rv = trans->Read(buf.get(), 10, callback.callback());
 | 
| -    EXPECT_EQ(callback.GetResult(rv), 10);
 | 
|    }
 | 
|    RemoveMockTransaction(&mock_transaction);
 | 
|  
 | 
| @@ -7219,7 +8075,9 @@ TEST(HttpCache, StopCachingSavesEntry) {
 | 
|    }
 | 
|  
 | 
|    // Verify that the entry is marked as incomplete.
 | 
| -  VerifyTruncatedFlag(&cache, kSimpleGET_Transaction.url, true, 0);
 | 
| +  // VerifyTruncatedFlag(&cache, kSimpleGET_Transaction.url, true, 0);
 | 
| +  // Verify that the entry is doomed.
 | 
| +  cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url);
 | 
|  }
 | 
|  
 | 
|  // Tests that we handle truncated enries when StopCaching is called.
 | 
| 
 |