OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <algorithm> | 5 #include "base/memory/ref_counted.h" |
6 #include <fstream> | |
7 | |
8 #include "base/file_path.h" | |
9 #include "base/file_util.h" | |
10 #include "base/message_loop.h" | |
11 #include "base/path_service.h" | |
12 #include "base/scoped_temp_dir.h" | |
13 #include "base/string16.h" | |
14 #include "base/string_util.h" | 6 #include "base/string_util.h" |
| 7 #include "base/stringprintf.h" |
| 8 #include "base/threading/sequenced_worker_pool.h" |
15 #include "base/utf_string_conversions.h" | 9 #include "base/utf_string_conversions.h" |
16 #include "chrome/browser/autocomplete/autocomplete_provider.h" | 10 #include "chrome/browser/autocomplete/autocomplete_provider.h" |
17 #include "chrome/browser/history/history.h" | 11 #include "chrome/browser/bookmarks/bookmark_model.h" |
18 #include "chrome/browser/history/history_backend.h" | 12 #include "chrome/browser/bookmarks/bookmark_model_factory.h" |
19 #include "chrome/browser/history/history_database.h" | |
20 #include "chrome/browser/history/history_notifications.h" | 13 #include "chrome/browser/history/history_notifications.h" |
21 #include "chrome/browser/history/history_service_factory.h" | 14 #include "chrome/browser/history/in_memory_url_cache_database.h" |
22 #include "chrome/browser/history/in_memory_url_index.h" | 15 #include "chrome/browser/history/in_memory_url_index_base_unittest.h" |
23 #include "chrome/browser/history/in_memory_url_index_types.h" | 16 #include "chrome/browser/history/in_memory_url_index_types.h" |
24 #include "chrome/browser/history/url_index_private_data.h" | 17 #include "chrome/browser/history/url_index_private_data.h" |
| 18 #include "chrome/common/chrome_constants.h" |
25 #include "chrome/common/chrome_notification_types.h" | 19 #include "chrome/common/chrome_notification_types.h" |
26 #include "chrome/common/chrome_paths.h" | 20 #include "chrome/test/base/ui_test_utils.h" |
27 #include "chrome/test/base/testing_profile.h" | 21 #include "content/public/browser/notification_service.h" |
28 #include "content/public/browser/notification_details.h" | |
29 #include "content/public/browser/notification_source.h" | 22 #include "content/public/browser/notification_source.h" |
30 #include "content/public/test/test_browser_thread.h" | 23 #include "sql/connection.h" |
31 #include "sql/transaction.h" | 24 #include "third_party/sqlite/sqlite3.h" |
32 #include "testing/gtest/include/gtest/gtest.h" | |
33 | 25 |
34 using content::BrowserThread; | 26 using history::String16SetFromString16; |
35 | 27 |
36 // The test version of the history url database table ('url') is contained in | 28 //------------------------------------------------------------------------------ |
37 // a database file created from a text file('url_history_provider_test.db.txt'). | 29 |
38 // The only difference between this table and a live 'urls' table from a | 30 // This class overrides the standard TestingProfile's disabling of the |
39 // profile is that the last_visit_time column in the test table contains a | 31 // InMemoryURLIndexCacheDatabase. |
40 // number specifying the number of days relative to 'today' to which the | 32 class CacheTestingProfile : public TestingProfile { |
41 // absolute time should be set during the test setup stage. | 33 public: |
42 // | 34 // Creates a testing profile but enables the cache database. |
43 // The format of the test database text file is of a SQLite .dump file. | 35 bool InitHistoryService(HistoryService* history_service, |
44 // Note that only lines whose first character is an upper-case letter are | 36 bool no_db) OVERRIDE; |
45 // processed when creating the test database. | 37 }; |
| 38 |
| 39 bool CacheTestingProfile::InitHistoryService(HistoryService* history_service, |
| 40 bool no_db) { |
| 41 DCHECK(history_service); |
| 42 return history_service->Init(GetPath(), |
| 43 reinterpret_cast<BookmarkService*>( |
| 44 BookmarkModelFactory::GetForProfile(this)), |
| 45 no_db, false); |
| 46 } |
46 | 47 |
47 namespace history { | 48 namespace history { |
48 | 49 |
49 // ----------------------------------------------------------------------------- | 50 // ----------------------------------------------------------------------------- |
50 | 51 |
51 // Observer class so the unit tests can wait while the cache is being saved. | 52 // Creates a URLRow with basic data for |url|, |title|, |visit_count|, |
52 class CacheFileSaverObserver : public InMemoryURLIndex::SaveCacheObserver { | 53 // |typed_count| and |id|. |last_visit_ago| gives the number of days from now |
53 public: | 54 // to which to set the URL's last_visit. |
54 explicit CacheFileSaverObserver(MessageLoop* loop); | 55 URLRow MakeURLRowWithID(const char* url, |
55 virtual void OnCacheSaveFinished(bool succeeded) OVERRIDE; | 56 const char* title, |
| 57 int visit_count, |
| 58 int last_visit_ago, |
| 59 int typed_count, |
| 60 URLID id); |
56 | 61 |
57 MessageLoop* loop_; | 62 class InMemoryURLIndexTest : public InMemoryURLIndexBaseTest { |
58 bool succeeded_; | 63 protected: |
59 DISALLOW_COPY_AND_ASSIGN(CacheFileSaverObserver); | 64 void SetUp() OVERRIDE; |
60 }; | |
61 | 65 |
62 CacheFileSaverObserver::CacheFileSaverObserver(MessageLoop* loop) | 66 // Provides custom test data file name. |
63 : loop_(loop), | 67 virtual FilePath::StringType TestDBName() const OVERRIDE; |
64 succeeded_(false) { | |
65 DCHECK(loop); | |
66 } | |
67 | |
68 void CacheFileSaverObserver::OnCacheSaveFinished(bool succeeded) { | |
69 succeeded_ = succeeded; | |
70 loop_->Quit(); | |
71 } | |
72 | |
73 // Observer class so the unit tests can wait while the cache is being restored. | |
74 class CacheFileReaderObserver : public InMemoryURLIndex::RestoreCacheObserver { | |
75 public: | |
76 explicit CacheFileReaderObserver(MessageLoop* loop); | |
77 virtual void OnCacheRestoreFinished(bool succeeded) OVERRIDE; | |
78 | |
79 MessageLoop* loop_; | |
80 bool succeeded_; | |
81 DISALLOW_COPY_AND_ASSIGN(CacheFileReaderObserver); | |
82 }; | |
83 | |
84 CacheFileReaderObserver::CacheFileReaderObserver(MessageLoop* loop) | |
85 : loop_(loop), | |
86 succeeded_(false) { | |
87 DCHECK(loop); | |
88 } | |
89 | |
90 void CacheFileReaderObserver::OnCacheRestoreFinished(bool succeeded) { | |
91 succeeded_ = succeeded; | |
92 loop_->Quit(); | |
93 } | |
94 | |
95 // ----------------------------------------------------------------------------- | |
96 | |
97 class InMemoryURLIndexTest : public testing::Test { | |
98 public: | |
99 InMemoryURLIndexTest(); | |
100 | |
101 protected: | |
102 // Test setup. | |
103 virtual void SetUp(); | |
104 | |
105 // Allows the database containing the test data to be customized by | |
106 // subclasses. | |
107 virtual FilePath::StringType TestDBName() const; | |
108 | |
109 #if 0 | |
110 // Convenience function to create a URLRow with basic data for |url|, |title|, | |
111 // |visit_count|, and |typed_count|. |last_visit_ago| gives the number of | |
112 // days from now to set the URL's last_visit. | |
113 URLRow MakeURLRow(const char* url, | |
114 const char* title, | |
115 int visit_count, | |
116 int last_visit_ago, | |
117 int typed_count); | |
118 | |
119 // Convenience functions for easily creating vectors of search terms. | |
120 String16Vector Make1Term(const char* term) const; | |
121 String16Vector Make2Terms(const char* term_1, const char* term_2) const; | |
122 | |
123 // Convenience function for GetTopicalityScore() that builds the | |
124 // term match and word break information automatically that are needed | |
125 // to call GetTopicalityScore(). It only works for scoring a single term, | |
126 // not multiple terms. | |
127 float GetTopicalityScoreOfTermAgainstURLAndTitle(const string16& term, | |
128 const string16& url, | |
129 const string16& title); | |
130 #endif | |
131 | 68 |
132 // Validates that the given |term| is contained in |cache| and that it is | 69 // Validates that the given |term| is contained in |cache| and that it is |
133 // marked as in-use. | 70 // marked as in-use. |
134 void CheckTerm(const URLIndexPrivateData::SearchTermCacheMap& cache, | 71 void CheckTerm(const URLIndexPrivateData::SearchTermCacheMap& cache, |
135 string16 term) const; | 72 string16 term) const; |
136 | |
137 // Pass-through function to simplify our friendship with HistoryService. | |
138 sql::Connection& GetDB(); | |
139 | |
140 // Pass-through functions to simplify our friendship with InMemoryURLIndex. | |
141 URLIndexPrivateData* GetPrivateData() const; | |
142 void ClearPrivateData(); | |
143 void set_history_dir(const FilePath& dir_path); | |
144 bool GetCacheFilePath(FilePath* file_path) const; | |
145 void PostRestoreFromCacheFileTask(); | |
146 void PostSaveToCacheFileTask(); | |
147 void Observe(int notification_type, | |
148 const content::NotificationSource& source, | |
149 const content::NotificationDetails& details); | |
150 const std::set<std::string>& scheme_whitelist(); | |
151 | |
152 | |
153 // Pass-through functions to simplify our friendship with URLIndexPrivateData. | |
154 bool UpdateURL(const URLRow& row); | |
155 bool DeleteURL(const GURL& url); | |
156 | |
157 // Data verification helper functions. | |
158 void ExpectPrivateDataNotEmpty(const URLIndexPrivateData& data); | |
159 void ExpectPrivateDataEmpty(const URLIndexPrivateData& data); | |
160 void ExpectPrivateDataEqual(const URLIndexPrivateData& expected, | |
161 const URLIndexPrivateData& actual); | |
162 | |
163 MessageLoopForUI message_loop_; | |
164 content::TestBrowserThread ui_thread_; | |
165 content::TestBrowserThread file_thread_; | |
166 TestingProfile profile_; | |
167 | |
168 scoped_ptr<InMemoryURLIndex> url_index_; | |
169 HistoryDatabase* history_database_; | |
170 }; | 73 }; |
171 | 74 |
172 InMemoryURLIndexTest::InMemoryURLIndexTest() | |
173 : ui_thread_(content::BrowserThread::UI, &message_loop_), | |
174 file_thread_(content::BrowserThread::FILE, &message_loop_) { | |
175 } | |
176 | |
177 sql::Connection& InMemoryURLIndexTest::GetDB() { | |
178 return history_database_->GetDB(); | |
179 } | |
180 | |
181 URLIndexPrivateData* InMemoryURLIndexTest::GetPrivateData() const { | |
182 DCHECK(url_index_->private_data()); | |
183 return url_index_->private_data(); | |
184 } | |
185 | |
186 void InMemoryURLIndexTest::ClearPrivateData() { | |
187 return url_index_->ClearPrivateData(); | |
188 } | |
189 | |
190 void InMemoryURLIndexTest::set_history_dir(const FilePath& dir_path) { | |
191 return url_index_->set_history_dir(dir_path); | |
192 } | |
193 | |
194 bool InMemoryURLIndexTest::GetCacheFilePath(FilePath* file_path) const { | |
195 DCHECK(file_path); | |
196 return url_index_->GetCacheFilePath(file_path); | |
197 } | |
198 | |
199 void InMemoryURLIndexTest::PostRestoreFromCacheFileTask() { | |
200 url_index_->PostRestoreFromCacheFileTask(); | |
201 } | |
202 | |
203 void InMemoryURLIndexTest::PostSaveToCacheFileTask() { | |
204 url_index_->PostSaveToCacheFileTask(); | |
205 } | |
206 | |
207 void InMemoryURLIndexTest::Observe( | |
208 int notification_type, | |
209 const content::NotificationSource& source, | |
210 const content::NotificationDetails& details) { | |
211 url_index_->Observe(notification_type, source, details); | |
212 } | |
213 | |
214 const std::set<std::string>& InMemoryURLIndexTest::scheme_whitelist() { | |
215 return url_index_->scheme_whitelist(); | |
216 } | |
217 | |
218 bool InMemoryURLIndexTest::UpdateURL(const URLRow& row) { | |
219 return GetPrivateData()->UpdateURL(row, url_index_->languages_, | |
220 url_index_->scheme_whitelist_); | |
221 } | |
222 | |
223 bool InMemoryURLIndexTest::DeleteURL(const GURL& url) { | |
224 return GetPrivateData()->DeleteURL(url); | |
225 } | |
226 | |
227 void InMemoryURLIndexTest::SetUp() { | 75 void InMemoryURLIndexTest::SetUp() { |
228 // We cannot access the database until the backend has been loaded. | 76 // Set things up without enabling the cache database. |
229 profile_.CreateHistoryService(true, false); | 77 InMemoryURLIndexBaseTest::SetUp(); |
230 profile_.CreateBookmarkModel(true); | 78 LoadIndex(); |
231 profile_.BlockUntilBookmarkModelLoaded(); | |
232 profile_.BlockUntilHistoryProcessesPendingRequests(); | |
233 HistoryService* history_service = | |
234 HistoryServiceFactory::GetForProfile(&profile_, | |
235 Profile::EXPLICIT_ACCESS); | |
236 ASSERT_TRUE(history_service); | |
237 HistoryBackend* backend = history_service->history_backend_.get(); | |
238 history_database_ = backend->db(); | |
239 | |
240 // Create and populate a working copy of the URL history database. | |
241 FilePath history_proto_path; | |
242 PathService::Get(chrome::DIR_TEST_DATA, &history_proto_path); | |
243 history_proto_path = history_proto_path.Append( | |
244 FILE_PATH_LITERAL("History")); | |
245 history_proto_path = history_proto_path.Append(TestDBName()); | |
246 EXPECT_TRUE(file_util::PathExists(history_proto_path)); | |
247 | |
248 std::ifstream proto_file(history_proto_path.value().c_str()); | |
249 static const size_t kCommandBufferMaxSize = 2048; | |
250 char sql_cmd_line[kCommandBufferMaxSize]; | |
251 | |
252 sql::Connection& db(GetDB()); | |
253 ASSERT_TRUE(db.is_open()); | |
254 { | |
255 sql::Transaction transaction(&db); | |
256 transaction.Begin(); | |
257 while (!proto_file.eof()) { | |
258 proto_file.getline(sql_cmd_line, kCommandBufferMaxSize); | |
259 if (!proto_file.eof()) { | |
260 // We only process lines which begin with a upper-case letter. | |
261 // TODO(mrossetti): Can iswupper() be used here? | |
262 if (sql_cmd_line[0] >= 'A' && sql_cmd_line[0] <= 'Z') { | |
263 std::string sql_cmd(sql_cmd_line); | |
264 sql::Statement sql_stmt(db.GetUniqueStatement(sql_cmd_line)); | |
265 EXPECT_TRUE(sql_stmt.Run()); | |
266 } | |
267 } | |
268 } | |
269 transaction.Commit(); | |
270 } | |
271 proto_file.close(); | |
272 | |
273 // Update the last_visit_time table column | |
274 // such that it represents a time relative to 'now'. | |
275 sql::Statement statement(db.GetUniqueStatement( | |
276 "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls;")); | |
277 ASSERT_TRUE(statement.is_valid()); | |
278 base::Time time_right_now = base::Time::NowFromSystemTime(); | |
279 base::TimeDelta day_delta = base::TimeDelta::FromDays(1); | |
280 { | |
281 sql::Transaction transaction(&db); | |
282 transaction.Begin(); | |
283 while (statement.Step()) { | |
284 URLRow row; | |
285 history_database_->FillURLRow(statement, &row); | |
286 base::Time last_visit = time_right_now; | |
287 for (int64 i = row.last_visit().ToInternalValue(); i > 0; --i) | |
288 last_visit -= day_delta; | |
289 row.set_last_visit(last_visit); | |
290 history_database_->UpdateURLRow(row.id(), row); | |
291 } | |
292 transaction.Commit(); | |
293 } | |
294 | |
295 url_index_.reset( | |
296 new InMemoryURLIndex(&profile_, FilePath(), "en,ja,hi,zh")); | |
297 url_index_->Init(); | |
298 url_index_->RebuildFromHistory(history_database_); | |
299 } | 79 } |
300 | 80 |
301 FilePath::StringType InMemoryURLIndexTest::TestDBName() const { | 81 FilePath::StringType InMemoryURLIndexTest::TestDBName() const { |
302 return FILE_PATH_LITERAL("url_history_provider_test.db.txt"); | 82 return FILE_PATH_LITERAL("url_history_provider_test.db.txt"); |
303 } | 83 } |
304 | 84 |
305 #if 0 | 85 URLRow MakeURLRowWithID(const char* url, |
306 URLRow InMemoryURLIndexTest::MakeURLRow(const char* url, | 86 const char* title, |
307 const char* title, | 87 int visit_count, |
308 int visit_count, | 88 int last_visit_ago, |
309 int last_visit_ago, | 89 int typed_count, |
310 int typed_count) { | 90 URLID id) { |
311 URLRow row(GURL(url), 0); | 91 URLRow row(GURL(url), id); |
312 row.set_title(UTF8ToUTF16(title)); | 92 row.set_title(UTF8ToUTF16(title)); |
313 row.set_visit_count(visit_count); | 93 row.set_visit_count(visit_count); |
314 row.set_typed_count(typed_count); | 94 row.set_typed_count(typed_count); |
315 row.set_last_visit(base::Time::NowFromSystemTime() - | 95 row.set_last_visit(base::Time::NowFromSystemTime() - |
316 base::TimeDelta::FromDays(last_visit_ago)); | 96 base::TimeDelta::FromDays(last_visit_ago)); |
317 return row; | 97 return row; |
318 } | 98 } |
319 | 99 |
320 String16Vector InMemoryURLIndexTest::Make1Term(const char* term) const { | |
321 String16Vector original_terms; | |
322 original_terms.push_back(UTF8ToUTF16(term)); | |
323 return original_terms; | |
324 } | |
325 | |
326 String16Vector InMemoryURLIndexTest::Make2Terms(const char* term_1, | |
327 const char* term_2) const { | |
328 String16Vector original_terms; | |
329 original_terms.push_back(UTF8ToUTF16(term_1)); | |
330 original_terms.push_back(UTF8ToUTF16(term_2)); | |
331 return original_terms; | |
332 } | |
333 | |
334 float InMemoryURLIndexTest::GetTopicalityScoreOfTermAgainstURLAndTitle( | |
335 const string16& term, | |
336 const string16& url, | |
337 const string16& title) { | |
338 TermMatches url_matches = MatchTermInString(term, url, 0); | |
339 TermMatches title_matches = MatchTermInString(term, title, 0); | |
340 RowWordStarts word_starts; | |
341 String16SetFromString16(url, &word_starts.url_word_starts_); | |
342 String16SetFromString16(title, &word_starts.title_word_starts_); | |
343 return ScoredHistoryMatch::GetTopicalityScore( | |
344 1, url, url_matches, title_matches, word_starts); | |
345 } | |
346 #endif | |
347 | |
348 void InMemoryURLIndexTest::CheckTerm( | 100 void InMemoryURLIndexTest::CheckTerm( |
349 const URLIndexPrivateData::SearchTermCacheMap& cache, | 101 const URLIndexPrivateData::SearchTermCacheMap& cache, |
350 string16 term) const { | 102 string16 term) const { |
351 URLIndexPrivateData::SearchTermCacheMap::const_iterator cache_iter( | 103 URLIndexPrivateData::SearchTermCacheMap::const_iterator cache_iter( |
352 cache.find(term)); | 104 cache.find(term)); |
353 ASSERT_TRUE(cache.end() != cache_iter) | 105 ASSERT_TRUE(cache.end() != cache_iter) |
354 << "Cache does not contain '" << term << "' but should."; | 106 << "Cache does not contain '" << term << "' but should."; |
355 URLIndexPrivateData::SearchTermCacheItem cache_item = cache_iter->second; | 107 URLIndexPrivateData::SearchTermCacheItem cache_item = cache_iter->second; |
356 EXPECT_TRUE(cache_item.used_) | 108 EXPECT_TRUE(cache_item.used_) |
357 << "Cache item '" << term << "' should be marked as being in use."; | 109 << "Cache item '" << term << "' should be marked as being in use."; |
358 } | 110 } |
359 | 111 |
360 void InMemoryURLIndexTest::ExpectPrivateDataNotEmpty( | |
361 const URLIndexPrivateData& data) { | |
362 EXPECT_FALSE(data.word_list_.empty()); | |
363 // available_words_ will be empty since we have freshly built the | |
364 // data set for these tests. | |
365 EXPECT_TRUE(data.available_words_.empty()); | |
366 EXPECT_FALSE(data.word_map_.empty()); | |
367 EXPECT_FALSE(data.char_word_map_.empty()); | |
368 EXPECT_FALSE(data.word_id_history_map_.empty()); | |
369 EXPECT_FALSE(data.history_id_word_map_.empty()); | |
370 EXPECT_FALSE(data.history_info_map_.empty()); | |
371 } | |
372 | |
373 void InMemoryURLIndexTest::ExpectPrivateDataEmpty( | |
374 const URLIndexPrivateData& data) { | |
375 EXPECT_TRUE(data.word_list_.empty()); | |
376 EXPECT_TRUE(data.available_words_.empty()); | |
377 EXPECT_TRUE(data.word_map_.empty()); | |
378 EXPECT_TRUE(data.char_word_map_.empty()); | |
379 EXPECT_TRUE(data.word_id_history_map_.empty()); | |
380 EXPECT_TRUE(data.history_id_word_map_.empty()); | |
381 EXPECT_TRUE(data.history_info_map_.empty()); | |
382 } | |
383 | |
384 // Helper function which compares two maps for equivalence. The maps' values | |
385 // are associative containers and their contents are compared as well. | |
386 template<typename T> | |
387 void ExpectMapOfContainersIdentical(const T& expected, const T& actual) { | |
388 ASSERT_EQ(expected.size(), actual.size()); | |
389 for (typename T::const_iterator expected_iter = expected.begin(); | |
390 expected_iter != expected.end(); ++expected_iter) { | |
391 typename T::const_iterator actual_iter = actual.find(expected_iter->first); | |
392 ASSERT_TRUE(actual.end() != actual_iter); | |
393 typename T::mapped_type const& expected_values(expected_iter->second); | |
394 typename T::mapped_type const& actual_values(actual_iter->second); | |
395 ASSERT_EQ(expected_values.size(), actual_values.size()); | |
396 for (typename T::mapped_type::const_iterator set_iter = | |
397 expected_values.begin(); set_iter != expected_values.end(); ++set_iter) | |
398 EXPECT_EQ(actual_values.count(*set_iter), | |
399 expected_values.count(*set_iter)); | |
400 } | |
401 } | |
402 | |
403 void InMemoryURLIndexTest::ExpectPrivateDataEqual( | |
404 const URLIndexPrivateData& expected, | |
405 const URLIndexPrivateData& actual) { | |
406 EXPECT_EQ(expected.word_list_.size(), actual.word_list_.size()); | |
407 EXPECT_EQ(expected.word_map_.size(), actual.word_map_.size()); | |
408 EXPECT_EQ(expected.char_word_map_.size(), actual.char_word_map_.size()); | |
409 EXPECT_EQ(expected.word_id_history_map_.size(), | |
410 actual.word_id_history_map_.size()); | |
411 EXPECT_EQ(expected.history_id_word_map_.size(), | |
412 actual.history_id_word_map_.size()); | |
413 EXPECT_EQ(expected.history_info_map_.size(), actual.history_info_map_.size()); | |
414 EXPECT_EQ(expected.word_starts_map_.size(), actual.word_starts_map_.size()); | |
415 // WordList must be index-by-index equal. | |
416 size_t count = expected.word_list_.size(); | |
417 for (size_t i = 0; i < count; ++i) | |
418 EXPECT_EQ(expected.word_list_[i], actual.word_list_[i]); | |
419 | |
420 ExpectMapOfContainersIdentical(expected.char_word_map_, | |
421 actual.char_word_map_); | |
422 ExpectMapOfContainersIdentical(expected.word_id_history_map_, | |
423 actual.word_id_history_map_); | |
424 ExpectMapOfContainersIdentical(expected.history_id_word_map_, | |
425 actual.history_id_word_map_); | |
426 | |
427 for (HistoryInfoMap::const_iterator expected_info = | |
428 expected.history_info_map_.begin(); | |
429 expected_info != expected.history_info_map_.end(); ++expected_info) { | |
430 HistoryInfoMap::const_iterator actual_info = | |
431 actual.history_info_map_.find(expected_info->first); | |
432 // NOTE(yfriedman): ASSERT_NE can't be used due to incompatibility between | |
433 // gtest and STLPort in the Android build. See | |
434 // http://code.google.com/p/googletest/issues/detail?id=359 | |
435 ASSERT_TRUE(actual_info != actual.history_info_map_.end()); | |
436 const URLRow& expected_row(expected_info->second); | |
437 const URLRow& actual_row(actual_info->second); | |
438 EXPECT_EQ(expected_row.visit_count(), actual_row.visit_count()); | |
439 EXPECT_EQ(expected_row.typed_count(), actual_row.typed_count()); | |
440 EXPECT_EQ(expected_row.last_visit(), actual_row.last_visit()); | |
441 EXPECT_EQ(expected_row.url(), actual_row.url()); | |
442 } | |
443 | |
444 for (WordStartsMap::const_iterator expected_starts = | |
445 expected.word_starts_map_.begin(); | |
446 expected_starts != expected.word_starts_map_.end(); | |
447 ++expected_starts) { | |
448 WordStartsMap::const_iterator actual_starts = | |
449 actual.word_starts_map_.find(expected_starts->first); | |
450 // NOTE(yfriedman): ASSERT_NE can't be used due to incompatibility between | |
451 // gtest and STLPort in the Android build. See | |
452 // http://code.google.com/p/googletest/issues/detail?id=359 | |
453 ASSERT_TRUE(actual_starts != actual.word_starts_map_.end()); | |
454 const RowWordStarts& expected_word_starts(expected_starts->second); | |
455 const RowWordStarts& actual_word_starts(actual_starts->second); | |
456 EXPECT_EQ(expected_word_starts.url_word_starts_.size(), | |
457 actual_word_starts.url_word_starts_.size()); | |
458 EXPECT_TRUE(std::equal(expected_word_starts.url_word_starts_.begin(), | |
459 expected_word_starts.url_word_starts_.end(), | |
460 actual_word_starts.url_word_starts_.begin())); | |
461 EXPECT_EQ(expected_word_starts.title_word_starts_.size(), | |
462 actual_word_starts.title_word_starts_.size()); | |
463 EXPECT_TRUE(std::equal(expected_word_starts.title_word_starts_.begin(), | |
464 expected_word_starts.title_word_starts_.end(), | |
465 actual_word_starts.title_word_starts_.begin())); | |
466 } | |
467 } | |
468 | |
469 //------------------------------------------------------------------------------ | 112 //------------------------------------------------------------------------------ |
470 | 113 |
471 class LimitedInMemoryURLIndexTest : public InMemoryURLIndexTest { | 114 class LimitedInMemoryURLIndexTest : public InMemoryURLIndexTest { |
472 protected: | 115 protected: |
473 FilePath::StringType TestDBName() const; | 116 FilePath::StringType TestDBName() const OVERRIDE; |
474 }; | 117 }; |
475 | 118 |
476 FilePath::StringType LimitedInMemoryURLIndexTest::TestDBName() const { | 119 FilePath::StringType LimitedInMemoryURLIndexTest::TestDBName() const { |
477 return FILE_PATH_LITERAL("url_history_provider_test_limited.db.txt"); | 120 return FILE_PATH_LITERAL("url_history_provider_test_limited.db.txt"); |
478 } | 121 } |
479 | 122 |
480 TEST_F(LimitedInMemoryURLIndexTest, Initialization) { | 123 TEST_F(LimitedInMemoryURLIndexTest, Initialization) { |
481 // Verify that the database contains the expected number of items, which | 124 // Verify that the database contains the expected number of items, which |
482 // is the pre-filtered count, i.e. all of the items. | 125 // is the pre-filtered count, i.e. all of the items. |
483 sql::Statement statement(GetDB().GetUniqueStatement("SELECT * FROM urls;")); | 126 sql::Statement statement( |
| 127 history_database_->get_db_for_testing()->GetUniqueStatement( |
| 128 "SELECT * FROM urls;")); |
484 ASSERT_TRUE(statement.is_valid()); | 129 ASSERT_TRUE(statement.is_valid()); |
485 uint64 row_count = 0; | 130 uint64 row_count = 0; |
486 while (statement.Step()) ++row_count; | 131 while (statement.Step()) ++row_count; |
487 EXPECT_EQ(1U, row_count); | 132 EXPECT_EQ(1U, row_count); |
488 url_index_.reset( | |
489 new InMemoryURLIndex(&profile_, FilePath(), "en,ja,hi,zh")); | |
490 url_index_->Init(); | |
491 url_index_->RebuildFromHistory(history_database_); | |
492 URLIndexPrivateData& private_data(*GetPrivateData()); | |
493 | 133 |
494 // history_info_map_ should have the same number of items as were filtered. | 134 // history_info_map_ should have the same number of items as were filtered. |
495 EXPECT_EQ(1U, private_data.history_info_map_.size()); | 135 EXPECT_EQ(1U, url_index_->private_data_->history_info_map_.size()); |
496 EXPECT_EQ(35U, private_data.char_word_map_.size()); | 136 EXPECT_EQ(35U, url_index_->private_data_->char_word_map_.size()); |
497 EXPECT_EQ(17U, private_data.word_map_.size()); | 137 EXPECT_EQ(17U, url_index_->private_data_->word_map_.size()); |
498 } | 138 } |
499 | 139 |
| 140 //------------------------------------------------------------------------------ |
| 141 |
500 TEST_F(InMemoryURLIndexTest, Retrieval) { | 142 TEST_F(InMemoryURLIndexTest, Retrieval) { |
501 // See if a very specific term gives a single result. | 143 // See if a very specific term gives a single result. |
| 144 // Note that in each case the term will be lowercased by the search. |
502 ScoredHistoryMatches matches = | 145 ScoredHistoryMatches matches = |
503 url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport")); | 146 url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport")); |
504 ASSERT_EQ(1U, matches.size()); | 147 ASSERT_EQ(1U, matches.size()); |
505 | 148 |
506 // Verify that we got back the result we expected. | 149 // Verify that we got back the result we expected. |
507 EXPECT_EQ(5, matches[0].url_info.id()); | 150 EXPECT_EQ(5, matches[0].url_info.id()); |
508 EXPECT_EQ("http://drudgereport.com/", matches[0].url_info.url().spec()); | 151 EXPECT_EQ("http://drudgereport.com/", matches[0].url_info.url().spec()); |
509 EXPECT_EQ(ASCIIToUTF16("DRUDGE REPORT 2010"), matches[0].url_info.title()); | 152 EXPECT_EQ(ASCIIToUTF16("DRUDGE REPORT 2010"), matches[0].url_info.title()); |
510 EXPECT_TRUE(matches[0].can_inline); | 153 EXPECT_TRUE(matches[0].can_inline); |
511 | 154 |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
643 TEST_F(InMemoryURLIndexTest, HugeResultSet) { | 286 TEST_F(InMemoryURLIndexTest, HugeResultSet) { |
644 // Create a huge set of qualifying history items. | 287 // Create a huge set of qualifying history items. |
645 for (URLID row_id = 5000; row_id < 6000; ++row_id) { | 288 for (URLID row_id = 5000; row_id < 6000; ++row_id) { |
646 URLRow new_row(GURL("http://www.brokeandaloneinmanitoba.com/"), row_id); | 289 URLRow new_row(GURL("http://www.brokeandaloneinmanitoba.com/"), row_id); |
647 new_row.set_last_visit(base::Time::Now()); | 290 new_row.set_last_visit(base::Time::Now()); |
648 EXPECT_TRUE(UpdateURL(new_row)); | 291 EXPECT_TRUE(UpdateURL(new_row)); |
649 } | 292 } |
650 | 293 |
651 ScoredHistoryMatches matches = | 294 ScoredHistoryMatches matches = |
652 url_index_->HistoryItemsForTerms(ASCIIToUTF16("b")); | 295 url_index_->HistoryItemsForTerms(ASCIIToUTF16("b")); |
653 URLIndexPrivateData& private_data(*GetPrivateData()); | 296 const URLIndexPrivateData* private_data(GetPrivateData()); |
654 ASSERT_EQ(AutocompleteProvider::kMaxMatches, matches.size()); | 297 ASSERT_EQ(AutocompleteProvider::kMaxMatches, matches.size()); |
655 // There are 7 matches already in the database. | 298 // There are 7 matches already in the database. |
656 ASSERT_EQ(1008U, private_data.pre_filter_item_count_); | 299 ASSERT_EQ(1008U, private_data->pre_filter_item_count_); |
657 ASSERT_EQ(500U, private_data.post_filter_item_count_); | 300 ASSERT_EQ(500U, private_data->post_filter_item_count_); |
658 ASSERT_EQ(AutocompleteProvider::kMaxMatches, | 301 ASSERT_EQ(AutocompleteProvider::kMaxMatches, |
659 private_data.post_scoring_item_count_); | 302 private_data->post_scoring_item_count_); |
660 } | 303 } |
661 | 304 |
662 TEST_F(InMemoryURLIndexTest, TitleSearch) { | 305 TEST_F(InMemoryURLIndexTest, TitleSearch) { |
663 // Signal if someone has changed the test DB. | 306 // Signal if someone has changed the test DB. |
664 EXPECT_EQ(28U, GetPrivateData()->history_info_map_.size()); | 307 EXPECT_EQ(28U, GetPrivateData()->history_info_map_.size()); |
665 | 308 |
666 // Ensure title is being searched. | 309 // Ensure title is being searched. |
667 ScoredHistoryMatches matches = | 310 ScoredHistoryMatches matches = |
668 url_index_->HistoryItemsForTerms(ASCIIToUTF16("MORTGAGE RATE DROPS")); | 311 url_index_->HistoryItemsForTerms(ASCIIToUTF16("MORTGAGE RATE DROPS")); |
669 ASSERT_EQ(1U, matches.size()); | 312 ASSERT_EQ(1U, matches.size()); |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
739 EXPECT_EQ(28, matches[0].url_info.id()); | 382 EXPECT_EQ(28, matches[0].url_info.id()); |
740 } | 383 } |
741 | 384 |
742 TEST_F(InMemoryURLIndexTest, TypedCharacterCaching) { | 385 TEST_F(InMemoryURLIndexTest, TypedCharacterCaching) { |
743 // Verify that match results for previously typed characters are retained | 386 // Verify that match results for previously typed characters are retained |
744 // (in the term_char_word_set_cache_) and reused, if possible, in future | 387 // (in the term_char_word_set_cache_) and reused, if possible, in future |
745 // autocompletes. | 388 // autocompletes. |
746 typedef URLIndexPrivateData::SearchTermCacheMap::iterator CacheIter; | 389 typedef URLIndexPrivateData::SearchTermCacheMap::iterator CacheIter; |
747 typedef URLIndexPrivateData::SearchTermCacheItem CacheItem; | 390 typedef URLIndexPrivateData::SearchTermCacheItem CacheItem; |
748 | 391 |
749 URLIndexPrivateData::SearchTermCacheMap& cache( | 392 const URLIndexPrivateData::SearchTermCacheMap& cache( |
750 GetPrivateData()->search_term_cache_); | 393 GetPrivateData()->search_term_cache_); |
751 | 394 |
752 // The cache should be empty at this point. | 395 // The cache should be empty at this point. |
753 EXPECT_EQ(0U, cache.size()); | 396 EXPECT_EQ(0U, cache.size()); |
754 | 397 |
755 // Now simulate typing search terms into the omnibox and check the state of | 398 // Now simulate typing search terms into the omnibox and check the state of |
756 // the cache as each item is 'typed'. | 399 // the cache as each item is 'typed'. |
757 | 400 |
758 // Simulate typing "r" giving "r" in the simulated omnibox. The results for | 401 // Simulate typing "r" giving "r" in the simulated omnibox. The results for |
759 // 'r' will be not cached because it is only 1 character long. | 402 // 'r' will be not cached because it is only 1 character long. |
(...skipping 28 matching lines...) Expand all Loading... |
788 CheckTerm(cache, ASCIIToUTF16("mort")); | 431 CheckTerm(cache, ASCIIToUTF16("mort")); |
789 CheckTerm(cache, ASCIIToUTF16("reco")); | 432 CheckTerm(cache, ASCIIToUTF16("reco")); |
790 | 433 |
791 // Simulate a <DELETE> by removing the 'reco' and adding back the 'rec'. | 434 // Simulate a <DELETE> by removing the 'reco' and adding back the 'rec'. |
792 url_index_->HistoryItemsForTerms(ASCIIToUTF16("mort rec")); | 435 url_index_->HistoryItemsForTerms(ASCIIToUTF16("mort rec")); |
793 ASSERT_EQ(2U, cache.size()); | 436 ASSERT_EQ(2U, cache.size()); |
794 CheckTerm(cache, ASCIIToUTF16("mort")); | 437 CheckTerm(cache, ASCIIToUTF16("mort")); |
795 CheckTerm(cache, ASCIIToUTF16("rec")); | 438 CheckTerm(cache, ASCIIToUTF16("rec")); |
796 } | 439 } |
797 | 440 |
798 #if 0 | |
799 TEST_F(InMemoryURLIndexTest, Scoring) { | |
800 URLRow row_a(MakeURLRow("http://abcdef", "fedcba", 3, 30, 1)); | |
801 // We use NowFromSystemTime() because MakeURLRow uses the same function | |
802 // to calculate last visit time when building a row. | |
803 base::Time now = base::Time::NowFromSystemTime(); | |
804 RowWordStarts word_starts; | |
805 // Test scores based on position. | |
806 // TODO(mpearson): Test new_scoring if we're actually going to turn it | |
807 // on by default. This requires setting word_starts, which isn't done | |
808 // right now. | |
809 ScoredHistoryMatch scored_a(row_a, ASCIIToUTF16("abc"), Make1Term("abc"), | |
810 word_starts, now); | |
811 ScoredHistoryMatch scored_b(row_a, ASCIIToUTF16("bcd"), Make1Term("bcd"), | |
812 word_starts, now); | |
813 EXPECT_GT(scored_a.raw_score, scored_b.raw_score); | |
814 // Test scores based on length. | |
815 ScoredHistoryMatch scored_c(row_a, ASCIIToUTF16("abcd"), Make1Term("abcd"), | |
816 word_starts, now); | |
817 EXPECT_LT(scored_a.raw_score, scored_c.raw_score); | |
818 // Test scores based on order. | |
819 ScoredHistoryMatch scored_d(row_a, ASCIIToUTF16("abcdef"), | |
820 Make2Terms("abc", "def"), word_starts, now); | |
821 ScoredHistoryMatch scored_e(row_a, ASCIIToUTF16("def abc"), | |
822 Make2Terms("def", "abc"), word_starts, now); | |
823 EXPECT_GT(scored_d.raw_score, scored_e.raw_score); | |
824 // Test scores based on visit_count. | |
825 URLRow row_b(MakeURLRow("http://abcdef", "fedcba", 10, 30, 1)); | |
826 ScoredHistoryMatch scored_f(row_b, ASCIIToUTF16("abc"), Make1Term("abc"), | |
827 word_starts, now); | |
828 EXPECT_GT(scored_f.raw_score, scored_a.raw_score); | |
829 // Test scores based on last_visit. | |
830 URLRow row_c(MakeURLRow("http://abcdef", "fedcba", 3, 10, 1)); | |
831 ScoredHistoryMatch scored_g(row_c, ASCIIToUTF16("abc"), Make1Term("abc"), | |
832 word_starts, now); | |
833 EXPECT_GT(scored_g.raw_score, scored_a.raw_score); | |
834 // Test scores based on typed_count. | |
835 URLRow row_d(MakeURLRow("http://abcdef", "fedcba", 3, 30, 10)); | |
836 ScoredHistoryMatch scored_h(row_d, ASCIIToUTF16("abc"), Make1Term("abc"), | |
837 word_starts, now); | |
838 EXPECT_GT(scored_h.raw_score, scored_a.raw_score); | |
839 // Test scores based on a terms appearing multiple times. | |
840 URLRow row_i(MakeURLRow("http://csi.csi.csi/csi_csi", | |
841 "CSI Guide to CSI Las Vegas, CSI New York, CSI Provo", 3, 30, 10)); | |
842 ScoredHistoryMatch scored_i(row_i, ASCIIToUTF16("csi"), Make1Term("csi"), | |
843 word_starts, now); | |
844 EXPECT_LT(scored_i.raw_score, 1400); | |
845 } | |
846 | |
847 TEST_F(InMemoryURLIndexTest, GetTopicalityScoreTrailingSlash) { | |
848 const float hostname = GetTopicalityScoreOfTermAgainstURLAndTitle( | |
849 ASCIIToUTF16("def"), | |
850 ASCIIToUTF16("http://abc.def.com/"), | |
851 ASCIIToUTF16("Non-Matching Title")); | |
852 const float hostname_no_slash = GetTopicalityScoreOfTermAgainstURLAndTitle( | |
853 ASCIIToUTF16("def"), | |
854 ASCIIToUTF16("http://abc.def.com"), | |
855 ASCIIToUTF16("Non-Matching Title")); | |
856 EXPECT_EQ(hostname_no_slash, hostname); | |
857 } | |
858 | |
859 // This function only tests scoring of single terms that match exactly | |
860 // once somewhere in the URL or title. | |
861 TEST_F(InMemoryURLIndexTest, GetTopicalityScore) { | |
862 string16 url = ASCIIToUTF16("http://abc.def.com/path1/path2?" | |
863 "arg1=val1&arg2=val2#hash_component"); | |
864 string16 title = ASCIIToUTF16("here is a title"); | |
865 const float hostname_score = | |
866 GetTopicalityScoreOfTermAgainstURLAndTitle( | |
867 ASCIIToUTF16("abc"), url, title); | |
868 const float hostname_mid_word_score = | |
869 GetTopicalityScoreOfTermAgainstURLAndTitle( | |
870 ASCIIToUTF16("bc"), url, title); | |
871 const float path_score = | |
872 GetTopicalityScoreOfTermAgainstURLAndTitle( | |
873 ASCIIToUTF16("path1"), url, title); | |
874 const float path_mid_word_score = | |
875 GetTopicalityScoreOfTermAgainstURLAndTitle( | |
876 ASCIIToUTF16("ath1"), url, title); | |
877 const float arg_score = | |
878 GetTopicalityScoreOfTermAgainstURLAndTitle( | |
879 ASCIIToUTF16("arg2"), url, title); | |
880 const float arg_mid_word_score = | |
881 GetTopicalityScoreOfTermAgainstURLAndTitle( | |
882 ASCIIToUTF16("rg2"), url, title); | |
883 const float protocol_score = | |
884 GetTopicalityScoreOfTermAgainstURLAndTitle( | |
885 ASCIIToUTF16("htt"), url, title); | |
886 const float protocol_mid_word_score = | |
887 GetTopicalityScoreOfTermAgainstURLAndTitle( | |
888 ASCIIToUTF16("tt"), url, title); | |
889 const float title_score = | |
890 GetTopicalityScoreOfTermAgainstURLAndTitle( | |
891 ASCIIToUTF16("her"), url, title); | |
892 const float title_mid_word_score = | |
893 GetTopicalityScoreOfTermAgainstURLAndTitle( | |
894 ASCIIToUTF16("er"), url, title); | |
895 // Verify hostname > path > arg, and the same for the matches at | |
896 // non-word-boundaries. | |
897 EXPECT_GT(hostname_score, path_score); | |
898 EXPECT_GT(path_score, arg_score); | |
899 EXPECT_GT(hostname_mid_word_score, path_mid_word_score); | |
900 EXPECT_GT(path_mid_word_score, arg_mid_word_score); | |
901 // Also verify that the matches at non-word-boundaries all score | |
902 // worse than the matches at word boundaries. These two sets suffice. | |
903 EXPECT_GT(arg_score, hostname_mid_word_score); | |
904 EXPECT_GT(title_score, title_mid_word_score); | |
905 // Check that title matches fit somewhere reasonable compared to the | |
906 // various types of URL matches. | |
907 EXPECT_GT(title_score, arg_score); | |
908 EXPECT_GT(arg_score, hostname_mid_word_score); | |
909 EXPECT_GT(title_mid_word_score, arg_mid_word_score); | |
910 // Finally, verify that protocol matches score worse than everything | |
911 // (except possibly mid-word matches in the ?arg section of the URL--I | |
912 // can imagine scoring those pretty harshly as well). | |
913 EXPECT_GT(path_mid_word_score, protocol_score); | |
914 EXPECT_GT(path_mid_word_score, protocol_mid_word_score); | |
915 EXPECT_GT(title_mid_word_score, protocol_score); | |
916 EXPECT_GT(title_mid_word_score, protocol_mid_word_score); | |
917 } | |
918 #endif | |
919 | |
920 TEST_F(InMemoryURLIndexTest, AddNewRows) { | 441 TEST_F(InMemoryURLIndexTest, AddNewRows) { |
921 // Verify that the row we're going to add does not already exist. | 442 // Verify that the row we're going to add does not already exist. |
922 URLID new_row_id = 87654321; | 443 URLID new_row_id = 87654321; |
923 // Newly created URLRows get a last_visit time of 'right now' so it should | 444 // Newly created URLRows get a last_visit time of 'right now' so it should |
924 // qualify as a quick result candidate. | 445 // qualify as a quick result candidate. |
925 EXPECT_TRUE(url_index_->HistoryItemsForTerms( | 446 EXPECT_TRUE(url_index_->HistoryItemsForTerms( |
926 ASCIIToUTF16("brokeandalone")).empty()); | 447 ASCIIToUTF16("brokeandalone")).empty()); |
927 | 448 |
928 // Add a new row. | 449 // Add a new row. |
929 URLRow new_row(GURL("http://www.brokeandaloneinmanitoba.com/"), new_row_id++); | 450 URLRow new_row(GURL("http://www.brokeandaloneinmanitoba.com/"), new_row_id++); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
964 TEST_F(InMemoryURLIndexTest, ExpireRow) { | 485 TEST_F(InMemoryURLIndexTest, ExpireRow) { |
965 ScoredHistoryMatches matches = | 486 ScoredHistoryMatches matches = |
966 url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport")); | 487 url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport")); |
967 ASSERT_EQ(1U, matches.size()); | 488 ASSERT_EQ(1U, matches.size()); |
968 | 489 |
969 // Determine the row id for the result, remember that id, broadcast a | 490 // Determine the row id for the result, remember that id, broadcast a |
970 // delete notification, then ensure that the row has been deleted. | 491 // delete notification, then ensure that the row has been deleted. |
971 URLsDeletedDetails deleted_details; | 492 URLsDeletedDetails deleted_details; |
972 deleted_details.all_history = false; | 493 deleted_details.all_history = false; |
973 deleted_details.rows.push_back(matches[0].url_info); | 494 deleted_details.rows.push_back(matches[0].url_info); |
974 Observe(chrome::NOTIFICATION_HISTORY_URLS_DELETED, | 495 content::Source<InMemoryURLIndexTest> source(this); |
975 content::Source<InMemoryURLIndexTest>(this), | 496 url_index_->Observe( |
976 content::Details<history::HistoryDetails>(&deleted_details)); | 497 chrome::NOTIFICATION_HISTORY_URLS_DELETED, |
| 498 content::Source<InMemoryURLIndexTest>(this), |
| 499 content::Details<history::HistoryDetails>(&deleted_details)); |
977 EXPECT_TRUE(url_index_->HistoryItemsForTerms( | 500 EXPECT_TRUE(url_index_->HistoryItemsForTerms( |
978 ASCIIToUTF16("DrudgeReport")).empty()); | 501 ASCIIToUTF16("DrudgeReport")).empty()); |
979 } | 502 } |
980 | 503 |
981 TEST_F(InMemoryURLIndexTest, WhitelistedURLs) { | 504 TEST_F(InMemoryURLIndexTest, WhitelistedURLs) { |
982 struct TestData { | 505 struct TestData { |
983 const std::string url_spec; | 506 const std::string url_spec; |
984 const bool expected_is_whitelisted; | 507 const bool expected_is_whitelisted; |
985 } data[] = { | 508 } data[] = { |
986 // URLs with whitelisted schemes. | 509 // URLs with whitelisted schemes. |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1045 { "tftp://example.com/mystartupfile", false }, | 568 { "tftp://example.com/mystartupfile", false }, |
1046 { "tip://123.123.123.123/?urn:xopen:xid", false }, | 569 { "tip://123.123.123.123/?urn:xopen:xid", false }, |
1047 { "tv:nbc.com", false }, | 570 { "tv:nbc.com", false }, |
1048 { "urn:foo:A123,456", false }, | 571 { "urn:foo:A123,456", false }, |
1049 { "vemmi://zeus.mctel.fr/demo", false }, | 572 { "vemmi://zeus.mctel.fr/demo", false }, |
1050 { "wais://www.mydomain.net:8765/mydatabase", false }, | 573 { "wais://www.mydomain.net:8765/mydatabase", false }, |
1051 { "xmpp:node@example.com", false }, | 574 { "xmpp:node@example.com", false }, |
1052 { "xmpp://guest@example.com", false }, | 575 { "xmpp://guest@example.com", false }, |
1053 }; | 576 }; |
1054 | 577 |
1055 URLIndexPrivateData& private_data(*GetPrivateData()); | 578 std::set<std::string> whitelist; |
1056 const std::set<std::string>& whitelist(scheme_whitelist()); | 579 URLIndexPrivateData::InitializeSchemeWhitelistForTesting(&whitelist); |
1057 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) { | 580 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) { |
1058 GURL url(data[i].url_spec); | 581 GURL url(data[i].url_spec); |
1059 EXPECT_EQ(data[i].expected_is_whitelisted, | 582 EXPECT_EQ(data[i].expected_is_whitelisted, |
1060 private_data.URLSchemeIsWhitelisted(url, whitelist)); | 583 URLIndexPrivateData::URLSchemeIsWhitelisted(url, whitelist)); |
1061 } | 584 } |
1062 } | 585 } |
1063 | 586 |
1064 TEST_F(InMemoryURLIndexTest, CacheSaveRestore) { | 587 // InMemoryURLIndexCacheTest --------------------------------------------------- |
1065 ScopedTempDir temp_directory; | 588 |
1066 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); | 589 class InMemoryURLIndexCacheTest : public InMemoryURLIndexBaseTest { |
1067 set_history_dir(temp_directory.path()); | |
1068 | |
1069 URLIndexPrivateData& private_data(*GetPrivateData()); | |
1070 | |
1071 // Ensure that there is really something there to be saved. | |
1072 EXPECT_FALSE(private_data.word_list_.empty()); | |
1073 // available_words_ will already be empty since we have freshly built the | |
1074 // data set for this test. | |
1075 EXPECT_TRUE(private_data.available_words_.empty()); | |
1076 EXPECT_FALSE(private_data.word_map_.empty()); | |
1077 EXPECT_FALSE(private_data.char_word_map_.empty()); | |
1078 EXPECT_FALSE(private_data.word_id_history_map_.empty()); | |
1079 EXPECT_FALSE(private_data.history_id_word_map_.empty()); | |
1080 EXPECT_FALSE(private_data.history_info_map_.empty()); | |
1081 EXPECT_FALSE(private_data.word_starts_map_.empty()); | |
1082 | |
1083 // Capture the current private data for later comparison to restored data. | |
1084 scoped_refptr<URLIndexPrivateData> old_data(private_data.Duplicate()); | |
1085 | |
1086 // Save then restore our private data. | |
1087 CacheFileSaverObserver save_observer(&message_loop_); | |
1088 url_index_->set_save_cache_observer(&save_observer); | |
1089 PostSaveToCacheFileTask(); | |
1090 message_loop_.Run(); | |
1091 EXPECT_TRUE(save_observer.succeeded_); | |
1092 | |
1093 // Clear and then prove it's clear before restoring. | |
1094 ClearPrivateData(); | |
1095 EXPECT_TRUE(private_data.word_list_.empty()); | |
1096 EXPECT_TRUE(private_data.available_words_.empty()); | |
1097 EXPECT_TRUE(private_data.word_map_.empty()); | |
1098 EXPECT_TRUE(private_data.char_word_map_.empty()); | |
1099 EXPECT_TRUE(private_data.word_id_history_map_.empty()); | |
1100 EXPECT_TRUE(private_data.history_id_word_map_.empty()); | |
1101 EXPECT_TRUE(private_data.history_info_map_.empty()); | |
1102 EXPECT_TRUE(private_data.word_starts_map_.empty()); | |
1103 | |
1104 CacheFileReaderObserver read_observer(&message_loop_); | |
1105 url_index_->set_restore_cache_observer(&read_observer); | |
1106 PostRestoreFromCacheFileTask(); | |
1107 message_loop_.Run(); | |
1108 EXPECT_TRUE(read_observer.succeeded_); | |
1109 | |
1110 URLIndexPrivateData& new_data(*GetPrivateData()); | |
1111 | |
1112 // Compare the captured and restored for equality. | |
1113 ExpectPrivateDataEqual(*old_data, new_data); | |
1114 } | |
1115 | |
1116 class InMemoryURLIndexCacheTest : public testing::Test { | |
1117 public: | 590 public: |
1118 InMemoryURLIndexCacheTest() {} | 591 // A test helper class which captures the state of the URL index and cache |
| 592 // prior to the addition, update or delete of a history URL visit, determines |
| 593 // the expected state after the URL change, and verifies the actual state. |
| 594 class CacheChecker { |
| 595 public: |
| 596 enum ChangeType { |
| 597 URL_ADDED, |
| 598 URL_UPDATED, |
| 599 URL_REMOVED, |
| 600 }; |
| 601 |
| 602 // Captures the state of the index and cache and the changes which are |
| 603 // expected to be made. |index| points to the test index. |change_type| |
| 604 // specifies the kind of history URL visit change that will take place. |
| 605 // |existing_words| lists the words that are referenced by the history item |
| 606 // (both URL and page title) which are expected to already be recorded in |
| 607 // the index. |added_words| gives terms which are not currently in the index |
| 608 // but will be added for this history item. |removed_words| gives words in |
| 609 // the index but which are not referenced by any other history item and |
| 610 // which will be removed by the test action. |
| 611 CacheChecker(InMemoryURLIndex* index, |
| 612 ChangeType change_type, |
| 613 URLID history_id, |
| 614 const String16Set& existing_words, |
| 615 const String16Set& added_words, |
| 616 const String16Set& removed_words); |
| 617 virtual ~CacheChecker(); |
| 618 |
| 619 // Initializes and destroys things in a way that the standard gtest |
| 620 // EXPECT_xxx macros can be used since those macros cannot be used in |
| 621 // constructors and destructors. |
| 622 void Init(); |
| 623 void Destroy(); |
| 624 |
| 625 private: |
| 626 // A helper struct that records index current state for words in the list of |
| 627 // |existing_words| and |removed_words|. |
| 628 struct ExistingWord { |
| 629 ExistingWord() : word_id(0), word_map_count(0), word_table_count(0) {} |
| 630 ExistingWord(WordID word_id, |
| 631 size_t word_map_count, |
| 632 size_t word_table_count) |
| 633 : word_id(word_id), |
| 634 word_map_count(word_map_count), |
| 635 word_table_count(word_table_count) {} |
| 636 |
| 637 WordID word_id; // Used for removed_words only. |
| 638 size_t word_map_count; |
| 639 size_t word_table_count; |
| 640 }; |
| 641 |
| 642 // Table names. |
| 643 static const char* kWordsTableName; |
| 644 static const char* kWordHistoryTableName; |
| 645 static const char* kURLsTableName; |
| 646 static const char* kURLWordStartsTableName; |
| 647 static const char* kTitleWordStartsTableName; |
| 648 // Field names. |
| 649 static const char* kWord; |
| 650 static const char* kWordID; |
| 651 static const char* kHistoryID; |
| 652 static const char* kWordStart; |
| 653 |
| 654 // Helper functions that perform a COUNT query of the cache database with |
| 655 // the given criteria and return the number of rows meeting that criteria. |
| 656 // |table| specifies the name of the table in which the SELECT will be |
| 657 // performed. |column| gives the column against which the desired |word|, |
| 658 // |word_id| or |history_id| will be matched. |
| 659 int SelectCount(const char* table); |
| 660 int SelectCount(const char* table, |
| 661 const char* column, |
| 662 const string16& word); |
| 663 int SelectCount(const char* table, |
| 664 const char* column, |
| 665 WordID word_id); |
| 666 int SelectCount(const char* table, |
| 667 const char* column, |
| 668 HistoryID history_id); |
| 669 |
| 670 InMemoryURLIndex& index_; |
| 671 sql::Connection* db_; |
| 672 // Information about the changes that the test is expected to cause. |
| 673 ChangeType change_type_; |
| 674 URLID history_id_; |
| 675 String16Set existing_words_; |
| 676 String16Set added_words_; |
| 677 String16Set removed_words_; |
| 678 // Current state information about the index. |
| 679 size_t word_list_count_; |
| 680 size_t word_table_count; |
| 681 size_t history_id_word_map_count_; |
| 682 size_t available_words_count_; |
| 683 std::map<string16, ExistingWord> existing_words_and_counts_; |
| 684 std::map<string16, ExistingWord> removed_words_and_counts_; |
| 685 }; |
| 686 |
| 687 void SetUp() OVERRIDE; |
| 688 |
| 689 // Provides custom test data file name. |
| 690 virtual FilePath::StringType TestDBName() const OVERRIDE; |
| 691 }; |
| 692 |
| 693 // InMemoryURLIndexCacheTest::CacheChecker ------------------------------------- |
| 694 |
| 695 // Cache database table names. See in_memory_url_cache_database.cc. |
| 696 typedef InMemoryURLIndexCacheTest::CacheChecker CacheChecker; |
| 697 const char* CacheChecker::kWordsTableName = "words"; |
| 698 const char* CacheChecker::kWordHistoryTableName = "word_history"; |
| 699 const char* CacheChecker::kURLsTableName = "urls"; |
| 700 const char* CacheChecker::kURLWordStartsTableName = "url_word_starts"; |
| 701 const char* CacheChecker::kTitleWordStartsTableName = "title_word_starts"; |
| 702 |
| 703 // Cache database table field names. |
| 704 const char* CacheChecker::kWord = "word"; |
| 705 const char* CacheChecker::kWordID = "word_id"; |
| 706 const char* CacheChecker::kHistoryID = "history_id"; |
| 707 const char* CacheChecker::kWordStart = "word_start"; |
| 708 |
| 709 CacheChecker::CacheChecker( |
| 710 InMemoryURLIndex* index, |
| 711 ChangeType change_type, |
| 712 URLID history_id, |
| 713 const String16Set& existing_words, |
| 714 const String16Set& added_words, |
| 715 const String16Set& removed_words) |
| 716 : index_(*index), |
| 717 db_(index_.private_data_->cache_db()->get_db_for_testing()), |
| 718 change_type_(change_type), |
| 719 history_id_(history_id), |
| 720 existing_words_(existing_words), |
| 721 added_words_(added_words), |
| 722 removed_words_(removed_words), |
| 723 // Remember the old word count, the old word table count, the old history |
| 724 // ID count, and how many unused words are available for reuse. |
| 725 word_list_count_(index_.private_data_->word_list_.size()), |
| 726 word_table_count(SelectCount(kWordsTableName)), |
| 727 history_id_word_map_count_( |
| 728 index_.private_data_->history_id_word_map_.size()), |
| 729 available_words_count_(index_.private_data_->available_words_.size()) { |
| 730 DCHECK(db_); |
| 731 Init(); |
| 732 } |
| 733 |
| 734 void CacheChecker::Init() { |
| 735 // Verify that the existing words exist and remember their counts. |
| 736 URLIndexPrivateData* private_data(index_.private_data_.get()); |
| 737 for (String16Set::iterator word_iter = existing_words_.begin(); |
| 738 word_iter != existing_words_.end(); ++word_iter) { |
| 739 string16 word(*word_iter); |
| 740 ASSERT_NE(0U, private_data->word_map_.count(word)) |
| 741 << "Existing word '" << word << "' not found."; |
| 742 EXPECT_GT(SelectCount(kWordsTableName, kWord, word), 0) |
| 743 << "Existing word '" << word << "' not found in words table."; |
| 744 WordID word_id = private_data->word_map_[word]; |
| 745 EXPECT_GT(SelectCount(kWordHistoryTableName, kWordID, word_id), 0) |
| 746 << "Existing word '" << word << "' not found in word_history table."; |
| 747 existing_words_and_counts_[word] = |
| 748 ExistingWord(word_id, private_data->word_id_history_map_[word_id]. |
| 749 size(), |
| 750 SelectCount(kWordHistoryTableName, kWordID, word_id)); |
| 751 } |
| 752 |
| 753 // Verify that the added words don't already exist. |
| 754 for (String16Set::iterator word_iter = added_words_.begin(); |
| 755 word_iter != added_words_.end(); ++word_iter) { |
| 756 string16 word(*word_iter); |
| 757 EXPECT_EQ(0U, private_data->word_map_.count(word)) |
| 758 << "Word '" << word << "' to be added is already there."; |
| 759 EXPECT_EQ(0, SelectCount(kWordsTableName, kWord, word)) |
| 760 << "Word '" << word << "' to be added is already in words table."; |
| 761 } |
| 762 |
| 763 // Verify that the removed words exist and remember their counts. |
| 764 for (String16Set::iterator word_iter = removed_words_.begin(); |
| 765 word_iter != removed_words_.end(); ++word_iter) { |
| 766 string16 word(*word_iter); |
| 767 ASSERT_EQ(1U, private_data->word_map_.count(word)) |
| 768 << "Word '" << word << "' to be removed not found."; |
| 769 WordID word_id = private_data->word_map_[word]; |
| 770 EXPECT_GT(private_data->word_id_history_map_[word_id].size(), 0U); |
| 771 EXPECT_GT(SelectCount(kWordsTableName, kWord, word), 0) |
| 772 << "Word '" << word << "' to be removed not found in words table."; |
| 773 EXPECT_GT(SelectCount(kWordHistoryTableName, kWordID, word_id), 0) |
| 774 << "Word '" << word << "' to remove not found in word_history table."; |
| 775 removed_words_and_counts_[word] = |
| 776 ExistingWord(word_id, private_data->word_id_history_map_[word_id]. |
| 777 size(), |
| 778 SelectCount(kWordHistoryTableName, kWordID, word_id)); |
| 779 EXPECT_EQ(removed_words_and_counts_[word].word_map_count, |
| 780 removed_words_and_counts_[word].word_table_count); |
| 781 } |
| 782 } |
| 783 |
| 784 CacheChecker::~CacheChecker() { |
| 785 Destroy(); |
| 786 } |
| 787 |
| 788 void CacheChecker::Destroy() { |
| 789 // Verify that the existing words still exist and their counts have |
| 790 // incremented by 1. |
| 791 URLIndexPrivateData* private_data(index_.private_data_.get()); |
| 792 for (String16Set::iterator word_iter = existing_words_.begin(); |
| 793 word_iter != existing_words_.end(); ++word_iter) { |
| 794 string16 word(*word_iter); |
| 795 const ExistingWord& existing_word(existing_words_and_counts_[word]); |
| 796 size_t expected_count = existing_word.word_map_count; |
| 797 if (change_type_ == URL_ADDED) |
| 798 ++expected_count; |
| 799 WordID word_id = private_data->word_map_[word]; |
| 800 EXPECT_EQ(expected_count, |
| 801 private_data->word_id_history_map_[word_id].size()) |
| 802 << "Occurrence count for existing word '" << word |
| 803 << "' in the word_id_history_map_ is incorrect."; |
| 804 EXPECT_EQ(static_cast<int>(expected_count), |
| 805 SelectCount(kWordHistoryTableName, kWordID, word_id)) |
| 806 << "Occurrence count for existing word '" << word << "' in " |
| 807 "the word_history database table is incorrect."; |
| 808 } |
| 809 |
| 810 // Verify the added words have been added and their counts are 1. |
| 811 for (String16Set::iterator word_iter = added_words_.begin(); |
| 812 word_iter != added_words_.end(); ++word_iter) { |
| 813 string16 word(*word_iter); |
| 814 ASSERT_EQ(1U, private_data->word_map_.count(word)); |
| 815 WordID word_id = private_data->word_map_[word]; |
| 816 EXPECT_EQ(1U, private_data->word_id_history_map_[word_id].size()) |
| 817 << "Count for added word '" << word << "' not 1."; |
| 818 EXPECT_EQ(1, SelectCount(kWordsTableName, kWord, word)) |
| 819 << "Word '" << word << "' not added to words table."; |
| 820 EXPECT_EQ(1, SelectCount(kWordHistoryTableName, kWordID, word_id)) |
| 821 << "Word '" << word << "' not added to word_history table."; |
| 822 } |
| 823 |
| 824 switch (change_type_) { |
| 825 case URL_ADDED: |
| 826 ASSERT_EQ(0U, removed_words_.size()) |
| 827 << "BAD TEST DATA -- removed_words must be empty for URL_ADDED."; |
| 828 // Fall through. |
| 829 case URL_UPDATED: { |
| 830 // There should be added-words + existing-words - removed-words entries |
| 831 // in the word_history table for the history ID. |
| 832 size_t word_count_for_id = existing_words_.size() + added_words_.size(); |
| 833 EXPECT_EQ(word_count_for_id, |
| 834 private_data->history_id_word_map_[history_id_].size()); |
| 835 EXPECT_EQ(static_cast<int>(word_count_for_id), |
| 836 SelectCount(kWordHistoryTableName, kHistoryID, history_id_)); |
| 837 EXPECT_EQ(1U, private_data->history_id_word_map_.count(history_id_)); |
| 838 EXPECT_EQ(1, SelectCount(kURLsTableName, kHistoryID, history_id_)); |
| 839 } |
| 840 break; |
| 841 case URL_REMOVED: |
| 842 // There should be no entries in the word_history table for the history |
| 843 // ID. |
| 844 ASSERT_EQ(0U, added_words_.size()) |
| 845 << "BAD TEST DATA -- added_words must be empty for URL_REMOVED."; |
| 846 EXPECT_EQ(0U, private_data->history_id_word_map_.count(history_id_)); |
| 847 EXPECT_EQ(0, SelectCount(kWordHistoryTableName, kHistoryID, history_id_)); |
| 848 EXPECT_EQ(0, SelectCount(kURLsTableName, kHistoryID, history_id_)); |
| 849 break; |
| 850 } |
| 851 |
| 852 // Verify that the count for removed words has been decremented or that |
| 853 // they have been deleted if their count has dropped to 0. |
| 854 int completely_removed = 0; |
| 855 for (String16Set::iterator word_iter = removed_words_.begin(); |
| 856 word_iter != removed_words_.end(); ++word_iter) { |
| 857 string16 word(*word_iter); |
| 858 const ExistingWord& removed_word(removed_words_and_counts_[word]); |
| 859 size_t expected_word_count = removed_word.word_map_count - 1; |
| 860 if (expected_word_count > 0) { |
| 861 EXPECT_EQ(1, SelectCount(kWordsTableName, kWord, word)) |
| 862 << "Word '" << word << "' not removed from words table."; |
| 863 ASSERT_EQ(1U, private_data->word_map_.count(word)) |
| 864 << "Word '" << word << "' is gone but should still be there."; |
| 865 EXPECT_EQ(expected_word_count, |
| 866 private_data->word_id_history_map_[removed_word.word_id].size()) |
| 867 << "Count for existing word '" << word << "' not decremented. A"; |
| 868 EXPECT_EQ(static_cast<int>(expected_word_count), |
| 869 SelectCount(kWordHistoryTableName, kWordID, |
| 870 removed_word.word_id)) |
| 871 << "Count for existing word '" << word << "' not decremented. B"; |
| 872 } else { |
| 873 EXPECT_EQ(0, SelectCount(kWordsTableName, kWord, word)) |
| 874 << "Word '" << word << "' not removed from words table."; |
| 875 EXPECT_EQ(0U, private_data->word_map_.count(word)) |
| 876 << "Word '" << word << "' to be removed is still there."; |
| 877 ++completely_removed; |
| 878 } |
| 879 } |
| 880 |
| 881 // Verify that the size of the in-memory and on-disk database tables have |
| 882 // changed as expected. |
| 883 EXPECT_EQ(std::max(0, static_cast<int>(available_words_count_) + |
| 884 completely_removed - static_cast<int>(added_words_.size())), |
| 885 static_cast<int>(private_data->available_words_.size())); |
| 886 // The in-memory table will never get smaller as we remember the freed-up |
| 887 // slots and reuse them. |
| 888 EXPECT_EQ(static_cast<int>(word_list_count_) + std::max(0, |
| 889 static_cast<int>(added_words_.size()) - completely_removed), |
| 890 static_cast<int>(private_data->word_list_.size())); |
| 891 EXPECT_EQ(static_cast<int>(word_table_count) + |
| 892 static_cast<int>(added_words_.size()) - completely_removed, |
| 893 SelectCount(kWordsTableName)); |
| 894 } |
| 895 |
| 896 int CacheChecker::SelectCount(const char* table) { |
| 897 std::string sql(StringPrintf("SELECT COUNT(*) FROM %s", table)); |
| 898 sql::Statement statement(db_->GetUniqueStatement(sql.c_str())); |
| 899 return (statement.Step()) ? statement.ColumnInt(0) : -1; |
| 900 } |
| 901 |
| 902 int CacheChecker::SelectCount(const char* table, |
| 903 const char* column, |
| 904 const string16& word) { |
| 905 std::string sql(StringPrintf("SELECT COUNT(*) FROM %s WHERE %s = ?", |
| 906 table, column)); |
| 907 sql::Statement statement(db_->GetUniqueStatement(sql.c_str())); |
| 908 statement.BindString16(0, word); |
| 909 return (statement.Step()) ? statement.ColumnInt(0) : -1; |
| 910 } |
| 911 |
| 912 int CacheChecker::SelectCount( |
| 913 const char* table, |
| 914 const char* column, |
| 915 WordID word_id) { |
| 916 std::string sql(StringPrintf("SELECT COUNT(*) FROM %s WHERE %s = ?", |
| 917 table, column)); |
| 918 sql::Statement statement(db_->GetUniqueStatement(sql.c_str())); |
| 919 statement.BindInt(0, word_id); |
| 920 return (statement.Step()) ? statement.ColumnInt(0) : -1; |
| 921 } |
| 922 |
| 923 int CacheChecker::SelectCount(const char* table, |
| 924 const char* column, |
| 925 HistoryID history_id) { |
| 926 std::string sql(StringPrintf("SELECT COUNT(*) FROM %s WHERE %s = ?", |
| 927 table, column)); |
| 928 sql::Statement statement(db_->GetUniqueStatement(sql.c_str())); |
| 929 statement.BindInt(0, history_id); |
| 930 return (statement.Step()) ? statement.ColumnInt(0) : -1; |
| 931 } |
| 932 |
| 933 // InMemoryURLIndexCacheTest --------------------------------------------------- |
| 934 |
| 935 void InMemoryURLIndexCacheTest::SetUp() { |
| 936 profile_.reset(new CacheTestingProfile); |
| 937 InMemoryURLIndexBaseTest::SetUp(); |
| 938 LoadIndex(); |
| 939 DCHECK(url_index_->index_available()); |
| 940 } |
| 941 |
| 942 FilePath::StringType InMemoryURLIndexCacheTest::TestDBName() const { |
| 943 return FILE_PATH_LITERAL("url_history_provider_test.db.txt"); |
| 944 } |
| 945 |
| 946 TEST_F(InMemoryURLIndexCacheTest, CacheAddRemove) { |
| 947 // Initialize the cache then add and remove some history items. |
| 948 const URLID kNewRowID = 250; |
| 949 URLRow new_row(MakeURLRowWithID("http://www.frank-and-earnest.com/", |
| 950 "Frank and Earnest Go Crash in Washington", |
| 951 10, 0, 5, kNewRowID)); |
| 952 |
| 953 { |
| 954 // Add a new URL. |
| 955 String16Set existing_words(String16SetFromString16( |
| 956 UTF8ToUTF16("http www and com crash in"), NULL)); |
| 957 // New words: frank, earnest, go, washington. |
| 958 String16Set added_words(String16SetFromString16( |
| 959 UTF8ToUTF16("frank earnest go washington"), NULL)); |
| 960 String16Set removed_words; |
| 961 CacheChecker cache_checker(url_index_, CacheChecker::URL_ADDED, |
| 962 kNewRowID, existing_words, added_words, removed_words); |
| 963 UpdateURL(new_row); |
| 964 } |
| 965 |
| 966 URLRow old_row(new_row); |
| 967 { |
| 968 // Update an existing URL resulting in a net addition of words. |
| 969 old_row.set_title(UTF8ToUTF16( |
| 970 "Frank and Earnest Go Crazy in Draper Utah USA")); |
| 971 String16Set existing_words(String16SetFromString16( |
| 972 UTF8ToUTF16("http www and com in frank earnest go"), NULL)); |
| 973 String16Set added_words(String16SetFromString16( |
| 974 UTF8ToUTF16("crazy draper utah usa"), NULL)); |
| 975 String16Set removed_words(String16SetFromString16( |
| 976 UTF8ToUTF16("washington crash"), NULL)); |
| 977 CacheChecker cache_checker(url_index_, CacheChecker::URL_UPDATED, |
| 978 kNewRowID, existing_words, added_words, removed_words); |
| 979 UpdateURL(old_row); |
| 980 } |
| 981 |
| 982 { |
| 983 // Update an existing URL resulting in a net removal of words. |
| 984 old_row.set_title(UTF8ToUTF16("Frank and Earnest Go Crazy Crazy")); |
| 985 String16Set existing_words(String16SetFromString16( |
| 986 UTF8ToUTF16("http www and com frank earnest go crazy"), NULL)); |
| 987 String16Set added_words; |
| 988 String16Set removed_words(String16SetFromString16( |
| 989 UTF8ToUTF16("in draper utah usa"), NULL)); |
| 990 CacheChecker cache_checker(url_index_, CacheChecker::URL_UPDATED, |
| 991 kNewRowID, existing_words, added_words, removed_words); |
| 992 UpdateURL(old_row); |
| 993 } |
| 994 |
| 995 { |
| 996 // Delete an existing URL. |
| 997 old_row.set_title(UTF8ToUTF16("Frank and Earnest Go Crazy Crazy")); |
| 998 String16Set existing_words; |
| 999 String16Set added_words; |
| 1000 String16Set removed_words(String16SetFromString16( |
| 1001 UTF8ToUTF16("http www and com frank earnest go crazy"), NULL)); |
| 1002 CacheChecker cache_checker(url_index_, CacheChecker::URL_REMOVED, |
| 1003 kNewRowID, existing_words, added_words, removed_words); |
| 1004 DeleteURL(old_row.url()); |
| 1005 } |
| 1006 } |
| 1007 |
| 1008 // InterposingCacheDatabase ---------------------------------------------------- |
| 1009 |
| 1010 // This class allows certain InMemoryURLCacheDatabase methods to be intercepted |
| 1011 // for purposes of recording or simulating failures. |
| 1012 class InterposingCacheDatabase : public InMemoryURLCacheDatabase { |
| 1013 public: |
| 1014 InterposingCacheDatabase(); |
| 1015 |
| 1016 virtual bool Reset() OVERRIDE; |
| 1017 virtual bool Refresh(const URLIndexPrivateData& index_data) OVERRIDE; |
| 1018 virtual void AddWordToWordsTask(WordID word_id, |
| 1019 const string16& uni_word) OVERRIDE; |
| 1020 |
| 1021 void set_simulate_update_fail(bool fail) { simulate_update_fail_ = fail; } |
| 1022 void set_simulate_refresh_fail(bool fail) { simulate_refresh_fail_ = fail; } |
| 1023 void set_tracking_calls(bool tracking) { tracking_calls_ = tracking; } |
| 1024 int reset_calling_sequence() const { return reset_calling_sequence_; } |
| 1025 int refresh_calling_sequence() const { return refresh_calling_sequence_; } |
| 1026 void set_update_error(int error) { update_error_ = error; } |
| 1027 bool failure_occurred() const { return update_error_ != SQLITE_OK; } |
| 1028 |
| 1029 private: |
| 1030 virtual ~InterposingCacheDatabase() {} |
| 1031 |
| 1032 bool simulate_update_fail_; |
| 1033 bool simulate_refresh_fail_; |
| 1034 int next_calling_sequence_; |
| 1035 bool tracking_calls_; |
| 1036 int reset_calling_sequence_; |
| 1037 int refresh_calling_sequence_; |
| 1038 }; |
| 1039 |
| 1040 InterposingCacheDatabase::InterposingCacheDatabase() |
| 1041 : simulate_update_fail_(false), |
| 1042 simulate_refresh_fail_(false), |
| 1043 next_calling_sequence_(0), |
| 1044 tracking_calls_(false), |
| 1045 reset_calling_sequence_(-1), |
| 1046 refresh_calling_sequence_(-1) { |
| 1047 } |
| 1048 |
| 1049 bool InterposingCacheDatabase::Reset() { |
| 1050 bool success = InMemoryURLCacheDatabase::Reset(); |
| 1051 if (tracking_calls_) |
| 1052 reset_calling_sequence_ = next_calling_sequence_++; |
| 1053 return success; |
| 1054 } |
| 1055 |
| 1056 bool InterposingCacheDatabase::Refresh( |
| 1057 const URLIndexPrivateData& index_data) { |
| 1058 bool success = |
| 1059 !simulate_refresh_fail_ && InMemoryURLCacheDatabase::Refresh(index_data); |
| 1060 if (tracking_calls_) |
| 1061 refresh_calling_sequence_ = next_calling_sequence_++; |
| 1062 return success; |
| 1063 } |
| 1064 |
| 1065 void InterposingCacheDatabase::AddWordToWordsTask( |
| 1066 WordID word_id, |
| 1067 const string16& uni_word) { |
| 1068 if (simulate_update_fail_) |
| 1069 set_update_error(SQLITE_CORRUPT); |
| 1070 else |
| 1071 InMemoryURLCacheDatabase::AddWordToWordsTask(word_id, uni_word); |
| 1072 } |
| 1073 |
| 1074 // IntercessionaryIndexTest ---------------------------------------------------- |
| 1075 |
| 1076 // Tests which intercede in some way with the normal operation of the index, |
| 1077 // its private data, and/or the cache database. |
| 1078 class IntercessionaryIndexTest : public InMemoryURLIndexBaseTest { |
| 1079 public: |
| 1080 IntercessionaryIndexTest(); |
| 1081 ~IntercessionaryIndexTest() {} |
| 1082 |
| 1083 void SetUp() OVERRIDE; |
| 1084 |
| 1085 // Provides custom test data file name. |
| 1086 virtual FilePath::StringType TestDBName() const OVERRIDE; |
1119 | 1087 |
1120 protected: | 1088 protected: |
1121 virtual void SetUp() OVERRIDE; | 1089 scoped_refptr<InterposingCacheDatabase> test_db_; |
1122 | 1090 URLIndexPrivateData* private_data_; |
1123 // Pass-through functions to simplify our friendship with InMemoryURLIndex. | |
1124 void set_history_dir(const FilePath& dir_path); | |
1125 bool GetCacheFilePath(FilePath* file_path) const; | |
1126 | |
1127 ScopedTempDir temp_dir_; | |
1128 scoped_ptr<InMemoryURLIndex> url_index_; | |
1129 }; | 1091 }; |
1130 | 1092 |
1131 void InMemoryURLIndexCacheTest::SetUp() { | 1093 IntercessionaryIndexTest::IntercessionaryIndexTest() |
1132 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | 1094 : test_db_(new InterposingCacheDatabase) { |
1133 FilePath path(temp_dir_.path()); | 1095 } |
1134 url_index_.reset( | 1096 |
1135 new InMemoryURLIndex(NULL, path, "en,ja,hi,zh")); | 1097 void IntercessionaryIndexTest::SetUp() { |
1136 } | 1098 // Set things up without enabling the cache database. |
1137 | 1099 InMemoryURLIndexBaseTest::SetUp(); |
1138 void InMemoryURLIndexCacheTest::set_history_dir(const FilePath& dir_path) { | 1100 LoadIndex(); |
1139 return url_index_->set_history_dir(dir_path); | 1101 DCHECK(url_index_->index_available()); |
1140 } | 1102 |
1141 | 1103 // Enabled our custom database and refresh it. |
1142 bool InMemoryURLIndexCacheTest::GetCacheFilePath(FilePath* file_path) const { | 1104 FilePath dir_path; |
1143 DCHECK(file_path); | 1105 private_data_ = GetPrivateData(); |
1144 return url_index_->GetCacheFilePath(file_path); | 1106 DCHECK(private_data_->GetCacheDBPath(&dir_path)); |
1145 } | 1107 base::SequencedWorkerPool::SequenceToken sequence_token = |
1146 | 1108 url_index_->sequence_token_for_testing(); |
1147 TEST_F(InMemoryURLIndexCacheTest, CacheFilePath) { | 1109 content::BrowserThread::GetBlockingPool()->PostWorkerTask(FROM_HERE, |
1148 FilePath expectedPath = | 1110 base::Bind(base::IgnoreResult(&InMemoryURLCacheDatabase::Init), |
1149 temp_dir_.path().Append(FILE_PATH_LITERAL("History Provider Cache")); | 1111 test_db_, dir_path, sequence_token)); |
1150 std::vector<FilePath::StringType> expected_parts; | 1112 content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
1151 expectedPath.GetComponents(&expected_parts); | 1113 private_data_->SetCacheDatabaseForTesting(test_db_); |
1152 FilePath full_file_path; | 1114 private_data_->set_cache_enabled(true); |
1153 ASSERT_TRUE(GetCacheFilePath(&full_file_path)); | 1115 content::BrowserThread::GetBlockingPool()->PostWorkerTask(FROM_HERE, |
1154 std::vector<FilePath::StringType> actual_parts; | 1116 base::Bind(&URLIndexPrivateData::RefreshCacheTask, private_data_)); |
1155 full_file_path.GetComponents(&actual_parts); | 1117 content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
1156 ASSERT_EQ(expected_parts.size(), actual_parts.size()); | 1118 test_db_->set_tracking_calls(true); |
1157 size_t count = expected_parts.size(); | 1119 } |
1158 for (size_t i = 0; i < count; ++i) | 1120 |
1159 EXPECT_EQ(expected_parts[i], actual_parts[i]); | 1121 FilePath::StringType IntercessionaryIndexTest::TestDBName() const { |
1160 // Must clear the history_dir_ to satisfy the dtor's DCHECK. | 1122 return FILE_PATH_LITERAL("url_history_provider_test.db.txt"); |
1161 set_history_dir(FilePath()); | 1123 } |
| 1124 |
| 1125 TEST_F(IntercessionaryIndexTest, CacheDatabaseFailure) { |
| 1126 // Perform an update but do not fail. |
| 1127 const URLID kNewRowID = 250; |
| 1128 URLRow new_row(MakeURLRowWithID("http://www.frank-and-earnest.com/", |
| 1129 "Frank and Earnest Go Crash in Washington", |
| 1130 10, 0, 5, kNewRowID)); |
| 1131 UpdateURL(new_row); |
| 1132 EXPECT_EQ(-1, test_db_->reset_calling_sequence()); |
| 1133 EXPECT_EQ(-1, test_db_->refresh_calling_sequence()); |
| 1134 EXPECT_FALSE(test_db_->failure_occurred()); |
| 1135 EXPECT_TRUE(private_data_->cache_enabled()); |
| 1136 |
| 1137 // Perform an update that fails but remediation succeeds. |
| 1138 test_db_->set_simulate_update_fail(true); |
| 1139 URLRow old_row(new_row); |
| 1140 old_row.set_title(UTF8ToUTF16( |
| 1141 "Frank and Earnest Go Crazy in Draper Utah USA")); |
| 1142 content::WindowedNotificationObserver update_failure_observer( |
| 1143 chrome::NOTIFICATION_IN_MEMORY_URL_CACHE_DATABASE_FAILURE, |
| 1144 content::NotificationService::AllSources()); |
| 1145 UpdateURL(old_row); |
| 1146 update_failure_observer.Wait(); |
| 1147 // Wait for the pending reset and refresh to complete. |
| 1148 content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
| 1149 EXPECT_EQ(0, test_db_->reset_calling_sequence()); |
| 1150 EXPECT_EQ(1, test_db_->refresh_calling_sequence()); |
| 1151 EXPECT_TRUE(test_db_->failure_occurred()); |
| 1152 EXPECT_TRUE(private_data_->cache_enabled()); |
| 1153 |
| 1154 // Perform an update that fails and remediation fails. |
| 1155 test_db_->set_simulate_update_fail(true); |
| 1156 test_db_->set_simulate_refresh_fail(true); |
| 1157 test_db_->set_update_error(SQLITE_OK); |
| 1158 old_row.set_title(UTF8ToUTF16( |
| 1159 "Frank and Earnest Light Up Dizzy World")); |
| 1160 content::WindowedNotificationObserver refresh_failure_observer( |
| 1161 chrome::NOTIFICATION_IN_MEMORY_URL_CACHE_DATABASE_FAILURE, |
| 1162 content::NotificationService::AllSources()); |
| 1163 UpdateURL(old_row); |
| 1164 refresh_failure_observer.Wait(); |
| 1165 // Wait for the pending reset and refresh to complete. |
| 1166 content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
| 1167 EXPECT_EQ(2, test_db_->reset_calling_sequence()); |
| 1168 EXPECT_EQ(3, test_db_->refresh_calling_sequence()); |
| 1169 EXPECT_TRUE(test_db_->failure_occurred()); |
| 1170 EXPECT_FALSE(private_data_->cache_enabled()); |
| 1171 } |
| 1172 |
| 1173 TEST_F(IntercessionaryIndexTest, ShutdownDuringCacheRefresh) { |
| 1174 // Pretend that the cache is dysfunctional. |
| 1175 private_data_->set_cache_enabled(false); |
| 1176 // Kick off a refresh and immediately shut down. |
| 1177 url_index_->PostRefreshCacheTask(); |
| 1178 url_index_->Shutdown(); |
| 1179 // The index should still be viable but the cache should have been shut down. |
| 1180 EXPECT_FALSE(private_data_->cache_enabled()); |
| 1181 EXPECT_TRUE(url_index_->index_available()); |
1162 } | 1182 } |
1163 | 1183 |
1164 } // namespace history | 1184 } // namespace history |
OLD | NEW |