普通文本  |  799行  |  26.64 KB

// 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 <string>
#include <vector>

#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/shared_memory.h"
#include "base/message_loop/message_loop.h"
#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "content/child/request_extra_data.h"
#include "content/child/request_info.h"
#include "content/child/resource_dispatcher.h"
#include "content/child/resource_loader_bridge.h"
#include "content/common/appcache_interfaces.h"
#include "content/common/resource_messages.h"
#include "content/common/service_worker/service_worker_types.h"
#include "content/public/child/request_peer.h"
#include "content/public/common/resource_response.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {

static const char kTestPageUrl[] = "http://www.google.com/";
static const char kTestPageHeaders[] =
  "HTTP/1.1 200 OK\nContent-Type:text/html\n\n";
static const char kTestPageMimeType[] = "text/html";
static const char kTestPageCharset[] = "";
static const char kTestPageContents[] =
  "<html><head><title>Google</title></head><body><h1>Google</h1></body></html>";
static const char kTestRedirectHeaders[] =
  "HTTP/1.1 302 Found\nLocation:http://www.google.com/\n\n";

// Listens for request response data and stores it so that it can be compared
// to the reference data.
class TestRequestPeer : public RequestPeer {
 public:
  TestRequestPeer(ResourceLoaderBridge* bridge)
      :  follow_redirects_(true),
         defer_on_redirect_(false),
         seen_redirects_(0),
         cancel_on_receive_response_(false),
         received_response_(false),
         total_encoded_data_length_(0),
         total_downloaded_data_length_(0),
         complete_(false),
         bridge_(bridge) {
  }

  virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE {
  }

  virtual bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
                                  const ResourceResponseInfo& info) OVERRIDE {
    ++seen_redirects_;
    if (defer_on_redirect_)
      bridge_->SetDefersLoading(true);
    return follow_redirects_;
  }

  virtual void OnReceivedResponse(const ResourceResponseInfo& info) OVERRIDE {
    EXPECT_FALSE(received_response_);
    received_response_ = true;
    if (cancel_on_receive_response_)
      bridge_->Cancel();
  }

  virtual void OnDownloadedData(int len, int encoded_data_length) OVERRIDE {
    total_downloaded_data_length_ += len;
    total_encoded_data_length_ += encoded_data_length;
  }

  virtual void OnReceivedData(const char* data,
                              int data_length,
                              int encoded_data_length) OVERRIDE {
    EXPECT_TRUE(received_response_);
    EXPECT_FALSE(complete_);
    data_.append(data, data_length);
    total_encoded_data_length_ += encoded_data_length;
  }

  virtual void OnCompletedRequest(
      int error_code,
      bool was_ignored_by_handler,
      bool stale_copy_in_cache,
      const std::string& security_info,
      const base::TimeTicks& completion_time,
      int64 total_transfer_size) OVERRIDE {
    EXPECT_TRUE(received_response_);
    EXPECT_FALSE(complete_);
    complete_ = true;
  }

  void set_follow_redirects(bool follow_redirects) {
    follow_redirects_ = follow_redirects;
  }

  void set_defer_on_redirect(bool defer_on_redirect) {
    defer_on_redirect_ = defer_on_redirect;
  }

  void set_cancel_on_receive_response(bool cancel_on_receive_response) {
    cancel_on_receive_response_ = cancel_on_receive_response;
  }

  int seen_redirects() const { return seen_redirects_; }

  bool received_response() const { return received_response_; }

  const std::string& data() const {
    return data_;
  }
  int total_encoded_data_length() const {
    return total_encoded_data_length_;
  }
  int total_downloaded_data_length() const {
    return total_downloaded_data_length_;
  }

  bool complete() const { return complete_; }

 private:
  // True if should follow redirects, false if should cancel them.
  bool follow_redirects_;
  // True if the request should be deferred on redirects.
  bool defer_on_redirect_;
  // Number of total redirects seen.
  int seen_redirects_;

  bool cancel_on_receive_response_;
  bool received_response_;

  // Data received.   If downloading to file, remains empty.
  std::string data_;
  // Total encoded data length, regardless of whether downloading to a file or
  // not.
  int total_encoded_data_length_;
  // Total length when downloading to a file.
  int total_downloaded_data_length_;

  bool complete_;

  ResourceLoaderBridge* bridge_;

  DISALLOW_COPY_AND_ASSIGN(TestRequestPeer);
};

