1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <string.h> 6 #include <algorithm> 7 #include <string> 8 #include <vector> 9 #include "base/message_loop.h" 10 #include "googleurl/src/gurl.h" 11 #include "net/base/filter.h" 12 #include "net/base/io_buffer.h" 13 #include "net/url_request/url_request.h" 14 #include "net/url_request/url_request_job.h" 15 #include "net/url_request/url_request_job_tracker.h" 16 #include "net/url_request/url_request_status.h" 17 #include "net/url_request/url_request_test_util.h" 18 #include "testing/gmock/include/gmock/gmock.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 #include "testing/platform_test.h" 21 22 using testing::Eq; 23 using testing::InSequence; 24 using testing::NotNull; 25 using testing::StrictMock; 26 27 namespace net { 28 namespace { 29 30 const char kBasic[] = "Hello\n"; 31 32 // The above string "Hello\n", gzip compressed. 33 const unsigned char kCompressed[] = { 34 0x1f, 0x8b, 0x08, 0x08, 0x38, 0x18, 0x2e, 0x4c, 0x00, 0x03, 0x63, 35 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x2e, 0x68, 36 0x74, 0x6d, 0x6c, 0x00, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0xe7, 0x02, 37 0x00, 0x16, 0x35, 0x96, 0x31, 0x06, 0x00, 0x00, 0x00 38 }; 39 40 bool GetResponseBody(const GURL& url, std::string* out_body) { 41 if (url.spec() == "test:basic") { 42 *out_body = kBasic; 43 } else if (url.spec() == "test:compressed") { 44 out_body->assign(reinterpret_cast<const char*>(kCompressed), 45 sizeof(kCompressed)); 46 } else { 47 return false; 48 } 49 50 return true; 51 } 52 53 class MockJobObserver : public URLRequestJobTracker::JobObserver { 54 public: 55 MOCK_METHOD1(OnJobAdded, void(URLRequestJob* job)); 56 MOCK_METHOD1(OnJobRemoved, void(URLRequestJob* job)); 57 MOCK_METHOD2(OnJobDone, void(URLRequestJob* job, 58 const URLRequestStatus& status)); 59 MOCK_METHOD3(OnJobRedirect, void(URLRequestJob* job, 60 const GURL& location, 61 int status_code)); 62 MOCK_METHOD3(OnBytesRead, void(URLRequestJob* job, 63 const char* buf, 64 int byte_count)); 65 }; 66 67 // A URLRequestJob that returns static content for given URLs. We do 68 // not use URLRequestTestJob here because URLRequestTestJob fakes 69 // async operations by calling ReadRawData synchronously in an async 70 // callback. This test requires a URLRequestJob that returns false for 71 // async reads, in order to exercise the real async read codepath. 72 class URLRequestJobTrackerTestJob : public URLRequestJob { 73 public: 74 URLRequestJobTrackerTestJob(URLRequest* request, bool async_reads) 75 : URLRequestJob(request), async_reads_(async_reads) {} 76 77 void Start() { 78 ASSERT_TRUE(GetResponseBody(request_->url(), &response_data_)); 79 80 // Start reading asynchronously so that all error reporting and data 81 // callbacks happen as they would for network requests. 82 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( 83 this, &URLRequestJobTrackerTestJob::NotifyHeadersComplete)); 84 } 85 86 bool ReadRawData(IOBuffer* buf, int buf_size, 87 int *bytes_read) { 88 const size_t bytes_to_read = std::min( 89 response_data_.size(), static_cast<size_t>(buf_size)); 90 91 // Regardless of whether we're performing a sync or async read, 92 // copy the data into the caller's buffer now. That way we don't 93 // have to hold on to the buffers in the async case. 94 memcpy(buf->data(), response_data_.data(), bytes_to_read); 95 response_data_.erase(0, bytes_to_read); 96 97 if (async_reads_) { 98 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); 99 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( 100 this, &URLRequestJobTrackerTestJob::OnReadCompleted, 101 bytes_to_read)); 102 } else { 103 SetStatus(URLRequestStatus()); 104 *bytes_read = bytes_to_read; 105 } 106 return !async_reads_; 107 } 108 109 void OnReadCompleted(int status) { 110 if (status == 0) { 111 NotifyDone(URLRequestStatus()); 112 } else if (status > 0) { 113 SetStatus(URLRequestStatus()); 114 } else { 115 ASSERT_FALSE(true) << "Unexpected OnReadCompleted callback."; 116 } 117 118 NotifyReadComplete(status); 119 } 120 121 Filter* SetupFilter() const { 122 return request_->url().spec() == "test:compressed" 123 ? Filter::GZipFactory() : NULL; 124 } 125 126 // The data to send, will be set in Start(). 127 std::string response_data_; 128 129 // Should reads be synchronous or asynchronous? 130 const bool async_reads_; 131 }; 132 133 // Google Mock Matcher to check two URLRequestStatus instances for 134 // equality. 135 MATCHER_P(StatusEq, other, "") { 136 return (arg.status() == other.status() && 137 arg.os_error() == other.os_error()); 138 } 139 140 // Google Mock Matcher to check that two blocks of memory are equal. 141 MATCHER_P2(MemEq, other, len, "") { 142 return memcmp(arg, other, len) == 0; 143 } 144 145 class URLRequestJobTrackerTest : public PlatformTest { 146 protected: 147 static void SetUpTestCase() { 148 URLRequest::RegisterProtocolFactory("test", &Factory); 149 } 150 151 virtual void SetUp() { 152 g_async_reads = true; 153 } 154 155 void AssertJobTrackerCallbacks(const char* url) { 156 InSequence seq; 157 testing::StrictMock<MockJobObserver> observer; 158 159 const GURL gurl(url); 160 std::string body; 161 ASSERT_TRUE(GetResponseBody(gurl, &body)); 162 163 // We expect to receive one call for each method on the JobObserver, 164 // in the following order: 165 EXPECT_CALL(observer, OnJobAdded(NotNull())); 166 EXPECT_CALL(observer, OnBytesRead(NotNull(), 167 MemEq(body.data(), body.size()), 168 Eq(static_cast<int>(body.size())))); 169 EXPECT_CALL(observer, OnJobDone(NotNull(), 170 StatusEq(URLRequestStatus()))); 171 EXPECT_CALL(observer, OnJobRemoved(NotNull())); 172 173 // Attach our observer and perform the resource fetch. 174 g_url_request_job_tracker.AddObserver(&observer); 175 Fetch(gurl); 176 g_url_request_job_tracker.RemoveObserver(&observer); 177 } 178 179 void Fetch(const GURL& url) { 180 TestDelegate d; 181 { 182 URLRequest request(url, &d); 183 request.Start(); 184 MessageLoop::current()->RunAllPending(); 185 } 186 187 // A few sanity checks to make sure that the delegate also 188 // receives the expected callbacks. 189 EXPECT_EQ(1, d.response_started_count()); 190 EXPECT_FALSE(d.received_data_before_response()); 191 EXPECT_STREQ(kBasic, d.data_received().c_str()); 192 } 193 194 static URLRequest::ProtocolFactory Factory; 195 static bool g_async_reads; 196 }; 197 198 // static 199 URLRequestJob* URLRequestJobTrackerTest::Factory( 200 URLRequest* request, 201 const std::string& scheme) { 202 return new URLRequestJobTrackerTestJob(request, g_async_reads); 203 } 204 205 // static 206 bool URLRequestJobTrackerTest::g_async_reads = true; 207 208 TEST_F(URLRequestJobTrackerTest, BasicAsync) { 209 g_async_reads = true; 210 AssertJobTrackerCallbacks("test:basic"); 211 } 212 213 TEST_F(URLRequestJobTrackerTest, BasicSync) { 214 g_async_reads = false; 215 AssertJobTrackerCallbacks("test:basic"); 216 } 217 218 TEST_F(URLRequestJobTrackerTest, CompressedAsync) { 219 g_async_reads = true; 220 AssertJobTrackerCallbacks("test:compressed"); 221 } 222 223 TEST_F(URLRequestJobTrackerTest, CompressedSync) { 224 g_async_reads = false; 225 AssertJobTrackerCallbacks("test:compressed"); 226 } 227 228 } // namespace 229 } // namespace net 230