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(base::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 775