blob: 89447adfda836c3cb5763e3a478d17d64ac961a6 [file] [log] [blame]
// Copyright 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 "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h"
#include <memory>
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/sessions/chrome_tab_restore_service_client.h"
#include "chrome/browser/sessions/session_service.h"
#include "chrome/browser/sessions/session_service_factory.h"
#include "chrome/browser/sessions/tab_restore_service_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/sync/profile_sync_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/sync/browser_synced_window_delegates_getter.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/menu_model_test.h"
#include "chrome/test/base/testing_profile.h"
#include "components/browser_sync/profile_sync_service_mock.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/sessions/core/persistent_tab_restore_service.h"
#include "components/sessions/core/serialized_navigation_entry_test_helper.h"
#include "components/sessions/core/session_types.h"
#include "components/sync/base/sync_prefs.h"
#include "components/sync/device_info/local_device_info_provider_mock.h"
#include "components/sync/driver/sync_client.h"
#include "components/sync/model/fake_sync_change_processor.h"
#include "components/sync/model/sync_error_factory_mock.h"
#include "components/sync_sessions/sessions_sync_manager.h"
#include "components/sync_sessions/synced_session.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::Invoke;
using testing::Return;
namespace {
// This copies parts of MenuModelTest::Delegate and combines them with the
// RecentTabsSubMenuModel since RecentTabsSubMenuModel is a
// SimpleMenuModel::Delegate and not just derived from SimpleMenuModel.
class TestRecentTabsSubMenuModel : public RecentTabsSubMenuModel {
public:
TestRecentTabsSubMenuModel(ui::AcceleratorProvider* provider,
Browser* browser)
: RecentTabsSubMenuModel(provider, browser),
execute_count_(0),
enable_count_(0) {}
// Testing overrides to ui::SimpleMenuModel::Delegate:
bool IsCommandIdEnabled(int command_id) const override {
bool val = RecentTabsSubMenuModel::IsCommandIdEnabled(command_id);
if (val)
++enable_count_;
return val;
}
void ExecuteCommand(int command_id, int event_flags) override {
++execute_count_;
}
int execute_count() const { return execute_count_; }
int enable_count() const { return enable_count_; }
private:
int execute_count_;
int mutable enable_count_; // Mutable because IsCommandIdEnabledAt is const.
DISALLOW_COPY_AND_ASSIGN(TestRecentTabsSubMenuModel);
};
class TestRecentTabsMenuModelDelegate : public ui::MenuModelDelegate {
public:
explicit TestRecentTabsMenuModelDelegate(ui::MenuModel* model)
: model_(model),
got_changes_(false) {
model_->SetMenuModelDelegate(this);
}
~TestRecentTabsMenuModelDelegate() override {
model_->SetMenuModelDelegate(NULL);
}
// ui::MenuModelDelegate implementation:
void OnIconChanged(int index) override {}
void OnMenuStructureChanged() override { got_changes_ = true; }
bool got_changes() const { return got_changes_; }
private:
ui::MenuModel* model_;
bool got_changes_;
DISALLOW_COPY_AND_ASSIGN(TestRecentTabsMenuModelDelegate);
};
class DummyRouter : public sync_sessions::LocalSessionEventRouter {
public:
~DummyRouter() override {}
void StartRoutingTo(
sync_sessions::LocalSessionEventHandler* handler) override {}
void Stop() override {}
};
class FakeSyncServiceObserverList {
public:
FakeSyncServiceObserverList() {}
~FakeSyncServiceObserverList() {}
void AddObserver(syncer::SyncServiceObserver* observer) {
observers_.AddObserver(observer);
}
void RemoveObserver(syncer::SyncServiceObserver* observer) {
observers_.RemoveObserver(observer);
}
void NotifyConfigureDone() {
for (auto& observer : observers_)
observer.OnSyncConfigurationCompleted(nullptr);
}
void NotifyForeignSessionUpdated() {
for (auto& observer : observers_)
observer.OnForeignSessionUpdated(nullptr);
}
private:
base::ObserverList<syncer::SyncServiceObserver, true> observers_;
DISALLOW_COPY_AND_ASSIGN(FakeSyncServiceObserverList);
};
} // namespace
class RecentTabsSubMenuModelTest
: public BrowserWithTestWindowTest {
public:
RecentTabsSubMenuModelTest() {}
void SetUp() override {
// Set up our mock sync service factory before the sync service (and any
// other services that depend on it) gets created.
will_create_browser_context_services_subscription_ =
BrowserContextDependencyManager::GetInstance()
->RegisterWillCreateBrowserContextServicesCallbackForTesting(
base::Bind(OnWillCreateBrowserContextServices));
BrowserWithTestWindowTest::SetUp();
local_device_ = base::MakeUnique<syncer::LocalDeviceInfoProviderMock>(
"RecentTabsSubMenuModelTest", "Test Machine", "Chromium 10k",
"Chrome 10k", sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id");
sync_prefs_ = base::MakeUnique<syncer::SyncPrefs>(profile()->GetPrefs());
mock_sync_service_ = static_cast<browser_sync::ProfileSyncServiceMock*>(
ProfileSyncServiceFactory::GetForProfile(profile()));
EXPECT_CALL(*mock_sync_service_, AddObserver(_))
.WillRepeatedly(Invoke(&fake_sync_service_observer_list_,
&FakeSyncServiceObserverList::AddObserver));
EXPECT_CALL(*mock_sync_service_, RemoveObserver(_))
.WillRepeatedly(Invoke(&fake_sync_service_observer_list_,
&FakeSyncServiceObserverList::RemoveObserver));
manager_ = base::MakeUnique<sync_sessions::SessionsSyncManager>(
mock_sync_service_->GetSyncClient()->GetSyncSessionsClient(),
sync_prefs_.get(), local_device_.get(), &dummy_router_,
base::Bind(&FakeSyncServiceObserverList::NotifyForeignSessionUpdated,
base::Unretained(&fake_sync_service_observer_list_)),
base::Closure());
manager_->MergeDataAndStartSyncing(
syncer::SESSIONS, syncer::SyncDataList(),
std::unique_ptr<syncer::SyncChangeProcessor>(
new syncer::FakeSyncChangeProcessor),
std::unique_ptr<syncer::SyncErrorFactory>(
new syncer::SyncErrorFactoryMock));
}
void TearDown() override {
manager_.reset();
sync_prefs_.reset();
local_device_.reset();
BrowserWithTestWindowTest::TearDown();
}
void WaitForLoadFromLastSession() { content::RunAllTasksUntilIdle(); }
void DisableSync() {
EXPECT_CALL(*mock_sync_service_, IsSyncActive())
.WillRepeatedly(Return(false));
EXPECT_CALL(*mock_sync_service_, IsDataTypeControllerRunning(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(*mock_sync_service_, GetOpenTabsUIDelegateMock())
.WillRepeatedly(Return(nullptr));
}
void EnableSync() {
EXPECT_CALL(*mock_sync_service_, IsSyncActive())
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_sync_service_,
IsDataTypeControllerRunning(syncer::SESSIONS))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_sync_service_,
IsDataTypeControllerRunning(syncer::PROXY_TABS))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_sync_service_, GetOpenTabsUIDelegateMock())
.WillRepeatedly(Return(manager_.get()));
}
void NotifySyncEnabled() {
fake_sync_service_observer_list_.NotifyConfigureDone();
}
static std::unique_ptr<KeyedService> GetTabRestoreService(
content::BrowserContext* browser_context) {
return base::MakeUnique<sessions::PersistentTabRestoreService>(
base::WrapUnique(new ChromeTabRestoreServiceClient(
Profile::FromBrowserContext(browser_context))),
nullptr);
}
void RegisterRecentTabs(RecentTabsBuilderTestHelper* helper) {
helper->ExportToSessionsSyncManager(manager_.get());
}
private:
static void OnWillCreateBrowserContextServices(
content::BrowserContext* context) {
ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(
context, BuildMockProfileSyncService);
}
std::unique_ptr<
base::CallbackList<void(content::BrowserContext*)>::Subscription>
will_create_browser_context_services_subscription_;
std::unique_ptr<syncer::LocalDeviceInfoProviderMock> local_device_;
DummyRouter dummy_router_;
std::unique_ptr<syncer::SyncPrefs> sync_prefs_;
FakeSyncServiceObserverList fake_sync_service_observer_list_;
browser_sync::ProfileSyncServiceMock* mock_sync_service_ = nullptr;
std::unique_ptr<sync_sessions::SessionsSyncManager> manager_;
};
// Test disabled "Recently closed" header with no foreign tabs.
TEST_F(RecentTabsSubMenuModelTest, NoTabs) {
DisableSync();
TestRecentTabsSubMenuModel model(nullptr, browser());
// Expected menu:
// Menu index Menu items
// ---------------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed header (disabled)
// 3 <separator>
// 4 No tabs from other Devices
int num_items = model.GetItemCount();
EXPECT_EQ(5, num_items);
EXPECT_FALSE(model.IsEnabledAt(2));
EXPECT_FALSE(model.IsEnabledAt(4));
EXPECT_EQ(0, model.enable_count());
EXPECT_EQ(NULL, model.GetLabelFontListAt(0));
EXPECT_EQ(NULL, model.GetLabelFontListAt(1));
EXPECT_EQ(NULL, model.GetLabelFontListAt(2));
EXPECT_EQ(NULL, model.GetLabelFontListAt(3));
EXPECT_EQ(NULL, model.GetLabelFontListAt(4));
std::string url;
base::string16 title;
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(0, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(1, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(2, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(3, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(4, &url, &title));
}
// Test enabled "Recently closed" header with no foreign tabs.
TEST_F(RecentTabsSubMenuModelTest, RecentlyClosedTabsFromCurrentSession) {
DisableSync();
TabRestoreServiceFactory::GetInstance()->SetTestingFactory(
profile(), RecentTabsSubMenuModelTest::GetTabRestoreService);
// Add 2 tabs and close them.
AddTab(browser(), GURL("http://foo/1"));
AddTab(browser(), GURL("http://foo/2"));
browser()->tab_strip_model()->CloseAllTabs();
TestRecentTabsSubMenuModel model(nullptr, browser());
// Expected menu:
// Menu index Menu items
// --------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed header
// 3 <tab for http://foo/2>
// 4 <tab for http://foo/1>
// 5 <separator>
// 6 No tabs from other Devices
int num_items = model.GetItemCount();
EXPECT_EQ(7, num_items);
EXPECT_TRUE(model.IsEnabledAt(0));
model.ActivatedAt(0);
EXPECT_TRUE(model.IsEnabledAt(1));
EXPECT_FALSE(model.IsEnabledAt(2));
EXPECT_TRUE(model.IsEnabledAt(3));
EXPECT_TRUE(model.IsEnabledAt(4));
model.ActivatedAt(3);
model.ActivatedAt(4);
EXPECT_FALSE(model.IsEnabledAt(6));
EXPECT_EQ(3, model.enable_count());
EXPECT_EQ(3, model.execute_count());
EXPECT_EQ(NULL, model.GetLabelFontListAt(0));
EXPECT_EQ(NULL, model.GetLabelFontListAt(1));
EXPECT_TRUE(model.GetLabelFontListAt(2) != nullptr);
EXPECT_EQ(NULL, model.GetLabelFontListAt(3));
EXPECT_EQ(NULL, model.GetLabelFontListAt(4));
EXPECT_EQ(NULL, model.GetLabelFontListAt(5));
EXPECT_EQ(NULL, model.GetLabelFontListAt(6));
std::string url;
base::string16 title;
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(0, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(1, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(2, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(3, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(4, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(5, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(6, &url, &title));
}
// TODO(sail): enable this test when dynamic model is enabled in
// RecentTabsSubMenuModel.
#if defined(OS_MACOSX)
#define MAYBE_RecentlyClosedTabsAndWindowsFromLastSession \
DISABLED_RecentlyClosedTabsAndWindowsFromLastSession
#else
#define MAYBE_RecentlyClosedTabsAndWindowsFromLastSession \
RecentlyClosedTabsAndWindowsFromLastSession
#endif
TEST_F(RecentTabsSubMenuModelTest,
MAYBE_RecentlyClosedTabsAndWindowsFromLastSession) {
DisableSync();
TabRestoreServiceFactory::GetInstance()->SetTestingFactory(
profile(), RecentTabsSubMenuModelTest::GetTabRestoreService);
// Add 2 tabs and close them.
AddTab(browser(), GURL("http://wnd/tab0"));
AddTab(browser(), GURL("http://wnd/tab1"));
browser()->tab_strip_model()->CloseAllTabs();
// Create a SessionService for the profile (profile owns the service) and add
// a window with a tab to this session.
SessionService* session_service = new SessionService(profile());
SessionServiceFactory::SetForTestProfile(profile(),
base::WrapUnique(session_service));
SessionID tab_id;
SessionID window_id;
session_service->SetWindowType(window_id,
Browser::TYPE_TABBED,
SessionService::TYPE_NORMAL);
session_service->SetTabWindow(window_id, tab_id);
session_service->SetTabIndexInWindow(window_id, tab_id, 0);
session_service->SetSelectedTabInWindow(window_id, 0);
session_service->UpdateTabNavigation(
window_id, tab_id,
sessions::SerializedNavigationEntryTestHelper::CreateNavigation(
"http://wnd1/tab0", "title"));
// Set this, otherwise previous session won't be loaded.
profile()->set_last_session_exited_cleanly(false);
// Move this session to the last so that TabRestoreService will load it as the
// last session.
SessionServiceFactory::GetForProfile(profile())->
MoveCurrentSessionToLastSession();
// Create a new TabRestoreService so that it'll load the recently closed tabs
// and windows afresh.
TabRestoreServiceFactory::GetInstance()->SetTestingFactory(
profile(), RecentTabsSubMenuModelTest::GetTabRestoreService);
// Let the shutdown of previous TabRestoreService run.
content::RunAllTasksUntilIdle();
TestRecentTabsSubMenuModel model(nullptr, browser());
TestRecentTabsMenuModelDelegate delegate(&model);
EXPECT_FALSE(delegate.got_changes());
// Expected menu before tabs/windows from last session are loaded:
// Menu index Menu items
// ----------------------------------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed header
// 3 <separator>
// 4 No tabs from other Devices
int num_items = model.GetItemCount();
EXPECT_EQ(5, num_items);
EXPECT_TRUE(model.IsEnabledAt(0));
EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model.GetTypeAt(1));
EXPECT_FALSE(model.IsEnabledAt(2));
EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model.GetTypeAt(3));
EXPECT_FALSE(model.IsEnabledAt(4));
EXPECT_EQ(1, model.enable_count());
// Wait for tabs from last session to be loaded.
WaitForLoadFromLastSession();
// Expected menu after tabs/windows from last session are loaded:
// Menu index Menu items
// --------------------------------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed header
// 3 <window for the tab http://wnd1/tab0>
// 4 <tab for http://wnd0/tab1>
// 5 <tab for http://wnd0/tab0>
// 6 <separator>
// 7 No tabs from other Devices
EXPECT_TRUE(delegate.got_changes());
num_items = model.GetItemCount();
EXPECT_EQ(8, num_items);
EXPECT_TRUE(model.IsEnabledAt(0));
model.ActivatedAt(0);
EXPECT_TRUE(model.IsEnabledAt(1));
EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model.GetTypeAt(1));
EXPECT_FALSE(model.IsEnabledAt(2));
EXPECT_TRUE(model.IsEnabledAt(3));
EXPECT_TRUE(model.IsEnabledAt(4));
EXPECT_TRUE(model.IsEnabledAt(5));
model.ActivatedAt(3);
model.ActivatedAt(4);
model.ActivatedAt(5);
EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model.GetTypeAt(6));
EXPECT_FALSE(model.IsEnabledAt(7));
EXPECT_EQ(5, model.enable_count());
EXPECT_EQ(4, model.execute_count());
EXPECT_EQ(NULL, model.GetLabelFontListAt(0));
EXPECT_EQ(NULL, model.GetLabelFontListAt(1));
EXPECT_TRUE(model.GetLabelFontListAt(2) != nullptr);
EXPECT_EQ(NULL, model.GetLabelFontListAt(3));
EXPECT_EQ(NULL, model.GetLabelFontListAt(4));
EXPECT_EQ(NULL, model.GetLabelFontListAt(5));
EXPECT_EQ(NULL, model.GetLabelFontListAt(6));
EXPECT_EQ(NULL, model.GetLabelFontListAt(7));
std::string url;
base::string16 title;
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(0, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(1, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(2, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(3, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(4, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(5, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(6, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(7, &url, &title));
}
// Test disabled "Recently closed" header with multiple sessions, multiple
// windows, and multiple enabled tabs from other devices.
TEST_F(RecentTabsSubMenuModelTest, OtherDevices) {
EnableSync();
// Tabs are populated in decreasing timestamp.
base::Time timestamp = base::Time::Now();
const base::TimeDelta time_delta = base::TimeDelta::FromMinutes(10);
RecentTabsBuilderTestHelper recent_tabs_builder;
// Create 1st session : 1 window, 3 tabs
recent_tabs_builder.AddSession();
recent_tabs_builder.AddWindow(0);
for (int i = 0; i < 3; ++i) {
timestamp -= time_delta;
recent_tabs_builder.AddTabWithInfo(0, 0, timestamp, base::string16());
}
// Create 2nd session : 2 windows, 1 tab in 1st window, 2 tabs in 2nd window
recent_tabs_builder.AddSession();
recent_tabs_builder.AddWindow(1);
recent_tabs_builder.AddWindow(1);
timestamp -= time_delta;
recent_tabs_builder.AddTabWithInfo(1, 0, timestamp, base::string16());
timestamp -= time_delta;
recent_tabs_builder.AddTabWithInfo(1, 1, timestamp, base::string16());
timestamp -= time_delta;
recent_tabs_builder.AddTabWithInfo(1, 1, timestamp, base::string16());
RegisterRecentTabs(&recent_tabs_builder);
// Verify that data is populated correctly in RecentTabsSubMenuModel.
// Expected menu:
// - first inserted tab is most recent and hence is top
// Menu index Menu items
// -----------------------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed header (disabled)
// 3 <separator>
// 4 <section header for 1st session>
// 5-7 <3 tabs of the only window of session 0>
// 8 <separator>
// 9 <section header for 2nd session>
// 10 <the only tab of window 0 of session 1>
// 11-12 <2 tabs of window 1 of session 2>
TestRecentTabsSubMenuModel model(nullptr, browser());
int num_items = model.GetItemCount();
EXPECT_EQ(13, num_items);
model.ActivatedAt(0);
EXPECT_TRUE(model.IsEnabledAt(0));
model.ActivatedAt(1);
EXPECT_TRUE(model.IsEnabledAt(1));
model.ActivatedAt(2);
EXPECT_FALSE(model.IsEnabledAt(2));
model.ActivatedAt(3);
EXPECT_TRUE(model.IsEnabledAt(3));
model.ActivatedAt(5);
EXPECT_TRUE(model.IsEnabledAt(5));
model.ActivatedAt(6);
EXPECT_TRUE(model.IsEnabledAt(6));
model.ActivatedAt(7);
EXPECT_TRUE(model.IsEnabledAt(7));
model.ActivatedAt(10);
EXPECT_TRUE(model.IsEnabledAt(10));
model.ActivatedAt(11);
EXPECT_TRUE(model.IsEnabledAt(11));
model.ActivatedAt(12);
EXPECT_TRUE(model.IsEnabledAt(12));
EXPECT_EQ(7, model.enable_count());
EXPECT_EQ(10, model.execute_count());
EXPECT_EQ(NULL, model.GetLabelFontListAt(0));
EXPECT_EQ(NULL, model.GetLabelFontListAt(1));
EXPECT_EQ(NULL, model.GetLabelFontListAt(2));
EXPECT_EQ(NULL, model.GetLabelFontListAt(3));
EXPECT_TRUE(model.GetLabelFontListAt(4) != nullptr);
EXPECT_EQ(NULL, model.GetLabelFontListAt(5));
EXPECT_EQ(NULL, model.GetLabelFontListAt(6));
EXPECT_EQ(NULL, model.GetLabelFontListAt(7));
EXPECT_EQ(NULL, model.GetLabelFontListAt(8));
EXPECT_TRUE(model.GetLabelFontListAt(9) != nullptr);
EXPECT_EQ(NULL, model.GetLabelFontListAt(10));
EXPECT_EQ(NULL, model.GetLabelFontListAt(11));
EXPECT_EQ(NULL, model.GetLabelFontListAt(12));
std::string url;
base::string16 title;
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(0, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(1, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(2, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(3, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(4, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(5, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(6, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(7, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(8, &url, &title));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(9, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(10, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(11, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(12, &url, &title));
}
// Mac doesn't support the dynamic menu.
#if defined(OS_MACOSX)
#define MAYBE_OtherDevicesDynamicUpdate DISABLED_OtherDevicesDynamicUpdate
#else
#define MAYBE_OtherDevicesDynamicUpdate OtherDevicesDynamicUpdate
#endif
TEST_F(RecentTabsSubMenuModelTest, MAYBE_OtherDevicesDynamicUpdate) {
// Create menu with disabled synchronization.
DisableSync();
// Before creating menu fill foreign sessions.
base::Time update_timestamp =
base::Time::Now() - base::TimeDelta::FromMinutes(10);
RecentTabsBuilderTestHelper recent_tabs_builder;
// Create one session with one window and one tab.
recent_tabs_builder.AddSession();
recent_tabs_builder.AddWindow(0);
recent_tabs_builder.AddTabWithInfo(0, 0, update_timestamp, base::string16());
RegisterRecentTabs(&recent_tabs_builder);
// Verify that data is populated correctly in RecentTabsSubMenuModel.
// Expected menu:
// Menu index Menu items
// -----------------------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed header (disabled)
// 3 <separator>
// 4 No tabs from other Devices
TestRecentTabsSubMenuModel model(nullptr, browser());
EXPECT_EQ(5, model.GetItemCount());
model.ActivatedAt(4);
EXPECT_FALSE(model.IsEnabledAt(4));
EXPECT_EQ(0, model.enable_count());
EXPECT_EQ(1, model.execute_count());
EXPECT_EQ(nullptr, model.GetLabelFontListAt(4));
std::string url;
base::string16 title;
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(4, &url, &title));
// Enable synchronization and notify menu that synchronization was enabled.
int previous_enable_count = model.enable_count();
int previous_execute_count = model.execute_count();
EnableSync();
NotifySyncEnabled();
// Verify that data is populated correctly in RecentTabsSubMenuModel.
// Expected menu:
// Menu index Menu items
// -----------------------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed header (disabled)
// 3 <separator>
// 4 <section header for 1st session>
// 5 <tab of the only window of session 0>
EXPECT_EQ(6, model.GetItemCount());
model.ActivatedAt(4);
EXPECT_FALSE(model.IsEnabledAt(4));
model.ActivatedAt(5);
EXPECT_TRUE(model.IsEnabledAt(5));
EXPECT_EQ(previous_enable_count + 1, model.enable_count());
EXPECT_EQ(previous_execute_count + 2, model.execute_count());
EXPECT_NE(nullptr, model.GetLabelFontListAt(4));
EXPECT_EQ(nullptr, model.GetLabelFontListAt(5));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(4, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(5, &url, &title));
// Make changes dynamically.
previous_enable_count = model.enable_count();
previous_execute_count = model.execute_count();
update_timestamp = base::Time::Now() - base::TimeDelta::FromMinutes(5);
// Add tab to the only window.
recent_tabs_builder.AddTabWithInfo(0, 0, update_timestamp, base::string16());
RegisterRecentTabs(&recent_tabs_builder);
// Verify that data is populated correctly in RecentTabsSubMenuModel.
// Expected menu:
// Menu index Menu items
// -----------------------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed header (disabled)
// 3 <separator>
// 4 <section header for 1st session>
// 5 <new added tab of the only window of session 0>
// 6 <tab of the only window of session 0>
EXPECT_EQ(7, model.GetItemCount());
model.ActivatedAt(4);
EXPECT_FALSE(model.IsEnabledAt(4));
model.ActivatedAt(5);
EXPECT_TRUE(model.IsEnabledAt(5));
model.ActivatedAt(6);
EXPECT_TRUE(model.IsEnabledAt(6));
EXPECT_EQ(previous_enable_count + 2, model.enable_count());
EXPECT_EQ(previous_execute_count + 3, model.execute_count());
EXPECT_NE(nullptr, model.GetLabelFontListAt(4));
EXPECT_EQ(nullptr, model.GetLabelFontListAt(5));
EXPECT_EQ(nullptr, model.GetLabelFontListAt(6));
EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(4, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(5, &url, &title));
EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(6, &url, &title));
}
TEST_F(RecentTabsSubMenuModelTest, MaxSessionsAndRecency) {
EnableSync();
// Create 4 sessions : each session has 1 window with 1 tab each.
RecentTabsBuilderTestHelper recent_tabs_builder;
for (int s = 0; s < 4; ++s) {
recent_tabs_builder.AddSession();
recent_tabs_builder.AddWindow(s);
recent_tabs_builder.AddTab(s, 0);
}
RegisterRecentTabs(&recent_tabs_builder);
// Verify that data is populated correctly in RecentTabsSubMenuModel.
// Expected menu:
// - max sessions is 3, so only 3 most-recent sessions will show.
// Menu index Menu items
// ----------------------------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed header (disabled)
// 3 <separator>
// 4 <section header for 1st session>
// 5 <the only tab of the only window of session 3>
// 6 <separator>
// 7 <section header for 2nd session>
// 8 <the only tab of the only window of session 2>
// 9 <separator>
// 10 <section header for 3rd session>
// 11 <the only tab of the only window of session 1>
TestRecentTabsSubMenuModel model(nullptr, browser());
int num_items = model.GetItemCount();
EXPECT_EQ(12, num_items);
std::vector<base::string16> tab_titles =
recent_tabs_builder.GetTabTitlesSortedByRecency();
EXPECT_EQ(tab_titles[0], model.GetLabelAt(5));
EXPECT_EQ(tab_titles[1], model.GetLabelAt(8));
EXPECT_EQ(tab_titles[2], model.GetLabelAt(11));
}
TEST_F(RecentTabsSubMenuModelTest, MaxTabsPerSessionAndRecency) {
EnableSync();
// Create a session: 2 windows with 5 tabs each.
RecentTabsBuilderTestHelper recent_tabs_builder;
recent_tabs_builder.AddSession();
for (int w = 0; w < 2; ++w) {
recent_tabs_builder.AddWindow(0);
for (int t = 0; t < 5; ++t)
recent_tabs_builder.AddTab(0, w);
}
RegisterRecentTabs(&recent_tabs_builder);
// Verify that data is populated correctly in RecentTabsSubMenuModel.
// Expected menu:
// - max tabs per session is 4, so only 4 most-recent tabs will show,
// independent of which window they came from.
// Menu index Menu items
// ---------------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed header (disabled)
// 3 <separator>
// 4 <section header for session>
// 5-8 <4 most-recent tabs of session>
TestRecentTabsSubMenuModel model(nullptr, browser());
int num_items = model.GetItemCount();
EXPECT_EQ(9, num_items);
std::vector<base::string16> tab_titles =
recent_tabs_builder.GetTabTitlesSortedByRecency();
for (int i = 0; i < 4; ++i)
EXPECT_EQ(tab_titles[i], model.GetLabelAt(i + 5));
}
TEST_F(RecentTabsSubMenuModelTest, MaxWidth) {
EnableSync();
// Create 1 session with 1 window and 1 tab.
RecentTabsBuilderTestHelper recent_tabs_builder;
recent_tabs_builder.AddSession();
recent_tabs_builder.AddWindow(0);
recent_tabs_builder.AddTab(0, 0);
RegisterRecentTabs(&recent_tabs_builder);
// Menu index Menu items
// ----------------------------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed header (disabled)
// 3 <separator>
// 4 <section header for 1st session>
// 5 <the only tab of the only window of session 1>
TestRecentTabsSubMenuModel model(nullptr, browser());
EXPECT_EQ(6, model.GetItemCount());
EXPECT_EQ(-1, model.GetMaxWidthForItemAtIndex(2));
EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(3));
EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(4));
EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(5));
}
TEST_F(RecentTabsSubMenuModelTest, MaxWidthNoDevices) {
DisableSync();
// Expected menu:
// Menu index Menu items
// --------------------------------------------
// 0 History
// 1 <separator>
// 2 Recently closed heaer (disabled)
// 3 <separator>
// 4 No tabs from other Devices
TestRecentTabsSubMenuModel model(nullptr, browser());
EXPECT_EQ(5, model.GetItemCount());
EXPECT_EQ(-1, model.GetMaxWidthForItemAtIndex(2));
EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(3));
EXPECT_EQ(-1, model.GetMaxWidthForItemAtIndex(4));
}