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 "base/memory/ref_counted.h" | 5 #include <algorithm> |
| 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" |
6 #include "base/string_util.h" | 14 #include "base/string_util.h" |
7 #include "base/stringprintf.h" | |
8 #include "base/threading/sequenced_worker_pool.h" | |
9 #include "base/utf_string_conversions.h" | 15 #include "base/utf_string_conversions.h" |
10 #include "chrome/browser/autocomplete/autocomplete_provider.h" | 16 #include "chrome/browser/autocomplete/autocomplete_provider.h" |
11 #include "chrome/browser/bookmarks/bookmark_model.h" | 17 #include "chrome/browser/history/history.h" |
12 #include "chrome/browser/bookmarks/bookmark_model_factory.h" | 18 #include "chrome/browser/history/history_backend.h" |
| 19 #include "chrome/browser/history/history_database.h" |
13 #include "chrome/browser/history/history_notifications.h" | 20 #include "chrome/browser/history/history_notifications.h" |
14 #include "chrome/browser/history/in_memory_url_cache_database.h" | 21 #include "chrome/browser/history/history_service_factory.h" |
15 #include "chrome/browser/history/in_memory_url_index_base_unittest.h" | 22 #include "chrome/browser/history/in_memory_url_index.h" |
16 #include "chrome/browser/history/in_memory_url_index_types.h" | 23 #include "chrome/browser/history/in_memory_url_index_types.h" |
17 #include "chrome/browser/history/url_index_private_data.h" | 24 #include "chrome/browser/history/url_index_private_data.h" |
18 #include "chrome/common/chrome_constants.h" | |
19 #include "chrome/common/chrome_notification_types.h" | 25 #include "chrome/common/chrome_notification_types.h" |
20 #include "chrome/test/base/ui_test_utils.h" | 26 #include "chrome/common/chrome_paths.h" |
21 #include "content/public/browser/notification_service.h" | 27 #include "chrome/test/base/testing_profile.h" |
| 28 #include "content/public/browser/notification_details.h" |
22 #include "content/public/browser/notification_source.h" | 29 #include "content/public/browser/notification_source.h" |
23 #include "sql/connection.h" | 30 #include "content/public/test/test_browser_thread.h" |
24 #include "third_party/sqlite/sqlite3.h" | 31 #include "sql/transaction.h" |
25 | 32 #include "testing/gtest/include/gtest/gtest.h" |
26 using history::String16SetFromString16; | 33 |
27 | 34 using content::BrowserThread; |
28 //------------------------------------------------------------------------------ | 35 |
29 | 36 // The test version of the history url database table ('url') is contained in |
30 // This class overrides the standard TestingProfile's disabling of the | 37 // a database file created from a text file('url_history_provider_test.db.txt'). |
31 // InMemoryURLIndexCacheDatabase. | 38 // The only difference between this table and a live 'urls' table from a |
32 class CacheTestingProfile : public TestingProfile { | 39 // profile is that the last_visit_time column in the test table contains a |
| 40 // number specifying the number of days relative to 'today' to which the |
| 41 // absolute time should be set during the test setup stage. |
| 42 // |
| 43 // The format of the test database text file is of a SQLite .dump file. |
| 44 // Note that only lines whose first character is an upper-case letter are |
| 45 // processed when creating the test database. |
| 46 |
| 47 namespace history { |
| 48 |
| 49 // ----------------------------------------------------------------------------- |
| 50 |
| 51 // Observer class so the unit tests can wait while the cache is being saved. |
| 52 class CacheFileSaverObserver : public InMemoryURLIndex::SaveCacheObserver { |
33 public: | 53 public: |
34 // Creates a testing profile but enables the cache database. | 54 explicit CacheFileSaverObserver(MessageLoop* loop); |
35 bool InitHistoryService(HistoryService* history_service, | 55 virtual void OnCacheSaveFinished(bool succeeded) OVERRIDE; |
36 bool no_db) OVERRIDE; | 56 |
| 57 MessageLoop* loop_; |
| 58 bool succeeded_; |
| 59 DISALLOW_COPY_AND_ASSIGN(CacheFileSaverObserver); |
37 }; | 60 }; |
38 | 61 |
39 bool CacheTestingProfile::InitHistoryService(HistoryService* history_service, | 62 CacheFileSaverObserver::CacheFileSaverObserver(MessageLoop* loop) |
40 bool no_db) { | 63 : loop_(loop), |
41 DCHECK(history_service); | 64 succeeded_(false) { |
42 return history_service->Init(GetPath(), | 65 DCHECK(loop); |
43 reinterpret_cast<BookmarkService*>( | 66 } |
44 BookmarkModelFactory::GetForProfile(this)), | 67 |
45 no_db, false); | 68 void CacheFileSaverObserver::OnCacheSaveFinished(bool succeeded) { |
46 } | 69 succeeded_ = succeeded; |
47 | 70 loop_->Quit(); |
48 namespace history { | 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 } |
49 | 94 |
50 // ----------------------------------------------------------------------------- | 95 // ----------------------------------------------------------------------------- |
51 | 96 |
52 // Creates a URLRow with basic data for |url|, |title|, |visit_count|, | 97 class InMemoryURLIndexTest : public testing::Test { |
53 // |typed_count| and |id|. |last_visit_ago| gives the number of days from now | 98 public: |
54 // to which to set the URL's last_visit. | 99 InMemoryURLIndexTest(); |
55 URLRow MakeURLRowWithID(const char* url, | 100 |
56 const char* title, | |
57 int visit_count, | |
58 int last_visit_ago, | |
59 int typed_count, | |
60 URLID id); | |
61 | |
62 class InMemoryURLIndexTest : public InMemoryURLIndexBaseTest { | |
63 protected: | 101 protected: |
64 void SetUp() OVERRIDE; | 102 // Test setup. |
65 | 103 virtual void SetUp(); |
66 // Provides custom test data file name. | 104 |
67 virtual FilePath::StringType TestDBName() const OVERRIDE; | 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 |
68 | 131 |
69 // Validates that the given |term| is contained in |cache| and that it is | 132 // Validates that the given |term| is contained in |cache| and that it is |
70 // marked as in-use. | 133 // marked as in-use. |
71 void CheckTerm(const URLIndexPrivateData::SearchTermCacheMap& cache, | 134 void CheckTerm(const URLIndexPrivateData::SearchTermCacheMap& cache, |
72 string16 term) const; | 135 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_; |
73 }; | 170 }; |
74 | 171 |
| 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 |
75 void InMemoryURLIndexTest::SetUp() { | 227 void InMemoryURLIndexTest::SetUp() { |
76 // Set things up without enabling the cache database. | 228 // We cannot access the database until the backend has been loaded. |
77 InMemoryURLIndexBaseTest::SetUp(); | 229 profile_.CreateHistoryService(true, false); |
78 LoadIndex(); | 230 profile_.CreateBookmarkModel(true); |
| 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_); |
79 } | 299 } |
80 | 300 |
81 FilePath::StringType InMemoryURLIndexTest::TestDBName() const { | 301 FilePath::StringType InMemoryURLIndexTest::TestDBName() const { |
82 return FILE_PATH_LITERAL("url_history_provider_test.db.txt"); | 302 return FILE_PATH_LITERAL("url_history_provider_test.db.txt"); |
83 } | 303 } |
84 | 304 |
85 URLRow MakeURLRowWithID(const char* url, | 305 #if 0 |
86 const char* title, | 306 URLRow InMemoryURLIndexTest::MakeURLRow(const char* url, |
87 int visit_count, | 307 const char* title, |
88 int last_visit_ago, | 308 int visit_count, |
89 int typed_count, | 309 int last_visit_ago, |
90 URLID id) { | 310 int typed_count) { |
91 URLRow row(GURL(url), id); | 311 URLRow row(GURL(url), 0); |
92 row.set_title(UTF8ToUTF16(title)); | 312 row.set_title(UTF8ToUTF16(title)); |
93 row.set_visit_count(visit_count); | 313 row.set_visit_count(visit_count); |
94 row.set_typed_count(typed_count); | 314 row.set_typed_count(typed_count); |
95 row.set_last_visit(base::Time::NowFromSystemTime() - | 315 row.set_last_visit(base::Time::NowFromSystemTime() - |
96 base::TimeDelta::FromDays(last_visit_ago)); | 316 base::TimeDelta::FromDays(last_visit_ago)); |
97 return row; | 317 return row; |
98 } | 318 } |
99 | 319 |
| 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 |
100 void InMemoryURLIndexTest::CheckTerm( | 348 void InMemoryURLIndexTest::CheckTerm( |
101 const URLIndexPrivateData::SearchTermCacheMap& cache, | 349 const URLIndexPrivateData::SearchTermCacheMap& cache, |
102 string16 term) const { | 350 string16 term) const { |
103 URLIndexPrivateData::SearchTermCacheMap::const_iterator cache_iter( | 351 URLIndexPrivateData::SearchTermCacheMap::const_iterator cache_iter( |
104 cache.find(term)); | 352 cache.find(term)); |
105 ASSERT_TRUE(cache.end() != cache_iter) | 353 ASSERT_TRUE(cache.end() != cache_iter) |
106 << "Cache does not contain '" << term << "' but should."; | 354 << "Cache does not contain '" << term << "' but should."; |
107 URLIndexPrivateData::SearchTermCacheItem cache_item = cache_iter->second; | 355 URLIndexPrivateData::SearchTermCacheItem cache_item = cache_iter->second; |
108 EXPECT_TRUE(cache_item.used_) | 356 EXPECT_TRUE(cache_item.used_) |
109 << "Cache item '" << term << "' should be marked as being in use."; | 357 << "Cache item '" << term << "' should be marked as being in use."; |
110 } | 358 } |
111 | 359 |
| 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 |
112 //------------------------------------------------------------------------------ | 469 //------------------------------------------------------------------------------ |
113 | 470 |
114 class LimitedInMemoryURLIndexTest : public InMemoryURLIndexTest { | 471 class LimitedInMemoryURLIndexTest : public InMemoryURLIndexTest { |
115 protected: | 472 protected: |
116 FilePath::StringType TestDBName() const OVERRIDE; | 473 FilePath::StringType TestDBName() const; |
117 }; | 474 }; |
118 | 475 |
119 FilePath::StringType LimitedInMemoryURLIndexTest::TestDBName() const { | 476 FilePath::StringType LimitedInMemoryURLIndexTest::TestDBName() const { |
120 return FILE_PATH_LITERAL("url_history_provider_test_limited.db.txt"); | 477 return FILE_PATH_LITERAL("url_history_provider_test_limited.db.txt"); |
121 } | 478 } |
122 | 479 |
123 TEST_F(LimitedInMemoryURLIndexTest, Initialization) { | 480 TEST_F(LimitedInMemoryURLIndexTest, Initialization) { |
124 // Verify that the database contains the expected number of items, which | 481 // Verify that the database contains the expected number of items, which |
125 // is the pre-filtered count, i.e. all of the items. | 482 // is the pre-filtered count, i.e. all of the items. |
126 sql::Statement statement( | 483 sql::Statement statement(GetDB().GetUniqueStatement("SELECT * FROM urls;")); |
127 history_database_->get_db_for_testing()->GetUniqueStatement( | |
128 "SELECT * FROM urls;")); | |
129 ASSERT_TRUE(statement.is_valid()); | 484 ASSERT_TRUE(statement.is_valid()); |
130 uint64 row_count = 0; | 485 uint64 row_count = 0; |
131 while (statement.Step()) ++row_count; | 486 while (statement.Step()) ++row_count; |
132 EXPECT_EQ(1U, row_count); | 487 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()); |
133 | 493 |
134 // history_info_map_ should have the same number of items as were filtered. | 494 // history_info_map_ should have the same number of items as were filtered. |
135 EXPECT_EQ(1U, url_index_->private_data_->history_info_map_.size()); | 495 EXPECT_EQ(1U, private_data.history_info_map_.size()); |
136 EXPECT_EQ(35U, url_index_->private_data_->char_word_map_.size()); | 496 EXPECT_EQ(35U, private_data.char_word_map_.size()); |
137 EXPECT_EQ(17U, url_index_->private_data_->word_map_.size()); | 497 EXPECT_EQ(17U, private_data.word_map_.size()); |
138 } | 498 } |
139 | 499 |
140 //------------------------------------------------------------------------------ | |
141 | |
142 TEST_F(InMemoryURLIndexTest, Retrieval) { | 500 TEST_F(InMemoryURLIndexTest, Retrieval) { |
143 // See if a very specific term gives a single result. | 501 // See if a very specific term gives a single result. |
144 // Note that in each case the term will be lowercased by the search. | |
145 ScoredHistoryMatches matches = | 502 ScoredHistoryMatches matches = |
146 url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport")); | 503 url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport")); |
147 ASSERT_EQ(1U, matches.size()); | 504 ASSERT_EQ(1U, matches.size()); |
148 | 505 |
149 // Verify that we got back the result we expected. | 506 // Verify that we got back the result we expected. |
150 EXPECT_EQ(5, matches[0].url_info.id()); | 507 EXPECT_EQ(5, matches[0].url_info.id()); |
151 EXPECT_EQ("http://drudgereport.com/", matches[0].url_info.url().spec()); | 508 EXPECT_EQ("http://drudgereport.com/", matches[0].url_info.url().spec()); |
152 EXPECT_EQ(ASCIIToUTF16("DRUDGE REPORT 2010"), matches[0].url_info.title()); | 509 EXPECT_EQ(ASCIIToUTF16("DRUDGE REPORT 2010"), matches[0].url_info.title()); |
153 EXPECT_TRUE(matches[0].can_inline); | 510 EXPECT_TRUE(matches[0].can_inline); |
154 | 511 |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
286 TEST_F(InMemoryURLIndexTest, HugeResultSet) { | 643 TEST_F(InMemoryURLIndexTest, HugeResultSet) { |
287 // Create a huge set of qualifying history items. | 644 // Create a huge set of qualifying history items. |
288 for (URLID row_id = 5000; row_id < 6000; ++row_id) { | 645 for (URLID row_id = 5000; row_id < 6000; ++row_id) { |
289 URLRow new_row(GURL("http://www.brokeandaloneinmanitoba.com/"), row_id); | 646 URLRow new_row(GURL("http://www.brokeandaloneinmanitoba.com/"), row_id); |
290 new_row.set_last_visit(base::Time::Now()); | 647 new_row.set_last_visit(base::Time::Now()); |
291 EXPECT_TRUE(UpdateURL(new_row)); | 648 EXPECT_TRUE(UpdateURL(new_row)); |
292 } | 649 } |
293 | 650 |
294 ScoredHistoryMatches matches = | 651 ScoredHistoryMatches matches = |
295 url_index_->HistoryItemsForTerms(ASCIIToUTF16("b")); | 652 url_index_->HistoryItemsForTerms(ASCIIToUTF16("b")); |
296 const URLIndexPrivateData* private_data(GetPrivateData()); | 653 URLIndexPrivateData& private_data(*GetPrivateData()); |
297 ASSERT_EQ(AutocompleteProvider::kMaxMatches, matches.size()); | 654 ASSERT_EQ(AutocompleteProvider::kMaxMatches, matches.size()); |
298 // There are 7 matches already in the database. | 655 // There are 7 matches already in the database. |
299 ASSERT_EQ(1008U, private_data->pre_filter_item_count_); | 656 ASSERT_EQ(1008U, private_data.pre_filter_item_count_); |
300 ASSERT_EQ(500U, private_data->post_filter_item_count_); | 657 ASSERT_EQ(500U, private_data.post_filter_item_count_); |
301 ASSERT_EQ(AutocompleteProvider::kMaxMatches, | 658 ASSERT_EQ(AutocompleteProvider::kMaxMatches, |
302 private_data->post_scoring_item_count_); | 659 private_data.post_scoring_item_count_); |
303 } | 660 } |
304 | 661 |
305 TEST_F(InMemoryURLIndexTest, TitleSearch) { | 662 TEST_F(InMemoryURLIndexTest, TitleSearch) { |
306 // Signal if someone has changed the test DB. | 663 // Signal if someone has changed the test DB. |
307 EXPECT_EQ(28U, GetPrivateData()->history_info_map_.size()); | 664 EXPECT_EQ(28U, GetPrivateData()->history_info_map_.size()); |
308 | 665 |
309 // Ensure title is being searched. | 666 // Ensure title is being searched. |
310 ScoredHistoryMatches matches = | 667 ScoredHistoryMatches matches = |
311 url_index_->HistoryItemsForTerms(ASCIIToUTF16("MORTGAGE RATE DROPS")); | 668 url_index_->HistoryItemsForTerms(ASCIIToUTF16("MORTGAGE RATE DROPS")); |
312 ASSERT_EQ(1U, matches.size()); | 669 ASSERT_EQ(1U, matches.size()); |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
382 EXPECT_EQ(28, matches[0].url_info.id()); | 739 EXPECT_EQ(28, matches[0].url_info.id()); |
383 } | 740 } |
384 | 741 |
385 TEST_F(InMemoryURLIndexTest, TypedCharacterCaching) { | 742 TEST_F(InMemoryURLIndexTest, TypedCharacterCaching) { |
386 // Verify that match results for previously typed characters are retained | 743 // Verify that match results for previously typed characters are retained |
387 // (in the term_char_word_set_cache_) and reused, if possible, in future | 744 // (in the term_char_word_set_cache_) and reused, if possible, in future |
388 // autocompletes. | 745 // autocompletes. |
389 typedef URLIndexPrivateData::SearchTermCacheMap::iterator CacheIter; | 746 typedef URLIndexPrivateData::SearchTermCacheMap::iterator CacheIter; |
390 typedef URLIndexPrivateData::SearchTermCacheItem CacheItem; | 747 typedef URLIndexPrivateData::SearchTermCacheItem CacheItem; |
391 | 748 |
392 const URLIndexPrivateData::SearchTermCacheMap& cache( | 749 URLIndexPrivateData::SearchTermCacheMap& cache( |
393 GetPrivateData()->search_term_cache_); | 750 GetPrivateData()->search_term_cache_); |
394 | 751 |
395 // The cache should be empty at this point. | 752 // The cache should be empty at this point. |
396 EXPECT_EQ(0U, cache.size()); | 753 EXPECT_EQ(0U, cache.size()); |
397 | 754 |
398 // Now simulate typing search terms into the omnibox and check the state of | 755 // Now simulate typing search terms into the omnibox and check the state of |
399 // the cache as each item is 'typed'. | 756 // the cache as each item is 'typed'. |
400 | 757 |
401 // Simulate typing "r" giving "r" in the simulated omnibox. The results for | 758 // Simulate typing "r" giving "r" in the simulated omnibox. The results for |
402 // 'r' will be not cached because it is only 1 character long. | 759 // 'r' will be not cached because it is only 1 character long. |
(...skipping 28 matching lines...) Expand all Loading... |
431 CheckTerm(cache, ASCIIToUTF16("mort")); | 788 CheckTerm(cache, ASCIIToUTF16("mort")); |
432 CheckTerm(cache, ASCIIToUTF16("reco")); | 789 CheckTerm(cache, ASCIIToUTF16("reco")); |
433 | 790 |
434 // Simulate a <DELETE> by removing the 'reco' and adding back the 'rec'. | 791 // Simulate a <DELETE> by removing the 'reco' and adding back the 'rec'. |
435 url_index_->HistoryItemsForTerms(ASCIIToUTF16("mort rec")); | 792 url_index_->HistoryItemsForTerms(ASCIIToUTF16("mort rec")); |
436 ASSERT_EQ(2U, cache.size()); | 793 ASSERT_EQ(2U, cache.size()); |
437 CheckTerm(cache, ASCIIToUTF16("mort")); | 794 CheckTerm(cache, ASCIIToUTF16("mort")); |
438 CheckTerm(cache, ASCIIToUTF16("rec")); | 795 CheckTerm(cache, ASCIIToUTF16("rec")); |
439 } | 796 } |
440 | 797 |
| 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 |
441 TEST_F(InMemoryURLIndexTest, AddNewRows) { | 920 TEST_F(InMemoryURLIndexTest, AddNewRows) { |
442 // Verify that the row we're going to add does not already exist. | 921 // Verify that the row we're going to add does not already exist. |
443 URLID new_row_id = 87654321; | 922 URLID new_row_id = 87654321; |
444 // Newly created URLRows get a last_visit time of 'right now' so it should | 923 // Newly created URLRows get a last_visit time of 'right now' so it should |
445 // qualify as a quick result candidate. | 924 // qualify as a quick result candidate. |
446 EXPECT_TRUE(url_index_->HistoryItemsForTerms( | 925 EXPECT_TRUE(url_index_->HistoryItemsForTerms( |
447 ASCIIToUTF16("brokeandalone")).empty()); | 926 ASCIIToUTF16("brokeandalone")).empty()); |
448 | 927 |
449 // Add a new row. | 928 // Add a new row. |
450 URLRow new_row(GURL("http://www.brokeandaloneinmanitoba.com/"), new_row_id++); | 929 URLRow new_row(GURL("http://www.brokeandaloneinmanitoba.com/"), new_row_id++); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
485 TEST_F(InMemoryURLIndexTest, ExpireRow) { | 964 TEST_F(InMemoryURLIndexTest, ExpireRow) { |
486 ScoredHistoryMatches matches = | 965 ScoredHistoryMatches matches = |
487 url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport")); | 966 url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport")); |
488 ASSERT_EQ(1U, matches.size()); | 967 ASSERT_EQ(1U, matches.size()); |
489 | 968 |
490 // Determine the row id for the result, remember that id, broadcast a | 969 // Determine the row id for the result, remember that id, broadcast a |
491 // delete notification, then ensure that the row has been deleted. | 970 // delete notification, then ensure that the row has been deleted. |
492 URLsDeletedDetails deleted_details; | 971 URLsDeletedDetails deleted_details; |
493 deleted_details.all_history = false; | 972 deleted_details.all_history = false; |
494 deleted_details.rows.push_back(matches[0].url_info); | 973 deleted_details.rows.push_back(matches[0].url_info); |
495 content::Source<InMemoryURLIndexTest> source(this); | 974 Observe(chrome::NOTIFICATION_HISTORY_URLS_DELETED, |
496 url_index_->Observe( | 975 content::Source<InMemoryURLIndexTest>(this), |
497 chrome::NOTIFICATION_HISTORY_URLS_DELETED, | 976 content::Details<history::HistoryDetails>(&deleted_details)); |
498 content::Source<InMemoryURLIndexTest>(this), | |
499 content::Details<history::HistoryDetails>(&deleted_details)); | |
500 EXPECT_TRUE(url_index_->HistoryItemsForTerms( | 977 EXPECT_TRUE(url_index_->HistoryItemsForTerms( |
501 ASCIIToUTF16("DrudgeReport")).empty()); | 978 ASCIIToUTF16("DrudgeReport")).empty()); |
502 } | 979 } |
503 | 980 |
504 TEST_F(InMemoryURLIndexTest, WhitelistedURLs) { | 981 TEST_F(InMemoryURLIndexTest, WhitelistedURLs) { |
505 struct TestData { | 982 struct TestData { |
506 const std::string url_spec; | 983 const std::string url_spec; |
507 const bool expected_is_whitelisted; | 984 const bool expected_is_whitelisted; |
508 } data[] = { | 985 } data[] = { |
509 // URLs with whitelisted schemes. | 986 // URLs with whitelisted schemes. |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
568 { "tftp://example.com/mystartupfile", false }, | 1045 { "tftp://example.com/mystartupfile", false }, |
569 { "tip://123.123.123.123/?urn:xopen:xid", false }, | 1046 { "tip://123.123.123.123/?urn:xopen:xid", false }, |
570 { "tv:nbc.com", false }, | 1047 { "tv:nbc.com", false }, |
571 { "urn:foo:A123,456", false }, | 1048 { "urn:foo:A123,456", false }, |
572 { "vemmi://zeus.mctel.fr/demo", false }, | 1049 { "vemmi://zeus.mctel.fr/demo", false }, |
573 { "wais://www.mydomain.net:8765/mydatabase", false }, | 1050 { "wais://www.mydomain.net:8765/mydatabase", false }, |
574 { "xmpp:node@example.com", false }, | 1051 { "xmpp:node@example.com", false }, |
575 { "xmpp://guest@example.com", false }, | 1052 { "xmpp://guest@example.com", false }, |
576 }; | 1053 }; |
577 | 1054 |
578 std::set<std::string> whitelist; | 1055 URLIndexPrivateData& private_data(*GetPrivateData()); |
579 URLIndexPrivateData::InitializeSchemeWhitelistForTesting(&whitelist); | 1056 const std::set<std::string>& whitelist(scheme_whitelist()); |
580 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) { | 1057 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) { |
581 GURL url(data[i].url_spec); | 1058 GURL url(data[i].url_spec); |
582 EXPECT_EQ(data[i].expected_is_whitelisted, | 1059 EXPECT_EQ(data[i].expected_is_whitelisted, |
583 URLIndexPrivateData::URLSchemeIsWhitelisted(url, whitelist)); | 1060 private_data.URLSchemeIsWhitelisted(url, whitelist)); |
584 } | 1061 } |
585 } | 1062 } |
586 | 1063 |
587 // InMemoryURLIndexCacheTest --------------------------------------------------- | 1064 TEST_F(InMemoryURLIndexTest, CacheSaveRestore) { |
| 1065 ScopedTempDir temp_directory; |
| 1066 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); |
| 1067 set_history_dir(temp_directory.path()); |
588 | 1068 |
589 class InMemoryURLIndexCacheTest : public InMemoryURLIndexBaseTest { | 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 { |
590 public: | 1117 public: |
591 // A test helper class which captures the state of the URL index and cache | 1118 InMemoryURLIndexCacheTest() {} |
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 | 1119 |
602 // Captures the state of the index and cache and the changes which are | 1120 protected: |
603 // expected to be made. |index| points to the test index. |change_type| | 1121 virtual void SetUp() OVERRIDE; |
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 | 1122 |
619 // Initializes and destroys things in a way that the standard gtest | 1123 // Pass-through functions to simplify our friendship with InMemoryURLIndex. |
620 // EXPECT_xxx macros can be used since those macros cannot be used in | 1124 void set_history_dir(const FilePath& dir_path); |
621 // constructors and destructors. | 1125 bool GetCacheFilePath(FilePath* file_path) const; |
622 void Init(); | |
623 void Destroy(); | |
624 | 1126 |
625 private: | 1127 ScopedTempDir temp_dir_; |
626 // A helper struct that records index current state for words in the list of | 1128 scoped_ptr<InMemoryURLIndex> url_index_; |
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 }; | 1129 }; |
692 | 1130 |
693 // InMemoryURLIndexCacheTest::CacheChecker ------------------------------------- | 1131 void InMemoryURLIndexCacheTest::SetUp() { |
694 | 1132 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
695 // Cache database table names. See in_memory_url_cache_database.cc. | 1133 FilePath path(temp_dir_.path()); |
696 typedef InMemoryURLIndexCacheTest::CacheChecker CacheChecker; | 1134 url_index_.reset( |
697 const char* CacheChecker::kWordsTableName = "words"; | 1135 new InMemoryURLIndex(NULL, path, "en,ja,hi,zh")); |
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 } | 1136 } |
733 | 1137 |
734 void CacheChecker::Init() { | 1138 void InMemoryURLIndexCacheTest::set_history_dir(const FilePath& dir_path) { |
735 // Verify that the existing words exist and remember their counts. | 1139 return url_index_->set_history_dir(dir_path); |
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 } | 1140 } |
783 | 1141 |
784 CacheChecker::~CacheChecker() { | 1142 bool InMemoryURLIndexCacheTest::GetCacheFilePath(FilePath* file_path) const { |
785 Destroy(); | 1143 DCHECK(file_path); |
| 1144 return url_index_->GetCacheFilePath(file_path); |
786 } | 1145 } |
787 | 1146 |
788 void CacheChecker::Destroy() { | 1147 TEST_F(InMemoryURLIndexCacheTest, CacheFilePath) { |
789 // Verify that the existing words still exist and their counts have | 1148 FilePath expectedPath = |
790 // incremented by 1. | 1149 temp_dir_.path().Append(FILE_PATH_LITERAL("History Provider Cache")); |
791 URLIndexPrivateData* private_data(index_.private_data_.get()); | 1150 std::vector<FilePath::StringType> expected_parts; |
792 for (String16Set::iterator word_iter = existing_words_.begin(); | 1151 expectedPath.GetComponents(&expected_parts); |
793 word_iter != existing_words_.end(); ++word_iter) { | 1152 FilePath full_file_path; |
794 string16 word(*word_iter); | 1153 ASSERT_TRUE(GetCacheFilePath(&full_file_path)); |
795 const ExistingWord& existing_word(existing_words_and_counts_[word]); | 1154 std::vector<FilePath::StringType> actual_parts; |
796 size_t expected_count = existing_word.word_map_count; | 1155 full_file_path.GetComponents(&actual_parts); |
797 if (change_type_ == URL_ADDED) | 1156 ASSERT_EQ(expected_parts.size(), actual_parts.size()); |
798 ++expected_count; | 1157 size_t count = expected_parts.size(); |
799 WordID word_id = private_data->word_map_[word]; | 1158 for (size_t i = 0; i < count; ++i) |
800 EXPECT_EQ(expected_count, | 1159 EXPECT_EQ(expected_parts[i], actual_parts[i]); |
801 private_data->word_id_history_map_[word_id].size()) | 1160 // Must clear the history_dir_ to satisfy the dtor's DCHECK. |
802 << "Occurrence count for existing word '" << word | 1161 set_history_dir(FilePath()); |
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; | |
1087 | |
1088 protected: | |
1089 scoped_refptr<InterposingCacheDatabase> test_db_; | |
1090 URLIndexPrivateData* private_data_; | |
1091 }; | |
1092 | |
1093 IntercessionaryIndexTest::IntercessionaryIndexTest() | |
1094 : test_db_(new InterposingCacheDatabase) { | |
1095 } | |
1096 | |
1097 void IntercessionaryIndexTest::SetUp() { | |
1098 // Set things up without enabling the cache database. | |
1099 InMemoryURLIndexBaseTest::SetUp(); | |
1100 LoadIndex(); | |
1101 DCHECK(url_index_->index_available()); | |
1102 | |
1103 // Enabled our custom database and refresh it. | |
1104 FilePath dir_path; | |
1105 private_data_ = GetPrivateData(); | |
1106 DCHECK(private_data_->GetCacheDBPath(&dir_path)); | |
1107 base::SequencedWorkerPool::SequenceToken sequence_token = | |
1108 url_index_->sequence_token_for_testing(); | |
1109 content::BrowserThread::GetBlockingPool()->PostWorkerTask(FROM_HERE, | |
1110 base::Bind(base::IgnoreResult(&InMemoryURLCacheDatabase::Init), | |
1111 test_db_, dir_path, sequence_token)); | |
1112 content::BrowserThread::GetBlockingPool()->FlushForTesting(); | |
1113 private_data_->SetCacheDatabaseForTesting(test_db_); | |
1114 private_data_->set_cache_enabled(true); | |
1115 content::BrowserThread::GetBlockingPool()->PostWorkerTask(FROM_HERE, | |
1116 base::Bind(&URLIndexPrivateData::RefreshCacheTask, private_data_)); | |
1117 content::BrowserThread::GetBlockingPool()->FlushForTesting(); | |
1118 test_db_->set_tracking_calls(true); | |
1119 } | |
1120 | |
1121 FilePath::StringType IntercessionaryIndexTest::TestDBName() const { | |
1122 return FILE_PATH_LITERAL("url_history_provider_test.db.txt"); | |
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()); | |
1182 } | 1162 } |
1183 | 1163 |
1184 } // namespace history | 1164 } // namespace history |
OLD | NEW |