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 |