// 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 #include #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[] = "Google

Google

"; 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], ¶ms)) { 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 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 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 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 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 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 shared_memory_map_; std::vector 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 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 bridge1(CreateBridge()); TestRequestPeer peer1(bridge1.get()); scoped_ptr 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 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 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 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 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 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 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 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 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 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 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