Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / components / visitedlink / test / visitedlink_unittest.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <cstdio>
6 #include <string>
7 #include <vector>
8
9 #include "base/files/file_util.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/process/process_handle.h"
13 #include "base/run_loop.h"
14 #include "base/strings/string_util.h"
15 #include "base/time/time.h"
16 #include "components/visitedlink/browser/visitedlink_delegate.h"
17 #include "components/visitedlink/browser/visitedlink_event_listener.h"
18 #include "components/visitedlink/browser/visitedlink_master.h"
19 #include "components/visitedlink/common/visitedlink_messages.h"
20 #include "components/visitedlink/renderer/visitedlink_slave.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/notification_types.h"
24 #include "content/public/test/mock_render_process_host.h"
25 #include "content/public/test/test_browser_context.h"
26 #include "content/public/test/test_browser_thread_bundle.h"
27 #include "content/public/test/test_renderer_host.h"
28 #include "content/public/test/test_utils.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "url/gurl.h"
31
32 using content::BrowserThread;
33 using content::MockRenderProcessHost;
34 using content::RenderViewHostTester;
35
36 namespace visitedlink {
37
38 namespace {
39
40 typedef std::vector<GURL> URLs;
41
42 // a nice long URL that we can append numbers to to get new URLs
43 const char g_test_prefix[] =
44   "http://www.google.com/products/foo/index.html?id=45028640526508376&seq=";
45 const int g_test_count = 1000;
46
47 // Returns a test URL for index |i|
48 GURL TestURL(int i) {
49   return GURL(base::StringPrintf("%s%d", g_test_prefix, i));
50 }
51
52 std::vector<VisitedLinkSlave*> g_slaves;
53
54 class TestVisitedLinkDelegate : public VisitedLinkDelegate {
55  public:
56   void RebuildTable(const scoped_refptr<URLEnumerator>& enumerator) override;
57
58   void AddURLForRebuild(const GURL& url);
59
60  private:
61   URLs rebuild_urls_;
62 };
63
64 void TestVisitedLinkDelegate::RebuildTable(
65     const scoped_refptr<URLEnumerator>& enumerator) {
66   for (URLs::const_iterator itr = rebuild_urls_.begin();
67        itr != rebuild_urls_.end();
68        ++itr)
69     enumerator->OnURL(*itr);
70   enumerator->OnComplete(true);
71 }
72
73 void TestVisitedLinkDelegate::AddURLForRebuild(const GURL& url) {
74   rebuild_urls_.push_back(url);
75 }
76
77 class TestURLIterator : public VisitedLinkMaster::URLIterator {
78  public:
79   explicit TestURLIterator(const URLs& urls);
80
81   const GURL& NextURL() override;
82   bool HasNextURL() const override;
83
84  private:
85   URLs::const_iterator iterator_;
86   URLs::const_iterator end_;
87 };
88
89 TestURLIterator::TestURLIterator(const URLs& urls)
90     : iterator_(urls.begin()),
91       end_(urls.end()) {
92 }
93
94 const GURL& TestURLIterator::NextURL() {
95   return *(iterator_++);
96 }
97
98 bool TestURLIterator::HasNextURL() const {
99   return iterator_ != end_;
100 }
101
102 }  // namespace
103
104 class TrackingVisitedLinkEventListener : public VisitedLinkMaster::Listener {
105  public:
106   TrackingVisitedLinkEventListener()
107       : reset_count_(0),
108         add_count_(0) {}
109
110   void NewTable(base::SharedMemory* table) override {
111     if (table) {
112       for (std::vector<VisitedLinkSlave>::size_type i = 0;
113            i < g_slaves.size(); i++) {
114         base::SharedMemoryHandle new_handle = base::SharedMemory::NULLHandle();
115         table->ShareToProcess(base::GetCurrentProcessHandle(), &new_handle);
116         g_slaves[i]->OnUpdateVisitedLinks(new_handle);
117       }
118     }
119   }
120   void Add(VisitedLinkCommon::Fingerprint) override { add_count_++; }
121   void Reset() override { reset_count_++; }
122
123   void SetUp() {
124     reset_count_ = 0;
125     add_count_ = 0;
126   }
127
128   int reset_count() const { return reset_count_; }
129   int add_count() const { return add_count_; }
130
131  private:
132   int reset_count_;
133   int add_count_;
134 };
135
136 class VisitedLinkTest : public testing::Test {
137  protected:
138   // Initializes the visited link objects. Pass in the size that you want a
139   // freshly created table to be. 0 means use the default.
140   //
141   // |suppress_rebuild| is set when we're not testing rebuilding, see
142   // the VisitedLinkMaster constructor.
143   bool InitVisited(int initial_size, bool suppress_rebuild) {
144     // Initialize the visited link system.
145     master_.reset(new VisitedLinkMaster(new TrackingVisitedLinkEventListener(),
146                                         &delegate_,
147                                         true,
148                                         suppress_rebuild, visited_file_,
149                                         initial_size));
150     return master_->Init();
151   }
152
153   // May be called multiple times (some tests will do this to clear things,
154   // and TearDown will do this to make sure eveything is shiny before quitting.
155   void ClearDB() {
156     if (master_.get())
157       master_.reset(NULL);
158
159     // Wait for all pending file I/O to be completed.
160     content::RunAllBlockingPoolTasksUntilIdle();
161   }
162
163   // Loads the database from disk and makes sure that the same URLs are present
164   // as were generated by TestIO_Create(). This also checks the URLs with a
165   // slave to make sure it reads the data properly.
166   void Reload() {
167     // Clean up after our caller, who may have left the database open.
168     ClearDB();
169
170     ASSERT_TRUE(InitVisited(0, true));
171     master_->DebugValidate();
172
173     // check that the table has the proper number of entries
174     int used_count = master_->GetUsedCount();
175     ASSERT_EQ(used_count, g_test_count);
176
177     // Create a slave database.
178     VisitedLinkSlave slave;
179     base::SharedMemoryHandle new_handle = base::SharedMemory::NULLHandle();
180     master_->shared_memory()->ShareToProcess(
181         base::GetCurrentProcessHandle(), &new_handle);
182     slave.OnUpdateVisitedLinks(new_handle);
183     g_slaves.push_back(&slave);
184
185     bool found;
186     for (int i = 0; i < g_test_count; i++) {
187       GURL cur = TestURL(i);
188       found = master_->IsVisited(cur);
189       EXPECT_TRUE(found) << "URL " << i << "not found in master.";
190
191       found = slave.IsVisited(cur);
192       EXPECT_TRUE(found) << "URL " << i << "not found in slave.";
193     }
194
195     // test some random URL so we know that it returns false sometimes too
196     found = master_->IsVisited(GURL("http://unfound.site/"));
197     ASSERT_FALSE(found);
198     found = slave.IsVisited(GURL("http://unfound.site/"));
199     ASSERT_FALSE(found);
200
201     master_->DebugValidate();
202
203     g_slaves.clear();
204   }
205
206   // testing::Test
207   void SetUp() override {
208     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
209
210     history_dir_ = temp_dir_.path().AppendASCII("VisitedLinkTest");
211     ASSERT_TRUE(base::CreateDirectory(history_dir_));
212
213     visited_file_ = history_dir_.Append(FILE_PATH_LITERAL("VisitedLinks"));
214   }
215
216   void TearDown() override { ClearDB(); }
217
218   base::ScopedTempDir temp_dir_;
219
220   // Filenames for the services;
221   base::FilePath history_dir_;
222   base::FilePath visited_file_;
223
224   scoped_ptr<VisitedLinkMaster> master_;
225   TestVisitedLinkDelegate delegate_;
226   content::TestBrowserThreadBundle thread_bundle_;
227 };
228
229 // This test creates and reads some databases to make sure the data is
230 // preserved throughout those operations.
231 TEST_F(VisitedLinkTest, DatabaseIO) {
232   ASSERT_TRUE(InitVisited(0, true));
233
234   for (int i = 0; i < g_test_count; i++)
235     master_->AddURL(TestURL(i));
236
237   // Test that the database was written properly
238   Reload();
239 }
240
241 // Checks that we can delete things properly when there are collisions.
242 TEST_F(VisitedLinkTest, Delete) {
243   static const int32 kInitialSize = 17;
244   ASSERT_TRUE(InitVisited(kInitialSize, true));
245
246   // Add a cluster from 14-17 wrapping around to 0. These will all hash to the
247   // same value.
248   const VisitedLinkCommon::Fingerprint kFingerprint0 = kInitialSize * 0 + 14;
249   const VisitedLinkCommon::Fingerprint kFingerprint1 = kInitialSize * 1 + 14;
250   const VisitedLinkCommon::Fingerprint kFingerprint2 = kInitialSize * 2 + 14;
251   const VisitedLinkCommon::Fingerprint kFingerprint3 = kInitialSize * 3 + 14;
252   const VisitedLinkCommon::Fingerprint kFingerprint4 = kInitialSize * 4 + 14;
253   master_->AddFingerprint(kFingerprint0, false);  // @14
254   master_->AddFingerprint(kFingerprint1, false);  // @15
255   master_->AddFingerprint(kFingerprint2, false);  // @16
256   master_->AddFingerprint(kFingerprint3, false);  // @0
257   master_->AddFingerprint(kFingerprint4, false);  // @1
258
259   // Deleting 14 should move the next value up one slot (we do not specify an
260   // order).
261   EXPECT_EQ(kFingerprint3, master_->hash_table_[0]);
262   master_->DeleteFingerprint(kFingerprint3, false);
263   VisitedLinkCommon::Fingerprint zero_fingerprint = 0;
264   EXPECT_EQ(zero_fingerprint, master_->hash_table_[1]);
265   EXPECT_NE(zero_fingerprint, master_->hash_table_[0]);
266
267   // Deleting the other four should leave the table empty.
268   master_->DeleteFingerprint(kFingerprint0, false);
269   master_->DeleteFingerprint(kFingerprint1, false);
270   master_->DeleteFingerprint(kFingerprint2, false);
271   master_->DeleteFingerprint(kFingerprint4, false);
272
273   EXPECT_EQ(0, master_->used_items_);
274   for (int i = 0; i < kInitialSize; i++)
275     EXPECT_EQ(zero_fingerprint, master_->hash_table_[i]) <<
276         "Hash table has values in it.";
277 }
278
279 // When we delete more than kBigDeleteThreshold we trigger different behavior
280 // where the entire file is rewritten.
281 TEST_F(VisitedLinkTest, BigDelete) {
282   ASSERT_TRUE(InitVisited(16381, true));
283
284   // Add the base set of URLs that won't be deleted.
285   // Reload() will test for these.
286   for (int32 i = 0; i < g_test_count; i++)
287     master_->AddURL(TestURL(i));
288
289   // Add more URLs than necessary to trigger this case.
290   const int kTestDeleteCount = VisitedLinkMaster::kBigDeleteThreshold + 2;
291   URLs urls_to_delete;
292   for (int32 i = g_test_count; i < g_test_count + kTestDeleteCount; i++) {
293     GURL url(TestURL(i));
294     master_->AddURL(url);
295     urls_to_delete.push_back(url);
296   }
297
298   TestURLIterator iterator(urls_to_delete);
299   master_->DeleteURLs(&iterator);
300   master_->DebugValidate();
301
302   Reload();
303 }
304
305 TEST_F(VisitedLinkTest, DeleteAll) {
306   ASSERT_TRUE(InitVisited(0, true));
307
308   {
309     VisitedLinkSlave slave;
310     base::SharedMemoryHandle new_handle = base::SharedMemory::NULLHandle();
311     master_->shared_memory()->ShareToProcess(
312         base::GetCurrentProcessHandle(), &new_handle);
313     slave.OnUpdateVisitedLinks(new_handle);
314     g_slaves.push_back(&slave);
315
316     // Add the test URLs.
317     for (int i = 0; i < g_test_count; i++) {
318       master_->AddURL(TestURL(i));
319       ASSERT_EQ(i + 1, master_->GetUsedCount());
320     }
321     master_->DebugValidate();
322
323     // Make sure the slave picked up the adds.
324     for (int i = 0; i < g_test_count; i++)
325       EXPECT_TRUE(slave.IsVisited(TestURL(i)));
326
327     // Clear the table and make sure the slave picked it up.
328     master_->DeleteAllURLs();
329     EXPECT_EQ(0, master_->GetUsedCount());
330     for (int i = 0; i < g_test_count; i++) {
331       EXPECT_FALSE(master_->IsVisited(TestURL(i)));
332       EXPECT_FALSE(slave.IsVisited(TestURL(i)));
333     }
334
335     // Close the database.
336     g_slaves.clear();
337     ClearDB();
338   }
339
340   // Reopen and validate.
341   ASSERT_TRUE(InitVisited(0, true));
342   master_->DebugValidate();
343   EXPECT_EQ(0, master_->GetUsedCount());
344   for (int i = 0; i < g_test_count; i++)
345     EXPECT_FALSE(master_->IsVisited(TestURL(i)));
346 }
347
348 // This tests that the master correctly resizes its tables when it gets too
349 // full, notifies its slaves of the change, and updates the disk.
350 TEST_F(VisitedLinkTest, Resizing) {
351   // Create a very small database.
352   const int32 initial_size = 17;
353   ASSERT_TRUE(InitVisited(initial_size, true));
354
355   // ...and a slave
356   VisitedLinkSlave slave;
357   base::SharedMemoryHandle new_handle = base::SharedMemory::NULLHandle();
358   master_->shared_memory()->ShareToProcess(
359       base::GetCurrentProcessHandle(), &new_handle);
360   slave.OnUpdateVisitedLinks(new_handle);
361   g_slaves.push_back(&slave);
362
363   int32 used_count = master_->GetUsedCount();
364   ASSERT_EQ(used_count, 0);
365
366   for (int i = 0; i < g_test_count; i++) {
367     master_->AddURL(TestURL(i));
368     used_count = master_->GetUsedCount();
369     ASSERT_EQ(i + 1, used_count);
370   }
371
372   // Verify that the table got resized sufficiently.
373   int32 table_size;
374   VisitedLinkCommon::Fingerprint* table;
375   master_->GetUsageStatistics(&table_size, &table);
376   used_count = master_->GetUsedCount();
377   ASSERT_GT(table_size, used_count);
378   ASSERT_EQ(used_count, g_test_count) <<
379                 "table count doesn't match the # of things we added";
380
381   // Verify that the slave got the resize message and has the same
382   // table information.
383   int32 child_table_size;
384   VisitedLinkCommon::Fingerprint* child_table;
385   slave.GetUsageStatistics(&child_table_size, &child_table);
386   ASSERT_EQ(table_size, child_table_size);
387   for (int32 i = 0; i < table_size; i++) {
388     ASSERT_EQ(table[i], child_table[i]);
389   }
390
391   master_->DebugValidate();
392   g_slaves.clear();
393
394   // This tests that the file is written correctly by reading it in using
395   // a new database.
396   Reload();
397 }
398
399 // Tests that if the database doesn't exist, it will be rebuilt from history.
400 TEST_F(VisitedLinkTest, Rebuild) {
401   // Add half of our URLs to history. This needs to be done before we
402   // initialize the visited link DB.
403   int history_count = g_test_count / 2;
404   for (int i = 0; i < history_count; i++)
405     delegate_.AddURLForRebuild(TestURL(i));
406
407   // Initialize the visited link DB. Since the visited links file doesn't exist
408   // and we don't suppress history rebuilding, this will load from history.
409   ASSERT_TRUE(InitVisited(0, false));
410
411   // While the table is rebuilding, add the rest of the URLs to the visited
412   // link system. This isn't guaranteed to happen during the rebuild, so we
413   // can't be 100% sure we're testing the right thing, but in practice is.
414   // All the adds above will generally take some time queuing up on the
415   // history thread, and it will take a while to catch up to actually
416   // processing the rebuild that has queued behind it. We will generally
417   // finish adding all of the URLs before it has even found the first URL.
418   for (int i = history_count; i < g_test_count; i++)
419     master_->AddURL(TestURL(i));
420
421   // Add one more and then delete it.
422   master_->AddURL(TestURL(g_test_count));
423   URLs urls_to_delete;
424   urls_to_delete.push_back(TestURL(g_test_count));
425   TestURLIterator iterator(urls_to_delete);
426   master_->DeleteURLs(&iterator);
427
428   // Wait for the rebuild to complete. The task will terminate the message
429   // loop when the rebuild is done. There's no chance that the rebuild will
430   // complete before we set the task because the rebuild completion message
431   // is posted to the message loop; until we Run() it, rebuild can not
432   // complete.
433   base::RunLoop run_loop;
434   master_->set_rebuild_complete_task(run_loop.QuitClosure());
435   run_loop.Run();
436
437   // Test that all URLs were written to the database properly.
438   Reload();
439
440   // Make sure the extra one was *not* written (Reload won't test this).
441   EXPECT_FALSE(master_->IsVisited(TestURL(g_test_count)));
442 }
443
444 // Test that importing a large number of URLs will work
445 TEST_F(VisitedLinkTest, BigImport) {
446   ASSERT_TRUE(InitVisited(0, false));
447
448   // Before the table rebuilds, add a large number of URLs
449   int total_count = VisitedLinkMaster::kDefaultTableSize + 10;
450   for (int i = 0; i < total_count; i++)
451     master_->AddURL(TestURL(i));
452
453   // Wait for the rebuild to complete.
454   base::RunLoop run_loop;
455   master_->set_rebuild_complete_task(run_loop.QuitClosure());
456   run_loop.Run();
457
458   // Ensure that the right number of URLs are present
459   int used_count = master_->GetUsedCount();
460   ASSERT_EQ(used_count, total_count);
461 }
462
463 TEST_F(VisitedLinkTest, Listener) {
464   ASSERT_TRUE(InitVisited(0, true));
465
466   // Add test URLs.
467   for (int i = 0; i < g_test_count; i++) {
468     master_->AddURL(TestURL(i));
469     ASSERT_EQ(i + 1, master_->GetUsedCount());
470   }
471
472   // Delete an URL.
473   URLs urls_to_delete;
474   urls_to_delete.push_back(TestURL(0));
475   TestURLIterator iterator(urls_to_delete);
476   master_->DeleteURLs(&iterator);
477
478   // ... and all of the remaining ones.
479   master_->DeleteAllURLs();
480
481   TrackingVisitedLinkEventListener* listener =
482       static_cast<TrackingVisitedLinkEventListener*>(master_->GetListener());
483
484   // Verify that VisitedLinkMaster::Listener::Add was called for each added URL.
485   EXPECT_EQ(g_test_count, listener->add_count());
486   // Verify that VisitedLinkMaster::Listener::Reset was called both when one and
487   // all URLs are deleted.
488   EXPECT_EQ(2, listener->reset_count());
489 }
490
491 class VisitCountingContext : public content::TestBrowserContext {
492  public:
493   VisitCountingContext()
494       : add_count_(0),
495         add_event_count_(0),
496         reset_event_count_(0),
497         new_table_count_(0) {}
498
499   void CountAddEvent(int by) {
500     add_count_ += by;
501     add_event_count_++;
502   }
503
504   void CountResetEvent() {
505     reset_event_count_++;
506   }
507
508   void CountNewTable() {
509     new_table_count_++;
510   }
511
512   int add_count() const { return add_count_; }
513   int add_event_count() const { return add_event_count_; }
514   int reset_event_count() const { return reset_event_count_; }
515   int new_table_count() const { return new_table_count_; }
516
517  private:
518   int add_count_;
519   int add_event_count_;
520   int reset_event_count_;
521   int new_table_count_;
522 };
523
524 // Stub out as little as possible, borrowing from RenderProcessHost.
525 class VisitRelayingRenderProcessHost : public MockRenderProcessHost {
526  public:
527   explicit VisitRelayingRenderProcessHost(
528       content::BrowserContext* browser_context)
529           : MockRenderProcessHost(browser_context), widgets_(0) {
530     content::NotificationService::current()->Notify(
531         content::NOTIFICATION_RENDERER_PROCESS_CREATED,
532         content::Source<RenderProcessHost>(this),
533         content::NotificationService::NoDetails());
534   }
535   ~VisitRelayingRenderProcessHost() override {
536     content::NotificationService::current()->Notify(
537         content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
538         content::Source<content::RenderProcessHost>(this),
539         content::NotificationService::NoDetails());
540   }
541
542   void WidgetRestored() override { widgets_++; }
543   void WidgetHidden() override { widgets_--; }
544   int VisibleWidgetCount() const override { return widgets_; }
545
546   bool Send(IPC::Message* msg) override {
547     VisitCountingContext* counting_context =
548         static_cast<VisitCountingContext*>(
549             GetBrowserContext());
550
551     if (msg->type() == ChromeViewMsg_VisitedLink_Add::ID) {
552       PickleIterator iter(*msg);
553       std::vector<uint64> fingerprints;
554       CHECK(IPC::ReadParam(msg, &iter, &fingerprints));
555       counting_context->CountAddEvent(fingerprints.size());
556     } else if (msg->type() == ChromeViewMsg_VisitedLink_Reset::ID) {
557       counting_context->CountResetEvent();
558     } else if (msg->type() == ChromeViewMsg_VisitedLink_NewTable::ID) {
559       counting_context->CountNewTable();
560     }
561
562     delete msg;
563     return true;
564   }
565
566  private:
567   int widgets_;
568
569   DISALLOW_COPY_AND_ASSIGN(VisitRelayingRenderProcessHost);
570 };
571
572 class VisitedLinkRenderProcessHostFactory
573     : public content::RenderProcessHostFactory {
574  public:
575   VisitedLinkRenderProcessHostFactory()
576       : content::RenderProcessHostFactory() {}
577   content::RenderProcessHost* CreateRenderProcessHost(
578       content::BrowserContext* browser_context,
579       content::SiteInstance* site_instance) const override {
580     return new VisitRelayingRenderProcessHost(browser_context);
581   }
582
583  private:
584   DISALLOW_COPY_AND_ASSIGN(VisitedLinkRenderProcessHostFactory);
585 };
586
587 class VisitedLinkEventsTest : public content::RenderViewHostTestHarness {
588  public:
589   void SetUp() override {
590     SetRenderProcessHostFactory(&vc_rph_factory_);
591     content::RenderViewHostTestHarness::SetUp();
592   }
593
594   content::BrowserContext* CreateBrowserContext() override {
595     VisitCountingContext* context = new VisitCountingContext();
596     master_.reset(new VisitedLinkMaster(context, &delegate_, true));
597     master_->Init();
598     return context;
599   }
600
601   VisitCountingContext* context() {
602     return static_cast<VisitCountingContext*>(browser_context());
603   }
604
605   VisitedLinkMaster* master() const {
606     return master_.get();
607   }
608
609   void WaitForCoalescense() {
610     // Let the timer fire.
611     //
612     // TODO(ajwong): This is horrid! What is the right synchronization method?
613     base::RunLoop run_loop;
614     base::MessageLoop::current()->PostDelayedTask(
615         FROM_HERE,
616         run_loop.QuitClosure(),
617         base::TimeDelta::FromMilliseconds(110));
618     run_loop.Run();
619   }
620
621  protected:
622   VisitedLinkRenderProcessHostFactory vc_rph_factory_;
623
624  private:
625   TestVisitedLinkDelegate delegate_;
626   scoped_ptr<VisitedLinkMaster> master_;
627 };
628
629 TEST_F(VisitedLinkEventsTest, Coalescense) {
630   // add some URLs to master.
631   // Add a few URLs.
632   master()->AddURL(GURL("http://acidtests.org/"));
633   master()->AddURL(GURL("http://google.com/"));
634   master()->AddURL(GURL("http://chromium.org/"));
635   // Just for kicks, add a duplicate URL. This shouldn't increase the resulting
636   master()->AddURL(GURL("http://acidtests.org/"));
637
638   // Make sure that coalescing actually occurs. There should be no links or
639   // events received by the renderer.
640   EXPECT_EQ(0, context()->add_count());
641   EXPECT_EQ(0, context()->add_event_count());
642
643   WaitForCoalescense();
644
645   // We now should have 3 entries added in 1 event.
646   EXPECT_EQ(3, context()->add_count());
647   EXPECT_EQ(1, context()->add_event_count());
648
649   // Test whether the coalescing continues by adding a few more URLs.
650   master()->AddURL(GURL("http://google.com/chrome/"));
651   master()->AddURL(GURL("http://webkit.org/"));
652   master()->AddURL(GURL("http://acid3.acidtests.org/"));
653
654   WaitForCoalescense();
655
656   // We should have 6 entries added in 2 events.
657   EXPECT_EQ(6, context()->add_count());
658   EXPECT_EQ(2, context()->add_event_count());
659
660   // Test whether duplicate entries produce add events.
661   master()->AddURL(GURL("http://acidtests.org/"));
662
663   WaitForCoalescense();
664
665   // We should have no change in results.
666   EXPECT_EQ(6, context()->add_count());
667   EXPECT_EQ(2, context()->add_event_count());
668
669   // Ensure that the coalescing does not resume after resetting.
670   master()->AddURL(GURL("http://build.chromium.org/"));
671   master()->DeleteAllURLs();
672
673   WaitForCoalescense();
674
675   // We should have no change in results except for one new reset event.
676   EXPECT_EQ(6, context()->add_count());
677   EXPECT_EQ(2, context()->add_event_count());
678   EXPECT_EQ(1, context()->reset_event_count());
679 }
680
681 TEST_F(VisitedLinkEventsTest, Basics) {
682   RenderViewHostTester::For(rvh())->CreateRenderView(
683       base::string16(), MSG_ROUTING_NONE, MSG_ROUTING_NONE, -1, false);
684
685   // Add a few URLs.
686   master()->AddURL(GURL("http://acidtests.org/"));
687   master()->AddURL(GURL("http://google.com/"));
688   master()->AddURL(GURL("http://chromium.org/"));
689
690   WaitForCoalescense();
691
692   // We now should have 1 add event.
693   EXPECT_EQ(1, context()->add_event_count());
694   EXPECT_EQ(0, context()->reset_event_count());
695
696   master()->DeleteAllURLs();
697
698   WaitForCoalescense();
699
700   // We should have no change in add results, plus one new reset event.
701   EXPECT_EQ(1, context()->add_event_count());
702   EXPECT_EQ(1, context()->reset_event_count());
703 }
704
705 TEST_F(VisitedLinkEventsTest, TabVisibility) {
706   RenderViewHostTester::For(rvh())->CreateRenderView(
707       base::string16(), MSG_ROUTING_NONE, MSG_ROUTING_NONE, -1, false);
708
709   // Simulate tab becoming inactive.
710   RenderViewHostTester::For(rvh())->SimulateWasHidden();
711
712   // Add a few URLs.
713   master()->AddURL(GURL("http://acidtests.org/"));
714   master()->AddURL(GURL("http://google.com/"));
715   master()->AddURL(GURL("http://chromium.org/"));
716
717   WaitForCoalescense();
718
719   // We shouldn't have any events.
720   EXPECT_EQ(0, context()->add_event_count());
721   EXPECT_EQ(0, context()->reset_event_count());
722
723   // Simulate the tab becoming active.
724   RenderViewHostTester::For(rvh())->SimulateWasShown();
725
726   // We should now have 3 add events, still no reset events.
727   EXPECT_EQ(1, context()->add_event_count());
728   EXPECT_EQ(0, context()->reset_event_count());
729
730   // Deactivate the tab again.
731   RenderViewHostTester::For(rvh())->SimulateWasHidden();
732
733   // Add a bunch of URLs (over 50) to exhaust the link event buffer.
734   for (int i = 0; i < 100; i++)
735     master()->AddURL(TestURL(i));
736
737   WaitForCoalescense();
738
739   // Again, no change in events until tab is active.
740   EXPECT_EQ(1, context()->add_event_count());
741   EXPECT_EQ(0, context()->reset_event_count());
742
743   // Activate the tab.
744   RenderViewHostTester::For(rvh())->SimulateWasShown();
745
746   // We should have only one more reset event.
747   EXPECT_EQ(1, context()->add_event_count());
748   EXPECT_EQ(1, context()->reset_event_count());
749 }
750
751 // Tests that VisitedLink ignores renderer process creation notification for a
752 // different context.
753 TEST_F(VisitedLinkEventsTest, IgnoreRendererCreationFromDifferentContext) {
754   VisitCountingContext different_context;
755   VisitRelayingRenderProcessHost different_process_host(&different_context);
756
757   content::NotificationService::current()->Notify(
758       content::NOTIFICATION_RENDERER_PROCESS_CREATED,
759       content::Source<content::RenderProcessHost>(&different_process_host),
760       content::NotificationService::NoDetails());
761   WaitForCoalescense();
762
763   EXPECT_EQ(0, different_context.new_table_count());
764 }
765
766 }  // namespace visitedlink