// Sets up the message sender override for the unit test.
class ResourceDispatcherTest : public testing::Test, public IPC::Sender {
 public:
  ResourceDispatcherTest() : dispatcher_(this) {}

  virtual ~ResourceDispatcherTest() {
    STLDeleteContainerPairSecondPointers(shared_memory_map_.begin(),
                                         shared_memory_map_.end());
  }

  // Emulates IPC send operations (IPC::Sender) by adding
  // pending messages to the queue.
  virtual bool Send(IPC::Message* msg) OVERRIDE {
    message_queue_.push_back(IPC::Message(*msg));
    delete msg;
    return true;
  }

  size_t queued_messages() const { return message_queue_.size(); }

  // Returns the ID of the consumed request.  Can't make assumptions about the
  // ID, because numbering is based on a global.
  int ConsumeRequestResource() {
    if (message_queue_.empty()) {
      ADD_FAILURE() << "Missing resource request message";
      return -1;
    }

    ResourceHostMsg_RequestResource::Param params;
    if (ResourceHostMsg_RequestResource::ID != message_queue_[0].type() ||
        !ResourceHostMsg_RequestResource::Read(&message_queue_[0], &params)) {
      ADD_FAILURE() << "Expected ResourceHostMsg_RequestResource message";
      return -1;
    }
    ResourceHostMsg_Request request = params.c;
    EXPECT_EQ(kTestPageUrl, request.url.spec());
    message_queue_.erase(message_queue_.begin());
    return params.b;
  }

  void ConsumeFollowRedirect(int expected_request_id) {
    ASSERT_FALSE(message_queue_.empty());
    Tuple1<int> args;
    ASSERT_EQ(ResourceHostMsg_FollowRedirect::ID, message_queue_[0].type());
    ASSERT_TRUE(ResourceHostMsg_FollowRedirect::Read(
        &message_queue_[0], &args));
    EXPECT_EQ(expected_request_id, args.a);
    message_queue_.erase(message_queue_.begin());
  }

  void ConsumeDataReceived_ACK(int expected_request_id) {
    ASSERT_FALSE(message_queue_.empty());
    Tuple1<int> args;
    ASSERT_EQ(ResourceHostMsg_DataReceived_ACK::ID, message_queue_[0].type());
    ASSERT_TRUE(ResourceHostMsg_DataReceived_ACK::Read(
        &message_queue_[0], &args));
    EXPECT_EQ(expected_request_id, args.a);
    message_queue_.erase(message_queue_.begin());
  }

  void ConsumeDataDownloaded_ACK(int expected_request_id) {
    ASSERT_FALSE(message_queue_.empty());
    Tuple1<int> args;
    ASSERT_EQ(ResourceHostMsg_DataDownloaded_ACK::ID, message_queue_[0].type());
    ASSERT_TRUE(ResourceHostMsg_DataDownloaded_ACK::Read(
        &message_queue_[0], &args));
    EXPECT_EQ(expected_request_id, args.a);
    message_queue_.erase(message_queue_.begin());
  }

  void ConsumeReleaseDownloadedFile(int expected_request_id) {
    ASSERT_FALSE(message_queue_.empty());
    Tuple1<int> args;
    ASSERT_EQ(ResourceHostMsg_ReleaseDownloadedFile::ID,
              message_queue_[0].type());
    ASSERT_TRUE(ResourceHostMsg_ReleaseDownloadedFile::Read(
        &message_queue_[0], &args));
    EXPECT_EQ(expected_request_id, args.a);
    message_queue_.erase(message_queue_.begin());
  }

  void ConsumeCancelRequest(int expected_request_id) {
    ASSERT_FALSE(message_queue_.empty());
    Tuple1<int> args;
    ASSERT_EQ(ResourceHostMsg_CancelRequest::ID, message_queue_[0].type());
    ASSERT_TRUE(ResourceHostMsg_CancelRequest::Read(
        &message_queue_[0], &args));
    EXPECT_EQ(expected_request_id, args.a);
    message_queue_.erase(message_queue_.begin());
  }

