summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/autocomplete/zero_suggest_provider_unittest.cc5
-rw-r--r--chrome/browser/history/history_backend_unittest.cc3337
-rw-r--r--chrome/browser/history/top_sites_factory.cc11
-rw-r--r--chrome/browser/history/top_sites_factory.h5
-rw-r--r--chrome/chrome_tests_unit.gypi7
-rw-r--r--chrome/common/chrome_constants.cc1
-rw-r--r--chrome/common/chrome_constants.h2
-rw-r--r--chrome/test/base/testing_profile.cc76
-rw-r--r--chrome/test/base/testing_profile.h14
-rw-r--r--chrome/test/data/History/ArchivedNoDurationbin53248 -> 0 bytes
-rw-r--r--chrome/test/data/History/Favicons.v3.history.sql15
-rw-r--r--components/components_tests.gyp9
-rw-r--r--components/history.gypi19
-rw-r--r--components/history/content/browser/content_history_backend_db_unittest.cc (renamed from components/history/content/browser/content_history_backend_unittest.cc)4
-rw-r--r--components/history/core/browser/expire_history_backend_unittest.cc (renamed from chrome/browser/history/expire_history_backend_unittest.cc)183
-rw-r--r--components/history/core/browser/history_backend.h7
-rw-r--r--components/history/core/browser/history_backend_db_unittest.cc760
-rw-r--r--components/history/core/browser/history_backend_unittest.cc3987
-rw-r--r--components/history/core/browser/history_constants.cc15
-rw-r--r--components/history/core/browser/history_constants.h1
-rw-r--r--components/history/core/browser/history_database_unittest.cc (renamed from chrome/browser/history/history_database_unittest.cc)9
-rw-r--r--components/history/core/browser/history_querying_unittest.cc (renamed from chrome/browser/history/history_querying_unittest.cc)18
-rw-r--r--components/history/core/browser/in_memory_history_backend.h2
-rw-r--r--components/history/core/browser/thumbnail_database_unittest.cc (renamed from chrome/browser/history/thumbnail_database_unittest.cc)22
-rw-r--r--components/history/core/browser/top_sites_impl.cc1
-rw-r--r--components/history/core/browser/top_sites_impl_unittest.cc (renamed from chrome/browser/history/top_sites_impl_unittest.cc)227
-rw-r--r--components/history/core/browser/typed_url_syncable_service_unittest.cc (renamed from chrome/browser/history/typed_url_syncable_service_unittest.cc)39
-rw-r--r--components/history/core/test/BUILD.gn15
-rw-r--r--components/history/core/test/history_backend_db_base_test.cc (renamed from components/history/core/test/history_backend_base_test.cc)26
-rw-r--r--components/history/core/test/history_backend_db_base_test.h (renamed from components/history/core/test/history_backend_base_test.h)12
-rw-r--r--components/history/core/test/thumbnail.cc21
-rw-r--r--components/history/core/test/thumbnail.h19
-rw-r--r--components/history/core/test/thumbnail_ios.mm30
-rw-r--r--components/history/core/test/wait_top_sites_loaded_observer.cc35
-rw-r--r--components/history/core/test/wait_top_sites_loaded_observer.h39
-rw-r--r--components/test/data/history/Favicons.v3.sql (renamed from chrome/test/data/History/Favicons.v3.sql)0
-rw-r--r--components/test/data/history/Favicons.v4.sql (renamed from chrome/test/data/History/Favicons.v4.sql)0
-rw-r--r--components/test/data/history/Favicons.v5.sql (renamed from chrome/test/data/History/Favicons.v5.sql)0
-rw-r--r--components/test/data/history/Favicons.v6.sql (renamed from chrome/test/data/History/Favicons.v6.sql)0
-rw-r--r--components/test/data/history/Favicons.v7.sql (renamed from chrome/test/data/History/Favicons.v7.sql)0
-rw-r--r--components/test/data/history/Favicons.v8.sql (renamed from chrome/test/data/History/Favicons.v8.sql)0
-rw-r--r--components/test/data/history/HistoryNoDuration (renamed from chrome/test/data/History/HistoryNoDuration)bin86016 -> 86016 bytes
-rw-r--r--components/test/data/history/HistoryNoSource (renamed from chrome/test/data/History/HistoryNoSource)bin249856 -> 249856 bytes
-rw-r--r--components/test/data/history/History_with_starred (renamed from chrome/test/data/bookmarks/History_with_starred)bin98304 -> 98304 bytes
-rw-r--r--components/test/data/history/thumbnail_wild/Favicons.corrupt_meta.disable (renamed from chrome/test/data/History/thumbnail_wild/Favicons.corrupt_meta.disable)0
-rw-r--r--components/test/data/history/thumbnail_wild/Favicons.v2.init.sql (renamed from chrome/test/data/History/thumbnail_wild/Favicons.v2.init.sql)0
-rw-r--r--components/test/data/history/thumbnail_wild/Favicons.v3.init.sql (renamed from chrome/test/data/History/thumbnail_wild/Favicons.v3.init.sql)0
-rw-r--r--components/test/data/history/thumbnail_wild/Favicons.v4.init.sql (renamed from chrome/test/data/History/thumbnail_wild/Favicons.v4.init.sql)0
-rw-r--r--components/test/data/history/thumbnail_wild/Favicons.v5.icon_type.sql (renamed from chrome/test/data/History/thumbnail_wild/Favicons.v5.icon_type.sql)0
-rw-r--r--components/test/data/history/thumbnail_wild/Favicons.v5.icon_type2.sql (renamed from chrome/test/data/History/thumbnail_wild/Favicons.v5.icon_type2.sql)0
-rw-r--r--components/test/data/history/thumbnail_wild/Favicons.v5.init.sql (renamed from chrome/test/data/History/thumbnail_wild/Favicons.v5.init.sql)0
-rw-r--r--components/test/data/history/thumbnail_wild/Favicons.v5.sizes.sql (renamed from chrome/test/data/History/thumbnail_wild/Favicons.v5.sizes.sql)0
-rw-r--r--components/test/data/history/thumbnail_wild/Favicons.v6.init.sql (renamed from chrome/test/data/History/thumbnail_wild/Favicons.v6.init.sql)0
-rw-r--r--components/test/data/history/thumbnail_wild/README (renamed from chrome/test/data/History/thumbnail_wild/README)0
54 files changed, 4496 insertions, 4477 deletions
diff --git a/chrome/browser/autocomplete/zero_suggest_provider_unittest.cc b/chrome/browser/autocomplete/zero_suggest_provider_unittest.cc
index e15c471..5699e93 100644
--- a/chrome/browser/autocomplete/zero_suggest_provider_unittest.cc
+++ b/chrome/browser/autocomplete/zero_suggest_provider_unittest.cc
@@ -181,9 +181,8 @@ void ZeroSuggestProviderTest::SetUp() {
turl_model->Add(default_t_url_);
turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
- profile_.DestroyTopSites();
- TopSitesFactory::GetInstance()->SetTestingFactory(&profile_,
- BuildFakeEmptyTopSites);
+ TopSitesFactory* top_sites_factory = TopSitesFactory::GetInstance();
+ top_sites_factory->SetTestingFactory(&profile_, BuildFakeEmptyTopSites);
provider_ = ZeroSuggestProvider::Create(this, turl_model, &profile_);
}
diff --git a/chrome/browser/history/history_backend_unittest.cc b/chrome/browser/history/history_backend_unittest.cc
deleted file mode 100644
index 308b9fd..0000000
--- a/chrome/browser/history/history_backend_unittest.cc
+++ /dev/null
@@ -1,3337 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/history/core/browser/history_backend.h"
-
-#include <algorithm>
-#include <set>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/path_service.h"
-#include "base/prefs/pref_service.h"
-#include "base/run_loop.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/history/history_service_factory.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/favicon_base/favicon_usage_data.h"
-#include "components/history/content/browser/content_visit_delegate.h"
-#include "components/history/core/browser/history_constants.h"
-#include "components/history/core/browser/history_database_params.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/history/core/browser/history_service_observer.h"
-#include "components/history/core/browser/in_memory_database.h"
-#include "components/history/core/browser/in_memory_history_backend.h"
-#include "components/history/core/browser/keyword_search_term.h"
-#include "components/history/core/browser/visit_filter.h"
-#include "components/history/core/test/history_client_fake_bookmarks.h"
-#include "components/history/core/test/test_history_database.h"
-#include "content/public/test/test_browser_thread.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/codec/png_codec.h"
-#include "url/gurl.h"
-
-using base::Time;
-
-// This file only tests functionality where it is most convenient to call the
-// backend directly. Most of the history backend functions are tested by the
-// history unit test. Because of the elaborate callbacks involved, this is no
-// harder than calling it directly for many things.
-
-namespace {
-
-const int kTinyEdgeSize = 10;
-const int kSmallEdgeSize = 16;
-const int kLargeEdgeSize = 32;
-
-const gfx::Size kTinySize = gfx::Size(kTinyEdgeSize, kTinyEdgeSize);
-const gfx::Size kSmallSize = gfx::Size(kSmallEdgeSize, kSmallEdgeSize);
-const gfx::Size kLargeSize = gfx::Size(kLargeEdgeSize, kLargeEdgeSize);
-
-typedef base::Callback<void(const history::URLRow*,
- const history::URLRow*,
- const history::URLRow*)>
- SimulateNotificationCallback;
-
-class HistoryClientMock : public history::HistoryClientFakeBookmarks {
- public:
- MOCK_METHOD0(BlockUntilBookmarksLoaded, void());
-};
-
-void SimulateNotificationURLVisited(history::HistoryServiceObserver* observer,
- const history::URLRow* row1,
- const history::URLRow* row2,
- const history::URLRow* row3) {
- history::URLRows rows;
- rows.push_back(*row1);
- if (row2)
- rows.push_back(*row2);
- if (row3)
- rows.push_back(*row3);
-
- base::Time visit_time;
- history::RedirectList redirects;
- for (const auto& row : rows) {
- observer->OnURLVisited(
- nullptr, ui::PAGE_TRANSITION_LINK, row, redirects, visit_time);
- }
-}
-
-void SimulateNotificationURLsModified(history::HistoryServiceObserver* observer,
- const history::URLRow* row1,
- const history::URLRow* row2,
- const history::URLRow* row3) {
- history::URLRows rows;
- rows.push_back(*row1);
- if (row2)
- rows.push_back(*row2);
- if (row3)
- rows.push_back(*row3);
-
- observer->OnURLsModified(nullptr, rows);
-}
-
-} // namespace
-
-namespace history {
-
-class HistoryBackendTestBase;
-
-// This must be a separate object since HistoryBackend manages its lifetime.
-// This just forwards the messages we're interested in to the test object.
-class HistoryBackendTestDelegate : public HistoryBackend::Delegate {
- public:
- explicit HistoryBackendTestDelegate(HistoryBackendTestBase* test)
- : test_(test) {}
-
- void NotifyProfileError(sql::InitStatus init_status) override {}
- void SetInMemoryBackend(scoped_ptr<InMemoryHistoryBackend> backend) override;
- void NotifyFaviconChanged(const std::set<GURL>& urls) override;
- void NotifyURLVisited(ui::PageTransition transition,
- const URLRow& row,
- const RedirectList& redirects,
- base::Time visit_time) override;
- void NotifyURLsModified(const URLRows& changed_urls) override;
- void NotifyURLsDeleted(bool all_history,
- bool expired,
- const URLRows& deleted_rows,
- const std::set<GURL>& favicon_urls) override;
- void NotifyKeywordSearchTermUpdated(const URLRow& row,
- KeywordID keyword_id,
- const base::string16& term) override;
- void NotifyKeywordSearchTermDeleted(URLID url_id) override;
- void DBLoaded() override;
-
- private:
- // Not owned by us.
- HistoryBackendTestBase* test_;
-
- DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate);
-};
-
-class HistoryBackendTestBase : public testing::Test {
- public:
- typedef std::vector<std::pair<ui::PageTransition, URLRow>> URLVisitedList;
- typedef std::vector<URLRows> URLsModifiedList;
- typedef std::vector<std::pair<bool, bool>> URLsDeletedList;
-
- HistoryBackendTestBase()
- : loaded_(false),
- favicon_changed_notifications_(0),
- ui_thread_(content::BrowserThread::UI, &message_loop_) {}
-
- ~HistoryBackendTestBase() override {
- }
-
- protected:
- int favicon_changed_notifications() const {
- return favicon_changed_notifications_;
- }
-
- void ClearFaviconChangedNotificationCounter() {
- favicon_changed_notifications_ = 0;
- }
-
- int num_url_visited_notifications() const {
- return url_visited_notifications_.size();
- }
-
- const URLVisitedList& url_visited_notifications() const {
- return url_visited_notifications_;
- }
-
- int num_urls_modified_notifications() const {
- return urls_modified_notifications_.size();
- }
-
- const URLsModifiedList& urls_modified_notifications() const {
- return urls_modified_notifications_;
- }
-
- const URLsDeletedList& urls_deleted_notifications() const {
- return urls_deleted_notifications_;
- }
-
- void ClearBroadcastedNotifications() {
- url_visited_notifications_.clear();
- urls_modified_notifications_.clear();
- urls_deleted_notifications_.clear();
- }
-
- base::FilePath test_dir() {
- return test_dir_;
- }
-
- void NotifyFaviconChanged(const std::set<GURL>& changed_favicons) {
- ++favicon_changed_notifications_;
- }
-
- void NotifyURLVisited(ui::PageTransition transition,
- const URLRow& row,
- const RedirectList& redirects,
- base::Time visit_time) {
- // Send the notifications directly to the in-memory database.
- mem_backend_->OnURLVisited(nullptr, transition, row, redirects, visit_time);
- url_visited_notifications_.push_back(std::make_pair(transition, row));
- }
-
- void NotifyURLsModified(const URLRows& changed_urls) {
- // Send the notifications directly to the in-memory database.
- mem_backend_->OnURLsModified(nullptr, changed_urls);
- urls_modified_notifications_.push_back(changed_urls);
- }
-
- void NotifyURLsDeleted(bool all_history,
- bool expired,
- const URLRows& deleted_rows,
- const std::set<GURL>& favicon_urls) {
- mem_backend_->OnURLsDeleted(nullptr, all_history, expired, deleted_rows,
- favicon_urls);
- urls_deleted_notifications_.push_back(std::make_pair(all_history, expired));
- }
-
- void NotifyKeywordSearchTermUpdated(const URLRow& row,
- KeywordID keyword_id,
- const base::string16& term) {
- mem_backend_->OnKeywordSearchTermUpdated(nullptr, row, keyword_id, term);
- }
-
- void NotifyKeywordSearchTermDeleted(URLID url_id) {
- mem_backend_->OnKeywordSearchTermDeleted(nullptr, url_id);
- }
-
- history::HistoryClientFakeBookmarks history_client_;
- scoped_refptr<HistoryBackend> backend_; // Will be NULL on init failure.
- scoped_ptr<InMemoryHistoryBackend> mem_backend_;
- bool loaded_;
-
- private:
- friend class HistoryBackendTestDelegate;
-
- // testing::Test
- void SetUp() override {
- ClearFaviconChangedNotificationCounter();
- if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"),
- &test_dir_))
- return;
- backend_ = new HistoryBackend(new HistoryBackendTestDelegate(this),
- &history_client_);
- backend_->Init(std::string(), false,
- TestHistoryDatabaseParamsForPath(test_dir_));
- }
-
- void TearDown() override {
- if (backend_.get())
- backend_->Closing();
- backend_ = NULL;
- mem_backend_.reset();
- base::DeleteFile(test_dir_, true);
- base::RunLoop().RunUntilIdle();
- history_client_.ClearAllBookmarks();
- }
-
- void SetInMemoryBackend(scoped_ptr<InMemoryHistoryBackend> backend) {
- mem_backend_.swap(backend);
- }
-
- // The types and details of notifications which were broadcasted.
- int favicon_changed_notifications_;
- URLVisitedList url_visited_notifications_;
- URLsModifiedList urls_modified_notifications_;
- URLsDeletedList urls_deleted_notifications_;
-
- base::MessageLoop message_loop_;
- base::FilePath test_dir_;
- content::TestBrowserThread ui_thread_;
-
- DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestBase);
-};
-
-void HistoryBackendTestDelegate::SetInMemoryBackend(
- scoped_ptr<InMemoryHistoryBackend> backend) {
- test_->SetInMemoryBackend(backend.Pass());
-}
-
-void HistoryBackendTestDelegate::NotifyFaviconChanged(
- const std::set<GURL>& changed_favicons) {
- test_->NotifyFaviconChanged(changed_favicons);
-}
-
-void HistoryBackendTestDelegate::NotifyURLVisited(ui::PageTransition transition,
- const URLRow& row,
- const RedirectList& redirects,
- base::Time visit_time) {
- test_->NotifyURLVisited(transition, row, redirects, visit_time);
-}
-
-void HistoryBackendTestDelegate::NotifyURLsModified(
- const URLRows& changed_urls) {
- test_->NotifyURLsModified(changed_urls);
-}
-
-void HistoryBackendTestDelegate::NotifyURLsDeleted(
- bool all_history,
- bool expired,
- const URLRows& deleted_rows,
- const std::set<GURL>& favicon_urls) {
- test_->NotifyURLsDeleted(all_history, expired, deleted_rows, favicon_urls);
-}
-
-void HistoryBackendTestDelegate::NotifyKeywordSearchTermUpdated(
- const URLRow& row,
- KeywordID keyword_id,
- const base::string16& term) {
- test_->NotifyKeywordSearchTermUpdated(row, keyword_id, term);
-}
-
-void HistoryBackendTestDelegate::NotifyKeywordSearchTermDeleted(URLID url_id) {
- test_->NotifyKeywordSearchTermDeleted(url_id);
-}
-
-void HistoryBackendTestDelegate::DBLoaded() {
- test_->loaded_ = true;
-}
-
-class HistoryBackendTest : public HistoryBackendTestBase {
- public:
- HistoryBackendTest() {}
- ~HistoryBackendTest() override {}
-
- protected:
- void AddRedirectChain(const char* sequence[], int nav_entry_id) {
- AddRedirectChainWithTransitionAndTime(sequence, nav_entry_id,
- ui::PAGE_TRANSITION_LINK,
- Time::Now());
- }
-
- void AddRedirectChainWithTransitionAndTime(
- const char* sequence[],
- int nav_entry_id,
- ui::PageTransition transition,
- base::Time time) {
- history::RedirectList redirects;
- for (int i = 0; sequence[i] != NULL; ++i)
- redirects.push_back(GURL(sequence[i]));
-
- ContextID context_id = reinterpret_cast<ContextID>(1);
- history::HistoryAddPageArgs request(
- redirects.back(), time, context_id, nav_entry_id, GURL(),
- redirects, transition, history::SOURCE_BROWSED,
- true);
- backend_->AddPage(request);
- }
-
- // Adds CLIENT_REDIRECT page transition.
- // |url1| is the source URL and |url2| is the destination.
- // |did_replace| is true if the transition is non-user initiated and the
- // navigation entry for |url2| has replaced that for |url1|. The possibly
- // updated transition code of the visit records for |url1| and |url2| is
- // returned by filling in |*transition1| and |*transition2|, respectively.
- // |time| is a time of the redirect.
- void AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace,
- base::Time time,
- int* transition1, int* transition2) {
- ContextID dummy_context_id = reinterpret_cast<ContextID>(0x87654321);
- history::RedirectList redirects;
- if (url1.is_valid())
- redirects.push_back(url1);
- if (url2.is_valid())
- redirects.push_back(url2);
- HistoryAddPageArgs request(
- url2, time, dummy_context_id, 0, url1,
- redirects, ui::PAGE_TRANSITION_CLIENT_REDIRECT,
- history::SOURCE_BROWSED, did_replace);
- backend_->AddPage(request);
-
- *transition1 = GetTransition(url1);
- *transition2 = GetTransition(url2);
- }
-
- int GetTransition(const GURL& url) {
- if (!url.is_valid())
- return 0;
- URLRow row;
- URLID id = backend_->db()->GetRowForURL(url, &row);
- VisitVector visits;
- EXPECT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- return visits[0].transition;
- }
-
- // Returns a vector with the small and large edge sizes.
- const std::vector<int> GetEdgeSizesSmallAndLarge() {
- std::vector<int> sizes_small_and_large;
- sizes_small_and_large.push_back(kSmallEdgeSize);
- sizes_small_and_large.push_back(kLargeEdgeSize);
- return sizes_small_and_large;
- }
-
- // Returns the number of icon mappings of |icon_type| to |page_url|.
- size_t NumIconMappingsForPageURL(const GURL& page_url,
- favicon_base::IconType icon_type) {
- std::vector<IconMapping> icon_mappings;
- backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_type,
- &icon_mappings);
- return icon_mappings.size();
- }
-
- // Returns the icon mappings for |page_url| sorted alphabetically by icon
- // URL in ascending order. Returns true if there is at least one icon
- // mapping.
- bool GetSortedIconMappingsForPageURL(
- const GURL& page_url,
- std::vector<IconMapping>* icon_mappings) {
- if (!backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
- icon_mappings)) {
- return false;
- }
- std::sort(icon_mappings->begin(), icon_mappings->end(),
- [](const history::IconMapping& a, const history::IconMapping& b) {
- return a.icon_url < b.icon_url;
- });
- return true;
- }
-
- // Returns the favicon bitmaps for |icon_id| sorted by pixel size in
- // ascending order. Returns true if there is at least one favicon bitmap.
- bool GetSortedFaviconBitmaps(favicon_base::FaviconID icon_id,
- std::vector<FaviconBitmap>* favicon_bitmaps) {
- if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, favicon_bitmaps))
- return false;
- std::sort(
- favicon_bitmaps->begin(), favicon_bitmaps->end(),
- [](const history::FaviconBitmap& a, const history::FaviconBitmap& b) {
- return a.pixel_size.GetArea() < b.pixel_size.GetArea();
- });
- return true;
- }
-
- // Returns true if there is exactly one favicon bitmap associated to
- // |favicon_id|. If true, returns favicon bitmap in output parameter.
- bool GetOnlyFaviconBitmap(const favicon_base::FaviconID icon_id,
- FaviconBitmap* favicon_bitmap) {
- std::vector<FaviconBitmap> favicon_bitmaps;
- if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, &favicon_bitmaps))
- return false;
- if (favicon_bitmaps.size() != 1)
- return false;
- *favicon_bitmap = favicon_bitmaps[0];
- return true;
- }
-
- // Creates an |edge_size|x|edge_size| bitmap of |color|.
- SkBitmap CreateBitmap(SkColor color, int edge_size) {
- SkBitmap bitmap;
- bitmap.allocN32Pixels(edge_size, edge_size);
- bitmap.eraseColor(color);
- return bitmap;
- }
-
- // Returns true if |bitmap_data| is equal to |expected_data|.
- bool BitmapDataEqual(char expected_data,
- scoped_refptr<base::RefCountedMemory> bitmap_data) {
- return bitmap_data.get() &&
- bitmap_data->size() == 1u &&
- *bitmap_data->front() == expected_data;
- }
-
- // Returns true if |bitmap_data| is of |color|.
- bool BitmapColorEqual(SkColor expected_color,
- scoped_refptr<base::RefCountedMemory> bitmap_data) {
- SkBitmap bitmap;
- if (!gfx::PNGCodec::Decode(
- bitmap_data->front(), bitmap_data->size(), &bitmap))
- return false;
- SkAutoLockPixels bitmap_lock(bitmap);
- return expected_color == bitmap.getColor(0, 0);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(HistoryBackendTest);
-};
-
-class InMemoryHistoryBackendTest : public HistoryBackendTestBase {
- public:
- InMemoryHistoryBackendTest() {}
- ~InMemoryHistoryBackendTest() override {}
-
- protected:
- void SimulateNotificationURLsDeleted(const URLRow* row1,
- const URLRow* row2 = NULL,
- const URLRow* row3 = NULL) {
- URLRows rows;
- rows.push_back(*row1);
- if (row2) rows.push_back(*row2);
- if (row3) rows.push_back(*row3);
-
- NotifyURLsDeleted(false, false, rows, std::set<GURL>());
- }
-
- size_t GetNumberOfMatchingSearchTerms(const int keyword_id,
- const base::string16& prefix) {
- std::vector<KeywordSearchTermVisit> matching_terms;
- mem_backend_->db()->GetMostRecentKeywordSearchTerms(
- keyword_id, prefix, 1, &matching_terms);
- return matching_terms.size();
- }
-
- static URLRow CreateTestTypedURL() {
- URLRow url_row(GURL("https://www.google.com/"));
- url_row.set_id(10);
- url_row.set_title(base::UTF8ToUTF16("Google Search"));
- url_row.set_typed_count(1);
- url_row.set_visit_count(1);
- url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(1));
- return url_row;
- }
-
- static URLRow CreateAnotherTestTypedURL() {
- URLRow url_row(GURL("https://maps.google.com/"));
- url_row.set_id(20);
- url_row.set_title(base::UTF8ToUTF16("Google Maps"));
- url_row.set_typed_count(2);
- url_row.set_visit_count(3);
- url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(2));
- return url_row;
- }
-
- static URLRow CreateTestNonTypedURL() {
- URLRow url_row(GURL("https://news.google.com/"));
- url_row.set_id(30);
- url_row.set_title(base::UTF8ToUTF16("Google News"));
- url_row.set_visit_count(5);
- url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(3));
- return url_row;
- }
-
- void PopulateTestURLsAndSearchTerms(URLRow* row1,
- URLRow* row2,
- const base::string16& term1,
- const base::string16& term2);
-
- void TestAddingAndChangingURLRows(
- const SimulateNotificationCallback& callback);
-
- static const KeywordID kTestKeywordId;
- static const char kTestSearchTerm1[];
- static const char kTestSearchTerm2[];
-
- private:
- DISALLOW_COPY_AND_ASSIGN(InMemoryHistoryBackendTest);
-};
-
-const KeywordID InMemoryHistoryBackendTest::kTestKeywordId = 42;
-const char InMemoryHistoryBackendTest::kTestSearchTerm1[] = "banana";
-const char InMemoryHistoryBackendTest::kTestSearchTerm2[] = "orange";
-
-// http://crbug.com/114287
-#if defined(OS_WIN)
-#define MAYBE_Loaded DISABLED_Loaded
-#else
-#define MAYBE_Loaded Loaded
-#endif // defined(OS_WIN)
-TEST_F(HistoryBackendTest, MAYBE_Loaded) {
- ASSERT_TRUE(backend_.get());
- ASSERT_TRUE(loaded_);
-}
-
-TEST_F(HistoryBackendTest, DeleteAll) {
- ASSERT_TRUE(backend_.get());
-
- // Add two favicons, each with two bitmaps. Note that we add favicon2 before
- // adding favicon1. This is so that favicon1 one gets ID 2 autoassigned to
- // the database, which will change when the other one is deleted. This way
- // we can test that updating works properly.
- GURL favicon_url1("http://www.google.com/favicon.ico");
- GURL favicon_url2("http://news.google.com/favicon.ico");
- favicon_base::FaviconID favicon2 =
- backend_->thumbnail_db_->AddFavicon(favicon_url2, favicon_base::FAVICON);
- favicon_base::FaviconID favicon1 =
- backend_->thumbnail_db_->AddFavicon(favicon_url1, favicon_base::FAVICON);
-
- std::vector<unsigned char> data;
- data.push_back('a');
- EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
- new base::RefCountedBytes(data), Time::Now(), kSmallSize));
- data[0] = 'b';
- EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
- new base::RefCountedBytes(data), Time::Now(), kLargeSize));
-
- data[0] = 'c';
- EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
- new base::RefCountedBytes(data), Time::Now(), kSmallSize));
- data[0] = 'd';
- EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
- new base::RefCountedBytes(data), Time::Now(), kLargeSize));
-
- // First visit two URLs.
- URLRow row1(GURL("http://www.google.com/"));
- row1.set_visit_count(2);
- row1.set_typed_count(1);
- row1.set_last_visit(Time::Now());
- backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1);
-
- URLRow row2(GURL("http://news.google.com/"));
- row2.set_visit_count(1);
- row2.set_last_visit(Time::Now());
- backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2);
-
- URLRows rows;
- rows.push_back(row2); // Reversed order for the same reason as favicons.
- rows.push_back(row1);
- backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
-
- URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
- URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
-
- // Get the two visits for the URLs we just added.
- VisitVector visits;
- backend_->db_->GetVisitsForURL(row1_id, &visits);
- ASSERT_EQ(1U, visits.size());
-
- visits.clear();
- backend_->db_->GetVisitsForURL(row2_id, &visits);
- ASSERT_EQ(1U, visits.size());
-
- // The in-memory backend should have been set and it should have gotten the
- // typed URL.
- ASSERT_TRUE(mem_backend_.get());
- URLRow outrow1;
- EXPECT_TRUE(mem_backend_->db_->GetRowForURL(row1.url(), NULL));
-
- // Star row1.
- history_client_.AddBookmark(row1.url());
-
- // Now finally clear all history.
- ClearBroadcastedNotifications();
- backend_->DeleteAllHistory();
-
- // The first URL should be preserved but the time should be cleared.
- EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1));
- EXPECT_EQ(row1.url(), outrow1.url());
- EXPECT_EQ(0, outrow1.visit_count());
- EXPECT_EQ(0, outrow1.typed_count());
- EXPECT_TRUE(Time() == outrow1.last_visit());
-
- // The second row should be deleted.
- URLRow outrow2;
- EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &outrow2));
-
- // All visits should be deleted for both URLs.
- VisitVector all_visits;
- backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
- ASSERT_EQ(0U, all_visits.size());
-
- // We should have a favicon and favicon bitmaps for the first URL only. We
- // look them up by favicon URL since the IDs may have changed.
- favicon_base::FaviconID out_favicon1 =
- backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- favicon_url1, favicon_base::FAVICON, NULL);
- EXPECT_TRUE(out_favicon1);
-
- std::vector<FaviconBitmap> favicon_bitmaps;
- EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
- out_favicon1, &favicon_bitmaps));
- ASSERT_EQ(2u, favicon_bitmaps.size());
-
- FaviconBitmap favicon_bitmap1 = favicon_bitmaps[0];
- FaviconBitmap favicon_bitmap2 = favicon_bitmaps[1];
-
- // Favicon bitmaps do not need to be in particular order.
- if (favicon_bitmap1.pixel_size == kLargeSize) {
- FaviconBitmap tmp_favicon_bitmap = favicon_bitmap1;
- favicon_bitmap1 = favicon_bitmap2;
- favicon_bitmap2 = tmp_favicon_bitmap;
- }
-
- EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap1.bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmap1.pixel_size);
-
- EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap2.bitmap_data));
- EXPECT_EQ(kLargeSize, favicon_bitmap2.pixel_size);
-
- favicon_base::FaviconID out_favicon2 =
- backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- favicon_url2, favicon_base::FAVICON, NULL);
- EXPECT_FALSE(out_favicon2) << "Favicon not deleted";
-
- // The remaining URL should still reference the same favicon, even if its
- // ID has changed.
- std::vector<IconMapping> mappings;
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
- outrow1.url(), favicon_base::FAVICON, &mappings));
- EXPECT_EQ(1u, mappings.size());
- EXPECT_EQ(out_favicon1, mappings[0].icon_id);
-
- // The first URL should still be bookmarked.
- EXPECT_TRUE(history_client_.IsBookmarked(row1.url()));
-
- // Check that we fire the notification about all history having been deleted.
- ASSERT_EQ(1u, urls_deleted_notifications().size());
- EXPECT_TRUE(urls_deleted_notifications()[0].first);
- EXPECT_FALSE(urls_deleted_notifications()[0].second);
-}
-
-// Checks that adding a visit, then calling DeleteAll, and then trying to add
-// data for the visited page works. This can happen when clearing the history
-// immediately after visiting a page.
-TEST_F(HistoryBackendTest, DeleteAllThenAddData) {
- ASSERT_TRUE(backend_.get());
-
- Time visit_time = Time::Now();
- GURL url("http://www.google.com/");
- HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
- history::RedirectList(),
- ui::PAGE_TRANSITION_KEYWORD_GENERATED,
- history::SOURCE_BROWSED, false);
- backend_->AddPage(request);
-
- // Check that a row was added.
- URLRow outrow;
- EXPECT_TRUE(backend_->db_->GetRowForURL(url, &outrow));
-
- // Check that the visit was added.
- VisitVector all_visits;
- backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
- ASSERT_EQ(1U, all_visits.size());
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- // The row should be deleted.
- EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
-
- // The visit should be deleted.
- backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
- ASSERT_EQ(0U, all_visits.size());
-
- // Try and set the title.
- backend_->SetPageTitle(url, base::UTF8ToUTF16("Title"));
-
- // The row should still be deleted.
- EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
-
- // The visit should still be deleted.
- backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
- ASSERT_EQ(0U, all_visits.size());
-}
-
-TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {
- GURL favicon_url1("http://www.google.com/favicon.ico");
- GURL favicon_url2("http://news.google.com/favicon.ico");
-
- std::vector<unsigned char> data;
- data.push_back('1');
- favicon_base::FaviconID favicon1 =
- backend_->thumbnail_db_->AddFavicon(favicon_url1,
- favicon_base::FAVICON,
- new base::RefCountedBytes(data),
- Time::Now(),
- gfx::Size());
-
- data[0] = '2';
- favicon_base::FaviconID favicon2 =
- backend_->thumbnail_db_->AddFavicon(favicon_url2,
- favicon_base::FAVICON,
- new base::RefCountedBytes(data),
- Time::Now(),
- gfx::Size());
-
- // First visit two URLs.
- URLRow row1(GURL("http://www.google.com/"));
- row1.set_visit_count(2);
- row1.set_typed_count(1);
- row1.set_last_visit(Time::Now());
- EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
-
- URLRow row2(GURL("http://news.google.com/"));
- row2.set_visit_count(1);
- row2.set_last_visit(Time::Now());
- EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2));
-
- URLRows rows;
- rows.push_back(row2); // Reversed order for the same reason as favicons.
- rows.push_back(row1);
- backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
-
- URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
- URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
-
- // Star the two URLs.
- history_client_.AddBookmark(row1.url());
- history_client_.AddBookmark(row2.url());
-
- // Delete url 2.
- backend_->expirer_.DeleteURL(row2.url());
- EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), NULL));
- VisitVector visits;
- backend_->db_->GetVisitsForURL(row2_id, &visits);
- EXPECT_EQ(0U, visits.size());
- // The favicon should still be valid.
- EXPECT_EQ(favicon2,
- backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- favicon_url2, favicon_base::FAVICON, NULL));
-
- // Unstar row2.
- history_client_.DelBookmark(row2.url());
-
- // Tell the backend it was unstarred. We have to explicitly do this as
- // BookmarkModel isn't wired up to the backend during testing.
- std::set<GURL> unstarred_urls;
- unstarred_urls.insert(row2.url());
- backend_->URLsNoLongerBookmarked(unstarred_urls);
-
- // The URL should still not exist.
- EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), NULL));
- // And the favicon should be deleted.
- EXPECT_EQ(0,
- backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- favicon_url2, favicon_base::FAVICON, NULL));
-
- // Unstar row 1.
- history_client_.DelBookmark(row1.url());
-
- // Tell the backend it was unstarred. We have to explicitly do this as
- // BookmarkModel isn't wired up to the backend during testing.
- unstarred_urls.clear();
- unstarred_urls.insert(row1.url());
- backend_->URLsNoLongerBookmarked(unstarred_urls);
-
- // The URL should still exist (because there were visits).
- EXPECT_EQ(row1_id, backend_->db_->GetRowForURL(row1.url(), NULL));
-
- // There should still be visits.
- visits.clear();
- backend_->db_->GetVisitsForURL(row1_id, &visits);
- EXPECT_EQ(1U, visits.size());
-
- // The favicon should still be valid.
- EXPECT_EQ(favicon1,
- backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- favicon_url1, favicon_base::FAVICON, NULL));
-}
-
-// Tests a handful of assertions for a navigation with a type of
-// KEYWORD_GENERATED.
-TEST_F(HistoryBackendTest, KeywordGenerated) {
- ASSERT_TRUE(backend_.get());
-
- GURL url("http://google.com");
-
- Time visit_time = Time::Now() - base::TimeDelta::FromDays(1);
- HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
- history::RedirectList(),
- ui::PAGE_TRANSITION_KEYWORD_GENERATED,
- history::SOURCE_BROWSED, false);
- backend_->AddPage(request);
-
- // A row should have been added for the url.
- URLRow row;
- URLID url_id = backend_->db()->GetRowForURL(url, &row);
- ASSERT_NE(0, url_id);
-
- // The typed count should be 1.
- ASSERT_EQ(1, row.typed_count());
-
- // KEYWORD_GENERATED urls should not be added to the segment db.
- std::string segment_name = VisitSegmentDatabase::ComputeSegmentName(url);
- EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment_name));
-
- // One visit should be added.
- VisitVector visits;
- EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
- EXPECT_EQ(1U, visits.size());
-
- // But no visible visits.
- visits.clear();
- QueryOptions query_options;
- query_options.max_count = 1;
- backend_->db()->GetVisibleVisitsInRange(query_options, &visits);
- EXPECT_TRUE(visits.empty());
-
- // Going back to the same entry should not increment the typed count.
- ui::PageTransition back_transition = ui::PageTransitionFromInt(
- ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FORWARD_BACK);
- HistoryAddPageArgs back_request(url, visit_time, NULL, 0, GURL(),
- history::RedirectList(), back_transition,
- history::SOURCE_BROWSED, false);
- backend_->AddPage(back_request);
- url_id = backend_->db()->GetRowForURL(url, &row);
- ASSERT_NE(0, url_id);
- ASSERT_EQ(1, row.typed_count());
-
- // Expire the visits.
- std::set<GURL> restrict_urls;
- backend_->expire_backend()->ExpireHistoryBetween(restrict_urls,
- visit_time, Time::Now());
-
- // The visit should have been nuked.
- visits.clear();
- EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
- EXPECT_TRUE(visits.empty());
-
- // As well as the url.
- ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row));
-}
-
-TEST_F(HistoryBackendTest, ClientRedirect) {
- ASSERT_TRUE(backend_.get());
-
- int transition1;
- int transition2;
-
- // Initial transition to page A.
- GURL url_a("http://google.com/a");
- AddClientRedirect(GURL(), url_a, false, base::Time(),
- &transition1, &transition2);
- EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END);
-
- // User initiated redirect to page B.
- GURL url_b("http://google.com/b");
- AddClientRedirect(url_a, url_b, false, base::Time(),
- &transition1, &transition2);
- EXPECT_TRUE(transition1 & ui::PAGE_TRANSITION_CHAIN_END);
- EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END);
-
- // Non-user initiated redirect to page C.
- GURL url_c("http://google.com/c");
- AddClientRedirect(url_b, url_c, true, base::Time(),
- &transition1, &transition2);
- EXPECT_FALSE(transition1 & ui::PAGE_TRANSITION_CHAIN_END);
- EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END);
-}
-
-TEST_F(HistoryBackendTest, AddPagesWithDetails) {
- ASSERT_TRUE(backend_.get());
-
- // Import one non-typed URL, and two recent and one expired typed URLs.
- URLRow row1(GURL("https://news.google.com/"));
- row1.set_visit_count(1);
- row1.set_last_visit(Time::Now());
- URLRow row2(GURL("https://www.google.com/"));
- row2.set_typed_count(1);
- row2.set_last_visit(Time::Now());
- URLRow row3(GURL("https://mail.google.com/"));
- row3.set_visit_count(1);
- row3.set_typed_count(1);
- row3.set_last_visit(Time::Now() - base::TimeDelta::FromDays(7 - 1));
- URLRow row4(GURL("https://maps.google.com/"));
- row4.set_visit_count(1);
- row4.set_typed_count(1);
- row4.set_last_visit(Time::Now() - base::TimeDelta::FromDays(365 + 2));
-
- URLRows rows;
- rows.push_back(row1);
- rows.push_back(row2);
- rows.push_back(row3);
- rows.push_back(row4);
- backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
-
- // Verify that recent URLs have ended up in the main |db_|, while the already
- // expired URL has been ignored.
- URLRow stored_row1, stored_row2, stored_row3, stored_row4;
- EXPECT_NE(0, backend_->db_->GetRowForURL(row1.url(), &stored_row1));
- EXPECT_NE(0, backend_->db_->GetRowForURL(row2.url(), &stored_row2));
- EXPECT_NE(0, backend_->db_->GetRowForURL(row3.url(), &stored_row3));
- EXPECT_EQ(0, backend_->db_->GetRowForURL(row4.url(), &stored_row4));
-
- // Ensure that a notification was fired for both typed and non-typed URLs.
- // Further verify that the IDs in the notification are set to those that are
- // in effect in the main database. The InMemoryHistoryBackend relies on this
- // for caching.
- ASSERT_EQ(1, num_urls_modified_notifications());
-
- const URLRows& changed_urls = urls_modified_notifications()[0];
- EXPECT_EQ(3u, changed_urls.size());
-
- URLRows::const_iterator it_row1 =
- std::find_if(changed_urls.begin(),
- changed_urls.end(),
- history::URLRow::URLRowHasURL(row1.url()));
- ASSERT_NE(changed_urls.end(), it_row1);
- EXPECT_EQ(stored_row1.id(), it_row1->id());
-
- URLRows::const_iterator it_row2 =
- std::find_if(changed_urls.begin(),
- changed_urls.end(),
- history::URLRow::URLRowHasURL(row2.url()));
- ASSERT_NE(changed_urls.end(), it_row2);
- EXPECT_EQ(stored_row2.id(), it_row2->id());
-
- URLRows::const_iterator it_row3 =
- std::find_if(changed_urls.begin(),
- changed_urls.end(),
- history::URLRow::URLRowHasURL(row3.url()));
- ASSERT_NE(changed_urls.end(), it_row3);
- EXPECT_EQ(stored_row3.id(), it_row3->id());
-}
-
-TEST_F(HistoryBackendTest, UpdateURLs) {
- ASSERT_TRUE(backend_.get());
-
- // Add three pages directly to the database.
- URLRow row1(GURL("https://news.google.com/"));
- row1.set_visit_count(1);
- row1.set_last_visit(Time::Now());
- URLRow row2(GURL("https://maps.google.com/"));
- row2.set_visit_count(2);
- row2.set_last_visit(Time::Now());
- URLRow row3(GURL("https://www.google.com/"));
- row3.set_visit_count(3);
- row3.set_last_visit(Time::Now());
-
- backend_->db_->AddURL(row1);
- backend_->db_->AddURL(row2);
- backend_->db_->AddURL(row3);
-
- // Now create changed versions of all URLRows by incrementing their visit
- // counts, and in the meantime, also delete the second row from the database.
- URLRow altered_row1, altered_row2, altered_row3;
- backend_->db_->GetRowForURL(row1.url(), &altered_row1);
- altered_row1.set_visit_count(42);
- backend_->db_->GetRowForURL(row2.url(), &altered_row2);
- altered_row2.set_visit_count(43);
- backend_->db_->GetRowForURL(row3.url(), &altered_row3);
- altered_row3.set_visit_count(44);
-
- backend_->db_->DeleteURLRow(altered_row2.id());
-
- // Now try to update all three rows at once. The change to the second URLRow
- // should be ignored, as it is no longer present in the DB.
- URLRows rows;
- rows.push_back(altered_row1);
- rows.push_back(altered_row2);
- rows.push_back(altered_row3);
- EXPECT_EQ(2u, backend_->UpdateURLs(rows));
-
- URLRow stored_row1, stored_row3;
- EXPECT_NE(0, backend_->db_->GetRowForURL(row1.url(), &stored_row1));
- EXPECT_NE(0, backend_->db_->GetRowForURL(row3.url(), &stored_row3));
- EXPECT_EQ(altered_row1.visit_count(), stored_row1.visit_count());
- EXPECT_EQ(altered_row3.visit_count(), stored_row3.visit_count());
-
- // Ensure that a notification was fired, and further verify that the IDs in
- // the notification are set to those that are in effect in the main database.
- // The InMemoryHistoryBackend relies on this for caching.
- ASSERT_EQ(1, num_urls_modified_notifications());
-
- const URLRows& changed_urls = urls_modified_notifications()[0];
- EXPECT_EQ(2u, changed_urls.size());
-
- URLRows::const_iterator it_row1 =
- std::find_if(changed_urls.begin(),
- changed_urls.end(),
- history::URLRow::URLRowHasURL(row1.url()));
- ASSERT_NE(changed_urls.end(), it_row1);
- EXPECT_EQ(altered_row1.id(), it_row1->id());
- EXPECT_EQ(altered_row1.visit_count(), it_row1->visit_count());
-
- URLRows::const_iterator it_row3 =
- std::find_if(changed_urls.begin(),
- changed_urls.end(),
- history::URLRow::URLRowHasURL(row3.url()));
- ASSERT_NE(changed_urls.end(), it_row3);
- EXPECT_EQ(altered_row3.id(), it_row3->id());
- EXPECT_EQ(altered_row3.visit_count(), it_row3->visit_count());
-}
-
-// This verifies that a notification is fired. In-depth testing of logic should
-// be done in HistoryTest.SetTitle.
-TEST_F(HistoryBackendTest, SetPageTitleFiresNotificationWithCorrectDetails) {
- const char kTestUrlTitle[] = "Google Search";
-
- ASSERT_TRUE(backend_.get());
-
- // Add two pages, then change the title of the second one.
- URLRow row1(GURL("https://news.google.com/"));
- row1.set_typed_count(1);
- row1.set_last_visit(Time::Now());
- URLRow row2(GURL("https://www.google.com/"));
- row2.set_visit_count(2);
- row2.set_last_visit(Time::Now());
-
- URLRows rows;
- rows.push_back(row1);
- rows.push_back(row2);
- backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
-
- ClearBroadcastedNotifications();
- backend_->SetPageTitle(row2.url(), base::UTF8ToUTF16(kTestUrlTitle));
-
- // Ensure that a notification was fired, and further verify that the IDs in
- // the notification are set to those that are in effect in the main database.
- // The InMemoryHistoryBackend relies on this for caching.
- URLRow stored_row2;
- EXPECT_TRUE(backend_->GetURL(row2.url(), &stored_row2));
- ASSERT_EQ(1, num_urls_modified_notifications());
-
- const URLRows& changed_urls = urls_modified_notifications()[0];
- ASSERT_EQ(1u, changed_urls.size());
- EXPECT_EQ(base::UTF8ToUTF16(kTestUrlTitle), changed_urls[0].title());
- EXPECT_EQ(stored_row2.id(), changed_urls[0].id());
-}
-
-// There's no importer on Android.
-#if !defined(OS_ANDROID)
-TEST_F(HistoryBackendTest, ImportedFaviconsTest) {
- // Setup test data - two Urls in the history, one with favicon assigned and
- // one without.
- GURL favicon_url1("http://www.google.com/favicon.ico");
- std::vector<unsigned char> data;
- data.push_back('1');
- favicon_base::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
- favicon_url1,
- favicon_base::FAVICON,
- base::RefCountedBytes::TakeVector(&data),
- Time::Now(),
- gfx::Size());
- URLRow row1(GURL("http://www.google.com/"));
- row1.set_visit_count(1);
- row1.set_last_visit(Time::Now());
- EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
-
- URLRow row2(GURL("http://news.google.com/"));
- row2.set_visit_count(1);
- row2.set_last_visit(Time::Now());
- URLRows rows;
- rows.push_back(row1);
- rows.push_back(row2);
- backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
- URLRow url_row1, url_row2;
- EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
- EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
- EXPECT_EQ(1u, NumIconMappingsForPageURL(row1.url(), favicon_base::FAVICON));
- EXPECT_EQ(0u, NumIconMappingsForPageURL(row2.url(), favicon_base::FAVICON));
-
- // Now provide one imported favicon for both URLs already in the registry.
- // The new favicon should only be used with the URL that doesn't already have
- // a favicon.
- favicon_base::FaviconUsageDataList favicons;
- favicon_base::FaviconUsageData favicon;
- favicon.favicon_url = GURL("http://news.google.com/favicon.ico");
- favicon.png_data.push_back('2');
- favicon.urls.insert(row1.url());
- favicon.urls.insert(row2.url());
- favicons.push_back(favicon);
- backend_->SetImportedFavicons(favicons);
- EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
- EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
-
- std::vector<IconMapping> mappings;
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
- row1.url(), favicon_base::FAVICON, &mappings));
- EXPECT_EQ(1u, mappings.size());
- EXPECT_EQ(favicon1, mappings[0].icon_id);
- EXPECT_EQ(favicon_url1, mappings[0].icon_url);
-
- mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
- row2.url(), favicon_base::FAVICON, &mappings));
- EXPECT_EQ(1u, mappings.size());
- EXPECT_EQ(favicon.favicon_url, mappings[0].icon_url);
-
- // A URL should not be added to history (to store favicon), if
- // the URL is not bookmarked.
- GURL url3("http://mail.google.com");
- favicons.clear();
- favicon.favicon_url = GURL("http://mail.google.com/favicon.ico");
- favicon.png_data.push_back('3');
- favicon.urls.insert(url3);
- favicons.push_back(favicon);
- backend_->SetImportedFavicons(favicons);
- URLRow url_row3;
- EXPECT_TRUE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
-
- // If the URL is bookmarked, it should get added to history with 0 visits.
- history_client_.AddBookmark(url3);
- backend_->SetImportedFavicons(favicons);
- EXPECT_FALSE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
- EXPECT_TRUE(url_row3.visit_count() == 0);
-}
-#endif // !defined(OS_ANDROID)
-
-TEST_F(HistoryBackendTest, StripUsernamePasswordTest) {
- ASSERT_TRUE(backend_.get());
-
- GURL url("http://anyuser:anypass@www.google.com");
- GURL stripped_url("http://www.google.com");
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- // Visit the url with username, password.
- backend_->AddPageVisit(url, base::Time::Now(), 0,
- ui::PageTransitionFromInt(
- ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
- history::SOURCE_BROWSED);
-
- // Fetch the row information about stripped url from history db.
- VisitVector visits;
- URLID row_id = backend_->db_->GetRowForURL(stripped_url, NULL);
- backend_->db_->GetVisitsForURL(row_id, &visits);
-
- // Check if stripped url is stored in database.
- ASSERT_EQ(1U, visits.size());
-}
-
-TEST_F(HistoryBackendTest, AddPageVisitBackForward) {
- ASSERT_TRUE(backend_.get());
-
- GURL url("http://www.google.com");
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- // Visit the url after typing it.
- backend_->AddPageVisit(url, base::Time::Now(), 0,
- ui::PAGE_TRANSITION_TYPED,
- history::SOURCE_BROWSED);
-
- // Ensure both the typed count and visit count are 1.
- VisitVector visits;
- URLRow row;
- URLID id = backend_->db()->GetRowForURL(url, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- EXPECT_EQ(1, row.typed_count());
- EXPECT_EQ(1, row.visit_count());
-
- // Visit the url again via back/forward.
- backend_->AddPageVisit(url, base::Time::Now(), 0,
- ui::PageTransitionFromInt(
- ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FORWARD_BACK),
- history::SOURCE_BROWSED);
-
- // Ensure the typed count is still 1 but the visit count is 2.
- id = backend_->db()->GetRowForURL(url, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- EXPECT_EQ(1, row.typed_count());
- EXPECT_EQ(2, row.visit_count());
-}
-
-TEST_F(HistoryBackendTest, AddPageVisitRedirectBackForward) {
- ASSERT_TRUE(backend_.get());
-
- GURL url1("http://www.google.com");
- GURL url2("http://www.chromium.org");
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- // Visit a typed URL with a redirect.
- backend_->AddPageVisit(url1, base::Time::Now(), 0,
- ui::PAGE_TRANSITION_TYPED,
- history::SOURCE_BROWSED);
- backend_->AddPageVisit(url2, base::Time::Now(), 0,
- ui::PageTransitionFromInt(
- ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- history::SOURCE_BROWSED);
-
- // Ensure the redirected URL does not count as typed.
- VisitVector visits;
- URLRow row;
- URLID id = backend_->db()->GetRowForURL(url2, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- EXPECT_EQ(0, row.typed_count());
- EXPECT_EQ(1, row.visit_count());
-
- // Visit the redirected url again via back/forward.
- backend_->AddPageVisit(url2, base::Time::Now(), 0,
- ui::PageTransitionFromInt(
- ui::PAGE_TRANSITION_TYPED |
- ui::PAGE_TRANSITION_FORWARD_BACK |
- ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- history::SOURCE_BROWSED);
-
- // Ensure the typed count is still 1 but the visit count is 2.
- id = backend_->db()->GetRowForURL(url2, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- EXPECT_EQ(0, row.typed_count());
- EXPECT_EQ(2, row.visit_count());
-}
-
-TEST_F(HistoryBackendTest, AddPageVisitSource) {
- ASSERT_TRUE(backend_.get());
-
- GURL url("http://www.google.com");
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- // Assume visiting the url from an externsion.
- backend_->AddPageVisit(
- url, base::Time::Now(), 0, ui::PAGE_TRANSITION_TYPED,
- history::SOURCE_EXTENSION);
- // Assume the url is imported from Firefox.
- backend_->AddPageVisit(url, base::Time::Now(), 0,
- ui::PAGE_TRANSITION_TYPED,
- history::SOURCE_FIREFOX_IMPORTED);
- // Assume this url is also synced.
- backend_->AddPageVisit(url, base::Time::Now(), 0,
- ui::PAGE_TRANSITION_TYPED,
- history::SOURCE_SYNCED);
-
- // Fetch the row information about the url from history db.
- VisitVector visits;
- URLID row_id = backend_->db_->GetRowForURL(url, NULL);
- backend_->db_->GetVisitsForURL(row_id, &visits);
-
- // Check if all the visits to the url are stored in database.
- ASSERT_EQ(3U, visits.size());
- VisitSourceMap visit_sources;
- ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
- ASSERT_EQ(3U, visit_sources.size());
- int sources = 0;
- for (int i = 0; i < 3; i++) {
- switch (visit_sources[visits[i].visit_id]) {
- case history::SOURCE_EXTENSION:
- sources |= 0x1;
- break;
- case history::SOURCE_FIREFOX_IMPORTED:
- sources |= 0x2;
- break;
- case history::SOURCE_SYNCED:
- sources |= 0x4;
- default:
- break;
- }
- }
- EXPECT_EQ(0x7, sources);
-}
-
-TEST_F(HistoryBackendTest, AddPageVisitNotLastVisit) {
- ASSERT_TRUE(backend_.get());
-
- GURL url("http://www.google.com");
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- // Create visit times
- base::Time recent_time = base::Time::Now();
- base::TimeDelta visit_age = base::TimeDelta::FromDays(3);
- base::Time older_time = recent_time - visit_age;
-
- // Visit the url with recent time.
- backend_->AddPageVisit(url, recent_time, 0,
- ui::PageTransitionFromInt(
- ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
- history::SOURCE_BROWSED);
-
- // Add to the url a visit with older time (could be syncing from another
- // client, etc.).
- backend_->AddPageVisit(url, older_time, 0,
- ui::PageTransitionFromInt(
- ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
- history::SOURCE_SYNCED);
-
- // Fetch the row information about url from history db.
- VisitVector visits;
- URLRow row;
- URLID row_id = backend_->db_->GetRowForURL(url, &row);
- backend_->db_->GetVisitsForURL(row_id, &visits);
-
- // Last visit time should be the most recent time, not the most recently added
- // visit.
- ASSERT_EQ(2U, visits.size());
- ASSERT_EQ(recent_time, row.last_visit());
-}
-
-TEST_F(HistoryBackendTest, AddPageVisitFiresNotificationWithCorrectDetails) {
- ASSERT_TRUE(backend_.get());
-
- GURL url1("http://www.google.com");
- GURL url2("http://maps.google.com");
-
- // Clear all history.
- backend_->DeleteAllHistory();
- ClearBroadcastedNotifications();
-
- // Visit two distinct URLs, the second one twice.
- backend_->AddPageVisit(url1, base::Time::Now(), 0,
- ui::PAGE_TRANSITION_LINK,
- history::SOURCE_BROWSED);
- for (int i = 0; i < 2; ++i) {
- backend_->AddPageVisit(url2, base::Time::Now(), 0,
- ui::PAGE_TRANSITION_TYPED,
- history::SOURCE_BROWSED);
- }
-
- URLRow stored_row1, stored_row2;
- EXPECT_NE(0, backend_->db_->GetRowForURL(url1, &stored_row1));
- EXPECT_NE(0, backend_->db_->GetRowForURL(url2, &stored_row2));
-
- // Expect that HistoryServiceObserver::OnURLVisited has been called 3 times,
- // and that each time the URLRows have the correct URLs and IDs set.
- ASSERT_EQ(3, num_url_visited_notifications());
- EXPECT_TRUE(ui::PageTransitionCoreTypeIs(url_visited_notifications()[0].first,
- ui::PAGE_TRANSITION_LINK));
- EXPECT_EQ(stored_row1.id(), url_visited_notifications()[0].second.id());
- EXPECT_EQ(stored_row1.url(), url_visited_notifications()[0].second.url());
-
- EXPECT_TRUE(ui::PageTransitionCoreTypeIs(url_visited_notifications()[1].first,
- ui::PAGE_TRANSITION_TYPED));
- EXPECT_EQ(stored_row2.id(), url_visited_notifications()[1].second.id());
- EXPECT_EQ(stored_row2.url(), url_visited_notifications()[1].second.url());
-
- EXPECT_TRUE(ui::PageTransitionCoreTypeIs(url_visited_notifications()[2].first,
- ui::PAGE_TRANSITION_TYPED));
- EXPECT_EQ(stored_row2.id(), url_visited_notifications()[2].second.id());
- EXPECT_EQ(stored_row2.url(), url_visited_notifications()[2].second.url());
-}
-
-TEST_F(HistoryBackendTest, AddPageArgsSource) {
- ASSERT_TRUE(backend_.get());
-
- GURL url("http://testpageargs.com");
-
- // Assume this page is browsed by user.
- HistoryAddPageArgs request1(url, base::Time::Now(), NULL, 0, GURL(),
- history::RedirectList(),
- ui::PAGE_TRANSITION_KEYWORD_GENERATED,
- history::SOURCE_BROWSED, false);
- backend_->AddPage(request1);
- // Assume this page is synced.
- HistoryAddPageArgs request2(url, base::Time::Now(), NULL, 0, GURL(),
- history::RedirectList(),
- ui::PAGE_TRANSITION_LINK,
- history::SOURCE_SYNCED, false);
- backend_->AddPage(request2);
- // Assume this page is browsed again.
- HistoryAddPageArgs request3(url, base::Time::Now(), NULL, 0, GURL(),
- history::RedirectList(),
- ui::PAGE_TRANSITION_TYPED,
- history::SOURCE_BROWSED, false);
- backend_->AddPage(request3);
-
- // Three visits should be added with proper sources.
- VisitVector visits;
- URLRow row;
- URLID id = backend_->db()->GetRowForURL(url, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- ASSERT_EQ(3U, visits.size());
- VisitSourceMap visit_sources;
- ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
- ASSERT_EQ(1U, visit_sources.size());
- EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second);
-}
-
-TEST_F(HistoryBackendTest, AddVisitsSource) {
- ASSERT_TRUE(backend_.get());
-
- GURL url1("http://www.cnn.com");
- std::vector<VisitInfo> visits1, visits2;
- visits1.push_back(VisitInfo(
- Time::Now() - base::TimeDelta::FromDays(5),
- ui::PAGE_TRANSITION_LINK));
- visits1.push_back(VisitInfo(
- Time::Now() - base::TimeDelta::FromDays(1),
- ui::PAGE_TRANSITION_LINK));
- visits1.push_back(VisitInfo(
- Time::Now(), ui::PAGE_TRANSITION_LINK));
-
- GURL url2("http://www.example.com");
- visits2.push_back(VisitInfo(
- Time::Now() - base::TimeDelta::FromDays(10),
- ui::PAGE_TRANSITION_LINK));
- visits2.push_back(VisitInfo(Time::Now(), ui::PAGE_TRANSITION_LINK));
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- // Add the visits.
- backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
- backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
-
- // Verify the visits were added with their sources.
- VisitVector visits;
- URLRow row;
- URLID id = backend_->db()->GetRowForURL(url1, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- ASSERT_EQ(3U, visits.size());
- VisitSourceMap visit_sources;
- ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
- ASSERT_EQ(3U, visit_sources.size());
- for (int i = 0; i < 3; i++)
- EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_sources[visits[i].visit_id]);
- id = backend_->db()->GetRowForURL(url2, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- ASSERT_EQ(2U, visits.size());
- ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
- ASSERT_EQ(2U, visit_sources.size());
- for (int i = 0; i < 2; i++)
- EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
-}
-
-TEST_F(HistoryBackendTest, GetMostRecentVisits) {
- ASSERT_TRUE(backend_.get());
-
- GURL url1("http://www.cnn.com");
- std::vector<VisitInfo> visits1;
- visits1.push_back(VisitInfo(
- Time::Now() - base::TimeDelta::FromDays(5),
- ui::PAGE_TRANSITION_LINK));
- visits1.push_back(VisitInfo(
- Time::Now() - base::TimeDelta::FromDays(1),
- ui::PAGE_TRANSITION_LINK));
- visits1.push_back(VisitInfo(
- Time::Now(), ui::PAGE_TRANSITION_LINK));
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- // Add the visits.
- backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
-
- // Verify the visits were added with their sources.
- VisitVector visits;
- URLRow row;
- URLID id = backend_->db()->GetRowForURL(url1, &row);
- ASSERT_TRUE(backend_->db()->GetMostRecentVisitsForURL(id, 1, &visits));
- ASSERT_EQ(1U, visits.size());
- EXPECT_EQ(visits1[2].first, visits[0].visit_time);
-}
-
-TEST_F(HistoryBackendTest, RemoveVisitsTransitions) {
- ASSERT_TRUE(backend_.get());
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- GURL url1("http://www.cnn.com");
- VisitInfo typed_visit(
- Time::Now() - base::TimeDelta::FromDays(6),
- ui::PAGE_TRANSITION_TYPED);
- VisitInfo reload_visit(
- Time::Now() - base::TimeDelta::FromDays(5),
- ui::PAGE_TRANSITION_RELOAD);
- VisitInfo link_visit(
- Time::Now() - base::TimeDelta::FromDays(4),
- ui::PAGE_TRANSITION_LINK);
- std::vector<VisitInfo> visits_to_add;
- visits_to_add.push_back(typed_visit);
- visits_to_add.push_back(reload_visit);
- visits_to_add.push_back(link_visit);
-
- // Add the visits.
- backend_->AddVisits(url1, visits_to_add, history::SOURCE_SYNCED);
-
- // Verify that the various counts are what we expect.
- VisitVector visits;
- URLRow row;
- URLID id = backend_->db()->GetRowForURL(url1, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- ASSERT_EQ(3U, visits.size());
- ASSERT_EQ(1, row.typed_count());
- ASSERT_EQ(2, row.visit_count());
-
- // Now, delete the typed visit and verify that typed_count is updated.
- ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
- id = backend_->db()->GetRowForURL(url1, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- ASSERT_EQ(2U, visits.size());
- ASSERT_EQ(0, row.typed_count());
- ASSERT_EQ(1, row.visit_count());
-
- // Delete the reload visit now and verify that none of the counts have
- // changed.
- ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
- id = backend_->db()->GetRowForURL(url1, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- ASSERT_EQ(1U, visits.size());
- ASSERT_EQ(0, row.typed_count());
- ASSERT_EQ(1, row.visit_count());
-
- // Delete the last visit and verify that we delete the URL.
- ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
- ASSERT_EQ(0, backend_->db()->GetRowForURL(url1, &row));
-}
-
-TEST_F(HistoryBackendTest, RemoveVisitsSource) {
- ASSERT_TRUE(backend_.get());
-
- GURL url1("http://www.cnn.com");
- std::vector<VisitInfo> visits1, visits2;
- visits1.push_back(VisitInfo(
- Time::Now() - base::TimeDelta::FromDays(5),
- ui::PAGE_TRANSITION_LINK));
- visits1.push_back(VisitInfo(Time::Now(),
- ui::PAGE_TRANSITION_LINK));
-
- GURL url2("http://www.example.com");
- visits2.push_back(VisitInfo(
- Time::Now() - base::TimeDelta::FromDays(10),
- ui::PAGE_TRANSITION_LINK));
- visits2.push_back(VisitInfo(Time::Now(), ui::PAGE_TRANSITION_LINK));
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- // Add the visits.
- backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
- backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
-
- // Verify the visits of url1 were added.
- VisitVector visits;
- URLRow row;
- URLID id = backend_->db()->GetRowForURL(url1, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- ASSERT_EQ(2U, visits.size());
- // Remove these visits.
- ASSERT_TRUE(backend_->RemoveVisits(visits));
-
- // Now check only url2's source in visit_source table.
- VisitSourceMap visit_sources;
- ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
- ASSERT_EQ(0U, visit_sources.size());
- id = backend_->db()->GetRowForURL(url2, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
- ASSERT_EQ(2U, visits.size());
- ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
- ASSERT_EQ(2U, visit_sources.size());
- for (int i = 0; i < 2; i++)
- EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
-}
-
-// Test for migration of adding visit_source table.
-TEST_F(HistoryBackendTest, MigrationVisitSource) {
- ASSERT_TRUE(backend_.get());
- backend_->Closing();
- backend_ = NULL;
-
- base::FilePath old_history_path;
- ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
- old_history_path = old_history_path.AppendASCII("History");
- old_history_path = old_history_path.AppendASCII("HistoryNoSource");
-
- // Copy history database file to current directory so that it will be deleted
- // in Teardown.
- base::FilePath new_history_path(test_dir());
- base::DeleteFile(new_history_path, true);
- base::CreateDirectory(new_history_path);
- base::FilePath new_history_file = new_history_path.Append(kHistoryFilename);
- ASSERT_TRUE(base::CopyFile(old_history_path, new_history_file));
-
- backend_ = new HistoryBackend(new HistoryBackendTestDelegate(this),
- &history_client_);
- backend_->Init(std::string(), false,
- TestHistoryDatabaseParamsForPath(new_history_path));
- backend_->Closing();
- backend_ = NULL;
-
- // Now the database should already be migrated.
- // Check version first.
- int cur_version = HistoryDatabase::GetCurrentVersion();
- sql::Connection db;
- ASSERT_TRUE(db.Open(new_history_file));
- sql::Statement s(db.GetUniqueStatement(
- "SELECT value FROM meta WHERE key = 'version'"));
- ASSERT_TRUE(s.Step());
- int file_version = s.ColumnInt(0);
- EXPECT_EQ(cur_version, file_version);
-
- // Check visit_source table is created and empty.
- s.Assign(db.GetUniqueStatement(
- "SELECT name FROM sqlite_master WHERE name=\"visit_source\""));
- ASSERT_TRUE(s.Step());
- s.Assign(db.GetUniqueStatement("SELECT * FROM visit_source LIMIT 10"));
- EXPECT_FALSE(s.Step());
-}
-
-// Test that SetFaviconMappingsForPageAndRedirects correctly updates icon
-// mappings based on redirects, icon URLs and icon types.
-TEST_F(HistoryBackendTest, SetFaviconMappingsForPageAndRedirects) {
- // Init recent_redirects_
- const GURL url1("http://www.google.com");
- const GURL url2("http://www.google.com/m");
- URLRow url_info1(url1);
- url_info1.set_visit_count(0);
- url_info1.set_typed_count(0);
- url_info1.set_last_visit(base::Time());
- url_info1.set_hidden(false);
- backend_->db_->AddURL(url_info1);
-
- URLRow url_info2(url2);
- url_info2.set_visit_count(0);
- url_info2.set_typed_count(0);
- url_info2.set_last_visit(base::Time());
- url_info2.set_hidden(false);
- backend_->db_->AddURL(url_info2);
-
- history::RedirectList redirects;
- redirects.push_back(url2);
- redirects.push_back(url1);
- backend_->recent_redirects_.Put(url1, redirects);
-
- const GURL icon_url1("http://www.google.com/icon");
- const GURL icon_url2("http://www.google.com/icon2");
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
- bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
-
- // Add a favicon.
- backend_->SetFavicons(url1, favicon_base::FAVICON, icon_url1, bitmaps);
- EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
- EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::FAVICON));
-
- // Add one touch_icon
- backend_->SetFavicons(url1, favicon_base::TOUCH_ICON, icon_url1, bitmaps);
- EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
- EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::TOUCH_ICON));
- EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
-
- // Add one TOUCH_PRECOMPOSED_ICON
- backend_->SetFavicons(
- url1, favicon_base::TOUCH_PRECOMPOSED_ICON, icon_url1, bitmaps);
- // The touch_icon was replaced.
- EXPECT_EQ(0u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
- EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
- EXPECT_EQ(
- 1u,
- NumIconMappingsForPageURL(url1, favicon_base::TOUCH_PRECOMPOSED_ICON));
- EXPECT_EQ(
- 1u,
- NumIconMappingsForPageURL(url2, favicon_base::TOUCH_PRECOMPOSED_ICON));
-
- // Add a touch_icon.
- backend_->SetFavicons(url1, favicon_base::TOUCH_ICON, icon_url1, bitmaps);
- EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
- EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
- // The TOUCH_PRECOMPOSED_ICON was replaced.
- EXPECT_EQ(
- 0u,
- NumIconMappingsForPageURL(url1, favicon_base::TOUCH_PRECOMPOSED_ICON));
-
- // Add a different favicon.
- backend_->SetFavicons(url1, favicon_base::FAVICON, icon_url2, bitmaps);
- EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
- EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
- EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::FAVICON));
-}
-
-// Test that there is no churn in icon mappings from calling
-// SetFavicons() twice with the same |bitmaps| parameter.
-TEST_F(HistoryBackendTest, SetFaviconMappingsForPageDuplicates) {
- const GURL url("http://www.google.com/");
- const GURL icon_url("http://www.google.com/icon");
-
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
- bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
-
- backend_->SetFavicons(url, favicon_base::FAVICON, icon_url, bitmaps);
-
- std::vector<IconMapping> icon_mappings;
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
- url, favicon_base::FAVICON, &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- IconMappingID mapping_id = icon_mappings[0].mapping_id;
-
- backend_->SetFavicons(url, favicon_base::FAVICON, icon_url, bitmaps);
-
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
- url, favicon_base::FAVICON, &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
-
- // The same row in the icon_mapping table should be used for the mapping as
- // before.
- EXPECT_EQ(mapping_id, icon_mappings[0].mapping_id);
-}
-
-// Test that calling SetFavicons() with FaviconBitmapData of different pixel
-// sizes than the initially passed in FaviconBitmapData deletes the no longer
-// used favicon bitmaps.
-TEST_F(HistoryBackendTest, SetFaviconsDeleteBitmaps) {
- const GURL page_url("http://www.google.com/");
- const GURL icon_url("http://www.google.com/icon");
-
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
- bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
- backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
-
- // Test initial state.
- std::vector<IconMapping> icon_mappings;
- EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url, &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
- EXPECT_EQ(favicon_base::FAVICON, icon_mappings[0].icon_type);
- favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
-
- std::vector<FaviconBitmap> favicon_bitmaps;
- EXPECT_TRUE(GetSortedFaviconBitmaps(favicon_id, &favicon_bitmaps));
- EXPECT_EQ(2u, favicon_bitmaps.size());
- FaviconBitmapID small_bitmap_id = favicon_bitmaps[0].bitmap_id;
- EXPECT_NE(0, small_bitmap_id);
- EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmaps[0].bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmaps[0].pixel_size);
- FaviconBitmapID large_bitmap_id = favicon_bitmaps[1].bitmap_id;
- EXPECT_NE(0, large_bitmap_id);
- EXPECT_TRUE(BitmapColorEqual(SK_ColorRED, favicon_bitmaps[1].bitmap_data));
- EXPECT_EQ(kLargeSize, favicon_bitmaps[1].pixel_size);
-
- // Call SetFavicons() with bitmap data for only the large bitmap. Check that
- // the small bitmap is in fact deleted.
- bitmaps.clear();
- bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kLargeEdgeSize));
- backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
-
- scoped_refptr<base::RefCountedMemory> bitmap_data_out;
- gfx::Size pixel_size_out;
- EXPECT_FALSE(backend_->thumbnail_db_->GetFaviconBitmap(small_bitmap_id,
- NULL, NULL, &bitmap_data_out, &pixel_size_out));
- EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmap(large_bitmap_id,
- NULL, NULL, &bitmap_data_out, &pixel_size_out));
- EXPECT_TRUE(BitmapColorEqual(SK_ColorWHITE, bitmap_data_out));
- EXPECT_EQ(kLargeSize, pixel_size_out);
-
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
-
- // Notifications should have been broadcast for each call to SetFavicons().
- EXPECT_EQ(2, favicon_changed_notifications());
-}
-
-// Test updating a single favicon bitmap's data via SetFavicons.
-TEST_F(HistoryBackendTest, SetFaviconsReplaceBitmapData) {
- const GURL page_url("http://www.google.com/");
- const GURL icon_url("http://www.google.com/icon");
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
-
- // Add bitmap to the database.
- backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
-
- favicon_base::FaviconID original_favicon_id =
- backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- icon_url, favicon_base::FAVICON, NULL);
- EXPECT_NE(0, original_favicon_id);
- FaviconBitmap original_favicon_bitmap;
- EXPECT_TRUE(
- GetOnlyFaviconBitmap(original_favicon_id, &original_favicon_bitmap));
- EXPECT_TRUE(
- BitmapColorEqual(SK_ColorBLUE, original_favicon_bitmap.bitmap_data));
-
- EXPECT_EQ(1, favicon_changed_notifications());
-
- // Call SetFavicons() with completely identical data.
- bitmaps[0] = CreateBitmap(SK_ColorBLUE, kSmallEdgeSize);
- backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
-
- favicon_base::FaviconID updated_favicon_id =
- backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- icon_url, favicon_base::FAVICON, NULL);
- EXPECT_NE(0, updated_favicon_id);
- FaviconBitmap updated_favicon_bitmap;
- EXPECT_TRUE(
- GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
- EXPECT_TRUE(
- BitmapColorEqual(SK_ColorBLUE, updated_favicon_bitmap.bitmap_data));
-
- // Because the bitmap data is byte equivalent, no notifications should have
- // been broadcasted.
- EXPECT_EQ(1, favicon_changed_notifications());
-
- // Call SetFavicons() with a different bitmap of the same size.
- bitmaps[0] = CreateBitmap(SK_ColorWHITE, kSmallEdgeSize);
- backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
-
- updated_favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- icon_url, favicon_base::FAVICON, NULL);
- EXPECT_NE(0, updated_favicon_id);
- EXPECT_TRUE(
- GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
- EXPECT_TRUE(
- BitmapColorEqual(SK_ColorWHITE, updated_favicon_bitmap.bitmap_data));
-
- // There should be no churn in FaviconIDs or FaviconBitmapIds even though
- // the bitmap data changed.
- EXPECT_EQ(original_favicon_bitmap.icon_id, updated_favicon_bitmap.icon_id);
- EXPECT_EQ(original_favicon_bitmap.bitmap_id,
- updated_favicon_bitmap.bitmap_id);
-
- // A notification should have been broadcasted as the favicon bitmap data has
- // changed.
- EXPECT_EQ(2, favicon_changed_notifications());
-}
-
-// Test that if two pages share the same FaviconID, changing the favicon for
-// one page does not affect the other.
-TEST_F(HistoryBackendTest, SetFaviconsSameFaviconURLForTwoPages) {
- GURL icon_url("http://www.google.com/favicon.ico");
- GURL icon_url_new("http://www.google.com/favicon2.ico");
- GURL page_url1("http://www.google.com");
- GURL page_url2("http://www.google.ca");
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
- bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
-
- backend_->SetFavicons(page_url1, favicon_base::FAVICON, icon_url, bitmaps);
-
- std::vector<GURL> icon_urls;
- icon_urls.push_back(icon_url);
-
- std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
- backend_->UpdateFaviconMappingsAndFetch(page_url2,
- icon_urls,
- favicon_base::FAVICON,
- GetEdgeSizesSmallAndLarge(),
- &bitmap_results);
-
- // Check that the same FaviconID is mapped to both page URLs.
- std::vector<IconMapping> icon_mappings;
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
- page_url1, &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
- EXPECT_NE(0, favicon_id);
-
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
- page_url2, &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
-
- // Change the icon URL that |page_url1| is mapped to.
- bitmaps.clear();
- bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kSmallEdgeSize));
- backend_->SetFavicons(
- page_url1, favicon_base::FAVICON, icon_url_new, bitmaps);
-
- // |page_url1| should map to a new FaviconID and have valid bitmap data.
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
- page_url1, &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_url_new, icon_mappings[0].icon_url);
- EXPECT_NE(favicon_id, icon_mappings[0].icon_id);
-
- std::vector<FaviconBitmap> favicon_bitmaps;
- EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
- icon_mappings[0].icon_id, &favicon_bitmaps));
- EXPECT_EQ(1u, favicon_bitmaps.size());
-
- // |page_url2| should still map to the same FaviconID and have valid bitmap
- // data.
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
- page_url2, &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
-
- favicon_bitmaps.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(favicon_id,
- &favicon_bitmaps));
- EXPECT_EQ(2u, favicon_bitmaps.size());
-
- // A notification should have been broadcast for each call to SetFavicons()
- // and each call to UpdateFaviconMappingsAndFetch().
- EXPECT_EQ(3, favicon_changed_notifications());
-}
-
-// Test that no notifications are broadcast as a result of calling
-// UpdateFaviconMappingsAndFetch() for an icon URL which is already
-// mapped to the passed in page URL.
-TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoChange) {
- GURL page_url("http://www.google.com");
- GURL icon_url("http://www.google.com/favicon.ico");
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
-
- backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
-
- favicon_base::FaviconID icon_id =
- backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- icon_url, favicon_base::FAVICON, NULL);
- EXPECT_NE(0, icon_id);
- EXPECT_EQ(1, favicon_changed_notifications());
-
- std::vector<GURL> icon_urls;
- icon_urls.push_back(icon_url);
-
- std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
- backend_->UpdateFaviconMappingsAndFetch(page_url,
- icon_urls,
- favicon_base::FAVICON,
- GetEdgeSizesSmallAndLarge(),
- &bitmap_results);
-
- EXPECT_EQ(icon_id,
- backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- icon_url, favicon_base::FAVICON, NULL));
-
- // No notification should have been broadcast as no icon mapping, favicon,
- // or favicon bitmap was updated, added or removed.
- EXPECT_EQ(1, favicon_changed_notifications());
-}
-
-// Test repeatedly calling MergeFavicon(). |page_url| is initially not known
-// to the database.
-TEST_F(HistoryBackendTest, MergeFaviconPageURLNotInDB) {
- GURL page_url("http://www.google.com");
- GURL icon_url("http:/www.google.com/favicon.ico");
-
- std::vector<unsigned char> data;
- data.push_back('a');
- scoped_refptr<base::RefCountedBytes> bitmap_data(
- new base::RefCountedBytes(data));
-
- backend_->MergeFavicon(
- page_url, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
-
- // |page_url| should now be mapped to |icon_url| and the favicon bitmap should
- // not be expired.
- std::vector<IconMapping> icon_mappings;
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
-
- FaviconBitmap favicon_bitmap;
- EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
- EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
- EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
-
- data[0] = 'b';
- bitmap_data = new base::RefCountedBytes(data);
- backend_->MergeFavicon(
- page_url, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
-
- // |page_url| should still have a single favicon bitmap. The bitmap data
- // should be updated.
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
-
- EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
- EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);
- EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
-}
-
-// Test calling MergeFavicon() when |page_url| is known to the database.
-TEST_F(HistoryBackendTest, MergeFaviconPageURLInDB) {
- GURL page_url("http://www.google.com");
- GURL icon_url1("http:/www.google.com/favicon.ico");
- GURL icon_url2("http://www.google.com/favicon2.ico");
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
-
- backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url1, bitmaps);
-
- // Test initial state.
- std::vector<IconMapping> icon_mappings;
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
-
- FaviconBitmap favicon_bitmap;
- EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
- EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
- EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
-
- EXPECT_EQ(1, favicon_changed_notifications());
-
- // 1) Merge identical favicon bitmap.
- std::vector<unsigned char> data;
- gfx::PNGCodec::EncodeBGRASkBitmap(bitmaps[0], false, &data);
- scoped_refptr<base::RefCountedBytes> bitmap_data(
- new base::RefCountedBytes(data));
- backend_->MergeFavicon(
- page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kSmallSize);
-
- // All the data should stay the same and no notifications should have been
- // sent.
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
-
- EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
- EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
- EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
-
- EXPECT_EQ(1, favicon_changed_notifications());
-
- // 2) Merge favicon bitmap of the same size.
- data.clear();
- data.push_back('b');
- bitmap_data = new base::RefCountedBytes(data);
- backend_->MergeFavicon(
- page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kSmallSize);
-
- // The small favicon bitmap at |icon_url1| should be overwritten.
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
-
- EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
- EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);
- EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
-
- // 3) Merge favicon for the same icon URL, but a pixel size for which there is
- // no favicon bitmap.
- data[0] = 'c';
- bitmap_data = new base::RefCountedBytes(data);
- backend_->MergeFavicon(
- page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kTinySize);
-
- // A new favicon bitmap should be created and the preexisting favicon bitmap
- // ('b') should be expired.
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
-
- std::vector<FaviconBitmap> favicon_bitmaps;
- EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
- &favicon_bitmaps));
- EXPECT_NE(base::Time(), favicon_bitmaps[0].last_updated);
- EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
- EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
- EXPECT_EQ(base::Time(), favicon_bitmaps[1].last_updated);
- EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmaps[1].bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
-
- // 4) Merge favicon for an icon URL different from the icon URLs already
- // mapped to page URL.
- data[0] = 'd';
- bitmap_data = new base::RefCountedBytes(data);
- backend_->MergeFavicon(
- page_url, icon_url2, favicon_base::FAVICON, bitmap_data, kSmallSize);
-
- // The existing favicon bitmaps should be copied over to the newly created
- // favicon at |icon_url2|. |page_url| should solely be mapped to |icon_url2|.
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_url2, icon_mappings[0].icon_url);
-
- favicon_bitmaps.clear();
- EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
- &favicon_bitmaps));
- EXPECT_EQ(base::Time(), favicon_bitmaps[0].last_updated);
- EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
- EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
- // The favicon being merged should take precedence over the preexisting
- // favicon bitmaps.
- EXPECT_NE(base::Time(), favicon_bitmaps[1].last_updated);
- EXPECT_TRUE(BitmapDataEqual('d', favicon_bitmaps[1].bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
-
- // A notification should have been broadcast for each call to SetFavicons()
- // and MergeFavicon().
- EXPECT_EQ(4, favicon_changed_notifications());
-}
-
-// Test calling MergeFavicon() when |icon_url| is known to the database but not
-// mapped to |page_url|.
-TEST_F(HistoryBackendTest, MergeFaviconIconURLMappedToDifferentPageURL) {
- GURL page_url1("http://www.google.com");
- GURL page_url2("http://news.google.com");
- GURL page_url3("http://maps.google.com");
- GURL icon_url("http:/www.google.com/favicon.ico");
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
-
- backend_->SetFavicons(page_url1, favicon_base::FAVICON, icon_url, bitmaps);
-
- // Test initial state.
- std::vector<IconMapping> icon_mappings;
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
-
- FaviconBitmap favicon_bitmap;
- EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
- EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
- EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
-
- // 1) Merge in an identical favicon bitmap data but for a different page URL.
- std::vector<unsigned char> data;
- gfx::PNGCodec::EncodeBGRASkBitmap(bitmaps[0], false, &data);
- scoped_refptr<base::RefCountedBytes> bitmap_data(
- new base::RefCountedBytes(data));
-
- backend_->MergeFavicon(
- page_url2, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
-
- favicon_base::FaviconID favicon_id =
- backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- icon_url, favicon_base::FAVICON, NULL);
- EXPECT_NE(0, favicon_id);
-
- EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
- EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
- EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
-
- // 2) Merging a favicon bitmap with different bitmap data for the same icon
- // URL should overwrite the small favicon bitmap at |icon_url|.
- data.clear();
- data.push_back('b');
- bitmap_data = new base::RefCountedBytes(data);
- backend_->MergeFavicon(
- page_url3, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
-
- favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
- icon_url, favicon_base::FAVICON, NULL);
- EXPECT_NE(0, favicon_id);
-
- EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
- EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);
- EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
- EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
-
- // |icon_url| should be mapped to all three page URLs.
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
-
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url2,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
-
- icon_mappings.clear();
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url3,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
-
- // A notification should have been broadcast for each call to SetFavicons()
- // and MergeFavicon().
- EXPECT_EQ(3, favicon_changed_notifications());
-}
-
-// Test that MergeFavicon() does not add more than
-// |kMaxFaviconBitmapsPerIconURL| to a favicon.
-TEST_F(HistoryBackendTest, MergeFaviconMaxFaviconBitmapsPerIconURL) {
- GURL page_url("http://www.google.com");
- std::string icon_url_string("http://www.google.com/favicon.ico");
- size_t replace_index = icon_url_string.size() - 1;
-
- std::vector<unsigned char> data;
- data.push_back('a');
- scoped_refptr<base::RefCountedMemory> bitmap_data =
- base::RefCountedBytes::TakeVector(&data);
-
- int pixel_size = 1;
- for (size_t i = 0; i < kMaxFaviconBitmapsPerIconURL + 1; ++i) {
- icon_url_string[replace_index] = '0' + i;
- GURL icon_url(icon_url_string);
-
- backend_->MergeFavicon(page_url,
- icon_url,
- favicon_base::FAVICON,
- bitmap_data,
- gfx::Size(pixel_size, pixel_size));
- ++pixel_size;
- }
-
- // There should be a single favicon mapped to |page_url| with exactly
- // kMaxFaviconBitmapsPerIconURL favicon bitmaps.
- std::vector<IconMapping> icon_mappings;
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- std::vector<FaviconBitmap> favicon_bitmaps;
- EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
- icon_mappings[0].icon_id, &favicon_bitmaps));
- EXPECT_EQ(kMaxFaviconBitmapsPerIconURL, favicon_bitmaps.size());
-}
-
-// Tests that the favicon set by MergeFavicon() shows up in the result of
-// GetFaviconsForURL().
-TEST_F(HistoryBackendTest, MergeFaviconShowsUpInGetFaviconsForURLResult) {
- GURL page_url("http://www.google.com");
- GURL icon_url("http://www.google.com/favicon.ico");
- GURL merged_icon_url("http://wwww.google.com/favicon2.ico");
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
- bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
-
- // Set some preexisting favicons for |page_url|.
- backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
-
- // Merge small favicon.
- std::vector<unsigned char> data;
- data.push_back('c');
- scoped_refptr<base::RefCountedBytes> bitmap_data(
- new base::RefCountedBytes(data));
- backend_->MergeFavicon(page_url,
- merged_icon_url,
- favicon_base::FAVICON,
- bitmap_data,
- kSmallSize);
-
- // Request favicon bitmaps for both 1x and 2x to simulate request done by
- // BookmarkModel::GetFavicon().
- std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
- backend_->GetFaviconsForURL(page_url,
- favicon_base::FAVICON,
- GetEdgeSizesSmallAndLarge(),
- &bitmap_results);
-
- EXPECT_EQ(2u, bitmap_results.size());
- const favicon_base::FaviconRawBitmapResult& first_result = bitmap_results[0];
- const favicon_base::FaviconRawBitmapResult& result =
- (first_result.pixel_size == kSmallSize) ? first_result
- : bitmap_results[1];
- EXPECT_TRUE(BitmapDataEqual('c', result.bitmap_data));
-}
-
-// Tests GetFaviconsForURL with icon_types priority,
-TEST_F(HistoryBackendTest, TestGetFaviconsForURLWithIconTypesPriority) {
- GURL page_url("http://www.google.com");
- GURL icon_url("http://www.google.com/favicon.ico");
- GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");
-
- std::vector<SkBitmap> favicon_bitmaps;
- favicon_bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));
- favicon_bitmaps.push_back(CreateBitmap(SK_ColorRED, 32));
-
- std::vector<SkBitmap> touch_bitmaps;
- touch_bitmaps.push_back(CreateBitmap(SK_ColorWHITE, 64));
-
- // Set some preexisting favicons for |page_url|.
- backend_->SetFavicons(
- page_url, favicon_base::FAVICON, icon_url, favicon_bitmaps);
- backend_->SetFavicons(
- page_url, favicon_base::TOUCH_ICON, touch_icon_url, touch_bitmaps);
-
- favicon_base::FaviconRawBitmapResult result;
- std::vector<int> icon_types;
- icon_types.push_back(favicon_base::FAVICON);
- icon_types.push_back(favicon_base::TOUCH_ICON);
-
- backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
-
- // Verify the result icon is 32x32 favicon.
- EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
- EXPECT_EQ(favicon_base::FAVICON, result.icon_type);
-
- // Change Minimal size to 32x32 and verify the 64x64 touch icon returned.
- backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result);
- EXPECT_EQ(gfx::Size(64, 64), result.pixel_size);
- EXPECT_EQ(favicon_base::TOUCH_ICON, result.icon_type);
-}
-
-// Test the the first types of icon is returned if its size equal to the
-// second types icon.
-TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFavicon) {
- GURL page_url("http://www.google.com");
- GURL icon_url("http://www.google.com/favicon.ico");
- GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");
-
- std::vector<SkBitmap> favicon_bitmaps;
- favicon_bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));
- favicon_bitmaps.push_back(CreateBitmap(SK_ColorRED, 32));
-
- std::vector<SkBitmap> touch_bitmaps;
- touch_bitmaps.push_back(CreateBitmap(SK_ColorWHITE, 32));
-
- // Set some preexisting favicons for |page_url|.
- backend_->SetFavicons(
- page_url, favicon_base::FAVICON, icon_url, favicon_bitmaps);
- backend_->SetFavicons(
- page_url, favicon_base::TOUCH_ICON, touch_icon_url, touch_bitmaps);
-
- favicon_base::FaviconRawBitmapResult result;
- std::vector<int> icon_types;
- icon_types.push_back(favicon_base::FAVICON);
- icon_types.push_back(favicon_base::TOUCH_ICON);
-
- backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
-
- // Verify the result icon is 32x32 favicon.
- EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
- EXPECT_EQ(favicon_base::FAVICON, result.icon_type);
-
- // Change minimal size to 32x32 and verify the 32x32 favicon returned.
- favicon_base::FaviconRawBitmapResult result1;
- backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result1);
- EXPECT_EQ(gfx::Size(32, 32), result1.pixel_size);
- EXPECT_EQ(favicon_base::FAVICON, result1.icon_type);
-}
-
-// Test the favicon is returned if its size is smaller than minimal size,
-// because it is only one available.
-TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFaviconEvenItSmaller) {
- GURL page_url("http://www.google.com");
- GURL icon_url("http://www.google.com/favicon.ico");
-
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));
-
- // Set preexisting favicons for |page_url|.
- backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
-
- favicon_base::FaviconRawBitmapResult result;
- std::vector<int> icon_types;
- icon_types.push_back(favicon_base::FAVICON);
- icon_types.push_back(favicon_base::TOUCH_ICON);
-
- backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result);
-
- // Verify 16x16 icon is returned, even it small than minimal_size.
- EXPECT_EQ(gfx::Size(16, 16), result.pixel_size);
- EXPECT_EQ(favicon_base::FAVICON, result.icon_type);
-}
-
-// Test UpdateFaviconMapingsAndFetch() when multiple icon types are passed in.
-TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchMultipleIconTypes) {
- GURL page_url1("http://www.google.com");
- GURL page_url2("http://news.google.com");
- GURL page_url3("http://mail.google.com");
- GURL icon_urla("http://www.google.com/favicon1.ico");
- GURL icon_urlb("http://www.google.com/favicon2.ico");
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
-
- // |page_url1| is mapped to |icon_urla| which if of type TOUCH_ICON.
- backend_->SetFavicons(
- page_url1, favicon_base::TOUCH_ICON, icon_urla, bitmaps);
-
- // |page_url2| is mapped to |icon_urlb| which is of type
- // TOUCH_PRECOMPOSED_ICON.
- backend_->SetFavicons(
- page_url2, favicon_base::TOUCH_PRECOMPOSED_ICON, icon_urlb, bitmaps);
-
- std::vector<GURL> icon_urls;
- icon_urls.push_back(icon_urla);
- icon_urls.push_back(icon_urlb);
-
- std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
- backend_->UpdateFaviconMappingsAndFetch(
- page_url3,
- icon_urls,
- (favicon_base::TOUCH_ICON | favicon_base::TOUCH_PRECOMPOSED_ICON),
- GetEdgeSizesSmallAndLarge(),
- &bitmap_results);
-
- // |page_url1| and |page_url2| should still be mapped to the same icon URLs.
- std::vector<IconMapping> icon_mappings;
- EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
- &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_urla, icon_mappings[0].icon_url);
- EXPECT_EQ(favicon_base::TOUCH_ICON, icon_mappings[0].icon_type);
-
- icon_mappings.clear();
- EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url2, &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
- EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
-
- // |page_url3| should be mapped only to |icon_urlb| as TOUCH_PRECOMPOSED_ICON
- // is the largest IconType.
- icon_mappings.clear();
- EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url3, &icon_mappings));
- EXPECT_EQ(1u, icon_mappings.size());
- EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
- EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
-}
-
-// Test the results of GetFaviconsFromDB() when there are no found favicons.
-TEST_F(HistoryBackendTest, GetFaviconsFromDBEmpty) {
- const GURL page_url("http://www.google.com/");
-
- std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
- EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url,
- favicon_base::FAVICON,
- GetEdgeSizesSmallAndLarge(),
- &bitmap_results));
- EXPECT_TRUE(bitmap_results.empty());
-}
-
-// Test the results of GetFaviconsFromDB() when there are matching favicons
-// but there are no associated favicon bitmaps.
-TEST_F(HistoryBackendTest, GetFaviconsFromDBNoFaviconBitmaps) {
- const GURL page_url("http://www.google.com/");
- const GURL icon_url("http://www.google.com/icon1");
-
- favicon_base::FaviconID icon_id =
- backend_->thumbnail_db_->AddFavicon(icon_url, favicon_base::FAVICON);
- EXPECT_NE(0, icon_id);
- EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
-
- std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
- EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url,
- favicon_base::FAVICON,
- GetEdgeSizesSmallAndLarge(),
- &bitmap_results_out));
- EXPECT_TRUE(bitmap_results_out.empty());
-}
-
-// Test that GetFaviconsFromDB() returns results for the bitmaps which most
-// closely match the passed in the desired pixel sizes.
-TEST_F(HistoryBackendTest, GetFaviconsFromDBSelectClosestMatch) {
- const GURL page_url("http://www.google.com/");
- const GURL icon_url("http://www.google.com/icon1");
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kTinyEdgeSize));
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
- bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
-
- backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
-
- std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
- EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
- favicon_base::FAVICON,
- GetEdgeSizesSmallAndLarge(),
- &bitmap_results_out));
-
- // The bitmap data for the small and large bitmaps should be returned as their
- // sizes match exactly.
- EXPECT_EQ(2u, bitmap_results_out.size());
- // No required order for results.
- if (bitmap_results_out[0].pixel_size == kLargeSize) {
- favicon_base::FaviconRawBitmapResult tmp_result = bitmap_results_out[0];
- bitmap_results_out[0] = bitmap_results_out[1];
- bitmap_results_out[1] = tmp_result;
- }
-
- EXPECT_FALSE(bitmap_results_out[0].expired);
- EXPECT_TRUE(
- BitmapColorEqual(SK_ColorBLUE, bitmap_results_out[0].bitmap_data));
- EXPECT_EQ(kSmallSize, bitmap_results_out[0].pixel_size);
- EXPECT_EQ(icon_url, bitmap_results_out[0].icon_url);
- EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[0].icon_type);
-
- EXPECT_FALSE(bitmap_results_out[1].expired);
- EXPECT_TRUE(BitmapColorEqual(SK_ColorRED, bitmap_results_out[1].bitmap_data));
- EXPECT_EQ(kLargeSize, bitmap_results_out[1].pixel_size);
- EXPECT_EQ(icon_url, bitmap_results_out[1].icon_url);
- EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[1].icon_type);
-}
-
-// Test the results of GetFaviconsFromDB() when called with different
-// |icon_types|.
-TEST_F(HistoryBackendTest, GetFaviconsFromDBIconType) {
- const GURL page_url("http://www.google.com/");
- const GURL icon_url1("http://www.google.com/icon1.png");
- const GURL icon_url2("http://www.google.com/icon2.png");
- std::vector<SkBitmap> bitmaps;
- bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
-
- std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
- backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url1, bitmaps);
- backend_->SetFavicons(page_url, favicon_base::TOUCH_ICON, icon_url2, bitmaps);
-
- std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
- EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
- favicon_base::FAVICON,
- GetEdgeSizesSmallAndLarge(),
- &bitmap_results_out));
-
- EXPECT_EQ(1u, bitmap_results_out.size());
- EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[0].icon_type);
- EXPECT_EQ(icon_url1, bitmap_results_out[0].icon_url);
-
- bitmap_results_out.clear();
- EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
- favicon_base::TOUCH_ICON,
- GetEdgeSizesSmallAndLarge(),
- &bitmap_results_out));
-
- EXPECT_EQ(1u, bitmap_results_out.size());
- EXPECT_EQ(favicon_base::TOUCH_ICON, bitmap_results_out[0].icon_type);
- EXPECT_EQ(icon_url2, bitmap_results_out[0].icon_url);
-}
-
-// Test that GetFaviconsFromDB() correctly sets the expired flag for bitmap
-// reults.
-TEST_F(HistoryBackendTest, GetFaviconsFromDBExpired) {
- const GURL page_url("http://www.google.com/");
- const GURL icon_url("http://www.google.com/icon.png");
-
- std::vector<unsigned char> data;
- data.push_back('a');
- scoped_refptr<base::RefCountedBytes> bitmap_data(
- base::RefCountedBytes::TakeVector(&data));
- base::Time last_updated = base::Time::FromTimeT(0);
- favicon_base::FaviconID icon_id = backend_->thumbnail_db_->AddFavicon(
- icon_url, favicon_base::FAVICON, bitmap_data, last_updated, kSmallSize);
- EXPECT_NE(0, icon_id);
- EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
-
- std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
- EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
- favicon_base::FAVICON,
- GetEdgeSizesSmallAndLarge(),
- &bitmap_results_out));
-
- EXPECT_EQ(1u, bitmap_results_out.size());
- EXPECT_TRUE(bitmap_results_out[0].expired);
-}
-
-// Check that UpdateFaviconMappingsAndFetch() call back to the UI when there is
-// no valid thumbnail database.
-TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoDB) {
- // Make the thumbnail database invalid.
- backend_->thumbnail_db_.reset();
-
- std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
-
- backend_->UpdateFaviconMappingsAndFetch(GURL(),
- std::vector<GURL>(),
- favicon_base::FAVICON,
- GetEdgeSizesSmallAndLarge(),
- &bitmap_results);
-
- EXPECT_TRUE(bitmap_results.empty());
-}
-
-TEST_F(HistoryBackendTest, QueryFilteredURLs) {
- const char* google = "http://www.google.com/";
- const char* yahoo = "http://www.yahoo.com/";
- const char* yahoo_sports = "http://sports.yahoo.com/";
- const char* yahoo_sports_with_article1 =
- "http://sports.yahoo.com/article1.htm";
- const char* yahoo_sports_with_article2 =
- "http://sports.yahoo.com/article2.htm";
- const char* yahoo_sports_soccer = "http://sports.yahoo.com/soccer";
- const char* apple = "http://www.apple.com/";
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- Time tested_time = Time::Now().LocalMidnight() +
- base::TimeDelta::FromHours(4);
- base::TimeDelta half_an_hour = base::TimeDelta::FromMinutes(30);
- base::TimeDelta one_hour = base::TimeDelta::FromHours(1);
- base::TimeDelta one_day = base::TimeDelta::FromDays(1);
-
- const ui::PageTransition kTypedTransition =
- ui::PAGE_TRANSITION_TYPED;
- const ui::PageTransition kKeywordGeneratedTransition =
- ui::PAGE_TRANSITION_KEYWORD_GENERATED;
-
- const char* redirect_sequence[2];
- redirect_sequence[1] = NULL;
-
- redirect_sequence[0] = google;
- AddRedirectChainWithTransitionAndTime(
- redirect_sequence, 0, kTypedTransition,
- tested_time - one_day - half_an_hour * 2);
- AddRedirectChainWithTransitionAndTime(
- redirect_sequence, 0,
- kTypedTransition, tested_time - one_day);
- AddRedirectChainWithTransitionAndTime(
- redirect_sequence, 0,
- kTypedTransition, tested_time - half_an_hour / 2);
- AddRedirectChainWithTransitionAndTime(
- redirect_sequence, 0,
- kTypedTransition, tested_time);
-
- // Add a visit with a transition that will make sure that no segment gets
- // created for this page (so the subsequent entries will have different URLIDs
- // and SegmentIDs).
- redirect_sequence[0] = apple;
- AddRedirectChainWithTransitionAndTime(
- redirect_sequence, 0, kKeywordGeneratedTransition,
- tested_time - one_day + one_hour * 6);
-
- redirect_sequence[0] = yahoo;
- AddRedirectChainWithTransitionAndTime(
- redirect_sequence, 0, kTypedTransition,
- tested_time - one_day + half_an_hour);
- AddRedirectChainWithTransitionAndTime(
- redirect_sequence, 0, kTypedTransition,
- tested_time - one_day + half_an_hour * 2);
-
- redirect_sequence[0] = yahoo_sports;
- AddRedirectChainWithTransitionAndTime(
- redirect_sequence, 0, kTypedTransition,
- tested_time - one_day - half_an_hour * 2);
- AddRedirectChainWithTransitionAndTime(
- redirect_sequence, 0, kTypedTransition,
- tested_time - one_day);
- int transition1, transition2;
- AddClientRedirect(GURL(yahoo_sports), GURL(yahoo_sports_with_article1), false,
- tested_time - one_day + half_an_hour,
- &transition1, &transition2);
- AddClientRedirect(GURL(yahoo_sports_with_article1),
- GURL(yahoo_sports_with_article2),
- false,
- tested_time - one_day + half_an_hour * 2,
- &transition1, &transition2);
-
- redirect_sequence[0] = yahoo_sports_soccer;
- AddRedirectChainWithTransitionAndTime(redirect_sequence, 0,
- kTypedTransition,
- tested_time - half_an_hour);
- backend_->Commit();
-
- VisitFilter filter;
- FilteredURLList filtered_list;
- // Time limit is |tested_time| +/- 45 min.
- base::TimeDelta three_quarters_of_an_hour = base::TimeDelta::FromMinutes(45);
- filter.SetFilterTime(tested_time);
- filter.SetFilterWidth(three_quarters_of_an_hour);
- backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
-
- ASSERT_EQ(4U, filtered_list.size());
- EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
- EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
- EXPECT_EQ(std::string(yahoo), filtered_list[2].url.spec());
- EXPECT_EQ(std::string(yahoo_sports), filtered_list[3].url.spec());
-
- // Time limit is between |tested_time| and |tested_time| + 2 hours.
- filter.SetFilterTime(tested_time + one_hour);
- filter.SetFilterWidth(one_hour);
- backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
-
- ASSERT_EQ(3U, filtered_list.size());
- EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
- EXPECT_EQ(std::string(yahoo), filtered_list[1].url.spec());
- EXPECT_EQ(std::string(yahoo_sports), filtered_list[2].url.spec());
-
- // Time limit is between |tested_time| - 2 hours and |tested_time|.
- filter.SetFilterTime(tested_time - one_hour);
- filter.SetFilterWidth(one_hour);
- backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
-
- ASSERT_EQ(3U, filtered_list.size());
- EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
- EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
- EXPECT_EQ(std::string(yahoo_sports), filtered_list[2].url.spec());
-
- filter.ClearFilters();
- base::Time::Exploded exploded_time;
- tested_time.LocalExplode(&exploded_time);
-
- // Today.
- filter.SetFilterTime(tested_time);
- filter.SetDayOfTheWeekFilter(static_cast<int>(exploded_time.day_of_week));
- backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
-
- ASSERT_EQ(2U, filtered_list.size());
- EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
- EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
-
- // Today + time limit - only yahoo_sports_soccer should fit.
- filter.SetFilterTime(tested_time - base::TimeDelta::FromMinutes(40));
- filter.SetFilterWidth(base::TimeDelta::FromMinutes(20));
- backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
-
- ASSERT_EQ(1U, filtered_list.size());
- EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[0].url.spec());
-
- // Make sure we get debug data if we request it.
- filter.SetFilterTime(tested_time);
- filter.SetFilterWidth(one_hour * 2);
- backend_->QueryFilteredURLs(100, filter, true, &filtered_list);
-
- // If the SegmentID is used by QueryFilteredURLs when generating the debug
- // data instead of the URLID, the |total_visits| for the |yahoo_sports_soccer|
- // entry will be zero instead of 1.
- ASSERT_GE(filtered_list.size(), 2U);
- EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
- EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
- EXPECT_EQ(4U, filtered_list[0].extended_info.total_visits);
- EXPECT_EQ(1U, filtered_list[1].extended_info.total_visits);
-}
-
-TEST_F(HistoryBackendTest, UpdateVisitDuration) {
- // This unit test will test adding and deleting visit details information.
- ASSERT_TRUE(backend_.get());
-
- GURL url1("http://www.cnn.com");
- std::vector<VisitInfo> visit_info1, visit_info2;
- Time start_ts = Time::Now() - base::TimeDelta::FromDays(5);
- Time end_ts = start_ts + base::TimeDelta::FromDays(2);
- visit_info1.push_back(VisitInfo(start_ts, ui::PAGE_TRANSITION_LINK));
-
- GURL url2("http://www.example.com");
- visit_info2.push_back(VisitInfo(Time::Now() - base::TimeDelta::FromDays(10),
- ui::PAGE_TRANSITION_LINK));
-
- // Clear all history.
- backend_->DeleteAllHistory();
-
- // Add the visits.
- backend_->AddVisits(url1, visit_info1, history::SOURCE_BROWSED);
- backend_->AddVisits(url2, visit_info2, history::SOURCE_BROWSED);
-
- // Verify the entries for both visits were added in visit_details.
- VisitVector visits1, visits2;
- URLRow row;
- URLID url_id1 = backend_->db()->GetRowForURL(url1, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
- ASSERT_EQ(1U, visits1.size());
- EXPECT_EQ(0, visits1[0].visit_duration.ToInternalValue());
-
- URLID url_id2 = backend_->db()->GetRowForURL(url2, &row);
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id2, &visits2));
- ASSERT_EQ(1U, visits2.size());
- EXPECT_EQ(0, visits2[0].visit_duration.ToInternalValue());
-
- // Update the visit to cnn.com.
- backend_->UpdateVisitDuration(visits1[0].visit_id, end_ts);
-
- // Check the duration for visiting cnn.com was correctly updated.
- ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
- ASSERT_EQ(1U, visits1.size());
- base::TimeDelta expected_duration = end_ts - start_ts;
- EXPECT_EQ(expected_duration.ToInternalValue(),
- visits1[0].visit_duration.ToInternalValue());
-
- // Remove the visit to cnn.com.
- ASSERT_TRUE(backend_->RemoveVisits(visits1));
-}
-
-// Test for migration of adding visit_duration column.
-TEST_F(HistoryBackendTest, MigrationVisitDuration) {
- ASSERT_TRUE(backend_.get());
- backend_->Closing();
- backend_ = NULL;
-
- base::FilePath old_history_path, old_history;
- ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
- old_history_path = old_history_path.AppendASCII("History");
- old_history = old_history_path.AppendASCII("HistoryNoDuration");
-
- // Copy history database file to current directory so that it will be deleted
- // in Teardown.
- base::FilePath new_history_path(test_dir());
- base::DeleteFile(new_history_path, true);
- base::CreateDirectory(new_history_path);
- base::FilePath new_history_file = new_history_path.Append(kHistoryFilename);
- ASSERT_TRUE(base::CopyFile(old_history, new_history_file));
-
- backend_ = new HistoryBackend(new HistoryBackendTestDelegate(this),
- &history_client_);
- backend_->Init(std::string(), false,
- TestHistoryDatabaseParamsForPath(new_history_path));
- backend_->Closing();
- backend_ = NULL;
-
- // Now the history database should already be migrated.
-
- // Check version in history database first.
- int cur_version = HistoryDatabase::GetCurrentVersion();
- sql::Connection db;
- ASSERT_TRUE(db.Open(new_history_file));
- sql::Statement s(db.GetUniqueStatement(
- "SELECT value FROM meta WHERE key = 'version'"));
- ASSERT_TRUE(s.Step());
- int file_version = s.ColumnInt(0);
- EXPECT_EQ(cur_version, file_version);
-
- // Check visit_duration column in visits table is created and set to 0.
- s.Assign(db.GetUniqueStatement(
- "SELECT visit_duration FROM visits LIMIT 1"));
- ASSERT_TRUE(s.Step());
- EXPECT_EQ(0, s.ColumnInt(0));
-}
-
-TEST_F(HistoryBackendTest, AddPageNoVisitForBookmark) {
- ASSERT_TRUE(backend_.get());
-
- GURL url("http://www.google.com");
- base::string16 title(base::UTF8ToUTF16("Bookmark title"));
- backend_->AddPageNoVisitForBookmark(url, title);
-
- URLRow row;
- backend_->GetURL(url, &row);
- EXPECT_EQ(url, row.url());
- EXPECT_EQ(title, row.title());
- EXPECT_EQ(0, row.visit_count());
-
- backend_->DeleteURL(url);
- backend_->AddPageNoVisitForBookmark(url, base::string16());
- backend_->GetURL(url, &row);
- EXPECT_EQ(url, row.url());
- EXPECT_EQ(base::UTF8ToUTF16(url.spec()), row.title());
- EXPECT_EQ(0, row.visit_count());
-}
-
-TEST_F(HistoryBackendTest, ExpireHistoryForTimes) {
- ASSERT_TRUE(backend_.get());
-
- HistoryAddPageArgs args[10];
- for (size_t i = 0; i < arraysize(args); ++i) {
- args[i].url = GURL("http://example" +
- std::string((i % 2 == 0 ? ".com" : ".net")));
- args[i].time = base::Time::FromInternalValue(i);
- backend_->AddPage(args[i]);
- }
- EXPECT_EQ(base::Time(), backend_->GetFirstRecordedTimeForTest());
-
- URLRow row;
- for (size_t i = 0; i < arraysize(args); ++i) {
- EXPECT_TRUE(backend_->GetURL(args[i].url, &row));
- }
-
- std::set<base::Time> times;
- times.insert(args[5].time);
- backend_->ExpireHistoryForTimes(times,
- base::Time::FromInternalValue(2),
- base::Time::FromInternalValue(8));
-
- EXPECT_EQ(base::Time::FromInternalValue(0),
- backend_->GetFirstRecordedTimeForTest());
-
- // Visits to http://example.com are untouched.
- VisitVector visit_vector;
- EXPECT_TRUE(backend_->GetVisitsForURL(
- backend_->db_->GetRowForURL(GURL("http://example.com"), NULL),
- &visit_vector));
- ASSERT_EQ(5u, visit_vector.size());
- EXPECT_EQ(base::Time::FromInternalValue(0), visit_vector[0].visit_time);
- EXPECT_EQ(base::Time::FromInternalValue(2), visit_vector[1].visit_time);
- EXPECT_EQ(base::Time::FromInternalValue(4), visit_vector[2].visit_time);
- EXPECT_EQ(base::Time::FromInternalValue(6), visit_vector[3].visit_time);
- EXPECT_EQ(base::Time::FromInternalValue(8), visit_vector[4].visit_time);
-
- // Visits to http://example.net between [2,8] are removed.
- visit_vector.clear();
- EXPECT_TRUE(backend_->GetVisitsForURL(
- backend_->db_->GetRowForURL(GURL("http://example.net"), NULL),
- &visit_vector));
- ASSERT_EQ(2u, visit_vector.size());
- EXPECT_EQ(base::Time::FromInternalValue(1), visit_vector[0].visit_time);
- EXPECT_EQ(base::Time::FromInternalValue(9), visit_vector[1].visit_time);
-
- EXPECT_EQ(base::Time::FromInternalValue(0),
- backend_->GetFirstRecordedTimeForTest());
-}
-
-TEST_F(HistoryBackendTest, ExpireHistory) {
- ASSERT_TRUE(backend_.get());
- // Since history operations are dependent on the local timezone, make all
- // entries relative to a fixed, local reference time.
- base::Time reference_time = base::Time::UnixEpoch().LocalMidnight() +
- base::TimeDelta::FromHours(12);
-
- // Insert 4 entries into the database.
- HistoryAddPageArgs args[4];
- for (size_t i = 0; i < arraysize(args); ++i) {
- args[i].url = GURL("http://example" + base::IntToString(i) + ".com");
- args[i].time = reference_time + base::TimeDelta::FromDays(i);
- backend_->AddPage(args[i]);
- }
-
- URLRow url_rows[4];
- for (unsigned int i = 0; i < arraysize(args); ++i)
- ASSERT_TRUE(backend_->GetURL(args[i].url, &url_rows[i]));
-
- std::vector<ExpireHistoryArgs> expire_list;
- VisitVector visits;
-
- // Passing an empty map should be a no-op.
- backend_->ExpireHistory(expire_list);
- backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
- EXPECT_EQ(4U, visits.size());
-
- // Trying to delete an unknown URL with the time of the first visit should
- // also be a no-op.
- expire_list.resize(expire_list.size() + 1);
- expire_list[0].SetTimeRangeForOneDay(args[0].time);
- expire_list[0].urls.insert(GURL("http://google.does-not-exist"));
- backend_->ExpireHistory(expire_list);
- backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
- EXPECT_EQ(4U, visits.size());
-
- // Now add the first URL with the same time -- it should get deleted.
- expire_list.back().urls.insert(url_rows[0].url());
- backend_->ExpireHistory(expire_list);
-
- backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
- ASSERT_EQ(3U, visits.size());
- EXPECT_EQ(visits[0].url_id, url_rows[1].id());
- EXPECT_EQ(visits[1].url_id, url_rows[2].id());
- EXPECT_EQ(visits[2].url_id, url_rows[3].id());
-
- // The first recorded time should also get updated.
- EXPECT_EQ(backend_->GetFirstRecordedTimeForTest(), args[1].time);
-
- // Now delete the rest of the visits in one call.
- for (unsigned int i = 1; i < arraysize(args); ++i) {
- expire_list.resize(expire_list.size() + 1);
- expire_list[i].SetTimeRangeForOneDay(args[i].time);
- expire_list[i].urls.insert(args[i].url);
- }
- backend_->ExpireHistory(expire_list);
-
- backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
- ASSERT_EQ(0U, visits.size());
-}
-
-TEST_F(HistoryBackendTest, DeleteMatchingUrlsForKeyword) {
- // Set up urls and keyword_search_terms
- GURL url1("https://www.bing.com/?q=bar");
- URLRow url_info1(url1);
- url_info1.set_visit_count(0);
- url_info1.set_typed_count(0);
- url_info1.set_last_visit(Time());
- url_info1.set_hidden(false);
- const URLID url1_id = backend_->db()->AddURL(url_info1);
- EXPECT_NE(0, url1_id);
-
- KeywordID keyword_id = 1;
- base::string16 keyword = base::UTF8ToUTF16("bar");
- ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
- url1_id, keyword_id, keyword));
-
- GURL url2("https://www.google.com/?q=bar");
- URLRow url_info2(url2);
- url_info2.set_visit_count(0);
- url_info2.set_typed_count(0);
- url_info2.set_last_visit(Time());
- url_info2.set_hidden(false);
- const URLID url2_id = backend_->db()->AddURL(url_info2);
- EXPECT_NE(0, url2_id);
-
- KeywordID keyword_id2 = 2;
- ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
- url2_id, keyword_id2, keyword));
-
- // Add another visit to the same URL
- URLRow url_info3(url2);
- url_info3.set_visit_count(0);
- url_info3.set_typed_count(0);
- url_info3.set_last_visit(Time());
- url_info3.set_hidden(false);
- const URLID url3_id = backend_->db()->AddURL(url_info3);
- EXPECT_NE(0, url3_id);
- ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
- url3_id, keyword_id2, keyword));
-
- // Test that deletion works correctly
- backend_->DeleteMatchingURLsForKeyword(keyword_id2, keyword);
-
- // Test that rows 2 and 3 are deleted, while 1 is intact
- URLRow row;
- EXPECT_TRUE(backend_->db()->GetURLRow(url1_id, &row));
- EXPECT_EQ(url1.spec(), row.url().spec());
- EXPECT_FALSE(backend_->db()->GetURLRow(url2_id, &row));
- EXPECT_FALSE(backend_->db()->GetURLRow(url3_id, &row));
-
- // Test that corresponding keyword search terms are deleted for rows 2 & 3,
- // but not for row 1
- EXPECT_TRUE(backend_->db()->GetKeywordSearchTermRow(url1_id, NULL));
- EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url2_id, NULL));
- EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url3_id, NULL));
-}
-
-// Simple test that removes a bookmark. This test exercises the code paths in
-// History that block till bookmark bar model is loaded.
-TEST_F(HistoryBackendTest, RemoveNotification) {
- scoped_ptr<TestingProfile> profile(new TestingProfile());
-
- // Add a URL.
- GURL url("http://www.google.com");
- HistoryClientMock history_client;
- history_client.AddBookmark(url);
- scoped_ptr<HistoryService> service(new HistoryService(
- &history_client, scoped_ptr<history::VisitDelegate>()));
- EXPECT_TRUE(
- service->Init(profile->GetPrefs()->GetString(prefs::kAcceptLanguages),
- TestHistoryDatabaseParamsForPath(profile->GetPath())));
-
- service->AddPage(
- url, base::Time::Now(), NULL, 1, GURL(), RedirectList(),
- ui::PAGE_TRANSITION_TYPED, SOURCE_BROWSED, false);
-
- // This won't actually delete the URL, rather it'll empty out the visits.
- // This triggers blocking on the BookmarkModel.
- EXPECT_CALL(history_client, BlockUntilBookmarksLoaded());
- service->DeleteURL(url);
-}
-
-// Test DeleteFTSIndexDatabases deletes expected files.
-TEST_F(HistoryBackendTest, DeleteFTSIndexDatabases) {
- ASSERT_TRUE(backend_.get());
-
- base::FilePath history_path(test_dir());
- base::FilePath db1(history_path.AppendASCII("History Index 2013-05"));
- base::FilePath db1_journal(db1.InsertBeforeExtensionASCII("-journal"));
- base::FilePath db1_wal(db1.InsertBeforeExtensionASCII("-wal"));
- base::FilePath db2_symlink(history_path.AppendASCII("History Index 2013-06"));
- base::FilePath db2_actual(history_path.AppendASCII("Underlying DB"));
-
- // Setup dummy index database files.
- const char* data = "Dummy";
- const size_t data_len = 5;
- ASSERT_TRUE(base::WriteFile(db1, data, data_len));
- ASSERT_TRUE(base::WriteFile(db1_journal, data, data_len));
- ASSERT_TRUE(base::WriteFile(db1_wal, data, data_len));
- ASSERT_TRUE(base::WriteFile(db2_actual, data, data_len));
-#if defined(OS_POSIX)
- EXPECT_TRUE(base::CreateSymbolicLink(db2_actual, db2_symlink));
-#endif
-
- // Delete all DTS index databases.
- backend_->DeleteFTSIndexDatabases();
- EXPECT_FALSE(base::PathExists(db1));
- EXPECT_FALSE(base::PathExists(db1_wal));
- EXPECT_FALSE(base::PathExists(db1_journal));
- EXPECT_FALSE(base::PathExists(db2_symlink));
- EXPECT_TRUE(base::PathExists(db2_actual)); // Symlinks shouldn't be followed.
-}
-
-// Common implementation for the two tests below, given that the only difference
-// between them is the type of the notification sent out.
-void InMemoryHistoryBackendTest::TestAddingAndChangingURLRows(
- const SimulateNotificationCallback& callback) {
- const char kTestTypedURLAlternativeTitle[] = "Google Search Again";
- const char kTestNonTypedURLAlternativeTitle[] = "Google News Again";
-
- // Notify the in-memory database that a typed and non-typed URLRow (which were
- // never before seen by the cache) have been modified.
- URLRow row1(CreateTestTypedURL());
- URLRow row2(CreateTestNonTypedURL());
- callback.Run(&row1, &row2, nullptr);
-
- // The in-memory database should only pick up the typed URL, and should ignore
- // the non-typed one. The typed URL should retain the ID that was present in
- // the notification.
- URLRow cached_row1, cached_row2;
- EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
- EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
- EXPECT_EQ(row1.id(), cached_row1.id());
-
- // Try changing attributes (other than typed_count) for existing URLRows.
- row1.set_title(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle));
- row2.set_title(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle));
- callback.Run(&row1, &row2, nullptr);
-
- // URLRows that are cached by the in-memory database should be updated.
- EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
- EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
- EXPECT_EQ(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle),
- cached_row1.title());
-
- // Now decrease the typed count for the typed URLRow, and increase it for the
- // previously non-typed URLRow.
- row1.set_typed_count(0);
- row2.set_typed_count(2);
- callback.Run(&row1, &row2, nullptr);
-
- // The in-memory database should stop caching the first URLRow, and start
- // caching the second URLRow.
- EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
- EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
- EXPECT_EQ(row2.id(), cached_row2.id());
- EXPECT_EQ(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle),
- cached_row2.title());
-}
-
-TEST_F(InMemoryHistoryBackendTest, OnURLsModified) {
- TestAddingAndChangingURLRows(base::Bind(
- &SimulateNotificationURLsModified, base::Unretained(mem_backend_.get())));
-}
-
-TEST_F(InMemoryHistoryBackendTest, OnURLsVisisted) {
- TestAddingAndChangingURLRows(base::Bind(
- &SimulateNotificationURLVisited, base::Unretained(mem_backend_.get())));
-}
-
-TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedPiecewise) {
- // Add two typed and one non-typed URLRow to the in-memory database.
- URLRow row1(CreateTestTypedURL());
- URLRow row2(CreateAnotherTestTypedURL());
- URLRow row3(CreateTestNonTypedURL());
- SimulateNotificationURLsModified(mem_backend_.get(), &row1, &row2, &row3);
-
- // Notify the in-memory database that the second typed URL and the non-typed
- // URL has been deleted.
- SimulateNotificationURLsDeleted(&row2, &row3);
-
- // Expect that the first typed URL remains intact, the second typed URL is
- // correctly removed, and the non-typed URL does not magically appear.
- URLRow cached_row1;
- EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
- EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), NULL));
- EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row3.url(), NULL));
- EXPECT_EQ(row1.id(), cached_row1.id());
-}
-
-TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedEnMasse) {
- // Add two typed and one non-typed URLRow to the in-memory database.
- URLRow row1(CreateTestTypedURL());
- URLRow row2(CreateAnotherTestTypedURL());
- URLRow row3(CreateTestNonTypedURL());
- SimulateNotificationURLsModified(mem_backend_.get(), &row1, &row2, &row3);
-
- // Now notify the in-memory database that all history has been deleted.
- mem_backend_->OnURLsDeleted(nullptr, true, false, URLRows(),
- std::set<GURL>());
-
- // Expect that everything goes away.
- EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), NULL));
- EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), NULL));
- EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row3.url(), NULL));
-}
-
-void InMemoryHistoryBackendTest::PopulateTestURLsAndSearchTerms(
- URLRow* row1,
- URLRow* row2,
- const base::string16& term1,
- const base::string16& term2) {
- // Add a typed and a non-typed URLRow to the in-memory database. This time,
- // though, do it through the history backend...
- URLRows rows;
- rows.push_back(*row1);
- rows.push_back(*row2);
- backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
- backend_->db()->GetRowForURL(row1->url(), row1); // Get effective IDs from
- backend_->db()->GetRowForURL(row2->url(), row2); // the database.
-
- // ... so that we can also use that for adding the search terms. This way, we
- // not only test that the notifications involved are handled correctly, but
- // also that they are fired correctly (in the history backend).
- backend_->SetKeywordSearchTermsForURL(row1->url(), kTestKeywordId, term1);
- backend_->SetKeywordSearchTermsForURL(row2->url(), kTestKeywordId, term2);
-}
-
-TEST_F(InMemoryHistoryBackendTest, SetKeywordSearchTerms) {
- URLRow row1(CreateTestTypedURL());
- URLRow row2(CreateTestNonTypedURL());
- base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
- base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
- PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
-
- // Both URLs now have associated search terms, so the in-memory database
- // should cache both of them, regardless whether they have been typed or not.
- URLRow cached_row1, cached_row2;
- EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
- EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
- EXPECT_EQ(row1.id(), cached_row1.id());
- EXPECT_EQ(row2.id(), cached_row2.id());
-
- // Verify that lookups will actually return both search terms; and also check
- // at the low level that the rows are there.
- EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
- EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
- EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
- EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
-}
-
-TEST_F(InMemoryHistoryBackendTest, DeleteKeywordSearchTerms) {
- URLRow row1(CreateTestTypedURL());
- URLRow row2(CreateTestNonTypedURL());
- base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
- base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
- PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
-
- // Delete both search terms. This should be reflected in the in-memory DB.
- backend_->DeleteKeywordSearchTermForURL(row1.url());
- backend_->DeleteKeywordSearchTermForURL(row2.url());
-
- // The typed URL should remain intact.
- // Note: we do not need to guarantee anything about the non-typed URL.
- URLRow cached_row1;
- EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
- EXPECT_EQ(row1.id(), cached_row1.id());
-
- // Verify that the search terms are no longer returned as results, and also
- // check at the low level that they are gone for good.
- EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
- EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
- EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
- EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
-}
-
-TEST_F(InMemoryHistoryBackendTest, DeleteAllSearchTermsForKeyword) {
- URLRow row1(CreateTestTypedURL());
- URLRow row2(CreateTestNonTypedURL());
- base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
- base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
- PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
-
- // Delete all corresponding search terms from the in-memory database.
- KeywordID id = kTestKeywordId;
- mem_backend_->DeleteAllSearchTermsForKeyword(id);
-
- // The typed URL should remain intact.
- // Note: we do not need to guarantee anything about the non-typed URL.
- URLRow cached_row1;
- EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
- EXPECT_EQ(row1.id(), cached_row1.id());
-
- // Verify that the search terms are no longer returned as results, and also
- // check at the low level that they are gone for good.
- EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
- EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
- EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
- EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
-}
-
-TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedWithSearchTerms) {
- URLRow row1(CreateTestTypedURL());
- URLRow row2(CreateTestNonTypedURL());
- base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
- base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
- PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
-
- // Notify the in-memory database that the second typed URL has been deleted.
- SimulateNotificationURLsDeleted(&row2);
-
- // Verify that the second term is no longer returned as result, and also check
- // at the low level that it is gone for good. The term corresponding to the
- // first URLRow should not be affected.
- EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
- EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
- EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
- EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
-}
-
-} // namespace history
diff --git a/chrome/browser/history/top_sites_factory.cc b/chrome/browser/history/top_sites_factory.cc
index a102fcc..0c56d8c 100644
--- a/chrome/browser/history/top_sites_factory.cc
+++ b/chrome/browser/history/top_sites_factory.cc
@@ -10,11 +10,11 @@
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/history/history_utils.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_constants.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/locale_settings.h"
+#include "components/history/core/browser/history_constants.h"
#include "components/history/core/browser/top_sites_impl.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "content/public/browser/browser_thread.h"
@@ -78,13 +78,6 @@ scoped_refptr<history::TopSites> TopSitesFactory::GetForProfile(
}
// static
-scoped_refptr<history::TopSites> TopSitesFactory::GetForProfileIfExists(
- Profile* profile) {
- return static_cast<history::TopSites*>(
- GetInstance()->GetServiceForBrowserContext(profile, false).get());
-}
-
-// static
TopSitesFactory* TopSitesFactory::GetInstance() {
return Singleton<TopSitesFactory>::get();
}
@@ -99,7 +92,7 @@ scoped_refptr<history::TopSites> TopSitesFactory::BuildTopSites(
profile, ServiceAccessType::EXPLICIT_ACCESS),
prefs::kNtpMostVisitedURLsBlacklist, prepopulated_page_list,
base::Bind(CanAddURLToHistory)));
- top_sites->Init(context->GetPath().Append(chrome::kTopSitesFilename),
+ top_sites->Init(context->GetPath().Append(history::kTopSitesFilename),
content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::DB));
return top_sites;
diff --git a/chrome/browser/history/top_sites_factory.h b/chrome/browser/history/top_sites_factory.h
index 3978dbe..0f1e811 100644
--- a/chrome/browser/history/top_sites_factory.h
+++ b/chrome/browser/history/top_sites_factory.h
@@ -29,11 +29,6 @@ class TopSitesFactory : public RefcountedBrowserContextKeyedServiceFactory {
// Get the TopSites service for |profile|, creating one if needed.
static scoped_refptr<history::TopSites> GetForProfile(Profile* profile);
- // Get the TopSites service for |profile|, but do not create it if it doesn't
- // exist.
- static scoped_refptr<history::TopSites> GetForProfileIfExists(
- Profile* profile);
-
// Get the singleton instance of the factory.
static TopSitesFactory* GetInstance();
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index c1404ce..424d27d 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -110,13 +110,6 @@
'browser/history/android/sqlite_cursor_unittest.cc',
'browser/history/android/urls_sql_handler_unittest.cc',
'browser/history/android/visit_sql_handler_unittest.cc',
- 'browser/history/expire_history_backend_unittest.cc',
- 'browser/history/history_backend_unittest.cc',
- 'browser/history/history_database_unittest.cc',
- 'browser/history/history_querying_unittest.cc',
- 'browser/history/thumbnail_database_unittest.cc',
- 'browser/history/top_sites_impl_unittest.cc',
- 'browser/history/typed_url_syncable_service_unittest.cc',
'browser/history/web_history_service_unittest.cc',
'browser/image_holder_unittest.cc',
'browser/install_verification/win/imported_module_verification_unittest.cc',
diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc
index 7a92eee..db0d5cb 100644
--- a/chrome/common/chrome_constants.cc
+++ b/chrome/common/chrome_constants.cc
@@ -183,7 +183,6 @@ const base::FilePath::CharType kSupervisedUserSettingsFilename[] =
const base::FilePath::CharType kSyncCredentialsFilename[] =
FPL("Sync Credentials");
const base::FilePath::CharType kThemePackFilename[] = FPL("Cached Theme.pak");
-const base::FilePath::CharType kTopSitesFilename[] = FPL("Top Sites");
const base::FilePath::CharType kWebAppDirname[] = FPL("Web Applications");
// File name of the Pepper Flash plugin on different platforms.
diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h
index 5bfbd71..85f09e7 100644
--- a/chrome/common/chrome_constants.h
+++ b/chrome/common/chrome_constants.h
@@ -94,8 +94,6 @@ extern const base::FilePath::CharType kSingletonSocketFilename[];
extern const base::FilePath::CharType kSupervisedUserSettingsFilename[];
extern const base::FilePath::CharType kSyncCredentialsFilename[];
extern const base::FilePath::CharType kThemePackFilename[];
-extern const base::FilePath::CharType kThumbnailsFilename[];
-extern const base::FilePath::CharType kTopSitesFilename[];
extern const base::FilePath::CharType kWebAppDirname[];
// File name of the Pepper Flash plugin on different platforms.
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index aee4123..f544479a 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -27,7 +27,6 @@
#include "chrome/browser/history/chrome_history_client.h"
#include "chrome/browser/history/chrome_history_client_factory.h"
#include "chrome/browser/history/history_service_factory.h"
-#include "chrome/browser/history/top_sites_factory.h"
#include "chrome/browser/history/web_history_service_factory.h"
#include "chrome/browser/net/pref_proxy_config_tracker.h"
#include "chrome/browser/net/proxy_service_factory.h"
@@ -64,9 +63,6 @@
#include "components/history/core/browser/history_database_params.h"
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/history_service.h"
-#include "components/history/core/browser/top_sites.h"
-#include "components/history/core/browser/top_sites_impl.h"
-#include "components/history/core/browser/top_sites_observer.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/keyed_service/core/refcounted_keyed_service.h"
#include "components/policy/core/common/policy_service.h"
@@ -126,35 +122,6 @@ using testing::Return;
namespace {
-// TopSitesImpl::Shutdown schedules some tasks (from TopSitesBackend) that
-// need to be run to properly shutdown. Run all pending tasks now. This is
-// normally handled by browser_process shutdown.
-
-void CleanupAfterTopSitesDestroyed() {
- if (base::MessageLoop::current())
- base::MessageLoop::current()->RunUntilIdle();
-}
-
-// Returns true if a TopSites service has been registered for |profile|.
-bool HasTopSites(Profile* profile) {
- return !!TopSitesFactory::GetInstance()->GetForProfileIfExists(profile);
-}
-
-// Used to make sure TopSites has finished loading
-class WaitTopSitesLoadedObserver : public history::TopSitesObserver {
- public:
- explicit WaitTopSitesLoadedObserver(content::MessageLoopRunner* runner)
- : runner_(runner) {}
- void TopSitesLoaded(history::TopSites* top_sites) override {
- runner_->Quit();
- }
- void TopSitesChanged(history::TopSites* top_sites) override {}
-
- private:
- // weak
- content::MessageLoopRunner* runner_;
-};
-
// Task used to make sure history has finished processing a request. Intended
// for use with BlockUntilHistoryProcessesPendingRequests.
@@ -286,12 +253,6 @@ KeyedService* BuildWebDataService(content::BrowserContext* context) {
&TestProfileErrorCallback);
}
-scoped_refptr<RefcountedKeyedService> BuildTopSites(
- content::BrowserContext* context) {
- return TopSitesFactory::BuildTopSites(context,
- history::PrepopulatedPageList());
-}
-
} // namespace
// static
@@ -545,20 +506,11 @@ TestingProfile::~TestingProfile() {
MaybeSendDestroyedNotification();
- // Remember whether a TopSites has been created for the current profile,
- // so that we can run cleanup after destroying all services.
- bool had_top_sites = HasTopSites(this);
-
browser_context_dependency_manager_->DestroyBrowserContextServices(this);
if (host_content_settings_map_.get())
host_content_settings_map_->ShutdownOnUIThread();
- // Wait until TopSites shutdown tasks have completed if a TopSites has
- // been created for the current profile.
- if (had_top_sites)
- CleanupAfterTopSitesDestroyed();
-
if (pref_proxy_config_tracker_.get())
pref_proxy_config_tracker_->DetachFromPrefService();
// Failing a post == leaks == heapcheck failure. Make that an immediate test
@@ -629,22 +581,6 @@ void TestingProfile::DestroyHistoryService() {
base::MessageLoop::current()->Run();
}
-void TestingProfile::CreateTopSites() {
- DestroyTopSites();
- TopSitesFactory::GetInstance()->SetTestingFactoryAndUse(this, BuildTopSites);
-}
-
-void TestingProfile::DestroyTopSites() {
- TopSitesFactory* top_sites_factory = TopSitesFactory::GetInstance();
- if (top_sites_factory->GetForProfileIfExists(this)) {
- // BrowserContextKeyedServiceFactory will destroy the previous service when
- // registering a new testing factory so use this to ensure that destroy the
- // old service.
- top_sites_factory->SetTestingFactory(this, nullptr);
- CleanupAfterTopSitesDestroyed();
- }
-}
-
void TestingProfile::CreateBookmarkModel(bool delete_file) {
if (delete_file) {
base::FilePath path = GetPath().Append(bookmarks::kBookmarksFileName);
@@ -682,18 +618,6 @@ void TestingProfile::BlockUntilHistoryIndexIsRefreshed() {
DCHECK(index->restored());
}
-// TODO(phajdan.jr): Doesn't this hang if Top Sites are already loaded?
-void TestingProfile::BlockUntilTopSitesLoaded() {
- scoped_refptr<content::MessageLoopRunner> runner =
- new content::MessageLoopRunner;
- WaitTopSitesLoadedObserver observer(runner.get());
- scoped_refptr<history::TopSites> top_sites =
- TopSitesFactory::GetForProfile(this);
- top_sites->AddObserver(&observer);
- runner->Run();
- top_sites->RemoveObserver(&observer);
-}
-
void TestingProfile::SetGuestSession(bool guest) {
guest_session_ = guest;
}
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 322a7b0..2f367bb 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -22,10 +22,6 @@ class SSLHostStateDelegate;
class ZoomLevelDelegate;
}
-namespace history {
-class TopSites;
-}
-
namespace net {
class CookieMonster;
class URLRequestContextGetter;
@@ -180,13 +176,6 @@ class TestingProfile : public Profile {
// Shuts down and nulls out the reference to HistoryService.
void DestroyHistoryService();
- // Creates TopSites. This returns immediately, and top sites may not be
- // loaded. Use BlockUntilTopSitesLoaded to ensure TopSites has finished
- // loading.
- void CreateTopSites();
-
- void DestroyTopSites();
-
// Creates the BookmarkBarModel. If not invoked the bookmark bar model is
// NULL. If |delete_file| is true, the bookmarks file is deleted first, then
// the model is created. As TestingProfile deletes the directory containing
@@ -204,9 +193,6 @@ class TestingProfile : public Profile {
// This is NOT invoked from CreateHistoryService.
void BlockUntilHistoryIndexIsRefreshed();
- // Blocks until TopSites finishes loading.
- void BlockUntilTopSitesLoaded();
-
// Allow setting a profile as Guest after-the-fact to simplify some tests.
void SetGuestSession(bool guest);
diff --git a/chrome/test/data/History/ArchivedNoDuration b/chrome/test/data/History/ArchivedNoDuration
deleted file mode 100644
index 5b51922..0000000
--- a/chrome/test/data/History/ArchivedNoDuration
+++ /dev/null
Binary files differ
diff --git a/chrome/test/data/History/Favicons.v3.history.sql b/chrome/test/data/History/Favicons.v3.history.sql
deleted file mode 100644
index 1c072d5..0000000
--- a/chrome/test/data/History/Favicons.v3.history.sql
+++ /dev/null
@@ -1,15 +0,0 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.Version3
---
--- .dump that portion of the History database needed to migrate a
--- Favicons version 3 database. See also Favicons.v3.sql.
-BEGIN TRANSACTION;
-CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY,value LONGVARCHAR);
-INSERT INTO "meta" VALUES('version','20');
-INSERT INTO "meta" VALUES('last_compatible_version','16');
-CREATE TABLE urls(id INTEGER PRIMARY KEY,url LONGVARCHAR,title LONGVARCHAR,visit_count INTEGER DEFAULT 0 NOT NULL,typed_count INTEGER DEFAULT 0 NOT NULL,last_visit_time INTEGER NOT NULL,hidden INTEGER DEFAULT 0 NOT NULL,favicon_id INTEGER DEFAULT 0 NOT NULL);
-INSERT INTO "urls" VALUES(1,'http://google.com/','Google',1,1,0,0,1);
-INSERT INTO "urls" VALUES(2,'http://www.google.com/','Google',1,0,0,0,1);
-INSERT INTO "urls" VALUES(3,'http://www.google.com/blank.html','',1,0,0,1,0);
-INSERT INTO "urls" VALUES(4,'http://yahoo.com/','Yahoo!',1,1,0,0,2);
-INSERT INTO "urls" VALUES(5,'http://www.yahoo.com/','Yahoo!',1,0,0,0,2);
-COMMIT;
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 2ee73c9..2330cf3 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -221,13 +221,20 @@
'guest_view/browser/guest_view_manager_unittest.cc'
],
'history_unittest_sources': [
- 'history/content/browser/content_history_backend_unittest.cc',
+ 'history/content/browser/content_history_backend_db_unittest.cc',
'history/core/browser/android/android_history_types_unittest.cc',
+ 'history/core/browser/expire_history_backend_unittest.cc',
+ 'history/core/browser/history_backend_db_unittest.cc',
'history/core/browser/history_backend_unittest.cc',
+ 'history/core/browser/history_database_unittest.cc',
+ 'history/core/browser/history_querying_unittest.cc',
'history/core/browser/history_service_unittest.cc',
'history/core/browser/history_types_unittest.cc',
+ 'history/core/browser/thumbnail_database_unittest.cc',
'history/core/browser/top_sites_cache_unittest.cc',
'history/core/browser/top_sites_database_unittest.cc',
+ 'history/core/browser/top_sites_impl_unittest.cc',
+ 'history/core/browser/typed_url_syncable_service_unittest.cc',
'history/core/browser/url_database_unittest.cc',
'history/core/browser/url_utils_unittest.cc',
'history/core/browser/visit_database_unittest.cc',
diff --git a/components/history.gypi b/components/history.gypi
index bd7a09f..9ecec9f 100644
--- a/components/history.gypi
+++ b/components/history.gypi
@@ -156,10 +156,12 @@
],
'dependencies': [
'../base/base.gyp:base',
+ '../skia/skia.gyp:skia',
'../sql/sql.gyp:sql',
'../sql/sql.gyp:test_support_sql',
'../sync/sync.gyp:sync',
'../testing/gtest.gyp:gtest',
+ '../ui/gfx/gfx.gyp:gfx',
'../url/url.gyp:url_lib',
'history_core_browser',
],
@@ -167,14 +169,27 @@
# Note: sources list duplicated in GN build.
'history/core/test/database_test_utils.cc',
'history/core/test/database_test_utils.h',
- 'history/core/test/history_backend_base_test.cc',
- 'history/core/test/history_backend_base_test.h',
+ 'history/core/test/history_backend_db_base_test.cc',
+ 'history/core/test/history_backend_db_base_test.h',
'history/core/test/history_client_fake_bookmarks.cc',
'history/core/test/history_client_fake_bookmarks.h',
'history/core/test/history_unittest_base.cc',
'history/core/test/history_unittest_base.h',
'history/core/test/test_history_database.cc',
'history/core/test/test_history_database.h',
+ 'history/core/test/thumbnail-inl.h',
+ 'history/core/test/thumbnail.cc',
+ 'history/core/test/thumbnail.h',
+ 'history/core/test/thumbnail_ios.mm',
+ 'history/core/test/wait_top_sites_loaded_observer.cc',
+ 'history/core/test/wait_top_sites_loaded_observer.h',
+ ],
+ 'conditions': [
+ ['OS=="ios"', {
+ 'sources!': [
+ 'history/core/test/thumbnail.cc',
+ ],
+ }],
],
},
],
diff --git a/components/history/content/browser/content_history_backend_unittest.cc b/components/history/content/browser/content_history_backend_db_unittest.cc
index 8e3a2aa..306c289 100644
--- a/components/history/content/browser/content_history_backend_unittest.cc
+++ b/components/history/content/browser/content_history_backend_db_unittest.cc
@@ -19,14 +19,14 @@
#include "components/history/core/browser/history_backend.h"
-#include "components/history/core/test/history_backend_base_test.h"
+#include "components/history/core/test/history_backend_db_base_test.h"
namespace history {
namespace {
// This must be outside the anonymous namespace for the friend statement in
// HistoryBackend to work.
-class ContentHistoryBackendDBTest : public HistoryBackendBaseTest {
+class ContentHistoryBackendDBTest : public HistoryBackendDBBaseTest {
public:
ContentHistoryBackendDBTest() {}
~ContentHistoryBackendDBTest() override {}
diff --git a/chrome/browser/history/expire_history_backend_unittest.cc b/components/history/core/browser/expire_history_backend_unittest.cc
index 32a26d2..d976373 100644
--- a/chrome/browser/history/expire_history_backend_unittest.cc
+++ b/components/history/core/browser/expire_history_backend_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "components/history/core/browser/expire_history_backend.h"
+
#include <algorithm>
#include <string>
#include <utility>
@@ -11,54 +13,53 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/testing_pref_service.h"
+#include "base/scoped_observer.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/history/top_sites_factory.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/history/core/browser/expire_history_backend.h"
#include "components/history/core/browser/history_backend_notifier.h"
+#include "components/history/core/browser/history_constants.h"
#include "components/history/core/browser/history_database.h"
#include "components/history/core/browser/thumbnail_database.h"
#include "components/history/core/browser/top_sites.h"
+#include "components/history/core/browser/top_sites_impl.h"
+#include "components/history/core/browser/top_sites_observer.h"
#include "components/history/core/common/thumbnail_score.h"
#include "components/history/core/test/history_client_fake_bookmarks.h"
#include "components/history/core/test/test_history_database.h"
-#include "components/history/core/test/thumbnail-inl.h"
-#include "content/public/test/test_browser_thread.h"
+#include "components/history/core/test/thumbnail.h"
+#include "components/history/core/test/wait_top_sites_loaded_observer.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/codec/jpeg_codec.h"
-
-using base::Time;
-using base::TimeDelta;
-using base::TimeTicks;
-using content::BrowserThread;
-
-// Filename constants.
-static const base::FilePath::CharType kHistoryFile[] =
- FILE_PATH_LITERAL("History");
-static const base::FilePath::CharType kThumbnailFile[] =
- FILE_PATH_LITERAL("Thumbnails");
// The test must be in the history namespace for the gtest forward declarations
// to work. It also eliminates a bunch of ugly "history::".
namespace history {
+namespace {
+// Key for URL blacklist.
+const char kBlacklistURLKey[] = "test.blacklist.url";
+
+// Returns whether |url| can be added to history.
+bool MockCanAddURLToHistory(const GURL& url) {
+ return url.is_valid();
+}
+
+} // namespace
+
// ExpireHistoryTest -----------------------------------------------------------
-class ExpireHistoryTest : public testing::Test,
- public HistoryBackendNotifier {
+class ExpireHistoryTest : public testing::Test, public HistoryBackendNotifier {
public:
ExpireHistoryTest()
- : ui_thread_(BrowserThread::UI, &message_loop_),
- db_thread_(BrowserThread::DB, &message_loop_),
- expirer_(this, &history_client_),
- now_(Time::Now()) {}
+ : expirer_(this, &history_client_), now_(base::Time::Now()) {}
protected:
// Called by individual tests when they want data populated.
- void AddExampleData(URLID url_ids[3], Time visit_times[4]);
+ void AddExampleData(URLID url_ids[3], base::Time visit_times[4]);
// Add visits with source information.
void AddExampleSourceData(const GURL& url, URLID* id);
@@ -97,17 +98,16 @@ class ExpireHistoryTest : public testing::Test,
HistoryClientFakeBookmarks history_client_;
base::MessageLoopForUI message_loop_;
- content::TestBrowserThread ui_thread_;
- content::TestBrowserThread db_thread_;
ExpireHistoryBackend expirer_;
+ scoped_ptr<TestingPrefServiceSimple> pref_service_;
scoped_ptr<HistoryDatabase> main_db_;
scoped_ptr<ThumbnailDatabase> thumb_db_;
- TestingProfile profile_;
+ scoped_refptr<TopSitesImpl> top_sites_;
- // Time at the beginning of the test, so everybody agrees what "now" is.
- const Time now_;
+ // base::Time at the beginning of the test, so everybody agrees what "now" is.
+ const base::Time now_;
typedef std::vector<URLRows> URLsModifiedNotificationList;
URLsModifiedNotificationList urls_modified_notifications_;
@@ -119,29 +119,45 @@ class ExpireHistoryTest : public testing::Test,
void SetUp() override {
ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
- base::FilePath history_name = path().Append(kHistoryFile);
+ base::FilePath history_name = path().Append(kHistoryFilename);
main_db_.reset(new TestHistoryDatabase);
if (main_db_->Init(history_name) != sql::INIT_OK)
main_db_.reset();
- base::FilePath thumb_name = path().Append(kThumbnailFile);
- thumb_db_.reset(new ThumbnailDatabase(NULL));
+ base::FilePath thumb_name = path().Append(kThumbnailsFilename);
+ thumb_db_.reset(new ThumbnailDatabase(nullptr));
if (thumb_db_->Init(thumb_name) != sql::INIT_OK)
thumb_db_.reset();
+ pref_service_.reset(new TestingPrefServiceSimple);
+ pref_service_->registry()->RegisterDictionaryPref(kBlacklistURLKey);
+
expirer_.SetDatabases(main_db_.get(), thumb_db_.get());
- profile_.CreateTopSites();
- profile_.BlockUntilTopSitesLoaded();
+
+ top_sites_ = new TopSitesImpl(pref_service_.get(), nullptr,
+ kBlacklistURLKey, PrepopulatedPageList(),
+ base::Bind(MockCanAddURLToHistory));
+ WaitTopSitesLoadedObserver wait_top_sites_observer(top_sites_);
+ top_sites_->Init(path().Append(kTopSitesFilename),
+ message_loop_.task_runner());
+ wait_top_sites_observer.Run();
}
void TearDown() override {
-
ClearLastNotifications();
- expirer_.SetDatabases(NULL, NULL);
+ expirer_.SetDatabases(nullptr, nullptr);
main_db_.reset();
thumb_db_.reset();
+
+ top_sites_->ShutdownOnUIThread();
+ top_sites_ = nullptr;
+
+ if (base::MessageLoop::current())
+ base::MessageLoop::current()->RunUntilIdle();
+
+ pref_service_.reset();
}
// HistoryBackendNotifier:
@@ -168,20 +184,21 @@ class ExpireHistoryTest : public testing::Test,
// (with the one in the middle) when it picks the proper threshold time.
//
// Each visit has indexed data, each URL has thumbnail. The first two URLs will
-// share the same avicon, while the last one will have a unique favicon. The
+// share the same favicon, while the last one will have a unique favicon. The
// second visit for the middle URL is typed.
//
// The IDs of the added URLs, and the times of the four added visits will be
// added to the given arrays.
-void ExpireHistoryTest::AddExampleData(URLID url_ids[3], Time visit_times[4]) {
+void ExpireHistoryTest::AddExampleData(URLID url_ids[3],
+ base::Time visit_times[4]) {
if (!main_db_.get())
return;
// Four times for each visit.
- visit_times[3] = Time::Now();
- visit_times[2] = visit_times[3] - TimeDelta::FromDays(1);
- visit_times[1] = visit_times[3] - TimeDelta::FromDays(2);
- visit_times[0] = visit_times[3] - TimeDelta::FromDays(3);
+ visit_times[3] = base::Time::Now();
+ visit_times[2] = visit_times[3] - base::TimeDelta::FromDays(1);
+ visit_times[1] = visit_times[3] - base::TimeDelta::FromDays(2);
+ visit_times[0] = visit_times[3] - base::TimeDelta::FromDays(3);
// Two favicons. The first two URLs will share the same one, while the last
// one will have a unique favicon.
@@ -210,19 +227,15 @@ void ExpireHistoryTest::AddExampleData(URLID url_ids[3], Time visit_times[4]) {
url_ids[2] = main_db_->AddURL(url_row3);
thumb_db_->AddIconMapping(url_row3.url(), favicon2);
- // Thumbnails for each URL. |thumbnail| takes ownership of decoded SkBitmap.
- scoped_ptr<SkBitmap> thumbnail_bitmap(
- gfx::JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail)));
- gfx::Image thumbnail = gfx::Image::CreateFrom1xBitmap(*thumbnail_bitmap);
- ThumbnailScore score(0.25, true, true, Time::Now());
+ // Thumbnails for each URL.
+ gfx::Image thumbnail = CreateGoogleThumbnailForTest();
+ ThumbnailScore score(0.25, true, true, base::Time::Now());
- Time time;
+ base::Time time;
GURL gurl;
- scoped_refptr<history::TopSites> top_sites =
- TopSitesFactory::GetForProfile(&profile_);
- top_sites->SetPageThumbnail(url_row1.url(), thumbnail, score);
- top_sites->SetPageThumbnail(url_row2.url(), thumbnail, score);
- top_sites->SetPageThumbnail(url_row3.url(), thumbnail, score);
+ top_sites_->SetPageThumbnail(url_row1.url(), thumbnail, score);
+ top_sites_->SetPageThumbnail(url_row2.url(), thumbnail, score);
+ top_sites_->SetPageThumbnail(url_row3.url(), thumbnail, score);
// Four visits.
VisitRow visit_row1;
@@ -251,7 +264,7 @@ void ExpireHistoryTest::AddExampleSourceData(const GURL& url, URLID* id) {
if (!main_db_)
return;
- Time last_visit_time = Time::Now();
+ base::Time last_visit_time = base::Time::Now();
// Add one URL.
URLRow url_row1(url);
url_row1.set_last_visit(last_visit_time);
@@ -260,27 +273,26 @@ void ExpireHistoryTest::AddExampleSourceData(const GURL& url, URLID* id) {
*id = url_id;
// Four times for each visit.
- VisitRow visit_row1(url_id, last_visit_time - TimeDelta::FromDays(4), 0,
+ VisitRow visit_row1(url_id, last_visit_time - base::TimeDelta::FromDays(4), 0,
ui::PAGE_TRANSITION_TYPED, 0);
main_db_->AddVisit(&visit_row1, SOURCE_SYNCED);
- VisitRow visit_row2(url_id, last_visit_time - TimeDelta::FromDays(3), 0,
+ VisitRow visit_row2(url_id, last_visit_time - base::TimeDelta::FromDays(3), 0,
ui::PAGE_TRANSITION_TYPED, 0);
main_db_->AddVisit(&visit_row2, SOURCE_BROWSED);
- VisitRow visit_row3(url_id, last_visit_time - TimeDelta::FromDays(2), 0,
+ VisitRow visit_row3(url_id, last_visit_time - base::TimeDelta::FromDays(2), 0,
ui::PAGE_TRANSITION_TYPED, 0);
main_db_->AddVisit(&visit_row3, SOURCE_EXTENSION);
- VisitRow visit_row4(
- url_id, last_visit_time, 0, ui::PAGE_TRANSITION_TYPED, 0);
+ VisitRow visit_row4(url_id, last_visit_time, 0, ui::PAGE_TRANSITION_TYPED, 0);
main_db_->AddVisit(&visit_row4, SOURCE_FIREFOX_IMPORTED);
}
bool ExpireHistoryTest::HasFavicon(favicon_base::FaviconID favicon_id) {
if (!thumb_db_.get() || favicon_id == 0)
return false;
- return thumb_db_->GetFaviconHeader(favicon_id, NULL, NULL);
+ return thumb_db_->GetFaviconHeader(favicon_id, nullptr, nullptr);
}
favicon_base::FaviconID ExpireHistoryTest::GetFavicon(
@@ -302,9 +314,7 @@ bool ExpireHistoryTest::HasThumbnail(URLID url_id) {
return false;
GURL url = info.url();
scoped_refptr<base::RefCountedMemory> data;
- scoped_refptr<history::TopSites> top_sites =
- TopSitesFactory::GetForProfile(&profile_);
- return top_sites->GetPageThumbnail(url, false, &data);
+ return top_sites_->GetPageThumbnail(url, false, &data);
}
void ExpireHistoryTest::EnsureURLInfoGone(const URLRow& row, bool expired) {
@@ -340,8 +350,7 @@ void ExpireHistoryTest::EnsureURLInfoGone(const URLRow& row, bool expired) {
}
}
for (const auto& rows : urls_modified_notifications_) {
- EXPECT_TRUE(std::find_if(rows.begin(),
- rows.end(),
+ EXPECT_TRUE(std::find_if(rows.begin(), rows.end(),
history::URLRow::URLRowHasURL(row.url())) ==
rows.end());
}
@@ -350,8 +359,7 @@ void ExpireHistoryTest::EnsureURLInfoGone(const URLRow& row, bool expired) {
bool ExpireHistoryTest::ModifiedNotificationSent(const GURL& url) {
for (const auto& rows : urls_modified_notifications_) {
- if (std::find_if(rows.begin(),
- rows.end(),
+ if (std::find_if(rows.begin(), rows.end(),
history::URLRow::URLRowHasURL(url)) != rows.end())
return true;
}
@@ -410,7 +418,7 @@ bool ExpireHistoryTest::IsStringInFile(const base::FilePath& filename,
// Fails near end of month. http://crbug.com/43586
TEST_F(ExpireHistoryTest, DISABLED_DeleteURLAndFavicon) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
// Verify things are the way we expect with a URL row, favicon, thumbnail.
@@ -439,7 +447,7 @@ TEST_F(ExpireHistoryTest, DISABLED_DeleteURLAndFavicon) {
// should not get deleted. This also tests deleting more than one visit.
TEST_F(ExpireHistoryTest, DeleteURLWithoutFavicon) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
// Verify things are the way we expect with a URL row, favicon, thumbnail.
@@ -467,7 +475,7 @@ TEST_F(ExpireHistoryTest, DeleteURLWithoutFavicon) {
// remain starred and its favicon should remain too.
TEST_F(ExpireHistoryTest, DeleteStarredVisitedURL) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
URLRow url_row;
@@ -485,8 +493,7 @@ TEST_F(ExpireHistoryTest, DeleteStarredVisitedURL) {
EnsureURLInfoGone(url_row, false);
// Yet the favicon should exist.
- favicon_base::FaviconID favicon_id =
- GetFavicon(url, favicon_base::FAVICON);
+ favicon_base::FaviconID favicon_id = GetFavicon(url, favicon_base::FAVICON);
EXPECT_TRUE(HasFavicon(favicon_id));
// Should still have the thumbnail.
@@ -523,7 +530,7 @@ TEST_F(ExpireHistoryTest, DeleteStarredUnvisitedURL) {
// not the first two should be deleted.
TEST_F(ExpireHistoryTest, DeleteURLs) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
// Verify things are the way we expect with URL rows, favicons,
@@ -560,7 +567,7 @@ TEST_F(ExpireHistoryTest, DeleteURLs) {
// the two visits) and one is deleted.
TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarred) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
URLRow url_row1, url_row2;
@@ -573,7 +580,7 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarred) {
// This should delete the last two visits.
std::set<GURL> restrict_urls;
- expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
+ expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], base::Time());
// Verify that the middle URL had its last visit deleted only.
visits.clear();
@@ -608,7 +615,7 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarred) {
// Expires all URLs with times in a given set.
TEST_F(ExpireHistoryTest, FlushURLsForTimes) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
URLRow url_row1, url_row2;
@@ -660,7 +667,7 @@ TEST_F(ExpireHistoryTest, FlushURLsForTimes) {
// one of the two visits).
TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
URLRow url_row1, url_row2;
@@ -674,7 +681,7 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted) {
// This should delete the last two visits.
std::set<GURL> restrict_urls;
restrict_urls.insert(url_row1.url());
- expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
+ expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], base::Time());
// Verify that the middle URL had its last visit deleted only.
visits.clear();
@@ -709,7 +716,7 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted) {
// Expire a starred URL, it shouldn't get deleted
TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
URLRow url_row1, url_row2;
@@ -722,7 +729,7 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) {
// This should delete the last two visits.
std::set<GURL> restrict_urls;
- expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
+ expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], base::Time());
// The URL rows should still exist.
URLRow new_url_row1, new_url_row2;
@@ -758,7 +765,7 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) {
TEST_F(ExpireHistoryTest, ExpireHistoryBeforeUnstarred) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
URLRow url_row0, url_row1, url_row2;
@@ -794,7 +801,7 @@ TEST_F(ExpireHistoryTest, ExpireHistoryBeforeUnstarred) {
TEST_F(ExpireHistoryTest, ExpireHistoryBeforeStarred) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
URLRow url_row0, url_row1;
@@ -834,13 +841,13 @@ TEST_F(ExpireHistoryTest, ExpireHistoryBeforeStarred) {
// tests which use this function internally.
TEST_F(ExpireHistoryTest, ExpireSomeOldHistory) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
const ExpiringVisitsReader* reader = expirer_.GetAllVisitsReader();
// Deleting a time range with no URLs should return false (nothing found).
EXPECT_FALSE(expirer_.ExpireSomeOldHistory(
- visit_times[0] - TimeDelta::FromDays(100), reader, 1));
+ visit_times[0] - base::TimeDelta::FromDays(100), reader, 1));
// Deleting a time range with not up the the max results should also return
// false (there will only be one visit deleted in this range).
@@ -853,7 +860,7 @@ TEST_F(ExpireHistoryTest, ExpireSomeOldHistory) {
TEST_F(ExpireHistoryTest, ExpiringVisitsReader) {
URLID url_ids[3];
- Time visit_times[4];
+ base::Time visit_times[4];
AddExampleData(url_ids, visit_times);
const ExpiringVisitsReader* all = expirer_.GetAllVisitsReader();
@@ -861,12 +868,12 @@ TEST_F(ExpireHistoryTest, ExpiringVisitsReader) {
expirer_.GetAutoSubframeVisitsReader();
VisitVector visits;
- Time now = Time::Now();
+ base::Time now = base::Time::Now();
// Verify that the early expiration threshold, stored in the meta table is
// initialized.
EXPECT_TRUE(main_db_->GetEarlyExpirationThreshold() ==
- Time::FromInternalValue(1L));
+ base::Time::FromInternalValue(1L));
// First, attempt reading AUTO_SUBFRAME visits. We should get none.
EXPECT_FALSE(auto_subframes->Read(now, main_db_.get(), &visits, 1));
diff --git a/components/history/core/browser/history_backend.h b/components/history/core/browser/history_backend.h
index 6332e30..476008d 100644
--- a/components/history/core/browser/history_backend.h
+++ b/components/history/core/browser/history_backend.h
@@ -42,8 +42,9 @@ class SingleThreadTaskRunner;
namespace history {
class CommitLaterTask;
struct DownloadRow;
-class HistoryBackendBaseTest;
+class HistoryBackendDBBaseTest;
class HistoryBackendObserver;
+class HistoryBackendTest;
class HistoryClient;
class HistoryDatabase;
struct HistoryDatabaseParams;
@@ -451,8 +452,8 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
friend class base::RefCountedThreadSafe<HistoryBackend>;
friend class CommitLaterTask; // The commit task needs to call Commit().
friend class HistoryBackendTest;
- friend class HistoryBackendBaseTest; // So the unit tests can poke our
- // innards.
+ friend class HistoryBackendDBBaseTest; // So the unit tests can poke our
+ // innards.
FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, DeleteAll);
FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, DeleteAllThenAddData);
FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, AddPagesWithDetails);
diff --git a/components/history/core/browser/history_backend_db_unittest.cc b/components/history/core/browser/history_backend_db_unittest.cc
new file mode 100644
index 0000000..1f7c5ae
--- /dev/null
+++ b/components/history/core/browser/history_backend_db_unittest.cc
@@ -0,0 +1,760 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// History unit tests come in two flavors:
+//
+// 1. The more complicated style is that the unit test creates a full history
+// service. This spawns a background thread for the history backend, and
+// all communication is asynchronous. This is useful for testing more
+// complicated things or end-to-end behavior.
+//
+// 2. The simpler style is to create a history backend on this thread and
+// access it directly without a HistoryService object. This is much simpler
+// because communication is synchronous. Generally, sets should go through
+// the history backend (since there is a lot of logic) but gets can come
+// directly from the HistoryDatabase. This is because the backend generally
+// has no logic in the getter except threading stuff, which we don't want
+// to run.
+
+#include "components/history/core/browser/history_backend.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/history/core/browser/download_constants.h"
+#include "components/history/core/browser/download_row.h"
+#include "components/history/core/browser/history_constants.h"
+#include "components/history/core/browser/history_database.h"
+#include "components/history/core/browser/page_usage_data.h"
+#include "components/history/core/test/history_backend_db_base_test.h"
+#include "components/history/core/test/test_history_database.h"
+
+namespace history {
+namespace {
+
+// This must be outside the anonymous namespace for the friend statement in
+// HistoryBackend to work.
+class HistoryBackendDBTest : public HistoryBackendDBBaseTest {
+ public:
+ HistoryBackendDBTest() {}
+ ~HistoryBackendDBTest() override {}
+};
+
+TEST_F(HistoryBackendDBTest, ClearBrowsingData_Downloads) {
+ CreateBackendAndDatabase();
+
+ // Initially there should be nothing in the downloads database.
+ std::vector<DownloadRow> downloads;
+ db_->QueryDownloads(&downloads);
+ EXPECT_EQ(0U, downloads.size());
+
+ // Add a download, test that it was added correctly, remove it, test that it
+ // was removed.
+ base::Time now = base::Time();
+ uint32 id = 1;
+ EXPECT_TRUE(AddDownload(id, DownloadState::COMPLETE, base::Time()));
+ db_->QueryDownloads(&downloads);
+ EXPECT_EQ(1U, downloads.size());
+
+ EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("current-path")),
+ downloads[0].current_path);
+ EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("target-path")),
+ downloads[0].target_path);
+ EXPECT_EQ(1UL, downloads[0].url_chain.size());
+ EXPECT_EQ(GURL("foo-url"), downloads[0].url_chain[0]);
+ EXPECT_EQ(std::string("http://referrer.com/"),
+ std::string(downloads[0].referrer_url.spec()));
+ EXPECT_EQ(now, downloads[0].start_time);
+ EXPECT_EQ(now, downloads[0].end_time);
+ EXPECT_EQ(0, downloads[0].received_bytes);
+ EXPECT_EQ(512, downloads[0].total_bytes);
+ EXPECT_EQ(DownloadState::COMPLETE, downloads[0].state);
+ EXPECT_EQ(DownloadDangerType::NOT_DANGEROUS, downloads[0].danger_type);
+ EXPECT_EQ(kTestDownloadInterruptReasonNone, downloads[0].interrupt_reason);
+ EXPECT_FALSE(downloads[0].opened);
+ EXPECT_EQ("by_ext_id", downloads[0].by_ext_id);
+ EXPECT_EQ("by_ext_name", downloads[0].by_ext_name);
+ EXPECT_EQ("application/vnd.oasis.opendocument.text", downloads[0].mime_type);
+ EXPECT_EQ("application/octet-stream", downloads[0].original_mime_type);
+
+ db_->QueryDownloads(&downloads);
+ EXPECT_EQ(1U, downloads.size());
+ db_->RemoveDownload(id);
+ db_->QueryDownloads(&downloads);
+ EXPECT_EQ(0U, downloads.size());
+}
+
+TEST_F(HistoryBackendDBTest, MigrateDownloadsState) {
+ // Create the db we want.
+ ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
+ {
+ // Open the db for manual manipulation.
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+
+ // Manually insert corrupted rows; there's infrastructure in place now to
+ // make this impossible, at least according to the test above.
+ for (int state = 0; state < 5; ++state) {
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO downloads (id, full_path, url, start_time, "
+ "received_bytes, total_bytes, state, end_time, opened) VALUES "
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+ s.BindInt64(0, 1 + state);
+ s.BindString(1, "path");
+ s.BindString(2, "url");
+ s.BindInt64(3, base::Time::Now().ToTimeT());
+ s.BindInt64(4, 100);
+ s.BindInt64(5, 100);
+ s.BindInt(6, state);
+ s.BindInt64(7, base::Time::Now().ToTimeT());
+ s.BindInt(8, state % 2);
+ ASSERT_TRUE(s.Run());
+ }
+ }
+
+ // Re-open the db using the HistoryDatabase, which should migrate from version
+ // 22 to the current version, fixing just the row whose state was 3.
+ // Then close the db so that we can re-open it directly.
+ CreateBackendAndDatabase();
+ DeleteBackend();
+ {
+ // Re-open the db for manual manipulation.
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ {
+ // The version should have been updated.
+ int cur_version = HistoryDatabase::GetCurrentVersion();
+ ASSERT_LT(22, cur_version);
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT value FROM meta WHERE key = 'version'"));
+ EXPECT_TRUE(s.Step());
+ EXPECT_EQ(cur_version, s.ColumnInt(0));
+ }
+ {
+ sql::Statement statement(db.GetUniqueStatement(
+ "SELECT id, state, opened "
+ "FROM downloads "
+ "ORDER BY id"));
+ int counter = 0;
+ while (statement.Step()) {
+ EXPECT_EQ(1 + counter, statement.ColumnInt64(0));
+ // The only thing that migration should have changed was state from 3 to
+ // 4.
+ EXPECT_EQ(((counter == 3) ? 4 : counter), statement.ColumnInt(1));
+ EXPECT_EQ(counter % 2, statement.ColumnInt(2));
+ ++counter;
+ }
+ EXPECT_EQ(5, counter);
+ }
+ }
+}
+
+TEST_F(HistoryBackendDBTest, MigrateDownloadsReasonPathsAndDangerType) {
+ base::Time now(base::Time::Now());
+
+ // Create the db we want. The schema didn't change from 22->23, so just
+ // re-use the v22 file.
+ ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
+ {
+ // Re-open the db for manual manipulation.
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+
+ // Manually insert some rows.
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO downloads (id, full_path, url, start_time, "
+ "received_bytes, total_bytes, state, end_time, opened) VALUES "
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+
+ int64 id = 0;
+ // Null path.
+ s.BindInt64(0, ++id);
+ s.BindString(1, std::string());
+ s.BindString(2, "http://whatever.com/index.html");
+ s.BindInt64(3, now.ToTimeT());
+ s.BindInt64(4, 100);
+ s.BindInt64(5, 100);
+ s.BindInt(6, 1);
+ s.BindInt64(7, now.ToTimeT());
+ s.BindInt(8, 1);
+ ASSERT_TRUE(s.Run());
+ s.Reset(true);
+
+ // Non-null path.
+ s.BindInt64(0, ++id);
+ s.BindString(1, "/path/to/some/file");
+ s.BindString(2, "http://whatever.com/index1.html");
+ s.BindInt64(3, now.ToTimeT());
+ s.BindInt64(4, 100);
+ s.BindInt64(5, 100);
+ s.BindInt(6, 1);
+ s.BindInt64(7, now.ToTimeT());
+ s.BindInt(8, 1);
+ ASSERT_TRUE(s.Run());
+ }
+
+ // Re-open the db using the HistoryDatabase, which should migrate from version
+ // 23 to 24, creating the new tables and creating the new path, reason,
+ // and danger columns.
+ CreateBackendAndDatabase();
+ DeleteBackend();
+ {
+ // Re-open the db for manual manipulation.
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ {
+ // The version should have been updated.
+ int cur_version = HistoryDatabase::GetCurrentVersion();
+ ASSERT_LT(23, cur_version);
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT value FROM meta WHERE key = 'version'"));
+ EXPECT_TRUE(s.Step());
+ EXPECT_EQ(cur_version, s.ColumnInt(0));
+ }
+ {
+ base::Time nowish(base::Time::FromTimeT(now.ToTimeT()));
+
+ // Confirm downloads table is valid.
+ sql::Statement statement(db.GetUniqueStatement(
+ "SELECT id, interrupt_reason, current_path, target_path, "
+ " danger_type, start_time, end_time "
+ "FROM downloads ORDER BY id"));
+ EXPECT_TRUE(statement.Step());
+ EXPECT_EQ(1, statement.ColumnInt64(0));
+ EXPECT_EQ(DownloadInterruptReasonToInt(kTestDownloadInterruptReasonNone),
+ statement.ColumnInt(1));
+ EXPECT_EQ("", statement.ColumnString(2));
+ EXPECT_EQ("", statement.ColumnString(3));
+ // Implicit dependence on value of kDangerTypeNotDangerous from
+ // download_database.cc.
+ EXPECT_EQ(0, statement.ColumnInt(4));
+ EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
+ EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
+
+ EXPECT_TRUE(statement.Step());
+ EXPECT_EQ(2, statement.ColumnInt64(0));
+ EXPECT_EQ(DownloadInterruptReasonToInt(kTestDownloadInterruptReasonNone),
+ statement.ColumnInt(1));
+ EXPECT_EQ("/path/to/some/file", statement.ColumnString(2));
+ EXPECT_EQ("/path/to/some/file", statement.ColumnString(3));
+ EXPECT_EQ(0, statement.ColumnInt(4));
+ EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
+ EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
+
+ EXPECT_FALSE(statement.Step());
+ }
+ {
+ // Confirm downloads_url_chains table is valid.
+ sql::Statement statement(db.GetUniqueStatement(
+ "SELECT id, chain_index, url FROM downloads_url_chains "
+ " ORDER BY id, chain_index"));
+ EXPECT_TRUE(statement.Step());
+ EXPECT_EQ(1, statement.ColumnInt64(0));
+ EXPECT_EQ(0, statement.ColumnInt(1));
+ EXPECT_EQ("http://whatever.com/index.html", statement.ColumnString(2));
+
+ EXPECT_TRUE(statement.Step());
+ EXPECT_EQ(2, statement.ColumnInt64(0));
+ EXPECT_EQ(0, statement.ColumnInt(1));
+ EXPECT_EQ("http://whatever.com/index1.html", statement.ColumnString(2));
+
+ EXPECT_FALSE(statement.Step());
+ }
+ }
+}
+
+TEST_F(HistoryBackendDBTest, MigrateReferrer) {
+ base::Time now(base::Time::Now());
+ ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
+ {
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO downloads (id, full_path, url, start_time, "
+ "received_bytes, total_bytes, state, end_time, opened) VALUES "
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+ int64 db_handle = 0;
+ s.BindInt64(0, ++db_handle);
+ s.BindString(1, "full_path");
+ s.BindString(2, "http://whatever.com/index.html");
+ s.BindInt64(3, now.ToTimeT());
+ s.BindInt64(4, 100);
+ s.BindInt64(5, 100);
+ s.BindInt(6, 1);
+ s.BindInt64(7, now.ToTimeT());
+ s.BindInt(8, 1);
+ ASSERT_TRUE(s.Run());
+ }
+ // Re-open the db using the HistoryDatabase, which should migrate to version
+ // 26, creating the referrer column.
+ CreateBackendAndDatabase();
+ DeleteBackend();
+ {
+ // Re-open the db for manual manipulation.
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ // The version should have been updated.
+ int cur_version = HistoryDatabase::GetCurrentVersion();
+ ASSERT_LE(26, cur_version);
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT value FROM meta WHERE key = 'version'"));
+ EXPECT_TRUE(s.Step());
+ EXPECT_EQ(cur_version, s.ColumnInt(0));
+ }
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT referrer from downloads"));
+ EXPECT_TRUE(s.Step());
+ EXPECT_EQ(std::string(), s.ColumnString(0));
+ }
+ }
+}
+
+TEST_F(HistoryBackendDBTest, MigrateDownloadedByExtension) {
+ base::Time now(base::Time::Now());
+ ASSERT_NO_FATAL_FAILURE(CreateDBVersion(26));
+ {
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO downloads (id, current_path, target_path, start_time, "
+ "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
+ "end_time, opened, referrer) VALUES "
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+ s.BindInt64(0, 1);
+ s.BindString(1, "current_path");
+ s.BindString(2, "target_path");
+ s.BindInt64(3, now.ToTimeT());
+ s.BindInt64(4, 100);
+ s.BindInt64(5, 100);
+ s.BindInt(6, 1);
+ s.BindInt(7, 0);
+ s.BindInt(8, 0);
+ s.BindInt64(9, now.ToTimeT());
+ s.BindInt(10, 1);
+ s.BindString(11, "referrer");
+ ASSERT_TRUE(s.Run());
+ }
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
+ "(?, ?, ?)"));
+ s.BindInt64(0, 4);
+ s.BindInt64(1, 0);
+ s.BindString(2, "url");
+ ASSERT_TRUE(s.Run());
+ }
+ }
+ // Re-open the db using the HistoryDatabase, which should migrate to version
+ // 27, creating the by_ext_id and by_ext_name columns.
+ CreateBackendAndDatabase();
+ DeleteBackend();
+ {
+ // Re-open the db for manual manipulation.
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ // The version should have been updated.
+ int cur_version = HistoryDatabase::GetCurrentVersion();
+ ASSERT_LE(27, cur_version);
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT value FROM meta WHERE key = 'version'"));
+ EXPECT_TRUE(s.Step());
+ EXPECT_EQ(cur_version, s.ColumnInt(0));
+ }
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT by_ext_id, by_ext_name from downloads"));
+ EXPECT_TRUE(s.Step());
+ EXPECT_EQ(std::string(), s.ColumnString(0));
+ EXPECT_EQ(std::string(), s.ColumnString(1));
+ }
+ }
+}
+
+TEST_F(HistoryBackendDBTest, MigrateDownloadValidators) {
+ base::Time now(base::Time::Now());
+ ASSERT_NO_FATAL_FAILURE(CreateDBVersion(27));
+ {
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO downloads (id, current_path, target_path, start_time, "
+ "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
+ "end_time, opened, referrer, by_ext_id, by_ext_name) VALUES "
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+ s.BindInt64(0, 1);
+ s.BindString(1, "current_path");
+ s.BindString(2, "target_path");
+ s.BindInt64(3, now.ToTimeT());
+ s.BindInt64(4, 100);
+ s.BindInt64(5, 100);
+ s.BindInt(6, 1);
+ s.BindInt(7, 0);
+ s.BindInt(8, 0);
+ s.BindInt64(9, now.ToTimeT());
+ s.BindInt(10, 1);
+ s.BindString(11, "referrer");
+ s.BindString(12, "by extension ID");
+ s.BindString(13, "by extension name");
+ ASSERT_TRUE(s.Run());
+ }
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
+ "(?, ?, ?)"));
+ s.BindInt64(0, 4);
+ s.BindInt64(1, 0);
+ s.BindString(2, "url");
+ ASSERT_TRUE(s.Run());
+ }
+ }
+ // Re-open the db using the HistoryDatabase, which should migrate to the
+ // current version, creating the etag and last_modified columns.
+ CreateBackendAndDatabase();
+ DeleteBackend();
+ {
+ // Re-open the db for manual manipulation.
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ // The version should have been updated.
+ int cur_version = HistoryDatabase::GetCurrentVersion();
+ ASSERT_LE(28, cur_version);
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT value FROM meta WHERE key = 'version'"));
+ EXPECT_TRUE(s.Step());
+ EXPECT_EQ(cur_version, s.ColumnInt(0));
+ }
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT etag, last_modified from downloads"));
+ EXPECT_TRUE(s.Step());
+ EXPECT_EQ(std::string(), s.ColumnString(0));
+ EXPECT_EQ(std::string(), s.ColumnString(1));
+ }
+ }
+}
+
+TEST_F(HistoryBackendDBTest, PurgeArchivedDatabase) {
+ ASSERT_NO_FATAL_FAILURE(CreateDBVersion(27));
+ ASSERT_NO_FATAL_FAILURE(CreateArchivedDB());
+
+ ASSERT_TRUE(base::PathExists(history_dir_.Append(kArchivedHistoryFilename)));
+
+ CreateBackendAndDatabase();
+ DeleteBackend();
+
+ // We do not retain expired history entries in an archived database as of M37.
+ // Verify that any legacy archived database is deleted on start-up.
+ ASSERT_FALSE(base::PathExists(history_dir_.Append(kArchivedHistoryFilename)));
+}
+
+TEST_F(HistoryBackendDBTest, MigrateDownloadMimeType) {
+ base::Time now(base::Time::Now());
+ ASSERT_NO_FATAL_FAILURE(CreateDBVersion(28));
+ {
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO downloads (id, current_path, target_path, start_time, "
+ "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
+ "end_time, opened, referrer, by_ext_id, by_ext_name, etag, "
+ "last_modified) VALUES "
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+ s.BindInt64(0, 1);
+ s.BindString(1, "current_path");
+ s.BindString(2, "target_path");
+ s.BindInt64(3, now.ToTimeT());
+ s.BindInt64(4, 100);
+ s.BindInt64(5, 100);
+ s.BindInt(6, 1);
+ s.BindInt(7, 0);
+ s.BindInt(8, 0);
+ s.BindInt64(9, now.ToTimeT());
+ s.BindInt(10, 1);
+ s.BindString(11, "referrer");
+ s.BindString(12, "by extension ID");
+ s.BindString(13, "by extension name");
+ s.BindString(14, "etag");
+ s.BindInt64(15, now.ToTimeT());
+ ASSERT_TRUE(s.Run());
+ }
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
+ "(?, ?, ?)"));
+ s.BindInt64(0, 4);
+ s.BindInt64(1, 0);
+ s.BindString(2, "url");
+ ASSERT_TRUE(s.Run());
+ }
+ }
+ // Re-open the db using the HistoryDatabase, which should migrate to the
+ // current version, creating themime_type abd original_mime_type columns.
+ CreateBackendAndDatabase();
+ DeleteBackend();
+ {
+ // Re-open the db for manual manipulation.
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ // The version should have been updated.
+ int cur_version = HistoryDatabase::GetCurrentVersion();
+ ASSERT_LE(29, cur_version);
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT value FROM meta WHERE key = 'version'"));
+ EXPECT_TRUE(s.Step());
+ EXPECT_EQ(cur_version, s.ColumnInt(0));
+ }
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT mime_type, original_mime_type from downloads"));
+ EXPECT_TRUE(s.Step());
+ EXPECT_EQ(std::string(), s.ColumnString(0));
+ EXPECT_EQ(std::string(), s.ColumnString(1));
+ }
+ }
+}
+
+TEST_F(HistoryBackendDBTest, ConfirmDownloadRowCreateAndDelete) {
+ // Create the DB.
+ CreateBackendAndDatabase();
+
+ base::Time now(base::Time::Now());
+
+ // Add some downloads.
+ uint32 id1 = 1, id2 = 2, id3 = 3;
+ AddDownload(id1, DownloadState::COMPLETE, now);
+ AddDownload(id2, DownloadState::COMPLETE, now + base::TimeDelta::FromDays(2));
+ AddDownload(id3, DownloadState::COMPLETE, now - base::TimeDelta::FromDays(2));
+
+ // Confirm that resulted in the correct number of rows in the DB.
+ DeleteBackend();
+ {
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ sql::Statement statement(db.GetUniqueStatement(
+ "Select Count(*) from downloads"));
+ EXPECT_TRUE(statement.Step());
+ EXPECT_EQ(3, statement.ColumnInt(0));
+
+ sql::Statement statement1(db.GetUniqueStatement(
+ "Select Count(*) from downloads_url_chains"));
+ EXPECT_TRUE(statement1.Step());
+ EXPECT_EQ(3, statement1.ColumnInt(0));
+ }
+
+ // Delete some rows and make sure the results are still correct.
+ CreateBackendAndDatabase();
+ db_->RemoveDownload(id2);
+ db_->RemoveDownload(id3);
+ DeleteBackend();
+ {
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ sql::Statement statement(db.GetUniqueStatement(
+ "Select Count(*) from downloads"));
+ EXPECT_TRUE(statement.Step());
+ EXPECT_EQ(1, statement.ColumnInt(0));
+
+ sql::Statement statement1(db.GetUniqueStatement(
+ "Select Count(*) from downloads_url_chains"));
+ EXPECT_TRUE(statement1.Step());
+ EXPECT_EQ(1, statement1.ColumnInt(0));
+ }
+}
+
+TEST_F(HistoryBackendDBTest, DownloadNukeRecordsMissingURLs) {
+ CreateBackendAndDatabase();
+ base::Time now(base::Time::Now());
+ std::vector<GURL> url_chain;
+ DownloadRow download(base::FilePath(FILE_PATH_LITERAL("foo-path")),
+ base::FilePath(FILE_PATH_LITERAL("foo-path")),
+ url_chain,
+ GURL(std::string()),
+ "application/octet-stream",
+ "application/octet-stream",
+ now,
+ now,
+ std::string(),
+ std::string(),
+ 0,
+ 512,
+ DownloadState::COMPLETE,
+ DownloadDangerType::NOT_DANGEROUS,
+ kTestDownloadInterruptReasonNone,
+ 1,
+ 0,
+ "by_ext_id",
+ "by_ext_name");
+
+ // Creating records without any urls should fail.
+ EXPECT_FALSE(db_->CreateDownload(download));
+
+ download.url_chain.push_back(GURL("foo-url"));
+ EXPECT_TRUE(db_->CreateDownload(download));
+
+ // Pretend that the URLs were dropped.
+ DeleteBackend();
+ {
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ sql::Statement statement(db.GetUniqueStatement(
+ "DELETE FROM downloads_url_chains WHERE id=1"));
+ ASSERT_TRUE(statement.Run());
+ }
+ CreateBackendAndDatabase();
+ std::vector<DownloadRow> downloads;
+ db_->QueryDownloads(&downloads);
+ EXPECT_EQ(0U, downloads.size());
+
+ // QueryDownloads should have nuked the corrupt record.
+ DeleteBackend();
+ {
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ {
+ sql::Statement statement(db.GetUniqueStatement(
+ "SELECT count(*) from downloads"));
+ ASSERT_TRUE(statement.Step());
+ EXPECT_EQ(0, statement.ColumnInt(0));
+ }
+ }
+}
+
+TEST_F(HistoryBackendDBTest, ConfirmDownloadInProgressCleanup) {
+ // Create the DB.
+ CreateBackendAndDatabase();
+
+ base::Time now(base::Time::Now());
+
+ // Put an IN_PROGRESS download in the DB.
+ AddDownload(1, DownloadState::IN_PROGRESS, now);
+
+ // Confirm that they made it into the DB unchanged.
+ DeleteBackend();
+ {
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ sql::Statement statement(db.GetUniqueStatement(
+ "Select Count(*) from downloads"));
+ EXPECT_TRUE(statement.Step());
+ EXPECT_EQ(1, statement.ColumnInt(0));
+
+ sql::Statement statement1(db.GetUniqueStatement(
+ "Select state, interrupt_reason from downloads"));
+ EXPECT_TRUE(statement1.Step());
+ EXPECT_EQ(DownloadStateToInt(DownloadState::IN_PROGRESS),
+ statement1.ColumnInt(0));
+ EXPECT_EQ(DownloadInterruptReasonToInt(kTestDownloadInterruptReasonNone),
+ statement1.ColumnInt(1));
+ EXPECT_FALSE(statement1.Step());
+ }
+
+ // Read in the DB through query downloads, then test that the
+ // right transformation was returned.
+ CreateBackendAndDatabase();
+ std::vector<DownloadRow> results;
+ db_->QueryDownloads(&results);
+ ASSERT_EQ(1u, results.size());
+ EXPECT_EQ(DownloadState::INTERRUPTED, results[0].state);
+ EXPECT_EQ(kTestDownloadInterruptReasonCrash, results[0].interrupt_reason);
+
+ // Allow the update to propagate, shut down the DB, and confirm that
+ // the query updated the on disk database as well.
+ base::MessageLoop::current()->RunUntilIdle();
+ DeleteBackend();
+ {
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ sql::Statement statement(db.GetUniqueStatement(
+ "Select Count(*) from downloads"));
+ EXPECT_TRUE(statement.Step());
+ EXPECT_EQ(1, statement.ColumnInt(0));
+
+ sql::Statement statement1(db.GetUniqueStatement(
+ "Select state, interrupt_reason from downloads"));
+ EXPECT_TRUE(statement1.Step());
+ EXPECT_EQ(DownloadStateToInt(DownloadState::INTERRUPTED),
+ statement1.ColumnInt(0));
+ EXPECT_EQ(DownloadInterruptReasonToInt(kTestDownloadInterruptReasonCrash),
+ statement1.ColumnInt(1));
+ EXPECT_FALSE(statement1.Step());
+ }
+}
+
+TEST_F(HistoryBackendDBTest, MigratePresentations) {
+ // Create the db we want. Use 22 since segments didn't change in that time
+ // frame.
+ ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
+
+ const SegmentID segment_id = 2;
+ const URLID url_id = 3;
+ const GURL url("http://www.foo.com");
+ const std::string url_name(VisitSegmentDatabase::ComputeSegmentName(url));
+ const base::string16 title(base::ASCIIToUTF16("Title1"));
+ const base::Time segment_time(base::Time::Now());
+
+ {
+ // Re-open the db for manual manipulation.
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+
+ // Add an entry to urls.
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO urls "
+ "(id, url, title, last_visit_time) VALUES "
+ "(?, ?, ?, ?)"));
+ s.BindInt64(0, url_id);
+ s.BindString(1, url.spec());
+ s.BindString16(2, title);
+ s.BindInt64(3, segment_time.ToInternalValue());
+ ASSERT_TRUE(s.Run());
+ }
+
+ // Add an entry to segments.
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO segments "
+ "(id, name, url_id, pres_index) VALUES "
+ "(?, ?, ?, ?)"));
+ s.BindInt64(0, segment_id);
+ s.BindString(1, url_name);
+ s.BindInt64(2, url_id);
+ s.BindInt(3, 4); // pres_index
+ ASSERT_TRUE(s.Run());
+ }
+
+ // And one to segment_usage.
+ {
+ sql::Statement s(db.GetUniqueStatement(
+ "INSERT INTO segment_usage "
+ "(id, segment_id, time_slot, visit_count) VALUES "
+ "(?, ?, ?, ?)"));
+ s.BindInt64(0, 4); // id.
+ s.BindInt64(1, segment_id);
+ s.BindInt64(2, segment_time.ToInternalValue());
+ s.BindInt(3, 5); // visit count.
+ ASSERT_TRUE(s.Run());
+ }
+ }
+
+ // Re-open the db, triggering migration.
+ CreateBackendAndDatabase();
+
+ std::vector<PageUsageData*> results;
+ db_->QuerySegmentUsage(segment_time, 10, &results);
+ ASSERT_EQ(1u, results.size());
+ EXPECT_EQ(url, results[0]->GetURL());
+ EXPECT_EQ(segment_id, results[0]->GetID());
+ EXPECT_EQ(title, results[0]->GetTitle());
+ STLDeleteElements(&results);
+}
+
+} // namespace
+} // namespace history
diff --git a/components/history/core/browser/history_backend_unittest.cc b/components/history/core/browser/history_backend_unittest.cc
index b6d1d8e33..d16e89d 100644
--- a/components/history/core/browser/history_backend_unittest.cc
+++ b/components/history/core/browser/history_backend_unittest.cc
@@ -2,759 +2,3326 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// History unit tests come in two flavors:
-//
-// 1. The more complicated style is that the unit test creates a full history
-// service. This spawns a background thread for the history backend, and
-// all communication is asynchronous. This is useful for testing more
-// complicated things or end-to-end behavior.
-//
-// 2. The simpler style is to create a history backend on this thread and
-// access it directly without a HistoryService object. This is much simpler
-// because communication is synchronous. Generally, sets should go through
-// the history backend (since there is a lot of logic) but gets can come
-// directly from the HistoryDatabase. This is because the backend generally
-// has no logic in the getter except threading stuff, which we don't want
-// to run.
-
#include "components/history/core/browser/history_backend.h"
+#include <algorithm>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/prefs/pref_service.h"
+#include "base/run_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
-#include "components/history/core/browser/download_constants.h"
-#include "components/history/core/browser/download_row.h"
+#include "components/favicon_base/favicon_usage_data.h"
#include "components/history/core/browser/history_constants.h"
-#include "components/history/core/browser/history_database.h"
-#include "components/history/core/browser/page_usage_data.h"
-#include "components/history/core/test/history_backend_base_test.h"
+#include "components/history/core/browser/history_database_params.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history/core/browser/history_service_observer.h"
+#include "components/history/core/browser/in_memory_database.h"
+#include "components/history/core/browser/in_memory_history_backend.h"
+#include "components/history/core/browser/keyword_search_term.h"
+#include "components/history/core/browser/visit_delegate.h"
+#include "components/history/core/browser/visit_filter.h"
+#include "components/history/core/test/database_test_utils.h"
+#include "components/history/core/test/history_client_fake_bookmarks.h"
#include "components/history/core/test/test_history_database.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "url/gurl.h"
+
+// This file only tests functionality where it is most convenient to call the
+// backend directly. Most of the history backend functions are tested by the
+// history unit test. Because of the elaborate callbacks involved, this is no
+// harder than calling it directly for many things.
-namespace history {
namespace {
-// This must be outside the anonymous namespace for the friend statement in
-// HistoryBackend to work.
-class HistoryBackendDBTest : public HistoryBackendBaseTest {
+const int kTinyEdgeSize = 10;
+const int kSmallEdgeSize = 16;
+const int kLargeEdgeSize = 32;
+
+const char kAcceptLanguagesForTest[] = "en-US,en";
+
+const gfx::Size kTinySize = gfx::Size(kTinyEdgeSize, kTinyEdgeSize);
+const gfx::Size kSmallSize = gfx::Size(kSmallEdgeSize, kSmallEdgeSize);
+const gfx::Size kLargeSize = gfx::Size(kLargeEdgeSize, kLargeEdgeSize);
+
+typedef base::Callback<void(const history::URLRow*,
+ const history::URLRow*,
+ const history::URLRow*)>
+ SimulateNotificationCallback;
+
+class HistoryClientMock : public history::HistoryClientFakeBookmarks {
public:
- HistoryBackendDBTest() {}
- ~HistoryBackendDBTest() override {}
+ MOCK_METHOD0(BlockUntilBookmarksLoaded, void());
};
-TEST_F(HistoryBackendDBTest, ClearBrowsingData_Downloads) {
- CreateBackendAndDatabase();
-
- // Initially there should be nothing in the downloads database.
- std::vector<DownloadRow> downloads;
- db_->QueryDownloads(&downloads);
- EXPECT_EQ(0U, downloads.size());
-
- // Add a download, test that it was added correctly, remove it, test that it
- // was removed.
- base::Time now = base::Time();
- uint32 id = 1;
- EXPECT_TRUE(AddDownload(id, DownloadState::COMPLETE, base::Time()));
- db_->QueryDownloads(&downloads);
- EXPECT_EQ(1U, downloads.size());
-
- EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("current-path")),
- downloads[0].current_path);
- EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("target-path")),
- downloads[0].target_path);
- EXPECT_EQ(1UL, downloads[0].url_chain.size());
- EXPECT_EQ(GURL("foo-url"), downloads[0].url_chain[0]);
- EXPECT_EQ(std::string("http://referrer.com/"),
- std::string(downloads[0].referrer_url.spec()));
- EXPECT_EQ(now, downloads[0].start_time);
- EXPECT_EQ(now, downloads[0].end_time);
- EXPECT_EQ(0, downloads[0].received_bytes);
- EXPECT_EQ(512, downloads[0].total_bytes);
- EXPECT_EQ(DownloadState::COMPLETE, downloads[0].state);
- EXPECT_EQ(DownloadDangerType::NOT_DANGEROUS, downloads[0].danger_type);
- EXPECT_EQ(kTestDownloadInterruptReasonNone, downloads[0].interrupt_reason);
- EXPECT_FALSE(downloads[0].opened);
- EXPECT_EQ("by_ext_id", downloads[0].by_ext_id);
- EXPECT_EQ("by_ext_name", downloads[0].by_ext_name);
- EXPECT_EQ("application/vnd.oasis.opendocument.text", downloads[0].mime_type);
- EXPECT_EQ("application/octet-stream", downloads[0].original_mime_type);
-
- db_->QueryDownloads(&downloads);
- EXPECT_EQ(1U, downloads.size());
- db_->RemoveDownload(id);
- db_->QueryDownloads(&downloads);
- EXPECT_EQ(0U, downloads.size());
-}
-
-TEST_F(HistoryBackendDBTest, MigrateDownloadsState) {
- // Create the db we want.
- ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
- {
- // Open the db for manual manipulation.
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
-
- // Manually insert corrupted rows; there's infrastructure in place now to
- // make this impossible, at least according to the test above.
- for (int state = 0; state < 5; ++state) {
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO downloads (id, full_path, url, start_time, "
- "received_bytes, total_bytes, state, end_time, opened) VALUES "
- "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
- s.BindInt64(0, 1 + state);
- s.BindString(1, "path");
- s.BindString(2, "url");
- s.BindInt64(3, base::Time::Now().ToTimeT());
- s.BindInt64(4, 100);
- s.BindInt64(5, 100);
- s.BindInt(6, state);
- s.BindInt64(7, base::Time::Now().ToTimeT());
- s.BindInt(8, state % 2);
- ASSERT_TRUE(s.Run());
- }
+void SimulateNotificationURLVisited(history::HistoryServiceObserver* observer,
+ const history::URLRow* row1,
+ const history::URLRow* row2,
+ const history::URLRow* row3) {
+ history::URLRows rows;
+ rows.push_back(*row1);
+ if (row2)
+ rows.push_back(*row2);
+ if (row3)
+ rows.push_back(*row3);
+
+ base::Time visit_time;
+ history::RedirectList redirects;
+ for (const auto& row : rows) {
+ observer->OnURLVisited(nullptr, ui::PAGE_TRANSITION_LINK, row, redirects,
+ visit_time);
}
+}
- // Re-open the db using the HistoryDatabase, which should migrate from version
- // 22 to the current version, fixing just the row whose state was 3.
- // Then close the db so that we can re-open it directly.
- CreateBackendAndDatabase();
- DeleteBackend();
- {
- // Re-open the db for manual manipulation.
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- {
- // The version should have been updated.
- int cur_version = HistoryDatabase::GetCurrentVersion();
- ASSERT_LT(22, cur_version);
- sql::Statement s(db.GetUniqueStatement(
- "SELECT value FROM meta WHERE key = 'version'"));
- EXPECT_TRUE(s.Step());
- EXPECT_EQ(cur_version, s.ColumnInt(0));
- }
- {
- sql::Statement statement(db.GetUniqueStatement(
- "SELECT id, state, opened "
- "FROM downloads "
- "ORDER BY id"));
- int counter = 0;
- while (statement.Step()) {
- EXPECT_EQ(1 + counter, statement.ColumnInt64(0));
- // The only thing that migration should have changed was state from 3 to
- // 4.
- EXPECT_EQ(((counter == 3) ? 4 : counter), statement.ColumnInt(1));
- EXPECT_EQ(counter % 2, statement.ColumnInt(2));
- ++counter;
- }
- EXPECT_EQ(5, counter);
- }
+void SimulateNotificationURLsModified(history::HistoryServiceObserver* observer,
+ const history::URLRow* row1,
+ const history::URLRow* row2,
+ const history::URLRow* row3) {
+ history::URLRows rows;
+ rows.push_back(*row1);
+ if (row2)
+ rows.push_back(*row2);
+ if (row3)
+ rows.push_back(*row3);
+
+ observer->OnURLsModified(nullptr, rows);
+}
+
+} // namespace
+
+namespace history {
+
+class HistoryBackendTestBase;
+
+// This must be a separate object since HistoryBackend manages its lifetime.
+// This just forwards the messages we're interested in to the test object.
+class HistoryBackendTestDelegate : public HistoryBackend::Delegate {
+ public:
+ explicit HistoryBackendTestDelegate(HistoryBackendTestBase* test)
+ : test_(test) {}
+
+ void NotifyProfileError(sql::InitStatus init_status) override {}
+ void SetInMemoryBackend(scoped_ptr<InMemoryHistoryBackend> backend) override;
+ void NotifyFaviconChanged(const std::set<GURL>& urls) override;
+ void NotifyURLVisited(ui::PageTransition transition,
+ const URLRow& row,
+ const RedirectList& redirects,
+ base::Time visit_time) override;
+ void NotifyURLsModified(const URLRows& changed_urls) override;
+ void NotifyURLsDeleted(bool all_history,
+ bool expired,
+ const URLRows& deleted_rows,
+ const std::set<GURL>& favicon_urls) override;
+ void NotifyKeywordSearchTermUpdated(const URLRow& row,
+ KeywordID keyword_id,
+ const base::string16& term) override;
+ void NotifyKeywordSearchTermDeleted(URLID url_id) override;
+ void DBLoaded() override;
+
+ private:
+ // Not owned by us.
+ HistoryBackendTestBase* test_;
+
+ DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate);
+};
+
+class HistoryBackendTestBase : public testing::Test {
+ public:
+ typedef std::vector<std::pair<ui::PageTransition, URLRow>> URLVisitedList;
+ typedef std::vector<URLRows> URLsModifiedList;
+ typedef std::vector<std::pair<bool, bool>> URLsDeletedList;
+
+ HistoryBackendTestBase()
+ : loaded_(false), favicon_changed_notifications_(0) {}
+
+ ~HistoryBackendTestBase() override {}
+
+ protected:
+ int favicon_changed_notifications() const {
+ return favicon_changed_notifications_;
+ }
+
+ void ClearFaviconChangedNotificationCounter() {
+ favicon_changed_notifications_ = 0;
+ }
+
+ int num_url_visited_notifications() const {
+ return url_visited_notifications_.size();
+ }
+
+ const URLVisitedList& url_visited_notifications() const {
+ return url_visited_notifications_;
+ }
+
+ int num_urls_modified_notifications() const {
+ return urls_modified_notifications_.size();
+ }
+
+ const URLsModifiedList& urls_modified_notifications() const {
+ return urls_modified_notifications_;
}
+
+ const URLsDeletedList& urls_deleted_notifications() const {
+ return urls_deleted_notifications_;
+ }
+
+ void ClearBroadcastedNotifications() {
+ url_visited_notifications_.clear();
+ urls_modified_notifications_.clear();
+ urls_deleted_notifications_.clear();
+ }
+
+ base::FilePath test_dir() { return test_dir_; }
+
+ void NotifyFaviconChanged(const std::set<GURL>& changed_favicons) {
+ ++favicon_changed_notifications_;
+ }
+
+ void NotifyURLVisited(ui::PageTransition transition,
+ const URLRow& row,
+ const RedirectList& redirects,
+ base::Time visit_time) {
+ // Send the notifications directly to the in-memory database.
+ mem_backend_->OnURLVisited(nullptr, transition, row, redirects, visit_time);
+ url_visited_notifications_.push_back(std::make_pair(transition, row));
+ }
+
+ void NotifyURLsModified(const URLRows& changed_urls) {
+ // Send the notifications directly to the in-memory database.
+ mem_backend_->OnURLsModified(nullptr, changed_urls);
+ urls_modified_notifications_.push_back(changed_urls);
+ }
+
+ void NotifyURLsDeleted(bool all_history,
+ bool expired,
+ const URLRows& deleted_rows,
+ const std::set<GURL>& favicon_urls) {
+ mem_backend_->OnURLsDeleted(nullptr, all_history, expired, deleted_rows,
+ favicon_urls);
+ urls_deleted_notifications_.push_back(std::make_pair(all_history, expired));
+ }
+
+ void NotifyKeywordSearchTermUpdated(const URLRow& row,
+ KeywordID keyword_id,
+ const base::string16& term) {
+ mem_backend_->OnKeywordSearchTermUpdated(nullptr, row, keyword_id, term);
+ }
+
+ void NotifyKeywordSearchTermDeleted(URLID url_id) {
+ mem_backend_->OnKeywordSearchTermDeleted(nullptr, url_id);
+ }
+
+ history::HistoryClientFakeBookmarks history_client_;
+ scoped_refptr<HistoryBackend> backend_; // Will be NULL on init failure.
+ scoped_ptr<InMemoryHistoryBackend> mem_backend_;
+ bool loaded_;
+
+ private:
+ friend class HistoryBackendTestDelegate;
+
+ // testing::Test
+ void SetUp() override {
+ ClearFaviconChangedNotificationCounter();
+ if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"),
+ &test_dir_))
+ return;
+ backend_ = new HistoryBackend(new HistoryBackendTestDelegate(this),
+ &history_client_);
+ backend_->Init(std::string(), false,
+ TestHistoryDatabaseParamsForPath(test_dir_));
+ }
+
+ void TearDown() override {
+ if (backend_.get())
+ backend_->Closing();
+ backend_ = NULL;
+ mem_backend_.reset();
+ base::DeleteFile(test_dir_, true);
+ base::RunLoop().RunUntilIdle();
+ history_client_.ClearAllBookmarks();
+ }
+
+ void SetInMemoryBackend(scoped_ptr<InMemoryHistoryBackend> backend) {
+ mem_backend_.swap(backend);
+ }
+
+ // The types and details of notifications which were broadcasted.
+ int favicon_changed_notifications_;
+ URLVisitedList url_visited_notifications_;
+ URLsModifiedList urls_modified_notifications_;
+ URLsDeletedList urls_deleted_notifications_;
+
+ base::MessageLoop message_loop_;
+ base::FilePath test_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestBase);
+};
+
+void HistoryBackendTestDelegate::SetInMemoryBackend(
+ scoped_ptr<InMemoryHistoryBackend> backend) {
+ test_->SetInMemoryBackend(backend.Pass());
}
-TEST_F(HistoryBackendDBTest, MigrateDownloadsReasonPathsAndDangerType) {
- base::Time now(base::Time::Now());
-
- // Create the db we want. The schema didn't change from 22->23, so just
- // re-use the v22 file.
- ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
- {
- // Re-open the db for manual manipulation.
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
-
- // Manually insert some rows.
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO downloads (id, full_path, url, start_time, "
- "received_bytes, total_bytes, state, end_time, opened) VALUES "
- "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
-
- int64 id = 0;
- // Null path.
- s.BindInt64(0, ++id);
- s.BindString(1, std::string());
- s.BindString(2, "http://whatever.com/index.html");
- s.BindInt64(3, now.ToTimeT());
- s.BindInt64(4, 100);
- s.BindInt64(5, 100);
- s.BindInt(6, 1);
- s.BindInt64(7, now.ToTimeT());
- s.BindInt(8, 1);
- ASSERT_TRUE(s.Run());
- s.Reset(true);
-
- // Non-null path.
- s.BindInt64(0, ++id);
- s.BindString(1, "/path/to/some/file");
- s.BindString(2, "http://whatever.com/index1.html");
- s.BindInt64(3, now.ToTimeT());
- s.BindInt64(4, 100);
- s.BindInt64(5, 100);
- s.BindInt(6, 1);
- s.BindInt64(7, now.ToTimeT());
- s.BindInt(8, 1);
- ASSERT_TRUE(s.Run());
- }
-
- // Re-open the db using the HistoryDatabase, which should migrate from version
- // 23 to 24, creating the new tables and creating the new path, reason,
- // and danger columns.
- CreateBackendAndDatabase();
- DeleteBackend();
- {
- // Re-open the db for manual manipulation.
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- {
- // The version should have been updated.
- int cur_version = HistoryDatabase::GetCurrentVersion();
- ASSERT_LT(23, cur_version);
- sql::Statement s(db.GetUniqueStatement(
- "SELECT value FROM meta WHERE key = 'version'"));
- EXPECT_TRUE(s.Step());
- EXPECT_EQ(cur_version, s.ColumnInt(0));
- }
- {
- base::Time nowish(base::Time::FromTimeT(now.ToTimeT()));
-
- // Confirm downloads table is valid.
- sql::Statement statement(db.GetUniqueStatement(
- "SELECT id, interrupt_reason, current_path, target_path, "
- " danger_type, start_time, end_time "
- "FROM downloads ORDER BY id"));
- EXPECT_TRUE(statement.Step());
- EXPECT_EQ(1, statement.ColumnInt64(0));
- EXPECT_EQ(DownloadInterruptReasonToInt(kTestDownloadInterruptReasonNone),
- statement.ColumnInt(1));
- EXPECT_EQ("", statement.ColumnString(2));
- EXPECT_EQ("", statement.ColumnString(3));
- // Implicit dependence on value of kDangerTypeNotDangerous from
- // download_database.cc.
- EXPECT_EQ(0, statement.ColumnInt(4));
- EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
- EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
-
- EXPECT_TRUE(statement.Step());
- EXPECT_EQ(2, statement.ColumnInt64(0));
- EXPECT_EQ(DownloadInterruptReasonToInt(kTestDownloadInterruptReasonNone),
- statement.ColumnInt(1));
- EXPECT_EQ("/path/to/some/file", statement.ColumnString(2));
- EXPECT_EQ("/path/to/some/file", statement.ColumnString(3));
- EXPECT_EQ(0, statement.ColumnInt(4));
- EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
- EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
-
- EXPECT_FALSE(statement.Step());
- }
- {
- // Confirm downloads_url_chains table is valid.
- sql::Statement statement(db.GetUniqueStatement(
- "SELECT id, chain_index, url FROM downloads_url_chains "
- " ORDER BY id, chain_index"));
- EXPECT_TRUE(statement.Step());
- EXPECT_EQ(1, statement.ColumnInt64(0));
- EXPECT_EQ(0, statement.ColumnInt(1));
- EXPECT_EQ("http://whatever.com/index.html", statement.ColumnString(2));
-
- EXPECT_TRUE(statement.Step());
- EXPECT_EQ(2, statement.ColumnInt64(0));
- EXPECT_EQ(0, statement.ColumnInt(1));
- EXPECT_EQ("http://whatever.com/index1.html", statement.ColumnString(2));
-
- EXPECT_FALSE(statement.Step());
+void HistoryBackendTestDelegate::NotifyFaviconChanged(
+ const std::set<GURL>& changed_favicons) {
+ test_->NotifyFaviconChanged(changed_favicons);
+}
+
+void HistoryBackendTestDelegate::NotifyURLVisited(ui::PageTransition transition,
+ const URLRow& row,
+ const RedirectList& redirects,
+ base::Time visit_time) {
+ test_->NotifyURLVisited(transition, row, redirects, visit_time);
+}
+
+void HistoryBackendTestDelegate::NotifyURLsModified(
+ const URLRows& changed_urls) {
+ test_->NotifyURLsModified(changed_urls);
+}
+
+void HistoryBackendTestDelegate::NotifyURLsDeleted(
+ bool all_history,
+ bool expired,
+ const URLRows& deleted_rows,
+ const std::set<GURL>& favicon_urls) {
+ test_->NotifyURLsDeleted(all_history, expired, deleted_rows, favicon_urls);
+}
+
+void HistoryBackendTestDelegate::NotifyKeywordSearchTermUpdated(
+ const URLRow& row,
+ KeywordID keyword_id,
+ const base::string16& term) {
+ test_->NotifyKeywordSearchTermUpdated(row, keyword_id, term);
+}
+
+void HistoryBackendTestDelegate::NotifyKeywordSearchTermDeleted(URLID url_id) {
+ test_->NotifyKeywordSearchTermDeleted(url_id);
+}
+
+void HistoryBackendTestDelegate::DBLoaded() {
+ test_->loaded_ = true;
+}
+
+class HistoryBackendTest : public HistoryBackendTestBase {
+ public:
+ HistoryBackendTest() {}
+ ~HistoryBackendTest() override {}
+
+ protected:
+ void AddRedirectChain(const char* sequence[], int nav_entry_id) {
+ AddRedirectChainWithTransitionAndTime(
+ sequence, nav_entry_id, ui::PAGE_TRANSITION_LINK, base::Time::Now());
+ }
+
+ void AddRedirectChainWithTransitionAndTime(const char* sequence[],
+ int nav_entry_id,
+ ui::PageTransition transition,
+ base::Time time) {
+ history::RedirectList redirects;
+ for (int i = 0; sequence[i] != NULL; ++i)
+ redirects.push_back(GURL(sequence[i]));
+
+ ContextID context_id = reinterpret_cast<ContextID>(1);
+ history::HistoryAddPageArgs request(
+ redirects.back(), time, context_id, nav_entry_id, GURL(),
+ redirects, transition, history::SOURCE_BROWSED,
+ true);
+ backend_->AddPage(request);
+ }
+
+ // Adds CLIENT_REDIRECT page transition.
+ // |url1| is the source URL and |url2| is the destination.
+ // |did_replace| is true if the transition is non-user initiated and the
+ // navigation entry for |url2| has replaced that for |url1|. The possibly
+ // updated transition code of the visit records for |url1| and |url2| is
+ // returned by filling in |*transition1| and |*transition2|, respectively.
+ // |time| is a time of the redirect.
+ void AddClientRedirect(const GURL& url1,
+ const GURL& url2,
+ bool did_replace,
+ base::Time time,
+ int* transition1,
+ int* transition2) {
+ ContextID dummy_context_id = reinterpret_cast<ContextID>(0x87654321);
+ history::RedirectList redirects;
+ if (url1.is_valid())
+ redirects.push_back(url1);
+ if (url2.is_valid())
+ redirects.push_back(url2);
+ HistoryAddPageArgs request(
+ url2, time, dummy_context_id, 0, url1,
+ redirects, ui::PAGE_TRANSITION_CLIENT_REDIRECT,
+ history::SOURCE_BROWSED, did_replace);
+ backend_->AddPage(request);
+
+ *transition1 = GetTransition(url1);
+ *transition2 = GetTransition(url2);
+ }
+
+ int GetTransition(const GURL& url) {
+ if (!url.is_valid())
+ return 0;
+ URLRow row;
+ URLID id = backend_->db()->GetRowForURL(url, &row);
+ VisitVector visits;
+ EXPECT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ return visits[0].transition;
+ }
+
+ // Returns a vector with the small and large edge sizes.
+ const std::vector<int> GetEdgeSizesSmallAndLarge() {
+ std::vector<int> sizes_small_and_large;
+ sizes_small_and_large.push_back(kSmallEdgeSize);
+ sizes_small_and_large.push_back(kLargeEdgeSize);
+ return sizes_small_and_large;
+ }
+
+ // Returns the number of icon mappings of |icon_type| to |page_url|.
+ size_t NumIconMappingsForPageURL(const GURL& page_url,
+ favicon_base::IconType icon_type) {
+ std::vector<IconMapping> icon_mappings;
+ backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_type,
+ &icon_mappings);
+ return icon_mappings.size();
+ }
+
+ // Returns the icon mappings for |page_url| sorted alphabetically by icon
+ // URL in ascending order. Returns true if there is at least one icon
+ // mapping.
+ bool GetSortedIconMappingsForPageURL(
+ const GURL& page_url,
+ std::vector<IconMapping>* icon_mappings) {
+ if (!backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
+ icon_mappings)) {
+ return false;
}
+ std::sort(icon_mappings->begin(), icon_mappings->end(),
+ [](const history::IconMapping& a, const history::IconMapping& b) {
+ return a.icon_url < b.icon_url;
+ });
+ return true;
+ }
+
+ // Returns the favicon bitmaps for |icon_id| sorted by pixel size in
+ // ascending order. Returns true if there is at least one favicon bitmap.
+ bool GetSortedFaviconBitmaps(favicon_base::FaviconID icon_id,
+ std::vector<FaviconBitmap>* favicon_bitmaps) {
+ if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, favicon_bitmaps))
+ return false;
+ std::sort(
+ favicon_bitmaps->begin(), favicon_bitmaps->end(),
+ [](const history::FaviconBitmap& a, const history::FaviconBitmap& b) {
+ return a.pixel_size.GetArea() < b.pixel_size.GetArea();
+ });
+ return true;
+ }
+
+ // Returns true if there is exactly one favicon bitmap associated to
+ // |favicon_id|. If true, returns favicon bitmap in output parameter.
+ bool GetOnlyFaviconBitmap(const favicon_base::FaviconID icon_id,
+ FaviconBitmap* favicon_bitmap) {
+ std::vector<FaviconBitmap> favicon_bitmaps;
+ if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, &favicon_bitmaps))
+ return false;
+ if (favicon_bitmaps.size() != 1)
+ return false;
+ *favicon_bitmap = favicon_bitmaps[0];
+ return true;
+ }
+
+ // Creates an |edge_size|x|edge_size| bitmap of |color|.
+ SkBitmap CreateBitmap(SkColor color, int edge_size) {
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(edge_size, edge_size);
+ bitmap.eraseColor(color);
+ return bitmap;
+ }
+
+ // Returns true if |bitmap_data| is equal to |expected_data|.
+ bool BitmapDataEqual(char expected_data,
+ scoped_refptr<base::RefCountedMemory> bitmap_data) {
+ return bitmap_data.get() &&
+ bitmap_data->size() == 1u &&
+ *bitmap_data->front() == expected_data;
+ }
+
+ // Returns true if |bitmap_data| is of |color|.
+ bool BitmapColorEqual(SkColor expected_color,
+ scoped_refptr<base::RefCountedMemory> bitmap_data) {
+ SkBitmap bitmap;
+ if (!gfx::PNGCodec::Decode(bitmap_data->front(), bitmap_data->size(),
+ &bitmap))
+ return false;
+ SkAutoLockPixels bitmap_lock(bitmap);
+ return expected_color == bitmap.getColor(0, 0);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HistoryBackendTest);
+};
+
+class InMemoryHistoryBackendTest : public HistoryBackendTestBase {
+ public:
+ InMemoryHistoryBackendTest() {}
+ ~InMemoryHistoryBackendTest() override {}
+
+ protected:
+ void SimulateNotificationURLsDeleted(const URLRow* row1,
+ const URLRow* row2 = NULL,
+ const URLRow* row3 = NULL) {
+ URLRows rows;
+ rows.push_back(*row1);
+ if (row2) rows.push_back(*row2);
+ if (row3) rows.push_back(*row3);
+
+ NotifyURLsDeleted(false, false, rows, std::set<GURL>());
+ }
+
+ size_t GetNumberOfMatchingSearchTerms(const int keyword_id,
+ const base::string16& prefix) {
+ std::vector<KeywordSearchTermVisit> matching_terms;
+ mem_backend_->db()->GetMostRecentKeywordSearchTerms(
+ keyword_id, prefix, 1, &matching_terms);
+ return matching_terms.size();
+ }
+
+ static URLRow CreateTestTypedURL() {
+ URLRow url_row(GURL("https://www.google.com/"));
+ url_row.set_id(10);
+ url_row.set_title(base::UTF8ToUTF16("Google Search"));
+ url_row.set_typed_count(1);
+ url_row.set_visit_count(1);
+ url_row.set_last_visit(base::Time::Now() - base::TimeDelta::FromHours(1));
+ return url_row;
+ }
+
+ static URLRow CreateAnotherTestTypedURL() {
+ URLRow url_row(GURL("https://maps.google.com/"));
+ url_row.set_id(20);
+ url_row.set_title(base::UTF8ToUTF16("Google Maps"));
+ url_row.set_typed_count(2);
+ url_row.set_visit_count(3);
+ url_row.set_last_visit(base::Time::Now() - base::TimeDelta::FromHours(2));
+ return url_row;
}
+
+ static URLRow CreateTestNonTypedURL() {
+ URLRow url_row(GURL("https://news.google.com/"));
+ url_row.set_id(30);
+ url_row.set_title(base::UTF8ToUTF16("Google News"));
+ url_row.set_visit_count(5);
+ url_row.set_last_visit(base::Time::Now() - base::TimeDelta::FromHours(3));
+ return url_row;
+ }
+
+ void PopulateTestURLsAndSearchTerms(URLRow* row1,
+ URLRow* row2,
+ const base::string16& term1,
+ const base::string16& term2);
+
+ void TestAddingAndChangingURLRows(
+ const SimulateNotificationCallback& callback);
+
+ static const KeywordID kTestKeywordId;
+ static const char kTestSearchTerm1[];
+ static const char kTestSearchTerm2[];
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InMemoryHistoryBackendTest);
+};
+
+const KeywordID InMemoryHistoryBackendTest::kTestKeywordId = 42;
+const char InMemoryHistoryBackendTest::kTestSearchTerm1[] = "banana";
+const char InMemoryHistoryBackendTest::kTestSearchTerm2[] = "orange";
+
+// http://crbug.com/114287
+#if defined(OS_WIN)
+#define MAYBE_Loaded DISABLED_Loaded
+#else
+#define MAYBE_Loaded Loaded
+#endif // defined(OS_WIN)
+TEST_F(HistoryBackendTest, MAYBE_Loaded) {
+ ASSERT_TRUE(backend_.get());
+ ASSERT_TRUE(loaded_);
}
-TEST_F(HistoryBackendDBTest, MigrateReferrer) {
- base::Time now(base::Time::Now());
- ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
- {
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO downloads (id, full_path, url, start_time, "
- "received_bytes, total_bytes, state, end_time, opened) VALUES "
- "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
- int64 db_handle = 0;
- s.BindInt64(0, ++db_handle);
- s.BindString(1, "full_path");
- s.BindString(2, "http://whatever.com/index.html");
- s.BindInt64(3, now.ToTimeT());
- s.BindInt64(4, 100);
- s.BindInt64(5, 100);
- s.BindInt(6, 1);
- s.BindInt64(7, now.ToTimeT());
- s.BindInt(8, 1);
- ASSERT_TRUE(s.Run());
- }
- // Re-open the db using the HistoryDatabase, which should migrate to version
- // 26, creating the referrer column.
- CreateBackendAndDatabase();
- DeleteBackend();
- {
- // Re-open the db for manual manipulation.
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- // The version should have been updated.
- int cur_version = HistoryDatabase::GetCurrentVersion();
- ASSERT_LE(26, cur_version);
- {
- sql::Statement s(db.GetUniqueStatement(
- "SELECT value FROM meta WHERE key = 'version'"));
- EXPECT_TRUE(s.Step());
- EXPECT_EQ(cur_version, s.ColumnInt(0));
- }
- {
- sql::Statement s(db.GetUniqueStatement(
- "SELECT referrer from downloads"));
- EXPECT_TRUE(s.Step());
- EXPECT_EQ(std::string(), s.ColumnString(0));
- }
+TEST_F(HistoryBackendTest, DeleteAll) {
+ ASSERT_TRUE(backend_.get());
+
+ // Add two favicons, each with two bitmaps. Note that we add favicon2 before
+ // adding favicon1. This is so that favicon1 one gets ID 2 autoassigned to
+ // the database, which will change when the other one is deleted. This way
+ // we can test that updating works properly.
+ GURL favicon_url1("http://www.google.com/favicon.ico");
+ GURL favicon_url2("http://news.google.com/favicon.ico");
+ favicon_base::FaviconID favicon2 =
+ backend_->thumbnail_db_->AddFavicon(favicon_url2, favicon_base::FAVICON);
+ favicon_base::FaviconID favicon1 =
+ backend_->thumbnail_db_->AddFavicon(favicon_url1, favicon_base::FAVICON);
+
+ std::vector<unsigned char> data;
+ data.push_back('a');
+ EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
+ new base::RefCountedBytes(data), base::Time::Now(), kSmallSize));
+ data[0] = 'b';
+ EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
+ new base::RefCountedBytes(data), base::Time::Now(), kLargeSize));
+
+ data[0] = 'c';
+ EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
+ new base::RefCountedBytes(data), base::Time::Now(), kSmallSize));
+ data[0] = 'd';
+ EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
+ new base::RefCountedBytes(data), base::Time::Now(), kLargeSize));
+
+ // First visit two URLs.
+ URLRow row1(GURL("http://www.google.com/"));
+ row1.set_visit_count(2);
+ row1.set_typed_count(1);
+ row1.set_last_visit(base::Time::Now());
+ backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1);
+
+ URLRow row2(GURL("http://news.google.com/"));
+ row2.set_visit_count(1);
+ row2.set_last_visit(base::Time::Now());
+ backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2);
+
+ URLRows rows;
+ rows.push_back(row2); // Reversed order for the same reason as favicons.
+ rows.push_back(row1);
+ backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
+
+ URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
+ URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
+
+ // Get the two visits for the URLs we just added.
+ VisitVector visits;
+ backend_->db_->GetVisitsForURL(row1_id, &visits);
+ ASSERT_EQ(1U, visits.size());
+
+ visits.clear();
+ backend_->db_->GetVisitsForURL(row2_id, &visits);
+ ASSERT_EQ(1U, visits.size());
+
+ // The in-memory backend should have been set and it should have gotten the
+ // typed URL.
+ ASSERT_TRUE(mem_backend_.get());
+ URLRow outrow1;
+ EXPECT_TRUE(mem_backend_->db_->GetRowForURL(row1.url(), NULL));
+
+ // Star row1.
+ history_client_.AddBookmark(row1.url());
+
+ // Now finally clear all history.
+ ClearBroadcastedNotifications();
+ backend_->DeleteAllHistory();
+
+ // The first URL should be preserved but the time should be cleared.
+ EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1));
+ EXPECT_EQ(row1.url(), outrow1.url());
+ EXPECT_EQ(0, outrow1.visit_count());
+ EXPECT_EQ(0, outrow1.typed_count());
+ EXPECT_TRUE(base::Time() == outrow1.last_visit());
+
+ // The second row should be deleted.
+ URLRow outrow2;
+ EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &outrow2));
+
+ // All visits should be deleted for both URLs.
+ VisitVector all_visits;
+ backend_->db_->GetAllVisitsInRange(base::Time(), base::Time(), 0,
+ &all_visits);
+ ASSERT_EQ(0U, all_visits.size());
+
+ // We should have a favicon and favicon bitmaps for the first URL only. We
+ // look them up by favicon URL since the IDs may have changed.
+ favicon_base::FaviconID out_favicon1 =
+ backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ favicon_url1, favicon_base::FAVICON, NULL);
+ EXPECT_TRUE(out_favicon1);
+
+ std::vector<FaviconBitmap> favicon_bitmaps;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
+ out_favicon1, &favicon_bitmaps));
+ ASSERT_EQ(2u, favicon_bitmaps.size());
+
+ FaviconBitmap favicon_bitmap1 = favicon_bitmaps[0];
+ FaviconBitmap favicon_bitmap2 = favicon_bitmaps[1];
+
+ // Favicon bitmaps do not need to be in particular order.
+ if (favicon_bitmap1.pixel_size == kLargeSize) {
+ FaviconBitmap tmp_favicon_bitmap = favicon_bitmap1;
+ favicon_bitmap1 = favicon_bitmap2;
+ favicon_bitmap2 = tmp_favicon_bitmap;
}
+
+ EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap1.bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmap1.pixel_size);
+
+ EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap2.bitmap_data));
+ EXPECT_EQ(kLargeSize, favicon_bitmap2.pixel_size);
+
+ favicon_base::FaviconID out_favicon2 =
+ backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ favicon_url2, favicon_base::FAVICON, NULL);
+ EXPECT_FALSE(out_favicon2) << "Favicon not deleted";
+
+ // The remaining URL should still reference the same favicon, even if its
+ // ID has changed.
+ std::vector<IconMapping> mappings;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+ outrow1.url(), favicon_base::FAVICON, &mappings));
+ EXPECT_EQ(1u, mappings.size());
+ EXPECT_EQ(out_favicon1, mappings[0].icon_id);
+
+ // The first URL should still be bookmarked.
+ EXPECT_TRUE(history_client_.IsBookmarked(row1.url()));
+
+ // Check that we fire the notification about all history having been deleted.
+ ASSERT_EQ(1u, urls_deleted_notifications().size());
+ EXPECT_TRUE(urls_deleted_notifications()[0].first);
+ EXPECT_FALSE(urls_deleted_notifications()[0].second);
}
-TEST_F(HistoryBackendDBTest, MigrateDownloadedByExtension) {
- base::Time now(base::Time::Now());
- ASSERT_NO_FATAL_FAILURE(CreateDBVersion(26));
- {
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- {
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO downloads (id, current_path, target_path, start_time, "
- "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
- "end_time, opened, referrer) VALUES "
- "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
- s.BindInt64(0, 1);
- s.BindString(1, "current_path");
- s.BindString(2, "target_path");
- s.BindInt64(3, now.ToTimeT());
- s.BindInt64(4, 100);
- s.BindInt64(5, 100);
- s.BindInt(6, 1);
- s.BindInt(7, 0);
- s.BindInt(8, 0);
- s.BindInt64(9, now.ToTimeT());
- s.BindInt(10, 1);
- s.BindString(11, "referrer");
- ASSERT_TRUE(s.Run());
- }
- {
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
- "(?, ?, ?)"));
- s.BindInt64(0, 4);
- s.BindInt64(1, 0);
- s.BindString(2, "url");
- ASSERT_TRUE(s.Run());
+// Checks that adding a visit, then calling DeleteAll, and then trying to add
+// data for the visited page works. This can happen when clearing the history
+// immediately after visiting a page.
+TEST_F(HistoryBackendTest, DeleteAllThenAddData) {
+ ASSERT_TRUE(backend_.get());
+
+ base::Time visit_time = base::Time::Now();
+ GURL url("http://www.google.com/");
+ HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
+ history::RedirectList(),
+ ui::PAGE_TRANSITION_KEYWORD_GENERATED,
+ history::SOURCE_BROWSED, false);
+ backend_->AddPage(request);
+
+ // Check that a row was added.
+ URLRow outrow;
+ EXPECT_TRUE(backend_->db_->GetRowForURL(url, &outrow));
+
+ // Check that the visit was added.
+ VisitVector all_visits;
+ backend_->db_->GetAllVisitsInRange(base::Time(), base::Time(), 0,
+ &all_visits);
+ ASSERT_EQ(1U, all_visits.size());
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ // The row should be deleted.
+ EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
+
+ // The visit should be deleted.
+ backend_->db_->GetAllVisitsInRange(base::Time(), base::Time(), 0,
+ &all_visits);
+ ASSERT_EQ(0U, all_visits.size());
+
+ // Try and set the title.
+ backend_->SetPageTitle(url, base::UTF8ToUTF16("Title"));
+
+ // The row should still be deleted.
+ EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
+
+ // The visit should still be deleted.
+ backend_->db_->GetAllVisitsInRange(base::Time(), base::Time(), 0,
+ &all_visits);
+ ASSERT_EQ(0U, all_visits.size());
+}
+
+TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {
+ GURL favicon_url1("http://www.google.com/favicon.ico");
+ GURL favicon_url2("http://news.google.com/favicon.ico");
+
+ std::vector<unsigned char> data;
+ data.push_back('1');
+ favicon_base::FaviconID favicon1 =
+ backend_->thumbnail_db_->AddFavicon(favicon_url1,
+ favicon_base::FAVICON,
+ new base::RefCountedBytes(data),
+ base::Time::Now(),
+ gfx::Size());
+
+ data[0] = '2';
+ favicon_base::FaviconID favicon2 =
+ backend_->thumbnail_db_->AddFavicon(favicon_url2,
+ favicon_base::FAVICON,
+ new base::RefCountedBytes(data),
+ base::Time::Now(),
+ gfx::Size());
+
+ // First visit two URLs.
+ URLRow row1(GURL("http://www.google.com/"));
+ row1.set_visit_count(2);
+ row1.set_typed_count(1);
+ row1.set_last_visit(base::Time::Now());
+ EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
+
+ URLRow row2(GURL("http://news.google.com/"));
+ row2.set_visit_count(1);
+ row2.set_last_visit(base::Time::Now());
+ EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2));
+
+ URLRows rows;
+ rows.push_back(row2); // Reversed order for the same reason as favicons.
+ rows.push_back(row1);
+ backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
+
+ URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
+ URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
+
+ // Star the two URLs.
+ history_client_.AddBookmark(row1.url());
+ history_client_.AddBookmark(row2.url());
+
+ // Delete url 2.
+ backend_->expirer_.DeleteURL(row2.url());
+ EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), NULL));
+ VisitVector visits;
+ backend_->db_->GetVisitsForURL(row2_id, &visits);
+ EXPECT_EQ(0U, visits.size());
+ // The favicon should still be valid.
+ EXPECT_EQ(favicon2,
+ backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ favicon_url2, favicon_base::FAVICON, NULL));
+
+ // Unstar row2.
+ history_client_.DelBookmark(row2.url());
+
+ // Tell the backend it was unstarred. We have to explicitly do this as
+ // BookmarkModel isn't wired up to the backend during testing.
+ std::set<GURL> unstarred_urls;
+ unstarred_urls.insert(row2.url());
+ backend_->URLsNoLongerBookmarked(unstarred_urls);
+
+ // The URL should still not exist.
+ EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), NULL));
+ // And the favicon should be deleted.
+ EXPECT_EQ(0,
+ backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ favicon_url2, favicon_base::FAVICON, NULL));
+
+ // Unstar row 1.
+ history_client_.DelBookmark(row1.url());
+
+ // Tell the backend it was unstarred. We have to explicitly do this as
+ // BookmarkModel isn't wired up to the backend during testing.
+ unstarred_urls.clear();
+ unstarred_urls.insert(row1.url());
+ backend_->URLsNoLongerBookmarked(unstarred_urls);
+
+ // The URL should still exist (because there were visits).
+ EXPECT_EQ(row1_id, backend_->db_->GetRowForURL(row1.url(), NULL));
+
+ // There should still be visits.
+ visits.clear();
+ backend_->db_->GetVisitsForURL(row1_id, &visits);
+ EXPECT_EQ(1U, visits.size());
+
+ // The favicon should still be valid.
+ EXPECT_EQ(favicon1,
+ backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ favicon_url1, favicon_base::FAVICON, NULL));
+}
+
+// Tests a handful of assertions for a navigation with a type of
+// KEYWORD_GENERATED.
+TEST_F(HistoryBackendTest, KeywordGenerated) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url("http://google.com");
+
+ base::Time visit_time = base::Time::Now() - base::TimeDelta::FromDays(1);
+ HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
+ history::RedirectList(),
+ ui::PAGE_TRANSITION_KEYWORD_GENERATED,
+ history::SOURCE_BROWSED, false);
+ backend_->AddPage(request);
+
+ // A row should have been added for the url.
+ URLRow row;
+ URLID url_id = backend_->db()->GetRowForURL(url, &row);
+ ASSERT_NE(0, url_id);
+
+ // The typed count should be 1.
+ ASSERT_EQ(1, row.typed_count());
+
+ // KEYWORD_GENERATED urls should not be added to the segment db.
+ std::string segment_name = VisitSegmentDatabase::ComputeSegmentName(url);
+ EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment_name));
+
+ // One visit should be added.
+ VisitVector visits;
+ EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
+ EXPECT_EQ(1U, visits.size());
+
+ // But no visible visits.
+ visits.clear();
+ QueryOptions query_options;
+ query_options.max_count = 1;
+ backend_->db()->GetVisibleVisitsInRange(query_options, &visits);
+ EXPECT_TRUE(visits.empty());
+
+ // Going back to the same entry should not increment the typed count.
+ ui::PageTransition back_transition = ui::PageTransitionFromInt(
+ ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FORWARD_BACK);
+ HistoryAddPageArgs back_request(url, visit_time, NULL, 0, GURL(),
+ history::RedirectList(), back_transition,
+ history::SOURCE_BROWSED, false);
+ backend_->AddPage(back_request);
+ url_id = backend_->db()->GetRowForURL(url, &row);
+ ASSERT_NE(0, url_id);
+ ASSERT_EQ(1, row.typed_count());
+
+ // Expire the visits.
+ std::set<GURL> restrict_urls;
+ backend_->expire_backend()->ExpireHistoryBetween(restrict_urls, visit_time,
+ base::Time::Now());
+
+ // The visit should have been nuked.
+ visits.clear();
+ EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
+ EXPECT_TRUE(visits.empty());
+
+ // As well as the url.
+ ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row));
+}
+
+TEST_F(HistoryBackendTest, ClientRedirect) {
+ ASSERT_TRUE(backend_.get());
+
+ int transition1;
+ int transition2;
+
+ // Initial transition to page A.
+ GURL url_a("http://google.com/a");
+ AddClientRedirect(GURL(), url_a, false, base::Time(),
+ &transition1, &transition2);
+ EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END);
+
+ // User initiated redirect to page B.
+ GURL url_b("http://google.com/b");
+ AddClientRedirect(url_a, url_b, false, base::Time(),
+ &transition1, &transition2);
+ EXPECT_TRUE(transition1 & ui::PAGE_TRANSITION_CHAIN_END);
+ EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END);
+
+ // Non-user initiated redirect to page C.
+ GURL url_c("http://google.com/c");
+ AddClientRedirect(url_b, url_c, true, base::Time(),
+ &transition1, &transition2);
+ EXPECT_FALSE(transition1 & ui::PAGE_TRANSITION_CHAIN_END);
+ EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END);
+}
+
+TEST_F(HistoryBackendTest, AddPagesWithDetails) {
+ ASSERT_TRUE(backend_.get());
+
+ // Import one non-typed URL, and two recent and one expired typed URLs.
+ URLRow row1(GURL("https://news.google.com/"));
+ row1.set_visit_count(1);
+ row1.set_last_visit(base::Time::Now());
+ URLRow row2(GURL("https://www.google.com/"));
+ row2.set_typed_count(1);
+ row2.set_last_visit(base::Time::Now());
+ URLRow row3(GURL("https://mail.google.com/"));
+ row3.set_visit_count(1);
+ row3.set_typed_count(1);
+ row3.set_last_visit(base::Time::Now() - base::TimeDelta::FromDays(7 - 1));
+ URLRow row4(GURL("https://maps.google.com/"));
+ row4.set_visit_count(1);
+ row4.set_typed_count(1);
+ row4.set_last_visit(base::Time::Now() - base::TimeDelta::FromDays(365 + 2));
+
+ URLRows rows;
+ rows.push_back(row1);
+ rows.push_back(row2);
+ rows.push_back(row3);
+ rows.push_back(row4);
+ backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
+
+ // Verify that recent URLs have ended up in the main |db_|, while the already
+ // expired URL has been ignored.
+ URLRow stored_row1, stored_row2, stored_row3, stored_row4;
+ EXPECT_NE(0, backend_->db_->GetRowForURL(row1.url(), &stored_row1));
+ EXPECT_NE(0, backend_->db_->GetRowForURL(row2.url(), &stored_row2));
+ EXPECT_NE(0, backend_->db_->GetRowForURL(row3.url(), &stored_row3));
+ EXPECT_EQ(0, backend_->db_->GetRowForURL(row4.url(), &stored_row4));
+
+ // Ensure that a notification was fired for both typed and non-typed URLs.
+ // Further verify that the IDs in the notification are set to those that are
+ // in effect in the main database. The InMemoryHistoryBackend relies on this
+ // for caching.
+ ASSERT_EQ(1, num_urls_modified_notifications());
+
+ const URLRows& changed_urls = urls_modified_notifications()[0];
+ EXPECT_EQ(3u, changed_urls.size());
+
+ URLRows::const_iterator it_row1 =
+ std::find_if(changed_urls.begin(), changed_urls.end(),
+ history::URLRow::URLRowHasURL(row1.url()));
+ ASSERT_NE(changed_urls.end(), it_row1);
+ EXPECT_EQ(stored_row1.id(), it_row1->id());
+
+ URLRows::const_iterator it_row2 =
+ std::find_if(changed_urls.begin(), changed_urls.end(),
+ history::URLRow::URLRowHasURL(row2.url()));
+ ASSERT_NE(changed_urls.end(), it_row2);
+ EXPECT_EQ(stored_row2.id(), it_row2->id());
+
+ URLRows::const_iterator it_row3 =
+ std::find_if(changed_urls.begin(), changed_urls.end(),
+ history::URLRow::URLRowHasURL(row3.url()));
+ ASSERT_NE(changed_urls.end(), it_row3);
+ EXPECT_EQ(stored_row3.id(), it_row3->id());
+}
+
+TEST_F(HistoryBackendTest, UpdateURLs) {
+ ASSERT_TRUE(backend_.get());
+
+ // Add three pages directly to the database.
+ URLRow row1(GURL("https://news.google.com/"));
+ row1.set_visit_count(1);
+ row1.set_last_visit(base::Time::Now());
+ URLRow row2(GURL("https://maps.google.com/"));
+ row2.set_visit_count(2);
+ row2.set_last_visit(base::Time::Now());
+ URLRow row3(GURL("https://www.google.com/"));
+ row3.set_visit_count(3);
+ row3.set_last_visit(base::Time::Now());
+
+ backend_->db_->AddURL(row1);
+ backend_->db_->AddURL(row2);
+ backend_->db_->AddURL(row3);
+
+ // Now create changed versions of all URLRows by incrementing their visit
+ // counts, and in the meantime, also delete the second row from the database.
+ URLRow altered_row1, altered_row2, altered_row3;
+ backend_->db_->GetRowForURL(row1.url(), &altered_row1);
+ altered_row1.set_visit_count(42);
+ backend_->db_->GetRowForURL(row2.url(), &altered_row2);
+ altered_row2.set_visit_count(43);
+ backend_->db_->GetRowForURL(row3.url(), &altered_row3);
+ altered_row3.set_visit_count(44);
+
+ backend_->db_->DeleteURLRow(altered_row2.id());
+
+ // Now try to update all three rows at once. The change to the second URLRow
+ // should be ignored, as it is no longer present in the DB.
+ URLRows rows;
+ rows.push_back(altered_row1);
+ rows.push_back(altered_row2);
+ rows.push_back(altered_row3);
+ EXPECT_EQ(2u, backend_->UpdateURLs(rows));
+
+ URLRow stored_row1, stored_row3;
+ EXPECT_NE(0, backend_->db_->GetRowForURL(row1.url(), &stored_row1));
+ EXPECT_NE(0, backend_->db_->GetRowForURL(row3.url(), &stored_row3));
+ EXPECT_EQ(altered_row1.visit_count(), stored_row1.visit_count());
+ EXPECT_EQ(altered_row3.visit_count(), stored_row3.visit_count());
+
+ // Ensure that a notification was fired, and further verify that the IDs in
+ // the notification are set to those that are in effect in the main database.
+ // The InMemoryHistoryBackend relies on this for caching.
+ ASSERT_EQ(1, num_urls_modified_notifications());
+
+ const URLRows& changed_urls = urls_modified_notifications()[0];
+ EXPECT_EQ(2u, changed_urls.size());
+
+ URLRows::const_iterator it_row1 =
+ std::find_if(changed_urls.begin(), changed_urls.end(),
+ history::URLRow::URLRowHasURL(row1.url()));
+ ASSERT_NE(changed_urls.end(), it_row1);
+ EXPECT_EQ(altered_row1.id(), it_row1->id());
+ EXPECT_EQ(altered_row1.visit_count(), it_row1->visit_count());
+
+ URLRows::const_iterator it_row3 =
+ std::find_if(changed_urls.begin(), changed_urls.end(),
+ history::URLRow::URLRowHasURL(row3.url()));
+ ASSERT_NE(changed_urls.end(), it_row3);
+ EXPECT_EQ(altered_row3.id(), it_row3->id());
+ EXPECT_EQ(altered_row3.visit_count(), it_row3->visit_count());
+}
+
+// This verifies that a notification is fired. In-depth testing of logic should
+// be done in HistoryTest.SetTitle.
+TEST_F(HistoryBackendTest, SetPageTitleFiresNotificationWithCorrectDetails) {
+ const char kTestUrlTitle[] = "Google Search";
+
+ ASSERT_TRUE(backend_.get());
+
+ // Add two pages, then change the title of the second one.
+ URLRow row1(GURL("https://news.google.com/"));
+ row1.set_typed_count(1);
+ row1.set_last_visit(base::Time::Now());
+ URLRow row2(GURL("https://www.google.com/"));
+ row2.set_visit_count(2);
+ row2.set_last_visit(base::Time::Now());
+
+ URLRows rows;
+ rows.push_back(row1);
+ rows.push_back(row2);
+ backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
+
+ ClearBroadcastedNotifications();
+ backend_->SetPageTitle(row2.url(), base::UTF8ToUTF16(kTestUrlTitle));
+
+ // Ensure that a notification was fired, and further verify that the IDs in
+ // the notification are set to those that are in effect in the main database.
+ // The InMemoryHistoryBackend relies on this for caching.
+ URLRow stored_row2;
+ EXPECT_TRUE(backend_->GetURL(row2.url(), &stored_row2));
+ ASSERT_EQ(1, num_urls_modified_notifications());
+
+ const URLRows& changed_urls = urls_modified_notifications()[0];
+ ASSERT_EQ(1u, changed_urls.size());
+ EXPECT_EQ(base::UTF8ToUTF16(kTestUrlTitle), changed_urls[0].title());
+ EXPECT_EQ(stored_row2.id(), changed_urls[0].id());
+}
+
+// There's no importer on Android.
+#if !defined(OS_ANDROID)
+TEST_F(HistoryBackendTest, ImportedFaviconsTest) {
+ // Setup test data - two Urls in the history, one with favicon assigned and
+ // one without.
+ GURL favicon_url1("http://www.google.com/favicon.ico");
+ std::vector<unsigned char> data;
+ data.push_back('1');
+ favicon_base::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
+ favicon_url1,
+ favicon_base::FAVICON,
+ base::RefCountedBytes::TakeVector(&data),
+ base::Time::Now(),
+ gfx::Size());
+ URLRow row1(GURL("http://www.google.com/"));
+ row1.set_visit_count(1);
+ row1.set_last_visit(base::Time::Now());
+ EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
+
+ URLRow row2(GURL("http://news.google.com/"));
+ row2.set_visit_count(1);
+ row2.set_last_visit(base::Time::Now());
+ URLRows rows;
+ rows.push_back(row1);
+ rows.push_back(row2);
+ backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
+ URLRow url_row1, url_row2;
+ EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
+ EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(row1.url(), favicon_base::FAVICON));
+ EXPECT_EQ(0u, NumIconMappingsForPageURL(row2.url(), favicon_base::FAVICON));
+
+ // Now provide one imported favicon for both URLs already in the registry.
+ // The new favicon should only be used with the URL that doesn't already have
+ // a favicon.
+ favicon_base::FaviconUsageDataList favicons;
+ favicon_base::FaviconUsageData favicon;
+ favicon.favicon_url = GURL("http://news.google.com/favicon.ico");
+ favicon.png_data.push_back('2');
+ favicon.urls.insert(row1.url());
+ favicon.urls.insert(row2.url());
+ favicons.push_back(favicon);
+ backend_->SetImportedFavicons(favicons);
+ EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
+ EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
+
+ std::vector<IconMapping> mappings;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+ row1.url(), favicon_base::FAVICON, &mappings));
+ EXPECT_EQ(1u, mappings.size());
+ EXPECT_EQ(favicon1, mappings[0].icon_id);
+ EXPECT_EQ(favicon_url1, mappings[0].icon_url);
+
+ mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+ row2.url(), favicon_base::FAVICON, &mappings));
+ EXPECT_EQ(1u, mappings.size());
+ EXPECT_EQ(favicon.favicon_url, mappings[0].icon_url);
+
+ // A URL should not be added to history (to store favicon), if
+ // the URL is not bookmarked.
+ GURL url3("http://mail.google.com");
+ favicons.clear();
+ favicon.favicon_url = GURL("http://mail.google.com/favicon.ico");
+ favicon.png_data.push_back('3');
+ favicon.urls.insert(url3);
+ favicons.push_back(favicon);
+ backend_->SetImportedFavicons(favicons);
+ URLRow url_row3;
+ EXPECT_TRUE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
+
+ // If the URL is bookmarked, it should get added to history with 0 visits.
+ history_client_.AddBookmark(url3);
+ backend_->SetImportedFavicons(favicons);
+ EXPECT_FALSE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
+ EXPECT_TRUE(url_row3.visit_count() == 0);
+}
+#endif // !defined(OS_ANDROID)
+
+TEST_F(HistoryBackendTest, StripUsernamePasswordTest) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url("http://anyuser:anypass@www.google.com");
+ GURL stripped_url("http://www.google.com");
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ // Visit the url with username, password.
+ backend_->AddPageVisit(url, base::Time::Now(), 0,
+ ui::PageTransitionFromInt(
+ ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
+ history::SOURCE_BROWSED);
+
+ // Fetch the row information about stripped url from history db.
+ VisitVector visits;
+ URLID row_id = backend_->db_->GetRowForURL(stripped_url, NULL);
+ backend_->db_->GetVisitsForURL(row_id, &visits);
+
+ // Check if stripped url is stored in database.
+ ASSERT_EQ(1U, visits.size());
+}
+
+TEST_F(HistoryBackendTest, AddPageVisitBackForward) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url("http://www.google.com");
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ // Visit the url after typing it.
+ backend_->AddPageVisit(url, base::Time::Now(), 0,
+ ui::PAGE_TRANSITION_TYPED,
+ history::SOURCE_BROWSED);
+
+ // Ensure both the typed count and visit count are 1.
+ VisitVector visits;
+ URLRow row;
+ URLID id = backend_->db()->GetRowForURL(url, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ EXPECT_EQ(1, row.typed_count());
+ EXPECT_EQ(1, row.visit_count());
+
+ // Visit the url again via back/forward.
+ backend_->AddPageVisit(url, base::Time::Now(), 0,
+ ui::PageTransitionFromInt(
+ ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FORWARD_BACK),
+ history::SOURCE_BROWSED);
+
+ // Ensure the typed count is still 1 but the visit count is 2.
+ id = backend_->db()->GetRowForURL(url, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ EXPECT_EQ(1, row.typed_count());
+ EXPECT_EQ(2, row.visit_count());
+}
+
+TEST_F(HistoryBackendTest, AddPageVisitRedirectBackForward) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url1("http://www.google.com");
+ GURL url2("http://www.chromium.org");
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ // Visit a typed URL with a redirect.
+ backend_->AddPageVisit(url1, base::Time::Now(), 0,
+ ui::PAGE_TRANSITION_TYPED,
+ history::SOURCE_BROWSED);
+ backend_->AddPageVisit(url2, base::Time::Now(), 0,
+ ui::PageTransitionFromInt(
+ ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_CLIENT_REDIRECT),
+ history::SOURCE_BROWSED);
+
+ // Ensure the redirected URL does not count as typed.
+ VisitVector visits;
+ URLRow row;
+ URLID id = backend_->db()->GetRowForURL(url2, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ EXPECT_EQ(0, row.typed_count());
+ EXPECT_EQ(1, row.visit_count());
+
+ // Visit the redirected url again via back/forward.
+ backend_->AddPageVisit(url2, base::Time::Now(), 0,
+ ui::PageTransitionFromInt(
+ ui::PAGE_TRANSITION_TYPED |
+ ui::PAGE_TRANSITION_FORWARD_BACK |
+ ui::PAGE_TRANSITION_CLIENT_REDIRECT),
+ history::SOURCE_BROWSED);
+
+ // Ensure the typed count is still 1 but the visit count is 2.
+ id = backend_->db()->GetRowForURL(url2, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ EXPECT_EQ(0, row.typed_count());
+ EXPECT_EQ(2, row.visit_count());
+}
+
+TEST_F(HistoryBackendTest, AddPageVisitSource) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url("http://www.google.com");
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ // Assume visiting the url from an externsion.
+ backend_->AddPageVisit(
+ url, base::Time::Now(), 0, ui::PAGE_TRANSITION_TYPED,
+ history::SOURCE_EXTENSION);
+ // Assume the url is imported from Firefox.
+ backend_->AddPageVisit(url, base::Time::Now(), 0,
+ ui::PAGE_TRANSITION_TYPED,
+ history::SOURCE_FIREFOX_IMPORTED);
+ // Assume this url is also synced.
+ backend_->AddPageVisit(url, base::Time::Now(), 0,
+ ui::PAGE_TRANSITION_TYPED,
+ history::SOURCE_SYNCED);
+
+ // Fetch the row information about the url from history db.
+ VisitVector visits;
+ URLID row_id = backend_->db_->GetRowForURL(url, NULL);
+ backend_->db_->GetVisitsForURL(row_id, &visits);
+
+ // Check if all the visits to the url are stored in database.
+ ASSERT_EQ(3U, visits.size());
+ VisitSourceMap visit_sources;
+ ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
+ ASSERT_EQ(3U, visit_sources.size());
+ int sources = 0;
+ for (int i = 0; i < 3; i++) {
+ switch (visit_sources[visits[i].visit_id]) {
+ case history::SOURCE_EXTENSION:
+ sources |= 0x1;
+ break;
+ case history::SOURCE_FIREFOX_IMPORTED:
+ sources |= 0x2;
+ break;
+ case history::SOURCE_SYNCED:
+ sources |= 0x4;
+ default:
+ break;
}
}
- // Re-open the db using the HistoryDatabase, which should migrate to version
- // 27, creating the by_ext_id and by_ext_name columns.
- CreateBackendAndDatabase();
- DeleteBackend();
- {
- // Re-open the db for manual manipulation.
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- // The version should have been updated.
- int cur_version = HistoryDatabase::GetCurrentVersion();
- ASSERT_LE(27, cur_version);
- {
- sql::Statement s(db.GetUniqueStatement(
- "SELECT value FROM meta WHERE key = 'version'"));
- EXPECT_TRUE(s.Step());
- EXPECT_EQ(cur_version, s.ColumnInt(0));
- }
- {
- sql::Statement s(db.GetUniqueStatement(
- "SELECT by_ext_id, by_ext_name from downloads"));
- EXPECT_TRUE(s.Step());
- EXPECT_EQ(std::string(), s.ColumnString(0));
- EXPECT_EQ(std::string(), s.ColumnString(1));
- }
+ EXPECT_EQ(0x7, sources);
+}
+
+TEST_F(HistoryBackendTest, AddPageVisitNotLastVisit) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url("http://www.google.com");
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ // Create visit times
+ base::Time recent_time = base::Time::Now();
+ base::TimeDelta visit_age = base::TimeDelta::FromDays(3);
+ base::Time older_time = recent_time - visit_age;
+
+ // Visit the url with recent time.
+ backend_->AddPageVisit(url, recent_time, 0,
+ ui::PageTransitionFromInt(
+ ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
+ history::SOURCE_BROWSED);
+
+ // Add to the url a visit with older time (could be syncing from another
+ // client, etc.).
+ backend_->AddPageVisit(url, older_time, 0,
+ ui::PageTransitionFromInt(
+ ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
+ history::SOURCE_SYNCED);
+
+ // Fetch the row information about url from history db.
+ VisitVector visits;
+ URLRow row;
+ URLID row_id = backend_->db_->GetRowForURL(url, &row);
+ backend_->db_->GetVisitsForURL(row_id, &visits);
+
+ // Last visit time should be the most recent time, not the most recently added
+ // visit.
+ ASSERT_EQ(2U, visits.size());
+ ASSERT_EQ(recent_time, row.last_visit());
+}
+
+TEST_F(HistoryBackendTest, AddPageVisitFiresNotificationWithCorrectDetails) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url1("http://www.google.com");
+ GURL url2("http://maps.google.com");
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+ ClearBroadcastedNotifications();
+
+ // Visit two distinct URLs, the second one twice.
+ backend_->AddPageVisit(url1, base::Time::Now(), 0,
+ ui::PAGE_TRANSITION_LINK,
+ history::SOURCE_BROWSED);
+ for (int i = 0; i < 2; ++i) {
+ backend_->AddPageVisit(url2, base::Time::Now(), 0,
+ ui::PAGE_TRANSITION_TYPED,
+ history::SOURCE_BROWSED);
}
+
+ URLRow stored_row1, stored_row2;
+ EXPECT_NE(0, backend_->db_->GetRowForURL(url1, &stored_row1));
+ EXPECT_NE(0, backend_->db_->GetRowForURL(url2, &stored_row2));
+
+ // Expect that HistoryServiceObserver::OnURLVisited has been called 3 times,
+ // and that each time the URLRows have the correct URLs and IDs set.
+ ASSERT_EQ(3, num_url_visited_notifications());
+ EXPECT_TRUE(ui::PageTransitionCoreTypeIs(url_visited_notifications()[0].first,
+ ui::PAGE_TRANSITION_LINK));
+ EXPECT_EQ(stored_row1.id(), url_visited_notifications()[0].second.id());
+ EXPECT_EQ(stored_row1.url(), url_visited_notifications()[0].second.url());
+
+ EXPECT_TRUE(ui::PageTransitionCoreTypeIs(url_visited_notifications()[1].first,
+ ui::PAGE_TRANSITION_TYPED));
+ EXPECT_EQ(stored_row2.id(), url_visited_notifications()[1].second.id());
+ EXPECT_EQ(stored_row2.url(), url_visited_notifications()[1].second.url());
+
+ EXPECT_TRUE(ui::PageTransitionCoreTypeIs(url_visited_notifications()[2].first,
+ ui::PAGE_TRANSITION_TYPED));
+ EXPECT_EQ(stored_row2.id(), url_visited_notifications()[2].second.id());
+ EXPECT_EQ(stored_row2.url(), url_visited_notifications()[2].second.url());
}
-TEST_F(HistoryBackendDBTest, MigrateDownloadValidators) {
- base::Time now(base::Time::Now());
- ASSERT_NO_FATAL_FAILURE(CreateDBVersion(27));
- {
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- {
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO downloads (id, current_path, target_path, start_time, "
- "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
- "end_time, opened, referrer, by_ext_id, by_ext_name) VALUES "
- "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
- s.BindInt64(0, 1);
- s.BindString(1, "current_path");
- s.BindString(2, "target_path");
- s.BindInt64(3, now.ToTimeT());
- s.BindInt64(4, 100);
- s.BindInt64(5, 100);
- s.BindInt(6, 1);
- s.BindInt(7, 0);
- s.BindInt(8, 0);
- s.BindInt64(9, now.ToTimeT());
- s.BindInt(10, 1);
- s.BindString(11, "referrer");
- s.BindString(12, "by extension ID");
- s.BindString(13, "by extension name");
- ASSERT_TRUE(s.Run());
- }
- {
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
- "(?, ?, ?)"));
- s.BindInt64(0, 4);
- s.BindInt64(1, 0);
- s.BindString(2, "url");
- ASSERT_TRUE(s.Run());
- }
+TEST_F(HistoryBackendTest, AddPageArgsSource) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url("http://testpageargs.com");
+
+ // Assume this page is browsed by user.
+ HistoryAddPageArgs request1(url, base::Time::Now(), NULL, 0, GURL(),
+ history::RedirectList(),
+ ui::PAGE_TRANSITION_KEYWORD_GENERATED,
+ history::SOURCE_BROWSED, false);
+ backend_->AddPage(request1);
+ // Assume this page is synced.
+ HistoryAddPageArgs request2(url, base::Time::Now(), NULL, 0, GURL(),
+ history::RedirectList(),
+ ui::PAGE_TRANSITION_LINK,
+ history::SOURCE_SYNCED, false);
+ backend_->AddPage(request2);
+ // Assume this page is browsed again.
+ HistoryAddPageArgs request3(url, base::Time::Now(), NULL, 0, GURL(),
+ history::RedirectList(),
+ ui::PAGE_TRANSITION_TYPED,
+ history::SOURCE_BROWSED, false);
+ backend_->AddPage(request3);
+
+ // Three visits should be added with proper sources.
+ VisitVector visits;
+ URLRow row;
+ URLID id = backend_->db()->GetRowForURL(url, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ ASSERT_EQ(3U, visits.size());
+ VisitSourceMap visit_sources;
+ ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
+ ASSERT_EQ(1U, visit_sources.size());
+ EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second);
+}
+
+TEST_F(HistoryBackendTest, AddVisitsSource) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url1("http://www.cnn.com");
+ std::vector<VisitInfo> visits1, visits2;
+ visits1.push_back(VisitInfo(
+ base::Time::Now() - base::TimeDelta::FromDays(5),
+ ui::PAGE_TRANSITION_LINK));
+ visits1.push_back(VisitInfo(
+ base::Time::Now() - base::TimeDelta::FromDays(1),
+ ui::PAGE_TRANSITION_LINK));
+ visits1.push_back(VisitInfo(
+ base::Time::Now(), ui::PAGE_TRANSITION_LINK));
+
+ GURL url2("http://www.example.com");
+ visits2.push_back(VisitInfo(
+ base::Time::Now() - base::TimeDelta::FromDays(10),
+ ui::PAGE_TRANSITION_LINK));
+ visits2.push_back(VisitInfo(base::Time::Now(), ui::PAGE_TRANSITION_LINK));
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ // Add the visits.
+ backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
+ backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
+
+ // Verify the visits were added with their sources.
+ VisitVector visits;
+ URLRow row;
+ URLID id = backend_->db()->GetRowForURL(url1, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ ASSERT_EQ(3U, visits.size());
+ VisitSourceMap visit_sources;
+ ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
+ ASSERT_EQ(3U, visit_sources.size());
+ for (int i = 0; i < 3; i++)
+ EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_sources[visits[i].visit_id]);
+ id = backend_->db()->GetRowForURL(url2, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ ASSERT_EQ(2U, visits.size());
+ ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
+ ASSERT_EQ(2U, visit_sources.size());
+ for (int i = 0; i < 2; i++)
+ EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
+}
+
+TEST_F(HistoryBackendTest, GetMostRecentVisits) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url1("http://www.cnn.com");
+ std::vector<VisitInfo> visits1;
+ visits1.push_back(VisitInfo(
+ base::Time::Now() - base::TimeDelta::FromDays(5),
+ ui::PAGE_TRANSITION_LINK));
+ visits1.push_back(VisitInfo(
+ base::Time::Now() - base::TimeDelta::FromDays(1),
+ ui::PAGE_TRANSITION_LINK));
+ visits1.push_back(VisitInfo(
+ base::Time::Now(), ui::PAGE_TRANSITION_LINK));
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ // Add the visits.
+ backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
+
+ // Verify the visits were added with their sources.
+ VisitVector visits;
+ URLRow row;
+ URLID id = backend_->db()->GetRowForURL(url1, &row);
+ ASSERT_TRUE(backend_->db()->GetMostRecentVisitsForURL(id, 1, &visits));
+ ASSERT_EQ(1U, visits.size());
+ EXPECT_EQ(visits1[2].first, visits[0].visit_time);
+}
+
+TEST_F(HistoryBackendTest, RemoveVisitsTransitions) {
+ ASSERT_TRUE(backend_.get());
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ GURL url1("http://www.cnn.com");
+ VisitInfo typed_visit(
+ base::Time::Now() - base::TimeDelta::FromDays(6),
+ ui::PAGE_TRANSITION_TYPED);
+ VisitInfo reload_visit(
+ base::Time::Now() - base::TimeDelta::FromDays(5),
+ ui::PAGE_TRANSITION_RELOAD);
+ VisitInfo link_visit(
+ base::Time::Now() - base::TimeDelta::FromDays(4),
+ ui::PAGE_TRANSITION_LINK);
+ std::vector<VisitInfo> visits_to_add;
+ visits_to_add.push_back(typed_visit);
+ visits_to_add.push_back(reload_visit);
+ visits_to_add.push_back(link_visit);
+
+ // Add the visits.
+ backend_->AddVisits(url1, visits_to_add, history::SOURCE_SYNCED);
+
+ // Verify that the various counts are what we expect.
+ VisitVector visits;
+ URLRow row;
+ URLID id = backend_->db()->GetRowForURL(url1, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ ASSERT_EQ(3U, visits.size());
+ ASSERT_EQ(1, row.typed_count());
+ ASSERT_EQ(2, row.visit_count());
+
+ // Now, delete the typed visit and verify that typed_count is updated.
+ ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
+ id = backend_->db()->GetRowForURL(url1, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ ASSERT_EQ(2U, visits.size());
+ ASSERT_EQ(0, row.typed_count());
+ ASSERT_EQ(1, row.visit_count());
+
+ // Delete the reload visit now and verify that none of the counts have
+ // changed.
+ ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
+ id = backend_->db()->GetRowForURL(url1, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ ASSERT_EQ(1U, visits.size());
+ ASSERT_EQ(0, row.typed_count());
+ ASSERT_EQ(1, row.visit_count());
+
+ // Delete the last visit and verify that we delete the URL.
+ ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
+ ASSERT_EQ(0, backend_->db()->GetRowForURL(url1, &row));
+}
+
+TEST_F(HistoryBackendTest, RemoveVisitsSource) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url1("http://www.cnn.com");
+ std::vector<VisitInfo> visits1, visits2;
+ visits1.push_back(VisitInfo(
+ base::Time::Now() - base::TimeDelta::FromDays(5),
+ ui::PAGE_TRANSITION_LINK));
+ visits1.push_back(VisitInfo(base::Time::Now(),
+ ui::PAGE_TRANSITION_LINK));
+
+ GURL url2("http://www.example.com");
+ visits2.push_back(VisitInfo(
+ base::Time::Now() - base::TimeDelta::FromDays(10),
+ ui::PAGE_TRANSITION_LINK));
+ visits2.push_back(VisitInfo(base::Time::Now(), ui::PAGE_TRANSITION_LINK));
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ // Add the visits.
+ backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
+ backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
+
+ // Verify the visits of url1 were added.
+ VisitVector visits;
+ URLRow row;
+ URLID id = backend_->db()->GetRowForURL(url1, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ ASSERT_EQ(2U, visits.size());
+ // Remove these visits.
+ ASSERT_TRUE(backend_->RemoveVisits(visits));
+
+ // Now check only url2's source in visit_source table.
+ VisitSourceMap visit_sources;
+ ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
+ ASSERT_EQ(0U, visit_sources.size());
+ id = backend_->db()->GetRowForURL(url2, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ ASSERT_EQ(2U, visits.size());
+ ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
+ ASSERT_EQ(2U, visit_sources.size());
+ for (int i = 0; i < 2; i++)
+ EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
+}
+
+// Test for migration of adding visit_source table.
+TEST_F(HistoryBackendTest, MigrationVisitSource) {
+ ASSERT_TRUE(backend_.get());
+ backend_->Closing();
+ backend_ = NULL;
+
+ base::FilePath old_history_path;
+ ASSERT_TRUE(GetTestDataHistoryDir(&old_history_path));
+ old_history_path = old_history_path.AppendASCII("HistoryNoSource");
+
+ // Copy history database file to current directory so that it will be deleted
+ // in Teardown.
+ base::FilePath new_history_path(test_dir());
+ base::DeleteFile(new_history_path, true);
+ base::CreateDirectory(new_history_path);
+ base::FilePath new_history_file = new_history_path.Append(kHistoryFilename);
+ ASSERT_TRUE(base::CopyFile(old_history_path, new_history_file));
+
+ backend_ = new HistoryBackend(new HistoryBackendTestDelegate(this),
+ &history_client_);
+ backend_->Init(std::string(), false,
+ TestHistoryDatabaseParamsForPath(new_history_path));
+ backend_->Closing();
+ backend_ = NULL;
+
+ // Now the database should already be migrated.
+ // Check version first.
+ int cur_version = HistoryDatabase::GetCurrentVersion();
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(new_history_file));
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT value FROM meta WHERE key = 'version'"));
+ ASSERT_TRUE(s.Step());
+ int file_version = s.ColumnInt(0);
+ EXPECT_EQ(cur_version, file_version);
+
+ // Check visit_source table is created and empty.
+ s.Assign(db.GetUniqueStatement(
+ "SELECT name FROM sqlite_master WHERE name=\"visit_source\""));
+ ASSERT_TRUE(s.Step());
+ s.Assign(db.GetUniqueStatement("SELECT * FROM visit_source LIMIT 10"));
+ EXPECT_FALSE(s.Step());
+}
+
+// Test that SetFaviconMappingsForPageAndRedirects correctly updates icon
+// mappings based on redirects, icon URLs and icon types.
+TEST_F(HistoryBackendTest, SetFaviconMappingsForPageAndRedirects) {
+ // Init recent_redirects_
+ const GURL url1("http://www.google.com");
+ const GURL url2("http://www.google.com/m");
+ URLRow url_info1(url1);
+ url_info1.set_visit_count(0);
+ url_info1.set_typed_count(0);
+ url_info1.set_last_visit(base::Time());
+ url_info1.set_hidden(false);
+ backend_->db_->AddURL(url_info1);
+
+ URLRow url_info2(url2);
+ url_info2.set_visit_count(0);
+ url_info2.set_typed_count(0);
+ url_info2.set_last_visit(base::Time());
+ url_info2.set_hidden(false);
+ backend_->db_->AddURL(url_info2);
+
+ history::RedirectList redirects;
+ redirects.push_back(url2);
+ redirects.push_back(url1);
+ backend_->recent_redirects_.Put(url1, redirects);
+
+ const GURL icon_url1("http://www.google.com/icon");
+ const GURL icon_url2("http://www.google.com/icon2");
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+ bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
+
+ // Add a favicon.
+ backend_->SetFavicons(url1, favicon_base::FAVICON, icon_url1, bitmaps);
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::FAVICON));
+
+ // Add one touch_icon
+ backend_->SetFavicons(url1, favicon_base::TOUCH_ICON, icon_url1, bitmaps);
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::TOUCH_ICON));
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
+
+ // Add one TOUCH_PRECOMPOSED_ICON
+ backend_->SetFavicons(
+ url1, favicon_base::TOUCH_PRECOMPOSED_ICON, icon_url1, bitmaps);
+ // The touch_icon was replaced.
+ EXPECT_EQ(0u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
+ EXPECT_EQ(
+ 1u,
+ NumIconMappingsForPageURL(url1, favicon_base::TOUCH_PRECOMPOSED_ICON));
+ EXPECT_EQ(
+ 1u,
+ NumIconMappingsForPageURL(url2, favicon_base::TOUCH_PRECOMPOSED_ICON));
+
+ // Add a touch_icon.
+ backend_->SetFavicons(url1, favicon_base::TOUCH_ICON, icon_url1, bitmaps);
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
+ // The TOUCH_PRECOMPOSED_ICON was replaced.
+ EXPECT_EQ(
+ 0u,
+ NumIconMappingsForPageURL(url1, favicon_base::TOUCH_PRECOMPOSED_ICON));
+
+ // Add a different favicon.
+ backend_->SetFavicons(url1, favicon_base::FAVICON, icon_url2, bitmaps);
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
+ EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::FAVICON));
+}
+
+// Test that there is no churn in icon mappings from calling
+// SetFavicons() twice with the same |bitmaps| parameter.
+TEST_F(HistoryBackendTest, SetFaviconMappingsForPageDuplicates) {
+ const GURL url("http://www.google.com/");
+ const GURL icon_url("http://www.google.com/icon");
+
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+ bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
+
+ backend_->SetFavicons(url, favicon_base::FAVICON, icon_url, bitmaps);
+
+ std::vector<IconMapping> icon_mappings;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+ url, favicon_base::FAVICON, &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ IconMappingID mapping_id = icon_mappings[0].mapping_id;
+
+ backend_->SetFavicons(url, favicon_base::FAVICON, icon_url, bitmaps);
+
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+ url, favicon_base::FAVICON, &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+
+ // The same row in the icon_mapping table should be used for the mapping as
+ // before.
+ EXPECT_EQ(mapping_id, icon_mappings[0].mapping_id);
+}
+
+// Test that calling SetFavicons() with FaviconBitmapData of different pixel
+// sizes than the initially passed in FaviconBitmapData deletes the no longer
+// used favicon bitmaps.
+TEST_F(HistoryBackendTest, SetFaviconsDeleteBitmaps) {
+ const GURL page_url("http://www.google.com/");
+ const GURL icon_url("http://www.google.com/icon");
+
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+ bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
+ backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
+
+ // Test initial state.
+ std::vector<IconMapping> icon_mappings;
+ EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url, &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
+ EXPECT_EQ(favicon_base::FAVICON, icon_mappings[0].icon_type);
+ favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
+
+ std::vector<FaviconBitmap> favicon_bitmaps;
+ EXPECT_TRUE(GetSortedFaviconBitmaps(favicon_id, &favicon_bitmaps));
+ EXPECT_EQ(2u, favicon_bitmaps.size());
+ FaviconBitmapID small_bitmap_id = favicon_bitmaps[0].bitmap_id;
+ EXPECT_NE(0, small_bitmap_id);
+ EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmaps[0].bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmaps[0].pixel_size);
+ FaviconBitmapID large_bitmap_id = favicon_bitmaps[1].bitmap_id;
+ EXPECT_NE(0, large_bitmap_id);
+ EXPECT_TRUE(BitmapColorEqual(SK_ColorRED, favicon_bitmaps[1].bitmap_data));
+ EXPECT_EQ(kLargeSize, favicon_bitmaps[1].pixel_size);
+
+ // Call SetFavicons() with bitmap data for only the large bitmap. Check that
+ // the small bitmap is in fact deleted.
+ bitmaps.clear();
+ bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kLargeEdgeSize));
+ backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
+
+ scoped_refptr<base::RefCountedMemory> bitmap_data_out;
+ gfx::Size pixel_size_out;
+ EXPECT_FALSE(backend_->thumbnail_db_->GetFaviconBitmap(small_bitmap_id,
+ NULL, NULL, &bitmap_data_out, &pixel_size_out));
+ EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmap(large_bitmap_id,
+ NULL, NULL, &bitmap_data_out, &pixel_size_out));
+ EXPECT_TRUE(BitmapColorEqual(SK_ColorWHITE, bitmap_data_out));
+ EXPECT_EQ(kLargeSize, pixel_size_out);
+
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
+
+ // Notifications should have been broadcast for each call to SetFavicons().
+ EXPECT_EQ(2, favicon_changed_notifications());
+}
+
+// Test updating a single favicon bitmap's data via SetFavicons.
+TEST_F(HistoryBackendTest, SetFaviconsReplaceBitmapData) {
+ const GURL page_url("http://www.google.com/");
+ const GURL icon_url("http://www.google.com/icon");
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+
+ // Add bitmap to the database.
+ backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
+
+ favicon_base::FaviconID original_favicon_id =
+ backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ icon_url, favicon_base::FAVICON, NULL);
+ EXPECT_NE(0, original_favicon_id);
+ FaviconBitmap original_favicon_bitmap;
+ EXPECT_TRUE(
+ GetOnlyFaviconBitmap(original_favicon_id, &original_favicon_bitmap));
+ EXPECT_TRUE(
+ BitmapColorEqual(SK_ColorBLUE, original_favicon_bitmap.bitmap_data));
+
+ EXPECT_EQ(1, favicon_changed_notifications());
+
+ // Call SetFavicons() with completely identical data.
+ bitmaps[0] = CreateBitmap(SK_ColorBLUE, kSmallEdgeSize);
+ backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
+
+ favicon_base::FaviconID updated_favicon_id =
+ backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ icon_url, favicon_base::FAVICON, NULL);
+ EXPECT_NE(0, updated_favicon_id);
+ FaviconBitmap updated_favicon_bitmap;
+ EXPECT_TRUE(
+ GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
+ EXPECT_TRUE(
+ BitmapColorEqual(SK_ColorBLUE, updated_favicon_bitmap.bitmap_data));
+
+ // Because the bitmap data is byte equivalent, no notifications should have
+ // been broadcasted.
+ EXPECT_EQ(1, favicon_changed_notifications());
+
+ // Call SetFavicons() with a different bitmap of the same size.
+ bitmaps[0] = CreateBitmap(SK_ColorWHITE, kSmallEdgeSize);
+ backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
+
+ updated_favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ icon_url, favicon_base::FAVICON, NULL);
+ EXPECT_NE(0, updated_favicon_id);
+ EXPECT_TRUE(
+ GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
+ EXPECT_TRUE(
+ BitmapColorEqual(SK_ColorWHITE, updated_favicon_bitmap.bitmap_data));
+
+ // There should be no churn in FaviconIDs or FaviconBitmapIds even though
+ // the bitmap data changed.
+ EXPECT_EQ(original_favicon_bitmap.icon_id, updated_favicon_bitmap.icon_id);
+ EXPECT_EQ(original_favicon_bitmap.bitmap_id,
+ updated_favicon_bitmap.bitmap_id);
+
+ // A notification should have been broadcasted as the favicon bitmap data has
+ // changed.
+ EXPECT_EQ(2, favicon_changed_notifications());
+}
+
+// Test that if two pages share the same FaviconID, changing the favicon for
+// one page does not affect the other.
+TEST_F(HistoryBackendTest, SetFaviconsSameFaviconURLForTwoPages) {
+ GURL icon_url("http://www.google.com/favicon.ico");
+ GURL icon_url_new("http://www.google.com/favicon2.ico");
+ GURL page_url1("http://www.google.com");
+ GURL page_url2("http://www.google.ca");
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+ bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
+
+ backend_->SetFavicons(page_url1, favicon_base::FAVICON, icon_url, bitmaps);
+
+ std::vector<GURL> icon_urls;
+ icon_urls.push_back(icon_url);
+
+ std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
+ backend_->UpdateFaviconMappingsAndFetch(page_url2,
+ icon_urls,
+ favicon_base::FAVICON,
+ GetEdgeSizesSmallAndLarge(),
+ &bitmap_results);
+
+ // Check that the same FaviconID is mapped to both page URLs.
+ std::vector<IconMapping> icon_mappings;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+ page_url1, &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
+ EXPECT_NE(0, favicon_id);
+
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+ page_url2, &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
+
+ // Change the icon URL that |page_url1| is mapped to.
+ bitmaps.clear();
+ bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kSmallEdgeSize));
+ backend_->SetFavicons(
+ page_url1, favicon_base::FAVICON, icon_url_new, bitmaps);
+
+ // |page_url1| should map to a new FaviconID and have valid bitmap data.
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+ page_url1, &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_url_new, icon_mappings[0].icon_url);
+ EXPECT_NE(favicon_id, icon_mappings[0].icon_id);
+
+ std::vector<FaviconBitmap> favicon_bitmaps;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
+ icon_mappings[0].icon_id, &favicon_bitmaps));
+ EXPECT_EQ(1u, favicon_bitmaps.size());
+
+ // |page_url2| should still map to the same FaviconID and have valid bitmap
+ // data.
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+ page_url2, &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
+
+ favicon_bitmaps.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(favicon_id,
+ &favicon_bitmaps));
+ EXPECT_EQ(2u, favicon_bitmaps.size());
+
+ // A notification should have been broadcast for each call to SetFavicons()
+ // and each call to UpdateFaviconMappingsAndFetch().
+ EXPECT_EQ(3, favicon_changed_notifications());
+}
+
+// Test that no notifications are broadcast as a result of calling
+// UpdateFaviconMappingsAndFetch() for an icon URL which is already
+// mapped to the passed in page URL.
+TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoChange) {
+ GURL page_url("http://www.google.com");
+ GURL icon_url("http://www.google.com/favicon.ico");
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+
+ backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
+
+ favicon_base::FaviconID icon_id =
+ backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ icon_url, favicon_base::FAVICON, NULL);
+ EXPECT_NE(0, icon_id);
+ EXPECT_EQ(1, favicon_changed_notifications());
+
+ std::vector<GURL> icon_urls;
+ icon_urls.push_back(icon_url);
+
+ std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
+ backend_->UpdateFaviconMappingsAndFetch(page_url,
+ icon_urls,
+ favicon_base::FAVICON,
+ GetEdgeSizesSmallAndLarge(),
+ &bitmap_results);
+
+ EXPECT_EQ(icon_id,
+ backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ icon_url, favicon_base::FAVICON, NULL));
+
+ // No notification should have been broadcast as no icon mapping, favicon,
+ // or favicon bitmap was updated, added or removed.
+ EXPECT_EQ(1, favicon_changed_notifications());
+}
+
+// Test repeatedly calling MergeFavicon(). |page_url| is initially not known
+// to the database.
+TEST_F(HistoryBackendTest, MergeFaviconPageURLNotInDB) {
+ GURL page_url("http://www.google.com");
+ GURL icon_url("http:/www.google.com/favicon.ico");
+
+ std::vector<unsigned char> data;
+ data.push_back('a');
+ scoped_refptr<base::RefCountedBytes> bitmap_data(
+ new base::RefCountedBytes(data));
+
+ backend_->MergeFavicon(
+ page_url, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
+
+ // |page_url| should now be mapped to |icon_url| and the favicon bitmap should
+ // not be expired.
+ std::vector<IconMapping> icon_mappings;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
+
+ FaviconBitmap favicon_bitmap;
+ EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
+ EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
+ EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
+
+ data[0] = 'b';
+ bitmap_data = new base::RefCountedBytes(data);
+ backend_->MergeFavicon(
+ page_url, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
+
+ // |page_url| should still have a single favicon bitmap. The bitmap data
+ // should be updated.
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
+
+ EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
+ EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);
+ EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
+}
+
+// Test calling MergeFavicon() when |page_url| is known to the database.
+TEST_F(HistoryBackendTest, MergeFaviconPageURLInDB) {
+ GURL page_url("http://www.google.com");
+ GURL icon_url1("http:/www.google.com/favicon.ico");
+ GURL icon_url2("http://www.google.com/favicon2.ico");
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+
+ backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url1, bitmaps);
+
+ // Test initial state.
+ std::vector<IconMapping> icon_mappings;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
+
+ FaviconBitmap favicon_bitmap;
+ EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
+ EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
+ EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
+
+ EXPECT_EQ(1, favicon_changed_notifications());
+
+ // 1) Merge identical favicon bitmap.
+ std::vector<unsigned char> data;
+ gfx::PNGCodec::EncodeBGRASkBitmap(bitmaps[0], false, &data);
+ scoped_refptr<base::RefCountedBytes> bitmap_data(
+ new base::RefCountedBytes(data));
+ backend_->MergeFavicon(
+ page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kSmallSize);
+
+ // All the data should stay the same and no notifications should have been
+ // sent.
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
+
+ EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
+ EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
+ EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
+
+ EXPECT_EQ(1, favicon_changed_notifications());
+
+ // 2) Merge favicon bitmap of the same size.
+ data.clear();
+ data.push_back('b');
+ bitmap_data = new base::RefCountedBytes(data);
+ backend_->MergeFavicon(
+ page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kSmallSize);
+
+ // The small favicon bitmap at |icon_url1| should be overwritten.
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
+
+ EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
+ EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);
+ EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
+
+ // 3) Merge favicon for the same icon URL, but a pixel size for which there is
+ // no favicon bitmap.
+ data[0] = 'c';
+ bitmap_data = new base::RefCountedBytes(data);
+ backend_->MergeFavicon(
+ page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kTinySize);
+
+ // A new favicon bitmap should be created and the preexisting favicon bitmap
+ // ('b') should be expired.
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
+
+ std::vector<FaviconBitmap> favicon_bitmaps;
+ EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
+ &favicon_bitmaps));
+ EXPECT_NE(base::Time(), favicon_bitmaps[0].last_updated);
+ EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
+ EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
+ EXPECT_EQ(base::Time(), favicon_bitmaps[1].last_updated);
+ EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmaps[1].bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
+
+ // 4) Merge favicon for an icon URL different from the icon URLs already
+ // mapped to page URL.
+ data[0] = 'd';
+ bitmap_data = new base::RefCountedBytes(data);
+ backend_->MergeFavicon(
+ page_url, icon_url2, favicon_base::FAVICON, bitmap_data, kSmallSize);
+
+ // The existing favicon bitmaps should be copied over to the newly created
+ // favicon at |icon_url2|. |page_url| should solely be mapped to |icon_url2|.
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_url2, icon_mappings[0].icon_url);
+
+ favicon_bitmaps.clear();
+ EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
+ &favicon_bitmaps));
+ EXPECT_EQ(base::Time(), favicon_bitmaps[0].last_updated);
+ EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
+ EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
+ // The favicon being merged should take precedence over the preexisting
+ // favicon bitmaps.
+ EXPECT_NE(base::Time(), favicon_bitmaps[1].last_updated);
+ EXPECT_TRUE(BitmapDataEqual('d', favicon_bitmaps[1].bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
+
+ // A notification should have been broadcast for each call to SetFavicons()
+ // and MergeFavicon().
+ EXPECT_EQ(4, favicon_changed_notifications());
+}
+
+// Test calling MergeFavicon() when |icon_url| is known to the database but not
+// mapped to |page_url|.
+TEST_F(HistoryBackendTest, MergeFaviconIconURLMappedToDifferentPageURL) {
+ GURL page_url1("http://www.google.com");
+ GURL page_url2("http://news.google.com");
+ GURL page_url3("http://maps.google.com");
+ GURL icon_url("http:/www.google.com/favicon.ico");
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+
+ backend_->SetFavicons(page_url1, favicon_base::FAVICON, icon_url, bitmaps);
+
+ // Test initial state.
+ std::vector<IconMapping> icon_mappings;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
+
+ FaviconBitmap favicon_bitmap;
+ EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
+ EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
+ EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
+
+ // 1) Merge in an identical favicon bitmap data but for a different page URL.
+ std::vector<unsigned char> data;
+ gfx::PNGCodec::EncodeBGRASkBitmap(bitmaps[0], false, &data);
+ scoped_refptr<base::RefCountedBytes> bitmap_data(
+ new base::RefCountedBytes(data));
+
+ backend_->MergeFavicon(
+ page_url2, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
+
+ favicon_base::FaviconID favicon_id =
+ backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ icon_url, favicon_base::FAVICON, NULL);
+ EXPECT_NE(0, favicon_id);
+
+ EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
+ EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
+ EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
+
+ // 2) Merging a favicon bitmap with different bitmap data for the same icon
+ // URL should overwrite the small favicon bitmap at |icon_url|.
+ data.clear();
+ data.push_back('b');
+ bitmap_data = new base::RefCountedBytes(data);
+ backend_->MergeFavicon(
+ page_url3, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
+
+ favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+ icon_url, favicon_base::FAVICON, NULL);
+ EXPECT_NE(0, favicon_id);
+
+ EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
+ EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);
+ EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
+ EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
+
+ // |icon_url| should be mapped to all three page URLs.
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
+
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url2,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
+
+ icon_mappings.clear();
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url3,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
+
+ // A notification should have been broadcast for each call to SetFavicons()
+ // and MergeFavicon().
+ EXPECT_EQ(3, favicon_changed_notifications());
+}
+
+// Test that MergeFavicon() does not add more than
+// |kMaxFaviconBitmapsPerIconURL| to a favicon.
+TEST_F(HistoryBackendTest, MergeFaviconMaxFaviconBitmapsPerIconURL) {
+ GURL page_url("http://www.google.com");
+ std::string icon_url_string("http://www.google.com/favicon.ico");
+ size_t replace_index = icon_url_string.size() - 1;
+
+ std::vector<unsigned char> data;
+ data.push_back('a');
+ scoped_refptr<base::RefCountedMemory> bitmap_data =
+ base::RefCountedBytes::TakeVector(&data);
+
+ int pixel_size = 1;
+ for (size_t i = 0; i < kMaxFaviconBitmapsPerIconURL + 1; ++i) {
+ icon_url_string[replace_index] = '0' + i;
+ GURL icon_url(icon_url_string);
+
+ backend_->MergeFavicon(page_url,
+ icon_url,
+ favicon_base::FAVICON,
+ bitmap_data,
+ gfx::Size(pixel_size, pixel_size));
+ ++pixel_size;
}
- // Re-open the db using the HistoryDatabase, which should migrate to the
- // current version, creating the etag and last_modified columns.
- CreateBackendAndDatabase();
- DeleteBackend();
- {
- // Re-open the db for manual manipulation.
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- // The version should have been updated.
- int cur_version = HistoryDatabase::GetCurrentVersion();
- ASSERT_LE(28, cur_version);
- {
- sql::Statement s(db.GetUniqueStatement(
- "SELECT value FROM meta WHERE key = 'version'"));
- EXPECT_TRUE(s.Step());
- EXPECT_EQ(cur_version, s.ColumnInt(0));
- }
- {
- sql::Statement s(db.GetUniqueStatement(
- "SELECT etag, last_modified from downloads"));
- EXPECT_TRUE(s.Step());
- EXPECT_EQ(std::string(), s.ColumnString(0));
- EXPECT_EQ(std::string(), s.ColumnString(1));
- }
+
+ // There should be a single favicon mapped to |page_url| with exactly
+ // kMaxFaviconBitmapsPerIconURL favicon bitmaps.
+ std::vector<IconMapping> icon_mappings;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ std::vector<FaviconBitmap> favicon_bitmaps;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
+ icon_mappings[0].icon_id, &favicon_bitmaps));
+ EXPECT_EQ(kMaxFaviconBitmapsPerIconURL, favicon_bitmaps.size());
+}
+
+// Tests that the favicon set by MergeFavicon() shows up in the result of
+// GetFaviconsForURL().
+TEST_F(HistoryBackendTest, MergeFaviconShowsUpInGetFaviconsForURLResult) {
+ GURL page_url("http://www.google.com");
+ GURL icon_url("http://www.google.com/favicon.ico");
+ GURL merged_icon_url("http://wwww.google.com/favicon2.ico");
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+ bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
+
+ // Set some preexisting favicons for |page_url|.
+ backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
+
+ // Merge small favicon.
+ std::vector<unsigned char> data;
+ data.push_back('c');
+ scoped_refptr<base::RefCountedBytes> bitmap_data(
+ new base::RefCountedBytes(data));
+ backend_->MergeFavicon(page_url,
+ merged_icon_url,
+ favicon_base::FAVICON,
+ bitmap_data,
+ kSmallSize);
+
+ // Request favicon bitmaps for both 1x and 2x to simulate request done by
+ // BookmarkModel::GetFavicon().
+ std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
+ backend_->GetFaviconsForURL(page_url,
+ favicon_base::FAVICON,
+ GetEdgeSizesSmallAndLarge(),
+ &bitmap_results);
+
+ EXPECT_EQ(2u, bitmap_results.size());
+ const favicon_base::FaviconRawBitmapResult& first_result = bitmap_results[0];
+ const favicon_base::FaviconRawBitmapResult& result =
+ (first_result.pixel_size == kSmallSize) ? first_result
+ : bitmap_results[1];
+ EXPECT_TRUE(BitmapDataEqual('c', result.bitmap_data));
+}
+
+// Tests GetFaviconsForURL with icon_types priority,
+TEST_F(HistoryBackendTest, TestGetFaviconsForURLWithIconTypesPriority) {
+ GURL page_url("http://www.google.com");
+ GURL icon_url("http://www.google.com/favicon.ico");
+ GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");
+
+ std::vector<SkBitmap> favicon_bitmaps;
+ favicon_bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));
+ favicon_bitmaps.push_back(CreateBitmap(SK_ColorRED, 32));
+
+ std::vector<SkBitmap> touch_bitmaps;
+ touch_bitmaps.push_back(CreateBitmap(SK_ColorWHITE, 64));
+
+ // Set some preexisting favicons for |page_url|.
+ backend_->SetFavicons(
+ page_url, favicon_base::FAVICON, icon_url, favicon_bitmaps);
+ backend_->SetFavicons(
+ page_url, favicon_base::TOUCH_ICON, touch_icon_url, touch_bitmaps);
+
+ favicon_base::FaviconRawBitmapResult result;
+ std::vector<int> icon_types;
+ icon_types.push_back(favicon_base::FAVICON);
+ icon_types.push_back(favicon_base::TOUCH_ICON);
+
+ backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
+
+ // Verify the result icon is 32x32 favicon.
+ EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
+ EXPECT_EQ(favicon_base::FAVICON, result.icon_type);
+
+ // Change Minimal size to 32x32 and verify the 64x64 touch icon returned.
+ backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result);
+ EXPECT_EQ(gfx::Size(64, 64), result.pixel_size);
+ EXPECT_EQ(favicon_base::TOUCH_ICON, result.icon_type);
+}
+
+// Test the the first types of icon is returned if its size equal to the
+// second types icon.
+TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFavicon) {
+ GURL page_url("http://www.google.com");
+ GURL icon_url("http://www.google.com/favicon.ico");
+ GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");
+
+ std::vector<SkBitmap> favicon_bitmaps;
+ favicon_bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));
+ favicon_bitmaps.push_back(CreateBitmap(SK_ColorRED, 32));
+
+ std::vector<SkBitmap> touch_bitmaps;
+ touch_bitmaps.push_back(CreateBitmap(SK_ColorWHITE, 32));
+
+ // Set some preexisting favicons for |page_url|.
+ backend_->SetFavicons(
+ page_url, favicon_base::FAVICON, icon_url, favicon_bitmaps);
+ backend_->SetFavicons(
+ page_url, favicon_base::TOUCH_ICON, touch_icon_url, touch_bitmaps);
+
+ favicon_base::FaviconRawBitmapResult result;
+ std::vector<int> icon_types;
+ icon_types.push_back(favicon_base::FAVICON);
+ icon_types.push_back(favicon_base::TOUCH_ICON);
+
+ backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
+
+ // Verify the result icon is 32x32 favicon.
+ EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
+ EXPECT_EQ(favicon_base::FAVICON, result.icon_type);
+
+ // Change minimal size to 32x32 and verify the 32x32 favicon returned.
+ favicon_base::FaviconRawBitmapResult result1;
+ backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result1);
+ EXPECT_EQ(gfx::Size(32, 32), result1.pixel_size);
+ EXPECT_EQ(favicon_base::FAVICON, result1.icon_type);
+}
+
+// Test the favicon is returned if its size is smaller than minimal size,
+// because it is only one available.
+TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFaviconEvenItSmaller) {
+ GURL page_url("http://www.google.com");
+ GURL icon_url("http://www.google.com/favicon.ico");
+
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));
+
+ // Set preexisting favicons for |page_url|.
+ backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
+
+ favicon_base::FaviconRawBitmapResult result;
+ std::vector<int> icon_types;
+ icon_types.push_back(favicon_base::FAVICON);
+ icon_types.push_back(favicon_base::TOUCH_ICON);
+
+ backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result);
+
+ // Verify 16x16 icon is returned, even it small than minimal_size.
+ EXPECT_EQ(gfx::Size(16, 16), result.pixel_size);
+ EXPECT_EQ(favicon_base::FAVICON, result.icon_type);
+}
+
+// Test UpdateFaviconMapingsAndFetch() when multiple icon types are passed in.
+TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchMultipleIconTypes) {
+ GURL page_url1("http://www.google.com");
+ GURL page_url2("http://news.google.com");
+ GURL page_url3("http://mail.google.com");
+ GURL icon_urla("http://www.google.com/favicon1.ico");
+ GURL icon_urlb("http://www.google.com/favicon2.ico");
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+
+ // |page_url1| is mapped to |icon_urla| which if of type TOUCH_ICON.
+ backend_->SetFavicons(
+ page_url1, favicon_base::TOUCH_ICON, icon_urla, bitmaps);
+
+ // |page_url2| is mapped to |icon_urlb| which is of type
+ // TOUCH_PRECOMPOSED_ICON.
+ backend_->SetFavicons(
+ page_url2, favicon_base::TOUCH_PRECOMPOSED_ICON, icon_urlb, bitmaps);
+
+ std::vector<GURL> icon_urls;
+ icon_urls.push_back(icon_urla);
+ icon_urls.push_back(icon_urlb);
+
+ std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
+ backend_->UpdateFaviconMappingsAndFetch(
+ page_url3,
+ icon_urls,
+ (favicon_base::TOUCH_ICON | favicon_base::TOUCH_PRECOMPOSED_ICON),
+ GetEdgeSizesSmallAndLarge(),
+ &bitmap_results);
+
+ // |page_url1| and |page_url2| should still be mapped to the same icon URLs.
+ std::vector<IconMapping> icon_mappings;
+ EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
+ &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_urla, icon_mappings[0].icon_url);
+ EXPECT_EQ(favicon_base::TOUCH_ICON, icon_mappings[0].icon_type);
+
+ icon_mappings.clear();
+ EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url2, &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
+ EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
+
+ // |page_url3| should be mapped only to |icon_urlb| as TOUCH_PRECOMPOSED_ICON
+ // is the largest IconType.
+ icon_mappings.clear();
+ EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url3, &icon_mappings));
+ EXPECT_EQ(1u, icon_mappings.size());
+ EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
+ EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
+}
+
+// Test the results of GetFaviconsFromDB() when there are no found favicons.
+TEST_F(HistoryBackendTest, GetFaviconsFromDBEmpty) {
+ const GURL page_url("http://www.google.com/");
+
+ std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
+ EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url,
+ favicon_base::FAVICON,
+ GetEdgeSizesSmallAndLarge(),
+ &bitmap_results));
+ EXPECT_TRUE(bitmap_results.empty());
+}
+
+// Test the results of GetFaviconsFromDB() when there are matching favicons
+// but there are no associated favicon bitmaps.
+TEST_F(HistoryBackendTest, GetFaviconsFromDBNoFaviconBitmaps) {
+ const GURL page_url("http://www.google.com/");
+ const GURL icon_url("http://www.google.com/icon1");
+
+ favicon_base::FaviconID icon_id =
+ backend_->thumbnail_db_->AddFavicon(icon_url, favicon_base::FAVICON);
+ EXPECT_NE(0, icon_id);
+ EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
+
+ std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
+ EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url,
+ favicon_base::FAVICON,
+ GetEdgeSizesSmallAndLarge(),
+ &bitmap_results_out));
+ EXPECT_TRUE(bitmap_results_out.empty());
+}
+
+// Test that GetFaviconsFromDB() returns results for the bitmaps which most
+// closely match the passed in the desired pixel sizes.
+TEST_F(HistoryBackendTest, GetFaviconsFromDBSelectClosestMatch) {
+ const GURL page_url("http://www.google.com/");
+ const GURL icon_url("http://www.google.com/icon1");
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kTinyEdgeSize));
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+ bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
+
+ backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
+
+ std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
+ EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
+ favicon_base::FAVICON,
+ GetEdgeSizesSmallAndLarge(),
+ &bitmap_results_out));
+
+ // The bitmap data for the small and large bitmaps should be returned as their
+ // sizes match exactly.
+ EXPECT_EQ(2u, bitmap_results_out.size());
+ // No required order for results.
+ if (bitmap_results_out[0].pixel_size == kLargeSize) {
+ favicon_base::FaviconRawBitmapResult tmp_result = bitmap_results_out[0];
+ bitmap_results_out[0] = bitmap_results_out[1];
+ bitmap_results_out[1] = tmp_result;
}
+
+ EXPECT_FALSE(bitmap_results_out[0].expired);
+ EXPECT_TRUE(
+ BitmapColorEqual(SK_ColorBLUE, bitmap_results_out[0].bitmap_data));
+ EXPECT_EQ(kSmallSize, bitmap_results_out[0].pixel_size);
+ EXPECT_EQ(icon_url, bitmap_results_out[0].icon_url);
+ EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[0].icon_type);
+
+ EXPECT_FALSE(bitmap_results_out[1].expired);
+ EXPECT_TRUE(BitmapColorEqual(SK_ColorRED, bitmap_results_out[1].bitmap_data));
+ EXPECT_EQ(kLargeSize, bitmap_results_out[1].pixel_size);
+ EXPECT_EQ(icon_url, bitmap_results_out[1].icon_url);
+ EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[1].icon_type);
}
-TEST_F(HistoryBackendDBTest, PurgeArchivedDatabase) {
- ASSERT_NO_FATAL_FAILURE(CreateDBVersion(27));
- ASSERT_NO_FATAL_FAILURE(CreateArchivedDB());
-
- ASSERT_TRUE(base::PathExists(history_dir_.Append(kArchivedHistoryFilename)));
-
- CreateBackendAndDatabase();
- DeleteBackend();
-
- // We do not retain expired history entries in an archived database as of M37.
- // Verify that any legacy archived database is deleted on start-up.
- ASSERT_FALSE(base::PathExists(history_dir_.Append(kArchivedHistoryFilename)));
-}
-
-TEST_F(HistoryBackendDBTest, MigrateDownloadMimeType) {
- base::Time now(base::Time::Now());
- ASSERT_NO_FATAL_FAILURE(CreateDBVersion(28));
- {
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- {
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO downloads (id, current_path, target_path, start_time, "
- "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
- "end_time, opened, referrer, by_ext_id, by_ext_name, etag, "
- "last_modified) VALUES "
- "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
- s.BindInt64(0, 1);
- s.BindString(1, "current_path");
- s.BindString(2, "target_path");
- s.BindInt64(3, now.ToTimeT());
- s.BindInt64(4, 100);
- s.BindInt64(5, 100);
- s.BindInt(6, 1);
- s.BindInt(7, 0);
- s.BindInt(8, 0);
- s.BindInt64(9, now.ToTimeT());
- s.BindInt(10, 1);
- s.BindString(11, "referrer");
- s.BindString(12, "by extension ID");
- s.BindString(13, "by extension name");
- s.BindString(14, "etag");
- s.BindInt64(15, now.ToTimeT());
- ASSERT_TRUE(s.Run());
- }
- {
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
- "(?, ?, ?)"));
- s.BindInt64(0, 4);
- s.BindInt64(1, 0);
- s.BindString(2, "url");
- ASSERT_TRUE(s.Run());
- }
+// Test the results of GetFaviconsFromDB() when called with different
+// |icon_types|.
+TEST_F(HistoryBackendTest, GetFaviconsFromDBIconType) {
+ const GURL page_url("http://www.google.com/");
+ const GURL icon_url1("http://www.google.com/icon1.png");
+ const GURL icon_url2("http://www.google.com/icon2.png");
+ std::vector<SkBitmap> bitmaps;
+ bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
+
+ std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
+ backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url1, bitmaps);
+ backend_->SetFavicons(page_url, favicon_base::TOUCH_ICON, icon_url2, bitmaps);
+
+ std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
+ EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
+ favicon_base::FAVICON,
+ GetEdgeSizesSmallAndLarge(),
+ &bitmap_results_out));
+
+ EXPECT_EQ(1u, bitmap_results_out.size());
+ EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[0].icon_type);
+ EXPECT_EQ(icon_url1, bitmap_results_out[0].icon_url);
+
+ bitmap_results_out.clear();
+ EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
+ favicon_base::TOUCH_ICON,
+ GetEdgeSizesSmallAndLarge(),
+ &bitmap_results_out));
+
+ EXPECT_EQ(1u, bitmap_results_out.size());
+ EXPECT_EQ(favicon_base::TOUCH_ICON, bitmap_results_out[0].icon_type);
+ EXPECT_EQ(icon_url2, bitmap_results_out[0].icon_url);
+}
+
+// Test that GetFaviconsFromDB() correctly sets the expired flag for bitmap
+// reults.
+TEST_F(HistoryBackendTest, GetFaviconsFromDBExpired) {
+ const GURL page_url("http://www.google.com/");
+ const GURL icon_url("http://www.google.com/icon.png");
+
+ std::vector<unsigned char> data;
+ data.push_back('a');
+ scoped_refptr<base::RefCountedBytes> bitmap_data(
+ base::RefCountedBytes::TakeVector(&data));
+ base::Time last_updated = base::Time::FromTimeT(0);
+ favicon_base::FaviconID icon_id = backend_->thumbnail_db_->AddFavicon(
+ icon_url, favicon_base::FAVICON, bitmap_data, last_updated, kSmallSize);
+ EXPECT_NE(0, icon_id);
+ EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
+
+ std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
+ EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
+ favicon_base::FAVICON,
+ GetEdgeSizesSmallAndLarge(),
+ &bitmap_results_out));
+
+ EXPECT_EQ(1u, bitmap_results_out.size());
+ EXPECT_TRUE(bitmap_results_out[0].expired);
+}
+
+// Check that UpdateFaviconMappingsAndFetch() call back to the UI when there is
+// no valid thumbnail database.
+TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoDB) {
+ // Make the thumbnail database invalid.
+ backend_->thumbnail_db_.reset();
+
+ std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
+
+ backend_->UpdateFaviconMappingsAndFetch(GURL(),
+ std::vector<GURL>(),
+ favicon_base::FAVICON,
+ GetEdgeSizesSmallAndLarge(),
+ &bitmap_results);
+
+ EXPECT_TRUE(bitmap_results.empty());
+}
+
+TEST_F(HistoryBackendTest, QueryFilteredURLs) {
+ const char* google = "http://www.google.com/";
+ const char* yahoo = "http://www.yahoo.com/";
+ const char* yahoo_sports = "http://sports.yahoo.com/";
+ const char* yahoo_sports_with_article1 =
+ "http://sports.yahoo.com/article1.htm";
+ const char* yahoo_sports_with_article2 =
+ "http://sports.yahoo.com/article2.htm";
+ const char* yahoo_sports_soccer = "http://sports.yahoo.com/soccer";
+ const char* apple = "http://www.apple.com/";
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ base::Time tested_time = base::Time::Now().LocalMidnight() +
+ base::TimeDelta::FromHours(4);
+ base::TimeDelta half_an_hour = base::TimeDelta::FromMinutes(30);
+ base::TimeDelta one_hour = base::TimeDelta::FromHours(1);
+ base::TimeDelta one_day = base::TimeDelta::FromDays(1);
+
+ const ui::PageTransition kTypedTransition =
+ ui::PAGE_TRANSITION_TYPED;
+ const ui::PageTransition kKeywordGeneratedTransition =
+ ui::PAGE_TRANSITION_KEYWORD_GENERATED;
+
+ const char* redirect_sequence[2];
+ redirect_sequence[1] = NULL;
+
+ redirect_sequence[0] = google;
+ AddRedirectChainWithTransitionAndTime(
+ redirect_sequence, 0, kTypedTransition,
+ tested_time - one_day - half_an_hour * 2);
+ AddRedirectChainWithTransitionAndTime(
+ redirect_sequence, 0,
+ kTypedTransition, tested_time - one_day);
+ AddRedirectChainWithTransitionAndTime(
+ redirect_sequence, 0,
+ kTypedTransition, tested_time - half_an_hour / 2);
+ AddRedirectChainWithTransitionAndTime(
+ redirect_sequence, 0,
+ kTypedTransition, tested_time);
+
+ // Add a visit with a transition that will make sure that no segment gets
+ // created for this page (so the subsequent entries will have different URLIDs
+ // and SegmentIDs).
+ redirect_sequence[0] = apple;
+ AddRedirectChainWithTransitionAndTime(
+ redirect_sequence, 0, kKeywordGeneratedTransition,
+ tested_time - one_day + one_hour * 6);
+
+ redirect_sequence[0] = yahoo;
+ AddRedirectChainWithTransitionAndTime(
+ redirect_sequence, 0, kTypedTransition,
+ tested_time - one_day + half_an_hour);
+ AddRedirectChainWithTransitionAndTime(
+ redirect_sequence, 0, kTypedTransition,
+ tested_time - one_day + half_an_hour * 2);
+
+ redirect_sequence[0] = yahoo_sports;
+ AddRedirectChainWithTransitionAndTime(
+ redirect_sequence, 0, kTypedTransition,
+ tested_time - one_day - half_an_hour * 2);
+ AddRedirectChainWithTransitionAndTime(
+ redirect_sequence, 0, kTypedTransition,
+ tested_time - one_day);
+ int transition1, transition2;
+ AddClientRedirect(GURL(yahoo_sports), GURL(yahoo_sports_with_article1), false,
+ tested_time - one_day + half_an_hour,
+ &transition1, &transition2);
+ AddClientRedirect(GURL(yahoo_sports_with_article1),
+ GURL(yahoo_sports_with_article2),
+ false,
+ tested_time - one_day + half_an_hour * 2,
+ &transition1, &transition2);
+
+ redirect_sequence[0] = yahoo_sports_soccer;
+ AddRedirectChainWithTransitionAndTime(redirect_sequence, 0,
+ kTypedTransition,
+ tested_time - half_an_hour);
+ backend_->Commit();
+
+ VisitFilter filter;
+ FilteredURLList filtered_list;
+ // Time limit is |tested_time| +/- 45 min.
+ base::TimeDelta three_quarters_of_an_hour = base::TimeDelta::FromMinutes(45);
+ filter.SetFilterTime(tested_time);
+ filter.SetFilterWidth(three_quarters_of_an_hour);
+ backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
+
+ ASSERT_EQ(4U, filtered_list.size());
+ EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
+ EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
+ EXPECT_EQ(std::string(yahoo), filtered_list[2].url.spec());
+ EXPECT_EQ(std::string(yahoo_sports), filtered_list[3].url.spec());
+
+ // Time limit is between |tested_time| and |tested_time| + 2 hours.
+ filter.SetFilterTime(tested_time + one_hour);
+ filter.SetFilterWidth(one_hour);
+ backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
+
+ ASSERT_EQ(3U, filtered_list.size());
+ EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
+ EXPECT_EQ(std::string(yahoo), filtered_list[1].url.spec());
+ EXPECT_EQ(std::string(yahoo_sports), filtered_list[2].url.spec());
+
+ // Time limit is between |tested_time| - 2 hours and |tested_time|.
+ filter.SetFilterTime(tested_time - one_hour);
+ filter.SetFilterWidth(one_hour);
+ backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
+
+ ASSERT_EQ(3U, filtered_list.size());
+ EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
+ EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
+ EXPECT_EQ(std::string(yahoo_sports), filtered_list[2].url.spec());
+
+ filter.ClearFilters();
+ base::Time::Exploded exploded_time;
+ tested_time.LocalExplode(&exploded_time);
+
+ // Today.
+ filter.SetFilterTime(tested_time);
+ filter.SetDayOfTheWeekFilter(static_cast<int>(exploded_time.day_of_week));
+ backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
+
+ ASSERT_EQ(2U, filtered_list.size());
+ EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
+ EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
+
+ // Today + time limit - only yahoo_sports_soccer should fit.
+ filter.SetFilterTime(tested_time - base::TimeDelta::FromMinutes(40));
+ filter.SetFilterWidth(base::TimeDelta::FromMinutes(20));
+ backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
+
+ ASSERT_EQ(1U, filtered_list.size());
+ EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[0].url.spec());
+
+ // Make sure we get debug data if we request it.
+ filter.SetFilterTime(tested_time);
+ filter.SetFilterWidth(one_hour * 2);
+ backend_->QueryFilteredURLs(100, filter, true, &filtered_list);
+
+ // If the SegmentID is used by QueryFilteredURLs when generating the debug
+ // data instead of the URLID, the |total_visits| for the |yahoo_sports_soccer|
+ // entry will be zero instead of 1.
+ ASSERT_GE(filtered_list.size(), 2U);
+ EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
+ EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
+ EXPECT_EQ(4U, filtered_list[0].extended_info.total_visits);
+ EXPECT_EQ(1U, filtered_list[1].extended_info.total_visits);
+}
+
+TEST_F(HistoryBackendTest, UpdateVisitDuration) {
+ // This unit test will test adding and deleting visit details information.
+ ASSERT_TRUE(backend_.get());
+
+ GURL url1("http://www.cnn.com");
+ std::vector<VisitInfo> visit_info1, visit_info2;
+ base::Time start_ts = base::Time::Now() - base::TimeDelta::FromDays(5);
+ base::Time end_ts = start_ts + base::TimeDelta::FromDays(2);
+ visit_info1.push_back(VisitInfo(start_ts, ui::PAGE_TRANSITION_LINK));
+
+ GURL url2("http://www.example.com");
+ visit_info2.push_back(
+ VisitInfo(base::Time::Now() - base::TimeDelta::FromDays(10),
+ ui::PAGE_TRANSITION_LINK));
+
+ // Clear all history.
+ backend_->DeleteAllHistory();
+
+ // Add the visits.
+ backend_->AddVisits(url1, visit_info1, history::SOURCE_BROWSED);
+ backend_->AddVisits(url2, visit_info2, history::SOURCE_BROWSED);
+
+ // Verify the entries for both visits were added in visit_details.
+ VisitVector visits1, visits2;
+ URLRow row;
+ URLID url_id1 = backend_->db()->GetRowForURL(url1, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
+ ASSERT_EQ(1U, visits1.size());
+ EXPECT_EQ(0, visits1[0].visit_duration.ToInternalValue());
+
+ URLID url_id2 = backend_->db()->GetRowForURL(url2, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id2, &visits2));
+ ASSERT_EQ(1U, visits2.size());
+ EXPECT_EQ(0, visits2[0].visit_duration.ToInternalValue());
+
+ // Update the visit to cnn.com.
+ backend_->UpdateVisitDuration(visits1[0].visit_id, end_ts);
+
+ // Check the duration for visiting cnn.com was correctly updated.
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
+ ASSERT_EQ(1U, visits1.size());
+ base::TimeDelta expected_duration = end_ts - start_ts;
+ EXPECT_EQ(expected_duration.ToInternalValue(),
+ visits1[0].visit_duration.ToInternalValue());
+
+ // Remove the visit to cnn.com.
+ ASSERT_TRUE(backend_->RemoveVisits(visits1));
+}
+
+// Test for migration of adding visit_duration column.
+TEST_F(HistoryBackendTest, MigrationVisitDuration) {
+ ASSERT_TRUE(backend_.get());
+ backend_->Closing();
+ backend_ = NULL;
+
+ base::FilePath old_history_path, old_history;
+ ASSERT_TRUE(GetTestDataHistoryDir(&old_history_path));
+ old_history = old_history_path.AppendASCII("HistoryNoDuration");
+
+ // Copy history database file to current directory so that it will be deleted
+ // in Teardown.
+ base::FilePath new_history_path(test_dir());
+ base::DeleteFile(new_history_path, true);
+ base::CreateDirectory(new_history_path);
+ base::FilePath new_history_file = new_history_path.Append(kHistoryFilename);
+ ASSERT_TRUE(base::CopyFile(old_history, new_history_file));
+
+ backend_ = new HistoryBackend(new HistoryBackendTestDelegate(this),
+ &history_client_);
+ backend_->Init(std::string(), false,
+ TestHistoryDatabaseParamsForPath(new_history_path));
+ backend_->Closing();
+ backend_ = NULL;
+
+ // Now the history database should already be migrated.
+
+ // Check version in history database first.
+ int cur_version = HistoryDatabase::GetCurrentVersion();
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(new_history_file));
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT value FROM meta WHERE key = 'version'"));
+ ASSERT_TRUE(s.Step());
+ int file_version = s.ColumnInt(0);
+ EXPECT_EQ(cur_version, file_version);
+
+ // Check visit_duration column in visits table is created and set to 0.
+ s.Assign(db.GetUniqueStatement(
+ "SELECT visit_duration FROM visits LIMIT 1"));
+ ASSERT_TRUE(s.Step());
+ EXPECT_EQ(0, s.ColumnInt(0));
+}
+
+TEST_F(HistoryBackendTest, AddPageNoVisitForBookmark) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url("http://www.google.com");
+ base::string16 title(base::UTF8ToUTF16("Bookmark title"));
+ backend_->AddPageNoVisitForBookmark(url, title);
+
+ URLRow row;
+ backend_->GetURL(url, &row);
+ EXPECT_EQ(url, row.url());
+ EXPECT_EQ(title, row.title());
+ EXPECT_EQ(0, row.visit_count());
+
+ backend_->DeleteURL(url);
+ backend_->AddPageNoVisitForBookmark(url, base::string16());
+ backend_->GetURL(url, &row);
+ EXPECT_EQ(url, row.url());
+ EXPECT_EQ(base::UTF8ToUTF16(url.spec()), row.title());
+ EXPECT_EQ(0, row.visit_count());
+}
+
+TEST_F(HistoryBackendTest, ExpireHistoryForTimes) {
+ ASSERT_TRUE(backend_.get());
+
+ HistoryAddPageArgs args[10];
+ for (size_t i = 0; i < arraysize(args); ++i) {
+ args[i].url = GURL("http://example" +
+ std::string((i % 2 == 0 ? ".com" : ".net")));
+ args[i].time = base::Time::FromInternalValue(i);
+ backend_->AddPage(args[i]);
}
- // Re-open the db using the HistoryDatabase, which should migrate to the
- // current version, creating themime_type abd original_mime_type columns.
- CreateBackendAndDatabase();
- DeleteBackend();
- {
- // Re-open the db for manual manipulation.
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- // The version should have been updated.
- int cur_version = HistoryDatabase::GetCurrentVersion();
- ASSERT_LE(29, cur_version);
- {
- sql::Statement s(db.GetUniqueStatement(
- "SELECT value FROM meta WHERE key = 'version'"));
- EXPECT_TRUE(s.Step());
- EXPECT_EQ(cur_version, s.ColumnInt(0));
- }
- {
- sql::Statement s(db.GetUniqueStatement(
- "SELECT mime_type, original_mime_type from downloads"));
- EXPECT_TRUE(s.Step());
- EXPECT_EQ(std::string(), s.ColumnString(0));
- EXPECT_EQ(std::string(), s.ColumnString(1));
- }
+ EXPECT_EQ(base::Time(), backend_->GetFirstRecordedTimeForTest());
+
+ URLRow row;
+ for (size_t i = 0; i < arraysize(args); ++i) {
+ EXPECT_TRUE(backend_->GetURL(args[i].url, &row));
}
+
+ std::set<base::Time> times;
+ times.insert(args[5].time);
+ backend_->ExpireHistoryForTimes(times,
+ base::Time::FromInternalValue(2),
+ base::Time::FromInternalValue(8));
+
+ EXPECT_EQ(base::Time::FromInternalValue(0),
+ backend_->GetFirstRecordedTimeForTest());
+
+ // Visits to http://example.com are untouched.
+ VisitVector visit_vector;
+ EXPECT_TRUE(backend_->GetVisitsForURL(
+ backend_->db_->GetRowForURL(GURL("http://example.com"), NULL),
+ &visit_vector));
+ ASSERT_EQ(5u, visit_vector.size());
+ EXPECT_EQ(base::Time::FromInternalValue(0), visit_vector[0].visit_time);
+ EXPECT_EQ(base::Time::FromInternalValue(2), visit_vector[1].visit_time);
+ EXPECT_EQ(base::Time::FromInternalValue(4), visit_vector[2].visit_time);
+ EXPECT_EQ(base::Time::FromInternalValue(6), visit_vector[3].visit_time);
+ EXPECT_EQ(base::Time::FromInternalValue(8), visit_vector[4].visit_time);
+
+ // Visits to http://example.net between [2,8] are removed.
+ visit_vector.clear();
+ EXPECT_TRUE(backend_->GetVisitsForURL(
+ backend_->db_->GetRowForURL(GURL("http://example.net"), NULL),
+ &visit_vector));
+ ASSERT_EQ(2u, visit_vector.size());
+ EXPECT_EQ(base::Time::FromInternalValue(1), visit_vector[0].visit_time);
+ EXPECT_EQ(base::Time::FromInternalValue(9), visit_vector[1].visit_time);
+
+ EXPECT_EQ(base::Time::FromInternalValue(0),
+ backend_->GetFirstRecordedTimeForTest());
}
-TEST_F(HistoryBackendDBTest, ConfirmDownloadRowCreateAndDelete) {
- // Create the DB.
- CreateBackendAndDatabase();
-
- base::Time now(base::Time::Now());
-
- // Add some downloads.
- uint32 id1 = 1, id2 = 2, id3 = 3;
- AddDownload(id1, DownloadState::COMPLETE, now);
- AddDownload(id2, DownloadState::COMPLETE, now + base::TimeDelta::FromDays(2));
- AddDownload(id3, DownloadState::COMPLETE, now - base::TimeDelta::FromDays(2));
-
- // Confirm that resulted in the correct number of rows in the DB.
- DeleteBackend();
- {
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- sql::Statement statement(db.GetUniqueStatement(
- "Select Count(*) from downloads"));
- EXPECT_TRUE(statement.Step());
- EXPECT_EQ(3, statement.ColumnInt(0));
-
- sql::Statement statement1(db.GetUniqueStatement(
- "Select Count(*) from downloads_url_chains"));
- EXPECT_TRUE(statement1.Step());
- EXPECT_EQ(3, statement1.ColumnInt(0));
- }
-
- // Delete some rows and make sure the results are still correct.
- CreateBackendAndDatabase();
- db_->RemoveDownload(id2);
- db_->RemoveDownload(id3);
- DeleteBackend();
- {
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- sql::Statement statement(db.GetUniqueStatement(
- "Select Count(*) from downloads"));
- EXPECT_TRUE(statement.Step());
- EXPECT_EQ(1, statement.ColumnInt(0));
-
- sql::Statement statement1(db.GetUniqueStatement(
- "Select Count(*) from downloads_url_chains"));
- EXPECT_TRUE(statement1.Step());
- EXPECT_EQ(1, statement1.ColumnInt(0));
- }
-}
-
-TEST_F(HistoryBackendDBTest, DownloadNukeRecordsMissingURLs) {
- CreateBackendAndDatabase();
- base::Time now(base::Time::Now());
- std::vector<GURL> url_chain;
- DownloadRow download(base::FilePath(FILE_PATH_LITERAL("foo-path")),
- base::FilePath(FILE_PATH_LITERAL("foo-path")),
- url_chain,
- GURL(std::string()),
- "application/octet-stream",
- "application/octet-stream",
- now,
- now,
- std::string(),
- std::string(),
- 0,
- 512,
- DownloadState::COMPLETE,
- DownloadDangerType::NOT_DANGEROUS,
- kTestDownloadInterruptReasonNone,
- 1,
- 0,
- "by_ext_id",
- "by_ext_name");
-
- // Creating records without any urls should fail.
- EXPECT_FALSE(db_->CreateDownload(download));
-
- download.url_chain.push_back(GURL("foo-url"));
- EXPECT_TRUE(db_->CreateDownload(download));
-
- // Pretend that the URLs were dropped.
- DeleteBackend();
- {
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- sql::Statement statement(db.GetUniqueStatement(
- "DELETE FROM downloads_url_chains WHERE id=1"));
- ASSERT_TRUE(statement.Run());
- }
- CreateBackendAndDatabase();
- std::vector<DownloadRow> downloads;
- db_->QueryDownloads(&downloads);
- EXPECT_EQ(0U, downloads.size());
-
- // QueryDownloads should have nuked the corrupt record.
- DeleteBackend();
- {
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- {
- sql::Statement statement(db.GetUniqueStatement(
- "SELECT count(*) from downloads"));
- ASSERT_TRUE(statement.Step());
- EXPECT_EQ(0, statement.ColumnInt(0));
- }
+TEST_F(HistoryBackendTest, ExpireHistory) {
+ ASSERT_TRUE(backend_.get());
+ // Since history operations are dependent on the local timezone, make all
+ // entries relative to a fixed, local reference time.
+ base::Time reference_time = base::Time::UnixEpoch().LocalMidnight() +
+ base::TimeDelta::FromHours(12);
+
+ // Insert 4 entries into the database.
+ HistoryAddPageArgs args[4];
+ for (size_t i = 0; i < arraysize(args); ++i) {
+ args[i].url = GURL("http://example" + base::IntToString(i) + ".com");
+ args[i].time = reference_time + base::TimeDelta::FromDays(i);
+ backend_->AddPage(args[i]);
}
-}
-TEST_F(HistoryBackendDBTest, ConfirmDownloadInProgressCleanup) {
- // Create the DB.
- CreateBackendAndDatabase();
-
- base::Time now(base::Time::Now());
-
- // Put an IN_PROGRESS download in the DB.
- AddDownload(1, DownloadState::IN_PROGRESS, now);
-
- // Confirm that they made it into the DB unchanged.
- DeleteBackend();
- {
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- sql::Statement statement(db.GetUniqueStatement(
- "Select Count(*) from downloads"));
- EXPECT_TRUE(statement.Step());
- EXPECT_EQ(1, statement.ColumnInt(0));
-
- sql::Statement statement1(db.GetUniqueStatement(
- "Select state, interrupt_reason from downloads"));
- EXPECT_TRUE(statement1.Step());
- EXPECT_EQ(DownloadStateToInt(DownloadState::IN_PROGRESS),
- statement1.ColumnInt(0));
- EXPECT_EQ(DownloadInterruptReasonToInt(kTestDownloadInterruptReasonNone),
- statement1.ColumnInt(1));
- EXPECT_FALSE(statement1.Step());
- }
-
- // Read in the DB through query downloads, then test that the
- // right transformation was returned.
- CreateBackendAndDatabase();
- std::vector<DownloadRow> results;
- db_->QueryDownloads(&results);
- ASSERT_EQ(1u, results.size());
- EXPECT_EQ(DownloadState::INTERRUPTED, results[0].state);
- EXPECT_EQ(kTestDownloadInterruptReasonCrash, results[0].interrupt_reason);
-
- // Allow the update to propagate, shut down the DB, and confirm that
- // the query updated the on disk database as well.
- base::MessageLoop::current()->RunUntilIdle();
- DeleteBackend();
- {
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
- sql::Statement statement(db.GetUniqueStatement(
- "Select Count(*) from downloads"));
- EXPECT_TRUE(statement.Step());
- EXPECT_EQ(1, statement.ColumnInt(0));
-
- sql::Statement statement1(db.GetUniqueStatement(
- "Select state, interrupt_reason from downloads"));
- EXPECT_TRUE(statement1.Step());
- EXPECT_EQ(DownloadStateToInt(DownloadState::INTERRUPTED),
- statement1.ColumnInt(0));
- EXPECT_EQ(DownloadInterruptReasonToInt(kTestDownloadInterruptReasonCrash),
- statement1.ColumnInt(1));
- EXPECT_FALSE(statement1.Step());
- }
-}
-
-TEST_F(HistoryBackendDBTest, MigratePresentations) {
- // Create the db we want. Use 22 since segments didn't change in that time
- // frame.
- ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
-
- const SegmentID segment_id = 2;
- const URLID url_id = 3;
- const GURL url("http://www.foo.com");
- const std::string url_name(VisitSegmentDatabase::ComputeSegmentName(url));
- const base::string16 title(base::ASCIIToUTF16("Title1"));
- const base::Time segment_time(base::Time::Now());
-
- {
- // Re-open the db for manual manipulation.
- sql::Connection db;
- ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
-
- // Add an entry to urls.
- {
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO urls "
- "(id, url, title, last_visit_time) VALUES "
- "(?, ?, ?, ?)"));
- s.BindInt64(0, url_id);
- s.BindString(1, url.spec());
- s.BindString16(2, title);
- s.BindInt64(3, segment_time.ToInternalValue());
- ASSERT_TRUE(s.Run());
- }
+ URLRow url_rows[4];
+ for (unsigned int i = 0; i < arraysize(args); ++i)
+ ASSERT_TRUE(backend_->GetURL(args[i].url, &url_rows[i]));
- // Add an entry to segments.
- {
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO segments "
- "(id, name, url_id, pres_index) VALUES "
- "(?, ?, ?, ?)"));
- s.BindInt64(0, segment_id);
- s.BindString(1, url_name);
- s.BindInt64(2, url_id);
- s.BindInt(3, 4); // pres_index
- ASSERT_TRUE(s.Run());
- }
+ std::vector<ExpireHistoryArgs> expire_list;
+ VisitVector visits;
- // And one to segment_usage.
- {
- sql::Statement s(db.GetUniqueStatement(
- "INSERT INTO segment_usage "
- "(id, segment_id, time_slot, visit_count) VALUES "
- "(?, ?, ?, ?)"));
- s.BindInt64(0, 4); // id.
- s.BindInt64(1, segment_id);
- s.BindInt64(2, segment_time.ToInternalValue());
- s.BindInt(3, 5); // visit count.
- ASSERT_TRUE(s.Run());
- }
+ // Passing an empty map should be a no-op.
+ backend_->ExpireHistory(expire_list);
+ backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
+ EXPECT_EQ(4U, visits.size());
+
+ // Trying to delete an unknown URL with the time of the first visit should
+ // also be a no-op.
+ expire_list.resize(expire_list.size() + 1);
+ expire_list[0].SetTimeRangeForOneDay(args[0].time);
+ expire_list[0].urls.insert(GURL("http://google.does-not-exist"));
+ backend_->ExpireHistory(expire_list);
+ backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
+ EXPECT_EQ(4U, visits.size());
+
+ // Now add the first URL with the same time -- it should get deleted.
+ expire_list.back().urls.insert(url_rows[0].url());
+ backend_->ExpireHistory(expire_list);
+
+ backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
+ ASSERT_EQ(3U, visits.size());
+ EXPECT_EQ(visits[0].url_id, url_rows[1].id());
+ EXPECT_EQ(visits[1].url_id, url_rows[2].id());
+ EXPECT_EQ(visits[2].url_id, url_rows[3].id());
+
+ // The first recorded time should also get updated.
+ EXPECT_EQ(backend_->GetFirstRecordedTimeForTest(), args[1].time);
+
+ // Now delete the rest of the visits in one call.
+ for (unsigned int i = 1; i < arraysize(args); ++i) {
+ expire_list.resize(expire_list.size() + 1);
+ expire_list[i].SetTimeRangeForOneDay(args[i].time);
+ expire_list[i].urls.insert(args[i].url);
}
+ backend_->ExpireHistory(expire_list);
- // Re-open the db, triggering migration.
- CreateBackendAndDatabase();
+ backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
+ ASSERT_EQ(0U, visits.size());
+}
+
+TEST_F(HistoryBackendTest, DeleteMatchingUrlsForKeyword) {
+ // Set up urls and keyword_search_terms
+ GURL url1("https://www.bing.com/?q=bar");
+ URLRow url_info1(url1);
+ url_info1.set_visit_count(0);
+ url_info1.set_typed_count(0);
+ url_info1.set_last_visit(base::Time());
+ url_info1.set_hidden(false);
+ const URLID url1_id = backend_->db()->AddURL(url_info1);
+ EXPECT_NE(0, url1_id);
+
+ KeywordID keyword_id = 1;
+ base::string16 keyword = base::UTF8ToUTF16("bar");
+ ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
+ url1_id, keyword_id, keyword));
+
+ GURL url2("https://www.google.com/?q=bar");
+ URLRow url_info2(url2);
+ url_info2.set_visit_count(0);
+ url_info2.set_typed_count(0);
+ url_info2.set_last_visit(base::Time());
+ url_info2.set_hidden(false);
+ const URLID url2_id = backend_->db()->AddURL(url_info2);
+ EXPECT_NE(0, url2_id);
+
+ KeywordID keyword_id2 = 2;
+ ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
+ url2_id, keyword_id2, keyword));
- std::vector<PageUsageData*> results;
- db_->QuerySegmentUsage(segment_time, 10, &results);
- ASSERT_EQ(1u, results.size());
- EXPECT_EQ(url, results[0]->GetURL());
- EXPECT_EQ(segment_id, results[0]->GetID());
- EXPECT_EQ(title, results[0]->GetTitle());
- STLDeleteElements(&results);
+ // Add another visit to the same URL
+ URLRow url_info3(url2);
+ url_info3.set_visit_count(0);
+ url_info3.set_typed_count(0);
+ url_info3.set_last_visit(base::Time());
+ url_info3.set_hidden(false);
+ const URLID url3_id = backend_->db()->AddURL(url_info3);
+ EXPECT_NE(0, url3_id);
+ ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
+ url3_id, keyword_id2, keyword));
+
+ // Test that deletion works correctly
+ backend_->DeleteMatchingURLsForKeyword(keyword_id2, keyword);
+
+ // Test that rows 2 and 3 are deleted, while 1 is intact
+ URLRow row;
+ EXPECT_TRUE(backend_->db()->GetURLRow(url1_id, &row));
+ EXPECT_EQ(url1.spec(), row.url().spec());
+ EXPECT_FALSE(backend_->db()->GetURLRow(url2_id, &row));
+ EXPECT_FALSE(backend_->db()->GetURLRow(url3_id, &row));
+
+ // Test that corresponding keyword search terms are deleted for rows 2 & 3,
+ // but not for row 1
+ EXPECT_TRUE(backend_->db()->GetKeywordSearchTermRow(url1_id, NULL));
+ EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url2_id, NULL));
+ EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url3_id, NULL));
+}
+
+// Simple test that removes a bookmark. This test exercises the code paths in
+// History that block till bookmark bar model is loaded.
+TEST_F(HistoryBackendTest, RemoveNotification) {
+ base::ScopedTempDir scoped_temp_dir;
+ EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDirUnderPath(test_dir()));
+
+ // Add a URL.
+ GURL url("http://www.google.com");
+ HistoryClientMock history_client;
+ history_client.AddBookmark(url);
+ scoped_ptr<HistoryService> service(new HistoryService(
+ &history_client, scoped_ptr<history::VisitDelegate>()));
+ EXPECT_TRUE(
+ service->Init(kAcceptLanguagesForTest,
+ TestHistoryDatabaseParamsForPath(scoped_temp_dir.path())));
+
+ service->AddPage(
+ url, base::Time::Now(), NULL, 1, GURL(), RedirectList(),
+ ui::PAGE_TRANSITION_TYPED, SOURCE_BROWSED, false);
+
+ // This won't actually delete the URL, rather it'll empty out the visits.
+ // This triggers blocking on the BookmarkModel.
+ EXPECT_CALL(history_client, BlockUntilBookmarksLoaded());
+ service->DeleteURL(url);
+}
+
+// Test DeleteFTSIndexDatabases deletes expected files.
+TEST_F(HistoryBackendTest, DeleteFTSIndexDatabases) {
+ ASSERT_TRUE(backend_.get());
+
+ base::FilePath history_path(test_dir());
+ base::FilePath db1(history_path.AppendASCII("History Index 2013-05"));
+ base::FilePath db1_journal(db1.InsertBeforeExtensionASCII("-journal"));
+ base::FilePath db1_wal(db1.InsertBeforeExtensionASCII("-wal"));
+ base::FilePath db2_symlink(history_path.AppendASCII("History Index 2013-06"));
+ base::FilePath db2_actual(history_path.AppendASCII("Underlying DB"));
+
+ // Setup dummy index database files.
+ const char* data = "Dummy";
+ const size_t data_len = 5;
+ ASSERT_TRUE(base::WriteFile(db1, data, data_len));
+ ASSERT_TRUE(base::WriteFile(db1_journal, data, data_len));
+ ASSERT_TRUE(base::WriteFile(db1_wal, data, data_len));
+ ASSERT_TRUE(base::WriteFile(db2_actual, data, data_len));
+#if defined(OS_POSIX)
+ EXPECT_TRUE(base::CreateSymbolicLink(db2_actual, db2_symlink));
+#endif
+
+ // Delete all DTS index databases.
+ backend_->DeleteFTSIndexDatabases();
+ EXPECT_FALSE(base::PathExists(db1));
+ EXPECT_FALSE(base::PathExists(db1_wal));
+ EXPECT_FALSE(base::PathExists(db1_journal));
+ EXPECT_FALSE(base::PathExists(db2_symlink));
+ EXPECT_TRUE(base::PathExists(db2_actual)); // Symlinks shouldn't be followed.
+}
+
+// Common implementation for the two tests below, given that the only difference
+// between them is the type of the notification sent out.
+void InMemoryHistoryBackendTest::TestAddingAndChangingURLRows(
+ const SimulateNotificationCallback& callback) {
+ const char kTestTypedURLAlternativeTitle[] = "Google Search Again";
+ const char kTestNonTypedURLAlternativeTitle[] = "Google News Again";
+
+ // Notify the in-memory database that a typed and non-typed URLRow (which were
+ // never before seen by the cache) have been modified.
+ URLRow row1(CreateTestTypedURL());
+ URLRow row2(CreateTestNonTypedURL());
+ callback.Run(&row1, &row2, nullptr);
+
+ // The in-memory database should only pick up the typed URL, and should ignore
+ // the non-typed one. The typed URL should retain the ID that was present in
+ // the notification.
+ URLRow cached_row1, cached_row2;
+ EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
+ EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
+ EXPECT_EQ(row1.id(), cached_row1.id());
+
+ // Try changing attributes (other than typed_count) for existing URLRows.
+ row1.set_title(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle));
+ row2.set_title(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle));
+ callback.Run(&row1, &row2, nullptr);
+
+ // URLRows that are cached by the in-memory database should be updated.
+ EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
+ EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
+ EXPECT_EQ(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle),
+ cached_row1.title());
+
+ // Now decrease the typed count for the typed URLRow, and increase it for the
+ // previously non-typed URLRow.
+ row1.set_typed_count(0);
+ row2.set_typed_count(2);
+ callback.Run(&row1, &row2, nullptr);
+
+ // The in-memory database should stop caching the first URLRow, and start
+ // caching the second URLRow.
+ EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
+ EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
+ EXPECT_EQ(row2.id(), cached_row2.id());
+ EXPECT_EQ(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle),
+ cached_row2.title());
+}
+
+TEST_F(InMemoryHistoryBackendTest, OnURLsModified) {
+ TestAddingAndChangingURLRows(base::Bind(
+ &SimulateNotificationURLsModified, base::Unretained(mem_backend_.get())));
+}
+
+TEST_F(InMemoryHistoryBackendTest, OnURLsVisisted) {
+ TestAddingAndChangingURLRows(base::Bind(
+ &SimulateNotificationURLVisited, base::Unretained(mem_backend_.get())));
+}
+
+TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedPiecewise) {
+ // Add two typed and one non-typed URLRow to the in-memory database.
+ URLRow row1(CreateTestTypedURL());
+ URLRow row2(CreateAnotherTestTypedURL());
+ URLRow row3(CreateTestNonTypedURL());
+ SimulateNotificationURLsModified(mem_backend_.get(), &row1, &row2, &row3);
+
+ // Notify the in-memory database that the second typed URL and the non-typed
+ // URL has been deleted.
+ SimulateNotificationURLsDeleted(&row2, &row3);
+
+ // Expect that the first typed URL remains intact, the second typed URL is
+ // correctly removed, and the non-typed URL does not magically appear.
+ URLRow cached_row1;
+ EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
+ EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), NULL));
+ EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row3.url(), NULL));
+ EXPECT_EQ(row1.id(), cached_row1.id());
+}
+
+TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedEnMasse) {
+ // Add two typed and one non-typed URLRow to the in-memory database.
+ URLRow row1(CreateTestTypedURL());
+ URLRow row2(CreateAnotherTestTypedURL());
+ URLRow row3(CreateTestNonTypedURL());
+ SimulateNotificationURLsModified(mem_backend_.get(), &row1, &row2, &row3);
+
+ // Now notify the in-memory database that all history has been deleted.
+ mem_backend_->OnURLsDeleted(nullptr, true, false, URLRows(),
+ std::set<GURL>());
+
+ // Expect that everything goes away.
+ EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), NULL));
+ EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), NULL));
+ EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row3.url(), NULL));
+}
+
+void InMemoryHistoryBackendTest::PopulateTestURLsAndSearchTerms(
+ URLRow* row1,
+ URLRow* row2,
+ const base::string16& term1,
+ const base::string16& term2) {
+ // Add a typed and a non-typed URLRow to the in-memory database. This time,
+ // though, do it through the history backend...
+ URLRows rows;
+ rows.push_back(*row1);
+ rows.push_back(*row2);
+ backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
+ backend_->db()->GetRowForURL(row1->url(), row1); // Get effective IDs from
+ backend_->db()->GetRowForURL(row2->url(), row2); // the database.
+
+ // ... so that we can also use that for adding the search terms. This way, we
+ // not only test that the notifications involved are handled correctly, but
+ // also that they are fired correctly (in the history backend).
+ backend_->SetKeywordSearchTermsForURL(row1->url(), kTestKeywordId, term1);
+ backend_->SetKeywordSearchTermsForURL(row2->url(), kTestKeywordId, term2);
+}
+
+TEST_F(InMemoryHistoryBackendTest, SetKeywordSearchTerms) {
+ URLRow row1(CreateTestTypedURL());
+ URLRow row2(CreateTestNonTypedURL());
+ base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
+ base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
+ PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
+
+ // Both URLs now have associated search terms, so the in-memory database
+ // should cache both of them, regardless whether they have been typed or not.
+ URLRow cached_row1, cached_row2;
+ EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
+ EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
+ EXPECT_EQ(row1.id(), cached_row1.id());
+ EXPECT_EQ(row2.id(), cached_row2.id());
+
+ // Verify that lookups will actually return both search terms; and also check
+ // at the low level that the rows are there.
+ EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
+ EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
+ EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
+ EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
+}
+
+TEST_F(InMemoryHistoryBackendTest, DeleteKeywordSearchTerms) {
+ URLRow row1(CreateTestTypedURL());
+ URLRow row2(CreateTestNonTypedURL());
+ base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
+ base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
+ PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
+
+ // Delete both search terms. This should be reflected in the in-memory DB.
+ backend_->DeleteKeywordSearchTermForURL(row1.url());
+ backend_->DeleteKeywordSearchTermForURL(row2.url());
+
+ // The typed URL should remain intact.
+ // Note: we do not need to guarantee anything about the non-typed URL.
+ URLRow cached_row1;
+ EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
+ EXPECT_EQ(row1.id(), cached_row1.id());
+
+ // Verify that the search terms are no longer returned as results, and also
+ // check at the low level that they are gone for good.
+ EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
+ EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
+ EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
+ EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
+}
+
+TEST_F(InMemoryHistoryBackendTest, DeleteAllSearchTermsForKeyword) {
+ URLRow row1(CreateTestTypedURL());
+ URLRow row2(CreateTestNonTypedURL());
+ base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
+ base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
+ PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
+
+ // Delete all corresponding search terms from the in-memory database.
+ KeywordID id = kTestKeywordId;
+ mem_backend_->DeleteAllSearchTermsForKeyword(id);
+
+ // The typed URL should remain intact.
+ // Note: we do not need to guarantee anything about the non-typed URL.
+ URLRow cached_row1;
+ EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
+ EXPECT_EQ(row1.id(), cached_row1.id());
+
+ // Verify that the search terms are no longer returned as results, and also
+ // check at the low level that they are gone for good.
+ EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
+ EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
+ EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
+ EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
+}
+
+TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedWithSearchTerms) {
+ URLRow row1(CreateTestTypedURL());
+ URLRow row2(CreateTestNonTypedURL());
+ base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
+ base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
+ PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
+
+ // Notify the in-memory database that the second typed URL has been deleted.
+ SimulateNotificationURLsDeleted(&row2);
+
+ // Verify that the second term is no longer returned as result, and also check
+ // at the low level that it is gone for good. The term corresponding to the
+ // first URLRow should not be affected.
+ EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
+ EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
+ EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
+ EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
}
-} // namespace
} // namespace history
diff --git a/components/history/core/browser/history_constants.cc b/components/history/core/browser/history_constants.cc
index 223cca8..1b18dc7 100644
--- a/components/history/core/browser/history_constants.cc
+++ b/components/history/core/browser/history_constants.cc
@@ -6,15 +6,18 @@
#include "base/files/file_path.h"
-#define FPL FILE_PATH_LITERAL
-
namespace history {
// filenames
const base::FilePath::CharType kArchivedHistoryFilename[] =
- FPL("Archived History");
-const base::FilePath::CharType kFaviconsFilename[] = FPL("Favicons");
-const base::FilePath::CharType kHistoryFilename[] = FPL("History");
-const base::FilePath::CharType kThumbnailsFilename[] = FPL("Thumbnails");
+ FILE_PATH_LITERAL("Archived History");
+const base::FilePath::CharType kFaviconsFilename[] =
+ FILE_PATH_LITERAL("Favicons");
+const base::FilePath::CharType kHistoryFilename[] =
+ FILE_PATH_LITERAL("History");
+const base::FilePath::CharType kThumbnailsFilename[] =
+ FILE_PATH_LITERAL("Thumbnails");
+const base::FilePath::CharType kTopSitesFilename[] =
+ FILE_PATH_LITERAL("Top Sites");
} // namespace history
diff --git a/components/history/core/browser/history_constants.h b/components/history/core/browser/history_constants.h
index 4bbb478..875b099 100644
--- a/components/history/core/browser/history_constants.h
+++ b/components/history/core/browser/history_constants.h
@@ -14,6 +14,7 @@ extern const base::FilePath::CharType kArchivedHistoryFilename[];
extern const base::FilePath::CharType kFaviconsFilename[];
extern const base::FilePath::CharType kHistoryFilename[];
extern const base::FilePath::CharType kThumbnailsFilename[];
+extern const base::FilePath::CharType kTopSitesFilename[];
} // namespace history
diff --git a/chrome/browser/history/history_database_unittest.cc b/components/history/core/browser/history_database_unittest.cc
index 886d1e9..3f90bf8 100644
--- a/chrome/browser/history/history_database_unittest.cc
+++ b/components/history/core/browser/history_database_unittest.cc
@@ -8,7 +8,7 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
-#include "chrome/common/chrome_paths.h"
+#include "components/history/core/test/database_test_utils.h"
#include "components/history/core/test/test_history_database.h"
#include "sql/init_status.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -25,10 +25,9 @@ TEST(HistoryDatabaseTest, DropBookmarks) {
// Copy db file over that contains starred URLs.
base::FilePath old_history_path;
- PathService::Get(chrome::DIR_TEST_DATA, &old_history_path);
- old_history_path = old_history_path.AppendASCII("bookmarks");
- old_history_path = old_history_path.Append(
- FILE_PATH_LITERAL("History_with_starred"));
+ EXPECT_TRUE(GetTestDataHistoryDir(&old_history_path));
+ old_history_path =
+ old_history_path.Append(FILE_PATH_LITERAL("History_with_starred"));
base::CopyFile(old_history_path, db_file);
// Load the DB twice. The first time it should migrate. Make sure that the
diff --git a/chrome/browser/history/history_querying_unittest.cc b/components/history/core/browser/history_querying_unittest.cc
index 0897589..535688c 100644
--- a/chrome/browser/history/history_querying_unittest.cc
+++ b/components/history/core/browser/history_querying_unittest.cc
@@ -16,9 +16,6 @@
#include "components/history/core/test/test_history_database.h"
#include "testing/gtest/include/gtest/gtest.h"
-using base::Time;
-using base::TimeDelta;
-
// Tests the history service for querying functionality.
namespace history {
@@ -29,7 +26,7 @@ struct TestEntry {
const char* url;
const char* title;
const int days_ago;
- Time time; // Filled by SetUp.
+ base::Time time; // Filled by SetUp.
} test_entries[] = {
// This one is visited super long ago so it will be in a different database
// from the next appearance of it at the end.
@@ -74,8 +71,7 @@ bool NthResultIs(const QueryResults& results,
class HistoryQueryTest : public testing::Test {
public:
- HistoryQueryTest() : nav_entry_id_(0) {
- }
+ HistoryQueryTest() : nav_entry_id_(0) {}
// Acts like a synchronous call to history's QueryHistory.
void QueryHistory(const std::string& text_query,
@@ -174,10 +170,10 @@ class HistoryQueryTest : public testing::Test {
}
// Fill the test data.
- Time now = Time::Now().LocalMidnight();
+ base::Time now = base::Time::Now().LocalMidnight();
for (size_t i = 0; i < arraysize(test_entries); i++) {
test_entries[i].time =
- now - (test_entries[i].days_ago * TimeDelta::FromDays(1));
+ now - (test_entries[i].days_ago * base::TimeDelta::FromDays(1));
AddEntryToHistory(test_entries[i]);
}
}
@@ -273,7 +269,8 @@ TEST_F(HistoryQueryTest, ReachedBeginning) {
EXPECT_FALSE(results.reached_beginning());
// Try |begin_time| just later than the oldest visit.
- options.begin_time = test_entries[0].time + TimeDelta::FromMicroseconds(1);
+ options.begin_time =
+ test_entries[0].time + base::TimeDelta::FromMicroseconds(1);
QueryHistory(std::string(), options, &results);
EXPECT_FALSE(results.reached_beginning());
QueryHistory("some", options, &results);
@@ -287,7 +284,8 @@ TEST_F(HistoryQueryTest, ReachedBeginning) {
EXPECT_TRUE(results.reached_beginning());
// Try |begin_time| just earlier than the oldest visit.
- options.begin_time = test_entries[0].time - TimeDelta::FromMicroseconds(1);
+ options.begin_time =
+ test_entries[0].time - base::TimeDelta::FromMicroseconds(1);
QueryHistory(std::string(), options, &results);
EXPECT_TRUE(results.reached_beginning());
QueryHistory("some", options, &results);
diff --git a/components/history/core/browser/in_memory_history_backend.h b/components/history/core/browser/in_memory_history_backend.h
index 9335c9a..50828b9 100644
--- a/components/history/core/browser/in_memory_history_backend.h
+++ b/components/history/core/browser/in_memory_history_backend.h
@@ -35,8 +35,10 @@ class FilePath;
namespace history {
+class HistoryBackendTestBase;
class HistoryService;
class InMemoryDatabase;
+class InMemoryHistoryBackendTest;
class URLDatabase;
class URLRow;
diff --git a/chrome/browser/history/thumbnail_database_unittest.cc b/components/history/core/browser/thumbnail_database_unittest.cc
index 4b552d2..631d0b2 100644
--- a/chrome/browser/history/thumbnail_database_unittest.cc
+++ b/components/history/core/browser/thumbnail_database_unittest.cc
@@ -11,8 +11,8 @@
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted_memory.h"
#include "base/path_service.h"
-#include "chrome/common/chrome_paths.h"
#include "components/history/core/browser/thumbnail_database.h"
+#include "components/history/core/test/database_test_utils.h"
#include "sql/connection.h"
#include "sql/recovery.h"
#include "sql/test/scoped_error_ignorer.h"
@@ -48,17 +48,6 @@ const GURL kIconUrl5 = GURL("http://www.bing.com/favicon.ico");
const gfx::Size kSmallSize = gfx::Size(16, 16);
const gfx::Size kLargeSize = gfx::Size(32, 32);
-// Create the test database at |db_path| from the golden file at
-// |ascii_path| in the "History/" subdir of the test data dir.
-WARN_UNUSED_RESULT bool CreateDatabaseFromSQL(const base::FilePath &db_path,
- const char* ascii_path) {
- base::FilePath sql_path;
- if (!PathService::Get(chrome::DIR_TEST_DATA, &sql_path))
- return false;
- sql_path = sql_path.AppendASCII("History").AppendASCII(ascii_path);
- return sql::test::CreateDatabaseFromSQL(db_path, sql_path);
-}
-
// Verify that the up-to-date database has the expected tables and
// columns. Functional tests only check whether the things which
// should be there are, but do not check if extraneous items are
@@ -161,8 +150,7 @@ WARN_UNUSED_RESULT bool CheckPageHasIcon(
class ThumbnailDatabaseTest : public testing::Test {
public:
- ThumbnailDatabaseTest() {
- }
+ ThumbnailDatabaseTest() {}
~ThumbnailDatabaseTest() override {}
// Initialize a thumbnail database instance from the SQL file at
@@ -245,7 +233,7 @@ TEST_F(ThumbnailDatabaseTest, LastRequestedTime) {
EXPECT_TRUE(db.SetFaviconBitmapLastRequestedTime(id, now));
EXPECT_TRUE(db.GetFaviconBitmap(id, NULL, &last_requested, NULL, NULL));
EXPECT_EQ(last_requested, now);
- }
+}
TEST_F(ThumbnailDatabaseTest, DeleteIconMappings) {
ThumbnailDatabase db(NULL);
@@ -1098,8 +1086,8 @@ TEST_F(ThumbnailDatabaseTest, Recovery5) {
// successfully, and result in the correct schema.
TEST_F(ThumbnailDatabaseTest, WildSchema) {
base::FilePath sql_path;
- ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &sql_path));
- sql_path = sql_path.AppendASCII("History").AppendASCII("thumbnail_wild");
+ EXPECT_TRUE(GetTestDataHistoryDir(&sql_path));
+ sql_path = sql_path.AppendASCII("thumbnail_wild");
base::FileEnumerator fe(
sql_path, false, base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.sql"));
diff --git a/components/history/core/browser/top_sites_impl.cc b/components/history/core/browser/top_sites_impl.cc
index c5b1873..4df526b 100644
--- a/components/history/core/browser/top_sites_impl.cc
+++ b/components/history/core/browser/top_sites_impl.cc
@@ -22,6 +22,7 @@
#include "base/task_runner.h"
#include "base/values.h"
#include "components/history/core/browser/history_backend.h"
+#include "components/history/core/browser/history_constants.h"
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/page_usage_data.h"
#include "components/history/core/browser/top_sites_cache.h"
diff --git a/chrome/browser/history/top_sites_impl_unittest.cc b/components/history/core/browser/top_sites_impl_unittest.cc
index 25d3646..f57d814 100644
--- a/chrome/browser/history/top_sites_impl_unittest.cc
+++ b/components/history/core/browser/top_sites_impl_unittest.cc
@@ -2,45 +2,46 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "components/history/core/browser/top_sites_impl.h"
+
#include "base/bind.h"
+#include "base/files/scoped_temp_dir.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/testing_pref_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/cancelable_task_tracker.h"
-#include "chrome/browser/history/history_service_factory.h"
-#include "chrome/browser/history/top_sites_factory.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/test/base/testing_profile.h"
+#include "components/history/core/browser/history_constants.h"
+#include "components/history/core/browser/history_database_params.h"
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/history_types.h"
#include "components/history/core/browser/top_sites.h"
#include "components/history/core/browser/top_sites_cache.h"
-#include "components/history/core/browser/top_sites_impl.h"
#include "components/history/core/browser/top_sites_observer.h"
+#include "components/history/core/browser/visit_delegate.h"
#include "components/history/core/test/history_unittest_base.h"
-#include "content/public/test/test_browser_thread.h"
+#include "components/history/core/test/test_history_database.h"
+#include "components/history/core/test/wait_top_sites_loaded_observer.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/codec/jpeg_codec.h"
#include "url/gurl.h"
-using content::BrowserThread;
-
namespace history {
namespace {
-static const char kPrepopulatedPageURL[] =
+// Key for URL blacklist.
+const char kBlacklistURLKey[] = "test.blacklist.url";
+const char kAcceptLanguages[] = "en-US,en";
+
+const char kApplicationScheme[] = "application";
+const char kPrepopulatedPageURL[] =
"http://www.google.com/int/chrome/welcome.html";
-// Create a TopSites implementation for testing.
-scoped_refptr<RefcountedKeyedService> BuildTopSitesImpl(
- content::BrowserContext* context) {
- PrepopulatedPageList prepopulated_pages;
- prepopulated_pages.push_back(PrepopulatedPage(GURL(kPrepopulatedPageURL),
- base::string16(), -1, -1, 0));
- return TopSitesFactory::BuildTopSites(context, prepopulated_pages);
+// Returns whether |url| can be added to history.
+bool MockCanAddURLToHistory(const GURL& url) {
+ return url.is_valid() && !url.SchemeIs(kApplicationScheme);
}
// Used by WaitForHistory, see it for details.
@@ -66,9 +67,7 @@ class WaitForHistoryTask : public HistoryDBTask {
class TopSitesQuerier {
public:
TopSitesQuerier()
- : number_of_callbacks_(0),
- waiting_(false),
- weak_ptr_factory_(this) {}
+ : number_of_callbacks_(0), waiting_(false), weak_ptr_factory_(this) {}
// Queries top sites. If |wait| is true a nested message loop is run until the
// callback is notified.
@@ -92,9 +91,7 @@ class TopSitesQuerier {
}
}
- void CancelRequest() {
- weak_ptr_factory_.InvalidateWeakPtrs();
- }
+ void CancelRequest() { weak_ptr_factory_.InvalidateWeakPtrs(); }
void set_urls(const MostVisitedURLList& urls) { urls_ = urls; }
const MostVisitedURLList& urls() const { return urls_; }
@@ -120,14 +117,6 @@ class TopSitesQuerier {
DISALLOW_COPY_AND_ASSIGN(TopSitesQuerier);
};
-// Extracts the data from |t1| into a SkBitmap. This is intended for usage of
-// thumbnail data, which is stored as jpgs.
-SkBitmap ExtractThumbnail(const base::RefCountedMemory& t1) {
- scoped_ptr<SkBitmap> image(gfx::JPEGCodec::Decode(t1.front(),
- t1.size()));
- return image.get() ? *image : SkBitmap();
-}
-
// Returns true if t1 and t2 contain the same data.
bool ThumbnailsAreEqual(base::RefCountedMemory* t1,
base::RefCountedMemory* t2) {
@@ -142,34 +131,26 @@ bool ThumbnailsAreEqual(base::RefCountedMemory* t1,
class TopSitesImplTest : public HistoryUnitTestBase {
public:
- TopSitesImplTest()
- : ui_thread_(BrowserThread::UI, &message_loop_),
- db_thread_(BrowserThread::DB, &message_loop_) {
- }
+ TopSitesImplTest() {}
void SetUp() override {
- profile_.reset(new TestingProfile);
- if (CreateHistoryAndTopSites()) {
- ASSERT_TRUE(profile_->CreateHistoryService(false, false));
- ResetTopSites();
- profile_->BlockUntilTopSitesLoaded();
- }
+ ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
+ pref_service_.reset(new TestingPrefServiceSimple);
+ pref_service_->registry()->RegisterDictionaryPref(kBlacklistURLKey);
+ history_service_.reset(
+ new HistoryService(nullptr, scoped_ptr<VisitDelegate>()));
+ ASSERT_TRUE(history_service_->Init(
+ kAcceptLanguages,
+ TestHistoryDatabaseParamsForPath(scoped_temp_dir_.path())));
+ ResetTopSites();
+ WaitTopSitesLoaded();
}
void TearDown() override {
- profile_.reset();
- }
-
- // Returns true if history and top sites should be created in SetUp.
- virtual bool CreateHistoryAndTopSites() {
- return true;
- }
-
- // Gets the thumbnail for |url| from TopSites.
- SkBitmap GetThumbnail(const GURL& url) {
- scoped_refptr<base::RefCountedMemory> data;
- return top_sites()->GetPageThumbnail(url, false, &data) ?
- ExtractThumbnail(*data.get()) : SkBitmap();
+ DestroyTopSites();
+ history_service_->Shutdown();
+ history_service_.reset();
+ pref_service_.reset();
}
// Creates a bitmap of the specified color. Caller takes ownership.
@@ -207,15 +188,9 @@ class TopSitesImplTest : public HistoryUnitTestBase {
base::MessageLoop::current()->Run();
}
- TopSitesImpl* top_sites() {
- return static_cast<TopSitesImpl*>(
- TopSitesFactory::GetForProfile(profile_.get()).get());
- }
- TestingProfile* profile() {return profile_.get();}
- HistoryService* history_service() {
- return HistoryServiceFactory::GetForProfile(
- profile_.get(), ServiceAccessType::EXPLICIT_ACCESS);
- }
+ TopSitesImpl* top_sites() { return top_sites_impl_.get(); }
+
+ HistoryService* history_service() { return history_service_.get(); }
PrepopulatedPageList GetPrepopulatedPages() {
return top_sites()->GetPrepopulatedPages();
@@ -236,9 +211,7 @@ class TopSitesImplTest : public HistoryUnitTestBase {
// Quit the current message loop when invoked. Useful when running a nested
// message loop.
- void QuitCallback() {
- base::MessageLoop::current()->Quit();
- }
+ void QuitCallback() { base::MessageLoop::current()->Quit(); }
// Adds a page to history.
void AddPageToHistory(const GURL& url) {
@@ -246,8 +219,7 @@ class TopSitesImplTest : public HistoryUnitTestBase {
redirects.push_back(url);
history_service()->AddPage(
url, base::Time::Now(), reinterpret_cast<ContextID>(1), 0, GURL(),
- redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED,
- false);
+ redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false);
}
// Adds a page to history.
@@ -256,8 +228,7 @@ class TopSitesImplTest : public HistoryUnitTestBase {
redirects.push_back(url);
history_service()->AddPage(
url, base::Time::Now(), reinterpret_cast<ContextID>(1), 0, GURL(),
- redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED,
- false);
+ redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false);
history_service()->SetPageTitle(url, title);
}
@@ -274,9 +245,7 @@ class TopSitesImplTest : public HistoryUnitTestBase {
}
// Delets a url.
- void DeleteURL(const GURL& url) {
- history_service()->DeleteURL(url);
- }
+ void DeleteURL(const GURL& url) { history_service()->DeleteURL(url); }
// Returns true if the thumbnail equals the specified bytes.
bool ThumbnailEqualsBytes(const gfx::Image& image,
@@ -290,8 +259,7 @@ class TopSitesImplTest : public HistoryUnitTestBase {
void RecreateTopSitesAndBlock() {
// Recreate TopSites and wait for it to load.
ResetTopSites();
- // As history already loaded we have to fake this call.
- profile()->BlockUntilTopSitesLoaded();
+ WaitTopSitesLoaded();
}
// Wrappers that allow private TopSites functions to be called from the
@@ -309,9 +277,7 @@ class TopSitesImplTest : public HistoryUnitTestBase {
return top_sites()->AddForcedURL(url, time);
}
- void StartQueryForMostVisited() {
- top_sites()->StartQueryForMostVisited();
- }
+ void StartQueryForMostVisited() { top_sites()->StartQueryForMostVisited(); }
void SetLastNumUrlsChanged(size_t value) {
top_sites()->last_num_urls_changed_ = value;
@@ -319,9 +285,7 @@ class TopSitesImplTest : public HistoryUnitTestBase {
size_t last_num_urls_changed() { return top_sites()->last_num_urls_changed_; }
- base::TimeDelta GetUpdateDelay() {
- return top_sites()->GetUpdateDelay();
- }
+ base::TimeDelta GetUpdateDelay() { return top_sites()->GetUpdateDelay(); }
bool IsTopSitesLoaded() { return top_sites()->loaded_; }
@@ -337,19 +301,45 @@ class TopSitesImplTest : public HistoryUnitTestBase {
void ResetTopSites() {
// TopSites shutdown takes some time as it happens on the DB thread and does
- // not support the existence of two TopSitesImpl for a single profile (due
- // to database locking). TestingProfile::DestroyTopSites() waits for the
- // TopSites cleanup to complete before returning.
- profile_->DestroyTopSites();
- TopSitesFactory::GetInstance()->SetTestingFactory(profile_.get(),
- BuildTopSitesImpl);
+ // not support the existence of two TopSitesImpl for a location (due to
+ // database locking). DestroyTopSites() waits for the TopSites cleanup to
+ // complete before returning.
+ DestroyTopSites();
+ DCHECK(!top_sites_impl_);
+ PrepopulatedPageList prepopulated_pages;
+ prepopulated_pages.push_back(PrepopulatedPage(GURL(kPrepopulatedPageURL),
+ base::string16(), -1, -1, 0));
+ top_sites_impl_ = new TopSitesImpl(
+ pref_service_.get(), history_service_.get(), kBlacklistURLKey,
+ prepopulated_pages, base::Bind(MockCanAddURLToHistory));
+ top_sites_impl_->Init(scoped_temp_dir_.path().Append(kTopSitesFilename),
+ message_loop_.task_runner());
+ }
+
+ void DestroyTopSites() {
+ if (top_sites_impl_) {
+ top_sites_impl_->ShutdownOnUIThread();
+ top_sites_impl_ = nullptr;
+
+ if (base::MessageLoop::current())
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+ }
+
+ void WaitTopSitesLoaded() {
+ DCHECK(top_sites_impl_);
+ WaitTopSitesLoadedObserver wait_top_sites_loaded_observer(top_sites_impl_);
+ wait_top_sites_loaded_observer.Run();
}
private:
+ base::ScopedTempDir scoped_temp_dir_;
base::MessageLoopForUI message_loop_;
- content::TestBrowserThread ui_thread_;
- content::TestBrowserThread db_thread_;
- scoped_ptr<TestingProfile> profile_;
+
+ scoped_ptr<TestingPrefServiceSimple> pref_service_;
+ scoped_ptr<HistoryService> history_service_;
+ scoped_refptr<TopSitesImpl> top_sites_impl_;
+
// To cancel HistoryService tasks.
base::CancelableTaskTracker history_tracker_;
@@ -383,9 +373,9 @@ static void AppendForcedMostVisitedURL(std::vector<MostVisitedURL>* list,
// Same as AppendMostVisitedURL except that it adds a redirect from the first
// URL to the second.
-static void AppendMostVisitedURLWithRedirect(
- std::vector<MostVisitedURL>* list,
- const GURL& redirect_source, const GURL& redirect_dest) {
+static void AppendMostVisitedURLWithRedirect(std::vector<MostVisitedURL>* list,
+ const GURL& redirect_source,
+ const GURL& redirect_dest) {
MostVisitedURL mv;
mv.url = redirect_dest;
mv.redirects.push_back(redirect_source);
@@ -528,7 +518,7 @@ TEST_F(TopSitesImplTest, SetPageThumbnail) {
GURL url1a("http://google.com/");
GURL url1b("http://www.google.com/");
GURL url2("http://images.google.com/");
- GURL invalid_url("chrome://favicon/http://google.com/");
+ GURL invalid_url("application://favicon/http://google.com/");
std::vector<MostVisitedURL> list;
AppendMostVisitedURL(&list, url2);
@@ -551,8 +541,8 @@ TEST_F(TopSitesImplTest, SetPageThumbnail) {
ThumbnailScore high_score(0.0, true, true, now);
// Setting the thumbnail for invalid pages should fail.
- EXPECT_FALSE(top_sites()->SetPageThumbnail(invalid_url,
- thumbnail, medium_score));
+ EXPECT_FALSE(
+ top_sites()->SetPageThumbnail(invalid_url, thumbnail, medium_score));
// Setting the thumbnail for url2 should succeed, lower scores shouldn't
// replace it, higher scores should.
@@ -753,7 +743,9 @@ TEST_F(TopSitesImplTest, SaveForcedToDB) {
// Get the original thumbnail for later comparison. Some compression can
// happen in |top_sites| and we don't want to depend on that.
- SkBitmap orig_thumbnail = GetThumbnail(GURL("http://forced1"));
+ scoped_refptr<base::RefCountedMemory> orig_thumbnail_data;
+ ASSERT_TRUE(top_sites()->GetPageThumbnail(GURL("http://forced1"), false,
+ &orig_thumbnail_data));
// Force-flush the cache to ensure we don't reread from it inadvertently.
EmptyThreadSafeCache();
@@ -768,14 +760,11 @@ TEST_F(TopSitesImplTest, SaveForcedToDB) {
ASSERT_EQ(4u + GetPrepopulatedPages().size(), querier.urls().size());
EXPECT_EQ(GURL("http://forced1"), querier.urls()[0].url);
EXPECT_EQ(base::ASCIIToUTF16("forced1"), querier.urls()[0].title);
- SkBitmap thumbnail = GetThumbnail(GURL("http://forced1"));
- ASSERT_EQ(orig_thumbnail.getSize(), thumbnail.getSize());
- orig_thumbnail.lockPixels();
- thumbnail.lockPixels();
- EXPECT_EQ(0, memcmp(orig_thumbnail.getPixels(), thumbnail.getPixels(),
- orig_thumbnail.getSize()));
- thumbnail.unlockPixels();
- orig_thumbnail.unlockPixels();
+ scoped_refptr<base::RefCountedMemory> thumbnail_data;
+ ASSERT_TRUE(top_sites()->GetPageThumbnail(GURL("http://forced1"), false,
+ &thumbnail_data));
+ ASSERT_TRUE(
+ ThumbnailsAreEqual(orig_thumbnail_data.get(), thumbnail_data.get()));
EXPECT_EQ(base::Time::FromJsTime(1000), querier.urls()[0].last_forced_time);
EXPECT_EQ(GURL("http://forced2"), querier.urls()[1].url);
EXPECT_EQ(base::Time::FromJsTime(2000), querier.urls()[1].last_forced_time);
@@ -1019,7 +1008,7 @@ TEST_F(TopSitesImplTest, NotifyCallbacksWhenLoaded) {
EXPECT_EQ(0, querier3.number_of_callbacks());
// Wait for loading to complete.
- profile()->BlockUntilTopSitesLoaded();
+ WaitTopSitesLoaded();
// Now we should have gotten the callbacks.
EXPECT_EQ(1, querier1.number_of_callbacks());
@@ -1054,7 +1043,7 @@ TEST_F(TopSitesImplTest, NotifyCallbacksWhenLoaded) {
EXPECT_EQ(0, querier4.number_of_callbacks());
// Wait for loading to complete.
- profile()->BlockUntilTopSitesLoaded();
+ WaitTopSitesLoaded();
// Now we should have gotten the callbacks.
EXPECT_EQ(1, querier4.number_of_callbacks());
@@ -1104,7 +1093,7 @@ TEST_F(TopSitesImplTest, CancelingRequestsForTopSites) {
querier2.CancelRequest();
// Wait for loading to complete.
- profile()->BlockUntilTopSitesLoaded();
+ WaitTopSitesLoaded();
// The first callback should succeed.
EXPECT_EQ(1, querier1.number_of_callbacks());
@@ -1117,7 +1106,7 @@ TEST_F(TopSitesImplTest, CancelingRequestsForTopSites) {
// Makes sure temporary thumbnails are copied over correctly.
TEST_F(TopSitesImplTest, AddTemporaryThumbnail) {
GURL unknown_url("http://news.google.com/");
- GURL invalid_url("chrome://thumb/http://google.com/");
+ GURL invalid_url("application://thumb/http://google.com/");
GURL url1a("http://google.com/");
GURL url1b("http://www.google.com/");
@@ -1457,7 +1446,6 @@ TEST_F(TopSitesImplTest, SetForcedTopSites) {
}
TEST_F(TopSitesImplTest, SetForcedTopSitesWithCollisions) {
-
// Setup an old URL list in order to generate some collisions.
MostVisitedURLList old_url_list;
AppendForcedMostVisitedURL(&old_url_list, GURL("http://url/0"), 1000);
@@ -1638,7 +1626,9 @@ TEST_F(TopSitesImplTest, AddForcedURL) {
// Get the original thumbnail for later comparison. Some compression can
// happen in |top_sites| and we don't want to depend on that.
- SkBitmap orig_thumbnail = GetThumbnail(GURL("http://forced/5"));
+ scoped_refptr<base::RefCountedMemory> orig_thumbnail_data;
+ ASSERT_TRUE(top_sites()->GetPageThumbnail(GURL("http://forced/5"), false,
+ &orig_thumbnail_data));
EXPECT_TRUE(AddForcedURL(GURL("http://forced/5"),
base::Time::FromJsTime(6000)));
@@ -1647,14 +1637,11 @@ TEST_F(TopSitesImplTest, AddForcedURL) {
querier.QueryAllTopSites(top_sites(), false, true);
EXPECT_EQ("http://forced/5", querier.urls()[5].url.spec());
EXPECT_EQ(6000u, querier.urls()[5].last_forced_time.ToJsTime());
- SkBitmap thumbnail = GetThumbnail(GURL("http://forced/5"));
- ASSERT_EQ(orig_thumbnail.getSize(), thumbnail.getSize());
- orig_thumbnail.lockPixels();
- thumbnail.lockPixels();
- EXPECT_EQ(0, memcmp(orig_thumbnail.getPixels(), thumbnail.getPixels(),
- orig_thumbnail.getSize()));
- thumbnail.unlockPixels();
- orig_thumbnail.unlockPixels();
+ scoped_refptr<base::RefCountedMemory> thumbnail_data;
+ ASSERT_TRUE(top_sites()->GetPageThumbnail(GURL("http://forced/5"), false,
+ &thumbnail_data));
+ ASSERT_TRUE(
+ ThumbnailsAreEqual(orig_thumbnail_data.get(), thumbnail_data.get()));
}
} // namespace history
diff --git a/chrome/browser/history/typed_url_syncable_service_unittest.cc b/components/history/core/browser/typed_url_syncable_service_unittest.cc
index 2b5cc77..55ea26b 100644
--- a/chrome/browser/history/typed_url_syncable_service_unittest.cc
+++ b/components/history/core/browser/typed_url_syncable_service_unittest.cc
@@ -10,7 +10,6 @@
#include "base/strings/utf_string_conversions.h"
#include "components/history/core/browser/history_backend.h"
#include "components/history/core/browser/history_types.h"
-#include "content/public/browser/notification_types.h"
#include "sync/api/fake_sync_change_processor.h"
#include "sync/api/sync_change_processor_wrapper_for_test.h"
#include "sync/api/sync_error.h"
@@ -68,9 +67,7 @@ class TestHistoryBackend : public HistoryBackend {
visits->end());
}
- void DeleteVisitsForUrl(const URLID& id) {
- local_db_visits_.erase(id);
- }
+ void DeleteVisitsForUrl(const URLID& id) { local_db_visits_.erase(id); }
private:
~TestHistoryBackend() override {}
@@ -110,8 +107,7 @@ class TypedUrlSyncableServiceTest : public testing::Test {
ui::PageTransition transition,
int64 visit_time);
- static bool URLsEqual(URLRow& row,
- sync_pb::TypedUrlSpecifics& specifics) {
+ static bool URLsEqual(URLRow& row, sync_pb::TypedUrlSpecifics& specifics) {
return ((row.url().spec().compare(specifics.url()) == 0) &&
(base::UTF16ToUTF8(row.title()).compare(specifics.title()) == 0) &&
(row.hidden() == specifics.hidden()));
@@ -140,13 +136,12 @@ class TypedUrlSyncableServiceTest : public testing::Test {
scoped_ptr<syncer::FakeSyncChangeProcessor> fake_change_processor_;
};
-URLRow TypedUrlSyncableServiceTest::MakeTypedUrlRow(
- const char* url,
- const char* title,
- int typed_count,
- int64 last_visit,
- bool hidden,
- VisitVector* visits) {
+URLRow TypedUrlSyncableServiceTest::MakeTypedUrlRow(const char* url,
+ const char* title,
+ int typed_count,
+ int64 last_visit,
+ bool hidden,
+ VisitVector* visits) {
DCHECK(visits->empty());
// Give each URL a unique ID, to mimic the behavior of the real database.
@@ -177,11 +172,10 @@ URLRow TypedUrlSyncableServiceTest::MakeTypedUrlRow(
return history_url;
}
-void TypedUrlSyncableServiceTest::AddNewestVisit(
- URLRow* url,
- VisitVector* visits,
- ui::PageTransition transition,
- int64 visit_time) {
+void TypedUrlSyncableServiceTest::AddNewestVisit(URLRow* url,
+ VisitVector* visits,
+ ui::PageTransition transition,
+ int64 visit_time) {
base::Time time = base::Time::FromInternalValue(visit_time);
visits->insert(visits->begin(),
VisitRow(url->id(), time, 0, transition, 0));
@@ -194,11 +188,10 @@ void TypedUrlSyncableServiceTest::AddNewestVisit(
url->set_visit_count(visits->size());
}
-void TypedUrlSyncableServiceTest::AddOldestVisit(
- URLRow* url,
- VisitVector* visits,
- ui::PageTransition transition,
- int64 visit_time) {
+void TypedUrlSyncableServiceTest::AddOldestVisit(URLRow* url,
+ VisitVector* visits,
+ ui::PageTransition transition,
+ int64 visit_time) {
base::Time time = base::Time::FromInternalValue(visit_time);
visits->push_back(VisitRow(url->id(), time, 0, transition, 0));
diff --git a/components/history/core/test/BUILD.gn b/components/history/core/test/BUILD.gn
index 1577366..709a3a0 100644
--- a/components/history/core/test/BUILD.gn
+++ b/components/history/core/test/BUILD.gn
@@ -7,14 +7,20 @@ static_library("test") {
sources = [
"database_test_utils.cc",
"database_test_utils.h",
- "history_backend_base_test.cc",
- "history_backend_base_test.h",
+ "history_backend_db_base_test.cc",
+ "history_backend_db_base_test.h",
"history_client_fake_bookmarks.cc",
"history_client_fake_bookmarks.h",
"history_unittest_base.cc",
"history_unittest_base.h",
"test_history_database.cc",
"test_history_database.h",
+ "thumbnail-inl.h",
+ "thumbnail.cc",
+ "thumbnail.h",
+ "thumbnail_ios.mm",
+ "wait_top_sites_loaded_observer.cc",
+ "wait_top_sites_loaded_observer.h",
]
deps = [
@@ -23,6 +29,11 @@ static_library("test") {
"//sql",
"//sql:test_support",
"//testing/gtest",
+ "//ui/gfx",
"//url",
]
+
+ if (is_ios) {
+ sources -= [ "thumbnail.cc" ]
+ }
}
diff --git a/components/history/core/test/history_backend_base_test.cc b/components/history/core/test/history_backend_db_base_test.cc
index 39bfa7b..e94efbd 100644
--- a/components/history/core/test/history_backend_base_test.cc
+++ b/components/history/core/test/history_backend_db_base_test.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/history/core/test/history_backend_base_test.h"
+#include "components/history/core/test/history_backend_db_base_test.h"
#include "base/files/file_path.h"
#include "base/strings/stringprintf.h"
@@ -21,7 +21,7 @@ namespace history {
// Delegate class for when we create a backend without a HistoryService.
class BackendDelegate : public HistoryBackend::Delegate {
public:
- explicit BackendDelegate(HistoryBackendBaseTest* history_test)
+ explicit BackendDelegate(HistoryBackendDBBaseTest* history_test)
: history_test_(history_test) {}
// HistoryBackend::Delegate implementation.
@@ -48,22 +48,22 @@ class BackendDelegate : public HistoryBackend::Delegate {
void DBLoaded() override {}
private:
- HistoryBackendBaseTest* history_test_;
+ HistoryBackendDBBaseTest* history_test_;
};
-HistoryBackendBaseTest::HistoryBackendBaseTest() : db_(nullptr) {
+HistoryBackendDBBaseTest::HistoryBackendDBBaseTest() : db_(nullptr) {
}
-HistoryBackendBaseTest::~HistoryBackendBaseTest() {
+HistoryBackendDBBaseTest::~HistoryBackendDBBaseTest() {
}
-void HistoryBackendBaseTest::SetUp() {
+void HistoryBackendDBBaseTest::SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- history_dir_ = temp_dir_.path().AppendASCII("HistoryBackendBaseTest");
+ history_dir_ = temp_dir_.path().AppendASCII("HistoryBackendDBBaseTest");
ASSERT_TRUE(base::CreateDirectory(history_dir_));
}
-void HistoryBackendBaseTest::TearDown() {
+void HistoryBackendDBBaseTest::TearDown() {
DeleteBackend();
// Make sure we don't have any event pending that could disrupt the next
@@ -73,7 +73,7 @@ void HistoryBackendBaseTest::TearDown() {
base::MessageLoop::current()->Run();
}
-void HistoryBackendBaseTest::CreateBackendAndDatabase() {
+void HistoryBackendDBBaseTest::CreateBackendAndDatabase() {
backend_ = new HistoryBackend(new BackendDelegate(this), nullptr);
backend_->Init(std::string(), false,
TestHistoryDatabaseParamsForPath(history_dir_));
@@ -82,7 +82,7 @@ void HistoryBackendBaseTest::CreateBackendAndDatabase() {
"HistoryBackend::Init";
}
-void HistoryBackendBaseTest::CreateDBVersion(int version) {
+void HistoryBackendDBBaseTest::CreateDBVersion(int version) {
base::FilePath data_path;
ASSERT_TRUE(GetTestDataHistoryDir(&data_path));
data_path =
@@ -91,7 +91,7 @@ void HistoryBackendBaseTest::CreateDBVersion(int version) {
ExecuteSQLScript(data_path, history_dir_.Append(kHistoryFilename)));
}
-void HistoryBackendBaseTest::CreateArchivedDB() {
+void HistoryBackendDBBaseTest::CreateArchivedDB() {
base::FilePath data_path;
ASSERT_TRUE(GetTestDataHistoryDir(&data_path));
data_path = data_path.AppendASCII("archived_history.4.sql");
@@ -99,14 +99,14 @@ void HistoryBackendBaseTest::CreateArchivedDB() {
data_path, history_dir_.Append(kArchivedHistoryFilename)));
}
-void HistoryBackendBaseTest::DeleteBackend() {
+void HistoryBackendDBBaseTest::DeleteBackend() {
if (backend_.get()) {
backend_->Closing();
backend_ = nullptr;
}
}
-bool HistoryBackendBaseTest::AddDownload(uint32 id,
+bool HistoryBackendDBBaseTest::AddDownload(uint32 id,
DownloadState state,
base::Time time) {
std::vector<GURL> url_chain;
diff --git a/components/history/core/test/history_backend_base_test.h b/components/history/core/test/history_backend_db_base_test.h
index cd4cb7d..e07ed32 100644
--- a/components/history/core/test/history_backend_base_test.h
+++ b/components/history/core/test/history_backend_db_base_test.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_HISTORY_CORE_TEST_HISTORY_BACKEND_BASE_TEST_H_
-#define COMPONENTS_HISTORY_CORE_TEST_HISTORY_BACKEND_BASE_TEST_H_
+#ifndef COMPONENTS_HISTORY_CORE_TEST_HISTORY_BACKEND_DB_BASE_TEST_H_
+#define COMPONENTS_HISTORY_CORE_TEST_HISTORY_BACKEND_DB_BASE_TEST_H_
#include "base/files/file_path.h"
#include "base/files/file_util.h"
@@ -27,10 +27,10 @@ enum class DownloadState;
// This must be outside the anonymous namespace for the friend statement in
// HistoryBackend to work.
-class HistoryBackendBaseTest : public HistoryUnitTestBase {
+class HistoryBackendDBBaseTest : public HistoryUnitTestBase {
public:
- HistoryBackendBaseTest();
- ~HistoryBackendBaseTest() override;
+ HistoryBackendDBBaseTest();
+ ~HistoryBackendDBBaseTest() override;
protected:
friend class BackendDelegate;
@@ -66,4 +66,4 @@ class HistoryBackendBaseTest : public HistoryUnitTestBase {
} // namespace history
-#endif // COMPONENTS_HISTORY_CORE_TEST_HISTORY_BACKEND_BASE_TEST_H_
+#endif // COMPONENTS_HISTORY_CORE_TEST_HISTORY_BACKEND_DB_BASE_TEST_H_
diff --git a/components/history/core/test/thumbnail.cc b/components/history/core/test/thumbnail.cc
new file mode 100644
index 0000000..6586dca
--- /dev/null
+++ b/components/history/core/test/thumbnail.cc
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/history/core/test/thumbnail.h"
+
+#include "components/history/core/test/thumbnail-inl.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/image/image.h"
+
+namespace history {
+
+gfx::Image CreateGoogleThumbnailForTest() {
+ // Returned image takes ownership of decoded SkBitmap.
+ scoped_ptr<SkBitmap> thumbnail_bitmap(
+ gfx::JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail)));
+ return gfx::Image::CreateFrom1xBitmap(*thumbnail_bitmap);
+}
+
+} // namespace
diff --git a/components/history/core/test/thumbnail.h b/components/history/core/test/thumbnail.h
new file mode 100644
index 0000000..77be3d5
--- /dev/null
+++ b/components/history/core/test/thumbnail.h
@@ -0,0 +1,19 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_HISTORY_CORE_TEST_THUMBNAIL_H_
+#define COMPONENTS_HISTORY_CORE_TEST_THUMBNAIL_H_
+
+namespace gfx {
+class Image;
+}
+
+namespace history {
+
+// Returns a gfx::Image corresponding to kGoogleThumbnail data for test.
+gfx::Image CreateGoogleThumbnailForTest();
+
+} // namespace history
+
+#endif // COMPONENTS_HISTORY_CORE_TEST_THUMBNAIL_H_
diff --git a/components/history/core/test/thumbnail_ios.mm b/components/history/core/test/thumbnail_ios.mm
new file mode 100644
index 0000000..d3b0cee
--- /dev/null
+++ b/components/history/core/test/thumbnail_ios.mm
@@ -0,0 +1,30 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/history/core/test/thumbnail.h"
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+#import "base/mac/scoped_nsautorelease_pool.h"
+#include "components/history/core/test/thumbnail-inl.h"
+#include "ui/gfx/image/image.h"
+
+namespace history {
+
+gfx::Image CreateGoogleThumbnailForTest() {
+ base::mac::ScopedNSAutoreleasePool pool;
+ // -[NSData dataWithBytesNoCopy:length:freeWhenDone:] takes it first parameter
+ // as a void* but does not modify it (API is not const clean) so we need to
+ // use const_cast<> here.
+ NSData* data =
+ [NSData dataWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(
+ kGoogleThumbnail))
+ length:sizeof(kGoogleThumbnail)
+ freeWhenDone:NO];
+ UIImage* image = [UIImage imageWithData:data scale:1];
+ return gfx::Image([image retain]);
+}
+
+} // namespace
diff --git a/components/history/core/test/wait_top_sites_loaded_observer.cc b/components/history/core/test/wait_top_sites_loaded_observer.cc
new file mode 100644
index 0000000..79b1a38
--- /dev/null
+++ b/components/history/core/test/wait_top_sites_loaded_observer.cc
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/history/core/test/wait_top_sites_loaded_observer.h"
+
+#include "components/history/core/browser/top_sites.h"
+
+namespace history {
+
+WaitTopSitesLoadedObserver::WaitTopSitesLoadedObserver(
+ scoped_refptr<TopSites> top_sites)
+ : top_sites_(top_sites) {
+ if (top_sites_)
+ top_sites_->AddObserver(this);
+}
+
+WaitTopSitesLoadedObserver::~WaitTopSitesLoadedObserver() {
+ if (top_sites_)
+ top_sites_->RemoveObserver(this);
+}
+
+void WaitTopSitesLoadedObserver::Run() {
+ if (top_sites_ && !top_sites_->loaded())
+ run_loop_.Run();
+}
+
+void WaitTopSitesLoadedObserver::TopSitesLoaded(TopSites* top_sites) {
+ run_loop_.Quit();
+}
+
+void WaitTopSitesLoadedObserver::TopSitesChanged(TopSites* top_sites) {
+}
+
+} // namespace history
diff --git a/components/history/core/test/wait_top_sites_loaded_observer.h b/components/history/core/test/wait_top_sites_loaded_observer.h
new file mode 100644
index 0000000..d21cf7c
--- /dev/null
+++ b/components/history/core/test/wait_top_sites_loaded_observer.h
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_HISTORY_CORE_TEST_WAIT_TOP_SITES_LOADED_OBSERVER_H_
+#define COMPONENTS_HISTORY_CORE_TEST_WAIT_TOP_SITES_LOADED_OBSERVER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "components/history/core/browser/top_sites_observer.h"
+
+namespace history {
+
+class TopSites;
+
+// Used to make sure TopSites has finished loading
+class WaitTopSitesLoadedObserver : public TopSitesObserver {
+ public:
+ explicit WaitTopSitesLoadedObserver(scoped_refptr<TopSites> top_sites);
+ ~WaitTopSitesLoadedObserver() override;
+
+ // Wait until TopSites has finished loading. Returns immediately if it has
+ // already been loaded.
+ void Run();
+
+ private:
+ // TopSitesObserver implementation.
+ void TopSitesLoaded(TopSites* top_sites) override;
+ void TopSitesChanged(TopSites* top_sites) override;
+
+ scoped_refptr<TopSites> top_sites_;
+ base::RunLoop run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaitTopSitesLoadedObserver);
+};
+
+} // namespace history
+
+#endif // COMPONENTS_HISTORY_CORE_TEST_WAIT_TOP_SITES_LOADED_OBSERVER_H_
diff --git a/chrome/test/data/History/Favicons.v3.sql b/components/test/data/history/Favicons.v3.sql
index 1cdfc6f..1cdfc6f 100644
--- a/chrome/test/data/History/Favicons.v3.sql
+++ b/components/test/data/history/Favicons.v3.sql
diff --git a/chrome/test/data/History/Favicons.v4.sql b/components/test/data/history/Favicons.v4.sql
index d2549bf..d2549bf 100644
--- a/chrome/test/data/History/Favicons.v4.sql
+++ b/components/test/data/history/Favicons.v4.sql
diff --git a/chrome/test/data/History/Favicons.v5.sql b/components/test/data/history/Favicons.v5.sql
index 655d282..655d282 100644
--- a/chrome/test/data/History/Favicons.v5.sql
+++ b/components/test/data/history/Favicons.v5.sql
diff --git a/chrome/test/data/History/Favicons.v6.sql b/components/test/data/history/Favicons.v6.sql
index c27125b..c27125b 100644
--- a/chrome/test/data/History/Favicons.v6.sql
+++ b/components/test/data/history/Favicons.v6.sql
diff --git a/chrome/test/data/History/Favicons.v7.sql b/components/test/data/history/Favicons.v7.sql
index 8b0fd2c..8b0fd2c 100644
--- a/chrome/test/data/History/Favicons.v7.sql
+++ b/components/test/data/history/Favicons.v7.sql
diff --git a/chrome/test/data/History/Favicons.v8.sql b/components/test/data/history/Favicons.v8.sql
index 0905172..0905172 100644
--- a/chrome/test/data/History/Favicons.v8.sql
+++ b/components/test/data/history/Favicons.v8.sql
diff --git a/chrome/test/data/History/HistoryNoDuration b/components/test/data/history/HistoryNoDuration
index 3d00e5a..3d00e5a 100644
--- a/chrome/test/data/History/HistoryNoDuration
+++ b/components/test/data/history/HistoryNoDuration
Binary files differ
diff --git a/chrome/test/data/History/HistoryNoSource b/components/test/data/history/HistoryNoSource
index 19b4b8e..19b4b8e 100644
--- a/chrome/test/data/History/HistoryNoSource
+++ b/components/test/data/history/HistoryNoSource
Binary files differ
diff --git a/chrome/test/data/bookmarks/History_with_starred b/components/test/data/history/History_with_starred
index 4b913b2..4b913b2 100644
--- a/chrome/test/data/bookmarks/History_with_starred
+++ b/components/test/data/history/History_with_starred
Binary files differ
diff --git a/chrome/test/data/History/thumbnail_wild/Favicons.corrupt_meta.disable b/components/test/data/history/thumbnail_wild/Favicons.corrupt_meta.disable
index 05e46ef..05e46ef 100644
--- a/chrome/test/data/History/thumbnail_wild/Favicons.corrupt_meta.disable
+++ b/components/test/data/history/thumbnail_wild/Favicons.corrupt_meta.disable
diff --git a/chrome/test/data/History/thumbnail_wild/Favicons.v2.init.sql b/components/test/data/history/thumbnail_wild/Favicons.v2.init.sql
index 53b6b73..53b6b73 100644
--- a/chrome/test/data/History/thumbnail_wild/Favicons.v2.init.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v2.init.sql
diff --git a/chrome/test/data/History/thumbnail_wild/Favicons.v3.init.sql b/components/test/data/history/thumbnail_wild/Favicons.v3.init.sql
index 3453860..3453860 100644
--- a/chrome/test/data/History/thumbnail_wild/Favicons.v3.init.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v3.init.sql
diff --git a/chrome/test/data/History/thumbnail_wild/Favicons.v4.init.sql b/components/test/data/history/thumbnail_wild/Favicons.v4.init.sql
index 6c5738e..6c5738e 100644
--- a/chrome/test/data/History/thumbnail_wild/Favicons.v4.init.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v4.init.sql
diff --git a/chrome/test/data/History/thumbnail_wild/Favicons.v5.icon_type.sql b/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type.sql
index a00a439..a00a439 100644
--- a/chrome/test/data/History/thumbnail_wild/Favicons.v5.icon_type.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type.sql
diff --git a/chrome/test/data/History/thumbnail_wild/Favicons.v5.icon_type2.sql b/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type2.sql
index 25ceded..25ceded 100644
--- a/chrome/test/data/History/thumbnail_wild/Favicons.v5.icon_type2.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type2.sql
diff --git a/chrome/test/data/History/thumbnail_wild/Favicons.v5.init.sql b/components/test/data/history/thumbnail_wild/Favicons.v5.init.sql
index c92a110..c92a110 100644
--- a/chrome/test/data/History/thumbnail_wild/Favicons.v5.init.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v5.init.sql
diff --git a/chrome/test/data/History/thumbnail_wild/Favicons.v5.sizes.sql b/components/test/data/history/thumbnail_wild/Favicons.v5.sizes.sql
index 6dfa9e9..6dfa9e9 100644
--- a/chrome/test/data/History/thumbnail_wild/Favicons.v5.sizes.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v5.sizes.sql
diff --git a/chrome/test/data/History/thumbnail_wild/Favicons.v6.init.sql b/components/test/data/history/thumbnail_wild/Favicons.v6.init.sql
index 3014c37..3014c37 100644
--- a/chrome/test/data/History/thumbnail_wild/Favicons.v6.init.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v6.init.sql
diff --git a/chrome/test/data/History/thumbnail_wild/README b/components/test/data/history/thumbnail_wild/README
index efe0e53..efe0e53 100644
--- a/chrome/test/data/History/thumbnail_wild/README
+++ b/components/test/data/history/thumbnail_wild/README