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.
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/url_info.h"
18 #include "chrome/common/net/predictor_common.h"
19 #include "content/public/test/test_browser_thread.h"
20 #include "net/base/address_list.h"
21 #include "net/base/winsock_init.h"
22 #include "net/dns/mock_host_resolver.h"
23 #include "testing/gtest/include/gtest/gtest.h"
26 using base::TimeDelta;
27 using content::BrowserThread;
29 namespace chrome_browser_net {
31 class WaitForResolutionHelper;
33 typedef base::RepeatingTimer<WaitForResolutionHelper> HelperTimer;
35 class WaitForResolutionHelper {
37 WaitForResolutionHelper(Predictor* predictor, const UrlList& hosts,
39 : predictor_(predictor),
45 for (UrlList::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i)
46 if (predictor_->GetResolutionDuration(*i) ==
47 UrlInfo::NullDuration())
48 return; // We don't have resolution for that host.
50 // When all hostnames have been resolved, exit the loop.
52 base::MessageLoop::current()->Quit();
58 Predictor* predictor_;
63 class PredictorTest : public testing::Test {
66 : ui_thread_(BrowserThread::UI, &loop_),
67 io_thread_(BrowserThread::IO, &loop_),
68 host_resolver_(new net::MockCachingHostResolver()) {
72 virtual void SetUp() {
74 net::EnsureWinsockInit();
76 Predictor::set_max_parallel_resolves(
77 Predictor::kMaxSpeculativeParallelResolves);
78 Predictor::set_max_queueing_delay(
79 Predictor::kMaxSpeculativeResolveQueueDelayMs);
80 // Since we are using a caching HostResolver, the following latencies will
81 // only be incurred by the first request, after which the result will be
82 // cached internally by |host_resolver_|.
83 net::RuleBasedHostResolverProc* rules = host_resolver_->rules();
84 rules->AddRuleWithLatency("www.google.com", "127.0.0.1", 50);
85 rules->AddRuleWithLatency("gmail.google.com.com", "127.0.0.1", 70);
86 rules->AddRuleWithLatency("mail.google.com", "127.0.0.1", 44);
87 rules->AddRuleWithLatency("gmail.com", "127.0.0.1", 63);
90 void WaitForResolution(Predictor* predictor, const UrlList& hosts) {
91 HelperTimer* timer = new HelperTimer();
92 timer->Start(FROM_HERE, TimeDelta::FromMilliseconds(100),
93 new WaitForResolutionHelper(predictor, hosts, timer),
94 &WaitForResolutionHelper::Run);
95 base::MessageLoop::current()->Run();
99 // IMPORTANT: do not move this below |host_resolver_|; the host resolver
100 // must not outlive the message loop, otherwise bad things can happen
101 // (like posting to a deleted message loop).
102 base::MessageLoopForUI loop_;
103 content::TestBrowserThread ui_thread_;
104 content::TestBrowserThread io_thread_;
107 scoped_ptr<net::MockCachingHostResolver> host_resolver_;
110 //------------------------------------------------------------------------------
112 TEST_F(PredictorTest, StartupShutdownTest) {
113 Predictor testing_master(true);
114 testing_master.Shutdown();
118 TEST_F(PredictorTest, ShutdownWhenResolutionIsPendingTest) {
119 scoped_ptr<net::HostResolver> host_resolver(new net::HangingHostResolver());
121 Predictor testing_master(true);
122 testing_master.SetHostResolver(host_resolver.get());
124 GURL localhost("http://localhost:80");
126 names.push_back(localhost);
128 testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
130 base::MessageLoop::current()->PostDelayedTask(
132 base::MessageLoop::QuitClosure(),
133 base::TimeDelta::FromMilliseconds(500));
134 base::MessageLoop::current()->Run();
136 EXPECT_FALSE(testing_master.WasFound(localhost));
138 testing_master.Shutdown();
140 // Clean up after ourselves.
141 base::MessageLoop::current()->RunUntilIdle();
144 TEST_F(PredictorTest, SingleLookupTest) {
145 Predictor testing_master(true);
146 testing_master.SetHostResolver(host_resolver_.get());
148 GURL goog("http://www.google.com:80");
151 names.push_back(goog);
153 // Try to flood the predictor with many concurrent requests.
154 for (int i = 0; i < 10; i++)
155 testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
157 WaitForResolution(&testing_master, names);
159 EXPECT_TRUE(testing_master.WasFound(goog));
161 base::MessageLoop::current()->RunUntilIdle();
163 EXPECT_GT(testing_master.peak_pending_lookups(), names.size() / 2);
164 EXPECT_LE(testing_master.peak_pending_lookups(), names.size());
165 EXPECT_LE(testing_master.peak_pending_lookups(),
166 testing_master.max_concurrent_dns_lookups());
168 testing_master.Shutdown();
171 TEST_F(PredictorTest, ConcurrentLookupTest) {
172 host_resolver_->rules()->AddSimulatedFailure("*.notfound");
174 Predictor testing_master(true);
175 testing_master.SetHostResolver(host_resolver_.get());
177 GURL goog("http://www.google.com:80"),
178 goog2("http://gmail.google.com.com:80"),
179 goog3("http://mail.google.com:80"),
180 goog4("http://gmail.com:80");
181 GURL bad1("http://bad1.notfound:80"),
182 bad2("http://bad2.notfound:80");
185 names.push_back(goog);
186 names.push_back(goog3);
187 names.push_back(bad1);
188 names.push_back(goog2);
189 names.push_back(bad2);
190 names.push_back(goog4);
191 names.push_back(goog);
193 // Try to flood the predictor with many concurrent requests.
194 for (int i = 0; i < 10; i++)
195 testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
197 WaitForResolution(&testing_master, names);
199 EXPECT_TRUE(testing_master.WasFound(goog));
200 EXPECT_TRUE(testing_master.WasFound(goog3));
201 EXPECT_TRUE(testing_master.WasFound(goog2));
202 EXPECT_TRUE(testing_master.WasFound(goog4));
203 EXPECT_FALSE(testing_master.WasFound(bad1));
204 EXPECT_FALSE(testing_master.WasFound(bad2));
206 base::MessageLoop::current()->RunUntilIdle();
208 EXPECT_FALSE(testing_master.WasFound(bad1));
209 EXPECT_FALSE(testing_master.WasFound(bad2));
211 EXPECT_LE(testing_master.peak_pending_lookups(), names.size());
212 EXPECT_LE(testing_master.peak_pending_lookups(),
213 testing_master.max_concurrent_dns_lookups());
215 testing_master.Shutdown();
218 TEST_F(PredictorTest, MassiveConcurrentLookupTest) {
219 host_resolver_->rules()->AddSimulatedFailure("*.notfound");
221 Predictor testing_master(true);
222 testing_master.SetHostResolver(host_resolver_.get());
225 for (int i = 0; i < 100; i++)
226 names.push_back(GURL(
227 "http://host" + base::IntToString(i) + ".notfound:80"));
229 // Try to flood the predictor with many concurrent requests.
230 for (int i = 0; i < 10; i++)
231 testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED);
233 WaitForResolution(&testing_master, names);
235 base::MessageLoop::current()->RunUntilIdle();
237 EXPECT_LE(testing_master.peak_pending_lookups(), names.size());
238 EXPECT_LE(testing_master.peak_pending_lookups(),
239 testing_master.max_concurrent_dns_lookups());
241 testing_master.Shutdown();
244 //------------------------------------------------------------------------------
245 // Functions to help synthesize and test serializations of subresource referrer
248 // Return a motivation_list if we can find one for the given motivating_host (or
249 // NULL if a match is not found).
250 static const ListValue* FindSerializationMotivation(
251 const GURL& motivation,
252 const ListValue* referral_list) {
253 CHECK_LT(0u, referral_list->GetSize()); // Room for version.
254 int format_version = -1;
255 CHECK(referral_list->GetInteger(0, &format_version));
256 CHECK_EQ(Predictor::kPredictorReferrerVersion, format_version);
257 const ListValue* motivation_list(NULL);
258 for (size_t i = 1; i < referral_list->GetSize(); ++i) {
259 referral_list->GetList(i, &motivation_list);
260 std::string existing_spec;
261 EXPECT_TRUE(motivation_list->GetString(0, &existing_spec));
262 if (motivation == GURL(existing_spec))
263 return motivation_list;
268 static ListValue* FindSerializationMotivation(const GURL& motivation,
269 ListValue* referral_list) {
270 return const_cast<ListValue*>(FindSerializationMotivation(
271 motivation, static_cast<const ListValue*>(referral_list)));
274 // Create a new empty serialization list.
275 static ListValue* NewEmptySerializationList() {
276 base::ListValue* list = new base::ListValue;
278 new base::FundamentalValue(Predictor::kPredictorReferrerVersion));
282 // Add a motivating_url and a subresource_url to a serialized list, using
283 // this given latency. This is a helper function for quickly building these
285 static void AddToSerializedList(const GURL& motivation,
286 const GURL& subresource,
288 ListValue* referral_list ) {
289 // Find the motivation if it is already used.
290 ListValue* motivation_list = FindSerializationMotivation(motivation,
292 if (!motivation_list) {
293 // This is the first mention of this motivation, so build a list.
294 motivation_list = new ListValue;
295 motivation_list->Append(new StringValue(motivation.spec()));
296 // Provide empty subresource list.
297 motivation_list->Append(new ListValue());
299 // ...and make it part of the serialized referral_list.
300 referral_list->Append(motivation_list);
303 ListValue* subresource_list(NULL);
304 // 0 == url; 1 == subresource_list.
305 EXPECT_TRUE(motivation_list->GetList(1, &subresource_list));
307 // We won't bother to check for the subresource being there already. Worst
308 // case, during deserialization, the latency value we supply plus the
309 // existing value(s) will be added to the referrer.
311 subresource_list->Append(new base::StringValue(subresource.spec()));
312 subresource_list->Append(new base::FundamentalValue(use_rate));
315 // For a given motivation, and subresource, find what latency is currently
316 // listed. This assume a well formed serialization, which has at most one such
317 // entry for any pair of names. If no such pair is found, then return false.
318 // Data is written into use_rate arguments.
319 static bool GetDataFromSerialization(const GURL& motivation,
320 const GURL& subresource,
321 const ListValue& referral_list,
323 const ListValue* motivation_list =
324 FindSerializationMotivation(motivation, &referral_list);
325 if (!motivation_list)
327 const ListValue* subresource_list;
328 EXPECT_TRUE(motivation_list->GetList(1, &subresource_list));
329 for (size_t i = 0; i < subresource_list->GetSize();) {
330 std::string url_spec;
331 EXPECT_TRUE(subresource_list->GetString(i++, &url_spec));
332 EXPECT_TRUE(subresource_list->GetDouble(i++, use_rate));
333 if (subresource == GURL(url_spec)) {
340 //------------------------------------------------------------------------------
342 // Make sure nil referral lists really have no entries, and no latency listed.
343 TEST_F(PredictorTest, ReferrerSerializationNilTest) {
344 Predictor predictor(true);
345 predictor.SetHostResolver(host_resolver_.get());
347 scoped_ptr<ListValue> referral_list(NewEmptySerializationList());
348 predictor.SerializeReferrers(referral_list.get());
349 EXPECT_EQ(1U, referral_list->GetSize());
350 EXPECT_FALSE(GetDataFromSerialization(
351 GURL("http://a.com:79"), GURL("http://b.com:78"),
352 *referral_list.get(), NULL));
354 predictor.Shutdown();
357 // Make sure that when a serialization list includes a value, that it can be
358 // deserialized into the database, and can be extracted back out via
359 // serialization without being changed.
360 TEST_F(PredictorTest, ReferrerSerializationSingleReferrerTest) {
361 Predictor predictor(true);
362 predictor.SetHostResolver(host_resolver_.get());
363 const GURL motivation_url("http://www.google.com:91");
364 const GURL subresource_url("http://icons.google.com:90");
365 const double kUseRate = 23.4;
366 scoped_ptr<ListValue> referral_list(NewEmptySerializationList());
368 AddToSerializedList(motivation_url, subresource_url,
369 kUseRate, referral_list.get());
371 predictor.DeserializeReferrers(*referral_list.get());
373 ListValue recovered_referral_list;
374 predictor.SerializeReferrers(&recovered_referral_list);
375 EXPECT_EQ(2U, recovered_referral_list.GetSize());
377 EXPECT_TRUE(GetDataFromSerialization(
378 motivation_url, subresource_url, recovered_referral_list, &rate));
379 EXPECT_EQ(rate, kUseRate);
381 predictor.Shutdown();
384 // Check that GetHtmlReferrerLists() doesn't crash when given duplicated
385 // domains for referring URL, and that it sorts the results in the
387 TEST_F(PredictorTest, GetHtmlReferrerLists) {
388 Predictor predictor(true);
389 predictor.SetHostResolver(host_resolver_.get());
390 const double kUseRate = 23.4;
391 scoped_ptr<ListValue> referral_list(NewEmptySerializationList());
394 GURL("http://d.google.com/x1"),
395 GURL("http://foo.com/"),
396 kUseRate, referral_list.get());
398 // Duplicated hostname (d.google.com). This should not cause any crashes
399 // (i.e. crbug.com/116345)
401 GURL("http://d.google.com/x2"),
402 GURL("http://foo.com/"),
403 kUseRate, referral_list.get());
406 GURL("http://a.yahoo.com/y"),
407 GURL("http://foo1.com/"),
408 kUseRate, referral_list.get());
411 GURL("http://b.google.com/x3"),
412 GURL("http://foo2.com/"),
413 kUseRate, referral_list.get());
416 GURL("http://d.yahoo.com/x5"),
417 GURL("http://i.like.turtles/"),
418 kUseRate, referral_list.get());
421 GURL("http://c.yahoo.com/x4"),
422 GURL("http://foo3.com/"),
423 kUseRate, referral_list.get());
425 predictor.DeserializeReferrers(*referral_list.get());
428 predictor.GetHtmlReferrerLists(&html);
430 // The lexicographic sorting of hostnames would be:
437 // However we expect to sort them by domain in the output:
445 html.find("<td rowspan=1>http://b.google.com/x3"),
446 html.find("<td rowspan=1>http://d.google.com/x1"),
447 html.find("<td rowspan=1>http://d.google.com/x2"),
448 html.find("<td rowspan=1>http://a.yahoo.com/y"),
449 html.find("<td rowspan=1>http://c.yahoo.com/x4"),
450 html.find("<td rowspan=1>http://d.yahoo.com/x5"),
453 // Make sure things appeared in the expected order.
454 for (size_t i = 1; i < arraysize(pos); ++i) {
455 EXPECT_LT(pos[i - 1], pos[i]) << "Mismatch for pos[" << i << "]";
458 predictor.Shutdown();
461 // Verify that two floats are within 1% of each other in value.
462 #define EXPECT_SIMILAR(a, b) do { \
463 double espilon_ratio = 1.01; \
465 espilon_ratio = 1 / espilon_ratio; \
466 EXPECT_LT(a, espilon_ratio * (b)); \
467 EXPECT_GT((a) * espilon_ratio, b); \
471 // Make sure the Trim() functionality works as expected.
472 TEST_F(PredictorTest, ReferrerSerializationTrimTest) {
473 Predictor predictor(true);
474 predictor.SetHostResolver(host_resolver_.get());
475 GURL motivation_url("http://www.google.com:110");
477 GURL icon_subresource_url("http://icons.google.com:111");
478 const double kRateIcon = 16.0 * Predictor::kDiscardableExpectedValue;
479 GURL img_subresource_url("http://img.google.com:118");
480 const double kRateImg = 8.0 * Predictor::kDiscardableExpectedValue;
482 scoped_ptr<ListValue> referral_list(NewEmptySerializationList());
484 motivation_url, icon_subresource_url, kRateIcon, referral_list.get());
486 motivation_url, img_subresource_url, kRateImg, referral_list.get());
488 predictor.DeserializeReferrers(*referral_list.get());
490 ListValue recovered_referral_list;
491 predictor.SerializeReferrers(&recovered_referral_list);
492 EXPECT_EQ(2U, recovered_referral_list.GetSize());
494 EXPECT_TRUE(GetDataFromSerialization(
495 motivation_url, icon_subresource_url, recovered_referral_list,
497 EXPECT_SIMILAR(rate, kRateIcon);
499 EXPECT_TRUE(GetDataFromSerialization(
500 motivation_url, img_subresource_url, recovered_referral_list, &rate));
501 EXPECT_SIMILAR(rate, kRateImg);
503 // Each time we Trim 24 times, the user_rate figures should reduce by a factor
504 // of two, until they are small, and then a trim will delete the whole entry.
505 for (int i = 0; i < 24; ++i)
506 predictor.TrimReferrersNow();
507 predictor.SerializeReferrers(&recovered_referral_list);
508 EXPECT_EQ(2U, recovered_referral_list.GetSize());
509 EXPECT_TRUE(GetDataFromSerialization(
510 motivation_url, icon_subresource_url, recovered_referral_list, &rate));
511 EXPECT_SIMILAR(rate, kRateIcon / 2);
513 EXPECT_TRUE(GetDataFromSerialization(
514 motivation_url, img_subresource_url, recovered_referral_list, &rate));
515 EXPECT_SIMILAR(rate, kRateImg / 2);
517 for (int i = 0; i < 24; ++i)
518 predictor.TrimReferrersNow();
519 predictor.SerializeReferrers(&recovered_referral_list);
520 EXPECT_EQ(2U, recovered_referral_list.GetSize());
521 EXPECT_TRUE(GetDataFromSerialization(
522 motivation_url, icon_subresource_url, recovered_referral_list, &rate));
523 EXPECT_SIMILAR(rate, kRateIcon / 4);
524 EXPECT_TRUE(GetDataFromSerialization(
525 motivation_url, img_subresource_url, recovered_referral_list, &rate));
526 EXPECT_SIMILAR(rate, kRateImg / 4);
528 for (int i = 0; i < 24; ++i)
529 predictor.TrimReferrersNow();
530 predictor.SerializeReferrers(&recovered_referral_list);
531 EXPECT_EQ(2U, recovered_referral_list.GetSize());
532 EXPECT_TRUE(GetDataFromSerialization(
533 motivation_url, icon_subresource_url, recovered_referral_list, &rate));
534 EXPECT_SIMILAR(rate, kRateIcon / 8);
536 // Img is below threshold, and so it gets deleted.
537 EXPECT_FALSE(GetDataFromSerialization(
538 motivation_url, img_subresource_url, recovered_referral_list, &rate));
540 for (int i = 0; i < 24; ++i)
541 predictor.TrimReferrersNow();
542 predictor.SerializeReferrers(&recovered_referral_list);
543 // Icon is also trimmed away, so entire set gets discarded.
544 EXPECT_EQ(1U, recovered_referral_list.GetSize());
545 EXPECT_FALSE(GetDataFromSerialization(
546 motivation_url, icon_subresource_url, recovered_referral_list, &rate));
547 EXPECT_FALSE(GetDataFromSerialization(
548 motivation_url, img_subresource_url, recovered_referral_list, &rate));
550 predictor.Shutdown();
554 TEST_F(PredictorTest, PriorityQueuePushPopTest) {
555 Predictor::HostNameQueue queue;
557 GURL first("http://first:80"), second("http://second:90");
559 // First check high priority queue FIFO functionality.
560 EXPECT_TRUE(queue.IsEmpty());
561 queue.Push(first, UrlInfo::LEARNED_REFERAL_MOTIVATED);
562 EXPECT_FALSE(queue.IsEmpty());
563 queue.Push(second, UrlInfo::MOUSE_OVER_MOTIVATED);
564 EXPECT_FALSE(queue.IsEmpty());
565 EXPECT_EQ(queue.Pop(), first);
566 EXPECT_FALSE(queue.IsEmpty());
567 EXPECT_EQ(queue.Pop(), second);
568 EXPECT_TRUE(queue.IsEmpty());
570 // Then check low priority queue FIFO functionality.
571 queue.Push(first, UrlInfo::PAGE_SCAN_MOTIVATED);
572 EXPECT_FALSE(queue.IsEmpty());
573 queue.Push(second, UrlInfo::OMNIBOX_MOTIVATED);
574 EXPECT_FALSE(queue.IsEmpty());
575 EXPECT_EQ(queue.Pop(), first);
576 EXPECT_FALSE(queue.IsEmpty());
577 EXPECT_EQ(queue.Pop(), second);
578 EXPECT_TRUE(queue.IsEmpty());
581 TEST_F(PredictorTest, PriorityQueueReorderTest) {
582 Predictor::HostNameQueue queue;
584 // Push all the low priority items.
585 GURL low1("http://low1:80"),
586 low2("http://low2:80"),
587 low3("http://low3:443"),
588 low4("http://low4:80"),
589 low5("http://low5:80"),
590 hi1("http://hi1:80"),
591 hi2("http://hi2:80"),
592 hi3("http://hi3:80");
594 EXPECT_TRUE(queue.IsEmpty());
595 queue.Push(low1, UrlInfo::PAGE_SCAN_MOTIVATED);
596 queue.Push(low2, UrlInfo::UNIT_TEST_MOTIVATED);
597 queue.Push(low3, UrlInfo::LINKED_MAX_MOTIVATED);
598 queue.Push(low4, UrlInfo::OMNIBOX_MOTIVATED);
599 queue.Push(low5, UrlInfo::STARTUP_LIST_MOTIVATED);
600 queue.Push(low4, UrlInfo::OMNIBOX_MOTIVATED);
602 // Push all the high prority items
603 queue.Push(hi1, UrlInfo::LEARNED_REFERAL_MOTIVATED);
604 queue.Push(hi2, UrlInfo::STATIC_REFERAL_MOTIVATED);
605 queue.Push(hi3, UrlInfo::MOUSE_OVER_MOTIVATED);
607 // Check that high priority stuff comes out first, and in FIFO order.
608 EXPECT_EQ(queue.Pop(), hi1);
609 EXPECT_EQ(queue.Pop(), hi2);
610 EXPECT_EQ(queue.Pop(), hi3);
612 // ...and then low priority strings.
613 EXPECT_EQ(queue.Pop(), low1);
614 EXPECT_EQ(queue.Pop(), low2);
615 EXPECT_EQ(queue.Pop(), low3);
616 EXPECT_EQ(queue.Pop(), low4);
617 EXPECT_EQ(queue.Pop(), low5);
618 EXPECT_EQ(queue.Pop(), low4);
620 EXPECT_TRUE(queue.IsEmpty());
623 TEST_F(PredictorTest, CanonicalizeUrl) {
624 // Base case, only handles HTTP and HTTPS.
625 EXPECT_EQ(GURL(), Predictor::CanonicalizeUrl(GURL("ftp://anything")));
627 // Remove path testing.
628 GURL long_url("http://host:999/path?query=value");
629 EXPECT_EQ(Predictor::CanonicalizeUrl(long_url), long_url.GetWithEmptyPath());
631 // Default port cannoncalization.
632 GURL implied_port("http://test");
633 GURL explicit_port("http://test:80");
634 EXPECT_EQ(Predictor::CanonicalizeUrl(implied_port),
635 Predictor::CanonicalizeUrl(explicit_port));
637 // Port is still maintained.
638 GURL port_80("http://test:80");
639 GURL port_90("http://test:90");
640 EXPECT_NE(Predictor::CanonicalizeUrl(port_80),
641 Predictor::CanonicalizeUrl(port_90));
643 // Host is still maintained.
644 GURL host_1("http://test_1");
645 GURL host_2("http://test_2");
646 EXPECT_NE(Predictor::CanonicalizeUrl(host_1),
647 Predictor::CanonicalizeUrl(host_2));
649 // Scheme is maintained (mismatch identified).
650 GURL http("http://test");
651 GURL https("https://test");
652 EXPECT_NE(Predictor::CanonicalizeUrl(http),
653 Predictor::CanonicalizeUrl(https));
656 GURL long_https("https://host:999/path?query=value");
657 EXPECT_EQ(Predictor::CanonicalizeUrl(long_https),
658 long_https.GetWithEmptyPath());
661 TEST_F(PredictorTest, DiscardPredictorResults) {
662 Predictor predictor(true);
663 predictor.SetHostResolver(host_resolver_.get());
664 ListValue referral_list;
665 predictor.SerializeReferrers(&referral_list);
666 EXPECT_EQ(1U, referral_list.GetSize());
668 GURL host_1("http://test_1");
669 GURL host_2("http://test_2");
670 predictor.LearnFromNavigation(host_1, host_2);
672 predictor.SerializeReferrers(&referral_list);
673 EXPECT_EQ(2U, referral_list.GetSize());
675 predictor.DiscardAllResults();
676 predictor.SerializeReferrers(&referral_list);
677 EXPECT_EQ(1U, referral_list.GetSize());
679 predictor.Shutdown();
682 } // namespace chrome_browser_net