// 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 "base/bind.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.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/browser/history/top_sites_impl.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_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_observer.h" #include "components/history/core/test/history_unittest_base.h" #include "content/public/test/test_browser_thread.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[] = "http://www.google.com/int/chrome/welcome.html"; // Create a TopSites implementation for testing. scoped_refptr BuildTopSitesImpl( content::BrowserContext* context) { PrepopulatedPageList prepopulated_pages; prepopulated_pages.push_back(PrepopulatedPage(GURL(kPrepopulatedPageURL), base::string16(), -1, -1, 0)); scoped_refptr top_sites = new TopSitesImpl(static_cast(context), prepopulated_pages); top_sites->Init( context->GetPath().Append(chrome::kTopSitesFilename), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)); return top_sites; } // Used by WaitForHistory, see it for details. class WaitForHistoryTask : public HistoryDBTask { public: WaitForHistoryTask() {} bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) override { return true; } void DoneRunOnMainThread() override { base::MessageLoop::current()->Quit(); } private: ~WaitForHistoryTask() override {} DISALLOW_COPY_AND_ASSIGN(WaitForHistoryTask); }; // Used for querying top sites. Either runs sequentially, or runs a nested // nested message loop until the response is complete. The later is used when // TopSites is queried before it finishes loading. class TopSitesQuerier { public: TopSitesQuerier() : 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. void QueryTopSites(TopSitesImpl* top_sites, bool wait) { QueryAllTopSites(top_sites, wait, false); } // Queries top sites, including potentially forced URLs if // |include_forced_urls| is true. void QueryAllTopSites(TopSitesImpl* top_sites, bool wait, bool include_forced_urls) { int start_number_of_callbacks = number_of_callbacks_; top_sites->GetMostVisitedURLs( base::Bind(&TopSitesQuerier::OnTopSitesAvailable, weak_ptr_factory_.GetWeakPtr()), include_forced_urls); if (wait && start_number_of_callbacks == number_of_callbacks_) { waiting_ = true; base::MessageLoop::current()->Run(); } } void CancelRequest() { weak_ptr_factory_.InvalidateWeakPtrs(); } void set_urls(const MostVisitedURLList& urls) { urls_ = urls; } const MostVisitedURLList& urls() const { return urls_; } int number_of_callbacks() const { return number_of_callbacks_; } private: // Callback for TopSitesImpl::GetMostVisitedURLs. void OnTopSitesAvailable(const history::MostVisitedURLList& data) { urls_ = data; number_of_callbacks_++; if (waiting_) { base::MessageLoop::current()->Quit(); waiting_ = false; } } MostVisitedURLList urls_; int number_of_callbacks_; bool waiting_; base::WeakPtrFactory weak_ptr_factory_; 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 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) { if (!t1 || !t2) return false; if (t1->size() != t2->size()) return false; return !memcmp(t1->front(), t2->front(), t1->size()); } } // namespace class TopSitesImplTest : public HistoryUnitTestBase { public: TopSitesImplTest() : ui_thread_(BrowserThread::UI, &message_loop_), db_thread_(BrowserThread::DB, &message_loop_) { } void SetUp() override { profile_.reset(new TestingProfile); if (CreateHistoryAndTopSites()) { ASSERT_TRUE(profile_->CreateHistoryService(false, false)); ResetTopSites(); profile_->BlockUntilTopSitesLoaded(); } } 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 data; return top_sites()->GetPageThumbnail(url, false, &data) ? ExtractThumbnail(*data.get()) : SkBitmap(); } // Creates a bitmap of the specified color. Caller takes ownership. gfx::Image CreateBitmap(SkColor color) { SkBitmap thumbnail; thumbnail.allocN32Pixels(4, 4); thumbnail.eraseColor(color); return gfx::Image::CreateFrom1xBitmap(thumbnail); // adds ref. } // Forces top sites to load top sites from history, then recreates top sites. // Recreating top sites makes sure the changes from history are saved and // loaded from the db. void RefreshTopSitesAndRecreate() { StartQueryForMostVisited(); WaitForHistory(); RecreateTopSitesAndBlock(); } // Blocks the caller until history processes a task. This is useful if you // need to wait until you know history has processed a task. void WaitForHistory() { history_service()->ScheduleDBTask( scoped_ptr(new WaitForHistoryTask()), &history_tracker_); base::MessageLoop::current()->Run(); } // Waits for top sites to finish processing a task. This is useful if you need // to wait until top sites finishes processing a task. void WaitForTopSites() { top_sites()->backend_->DoEmptyRequest( base::Bind(&TopSitesImplTest::QuitCallback, base::Unretained(this)), &top_sites_tracker_); base::MessageLoop::current()->Run(); } TopSitesImpl* top_sites() { return static_cast( TopSitesFactory::GetForProfile(profile_.get()).get()); } TestingProfile* profile() {return profile_.get();} HistoryService* history_service() { return HistoryServiceFactory::GetForProfile( profile_.get(), ServiceAccessType::EXPLICIT_ACCESS); } PrepopulatedPageList GetPrepopulatedPages() { return top_sites()->GetPrepopulatedPages(); } // Returns true if the TopSitesQuerier contains the prepopulate data starting // at |start_index|. void ContainsPrepopulatePages(const TopSitesQuerier& querier, size_t start_index) { PrepopulatedPageList prepopulate_pages = GetPrepopulatedPages(); ASSERT_LE(start_index + prepopulate_pages.size(), querier.urls().size()); for (size_t i = 0; i < prepopulate_pages.size(); ++i) { EXPECT_EQ(prepopulate_pages[i].most_visited.url.spec(), querier.urls()[start_index + i].url.spec()) << " @ index " << i; } } // Quit the current message loop when invoked. Useful when running a nested // message loop. void QuitCallback() { base::MessageLoop::current()->Quit(); } // Adds a page to history. void AddPageToHistory(const GURL& url) { RedirectList redirects; redirects.push_back(url); history_service()->AddPage( url, base::Time::Now(), reinterpret_cast(1), 0, GURL(), redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false); } // Adds a page to history. void AddPageToHistory(const GURL& url, const base::string16& title) { RedirectList redirects; redirects.push_back(url); history_service()->AddPage( url, base::Time::Now(), reinterpret_cast(1), 0, GURL(), redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false); history_service()->SetPageTitle(url, title); } // Adds a page to history. void AddPageToHistory(const GURL& url, const base::string16& title, const history::RedirectList& redirects, base::Time time) { history_service()->AddPage( url, time, reinterpret_cast(1), 0, GURL(), redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false); history_service()->SetPageTitle(url, title); } // Delets a 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, base::RefCountedMemory* bytes) { scoped_refptr encoded_image; TopSitesImpl::EncodeBitmap(image, &encoded_image); return ThumbnailsAreEqual(encoded_image.get(), bytes); } // Recreates top sites. This forces top sites to reread from the db. void RecreateTopSitesAndBlock() { // Recreate TopSites and wait for it to load. ResetTopSites(); // As history already loaded we have to fake this call. profile()->BlockUntilTopSitesLoaded(); } // Wrappers that allow private TopSites functions to be called from the // individual tests without making them all be friends. GURL GetCanonicalURL(const GURL& url) { return top_sites()->cache_->GetCanonicalURL(url); } void SetTopSites(const MostVisitedURLList& new_top_sites) { top_sites()->SetTopSites(new_top_sites); } bool AddForcedURL(const GURL& url, base::Time time) { return top_sites()->AddForcedURL(url, time); } void StartQueryForMostVisited() { top_sites()->StartQueryForMostVisited(); } void SetLastNumUrlsChanged(size_t value) { top_sites()->last_num_urls_changed_ = value; } size_t last_num_urls_changed() { return top_sites()->last_num_urls_changed_; } base::TimeDelta GetUpdateDelay() { return top_sites()->GetUpdateDelay(); } bool IsTopSitesLoaded() { return top_sites()->loaded_; } bool AddPrepopulatedPages(MostVisitedURLList* urls) { return top_sites()->AddPrepopulatedPages(urls, 0u); } void EmptyThreadSafeCache() { base::AutoLock lock(top_sites()->lock_); MostVisitedURLList empty; top_sites()->thread_safe_cache_->SetTopSites(empty); } 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); } private: base::MessageLoopForUI message_loop_; content::TestBrowserThread ui_thread_; content::TestBrowserThread db_thread_; scoped_ptr profile_; // To cancel HistoryService tasks. base::CancelableTaskTracker history_tracker_; // To cancel TopSitesBackend tasks. base::CancelableTaskTracker top_sites_tracker_; DISALLOW_COPY_AND_ASSIGN(TopSitesImplTest); }; // Class TopSitesImplTest // Helper function for appending a URL to a vector of "most visited" URLs, // using the default values for everything but the URL. static void AppendMostVisitedURL(std::vector* list, const GURL& url) { MostVisitedURL mv; mv.url = url; mv.redirects.push_back(url); list->push_back(mv); } // Helper function for appending a URL to a vector of "most visited" URLs, // using the default values for everything but the URL. static void AppendForcedMostVisitedURL(std::vector* list, const GURL& url, double last_forced_time) { MostVisitedURL mv; mv.url = url; mv.last_forced_time = base::Time::FromJsTime(last_forced_time); mv.redirects.push_back(url); list->push_back(mv); } // Same as AppendMostVisitedURL except that it adds a redirect from the first // URL to the second. static void AppendMostVisitedURLWithRedirect( std::vector* list, const GURL& redirect_source, const GURL& redirect_dest) { MostVisitedURL mv; mv.url = redirect_dest; mv.redirects.push_back(redirect_source); mv.redirects.push_back(redirect_dest); list->push_back(mv); } // Tests GetCanonicalURL. TEST_F(TopSitesImplTest, GetCanonicalURL) { // Have two chains: // google.com -> www.google.com // news.google.com (no redirects) GURL news("http://news.google.com/"); GURL source("http://google.com/"); GURL dest("http://www.google.com/"); std::vector most_visited; AppendMostVisitedURLWithRedirect(&most_visited, source, dest); AppendMostVisitedURL(&most_visited, news); SetTopSites(most_visited); // Random URLs not in the database are returned unchanged. GURL result = GetCanonicalURL(GURL("http://fark.com/")); EXPECT_EQ(GURL("http://fark.com/"), result); // Easy case, there are no redirects and the exact URL is stored. result = GetCanonicalURL(news); EXPECT_EQ(news, result); // The URL in question is the source URL in a redirect list. result = GetCanonicalURL(source); EXPECT_EQ(dest, result); // The URL in question is the destination of a redirect. result = GetCanonicalURL(dest); EXPECT_EQ(dest, result); } // Tests DiffMostVisited. TEST_F(TopSitesImplTest, DiffMostVisited) { GURL stays_the_same("http://staysthesame/"); GURL gets_added_1("http://getsadded1/"); GURL gets_added_2("http://getsadded2/"); GURL gets_deleted_1("http://getsdeleted1/"); GURL gets_moved_1("http://getsmoved1/"); std::vector old_list; AppendMostVisitedURL(&old_list, stays_the_same); // 0 (unchanged) AppendMostVisitedURL(&old_list, gets_deleted_1); // 1 (deleted) AppendMostVisitedURL(&old_list, gets_moved_1); // 2 (moved to 3) std::vector new_list; AppendMostVisitedURL(&new_list, stays_the_same); // 0 (unchanged) AppendMostVisitedURL(&new_list, gets_added_1); // 1 (added) AppendMostVisitedURL(&new_list, gets_added_2); // 2 (added) AppendMostVisitedURL(&new_list, gets_moved_1); // 3 (moved from 2) history::TopSitesDelta delta; history::TopSitesImpl::DiffMostVisited(old_list, new_list, &delta); ASSERT_EQ(2u, delta.added.size()); EXPECT_TRUE(gets_added_1 == delta.added[0].url.url); EXPECT_EQ(1, delta.added[0].rank); EXPECT_TRUE(gets_added_2 == delta.added[1].url.url); EXPECT_EQ(2, delta.added[1].rank); ASSERT_EQ(1u, delta.deleted.size()); EXPECT_TRUE(gets_deleted_1 == delta.deleted[0].url); ASSERT_EQ(1u, delta.moved.size()); EXPECT_TRUE(gets_moved_1 == delta.moved[0].url.url); EXPECT_EQ(3, delta.moved[0].rank); } // Tests DiffMostVisited with forced URLs. TEST_F(TopSitesImplTest, DiffMostVisitedWithForced) { // Forced URLs. GURL stays_the_same_1("http://staysthesame1/"); GURL new_last_forced_time("http://newlastforcedtime/"); GURL stays_the_same_2("http://staysthesame2/"); GURL move_to_nonforced("http://movetononforced/"); GURL gets_added_1("http://getsadded1/"); GURL gets_deleted_1("http://getsdeleted1/"); // Non-forced URLs. GURL move_to_forced("http://movetoforced/"); GURL stays_the_same_3("http://staysthesame3/"); GURL gets_added_2("http://getsadded2/"); GURL gets_deleted_2("http://getsdeleted2/"); GURL gets_moved_1("http://getsmoved1/"); std::vector old_list; AppendForcedMostVisitedURL(&old_list, stays_the_same_1, 1000); AppendForcedMostVisitedURL(&old_list, new_last_forced_time, 2000); AppendForcedMostVisitedURL(&old_list, stays_the_same_2, 3000); AppendForcedMostVisitedURL(&old_list, move_to_nonforced, 4000); AppendForcedMostVisitedURL(&old_list, gets_deleted_1, 5000); AppendMostVisitedURL(&old_list, move_to_forced); AppendMostVisitedURL(&old_list, stays_the_same_3); AppendMostVisitedURL(&old_list, gets_deleted_2); AppendMostVisitedURL(&old_list, gets_moved_1); std::vector new_list; AppendForcedMostVisitedURL(&new_list, stays_the_same_1, 1000); AppendForcedMostVisitedURL(&new_list, stays_the_same_2, 3000); AppendForcedMostVisitedURL(&new_list, new_last_forced_time, 4000); AppendForcedMostVisitedURL(&new_list, gets_added_1, 5000); AppendForcedMostVisitedURL(&new_list, move_to_forced, 6000); AppendMostVisitedURL(&new_list, move_to_nonforced); AppendMostVisitedURL(&new_list, stays_the_same_3); AppendMostVisitedURL(&new_list, gets_added_2); AppendMostVisitedURL(&new_list, gets_moved_1); history::TopSitesDelta delta; history::TopSitesImpl::DiffMostVisited(old_list, new_list, &delta); ASSERT_EQ(2u, delta.added.size()); EXPECT_TRUE(gets_added_1 == delta.added[0].url.url); EXPECT_EQ(-1, delta.added[0].rank); EXPECT_TRUE(gets_added_2 == delta.added[1].url.url); EXPECT_EQ(2, delta.added[1].rank); ASSERT_EQ(2u, delta.deleted.size()); EXPECT_TRUE(gets_deleted_1 == delta.deleted[0].url); EXPECT_TRUE(gets_deleted_2 == delta.deleted[1].url); ASSERT_EQ(3u, delta.moved.size()); EXPECT_TRUE(new_last_forced_time == delta.moved[0].url.url); EXPECT_EQ(-1, delta.moved[0].rank); EXPECT_EQ(base::Time::FromJsTime(4000), delta.moved[0].url.last_forced_time); EXPECT_TRUE(move_to_forced == delta.moved[1].url.url); EXPECT_EQ(-1, delta.moved[1].rank); EXPECT_EQ(base::Time::FromJsTime(6000), delta.moved[1].url.last_forced_time); EXPECT_TRUE(move_to_nonforced == delta.moved[2].url.url); EXPECT_EQ(0, delta.moved[2].rank); EXPECT_TRUE(delta.moved[2].url.last_forced_time.is_null()); } // Tests SetPageThumbnail. 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/"); std::vector list; AppendMostVisitedURL(&list, url2); MostVisitedURL mv; mv.url = url1b; mv.redirects.push_back(url1a); mv.redirects.push_back(url1b); list.push_back(mv); // Save our most visited data containing that one site. SetTopSites(list); // Create a dummy thumbnail. gfx::Image thumbnail(CreateBitmap(SK_ColorWHITE)); base::Time now = base::Time::Now(); ThumbnailScore low_score(1.0, true, true, now); ThumbnailScore medium_score(0.5, true, true, now); 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)); // Setting the thumbnail for url2 should succeed, lower scores shouldn't // replace it, higher scores should. EXPECT_TRUE(top_sites()->SetPageThumbnail(url2, thumbnail, medium_score)); EXPECT_FALSE(top_sites()->SetPageThumbnail(url2, thumbnail, low_score)); EXPECT_TRUE(top_sites()->SetPageThumbnail(url2, thumbnail, high_score)); // Set on the redirect source should succeed. It should be replacable by // the same score on the redirect destination, which in turn should not // be replaced by the source again. EXPECT_TRUE(top_sites()->SetPageThumbnail(url1a, thumbnail, medium_score)); EXPECT_TRUE(top_sites()->SetPageThumbnail(url1b, thumbnail, medium_score)); EXPECT_FALSE(top_sites()->SetPageThumbnail(url1a, thumbnail, medium_score)); } // Makes sure a thumbnail is correctly removed when the page is removed. TEST_F(TopSitesImplTest, ThumbnailRemoved) { GURL url("http://google.com/"); // Configure top sites with 'google.com'. std::vector list; AppendMostVisitedURL(&list, url); SetTopSites(list); // Create a dummy thumbnail. gfx::Image thumbnail(CreateBitmap(SK_ColorRED)); base::Time now = base::Time::Now(); ThumbnailScore low_score(1.0, true, true, now); ThumbnailScore medium_score(0.5, true, true, now); ThumbnailScore high_score(0.0, true, true, now); // Set the thumbnail. EXPECT_TRUE(top_sites()->SetPageThumbnail(url, thumbnail, medium_score)); // Make sure the thumbnail was actually set. scoped_refptr result; EXPECT_TRUE(top_sites()->GetPageThumbnail(url, false, &result)); EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, result.get())); // Reset the thumbnails and make sure we don't get it back. SetTopSites(MostVisitedURLList()); RefreshTopSitesAndRecreate(); EXPECT_FALSE(top_sites()->GetPageThumbnail(url, false, &result)); } // Tests GetPageThumbnail. TEST_F(TopSitesImplTest, GetPageThumbnail) { MostVisitedURLList url_list; MostVisitedURL url1; url1.url = GURL("http://asdf.com"); url1.redirects.push_back(url1.url); url_list.push_back(url1); MostVisitedURL url2; url2.url = GURL("http://gmail.com"); url2.redirects.push_back(url2.url); url2.redirects.push_back(GURL("http://mail.google.com")); url_list.push_back(url2); SetTopSites(url_list); // Create a dummy thumbnail. gfx::Image thumbnail(CreateBitmap(SK_ColorWHITE)); ThumbnailScore score(0.5, true, true, base::Time::Now()); scoped_refptr result; EXPECT_TRUE(top_sites()->SetPageThumbnail(url1.url, thumbnail, score)); EXPECT_TRUE(top_sites()->GetPageThumbnail(url1.url, false, &result)); EXPECT_TRUE(top_sites()->SetPageThumbnail(GURL("http://gmail.com"), thumbnail, score)); EXPECT_TRUE(top_sites()->GetPageThumbnail(GURL("http://gmail.com"), false, &result)); // Get a thumbnail via a redirect. EXPECT_TRUE(top_sites()->GetPageThumbnail(GURL("http://mail.google.com"), false, &result)); EXPECT_TRUE(top_sites()->SetPageThumbnail(GURL("http://mail.google.com"), thumbnail, score)); EXPECT_TRUE(top_sites()->GetPageThumbnail(url2.url, false, &result)); EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, result.get())); } // Tests GetMostVisitedURLs. TEST_F(TopSitesImplTest, GetMostVisited) { GURL news("http://news.google.com/"); GURL google("http://google.com/"); AddPageToHistory(news); AddPageToHistory(google); StartQueryForMostVisited(); WaitForHistory(); TopSitesQuerier querier; querier.QueryTopSites(top_sites(), false); ASSERT_EQ(1, querier.number_of_callbacks()); // 2 extra prepopulated URLs. ASSERT_EQ(2u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ(news, querier.urls()[0].url); EXPECT_EQ(google, querier.urls()[1].url); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2)); } // Makes sure changes done to top sites get mirrored to the db. TEST_F(TopSitesImplTest, SaveToDB) { MostVisitedURL url; GURL asdf_url("http://asdf.com"); base::string16 asdf_title(base::ASCIIToUTF16("ASDF")); GURL google_url("http://google.com"); base::string16 google_title(base::ASCIIToUTF16("Google")); GURL news_url("http://news.google.com"); base::string16 news_title(base::ASCIIToUTF16("Google News")); // Add asdf_url to history. AddPageToHistory(asdf_url, asdf_title); // Make TopSites reread from the db. StartQueryForMostVisited(); WaitForHistory(); // Add a thumbnail. gfx::Image tmp_bitmap(CreateBitmap(SK_ColorBLUE)); ASSERT_TRUE(top_sites()->SetPageThumbnail(asdf_url, tmp_bitmap, ThumbnailScore())); RecreateTopSitesAndBlock(); { TopSitesQuerier querier; querier.QueryTopSites(top_sites(), false); ASSERT_EQ(1u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ(asdf_url, querier.urls()[0].url); EXPECT_EQ(asdf_title, querier.urls()[0].title); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1)); scoped_refptr read_data; EXPECT_TRUE(top_sites()->GetPageThumbnail(asdf_url, false, &read_data)); EXPECT_TRUE(ThumbnailEqualsBytes(tmp_bitmap, read_data.get())); } MostVisitedURL url2; url2.url = google_url; url2.title = google_title; url2.redirects.push_back(url2.url); AddPageToHistory(url2.url, url2.title); // Add new thumbnail at rank 0 and shift the other result to 1. ASSERT_TRUE(top_sites()->SetPageThumbnail(google_url, tmp_bitmap, ThumbnailScore())); // Make TopSites reread from the db. RefreshTopSitesAndRecreate(); { TopSitesQuerier querier; querier.QueryTopSites(top_sites(), false); ASSERT_EQ(2u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ(asdf_url, querier.urls()[0].url); EXPECT_EQ(asdf_title, querier.urls()[0].title); EXPECT_EQ(google_url, querier.urls()[1].url); EXPECT_EQ(google_title, querier.urls()[1].title); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2)); } } // Makes sure forced URLs in top sites get mirrored to the db. TEST_F(TopSitesImplTest, SaveForcedToDB) { MostVisitedURL url; GURL asdf_url("http://asdf.com"); base::string16 asdf_title(base::ASCIIToUTF16("ASDF")); GURL google_url("http://google.com"); base::string16 google_title(base::ASCIIToUTF16("Google")); GURL news_url("http://news.google.com"); base::string16 news_title(base::ASCIIToUTF16("Google News")); // Add a number of forced URLs. std::vector list; AppendForcedMostVisitedURL(&list, GURL("http://forced1"), 1000); list[0].title = base::ASCIIToUTF16("forced1"); AppendForcedMostVisitedURL(&list, GURL("http://forced2"), 2000); AppendForcedMostVisitedURL(&list, GURL("http://forced3"), 3000); AppendForcedMostVisitedURL(&list, GURL("http://forced4"), 4000); SetTopSites(list); // Add a thumbnail. gfx::Image red_thumbnail(CreateBitmap(SK_ColorRED)); ASSERT_TRUE(top_sites()->SetPageThumbnail( GURL("http://forced1"), red_thumbnail, ThumbnailScore())); // 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")); // Force-flush the cache to ensure we don't reread from it inadvertently. EmptyThreadSafeCache(); // Make TopSites reread from the db. StartQueryForMostVisited(); WaitForHistory(); TopSitesQuerier querier; querier.QueryAllTopSites(top_sites(), true, true); 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(); 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); EXPECT_EQ(GURL("http://forced3"), querier.urls()[2].url); EXPECT_EQ(base::Time::FromJsTime(3000), querier.urls()[2].last_forced_time); EXPECT_EQ(GURL("http://forced4"), querier.urls()[3].url); EXPECT_EQ(base::Time::FromJsTime(4000), querier.urls()[3].last_forced_time); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 4)); } // More permutations of saving to db. TEST_F(TopSitesImplTest, RealDatabase) { MostVisitedURL url; GURL asdf_url("http://asdf.com"); base::string16 asdf_title(base::ASCIIToUTF16("ASDF")); GURL google1_url("http://google.com"); GURL google2_url("http://google.com/redirect"); GURL google3_url("http://www.google.com"); base::string16 google_title(base::ASCIIToUTF16("Google")); GURL news_url("http://news.google.com"); base::string16 news_title(base::ASCIIToUTF16("Google News")); url.url = asdf_url; url.title = asdf_title; url.redirects.push_back(url.url); gfx::Image asdf_thumbnail(CreateBitmap(SK_ColorRED)); ASSERT_TRUE(top_sites()->SetPageThumbnail( asdf_url, asdf_thumbnail, ThumbnailScore())); base::Time add_time(base::Time::Now()); AddPageToHistory(url.url, url.title, url.redirects, add_time); RefreshTopSitesAndRecreate(); { TopSitesQuerier querier; querier.QueryTopSites(top_sites(), false); ASSERT_EQ(1u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ(asdf_url, querier.urls()[0].url); EXPECT_EQ(asdf_title, querier.urls()[0].title); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1)); scoped_refptr read_data; EXPECT_TRUE(top_sites()->GetPageThumbnail(asdf_url, false, &read_data)); EXPECT_TRUE(ThumbnailEqualsBytes(asdf_thumbnail, read_data.get())); } MostVisitedURL url2; url2.url = google3_url; url2.title = google_title; url2.redirects.push_back(google1_url); url2.redirects.push_back(google2_url); url2.redirects.push_back(google3_url); AddPageToHistory(google3_url, url2.title, url2.redirects, add_time - base::TimeDelta::FromMinutes(1)); // Add google twice so that it becomes the first visited site. AddPageToHistory(google3_url, url2.title, url2.redirects, add_time - base::TimeDelta::FromMinutes(2)); gfx::Image google_thumbnail(CreateBitmap(SK_ColorBLUE)); ASSERT_TRUE(top_sites()->SetPageThumbnail( url2.url, google_thumbnail, ThumbnailScore())); RefreshTopSitesAndRecreate(); { scoped_refptr read_data; TopSitesQuerier querier; querier.QueryTopSites(top_sites(), false); ASSERT_EQ(2u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ(google1_url, querier.urls()[0].url); EXPECT_EQ(google_title, querier.urls()[0].title); ASSERT_EQ(3u, querier.urls()[0].redirects.size()); EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, false, &read_data)); EXPECT_TRUE(ThumbnailEqualsBytes(google_thumbnail, read_data.get())); EXPECT_EQ(asdf_url, querier.urls()[1].url); EXPECT_EQ(asdf_title, querier.urls()[1].title); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2)); } gfx::Image weewar_bitmap(CreateBitmap(SK_ColorYELLOW)); base::Time thumbnail_time(base::Time::Now()); ThumbnailScore low_score(1.0, true, true, thumbnail_time); ThumbnailScore medium_score(0.5, true, true, thumbnail_time); ThumbnailScore high_score(0.0, true, true, thumbnail_time); // 1. Set to weewar. (Writes the thumbnail to the DB.) EXPECT_TRUE(top_sites()->SetPageThumbnail(google3_url, weewar_bitmap, medium_score)); RefreshTopSitesAndRecreate(); { scoped_refptr read_data; EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, false, &read_data)); EXPECT_TRUE(ThumbnailEqualsBytes(weewar_bitmap, read_data.get())); } gfx::Image green_bitmap(CreateBitmap(SK_ColorGREEN)); // 2. Set to google - low score. EXPECT_FALSE(top_sites()->SetPageThumbnail(google3_url, green_bitmap, low_score)); // 3. Set to google - high score. EXPECT_TRUE(top_sites()->SetPageThumbnail(google1_url, green_bitmap, high_score)); // Check that the thumbnail was updated. RefreshTopSitesAndRecreate(); { scoped_refptr read_data; EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, false, &read_data)); EXPECT_FALSE(ThumbnailEqualsBytes(weewar_bitmap, read_data.get())); EXPECT_TRUE(ThumbnailEqualsBytes(green_bitmap, read_data.get())); } } TEST_F(TopSitesImplTest, DeleteNotifications) { GURL google1_url("http://google.com"); GURL google2_url("http://google.com/redirect"); GURL google3_url("http://www.google.com"); base::string16 google_title(base::ASCIIToUTF16("Google")); GURL news_url("http://news.google.com"); base::string16 news_title(base::ASCIIToUTF16("Google News")); AddPageToHistory(google1_url, google_title); AddPageToHistory(news_url, news_title); RefreshTopSitesAndRecreate(); { TopSitesQuerier querier; querier.QueryTopSites(top_sites(), false); ASSERT_EQ(GetPrepopulatedPages().size() + 2, querier.urls().size()); } DeleteURL(news_url); // Wait for history to process the deletion. WaitForHistory(); { TopSitesQuerier querier; querier.QueryTopSites(top_sites(), false); ASSERT_EQ(1u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ(google_title, querier.urls()[0].title); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1)); } // Now reload. This verifies topsites actually wrote the deletion to disk. RefreshTopSitesAndRecreate(); { TopSitesQuerier querier; querier.QueryTopSites(top_sites(), false); ASSERT_EQ(1u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ(google_title, querier.urls()[0].title); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1)); } DeleteURL(google1_url); // Wait for history to process the deletion. WaitForHistory(); { TopSitesQuerier querier; querier.QueryTopSites(top_sites(), false); ASSERT_EQ(GetPrepopulatedPages().size(), querier.urls().size()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 0)); } // Now reload. This verifies topsites actually wrote the deletion to disk. RefreshTopSitesAndRecreate(); { TopSitesQuerier querier; querier.QueryTopSites(top_sites(), false); ASSERT_EQ(GetPrepopulatedPages().size(), querier.urls().size()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 0)); } } // Makes sure GetUpdateDelay is updated appropriately. TEST_F(TopSitesImplTest, GetUpdateDelay) { SetLastNumUrlsChanged(0); EXPECT_EQ(30, GetUpdateDelay().InSeconds()); MostVisitedURLList url_list; url_list.resize(20); GURL tmp_url(GURL("http://x")); for (size_t i = 0; i < url_list.size(); ++i) { url_list[i].url = tmp_url; url_list[i].redirects.push_back(tmp_url); } SetTopSites(url_list); EXPECT_EQ(20u, last_num_urls_changed()); SetLastNumUrlsChanged(0); EXPECT_EQ(60, GetUpdateDelay().InMinutes()); SetLastNumUrlsChanged(3); EXPECT_EQ(52, GetUpdateDelay().InMinutes()); SetLastNumUrlsChanged(20); EXPECT_EQ(1, GetUpdateDelay().InMinutes()); } // Verifies that callbacks are notified correctly if requested before top sites // has loaded. TEST_F(TopSitesImplTest, NotifyCallbacksWhenLoaded) { // Recreate top sites. It won't be loaded now. ResetTopSites(); EXPECT_FALSE(IsTopSitesLoaded()); TopSitesQuerier querier1; TopSitesQuerier querier2; TopSitesQuerier querier3; // Starts the queries. querier1.QueryTopSites(top_sites(), false); querier2.QueryTopSites(top_sites(), false); querier3.QueryTopSites(top_sites(), false); // We shouldn't have gotten a callback. EXPECT_EQ(0, querier1.number_of_callbacks()); EXPECT_EQ(0, querier2.number_of_callbacks()); EXPECT_EQ(0, querier3.number_of_callbacks()); // Wait for loading to complete. profile()->BlockUntilTopSitesLoaded(); // Now we should have gotten the callbacks. EXPECT_EQ(1, querier1.number_of_callbacks()); EXPECT_EQ(GetPrepopulatedPages().size(), querier1.urls().size()); EXPECT_EQ(1, querier2.number_of_callbacks()); EXPECT_EQ(GetPrepopulatedPages().size(), querier2.urls().size()); EXPECT_EQ(1, querier3.number_of_callbacks()); EXPECT_EQ(GetPrepopulatedPages().size(), querier3.urls().size()); // Reset the top sites. MostVisitedURLList pages; MostVisitedURL url; url.url = GURL("http://1.com/"); url.redirects.push_back(url.url); pages.push_back(url); url.url = GURL("http://2.com/"); url.redirects.push_back(url.url); pages.push_back(url); SetTopSites(pages); // Recreate top sites. It won't be loaded now. ResetTopSites(); EXPECT_FALSE(IsTopSitesLoaded()); TopSitesQuerier querier4; // Query again. querier4.QueryTopSites(top_sites(), false); // We shouldn't have gotten a callback. EXPECT_EQ(0, querier4.number_of_callbacks()); // Wait for loading to complete. profile()->BlockUntilTopSitesLoaded(); // Now we should have gotten the callbacks. EXPECT_EQ(1, querier4.number_of_callbacks()); ASSERT_EQ(2u + GetPrepopulatedPages().size(), querier4.urls().size()); EXPECT_EQ("http://1.com/", querier4.urls()[0].url.spec()); EXPECT_EQ("http://2.com/", querier4.urls()[1].url.spec()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier4, 2)); // Reset the top sites again, this time don't reload. url.url = GURL("http://3.com/"); url.redirects.push_back(url.url); pages.push_back(url); SetTopSites(pages); // Query again. TopSitesQuerier querier5; querier5.QueryTopSites(top_sites(), true); EXPECT_EQ(1, querier5.number_of_callbacks()); ASSERT_EQ(3u + GetPrepopulatedPages().size(), querier5.urls().size()); EXPECT_EQ("http://1.com/", querier5.urls()[0].url.spec()); EXPECT_EQ("http://2.com/", querier5.urls()[1].url.spec()); EXPECT_EQ("http://3.com/", querier5.urls()[2].url.spec()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier5, 3)); } // Makes sure canceled requests are not notified. TEST_F(TopSitesImplTest, CancelingRequestsForTopSites) { // Recreate top sites. It won't be loaded now. ResetTopSites(); EXPECT_FALSE(IsTopSitesLoaded()); TopSitesQuerier querier1; TopSitesQuerier querier2; // Starts the queries. querier1.QueryTopSites(top_sites(), false); querier2.QueryTopSites(top_sites(), false); // We shouldn't have gotten a callback. EXPECT_EQ(0, querier1.number_of_callbacks()); EXPECT_EQ(0, querier2.number_of_callbacks()); querier2.CancelRequest(); // Wait for loading to complete. profile()->BlockUntilTopSitesLoaded(); // The first callback should succeed. EXPECT_EQ(1, querier1.number_of_callbacks()); EXPECT_EQ(GetPrepopulatedPages().size(), querier1.urls().size()); // And the canceled callback should not be notified. EXPECT_EQ(0, querier2.number_of_callbacks()); } // 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 url1a("http://google.com/"); GURL url1b("http://www.google.com/"); // Create a dummy thumbnail. gfx::Image thumbnail(CreateBitmap(SK_ColorRED)); ThumbnailScore medium_score(0.5, true, true, base::Time::Now()); // Don't store thumbnails for Javascript URLs. EXPECT_FALSE(top_sites()->SetPageThumbnail(invalid_url, thumbnail, medium_score)); // Store thumbnails for unknown (but valid) URLs temporarily - calls // AddTemporaryThumbnail. EXPECT_TRUE(top_sites()->SetPageThumbnail(unknown_url, thumbnail, medium_score)); // We shouldn't get the thumnail back though (the url isn't in to sites yet). scoped_refptr out; EXPECT_FALSE(top_sites()->GetPageThumbnail(unknown_url, false, &out)); // But we should be able to get the temporary page thumbnail score. ThumbnailScore out_score; EXPECT_TRUE(top_sites()->GetTemporaryPageThumbnailScore(unknown_url, &out_score)); EXPECT_TRUE(medium_score.Equals(out_score)); std::vector list; MostVisitedURL mv; mv.url = unknown_url; mv.redirects.push_back(mv.url); mv.redirects.push_back(url1a); mv.redirects.push_back(url1b); list.push_back(mv); // Update URLs. This should result in using thumbnail. SetTopSites(list); ASSERT_TRUE(top_sites()->GetPageThumbnail(unknown_url, false, &out)); EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, out.get())); } // Tests variations of blacklisting without testing prepopulated page // blacklisting. TEST_F(TopSitesImplTest, BlacklistingWithoutPrepopulated) { MostVisitedURLList pages; MostVisitedURL url, url1; url.url = GURL("http://bbc.com/"); url.redirects.push_back(url.url); pages.push_back(url); url1.url = GURL("http://google.com/"); url1.redirects.push_back(url1.url); pages.push_back(url1); SetTopSites(pages); EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://bbc.com/"))); // Blacklist google.com. top_sites()->AddBlacklistedURL(GURL("http://google.com/")); EXPECT_TRUE(top_sites()->HasBlacklistedItems()); EXPECT_TRUE(top_sites()->IsBlacklisted(GURL("http://google.com/"))); EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://bbc.com/"))); // Make sure the blacklisted site isn't returned in the results. { TopSitesQuerier q; q.QueryTopSites(top_sites(), true); EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); } // Recreate top sites and make sure blacklisted url was correctly read. RecreateTopSitesAndBlock(); { TopSitesQuerier q; q.QueryTopSites(top_sites(), true); EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); } // Mark google as no longer blacklisted. top_sites()->RemoveBlacklistedURL(GURL("http://google.com/")); EXPECT_FALSE(top_sites()->HasBlacklistedItems()); EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://google.com/"))); // Make sure google is returned now. { TopSitesQuerier q; q.QueryTopSites(top_sites(), true); EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); EXPECT_EQ("http://google.com/", q.urls()[1].url.spec()); } // Remove all blacklisted sites. top_sites()->ClearBlacklistedURLs(); EXPECT_FALSE(top_sites()->HasBlacklistedItems()); { TopSitesQuerier q; q.QueryTopSites(top_sites(), true); EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); EXPECT_EQ("http://google.com/", q.urls()[1].url.spec()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 2)); } } // Tests variations of blacklisting including blacklisting prepopulated pages. // This test is disable for Android because Android does not have any // prepopulated pages. TEST_F(TopSitesImplTest, BlacklistingWithPrepopulated) { MostVisitedURLList pages; MostVisitedURL url, url1; url.url = GURL("http://bbc.com/"); url.redirects.push_back(url.url); pages.push_back(url); url1.url = GURL("http://google.com/"); url1.redirects.push_back(url1.url); pages.push_back(url1); SetTopSites(pages); EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://bbc.com/"))); // Blacklist google.com. top_sites()->AddBlacklistedURL(GURL("http://google.com/")); DCHECK_GE(GetPrepopulatedPages().size(), 1u); GURL prepopulate_url = GetPrepopulatedPages()[0].most_visited.url; EXPECT_TRUE(top_sites()->HasBlacklistedItems()); EXPECT_TRUE(top_sites()->IsBlacklisted(GURL("http://google.com/"))); EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://bbc.com/"))); EXPECT_FALSE(top_sites()->IsBlacklisted(prepopulate_url)); // Make sure the blacklisted site isn't returned in the results. { TopSitesQuerier q; q.QueryTopSites(top_sites(), true); ASSERT_EQ(1u + GetPrepopulatedPages().size(), q.urls().size()); EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 1)); } // Recreate top sites and make sure blacklisted url was correctly read. RecreateTopSitesAndBlock(); { TopSitesQuerier q; q.QueryTopSites(top_sites(), true); ASSERT_EQ(1u + GetPrepopulatedPages().size(), q.urls().size()); EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 1)); } // Blacklist one of the prepopulate urls. top_sites()->AddBlacklistedURL(prepopulate_url); EXPECT_TRUE(top_sites()->HasBlacklistedItems()); // Make sure the blacklisted prepopulate url isn't returned. { TopSitesQuerier q; q.QueryTopSites(top_sites(), true); ASSERT_EQ(1u + GetPrepopulatedPages().size() - 1, q.urls().size()); EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); for (size_t i = 1; i < q.urls().size(); ++i) EXPECT_NE(prepopulate_url.spec(), q.urls()[i].url.spec()); } // Mark google as no longer blacklisted. top_sites()->RemoveBlacklistedURL(GURL("http://google.com/")); EXPECT_TRUE(top_sites()->HasBlacklistedItems()); EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://google.com/"))); // Make sure google is returned now. { TopSitesQuerier q; q.QueryTopSites(top_sites(), true); ASSERT_EQ(2u + GetPrepopulatedPages().size() - 1, q.urls().size()); EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); EXPECT_EQ("http://google.com/", q.urls()[1].url.spec()); // Android has only one prepopulated page which has been blacklisted, so // only 2 urls are returned. if (q.urls().size() > 2) EXPECT_NE(prepopulate_url.spec(), q.urls()[2].url.spec()); else EXPECT_EQ(1u, GetPrepopulatedPages().size()); } // Remove all blacklisted sites. top_sites()->ClearBlacklistedURLs(); EXPECT_FALSE(top_sites()->HasBlacklistedItems()); { TopSitesQuerier q; q.QueryTopSites(top_sites(), true); ASSERT_EQ(2u + GetPrepopulatedPages().size(), q.urls().size()); EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec()); EXPECT_EQ("http://google.com/", q.urls()[1].url.spec()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 2)); } } // Makes sure prepopulated pages exist. TEST_F(TopSitesImplTest, AddPrepopulatedPages) { TopSitesQuerier q; q.QueryTopSites(top_sites(), true); EXPECT_EQ(GetPrepopulatedPages().size(), q.urls().size()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 0)); MostVisitedURLList pages = q.urls(); EXPECT_FALSE(AddPrepopulatedPages(&pages)); EXPECT_EQ(GetPrepopulatedPages().size(), pages.size()); q.set_urls(pages); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 0)); } // Ensure calling SetTopSites with forced sites already in the DB works. // This test both eviction and TEST_F(TopSitesImplTest, SetForcedTopSites) { // Create forced elements in old URL list. MostVisitedURLList old_url_list; AppendForcedMostVisitedURL(&old_url_list, GURL("http://oldforced/0"), 1000); AppendForcedMostVisitedURL(&old_url_list, GURL("http://oldforced/1"), 4000); AppendForcedMostVisitedURL(&old_url_list, GURL("http://oldforced/2"), 7000); AppendForcedMostVisitedURL(&old_url_list, GURL("http://oldforced/3"), 10000); AppendForcedMostVisitedURL(&old_url_list, GURL("http://oldforced/4"), 11000); AppendForcedMostVisitedURL(&old_url_list, GURL("http://oldforced/5"), 12000); AppendForcedMostVisitedURL(&old_url_list, GURL("http://oldforced/6"), 13000); AppendForcedMostVisitedURL(&old_url_list, GURL("http://oldforced/7"), 18000); AppendForcedMostVisitedURL(&old_url_list, GURL("http://oldforced/8"), 21000); const size_t kNumOldForcedURLs = 9; // Create forced elements in new URL list. MostVisitedURLList new_url_list; AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/0"), 2000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/1"), 3000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/2"), 5000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/3"), 6000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/4"), 8000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/5"), 9000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/6"), 14000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/7"), 15000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/8"), 16000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/9"), 17000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/10"), 19000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/11"), 20000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://newforced/12"), 22000); // Setup a number non-forced URLs in both old and new list. const size_t kNumNonForcedURLs = 20; // Maximum number of non-forced URLs. for (size_t i = 0; i < kNumNonForcedURLs; ++i) { std::ostringstream url; url << "http://oldnonforced/" << i; AppendMostVisitedURL(&old_url_list, GURL(url.str())); url.str(""); url << "http://newnonforced/" << i; AppendMostVisitedURL(&new_url_list, GURL(url.str())); } // Set the initial list of URLs. SetTopSites(old_url_list); EXPECT_EQ(kNumOldForcedURLs + kNumNonForcedURLs, last_num_urls_changed()); TopSitesQuerier querier; // Query only non-forced URLs first. querier.QueryTopSites(top_sites(), false); ASSERT_EQ(kNumNonForcedURLs, querier.urls().size()); // Check first URL. EXPECT_EQ("http://oldnonforced/0", querier.urls()[0].url.spec()); // Query all URLs. querier.QueryAllTopSites(top_sites(), false, true); EXPECT_EQ(kNumOldForcedURLs + kNumNonForcedURLs, querier.urls().size()); // Check first URLs. EXPECT_EQ("http://oldforced/0", querier.urls()[0].url.spec()); EXPECT_EQ("http://oldnonforced/0", querier.urls()[kNumOldForcedURLs].url.spec()); // Set the new list of URLs. SetTopSites(new_url_list); // Query all URLs. querier.QueryAllTopSites(top_sites(), false, true); // We should have reached the maximum of 20 forced URLs. ASSERT_EQ(20 + kNumNonForcedURLs, querier.urls().size()); // Check forced URLs. They follow the order of timestamps above, smaller // timestamps since they were evicted. EXPECT_EQ("http://newforced/1", querier.urls()[0].url.spec()); EXPECT_EQ(3000, querier.urls()[0].last_forced_time.ToJsTime()); EXPECT_EQ("http://oldforced/1", querier.urls()[1].url.spec()); EXPECT_EQ(4000, querier.urls()[1].last_forced_time.ToJsTime()); EXPECT_EQ("http://newforced/2", querier.urls()[2].url.spec()); EXPECT_EQ(5000, querier.urls()[2].last_forced_time.ToJsTime()); EXPECT_EQ("http://newforced/3", querier.urls()[3].url.spec()); EXPECT_EQ(6000, querier.urls()[3].last_forced_time.ToJsTime()); EXPECT_EQ("http://oldforced/2", querier.urls()[4].url.spec()); EXPECT_EQ(7000, querier.urls()[4].last_forced_time.ToJsTime()); EXPECT_EQ("http://newforced/4", querier.urls()[5].url.spec()); EXPECT_EQ(8000, querier.urls()[5].last_forced_time.ToJsTime()); EXPECT_EQ("http://newforced/5", querier.urls()[6].url.spec()); EXPECT_EQ(9000, querier.urls()[6].last_forced_time.ToJsTime()); EXPECT_EQ("http://oldforced/3", querier.urls()[7].url.spec()); EXPECT_EQ(10000, querier.urls()[7].last_forced_time.ToJsTime()); EXPECT_EQ("http://oldforced/4", querier.urls()[8].url.spec()); EXPECT_EQ(11000, querier.urls()[8].last_forced_time.ToJsTime()); EXPECT_EQ("http://oldforced/5", querier.urls()[9].url.spec()); EXPECT_EQ(12000, querier.urls()[9].last_forced_time.ToJsTime()); EXPECT_EQ("http://oldforced/6", querier.urls()[10].url.spec()); EXPECT_EQ(13000, querier.urls()[10].last_forced_time.ToJsTime()); EXPECT_EQ("http://newforced/6", querier.urls()[11].url.spec()); EXPECT_EQ(14000, querier.urls()[11].last_forced_time.ToJsTime()); EXPECT_EQ("http://newforced/7", querier.urls()[12].url.spec()); EXPECT_EQ(15000, querier.urls()[12].last_forced_time.ToJsTime()); EXPECT_EQ("http://newforced/8", querier.urls()[13].url.spec()); EXPECT_EQ(16000, querier.urls()[13].last_forced_time.ToJsTime()); EXPECT_EQ("http://newforced/9", querier.urls()[14].url.spec()); EXPECT_EQ(17000, querier.urls()[14].last_forced_time.ToJsTime()); EXPECT_EQ("http://oldforced/7", querier.urls()[15].url.spec()); EXPECT_EQ(18000, querier.urls()[15].last_forced_time.ToJsTime()); EXPECT_EQ("http://newforced/10", querier.urls()[16].url.spec()); EXPECT_EQ(19000, querier.urls()[16].last_forced_time.ToJsTime()); EXPECT_EQ("http://newforced/11", querier.urls()[17].url.spec()); EXPECT_EQ(20000, querier.urls()[17].last_forced_time.ToJsTime()); EXPECT_EQ("http://oldforced/8", querier.urls()[18].url.spec()); EXPECT_EQ(21000, querier.urls()[18].last_forced_time.ToJsTime()); EXPECT_EQ("http://newforced/12", querier.urls()[19].url.spec()); EXPECT_EQ(22000, querier.urls()[19].last_forced_time.ToJsTime()); // Check first and last non-forced URLs. EXPECT_EQ("http://newnonforced/0", querier.urls()[20].url.spec()); EXPECT_TRUE(querier.urls()[20].last_forced_time.is_null()); EXPECT_EQ("http://newnonforced/19", querier.urls()[39].url.spec()); EXPECT_TRUE(querier.urls()[39].last_forced_time.is_null()); } 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); // The following three will be evicted. AppendForcedMostVisitedURL(&old_url_list, GURL("http://collision/0"), 4000); AppendForcedMostVisitedURL(&old_url_list, GURL("http://collision/1"), 6000); AppendForcedMostVisitedURL(&old_url_list, GURL("http://collision/2"), 7000); // The following is evicted since all non-forced URLs are, therefore it // doesn't cause a collision. AppendMostVisitedURL(&old_url_list, GURL("http://noncollision/0")); SetTopSites(old_url_list); // Setup a new URL list that will cause collisions. MostVisitedURLList new_url_list; AppendForcedMostVisitedURL(&new_url_list, GURL("http://collision/1"), 2000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://url/2"), 3000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://collision/0"), 5000); AppendForcedMostVisitedURL(&new_url_list, GURL("http://noncollision/0"), 9000); AppendMostVisitedURL(&new_url_list, GURL("http://collision/2")); AppendMostVisitedURL(&new_url_list, GURL("http://url/3")); SetTopSites(new_url_list); // Query all URLs. TopSitesQuerier querier; querier.QueryAllTopSites(top_sites(), false, true); // Check URLs. When collision occurs, the incoming one is always preferred. ASSERT_EQ(7u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ("http://url/0", querier.urls()[0].url.spec()); EXPECT_EQ(1000u, querier.urls()[0].last_forced_time.ToJsTime()); EXPECT_EQ("http://collision/1", querier.urls()[1].url.spec()); EXPECT_EQ(2000u, querier.urls()[1].last_forced_time.ToJsTime()); EXPECT_EQ("http://url/2", querier.urls()[2].url.spec()); EXPECT_EQ(3000u, querier.urls()[2].last_forced_time.ToJsTime()); EXPECT_EQ("http://collision/0", querier.urls()[3].url.spec()); EXPECT_EQ(5000u, querier.urls()[3].last_forced_time.ToJsTime()); EXPECT_EQ("http://noncollision/0", querier.urls()[4].url.spec()); EXPECT_EQ(9000u, querier.urls()[4].last_forced_time.ToJsTime()); EXPECT_EQ("http://collision/2", querier.urls()[5].url.spec()); EXPECT_TRUE(querier.urls()[5].last_forced_time.is_null()); EXPECT_EQ("http://url/3", querier.urls()[6].url.spec()); EXPECT_TRUE(querier.urls()[6].last_forced_time.is_null()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 7)); } TEST_F(TopSitesImplTest, SetTopSitesIdentical) { // Set the initial list of URLs. MostVisitedURLList url_list; AppendForcedMostVisitedURL(&url_list, GURL("http://url/0"), 1000); AppendMostVisitedURL(&url_list, GURL("http://url/1")); AppendMostVisitedURL(&url_list, GURL("http://url/2")); SetTopSites(url_list); // Set the new list of URLs to be exactly the same. SetTopSites(MostVisitedURLList(url_list)); // Query all URLs. TopSitesQuerier querier; querier.QueryAllTopSites(top_sites(), false, true); // Check URLs. When collision occurs, the incoming one is always preferred. ASSERT_EQ(3u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ("http://url/0", querier.urls()[0].url.spec()); EXPECT_EQ(1000u, querier.urls()[0].last_forced_time.ToJsTime()); EXPECT_EQ("http://url/1", querier.urls()[1].url.spec()); EXPECT_EQ("http://url/2", querier.urls()[2].url.spec()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 3)); } TEST_F(TopSitesImplTest, SetTopSitesWithAlreadyExistingForcedURLs) { // Set the initial list of URLs. MostVisitedURLList old_url_list; AppendForcedMostVisitedURL(&old_url_list, GURL("http://url/0/redir"), 1000); AppendForcedMostVisitedURL(&old_url_list, GURL("http://url/1"), 2000); SetTopSites(old_url_list); // Setup a new URL list that will cause collisions. MostVisitedURLList new_url_list; AppendMostVisitedURLWithRedirect(&new_url_list, GURL("http://url/0/redir"), GURL("http://url/0")); AppendMostVisitedURL(&new_url_list, GURL("http://url/1")); SetTopSites(new_url_list); // Query all URLs. TopSitesQuerier querier; querier.QueryAllTopSites(top_sites(), false, true); // Check URLs. When collision occurs, the non-forced one is always preferred. ASSERT_EQ(2u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ("http://url/0", querier.urls()[0].url.spec()); EXPECT_EQ("http://url/0/redir", querier.urls()[0].redirects[0].spec()); EXPECT_TRUE(querier.urls()[0].last_forced_time.is_null()); EXPECT_EQ("http://url/1", querier.urls()[1].url.spec()); EXPECT_TRUE(querier.urls()[1].last_forced_time.is_null()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2)); } TEST_F(TopSitesImplTest, AddForcedURL) { // Set the initial list of URLs. MostVisitedURLList url_list; AppendForcedMostVisitedURL(&url_list, GURL("http://forced/0"), 2000); AppendForcedMostVisitedURL(&url_list, GURL("http://forced/1"), 4000); AppendMostVisitedURL(&url_list, GURL("http://nonforced/0")); AppendMostVisitedURL(&url_list, GURL("http://nonforced/1")); AppendMostVisitedURL(&url_list, GURL("http://nonforced/2")); SetTopSites(url_list); // Add forced sites here and there to exercise a couple of cases. EXPECT_TRUE(AddForcedURL(GURL("http://forced/2"), base::Time::FromJsTime(5000))); EXPECT_TRUE(AddForcedURL(GURL("http://forced/3"), base::Time::FromJsTime(1000))); EXPECT_TRUE(AddForcedURL(GURL("http://forced/4"), base::Time::FromJsTime(3000))); // Check URLs. TopSitesQuerier querier; querier.QueryAllTopSites(top_sites(), false, true); ASSERT_EQ(8u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ("http://forced/3", querier.urls()[0].url.spec()); EXPECT_EQ(1000u, querier.urls()[0].last_forced_time.ToJsTime()); EXPECT_EQ("http://forced/0", querier.urls()[1].url.spec()); EXPECT_EQ(2000u, querier.urls()[1].last_forced_time.ToJsTime()); EXPECT_EQ("http://forced/4", querier.urls()[2].url.spec()); EXPECT_EQ(3000u, querier.urls()[2].last_forced_time.ToJsTime()); EXPECT_EQ("http://forced/1", querier.urls()[3].url.spec()); EXPECT_EQ(4000u, querier.urls()[3].last_forced_time.ToJsTime()); EXPECT_EQ("http://forced/2", querier.urls()[4].url.spec()); EXPECT_EQ(5000u, querier.urls()[4].last_forced_time.ToJsTime()); EXPECT_EQ("http://nonforced/0", querier.urls()[5].url.spec()); EXPECT_TRUE(querier.urls()[5].last_forced_time.is_null()); EXPECT_EQ("http://nonforced/1", querier.urls()[6].url.spec()); EXPECT_TRUE(querier.urls()[6].last_forced_time.is_null()); EXPECT_EQ("http://nonforced/2", querier.urls()[7].url.spec()); EXPECT_TRUE(querier.urls()[7].last_forced_time.is_null()); ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 8)); // Add some collisions with forced and non-forced. Non-forced URLs are never // expected to move. EXPECT_TRUE(AddForcedURL(GURL("http://forced/3"), base::Time::FromJsTime(4000))); EXPECT_TRUE(AddForcedURL(GURL("http://forced/1"), base::Time::FromJsTime(1000))); EXPECT_FALSE(AddForcedURL(GURL("http://nonforced/0"), base::Time::FromJsTime(6000))); // Check relevant URLs. querier.QueryAllTopSites(top_sites(), false, true); ASSERT_EQ(8u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ("http://forced/1", querier.urls()[0].url.spec()); EXPECT_EQ(1000u, querier.urls()[0].last_forced_time.ToJsTime()); EXPECT_EQ("http://forced/3", querier.urls()[3].url.spec()); EXPECT_EQ(4000u, querier.urls()[3].last_forced_time.ToJsTime()); EXPECT_EQ("http://nonforced/0", querier.urls()[5].url.spec()); EXPECT_TRUE(querier.urls()[5].last_forced_time.is_null()); // Add a timestamp collision and make sure things don't break. EXPECT_TRUE(AddForcedURL(GURL("http://forced/5"), base::Time::FromJsTime(4000))); querier.QueryAllTopSites(top_sites(), false, true); ASSERT_EQ(9u + GetPrepopulatedPages().size(), querier.urls().size()); EXPECT_EQ(4000u, querier.urls()[3].last_forced_time.ToJsTime()); EXPECT_EQ(4000u, querier.urls()[4].last_forced_time.ToJsTime()); // We don't care which order they get sorted in. if (querier.urls()[3].url.spec() == "http://forced/3") { EXPECT_EQ("http://forced/3", querier.urls()[3].url.spec()); EXPECT_EQ("http://forced/5", querier.urls()[4].url.spec()); } else { EXPECT_EQ("http://forced/5", querier.urls()[3].url.spec()); EXPECT_EQ("http://forced/3", querier.urls()[4].url.spec()); } // Make sure the thumbnail is not lost when the timestamp is updated. gfx::Image red_thumbnail(CreateBitmap(SK_ColorRED)); ASSERT_TRUE(top_sites()->SetPageThumbnail( GURL("http://forced/5"), red_thumbnail, ThumbnailScore())); // 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")); EXPECT_TRUE(AddForcedURL(GURL("http://forced/5"), base::Time::FromJsTime(6000))); // Ensure the thumbnail is still there even if the timestamp changed. 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(); } } // namespace history