54b77a96938de5f50ad8b700640f74ef833b5efc
[platform/framework/web/crosswalk.git] / src / chrome / browser / prerender / prerender_tracker_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 <set>
6 #include <utility>
7
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "base/threading/sequenced_worker_pool.h"
13 #include "chrome/browser/net/url_request_mock_util.h"
14 #include "chrome/browser/prerender/prerender_contents.h"
15 #include "chrome/browser/prerender/prerender_manager.h"
16 #include "chrome/browser/prerender/prerender_resource_throttle.h"
17 #include "chrome/browser/prerender/prerender_tracker.h"
18 #include "chrome/test/base/testing_browser_process.h"
19 #include "content/public/browser/resource_controller.h"
20 #include "content/public/browser/resource_request_info.h"
21 #include "content/public/test/test_browser_thread.h"
22 #include "content/test/net/url_request_mock_http_job.h"
23 #include "ipc/ipc_message.h"
24 #include "net/base/request_priority.h"
25 #include "net/url_request/redirect_info.h"
26 #include "net/url_request/url_request.h"
27 #include "net/url_request/url_request_test_util.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29
30 using content::BrowserThread;
31 using content::ResourceType;
32
33 namespace prerender {
34
35 namespace {
36
37 class TestPrerenderContents : public PrerenderContents {
38  public:
39   TestPrerenderContents(PrerenderManager* prerender_manager,
40                         int child_id, int route_id)
41       : PrerenderContents(prerender_manager, static_cast<Profile*>(NULL),
42                           GURL(), content::Referrer(), ORIGIN_NONE,
43                           PrerenderManager::kNoExperiment),
44         child_id_(child_id),
45         route_id_(route_id) {
46     PrerenderResourceThrottle::OverridePrerenderContentsForTesting(this);
47   }
48
49   virtual ~TestPrerenderContents() {
50     if (final_status() == FINAL_STATUS_MAX)
51       SetFinalStatus(FINAL_STATUS_USED);
52     PrerenderResourceThrottle::OverridePrerenderContentsForTesting(NULL);
53   }
54
55   virtual bool GetChildId(int* child_id) const OVERRIDE {
56     *child_id = child_id_;
57     return true;
58   }
59
60   virtual bool GetRouteId(int* route_id) const OVERRIDE {
61     *route_id = route_id_;
62     return true;
63   }
64
65   void Start() {
66     prerendering_has_started_ = true;
67     NotifyPrerenderStart();
68   }
69
70   void Cancel() {
71     Destroy(FINAL_STATUS_CANCELLED);
72   }
73
74   void Use() {
75     PrepareForUse();
76   }
77
78  private:
79   int child_id_;
80   int route_id_;
81 };
82
83 class TestPrerenderManager : public PrerenderManager {
84  public:
85   explicit TestPrerenderManager(PrerenderTracker* prerender_tracker) :
86       PrerenderManager(NULL, prerender_tracker) {
87     mutable_config().rate_limit_enabled = false;
88   }
89
90   // We never allocate our PrerenderContents in PrerenderManager, so we don't
91   // ever want the default pending delete behaviour.
92   virtual void MoveEntryToPendingDelete(PrerenderContents* entry,
93                                         FinalStatus final_status) OVERRIDE {
94   }
95 };
96
97 class DeferredRedirectDelegate : public net::URLRequest::Delegate,
98                                  public content::ResourceController {
99  public:
100   DeferredRedirectDelegate()
101       : throttle_(NULL),
102         was_deferred_(false),
103         cancel_called_(false),
104         resume_called_(false) {
105   }
106
107   void SetThrottle(PrerenderResourceThrottle* throttle) {
108     throttle_ = throttle;
109     throttle_->set_controller_for_testing(this);
110   }
111
112   void Run() {
113     run_loop_.reset(new base::RunLoop());
114     run_loop_->Run();
115   }
116
117   bool was_deferred() const { return was_deferred_; }
118   bool cancel_called() const { return cancel_called_; }
119   bool resume_called() const { return resume_called_; }
120
121   // net::URLRequest::Delegate implementation:
122   virtual void OnReceivedRedirect(net::URLRequest* request,
123                                   const net::RedirectInfo& redirect_info,
124                                   bool* defer_redirect) OVERRIDE {
125     // Defer the redirect either way.
126     *defer_redirect = true;
127
128     // Find out what the throttle would have done.
129     throttle_->WillRedirectRequest(redirect_info.new_url, &was_deferred_);
130     run_loop_->Quit();
131   }
132   virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE { }
133   virtual void OnReadCompleted(net::URLRequest* request,
134                                int bytes_read) OVERRIDE {
135   }
136
137   // content::ResourceController implementation:
138   virtual void Cancel() OVERRIDE {
139     EXPECT_FALSE(cancel_called_);
140     EXPECT_FALSE(resume_called_);
141
142     cancel_called_ = true;
143     run_loop_->Quit();
144   }
145   virtual void CancelAndIgnore() OVERRIDE { Cancel(); }
146   virtual void CancelWithError(int error_code) OVERRIDE { Cancel(); }
147   virtual void Resume() OVERRIDE {
148     EXPECT_TRUE(was_deferred_);
149     EXPECT_FALSE(cancel_called_);
150     EXPECT_FALSE(resume_called_);
151
152     resume_called_ = true;
153     run_loop_->Quit();
154   }
155
156  private:
157   scoped_ptr<base::RunLoop> run_loop_;
158   PrerenderResourceThrottle* throttle_;
159   bool was_deferred_;
160   bool cancel_called_;
161   bool resume_called_;
162
163   DISALLOW_COPY_AND_ASSIGN(DeferredRedirectDelegate);
164 };
165
166 }  // namespace
167
168 class PrerenderTrackerTest : public testing::Test {
169  public:
170   static const int kDefaultChildId = 0;
171   static const int kDefaultRouteId = 100;
172
173   PrerenderTrackerTest() :
174       ui_thread_(BrowserThread::UI, &message_loop_),
175       io_thread_(BrowserThread::IO, &message_loop_),
176       prerender_manager_(prerender_tracker()),
177       test_contents_(&prerender_manager_, kDefaultChildId, kDefaultRouteId) {
178     chrome_browser_net::SetUrlRequestMocksEnabled(true);
179   }
180
181   virtual ~PrerenderTrackerTest() {
182     chrome_browser_net::SetUrlRequestMocksEnabled(false);
183
184     // Cleanup work so the file IO tasks from URLRequestMockHTTPJob
185     // are gone.
186     content::BrowserThread::GetBlockingPool()->FlushForTesting();
187     RunEvents();
188   }
189
190   PrerenderTracker* prerender_tracker() {
191     return g_browser_process->prerender_tracker();
192   }
193
194   TestPrerenderManager* prerender_manager() {
195     return &prerender_manager_;
196   }
197
198   TestPrerenderContents* test_contents() {
199     return &test_contents_;
200   }
201
202   // Runs any tasks queued on either thread.
203   void RunEvents() {
204     message_loop_.RunUntilIdle();
205   }
206
207  private:
208   base::MessageLoopForIO message_loop_;
209   content::TestBrowserThread ui_thread_;
210   content::TestBrowserThread io_thread_;
211
212   TestPrerenderManager prerender_manager_;
213   TestPrerenderContents test_contents_;
214 };
215
216 // Checks that deferred redirects are throttled and resumed correctly.
217 TEST_F(PrerenderTrackerTest, PrerenderThrottledRedirectResume) {
218   const base::FilePath::CharType kRedirectPath[] =
219       FILE_PATH_LITERAL("prerender/image-deferred.png");
220
221   test_contents()->Start();
222   RunEvents();
223
224   // Fake a request.
225   net::TestURLRequestContext url_request_context;
226   DeferredRedirectDelegate delegate;
227   net::URLRequest request(
228       content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath)),
229       net::DEFAULT_PRIORITY,
230       &delegate,
231       &url_request_context);
232   content::ResourceRequestInfo::AllocateForTesting(&request,
233                                                    content::RESOURCE_TYPE_IMAGE,
234                                                    NULL,
235                                                    kDefaultChildId,
236                                                    kDefaultRouteId,
237                                                    MSG_ROUTING_NONE,
238                                                    true);
239
240   // Install a prerender throttle.
241   PrerenderResourceThrottle throttle(&request);
242   delegate.SetThrottle(&throttle);
243
244   // Start the request and wait for a redirect.
245   request.Start();
246   delegate.Run();
247   EXPECT_TRUE(delegate.was_deferred());
248   // This calls WillRedirectRequestOnUI().
249   RunEvents();
250
251   // Display the prerendered RenderView and wait for the throttle to
252   // notice.
253   test_contents()->Use();
254   delegate.Run();
255   EXPECT_TRUE(delegate.resume_called());
256   EXPECT_FALSE(delegate.cancel_called());
257 }
258
259 // Checks that redirects in main frame loads are not deferred.
260 TEST_F(PrerenderTrackerTest, PrerenderThrottledRedirectMainFrame) {
261   const base::FilePath::CharType kRedirectPath[] =
262       FILE_PATH_LITERAL("prerender/image-deferred.png");
263
264   test_contents()->Start();
265   RunEvents();
266
267   // Fake a request.
268   net::TestURLRequestContext url_request_context;
269   DeferredRedirectDelegate delegate;
270   net::URLRequest request(
271       content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath)),
272       net::DEFAULT_PRIORITY,
273       &delegate,
274       &url_request_context);
275   content::ResourceRequestInfo::AllocateForTesting(
276       &request,
277       content::RESOURCE_TYPE_MAIN_FRAME,
278       NULL,
279       kDefaultChildId,
280       kDefaultRouteId,
281       MSG_ROUTING_NONE,
282       true);
283
284   // Install a prerender throttle.
285   PrerenderResourceThrottle throttle(&request);
286   delegate.SetThrottle(&throttle);
287
288   // Start the request and wait for a redirect. This time, it should
289   // not be deferred.
290   request.Start();
291   delegate.Run();
292   // This calls WillRedirectRequestOnUI().
293   RunEvents();
294
295   // Cleanup work so the prerender is gone.
296   test_contents()->Cancel();
297   RunEvents();
298 }
299
300 // Checks that attempting to defer a synchronous request aborts the
301 // prerender.
302 TEST_F(PrerenderTrackerTest, PrerenderThrottledRedirectSyncXHR) {
303   const base::FilePath::CharType kRedirectPath[] =
304       FILE_PATH_LITERAL("prerender/image-deferred.png");
305
306   test_contents()->Start();
307   RunEvents();
308
309   // Fake a request.
310   net::TestURLRequestContext url_request_context;
311   DeferredRedirectDelegate delegate;
312   net::URLRequest request(
313       content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath)),
314       net::DEFAULT_PRIORITY,
315       &delegate,
316       &url_request_context);
317   content::ResourceRequestInfo::AllocateForTesting(&request,
318                                                    content::RESOURCE_TYPE_XHR,
319                                                    NULL,
320                                                    kDefaultChildId,
321                                                    kDefaultRouteId,
322                                                    MSG_ROUTING_NONE,
323                                                    false);
324
325   // Install a prerender throttle.
326   PrerenderResourceThrottle throttle(&request);
327   delegate.SetThrottle(&throttle);
328
329   // Start the request and wait for a redirect.
330   request.Start();
331   delegate.Run();
332   // This calls WillRedirectRequestOnUI().
333   RunEvents();
334
335   // We should have cancelled the prerender.
336   EXPECT_EQ(FINAL_STATUS_BAD_DEFERRED_REDIRECT,
337             test_contents()->final_status());
338
339   // Cleanup work so the prerender is gone.
340   test_contents()->Cancel();
341   RunEvents();
342 }
343
344 }  // namespace prerender