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