IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / loader / resource_scheduler_unittest.cc
blob5f9c693952f19a414a75ceff03a267530638b0ce
1 // Copyright (c) 2012 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.
5 #include "content/browser/loader/resource_scheduler.h"
7 #include "base/memory/scoped_vector.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "content/browser/browser_thread_impl.h"
11 #include "content/browser/loader/resource_dispatcher_host_impl.h"
12 #include "content/browser/loader/resource_message_filter.h"
13 #include "content/browser/loader/resource_request_info_impl.h"
14 #include "content/common/resource_messages.h"
15 #include "content/public/browser/resource_context.h"
16 #include "content/public/browser/resource_controller.h"
17 #include "content/public/browser/resource_throttle.h"
18 #include "content/public/common/process_type.h"
19 #include "net/base/host_port_pair.h"
20 #include "net/base/request_priority.h"
21 #include "net/http/http_server_properties_impl.h"
22 #include "net/url_request/url_request.h"
23 #include "net/url_request/url_request_test_util.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "webkit/common/resource_type.h"
27 namespace content {
29 namespace {
31 class TestRequestFactory;
33 const int kChildId = 30;
34 const int kRouteId = 75;
36 class TestRequest : public ResourceController {
37 public:
38 TestRequest(scoped_ptr<ResourceThrottle> throttle,
39 scoped_ptr<net::URLRequest> url_request)
40 : started_(false),
41 throttle_(throttle.Pass()),
42 url_request_(url_request.Pass()) {
43 throttle_->set_controller_for_testing(this);
46 bool started() const { return started_; }
48 void Start() {
49 bool deferred = false;
50 throttle_->WillStartRequest(&deferred);
51 started_ = !deferred;
54 const net::URLRequest* url_request() const { return url_request_.get(); }
56 protected:
57 // ResourceController interface:
58 virtual void Cancel() OVERRIDE {}
59 virtual void CancelAndIgnore() OVERRIDE {}
60 virtual void CancelWithError(int error_code) OVERRIDE {}
61 virtual void Resume() OVERRIDE { started_ = true; }
63 private:
64 bool started_;
65 scoped_ptr<ResourceThrottle> throttle_;
66 scoped_ptr<net::URLRequest> url_request_;
69 class CancelingTestRequest : public TestRequest {
70 public:
71 CancelingTestRequest(scoped_ptr<ResourceThrottle> throttle,
72 scoped_ptr<net::URLRequest> url_request)
73 : TestRequest(throttle.Pass(), url_request.Pass()) {
76 void set_request_to_cancel(scoped_ptr<TestRequest> request_to_cancel) {
77 request_to_cancel_ = request_to_cancel.Pass();
80 private:
81 virtual void Resume() OVERRIDE {
82 TestRequest::Resume();
83 request_to_cancel_.reset();
86 scoped_ptr<TestRequest> request_to_cancel_;
89 class FakeResourceContext : public ResourceContext {
90 private:
91 virtual net::HostResolver* GetHostResolver() OVERRIDE { return NULL; }
92 virtual net::URLRequestContext* GetRequestContext() OVERRIDE { return NULL; }
93 virtual bool AllowMicAccess(const GURL& origin) OVERRIDE { return false; }
94 virtual bool AllowCameraAccess(const GURL& origin) OVERRIDE { return false; }
95 virtual std::string GetMediaDeviceIDSalt() OVERRIDE { return std::string(); }
98 class FakeResourceMessageFilter : public ResourceMessageFilter {
99 public:
100 FakeResourceMessageFilter(int child_id)
101 : ResourceMessageFilter(
102 child_id,
103 PROCESS_TYPE_RENDERER,
104 NULL /* appcache_service */,
105 NULL /* blob_storage_context */,
106 NULL /* file_system_context */,
107 base::Bind(&FakeResourceMessageFilter::GetContexts,
108 base::Unretained(this))) {
111 private:
112 virtual ~FakeResourceMessageFilter() {}
114 void GetContexts(const ResourceHostMsg_Request& request,
115 ResourceContext** resource_context,
116 net::URLRequestContext** request_context) {
117 *resource_context = &context_;
118 *request_context = NULL;
121 FakeResourceContext context_;
124 class ResourceSchedulerTest : public testing::Test {
125 protected:
126 ResourceSchedulerTest()
127 : next_request_id_(0),
128 message_loop_(base::MessageLoop::TYPE_IO),
129 ui_thread_(BrowserThread::UI, &message_loop_),
130 io_thread_(BrowserThread::IO, &message_loop_) {
131 scheduler_.OnClientCreated(kChildId, kRouteId);
132 context_.set_http_server_properties(http_server_properties_.GetWeakPtr());
135 virtual ~ResourceSchedulerTest() {
136 scheduler_.OnClientDeleted(kChildId, kRouteId);
139 scoped_ptr<net::URLRequest> NewURLRequestWithRoute(
140 const char* url,
141 net::RequestPriority priority,
142 int route_id) {
143 scoped_ptr<net::URLRequest> url_request(
144 context_.CreateRequest(GURL(url), priority, NULL));
145 ResourceRequestInfoImpl* info = new ResourceRequestInfoImpl(
146 PROCESS_TYPE_RENDERER, // process_type
147 kChildId, // child_id
148 route_id, // route_id
149 0, // origin_pid
150 ++next_request_id_, // request_id
151 MSG_ROUTING_NONE, // render_frame_id
152 false, // is_main_frame
153 0, // frame_id
154 false, // parent_is_main_frame
155 0, // parent_frame_id
156 ResourceType::SUB_RESOURCE, // resource_type
157 PAGE_TRANSITION_LINK, // transition_type
158 false, // should_replace_current_entry
159 false, // is_download
160 false, // is_stream
161 true, // allow_download
162 false, // has_user_gesture
163 blink::WebReferrerPolicyDefault, // referrer_policy
164 NULL, // context
165 base::WeakPtr<ResourceMessageFilter>(), // filter
166 true); // is_async
167 info->AssociateWithRequest(url_request.get());
168 return url_request.Pass();
171 scoped_ptr<net::URLRequest> NewURLRequest(const char* url,
172 net::RequestPriority priority) {
173 return NewURLRequestWithRoute(url, priority, kRouteId);
176 TestRequest* NewRequestWithRoute(const char* url,
177 net::RequestPriority priority,
178 int route_id) {
179 scoped_ptr<net::URLRequest> url_request(
180 NewURLRequestWithRoute(url, priority, route_id));
181 scoped_ptr<ResourceThrottle> throttle(scheduler_.ScheduleRequest(
182 kChildId, route_id, url_request.get()));
183 TestRequest* request = new TestRequest(throttle.Pass(), url_request.Pass());
184 request->Start();
185 return request;
188 TestRequest* NewRequest(const char* url, net::RequestPriority priority) {
189 return NewRequestWithRoute(url, priority, kRouteId);
192 void ChangeRequestPriority(TestRequest* request,
193 net::RequestPriority new_priority) {
194 scoped_refptr<FakeResourceMessageFilter> filter(
195 new FakeResourceMessageFilter(kChildId));
196 const ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
197 request->url_request());
198 const GlobalRequestID& id = info->GetGlobalRequestID();
199 ResourceHostMsg_DidChangePriority msg(id.request_id, new_priority);
200 bool ok = false;
201 rdh_.OnMessageReceived(msg, filter.get(), &ok);
202 EXPECT_TRUE(ok);
205 int next_request_id_;
206 base::MessageLoop message_loop_;
207 BrowserThreadImpl ui_thread_;
208 BrowserThreadImpl io_thread_;
209 ResourceDispatcherHostImpl rdh_;
210 ResourceScheduler scheduler_;
211 net::HttpServerPropertiesImpl http_server_properties_;
212 net::TestURLRequestContext context_;
215 TEST_F(ResourceSchedulerTest, OneIsolatedLowRequest) {
216 scoped_ptr<TestRequest> request(NewRequest("http://host/1", net::LOWEST));
217 EXPECT_TRUE(request->started());
220 TEST_F(ResourceSchedulerTest, OneLowLoadsUntilIdle) {
221 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
222 scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
223 scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
224 EXPECT_TRUE(high->started());
225 EXPECT_TRUE(low->started());
226 EXPECT_FALSE(low2->started());
227 high.reset();
228 EXPECT_TRUE(low2->started());
231 TEST_F(ResourceSchedulerTest, OneLowLoadsUntilBodyInserted) {
232 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
233 scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
234 scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
235 EXPECT_TRUE(high->started());
236 EXPECT_TRUE(low->started());
237 EXPECT_FALSE(low2->started());
238 scheduler_.OnWillInsertBody(kChildId, kRouteId);
239 EXPECT_TRUE(low2->started());
242 TEST_F(ResourceSchedulerTest, OneLowLoadsUntilBodyInsertedExceptSpdy) {
243 http_server_properties_.SetSupportsSpdy(
244 net::HostPortPair("spdyhost", 443), true);
245 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
246 scoped_ptr<TestRequest> low_spdy(
247 NewRequest("https://spdyhost/high", net::LOWEST));
248 scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
249 scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
250 EXPECT_TRUE(high->started());
251 EXPECT_TRUE(low_spdy->started());
252 EXPECT_TRUE(low->started());
253 EXPECT_FALSE(low2->started());
254 scheduler_.OnWillInsertBody(kChildId, kRouteId);
255 EXPECT_TRUE(low2->started());
258 TEST_F(ResourceSchedulerTest, NavigationResetsState) {
259 scheduler_.OnWillInsertBody(kChildId, kRouteId);
260 scheduler_.OnNavigate(kChildId, kRouteId);
261 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
262 scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
263 scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
264 EXPECT_TRUE(high->started());
265 EXPECT_TRUE(low->started());
266 EXPECT_FALSE(low2->started());
269 TEST_F(ResourceSchedulerTest, BackgroundRequestStartsImmediately) {
270 const int route_id = 0; // Indicates a background request.
271 scoped_ptr<TestRequest> request(NewRequestWithRoute("http://host/1",
272 net::LOWEST, route_id));
273 EXPECT_TRUE(request->started());
276 TEST_F(ResourceSchedulerTest, StartMultipleLowRequestsWhenIdle) {
277 scoped_ptr<TestRequest> high1(NewRequest("http://host/high1", net::HIGHEST));
278 scoped_ptr<TestRequest> high2(NewRequest("http://host/high2", net::HIGHEST));
279 scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
280 scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
281 EXPECT_TRUE(high1->started());
282 EXPECT_TRUE(high2->started());
283 EXPECT_TRUE(low->started());
284 EXPECT_FALSE(low2->started());
285 high1.reset();
286 EXPECT_FALSE(low2->started());
287 high2.reset();
288 EXPECT_TRUE(low2->started());
291 TEST_F(ResourceSchedulerTest, CancelOtherRequestsWhileResuming) {
292 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
293 scoped_ptr<TestRequest> low1(NewRequest("http://host/low1", net::LOWEST));
295 scoped_ptr<net::URLRequest> url_request(
296 NewURLRequest("http://host/low2", net::LOWEST));
297 scoped_ptr<ResourceThrottle> throttle(scheduler_.ScheduleRequest(
298 kChildId, kRouteId, url_request.get()));
299 scoped_ptr<CancelingTestRequest> low2(new CancelingTestRequest(
300 throttle.Pass(), url_request.Pass()));
301 low2->Start();
303 scoped_ptr<TestRequest> low3(NewRequest("http://host/low3", net::LOWEST));
304 low2->set_request_to_cancel(low3.Pass());
305 scoped_ptr<TestRequest> low4(NewRequest("http://host/low4", net::LOWEST));
307 EXPECT_TRUE(high->started());
308 EXPECT_FALSE(low2->started());
309 high.reset();
310 EXPECT_TRUE(low1->started());
311 EXPECT_TRUE(low2->started());
312 EXPECT_TRUE(low4->started());
315 TEST_F(ResourceSchedulerTest, LimitedNumberOfDelayableRequestsInFlight) {
316 // We only load low priority resources if there's a body.
317 scheduler_.OnWillInsertBody(kChildId, kRouteId);
319 // Throw in one high priority request to make sure that's not a factor.
320 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
321 EXPECT_TRUE(high->started());
323 const int kMaxNumDelayableRequestsPerClient = 10; // Should match the .cc.
324 const int kMaxNumDelayableRequestsPerHost = 6;
325 ScopedVector<TestRequest> lows_singlehost;
326 // Queue up to the per-host limit (we subtract the current high-pri request).
327 for (int i = 0; i < kMaxNumDelayableRequestsPerHost - 1; ++i) {
328 string url = "http://host/low" + base::IntToString(i);
329 lows_singlehost.push_back(NewRequest(url.c_str(), net::LOWEST));
330 EXPECT_TRUE(lows_singlehost[i]->started());
333 scoped_ptr<TestRequest> second_last_singlehost(NewRequest("http://host/last",
334 net::LOWEST));
335 scoped_ptr<TestRequest> last_singlehost(NewRequest("http://host/s_last",
336 net::LOWEST));
338 EXPECT_FALSE(second_last_singlehost->started());
339 high.reset();
340 EXPECT_TRUE(second_last_singlehost->started());
341 EXPECT_FALSE(last_singlehost->started());
342 lows_singlehost.erase(lows_singlehost.begin());
343 EXPECT_TRUE(last_singlehost->started());
345 // Queue more requests from different hosts until we reach the total limit.
346 int expected_slots_left =
347 kMaxNumDelayableRequestsPerClient - kMaxNumDelayableRequestsPerHost;
348 EXPECT_GT(expected_slots_left, 0);
349 ScopedVector<TestRequest> lows_differenthosts;
350 for (int i = 0; i < expected_slots_left; ++i) {
351 string url = "http://host" + base::IntToString(i) + "/low";
352 lows_differenthosts.push_back(NewRequest(url.c_str(), net::LOWEST));
353 EXPECT_TRUE(lows_differenthosts[i]->started());
356 scoped_ptr<TestRequest> last_differenthost(NewRequest("http://host_new/last",
357 net::LOWEST));
358 EXPECT_FALSE(last_differenthost->started());
361 TEST_F(ResourceSchedulerTest, RaisePriorityAndStart) {
362 // Dummies to enforce scheduling.
363 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
364 scoped_ptr<TestRequest> low(NewRequest("http://host/req", net::LOWEST));
366 scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::LOWEST));
367 EXPECT_FALSE(request->started());
369 ChangeRequestPriority(request.get(), net::HIGHEST);
370 EXPECT_TRUE(request->started());
373 TEST_F(ResourceSchedulerTest, RaisePriorityInQueue) {
374 // Dummies to enforce scheduling.
375 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
376 scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
378 scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::IDLE));
379 scoped_ptr<TestRequest> idle(NewRequest("http://host/idle", net::IDLE));
380 EXPECT_FALSE(request->started());
381 EXPECT_FALSE(idle->started());
383 ChangeRequestPriority(request.get(), net::LOWEST);
384 EXPECT_FALSE(request->started());
385 EXPECT_FALSE(idle->started());
387 const int kMaxNumDelayableRequestsPerClient = 10; // Should match the .cc.
388 ScopedVector<TestRequest> lows;
389 for (int i = 0; i < kMaxNumDelayableRequestsPerClient - 1; ++i) {
390 string url = "http://host/low" + base::IntToString(i);
391 lows.push_back(NewRequest(url.c_str(), net::LOWEST));
394 scheduler_.OnWillInsertBody(kChildId, kRouteId);
395 EXPECT_TRUE(request->started());
396 EXPECT_FALSE(idle->started());
399 TEST_F(ResourceSchedulerTest, LowerPriority) {
400 // Dummies to enforce scheduling.
401 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
402 scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
404 scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::LOWEST));
405 scoped_ptr<TestRequest> idle(NewRequest("http://host/idle", net::IDLE));
406 EXPECT_FALSE(request->started());
407 EXPECT_FALSE(idle->started());
409 ChangeRequestPriority(request.get(), net::IDLE);
410 EXPECT_FALSE(request->started());
411 EXPECT_FALSE(idle->started());
413 const int kMaxNumDelayableRequestsPerClient = 10; // Should match the .cc.
414 // 2 fewer filler requests: 1 for the "low" dummy at the start, and 1 for the
415 // one at the end, which will be tested.
416 const int kNumFillerRequests = kMaxNumDelayableRequestsPerClient - 2;
417 ScopedVector<TestRequest> lows;
418 for (int i = 0; i < kNumFillerRequests; ++i) {
419 string url = "http://host" + base::IntToString(i) + "/low";
420 lows.push_back(NewRequest(url.c_str(), net::LOWEST));
423 scheduler_.OnWillInsertBody(kChildId, kRouteId);
424 EXPECT_FALSE(request->started());
425 EXPECT_TRUE(idle->started());
428 TEST_F(ResourceSchedulerTest, ReprioritizedRequestGoesToBackOfQueue) {
429 // Dummies to enforce scheduling.
430 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
431 scoped_ptr<TestRequest> low(NewRequest("http://host/high", net::LOWEST));
433 scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::LOWEST));
434 scoped_ptr<TestRequest> idle(NewRequest("http://host/idle", net::IDLE));
435 EXPECT_FALSE(request->started());
436 EXPECT_FALSE(idle->started());
438 const int kMaxNumDelayableRequestsPerClient = 10; // Should match the .cc.
439 ScopedVector<TestRequest> lows;
440 for (int i = 0; i < kMaxNumDelayableRequestsPerClient; ++i) {
441 string url = "http://host/low" + base::IntToString(i);
442 lows.push_back(NewRequest(url.c_str(), net::LOWEST));
445 ChangeRequestPriority(request.get(), net::IDLE);
446 EXPECT_FALSE(request->started());
447 EXPECT_FALSE(idle->started());
449 ChangeRequestPriority(request.get(), net::LOWEST);
450 EXPECT_FALSE(request->started());
451 EXPECT_FALSE(idle->started());
453 scheduler_.OnWillInsertBody(kChildId, kRouteId);
454 EXPECT_FALSE(request->started());
455 EXPECT_FALSE(idle->started());
458 TEST_F(ResourceSchedulerTest, NonHTTPSchedulesImmediately) {
459 // Dummies to enforce scheduling.
460 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
461 scoped_ptr<TestRequest> low(NewRequest("http://host/high", net::LOWEST));
463 scoped_ptr<TestRequest> request(
464 NewRequest("chrome-extension://req", net::LOWEST));
465 EXPECT_TRUE(request->started());
468 TEST_F(ResourceSchedulerTest, SpdyProxySchedulesImmediately) {
469 scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
470 scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
472 scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::IDLE));
473 EXPECT_FALSE(request->started());
475 scheduler_.OnReceivedSpdyProxiedHttpResponse(kChildId, kRouteId);
476 EXPECT_TRUE(request->started());
478 scoped_ptr<TestRequest> after(NewRequest("http://host/after", net::IDLE));
479 EXPECT_TRUE(after->started());
482 } // unnamed namespace
484 } // namespace content