blob: 650ffab8241d96429b5f538af6d4185a3f4379ea [file] [log] [blame]
// 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 <string>
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "chrome/browser/sessions/session_types.h"
#include "chrome/browser/sessions/session_types_test_helper.h"
#include "chrome/browser/sync/glue/session_model_associator.h"
#include "chrome/browser/sync/glue/synced_tab_delegate.h"
#include "chrome/browser/sync/profile_sync_service_mock.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/profile_mock.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/common/page_transition_types.h"
#include "content/public/test/test_browser_thread.h"
#include "googleurl/src/gurl.h"
#include "sync/protocol/session_specifics.pb.h"
#include "sync/util/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using content::BrowserThread;
using testing::NiceMock;
using testing::Return;
using testing::StrictMock;
using testing::_;
namespace browser_sync {
class SyncSessionModelAssociatorTest : public testing::Test {
protected:
SyncSessionModelAssociatorTest()
: ui_thread_(BrowserThread::UI, &message_loop_),
sync_service_(&profile_),
model_associator_(&sync_service_, true) {}
// Helper methods to avoid having to friend individual tests.
bool GetFavicon(std::string page_url, std::string* favicon) {
return model_associator_.GetSyncedFaviconForPageURL(page_url, favicon);
}
void LoadTabFavicon(const sync_pb::SessionTab& tab) {
model_associator_.LoadForeignTabFavicon(tab);
}
size_t NumFavicons() {
return model_associator_.NumFaviconsForTesting();
}
void DecrementFavicon(std::string url) {
model_associator_.DecrementAndCleanFaviconForURL(url);
}
static GURL GetCurrentVirtualURL(const SyncedTabDelegate& tab_delegate) {
return SessionModelAssociator::GetCurrentVirtualURL(tab_delegate);
}
static void UpdateSessionTabFromDelegate(
const SyncedTabDelegate& tab_delegate,
base::Time mtime,
base::Time default_navigation_timestamp,
SessionTab* session_tab) {
SessionModelAssociator::UpdateSessionTabFromDelegate(
tab_delegate,
mtime,
default_navigation_timestamp,
session_tab);
}
private:
MessageLoopForUI message_loop_;
content::TestBrowserThread ui_thread_;
NiceMock<ProfileMock> profile_;
NiceMock<ProfileSyncServiceMock> sync_service_;
protected:
SessionModelAssociator model_associator_;
};
namespace {
TEST_F(SyncSessionModelAssociatorTest, SessionWindowHasNoTabsToSync) {
SessionWindow win;
ASSERT_TRUE(SessionWindowHasNoTabsToSync(win));
scoped_ptr<SessionTab> tab(new SessionTab());
win.tabs.push_back(tab.release());
ASSERT_TRUE(SessionWindowHasNoTabsToSync(win));
TabNavigation nav =
SessionTypesTestHelper::CreateNavigation("about:bubba", "title");
win.tabs[0]->navigations.push_back(nav);
ASSERT_FALSE(SessionWindowHasNoTabsToSync(win));
}
TEST_F(SyncSessionModelAssociatorTest, ShouldSyncSessionTab) {
SessionTab tab;
ASSERT_FALSE(ShouldSyncSessionTab(tab));
TabNavigation nav =
SessionTypesTestHelper::CreateNavigation(
chrome::kChromeUINewTabURL, "title");
tab.navigations.push_back(nav);
// NewTab does not count as valid if it's the only navigation.
ASSERT_FALSE(ShouldSyncSessionTab(tab));
TabNavigation nav2 =
SessionTypesTestHelper::CreateNavigation("about:bubba", "title");
tab.navigations.push_back(nav2);
// Once there's another navigation, the tab is valid.
ASSERT_TRUE(ShouldSyncSessionTab(tab));
}
TEST_F(SyncSessionModelAssociatorTest,
ShouldSyncSessionTabIgnoresFragmentForNtp) {
SessionTab tab;
ASSERT_FALSE(ShouldSyncSessionTab(tab));
TabNavigation nav =
SessionTypesTestHelper::CreateNavigation(
std::string(chrome::kChromeUINewTabURL) + "#bookmarks", "title");
tab.navigations.push_back(nav);
// NewTab does not count as valid if it's the only navigation.
ASSERT_FALSE(ShouldSyncSessionTab(tab));
}
} // namespace
TEST_F(SyncSessionModelAssociatorTest, PopulateSessionHeader) {
sync_pb::SessionHeader header_s;
header_s.set_client_name("Client 1");
header_s.set_device_type(sync_pb::SessionHeader_DeviceType_TYPE_WIN);
SyncedSession session;
base::Time time = base::Time::Now();
SessionModelAssociator::PopulateSessionHeaderFromSpecifics(
header_s, time, &session);
ASSERT_EQ("Client 1", session.session_name);
ASSERT_EQ(SyncedSession::TYPE_WIN, session.device_type);
ASSERT_EQ(time, session.modified_time);
}
TEST_F(SyncSessionModelAssociatorTest, PopulateSessionWindow) {
sync_pb::SessionWindow window_s;
window_s.add_tab(0);
window_s.set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
window_s.set_selected_tab_index(1);
std::string tag = "tag";
SyncedSessionTracker tracker;
SyncedSession* session = tracker.GetSession(tag);
tracker.PutWindowInSession(tag, 0);
SessionModelAssociator::PopulateSessionWindowFromSpecifics(
tag, window_s, base::Time(), session->windows[0], &tracker);
ASSERT_EQ(1U, session->windows[0]->tabs.size());
ASSERT_EQ(1, session->windows[0]->selected_tab_index);
ASSERT_EQ(1, session->windows[0]->type);
ASSERT_EQ(1U, tracker.num_synced_sessions());
ASSERT_EQ(1U, tracker.num_synced_tabs(std::string("tag")));
}
TEST_F(SyncSessionModelAssociatorTest, TabNodePool) {
SessionModelAssociator::TabNodePool pool(NULL);
pool.set_machine_tag("tag");
ASSERT_TRUE(pool.empty());
ASSERT_TRUE(pool.full());
ASSERT_EQ(0U, pool.capacity());
pool.AddTabNode(5);
pool.AddTabNode(10);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(pool.full());
ASSERT_EQ(2U, pool.capacity());
ASSERT_EQ(10, pool.GetFreeTabNode()); // Returns last free tab.
ASSERT_FALSE(pool.empty());
ASSERT_FALSE(pool.full());
ASSERT_EQ(2U, pool.capacity());
ASSERT_EQ(5, pool.GetFreeTabNode()); // Returns last free tab.
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(pool.full());
ASSERT_EQ(2U, pool.capacity());
// Release them in reverse order.
pool.FreeTabNode(10);
pool.FreeTabNode(5);
ASSERT_EQ(2U, pool.capacity());
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(pool.full());
ASSERT_EQ(5, pool.GetFreeTabNode()); // Returns last free tab.
ASSERT_FALSE(pool.empty());
ASSERT_FALSE(pool.full());
ASSERT_EQ(2U, pool.capacity());
ASSERT_FALSE(pool.empty());
ASSERT_FALSE(pool.full());
ASSERT_EQ(2U, pool.capacity());
ASSERT_EQ(10, pool.GetFreeTabNode()); // Returns last free tab.
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(pool.full());
ASSERT_EQ(2U, pool.capacity());
// Release them again.
pool.FreeTabNode(10);
pool.FreeTabNode(5);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(pool.full());
ASSERT_EQ(2U, pool.capacity());
pool.clear();
ASSERT_TRUE(pool.empty());
ASSERT_TRUE(pool.full());
ASSERT_EQ(0U, pool.capacity());
}
namespace {
class SyncedTabDelegateMock : public SyncedTabDelegate {
public:
SyncedTabDelegateMock() {}
virtual ~SyncedTabDelegateMock() {}
MOCK_CONST_METHOD0(GetWindowId, SessionID::id_type());
MOCK_CONST_METHOD0(GetSessionId, SessionID::id_type());
MOCK_CONST_METHOD0(IsBeingDestroyed, bool());
MOCK_CONST_METHOD0(profile, Profile*());
MOCK_CONST_METHOD0(GetExtensionAppId, std::string());
MOCK_CONST_METHOD0(GetCurrentEntryIndex, int());
MOCK_CONST_METHOD0(GetEntryCount, int());
MOCK_CONST_METHOD0(GetPendingEntryIndex, int());
MOCK_CONST_METHOD0(GetPendingEntry, content::NavigationEntry*());
MOCK_CONST_METHOD1(GetEntryAtIndex, content::NavigationEntry*(int i));
MOCK_CONST_METHOD0(GetActiveEntry, content::NavigationEntry*());
MOCK_CONST_METHOD0(IsPinned, bool());
};
class SyncRefreshListener : public content::NotificationObserver {
public:
SyncRefreshListener() : notified_of_refresh_(false) {
registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
content::NotificationService::AllSources());
}
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
if (type == chrome::NOTIFICATION_SYNC_REFRESH_LOCAL) {
notified_of_refresh_ = true;
}
}
bool notified_of_refresh() const { return notified_of_refresh_; }
private:
bool notified_of_refresh_;
content::NotificationRegistrar registrar_;
};
// Test that AttemptSessionsDataRefresh() triggers the
// NOTIFICATION_SYNC_REFRESH_LOCAL notification.
TEST_F(SyncSessionModelAssociatorTest, TriggerSessionRefresh) {
SyncRefreshListener refresh_listener;
EXPECT_FALSE(refresh_listener.notified_of_refresh());
model_associator_.AttemptSessionsDataRefresh();
EXPECT_TRUE(refresh_listener.notified_of_refresh());
}
// Test that we exclude tabs with only chrome:// and file:// schemed navigations
// from ShouldSyncTab(..).
TEST_F(SyncSessionModelAssociatorTest, ValidTabs) {
NiceMock<SyncedTabDelegateMock> tab_mock;
// A null entry shouldn't crash.
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(0));
EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
Return((content::NavigationEntry *)NULL));
EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(1));
EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
EXPECT_FALSE(model_associator_.ShouldSyncTab(tab_mock));
// A chrome:// entry isn't valid.
scoped_ptr<content::NavigationEntry> entry(
content::NavigationEntry::Create());
entry->SetVirtualURL(GURL("chrome://preferences/"));
testing::Mock::VerifyAndClearExpectations(&tab_mock);
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(0));
EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(Return(entry.get()));
EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(1));
EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
EXPECT_FALSE(model_associator_.ShouldSyncTab(tab_mock));
// A file:// entry isn't valid, even in addition to another entry.
scoped_ptr<content::NavigationEntry> entry2(
content::NavigationEntry::Create());
entry2->SetVirtualURL(GURL("file://bla"));
testing::Mock::VerifyAndClearExpectations(&tab_mock);
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(0));
EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(Return(entry.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(1)).WillRepeatedly(
Return(entry2.get()));
EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(2));
EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
EXPECT_FALSE(model_associator_.ShouldSyncTab(tab_mock));
// Add a valid scheme entry to tab, making the tab valid.
scoped_ptr<content::NavigationEntry> entry3(
content::NavigationEntry::Create());
entry3->SetVirtualURL(GURL("http://www.google.com"));
testing::Mock::VerifyAndClearExpectations(&tab_mock);
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(0));
EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
Return(entry.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(1)).WillRepeatedly(
Return(entry2.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(2)).WillRepeatedly(
Return(entry3.get()));
EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(3));
EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
EXPECT_TRUE(model_associator_.ShouldSyncTab(tab_mock));
}
// Create tab specifics with an empty favicon. Ensure it gets ignored and not
// stored into the synced favicon lookups.
TEST_F(SyncSessionModelAssociatorTest, LoadEmptyFavicon) {
std::string favicon = "";
std::string favicon_url = "http://www.faviconurl.com/favicon.ico";
std::string page_url = "http://www.faviconurl.com/page.html";
sync_pb::SessionTab tab;
tab.set_favicon(favicon);
tab.set_favicon_source(favicon_url);
tab.set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON);
sync_pb::TabNavigation* navigation = tab.add_navigation();
navigation->set_virtual_url(page_url);
tab.set_current_navigation_index(0);
std::string synced_favicon;
EXPECT_FALSE(GetFavicon(page_url, &synced_favicon));
EXPECT_TRUE(synced_favicon.empty());
LoadTabFavicon(tab);
EXPECT_FALSE(GetFavicon(page_url, &synced_favicon));
EXPECT_TRUE(synced_favicon.empty());
}
// Create tab specifics with a non-web favicon. Ensure it gets ignored and not
// stored into the synced favicon lookups.
TEST_F(SyncSessionModelAssociatorTest, LoadNonWebFavicon) {
std::string favicon = "these are icon synced_favicon";
std::string favicon_url = "http://www.faviconurl.com/favicon.ico";
std::string page_url = "http://www.faviconurl.com/page.html";
sync_pb::SessionTab tab;
tab.set_favicon(favicon);
tab.set_favicon_source(favicon_url);
// Set favicon type to an unsupported value (1 == WEB_FAVICON).
tab.mutable_unknown_fields()->AddVarint(9, 2);
sync_pb::TabNavigation* navigation = tab.add_navigation();
navigation->set_virtual_url(page_url);
tab.set_current_navigation_index(0);
std::string synced_favicon;
EXPECT_FALSE(GetFavicon(page_url, &synced_favicon));
EXPECT_TRUE(synced_favicon.empty());
LoadTabFavicon(tab);
EXPECT_FALSE(GetFavicon(page_url, &synced_favicon));
EXPECT_TRUE(synced_favicon.empty());
}
// Create tab specifics with a valid favicon. Ensure it gets stored in the
// synced favicon lookups and is accessible by the page url.
TEST_F(SyncSessionModelAssociatorTest, LoadValidFavicon) {
std::string favicon = "these are icon synced_favicon";
std::string favicon_url = "http://www.faviconurl.com/favicon.ico";
std::string page_url = "http://www.faviconurl.com/page.html";
sync_pb::SessionTab tab;
tab.set_favicon(favicon);
tab.set_favicon_source(favicon_url);
tab.set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON);
sync_pb::TabNavigation* navigation = tab.add_navigation();
navigation->set_virtual_url(page_url);
tab.set_current_navigation_index(0);
std::string synced_favicon;
EXPECT_FALSE(GetFavicon(page_url, &synced_favicon));
EXPECT_TRUE(synced_favicon.empty());
LoadTabFavicon(tab);
EXPECT_TRUE(GetFavicon(page_url, &synced_favicon));
ASSERT_FALSE(synced_favicon.empty());
EXPECT_EQ(favicon, synced_favicon);
}
// Create tab specifics with a valid favicon, load it, then load tab specifics
// with a new favicon for the same favicon source but different page. Ensure the
// new favicon overwrites the old favicon for both page urls.
TEST_F(SyncSessionModelAssociatorTest, UpdateValidFavicon) {
std::string favicon_url = "http://www.faviconurl.com/favicon.ico";
std::string favicon = "these are icon synced_favicon";
std::string page_url = "http://www.faviconurl.com/page.html";
sync_pb::SessionTab tab;
tab.set_favicon(favicon);
tab.set_favicon_source(favicon_url);
tab.set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON);
sync_pb::TabNavigation* navigation = tab.add_navigation();
navigation->set_virtual_url(page_url);
tab.set_current_navigation_index(0);
std::string synced_favicon;
EXPECT_FALSE(GetFavicon(page_url, &synced_favicon));
EXPECT_TRUE(synced_favicon.empty());
LoadTabFavicon(tab);
EXPECT_TRUE(GetFavicon(page_url, &synced_favicon));
ASSERT_FALSE(synced_favicon.empty());
EXPECT_EQ(favicon, synced_favicon);
// Now have a new page with same favicon source but newer favicon data.
std::string favicon2 = "these are new icon synced_favicon";
std::string page_url2 = "http://www.faviconurl.com/page2.html";
sync_pb::SessionTab tab2;
tab2.set_favicon(favicon2);
tab2.set_favicon_source(favicon_url);
tab2.set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON);
sync_pb::TabNavigation* navigation2 = tab2.add_navigation();
navigation2->set_virtual_url(page_url2);
tab2.set_current_navigation_index(0);
// Verify the favicons for both pages match the newest favicon.
synced_favicon.clear();
EXPECT_FALSE(GetFavicon(page_url2, &synced_favicon));
EXPECT_TRUE(synced_favicon.empty());
LoadTabFavicon(tab2);
EXPECT_TRUE(GetFavicon(page_url2, &synced_favicon));
ASSERT_FALSE(synced_favicon.empty());
EXPECT_EQ(favicon2, synced_favicon);
EXPECT_NE(favicon, synced_favicon);
synced_favicon.clear();
EXPECT_TRUE(GetFavicon(page_url, &synced_favicon));
ASSERT_FALSE(synced_favicon.empty());
EXPECT_EQ(favicon2, synced_favicon);
EXPECT_NE(favicon, synced_favicon);
}
// Ensure that favicon cleanup cleans up favicons no longer being used and
// doesn't touch those favicons still in use.
TEST_F(SyncSessionModelAssociatorTest, FaviconCleanup) {
EXPECT_EQ(NumFavicons(), 0U);
std::string double_favicon = "these are icon synced_favicon";
std::string double_favicon_url = "http://www.faviconurl.com/favicon.ico";
std::string page_url = "http://www.faviconurl.com/page.html";
sync_pb::SessionTab tab;
tab.set_favicon(double_favicon);
tab.set_favicon_source(double_favicon_url);
tab.set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON);
sync_pb::TabNavigation* navigation = tab.add_navigation();
navigation->set_virtual_url(page_url);
tab.set_current_navigation_index(0);
LoadTabFavicon(tab);
EXPECT_EQ(1U, NumFavicons());
// Add another page using the first favicon.
std::string page_url2 = "http://www.faviconurl.com/page2.html";
tab.mutable_navigation(0)->set_virtual_url(page_url2);
LoadTabFavicon(tab);
EXPECT_EQ(1U, NumFavicons());
// Add a favicon with a single user.
std::string single_favicon = "different favicon synced_favicon";
std::string single_favicon_url = "http://www.single_favicon_page.com/x.ico";
std::string single_favicon_page = "http://www.single_favicon_page.com/x.html";
tab.set_favicon(single_favicon);
tab.set_favicon_source(single_favicon_url);
tab.mutable_navigation(0)->set_virtual_url(single_favicon_page);
LoadTabFavicon(tab);
EXPECT_EQ(2U, NumFavicons());
// Decrementing a favicon used by one page should remove it.
std::string synced_favicon;
EXPECT_TRUE(GetFavicon(single_favicon_page, &synced_favicon));
EXPECT_EQ(synced_favicon, single_favicon);
DecrementFavicon(single_favicon_page);
synced_favicon.clear();
EXPECT_FALSE(GetFavicon(single_favicon_page, &synced_favicon));
EXPECT_TRUE(synced_favicon.empty());
EXPECT_EQ(1U, NumFavicons());
// Decrementing a favicon used by two pages shouldn't remove it.
synced_favicon.clear();
EXPECT_TRUE(GetFavicon(page_url, &synced_favicon));
EXPECT_EQ(synced_favicon, double_favicon);
synced_favicon.clear();
EXPECT_TRUE(GetFavicon(page_url2, &synced_favicon));
EXPECT_EQ(synced_favicon, double_favicon);
DecrementFavicon(page_url);
EXPECT_EQ(1U, NumFavicons());
synced_favicon.clear();
EXPECT_TRUE(GetFavicon(page_url, &synced_favicon));
EXPECT_EQ(synced_favicon, double_favicon);
synced_favicon.clear();
EXPECT_TRUE(GetFavicon(page_url2, &synced_favicon));
EXPECT_EQ(synced_favicon, double_favicon);
EXPECT_EQ(1U, NumFavicons());
// Attempting to decrement a page that's already removed should do nothing.
DecrementFavicon(single_favicon_page);
EXPECT_EQ(1U, NumFavicons());
// Attempting to decrement an empty url should do nothing.
DecrementFavicon("");
EXPECT_EQ(1U, NumFavicons());
// Decrementing the second and only remaining page should remove the favicon.
// Both pages that referred to it should now fail to look up their favicon.
DecrementFavicon(page_url2);
synced_favicon.clear();
EXPECT_FALSE(GetFavicon(page_url, &synced_favicon));
EXPECT_TRUE(synced_favicon.empty());
EXPECT_EQ(0U, NumFavicons());
synced_favicon.clear();
EXPECT_FALSE(GetFavicon(page_url2, &synced_favicon));
EXPECT_TRUE(synced_favicon.empty());
EXPECT_EQ(0U, NumFavicons());
}
// TODO(akalin): We should really use a fake for SyncedTabDelegate.
// Make sure GetCurrentVirtualURL() returns the virtual URL of the pending
// entry if the current entry is pending.
TEST_F(SyncSessionModelAssociatorTest, GetCurrentVirtualURLPending) {
StrictMock<SyncedTabDelegateMock> tab_mock;
scoped_ptr<content::NavigationEntry> entry(
content::NavigationEntry::Create());
entry->SetVirtualURL(GURL("http://www.google.com"));
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillOnce(Return(0));
EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillOnce(Return(0));
EXPECT_CALL(tab_mock, GetPendingEntry()).WillOnce(Return(entry.get()));
EXPECT_EQ(entry->GetVirtualURL(), GetCurrentVirtualURL(tab_mock));
}
// Make sure GetCurrentVirtualURL() returns the virtual URL of the current
// entry if the current entry is non-pending.
TEST_F(SyncSessionModelAssociatorTest, GetCurrentVirtualURLNonPending) {
StrictMock<SyncedTabDelegateMock> tab_mock;
scoped_ptr<content::NavigationEntry> entry(
content::NavigationEntry::Create());
entry->SetVirtualURL(GURL("http://www.google.com"));
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillOnce(Return(0));
EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillOnce(Return(-1));
EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillOnce(Return(entry.get()));
EXPECT_EQ(entry->GetVirtualURL(), GetCurrentVirtualURL(tab_mock));
}
const base::Time kTime1 = base::Time::FromInternalValue(100);
const base::Time kTime2 = base::Time::FromInternalValue(105);
const base::Time kTime3 = base::Time::FromInternalValue(110);
const base::Time kTime4 = base::Time::FromInternalValue(120);
const base::Time kTime5 = base::Time::FromInternalValue(150);
const base::Time kTime6 = base::Time::FromInternalValue(200);
const base::Time kTime7 = base::Time::FromInternalValue(500);
const base::Time kTime8 = base::Time::FromInternalValue(1000);
// Ensure new tabs have the current timestamp set for the current
// navigation, while other navigations have null timestamps.
TEST_F(SyncSessionModelAssociatorTest, UpdateNewTab) {
// Create a tab with three valid entries.
NiceMock<SyncedTabDelegateMock> tab_mock;
EXPECT_CALL(tab_mock, GetSessionId()).WillRepeatedly(Return(0));
scoped_ptr<content::NavigationEntry> entry1(
content::NavigationEntry::Create());
entry1->SetVirtualURL(GURL("http://www.google.com"));
scoped_ptr<content::NavigationEntry> entry2(
content::NavigationEntry::Create());
entry2->SetVirtualURL(GURL("http://www.noodle.com"));
scoped_ptr<content::NavigationEntry> entry3(
content::NavigationEntry::Create());
entry3->SetVirtualURL(GURL("http://www.doodle.com"));
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(2));
EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
Return(entry1.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(1)).WillRepeatedly(
Return(entry2.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(2)).WillRepeatedly(
Return(entry3.get()));
EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(3));
EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
SessionTab session_tab;
UpdateSessionTabFromDelegate(tab_mock, kTime1, kTime2, &session_tab);
EXPECT_EQ(0, session_tab.window_id.id());
EXPECT_EQ(0, session_tab.tab_id.id());
EXPECT_EQ(0, session_tab.tab_visual_index);
EXPECT_EQ(2, session_tab.current_navigation_index);
EXPECT_FALSE(session_tab.pinned);
EXPECT_TRUE(session_tab.extension_app_id.empty());
EXPECT_TRUE(session_tab.user_agent_override.empty());
EXPECT_EQ(kTime1, session_tab.timestamp);
ASSERT_EQ(3u, session_tab.navigations.size());
EXPECT_EQ(entry1->GetVirtualURL(),
session_tab.navigations[0].virtual_url());
EXPECT_EQ(entry2->GetVirtualURL(),
session_tab.navigations[1].virtual_url());
EXPECT_EQ(entry3->GetVirtualURL(),
session_tab.navigations[2].virtual_url());
EXPECT_EQ(2, session_tab.current_navigation_index);
EXPECT_TRUE(session_tab.navigations[0].timestamp().is_null());
EXPECT_TRUE(session_tab.navigations[1].timestamp().is_null());
EXPECT_EQ(kTime2, session_tab.navigations[2].timestamp());
EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
}
// Ensure we preserve old timestamps when the entries don't change.
TEST_F(SyncSessionModelAssociatorTest, UpdateExistingTab) {
// Create a tab with three valid entries.
NiceMock<SyncedTabDelegateMock> tab_mock;
EXPECT_CALL(tab_mock, GetSessionId()).WillRepeatedly(Return(0));
scoped_ptr<content::NavigationEntry> entry1(
content::NavigationEntry::Create());
entry1->SetVirtualURL(GURL("http://www.google.com"));
scoped_ptr<content::NavigationEntry> entry2(
content::NavigationEntry::Create());
entry2->SetVirtualURL(GURL("http://www.noodle.com"));
scoped_ptr<content::NavigationEntry> entry3(
content::NavigationEntry::Create());
entry3->SetVirtualURL(GURL("http://www.doodle.com"));
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(2));
EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
Return(entry1.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(1)).WillRepeatedly(
Return(entry2.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(2)).WillRepeatedly(
Return(entry3.get()));
EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(3));
EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
EXPECT_CALL(tab_mock, IsPinned()).WillRepeatedly(Return(true));
// The initial UpdateSessionTabFromDelegate call builds the session_tab.
SessionTab session_tab;
UpdateSessionTabFromDelegate(tab_mock, kTime1, kTime2, &session_tab);
// Tweak the timestamps a bit.
ASSERT_EQ(3u, session_tab.navigations.size());
SessionTypesTestHelper::SetTimestamp(&session_tab.navigations[0], kTime3);
SessionTypesTestHelper::SetTimestamp(&session_tab.navigations[1], kTime4);
SessionTypesTestHelper::SetTimestamp(&session_tab.navigations[2], kTime5);
// Now re-associate with the same data.
UpdateSessionTabFromDelegate(tab_mock, kTime3, kTime4, &session_tab);
EXPECT_TRUE(session_tab.pinned);
EXPECT_EQ(kTime3, session_tab.timestamp);
EXPECT_EQ(entry1->GetVirtualURL(),
session_tab.navigations[0].virtual_url());
EXPECT_EQ(entry2->GetVirtualURL(),
session_tab.navigations[1].virtual_url());
EXPECT_EQ(entry3->GetVirtualURL(),
session_tab.navigations[2].virtual_url());
EXPECT_EQ(2, session_tab.current_navigation_index);
EXPECT_EQ(kTime3, session_tab.navigations[0].timestamp());
EXPECT_EQ(kTime4, session_tab.navigations[1].timestamp());
EXPECT_EQ(kTime5, session_tab.navigations[2].timestamp());
}
// Ensure we add a fresh timestamp for new entries appended to the end.
TEST_F(SyncSessionModelAssociatorTest, UpdateAppendedTab) {
// Create a tab with three valid entries.
NiceMock<SyncedTabDelegateMock> tab_mock;
EXPECT_CALL(tab_mock, GetSessionId()).WillRepeatedly(Return(0));
scoped_ptr<content::NavigationEntry> entry1(
content::NavigationEntry::Create());
entry1->SetVirtualURL(GURL("http://www.google.com"));
scoped_ptr<content::NavigationEntry> entry2(
content::NavigationEntry::Create());
entry2->SetVirtualURL(GURL("http://www.noodle.com"));
scoped_ptr<content::NavigationEntry> entry3(
content::NavigationEntry::Create());
entry3->SetVirtualURL(GURL("http://www.doodle.com"));
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(2));
EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
Return(entry1.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(1)).WillRepeatedly(
Return(entry2.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(2)).WillRepeatedly(
Return(entry3.get()));
EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(3));
EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
// The initial UpdateSessionTabFromDelegate call builds the session_tab.
SessionTab session_tab;
UpdateSessionTabFromDelegate(tab_mock, kTime1, kTime2, &session_tab);
// Add a new entry and change the current navigation index.
scoped_ptr<content::NavigationEntry> entry4(
content::NavigationEntry::Create());
entry4->SetVirtualURL(GURL("http://www.poodle.com"));
EXPECT_CALL(tab_mock, GetEntryAtIndex(3)).WillRepeatedly(
Return(entry4.get()));
EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(4));
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(3));
// Now re-associate with the new version.
UpdateSessionTabFromDelegate(tab_mock, kTime3, kTime4, &session_tab);
ASSERT_EQ(4u, session_tab.navigations.size());
EXPECT_EQ(entry1->GetVirtualURL(),
session_tab.navigations[0].virtual_url());
EXPECT_EQ(entry2->GetVirtualURL(),
session_tab.navigations[1].virtual_url());
EXPECT_EQ(entry3->GetVirtualURL(),
session_tab.navigations[2].virtual_url());
EXPECT_EQ(entry4->GetVirtualURL(),
session_tab.navigations[3].virtual_url());
EXPECT_EQ(3, session_tab.current_navigation_index);
EXPECT_TRUE(session_tab.navigations[0].timestamp().is_null());
EXPECT_TRUE(session_tab.navigations[1].timestamp().is_null());
EXPECT_EQ(kTime2, session_tab.navigations[2].timestamp());
EXPECT_EQ(kTime4, session_tab.navigations[3].timestamp());
}
// We shouldn't get confused when old/new entries from the previous tab have
// been pruned in the new tab. Timestamps for old entries we move back to in the
// navigation stack should be refreshed.
TEST_F(SyncSessionModelAssociatorTest, UpdatePrunedTab) {
// Create a tab with four valid entries.
NiceMock<SyncedTabDelegateMock> tab_mock;
EXPECT_CALL(tab_mock, GetSessionId()).WillRepeatedly(Return(0));
scoped_ptr<content::NavigationEntry> entry1(
content::NavigationEntry::Create());
entry1->SetVirtualURL(GURL("http://www.google.com"));
scoped_ptr<content::NavigationEntry> entry2(
content::NavigationEntry::Create());
entry2->SetVirtualURL(GURL("http://www.noodle.com"));
scoped_ptr<content::NavigationEntry> entry3(
content::NavigationEntry::Create());
entry3->SetVirtualURL(GURL("http://www.doodle.com"));
scoped_ptr<content::NavigationEntry> entry4(
content::NavigationEntry::Create());
entry4->SetVirtualURL(GURL("http://www.poodle.com"));
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(3));
EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
Return(entry1.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(1)).WillRepeatedly(
Return(entry2.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(2)).WillRepeatedly(
Return(entry3.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(3)).WillRepeatedly(
Return(entry4.get()));
EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(4));
EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
// The initial UpdateSessionTabFromDelegate call builds the session_tab.
SessionTab session_tab;
UpdateSessionTabFromDelegate(tab_mock, kTime1, kTime2, &session_tab);
// Reset new tab to have the oldest entry pruned, the current navigation
// set to entry3, and a new entry added in place of entry4.
testing::Mock::VerifyAndClearExpectations(&tab_mock);
EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(1));
EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
Return(entry2.get()));
EXPECT_CALL(tab_mock, GetEntryAtIndex(1)).WillRepeatedly(
Return(entry3.get()));
scoped_ptr<content::NavigationEntry> entry5(
content::NavigationEntry::Create());
entry5->SetVirtualURL(GURL("http://www.noogle.com"));
EXPECT_CALL(tab_mock, GetEntryAtIndex(2)).WillRepeatedly(
Return(entry5.get()));
EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(3));
EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
// Tweak the timestamps a bit.
ASSERT_EQ(4u, session_tab.navigations.size());
SessionTypesTestHelper::SetTimestamp(&session_tab.navigations[0], kTime3);
SessionTypesTestHelper::SetTimestamp(&session_tab.navigations[1], kTime4);
SessionTypesTestHelper::SetTimestamp(&session_tab.navigations[2], kTime5);
SessionTypesTestHelper::SetTimestamp(&session_tab.navigations[3], kTime6);
// Now re-associate with the new version.
UpdateSessionTabFromDelegate(tab_mock, kTime7, kTime8, &session_tab);
// Only entry2's and entry3's timestamps should be preserved. The new
// entry should have a new timestamp.
ASSERT_EQ(3u, session_tab.navigations.size());
EXPECT_EQ(entry2->GetVirtualURL(),
session_tab.navigations[0].virtual_url());
EXPECT_EQ(entry3->GetVirtualURL(),
session_tab.navigations[1].virtual_url());
EXPECT_EQ(entry5->GetVirtualURL(),
session_tab.navigations[2].virtual_url());
EXPECT_EQ(1, session_tab.current_navigation_index);
EXPECT_EQ(kTime4, session_tab.navigations[0].timestamp());
EXPECT_EQ(kTime5, session_tab.navigations[1].timestamp());
EXPECT_EQ(kTime8, session_tab.navigations[2].timestamp());
}
} // namespace
} // namespace browser_sync