| 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
|
|
|