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