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