1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h" 6 7#include "chrome/app/chrome_command_ids.h" 8#include "chrome/browser/sessions/session_types.h" 9#include "chrome/browser/sessions/persistent_tab_restore_service.h" 10#include "chrome/browser/sessions/tab_restore_service_factory.h" 11#include "chrome/browser/sync/glue/session_model_associator.h" 12#include "chrome/browser/sync/glue/synced_session.h" 13#include "chrome/browser/sync/profile_sync_service_mock.h" 14#include "chrome/browser/ui/browser.h" 15#include "chrome/browser/ui/browser_tabstrip.h" 16#include "chrome/browser/ui/tabs/tab_strip_model.h" 17#include "chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h" 18#include "chrome/test/base/browser_with_test_window_test.h" 19#include "chrome/test/base/menu_model_test.h" 20#include "chrome/test/base/testing_profile.h" 21#include "grit/generated_resources.h" 22#include "testing/gmock/include/gmock/gmock.h" 23#include "testing/gtest/include/gtest/gtest.h" 24 25namespace { 26 27// This copies parts of MenuModelTest::Delegate and combines them with the 28// RecentTabsSubMenuModel since RecentTabsSubMenuModel is a 29// SimpleMenuModel::Delegate and not just derived from SimpleMenuModel. 30class TestRecentTabsSubMenuModel : public RecentTabsSubMenuModel { 31 public: 32 TestRecentTabsSubMenuModel(ui::AcceleratorProvider* provider, 33 Browser* browser, 34 browser_sync::SessionModelAssociator* associator) 35 : RecentTabsSubMenuModel(provider, browser, associator), 36 execute_count_(0), 37 enable_count_(0) { 38 } 39 40 // Testing overrides to ui::SimpleMenuModel::Delegate: 41 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE { 42 bool val = RecentTabsSubMenuModel::IsCommandIdEnabled(command_id); 43 if (val) 44 ++enable_count_; 45 return val; 46 } 47 48 virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE { 49 ++execute_count_; 50 } 51 52 int execute_count_; 53 int mutable enable_count_; // Mutable because IsCommandIdEnabledAt is const. 54}; 55 56} // namespace 57 58class RecentTabsSubMenuModelTest : public BrowserWithTestWindowTest { 59 public: 60 RecentTabsSubMenuModelTest() 61 : sync_service_(&testing_profile_), 62 associator_(&sync_service_, true) { 63 associator_.SetCurrentMachineTagForTesting("RecentTabsSubMenuModelTest"); 64 } 65 66 static BrowserContextKeyedService* GetTabRestoreService( 67 content::BrowserContext* browser_context) { 68 // Ownership is tranfered to the profile. 69 return new PersistentTabRestoreService( 70 Profile::FromBrowserContext(browser_context), NULL);; 71 } 72 private: 73 TestingProfile testing_profile_; 74 testing::NiceMock<ProfileSyncServiceMock> sync_service_; 75 76 protected: 77 browser_sync::SessionModelAssociator associator_; 78}; 79 80// Test disabled "Reopen closed tab" with no foreign tabs. 81TEST_F(RecentTabsSubMenuModelTest, NoTabs) { 82 TestRecentTabsSubMenuModel model(NULL, browser(), NULL); 83 84 // Expected menu: 85 // Menu index Menu items 86 // -------------------------------------- 87 // 0 Reopen closed tab 88 // 1 <separator> 89 // 2 No tabs from other Devices 90 91 int num_items = model.GetItemCount(); 92 EXPECT_EQ(3, num_items); 93 EXPECT_FALSE(model.IsEnabledAt(0)); 94 EXPECT_FALSE(model.IsEnabledAt(2)); 95 EXPECT_EQ(0, model.enable_count_); 96} 97 98// Test enabled "Reopen closed tab" with no foreign tabs. 99TEST_F(RecentTabsSubMenuModelTest, ReopenClosedTab) { 100 TabRestoreServiceFactory::GetInstance()->SetTestingFactory( 101 browser()->profile(), RecentTabsSubMenuModelTest::GetTabRestoreService); 102 103 // Add a tab and close it. 104 GURL url1("http://foo/1"); 105 AddTab(browser(), url1); 106 browser()->tab_strip_model()->CloseWebContentsAt( 107 0, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB); 108 109 TestRecentTabsSubMenuModel model(NULL, browser(), NULL); 110 // Expected menu: 111 // Menu index Menu items 112 // -------------------------------------- 113 // 0 Recently Closed Header 114 // 1 Single entry to restore 115 // 2 <separator> 116 // 3 No tabs from other Devices 117 int num_items = model.GetItemCount(); 118 EXPECT_EQ(4, num_items); 119 EXPECT_FALSE(model.IsEnabledAt(0)); 120 EXPECT_TRUE(model.IsEnabledAt(1)); 121 model.ActivatedAt(1); 122 EXPECT_FALSE(model.IsEnabledAt(3)); 123 EXPECT_EQ(1, model.enable_count_); 124 EXPECT_EQ(1, model.execute_count_); 125} 126 127// Test enabled "Reopen closed tab" with multiple sessions, multiple windows, 128// and multiple enabled tabs from other devices. 129TEST_F(RecentTabsSubMenuModelTest, OtherDevices) { 130 // Tabs are populated in decreasing timestamp. 131 base::Time timestamp = base::Time::Now(); 132 const base::TimeDelta time_delta = base::TimeDelta::FromMinutes(10); 133 134 RecentTabsBuilderTestHelper recent_tabs_builder; 135 136 // Create 1st session : 1 window, 3 tabs 137 recent_tabs_builder.AddSession(); 138 recent_tabs_builder.AddWindow(0); 139 for (int i = 0; i < 3; ++i) { 140 timestamp -= time_delta; 141 recent_tabs_builder.AddTabWithInfo(0, 0, timestamp, string16()); 142 } 143 144 // Create 2nd session : 2 windows, 1 tab in 1st window, 2 tabs in 2nd window 145 recent_tabs_builder.AddSession(); 146 recent_tabs_builder.AddWindow(1); 147 recent_tabs_builder.AddWindow(1); 148 timestamp -= time_delta; 149 recent_tabs_builder.AddTabWithInfo(1, 0, timestamp, string16()); 150 timestamp -= time_delta; 151 recent_tabs_builder.AddTabWithInfo(1, 1, timestamp, string16()); 152 timestamp -= time_delta; 153 recent_tabs_builder.AddTabWithInfo(1, 1, timestamp, string16()); 154 155 recent_tabs_builder.RegisterRecentTabs(&associator_); 156 157 // Verify that data is populated correctly in RecentTabsSubMenuModel. 158 // Expected menu: 159 // - first inserted tab is most recent and hence is top 160 // Menu index Menu items 161 // -------------------------------------- 162 // 0 Reopen closed tab 163 // 1 <separator> 164 // 2 <section header for 1st session> 165 // 3-5 <3 tabs of the only window of session 0> 166 // 6 <separator> 167 // 7 <section header for 2nd session> 168 // 8 <the only tab of window 0 of session 1> 169 // 9-10 <2 tabs of window 1 of session 2> 170 // 11 <separator> 171 // 12 More... 172 173 TestRecentTabsSubMenuModel model(NULL, browser(), &associator_); 174 int num_items = model.GetItemCount(); 175 EXPECT_EQ(13, num_items); 176 model.ActivatedAt(0); 177 EXPECT_FALSE(model.IsEnabledAt(0)); 178 model.ActivatedAt(3); 179 EXPECT_TRUE(model.IsEnabledAt(3)); 180 model.ActivatedAt(4); 181 EXPECT_TRUE(model.IsEnabledAt(4)); 182 model.ActivatedAt(5); 183 EXPECT_TRUE(model.IsEnabledAt(5)); 184 model.ActivatedAt(8); 185 EXPECT_TRUE(model.IsEnabledAt(8)); 186 model.ActivatedAt(9); 187 EXPECT_TRUE(model.IsEnabledAt(9)); 188 model.ActivatedAt(10); 189 EXPECT_TRUE(model.IsEnabledAt(10)); 190 EXPECT_TRUE(model.IsEnabledAt(12)); 191 EXPECT_EQ(7, model.enable_count_); 192 EXPECT_EQ(7, model.execute_count_); 193} 194 195TEST_F(RecentTabsSubMenuModelTest, MaxSessionsAndRecency) { 196 // Create 4 sessions : each session has 1 window with 1 tab each. 197 RecentTabsBuilderTestHelper recent_tabs_builder; 198 for (int s = 0; s < 4; ++s) { 199 recent_tabs_builder.AddSession(); 200 recent_tabs_builder.AddWindow(s); 201 recent_tabs_builder.AddTab(s, 0); 202 } 203 recent_tabs_builder.RegisterRecentTabs(&associator_); 204 205 // Verify that data is populated correctly in RecentTabsSubMenuModel. 206 // Expected menu: 207 // - max sessions is 3, so only 3 most-recent sessions will show. 208 // Menu index Menu items 209 // -------------------------------------- 210 // 0 Reopen closed tab 211 // 1 <separator> 212 // 2 <section header for 1st session> 213 // 3 <the only tab of the only window of session 3> 214 // 4 <separator> 215 // 5 <section header for 2nd session> 216 // 6 <the only tab of the only window of session 2> 217 // 7 <separator> 218 // 8 <section header for 3rd session> 219 // 9 <the only tab of the only window of session 1> 220 // 10 <separator> 221 // 11 More... 222 223 TestRecentTabsSubMenuModel model(NULL, browser(), &associator_); 224 int num_items = model.GetItemCount(); 225 EXPECT_EQ(12, num_items); 226 227 std::vector<string16> tab_titles = 228 recent_tabs_builder.GetTabTitlesSortedByRecency(); 229 EXPECT_EQ(tab_titles[0], model.GetLabelAt(3)); 230 EXPECT_EQ(tab_titles[1], model.GetLabelAt(6)); 231 EXPECT_EQ(tab_titles[2], model.GetLabelAt(9)); 232} 233 234TEST_F(RecentTabsSubMenuModelTest, MaxTabsPerSessionAndRecency) { 235 // Create a session: 2 windows with 5 tabs each. 236 RecentTabsBuilderTestHelper recent_tabs_builder; 237 recent_tabs_builder.AddSession(); 238 for (int w = 0; w < 2; ++w) { 239 recent_tabs_builder.AddWindow(0); 240 for (int t = 0; t < 5; ++t) 241 recent_tabs_builder.AddTab(0, w); 242 } 243 recent_tabs_builder.RegisterRecentTabs(&associator_); 244 245 // Verify that data is populated correctly in RecentTabsSubMenuModel. 246 // Expected menu: 247 // - max tabs per session is 4, so only 4 most-recent tabs will show, 248 // independent of which window they came from. 249 // Menu index Menu items 250 // -------------------------------------- 251 // 0 Reopen closed tab 252 // 1 <separator> 253 // 2 <section header for session> 254 // 3-6 <4 most-recent tabs of session> 255 // 7 <separator> 256 // 8 More... 257 258 TestRecentTabsSubMenuModel model(NULL, browser(), &associator_); 259 int num_items = model.GetItemCount(); 260 EXPECT_EQ(9, num_items); 261 262 std::vector<string16> tab_titles = 263 recent_tabs_builder.GetTabTitlesSortedByRecency(); 264 for (int i = 0; i < 4; ++i) 265 EXPECT_EQ(tab_titles[i], model.GetLabelAt(i + 3)); 266} 267 268TEST_F(RecentTabsSubMenuModelTest, MaxWidth) { 269 // Create 1 session with 1 window and 1 tab. 270 RecentTabsBuilderTestHelper recent_tabs_builder; 271 recent_tabs_builder.AddSession(); 272 recent_tabs_builder.AddWindow(0); 273 recent_tabs_builder.AddTab(0, 0); 274 recent_tabs_builder.RegisterRecentTabs(&associator_); 275 276 // Menu index Menu items 277 // -------------------------------------- 278 // 0 Reopen closed tab 279 // 1 <separator> 280 // 2 <section header for 1st session> 281 // 3 <the only tab of the only window of session 1> 282 // 4 <separator> 283 // 5 More... 284 285 TestRecentTabsSubMenuModel model(NULL, browser(), &associator_); 286 EXPECT_EQ(6, model.GetItemCount()); 287 EXPECT_EQ(-1, model.GetMaxWidthForItemAtIndex(0)); 288 EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(1)); 289 EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(2)); 290 EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(3)); 291} 292 293TEST_F(RecentTabsSubMenuModelTest, MaxWidthNoDevices) { 294 // Expected menu: 295 // Menu index Menu items 296 // -------------------------------------- 297 // 0 Reopen closed tab 298 // 1 <separator> 299 // 2 No tabs from other Devices 300 301 TestRecentTabsSubMenuModel model(NULL, browser(), NULL); 302 EXPECT_EQ(3, model.GetItemCount()); 303 EXPECT_EQ(-1, model.GetMaxWidthForItemAtIndex(0)); 304 EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(1)); 305 EXPECT_EQ(-1, model.GetMaxWidthForItemAtIndex(2)); 306} 307