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