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