Index: chrome/browser/history/in_memory_url_index_unittest.cc |
=================================================================== |
--- chrome/browser/history/in_memory_url_index_unittest.cc (revision 151481) |
+++ chrome/browser/history/in_memory_url_index_unittest.cc (working copy) |
@@ -2,313 +2,93 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include <algorithm> |
-#include <fstream> |
- |
-#include "base/file_path.h" |
-#include "base/file_util.h" |
-#include "base/message_loop.h" |
-#include "base/path_service.h" |
-#include "base/scoped_temp_dir.h" |
-#include "base/string16.h" |
+#include "base/memory/ref_counted.h" |
#include "base/string_util.h" |
+#include "base/stringprintf.h" |
+#include "base/threading/sequenced_worker_pool.h" |
#include "base/utf_string_conversions.h" |
#include "chrome/browser/autocomplete/autocomplete_provider.h" |
-#include "chrome/browser/history/history.h" |
-#include "chrome/browser/history/history_backend.h" |
-#include "chrome/browser/history/history_database.h" |
+#include "chrome/browser/bookmarks/bookmark_model.h" |
+#include "chrome/browser/bookmarks/bookmark_model_factory.h" |
#include "chrome/browser/history/history_notifications.h" |
-#include "chrome/browser/history/history_service_factory.h" |
-#include "chrome/browser/history/in_memory_url_index.h" |
+#include "chrome/browser/history/in_memory_url_cache_database.h" |
+#include "chrome/browser/history/in_memory_url_index_base_unittest.h" |
#include "chrome/browser/history/in_memory_url_index_types.h" |
#include "chrome/browser/history/url_index_private_data.h" |
+#include "chrome/common/chrome_constants.h" |
#include "chrome/common/chrome_notification_types.h" |
-#include "chrome/common/chrome_paths.h" |
-#include "chrome/test/base/testing_profile.h" |
-#include "content/public/browser/notification_details.h" |
+#include "chrome/test/base/ui_test_utils.h" |
+#include "content/public/browser/notification_service.h" |
#include "content/public/browser/notification_source.h" |
-#include "content/public/test/test_browser_thread.h" |
-#include "sql/transaction.h" |
-#include "testing/gtest/include/gtest/gtest.h" |
+#include "sql/connection.h" |
+#include "third_party/sqlite/sqlite3.h" |
-using content::BrowserThread; |
+using history::String16SetFromString16; |
-// The test version of the history url database table ('url') is contained in |
-// a database file created from a text file('url_history_provider_test.db.txt'). |
-// The only difference between this table and a live 'urls' table from a |
-// profile is that the last_visit_time column in the test table contains a |
-// number specifying the number of days relative to 'today' to which the |
-// absolute time should be set during the test setup stage. |
-// |
-// The format of the test database text file is of a SQLite .dump file. |
-// Note that only lines whose first character is an upper-case letter are |
-// processed when creating the test database. |
+//------------------------------------------------------------------------------ |
-namespace history { |
- |
-// ----------------------------------------------------------------------------- |
- |
-// Observer class so the unit tests can wait while the cache is being saved. |
-class CacheFileSaverObserver : public InMemoryURLIndex::SaveCacheObserver { |
+// This class overrides the standard TestingProfile's disabling of the |
+// InMemoryURLIndexCacheDatabase. |
+class CacheTestingProfile : public TestingProfile { |
public: |
- explicit CacheFileSaverObserver(MessageLoop* loop); |
- virtual void OnCacheSaveFinished(bool succeeded) OVERRIDE; |
- |
- MessageLoop* loop_; |
- bool succeeded_; |
- DISALLOW_COPY_AND_ASSIGN(CacheFileSaverObserver); |
+ // Creates a testing profile but enables the cache database. |
+ bool InitHistoryService(HistoryService* history_service, |
+ bool no_db) OVERRIDE; |
}; |
-CacheFileSaverObserver::CacheFileSaverObserver(MessageLoop* loop) |
- : loop_(loop), |
- succeeded_(false) { |
- DCHECK(loop); |
+bool CacheTestingProfile::InitHistoryService(HistoryService* history_service, |
+ bool no_db) { |
+ DCHECK(history_service); |
+ return history_service->Init(GetPath(), |
+ reinterpret_cast<BookmarkService*>( |
+ BookmarkModelFactory::GetForProfile(this)), |
+ no_db, false); |
} |
-void CacheFileSaverObserver::OnCacheSaveFinished(bool succeeded) { |
- succeeded_ = succeeded; |
- loop_->Quit(); |
-} |
+namespace history { |
-// Observer class so the unit tests can wait while the cache is being restored. |
-class CacheFileReaderObserver : public InMemoryURLIndex::RestoreCacheObserver { |
- public: |
- explicit CacheFileReaderObserver(MessageLoop* loop); |
- virtual void OnCacheRestoreFinished(bool succeeded) OVERRIDE; |
- |
- MessageLoop* loop_; |
- bool succeeded_; |
- DISALLOW_COPY_AND_ASSIGN(CacheFileReaderObserver); |
-}; |
- |
-CacheFileReaderObserver::CacheFileReaderObserver(MessageLoop* loop) |
- : loop_(loop), |
- succeeded_(false) { |
- DCHECK(loop); |
-} |
- |
-void CacheFileReaderObserver::OnCacheRestoreFinished(bool succeeded) { |
- succeeded_ = succeeded; |
- loop_->Quit(); |
-} |
- |
// ----------------------------------------------------------------------------- |
-class InMemoryURLIndexTest : public testing::Test { |
- public: |
- InMemoryURLIndexTest(); |
+// Creates a URLRow with basic data for |url|, |title|, |visit_count|, |
+// |typed_count| and |id|. |last_visit_ago| gives the number of days from now |
+// to which to set the URL's last_visit. |
+URLRow MakeURLRowWithID(const char* url, |
+ const char* title, |
+ int visit_count, |
+ int last_visit_ago, |
+ int typed_count, |
+ URLID id); |
+class InMemoryURLIndexTest : public InMemoryURLIndexBaseTest { |
protected: |
- // Test setup. |
- virtual void SetUp(); |
+ void SetUp() OVERRIDE; |
- // Allows the database containing the test data to be customized by |
- // subclasses. |
- virtual FilePath::StringType TestDBName() const; |
+ // Provides custom test data file name. |
+ virtual FilePath::StringType TestDBName() const OVERRIDE; |
-#if 0 |
- // Convenience function to create a URLRow with basic data for |url|, |title|, |
- // |visit_count|, and |typed_count|. |last_visit_ago| gives the number of |
- // days from now to set the URL's last_visit. |
- URLRow MakeURLRow(const char* url, |
- const char* title, |
- int visit_count, |
- int last_visit_ago, |
- int typed_count); |
- |
- // Convenience functions for easily creating vectors of search terms. |
- String16Vector Make1Term(const char* term) const; |
- String16Vector Make2Terms(const char* term_1, const char* term_2) const; |
- |
- // Convenience function for GetTopicalityScore() that builds the |
- // term match and word break information automatically that are needed |
- // to call GetTopicalityScore(). It only works for scoring a single term, |
- // not multiple terms. |
- float GetTopicalityScoreOfTermAgainstURLAndTitle(const string16& term, |
- const string16& url, |
- const string16& title); |
-#endif |
- |
// Validates that the given |term| is contained in |cache| and that it is |
// marked as in-use. |
void CheckTerm(const URLIndexPrivateData::SearchTermCacheMap& cache, |
string16 term) const; |
- |
- // Pass-through function to simplify our friendship with HistoryService. |
- sql::Connection& GetDB(); |
- |
- // Pass-through functions to simplify our friendship with InMemoryURLIndex. |
- URLIndexPrivateData* GetPrivateData() const; |
- void ClearPrivateData(); |
- void set_history_dir(const FilePath& dir_path); |
- bool GetCacheFilePath(FilePath* file_path) const; |
- void PostRestoreFromCacheFileTask(); |
- void PostSaveToCacheFileTask(); |
- void Observe(int notification_type, |
- const content::NotificationSource& source, |
- const content::NotificationDetails& details); |
- const std::set<std::string>& scheme_whitelist(); |
- |
- |
- // Pass-through functions to simplify our friendship with URLIndexPrivateData. |
- bool UpdateURL(const URLRow& row); |
- bool DeleteURL(const GURL& url); |
- |
- // Data verification helper functions. |
- void ExpectPrivateDataNotEmpty(const URLIndexPrivateData& data); |
- void ExpectPrivateDataEmpty(const URLIndexPrivateData& data); |
- void ExpectPrivateDataEqual(const URLIndexPrivateData& expected, |
- const URLIndexPrivateData& actual); |
- |
- MessageLoopForUI message_loop_; |
- content::TestBrowserThread ui_thread_; |
- content::TestBrowserThread file_thread_; |
- TestingProfile profile_; |
- |
- scoped_ptr<InMemoryURLIndex> url_index_; |
- HistoryDatabase* history_database_; |
}; |
-InMemoryURLIndexTest::InMemoryURLIndexTest() |
- : ui_thread_(content::BrowserThread::UI, &message_loop_), |
- file_thread_(content::BrowserThread::FILE, &message_loop_) { |
-} |
- |
-sql::Connection& InMemoryURLIndexTest::GetDB() { |
- return history_database_->GetDB(); |
-} |
- |
-URLIndexPrivateData* InMemoryURLIndexTest::GetPrivateData() const { |
- DCHECK(url_index_->private_data()); |
- return url_index_->private_data(); |
-} |
- |
-void InMemoryURLIndexTest::ClearPrivateData() { |
- return url_index_->ClearPrivateData(); |
-} |
- |
-void InMemoryURLIndexTest::set_history_dir(const FilePath& dir_path) { |
- return url_index_->set_history_dir(dir_path); |
-} |
- |
-bool InMemoryURLIndexTest::GetCacheFilePath(FilePath* file_path) const { |
- DCHECK(file_path); |
- return url_index_->GetCacheFilePath(file_path); |
-} |
- |
-void InMemoryURLIndexTest::PostRestoreFromCacheFileTask() { |
- url_index_->PostRestoreFromCacheFileTask(); |
-} |
- |
-void InMemoryURLIndexTest::PostSaveToCacheFileTask() { |
- url_index_->PostSaveToCacheFileTask(); |
-} |
- |
-void InMemoryURLIndexTest::Observe( |
- int notification_type, |
- const content::NotificationSource& source, |
- const content::NotificationDetails& details) { |
- url_index_->Observe(notification_type, source, details); |
-} |
- |
-const std::set<std::string>& InMemoryURLIndexTest::scheme_whitelist() { |
- return url_index_->scheme_whitelist(); |
-} |
- |
-bool InMemoryURLIndexTest::UpdateURL(const URLRow& row) { |
- return GetPrivateData()->UpdateURL(row, url_index_->languages_, |
- url_index_->scheme_whitelist_); |
-} |
- |
-bool InMemoryURLIndexTest::DeleteURL(const GURL& url) { |
- return GetPrivateData()->DeleteURL(url); |
-} |
- |
void InMemoryURLIndexTest::SetUp() { |
- // We cannot access the database until the backend has been loaded. |
- profile_.CreateHistoryService(true, false); |
- profile_.CreateBookmarkModel(true); |
- profile_.BlockUntilBookmarkModelLoaded(); |
- profile_.BlockUntilHistoryProcessesPendingRequests(); |
- HistoryService* history_service = |
- HistoryServiceFactory::GetForProfile(&profile_, |
- Profile::EXPLICIT_ACCESS); |
- ASSERT_TRUE(history_service); |
- HistoryBackend* backend = history_service->history_backend_.get(); |
- history_database_ = backend->db(); |
- |
- // Create and populate a working copy of the URL history database. |
- FilePath history_proto_path; |
- PathService::Get(chrome::DIR_TEST_DATA, &history_proto_path); |
- history_proto_path = history_proto_path.Append( |
- FILE_PATH_LITERAL("History")); |
- history_proto_path = history_proto_path.Append(TestDBName()); |
- EXPECT_TRUE(file_util::PathExists(history_proto_path)); |
- |
- std::ifstream proto_file(history_proto_path.value().c_str()); |
- static const size_t kCommandBufferMaxSize = 2048; |
- char sql_cmd_line[kCommandBufferMaxSize]; |
- |
- sql::Connection& db(GetDB()); |
- ASSERT_TRUE(db.is_open()); |
- { |
- sql::Transaction transaction(&db); |
- transaction.Begin(); |
- while (!proto_file.eof()) { |
- proto_file.getline(sql_cmd_line, kCommandBufferMaxSize); |
- if (!proto_file.eof()) { |
- // We only process lines which begin with a upper-case letter. |
- // TODO(mrossetti): Can iswupper() be used here? |
- if (sql_cmd_line[0] >= 'A' && sql_cmd_line[0] <= 'Z') { |
- std::string sql_cmd(sql_cmd_line); |
- sql::Statement sql_stmt(db.GetUniqueStatement(sql_cmd_line)); |
- EXPECT_TRUE(sql_stmt.Run()); |
- } |
- } |
- } |
- transaction.Commit(); |
- } |
- proto_file.close(); |
- |
- // Update the last_visit_time table column |
- // such that it represents a time relative to 'now'. |
- sql::Statement statement(db.GetUniqueStatement( |
- "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls;")); |
- ASSERT_TRUE(statement.is_valid()); |
- base::Time time_right_now = base::Time::NowFromSystemTime(); |
- base::TimeDelta day_delta = base::TimeDelta::FromDays(1); |
- { |
- sql::Transaction transaction(&db); |
- transaction.Begin(); |
- while (statement.Step()) { |
- URLRow row; |
- history_database_->FillURLRow(statement, &row); |
- base::Time last_visit = time_right_now; |
- for (int64 i = row.last_visit().ToInternalValue(); i > 0; --i) |
- last_visit -= day_delta; |
- row.set_last_visit(last_visit); |
- history_database_->UpdateURLRow(row.id(), row); |
- } |
- transaction.Commit(); |
- } |
- |
- url_index_.reset( |
- new InMemoryURLIndex(&profile_, FilePath(), "en,ja,hi,zh")); |
- url_index_->Init(); |
- url_index_->RebuildFromHistory(history_database_); |
+ // Set things up without enabling the cache database. |
+ InMemoryURLIndexBaseTest::SetUp(); |
+ LoadIndex(); |
} |
FilePath::StringType InMemoryURLIndexTest::TestDBName() const { |
return FILE_PATH_LITERAL("url_history_provider_test.db.txt"); |
} |
-#if 0 |
-URLRow InMemoryURLIndexTest::MakeURLRow(const char* url, |
- const char* title, |
- int visit_count, |
- int last_visit_ago, |
- int typed_count) { |
- URLRow row(GURL(url), 0); |
+URLRow MakeURLRowWithID(const char* url, |
+ const char* title, |
+ int visit_count, |
+ int last_visit_ago, |
+ int typed_count, |
+ URLID id) { |
+ URLRow row(GURL(url), id); |
row.set_title(UTF8ToUTF16(title)); |
row.set_visit_count(visit_count); |
row.set_typed_count(typed_count); |
@@ -317,34 +97,6 @@ |
return row; |
} |
-String16Vector InMemoryURLIndexTest::Make1Term(const char* term) const { |
- String16Vector original_terms; |
- original_terms.push_back(UTF8ToUTF16(term)); |
- return original_terms; |
-} |
- |
-String16Vector InMemoryURLIndexTest::Make2Terms(const char* term_1, |
- const char* term_2) const { |
- String16Vector original_terms; |
- original_terms.push_back(UTF8ToUTF16(term_1)); |
- original_terms.push_back(UTF8ToUTF16(term_2)); |
- return original_terms; |
-} |
- |
-float InMemoryURLIndexTest::GetTopicalityScoreOfTermAgainstURLAndTitle( |
- const string16& term, |
- const string16& url, |
- const string16& title) { |
- TermMatches url_matches = MatchTermInString(term, url, 0); |
- TermMatches title_matches = MatchTermInString(term, title, 0); |
- RowWordStarts word_starts; |
- String16SetFromString16(url, &word_starts.url_word_starts_); |
- String16SetFromString16(title, &word_starts.title_word_starts_); |
- return ScoredHistoryMatch::GetTopicalityScore( |
- 1, url, url_matches, title_matches, word_starts); |
-} |
-#endif |
- |
void InMemoryURLIndexTest::CheckTerm( |
const URLIndexPrivateData::SearchTermCacheMap& cache, |
string16 term) const { |
@@ -357,120 +109,11 @@ |
<< "Cache item '" << term << "' should be marked as being in use."; |
} |
-void InMemoryURLIndexTest::ExpectPrivateDataNotEmpty( |
- const URLIndexPrivateData& data) { |
- EXPECT_FALSE(data.word_list_.empty()); |
- // available_words_ will be empty since we have freshly built the |
- // data set for these tests. |
- EXPECT_TRUE(data.available_words_.empty()); |
- EXPECT_FALSE(data.word_map_.empty()); |
- EXPECT_FALSE(data.char_word_map_.empty()); |
- EXPECT_FALSE(data.word_id_history_map_.empty()); |
- EXPECT_FALSE(data.history_id_word_map_.empty()); |
- EXPECT_FALSE(data.history_info_map_.empty()); |
-} |
- |
-void InMemoryURLIndexTest::ExpectPrivateDataEmpty( |
- const URLIndexPrivateData& data) { |
- EXPECT_TRUE(data.word_list_.empty()); |
- EXPECT_TRUE(data.available_words_.empty()); |
- EXPECT_TRUE(data.word_map_.empty()); |
- EXPECT_TRUE(data.char_word_map_.empty()); |
- EXPECT_TRUE(data.word_id_history_map_.empty()); |
- EXPECT_TRUE(data.history_id_word_map_.empty()); |
- EXPECT_TRUE(data.history_info_map_.empty()); |
-} |
- |
-// Helper function which compares two maps for equivalence. The maps' values |
-// are associative containers and their contents are compared as well. |
-template<typename T> |
-void ExpectMapOfContainersIdentical(const T& expected, const T& actual) { |
- ASSERT_EQ(expected.size(), actual.size()); |
- for (typename T::const_iterator expected_iter = expected.begin(); |
- expected_iter != expected.end(); ++expected_iter) { |
- typename T::const_iterator actual_iter = actual.find(expected_iter->first); |
- ASSERT_TRUE(actual.end() != actual_iter); |
- typename T::mapped_type const& expected_values(expected_iter->second); |
- typename T::mapped_type const& actual_values(actual_iter->second); |
- ASSERT_EQ(expected_values.size(), actual_values.size()); |
- for (typename T::mapped_type::const_iterator set_iter = |
- expected_values.begin(); set_iter != expected_values.end(); ++set_iter) |
- EXPECT_EQ(actual_values.count(*set_iter), |
- expected_values.count(*set_iter)); |
- } |
-} |
- |
-void InMemoryURLIndexTest::ExpectPrivateDataEqual( |
- const URLIndexPrivateData& expected, |
- const URLIndexPrivateData& actual) { |
- EXPECT_EQ(expected.word_list_.size(), actual.word_list_.size()); |
- EXPECT_EQ(expected.word_map_.size(), actual.word_map_.size()); |
- EXPECT_EQ(expected.char_word_map_.size(), actual.char_word_map_.size()); |
- EXPECT_EQ(expected.word_id_history_map_.size(), |
- actual.word_id_history_map_.size()); |
- EXPECT_EQ(expected.history_id_word_map_.size(), |
- actual.history_id_word_map_.size()); |
- EXPECT_EQ(expected.history_info_map_.size(), actual.history_info_map_.size()); |
- EXPECT_EQ(expected.word_starts_map_.size(), actual.word_starts_map_.size()); |
- // WordList must be index-by-index equal. |
- size_t count = expected.word_list_.size(); |
- for (size_t i = 0; i < count; ++i) |
- EXPECT_EQ(expected.word_list_[i], actual.word_list_[i]); |
- |
- ExpectMapOfContainersIdentical(expected.char_word_map_, |
- actual.char_word_map_); |
- ExpectMapOfContainersIdentical(expected.word_id_history_map_, |
- actual.word_id_history_map_); |
- ExpectMapOfContainersIdentical(expected.history_id_word_map_, |
- actual.history_id_word_map_); |
- |
- for (HistoryInfoMap::const_iterator expected_info = |
- expected.history_info_map_.begin(); |
- expected_info != expected.history_info_map_.end(); ++expected_info) { |
- HistoryInfoMap::const_iterator actual_info = |
- actual.history_info_map_.find(expected_info->first); |
- // NOTE(yfriedman): ASSERT_NE can't be used due to incompatibility between |
- // gtest and STLPort in the Android build. See |
- // http://code.google.com/p/googletest/issues/detail?id=359 |
- ASSERT_TRUE(actual_info != actual.history_info_map_.end()); |
- const URLRow& expected_row(expected_info->second); |
- const URLRow& actual_row(actual_info->second); |
- EXPECT_EQ(expected_row.visit_count(), actual_row.visit_count()); |
- EXPECT_EQ(expected_row.typed_count(), actual_row.typed_count()); |
- EXPECT_EQ(expected_row.last_visit(), actual_row.last_visit()); |
- EXPECT_EQ(expected_row.url(), actual_row.url()); |
- } |
- |
- for (WordStartsMap::const_iterator expected_starts = |
- expected.word_starts_map_.begin(); |
- expected_starts != expected.word_starts_map_.end(); |
- ++expected_starts) { |
- WordStartsMap::const_iterator actual_starts = |
- actual.word_starts_map_.find(expected_starts->first); |
- // NOTE(yfriedman): ASSERT_NE can't be used due to incompatibility between |
- // gtest and STLPort in the Android build. See |
- // http://code.google.com/p/googletest/issues/detail?id=359 |
- ASSERT_TRUE(actual_starts != actual.word_starts_map_.end()); |
- const RowWordStarts& expected_word_starts(expected_starts->second); |
- const RowWordStarts& actual_word_starts(actual_starts->second); |
- EXPECT_EQ(expected_word_starts.url_word_starts_.size(), |
- actual_word_starts.url_word_starts_.size()); |
- EXPECT_TRUE(std::equal(expected_word_starts.url_word_starts_.begin(), |
- expected_word_starts.url_word_starts_.end(), |
- actual_word_starts.url_word_starts_.begin())); |
- EXPECT_EQ(expected_word_starts.title_word_starts_.size(), |
- actual_word_starts.title_word_starts_.size()); |
- EXPECT_TRUE(std::equal(expected_word_starts.title_word_starts_.begin(), |
- expected_word_starts.title_word_starts_.end(), |
- actual_word_starts.title_word_starts_.begin())); |
- } |
-} |
- |
//------------------------------------------------------------------------------ |
class LimitedInMemoryURLIndexTest : public InMemoryURLIndexTest { |
protected: |
- FilePath::StringType TestDBName() const; |
+ FilePath::StringType TestDBName() const OVERRIDE; |
}; |
FilePath::StringType LimitedInMemoryURLIndexTest::TestDBName() const { |
@@ -480,25 +123,25 @@ |
TEST_F(LimitedInMemoryURLIndexTest, Initialization) { |
// Verify that the database contains the expected number of items, which |
// is the pre-filtered count, i.e. all of the items. |
- sql::Statement statement(GetDB().GetUniqueStatement("SELECT * FROM urls;")); |
+ sql::Statement statement( |
+ history_database_->get_db_for_testing()->GetUniqueStatement( |
+ "SELECT * FROM urls;")); |
ASSERT_TRUE(statement.is_valid()); |
uint64 row_count = 0; |
while (statement.Step()) ++row_count; |
EXPECT_EQ(1U, row_count); |
- url_index_.reset( |
- new InMemoryURLIndex(&profile_, FilePath(), "en,ja,hi,zh")); |
- url_index_->Init(); |
- url_index_->RebuildFromHistory(history_database_); |
- URLIndexPrivateData& private_data(*GetPrivateData()); |
// history_info_map_ should have the same number of items as were filtered. |
- EXPECT_EQ(1U, private_data.history_info_map_.size()); |
- EXPECT_EQ(35U, private_data.char_word_map_.size()); |
- EXPECT_EQ(17U, private_data.word_map_.size()); |
+ EXPECT_EQ(1U, url_index_->private_data_->history_info_map_.size()); |
+ EXPECT_EQ(35U, url_index_->private_data_->char_word_map_.size()); |
+ EXPECT_EQ(17U, url_index_->private_data_->word_map_.size()); |
} |
+//------------------------------------------------------------------------------ |
+ |
TEST_F(InMemoryURLIndexTest, Retrieval) { |
// See if a very specific term gives a single result. |
+ // Note that in each case the term will be lowercased by the search. |
ScoredHistoryMatches matches = |
url_index_->HistoryItemsForTerms(ASCIIToUTF16("DrudgeReport")); |
ASSERT_EQ(1U, matches.size()); |
@@ -650,13 +293,13 @@ |
ScoredHistoryMatches matches = |
url_index_->HistoryItemsForTerms(ASCIIToUTF16("b")); |
- URLIndexPrivateData& private_data(*GetPrivateData()); |
+ const URLIndexPrivateData* private_data(GetPrivateData()); |
ASSERT_EQ(AutocompleteProvider::kMaxMatches, matches.size()); |
// There are 7 matches already in the database. |
- ASSERT_EQ(1008U, private_data.pre_filter_item_count_); |
- ASSERT_EQ(500U, private_data.post_filter_item_count_); |
+ ASSERT_EQ(1008U, private_data->pre_filter_item_count_); |
+ ASSERT_EQ(500U, private_data->post_filter_item_count_); |
ASSERT_EQ(AutocompleteProvider::kMaxMatches, |
- private_data.post_scoring_item_count_); |
+ private_data->post_scoring_item_count_); |
} |
TEST_F(InMemoryURLIndexTest, TitleSearch) { |
@@ -746,7 +389,7 @@ |
typedef URLIndexPrivateData::SearchTermCacheMap::iterator CacheIter; |
typedef URLIndexPrivateData::SearchTermCacheItem CacheItem; |
- URLIndexPrivateData::SearchTermCacheMap& cache( |
+ const URLIndexPrivateData::SearchTermCacheMap& cache( |
GetPrivateData()->search_term_cache_); |
// The cache should be empty at this point. |
@@ -795,128 +438,6 @@ |
CheckTerm(cache, ASCIIToUTF16("rec")); |
} |
-#if 0 |
-TEST_F(InMemoryURLIndexTest, Scoring) { |
- URLRow row_a(MakeURLRow("http://abcdef", "fedcba", 3, 30, 1)); |
- // We use NowFromSystemTime() because MakeURLRow uses the same function |
- // to calculate last visit time when building a row. |
- base::Time now = base::Time::NowFromSystemTime(); |
- RowWordStarts word_starts; |
- // Test scores based on position. |
- // TODO(mpearson): Test new_scoring if we're actually going to turn it |
- // on by default. This requires setting word_starts, which isn't done |
- // right now. |
- ScoredHistoryMatch scored_a(row_a, ASCIIToUTF16("abc"), Make1Term("abc"), |
- word_starts, now); |
- ScoredHistoryMatch scored_b(row_a, ASCIIToUTF16("bcd"), Make1Term("bcd"), |
- word_starts, now); |
- EXPECT_GT(scored_a.raw_score, scored_b.raw_score); |
- // Test scores based on length. |
- ScoredHistoryMatch scored_c(row_a, ASCIIToUTF16("abcd"), Make1Term("abcd"), |
- word_starts, now); |
- EXPECT_LT(scored_a.raw_score, scored_c.raw_score); |
- // Test scores based on order. |
- ScoredHistoryMatch scored_d(row_a, ASCIIToUTF16("abcdef"), |
- Make2Terms("abc", "def"), word_starts, now); |
- ScoredHistoryMatch scored_e(row_a, ASCIIToUTF16("def abc"), |
- Make2Terms("def", "abc"), word_starts, now); |
- EXPECT_GT(scored_d.raw_score, scored_e.raw_score); |
- // Test scores based on visit_count. |
- URLRow row_b(MakeURLRow("http://abcdef", "fedcba", 10, 30, 1)); |
- ScoredHistoryMatch scored_f(row_b, ASCIIToUTF16("abc"), Make1Term("abc"), |
- word_starts, now); |
- EXPECT_GT(scored_f.raw_score, scored_a.raw_score); |
- // Test scores based on last_visit. |
- URLRow row_c(MakeURLRow("http://abcdef", "fedcba", 3, 10, 1)); |
- ScoredHistoryMatch scored_g(row_c, ASCIIToUTF16("abc"), Make1Term("abc"), |
- word_starts, now); |
- EXPECT_GT(scored_g.raw_score, scored_a.raw_score); |
- // Test scores based on typed_count. |
- URLRow row_d(MakeURLRow("http://abcdef", "fedcba", 3, 30, 10)); |
- ScoredHistoryMatch scored_h(row_d, ASCIIToUTF16("abc"), Make1Term("abc"), |
- word_starts, now); |
- EXPECT_GT(scored_h.raw_score, scored_a.raw_score); |
- // Test scores based on a terms appearing multiple times. |
- URLRow row_i(MakeURLRow("http://csi.csi.csi/csi_csi", |
- "CSI Guide to CSI Las Vegas, CSI New York, CSI Provo", 3, 30, 10)); |
- ScoredHistoryMatch scored_i(row_i, ASCIIToUTF16("csi"), Make1Term("csi"), |
- word_starts, now); |
- EXPECT_LT(scored_i.raw_score, 1400); |
-} |
- |
-TEST_F(InMemoryURLIndexTest, GetTopicalityScoreTrailingSlash) { |
- const float hostname = GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("def"), |
- ASCIIToUTF16("http://abc.def.com/"), |
- ASCIIToUTF16("Non-Matching Title")); |
- const float hostname_no_slash = GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("def"), |
- ASCIIToUTF16("http://abc.def.com"), |
- ASCIIToUTF16("Non-Matching Title")); |
- EXPECT_EQ(hostname_no_slash, hostname); |
-} |
- |
-// This function only tests scoring of single terms that match exactly |
-// once somewhere in the URL or title. |
-TEST_F(InMemoryURLIndexTest, GetTopicalityScore) { |
- string16 url = ASCIIToUTF16("http://abc.def.com/path1/path2?" |
- "arg1=val1&arg2=val2#hash_component"); |
- string16 title = ASCIIToUTF16("here is a title"); |
- const float hostname_score = |
- GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("abc"), url, title); |
- const float hostname_mid_word_score = |
- GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("bc"), url, title); |
- const float path_score = |
- GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("path1"), url, title); |
- const float path_mid_word_score = |
- GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("ath1"), url, title); |
- const float arg_score = |
- GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("arg2"), url, title); |
- const float arg_mid_word_score = |
- GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("rg2"), url, title); |
- const float protocol_score = |
- GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("htt"), url, title); |
- const float protocol_mid_word_score = |
- GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("tt"), url, title); |
- const float title_score = |
- GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("her"), url, title); |
- const float title_mid_word_score = |
- GetTopicalityScoreOfTermAgainstURLAndTitle( |
- ASCIIToUTF16("er"), url, title); |
- // Verify hostname > path > arg, and the same for the matches at |
- // non-word-boundaries. |
- EXPECT_GT(hostname_score, path_score); |
- EXPECT_GT(path_score, arg_score); |
- EXPECT_GT(hostname_mid_word_score, path_mid_word_score); |
- EXPECT_GT(path_mid_word_score, arg_mid_word_score); |
- // Also verify that the matches at non-word-boundaries all score |
- // worse than the matches at word boundaries. These two sets suffice. |
- EXPECT_GT(arg_score, hostname_mid_word_score); |
- EXPECT_GT(title_score, title_mid_word_score); |
- // Check that title matches fit somewhere reasonable compared to the |
- // various types of URL matches. |
- EXPECT_GT(title_score, arg_score); |
- EXPECT_GT(arg_score, hostname_mid_word_score); |
- EXPECT_GT(title_mid_word_score, arg_mid_word_score); |
- // Finally, verify that protocol matches score worse than everything |
- // (except possibly mid-word matches in the ?arg section of the URL--I |
- // can imagine scoring those pretty harshly as well). |
- EXPECT_GT(path_mid_word_score, protocol_score); |
- EXPECT_GT(path_mid_word_score, protocol_mid_word_score); |
- EXPECT_GT(title_mid_word_score, protocol_score); |
- EXPECT_GT(title_mid_word_score, protocol_mid_word_score); |
-} |
-#endif |
- |
TEST_F(InMemoryURLIndexTest, AddNewRows) { |
// Verify that the row we're going to add does not already exist. |
URLID new_row_id = 87654321; |
@@ -971,9 +492,11 @@ |
URLsDeletedDetails deleted_details; |
deleted_details.all_history = false; |
deleted_details.rows.push_back(matches[0].url_info); |
- Observe(chrome::NOTIFICATION_HISTORY_URLS_DELETED, |
- content::Source<InMemoryURLIndexTest>(this), |
- content::Details<history::HistoryDetails>(&deleted_details)); |
+ content::Source<InMemoryURLIndexTest> source(this); |
+ url_index_->Observe( |
+ chrome::NOTIFICATION_HISTORY_URLS_DELETED, |
+ content::Source<InMemoryURLIndexTest>(this), |
+ content::Details<history::HistoryDetails>(&deleted_details)); |
EXPECT_TRUE(url_index_->HistoryItemsForTerms( |
ASCIIToUTF16("DrudgeReport")).empty()); |
} |
@@ -1052,113 +575,610 @@ |
{ "xmpp://guest@example.com", false }, |
}; |
- URLIndexPrivateData& private_data(*GetPrivateData()); |
- const std::set<std::string>& whitelist(scheme_whitelist()); |
+ std::set<std::string> whitelist; |
+ URLIndexPrivateData::InitializeSchemeWhitelistForTesting(&whitelist); |
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) { |
GURL url(data[i].url_spec); |
EXPECT_EQ(data[i].expected_is_whitelisted, |
- private_data.URLSchemeIsWhitelisted(url, whitelist)); |
+ URLIndexPrivateData::URLSchemeIsWhitelisted(url, whitelist)); |
} |
} |
-TEST_F(InMemoryURLIndexTest, CacheSaveRestore) { |
- ScopedTempDir temp_directory; |
- ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); |
- set_history_dir(temp_directory.path()); |
+// InMemoryURLIndexCacheTest --------------------------------------------------- |
- URLIndexPrivateData& private_data(*GetPrivateData()); |
+class InMemoryURLIndexCacheTest : public InMemoryURLIndexBaseTest { |
+ public: |
+ // A test helper class which captures the state of the URL index and cache |
+ // prior to the addition, update or delete of a history URL visit, determines |
+ // the expected state after the URL change, and verifies the actual state. |
+ class CacheChecker { |
+ public: |
+ enum ChangeType { |
+ URL_ADDED, |
+ URL_UPDATED, |
+ URL_REMOVED, |
+ }; |
- // Ensure that there is really something there to be saved. |
- EXPECT_FALSE(private_data.word_list_.empty()); |
- // available_words_ will already be empty since we have freshly built the |
- // data set for this test. |
- EXPECT_TRUE(private_data.available_words_.empty()); |
- EXPECT_FALSE(private_data.word_map_.empty()); |
- EXPECT_FALSE(private_data.char_word_map_.empty()); |
- EXPECT_FALSE(private_data.word_id_history_map_.empty()); |
- EXPECT_FALSE(private_data.history_id_word_map_.empty()); |
- EXPECT_FALSE(private_data.history_info_map_.empty()); |
- EXPECT_FALSE(private_data.word_starts_map_.empty()); |
+ // Captures the state of the index and cache and the changes which are |
+ // expected to be made. |index| points to the test index. |change_type| |
+ // specifies the kind of history URL visit change that will take place. |
+ // |existing_words| lists the words that are referenced by the history item |
+ // (both URL and page title) which are expected to already be recorded in |
+ // the index. |added_words| gives terms which are not currently in the index |
+ // but will be added for this history item. |removed_words| gives words in |
+ // the index but which are not referenced by any other history item and |
+ // which will be removed by the test action. |
+ CacheChecker(InMemoryURLIndex* index, |
+ ChangeType change_type, |
+ URLID history_id, |
+ const String16Set& existing_words, |
+ const String16Set& added_words, |
+ const String16Set& removed_words); |
+ virtual ~CacheChecker(); |
- // Capture the current private data for later comparison to restored data. |
- scoped_refptr<URLIndexPrivateData> old_data(private_data.Duplicate()); |
+ // Initializes and destroys things in a way that the standard gtest |
+ // EXPECT_xxx macros can be used since those macros cannot be used in |
+ // constructors and destructors. |
+ void Init(); |
+ void Destroy(); |
- // Save then restore our private data. |
- CacheFileSaverObserver save_observer(&message_loop_); |
- url_index_->set_save_cache_observer(&save_observer); |
- PostSaveToCacheFileTask(); |
- message_loop_.Run(); |
- EXPECT_TRUE(save_observer.succeeded_); |
+ private: |
+ // A helper struct that records index current state for words in the list of |
+ // |existing_words| and |removed_words|. |
+ struct ExistingWord { |
+ ExistingWord() : word_id(0), word_map_count(0), word_table_count(0) {} |
+ ExistingWord(WordID word_id, |
+ size_t word_map_count, |
+ size_t word_table_count) |
+ : word_id(word_id), |
+ word_map_count(word_map_count), |
+ word_table_count(word_table_count) {} |
- // Clear and then prove it's clear before restoring. |
- ClearPrivateData(); |
- EXPECT_TRUE(private_data.word_list_.empty()); |
- EXPECT_TRUE(private_data.available_words_.empty()); |
- EXPECT_TRUE(private_data.word_map_.empty()); |
- EXPECT_TRUE(private_data.char_word_map_.empty()); |
- EXPECT_TRUE(private_data.word_id_history_map_.empty()); |
- EXPECT_TRUE(private_data.history_id_word_map_.empty()); |
- EXPECT_TRUE(private_data.history_info_map_.empty()); |
- EXPECT_TRUE(private_data.word_starts_map_.empty()); |
+ WordID word_id; // Used for removed_words only. |
+ size_t word_map_count; |
+ size_t word_table_count; |
+ }; |
- CacheFileReaderObserver read_observer(&message_loop_); |
- url_index_->set_restore_cache_observer(&read_observer); |
- PostRestoreFromCacheFileTask(); |
- message_loop_.Run(); |
- EXPECT_TRUE(read_observer.succeeded_); |
+ // Table names. |
+ static const char* kWordsTableName; |
+ static const char* kWordHistoryTableName; |
+ static const char* kURLsTableName; |
+ static const char* kURLWordStartsTableName; |
+ static const char* kTitleWordStartsTableName; |
+ // Field names. |
+ static const char* kWord; |
+ static const char* kWordID; |
+ static const char* kHistoryID; |
+ static const char* kWordStart; |
- URLIndexPrivateData& new_data(*GetPrivateData()); |
+ // Helper functions that perform a COUNT query of the cache database with |
+ // the given criteria and return the number of rows meeting that criteria. |
+ // |table| specifies the name of the table in which the SELECT will be |
+ // performed. |column| gives the column against which the desired |word|, |
+ // |word_id| or |history_id| will be matched. |
+ int SelectCount(const char* table); |
+ int SelectCount(const char* table, |
+ const char* column, |
+ const string16& word); |
+ int SelectCount(const char* table, |
+ const char* column, |
+ WordID word_id); |
+ int SelectCount(const char* table, |
+ const char* column, |
+ HistoryID history_id); |
- // Compare the captured and restored for equality. |
- ExpectPrivateDataEqual(*old_data, new_data); |
+ InMemoryURLIndex& index_; |
+ sql::Connection* db_; |
+ // Information about the changes that the test is expected to cause. |
+ ChangeType change_type_; |
+ URLID history_id_; |
+ String16Set existing_words_; |
+ String16Set added_words_; |
+ String16Set removed_words_; |
+ // Current state information about the index. |
+ size_t word_list_count_; |
+ size_t word_table_count; |
+ size_t history_id_word_map_count_; |
+ size_t available_words_count_; |
+ std::map<string16, ExistingWord> existing_words_and_counts_; |
+ std::map<string16, ExistingWord> removed_words_and_counts_; |
+ }; |
+ |
+ void SetUp() OVERRIDE; |
+ |
+ // Provides custom test data file name. |
+ virtual FilePath::StringType TestDBName() const OVERRIDE; |
+}; |
+ |
+// InMemoryURLIndexCacheTest::CacheChecker ------------------------------------- |
+ |
+// Cache database table names. See in_memory_url_cache_database.cc. |
+typedef InMemoryURLIndexCacheTest::CacheChecker CacheChecker; |
+const char* CacheChecker::kWordsTableName = "words"; |
+const char* CacheChecker::kWordHistoryTableName = "word_history"; |
+const char* CacheChecker::kURLsTableName = "urls"; |
+const char* CacheChecker::kURLWordStartsTableName = "url_word_starts"; |
+const char* CacheChecker::kTitleWordStartsTableName = "title_word_starts"; |
+ |
+// Cache database table field names. |
+const char* CacheChecker::kWord = "word"; |
+const char* CacheChecker::kWordID = "word_id"; |
+const char* CacheChecker::kHistoryID = "history_id"; |
+const char* CacheChecker::kWordStart = "word_start"; |
+ |
+CacheChecker::CacheChecker( |
+ InMemoryURLIndex* index, |
+ ChangeType change_type, |
+ URLID history_id, |
+ const String16Set& existing_words, |
+ const String16Set& added_words, |
+ const String16Set& removed_words) |
+ : index_(*index), |
+ db_(index_.private_data_->cache_db()->get_db_for_testing()), |
+ change_type_(change_type), |
+ history_id_(history_id), |
+ existing_words_(existing_words), |
+ added_words_(added_words), |
+ removed_words_(removed_words), |
+ // Remember the old word count, the old word table count, the old history |
+ // ID count, and how many unused words are available for reuse. |
+ word_list_count_(index_.private_data_->word_list_.size()), |
+ word_table_count(SelectCount(kWordsTableName)), |
+ history_id_word_map_count_( |
+ index_.private_data_->history_id_word_map_.size()), |
+ available_words_count_(index_.private_data_->available_words_.size()) { |
+ DCHECK(db_); |
+ Init(); |
} |
-class InMemoryURLIndexCacheTest : public testing::Test { |
+void CacheChecker::Init() { |
+ // Verify that the existing words exist and remember their counts. |
+ URLIndexPrivateData* private_data(index_.private_data_.get()); |
+ for (String16Set::iterator word_iter = existing_words_.begin(); |
+ word_iter != existing_words_.end(); ++word_iter) { |
+ string16 word(*word_iter); |
+ ASSERT_NE(0U, private_data->word_map_.count(word)) |
+ << "Existing word '" << word << "' not found."; |
+ EXPECT_GT(SelectCount(kWordsTableName, kWord, word), 0) |
+ << "Existing word '" << word << "' not found in words table."; |
+ WordID word_id = private_data->word_map_[word]; |
+ EXPECT_GT(SelectCount(kWordHistoryTableName, kWordID, word_id), 0) |
+ << "Existing word '" << word << "' not found in word_history table."; |
+ existing_words_and_counts_[word] = |
+ ExistingWord(word_id, private_data->word_id_history_map_[word_id]. |
+ size(), |
+ SelectCount(kWordHistoryTableName, kWordID, word_id)); |
+ } |
+ |
+ // Verify that the added words don't already exist. |
+ for (String16Set::iterator word_iter = added_words_.begin(); |
+ word_iter != added_words_.end(); ++word_iter) { |
+ string16 word(*word_iter); |
+ EXPECT_EQ(0U, private_data->word_map_.count(word)) |
+ << "Word '" << word << "' to be added is already there."; |
+ EXPECT_EQ(0, SelectCount(kWordsTableName, kWord, word)) |
+ << "Word '" << word << "' to be added is already in words table."; |
+ } |
+ |
+ // Verify that the removed words exist and remember their counts. |
+ for (String16Set::iterator word_iter = removed_words_.begin(); |
+ word_iter != removed_words_.end(); ++word_iter) { |
+ string16 word(*word_iter); |
+ ASSERT_EQ(1U, private_data->word_map_.count(word)) |
+ << "Word '" << word << "' to be removed not found."; |
+ WordID word_id = private_data->word_map_[word]; |
+ EXPECT_GT(private_data->word_id_history_map_[word_id].size(), 0U); |
+ EXPECT_GT(SelectCount(kWordsTableName, kWord, word), 0) |
+ << "Word '" << word << "' to be removed not found in words table."; |
+ EXPECT_GT(SelectCount(kWordHistoryTableName, kWordID, word_id), 0) |
+ << "Word '" << word << "' to remove not found in word_history table."; |
+ removed_words_and_counts_[word] = |
+ ExistingWord(word_id, private_data->word_id_history_map_[word_id]. |
+ size(), |
+ SelectCount(kWordHistoryTableName, kWordID, word_id)); |
+ EXPECT_EQ(removed_words_and_counts_[word].word_map_count, |
+ removed_words_and_counts_[word].word_table_count); |
+ } |
+} |
+ |
+CacheChecker::~CacheChecker() { |
+ Destroy(); |
+} |
+ |
+void CacheChecker::Destroy() { |
+ // Verify that the existing words still exist and their counts have |
+ // incremented by 1. |
+ URLIndexPrivateData* private_data(index_.private_data_.get()); |
+ for (String16Set::iterator word_iter = existing_words_.begin(); |
+ word_iter != existing_words_.end(); ++word_iter) { |
+ string16 word(*word_iter); |
+ const ExistingWord& existing_word(existing_words_and_counts_[word]); |
+ size_t expected_count = existing_word.word_map_count; |
+ if (change_type_ == URL_ADDED) |
+ ++expected_count; |
+ WordID word_id = private_data->word_map_[word]; |
+ EXPECT_EQ(expected_count, |
+ private_data->word_id_history_map_[word_id].size()) |
+ << "Occurrence count for existing word '" << word |
+ << "' in the word_id_history_map_ is incorrect."; |
+ EXPECT_EQ(static_cast<int>(expected_count), |
+ SelectCount(kWordHistoryTableName, kWordID, word_id)) |
+ << "Occurrence count for existing word '" << word << "' in " |
+ "the word_history database table is incorrect."; |
+ } |
+ |
+ // Verify the added words have been added and their counts are 1. |
+ for (String16Set::iterator word_iter = added_words_.begin(); |
+ word_iter != added_words_.end(); ++word_iter) { |
+ string16 word(*word_iter); |
+ ASSERT_EQ(1U, private_data->word_map_.count(word)); |
+ WordID word_id = private_data->word_map_[word]; |
+ EXPECT_EQ(1U, private_data->word_id_history_map_[word_id].size()) |
+ << "Count for added word '" << word << "' not 1."; |
+ EXPECT_EQ(1, SelectCount(kWordsTableName, kWord, word)) |
+ << "Word '" << word << "' not added to words table."; |
+ EXPECT_EQ(1, SelectCount(kWordHistoryTableName, kWordID, word_id)) |
+ << "Word '" << word << "' not added to word_history table."; |
+ } |
+ |
+ switch (change_type_) { |
+ case URL_ADDED: |
+ ASSERT_EQ(0U, removed_words_.size()) |
+ << "BAD TEST DATA -- removed_words must be empty for URL_ADDED."; |
+ // Fall through. |
+ case URL_UPDATED: { |
+ // There should be added-words + existing-words - removed-words entries |
+ // in the word_history table for the history ID. |
+ size_t word_count_for_id = existing_words_.size() + added_words_.size(); |
+ EXPECT_EQ(word_count_for_id, |
+ private_data->history_id_word_map_[history_id_].size()); |
+ EXPECT_EQ(static_cast<int>(word_count_for_id), |
+ SelectCount(kWordHistoryTableName, kHistoryID, history_id_)); |
+ EXPECT_EQ(1U, private_data->history_id_word_map_.count(history_id_)); |
+ EXPECT_EQ(1, SelectCount(kURLsTableName, kHistoryID, history_id_)); |
+ } |
+ break; |
+ case URL_REMOVED: |
+ // There should be no entries in the word_history table for the history |
+ // ID. |
+ ASSERT_EQ(0U, added_words_.size()) |
+ << "BAD TEST DATA -- added_words must be empty for URL_REMOVED."; |
+ EXPECT_EQ(0U, private_data->history_id_word_map_.count(history_id_)); |
+ EXPECT_EQ(0, SelectCount(kWordHistoryTableName, kHistoryID, history_id_)); |
+ EXPECT_EQ(0, SelectCount(kURLsTableName, kHistoryID, history_id_)); |
+ break; |
+ } |
+ |
+ // Verify that the count for removed words has been decremented or that |
+ // they have been deleted if their count has dropped to 0. |
+ int completely_removed = 0; |
+ for (String16Set::iterator word_iter = removed_words_.begin(); |
+ word_iter != removed_words_.end(); ++word_iter) { |
+ string16 word(*word_iter); |
+ const ExistingWord& removed_word(removed_words_and_counts_[word]); |
+ size_t expected_word_count = removed_word.word_map_count - 1; |
+ if (expected_word_count > 0) { |
+ EXPECT_EQ(1, SelectCount(kWordsTableName, kWord, word)) |
+ << "Word '" << word << "' not removed from words table."; |
+ ASSERT_EQ(1U, private_data->word_map_.count(word)) |
+ << "Word '" << word << "' is gone but should still be there."; |
+ EXPECT_EQ(expected_word_count, |
+ private_data->word_id_history_map_[removed_word.word_id].size()) |
+ << "Count for existing word '" << word << "' not decremented. A"; |
+ EXPECT_EQ(static_cast<int>(expected_word_count), |
+ SelectCount(kWordHistoryTableName, kWordID, |
+ removed_word.word_id)) |
+ << "Count for existing word '" << word << "' not decremented. B"; |
+ } else { |
+ EXPECT_EQ(0, SelectCount(kWordsTableName, kWord, word)) |
+ << "Word '" << word << "' not removed from words table."; |
+ EXPECT_EQ(0U, private_data->word_map_.count(word)) |
+ << "Word '" << word << "' to be removed is still there."; |
+ ++completely_removed; |
+ } |
+ } |
+ |
+ // Verify that the size of the in-memory and on-disk database tables have |
+ // changed as expected. |
+ EXPECT_EQ(std::max(0, static_cast<int>(available_words_count_) + |
+ completely_removed - static_cast<int>(added_words_.size())), |
+ static_cast<int>(private_data->available_words_.size())); |
+ // The in-memory table will never get smaller as we remember the freed-up |
+ // slots and reuse them. |
+ EXPECT_EQ(static_cast<int>(word_list_count_) + std::max(0, |
+ static_cast<int>(added_words_.size()) - completely_removed), |
+ static_cast<int>(private_data->word_list_.size())); |
+ EXPECT_EQ(static_cast<int>(word_table_count) + |
+ static_cast<int>(added_words_.size()) - completely_removed, |
+ SelectCount(kWordsTableName)); |
+} |
+ |
+int CacheChecker::SelectCount(const char* table) { |
+ std::string sql(StringPrintf("SELECT COUNT(*) FROM %s", table)); |
+ sql::Statement statement(db_->GetUniqueStatement(sql.c_str())); |
+ return (statement.Step()) ? statement.ColumnInt(0) : -1; |
+} |
+ |
+int CacheChecker::SelectCount(const char* table, |
+ const char* column, |
+ const string16& word) { |
+ std::string sql(StringPrintf("SELECT COUNT(*) FROM %s WHERE %s = ?", |
+ table, column)); |
+ sql::Statement statement(db_->GetUniqueStatement(sql.c_str())); |
+ statement.BindString16(0, word); |
+ return (statement.Step()) ? statement.ColumnInt(0) : -1; |
+} |
+ |
+int CacheChecker::SelectCount( |
+ const char* table, |
+ const char* column, |
+ WordID word_id) { |
+ std::string sql(StringPrintf("SELECT COUNT(*) FROM %s WHERE %s = ?", |
+ table, column)); |
+ sql::Statement statement(db_->GetUniqueStatement(sql.c_str())); |
+ statement.BindInt(0, word_id); |
+ return (statement.Step()) ? statement.ColumnInt(0) : -1; |
+} |
+ |
+int CacheChecker::SelectCount(const char* table, |
+ const char* column, |
+ HistoryID history_id) { |
+ std::string sql(StringPrintf("SELECT COUNT(*) FROM %s WHERE %s = ?", |
+ table, column)); |
+ sql::Statement statement(db_->GetUniqueStatement(sql.c_str())); |
+ statement.BindInt(0, history_id); |
+ return (statement.Step()) ? statement.ColumnInt(0) : -1; |
+} |
+ |
+// InMemoryURLIndexCacheTest --------------------------------------------------- |
+ |
+void InMemoryURLIndexCacheTest::SetUp() { |
+ profile_.reset(new CacheTestingProfile); |
+ InMemoryURLIndexBaseTest::SetUp(); |
+ LoadIndex(); |
+ DCHECK(url_index_->index_available()); |
+} |
+ |
+FilePath::StringType InMemoryURLIndexCacheTest::TestDBName() const { |
+ return FILE_PATH_LITERAL("url_history_provider_test.db.txt"); |
+} |
+ |
+TEST_F(InMemoryURLIndexCacheTest, CacheAddRemove) { |
+ // Initialize the cache then add and remove some history items. |
+ const URLID kNewRowID = 250; |
+ URLRow new_row(MakeURLRowWithID("http://www.frank-and-earnest.com/", |
+ "Frank and Earnest Go Crash in Washington", |
+ 10, 0, 5, kNewRowID)); |
+ |
+ { |
+ // Add a new URL. |
+ String16Set existing_words(String16SetFromString16( |
+ UTF8ToUTF16("http www and com crash in"), NULL)); |
+ // New words: frank, earnest, go, washington. |
+ String16Set added_words(String16SetFromString16( |
+ UTF8ToUTF16("frank earnest go washington"), NULL)); |
+ String16Set removed_words; |
+ CacheChecker cache_checker(url_index_, CacheChecker::URL_ADDED, |
+ kNewRowID, existing_words, added_words, removed_words); |
+ UpdateURL(new_row); |
+ } |
+ |
+ URLRow old_row(new_row); |
+ { |
+ // Update an existing URL resulting in a net addition of words. |
+ old_row.set_title(UTF8ToUTF16( |
+ "Frank and Earnest Go Crazy in Draper Utah USA")); |
+ String16Set existing_words(String16SetFromString16( |
+ UTF8ToUTF16("http www and com in frank earnest go"), NULL)); |
+ String16Set added_words(String16SetFromString16( |
+ UTF8ToUTF16("crazy draper utah usa"), NULL)); |
+ String16Set removed_words(String16SetFromString16( |
+ UTF8ToUTF16("washington crash"), NULL)); |
+ CacheChecker cache_checker(url_index_, CacheChecker::URL_UPDATED, |
+ kNewRowID, existing_words, added_words, removed_words); |
+ UpdateURL(old_row); |
+ } |
+ |
+ { |
+ // Update an existing URL resulting in a net removal of words. |
+ old_row.set_title(UTF8ToUTF16("Frank and Earnest Go Crazy Crazy")); |
+ String16Set existing_words(String16SetFromString16( |
+ UTF8ToUTF16("http www and com frank earnest go crazy"), NULL)); |
+ String16Set added_words; |
+ String16Set removed_words(String16SetFromString16( |
+ UTF8ToUTF16("in draper utah usa"), NULL)); |
+ CacheChecker cache_checker(url_index_, CacheChecker::URL_UPDATED, |
+ kNewRowID, existing_words, added_words, removed_words); |
+ UpdateURL(old_row); |
+ } |
+ |
+ { |
+ // Delete an existing URL. |
+ old_row.set_title(UTF8ToUTF16("Frank and Earnest Go Crazy Crazy")); |
+ String16Set existing_words; |
+ String16Set added_words; |
+ String16Set removed_words(String16SetFromString16( |
+ UTF8ToUTF16("http www and com frank earnest go crazy"), NULL)); |
+ CacheChecker cache_checker(url_index_, CacheChecker::URL_REMOVED, |
+ kNewRowID, existing_words, added_words, removed_words); |
+ DeleteURL(old_row.url()); |
+ } |
+} |
+ |
+// InterposingCacheDatabase ---------------------------------------------------- |
+ |
+// This class allows certain InMemoryURLCacheDatabase methods to be intercepted |
+// for purposes of recording or simulating failures. |
+class InterposingCacheDatabase : public InMemoryURLCacheDatabase { |
public: |
- InMemoryURLIndexCacheTest() {} |
+ InterposingCacheDatabase(); |
- protected: |
- virtual void SetUp() OVERRIDE; |
+ virtual bool Reset() OVERRIDE; |
+ virtual bool Refresh(const URLIndexPrivateData& index_data) OVERRIDE; |
+ virtual void AddWordToWordsTask(WordID word_id, |
+ const string16& uni_word) OVERRIDE; |
- // Pass-through functions to simplify our friendship with InMemoryURLIndex. |
- void set_history_dir(const FilePath& dir_path); |
- bool GetCacheFilePath(FilePath* file_path) const; |
+ void set_simulate_update_fail(bool fail) { simulate_update_fail_ = fail; } |
+ void set_simulate_refresh_fail(bool fail) { simulate_refresh_fail_ = fail; } |
+ void set_tracking_calls(bool tracking) { tracking_calls_ = tracking; } |
+ int reset_calling_sequence() const { return reset_calling_sequence_; } |
+ int refresh_calling_sequence() const { return refresh_calling_sequence_; } |
+ void set_update_error(int error) { update_error_ = error; } |
+ bool failure_occurred() const { return update_error_ != SQLITE_OK; } |
- ScopedTempDir temp_dir_; |
- scoped_ptr<InMemoryURLIndex> url_index_; |
+ private: |
+ virtual ~InterposingCacheDatabase() {} |
+ |
+ bool simulate_update_fail_; |
+ bool simulate_refresh_fail_; |
+ int next_calling_sequence_; |
+ bool tracking_calls_; |
+ int reset_calling_sequence_; |
+ int refresh_calling_sequence_; |
}; |
-void InMemoryURLIndexCacheTest::SetUp() { |
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
- FilePath path(temp_dir_.path()); |
- url_index_.reset( |
- new InMemoryURLIndex(NULL, path, "en,ja,hi,zh")); |
+InterposingCacheDatabase::InterposingCacheDatabase() |
+ : simulate_update_fail_(false), |
+ simulate_refresh_fail_(false), |
+ next_calling_sequence_(0), |
+ tracking_calls_(false), |
+ reset_calling_sequence_(-1), |
+ refresh_calling_sequence_(-1) { |
} |
-void InMemoryURLIndexCacheTest::set_history_dir(const FilePath& dir_path) { |
- return url_index_->set_history_dir(dir_path); |
+bool InterposingCacheDatabase::Reset() { |
+ bool success = InMemoryURLCacheDatabase::Reset(); |
+ if (tracking_calls_) |
+ reset_calling_sequence_ = next_calling_sequence_++; |
+ return success; |
} |
-bool InMemoryURLIndexCacheTest::GetCacheFilePath(FilePath* file_path) const { |
- DCHECK(file_path); |
- return url_index_->GetCacheFilePath(file_path); |
+bool InterposingCacheDatabase::Refresh( |
+ const URLIndexPrivateData& index_data) { |
+ bool success = |
+ !simulate_refresh_fail_ && InMemoryURLCacheDatabase::Refresh(index_data); |
+ if (tracking_calls_) |
+ refresh_calling_sequence_ = next_calling_sequence_++; |
+ return success; |
} |
-TEST_F(InMemoryURLIndexCacheTest, CacheFilePath) { |
- FilePath expectedPath = |
- temp_dir_.path().Append(FILE_PATH_LITERAL("History Provider Cache")); |
- std::vector<FilePath::StringType> expected_parts; |
- expectedPath.GetComponents(&expected_parts); |
- FilePath full_file_path; |
- ASSERT_TRUE(GetCacheFilePath(&full_file_path)); |
- std::vector<FilePath::StringType> actual_parts; |
- full_file_path.GetComponents(&actual_parts); |
- ASSERT_EQ(expected_parts.size(), actual_parts.size()); |
- size_t count = expected_parts.size(); |
- for (size_t i = 0; i < count; ++i) |
- EXPECT_EQ(expected_parts[i], actual_parts[i]); |
- // Must clear the history_dir_ to satisfy the dtor's DCHECK. |
- set_history_dir(FilePath()); |
+void InterposingCacheDatabase::AddWordToWordsTask( |
+ WordID word_id, |
+ const string16& uni_word) { |
+ if (simulate_update_fail_) |
+ set_update_error(SQLITE_CORRUPT); |
+ else |
+ InMemoryURLCacheDatabase::AddWordToWordsTask(word_id, uni_word); |
} |
+// IntercessionaryIndexTest ---------------------------------------------------- |
+ |
+// Tests which intercede in some way with the normal operation of the index, |
+// its private data, and/or the cache database. |
+class IntercessionaryIndexTest : public InMemoryURLIndexBaseTest { |
+ public: |
+ IntercessionaryIndexTest(); |
+ ~IntercessionaryIndexTest() {} |
+ |
+ void SetUp() OVERRIDE; |
+ |
+ // Provides custom test data file name. |
+ virtual FilePath::StringType TestDBName() const OVERRIDE; |
+ |
+ protected: |
+ scoped_refptr<InterposingCacheDatabase> test_db_; |
+ URLIndexPrivateData* private_data_; |
+}; |
+ |
+IntercessionaryIndexTest::IntercessionaryIndexTest() |
+ : test_db_(new InterposingCacheDatabase) { |
+} |
+ |
+void IntercessionaryIndexTest::SetUp() { |
+ // Set things up without enabling the cache database. |
+ InMemoryURLIndexBaseTest::SetUp(); |
+ LoadIndex(); |
+ DCHECK(url_index_->index_available()); |
+ |
+ // Enabled our custom database and refresh it. |
+ FilePath dir_path; |
+ private_data_ = GetPrivateData(); |
+ DCHECK(private_data_->GetCacheDBPath(&dir_path)); |
+ base::SequencedWorkerPool::SequenceToken sequence_token = |
+ url_index_->sequence_token_for_testing(); |
+ content::BrowserThread::GetBlockingPool()->PostWorkerTask(FROM_HERE, |
+ base::Bind(base::IgnoreResult(&InMemoryURLCacheDatabase::Init), |
+ test_db_, dir_path, sequence_token)); |
+ content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
+ private_data_->SetCacheDatabaseForTesting(test_db_); |
+ private_data_->set_cache_enabled(true); |
+ content::BrowserThread::GetBlockingPool()->PostWorkerTask(FROM_HERE, |
+ base::Bind(&URLIndexPrivateData::RefreshCacheTask, private_data_)); |
+ content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
+ test_db_->set_tracking_calls(true); |
+} |
+ |
+FilePath::StringType IntercessionaryIndexTest::TestDBName() const { |
+ return FILE_PATH_LITERAL("url_history_provider_test.db.txt"); |
+} |
+ |
+TEST_F(IntercessionaryIndexTest, CacheDatabaseFailure) { |
+ // Perform an update but do not fail. |
+ const URLID kNewRowID = 250; |
+ URLRow new_row(MakeURLRowWithID("http://www.frank-and-earnest.com/", |
+ "Frank and Earnest Go Crash in Washington", |
+ 10, 0, 5, kNewRowID)); |
+ UpdateURL(new_row); |
+ EXPECT_EQ(-1, test_db_->reset_calling_sequence()); |
+ EXPECT_EQ(-1, test_db_->refresh_calling_sequence()); |
+ EXPECT_FALSE(test_db_->failure_occurred()); |
+ EXPECT_TRUE(private_data_->cache_enabled()); |
+ |
+ // Perform an update that fails but remediation succeeds. |
+ test_db_->set_simulate_update_fail(true); |
+ URLRow old_row(new_row); |
+ old_row.set_title(UTF8ToUTF16( |
+ "Frank and Earnest Go Crazy in Draper Utah USA")); |
+ content::WindowedNotificationObserver update_failure_observer( |
+ chrome::NOTIFICATION_IN_MEMORY_URL_CACHE_DATABASE_FAILURE, |
+ content::NotificationService::AllSources()); |
+ UpdateURL(old_row); |
+ update_failure_observer.Wait(); |
+ // Wait for the pending reset and refresh to complete. |
+ content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
+ EXPECT_EQ(0, test_db_->reset_calling_sequence()); |
+ EXPECT_EQ(1, test_db_->refresh_calling_sequence()); |
+ EXPECT_TRUE(test_db_->failure_occurred()); |
+ EXPECT_TRUE(private_data_->cache_enabled()); |
+ |
+ // Perform an update that fails and remediation fails. |
+ test_db_->set_simulate_update_fail(true); |
+ test_db_->set_simulate_refresh_fail(true); |
+ test_db_->set_update_error(SQLITE_OK); |
+ old_row.set_title(UTF8ToUTF16( |
+ "Frank and Earnest Light Up Dizzy World")); |
+ content::WindowedNotificationObserver refresh_failure_observer( |
+ chrome::NOTIFICATION_IN_MEMORY_URL_CACHE_DATABASE_FAILURE, |
+ content::NotificationService::AllSources()); |
+ UpdateURL(old_row); |
+ refresh_failure_observer.Wait(); |
+ // Wait for the pending reset and refresh to complete. |
+ content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
+ EXPECT_EQ(2, test_db_->reset_calling_sequence()); |
+ EXPECT_EQ(3, test_db_->refresh_calling_sequence()); |
+ EXPECT_TRUE(test_db_->failure_occurred()); |
+ EXPECT_FALSE(private_data_->cache_enabled()); |
+} |
+ |
+TEST_F(IntercessionaryIndexTest, ShutdownDuringCacheRefresh) { |
+ // Pretend that the cache is dysfunctional. |
+ private_data_->set_cache_enabled(false); |
+ // Kick off a refresh and immediately shut down. |
+ url_index_->PostRefreshCacheTask(); |
+ url_index_->Shutdown(); |
+ // The index should still be viable but the cache should have been shut down. |
+ EXPECT_FALSE(private_data_->cache_enabled()); |
+ EXPECT_TRUE(url_index_->index_available()); |
+} |
+ |
} // namespace history |