1// Copyright (c) 2011 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.h"
13#include "base/string_number_conversions.h"
14#include "base/timer.h"
15#include "base/values.h"
16#include "chrome/browser/net/predictor_api.h"
17#include "chrome/browser/net/url_info.h"
18#include "chrome/common/net/predictor_common.h"
19#include "content/browser/browser_thread.h"
20#include "net/base/address_list.h"
21#include "net/base/mock_host_resolver.h"
22#include "net/base/winsock_init.h"
23#include "testing/gtest/include/gtest/gtest.h"
24
25using base::Time;
26using base::TimeDelta;
27
28namespace chrome_browser_net {
29
30class WaitForResolutionHelper;
31
32typedef base::RepeatingTimer<WaitForResolutionHelper> HelperTimer;
33
34class WaitForResolutionHelper {
35 public:
36  WaitForResolutionHelper(Predictor* predictor, const UrlList& hosts,
37                          HelperTimer* timer)
38      : predictor_(predictor),
39        hosts_(hosts),
40        timer_(timer) {
41  }
42
43  void Run() {
44    for (UrlList::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i)
45      if (predictor_->GetResolutionDuration(*i) ==
46          UrlInfo::kNullDuration)
47        return;  // We don't have resolution for that host.
48
49    // When all hostnames have been resolved, exit the loop.
50    timer_->Stop();
51    MessageLoop::current()->Quit();
52    delete timer_;
53    delete this;
54  }
55
56 private:
57  Predictor* predictor_;
58  const UrlList hosts_;
59  HelperTimer* timer_;
60};
61
62class PredictorTest : public testing::Test {
63 public:
64  PredictorTest()
65      : io_thread_(BrowserThread::IO, &loop_),
66        host_resolver_(new net::MockCachingHostResolver()),
67        default_max_queueing_delay_(TimeDelta::FromMilliseconds(
68            PredictorInit::kMaxSpeculativeResolveQueueDelayMs)) {
69  }
70
71 protected:
72  virtual void SetUp() {
73#if defined(OS_WIN)
74    net::EnsureWinsockInit();
75#endif
76    // Since we are using a caching HostResolver, the following latencies will
77    // only be incurred by the first request, after which the result will be
78    // cached internally by |host_resolver_|.
79    net::RuleBasedHostResolverProc* rules = host_resolver_->rules();
80    rules->AddRuleWithLatency("www.google.com", "127.0.0.1", 50);
81    rules->AddRuleWithLatency("gmail.google.com.com", "127.0.0.1", 70);
82    rules->AddRuleWithLatency("mail.google.com", "127.0.0.1", 44);
83    rules->AddRuleWithLatency("gmail.com", "127.0.0.1", 63);
84  }
85
86  void WaitForResolution(Predictor* predictor, const UrlList& hosts) {
87    HelperTimer* timer = new HelperTimer();
88    timer->Start(TimeDelta::FromMilliseconds(100),
89                 new WaitForResolutionHelper(predictor, hosts, timer),
90                 &WaitForResolutionHelper::Run);
91    MessageLoop::current()->Run();
92  }
93
94 private:
95  // IMPORTANT: do not move this below |host_resolver_|; the host resolver
96  // must not outlive the message loop, otherwise bad things can happen
97  // (like posting to a deleted message loop).
98  MessageLoop loop_;
99  BrowserThread io_thread_;
100
101 protected:
102  scoped_ptr<net::MockCachingHostResolver> host_resolver_;
103
104  // Shorthand to access TimeDelta of PredictorInit::kMaxQueueingDelayMs.
105  // (It would be a static constant... except style rules preclude that :-/ ).
106  const TimeDelta default_max_queueing_delay_;
107};
108
109//------------------------------------------------------------------------------
110
111TEST_F(PredictorTest, StartupShutdownTest) {
112  scoped_refptr<Predictor> testing_master(
113      new Predictor(host_resolver_.get(),
114                    default_max_queueing_delay_,
115                    PredictorInit::kMaxSpeculativeParallelResolves,
116                    false));
117  testing_master->Shutdown();
118}
119
120
121TEST_F(PredictorTest, ShutdownWhenResolutionIsPendingTest) {
122  scoped_refptr<net::WaitingHostResolverProc> resolver_proc(
123      new net::WaitingHostResolverProc(NULL));
124  host_resolver_->Reset(resolver_proc);
125
126  scoped_refptr<Predictor> testing_master(
127      new Predictor(host_resolver_.get(),
128                    default_max_queueing_delay_,
129                    PredictorInit::kMaxSpeculativeParallelResolves,
130                    false));
131
132  GURL localhost("http://localhost:80");
133  UrlList names;
134  names.push_back(localhost);
135
136  testing_master->ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
137
138  MessageLoop::current()->PostDelayedTask(FROM_HERE,
139                                          new MessageLoop::QuitTask(), 500);
140  MessageLoop::current()->Run();
141
142  EXPECT_FALSE(testing_master->WasFound(localhost));
143
144  testing_master->Shutdown();
145
146  // Clean up after ourselves.
147  resolver_proc->Signal();
148  MessageLoop::current()->RunAllPending();
149}
150
151TEST_F(PredictorTest, SingleLookupTest) {
152  scoped_refptr<Predictor> testing_master(
153      new Predictor(host_resolver_.get(),
154                    default_max_queueing_delay_,
155                    PredictorInit::kMaxSpeculativeParallelResolves,
156                    false));
157
158  GURL goog("http://www.google.com:80");
159
160  UrlList names;
161  names.push_back(goog);
162
163  // Try to flood the predictor with many concurrent requests.
164  for (int i = 0; i < 10; i++)
165    testing_master->ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
166
167  WaitForResolution(testing_master, names);
168
169  EXPECT_TRUE(testing_master->WasFound(goog));
170
171  MessageLoop::current()->RunAllPending();
172
173  EXPECT_GT(testing_master->peak_pending_lookups(), names.size() / 2);
174  EXPECT_LE(testing_master->peak_pending_lookups(), names.size());
175  EXPECT_LE(testing_master->peak_pending_lookups(),
176            testing_master->max_concurrent_dns_lookups());
177
178  testing_master->Shutdown();
179}
180
181TEST_F(PredictorTest, ConcurrentLookupTest) {
182  host_resolver_->rules()->AddSimulatedFailure("*.notfound");
183
184  scoped_refptr<Predictor> testing_master(
185      new Predictor(host_resolver_.get(),
186                    default_max_queueing_delay_,
187                    PredictorInit::kMaxSpeculativeParallelResolves,
188                    false));
189
190  GURL goog("http://www.google.com:80"),
191      goog2("http://gmail.google.com.com:80"),
192      goog3("http://mail.google.com:80"),
193      goog4("http://gmail.com:80");
194  GURL bad1("http://bad1.notfound:80"),
195      bad2("http://bad2.notfound:80");
196
197  UrlList names;
198  names.push_back(goog);
199  names.push_back(goog3);
200  names.push_back(bad1);
201  names.push_back(goog2);
202  names.push_back(bad2);
203  names.push_back(goog4);
204  names.push_back(goog);
205
206  // Try to flood the predictor with many concurrent requests.
207  for (int i = 0; i < 10; i++)
208    testing_master->ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
209
210  WaitForResolution(testing_master, names);
211
212  EXPECT_TRUE(testing_master->WasFound(goog));
213  EXPECT_TRUE(testing_master->WasFound(goog3));
214  EXPECT_TRUE(testing_master->WasFound(goog2));
215  EXPECT_TRUE(testing_master->WasFound(goog4));
216  EXPECT_FALSE(testing_master->WasFound(bad1));
217  EXPECT_FALSE(testing_master->WasFound(bad2));
218
219  MessageLoop::current()->RunAllPending();
220
221  EXPECT_FALSE(testing_master->WasFound(bad1));
222  EXPECT_FALSE(testing_master->WasFound(bad2));
223
224  EXPECT_LE(testing_master->peak_pending_lookups(), names.size());
225  EXPECT_LE(testing_master->peak_pending_lookups(),
226            testing_master->max_concurrent_dns_lookups());
227
228  testing_master->Shutdown();
229}
230
231TEST_F(PredictorTest, MassiveConcurrentLookupTest) {
232  host_resolver_->rules()->AddSimulatedFailure("*.notfound");
233
234  scoped_refptr<Predictor> testing_master(
235      new Predictor(host_resolver_.get(),
236                    default_max_queueing_delay_,
237                    PredictorInit::kMaxSpeculativeParallelResolves,
238                    false));
239
240  UrlList names;
241  for (int i = 0; i < 100; i++)
242    names.push_back(GURL(
243        "http://host" + base::IntToString(i) + ".notfound:80"));
244
245  // Try to flood the predictor with many concurrent requests.
246  for (int i = 0; i < 10; i++)
247    testing_master->ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
248
249  WaitForResolution(testing_master, names);
250
251  MessageLoop::current()->RunAllPending();
252
253  EXPECT_LE(testing_master->peak_pending_lookups(), names.size());
254  EXPECT_LE(testing_master->peak_pending_lookups(),
255            testing_master->max_concurrent_dns_lookups());
256
257  testing_master->Shutdown();
258}
259
260//------------------------------------------------------------------------------
261// Functions to help synthesize and test serializations of subresource referrer
262// lists.
263
264// Return a motivation_list if we can find one for the given motivating_host (or
265// NULL if a match is not found).
266static ListValue* FindSerializationMotivation(
267    const GURL& motivation, const ListValue& referral_list) {
268  CHECK_LT(0u, referral_list.GetSize());  // Room for version.
269  int format_version = -1;
270  CHECK(referral_list.GetInteger(0, &format_version));
271  CHECK_EQ(Predictor::PREDICTOR_REFERRER_VERSION, format_version);
272  ListValue* motivation_list(NULL);
273  for (size_t i = 1; i < referral_list.GetSize(); ++i) {
274    referral_list.GetList(i, &motivation_list);
275    std::string existing_spec;
276    EXPECT_TRUE(motivation_list->GetString(0, &existing_spec));
277    if (motivation == GURL(existing_spec))
278      return motivation_list;
279  }
280  return NULL;
281}
282
283// Create a new empty serialization list.
284static ListValue* NewEmptySerializationList() {
285  ListValue* list = new ListValue;
286  list->Append(new FundamentalValue(Predictor::PREDICTOR_REFERRER_VERSION));
287  return list;
288}
289
290// Add a motivating_url and a subresource_url to a serialized list, using
291// this given latency. This is a helper function for quickly building these
292// lists.
293static void AddToSerializedList(const GURL& motivation,
294                                const GURL& subresource,
295                                double use_rate,
296                                ListValue* referral_list ) {
297  // Find the motivation if it is already used.
298  ListValue* motivation_list = FindSerializationMotivation(motivation,
299                                                           *referral_list);
300  if (!motivation_list) {
301    // This is the first mention of this motivation, so build a list.
302    motivation_list = new ListValue;
303    motivation_list->Append(new StringValue(motivation.spec()));
304    // Provide empty subresource list.
305    motivation_list->Append(new ListValue());
306
307    // ...and make it part of the serialized referral_list.
308    referral_list->Append(motivation_list);
309  }
310
311  ListValue* subresource_list(NULL);
312  // 0 == url; 1 == subresource_list.
313  EXPECT_TRUE(motivation_list->GetList(1, &subresource_list));
314
315  // We won't bother to check for the subresource being there already.  Worst
316  // case, during deserialization, the latency value we supply plus the
317  // existing value(s) will be added to the referrer.
318
319  subresource_list->Append(new StringValue(subresource.spec()));
320  subresource_list->Append(new FundamentalValue(use_rate));
321}
322
323static const int kLatencyNotFound = -1;
324
325// For a given motivation, and subresource, find what latency is currently
326// listed.  This assume a well formed serialization, which has at most one such
327// entry for any pair of names.  If no such pair is found, then return false.
328// Data is written into use_rate arguments.
329static bool GetDataFromSerialization(const GURL& motivation,
330                                     const GURL& subresource,
331                                     const ListValue& referral_list,
332                                     double* use_rate) {
333  ListValue* motivation_list = FindSerializationMotivation(motivation,
334                                                           referral_list);
335  if (!motivation_list)
336    return false;
337  ListValue* subresource_list;
338  EXPECT_TRUE(motivation_list->GetList(1, &subresource_list));
339  for (size_t i = 0; i < subresource_list->GetSize();) {
340    std::string url_spec;
341    EXPECT_TRUE(subresource_list->GetString(i++, &url_spec));
342    EXPECT_TRUE(subresource_list->GetDouble(i++, use_rate));
343    if (subresource == GURL(url_spec)) {
344      return true;
345    }
346  }
347  return false;
348}
349
350//------------------------------------------------------------------------------
351
352// Make sure nil referral lists really have no entries, and no latency listed.
353TEST_F(PredictorTest, ReferrerSerializationNilTest) {
354  scoped_refptr<Predictor> predictor(
355      new Predictor(host_resolver_.get(),
356                    default_max_queueing_delay_,
357                    PredictorInit::kMaxSpeculativeParallelResolves,
358                    false));
359  scoped_ptr<ListValue> referral_list(NewEmptySerializationList());
360  predictor->SerializeReferrers(referral_list.get());
361  EXPECT_EQ(1U, referral_list->GetSize());
362  EXPECT_FALSE(GetDataFromSerialization(
363    GURL("http://a.com:79"), GURL("http://b.com:78"),
364      *referral_list.get(), NULL));
365
366  predictor->Shutdown();
367}
368
369// Make sure that when a serialization list includes a value, that it can be
370// deserialized into the database, and can be extracted back out via
371// serialization without being changed.
372TEST_F(PredictorTest, ReferrerSerializationSingleReferrerTest) {
373  scoped_refptr<Predictor> predictor(
374      new Predictor(host_resolver_.get(),
375                    default_max_queueing_delay_,
376                    PredictorInit::kMaxSpeculativeParallelResolves,
377                    false));
378  const GURL motivation_url("http://www.google.com:91");
379  const GURL subresource_url("http://icons.google.com:90");
380  const double kUseRate = 23.4;
381  scoped_ptr<ListValue> referral_list(NewEmptySerializationList());
382
383  AddToSerializedList(motivation_url, subresource_url,
384      kUseRate, referral_list.get());
385
386  predictor->DeserializeReferrers(*referral_list.get());
387
388  ListValue recovered_referral_list;
389  predictor->SerializeReferrers(&recovered_referral_list);
390  EXPECT_EQ(2U, recovered_referral_list.GetSize());
391  double rate;
392  EXPECT_TRUE(GetDataFromSerialization(
393      motivation_url, subresource_url, recovered_referral_list, &rate));
394  EXPECT_EQ(rate, kUseRate);
395
396  predictor->Shutdown();
397}
398
399// Verify that two floats are within 1% of each other in value.
400#define EXPECT_SIMILAR(a, b) do { \
401    double espilon_ratio = 1.01;  \
402    if ((a) < 0.)  \
403      espilon_ratio = 1 / espilon_ratio;  \
404    EXPECT_LT(a, espilon_ratio * (b));   \
405    EXPECT_GT((a) * espilon_ratio, b);   \
406    } while (0)
407
408
409// Make sure the Trim() functionality works as expected.
410TEST_F(PredictorTest, ReferrerSerializationTrimTest) {
411  scoped_refptr<Predictor> predictor(
412      new Predictor(host_resolver_.get(),
413                    default_max_queueing_delay_,
414                    PredictorInit::kMaxSpeculativeParallelResolves,
415                    false));
416  GURL motivation_url("http://www.google.com:110");
417
418  GURL icon_subresource_url("http://icons.google.com:111");
419  const double kRateIcon = 16.0 * Predictor::kDiscardableExpectedValue;
420  GURL img_subresource_url("http://img.google.com:118");
421  const double kRateImg = 8.0 * Predictor::kDiscardableExpectedValue;
422
423  scoped_ptr<ListValue> referral_list(NewEmptySerializationList());
424  AddToSerializedList(
425      motivation_url, icon_subresource_url, kRateIcon, referral_list.get());
426  AddToSerializedList(
427      motivation_url, img_subresource_url, kRateImg, referral_list.get());
428
429  predictor->DeserializeReferrers(*referral_list.get());
430
431  ListValue recovered_referral_list;
432  predictor->SerializeReferrers(&recovered_referral_list);
433  EXPECT_EQ(2U, recovered_referral_list.GetSize());
434  double rate;
435  EXPECT_TRUE(GetDataFromSerialization(
436      motivation_url, icon_subresource_url, recovered_referral_list,
437      &rate));
438  EXPECT_SIMILAR(rate, kRateIcon);
439
440  EXPECT_TRUE(GetDataFromSerialization(
441      motivation_url, img_subresource_url, recovered_referral_list, &rate));
442  EXPECT_SIMILAR(rate, kRateImg);
443
444  // Each time we Trim 24 times, the user_rate figures should reduce by a factor
445  // of two,  until they are small, and then a trim will delete the whole entry.
446  for (int i = 0; i < 24; ++i)
447    predictor->TrimReferrersNow();
448  predictor->SerializeReferrers(&recovered_referral_list);
449  EXPECT_EQ(2U, recovered_referral_list.GetSize());
450  EXPECT_TRUE(GetDataFromSerialization(
451      motivation_url, icon_subresource_url, recovered_referral_list, &rate));
452  EXPECT_SIMILAR(rate, kRateIcon / 2);
453
454  EXPECT_TRUE(GetDataFromSerialization(
455      motivation_url, img_subresource_url, recovered_referral_list, &rate));
456  EXPECT_SIMILAR(rate, kRateImg / 2);
457
458  for (int i = 0; i < 24; ++i)
459    predictor->TrimReferrersNow();
460  predictor->SerializeReferrers(&recovered_referral_list);
461  EXPECT_EQ(2U, recovered_referral_list.GetSize());
462  EXPECT_TRUE(GetDataFromSerialization(
463      motivation_url, icon_subresource_url, recovered_referral_list, &rate));
464  EXPECT_SIMILAR(rate, kRateIcon / 4);
465  EXPECT_TRUE(GetDataFromSerialization(
466      motivation_url, img_subresource_url, recovered_referral_list, &rate));
467  EXPECT_SIMILAR(rate, kRateImg / 4);
468
469  for (int i = 0; i < 24; ++i)
470    predictor->TrimReferrersNow();
471  predictor->SerializeReferrers(&recovered_referral_list);
472  EXPECT_EQ(2U, recovered_referral_list.GetSize());
473  EXPECT_TRUE(GetDataFromSerialization(
474      motivation_url, icon_subresource_url, recovered_referral_list, &rate));
475  EXPECT_SIMILAR(rate, kRateIcon / 8);
476
477  // Img is below threshold, and so it gets deleted.
478  EXPECT_FALSE(GetDataFromSerialization(
479      motivation_url, img_subresource_url, recovered_referral_list, &rate));
480
481  for (int i = 0; i < 24; ++i)
482    predictor->TrimReferrersNow();
483  predictor->SerializeReferrers(&recovered_referral_list);
484  // Icon is also trimmed away, so entire set gets discarded.
485  EXPECT_EQ(1U, recovered_referral_list.GetSize());
486  EXPECT_FALSE(GetDataFromSerialization(
487      motivation_url, icon_subresource_url, recovered_referral_list, &rate));
488  EXPECT_FALSE(GetDataFromSerialization(
489      motivation_url, img_subresource_url, recovered_referral_list, &rate));
490
491  predictor->Shutdown();
492}
493
494
495TEST_F(PredictorTest, PriorityQueuePushPopTest) {
496  Predictor::HostNameQueue queue;
497
498  GURL first("http://first:80"), second("http://second:90");
499
500  // First check high priority queue FIFO functionality.
501  EXPECT_TRUE(queue.IsEmpty());
502  queue.Push(first, UrlInfo::LEARNED_REFERAL_MOTIVATED);
503  EXPECT_FALSE(queue.IsEmpty());
504  queue.Push(second, UrlInfo::MOUSE_OVER_MOTIVATED);
505  EXPECT_FALSE(queue.IsEmpty());
506  EXPECT_EQ(queue.Pop(), first);
507  EXPECT_FALSE(queue.IsEmpty());
508  EXPECT_EQ(queue.Pop(), second);
509  EXPECT_TRUE(queue.IsEmpty());
510
511  // Then check low priority queue FIFO functionality.
512  queue.Push(first, UrlInfo::PAGE_SCAN_MOTIVATED);
513  EXPECT_FALSE(queue.IsEmpty());
514  queue.Push(second, UrlInfo::OMNIBOX_MOTIVATED);
515  EXPECT_FALSE(queue.IsEmpty());
516  EXPECT_EQ(queue.Pop(), first);
517  EXPECT_FALSE(queue.IsEmpty());
518  EXPECT_EQ(queue.Pop(), second);
519  EXPECT_TRUE(queue.IsEmpty());
520}
521
522TEST_F(PredictorTest, PriorityQueueReorderTest) {
523  Predictor::HostNameQueue queue;
524
525  // Push all the low priority items.
526  GURL low1("http://low1:80"),
527      low2("http://low2:80"),
528      low3("http://low3:443"),
529      low4("http://low4:80"),
530      low5("http://low5:80"),
531      hi1("http://hi1:80"),
532      hi2("http://hi2:80"),
533      hi3("http://hi3:80");
534
535  EXPECT_TRUE(queue.IsEmpty());
536  queue.Push(low1, UrlInfo::PAGE_SCAN_MOTIVATED);
537  queue.Push(low2, UrlInfo::UNIT_TEST_MOTIVATED);
538  queue.Push(low3, UrlInfo::LINKED_MAX_MOTIVATED);
539  queue.Push(low4, UrlInfo::OMNIBOX_MOTIVATED);
540  queue.Push(low5, UrlInfo::STARTUP_LIST_MOTIVATED);
541  queue.Push(low4, UrlInfo::OMNIBOX_MOTIVATED);
542
543  // Push all the high prority items
544  queue.Push(hi1, UrlInfo::LEARNED_REFERAL_MOTIVATED);
545  queue.Push(hi2, UrlInfo::STATIC_REFERAL_MOTIVATED);
546  queue.Push(hi3, UrlInfo::MOUSE_OVER_MOTIVATED);
547
548  // Check that high priority stuff comes out first, and in FIFO order.
549  EXPECT_EQ(queue.Pop(), hi1);
550  EXPECT_EQ(queue.Pop(), hi2);
551  EXPECT_EQ(queue.Pop(), hi3);
552
553  // ...and then low priority strings.
554  EXPECT_EQ(queue.Pop(), low1);
555  EXPECT_EQ(queue.Pop(), low2);
556  EXPECT_EQ(queue.Pop(), low3);
557  EXPECT_EQ(queue.Pop(), low4);
558  EXPECT_EQ(queue.Pop(), low5);
559  EXPECT_EQ(queue.Pop(), low4);
560
561  EXPECT_TRUE(queue.IsEmpty());
562}
563
564TEST_F(PredictorTest, CanonicalizeUrl) {
565  // Base case, only handles HTTP and HTTPS.
566  EXPECT_EQ(GURL(), Predictor::CanonicalizeUrl(GURL("ftp://anything")));
567
568  // Remove path testing.
569  GURL long_url("http://host:999/path?query=value");
570  EXPECT_EQ(Predictor::CanonicalizeUrl(long_url), long_url.GetWithEmptyPath());
571
572  // Default port cannoncalization.
573  GURL implied_port("http://test");
574  GURL explicit_port("http://test:80");
575  EXPECT_EQ(Predictor::CanonicalizeUrl(implied_port),
576            Predictor::CanonicalizeUrl(explicit_port));
577
578  // Port is still maintained.
579  GURL port_80("http://test:80");
580  GURL port_90("http://test:90");
581  EXPECT_NE(Predictor::CanonicalizeUrl(port_80),
582            Predictor::CanonicalizeUrl(port_90));
583
584  // Host is still maintained.
585  GURL host_1("http://test_1");
586  GURL host_2("http://test_2");
587  EXPECT_NE(Predictor::CanonicalizeUrl(host_1),
588            Predictor::CanonicalizeUrl(host_2));
589
590  // Scheme is maintained (mismatch identified).
591  GURL http("http://test");
592  GURL https("https://test");
593  EXPECT_NE(Predictor::CanonicalizeUrl(http),
594            Predictor::CanonicalizeUrl(https));
595
596  // Https works fine.
597  GURL long_https("https://host:999/path?query=value");
598  EXPECT_EQ(Predictor::CanonicalizeUrl(long_https),
599            long_https.GetWithEmptyPath());
600}
601
602TEST_F(PredictorTest, DiscardPredictorResults) {
603  scoped_refptr<Predictor> predictor(
604      new Predictor(host_resolver_.get(),
605                    default_max_queueing_delay_,
606                    PredictorInit::kMaxSpeculativeParallelResolves,
607                    false));
608  ListValue referral_list;
609  predictor->SerializeReferrers(&referral_list);
610  EXPECT_EQ(1U, referral_list.GetSize());
611
612  GURL host_1("http://test_1");
613  GURL host_2("http://test_2");
614  predictor->LearnFromNavigation(host_1, host_2);
615
616  predictor->SerializeReferrers(&referral_list);
617  EXPECT_EQ(2U, referral_list.GetSize());
618
619  predictor->DiscardAllResults();
620  predictor->SerializeReferrers(&referral_list);
621  EXPECT_EQ(1U, referral_list.GetSize());
622
623  predictor->Shutdown();
624}
625
626}  // namespace chrome_browser_net
627