  void NotifyReceivedRedirect(int request_id) {
    ResourceResponseHead head;
    std::string raw_headers(kTestRedirectHeaders);
    std::replace(raw_headers.begin(), raw_headers.end(), '\n', '\0');
    head.headers = new net::HttpResponseHeaders(raw_headers);
    net::RedirectInfo redirect_info;
    redirect_info.status_code = 302;
    redirect_info.new_method = "GET";
    redirect_info.new_url = GURL(kTestPageUrl);
    redirect_info.new_first_party_for_cookies = GURL(kTestPageUrl);
    EXPECT_EQ(true, dispatcher_.OnMessageReceived(
        ResourceMsg_ReceivedRedirect(request_id, redirect_info, head)));
  }

  void NotifyReceivedResponse(int request_id) {
    ResourceResponseHead head;
    std::string raw_headers(kTestPageHeaders);
    std::replace(raw_headers.begin(), raw_headers.end(), '\n', '\0');
    head.headers = new net::HttpResponseHeaders(raw_headers);
    head.mime_type = kTestPageMimeType;
    head.charset = kTestPageCharset;
    EXPECT_EQ(true,
              dispatcher_.OnMessageReceived(
                  ResourceMsg_ReceivedResponse(request_id, head)));
  }

  void NotifySetDataBuffer(int request_id, size_t buffer_size) {
    base::SharedMemory* shared_memory = new base::SharedMemory();
    ASSERT_FALSE(shared_memory_map_[request_id]);
    shared_memory_map_[request_id] = shared_memory;
    EXPECT_TRUE(shared_memory->CreateAndMapAnonymous(buffer_size));

    base::SharedMemoryHandle duplicate_handle;
    EXPECT_TRUE(shared_memory->ShareToProcess(
        base::Process::Current().handle(), &duplicate_handle));
    EXPECT_TRUE(dispatcher_.OnMessageReceived(
        ResourceMsg_SetDataBuffer(request_id, duplicate_handle,
                                  shared_memory->requested_size(), 0)));
  }

  void NotifyDataReceived(int request_id, std::string data) {
    ASSERT_LE(data.length(), shared_memory_map_[request_id]->requested_size());
    memcpy(shared_memory_map_[request_id]->memory(), data.c_str(),
           data.length());

    EXPECT_TRUE(dispatcher_.OnMessageReceived(
        ResourceMsg_DataReceived(request_id, 0, data.length(), data.length())));
  }

  void NotifyDataDownloaded(int request_id, int decoded_length,
                            int encoded_length) {
    EXPECT_TRUE(dispatcher_.OnMessageReceived(
        ResourceMsg_DataDownloaded(request_id, decoded_length,
                                   encoded_length)));
  }

  void NotifyRequestComplete(int request_id, size_t total_size) {
    ResourceMsg_RequestCompleteData request_complete_data;
    request_complete_data.error_code = net::OK;
    request_complete_data.was_ignored_by_handler = false;
    request_complete_data.exists_in_cache = false;
    request_complete_data.encoded_data_length = total_size;
    EXPECT_TRUE(dispatcher_.OnMessageReceived(
        ResourceMsg_RequestComplete(request_id, request_complete_data)));
  }

  ResourceLoaderBridge* CreateBridge() {
    return CreateBridgeInternal(false);
  }

  ResourceLoaderBridge* CreateBridgeForDownloadToFile() {
    return CreateBridgeInternal(true);
  }

  ResourceDispatcher* dispatcher() { return &dispatcher_; }

 private:
  ResourceLoaderBridge* CreateBridgeInternal(bool download_to_file) {
    RequestInfo request_info;
    request_info.method = "GET";
    request_info.url = GURL(kTestPageUrl);
    request_info.first_party_for_cookies = GURL(kTestPageUrl);
    request_info.referrer = GURL();
    request_info.headers = std::string();
    request_info.load_flags = 0;
    request_info.requestor_pid = 0;
    request_info.request_type = RESOURCE_TYPE_SUB_RESOURCE;
    request_info.appcache_host_id = kAppCacheNoHostId;
    request_info.routing_id = 0;
    request_info.download_to_file = download_to_file;
    RequestExtraData extra_data;
    request_info.extra_data = &extra_data;

    return dispatcher_.CreateBridge(request_info);
  }

  // Map of request IDs to shared memory.
  std::map<int, base::SharedMemory*> shared_memory_map_;

