Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / child / resource_dispatcher_unittest.cc
index a9043e0..c13cc1a 100644 (file)
@@ -5,63 +5,82 @@
 #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"
-#include "webkit/common/appcache/appcache_interfaces.h"
-
-using webkit_glue::ResourceLoaderBridge;
-using webkit_glue::ResourceResponseInfo;
 
 namespace content {
 
-static const char test_page_url[] = "http://www.google.com/";
-static const char test_page_headers[] =
+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 test_page_mime_type[] = "text/html";
-static const char test_page_charset[] = "";
-static const char test_page_contents[] =
+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 uint32 test_page_contents_len = arraysize(test_page_contents) - 1;
-
-static const char kShmemSegmentName[] = "DeferredResourceLoaderTest";
+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 TestRequestCallback : public ResourceLoaderBridge::Peer {
+class TestRequestPeer : public RequestPeer {
  public:
-  TestRequestCallback() : complete_(false) {
+  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 GURL& new_url,
-      const ResourceResponseInfo& info,
-      bool* has_new_first_party_for_cookies,
-      GURL* new_first_party_for_cookies) OVERRIDE {
-    *has_new_first_party_for_cookies = false;
-    return true;
+  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;
@@ -70,32 +89,79 @@ class TestRequestCallback : public ResourceLoaderBridge::Peer {
   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) OVERRIDE {
+      const base::TimeTicks& completion_time,
+      int64 total_transfer_size) OVERRIDE {
+    EXPECT_TRUE(received_response_);
     EXPECT_FALSE(complete_);
     complete_ = true;
   }
 
-  bool complete() const {
-    return complete_;
+  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:
-  bool complete_;
+  // 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_;
 
-// Sets up the message sender override for the unit test
+  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 {
@@ -104,244 +170,539 @@ class ResourceDispatcherTest : public testing::Test, public IPC::Sender {
     return true;
   }
 
-  // Emulates the browser process and processes the pending IPC messages,
-  // returning the hardcoded file contents.
-  void ProcessMessages() {
-    while (!message_queue_.empty()) {
-      int request_id;
-      ResourceHostMsg_Request request;
-      ASSERT_TRUE(ResourceHostMsg_RequestResource::Read(
-          &message_queue_[0], &request_id, &request));
-
-      // check values
-      EXPECT_EQ(test_page_url, request.url.spec());
-
-      // received response message
-      ResourceResponseHead response;
-      std::string raw_headers(test_page_headers);
-      std::replace(raw_headers.begin(), raw_headers.end(), '\n', '\0');
-      response.headers = new net::HttpResponseHeaders(raw_headers);
-      response.mime_type = test_page_mime_type;
-      response.charset = test_page_charset;
-      dispatcher_->OnReceivedResponse(request_id, response);
-
-      // received data message with the test contents
-      base::SharedMemory shared_mem;
-      EXPECT_TRUE(shared_mem.CreateAndMapAnonymous(test_page_contents_len));
-      char* put_data_here = static_cast<char*>(shared_mem.memory());
-      memcpy(put_data_here, test_page_contents, test_page_contents_len);
-      base::SharedMemoryHandle dup_handle;
-      EXPECT_TRUE(shared_mem.GiveToProcess(
-          base::Process::Current().handle(), &dup_handle));
-      dispatcher_->OnSetDataBuffer(request_id, dup_handle,
-                                   test_page_contents_len, 0);
-      dispatcher_->OnReceivedData(request_id, 0, test_page_contents_len,
-                                  test_page_contents_len);
-
-      message_queue_.erase(message_queue_.begin());
-
-      // read the ack message.
-      Tuple1<int> request_ack;
-      ASSERT_TRUE(ResourceHostMsg_DataReceived_ACK::Read(
-          &message_queue_[0], &request_ack));
-
-      ASSERT_EQ(request_ack.a, request_id);
-
-      message_queue_.erase(message_queue_.begin());
+  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)));
   }
 
- protected:
-  // testing::Test
-  virtual void SetUp() OVERRIDE {
-    dispatcher_.reset(new ResourceDispatcher(this));
+  ResourceLoaderBridge* CreateBridge() {
+    return CreateBridgeInternal(false);
   }
-  virtual void TearDown() OVERRIDE {
-    dispatcher_.reset();
+
+  ResourceLoaderBridge* CreateBridgeForDownloadToFile() {
+    return CreateBridgeInternal(true);
   }
 
-  ResourceLoaderBridge* CreateBridge() {
-    webkit_glue::ResourceLoaderBridge::RequestInfo request_info;
+  ResourceDispatcher* dispatcher() { return &dispatcher_; }
+
+ private:
+  ResourceLoaderBridge* CreateBridgeInternal(bool download_to_file) {
+    RequestInfo request_info;
     request_info.method = "GET";
-    request_info.url = GURL(test_page_url);
-    request_info.first_party_for_cookies = GURL(test_page_url);
+    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 = ResourceType::SUB_RESOURCE;
-    request_info.appcache_host_id = appcache::kNoHostId;
+    request_info.request_type = RESOURCE_TYPE_SUB_RESOURCE;
+    request_info.appcache_host_id = kAppCacheNoHostId;
     request_info.routing_id = 0;
-    RequestExtraData extra_data(blink::WebReferrerPolicyDefault,
-                                blink::WebPageVisibilityStateVisible,
-                                blink::WebString(),
-                                false, MSG_ROUTING_NONE, true, 0, GURL(),
-                                false, -1, true,
-                                PAGE_TRANSITION_LINK, false, -1, -1);
+    request_info.download_to_file = download_to_file;
+    RequestExtraData extra_data;
     request_info.extra_data = &extra_data;
 
-    return dispatcher_->CreateBridge(request_info);
+    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_;
-  static scoped_ptr<ResourceDispatcher> dispatcher_;
+  ResourceDispatcher dispatcher_;
+  base::MessageLoop message_loop_;
 };
 
-/*static*/
-scoped_ptr<ResourceDispatcher> ResourceDispatcherTest::dispatcher_;
-
-// Does a simple request and tests that the correct data is received.
+// Does a simple request and tests that the correct data is received.  Simulates
+// two reads.
 TEST_F(ResourceDispatcherTest, RoundTrip) {
-  TestRequestCallback callback;
-  ResourceLoaderBridge* bridge = CreateBridge();
-
-  bridge->Start(&callback);
-
-  ProcessMessages();
-
-  // FIXME(brettw) when the request complete messages are actually handledo
-  // and dispatched, uncomment this.
-  //EXPECT_TRUE(callback.complete());
-  //EXPECT_STREQ(test_page_contents, callback.data().c_str());
-  //EXPECT_EQ(test_page_contents_len, callback.total_encoded_data_length());
-
-  delete bridge;
+  // 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 multiple requests.
+// Tests that the request IDs are straight when there are two interleaving
+// requests.
 TEST_F(ResourceDispatcherTest, MultipleRequests) {
-  // FIXME
+  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
+// Tests that the cancel method prevents other messages from being received.
 TEST_F(ResourceDispatcherTest, Cancel) {
-  // FIXME
+  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());
 }
 
-TEST_F(ResourceDispatcherTest, Cookies) {
-  // FIXME
+// 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());
 }
 
-TEST_F(ResourceDispatcherTest, SerializedPostData) {
-  // FIXME
-}
+// Checks that redirects work as expected.
+TEST_F(ResourceDispatcherTest, Redirect) {
+  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
+  TestRequestPeer peer(bridge.get());
 
-// This class provides functionality to validate whether the ResourceDispatcher
-// object honors the deferred loading contract correctly, i.e. if deferred
-// loading is enabled it should queue up any responses received. If deferred
-// loading is enabled/disabled in the context of a dispatched message, other
-// queued messages should not be dispatched until deferred load is turned off.
-class DeferredResourceLoadingTest : public ResourceDispatcherTest,
-                                    public ResourceLoaderBridge::Peer {
- public:
-  DeferredResourceLoadingTest()
-      : defer_loading_(false) {
-  }
+  EXPECT_TRUE(bridge->Start(&peer));
+  int id = ConsumeRequestResource();
 
-  virtual bool Send(IPC::Message* msg) OVERRIDE {
-    delete msg;
-    return true;
-  }
+  NotifyReceivedRedirect(id);
+  ConsumeFollowRedirect(id);
+  EXPECT_EQ(1, peer.seen_redirects());
 
-  void InitMessages() {
-    set_defer_loading(true);
+  NotifyReceivedRedirect(id);
+  ConsumeFollowRedirect(id);
+  EXPECT_EQ(2, peer.seen_redirects());
 
-    ResourceResponseHead response_head;
-    response_head.error_code = net::OK;
+  NotifyReceivedResponse(id);
+  EXPECT_TRUE(peer.received_response());
 
-    dispatcher_->OnMessageReceived(
-        ResourceMsg_ReceivedResponse(0, response_head));
-
-    // Duplicate the shared memory handle so both the test and the callee can
-    // close their copy.
-    base::SharedMemoryHandle duplicated_handle;
-    EXPECT_TRUE(shared_handle_.ShareToProcess(base::GetCurrentProcessHandle(),
-                                              &duplicated_handle));
+  NotifySetDataBuffer(id, strlen(kTestPageContents));
+  NotifyDataReceived(id, kTestPageContents);
+  ConsumeDataReceived_ACK(id);
 
-    dispatcher_->OnMessageReceived(
-        ResourceMsg_SetDataBuffer(0, duplicated_handle, 100, 0));
-    dispatcher_->OnMessageReceived(ResourceMsg_DataReceived(0, 0, 100, 100));
+  NotifyRequestComplete(id, strlen(kTestPageContents));
+  EXPECT_EQ(kTestPageContents, peer.data());
+  EXPECT_TRUE(peer.complete());
+  EXPECT_EQ(0u, queued_messages());
+  EXPECT_EQ(2, peer.seen_redirects());
+}
 
-    set_defer_loading(false);
-  }
+// 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());
+}
 
-  // ResourceLoaderBridge::Peer methods.
-  virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE {
-  }
+// 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());
+}
 
-  virtual bool OnReceivedRedirect(
-      const GURL& new_url,
-      const ResourceResponseInfo& info,
-      bool* has_new_first_party_for_cookies,
-      GURL* new_first_party_for_cookies) OVERRIDE {
-    *has_new_first_party_for_cookies = false;
-    return true;
-  }
+// 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());
+}
 
-  virtual void OnReceivedResponse(const ResourceResponseInfo& info) OVERRIDE {
-    EXPECT_EQ(defer_loading_, false);
-    set_defer_loading(true);
-  }
+// 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());
 
-  virtual void OnDownloadedData(int len, int encoded_data_length) OVERRIDE {
-  }
+  EXPECT_TRUE(bridge->Start(&peer));
+  int id = ConsumeRequestResource();
+  EXPECT_EQ(0u, queued_messages());
 
-  virtual void OnReceivedData(const char* data,
-                              int data_length,
-                              int encoded_data_length) OVERRIDE {
-    EXPECT_EQ(defer_loading_, false);
-    set_defer_loading(false);
-  }
+  bridge->SetDefersLoading(true);
+  NotifyReceivedRedirect(id);
+  bridge->Cancel();
+  ConsumeCancelRequest(id);
 
-  virtual void OnCompletedRequest(
-      int error_code,
-      bool was_ignored_by_handler,
-      const std::string& security_info,
-      const base::TimeTicks& completion_time) OVERRIDE {
-  }
+  NotifyRequestComplete(id, 0);
+  base::RunLoop().RunUntilIdle();
 
- protected:
-  virtual void SetUp() OVERRIDE {
-    ResourceDispatcherTest::SetUp();
-    shared_handle_.Delete(kShmemSegmentName);
-    EXPECT_TRUE(shared_handle_.CreateNamed(kShmemSegmentName, false, 100));
-  }
+  // 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());
+}
 
-  virtual void TearDown() OVERRIDE {
-    shared_handle_.Close();
-    EXPECT_TRUE(shared_handle_.Delete(kShmemSegmentName));
-    ResourceDispatcherTest::TearDown();
-  }
+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());
+}
 
- private:
-  void set_defer_loading(bool defer) {
-    defer_loading_ = defer;
-    dispatcher_->SetDefersLoading(0, defer);
-  }
+// 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());
 
-  bool defer_loading() const {
-    return defer_loading_;
-  }
+  EXPECT_TRUE(bridge->Start(&peer));
+  int id = ConsumeRequestResource();
+  EXPECT_EQ(0u, queued_messages());
 
-  bool defer_loading_;
-  base::SharedMemory shared_handle_;
-};
+  NotifyReceivedResponse(id);
+  EXPECT_EQ(0u, queued_messages());
+  EXPECT_TRUE(peer.received_response());
 
-TEST_F(DeferredResourceLoadingTest, DeferredLoadTest) {
-  base::MessageLoopForIO message_loop;
+  // Cancelling the request deletes the file.
+  bridge->Cancel();
+  ConsumeCancelRequest(id);
+  ConsumeReleaseDownloadedFile(id);
 
-  ResourceLoaderBridge* bridge = CreateBridge();
+  // Deleting the bridge shouldn't send another message to delete the file.
+  bridge.reset();
+  EXPECT_EQ(0u, queued_messages());
+}
 
-  bridge->Start(this);
-  InitMessages();
+TEST_F(ResourceDispatcherTest, Cookies) {
+  // FIXME
+}
 
-  // Dispatch deferred messages.
-  message_loop.RunUntilIdle();
-  delete bridge;
+TEST_F(ResourceDispatcherTest, SerializedPostData) {
+  // FIXME
 }
 
 class TimeConversionTest : public ResourceDispatcherTest,
-                           public ResourceLoaderBridge::Peer {
+                           public RequestPeer {
  public:
   virtual bool Send(IPC::Message* msg) OVERRIDE {
     delete msg;
@@ -352,19 +713,16 @@ class TimeConversionTest : public ResourceDispatcherTest,
     scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
     bridge->Start(this);
 
-    dispatcher_->OnMessageReceived(
+    dispatcher()->OnMessageReceived(
         ResourceMsg_ReceivedResponse(0, response_head));
   }
 
-  // ResourceLoaderBridge::Peer methods.
+  // RequestPeer methods.
   virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE {
   }
 
-  virtual bool OnReceivedRedirect(
-      const GURL& new_url,
-      const ResourceResponseInfo& info,
-      bool* has_new_first_party_for_cookies,
-      GURL* new_first_party_for_cookies) OVERRIDE {
+  virtual bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
+                                  const ResourceResponseInfo& info) OVERRIDE {
     return true;
   }
 
@@ -383,8 +741,10 @@ class TimeConversionTest : public ResourceDispatcherTest,
   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) OVERRIDE {
+      const base::TimeTicks& completion_time,
+      int64 total_transfer_size) OVERRIDE {
   }
 
   const ResourceResponseInfo& response_info() const { return response_info_; }
@@ -396,7 +756,6 @@ class TimeConversionTest : public ResourceDispatcherTest,
 // TODO(simonjam): Enable this when 10829031 lands.
 TEST_F(TimeConversionTest, DISABLED_ProperlyInitialized) {
   ResourceResponseHead response_head;
-  response_head.error_code = net::OK;
   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();
@@ -416,7 +775,6 @@ TEST_F(TimeConversionTest, DISABLED_ProperlyInitialized) {
 
 TEST_F(TimeConversionTest, PartiallyInitialized) {
   ResourceResponseHead response_head;
-  response_head.error_code = net::OK;
   response_head.request_start = base::TimeTicks::FromInternalValue(5);
   response_head.response_start = base::TimeTicks::FromInternalValue(15);
 
@@ -429,7 +787,6 @@ TEST_F(TimeConversionTest, PartiallyInitialized) {
 
 TEST_F(TimeConversionTest, NotInitialized) {
   ResourceResponseHead response_head;
-  response_head.error_code = net::OK;
 
   PerformTest(response_head);