Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: chrome/browser/history/in_memory_url_index_unittest.cc

Issue 963823003: Move InMemoryURLIndex into chrome/browser/autocomplete (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@shortcut-database
Patch Set: Fixing win_chromium_x64_rel_ng build Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <algorithm>
6 #include <fstream>
7
8 #include "base/auto_reset.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/path_service.h"
13 #include "base/run_loop.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/autocomplete/scored_history_match_builder_impl.h"
18 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
19 #include "chrome/browser/history/history_backend.h"
20 #include "chrome/browser/history/history_service.h"
21 #include "chrome/browser/history/history_service_factory.h"
22 #include "chrome/browser/history/in_memory_url_index.h"
23 #include "chrome/browser/history/url_index_private_data.h"
24 #include "chrome/common/chrome_paths.h"
25 #include "chrome/test/base/history_index_restore_observer.h"
26 #include "chrome/test/base/testing_profile.h"
27 #include "components/bookmarks/test/bookmark_test_helpers.h"
28 #include "components/history/core/browser/history_database.h"
29 #include "components/history/core/browser/in_memory_url_index_types.h"
30 #include "content/public/test/test_browser_thread_bundle.h"
31 #include "sql/transaction.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33
34 using base::ASCIIToUTF16;
35
36 // The test version of the history url database table ('url') is contained in
37 // a database file created from a text file('url_history_provider_test.db.txt').
38 // The only difference between this table and a live 'urls' table from a
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 namespace {
49 const size_t kMaxMatches = 3;
50 const char kTestLanguages[] = "en,ja,hi,zh";
51 } // namespace
52
53 // -----------------------------------------------------------------------------
54
55 // Observer class so the unit tests can wait while the cache is being saved.
56 class CacheFileSaverObserver : public InMemoryURLIndex::SaveCacheObserver {
57 public:
58 explicit CacheFileSaverObserver(const base::Closure& task);
59
60 bool succeeded() { return succeeded_; }
61
62 private:
63 // SaveCacheObserver implementation.
64 void OnCacheSaveFinished(bool succeeded) override;
65
66 base::Closure task_;
67 bool succeeded_;
68
69 DISALLOW_COPY_AND_ASSIGN(CacheFileSaverObserver);
70 };
71
72 CacheFileSaverObserver::CacheFileSaverObserver(const base::Closure& task)
73 : task_(task),
74 succeeded_(false) {
75 }
76
77 void CacheFileSaverObserver::OnCacheSaveFinished(bool succeeded) {
78 succeeded_ = succeeded;
79 task_.Run();
80 }
81
82 // -----------------------------------------------------------------------------
83
84 class InMemoryURLIndexTest : public testing::Test {
85 public:
86 InMemoryURLIndexTest();
87
88 protected:
89 // Test setup.
90 void SetUp() override;
91 void TearDown() override;
92
93 // Allows the database containing the test data to be customized by
94 // subclasses.
95 virtual base::FilePath::StringType TestDBName() const;
96
97 // Allows the test to control when the InMemoryURLIndex is initialized.
98 virtual bool InitializeInMemoryURLIndexInSetUp() const;
99
100 // Initialize the InMemoryURLIndex for the tests.
101 void InitializeInMemoryURLIndex();
102
103 // Validates that the given |term| is contained in |cache| and that it is
104 // marked as in-use.
105 void CheckTerm(const URLIndexPrivateData::SearchTermCacheMap& cache,
106 base::string16 term) const;
107
108 // Pass-through function to simplify our friendship with HistoryService.
109 sql::Connection& GetDB();
110
111 // Pass-through functions to simplify our friendship with InMemoryURLIndex.
112 URLIndexPrivateData* GetPrivateData() const;
113 base::CancelableTaskTracker* GetPrivateDataTracker() const;
114 void ClearPrivateData();
115 void set_history_dir(const base::FilePath& dir_path);
116 bool GetCacheFilePath(base::FilePath* file_path) const;
117 void PostRestoreFromCacheFileTask();
118 void PostSaveToCacheFileTask();
119 const std::set<std::string>& scheme_whitelist();
120
121
122 // Pass-through functions to simplify our friendship with URLIndexPrivateData.
123 bool UpdateURL(const URLRow& row);
124 bool DeleteURL(const GURL& url);
125
126 // Data verification helper functions.
127 void ExpectPrivateDataNotEmpty(const URLIndexPrivateData& data);
128 void ExpectPrivateDataEmpty(const URLIndexPrivateData& data);
129 void ExpectPrivateDataEqual(const URLIndexPrivateData& expected,
130 const URLIndexPrivateData& actual);
131
132 ScoredHistoryMatchBuilderImpl builder_;
133 content::TestBrowserThreadBundle thread_bundle_;
134 scoped_ptr<InMemoryURLIndex> url_index_;
135 TestingProfile profile_;
136 HistoryService* history_service_;
137 HistoryDatabase* history_database_;
138 };
139
140 InMemoryURLIndexTest::InMemoryURLIndexTest()
141 : builder_(ScoredHistoryMatchBuilderImpl::IsBookmarkedCallback()),
142 history_service_(nullptr),
143 history_database_(nullptr) {
144 }
145
146 sql::Connection& InMemoryURLIndexTest::GetDB() {
147 return history_database_->GetDB();
148 }
149
150 URLIndexPrivateData* InMemoryURLIndexTest::GetPrivateData() const {
151 DCHECK(url_index_->private_data());
152 return url_index_->private_data();
153 }
154
155 base::CancelableTaskTracker* InMemoryURLIndexTest::GetPrivateDataTracker()
156 const {
157 DCHECK(url_index_->private_data_tracker());
158 return url_index_->private_data_tracker();
159 }
160
161 void InMemoryURLIndexTest::ClearPrivateData() {
162 return url_index_->ClearPrivateData();
163 }
164
165 void InMemoryURLIndexTest::set_history_dir(const base::FilePath& dir_path) {
166 return url_index_->set_history_dir(dir_path);
167 }
168
169 bool InMemoryURLIndexTest::GetCacheFilePath(base::FilePath* file_path) const {
170 DCHECK(file_path);
171 return url_index_->GetCacheFilePath(file_path);
172 }
173
174 void InMemoryURLIndexTest::PostRestoreFromCacheFileTask() {
175 url_index_->PostRestoreFromCacheFileTask();
176 }
177
178 void InMemoryURLIndexTest::PostSaveToCacheFileTask() {
179 url_index_->PostSaveToCacheFileTask();
180 }
181
182 const std::set<std::string>& InMemoryURLIndexTest::scheme_whitelist() {
183 return url_index_->scheme_whitelist();
184 }
185
186 bool InMemoryURLIndexTest::UpdateURL(const URLRow& row) {
187 return GetPrivateData()->UpdateURL(history_service_,
188 row,
189 url_index_->languages_,
190 url_index_->scheme_whitelist_,
191 GetPrivateDataTracker());
192 }
193
194 bool InMemoryURLIndexTest::DeleteURL(const GURL& url) {
195 return GetPrivateData()->DeleteURL(url);
196 }
197
198 void InMemoryURLIndexTest::SetUp() {
199 // We cannot access the database until the backend has been loaded.
200 ASSERT_TRUE(profile_.CreateHistoryService(true, false));
201 profile_.CreateBookmarkModel(true);
202 bookmarks::test::WaitForBookmarkModelToLoad(
203 BookmarkModelFactory::GetForProfile(&profile_));
204 profile_.BlockUntilHistoryProcessesPendingRequests();
205 profile_.BlockUntilHistoryIndexIsRefreshed();
206 history_service_ = HistoryServiceFactory::GetForProfile(
207 &profile_, ServiceAccessType::EXPLICIT_ACCESS);
208 ASSERT_TRUE(history_service_);
209 HistoryBackend* backend = history_service_->history_backend_.get();
210 history_database_ = backend->db();
211
212 // Create and populate a working copy of the URL history database.
213 base::FilePath history_proto_path;
214 PathService::Get(chrome::DIR_TEST_DATA, &history_proto_path);
215 history_proto_path = history_proto_path.Append(
216 FILE_PATH_LITERAL("History"));
217 history_proto_path = history_proto_path.Append(TestDBName());
218 EXPECT_TRUE(base::PathExists(history_proto_path));
219
220 std::ifstream proto_file(history_proto_path.value().c_str());
221 static const size_t kCommandBufferMaxSize = 2048;
222 char sql_cmd_line[kCommandBufferMaxSize];
223
224 sql::Connection& db(GetDB());
225 ASSERT_TRUE(db.is_open());
226 {
227 sql::Transaction transaction(&db);
228 transaction.Begin();
229 while (!proto_file.eof()) {
230 proto_file.getline(sql_cmd_line, kCommandBufferMaxSize);
231 if (!proto_file.eof()) {
232 // We only process lines which begin with a upper-case letter.
233 // TODO(mrossetti): Can iswupper() be used here?
234 if (sql_cmd_line[0] >= 'A' && sql_cmd_line[0] <= 'Z') {
235 std::string sql_cmd(sql_cmd_line);
236 sql::Statement sql_stmt(db.GetUniqueStatement(sql_cmd_line));
237 EXPECT_TRUE(sql_stmt.Run());
238 }
239 }
240 }
241 transaction.Commit();
242 }
243
244 // Update the last_visit_time table column in the "urls" table
245 // such that it represents a time relative to 'now'.
246 sql::Statement statement(db.GetUniqueStatement(
247 "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls;"));
248 ASSERT_TRUE(statement.is_valid());
249 base::Time time_right_now = base::Time::NowFromSystemTime();
250 base::TimeDelta day_delta = base::TimeDelta::FromDays(1);
251 {
252 sql::Transaction transaction(&db);
253 transaction.Begin();
254 while (statement.Step()) {
255 URLRow row;
256 history_database_->FillURLRow(statement, &row);
257 base::Time last_visit = time_right_now;
258 for (int64 i = row.last_visit().ToInternalValue(); i > 0; --i)
259 last_visit -= day_delta;
260 row.set_last_visit(last_visit);
261 history_database_->UpdateURLRow(row.id(), row);
262 }
263 transaction.Commit();
264 }
265
266 // Update the visit_time table column in the "visits" table
267 // such that it represents a time relative to 'now'.
268 statement.Assign(db.GetUniqueStatement(
269 "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits;"));
270 ASSERT_TRUE(statement.is_valid());
271 {
272 sql::Transaction transaction(&db);
273 transaction.Begin();
274 while (statement.Step()) {
275 VisitRow row;
276 history_database_->FillVisitRow(statement, &row);
277 base::Time last_visit = time_right_now;
278 for (int64 i = row.visit_time.ToInternalValue(); i > 0; --i)
279 last_visit -= day_delta;
280 row.visit_time = last_visit;
281 history_database_->UpdateVisitRow(row);
282 }
283 transaction.Commit();
284 }
285
286 if (InitializeInMemoryURLIndexInSetUp())
287 InitializeInMemoryURLIndex();
288 }
289
290 void InMemoryURLIndexTest::TearDown() {
291 // Ensure that the InMemoryURLIndex no longer observer HistoryService before
292 // it is destroyed in order to prevent HistoryService calling dead observer.
293 if (url_index_)
294 url_index_->ShutDown();
295 }
296
297 base::FilePath::StringType InMemoryURLIndexTest::TestDBName() const {
298 return FILE_PATH_LITERAL("url_history_provider_test.db.txt");
299 }
300
301 bool InMemoryURLIndexTest::InitializeInMemoryURLIndexInSetUp() const {
302 return true;
303 }
304
305 void InMemoryURLIndexTest::InitializeInMemoryURLIndex() {
306 DCHECK(!url_index_);
307 url_index_.reset(
308 new InMemoryURLIndex(history_service_, base::FilePath(), kTestLanguages));
309 url_index_->Init();
310 url_index_->RebuildFromHistory(history_database_);
311 }
312
313 void InMemoryURLIndexTest::CheckTerm(
314 const URLIndexPrivateData::SearchTermCacheMap& cache,
315 base::string16 term) const {
316 URLIndexPrivateData::SearchTermCacheMap::const_iterator cache_iter(
317 cache.find(term));
318 ASSERT_TRUE(cache.end() != cache_iter)
319 << "Cache does not contain '" << term << "' but should.";
320 URLIndexPrivateData::SearchTermCacheItem cache_item = cache_iter->second;
321 EXPECT_TRUE(cache_item.used_)
322 << "Cache item '" << term << "' should be marked as being in use.";
323 }
324
325 void InMemoryURLIndexTest::ExpectPrivateDataNotEmpty(
326 const URLIndexPrivateData& data) {
327 EXPECT_FALSE(data.word_list_.empty());
328 // available_words_ will be empty since we have freshly built the
329 // data set for these tests.
330 EXPECT_TRUE(data.available_words_.empty());
331 EXPECT_FALSE(data.word_map_.empty());
332 EXPECT_FALSE(data.char_word_map_.empty());
333 EXPECT_FALSE(data.word_id_history_map_.empty());
334 EXPECT_FALSE(data.history_id_word_map_.empty());
335 EXPECT_FALSE(data.history_info_map_.empty());
336 }
337
338 void InMemoryURLIndexTest::ExpectPrivateDataEmpty(
339 const URLIndexPrivateData& data) {
340 EXPECT_TRUE(data.word_list_.empty());
341 EXPECT_TRUE(data.available_words_.empty());
342 EXPECT_TRUE(data.word_map_.empty());
343 EXPECT_TRUE(data.char_word_map_.empty());
344 EXPECT_TRUE(data.word_id_history_map_.empty());
345 EXPECT_TRUE(data.history_id_word_map_.empty());
346 EXPECT_TRUE(data.history_info_map_.empty());
347 }
348
349 // Helper function which compares two maps for equivalence. The maps' values
350 // are associative containers and their contents are compared as well.
351 template<typename T>
352 void ExpectMapOfContainersIdentical(const T& expected, const T& actual) {
353 ASSERT_EQ(expected.size(), actual.size());
354 for (typename T::const_iterator expected_iter = expected.begin();
355 expected_iter != expected.end(); ++expected_iter) {
356 typename T::const_iterator actual_iter = actual.find(expected_iter->first);
357 ASSERT_TRUE(actual.end() != actual_iter);
358 typename T::mapped_type const& expected_values(expected_iter->second);
359 typename T::mapped_type const& actual_values(actual_iter->second);
360 ASSERT_EQ(expected_values.size(), actual_values.size());
361 for (typename T::mapped_type::const_iterator set_iter =
362 expected_values.begin(); set_iter != expected_values.end(); ++set_iter)
363 EXPECT_EQ(actual_values.count(*set_iter),
364 expected_values.count(*set_iter));
365 }
366 }
367
368 void InMemoryURLIndexTest::ExpectPrivateDataEqual(
369 const URLIndexPrivateData& expected,
370 const URLIndexPrivateData& actual) {
371 EXPECT_EQ(expected.word_list_.size(), actual.word_list_.size());
372 EXPECT_EQ(expected.word_map_.size(), actual.word_map_.size());
373 EXPECT_EQ(expected.char_word_map_.size(), actual.char_word_map_.size());
374 EXPECT_EQ(expected.word_id_history_map_.size(),
375 actual.word_id_history_map_.size());
376 EXPECT_EQ(expected.history_id_word_map_.size(),
377 actual.history_id_word_map_.size());
378 EXPECT_EQ(expected.history_info_map_.size(), actual.history_info_map_.size());
379 EXPECT_EQ(expected.word_starts_map_.size(), actual.word_starts_map_.size());
380 // WordList must be index-by-index equal.
381 size_t count = expected.word_list_.size();
382 for (size_t i = 0; i < count; ++i)
383 EXPECT_EQ(expected.word_list_[i], actual.word_list_[i]);
384
385 ExpectMapOfContainersIdentical(expected.char_word_map_,
386 actual.char_word_map_);
387 ExpectMapOfContainersIdentical(expected.word_id_history_map_,
388 actual.word_id_history_map_);
389 ExpectMapOfContainersIdentical(expected.history_id_word_map_,
390 actual.history_id_word_map_);
391
392 for (HistoryInfoMap::const_iterator expected_info =
393 expected.history_info_map_.begin();
394 expected_info != expected.history_info_map_.end(); ++expected_info) {
395 HistoryInfoMap::const_iterator actual_info =
396 actual.history_info_map_.find(expected_info->first);
397 // NOTE(yfriedman): ASSERT_NE can't be used due to incompatibility between
398 // gtest and STLPort in the Android build. See
399 // http://code.google.com/p/googletest/issues/detail?id=359
400 ASSERT_TRUE(actual_info != actual.history_info_map_.end());
401 const URLRow& expected_row(expected_info->second.url_row);
402 const URLRow& actual_row(actual_info->second.url_row);
403 EXPECT_EQ(expected_row.visit_count(), actual_row.visit_count());
404 EXPECT_EQ(expected_row.typed_count(), actual_row.typed_count());
405 EXPECT_EQ(expected_row.last_visit(), actual_row.last_visit());
406 EXPECT_EQ(expected_row.url(), actual_row.url());
407 const VisitInfoVector& expected_visits(expected_info->second.visits);
408 const VisitInfoVector& actual_visits(actual_info->second.visits);
409 EXPECT_EQ(expected_visits.size(), actual_visits.size());
410 for (size_t i = 0;
411 i < std::min(expected_visits.size(), actual_visits.size()); ++i) {
412 EXPECT_EQ(expected_visits[i].first, actual_visits[i].first);
413 EXPECT_EQ(expected_visits[i].second, actual_visits[i].second);
414 }
415 }
416
417 for (WordStartsMap::const_iterator expected_starts =
418 expected.word_starts_map_.begin();
419 expected_starts != expected.word_starts_map_.end();
420 ++expected_starts) {
421 WordStartsMap::const_iterator actual_starts =
422 actual.word_starts_map_.find(expected_starts->first);
423 // NOTE(yfriedman): ASSERT_NE can't be used due to incompatibility between
424 // gtest and STLPort in the Android build. See
425 // http://code.google.com/p/googletest/issues/detail?id=359
426 ASSERT_TRUE(actual_starts != actual.word_starts_map_.end());
427 const RowWordStarts& expected_word_starts(expected_starts->second);
428 const RowWordStarts& actual_word_starts(actual_starts->second);
429 EXPECT_EQ(expected_word_starts.url_word_starts_.size(),
430 actual_word_starts.url_word_starts_.size());
431 EXPECT_TRUE(std::equal(expected_word_starts.url_word_starts_.begin(),
432 expected_word_starts.url_word_starts_.end(),
433 actual_word_starts.url_word_starts_.begin()));
434 EXPECT_EQ(expected_word_starts.title_word_starts_.size(),
435 actual_word_starts.title_word_starts_.size());
436 EXPECT_TRUE(std::equal(expected_word_starts.title_word_starts_.begin(),
437 expected_word_starts.title_word_starts_.end(),
438 actual_word_starts.title_word_starts_.begin()));
439 }
440 }
441
442 //------------------------------------------------------------------------------
443
444 class LimitedInMemoryURLIndexTest : public InMemoryURLIndexTest {
445 protected:
446 base::FilePath::StringType TestDBName() const override;
447 bool InitializeInMemoryURLIndexInSetUp() const override;
448 };
449
450 base::FilePath::StringType LimitedInMemoryURLIndexTest::TestDBName() const {
451 return FILE_PATH_LITERAL("url_history_provider_test_limited.db.txt");
452 }
453
454 bool LimitedInMemoryURLIndexTest::InitializeInMemoryURLIndexInSetUp() const {
455 return false;
456 }
457
458 TEST_F(LimitedInMemoryURLIndexTest, Initialization) {
459 // Verify that the database contains the expected number of items, which
460 // is the pre-filtered count, i.e. all of the items.
461 sql::Statement statement(GetDB().GetUniqueStatement("SELECT * FROM urls;"));
462 ASSERT_TRUE(statement.is_valid());
463 uint64 row_count = 0;
464 while (statement.Step()) ++row_count;
465 EXPECT_EQ(1U, row_count);
466
467 InitializeInMemoryURLIndex();
468 URLIndexPrivateData& private_data(*GetPrivateData());
469
470 // history_info_map_ should have the same number of items as were filtered.
471 EXPECT_EQ(1U, private_data.history_info_map_.size());
472 EXPECT_EQ(35U, private_data.char_word_map_.size());
473 EXPECT_EQ(17U, private_data.word_map_.size());
474 }
475
476 #if defined(OS_WIN)
477 // Flaky on windows trybots: http://crbug.com/351500
478 #define MAYBE_Retrieval DISABLED_Retrieval
479 #else
480 #define MAYBE_Retrieval Retrieval
481 #endif
482 TEST_F(InMemoryURLIndexTest, MAYBE_Retrieval) {
483 // See if a very specific term gives a single result.
484 ScoredHistoryMatches matches = url_index_->HistoryItemsForTerms(
485 ASCIIToUTF16("DrudgeReport"), base::string16::npos, kMaxMatches,
486 builder_);
487 ASSERT_EQ(1U, matches.size());
488
489 // Verify that we got back the result we expected.
490 EXPECT_EQ(5, matches[0].url_info.id());
491 EXPECT_EQ("http://drudgereport.com/", matches[0].url_info.url().spec());
492 EXPECT_EQ(ASCIIToUTF16("DRUDGE REPORT 2010"), matches[0].url_info.title());
493 EXPECT_TRUE(matches[0].can_inline);
494
495 // Make sure a trailing space prevents inline-ability but still results
496 // in the expected result.
497 matches = url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport "),
498 base::string16::npos, kMaxMatches,
499 builder_);
500 ASSERT_EQ(1U, matches.size());
501 EXPECT_EQ(5, matches[0].url_info.id());
502 EXPECT_EQ("http://drudgereport.com/", matches[0].url_info.url().spec());
503 EXPECT_EQ(ASCIIToUTF16("DRUDGE REPORT 2010"), matches[0].url_info.title());
504 EXPECT_FALSE(matches[0].can_inline);
505
506 // Search which should result in multiple results.
507 matches = url_index_->HistoryItemsForTerms(
508 ASCIIToUTF16("drudge"), base::string16::npos, kMaxMatches, builder_);
509 ASSERT_EQ(2U, matches.size());
510 // The results should be in descending score order.
511 EXPECT_GE(matches[0].raw_score, matches[1].raw_score);
512
513 // Search which should result in nearly perfect result.
514 matches = url_index_->HistoryItemsForTerms(
515 ASCIIToUTF16("Nearly Perfect Result"), base::string16::npos, kMaxMatches,
516 builder_);
517 ASSERT_EQ(1U, matches.size());
518 // The results should have a very high score.
519 EXPECT_GT(matches[0].raw_score, 900);
520 EXPECT_EQ(32, matches[0].url_info.id());
521 EXPECT_EQ("https://nearlyperfectresult.com/",
522 matches[0].url_info.url().spec()); // Note: URL gets lowercased.
523 EXPECT_EQ(ASCIIToUTF16("Practically Perfect Search Result"),
524 matches[0].url_info.title());
525 EXPECT_FALSE(matches[0].can_inline);
526
527 // Search which should result in very poor result.
528 matches = url_index_->HistoryItemsForTerms(
529 ASCIIToUTF16("qui c"), base::string16::npos, kMaxMatches, builder_);
530 ASSERT_EQ(1U, matches.size());
531 // The results should have a poor score.
532 EXPECT_LT(matches[0].raw_score, 500);
533 EXPECT_EQ(33, matches[0].url_info.id());
534 EXPECT_EQ("http://quiteuselesssearchresultxyz.com/",
535 matches[0].url_info.url().spec()); // Note: URL gets lowercased.
536 EXPECT_EQ(ASCIIToUTF16("Practically Useless Search Result"),
537 matches[0].url_info.title());
538 EXPECT_FALSE(matches[0].can_inline);
539
540 // Search which will match at the end of an URL with encoded characters.
541 matches = url_index_->HistoryItemsForTerms(
542 ASCIIToUTF16("Mice"), base::string16::npos, kMaxMatches, builder_);
543 ASSERT_EQ(1U, matches.size());
544 EXPECT_EQ(30, matches[0].url_info.id());
545 EXPECT_FALSE(matches[0].can_inline);
546
547 // Check that URLs are not escaped an escape time.
548 matches = url_index_->HistoryItemsForTerms(ASCIIToUTF16("1% wikipedia"),
549 base::string16::npos, kMaxMatches,
550 builder_);
551 ASSERT_EQ(1U, matches.size());
552 EXPECT_EQ(35, matches[0].url_info.id());
553 EXPECT_EQ("http://en.wikipedia.org/wiki/1%25_rule_(Internet_culture)",
554 matches[0].url_info.url().spec());
555
556 // Verify that a single term can appear multiple times in the URL and as long
557 // as one starts the URL it is still inlined.
558 matches = url_index_->HistoryItemsForTerms(
559 ASCIIToUTF16("fubar"), base::string16::npos, kMaxMatches, builder_);
560 ASSERT_EQ(1U, matches.size());
561 EXPECT_EQ(34, matches[0].url_info.id());
562 EXPECT_EQ("http://fubarfubarandfubar.com/", matches[0].url_info.url().spec());
563 EXPECT_EQ(ASCIIToUTF16("Situation Normal -- FUBARED"),
564 matches[0].url_info.title());
565 EXPECT_TRUE(matches[0].can_inline);
566 }
567
568 TEST_F(InMemoryURLIndexTest, CursorPositionRetrieval) {
569 // See if a very specific term with no cursor gives an empty result.
570 ScoredHistoryMatches matches = url_index_->HistoryItemsForTerms(
571 ASCIIToUTF16("DrudReport"), base::string16::npos, kMaxMatches, builder_);
572 ASSERT_EQ(0U, matches.size());
573
574 // The same test with the cursor at the end should give an empty result.
575 matches = url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudReport"), 10u,
576 kMaxMatches, builder_);
577 ASSERT_EQ(0U, matches.size());
578
579 // If the cursor is between Drud and Report, we should find the desired
580 // result.
581 matches = url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudReport"), 4u,
582 kMaxMatches, builder_);
583 ASSERT_EQ(1U, matches.size());
584 EXPECT_EQ("http://drudgereport.com/", matches[0].url_info.url().spec());
585 EXPECT_EQ(ASCIIToUTF16("DRUDGE REPORT 2010"), matches[0].url_info.title());
586
587 // Now check multi-word inputs. No cursor should fail to find a
588 // result on this input.
589 matches = url_index_->HistoryItemsForTerms(ASCIIToUTF16("MORTGAGERATE DROPS"),
590 base::string16::npos, kMaxMatches,
591 builder_);
592 ASSERT_EQ(0U, matches.size());
593
594 // Ditto with cursor at end.
595 matches = url_index_->HistoryItemsForTerms(ASCIIToUTF16("MORTGAGERATE DROPS"),
596 18u, kMaxMatches, builder_);
597 ASSERT_EQ(0U, matches.size());
598
599 // If the cursor is between MORTAGE And RATE, we should find the
600 // desired result.
601 matches = url_index_->HistoryItemsForTerms(ASCIIToUTF16("MORTGAGERATE DROPS"),
602 8u, kMaxMatches, builder_);
603 ASSERT_EQ(1U, matches.size());
604 EXPECT_EQ("http://www.reuters.com/article/idUSN0839880620100708",
605 matches[0].url_info.url().spec());
606 EXPECT_EQ(ASCIIToUTF16(
607 "UPDATE 1-US 30-yr mortgage rate drops to new record low | Reuters"),
608 matches[0].url_info.title());
609 }
610
611 TEST_F(InMemoryURLIndexTest, URLPrefixMatching) {
612 // "drudgere" - found, can inline
613 ScoredHistoryMatches matches = url_index_->HistoryItemsForTerms(
614 ASCIIToUTF16("drudgere"), base::string16::npos, kMaxMatches, builder_);
615 ASSERT_EQ(1U, matches.size());
616 EXPECT_TRUE(matches[0].can_inline);
617
618 // "drudgere" - found, can inline
619 matches = url_index_->HistoryItemsForTerms(
620 ASCIIToUTF16("drudgere"), base::string16::npos, kMaxMatches, builder_);
621 ASSERT_EQ(1U, matches.size());
622 EXPECT_TRUE(matches[0].can_inline);
623
624 // "www.atdmt" - not found
625 matches = url_index_->HistoryItemsForTerms(
626 ASCIIToUTF16("www.atdmt"), base::string16::npos, kMaxMatches, builder_);
627 EXPECT_EQ(0U, matches.size());
628
629 // "atdmt" - found, cannot inline
630 matches = url_index_->HistoryItemsForTerms(
631 ASCIIToUTF16("atdmt"), base::string16::npos, kMaxMatches, builder_);
632 ASSERT_EQ(1U, matches.size());
633 EXPECT_FALSE(matches[0].can_inline);
634
635 // "view.atdmt" - found, can inline
636 matches = url_index_->HistoryItemsForTerms(
637 ASCIIToUTF16("view.atdmt"), base::string16::npos, kMaxMatches, builder_);
638 ASSERT_EQ(1U, matches.size());
639 EXPECT_TRUE(matches[0].can_inline);
640
641 // "view.atdmt" - found, can inline
642 matches = url_index_->HistoryItemsForTerms(
643 ASCIIToUTF16("view.atdmt"), base::string16::npos, kMaxMatches, builder_);
644 ASSERT_EQ(1U, matches.size());
645 EXPECT_TRUE(matches[0].can_inline);
646
647 // "cnn.com" - found, can inline
648 matches = url_index_->HistoryItemsForTerms(
649 ASCIIToUTF16("cnn.com"), base::string16::npos, kMaxMatches, builder_);
650 ASSERT_EQ(2U, matches.size());
651 // One match should be inline-able, the other not.
652 EXPECT_TRUE(matches[0].can_inline != matches[1].can_inline);
653
654 // "www.cnn.com" - found, can inline
655 matches = url_index_->HistoryItemsForTerms(
656 ASCIIToUTF16("www.cnn.com"), base::string16::npos, kMaxMatches, builder_);
657 ASSERT_EQ(1U, matches.size());
658 EXPECT_TRUE(matches[0].can_inline);
659
660 // "ww.cnn.com" - found because we allow mid-term matches in hostnames
661 matches = url_index_->HistoryItemsForTerms(
662 ASCIIToUTF16("ww.cnn.com"), base::string16::npos, kMaxMatches, builder_);
663 ASSERT_EQ(1U, matches.size());
664
665 // "www.cnn.com" - found, can inline
666 matches = url_index_->HistoryItemsForTerms(
667 ASCIIToUTF16("www.cnn.com"), base::string16::npos, kMaxMatches, builder_);
668 ASSERT_EQ(1U, matches.size());
669 EXPECT_TRUE(matches[0].can_inline);
670
671 // "tp://www.cnn.com" - not found because we don't allow tp as a mid-term
672 // match
673 matches = url_index_->HistoryItemsForTerms(ASCIIToUTF16("tp://www.cnn.com"),
674 base::string16::npos, kMaxMatches,
675 builder_);
676 ASSERT_EQ(0U, matches.size());
677 }
678
679 TEST_F(InMemoryURLIndexTest, ProperStringMatching) {
680 // Search for the following with the expected results:
681 // "atdmt view" - found
682 // "atdmt.view" - not found
683 // "view.atdmt" - found
684 ScoredHistoryMatches matches = url_index_->HistoryItemsForTerms(
685 ASCIIToUTF16("atdmt view"), base::string16::npos, kMaxMatches, builder_);
686 ASSERT_EQ(1U, matches.size());
687 matches = url_index_->HistoryItemsForTerms(
688 ASCIIToUTF16("atdmt.view"), base::string16::npos, kMaxMatches, builder_);
689 ASSERT_EQ(0U, matches.size());
690 matches = url_index_->HistoryItemsForTerms(
691 ASCIIToUTF16("view.atdmt"), base::string16::npos, kMaxMatches, builder_);
692 ASSERT_EQ(1U, matches.size());
693 }
694
695 TEST_F(InMemoryURLIndexTest, HugeResultSet) {
696 // Create a huge set of qualifying history items.
697 for (URLID row_id = 5000; row_id < 6000; ++row_id) {
698 URLRow new_row(GURL("http://www.brokeandaloneinmanitoba.com/"), row_id);
699 new_row.set_last_visit(base::Time::Now());
700 EXPECT_TRUE(UpdateURL(new_row));
701 }
702
703 ScoredHistoryMatches matches = url_index_->HistoryItemsForTerms(
704 ASCIIToUTF16("b"), base::string16::npos, kMaxMatches, builder_);
705 URLIndexPrivateData& private_data(*GetPrivateData());
706 ASSERT_EQ(kMaxMatches, matches.size());
707 // There are 7 matches already in the database.
708 ASSERT_EQ(1008U, private_data.pre_filter_item_count_);
709 ASSERT_EQ(500U, private_data.post_filter_item_count_);
710 ASSERT_EQ(kMaxMatches, private_data.post_scoring_item_count_);
711 }
712
713 #if defined(OS_WIN)
714 // Flaky on windows trybots: http://crbug.com/351500
715 #define MAYBE_TitleSearch DISABLED_TitleSearch
716 #else
717 #define MAYBE_TitleSearch TitleSearch
718 #endif
719 TEST_F(InMemoryURLIndexTest, MAYBE_TitleSearch) {
720 // Signal if someone has changed the test DB.
721 EXPECT_EQ(29U, GetPrivateData()->history_info_map_.size());
722
723 // Ensure title is being searched.
724 ScoredHistoryMatches matches = url_index_->HistoryItemsForTerms(
725 ASCIIToUTF16("MORTGAGE RATE DROPS"), base::string16::npos, kMaxMatches,
726 builder_);
727 ASSERT_EQ(1U, matches.size());
728
729 // Verify that we got back the result we expected.
730 EXPECT_EQ(1, matches[0].url_info.id());
731 EXPECT_EQ("http://www.reuters.com/article/idUSN0839880620100708",
732 matches[0].url_info.url().spec());
733 EXPECT_EQ(ASCIIToUTF16(
734 "UPDATE 1-US 30-yr mortgage rate drops to new record low | Reuters"),
735 matches[0].url_info.title());
736 }
737
738 TEST_F(InMemoryURLIndexTest, TitleChange) {
739 // Verify current title terms retrieves desired item.
740 base::string16 original_terms =
741 ASCIIToUTF16("lebronomics could high taxes influence");
742 ScoredHistoryMatches matches = url_index_->HistoryItemsForTerms(
743 original_terms, base::string16::npos, kMaxMatches, builder_);
744 ASSERT_EQ(1U, matches.size());
745
746 // Verify that we got back the result we expected.
747 const URLID expected_id = 3;
748 EXPECT_EQ(expected_id, matches[0].url_info.id());
749 EXPECT_EQ("http://www.businessandmedia.org/articles/2010/20100708120415.aspx",
750 matches[0].url_info.url().spec());
751 EXPECT_EQ(ASCIIToUTF16(
752 "LeBronomics: Could High Taxes Influence James' Team Decision?"),
753 matches[0].url_info.title());
754 URLRow old_row(matches[0].url_info);
755
756 // Verify new title terms retrieves nothing.
757 base::string16 new_terms = ASCIIToUTF16("does eat oats little lambs ivy");
758 matches = url_index_->HistoryItemsForTerms(new_terms, base::string16::npos,
759 kMaxMatches, builder_);
760 ASSERT_EQ(0U, matches.size());
761
762 // Update the row.
763 old_row.set_title(ASCIIToUTF16("Does eat oats and little lambs eat ivy"));
764 EXPECT_TRUE(UpdateURL(old_row));
765
766 // Verify we get the row using the new terms but not the original terms.
767 matches = url_index_->HistoryItemsForTerms(new_terms, base::string16::npos,
768 kMaxMatches, builder_);
769 ASSERT_EQ(1U, matches.size());
770 EXPECT_EQ(expected_id, matches[0].url_info.id());
771 matches = url_index_->HistoryItemsForTerms(
772 original_terms, base::string16::npos, kMaxMatches, builder_);
773 ASSERT_EQ(0U, matches.size());
774 }
775
776 TEST_F(InMemoryURLIndexTest, NonUniqueTermCharacterSets) {
777 // The presence of duplicate characters should succeed. Exercise by cycling
778 // through a string with several duplicate characters.
779 ScoredHistoryMatches matches = url_index_->HistoryItemsForTerms(
780 ASCIIToUTF16("ABRA"), base::string16::npos, kMaxMatches, builder_);
781 ASSERT_EQ(1U, matches.size());
782 EXPECT_EQ(28, matches[0].url_info.id());
783 EXPECT_EQ("http://www.ddj.com/windows/184416623",
784 matches[0].url_info.url().spec());
785
786 matches = url_index_->HistoryItemsForTerms(
787 ASCIIToUTF16("ABRACAD"), base::string16::npos, kMaxMatches, builder_);
788 ASSERT_EQ(1U, matches.size());
789 EXPECT_EQ(28, matches[0].url_info.id());
790
791 matches = url_index_->HistoryItemsForTerms(
792 ASCIIToUTF16("ABRACADABRA"), base::string16::npos, kMaxMatches, builder_);
793 ASSERT_EQ(1U, matches.size());
794 EXPECT_EQ(28, matches[0].url_info.id());
795
796 matches = url_index_->HistoryItemsForTerms(
797 ASCIIToUTF16("ABRACADABR"), base::string16::npos, kMaxMatches, builder_);
798 ASSERT_EQ(1U, matches.size());
799 EXPECT_EQ(28, matches[0].url_info.id());
800
801 matches = url_index_->HistoryItemsForTerms(
802 ASCIIToUTF16("ABRACA"), base::string16::npos, kMaxMatches, builder_);
803 ASSERT_EQ(1U, matches.size());
804 EXPECT_EQ(28, matches[0].url_info.id());
805 }
806
807 TEST_F(InMemoryURLIndexTest, TypedCharacterCaching) {
808 // Verify that match results for previously typed characters are retained
809 // (in the term_char_word_set_cache_) and reused, if possible, in future
810 // autocompletes.
811
812 URLIndexPrivateData::SearchTermCacheMap& cache(
813 GetPrivateData()->search_term_cache_);
814
815 // The cache should be empty at this point.
816 EXPECT_EQ(0U, cache.size());
817
818 // Now simulate typing search terms into the omnibox and check the state of
819 // the cache as each item is 'typed'.
820
821 // Simulate typing "r" giving "r" in the simulated omnibox. The results for
822 // 'r' will be not cached because it is only 1 character long.
823 url_index_->HistoryItemsForTerms(ASCIIToUTF16("r"), base::string16::npos,
824 kMaxMatches, builder_);
825 EXPECT_EQ(0U, cache.size());
826
827 // Simulate typing "re" giving "r re" in the simulated omnibox.
828 // 're' should be cached at this point but not 'r' as it is a single
829 // character.
830 url_index_->HistoryItemsForTerms(ASCIIToUTF16("r re"), base::string16::npos,
831 kMaxMatches, builder_);
832 ASSERT_EQ(1U, cache.size());
833 CheckTerm(cache, ASCIIToUTF16("re"));
834
835 // Simulate typing "reco" giving "r re reco" in the simulated omnibox.
836 // 're' and 'reco' should be cached at this point but not 'r' as it is a
837 // single character.
838 url_index_->HistoryItemsForTerms(ASCIIToUTF16("r re reco"),
839 base::string16::npos, kMaxMatches, builder_);
840 ASSERT_EQ(2U, cache.size());
841 CheckTerm(cache, ASCIIToUTF16("re"));
842 CheckTerm(cache, ASCIIToUTF16("reco"));
843
844 // Simulate typing "mort".
845 // Since we now have only one search term, the cached results for 're' and
846 // 'reco' should be purged, giving us only 1 item in the cache (for 'mort').
847 url_index_->HistoryItemsForTerms(ASCIIToUTF16("mort"), base::string16::npos,
848 kMaxMatches, builder_);
849 ASSERT_EQ(1U, cache.size());
850 CheckTerm(cache, ASCIIToUTF16("mort"));
851
852 // Simulate typing "reco" giving "mort reco" in the simulated omnibox.
853 url_index_->HistoryItemsForTerms(ASCIIToUTF16("mort reco"),
854 base::string16::npos, kMaxMatches, builder_);
855 ASSERT_EQ(2U, cache.size());
856 CheckTerm(cache, ASCIIToUTF16("mort"));
857 CheckTerm(cache, ASCIIToUTF16("reco"));
858
859 // Simulate a <DELETE> by removing the 'reco' and adding back the 'rec'.
860 url_index_->HistoryItemsForTerms(ASCIIToUTF16("mort rec"),
861 base::string16::npos, kMaxMatches, builder_);
862 ASSERT_EQ(2U, cache.size());
863 CheckTerm(cache, ASCIIToUTF16("mort"));
864 CheckTerm(cache, ASCIIToUTF16("rec"));
865 }
866
867 TEST_F(InMemoryURLIndexTest, AddNewRows) {
868 // Verify that the row we're going to add does not already exist.
869 URLID new_row_id = 87654321;
870 // Newly created URLRows get a last_visit time of 'right now' so it should
871 // qualify as a quick result candidate.
872 EXPECT_TRUE(url_index_->HistoryItemsForTerms(ASCIIToUTF16("brokeandalone"),
873 base::string16::npos,
874 kMaxMatches, builder_).empty());
875
876 // Add a new row.
877 URLRow new_row(GURL("http://www.brokeandaloneinmanitoba.com/"), new_row_id++);
878 new_row.set_last_visit(base::Time::Now());
879 EXPECT_TRUE(UpdateURL(new_row));
880
881 // Verify that we can retrieve it.
882 EXPECT_EQ(1U, url_index_->HistoryItemsForTerms(ASCIIToUTF16("brokeandalone"),
883 base::string16::npos,
884 kMaxMatches, builder_).size());
885
886 // Add it again just to be sure that is harmless and that it does not update
887 // the index.
888 EXPECT_FALSE(UpdateURL(new_row));
889 EXPECT_EQ(1U, url_index_->HistoryItemsForTerms(ASCIIToUTF16("brokeandalone"),
890 base::string16::npos,
891 kMaxMatches, builder_).size());
892
893 // Make up an URL that does not qualify and try to add it.
894 URLRow unqualified_row(GURL("http://www.brokeandaloneinmanitoba.com/"),
895 new_row_id++);
896 EXPECT_FALSE(UpdateURL(new_row));
897 }
898
899 TEST_F(InMemoryURLIndexTest, DeleteRows) {
900 ScoredHistoryMatches matches = url_index_->HistoryItemsForTerms(
901 ASCIIToUTF16("DrudgeReport"), base::string16::npos, kMaxMatches,
902 builder_);
903 ASSERT_EQ(1U, matches.size());
904
905 // Delete the URL then search again.
906 EXPECT_TRUE(DeleteURL(matches[0].url_info.url()));
907 EXPECT_TRUE(url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport"),
908 base::string16::npos,
909 kMaxMatches, builder_).empty());
910
911 // Make up an URL that does not exist in the database and delete it.
912 GURL url("http://www.hokeypokey.com/putyourrightfootin.html");
913 EXPECT_FALSE(DeleteURL(url));
914 }
915
916 TEST_F(InMemoryURLIndexTest, ExpireRow) {
917 ScoredHistoryMatches matches = url_index_->HistoryItemsForTerms(
918 ASCIIToUTF16("DrudgeReport"), base::string16::npos, kMaxMatches,
919 builder_);
920 ASSERT_EQ(1U, matches.size());
921
922 // Determine the row id for the result, remember that id, broadcast a
923 // delete notification, then ensure that the row has been deleted.
924 URLRows deleted_rows;
925 deleted_rows.push_back(matches[0].url_info);
926 url_index_->OnURLsDeleted(nullptr, false, false, deleted_rows,
927 std::set<GURL>());
928 EXPECT_TRUE(url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport"),
929 base::string16::npos,
930 kMaxMatches, builder_).empty());
931 }
932
933 TEST_F(InMemoryURLIndexTest, WhitelistedURLs) {
934 struct TestData {
935 const std::string url_spec;
936 const bool expected_is_whitelisted;
937 } data[] = {
938 // URLs with whitelisted schemes.
939 { "about:histograms", true },
940 { "chrome://settings", true },
941 { "file://localhost/Users/joeschmoe/sekrets", true },
942 { "ftp://public.mycompany.com/myfile.txt", true },
943 { "http://www.google.com/translate", true },
944 { "https://www.gmail.com/", true },
945 { "mailto:support@google.com", true },
946 // URLs with unacceptable schemes.
947 { "aaa://www.dummyhost.com;frammy", false },
948 { "aaas://www.dummyhost.com;frammy", false },
949 { "acap://suzie@somebody.com", false },
950 { "cap://cal.example.com/Company/Holidays", false },
951 { "cid:foo4*foo1@bar.net", false },
952 { "crid://example.com/foobar", false },
953 { "data:image/png;base64,iVBORw0KGgoAAAANSUhE=", false },
954 { "dict://dict.org/d:shortcake:", false },
955 { "dns://192.168.1.1/ftp.example.org?type=A", false },
956 { "fax:+358.555.1234567", false },
957 { "geo:13.4125,103.8667", false },
958 { "go:Mercedes%20Benz", false },
959 { "gopher://farnsworth.ca:666/gopher", false },
960 { "h323:farmer-john;sixpence", false },
961 { "iax:johnQ@example.com/12022561414", false },
962 { "icap://icap.net/service?mode=translate&lang=french", false },
963 { "im:fred@example.com", false },
964 { "imap://michael@minbari.org/users.*", false },
965 { "info:ddc/22/eng//004.678", false },
966 { "ipp://example.com/printer/fox", false },
967 { "iris:dreg1//example.com/local/myhosts", false },
968 { "iris.beep:dreg1//example.com/local/myhosts", false },
969 { "iris.lws:dreg1//example.com/local/myhosts", false },
970 { "iris.xpc:dreg1//example.com/local/myhosts", false },
971 { "iris.xpcs:dreg1//example.com/local/myhosts", false },
972 { "ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US", false },
973 { "mid:foo4%25foo1@bar.net", false },
974 { "modem:+3585551234567;type=v32b?7e1;type=v110", false },
975 { "msrp://atlanta.example.com:7654/jshA7weztas;tcp", false },
976 { "msrps://atlanta.example.com:7654/jshA7weztas;tcp", false },
977 { "news:colorectal.info.banned", false },
978 { "nfs://server/d/e/f", false },
979 { "nntp://www.example.com:6543/info.comp.lies/1234", false },
980 { "pop://rg;AUTH=+APOP@mail.mycompany.com:8110", false },
981 { "pres:fred@example.com", false },
982 { "prospero://host.dom//pros/name", false },
983 { "rsync://syler@lost.com/Source", false },
984 { "rtsp://media.example.com:554/twister/audiotrack", false },
985 { "service:acap://some.where.net;authentication=KERBEROSV4", false },
986 { "shttp://www.terces.com/secret", false },
987 { "sieve://example.com//script", false },
988 { "sip:+1-212-555-1212:1234@gateway.com;user=phone", false },
989 { "sips:+1-212-555-1212:1234@gateway.com;user=phone", false },
990 { "sms:+15105551212?body=hello%20there", false },
991 { "snmp://tester5@example.com:8161/bridge1;800002b804616263", false },
992 { "soap.beep://stockquoteserver.example.com/StockQuote", false },
993 { "soap.beeps://stockquoteserver.example.com/StockQuote", false },
994 { "tag:blogger.com,1999:blog-555", false },
995 { "tel:+358-555-1234567;postd=pp22", false },
996 { "telnet://mayor_margie:one2rule4All@www.mycity.com:6789/", false },
997 { "tftp://example.com/mystartupfile", false },
998 { "tip://123.123.123.123/?urn:xopen:xid", false },
999 { "tv:nbc.com", false },
1000 { "urn:foo:A123,456", false },
1001 { "vemmi://zeus.mctel.fr/demo", false },
1002 { "wais://www.mydomain.net:8765/mydatabase", false },
1003 { "xmpp:node@example.com", false },
1004 { "xmpp://guest@example.com", false },
1005 };
1006
1007 const std::set<std::string>& whitelist(scheme_whitelist());
1008 for (size_t i = 0; i < arraysize(data); ++i) {
1009 GURL url(data[i].url_spec);
1010 EXPECT_EQ(data[i].expected_is_whitelisted,
1011 URLIndexPrivateData::URLSchemeIsWhitelisted(url, whitelist));
1012 }
1013 }
1014
1015 TEST_F(InMemoryURLIndexTest, ReadVisitsFromHistory) {
1016 const HistoryInfoMap& history_info_map = GetPrivateData()->history_info_map_;
1017
1018 // Check (for URL with id 1) that the number of visits and their
1019 // transition types are what we expect. We don't bother checking
1020 // the timestamps because it's too much trouble. (The timestamps go
1021 // through a transformation in InMemoryURLIndexTest::SetUp(). We
1022 // assume that if the count and transitions show up with the right
1023 // information, we're getting the right information from the history
1024 // database file.)
1025 HistoryInfoMap::const_iterator entry = history_info_map.find(1);
1026 ASSERT_TRUE(entry != history_info_map.end());
1027 {
1028 const VisitInfoVector& visits = entry->second.visits;
1029 EXPECT_EQ(3u, visits.size());
1030 EXPECT_EQ(0u, visits[0].second);
1031 EXPECT_EQ(1u, visits[1].second);
1032 EXPECT_EQ(0u, visits[2].second);
1033 }
1034
1035 // Ditto but for URL with id 35.
1036 entry = history_info_map.find(35);
1037 ASSERT_TRUE(entry != history_info_map.end());
1038 {
1039 const VisitInfoVector& visits = entry->second.visits;
1040 EXPECT_EQ(2u, visits.size());
1041 EXPECT_EQ(1u, visits[0].second);
1042 EXPECT_EQ(1u, visits[1].second);
1043 }
1044
1045 // The URL with id 32 has many visits listed in the database, but we
1046 // should only read the most recent 10 (which are all transition type 0).
1047 entry = history_info_map.find(32);
1048 ASSERT_TRUE(entry != history_info_map.end());
1049 {
1050 const VisitInfoVector& visits = entry->second.visits;
1051 EXPECT_EQ(10u, visits.size());
1052 for (size_t i = 0; i < visits.size(); ++i)
1053 EXPECT_EQ(0u, visits[i].second);
1054 }
1055 }
1056
1057 TEST_F(InMemoryURLIndexTest, CacheSaveRestore) {
1058 base::ScopedTempDir temp_directory;
1059 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
1060 set_history_dir(temp_directory.path());
1061
1062 URLIndexPrivateData& private_data(*GetPrivateData());
1063
1064 // Ensure that there is really something there to be saved.
1065 EXPECT_FALSE(private_data.word_list_.empty());
1066 // available_words_ will already be empty since we have freshly built the
1067 // data set for this test.
1068 EXPECT_TRUE(private_data.available_words_.empty());
1069 EXPECT_FALSE(private_data.word_map_.empty());
1070 EXPECT_FALSE(private_data.char_word_map_.empty());
1071 EXPECT_FALSE(private_data.word_id_history_map_.empty());
1072 EXPECT_FALSE(private_data.history_id_word_map_.empty());
1073 EXPECT_FALSE(private_data.history_info_map_.empty());
1074 EXPECT_FALSE(private_data.word_starts_map_.empty());
1075
1076 // Make sure the data we have was built from history. (Version 0
1077 // means rebuilt from history.)
1078 EXPECT_EQ(0, private_data.restored_cache_version_);
1079
1080 // Capture the current private data for later comparison to restored data.
1081 scoped_refptr<URLIndexPrivateData> old_data(private_data.Duplicate());
1082 const base::Time rebuild_time = private_data.last_time_rebuilt_from_history_;
1083
1084 {
1085 // Save then restore our private data.
1086 base::RunLoop run_loop;
1087 CacheFileSaverObserver save_observer(run_loop.QuitClosure());
1088 url_index_->set_save_cache_observer(&save_observer);
1089 PostSaveToCacheFileTask();
1090 run_loop.Run();
1091 EXPECT_TRUE(save_observer.succeeded());
1092 }
1093
1094 // Clear and then prove it's clear before restoring.
1095 ClearPrivateData();
1096 EXPECT_TRUE(private_data.word_list_.empty());
1097 EXPECT_TRUE(private_data.available_words_.empty());
1098 EXPECT_TRUE(private_data.word_map_.empty());
1099 EXPECT_TRUE(private_data.char_word_map_.empty());
1100 EXPECT_TRUE(private_data.word_id_history_map_.empty());
1101 EXPECT_TRUE(private_data.history_id_word_map_.empty());
1102 EXPECT_TRUE(private_data.history_info_map_.empty());
1103 EXPECT_TRUE(private_data.word_starts_map_.empty());
1104
1105 {
1106 base::RunLoop run_loop;
1107 HistoryIndexRestoreObserver restore_observer(run_loop.QuitClosure());
1108 url_index_->set_restore_cache_observer(&restore_observer);
1109 PostRestoreFromCacheFileTask();
1110 run_loop.Run();
1111 EXPECT_TRUE(restore_observer.succeeded());
1112 }
1113
1114 URLIndexPrivateData& new_data(*GetPrivateData());
1115
1116 // Make sure the data we have was reloaded from cache. (Version 0
1117 // means rebuilt from history; anything else means restored from
1118 // a cache version.) Also, the rebuild time should not have changed.
1119 EXPECT_GT(new_data.restored_cache_version_, 0);
1120 EXPECT_EQ(rebuild_time, new_data.last_time_rebuilt_from_history_);
1121
1122 // Compare the captured and restored for equality.
1123 ExpectPrivateDataEqual(*old_data.get(), new_data);
1124 }
1125
1126 #if defined(OS_WIN)
1127 // http://crbug.com/351500
1128 #define MAYBE_RebuildFromHistoryIfCacheOld DISABLED_RebuildFromHistoryIfCacheOld
1129 #else
1130 #define MAYBE_RebuildFromHistoryIfCacheOld RebuildFromHistoryIfCacheOld
1131 #endif
1132 TEST_F(InMemoryURLIndexTest, MAYBE_RebuildFromHistoryIfCacheOld) {
1133 base::ScopedTempDir temp_directory;
1134 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
1135 set_history_dir(temp_directory.path());
1136
1137 URLIndexPrivateData& private_data(*GetPrivateData());
1138
1139 // Ensure that there is really something there to be saved.
1140 EXPECT_FALSE(private_data.word_list_.empty());
1141 // available_words_ will already be empty since we have freshly built the
1142 // data set for this test.
1143 EXPECT_TRUE(private_data.available_words_.empty());
1144 EXPECT_FALSE(private_data.word_map_.empty());
1145 EXPECT_FALSE(private_data.char_word_map_.empty());
1146 EXPECT_FALSE(private_data.word_id_history_map_.empty());
1147 EXPECT_FALSE(private_data.history_id_word_map_.empty());
1148 EXPECT_FALSE(private_data.history_info_map_.empty());
1149 EXPECT_FALSE(private_data.word_starts_map_.empty());
1150
1151 // Make sure the data we have was built from history. (Version 0
1152 // means rebuilt from history.)
1153 EXPECT_EQ(0, private_data.restored_cache_version_);
1154
1155 // Overwrite the build time so that we'll think the data is too old
1156 // and rebuild the cache from history.
1157 const base::Time fake_rebuild_time =
1158 private_data.last_time_rebuilt_from_history_ -
1159 base::TimeDelta::FromDays(30);
1160 private_data.last_time_rebuilt_from_history_ = fake_rebuild_time;
1161
1162 // Capture the current private data for later comparison to restored data.
1163 scoped_refptr<URLIndexPrivateData> old_data(private_data.Duplicate());
1164
1165 {
1166 // Save then restore our private data.
1167 base::RunLoop run_loop;
1168 CacheFileSaverObserver save_observer(run_loop.QuitClosure());
1169 url_index_->set_save_cache_observer(&save_observer);
1170 PostSaveToCacheFileTask();
1171 run_loop.Run();
1172 EXPECT_TRUE(save_observer.succeeded());
1173 }
1174
1175 // Clear and then prove it's clear before restoring.
1176 ClearPrivateData();
1177 EXPECT_TRUE(private_data.word_list_.empty());
1178 EXPECT_TRUE(private_data.available_words_.empty());
1179 EXPECT_TRUE(private_data.word_map_.empty());
1180 EXPECT_TRUE(private_data.char_word_map_.empty());
1181 EXPECT_TRUE(private_data.word_id_history_map_.empty());
1182 EXPECT_TRUE(private_data.history_id_word_map_.empty());
1183 EXPECT_TRUE(private_data.history_info_map_.empty());
1184 EXPECT_TRUE(private_data.word_starts_map_.empty());
1185
1186 {
1187 base::RunLoop run_loop;
1188 HistoryIndexRestoreObserver restore_observer(run_loop.QuitClosure());
1189 url_index_->set_restore_cache_observer(&restore_observer);
1190 PostRestoreFromCacheFileTask();
1191 run_loop.Run();
1192 EXPECT_TRUE(restore_observer.succeeded());
1193 }
1194
1195 URLIndexPrivateData& new_data(*GetPrivateData());
1196
1197 // Make sure the data we have was rebuilt from history. (Version 0
1198 // means rebuilt from history; anything else means restored from
1199 // a cache version.)
1200 EXPECT_EQ(0, new_data.restored_cache_version_);
1201 EXPECT_NE(fake_rebuild_time, new_data.last_time_rebuilt_from_history_);
1202
1203 // Compare the captured and restored for equality.
1204 ExpectPrivateDataEqual(*old_data.get(), new_data);
1205 }
1206
1207 class InMemoryURLIndexCacheTest : public testing::Test {
1208 public:
1209 InMemoryURLIndexCacheTest() {}
1210
1211 protected:
1212 void SetUp() override;
1213 void TearDown() override;
1214
1215 // Pass-through functions to simplify our friendship with InMemoryURLIndex.
1216 void set_history_dir(const base::FilePath& dir_path);
1217 bool GetCacheFilePath(base::FilePath* file_path) const;
1218
1219 base::ScopedTempDir temp_dir_;
1220 scoped_ptr<InMemoryURLIndex> url_index_;
1221 };
1222
1223 void InMemoryURLIndexCacheTest::SetUp() {
1224 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
1225 base::FilePath path(temp_dir_.path());
1226 url_index_.reset(new InMemoryURLIndex(nullptr, path, kTestLanguages));
1227 }
1228
1229 void InMemoryURLIndexCacheTest::TearDown() {
1230 if (url_index_)
1231 url_index_->ShutDown();
1232 }
1233
1234 void InMemoryURLIndexCacheTest::set_history_dir(
1235 const base::FilePath& dir_path) {
1236 return url_index_->set_history_dir(dir_path);
1237 }
1238
1239 bool InMemoryURLIndexCacheTest::GetCacheFilePath(
1240 base::FilePath* file_path) const {
1241 DCHECK(file_path);
1242 return url_index_->GetCacheFilePath(file_path);
1243 }
1244
1245 TEST_F(InMemoryURLIndexCacheTest, CacheFilePath) {
1246 base::FilePath expectedPath =
1247 temp_dir_.path().Append(FILE_PATH_LITERAL("History Provider Cache"));
1248 std::vector<base::FilePath::StringType> expected_parts;
1249 expectedPath.GetComponents(&expected_parts);
1250 base::FilePath full_file_path;
1251 ASSERT_TRUE(GetCacheFilePath(&full_file_path));
1252 std::vector<base::FilePath::StringType> actual_parts;
1253 full_file_path.GetComponents(&actual_parts);
1254 ASSERT_EQ(expected_parts.size(), actual_parts.size());
1255 size_t count = expected_parts.size();
1256 for (size_t i = 0; i < count; ++i)
1257 EXPECT_EQ(expected_parts[i], actual_parts[i]);
1258 // Must clear the history_dir_ to satisfy the dtor's DCHECK.
1259 set_history_dir(base::FilePath());
1260 }
1261
1262 } // namespace history
OLDNEW
« no previous file with comments | « chrome/browser/history/in_memory_url_index.cc ('k') | chrome/browser/history/url_index_private_data.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698