  std::vector<IPC::Message> message_queue_;
  ResourceDispatcher dispatcher_;
  base::MessageLoop message_loop_;
};

// Does a simple request and tests that the correct data is received.  Simulates
// two reads.
TEST_F(ResourceDispatcherTest, RoundTrip) {
  // Number of bytes received in the first read.
  const size_t kFirstReceiveSize = 2;
  ASSERT_LT(kFirstReceiveSize, strlen(kTestPageContents));

  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
  TestRequestPeer peer(bridge.get());

  EXPECT_TRUE(bridge->Start(&peer));
  int id = ConsumeRequestResource();
  EXPECT_EQ(0u, queued_messages());

  NotifyReceivedResponse(id);
  EXPECT_EQ(0u, queued_messages());
  EXPECT_TRUE(peer.received_response());

  NotifySetDataBuffer(id, strlen(kTestPageContents));
  NotifyDataReceived(id, std::string(kTestPageContents, kFirstReceiveSize));
  ConsumeDataReceived_ACK(id);
  EXPECT_EQ(0u, queued_messages());

  NotifyDataReceived(id, kTestPageContents + kFirstReceiveSize);
  ConsumeDataReceived_ACK(id);
  EXPECT_EQ(0u, queued_messages());

  NotifyRequestComplete(id, strlen(kTestPageContents));
  EXPECT_EQ(kTestPageContents, peer.data());
  EXPECT_TRUE(peer.complete());
  EXPECT_EQ(0u, queued_messages());
}

// Tests that the request IDs are straight when there are two interleaving
// requests.
TEST_F(ResourceDispatcherTest, MultipleRequests) {
  const char kTestPageContents2[] = "Not kTestPageContents";

  scoped_ptr<ResourceLoaderBridge> bridge1(CreateBridge());
  TestRequestPeer peer1(bridge1.get());
  scoped_ptr<ResourceLoaderBridge> bridge2(CreateBridge());
  TestRequestPeer peer2(bridge2.get());

  EXPECT_TRUE(bridge1->Start(&peer1));
  int id1 = ConsumeRequestResource();
  EXPECT_TRUE(bridge2->Start(&peer2));
  int id2 = ConsumeRequestResource();
  EXPECT_EQ(0u, queued_messages());

  NotifyReceivedResponse(id1);
  EXPECT_TRUE(peer1.received_response());
  EXPECT_FALSE(peer2.received_response());
  NotifyReceivedResponse(id2);
  EXPECT_TRUE(peer2.received_response());
  EXPECT_EQ(0u, queued_messages());

  NotifySetDataBuffer(id2, strlen(kTestPageContents2));
  NotifyDataReceived(id2, kTestPageContents2);
  ConsumeDataReceived_ACK(id2);
  NotifySetDataBuffer(id1, strlen(kTestPageContents));
  NotifyDataReceived(id1, kTestPageContents);
  ConsumeDataReceived_ACK(id1);
  EXPECT_EQ(0u, queued_messages());

  NotifyRequestComplete(id1, strlen(kTestPageContents));
  EXPECT_EQ(kTestPageContents, peer1.data());
  EXPECT_TRUE(peer1.complete());
  EXPECT_FALSE(peer2.complete());

  NotifyRequestComplete(id2, strlen(kTestPageContents2));
  EXPECT_EQ(kTestPageContents2, peer2.data());
  EXPECT_TRUE(peer2.complete());

  EXPECT_EQ(0u, queued_messages());
}

// Tests that the cancel method prevents other messages from being received.
TEST_F(ResourceDispatcherTest, Cancel) {
  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
  TestRequestPeer peer(bridge.get());

  EXPECT_TRUE(bridge->Start(&peer));
  int id = ConsumeRequestResource();
  EXPECT_EQ(0u, queued_messages());

  // Cancel the request.
  bridge->Cancel();
  ConsumeCancelRequest(id);

  // Any future messages related to the request should be ignored.
  NotifyReceivedResponse(id);
  NotifySetDataBuffer(id, strlen(kTestPageContents));
  NotifyDataReceived(id, kTestPageContents);
  NotifyRequestComplete(id, strlen(kTestPageContents));

  EXPECT_EQ(0u, queued_messages());
  EXPECT_EQ("", peer.data());
  EXPECT_FALSE(peer.received_response());
  EXPECT_FALSE(peer.complete());
}

