Home | History | Annotate | Download | only in net
      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 <time.h>
      6 
      7 #include <algorithm>
      8 #include <sstream>
      9 #include <string>
     10 
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/timer/timer.h"
     15 #include "base/values.h"
     16 #include "chrome/browser/net/predictor.h"
     17 #include "chrome/browser/net/spdyproxy/proxy_advisor.h"
     18 #include "chrome/browser/net/url_info.h"
     19 #include "chrome/common/net/predictor_common.h"
     20 #include "content/public/test/test_browser_thread.h"
     21 #include "net/base/address_list.h"
     22 #include "net/base/winsock_init.h"
     23 #include "net/dns/mock_host_resolver.h"
     24 #include "net/http/transport_security_state.h"
     25 #include "testing/gmock/include/gmock/gmock.h"
     26 #include "testing/gtest/include/gtest/gtest.h"
     27 
     28 using base::Time;
     29 using base::TimeDelta;
     30 using content::BrowserThread;
     31 
     32 namespace chrome_browser_net {
     33 
     34 class WaitForResolutionHelper;
     35 
     36 typedef base::RepeatingTimer<WaitForResolutionHelper> HelperTimer;
     37 
     38 class WaitForResolutionHelper {
     39  public:
     40   WaitForResolutionHelper(Predictor* predictor, const UrlList& hosts,
     41                           HelperTimer* timer, int checks_until_quit)
     42       : predictor_(predictor),
     43         hosts_(hosts),
     44         timer_(timer),
     45         checks_until_quit_(checks_until_quit) {
     46   }
     47 
     48   void CheckIfResolutionsDone() {
     49     if (--checks_until_quit_ > 0) {
     50       for (UrlList::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i)
     51         if (predictor_->GetResolutionDuration(*i) ==
     52             UrlInfo::NullDuration())
     53           return;  // We don't have resolution for that host.
     54     }
     55 
     56     // When all hostnames have been resolved, or we've hit the limit,
     57     // exit the loop.
     58     timer_->Stop();
     59     base::MessageLoop::current()->Quit();
     60     delete timer_;
     61     delete this;
     62   }
     63 
     64  private:
     65   Predictor* predictor_;
     66   const UrlList hosts_;
     67   HelperTimer* timer_;
     68   int checks_until_quit_;
     69 };
     70 
     71 class PredictorTest : public testing::Test {
     72  public:
     73   PredictorTest()
     74       : ui_thread_(BrowserThread::UI, &loop_),
     75         io_thread_(BrowserThread::IO, &loop_),
     76         host_resolver_(new net::MockCachingHostResolver()) {
     77   }
     78 
     79  protected:
     80   virtual void SetUp() {
     81 #if defined(OS_WIN)
     82     net::EnsureWinsockInit();
     83 #endif
     84     Predictor::set_max_parallel_resolves(
     85         Predictor::kMaxSpeculativeParallelResolves);
     86     Predictor::set_max_queueing_delay(
     87         Predictor::kMaxSpeculativeResolveQueueDelayMs);
     88     // Since we are using a caching HostResolver, the following latencies will
     89     // only be incurred by the first request, after which the result will be
     90     // cached internally by |host_resolver_|.
     91     net::RuleBasedHostResolverProc* rules = host_resolver_->rules();
     92     rules->AddRuleWithLatency("www.google.com", "127.0.0.1", 50);
     93     rules->AddRuleWithLatency("gmail.google.com.com", "127.0.0.1", 70);
     94     rules->AddRuleWithLatency("mail.google.com", "127.0.0.1", 44);
     95     rules->AddRuleWithLatency("gmail.com", "127.0.0.1", 63);
     96   }
     97 
     98   void WaitForResolution(Predictor* predictor, const UrlList& hosts) {
     99     HelperTimer* timer = new HelperTimer();
    100     // By default allow the loop to run for a minute -- 600 iterations.
    101     timer->Start(FROM_HERE, TimeDelta::FromMilliseconds(100),
    102                  new WaitForResolutionHelper(predictor, hosts, timer, 600),
    103                  &WaitForResolutionHelper::CheckIfResolutionsDone);
    104     base::MessageLoop::current()->Run();
    105   }
    106 
    107   void WaitForResolutionWithLimit(
    108       Predictor* predictor, const UrlList& hosts, int limit) {
    109     HelperTimer* timer = new HelperTimer();
    110     timer->Start(FROM_HERE, TimeDelta::FromMilliseconds(100),
    111                  new WaitForResolutionHelper(predictor, hosts, timer, limit),
    112                  &WaitForResolutionHelper::CheckIfResolutionsDone);
    113     base::MessageLoop::current()->Run();
    114   }
    115 
    116  private:
    117   // IMPORTANT: do not move this below |host_resolver_|; the host resolver
    118   // must not outlive the message loop, otherwise bad things can happen
    119   // (like posting to a deleted message loop).
    120   base::MessageLoopForUI loop_;
    121   content::TestBrowserThread ui_thread_;
    122   content::TestBrowserThread io_thread_;
    123 
    124  protected:
    125   scoped_ptr<net::MockCachingHostResolver> host_resolver_;
    126 };
    127 
    128 //------------------------------------------------------------------------------
    129 
    130 TEST_F(PredictorTest, StartupShutdownTest) {
    131   Predictor testing_master(true);
    132   testing_master.Shutdown();
    133 }
    134 
    135 
    136 TEST_F(PredictorTest, ShutdownWhenResolutionIsPendingTest) {
    137   scoped_ptr<net::HostResolver> host_resolver(new net::HangingHostResolver());
    138 
    139   Predictor testing_master(true);
    140   testing_master.SetHostResolver(host_resolver.get());
    141 
    142   GURL localhost("http://localhost:80");
    143   UrlList names;
    144   names.push_back(localhost);
    145 
    146   testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
    147 
    148   base::MessageLoop::current()->PostDelayedTask(
    149       FROM_HERE,
    150       base::MessageLoop::QuitClosure(),
    151       base::TimeDelta::FromMilliseconds(500));
    152   base::MessageLoop::current()->Run();
    153 
    154   EXPECT_FALSE(testing_master.WasFound(localhost));
    155 
    156   testing_master.Shutdown();
    157 
    158   // Clean up after ourselves.
    159   base::MessageLoop::current()->RunUntilIdle();
    160 }
    161 
    162 TEST_F(PredictorTest, SingleLookupTest) {
    163   Predictor testing_master(true);
    164   testing_master.SetHostResolver(host_resolver_.get());
    165 
    166   GURL goog("http://www.google.com:80");
    167 
    168   UrlList names;
    169   names.push_back(goog);
    170 
    171   // Try to flood the predictor with many concurrent requests.
    172   for (int i = 0; i < 10; i++)
    173     testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
    174 
    175   WaitForResolution(&testing_master, names);
    176 
    177   EXPECT_TRUE(testing_master.WasFound(goog));
    178 
    179   base::MessageLoop::current()->RunUntilIdle();
    180 
    181   EXPECT_GT(testing_master.peak_pending_lookups(), names.size() / 2);
    182   EXPECT_LE(testing_master.peak_pending_lookups(), names.size());
    183   EXPECT_LE(testing_master.peak_pending_lookups(),
    184             testing_master.max_concurrent_dns_lookups());
    185 
    186   testing_master.Shutdown();
    187 }
    188 
    189 TEST_F(PredictorTest, ConcurrentLookupTest) {
    190   host_resolver_->rules()->AddSimulatedFailure("*.notfound");
    191 
    192   Predictor testing_master(true);
    193   testing_master.SetHostResolver(host_resolver_.get());
    194 
    195   GURL goog("http://www.google.com:80"),
    196       goog2("http://gmail.google.com.com:80"),
    197       goog3("http://mail.google.com:80"),
    198       goog4("http://gmail.com:80");
    199   GURL bad1("http://bad1.notfound:80"),
    200       bad2("http://bad2.notfound:80");
    201 
    202   UrlList names;
    203   names.push_back(goog);
    204   names.push_back(goog3);
    205   names.push_back(bad1);
    206   names.push_back(goog2);
    207   names.push_back(bad2);
    208   names.push_back(goog4);
    209   names.push_back(goog);
    210 
    211   // Try to flood the predictor with many concurrent requests.
    212   for (int i = 0; i < 10; i++)
    213     testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
    214 
    215   WaitForResolution(&testing_master, names);
    216 
    217   EXPECT_TRUE(testing_master.WasFound(goog));
    218   EXPECT_TRUE(testing_master.WasFound(goog3));
    219   EXPECT_TRUE(testing_master.WasFound(goog2));
    220   EXPECT_TRUE(testing_master.WasFound(goog4));
    221   EXPECT_FALSE(testing_master.WasFound(bad1));
    222   EXPECT_FALSE(testing_master.WasFound(bad2));
    223 
    224   base::MessageLoop::current()->RunUntilIdle();
    225 
    226   EXPECT_FALSE(testing_master.WasFound(bad1));
    227   EXPECT_FALSE(testing_master.WasFound(bad2));
    228 
    229   EXPECT_LE(testing_master.peak_pending_lookups(), names.size());
    230   EXPECT_LE(testing_master.peak_pending_lookups(),
    231             testing_master.max_concurrent_dns_lookups());
    232 
    233   testing_master.Shutdown();
    234 }
    235 
    236 TEST_F(PredictorTest, MassiveConcurrentLookupTest) {
    237   host_resolver_->rules()->AddSimulatedFailure("*.notfound");
    238 
    239   Predictor testing_master(true);
    240   testing_master.SetHostResolver(host_resolver_.get());
    241 
    242   UrlList names;
    243   for (int i = 0; i < 100; i++)
    244     names.push_back(GURL(
    245         "http://host" + base::IntToString(i) + ".notfound:80"));
    246 
    247   // Try to flood the predictor with many concurrent requests.
    248   for (int i = 0; i < 10; i++)
    249     testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
    250 
    251   WaitForResolution(&testing_master, names);
    252 
    253   base::MessageLoop::current()->RunUntilIdle();
    254 
    255   EXPECT_LE(testing_master.peak_pending_lookups(), names.size());
    256   EXPECT_LE(testing_master.peak_pending_lookups(),
    257             testing_master.max_concurrent_dns_lookups());
    258 
    259   testing_master.Shutdown();
    260 }
    261 
    262 //------------------------------------------------------------------------------
    263 // Functions to help synthesize and test serializations of subresource referrer
    264 // lists.
    265 
    266 // Return a motivation_list if we can find one for the given motivating_host (or
    267 // NULL if a match is not found).
    268 static const base::ListValue* FindSerializationMotivation(
    269     const GURL& motivation,
    270     const base::ListValue* referral_list) {
    271   CHECK_LT(0u, referral_list->GetSize());  // Room for version.
    272   int format_version = -1;
    273   CHECK(referral_list->GetInteger(0, &format_version));
    274   CHECK_EQ(Predictor::kPredictorReferrerVersion, format_version);
    275   const base::ListValue* motivation_list(NULL);
    276   for (size_t i = 1; i < referral_list->GetSize(); ++i) {
    277     referral_list->GetList(i, &motivation_list);
    278     std::string existing_spec;
    279     EXPECT_TRUE(motivation_list->GetString(0, &existing_spec));
    280     if (motivation == GURL(existing_spec))
    281       return motivation_list;
    282   }
    283   return NULL;
    284 }
    285 
    286 static base::ListValue* FindSerializationMotivation(
    287     const GURL& motivation,
    288     base::ListValue* referral_list) {
    289   return const_cast<base::ListValue*>(FindSerializationMotivation(
    290       motivation, static_cast<const base::ListValue*>(referral_list)));
    291 }
    292 
    293 // Create a new empty serialization list.
    294 static base::ListValue* NewEmptySerializationList() {
    295   base::ListValue* list = new base::ListValue;
    296   list->Append(
    297       new base::FundamentalValue(Predictor::kPredictorReferrerVersion));
    298   return list;
    299 }
    300 
    301 // Add a motivating_url and a subresource_url to a serialized list, using
    302 // this given latency. This is a helper function for quickly building these
    303 // lists.
    304 static void AddToSerializedList(const GURL& motivation,
    305                                 const GURL& subresource,
    306                                 double use_rate,
    307                                 base::ListValue* referral_list) {
    308   // Find the motivation if it is already used.
    309   base::ListValue* motivation_list = FindSerializationMotivation(motivation,
    310                                                            referral_list);
    311   if (!motivation_list) {
    312     // This is the first mention of this motivation, so build a list.
    313     motivation_list = new base::ListValue;
    314     motivation_list->Append(new base::StringValue(motivation.spec()));
    315     // Provide empty subresource list.
    316     motivation_list->Append(new base::ListValue());
    317 
    318     // ...and make it part of the serialized referral_list.
    319     referral_list->Append(motivation_list);
    320   }
    321 
    322   base::ListValue* subresource_list(NULL);
    323   // 0 == url; 1 == subresource_list.
    324   EXPECT_TRUE(motivation_list->GetList(1, &subresource_list));
    325 
    326   // We won't bother to check for the subresource being there already.  Worst
    327   // case, during deserialization, the latency value we supply plus the
    328   // existing value(s) will be added to the referrer.
    329 
    330   subresource_list->Append(new base::StringValue(subresource.spec()));
    331   subresource_list->Append(new base::FundamentalValue(use_rate));
    332 }
    333 
    334 // For a given motivation, and subresource, find what latency is currently
    335 // listed.  This assume a well formed serialization, which has at most one such
    336 // entry for any pair of names.  If no such pair is found, then return false.
    337 // Data is written into use_rate arguments.
    338 static bool GetDataFromSerialization(const GURL& motivation,
    339                                      const GURL& subresource,
    340                                      const base::ListValue& referral_list,
    341                                      double* use_rate) {
    342   const base::ListValue* motivation_list =
    343       FindSerializationMotivation(motivation, &referral_list);
    344   if (!motivation_list)
    345     return false;
    346   const base::ListValue* subresource_list;
    347   EXPECT_TRUE(motivation_list->GetList(1, &subresource_list));
    348   for (size_t i = 0; i < subresource_list->GetSize();) {
    349     std::string url_spec;
    350     EXPECT_TRUE(subresource_list->GetString(i++, &url_spec));
    351     EXPECT_TRUE(subresource_list->GetDouble(i++, use_rate));
    352     if (subresource == GURL(url_spec)) {
    353       return true;
    354     }
    355   }
    356   return false;
    357 }
    358 
    359 //------------------------------------------------------------------------------
    360 
    361 // Make sure nil referral lists really have no entries, and no latency listed.
    362 TEST_F(PredictorTest, ReferrerSerializationNilTest) {
    363   Predictor predictor(true);
    364   predictor.SetHostResolver(host_resolver_.get());
    365 
    366   scoped_ptr<base::ListValue> referral_list(NewEmptySerializationList());
    367   predictor.SerializeReferrers(referral_list.get());
    368   EXPECT_EQ(1U, referral_list->GetSize());
    369   EXPECT_FALSE(GetDataFromSerialization(
    370     GURL("http://a.com:79"), GURL("http://b.com:78"),
    371       *referral_list.get(), NULL));
    372 
    373   predictor.Shutdown();
    374 }
    375 
    376 // Make sure that when a serialization list includes a value, that it can be
    377 // deserialized into the database, and can be extracted back out via
    378 // serialization without being changed.
    379 TEST_F(PredictorTest, ReferrerSerializationSingleReferrerTest) {
    380   Predictor predictor(true);
    381   predictor.SetHostResolver(host_resolver_.get());
    382   const GURL motivation_url("http://www.google.com:91");
    383   const GURL subresource_url("http://icons.google.com:90");
    384   const double kUseRate = 23.4;
    385   scoped_ptr<base::ListValue> referral_list(NewEmptySerializationList());
    386 
    387   AddToSerializedList(motivation_url, subresource_url,
    388       kUseRate, referral_list.get());
    389 
    390   predictor.DeserializeReferrers(*referral_list.get());
    391 
    392   base::ListValue recovered_referral_list;
    393   predictor.SerializeReferrers(&recovered_referral_list);
    394   EXPECT_EQ(2U, recovered_referral_list.GetSize());
    395   double rate;
    396   EXPECT_TRUE(GetDataFromSerialization(
    397       motivation_url, subresource_url, recovered_referral_list, &rate));
    398   EXPECT_EQ(rate, kUseRate);
    399 
    400   predictor.Shutdown();
    401 }
    402 
    403 // Check that GetHtmlReferrerLists() doesn't crash when given duplicated
    404 // domains for referring URL, and that it sorts the results in the
    405 // correct order.
    406 TEST_F(PredictorTest, GetHtmlReferrerLists) {
    407   Predictor predictor(true);
    408   predictor.SetHostResolver(host_resolver_.get());
    409   const double kUseRate = 23.4;
    410   scoped_ptr<base::ListValue> referral_list(NewEmptySerializationList());
    411 
    412   AddToSerializedList(
    413       GURL("http://d.google.com/x1"),
    414       GURL("http://foo.com/"),
    415       kUseRate, referral_list.get());
    416 
    417   // Duplicated hostname (d.google.com). This should not cause any crashes
    418   // (i.e. crbug.com/116345)
    419   AddToSerializedList(
    420       GURL("http://d.google.com/x2"),
    421       GURL("http://foo.com/"),
    422       kUseRate, referral_list.get());
    423 
    424   AddToSerializedList(
    425       GURL("http://a.yahoo.com/y"),
    426       GURL("http://foo1.com/"),
    427       kUseRate, referral_list.get());
    428 
    429   AddToSerializedList(
    430       GURL("http://b.google.com/x3"),
    431       GURL("http://foo2.com/"),
    432       kUseRate, referral_list.get());
    433 
    434   AddToSerializedList(
    435       GURL("http://d.yahoo.com/x5"),
    436       GURL("http://i.like.turtles/"),
    437       kUseRate, referral_list.get());
    438 
    439   AddToSerializedList(
    440       GURL("http://c.yahoo.com/x4"),
    441       GURL("http://foo3.com/"),
    442       kUseRate, referral_list.get());
    443 
    444   predictor.DeserializeReferrers(*referral_list.get());
    445 
    446   std::string html;
    447   predictor.GetHtmlReferrerLists(&html);
    448 
    449   // The lexicographic sorting of hostnames would be:
    450   //   a.yahoo.com
    451   //   b.google.com
    452   //   c.yahoo.com
    453   //   d.google.com
    454   //   d.yahoo.com
    455   //
    456   // However we expect to sort them by domain in the output:
    457   //   b.google.com
    458   //   d.google.com
    459   //   a.yahoo.com
    460   //   c.yahoo.com
    461   //   d.yahoo.com
    462 
    463   size_t pos[] = {
    464       html.find("<td rowspan=1>http://b.google.com/x3"),
    465       html.find("<td rowspan=1>http://d.google.com/x1"),
    466       html.find("<td rowspan=1>http://d.google.com/x2"),
    467       html.find("<td rowspan=1>http://a.yahoo.com/y"),
    468       html.find("<td rowspan=1>http://c.yahoo.com/x4"),
    469       html.find("<td rowspan=1>http://d.yahoo.com/x5"),
    470   };
    471 
    472   // Make sure things appeared in the expected order.
    473   for (size_t i = 1; i < arraysize(pos); ++i) {
    474     EXPECT_LT(pos[i - 1], pos[i]) << "Mismatch for pos[" << i << "]";
    475   }
    476 
    477   predictor.Shutdown();
    478 }
    479 
    480 // Verify that two floats are within 1% of each other in value.
    481 #define EXPECT_SIMILAR(a, b) do { \
    482     double espilon_ratio = 1.01;  \
    483     if ((a) < 0.)  \
    484       espilon_ratio = 1 / espilon_ratio;  \
    485     EXPECT_LT(a, espilon_ratio * (b));   \
    486     EXPECT_GT((a) * espilon_ratio, b);   \
    487     } while (0)
    488 
    489 
    490 // Make sure the Trim() functionality works as expected.
    491 TEST_F(PredictorTest, ReferrerSerializationTrimTest) {
    492   Predictor predictor(true);
    493   predictor.SetHostResolver(host_resolver_.get());
    494   GURL motivation_url("http://www.google.com:110");
    495 
    496   GURL icon_subresource_url("http://icons.google.com:111");
    497   const double kRateIcon = 16.0 * Predictor::kDiscardableExpectedValue;
    498   GURL img_subresource_url("http://img.google.com:118");
    499   const double kRateImg = 8.0 * Predictor::kDiscardableExpectedValue;
    500 
    501   scoped_ptr<base::ListValue> referral_list(NewEmptySerializationList());
    502   AddToSerializedList(
    503       motivation_url, icon_subresource_url, kRateIcon, referral_list.get());
    504   AddToSerializedList(
    505       motivation_url, img_subresource_url, kRateImg, referral_list.get());
    506 
    507   predictor.DeserializeReferrers(*referral_list.get());
    508 
    509   base::ListValue recovered_referral_list;
    510   predictor.SerializeReferrers(&recovered_referral_list);
    511   EXPECT_EQ(2U, recovered_referral_list.GetSize());
    512   double rate;
    513   EXPECT_TRUE(GetDataFromSerialization(
    514       motivation_url, icon_subresource_url, recovered_referral_list,
    515       &rate));
    516   EXPECT_SIMILAR(rate, kRateIcon);
    517 
    518   EXPECT_TRUE(GetDataFromSerialization(
    519       motivation_url, img_subresource_url, recovered_referral_list, &rate));
    520   EXPECT_SIMILAR(rate, kRateImg);
    521 
    522   // Each time we Trim 24 times, the user_rate figures should reduce by a factor
    523   // of two,  until they are small, and then a trim will delete the whole entry.
    524   for (int i = 0; i < 24; ++i)
    525     predictor.TrimReferrersNow();
    526   predictor.SerializeReferrers(&recovered_referral_list);
    527   EXPECT_EQ(2U, recovered_referral_list.GetSize());
    528   EXPECT_TRUE(GetDataFromSerialization(
    529       motivation_url, icon_subresource_url, recovered_referral_list, &rate));
    530   EXPECT_SIMILAR(rate, kRateIcon / 2);
    531 
    532   EXPECT_TRUE(GetDataFromSerialization(
    533       motivation_url, img_subresource_url, recovered_referral_list, &rate));
    534   EXPECT_SIMILAR(rate, kRateImg / 2);
    535 
    536   for (int i = 0; i < 24; ++i)
    537     predictor.TrimReferrersNow();
    538   predictor.SerializeReferrers(&recovered_referral_list);
    539   EXPECT_EQ(2U, recovered_referral_list.GetSize());
    540   EXPECT_TRUE(GetDataFromSerialization(
    541       motivation_url, icon_subresource_url, recovered_referral_list, &rate));
    542   EXPECT_SIMILAR(rate, kRateIcon / 4);
    543   EXPECT_TRUE(GetDataFromSerialization(
    544       motivation_url, img_subresource_url, recovered_referral_list, &rate));
    545   EXPECT_SIMILAR(rate, kRateImg / 4);
    546 
    547   for (int i = 0; i < 24; ++i)
    548     predictor.TrimReferrersNow();
    549   predictor.SerializeReferrers(&recovered_referral_list);
    550   EXPECT_EQ(2U, recovered_referral_list.GetSize());
    551   EXPECT_TRUE(GetDataFromSerialization(
    552       motivation_url, icon_subresource_url, recovered_referral_list, &rate));
    553   EXPECT_SIMILAR(rate, kRateIcon / 8);
    554 
    555   // Img is below threshold, and so it gets deleted.
    556   EXPECT_FALSE(GetDataFromSerialization(
    557       motivation_url, img_subresource_url, recovered_referral_list, &rate));
    558 
    559   for (int i = 0; i < 24; ++i)
    560     predictor.TrimReferrersNow();
    561   predictor.SerializeReferrers(&recovered_referral_list);
    562   // Icon is also trimmed away, so entire set gets discarded.
    563   EXPECT_EQ(1U, recovered_referral_list.GetSize());
    564   EXPECT_FALSE(GetDataFromSerialization(
    565       motivation_url, icon_subresource_url, recovered_referral_list, &rate));
    566   EXPECT_FALSE(GetDataFromSerialization(
    567       motivation_url, img_subresource_url, recovered_referral_list, &rate));
    568 
    569   predictor.Shutdown();
    570 }
    571 
    572 
    573 TEST_F(PredictorTest, PriorityQueuePushPopTest) {
    574   Predictor::HostNameQueue queue;
    575 
    576   GURL first("http://first:80"), second("http://second:90");
    577 
    578   // First check high priority queue FIFO functionality.
    579   EXPECT_TRUE(queue.IsEmpty());
    580   queue.Push(first, UrlInfo::LEARNED_REFERAL_MOTIVATED);
    581   EXPECT_FALSE(queue.IsEmpty());
    582   queue.Push(second, UrlInfo::MOUSE_OVER_MOTIVATED);
    583   EXPECT_FALSE(queue.IsEmpty());
    584   EXPECT_EQ(queue.Pop(), first);
    585   EXPECT_FALSE(queue.IsEmpty());
    586   EXPECT_EQ(queue.Pop(), second);
    587   EXPECT_TRUE(queue.IsEmpty());
    588 
    589   // Then check low priority queue FIFO functionality.
    590   queue.Push(first, UrlInfo::PAGE_SCAN_MOTIVATED);
    591   EXPECT_FALSE(queue.IsEmpty());
    592   queue.Push(second, UrlInfo::OMNIBOX_MOTIVATED);
    593   EXPECT_FALSE(queue.IsEmpty());
    594   EXPECT_EQ(queue.Pop(), first);
    595   EXPECT_FALSE(queue.IsEmpty());
    596   EXPECT_EQ(queue.Pop(), second);
    597   EXPECT_TRUE(queue.IsEmpty());
    598 }
    599 
    600 TEST_F(PredictorTest, PriorityQueueReorderTest) {
    601   Predictor::HostNameQueue queue;
    602 
    603   // Push all the low priority items.
    604   GURL low1("http://low1:80"),
    605       low2("http://low2:80"),
    606       low3("http://low3:443"),
    607       low4("http://low4:80"),
    608       low5("http://low5:80"),
    609       hi1("http://hi1:80"),
    610       hi2("http://hi2:80"),
    611       hi3("http://hi3:80");
    612 
    613   EXPECT_TRUE(queue.IsEmpty());
    614   queue.Push(low1, UrlInfo::PAGE_SCAN_MOTIVATED);
    615   queue.Push(low2, UrlInfo::UNIT_TEST_MOTIVATED);
    616   queue.Push(low3, UrlInfo::LINKED_MAX_MOTIVATED);
    617   queue.Push(low4, UrlInfo::OMNIBOX_MOTIVATED);
    618   queue.Push(low5, UrlInfo::STARTUP_LIST_MOTIVATED);
    619   queue.Push(low4, UrlInfo::OMNIBOX_MOTIVATED);
    620 
    621   // Push all the high prority items
    622   queue.Push(hi1, UrlInfo::LEARNED_REFERAL_MOTIVATED);
    623   queue.Push(hi2, UrlInfo::STATIC_REFERAL_MOTIVATED);
    624   queue.Push(hi3, UrlInfo::MOUSE_OVER_MOTIVATED);
    625 
    626   // Check that high priority stuff comes out first, and in FIFO order.
    627   EXPECT_EQ(queue.Pop(), hi1);
    628   EXPECT_EQ(queue.Pop(), hi2);
    629   EXPECT_EQ(queue.Pop(), hi3);
    630 
    631   // ...and then low priority strings.
    632   EXPECT_EQ(queue.Pop(), low1);
    633   EXPECT_EQ(queue.Pop(), low2);
    634   EXPECT_EQ(queue.Pop(), low3);
    635   EXPECT_EQ(queue.Pop(), low4);
    636   EXPECT_EQ(queue.Pop(), low5);
    637   EXPECT_EQ(queue.Pop(), low4);
    638 
    639   EXPECT_TRUE(queue.IsEmpty());
    640 }
    641 
    642 TEST_F(PredictorTest, CanonicalizeUrl) {
    643   // Base case, only handles HTTP and HTTPS.
    644   EXPECT_EQ(GURL(), Predictor::CanonicalizeUrl(GURL("ftp://anything")));
    645 
    646   // Remove path testing.
    647   GURL long_url("http://host:999/path?query=value");
    648   EXPECT_EQ(Predictor::CanonicalizeUrl(long_url), long_url.GetWithEmptyPath());
    649 
    650   // Default port cannoncalization.
    651   GURL implied_port("http://test");
    652   GURL explicit_port("http://test:80");
    653   EXPECT_EQ(Predictor::CanonicalizeUrl(implied_port),
    654             Predictor::CanonicalizeUrl(explicit_port));
    655 
    656   // Port is still maintained.
    657   GURL port_80("http://test:80");
    658   GURL port_90("http://test:90");
    659   EXPECT_NE(Predictor::CanonicalizeUrl(port_80),
    660             Predictor::CanonicalizeUrl(port_90));
    661 
    662   // Host is still maintained.
    663   GURL host_1("http://test_1");
    664   GURL host_2("http://test_2");
    665   EXPECT_NE(Predictor::CanonicalizeUrl(host_1),
    666             Predictor::CanonicalizeUrl(host_2));
    667 
    668   // Scheme is maintained (mismatch identified).
    669   GURL http("http://test");
    670   GURL https("https://test");
    671   EXPECT_NE(Predictor::CanonicalizeUrl(http),
    672             Predictor::CanonicalizeUrl(https));
    673 
    674   // Https works fine.
    675   GURL long_https("https://host:999/path?query=value");
    676   EXPECT_EQ(Predictor::CanonicalizeUrl(long_https),
    677             long_https.GetWithEmptyPath());
    678 }
    679 
    680 TEST_F(PredictorTest, DiscardPredictorResults) {
    681   Predictor predictor(true);
    682   predictor.SetHostResolver(host_resolver_.get());
    683   base::ListValue referral_list;
    684   predictor.SerializeReferrers(&referral_list);
    685   EXPECT_EQ(1U, referral_list.GetSize());
    686 
    687   GURL host_1("http://test_1");
    688   GURL host_2("http://test_2");
    689   predictor.LearnFromNavigation(host_1, host_2);
    690 
    691   predictor.SerializeReferrers(&referral_list);
    692   EXPECT_EQ(2U, referral_list.GetSize());
    693 
    694   predictor.DiscardAllResults();
    695   predictor.SerializeReferrers(&referral_list);
    696   EXPECT_EQ(1U, referral_list.GetSize());
    697 
    698   predictor.Shutdown();
    699 }
    700 
    701 class TestPredictorObserver : public PredictorObserver {
    702  public:
    703   // PredictorObserver implementation:
    704   virtual void OnPreconnectUrl(const GURL& url,
    705                                const GURL& first_party_for_cookies,
    706                                UrlInfo::ResolutionMotivation motivation,
    707                                int count) OVERRIDE {
    708     preconnected_urls_.push_back(url);
    709   }
    710 
    711   std::vector<GURL> preconnected_urls_;
    712 };
    713 
    714 // Tests that preconnects apply the HSTS list.
    715 TEST_F(PredictorTest, HSTSRedirect) {
    716   const GURL kHttpUrl("http://example.com");
    717   const GURL kHttpsUrl("https://example.com");
    718 
    719   const base::Time expiry =
    720       base::Time::Now() + base::TimeDelta::FromSeconds(1000);
    721   net::TransportSecurityState state;
    722   state.AddHSTS(kHttpUrl.host(), expiry, false);
    723 
    724   Predictor predictor(true);
    725   TestPredictorObserver observer;
    726   predictor.SetObserver(&observer);
    727   predictor.SetTransportSecurityState(&state);
    728 
    729   predictor.PreconnectUrl(kHttpUrl, GURL(), UrlInfo::OMNIBOX_MOTIVATED, 2);
    730   ASSERT_EQ(1u, observer.preconnected_urls_.size());
    731   EXPECT_EQ(kHttpsUrl, observer.preconnected_urls_[0]);
    732 
    733   predictor.Shutdown();
    734 }
    735 
    736 // Tests that preconnecting a URL on the HSTS list preconnects the subresources
    737 // for the SSL version.
    738 TEST_F(PredictorTest, HSTSRedirectSubresources) {
    739   const GURL kHttpUrl("http://example.com");
    740   const GURL kHttpsUrl("https://example.com");
    741   const GURL kSubresourceUrl("https://images.example.com");
    742   const double kUseRate = 23.4;
    743 
    744   const base::Time expiry =
    745       base::Time::Now() + base::TimeDelta::FromSeconds(1000);
    746   net::TransportSecurityState state;
    747   state.AddHSTS(kHttpUrl.host(), expiry, false);
    748 
    749   Predictor predictor(true);
    750   TestPredictorObserver observer;
    751   predictor.SetObserver(&observer);
    752   predictor.SetTransportSecurityState(&state);
    753 
    754   scoped_ptr<base::ListValue> referral_list(NewEmptySerializationList());
    755   AddToSerializedList(
    756       kHttpsUrl, kSubresourceUrl, kUseRate, referral_list.get());
    757   predictor.DeserializeReferrers(*referral_list.get());
    758 
    759   predictor.PreconnectUrlAndSubresources(kHttpUrl, GURL());
    760   ASSERT_EQ(2u, observer.preconnected_urls_.size());
    761   EXPECT_EQ(kHttpsUrl, observer.preconnected_urls_[0]);
    762   EXPECT_EQ(kSubresourceUrl, observer.preconnected_urls_[1]);
    763 
    764   predictor.Shutdown();
    765 }
    766 
    767 #if defined(OS_ANDROID) || defined(OS_IOS)
    768 // Tests for the predictor with a proxy advisor
    769 
    770 class TestProxyAdvisor : public ProxyAdvisor {
    771  public:
    772   TestProxyAdvisor()
    773       : ProxyAdvisor(NULL, NULL),
    774         would_proxy_(false),
    775         advise_count_(0),
    776         would_proxy_count_(0) {
    777   }
    778 
    779   virtual ~TestProxyAdvisor() {}
    780 
    781   virtual void Advise(const GURL& url,
    782                       UrlInfo::ResolutionMotivation motivation,
    783                       bool is_preconnect) OVERRIDE {
    784     ++advise_count_;
    785   }
    786 
    787   virtual bool WouldProxyURL(const GURL& url) OVERRIDE {
    788     ++would_proxy_count_;
    789     return would_proxy_;
    790   }
    791 
    792   bool would_proxy_;
    793   int advise_count_;
    794   int would_proxy_count_;
    795 };
    796 
    797 TEST_F(PredictorTest, SingleLookupTestWithDisabledAdvisor) {
    798   Predictor testing_master(true);
    799   TestProxyAdvisor* advisor = new TestProxyAdvisor();
    800   testing_master.SetHostResolver(host_resolver_.get());
    801   testing_master.proxy_advisor_.reset(advisor);
    802 
    803   GURL goog("http://www.google.com:80");
    804 
    805   advisor->would_proxy_ = false;
    806 
    807   UrlList names;
    808   names.push_back(goog);
    809   testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
    810 
    811   WaitForResolution(&testing_master, names);
    812   EXPECT_TRUE(testing_master.WasFound(goog));
    813   EXPECT_EQ(advisor->would_proxy_count_, 1);
    814   EXPECT_EQ(advisor->advise_count_, 1);
    815 
    816   base::MessageLoop::current()->RunUntilIdle();
    817 
    818   testing_master.Shutdown();
    819 }
    820 
    821 TEST_F(PredictorTest, SingleLookupTestWithEnabledAdvisor) {
    822   Predictor testing_master(true);
    823   testing_master.SetHostResolver(host_resolver_.get());
    824   TestProxyAdvisor* advisor = new TestProxyAdvisor();
    825   testing_master.proxy_advisor_.reset(advisor);
    826 
    827   GURL goog("http://www.google.com:80");
    828 
    829   advisor->would_proxy_ = true;
    830 
    831   UrlList names;
    832   names.push_back(goog);
    833 
    834   testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
    835 
    836   // Attempt to resolve a few times.
    837   WaitForResolutionWithLimit(&testing_master, names, 10);
    838 
    839   // Because the advisor indicated that the url would be proxied,
    840   // no resolution should have occurred.
    841   EXPECT_FALSE(testing_master.WasFound(goog));
    842   EXPECT_EQ(advisor->would_proxy_count_, 1);
    843   EXPECT_EQ(advisor->advise_count_, 1);
    844 
    845   base::MessageLoop::current()->RunUntilIdle();
    846 
    847   testing_master.Shutdown();
    848 }
    849 
    850 TEST_F(PredictorTest, TestSimplePreconnectAdvisor) {
    851   Predictor testing_master(true);
    852   testing_master.SetHostResolver(host_resolver_.get());
    853   TestProxyAdvisor* advisor = new TestProxyAdvisor();
    854   testing_master.proxy_advisor_.reset(advisor);
    855 
    856   GURL goog("http://www.google.com:80");
    857 
    858   testing_master.PreconnectUrl(goog, goog, UrlInfo::OMNIBOX_MOTIVATED, 2);
    859 
    860   EXPECT_EQ(advisor->would_proxy_count_, 0);
    861   EXPECT_EQ(advisor->advise_count_, 1);
    862 
    863   testing_master.Shutdown();
    864 }
    865 
    866 #endif  // defined(OS_ANDROID) || defined(OS_IOS)
    867 
    868 }  // namespace chrome_browser_net
    869