blob: 5c4ddcecdeefd52b6f5c8e70b8f53ee72245d2e4 [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"
16#include "chrome/renderer/visitedlink_slave.h"
17#include "googleurl/src/gurl.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20namespace {
21
22// a nice long URL that we can append numbers to to get new URLs
23const char g_test_prefix[] =
24 "http://www.google.com/products/foo/index.html?id=45028640526508376&seq=";
25const int g_test_count = 1000;
26
27// Returns a test URL for index |i|
28GURL TestURL(int i) {
29 return GURL(StringPrintf("%s%d", g_test_prefix, i));
30}
31
initial.commit09911bf2008-07-26 23:55:2932std::vector<VisitedLinkSlave*> g_slaves;
33
34VisitedLinkMaster::PostNewTableEvent SynchronousBroadcastNewTableEvent;
[email protected]176aa482008-11-14 03:25:1535void SynchronousBroadcastNewTableEvent(base::SharedMemory* table) {
initial.commit09911bf2008-07-26 23:55:2936 if (table) {
37 for (std::vector<VisitedLinkSlave>::size_type i = 0;
[email protected]c2c998c2009-01-27 19:08:3938 i < g_slaves.size(); i++) {
[email protected]76aac1e2009-03-16 16:45:3639 base::SharedMemoryHandle new_handle = base::SharedMemory::NULLHandle();
[email protected]c2c998c2009-01-27 19:08:3940 table->ShareToProcess(base::GetCurrentProcessHandle(), &new_handle);
initial.commit09911bf2008-07-26 23:55:2941 g_slaves[i]->Init(new_handle);
42 }
43 }
44}
45
[email protected]c2c998c2009-01-27 19:08:3946} // namespace
47
initial.commit09911bf2008-07-26 23:55:2948class VisitedLinkTest : public testing::Test {
49 protected:
50 // Initialize the history system. This should be called before InitVisited().
51 bool InitHistory() {
52 history_service_ = new HistoryService;
[email protected]f7011fcb2009-01-28 21:54:3253 return history_service_->Init(history_dir_, NULL);
initial.commit09911bf2008-07-26 23:55:2954 }
55
56 // Initializes the visited link objects. Pass in the size that you want a
57 // freshly created table to be. 0 means use the default.
58 //
59 // |suppress_rebuild| is set when we're not testing rebuilding, see
60 // the VisitedLinkMaster constructor.
61 bool InitVisited(int initial_size, bool suppress_rebuild) {
62 // Initialize the visited link system.
63 master_.reset(new VisitedLinkMaster(NULL,
64 SynchronousBroadcastNewTableEvent,
65 history_service_, suppress_rebuild,
66 visited_file_, initial_size));
67 return master_->Init();
68 }
69
70 // May be called multiple times (some tests will do this to clear things,
71 // and TearDown will do this to make sure eveything is shiny before quitting.
72 void ClearDB() {
73 if (master_.get())
74 master_.reset(NULL);
75
76 if (history_service_.get()) {
77 history_service_->SetOnBackendDestroyTask(new MessageLoop::QuitTask);
78 history_service_->Cleanup();
79 history_service_ = NULL;
80
81 // Wait for the backend class to terminate before deleting the files and
82 // moving to the next test. Note: if this never terminates, somebody is
83 // probably leaking a reference to the history backend, so it never calls
84 // our destroy task.
85 MessageLoop::current()->Run();
86 }
87 }
88
89 // Loads the database from disk and makes sure that the same URLs are present
90 // as were generated by TestIO_Create(). This also checks the URLs with a
91 // slave to make sure it reads the data properly.
92 void Reload() {
93 // Clean up after our caller, who may have left the database open.
94 ClearDB();
95
96 ASSERT_TRUE(InitHistory());
97 ASSERT_TRUE(InitVisited(0, true));
98 master_->DebugValidate();
99
100 // check that the table has the proper number of entries
101 int used_count = master_->GetUsedCount();
102 ASSERT_EQ(used_count, g_test_count);
103
104 // Create a slave database.
105 VisitedLinkSlave slave;
[email protected]76aac1e2009-03-16 16:45:36106 base::SharedMemoryHandle new_handle = base::SharedMemory::NULLHandle();
[email protected]c2c998c2009-01-27 19:08:39107 master_->ShareToProcess(base::GetCurrentProcessHandle(), &new_handle);
initial.commit09911bf2008-07-26 23:55:29108 bool success = slave.Init(new_handle);
109 ASSERT_TRUE(success);
110 g_slaves.push_back(&slave);
111
112 bool found;
113 for (int i = 0; i < g_test_count; i++) {
114 GURL cur = TestURL(i);
115 found = master_->IsVisited(cur);
116 EXPECT_TRUE(found) << "URL " << i << "not found in master.";
117
118 found = slave.IsVisited(cur);
119 EXPECT_TRUE(found) << "URL " << i << "not found in slave.";
120 }
121
122 // test some random URL so we know that it returns false sometimes too
123 found = master_->IsVisited(GURL("http://unfound.site/"));
124 ASSERT_FALSE(found);
125 found = slave.IsVisited(GURL("http://unfound.site/"));
126 ASSERT_FALSE(found);
127
128 master_->DebugValidate();
129
130 g_slaves.clear();
131 }
132
133 // testing::Test
134 virtual void SetUp() {
135 PathService::Get(base::DIR_TEMP, &history_dir_);
[email protected]c2c998c2009-01-27 19:08:39136 history_dir_ = history_dir_.Append(FILE_PATH_LITERAL("VisitedLinkTest"));
initial.commit09911bf2008-07-26 23:55:29137 file_util::Delete(history_dir_, true);
138 file_util::CreateDirectory(history_dir_);
139
[email protected]c2c998c2009-01-27 19:08:39140 visited_file_ = history_dir_.Append(FILE_PATH_LITERAL("VisitedLinks"));
initial.commit09911bf2008-07-26 23:55:29141 }
142
143 virtual void TearDown() {
144 ClearDB();
145 file_util::Delete(history_dir_, true);
146 }
[email protected]f0a51fb52009-03-05 12:46:38147
[email protected]ab820df2008-08-26 05:55:10148 MessageLoop message_loop_;
initial.commit09911bf2008-07-26 23:55:29149
150 // Filenames for the services;
[email protected]c2c998c2009-01-27 19:08:39151 FilePath history_dir_;
152 FilePath visited_file_;
initial.commit09911bf2008-07-26 23:55:29153
154 scoped_ptr<VisitedLinkMaster> master_;
155 scoped_refptr<HistoryService> history_service_;
156};
157
initial.commit09911bf2008-07-26 23:55:29158// This test creates and reads some databases to make sure the data is
159// preserved throughout those operations.
160TEST_F(VisitedLinkTest, DatabaseIO) {
161 ASSERT_TRUE(InitHistory());
162 ASSERT_TRUE(InitVisited(0, true));
163
164 for (int i = 0; i < g_test_count; i++)
165 master_->AddURL(TestURL(i));
166
167 // Test that the database was written properly
168 Reload();
169}
170
171// Checks that we can delete things properly when there are collisions.
172TEST_F(VisitedLinkTest, Delete) {
173 static const int32 kInitialSize = 17;
174 ASSERT_TRUE(InitHistory());
175 ASSERT_TRUE(InitVisited(kInitialSize, true));
176
177 // Add a cluster from 14-17 wrapping around to 0. These will all hash to the
178 // same value.
[email protected]c2c998c2009-01-27 19:08:39179 const VisitedLinkCommon::Fingerprint kFingerprint0 = kInitialSize * 0 + 14;
180 const VisitedLinkCommon::Fingerprint kFingerprint1 = kInitialSize * 1 + 14;
181 const VisitedLinkCommon::Fingerprint kFingerprint2 = kInitialSize * 2 + 14;
182 const VisitedLinkCommon::Fingerprint kFingerprint3 = kInitialSize * 3 + 14;
183 const VisitedLinkCommon::Fingerprint kFingerprint4 = kInitialSize * 4 + 14;
initial.commit09911bf2008-07-26 23:55:29184 master_->AddFingerprint(kFingerprint0); // @14
185 master_->AddFingerprint(kFingerprint1); // @15
186 master_->AddFingerprint(kFingerprint2); // @16
187 master_->AddFingerprint(kFingerprint3); // @0
188 master_->AddFingerprint(kFingerprint4); // @1
189
190 // Deleting 14 should move the next value up one slot (we do not specify an
191 // order).
192 EXPECT_EQ(kFingerprint3, master_->hash_table_[0]);
193 master_->DeleteFingerprint(kFingerprint3, false);
[email protected]c2c998c2009-01-27 19:08:39194 VisitedLinkCommon::Fingerprint zero_fingerprint = 0;
195 EXPECT_EQ(zero_fingerprint, master_->hash_table_[1]);
196 EXPECT_NE(zero_fingerprint, master_->hash_table_[0]);
initial.commit09911bf2008-07-26 23:55:29197
198 // Deleting the other four should leave the table empty.
199 master_->DeleteFingerprint(kFingerprint0, false);
200 master_->DeleteFingerprint(kFingerprint1, false);
201 master_->DeleteFingerprint(kFingerprint2, false);
202 master_->DeleteFingerprint(kFingerprint4, false);
203
204 EXPECT_EQ(0, master_->used_items_);
205 for (int i = 0; i < kInitialSize; i++)
[email protected]c2c998c2009-01-27 19:08:39206 EXPECT_EQ(zero_fingerprint, master_->hash_table_[i]) <<
207 "Hash table has values in it.";
initial.commit09911bf2008-07-26 23:55:29208}
209
210// When we delete more than kBigDeleteThreshold we trigger different behavior
211// where the entire file is rewritten.
212TEST_F(VisitedLinkTest, BigDelete) {
213 ASSERT_TRUE(InitHistory());
214 ASSERT_TRUE(InitVisited(16381, true));
215
216 // Add the base set of URLs that won't be deleted.
217 // Reload() will test for these.
218 for (int32 i = 0; i < g_test_count; i++)
219 master_->AddURL(TestURL(i));
220
221 // Add more URLs than necessary to trigger this case.
222 const int kTestDeleteCount = VisitedLinkMaster::kBigDeleteThreshold + 2;
223 std::set<GURL> urls_to_delete;
224 for (int32 i = g_test_count; i < g_test_count + kTestDeleteCount; i++) {
225 GURL url(TestURL(i));
226 master_->AddURL(url);
227 urls_to_delete.insert(url);
228 }
229
230 master_->DeleteURLs(urls_to_delete);
231 master_->DebugValidate();
232
233 Reload();
234}
235
236TEST_F(VisitedLinkTest, DeleteAll) {
237 ASSERT_TRUE(InitHistory());
238 ASSERT_TRUE(InitVisited(0, true));
239
240 {
241 VisitedLinkSlave slave;
[email protected]76aac1e2009-03-16 16:45:36242 base::SharedMemoryHandle new_handle = base::SharedMemory::NULLHandle();
[email protected]c2c998c2009-01-27 19:08:39243 master_->ShareToProcess(base::GetCurrentProcessHandle(), &new_handle);
initial.commit09911bf2008-07-26 23:55:29244 ASSERT_TRUE(slave.Init(new_handle));
245 g_slaves.push_back(&slave);
246
247 // Add the test URLs.
248 for (int i = 0; i < g_test_count; i++) {
249 master_->AddURL(TestURL(i));
250 ASSERT_EQ(i + 1, master_->GetUsedCount());
251 }
252 master_->DebugValidate();
253
254 // Make sure the slave picked up the adds.
255 for (int i = 0; i < g_test_count; i++)
256 EXPECT_TRUE(slave.IsVisited(TestURL(i)));
257
258 // Clear the table and make sure the slave picked it up.
259 master_->DeleteAllURLs();
260 EXPECT_EQ(0, master_->GetUsedCount());
261 for (int i = 0; i < g_test_count; i++) {
262 EXPECT_FALSE(master_->IsVisited(TestURL(i)));
263 EXPECT_FALSE(slave.IsVisited(TestURL(i)));
264 }
265
266 // Close the database.
267 g_slaves.clear();
268 ClearDB();
269 }
270
271 // Reopen and validate.
272 ASSERT_TRUE(InitHistory());
273 ASSERT_TRUE(InitVisited(0, true));
274 master_->DebugValidate();
275 EXPECT_EQ(0, master_->GetUsedCount());
276 for (int i = 0; i < g_test_count; i++)
277 EXPECT_FALSE(master_->IsVisited(TestURL(i)));
278}
279
280// This tests that the master correctly resizes its tables when it gets too
281// full, notifies its slaves of the change, and updates the disk.
282TEST_F(VisitedLinkTest, Resizing) {
283 // Create a very small database.
284 const int32 initial_size = 17;
285 ASSERT_TRUE(InitHistory());
286 ASSERT_TRUE(InitVisited(initial_size, true));
287
288 // ...and a slave
289 VisitedLinkSlave slave;
[email protected]76aac1e2009-03-16 16:45:36290 base::SharedMemoryHandle new_handle = base::SharedMemory::NULLHandle();
[email protected]c2c998c2009-01-27 19:08:39291 master_->ShareToProcess(base::GetCurrentProcessHandle(), &new_handle);
initial.commit09911bf2008-07-26 23:55:29292 bool success = slave.Init(new_handle);
293 ASSERT_TRUE(success);
294 g_slaves.push_back(&slave);
295
296 int32 used_count = master_->GetUsedCount();
297 ASSERT_EQ(used_count, 0);
298
299 for (int i = 0; i < g_test_count; i++) {
300 master_->AddURL(TestURL(i));
301 used_count = master_->GetUsedCount();
302 ASSERT_EQ(i + 1, used_count);
303 }
304
305 // Verify that the table got resized sufficiently.
306 int32 table_size;
307 VisitedLinkCommon::Fingerprint* table;
308 master_->GetUsageStatistics(&table_size, &table);
309 used_count = master_->GetUsedCount();
310 ASSERT_GT(table_size, used_count);
311 ASSERT_EQ(used_count, g_test_count) <<
312 "table count doesn't match the # of things we added";
313
314 // Verify that the slave got the resize message and has the same
315 // table information.
316 int32 child_table_size;
317 VisitedLinkCommon::Fingerprint* child_table;
318 slave.GetUsageStatistics(&child_table_size, &child_table);
319 ASSERT_EQ(table_size, child_table_size);
320 for (int32 i = 0; i < table_size; i++) {
321 ASSERT_EQ(table[i], child_table[i]);
322 }
323
324 master_->DebugValidate();
325 g_slaves.clear();
326
327 // This tests that the file is written correctly by reading it in using
328 // a new database.
329 Reload();
330}
331
332// Tests that if the database doesn't exist, it will be rebuilt from history.
333TEST_F(VisitedLinkTest, Rebuild) {
334 ASSERT_TRUE(InitHistory());
335
336 // Add half of our URLs to history. This needs to be done before we
337 // initialize the visited link DB.
338 int history_count = g_test_count / 2;
339 for (int i = 0; i < history_count; i++)
340 history_service_->AddPage(TestURL(i));
341
342 // Initialize the visited link DB. Since the visited links file doesn't exist
343 // and we don't suppress history rebuilding, this will load from history.
344 ASSERT_TRUE(InitVisited(0, false));
345
346 // While the table is rebuilding, add the rest of the URLs to the visited
347 // link system. This isn't guaranteed to happen during the rebuild, so we
348 // can't be 100% sure we're testing the right thing, but in practice is.
349 // All the adds above will generally take some time queuing up on the
350 // history thread, and it will take a while to catch up to actually
351 // processing the rebuild that has queued behind it. We will generally
352 // finish adding all of the URLs before it has even found the first URL.
353 for (int i = history_count; i < g_test_count; i++)
354 master_->AddURL(TestURL(i));
355
356 // Add one more and then delete it.
357 master_->AddURL(TestURL(g_test_count));
358 std::set<GURL> deleted_urls;
359 deleted_urls.insert(TestURL(g_test_count));
360 master_->DeleteURLs(deleted_urls);
361
362 // Wait for the rebuild to complete. The task will terminate the message
363 // loop when the rebuild is done. There's no chance that the rebuild will
364 // complete before we set the task because the rebuild completion message
365 // is posted to the message loop; until we Run() it, rebuild can not
366 // complete.
367 master_->set_rebuild_complete_task(new MessageLoop::QuitTask);
368 MessageLoop::current()->Run();
369
370 // Test that all URLs were written to the database properly.
371 Reload();
372
373 // Make sure the extra one was *not* written (Reload won't test this).
374 EXPECT_FALSE(master_->IsVisited(TestURL(g_test_count)));
375}