// Tests that calling cancel during a callback works as expected.
TEST_F(ResourceDispatcherTest, CancelDuringCallback) {
  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
  TestRequestPeer peer(bridge.get());
  peer.set_cancel_on_receive_response(true);

  EXPECT_TRUE(bridge->Start(&peer));
  int id = ConsumeRequestResource();
  EXPECT_EQ(0u, queued_messages());

  NotifyReceivedResponse(id);
  EXPECT_TRUE(peer.received_response());
  // Request should have been cancelled.
  ConsumeCancelRequest(id);

  // Any future messages related to the request should be ignored.
  NotifySetDataBuffer(id, strlen(kTestPageContents));
  NotifyDataReceived(id, kTestPageContents);
  NotifyRequestComplete(id, strlen(kTestPageContents));

  EXPECT_EQ(0u, queued_messages());
  EXPECT_EQ("", peer.data());
  EXPECT_FALSE(peer.complete());
}

// Checks that redirects work as expected.
TEST_F(ResourceDispatcherTest, Redirect) {
  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
  TestRequestPeer peer(bridge.get());

  EXPECT_TRUE(bridge->Start(&peer));
  int id = ConsumeRequestResource();

  NotifyReceivedRedirect(id);
  ConsumeFollowRedirect(id);
  EXPECT_EQ(1, peer.seen_redirects());

  NotifyReceivedRedirect(id);
  ConsumeFollowRedirect(id);
  EXPECT_EQ(2, peer.seen_redirects());

  NotifyReceivedResponse(id);
  EXPECT_TRUE(peer.received_response());

  NotifySetDataBuffer(id, strlen(kTestPageContents));
  NotifyDataReceived(id, kTestPageContents);
  ConsumeDataReceived_ACK(id);

  NotifyRequestComplete(id, strlen(kTestPageContents));
  EXPECT_EQ(kTestPageContents, peer.data());
  EXPECT_TRUE(peer.complete());
  EXPECT_EQ(0u, queued_messages());
  EXPECT_EQ(2, peer.seen_redirects());
}

// Tests that that cancelling during a redirect method prevents other messages
// from being received.
TEST_F(ResourceDispatcherTest, CancelDuringRedirect) {
  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
  TestRequestPeer peer(bridge.get());
  peer.set_follow_redirects(false);

  EXPECT_TRUE(bridge->Start(&peer));
  int id = ConsumeRequestResource();
  EXPECT_EQ(0u, queued_messages());

  // Redirect the request, which triggers a cancellation.
  NotifyReceivedRedirect(id);
  ConsumeCancelRequest(id);
  EXPECT_EQ(1, peer.seen_redirects());
  EXPECT_EQ(0u, queued_messages());

  // Any future messages related to the request should be ignored.  In practice,
  // only the NotifyRequestComplete should be received after this point.
  NotifyReceivedRedirect(id);
  NotifyReceivedResponse(id);
  NotifySetDataBuffer(id, strlen(kTestPageContents));
  NotifyDataReceived(id, kTestPageContents);
  NotifyRequestComplete(id, strlen(kTestPageContents));

  EXPECT_EQ(0u, queued_messages());
  EXPECT_EQ("", peer.data());
  EXPECT_FALSE(peer.complete());
  EXPECT_EQ(1, peer.seen_redirects());
}

// Checks that deferring a request delays messages until it's resumed.
TEST_F(ResourceDispatcherTest, Defer) {
  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
  TestRequestPeer peer(bridge.get());

  EXPECT_TRUE(bridge->Start(&peer));
  int id = ConsumeRequestResource();
  EXPECT_EQ(0u, queued_messages());

  bridge->SetDefersLoading(true);
  NotifyReceivedResponse(id);
  NotifySetDataBuffer(id, strlen(kTestPageContents));
  NotifyDataReceived(id, kTestPageContents);
  NotifyRequestComplete(id, strlen(kTestPageContents));

  // None of the messages should have been processed yet, so no queued messages
  // to the browser process, and no data received by the peer.
  EXPECT_EQ(0u, queued_messages());
  EXPECT_EQ("", peer.data());
  EXPECT_FALSE(peer.complete());
  EXPECT_EQ(0, peer.seen_redirects());

  // Resuming the request should asynchronously unleash the deferred messages.
  bridge->SetDefersLoading(false);
  base::RunLoop().RunUntilIdle();

  ConsumeDataReceived_ACK(id);
  EXPECT_EQ(0u, queued_messages());
  EXPECT_TRUE(peer.received_response());
  EXPECT_EQ(kTestPageContents, peer.data());
  EXPECT_TRUE(peer.complete());
}

