Home | History | Annotate | Download | only in predictors
      1 // Copyright 2014 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 "base/memory/ref_counted.h"
      6 #include "base/memory/scoped_ptr.h"
      7 #include "base/message_loop/message_loop.h"
      8 #include "chrome/browser/predictors/resource_prefetcher.h"
      9 #include "chrome/browser/predictors/resource_prefetcher_manager.h"
     10 #include "chrome/test/base/testing_profile.h"
     11 #include "content/public/test/test_browser_thread.h"
     12 #include "net/url_request/redirect_info.h"
     13 #include "net/url_request/url_request.h"
     14 #include "net/url_request/url_request_test_util.h"
     15 #include "testing/gmock/include/gmock/gmock.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 
     18 using testing::Eq;
     19 using testing::Property;
     20 
     21 namespace predictors {
     22 
     23 // Wrapper over the ResourcePrefetcher that stubs out the StartURLRequest call
     24 // since we do not want to do network fetches in this unittest.
     25 class TestResourcePrefetcher : public ResourcePrefetcher {
     26  public:
     27   TestResourcePrefetcher(ResourcePrefetcher::Delegate* delegate,
     28                          const ResourcePrefetchPredictorConfig& config,
     29                          const NavigationID& navigation_id,
     30                          PrefetchKeyType key_type,
     31                          scoped_ptr<RequestVector> requests)
     32       : ResourcePrefetcher(delegate, config, navigation_id,
     33                            key_type, requests.Pass()) { }
     34 
     35   virtual ~TestResourcePrefetcher() { }
     36 
     37   MOCK_METHOD1(StartURLRequest, void(net::URLRequest* request));
     38 
     39   void ReadFullResponse(net::URLRequest* request) OVERRIDE {
     40     FinishRequest(request, Request::PREFETCH_STATUS_FROM_CACHE);
     41   }
     42 
     43  private:
     44   DISALLOW_COPY_AND_ASSIGN(TestResourcePrefetcher);
     45 };
     46 
     47 
     48 // Delegate for ResourcePrefetcher.
     49 class TestResourcePrefetcherDelegate : public ResourcePrefetcher::Delegate {
     50  public:
     51   explicit TestResourcePrefetcherDelegate(base::MessageLoop* loop)
     52       : request_context_getter_(new net::TestURLRequestContextGetter(
     53           loop->message_loop_proxy())) { }
     54   ~TestResourcePrefetcherDelegate() { }
     55 
     56   virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE {
     57     return request_context_getter_->GetURLRequestContext();
     58   }
     59 
     60   MOCK_METHOD2(ResourcePrefetcherFinished,
     61                void(ResourcePrefetcher* prefetcher,
     62                     ResourcePrefetcher::RequestVector* requests));
     63 
     64  private:
     65   scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
     66 
     67   DISALLOW_COPY_AND_ASSIGN(TestResourcePrefetcherDelegate);
     68 };
     69 
     70 
     71 // The following unittest tests most of the ResourcePrefetcher except for:
     72 // 1. Call to ReadFullResponse. There does not seem to be a good way to test the
     73 //    function in a unittest, and probably requires a browser_test.
     74 // 2. Setting of the Prefetch status for cache vs non cache.
     75 class ResourcePrefetcherTest : public testing::Test {
     76  public:
     77   ResourcePrefetcherTest();
     78   virtual ~ResourcePrefetcherTest();
     79 
     80  protected:
     81   typedef ResourcePrefetcher::Request Request;
     82 
     83   void AddStartUrlRequestExpectation(const std::string& url) {
     84     EXPECT_CALL(*prefetcher_,
     85                 StartURLRequest(Property(&net::URLRequest::original_url,
     86                                          Eq(GURL(url)))));
     87   }
     88 
     89   void CheckPrefetcherState(size_t inflight, size_t queue, size_t host) {
     90     EXPECT_EQ(prefetcher_->inflight_requests_.size(), inflight);
     91     EXPECT_EQ(prefetcher_->request_queue_.size(), queue);
     92     EXPECT_EQ(prefetcher_->host_inflight_counts_.size(), host);
     93   }
     94 
     95   net::URLRequest* GetInFlightRequest(const std::string& url_str) {
     96     GURL url(url_str);
     97 
     98     for (std::list<Request*>::const_iterator it =
     99          prefetcher_->request_queue_.begin();
    100          it != prefetcher_->request_queue_.end(); ++it) {
    101       EXPECT_NE((*it)->resource_url, url);
    102     }
    103     for (std::map<net::URLRequest*, Request*>::const_iterator it =
    104          prefetcher_->inflight_requests_.begin();
    105          it != prefetcher_->inflight_requests_.end(); ++it) {
    106       if (it->first->original_url() == url)
    107         return it->first;
    108     }
    109     EXPECT_TRUE(false) << "Infligh request not found: " << url_str;
    110     return NULL;
    111   }
    112 
    113 
    114   void OnReceivedRedirect(const std::string& url) {
    115     prefetcher_->OnReceivedRedirect(
    116         GetInFlightRequest(url), net::RedirectInfo(), NULL);
    117   }
    118   void OnAuthRequired(const std::string& url) {
    119     prefetcher_->OnAuthRequired(GetInFlightRequest(url), NULL);
    120   }
    121   void OnCertificateRequested(const std::string& url) {
    122     prefetcher_->OnCertificateRequested(GetInFlightRequest(url), NULL);
    123   }
    124   void OnSSLCertificateError(const std::string& url) {
    125     prefetcher_->OnSSLCertificateError(GetInFlightRequest(url),
    126                                        net::SSLInfo(), false);
    127   }
    128   void OnResponse(const std::string& url) {
    129     prefetcher_->OnResponseStarted(GetInFlightRequest(url));
    130   }
    131 
    132   base::MessageLoop loop_;
    133   content::TestBrowserThread io_thread_;
    134   ResourcePrefetchPredictorConfig config_;
    135   TestResourcePrefetcherDelegate prefetcher_delegate_;
    136   scoped_ptr<TestResourcePrefetcher> prefetcher_;
    137 
    138  private:
    139   DISALLOW_COPY_AND_ASSIGN(ResourcePrefetcherTest);
    140 };
    141 
    142 ResourcePrefetcherTest::ResourcePrefetcherTest()
    143     : loop_(base::MessageLoop::TYPE_IO),
    144       io_thread_(content::BrowserThread::IO, &loop_),
    145       prefetcher_delegate_(&loop_) {
    146   config_.max_prefetches_inflight_per_navigation = 5;
    147   config_.max_prefetches_inflight_per_host_per_navigation = 2;
    148 }
    149 
    150 ResourcePrefetcherTest::~ResourcePrefetcherTest() {
    151 }
    152 
    153 TEST_F(ResourcePrefetcherTest, TestPrefetcherFinishes) {
    154   scoped_ptr<ResourcePrefetcher::RequestVector> requests(
    155       new ResourcePrefetcher::RequestVector);
    156   requests->push_back(new ResourcePrefetcher::Request(GURL(
    157       "http://www.google.com/resource1.html")));
    158   requests->push_back(new ResourcePrefetcher::Request(GURL(
    159       "http://www.google.com/resource2.png")));
    160   requests->push_back(new ResourcePrefetcher::Request(GURL(
    161       "http://yahoo.com/resource1.png")));
    162   requests->push_back(new ResourcePrefetcher::Request(GURL(
    163       "http://yahoo.com/resource2.png")));
    164   requests->push_back(new ResourcePrefetcher::Request(GURL(
    165       "http://yahoo.com/resource3.png")));
    166   requests->push_back(new ResourcePrefetcher::Request(GURL(
    167       "http://m.google.com/resource1.jpg")));
    168   requests->push_back(new ResourcePrefetcher::Request(GURL(
    169       "http://www.google.com/resource3.html")));
    170   requests->push_back(new ResourcePrefetcher::Request(GURL(
    171       "http://m.google.com/resource2.html")));
    172   requests->push_back(new ResourcePrefetcher::Request(GURL(
    173       "http://m.google.com/resource3.css")));
    174   requests->push_back(new ResourcePrefetcher::Request(GURL(
    175       "http://m.google.com/resource4.png")));
    176   requests->push_back(new ResourcePrefetcher::Request(GURL(
    177       "http://yahoo.com/resource4.png")));
    178   requests->push_back(new ResourcePrefetcher::Request(GURL(
    179       "http://yahoo.com/resource5.png")));
    180 
    181   NavigationID navigation_id;
    182   navigation_id.render_process_id = 1;
    183   navigation_id.render_frame_id = 2;
    184   navigation_id.main_frame_url = GURL("http://www.google.com");
    185 
    186   // Needed later for comparison.
    187   ResourcePrefetcher::RequestVector* requests_ptr = requests.get();
    188 
    189   prefetcher_.reset(new TestResourcePrefetcher(&prefetcher_delegate_,
    190                                                config_,
    191                                                navigation_id,
    192                                                PREFETCH_KEY_TYPE_URL,
    193                                                requests.Pass()));
    194 
    195   // Starting the prefetcher maxes out the number of possible requests.
    196   AddStartUrlRequestExpectation("http://www.google.com/resource1.html");
    197   AddStartUrlRequestExpectation("http://www.google.com/resource2.png");
    198   AddStartUrlRequestExpectation("http://yahoo.com/resource1.png");
    199   AddStartUrlRequestExpectation("http://yahoo.com/resource2.png");
    200   AddStartUrlRequestExpectation("http://m.google.com/resource1.jpg");
    201 
    202   prefetcher_->Start();
    203   CheckPrefetcherState(5, 7, 3);
    204 
    205   AddStartUrlRequestExpectation("http://m.google.com/resource2.html");
    206   OnResponse("http://m.google.com/resource1.jpg");
    207   CheckPrefetcherState(5, 6, 3);
    208 
    209   AddStartUrlRequestExpectation("http://www.google.com/resource3.html");
    210   OnSSLCertificateError("http://www.google.com/resource1.html");
    211   CheckPrefetcherState(5, 5, 3);
    212 
    213   AddStartUrlRequestExpectation("http://m.google.com/resource3.css");
    214   OnResponse("http://m.google.com/resource2.html");
    215   CheckPrefetcherState(5, 4, 3);
    216 
    217   AddStartUrlRequestExpectation("http://m.google.com/resource4.png");
    218   OnReceivedRedirect("http://www.google.com/resource3.html");
    219   CheckPrefetcherState(5, 3, 3);
    220 
    221   OnResponse("http://www.google.com/resource2.png");
    222   CheckPrefetcherState(4, 3, 2);
    223 
    224   AddStartUrlRequestExpectation("http://yahoo.com/resource3.png");
    225   OnReceivedRedirect("http://yahoo.com/resource2.png");
    226   CheckPrefetcherState(4, 2, 2);
    227 
    228   AddStartUrlRequestExpectation("http://yahoo.com/resource4.png");
    229   OnResponse("http://yahoo.com/resource1.png");
    230   CheckPrefetcherState(4, 1, 2);
    231 
    232   AddStartUrlRequestExpectation("http://yahoo.com/resource5.png");
    233   OnResponse("http://yahoo.com/resource4.png");
    234   CheckPrefetcherState(4, 0, 2);
    235 
    236   OnResponse("http://yahoo.com/resource5.png");
    237   CheckPrefetcherState(3, 0, 2);
    238 
    239   OnCertificateRequested("http://m.google.com/resource4.png");
    240   CheckPrefetcherState(2, 0, 2);
    241 
    242   OnAuthRequired("http://m.google.com/resource3.css");
    243   CheckPrefetcherState(1, 0, 1);
    244 
    245   // Expect the final call.
    246   EXPECT_CALL(prefetcher_delegate_,
    247               ResourcePrefetcherFinished(Eq(prefetcher_.get()),
    248                                          Eq(requests_ptr)));
    249 
    250   OnResponse("http://yahoo.com/resource3.png");
    251   CheckPrefetcherState(0, 0, 0);
    252 
    253   // Check the prefetch status.
    254   EXPECT_EQ(Request::PREFETCH_STATUS_CERT_ERROR,
    255             (*requests_ptr)[0]->prefetch_status);
    256   EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE,
    257             (*requests_ptr)[1]->prefetch_status);
    258   EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE,
    259             (*requests_ptr)[2]->prefetch_status);
    260   EXPECT_EQ(Request::PREFETCH_STATUS_REDIRECTED,
    261             (*requests_ptr)[3]->prefetch_status);
    262   EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE,
    263             (*requests_ptr)[4]->prefetch_status);
    264   EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE,
    265             (*requests_ptr)[5]->prefetch_status);
    266   EXPECT_EQ(Request::PREFETCH_STATUS_REDIRECTED,
    267             (*requests_ptr)[6]->prefetch_status);
    268   EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE,
    269             (*requests_ptr)[7]->prefetch_status);
    270   EXPECT_EQ(Request::PREFETCH_STATUS_AUTH_REQUIRED,
    271             (*requests_ptr)[8]->prefetch_status);
    272   EXPECT_EQ(Request::PREFETCH_STATUS_CERT_REQUIRED,
    273             (*requests_ptr)[9]->prefetch_status);
    274   EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE,
    275             (*requests_ptr)[10]->prefetch_status);
    276   EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE,
    277             (*requests_ptr)[11]->prefetch_status);
    278 
    279   // We need to delete requests_ptr here, though it looks to be managed by the
    280   // scoped_ptr requests. The scoped_ptr requests releases itself and the raw
    281   // pointer requests_ptr is passed to ResourcePrefetcherFinished(). In the
    282   // test, ResourcePrefetcherFinished() is a mock function and does not handle
    283   // the raw pointer properly. In the real code, requests_ptr will eventually be
    284   // passed to and managed by ResourcePrefetchPredictor::Result::Result.
    285   delete requests_ptr;
    286 }
    287 
    288 TEST_F(ResourcePrefetcherTest, TestPrefetcherStopped) {
    289   scoped_ptr<ResourcePrefetcher::RequestVector> requests(
    290       new ResourcePrefetcher::RequestVector);
    291   requests->push_back(new ResourcePrefetcher::Request(GURL(
    292       "http://www.google.com/resource1.html")));
    293   requests->push_back(new ResourcePrefetcher::Request(GURL(
    294       "http://www.google.com/resource2.png")));
    295   requests->push_back(new ResourcePrefetcher::Request(GURL(
    296       "http://yahoo.com/resource1.png")));
    297   requests->push_back(new ResourcePrefetcher::Request(GURL(
    298       "http://yahoo.com/resource2.png")));
    299   requests->push_back(new ResourcePrefetcher::Request(GURL(
    300       "http://yahoo.com/resource3.png")));
    301   requests->push_back(new ResourcePrefetcher::Request(GURL(
    302       "http://m.google.com/resource1.jpg")));
    303 
    304   NavigationID navigation_id;
    305   navigation_id.render_process_id = 1;
    306   navigation_id.render_frame_id = 2;
    307   navigation_id.main_frame_url = GURL("http://www.google.com");
    308 
    309   // Needed later for comparison.
    310   ResourcePrefetcher::RequestVector* requests_ptr = requests.get();
    311 
    312   prefetcher_.reset(new TestResourcePrefetcher(&prefetcher_delegate_,
    313                                                config_,
    314                                                navigation_id,
    315                                                PREFETCH_KEY_TYPE_HOST,
    316                                                requests.Pass()));
    317 
    318   // Starting the prefetcher maxes out the number of possible requests.
    319   AddStartUrlRequestExpectation("http://www.google.com/resource1.html");
    320   AddStartUrlRequestExpectation("http://www.google.com/resource2.png");
    321   AddStartUrlRequestExpectation("http://yahoo.com/resource1.png");
    322   AddStartUrlRequestExpectation("http://yahoo.com/resource2.png");
    323   AddStartUrlRequestExpectation("http://m.google.com/resource1.jpg");
    324 
    325   prefetcher_->Start();
    326   CheckPrefetcherState(5, 1, 3);
    327 
    328   OnResponse("http://www.google.com/resource1.html");
    329   CheckPrefetcherState(4, 1, 3);
    330 
    331   prefetcher_->Stop();  // No more queueing.
    332 
    333   OnResponse("http://www.google.com/resource2.png");
    334   CheckPrefetcherState(3, 1, 2);
    335 
    336   OnResponse("http://yahoo.com/resource1.png");
    337   CheckPrefetcherState(2, 1, 2);
    338 
    339   OnResponse("http://yahoo.com/resource2.png");
    340   CheckPrefetcherState(1, 1, 1);
    341 
    342   // Expect the final call.
    343   EXPECT_CALL(prefetcher_delegate_,
    344               ResourcePrefetcherFinished(Eq(prefetcher_.get()),
    345                                          Eq(requests_ptr)));
    346 
    347   OnResponse("http://m.google.com/resource1.jpg");
    348   CheckPrefetcherState(0, 1, 0);
    349 
    350   // We need to delete requests_ptr here, though it looks to be managed by the
    351   // scoped_ptr requests. The scoped_ptr requests releases itself and the raw
    352   // pointer requests_ptr is passed to ResourcePrefetcherFinished(). In the
    353   // test, ResourcePrefetcherFinished() is a mock function and does not handle
    354   // the raw pointer properly. In the real code, requests_ptr will eventually be
    355   // passed to and managed by ResourcePrefetchPredictor::Result::Result.
    356   delete requests_ptr;
    357 }
    358 
    359 }  // namespace predictors
    360