| // 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)); |
| } |