// Checks that deferring a request during a redirect delays messages until it's
// resumed.
TEST_F(ResourceDispatcherTest, DeferOnRedirect) {
  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
  TestRequestPeer peer(bridge.get());
  peer.set_defer_on_redirect(true);

  EXPECT_TRUE(bridge->Start(&peer));
  int id = ConsumeRequestResource();
  EXPECT_EQ(0u, queued_messages());

  // The request should be deferred during the redirect, including the message
  // to follow the redirect.
  NotifyReceivedRedirect(id);
  NotifyReceivedResponse(id);
  NotifySetDataBuffer(id, strlen(kTestPageContents));
  NotifyDataReceived(id, kTestPageContents);
  NotifyRequestComplete(id, strlen(kTestPageContents));

  // None of the messages should have been processed yet, so no queued messages
  // to the browser process, and no data received by the peer.
  EXPECT_EQ(0u, queued_messages());
  EXPECT_EQ("", peer.data());
  EXPECT_FALSE(peer.complete());
  EXPECT_EQ(1, peer.seen_redirects());

  // Resuming the request should asynchronously unleash the deferred messages.
  bridge->SetDefersLoading(false);
  base::RunLoop().RunUntilIdle();

  ConsumeFollowRedirect(id);
  ConsumeDataReceived_ACK(id);

  EXPECT_EQ(0u, queued_messages());
  EXPECT_TRUE(peer.received_response());
  EXPECT_EQ(kTestPageContents, peer.data());
  EXPECT_TRUE(peer.complete());
  EXPECT_EQ(1, peer.seen_redirects());
}

// Checks that a deferred request that's cancelled doesn't receive any messages.
TEST_F(ResourceDispatcherTest, CancelDeferredRequest) {
  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
  TestRequestPeer peer(bridge.get());

  EXPECT_TRUE(bridge->Start(&peer));
  int id = ConsumeRequestResource();
  EXPECT_EQ(0u, queued_messages());

  bridge->SetDefersLoading(true);
  NotifyReceivedRedirect(id);
  bridge->Cancel();
  ConsumeCancelRequest(id);

  NotifyRequestComplete(id, 0);
  base::RunLoop().RunUntilIdle();

  // None of the messages should have been processed.
  EXPECT_EQ(0u, queued_messages());
  EXPECT_EQ("", peer.data());
  EXPECT_FALSE(peer.complete());
  EXPECT_EQ(0, peer.seen_redirects());
}

TEST_F(ResourceDispatcherTest, DownloadToFile) {
  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridgeForDownloadToFile());
  TestRequestPeer peer(bridge.get());
  const int kDownloadedIncrement = 100;
  const int kEncodedIncrement = 50;

  EXPECT_TRUE(bridge->Start(&peer));
  int id = ConsumeRequestResource();
  EXPECT_EQ(0u, queued_messages());

  NotifyReceivedResponse(id);
  EXPECT_EQ(0u, queued_messages());
  EXPECT_TRUE(peer.received_response());

  int expected_total_downloaded_length = 0;
  int expected_total_encoded_length = 0;
  for (int i = 0; i < 10; ++i) {
    NotifyDataDownloaded(id, kDownloadedIncrement, kEncodedIncrement);
    ConsumeDataDownloaded_ACK(id);
    expected_total_downloaded_length += kDownloadedIncrement;
    expected_total_encoded_length += kEncodedIncrement;
    EXPECT_EQ(expected_total_downloaded_length,
              peer.total_downloaded_data_length());
    EXPECT_EQ(expected_total_encoded_length, peer.total_encoded_data_length());
  }

  NotifyRequestComplete(id, strlen(kTestPageContents));
  EXPECT_EQ("", peer.data());
  EXPECT_TRUE(peer.complete());
  EXPECT_EQ(0u, queued_messages());

  bridge.reset();
  ConsumeReleaseDownloadedFile(id);
  EXPECT_EQ(0u, queued_messages());
  EXPECT_EQ(expected_total_downloaded_length,
            peer.total_downloaded_data_length());
  EXPECT_EQ(expected_total_encoded_length, peer.total_encoded_data_length());
}

