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 "base/command_line.h"
8#include "base/run_loop.h"
9#include "chrome/app/chrome_command_ids.h"
10#include "chrome/browser/sessions/session_service.h"
11#include "chrome/browser/sessions/session_service_factory.h"
12#include "chrome/browser/sessions/session_types.h"
13#include "chrome/browser/sessions/persistent_tab_restore_service.h"
14#include "chrome/browser/sessions/tab_restore_service_factory.h"
15#include "chrome/browser/sync/glue/synced_session.h"
16#include "chrome/browser/sync/profile_sync_service_mock.h"
17#include "chrome/browser/sync/sessions/sessions_sync_manager.h"
18#include "chrome/browser/ui/browser.h"
19#include "chrome/browser/ui/browser_tabstrip.h"
20#include "chrome/browser/ui/tabs/tab_strip_model.h"
21#include "chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h"
22#include "chrome/common/chrome_switches.h"
23#include "chrome/test/base/browser_with_test_window_test.h"
24#include "chrome/test/base/menu_model_test.h"
25#include "chrome/test/base/testing_profile.h"
26#include "components/sessions/serialized_navigation_entry_test_helper.h"
27#include "content/public/browser/browser_thread.h"
28#include "grit/generated_resources.h"
29#include "sync/api/fake_sync_change_processor.h"
30#include "sync/api/sync_error_factory_mock.h"
31#include "testing/gmock/include/gmock/gmock.h"
32#include "testing/gtest/include/gtest/gtest.h"
33
34namespace {
35
36// This copies parts of MenuModelTest::Delegate and combines them with the
37// RecentTabsSubMenuModel since RecentTabsSubMenuModel is a
38// SimpleMenuModel::Delegate and not just derived from SimpleMenuModel.
39class TestRecentTabsSubMenuModel : public RecentTabsSubMenuModel {
40 public:
41  TestRecentTabsSubMenuModel(ui::AcceleratorProvider* provider,
42                             Browser* browser,
43                             browser_sync::OpenTabsUIDelegate* delegate)
44      : RecentTabsSubMenuModel(provider, browser, delegate),
45        execute_count_(0),
46        enable_count_(0) {
47  }
48
49  // Testing overrides to ui::SimpleMenuModel::Delegate:
50  virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
51    bool val = RecentTabsSubMenuModel::IsCommandIdEnabled(command_id);
52    if (val)
53      ++enable_count_;
54    return val;
55  }
56
57  virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE {
58    ++execute_count_;
59  }
60
61  int execute_count() const { return execute_count_; }
62  int enable_count() const { return enable_count_; }
63
64 private:
65  int execute_count_;
66  int mutable enable_count_;  // Mutable because IsCommandIdEnabledAt is const.
67
68  DISALLOW_COPY_AND_ASSIGN(TestRecentTabsSubMenuModel);
69};
70
71class TestRecentTabsMenuModelDelegate : public ui::MenuModelDelegate {
72 public:
73  explicit TestRecentTabsMenuModelDelegate(ui::MenuModel* model)
74      : model_(model),
75        got_changes_(false) {
76    model_->SetMenuModelDelegate(this);
77  }
78
79  virtual ~TestRecentTabsMenuModelDelegate() {
80    model_->SetMenuModelDelegate(NULL);
81  }
82
83  // ui::MenuModelDelegate implementation:
84
85  virtual void OnIconChanged(int index) OVERRIDE {
86  }
87
88  virtual void OnMenuStructureChanged() OVERRIDE {
89    got_changes_ = true;
90  }
91
92  bool got_changes() const { return got_changes_; }
93
94 private:
95  ui::MenuModel* model_;
96  bool got_changes_;
97
98  DISALLOW_COPY_AND_ASSIGN(TestRecentTabsMenuModelDelegate);
99};
100
101class DummyRouter : public browser_sync::LocalSessionEventRouter {
102 public:
103  virtual ~DummyRouter() {}
104  virtual void StartRoutingTo(
105      browser_sync::LocalSessionEventHandler* handler) OVERRIDE {}
106  virtual void Stop() OVERRIDE {}
107};
108
109}  // namespace
110
111class RecentTabsSubMenuModelTest
112    : public BrowserWithTestWindowTest,
113      public browser_sync::SessionsSyncManager::SyncInternalApiDelegate {
114 public:
115  RecentTabsSubMenuModelTest()
116      : sync_service_(&testing_profile_) {
117    manager_.reset(new browser_sync::SessionsSyncManager(
118        &testing_profile_,
119        this,
120        scoped_ptr<browser_sync::LocalSessionEventRouter>(
121            new DummyRouter())));
122    manager_->MergeDataAndStartSyncing(
123        syncer::SESSIONS,
124        syncer::SyncDataList(),
125        scoped_ptr<syncer::SyncChangeProcessor>(
126          new syncer::FakeSyncChangeProcessor),
127        scoped_ptr<syncer::SyncErrorFactory>(
128            new syncer::SyncErrorFactoryMock));
129  }
130
131  void WaitForLoadFromLastSession() {
132    content::BrowserThread::GetBlockingPool()->FlushForTesting();
133    base::RunLoop().RunUntilIdle();
134    content::BrowserThread::GetBlockingPool()->FlushForTesting();
135  }
136
137  static KeyedService* GetTabRestoreService(
138      content::BrowserContext* browser_context) {
139    // Ownership is tranfered to the profile.
140    return new PersistentTabRestoreService(
141        Profile::FromBrowserContext(browser_context), NULL);
142  }
143
144
145  browser_sync::OpenTabsUIDelegate* GetOpenTabsDelegate() {
146    return manager_.get();
147  }
148
149  void RegisterRecentTabs(RecentTabsBuilderTestHelper* helper) {
150    helper->ExportToSessionsSyncManager(manager_.get());
151  }
152
153  virtual scoped_ptr<browser_sync::DeviceInfo> GetLocalDeviceInfo()
154      const OVERRIDE {
155    return scoped_ptr<browser_sync::DeviceInfo>(
156        new browser_sync::DeviceInfo(GetLocalSyncCacheGUID(),
157                       "Test Machine",
158                       "Chromium 10k",
159                       "Chrome 10k",
160                       sync_pb::SyncEnums_DeviceType_TYPE_LINUX));
161  }
162
163  virtual std::string GetLocalSyncCacheGUID() const OVERRIDE {
164    return "RecentTabsSubMenuModelTest";
165  }
166
167 private:
168  TestingProfile testing_profile_;
169  testing::NiceMock<ProfileSyncServiceMock> sync_service_;
170
171  scoped_ptr<browser_sync::SessionsSyncManager> manager_;
172};
173
174// Test disabled "Recently closed" header with no foreign tabs.
175TEST_F(RecentTabsSubMenuModelTest, NoTabs) {
176  TestRecentTabsSubMenuModel model(NULL, browser(), NULL);
177
178  // Expected menu:
179  // Menu index  Menu items
180  // ---------------------------------------------
181  // 0           Recently closed header (disabled)
182  // 1           <separator>
183  // 2           No tabs from other Devices
184
185  int num_items = model.GetItemCount();
186  EXPECT_EQ(3, num_items);
187  EXPECT_FALSE(model.IsEnabledAt(0));
188  EXPECT_FALSE(model.IsEnabledAt(2));
189  EXPECT_EQ(0, model.enable_count());
190
191  EXPECT_EQ(NULL, model.GetLabelFontListAt(0));
192  EXPECT_EQ(NULL, model.GetLabelFontListAt(1));
193  EXPECT_EQ(NULL, model.GetLabelFontListAt(2));
194
195  std::string url;
196  base::string16 title;
197  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(0, &url, &title));
198  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(1, &url, &title));
199  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(2, &url, &title));
200}
201
202// Test enabled "Recently closed" header with no foreign tabs.
203TEST_F(RecentTabsSubMenuModelTest, RecentlyClosedTabsFromCurrentSession) {
204  TabRestoreServiceFactory::GetInstance()->SetTestingFactory(
205      profile(), RecentTabsSubMenuModelTest::GetTabRestoreService);
206
207  // Add 2 tabs and close them.
208  AddTab(browser(), GURL("http://foo/1"));
209  AddTab(browser(), GURL("http://foo/2"));
210  browser()->tab_strip_model()->CloseAllTabs();
211
212  TestRecentTabsSubMenuModel model(NULL, browser(), NULL);
213  // Expected menu:
214  // Menu index  Menu items
215  // --------------------------------------
216  // 0           Recently closed header
217  // 1           <tab for http://foo/2>
218  // 2           <tab for http://foo/1>
219  // 3           <separator>
220  // 4           No tabs from other Devices
221  int num_items = model.GetItemCount();
222  EXPECT_EQ(5, num_items);
223  EXPECT_FALSE(model.IsEnabledAt(0));
224  EXPECT_TRUE(model.IsEnabledAt(1));
225  EXPECT_TRUE(model.IsEnabledAt(2));
226  model.ActivatedAt(1);
227  model.ActivatedAt(2);
228  EXPECT_FALSE(model.IsEnabledAt(4));
229  EXPECT_EQ(2, model.enable_count());
230  EXPECT_EQ(2, model.execute_count());
231
232  EXPECT_TRUE(model.GetLabelFontListAt(0) != NULL);
233  EXPECT_EQ(NULL, model.GetLabelFontListAt(1));
234  EXPECT_EQ(NULL, model.GetLabelFontListAt(2));
235  EXPECT_EQ(NULL, model.GetLabelFontListAt(3));
236  EXPECT_EQ(NULL, model.GetLabelFontListAt(4));
237
238  std::string url;
239  base::string16 title;
240  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(0, &url, &title));
241  EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(1, &url, &title));
242  EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(2, &url, &title));
243  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(3, &url, &title));
244  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(4, &url, &title));
245}
246
247// TODO(sail): enable this test when dynamic model is enabled in
248// RecentTabsSubMenuModel.
249#if defined(OS_MACOSX)
250#define MAYBE_RecentlyClosedTabsAndWindowsFromLastSession \
251    DISABLED_RecentlyClosedTabsAndWindowsFromLastSession
252#else
253#define MAYBE_RecentlyClosedTabsAndWindowsFromLastSession \
254    RecentlyClosedTabsAndWindowsFromLastSession
255#endif
256TEST_F(RecentTabsSubMenuModelTest,
257       MAYBE_RecentlyClosedTabsAndWindowsFromLastSession) {
258  TabRestoreServiceFactory::GetInstance()->SetTestingFactory(
259      profile(), RecentTabsSubMenuModelTest::GetTabRestoreService);
260
261  // Add 2 tabs and close them.
262  AddTab(browser(), GURL("http://wnd/tab0"));
263  AddTab(browser(), GURL("http://wnd/tab1"));
264  browser()->tab_strip_model()->CloseAllTabs();
265
266  // Create a SessionService for the profile (profile owns the service) and add
267  // a window with a tab to this session.
268  SessionService* session_service = new SessionService(profile());
269  SessionServiceFactory::SetForTestProfile(profile(), session_service);
270  SessionID tab_id;
271  SessionID window_id;
272  session_service->SetWindowType(
273      window_id, Browser::TYPE_TABBED, SessionService::TYPE_NORMAL);
274  session_service->SetTabWindow(window_id, tab_id);
275  session_service->SetTabIndexInWindow(window_id, tab_id, 0);
276  session_service->SetSelectedTabInWindow(window_id, 0);
277  session_service->UpdateTabNavigation(
278      window_id, tab_id,
279      sessions::SerializedNavigationEntryTestHelper::CreateNavigation(
280          "http://wnd1/tab0", "title"));
281  // Set this, otherwise previous session won't be loaded.
282  profile()->set_last_session_exited_cleanly(false);
283  // Move this session to the last so that TabRestoreService will load it as the
284  // last session.
285  SessionServiceFactory::GetForProfile(profile())->
286      MoveCurrentSessionToLastSession();
287
288  // Create a new TabRestoreService so that it'll load the recently closed tabs
289  // and windows afresh.
290  TabRestoreServiceFactory::GetInstance()->SetTestingFactory(
291      profile(), RecentTabsSubMenuModelTest::GetTabRestoreService);
292  // Let the shutdown of previous TabRestoreService run.
293  content::BrowserThread::GetBlockingPool()->FlushForTesting();
294
295  TestRecentTabsSubMenuModel model(NULL, browser(), NULL);
296  TestRecentTabsMenuModelDelegate delegate(&model);
297  EXPECT_FALSE(delegate.got_changes());
298
299  // Expected menu before tabs/windows from last session are loaded:
300  // Menu index  Menu items
301  // ----------------------------------------------------------------
302  // 0           Recently closed header
303  // 1           <separator>
304  // 2           No tabs from other Devices
305
306  int num_items = model.GetItemCount();
307  EXPECT_EQ(3, num_items);
308  EXPECT_FALSE(model.IsEnabledAt(0));
309  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model.GetTypeAt(1));
310  EXPECT_FALSE(model.IsEnabledAt(2));
311  EXPECT_EQ(0, model.enable_count());
312
313  // Wait for tabs from last session to be loaded.
314  WaitForLoadFromLastSession();
315
316  // Expected menu after tabs/windows from last session are loaded:
317  // Menu index  Menu items
318  // --------------------------------------------------------------
319  // 0           Recently closed header
320  // 1           <window for the tab http://wnd1/tab0>
321  // 2           <tab for http://wnd0/tab1>
322  // 3           <tab for http://wnd0/tab0>
323  // 4           <separator>
324  // 5           No tabs from other Devices
325
326  EXPECT_TRUE(delegate.got_changes());
327
328  num_items = model.GetItemCount();
329  EXPECT_EQ(6, num_items);
330  EXPECT_FALSE(model.IsEnabledAt(0));
331  EXPECT_TRUE(model.IsEnabledAt(1));
332  EXPECT_TRUE(model.IsEnabledAt(2));
333  EXPECT_TRUE(model.IsEnabledAt(3));
334  model.ActivatedAt(1);
335  model.ActivatedAt(2);
336  model.ActivatedAt(3);
337  EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model.GetTypeAt(4));
338  EXPECT_FALSE(model.IsEnabledAt(5));
339  EXPECT_EQ(3, model.enable_count());
340  EXPECT_EQ(3, model.execute_count());
341
342  EXPECT_TRUE(model.GetLabelFontListAt(0) != NULL);
343  EXPECT_EQ(NULL, model.GetLabelFontListAt(1));
344  EXPECT_EQ(NULL, model.GetLabelFontListAt(2));
345  EXPECT_EQ(NULL, model.GetLabelFontListAt(3));
346  EXPECT_EQ(NULL, model.GetLabelFontListAt(4));
347  EXPECT_EQ(NULL, model.GetLabelFontListAt(5));
348
349  std::string url;
350  base::string16 title;
351  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(0, &url, &title));
352  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(1, &url, &title));
353  EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(2, &url, &title));
354  EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(3, &url, &title));
355  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(4, &url, &title));
356  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(5, &url, &title));
357}
358
359// Test disabled "Recently closed" header with multiple sessions, multiple
360// windows, and multiple enabled tabs from other devices.
361TEST_F(RecentTabsSubMenuModelTest, OtherDevices) {
362  // Tabs are populated in decreasing timestamp.
363  base::Time timestamp = base::Time::Now();
364  const base::TimeDelta time_delta = base::TimeDelta::FromMinutes(10);
365
366  RecentTabsBuilderTestHelper recent_tabs_builder;
367
368  // Create 1st session : 1 window, 3 tabs
369  recent_tabs_builder.AddSession();
370  recent_tabs_builder.AddWindow(0);
371  for (int i = 0; i < 3; ++i) {
372    timestamp -= time_delta;
373    recent_tabs_builder.AddTabWithInfo(0, 0, timestamp, base::string16());
374  }
375
376  // Create 2nd session : 2 windows, 1 tab in 1st window, 2 tabs in 2nd window
377  recent_tabs_builder.AddSession();
378  recent_tabs_builder.AddWindow(1);
379  recent_tabs_builder.AddWindow(1);
380  timestamp -= time_delta;
381  recent_tabs_builder.AddTabWithInfo(1, 0, timestamp, base::string16());
382  timestamp -= time_delta;
383  recent_tabs_builder.AddTabWithInfo(1, 1, timestamp, base::string16());
384  timestamp -= time_delta;
385  recent_tabs_builder.AddTabWithInfo(1, 1, timestamp, base::string16());
386
387  RegisterRecentTabs(&recent_tabs_builder);
388
389  // Verify that data is populated correctly in RecentTabsSubMenuModel.
390  // Expected menu:
391  // - first inserted tab is most recent and hence is top
392  // Menu index  Menu items
393  // -----------------------------------------------------
394  // 0           Recently closed header (disabled)
395  // 1           <separator>
396  // 2           <section header for 1st session>
397  // 3-5         <3 tabs of the only window of session 0>
398  // 6           <separator>
399  // 7           <section header for 2nd session>
400  // 8           <the only tab of window 0 of session 1>
401  // 9-10        <2 tabs of window 1 of session 2>
402  // 11          <separator>
403  // 12          More...
404
405  TestRecentTabsSubMenuModel model(NULL, browser(), GetOpenTabsDelegate());
406  int num_items = model.GetItemCount();
407  EXPECT_EQ(13, num_items);
408  model.ActivatedAt(0);
409  EXPECT_FALSE(model.IsEnabledAt(0));
410  model.ActivatedAt(3);
411  EXPECT_TRUE(model.IsEnabledAt(3));
412  model.ActivatedAt(4);
413  EXPECT_TRUE(model.IsEnabledAt(4));
414  model.ActivatedAt(5);
415  EXPECT_TRUE(model.IsEnabledAt(5));
416  model.ActivatedAt(8);
417  EXPECT_TRUE(model.IsEnabledAt(8));
418  model.ActivatedAt(9);
419  EXPECT_TRUE(model.IsEnabledAt(9));
420  model.ActivatedAt(10);
421  EXPECT_TRUE(model.IsEnabledAt(10));
422  EXPECT_TRUE(model.IsEnabledAt(12));
423  EXPECT_EQ(7, model.enable_count());
424  EXPECT_EQ(7, model.execute_count());
425
426  EXPECT_EQ(NULL, model.GetLabelFontListAt(0));
427  EXPECT_EQ(NULL, model.GetLabelFontListAt(1));
428  EXPECT_TRUE(model.GetLabelFontListAt(2) != NULL);
429  EXPECT_EQ(NULL, model.GetLabelFontListAt(3));
430  EXPECT_EQ(NULL, model.GetLabelFontListAt(4));
431  EXPECT_EQ(NULL, model.GetLabelFontListAt(5));
432  EXPECT_EQ(NULL, model.GetLabelFontListAt(6));
433  EXPECT_TRUE(model.GetLabelFontListAt(7) != NULL);
434  EXPECT_EQ(NULL, model.GetLabelFontListAt(8));
435  EXPECT_EQ(NULL, model.GetLabelFontListAt(9));
436  EXPECT_EQ(NULL, model.GetLabelFontListAt(10));
437  EXPECT_EQ(NULL, model.GetLabelFontListAt(11));
438  EXPECT_EQ(NULL, model.GetLabelFontListAt(12));
439
440  std::string url;
441  base::string16 title;
442  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(0, &url, &title));
443  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(1, &url, &title));
444  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(2, &url, &title));
445  EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(3, &url, &title));
446  EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(4, &url, &title));
447  EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(5, &url, &title));
448  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(6, &url, &title));
449  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(7, &url, &title));
450  EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(8, &url, &title));
451  EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(9, &url, &title));
452  EXPECT_TRUE(model.GetURLAndTitleForItemAtIndex(10, &url, &title));
453  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(11, &url, &title));
454  EXPECT_FALSE(model.GetURLAndTitleForItemAtIndex(12, &url, &title));
455}
456
457TEST_F(RecentTabsSubMenuModelTest, MaxSessionsAndRecency) {
458  // Create 4 sessions : each session has 1 window with 1 tab each.
459  RecentTabsBuilderTestHelper recent_tabs_builder;
460  for (int s = 0; s < 4; ++s) {
461    recent_tabs_builder.AddSession();
462    recent_tabs_builder.AddWindow(s);
463    recent_tabs_builder.AddTab(s, 0);
464  }
465  RegisterRecentTabs(&recent_tabs_builder);
466
467  // Verify that data is populated correctly in RecentTabsSubMenuModel.
468  // Expected menu:
469  // - max sessions is 3, so only 3 most-recent sessions will show.
470  // Menu index  Menu items
471  // ----------------------------------------------------------
472  // 0           Recently closed header (disabled)
473  // 1           <separator>
474  // 2           <section header for 1st session>
475  // 3           <the only tab of the only window of session 3>
476  // 4           <separator>
477  // 5           <section header for 2nd session>
478  // 6           <the only tab of the only window of session 2>
479  // 7           <separator>
480  // 8           <section header for 3rd session>
481  // 9           <the only tab of the only window of session 1>
482  // 10          <separator>
483  // 11          More...
484
485  TestRecentTabsSubMenuModel model(NULL, browser(), GetOpenTabsDelegate());
486  int num_items = model.GetItemCount();
487  EXPECT_EQ(12, num_items);
488
489  std::vector<base::string16> tab_titles =
490      recent_tabs_builder.GetTabTitlesSortedByRecency();
491  EXPECT_EQ(tab_titles[0], model.GetLabelAt(3));
492  EXPECT_EQ(tab_titles[1], model.GetLabelAt(6));
493  EXPECT_EQ(tab_titles[2], model.GetLabelAt(9));
494}
495
496TEST_F(RecentTabsSubMenuModelTest, MaxTabsPerSessionAndRecency) {
497  // Create a session: 2 windows with 5 tabs each.
498  RecentTabsBuilderTestHelper recent_tabs_builder;
499  recent_tabs_builder.AddSession();
500  for (int w = 0; w < 2; ++w) {
501    recent_tabs_builder.AddWindow(0);
502    for (int t = 0; t < 5; ++t)
503      recent_tabs_builder.AddTab(0, w);
504  }
505  RegisterRecentTabs(&recent_tabs_builder);
506
507  // Verify that data is populated correctly in RecentTabsSubMenuModel.
508  // Expected menu:
509  // - max tabs per session is 4, so only 4 most-recent tabs will show,
510  //   independent of which window they came from.
511  // Menu index  Menu items
512  // ---------------------------------------------
513  // 0           Recently closed header (disabled)
514  // 1           <separator>
515  // 2           <section header for session>
516  // 3-6         <4 most-recent tabs of session>
517  // 7           <separator>
518  // 8           More...
519
520  TestRecentTabsSubMenuModel model(NULL, browser(), GetOpenTabsDelegate());
521  int num_items = model.GetItemCount();
522  EXPECT_EQ(9, num_items);
523
524  std::vector<base::string16> tab_titles =
525      recent_tabs_builder.GetTabTitlesSortedByRecency();
526  for (int i = 0; i < 4; ++i)
527    EXPECT_EQ(tab_titles[i], model.GetLabelAt(i + 3));
528}
529
530TEST_F(RecentTabsSubMenuModelTest, MaxWidth) {
531  // Create 1 session with 1 window and 1 tab.
532  RecentTabsBuilderTestHelper recent_tabs_builder;
533  recent_tabs_builder.AddSession();
534  recent_tabs_builder.AddWindow(0);
535  recent_tabs_builder.AddTab(0, 0);
536  RegisterRecentTabs(&recent_tabs_builder);
537
538  // Menu index  Menu items
539  // ----------------------------------------------------------
540  // 0           Recently closed header (disabled)
541  // 1           <separator>
542  // 2           <section header for 1st session>
543  // 3           <the only tab of the only window of session 1>
544  // 4           <separator>
545  // 5           More...
546
547  TestRecentTabsSubMenuModel model(NULL, browser(), GetOpenTabsDelegate());
548  EXPECT_EQ(6, model.GetItemCount());
549  EXPECT_EQ(-1, model.GetMaxWidthForItemAtIndex(0));
550  EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(1));
551  EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(2));
552  EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(3));
553}
554
555TEST_F(RecentTabsSubMenuModelTest, MaxWidthNoDevices) {
556  // Expected menu:
557  // Menu index  Menu items
558  // --------------------------------------------
559  // 0           Recently closed heaer (disabled)
560  // 1           <separator>
561  // 2           No tabs from other Devices
562
563  TestRecentTabsSubMenuModel model(NULL, browser(), NULL);
564  EXPECT_EQ(3, model.GetItemCount());
565  EXPECT_EQ(-1, model.GetMaxWidthForItemAtIndex(0));
566  EXPECT_NE(-1, model.GetMaxWidthForItemAtIndex(1));
567  EXPECT_EQ(-1, model.GetMaxWidthForItemAtIndex(2));
568}
569