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