// Make sure that when a download to file is cancelled, the file is destroyed.
TEST_F(ResourceDispatcherTest, CancelDownloadToFile) {
  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridgeForDownloadToFile());
  TestRequestPeer peer(bridge.get());

  EXPECT_TRUE(bridge->Start(&peer));
  int id = ConsumeRequestResource();
  EXPECT_EQ(0u, queued_messages());

  NotifyReceivedResponse(id);
  EXPECT_EQ(0u, queued_messages());
  EXPECT_TRUE(peer.received_response());

  // Cancelling the request deletes the file.
  bridge->Cancel();
  ConsumeCancelRequest(id);
  ConsumeReleaseDownloadedFile(id);

  // Deleting the bridge shouldn't send another message to delete the file.
  bridge.reset();
  EXPECT_EQ(0u, queued_messages());
}

TEST_F(ResourceDispatcherTest, Cookies) {
  // FIXME
}

TEST_F(ResourceDispatcherTest, SerializedPostData) {
  // FIXME
}

class TimeConversionTest : public ResourceDispatcherTest,
                           public RequestPeer {
 public:
  virtual bool Send(IPC::Message* msg) OVERRIDE {
    delete msg;
    return true;
  }

  void PerformTest(const ResourceResponseHead& response_head) {
    scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
    bridge->Start(this);

    dispatcher()->OnMessageReceived(
        ResourceMsg_ReceivedResponse(0, response_head));
  }

  // RequestPeer methods.
  virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE {
  }

  virtual bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
                                  const ResourceResponseInfo& info) OVERRIDE {
    return true;
  }

  virtual void OnReceivedResponse(const ResourceResponseInfo& info) OVERRIDE {
    response_info_ = info;
  }

  virtual void OnDownloadedData(int len, int encoded_data_length) OVERRIDE {
  }

  virtual void OnReceivedData(const char* data,
                              int data_length,
                              int encoded_data_length) OVERRIDE {
  }

  virtual void OnCompletedRequest(
      int error_code,
      bool was_ignored_by_handler,
      bool stale_copy_in_cache,
      const std::string& security_info,
      const base::TimeTicks& completion_time,
      int64 total_transfer_size) OVERRIDE {
  }

  const ResourceResponseInfo& response_info() const { return response_info_; }

 private:
  ResourceResponseInfo response_info_;
};

// TODO(simonjam): Enable this when 10829031 lands.
TEST_F(TimeConversionTest, DISABLED_ProperlyInitialized) {
  ResourceResponseHead response_head;
  response_head.request_start = base::TimeTicks::FromInternalValue(5);
  response_head.response_start = base::TimeTicks::FromInternalValue(15);
  response_head.load_timing.request_start_time = base::Time::Now();
  response_head.load_timing.request_start =
      base::TimeTicks::FromInternalValue(10);
  response_head.load_timing.connect_timing.connect_start =
      base::TimeTicks::FromInternalValue(13);

  PerformTest(response_head);

  EXPECT_LT(base::TimeTicks(), response_info().load_timing.request_start);
  EXPECT_EQ(base::TimeTicks(),
            response_info().load_timing.connect_timing.dns_start);
  EXPECT_LE(response_head.load_timing.request_start,
            response_info().load_timing.connect_timing.connect_start);
}

TEST_F(TimeConversionTest, PartiallyInitialized) {
  ResourceResponseHead response_head;
  response_head.request_start = base::TimeTicks::FromInternalValue(5);
  response_head.response_start = base::TimeTicks::FromInternalValue(15);

  PerformTest(response_head);

  EXPECT_EQ(base::TimeTicks(), response_info().load_timing.request_start);
  EXPECT_EQ(base::TimeTicks(),
            response_info().load_timing.connect_timing.dns_start);
}

TEST_F(TimeConversionTest, NotInitialized) {
  ResourceResponseHead response_head;

  PerformTest(response_head);

  EXPECT_EQ(base::TimeTicks(), response_info().load_timing.request_start);
  EXPECT_EQ(base::TimeTicks(),
            response_info().load_timing.connect_timing.dns_start);
}

}  // namespace content