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