Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / browser / loader / resource_scheduler_unittest.cc
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.
4
5 #include "content/browser/loader/resource_scheduler.h"
6
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"
26
27 namespace content {
28
29 namespace {
30
31 class TestRequestFactory;
32
33 const int kChildId = 30;
34 const int kRouteId = 75;
35
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);
44   }
45
46   bool started() const { return started_; }
47
48   void Start() {
49     bool deferred = false;
50     throttle_->WillStartRequest(&deferred);
51     started_ = !deferred;
52   }
53
54   const net::URLRequest* url_request() const { return url_request_.get(); }
55
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; }
62
63  private:
64   bool started_;
65   scoped_ptr<ResourceThrottle> throttle_;
66   scoped_ptr<net::URLRequest> url_request_;
67 };
68
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()) {
74   }
75
76   void set_request_to_cancel(scoped_ptr<TestRequest> request_to_cancel) {
77     request_to_cancel_ = request_to_cancel.Pass();
78   }
79
80  private:
81   virtual void Resume() OVERRIDE {
82     TestRequest::Resume();
83     request_to_cancel_.reset();
84   }
85
86   scoped_ptr<TestRequest> request_to_cancel_;
87 };
88
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 };
96
97 class FakeResourceMessageFilter : public ResourceMessageFilter {
98  public:
99   FakeResourceMessageFilter(int child_id)
100       : ResourceMessageFilter(
101           child_id,
102           PROCESS_TYPE_RENDERER,
103           NULL  /* appcache_service */,
104           NULL  /* blob_storage_context */,
105           NULL  /* file_system_context */,
106           base::Bind(&FakeResourceMessageFilter::GetContexts,
107                      base::Unretained(this))) {
108   }
109
110  private:
111   virtual ~FakeResourceMessageFilter() {}
112
113   void GetContexts(const ResourceHostMsg_Request& request,
114                    ResourceContext** resource_context,
115                    net::URLRequestContext** request_context) {
116     *resource_context = &context_;
117     *request_context = NULL;
118   }
119
120   FakeResourceContext context_;
121 };
122
123 class ResourceSchedulerTest : public testing::Test {
124  protected:
125   ResourceSchedulerTest()
126       : next_request_id_(0),
127         ui_thread_(BrowserThread::UI, &message_loop_),
128         io_thread_(BrowserThread::IO, &message_loop_) {
129     scheduler_.OnClientCreated(kChildId, kRouteId);
130     context_.set_http_server_properties(http_server_properties_.GetWeakPtr());
131   }
132
133   virtual ~ResourceSchedulerTest() {
134     scheduler_.OnClientDeleted(kChildId, kRouteId);
135   }
136
137   scoped_ptr<net::URLRequest> NewURLRequestWithRoute(
138       const char* url,
139       net::RequestPriority priority,
140       int route_id) {
141     scoped_ptr<net::URLRequest> url_request(
142         context_.CreateRequest(GURL(url), priority, NULL));
143     ResourceRequestInfoImpl* info = new ResourceRequestInfoImpl(
144         PROCESS_TYPE_RENDERER,             // process_type
145         kChildId,                          // child_id
146         route_id,                          // route_id
147         0,                                 // origin_pid
148         ++next_request_id_,                // request_id
149         MSG_ROUTING_NONE,                  // render_frame_id
150         false,                             // is_main_frame
151         0,                                 // frame_id
152         false,                             // parent_is_main_frame
153         0,                                 // parent_frame_id
154         ResourceType::SUB_RESOURCE,        // resource_type
155         PAGE_TRANSITION_LINK,              // transition_type
156         false,                             // should_replace_current_entry
157         false,                             // is_download
158         false,                             // is_stream
159         true,                              // allow_download
160         false,                             // has_user_gesture
161         blink::WebReferrerPolicyDefault,   // referrer_policy
162         blink::WebPageVisibilityStateVisible,  // visibility_state
163         NULL,                              // context
164         base::WeakPtr<ResourceMessageFilter>(),  // filter
165         true);                             // is_async
166     info->AssociateWithRequest(url_request.get());
167     return url_request.Pass();
168   }
169
170   scoped_ptr<net::URLRequest> NewURLRequest(const char* url,
171                                             net::RequestPriority priority) {
172     return NewURLRequestWithRoute(url, priority, kRouteId);
173   }
174
175   TestRequest* NewRequestWithRoute(const char* url,
176                                    net::RequestPriority priority,
177                                    int route_id) {
178     scoped_ptr<net::URLRequest> url_request(
179         NewURLRequestWithRoute(url, priority, route_id));
180     scoped_ptr<ResourceThrottle> throttle(scheduler_.ScheduleRequest(
181         kChildId, route_id, url_request.get()));
182     TestRequest* request = new TestRequest(throttle.Pass(), url_request.Pass());
183     request->Start();
184     return request;
185   }
186
187   TestRequest* NewRequest(const char* url, net::RequestPriority priority) {
188     return NewRequestWithRoute(url, priority, kRouteId);
189   }
190
191   void ChangeRequestPriority(TestRequest* request,
192                              net::RequestPriority new_priority) {
193     scoped_refptr<FakeResourceMessageFilter> filter(
194         new FakeResourceMessageFilter(kChildId));
195     const ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
196         request->url_request());
197     const GlobalRequestID& id = info->GetGlobalRequestID();
198     ResourceHostMsg_DidChangePriority msg(id.request_id, new_priority);
199     bool ok = false;
200     rdh_.OnMessageReceived(msg, filter.get(), &ok);
201     EXPECT_TRUE(ok);
202   }
203
204   int next_request_id_;
205   base::MessageLoopForIO message_loop_;
206   BrowserThreadImpl ui_thread_;
207   BrowserThreadImpl io_thread_;
208   ResourceDispatcherHostImpl rdh_;
209   ResourceScheduler scheduler_;
210   net::HttpServerPropertiesImpl http_server_properties_;
211   net::TestURLRequestContext context_;
212 };
213
214 TEST_F(ResourceSchedulerTest, OneIsolatedLowRequest) {
215   scoped_ptr<TestRequest> request(NewRequest("http://host/1", net::LOWEST));
216   EXPECT_TRUE(request->started());
217 }
218
219 TEST_F(ResourceSchedulerTest, OneLowLoadsUntilIdle) {
220   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
221   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
222   scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
223   EXPECT_TRUE(high->started());
224   EXPECT_TRUE(low->started());
225   EXPECT_FALSE(low2->started());
226   high.reset();
227   EXPECT_TRUE(low2->started());
228 }
229
230 TEST_F(ResourceSchedulerTest, OneLowLoadsUntilBodyInserted) {
231   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
232   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
233   scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
234   EXPECT_TRUE(high->started());
235   EXPECT_TRUE(low->started());
236   EXPECT_FALSE(low2->started());
237   scheduler_.OnWillInsertBody(kChildId, kRouteId);
238   EXPECT_TRUE(low2->started());
239 }
240
241 TEST_F(ResourceSchedulerTest, OneLowLoadsUntilBodyInsertedExceptSpdy) {
242   http_server_properties_.SetSupportsSpdy(
243       net::HostPortPair("spdyhost", 443), true);
244   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
245   scoped_ptr<TestRequest> low_spdy(
246       NewRequest("https://spdyhost/high", net::LOWEST));
247   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
248   scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
249   EXPECT_TRUE(high->started());
250   EXPECT_TRUE(low_spdy->started());
251   EXPECT_TRUE(low->started());
252   EXPECT_FALSE(low2->started());
253   scheduler_.OnWillInsertBody(kChildId, kRouteId);
254   EXPECT_TRUE(low2->started());
255 }
256
257 TEST_F(ResourceSchedulerTest, NavigationResetsState) {
258   scheduler_.OnWillInsertBody(kChildId, kRouteId);
259   scheduler_.OnNavigate(kChildId, kRouteId);
260   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
261   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
262   scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
263   EXPECT_TRUE(high->started());
264   EXPECT_TRUE(low->started());
265   EXPECT_FALSE(low2->started());
266 }
267
268 TEST_F(ResourceSchedulerTest, BackgroundRequestStartsImmediately) {
269   const int route_id = 0;  // Indicates a background request.
270   scoped_ptr<TestRequest> request(NewRequestWithRoute("http://host/1",
271                                                       net::LOWEST, route_id));
272   EXPECT_TRUE(request->started());
273 }
274
275 TEST_F(ResourceSchedulerTest, StartMultipleLowRequestsWhenIdle) {
276   scoped_ptr<TestRequest> high1(NewRequest("http://host/high1", net::HIGHEST));
277   scoped_ptr<TestRequest> high2(NewRequest("http://host/high2", net::HIGHEST));
278   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
279   scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
280   EXPECT_TRUE(high1->started());
281   EXPECT_TRUE(high2->started());
282   EXPECT_TRUE(low->started());
283   EXPECT_FALSE(low2->started());
284   high1.reset();
285   EXPECT_FALSE(low2->started());
286   high2.reset();
287   EXPECT_TRUE(low2->started());
288 }
289
290 TEST_F(ResourceSchedulerTest, CancelOtherRequestsWhileResuming) {
291   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
292   scoped_ptr<TestRequest> low1(NewRequest("http://host/low1", net::LOWEST));
293
294   scoped_ptr<net::URLRequest> url_request(
295       NewURLRequest("http://host/low2", net::LOWEST));
296   scoped_ptr<ResourceThrottle> throttle(scheduler_.ScheduleRequest(
297       kChildId, kRouteId, url_request.get()));
298   scoped_ptr<CancelingTestRequest> low2(new CancelingTestRequest(
299       throttle.Pass(), url_request.Pass()));
300   low2->Start();
301
302   scoped_ptr<TestRequest> low3(NewRequest("http://host/low3", net::LOWEST));
303   low2->set_request_to_cancel(low3.Pass());
304   scoped_ptr<TestRequest> low4(NewRequest("http://host/low4", net::LOWEST));
305
306   EXPECT_TRUE(high->started());
307   EXPECT_FALSE(low2->started());
308   high.reset();
309   EXPECT_TRUE(low1->started());
310   EXPECT_TRUE(low2->started());
311   EXPECT_TRUE(low4->started());
312 }
313
314 TEST_F(ResourceSchedulerTest, LimitedNumberOfDelayableRequestsInFlight) {
315   // We only load low priority resources if there's a body.
316   scheduler_.OnWillInsertBody(kChildId, kRouteId);
317
318   // Throw in one high priority request to make sure that's not a factor.
319   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
320   EXPECT_TRUE(high->started());
321
322   const int kMaxNumDelayableRequestsPerClient = 10;  // Should match the .cc.
323   const int kMaxNumDelayableRequestsPerHost = 6;
324   ScopedVector<TestRequest> lows_singlehost;
325   // Queue up to the per-host limit (we subtract the current high-pri request).
326   for (int i = 0; i < kMaxNumDelayableRequestsPerHost - 1; ++i) {
327     string url = "http://host/low" + base::IntToString(i);
328     lows_singlehost.push_back(NewRequest(url.c_str(), net::LOWEST));
329     EXPECT_TRUE(lows_singlehost[i]->started());
330   }
331
332   scoped_ptr<TestRequest> second_last_singlehost(NewRequest("http://host/last",
333                                                             net::LOWEST));
334   scoped_ptr<TestRequest> last_singlehost(NewRequest("http://host/s_last",
335                                                      net::LOWEST));
336
337   EXPECT_FALSE(second_last_singlehost->started());
338   high.reset();
339   EXPECT_TRUE(second_last_singlehost->started());
340   EXPECT_FALSE(last_singlehost->started());
341   lows_singlehost.erase(lows_singlehost.begin());
342   EXPECT_TRUE(last_singlehost->started());
343
344   // Queue more requests from different hosts until we reach the total limit.
345   int expected_slots_left =
346       kMaxNumDelayableRequestsPerClient - kMaxNumDelayableRequestsPerHost;
347   EXPECT_GT(expected_slots_left, 0);
348   ScopedVector<TestRequest> lows_differenthosts;
349   for (int i = 0; i < expected_slots_left; ++i) {
350     string url = "http://host" + base::IntToString(i) + "/low";
351     lows_differenthosts.push_back(NewRequest(url.c_str(), net::LOWEST));
352     EXPECT_TRUE(lows_differenthosts[i]->started());
353   }
354
355   scoped_ptr<TestRequest> last_differenthost(NewRequest("http://host_new/last",
356                                                         net::LOWEST));
357   EXPECT_FALSE(last_differenthost->started());
358 }
359
360 TEST_F(ResourceSchedulerTest, RaisePriorityAndStart) {
361   // Dummies to enforce scheduling.
362   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
363   scoped_ptr<TestRequest> low(NewRequest("http://host/req", net::LOWEST));
364
365   scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::LOWEST));
366   EXPECT_FALSE(request->started());
367
368   ChangeRequestPriority(request.get(), net::HIGHEST);
369   EXPECT_TRUE(request->started());
370 }
371
372 TEST_F(ResourceSchedulerTest, RaisePriorityInQueue) {
373   // Dummies to enforce scheduling.
374   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
375   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
376
377   scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::IDLE));
378   scoped_ptr<TestRequest> idle(NewRequest("http://host/idle", net::IDLE));
379   EXPECT_FALSE(request->started());
380   EXPECT_FALSE(idle->started());
381
382   ChangeRequestPriority(request.get(), net::LOWEST);
383   EXPECT_FALSE(request->started());
384   EXPECT_FALSE(idle->started());
385
386   const int kMaxNumDelayableRequestsPerClient = 10;  // Should match the .cc.
387   ScopedVector<TestRequest> lows;
388   for (int i = 0; i < kMaxNumDelayableRequestsPerClient - 1; ++i) {
389     string url = "http://host/low" + base::IntToString(i);
390     lows.push_back(NewRequest(url.c_str(), net::LOWEST));
391   }
392
393   scheduler_.OnWillInsertBody(kChildId, kRouteId);
394   EXPECT_TRUE(request->started());
395   EXPECT_FALSE(idle->started());
396 }
397
398 TEST_F(ResourceSchedulerTest, LowerPriority) {
399   // Dummies to enforce scheduling.
400   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
401   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
402
403   scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::LOWEST));
404   scoped_ptr<TestRequest> idle(NewRequest("http://host/idle", net::IDLE));
405   EXPECT_FALSE(request->started());
406   EXPECT_FALSE(idle->started());
407
408   ChangeRequestPriority(request.get(), net::IDLE);
409   EXPECT_FALSE(request->started());
410   EXPECT_FALSE(idle->started());
411
412   const int kMaxNumDelayableRequestsPerClient = 10;  // Should match the .cc.
413   // 2 fewer filler requests: 1 for the "low" dummy at the start, and 1 for the
414   // one at the end, which will be tested.
415   const int kNumFillerRequests = kMaxNumDelayableRequestsPerClient - 2;
416   ScopedVector<TestRequest> lows;
417   for (int i = 0; i < kNumFillerRequests; ++i) {
418     string url = "http://host" + base::IntToString(i) + "/low";
419     lows.push_back(NewRequest(url.c_str(), net::LOWEST));
420   }
421
422   scheduler_.OnWillInsertBody(kChildId, kRouteId);
423   EXPECT_FALSE(request->started());
424   EXPECT_TRUE(idle->started());
425 }
426
427 TEST_F(ResourceSchedulerTest, ReprioritizedRequestGoesToBackOfQueue) {
428   // Dummies to enforce scheduling.
429   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
430   scoped_ptr<TestRequest> low(NewRequest("http://host/high", net::LOWEST));
431
432   scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::LOWEST));
433   scoped_ptr<TestRequest> idle(NewRequest("http://host/idle", net::IDLE));
434   EXPECT_FALSE(request->started());
435   EXPECT_FALSE(idle->started());
436
437   const int kMaxNumDelayableRequestsPerClient = 10;  // Should match the .cc.
438   ScopedVector<TestRequest> lows;
439   for (int i = 0; i < kMaxNumDelayableRequestsPerClient; ++i) {
440     string url = "http://host/low" + base::IntToString(i);
441     lows.push_back(NewRequest(url.c_str(), net::LOWEST));
442   }
443
444   ChangeRequestPriority(request.get(), net::IDLE);
445   EXPECT_FALSE(request->started());
446   EXPECT_FALSE(idle->started());
447
448   ChangeRequestPriority(request.get(), net::LOWEST);
449   EXPECT_FALSE(request->started());
450   EXPECT_FALSE(idle->started());
451
452   scheduler_.OnWillInsertBody(kChildId, kRouteId);
453   EXPECT_FALSE(request->started());
454   EXPECT_FALSE(idle->started());
455 }
456
457 TEST_F(ResourceSchedulerTest, NonHTTPSchedulesImmediately) {
458   // Dummies to enforce scheduling.
459   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
460   scoped_ptr<TestRequest> low(NewRequest("http://host/high", net::LOWEST));
461
462   scoped_ptr<TestRequest> request(
463       NewRequest("chrome-extension://req", net::LOWEST));
464   EXPECT_TRUE(request->started());
465 }
466
467 TEST_F(ResourceSchedulerTest, SpdyProxySchedulesImmediately) {
468   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
469   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
470
471   scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::IDLE));
472   EXPECT_FALSE(request->started());
473
474   scheduler_.OnReceivedSpdyProxiedHttpResponse(kChildId, kRouteId);
475   EXPECT_TRUE(request->started());
476
477   scoped_ptr<TestRequest> after(NewRequest("http://host/after", net::IDLE));
478   EXPECT_TRUE(after->started());
479 }
480
481 }  // unnamed namespace
482
483 }  // namespace content