tab_strip_model_unittest.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
1// Copyright (c) 2011 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 <map>
6#include <string>
7
8#include "base/file_path.h"
9#include "base/file_util.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/path_service.h"
12#include "base/stl_util-inl.h"
13#include "base/string_number_conversions.h"
14#include "base/string_split.h"
15#include "base/string_util.h"
16#include "base/utf_string_conversions.h"
17#include "chrome/browser/defaults.h"
18#include "chrome/browser/extensions/extension_tab_helper.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/profiles/profile_manager.h"
21#include "chrome/browser/tabs/tab_strip_model.h"
22#include "chrome/browser/tabs/tab_strip_model_delegate.h"
23#include "chrome/browser/tabs/tab_strip_model_order_controller.h"
24#include "chrome/browser/ui/browser.h"
25#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
26#include "chrome/browser/ui/webui/new_tab_ui.h"
27#include "chrome/common/extensions/extension.h"
28#include "chrome/common/url_constants.h"
29#include "chrome/test/testing_profile.h"
30#include "content/browser/renderer_host/test_render_view_host.h"
31#include "content/browser/tab_contents/navigation_controller.h"
32#include "content/browser/tab_contents/navigation_entry.h"
33#include "content/browser/tab_contents/tab_contents.h"
34#include "content/common/notification_details.h"
35#include "content/common/notification_observer_mock.h"
36#include "content/common/notification_registrar.h"
37#include "content/common/notification_source.h"
38#include "content/common/property_bag.h"
39#include "testing/gtest/include/gtest/gtest.h"
40#include "ui/base/system_monitor/system_monitor.h"
41
42using testing::_;
43
44namespace {
45
46// Class used to delete a TabContents when another TabContents is destroyed.
47class DeleteTabContentsOnDestroyedObserver : public NotificationObserver {
48 public:
49  DeleteTabContentsOnDestroyedObserver(TabContentsWrapper* source,
50                                       TabContentsWrapper* tab_to_delete)
51      : source_(source),
52        tab_to_delete_(tab_to_delete) {
53    registrar_.Add(this,
54                   NotificationType::TAB_CONTENTS_DESTROYED,
55                   Source<TabContents>(source->tab_contents()));
56  }
57
58  virtual void Observe(NotificationType type,
59                       const NotificationSource& source,
60                       const NotificationDetails& details) {
61    TabContentsWrapper* tab_to_delete = tab_to_delete_;
62    tab_to_delete_ = NULL;
63    delete tab_to_delete;
64  }
65
66 private:
67  TabContentsWrapper* source_;
68  TabContentsWrapper* tab_to_delete_;
69  NotificationRegistrar registrar_;
70
71  DISALLOW_COPY_AND_ASSIGN(DeleteTabContentsOnDestroyedObserver);
72};
73
74}  // namespace
75
76class TabStripDummyDelegate : public TabStripModelDelegate {
77 public:
78  explicit TabStripDummyDelegate(TabContentsWrapper* dummy)
79      : dummy_contents_(dummy), can_close_(true), run_unload_(false) {}
80  virtual ~TabStripDummyDelegate() {}
81
82  void set_can_close(bool value) { can_close_ = value; }
83  void set_run_unload_listener(bool value) { run_unload_ = value; }
84
85  // Overridden from TabStripModelDelegate:
86  virtual TabContentsWrapper* AddBlankTab(bool foreground) {
87    return NULL;
88  }
89  virtual TabContentsWrapper* AddBlankTabAt(int index, bool foreground) {
90    return NULL;
91  }
92  virtual Browser* CreateNewStripWithContents(TabContentsWrapper* contents,
93                                              const gfx::Rect& window_bounds,
94                                              const DockInfo& dock_info,
95                                              bool maximize) {
96    return NULL;
97  }
98  virtual void ContinueDraggingDetachedTab(TabContentsWrapper* contents,
99                                           const gfx::Rect& window_bounds,
100                                           const gfx::Rect& tab_bounds) {
101  }
102  virtual int GetDragActions() const { return 0; }
103  virtual TabContentsWrapper* CreateTabContentsForURL(
104      const GURL& url,
105      const GURL& referrer,
106      Profile* profile,
107      PageTransition::Type transition,
108      bool defer_load,
109      SiteInstance* instance) const {
110    if (url == GURL(chrome::kChromeUINewTabURL))
111      return dummy_contents_;
112    return NULL;
113  }
114  virtual bool CanDuplicateContentsAt(int index) { return false; }
115  virtual void DuplicateContentsAt(int index) {}
116  virtual void CloseFrameAfterDragSession() {}
117  virtual void CreateHistoricalTab(TabContentsWrapper* contents) {}
118  virtual bool RunUnloadListenerBeforeClosing(TabContentsWrapper* contents) {
119    return run_unload_;
120  }
121  virtual bool CanRestoreTab() { return false; }
122  virtual void RestoreTab() {}
123  virtual bool CanCloseContentsAt(int index) { return can_close_ ; }
124  virtual bool CanBookmarkAllTabs() const { return false; }
125  virtual void BookmarkAllTabs() {}
126  virtual bool CanCloseTab() const { return true; }
127  virtual bool UseVerticalTabs() const { return false; }
128  virtual void ToggleUseVerticalTabs() {}
129  virtual bool LargeIconsPermitted() const { return true; }
130
131 private:
132  // A dummy TabContents we give to callers that expect us to actually build a
133  // Destinations tab for them.
134  TabContentsWrapper* dummy_contents_;
135
136  // Whether tabs can be closed.
137  bool can_close_;
138
139  // Whether to report that we need to run an unload listener before closing.
140  bool run_unload_;
141
142  DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate);
143};
144
145class TabStripModelTest : public RenderViewHostTestHarness {
146 public:
147  TabContentsWrapper* CreateTabContents() {
148    return Browser::TabContentsFactory(profile(), NULL, 0, NULL, NULL);
149  }
150
151  TabContentsWrapper* CreateTabContentsWithSharedRPH(
152      TabContents* tab_contents) {
153    TabContentsWrapper* retval = Browser::TabContentsFactory(profile(),
154        tab_contents->render_view_host()->site_instance(), MSG_ROUTING_NONE,
155        NULL, NULL);
156    EXPECT_EQ(retval->tab_contents()->GetRenderProcessHost(),
157              tab_contents->GetRenderProcessHost());
158    return retval;
159  }
160
161  // Forwards a URL "load" request through to our dummy TabContents
162  // implementation.
163  void LoadURL(TabContents* con, const std::wstring& url) {
164    controller().LoadURL(GURL(WideToUTF16(url)), GURL(), PageTransition::LINK);
165  }
166
167  void GoBack(TabContents* contents) {
168    controller().GoBack();
169  }
170
171  void GoForward(TabContents* contents) {
172    controller().GoForward();
173  }
174
175  void SwitchTabTo(TabContents* contents) {
176    // contents()->DidBecomeSelected();
177  }
178
179  // Sets the id of the specified contents.
180  void SetID(TabContents* contents, int id) {
181    GetIDAccessor()->SetProperty(contents->property_bag(), id);
182  }
183
184  // Returns the id of the specified contents.
185  int GetID(TabContents* contents) {
186    return *GetIDAccessor()->GetProperty(contents->property_bag());
187  }
188
189  // Returns the state of the given tab strip as a string. The state consists
190  // of the ID of each tab contents followed by a 'p' if pinned. For example,
191  // if the model consists of two tabs with ids 2 and 1, with the first
192  // tab pinned, this returns "2p 1".
193  std::string GetPinnedState(const TabStripModel& model) {
194    std::string actual;
195    for (int i = 0; i < model.count(); ++i) {
196      if (i > 0)
197        actual += " ";
198
199      actual +=
200          base::IntToString(GetID(model.GetTabContentsAt(i)->tab_contents()));
201
202      if (model.IsAppTab(i))
203        actual += "a";
204
205      if (model.IsTabPinned(i))
206        actual += "p";
207    }
208    return actual;
209  }
210
211  std::string GetIndicesClosedByCommandAsString(
212      const TabStripModel& model,
213      int index,
214      TabStripModel::ContextMenuCommand id) const {
215    std::vector<int> indices = model.GetIndicesClosedByCommand(index, id);
216    std::string result;
217    for (size_t i = 0; i < indices.size(); ++i) {
218      if (i != 0)
219        result += " ";
220      result += base::IntToString(indices[i]);
221    }
222    return result;
223  }
224
225  void PrepareTabstripForSelectionTest(TabStripModel* model,
226                                       int tab_count,
227                                       int pinned_count,
228                                       const std::string& selected_tabs) {
229    for (int i = 0; i < tab_count; ++i) {
230      TabContentsWrapper* contents = CreateTabContents();
231      SetID(contents->tab_contents(), i);
232      model->AppendTabContents(contents, true);
233    }
234    for (int i = 0; i < pinned_count; ++i)
235      model->SetTabPinned(i, true);
236
237    TabStripSelectionModel selection_model;
238    std::vector<std::string> selection;
239    base::SplitStringAlongWhitespace(selected_tabs, &selection);
240    for (size_t i = 0; i < selection.size(); ++i) {
241      int value;
242      ASSERT_TRUE(base::StringToInt(selection[i], &value));
243      selection_model.AddIndexToSelection(value);
244    }
245    selection_model.set_active(selection_model.selected_indices()[0]);
246    model->SetSelectionFromModel(selection_model);
247  }
248
249 private:
250  PropertyAccessor<int>* GetIDAccessor() {
251    static PropertyAccessor<int> accessor;
252    return &accessor;
253  }
254
255  std::wstring test_dir_;
256  std::wstring profile_path_;
257  std::map<TabContents*, int> foo_;
258
259  // ProfileManager requires a ui::SystemMonitor.
260  ui::SystemMonitor system_monitor;
261
262  ProfileManager pm_;
263};
264
265class MockTabStripModelObserver : public TabStripModelObserver {
266 public:
267  MockTabStripModelObserver() : empty_(true) {}
268  ~MockTabStripModelObserver() {
269    STLDeleteContainerPointers(states_.begin(), states_.end());
270  }
271
272  enum TabStripModelObserverAction {
273    INSERT,
274    CLOSE,
275    DETACH,
276    SELECT,
277    MOVE,
278    CHANGE,
279    PINNED,
280    REPLACED
281  };
282
283  struct State {
284    State(TabContentsWrapper* a_dst_contents,
285          int a_dst_index,
286          TabStripModelObserverAction a_action)
287        : src_contents(NULL),
288          dst_contents(a_dst_contents),
289          src_index(-1),
290          dst_index(a_dst_index),
291          user_gesture(false),
292          foreground(false),
293          action(a_action) {
294    }
295
296    TabContentsWrapper* src_contents;
297    TabContentsWrapper* dst_contents;
298    int src_index;
299    int dst_index;
300    bool user_gesture;
301    bool foreground;
302    TabStripModelObserverAction action;
303  };
304
305  int GetStateCount() const {
306    return static_cast<int>(states_.size());
307  }
308
309  State* GetStateAt(int index) const {
310    DCHECK(index >= 0 && index < GetStateCount());
311    return states_.at(index);
312  }
313
314  bool StateEquals(int index, const State& state) {
315    State* s = GetStateAt(index);
316    EXPECT_EQ(state.src_contents, s->src_contents);
317    EXPECT_EQ(state.dst_contents, s->dst_contents);
318    EXPECT_EQ(state.src_index, s->src_index);
319    EXPECT_EQ(state.dst_index, s->dst_index);
320    EXPECT_EQ(state.user_gesture, s->user_gesture);
321    EXPECT_EQ(state.foreground, s->foreground);
322    EXPECT_EQ(state.action, s->action);
323    return (s->src_contents == state.src_contents &&
324            s->dst_contents == state.dst_contents &&
325            s->src_index == state.src_index &&
326            s->dst_index == state.dst_index &&
327            s->user_gesture == state.user_gesture &&
328            s->foreground == state.foreground &&
329            s->action == state.action);
330  }
331
332  // TabStripModelObserver implementation:
333  virtual void TabInsertedAt(TabContentsWrapper* contents,
334                             int index,
335                             bool foreground) {
336    empty_ = false;
337    State* s = new State(contents, index, INSERT);
338    s->foreground = foreground;
339    states_.push_back(s);
340  }
341  virtual void TabSelectedAt(TabContentsWrapper* old_contents,
342                             TabContentsWrapper* new_contents,
343                             int index,
344                             bool user_gesture) {
345    State* s = new State(new_contents, index, SELECT);
346    s->src_contents = old_contents;
347    s->user_gesture = user_gesture;
348    states_.push_back(s);
349  }
350  virtual void TabMoved(
351      TabContentsWrapper* contents, int from_index, int to_index) {
352    State* s = new State(contents, to_index, MOVE);
353    s->src_index = from_index;
354    states_.push_back(s);
355  }
356
357  virtual void TabClosingAt(TabStripModel* tab_strip_model,
358                            TabContentsWrapper* contents,
359                            int index) {
360    states_.push_back(new State(contents, index, CLOSE));
361  }
362  virtual void TabDetachedAt(TabContentsWrapper* contents, int index) {
363    states_.push_back(new State(contents, index, DETACH));
364  }
365  virtual void TabChangedAt(TabContentsWrapper* contents, int index,
366                            TabChangeType change_type) {
367    states_.push_back(new State(contents, index, CHANGE));
368  }
369  virtual void TabReplacedAt(TabStripModel* tab_strip_model,
370                             TabContentsWrapper* old_contents,
371                             TabContentsWrapper* new_contents,
372                             int index) {
373    State* s = new State(new_contents, index, REPLACED);
374    s ->src_contents = old_contents;
375    states_.push_back(s);
376  }
377  virtual void TabPinnedStateChanged(TabContentsWrapper* contents, int index) {
378    states_.push_back(new State(contents, index, PINNED));
379  }
380  virtual void TabStripEmpty() {
381    empty_ = true;
382  }
383
384  void ClearStates() {
385    STLDeleteContainerPointers(states_.begin(), states_.end());
386    states_.clear();
387  }
388
389  bool empty() const { return empty_; }
390
391 private:
392  std::vector<State*> states_;
393
394  bool empty_;
395
396  DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver);
397};
398
399TEST_F(TabStripModelTest, TestBasicAPI) {
400  TabStripDummyDelegate delegate(NULL);
401  TabStripModel tabstrip(&delegate, profile());
402  MockTabStripModelObserver observer;
403  tabstrip.AddObserver(&observer);
404
405  EXPECT_TRUE(tabstrip.empty());
406
407  typedef MockTabStripModelObserver::State State;
408
409  TabContentsWrapper* contents1 = CreateTabContents();
410
411  // Note! The ordering of these tests is important, each subsequent test
412  // builds on the state established in the previous. This is important if you
413  // ever insert tests rather than append.
414
415  // Test AppendTabContents, ContainsIndex
416  {
417    EXPECT_FALSE(tabstrip.ContainsIndex(0));
418    tabstrip.AppendTabContents(contents1, true);
419    EXPECT_TRUE(tabstrip.ContainsIndex(0));
420    EXPECT_EQ(1, tabstrip.count());
421    EXPECT_EQ(2, observer.GetStateCount());
422    State s1(contents1, 0, MockTabStripModelObserver::INSERT);
423    s1.foreground = true;
424    EXPECT_TRUE(observer.StateEquals(0, s1));
425    State s2(contents1, 0, MockTabStripModelObserver::SELECT);
426    s2.src_contents = NULL;
427    EXPECT_TRUE(observer.StateEquals(1, s2));
428    observer.ClearStates();
429  }
430
431  // Test InsertTabContentsAt, foreground tab.
432  TabContentsWrapper* contents2 = CreateTabContents();
433  {
434    tabstrip.InsertTabContentsAt(1, contents2, TabStripModel::ADD_ACTIVE);
435
436    EXPECT_EQ(2, tabstrip.count());
437    EXPECT_EQ(2, observer.GetStateCount());
438    State s1(contents2, 1, MockTabStripModelObserver::INSERT);
439    s1.foreground = true;
440    EXPECT_TRUE(observer.StateEquals(0, s1));
441    State s2(contents2, 1, MockTabStripModelObserver::SELECT);
442    s2.src_contents = contents1;
443    EXPECT_TRUE(observer.StateEquals(1, s2));
444    observer.ClearStates();
445  }
446
447  // Test InsertTabContentsAt, background tab.
448  TabContentsWrapper* contents3 = CreateTabContents();
449  {
450    tabstrip.InsertTabContentsAt(2, contents3, TabStripModel::ADD_NONE);
451
452    EXPECT_EQ(3, tabstrip.count());
453    EXPECT_EQ(1, observer.GetStateCount());
454    State s1(contents3, 2, MockTabStripModelObserver::INSERT);
455    s1.foreground = false;
456    EXPECT_TRUE(observer.StateEquals(0, s1));
457    observer.ClearStates();
458  }
459
460  // Test ActivateTabAt
461  {
462    tabstrip.ActivateTabAt(2, true);
463    EXPECT_EQ(1, observer.GetStateCount());
464    State s1(contents3, 2, MockTabStripModelObserver::SELECT);
465    s1.src_contents = contents2;
466    s1.user_gesture = true;
467    EXPECT_TRUE(observer.StateEquals(0, s1));
468    observer.ClearStates();
469  }
470
471  // Test DetachTabContentsAt
472  {
473    // Detach
474    TabContentsWrapper* detached = tabstrip.DetachTabContentsAt(2);
475    // ... and append again because we want this for later.
476    tabstrip.AppendTabContents(detached, true);
477    EXPECT_EQ(4, observer.GetStateCount());
478    State s1(detached, 2, MockTabStripModelObserver::DETACH);
479    EXPECT_TRUE(observer.StateEquals(0, s1));
480    State s2(contents2, 1, MockTabStripModelObserver::SELECT);
481    s2.src_contents = contents3;
482    s2.user_gesture = false;
483    EXPECT_TRUE(observer.StateEquals(1, s2));
484    State s3(detached, 2, MockTabStripModelObserver::INSERT);
485    s3.foreground = true;
486    EXPECT_TRUE(observer.StateEquals(2, s3));
487    State s4(detached, 2, MockTabStripModelObserver::SELECT);
488    s4.src_contents = contents2;
489    s4.user_gesture = false;
490    EXPECT_TRUE(observer.StateEquals(3, s4));
491    observer.ClearStates();
492  }
493
494  // Test CloseTabContentsAt
495  {
496    // Let's test nothing happens when the delegate veto the close.
497    delegate.set_can_close(false);
498    EXPECT_FALSE(tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE));
499    EXPECT_EQ(3, tabstrip.count());
500    EXPECT_EQ(0, observer.GetStateCount());
501
502    // Now let's close for real.
503    delegate.set_can_close(true);
504    EXPECT_TRUE(tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE));
505    EXPECT_EQ(2, tabstrip.count());
506
507    EXPECT_EQ(3, observer.GetStateCount());
508    State s1(contents3, 2, MockTabStripModelObserver::CLOSE);
509    EXPECT_TRUE(observer.StateEquals(0, s1));
510    State s2(contents3, 2, MockTabStripModelObserver::DETACH);
511    EXPECT_TRUE(observer.StateEquals(1, s2));
512    State s3(contents2, 1, MockTabStripModelObserver::SELECT);
513    s3.src_contents = contents3;
514    s3.user_gesture = false;
515    EXPECT_TRUE(observer.StateEquals(2, s3));
516    observer.ClearStates();
517  }
518
519  // Test MoveTabContentsAt, select_after_move == true
520  {
521    tabstrip.MoveTabContentsAt(1, 0, true);
522
523    EXPECT_EQ(1, observer.GetStateCount());
524    State s1(contents2, 0, MockTabStripModelObserver::MOVE);
525    s1.src_index = 1;
526    EXPECT_TRUE(observer.StateEquals(0, s1));
527    EXPECT_EQ(0, tabstrip.active_index());
528    observer.ClearStates();
529  }
530
531  // Test MoveTabContentsAt, select_after_move == false
532  {
533    tabstrip.MoveTabContentsAt(1, 0, false);
534    EXPECT_EQ(1, observer.GetStateCount());
535    State s1(contents1, 0, MockTabStripModelObserver::MOVE);
536    s1.src_index = 1;
537    EXPECT_TRUE(observer.StateEquals(0, s1));
538    EXPECT_EQ(1, tabstrip.active_index());
539
540    tabstrip.MoveTabContentsAt(0, 1, false);
541    observer.ClearStates();
542  }
543
544  // Test Getters
545  {
546    EXPECT_EQ(contents2, tabstrip.GetSelectedTabContents());
547    EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(0));
548    EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(1));
549    EXPECT_EQ(0, tabstrip.GetIndexOfTabContents(contents2));
550    EXPECT_EQ(1, tabstrip.GetIndexOfTabContents(contents1));
551    EXPECT_EQ(0, tabstrip.GetIndexOfController(&contents2->controller()));
552    EXPECT_EQ(1, tabstrip.GetIndexOfController(&contents1->controller()));
553  }
554
555  // Test UpdateTabContentsStateAt
556  {
557    tabstrip.UpdateTabContentsStateAt(0, TabStripModelObserver::ALL);
558    EXPECT_EQ(1, observer.GetStateCount());
559    State s1(contents2, 0, MockTabStripModelObserver::CHANGE);
560    EXPECT_TRUE(observer.StateEquals(0, s1));
561    observer.ClearStates();
562  }
563
564  // Test SelectNextTab, SelectPreviousTab, SelectLastTab
565  {
566    // Make sure the second of the two tabs is selected first...
567    tabstrip.ActivateTabAt(1, true);
568    tabstrip.SelectPreviousTab();
569    EXPECT_EQ(0, tabstrip.active_index());
570    tabstrip.SelectLastTab();
571    EXPECT_EQ(1, tabstrip.active_index());
572    tabstrip.SelectNextTab();
573    EXPECT_EQ(0, tabstrip.active_index());
574  }
575
576  // Test CloseSelectedTabs
577  {
578    tabstrip.CloseSelectedTabs();
579    // |CloseSelectedTabs| calls CloseTabContentsAt, we already tested that, now
580    // just verify that the count and selected index have changed
581    // appropriately...
582    EXPECT_EQ(1, tabstrip.count());
583    EXPECT_EQ(0, tabstrip.active_index());
584  }
585
586  tabstrip.CloseAllTabs();
587  // TabStripModel should now be empty.
588  EXPECT_TRUE(tabstrip.empty());
589
590  // Opener methods are tested below...
591
592  tabstrip.RemoveObserver(&observer);
593}
594
595TEST_F(TabStripModelTest, TestBasicOpenerAPI) {
596  TabStripDummyDelegate delegate(NULL);
597  TabStripModel tabstrip(&delegate, profile());
598  EXPECT_TRUE(tabstrip.empty());
599
600  // This is a basic test of opener functionality. opener_contents is created
601  // as the first tab in the strip and then we create 5 other tabs in the
602  // background with opener_contents set as their opener.
603
604  TabContentsWrapper* opener_contents = CreateTabContents();
605  NavigationController* opener = &opener_contents->controller();
606  tabstrip.AppendTabContents(opener_contents, true);
607  TabContentsWrapper* contents1 = CreateTabContents();
608  TabContentsWrapper* contents2 = CreateTabContents();
609  TabContentsWrapper* contents3 = CreateTabContents();
610  TabContentsWrapper* contents4 = CreateTabContents();
611  TabContentsWrapper* contents5 = CreateTabContents();
612
613  // We use |InsertTabContentsAt| here instead of AppendTabContents so that
614  // openership relationships are preserved.
615  tabstrip.InsertTabContentsAt(tabstrip.count(), contents1,
616                               TabStripModel::ADD_INHERIT_GROUP);
617  tabstrip.InsertTabContentsAt(tabstrip.count(), contents2,
618                               TabStripModel::ADD_INHERIT_GROUP);
619  tabstrip.InsertTabContentsAt(tabstrip.count(), contents3,
620                               TabStripModel::ADD_INHERIT_GROUP);
621  tabstrip.InsertTabContentsAt(tabstrip.count(), contents4,
622                               TabStripModel::ADD_INHERIT_GROUP);
623  tabstrip.InsertTabContentsAt(tabstrip.count(), contents5,
624                               TabStripModel::ADD_INHERIT_GROUP);
625
626  // All the tabs should have the same opener.
627  for (int i = 1; i < tabstrip.count(); ++i)
628    EXPECT_EQ(opener, tabstrip.GetOpenerOfTabContentsAt(i));
629
630  // If there is a next adjacent item, then the index should be of that item.
631  EXPECT_EQ(2, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 1, false));
632  // If the last tab in the group is closed, the preceding tab in the same
633  // group should be selected.
634  EXPECT_EQ(4, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 5, false));
635
636  // Tests the method that finds the last tab opened by the same opener in the
637  // strip (this is the insertion index for the next background tab for the
638  // specified opener).
639  EXPECT_EQ(5, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1));
640
641  // For a tab that has opened no other tabs, the return value should always be
642  // -1...
643  NavigationController* o1 = &contents1->controller();
644  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(o1, 3, false));
645  EXPECT_EQ(-1, tabstrip.GetIndexOfLastTabContentsOpenedBy(o1, 3));
646
647  // ForgetAllOpeners should destroy all opener relationships.
648  tabstrip.ForgetAllOpeners();
649  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 1, false));
650  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 5, false));
651  EXPECT_EQ(-1, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1));
652
653  tabstrip.CloseAllTabs();
654  EXPECT_TRUE(tabstrip.empty());
655}
656
657static int GetInsertionIndex(TabStripModel* tabstrip,
658                             TabContentsWrapper* contents) {
659  return tabstrip->order_controller()->DetermineInsertionIndex(
660      contents, PageTransition::LINK, false);
661}
662
663static void InsertTabContentses(TabStripModel* tabstrip,
664                                TabContentsWrapper* contents1,
665                                TabContentsWrapper* contents2,
666                                TabContentsWrapper* contents3) {
667  tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents1),
668                                contents1, TabStripModel::ADD_INHERIT_GROUP);
669  tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents2),
670                                contents2, TabStripModel::ADD_INHERIT_GROUP);
671  tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents3),
672                                contents3, TabStripModel::ADD_INHERIT_GROUP);
673}
674
675// Tests opening background tabs.
676TEST_F(TabStripModelTest, TestLTRInsertionOptions) {
677  TabStripDummyDelegate delegate(NULL);
678  TabStripModel tabstrip(&delegate, profile());
679  EXPECT_TRUE(tabstrip.empty());
680
681  TabContentsWrapper* opener_contents = CreateTabContents();
682  tabstrip.AppendTabContents(opener_contents, true);
683
684  TabContentsWrapper* contents1 = CreateTabContents();
685  TabContentsWrapper* contents2 = CreateTabContents();
686  TabContentsWrapper* contents3 = CreateTabContents();
687
688  // Test LTR
689  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
690  EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(1));
691  EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(2));
692  EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(3));
693
694  tabstrip.CloseAllTabs();
695  EXPECT_TRUE(tabstrip.empty());
696}
697
698// Tests inserting tabs with InsertAfter set to false.
699TEST_F(TabStripModelTest, InsertBefore) {
700  TabStripDummyDelegate delegate(NULL);
701  TabStripModel tabstrip(&delegate, profile());
702  tabstrip.SetInsertionPolicy(TabStripModel::INSERT_BEFORE);
703  EXPECT_TRUE(tabstrip.empty());
704
705  TabContentsWrapper* contents1 = CreateTabContents();
706  TabContentsWrapper* contents2 = CreateTabContents();
707  TabContentsWrapper* contents3 = CreateTabContents();
708
709  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
710
711  // The order should be reversed.
712  EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(0));
713  EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(1));
714  EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(2));
715
716  tabstrip.CloseAllTabs();
717  EXPECT_TRUE(tabstrip.empty());
718}
719
720// Tests opening background tabs with InsertAfter set to false.
721TEST_F(TabStripModelTest, InsertBeforeOpeners) {
722  TabStripDummyDelegate delegate(NULL);
723  TabStripModel tabstrip(&delegate, profile());
724  tabstrip.SetInsertionPolicy(TabStripModel::INSERT_BEFORE);
725  EXPECT_TRUE(tabstrip.empty());
726  TabContentsWrapper* opener_contents = CreateTabContents();
727  tabstrip.AppendTabContents(opener_contents, true);
728
729  TabContentsWrapper* contents1 = CreateTabContents();
730  TabContentsWrapper* contents2 = CreateTabContents();
731  TabContentsWrapper* contents3 = CreateTabContents();
732
733  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
734
735  // The order should be reversed.
736  EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(0));
737  EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(1));
738  EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(2));
739
740  tabstrip.CloseAllTabs();
741  EXPECT_TRUE(tabstrip.empty());
742}
743
744// This test constructs a tabstrip, and then simulates loading several tabs in
745// the background from link clicks on the first tab. Then it simulates opening
746// a new tab from the first tab in the foreground via a link click, verifies
747// that this tab is opened adjacent to the opener, then closes it.
748// Finally it tests that a tab opened for some non-link purpose openes at the
749// end of the strip, not bundled to any existing context.
750TEST_F(TabStripModelTest, TestInsertionIndexDetermination) {
751  TabStripDummyDelegate delegate(NULL);
752  TabStripModel tabstrip(&delegate, profile());
753  EXPECT_TRUE(tabstrip.empty());
754
755  TabContentsWrapper* opener_contents = CreateTabContents();
756  NavigationController* opener = &opener_contents->controller();
757  tabstrip.AppendTabContents(opener_contents, true);
758
759  // Open some other random unrelated tab in the background to monkey with our
760  // insertion index.
761  TabContentsWrapper* other_contents = CreateTabContents();
762  tabstrip.AppendTabContents(other_contents, false);
763
764  TabContentsWrapper* contents1 = CreateTabContents();
765  TabContentsWrapper* contents2 = CreateTabContents();
766  TabContentsWrapper* contents3 = CreateTabContents();
767
768  // Start by testing LTR
769  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
770  EXPECT_EQ(opener_contents, tabstrip.GetTabContentsAt(0));
771  EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(1));
772  EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(2));
773  EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(3));
774  EXPECT_EQ(other_contents, tabstrip.GetTabContentsAt(4));
775
776  // The opener API should work...
777  EXPECT_EQ(3, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 2, false));
778  EXPECT_EQ(2, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 3, false));
779  EXPECT_EQ(3, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1));
780
781  // Now open a foreground tab from a link. It should be opened adjacent to the
782  // opener tab.
783  TabContentsWrapper* fg_link_contents = CreateTabContents();
784  int insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
785      fg_link_contents, PageTransition::LINK, true);
786  EXPECT_EQ(1, insert_index);
787  tabstrip.InsertTabContentsAt(insert_index, fg_link_contents,
788                               TabStripModel::ADD_ACTIVE |
789                               TabStripModel::ADD_INHERIT_GROUP);
790  EXPECT_EQ(1, tabstrip.active_index());
791  EXPECT_EQ(fg_link_contents, tabstrip.GetSelectedTabContents());
792
793  // Now close this contents. The selection should move to the opener contents.
794  tabstrip.CloseSelectedTabs();
795  EXPECT_EQ(0, tabstrip.active_index());
796
797  // Now open a new empty tab. It should open at the end of the strip.
798  TabContentsWrapper* fg_nonlink_contents = CreateTabContents();
799  insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
800      fg_nonlink_contents, PageTransition::AUTO_BOOKMARK, true);
801  EXPECT_EQ(tabstrip.count(), insert_index);
802  // We break the opener relationship...
803  tabstrip.InsertTabContentsAt(insert_index, fg_nonlink_contents,
804                               TabStripModel::ADD_NONE);
805  // Now select it, so that user_gesture == true causes the opener relationship
806  // to be forgotten...
807  tabstrip.ActivateTabAt(tabstrip.count() - 1, true);
808  EXPECT_EQ(tabstrip.count() - 1, tabstrip.active_index());
809  EXPECT_EQ(fg_nonlink_contents, tabstrip.GetSelectedTabContents());
810
811  // Verify that all opener relationships are forgotten.
812  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 2, false));
813  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 3, false));
814  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 3, false));
815  EXPECT_EQ(-1, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1));
816
817  tabstrip.CloseAllTabs();
818  EXPECT_TRUE(tabstrip.empty());
819}
820
821// Tests that selection is shifted to the correct tab when a tab is closed.
822// If a tab is in the background when it is closed, the selection does not
823// change.
824// If a tab is in the foreground (selected),
825//   If that tab does not have an opener, selection shifts to the right.
826//   If the tab has an opener,
827//     The next tab (scanning LTR) in the entire strip that has the same opener
828//     is selected
829//     If there are no other tabs that have the same opener,
830//       The opener is selected
831//
832TEST_F(TabStripModelTest, TestSelectOnClose) {
833  TabStripDummyDelegate delegate(NULL);
834  TabStripModel tabstrip(&delegate, profile());
835  EXPECT_TRUE(tabstrip.empty());
836
837  TabContentsWrapper* opener_contents = CreateTabContents();
838  tabstrip.AppendTabContents(opener_contents, true);
839
840  TabContentsWrapper* contents1 = CreateTabContents();
841  TabContentsWrapper* contents2 = CreateTabContents();
842  TabContentsWrapper* contents3 = CreateTabContents();
843
844  // Note that we use Detach instead of Close throughout this test to avoid
845  // having to keep reconstructing these TabContentses.
846
847  // First test that closing tabs that are in the background doesn't adjust the
848  // current selection.
849  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
850  EXPECT_EQ(0, tabstrip.active_index());
851
852  tabstrip.DetachTabContentsAt(1);
853  EXPECT_EQ(0, tabstrip.active_index());
854
855  for (int i = tabstrip.count() - 1; i >= 1; --i)
856    tabstrip.DetachTabContentsAt(i);
857
858  // Now test that when a tab doesn't have an opener, selection shifts to the
859  // right when the tab is closed.
860  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
861  EXPECT_EQ(0, tabstrip.active_index());
862
863  tabstrip.ForgetAllOpeners();
864  tabstrip.ActivateTabAt(1, true);
865  EXPECT_EQ(1, tabstrip.active_index());
866  tabstrip.DetachTabContentsAt(1);
867  EXPECT_EQ(1, tabstrip.active_index());
868  tabstrip.DetachTabContentsAt(1);
869  EXPECT_EQ(1, tabstrip.active_index());
870  tabstrip.DetachTabContentsAt(1);
871  EXPECT_EQ(0, tabstrip.active_index());
872
873  for (int i = tabstrip.count() - 1; i >= 1; --i)
874    tabstrip.DetachTabContentsAt(i);
875
876  // Now test that when a tab does have an opener, it selects the next tab
877  // opened by the same opener scanning LTR when it is closed.
878  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
879  EXPECT_EQ(0, tabstrip.active_index());
880  tabstrip.ActivateTabAt(2, false);
881  EXPECT_EQ(2, tabstrip.active_index());
882  tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE);
883  EXPECT_EQ(2, tabstrip.active_index());
884  tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE);
885  EXPECT_EQ(1, tabstrip.active_index());
886  tabstrip.CloseTabContentsAt(1, TabStripModel::CLOSE_NONE);
887  EXPECT_EQ(0, tabstrip.active_index());
888  // Finally test that when a tab has no "siblings" that the opener is
889  // selected.
890  TabContentsWrapper* other_contents = CreateTabContents();
891  tabstrip.InsertTabContentsAt(1, other_contents, TabStripModel::ADD_NONE);
892  EXPECT_EQ(2, tabstrip.count());
893  TabContentsWrapper* opened_contents = CreateTabContents();
894  tabstrip.InsertTabContentsAt(2, opened_contents,
895                               TabStripModel::ADD_ACTIVE |
896                               TabStripModel::ADD_INHERIT_GROUP);
897  EXPECT_EQ(2, tabstrip.active_index());
898  tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE);
899  EXPECT_EQ(0, tabstrip.active_index());
900
901  tabstrip.CloseAllTabs();
902  EXPECT_TRUE(tabstrip.empty());
903}
904
905// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
906// CommandCloseTab.
907TEST_F(TabStripModelTest, CommandCloseTab) {
908  TabStripDummyDelegate delegate(NULL);
909  TabStripModel tabstrip(&delegate, profile());
910  EXPECT_TRUE(tabstrip.empty());
911
912  // Make sure can_close is honored.
913  ASSERT_NO_FATAL_FAILURE(
914      PrepareTabstripForSelectionTest(&tabstrip, 1, 0, "0"));
915  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
916                  0, TabStripModel::CommandCloseTab));
917  delegate.set_can_close(false);
918  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
919                   0, TabStripModel::CommandCloseTab));
920  delegate.set_can_close(true);
921  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
922  ASSERT_TRUE(tabstrip.empty());
923
924  // Make sure close on a tab that is selected effects all the selected tabs.
925  ASSERT_NO_FATAL_FAILURE(
926      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
927  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
928                  0, TabStripModel::CommandCloseTab));
929  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
930  // Should have closed tabs 0 and 1.
931  EXPECT_EQ("2", GetPinnedState(tabstrip));
932
933  tabstrip.CloseAllTabs();
934  EXPECT_TRUE(tabstrip.empty());
935
936  // Select two tabs and make close on a tab that isn't selected doesn't effect
937  // selected tabs.
938  ASSERT_NO_FATAL_FAILURE(
939      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
940  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
941                  2, TabStripModel::CommandCloseTab));
942  tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab);
943  // Should have closed tab 2.
944  EXPECT_EQ("0 1", GetPinnedState(tabstrip));
945  tabstrip.CloseAllTabs();
946  EXPECT_TRUE(tabstrip.empty());
947
948  // Tests with 3 tabs, one pinned, two tab selected, one of which is pinned.
949  ASSERT_NO_FATAL_FAILURE(
950      PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1"));
951  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
952                  0, TabStripModel::CommandCloseTab));
953  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
954  // Should have closed tab 2.
955  EXPECT_EQ("2", GetPinnedState(tabstrip));
956  tabstrip.CloseAllTabs();
957  EXPECT_TRUE(tabstrip.empty());
958}
959
960// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
961// CommandCloseTabs.
962TEST_F(TabStripModelTest, CommandCloseOtherTabs) {
963  TabStripDummyDelegate delegate(NULL);
964  TabStripModel tabstrip(&delegate, profile());
965  EXPECT_TRUE(tabstrip.empty());
966
967  // Create three tabs, select two tabs, CommandCloseOtherTabs should be enabled
968  // and close two tabs.
969  ASSERT_NO_FATAL_FAILURE(
970      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
971  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
972                  0, TabStripModel::CommandCloseOtherTabs));
973  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs);
974  EXPECT_EQ("0 1", GetPinnedState(tabstrip));
975  tabstrip.CloseAllTabs();
976  EXPECT_TRUE(tabstrip.empty());
977
978  // Select two tabs, CommandCloseOtherTabs should be enabled and invoking it
979  // with a non-selected index should close the two other tabs.
980  ASSERT_NO_FATAL_FAILURE(
981      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
982  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
983                  2, TabStripModel::CommandCloseOtherTabs));
984  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs);
985  EXPECT_EQ("0 1", GetPinnedState(tabstrip));
986  tabstrip.CloseAllTabs();
987  EXPECT_TRUE(tabstrip.empty());
988
989  // Select all, CommandCloseOtherTabs should not be enabled.
990  ASSERT_NO_FATAL_FAILURE(
991      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1 2"));
992  EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
993                  2, TabStripModel::CommandCloseOtherTabs));
994  tabstrip.CloseAllTabs();
995  EXPECT_TRUE(tabstrip.empty());
996
997  // Three tabs, pin one, select the two non-pinned.
998  ASSERT_NO_FATAL_FAILURE(
999      PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1 2"));
1000  EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1001                  1, TabStripModel::CommandCloseOtherTabs));
1002  // If we don't pass in the pinned index, the command should be enabled.
1003  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1004                  0, TabStripModel::CommandCloseOtherTabs));
1005  tabstrip.CloseAllTabs();
1006  EXPECT_TRUE(tabstrip.empty());
1007
1008  // 3 tabs, one pinned.
1009  ASSERT_NO_FATAL_FAILURE(
1010      PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1"));
1011  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1012                  1, TabStripModel::CommandCloseOtherTabs));
1013  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1014                  0, TabStripModel::CommandCloseOtherTabs));
1015  tabstrip.ExecuteContextMenuCommand(1, TabStripModel::CommandCloseOtherTabs);
1016  // The pinned tab shouldn't be closed.
1017  EXPECT_EQ("0p 1", GetPinnedState(tabstrip));
1018  tabstrip.CloseAllTabs();
1019  EXPECT_TRUE(tabstrip.empty());
1020}
1021
1022// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1023// CommandCloseTabsToRight.
1024TEST_F(TabStripModelTest, CommandCloseTabsToRight) {
1025  TabStripDummyDelegate delegate(NULL);
1026  TabStripModel tabstrip(&delegate, profile());
1027  EXPECT_TRUE(tabstrip.empty());
1028
1029  // Create three tabs, select last two tabs, CommandCloseTabsToRight should
1030  // only be enabled for the first tab.
1031  ASSERT_NO_FATAL_FAILURE(
1032      PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "1 2"));
1033  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1034                  0, TabStripModel::CommandCloseTabsToRight));
1035  EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1036                   1, TabStripModel::CommandCloseTabsToRight));
1037  EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1038                   2, TabStripModel::CommandCloseTabsToRight));
1039  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight);
1040  EXPECT_EQ("0", GetPinnedState(tabstrip));
1041  tabstrip.CloseAllTabs();
1042  EXPECT_TRUE(tabstrip.empty());
1043}
1044
1045// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1046// CommandTogglePinned.
1047TEST_F(TabStripModelTest, CommandTogglePinned) {
1048  TabStripDummyDelegate delegate(NULL);
1049  TabStripModel tabstrip(&delegate, profile());
1050  EXPECT_TRUE(tabstrip.empty());
1051
1052  // Create three tabs with one pinned, pin the first two.
1053  ASSERT_NO_FATAL_FAILURE(
1054      PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1"));
1055  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1056                  0, TabStripModel::CommandTogglePinned));
1057  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1058                  1, TabStripModel::CommandTogglePinned));
1059  EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1060                  2, TabStripModel::CommandTogglePinned));
1061  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned);
1062  EXPECT_EQ("0p 1p 2", GetPinnedState(tabstrip));
1063
1064  // Execute CommandTogglePinned again, this should unpin.
1065  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned);
1066  EXPECT_EQ("0 1 2", GetPinnedState(tabstrip));
1067
1068  // Pin the last.
1069  tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandTogglePinned);
1070  EXPECT_EQ("2p 0 1", GetPinnedState(tabstrip));
1071
1072  tabstrip.CloseAllTabs();
1073  EXPECT_TRUE(tabstrip.empty());
1074}
1075
1076// Tests the following context menu commands:
1077//  - Close Tab
1078//  - Close Other Tabs
1079//  - Close Tabs To Right
1080TEST_F(TabStripModelTest, TestContextMenuCloseCommands) {
1081  TabStripDummyDelegate delegate(NULL);
1082  TabStripModel tabstrip(&delegate, profile());
1083  EXPECT_TRUE(tabstrip.empty());
1084
1085  TabContentsWrapper* opener_contents = CreateTabContents();
1086  tabstrip.AppendTabContents(opener_contents, true);
1087
1088  TabContentsWrapper* contents1 = CreateTabContents();
1089  TabContentsWrapper* contents2 = CreateTabContents();
1090  TabContentsWrapper* contents3 = CreateTabContents();
1091
1092  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
1093  EXPECT_EQ(0, tabstrip.active_index());
1094
1095  tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab);
1096  EXPECT_EQ(3, tabstrip.count());
1097
1098  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight);
1099  EXPECT_EQ(1, tabstrip.count());
1100  EXPECT_EQ(opener_contents, tabstrip.GetSelectedTabContents());
1101
1102  TabContentsWrapper* dummy_contents = CreateTabContents();
1103  tabstrip.AppendTabContents(dummy_contents, false);
1104
1105  contents1 = CreateTabContents();
1106  contents2 = CreateTabContents();
1107  contents3 = CreateTabContents();
1108  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
1109  EXPECT_EQ(5, tabstrip.count());
1110
1111  int dummy_index = tabstrip.count() - 1;
1112  tabstrip.ActivateTabAt(dummy_index, true);
1113  EXPECT_EQ(dummy_contents, tabstrip.GetSelectedTabContents());
1114
1115  tabstrip.ExecuteContextMenuCommand(dummy_index,
1116                                     TabStripModel::CommandCloseOtherTabs);
1117  EXPECT_EQ(1, tabstrip.count());
1118  EXPECT_EQ(dummy_contents, tabstrip.GetSelectedTabContents());
1119
1120  tabstrip.CloseAllTabs();
1121  EXPECT_TRUE(tabstrip.empty());
1122}
1123
1124// Tests GetIndicesClosedByCommand.
1125TEST_F(TabStripModelTest, GetIndicesClosedByCommand) {
1126  TabStripDummyDelegate delegate(NULL);
1127  TabStripModel tabstrip(&delegate, profile());
1128  EXPECT_TRUE(tabstrip.empty());
1129
1130  TabContentsWrapper* contents1 = CreateTabContents();
1131  TabContentsWrapper* contents2 = CreateTabContents();
1132  TabContentsWrapper* contents3 = CreateTabContents();
1133  TabContentsWrapper* contents4 = CreateTabContents();
1134  TabContentsWrapper* contents5 = CreateTabContents();
1135
1136  tabstrip.AppendTabContents(contents1, true);
1137  tabstrip.AppendTabContents(contents2, true);
1138  tabstrip.AppendTabContents(contents3, true);
1139  tabstrip.AppendTabContents(contents4, true);
1140  tabstrip.AppendTabContents(contents5, true);
1141
1142  EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1143                tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
1144  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1145                tabstrip, 1, TabStripModel::CommandCloseTabsToRight));
1146
1147  EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1148                tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
1149  EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString(
1150                tabstrip, 1, TabStripModel::CommandCloseOtherTabs));
1151
1152  // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other
1153  // commands.
1154  tabstrip.SetTabPinned(0, true);
1155  tabstrip.SetTabPinned(1, true);
1156
1157  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1158                tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
1159  EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1160                tabstrip, 2, TabStripModel::CommandCloseTabsToRight));
1161
1162  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1163                tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
1164  EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1165                tabstrip, 2, TabStripModel::CommandCloseOtherTabs));
1166
1167  tabstrip.CloseAllTabs();
1168  EXPECT_TRUE(tabstrip.empty());
1169}
1170
1171// Tests whether or not TabContentses are inserted in the correct position
1172// using this "smart" function with a simulated middle click action on a series
1173// of links on the home page.
1174TEST_F(TabStripModelTest, AddTabContents_MiddleClickLinksAndClose) {
1175  TabStripDummyDelegate delegate(NULL);
1176  TabStripModel tabstrip(&delegate, profile());
1177  EXPECT_TRUE(tabstrip.empty());
1178
1179  // Open the Home Page
1180  TabContentsWrapper* homepage_contents = CreateTabContents();
1181  tabstrip.AddTabContents(
1182      homepage_contents, -1, PageTransition::AUTO_BOOKMARK,
1183      TabStripModel::ADD_ACTIVE);
1184
1185  // Open some other tab, by user typing.
1186  TabContentsWrapper* typed_page_contents = CreateTabContents();
1187  tabstrip.AddTabContents(
1188      typed_page_contents, -1, PageTransition::TYPED,
1189      TabStripModel::ADD_ACTIVE);
1190
1191  EXPECT_EQ(2, tabstrip.count());
1192
1193  // Re-select the home page.
1194  tabstrip.ActivateTabAt(0, true);
1195
1196  // Open a bunch of tabs by simulating middle clicking on links on the home
1197  // page.
1198  TabContentsWrapper* middle_click_contents1 = CreateTabContents();
1199  tabstrip.AddTabContents(
1200      middle_click_contents1, -1, PageTransition::LINK,
1201      TabStripModel::ADD_NONE);
1202  TabContentsWrapper* middle_click_contents2 = CreateTabContents();
1203  tabstrip.AddTabContents(
1204      middle_click_contents2, -1, PageTransition::LINK,
1205      TabStripModel::ADD_NONE);
1206  TabContentsWrapper* middle_click_contents3 = CreateTabContents();
1207  tabstrip.AddTabContents(
1208      middle_click_contents3, -1, PageTransition::LINK,
1209      TabStripModel::ADD_NONE);
1210
1211  EXPECT_EQ(5, tabstrip.count());
1212
1213  EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0));
1214  EXPECT_EQ(middle_click_contents1, tabstrip.GetTabContentsAt(1));
1215  EXPECT_EQ(middle_click_contents2, tabstrip.GetTabContentsAt(2));
1216  EXPECT_EQ(middle_click_contents3, tabstrip.GetTabContentsAt(3));
1217  EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(4));
1218
1219  // Now simulate seleting a tab in the middle of the group of tabs opened from
1220  // the home page and start closing them. Each TabContents in the group should
1221  // be closed, right to left. This test is constructed to start at the middle
1222  // TabContents in the group to make sure the cursor wraps around to the first
1223  // TabContents in the group before closing the opener or any other
1224  // TabContents.
1225  tabstrip.ActivateTabAt(2, true);
1226  tabstrip.CloseSelectedTabs();
1227  EXPECT_EQ(middle_click_contents3, tabstrip.GetSelectedTabContents());
1228  tabstrip.CloseSelectedTabs();
1229  EXPECT_EQ(middle_click_contents1, tabstrip.GetSelectedTabContents());
1230  tabstrip.CloseSelectedTabs();
1231  EXPECT_EQ(homepage_contents, tabstrip.GetSelectedTabContents());
1232  tabstrip.CloseSelectedTabs();
1233  EXPECT_EQ(typed_page_contents, tabstrip.GetSelectedTabContents());
1234
1235  EXPECT_EQ(1, tabstrip.count());
1236
1237  tabstrip.CloseAllTabs();
1238  EXPECT_TRUE(tabstrip.empty());
1239}
1240
1241// Tests whether or not a TabContents created by a left click on a link that
1242// opens a new tab is inserted correctly adjacent to the tab that spawned it.
1243TEST_F(TabStripModelTest, AddTabContents_LeftClickPopup) {
1244  TabStripDummyDelegate delegate(NULL);
1245  TabStripModel tabstrip(&delegate, profile());
1246  EXPECT_TRUE(tabstrip.empty());
1247
1248  // Open the Home Page
1249  TabContentsWrapper* homepage_contents = CreateTabContents();
1250  tabstrip.AddTabContents(
1251      homepage_contents, -1, PageTransition::AUTO_BOOKMARK,
1252      TabStripModel::ADD_ACTIVE);
1253
1254  // Open some other tab, by user typing.
1255  TabContentsWrapper* typed_page_contents = CreateTabContents();
1256  tabstrip.AddTabContents(
1257      typed_page_contents, -1, PageTransition::TYPED,
1258      TabStripModel::ADD_ACTIVE);
1259
1260  EXPECT_EQ(2, tabstrip.count());
1261
1262  // Re-select the home page.
1263  tabstrip.ActivateTabAt(0, true);
1264
1265  // Open a tab by simulating a left click on a link that opens in a new tab.
1266  TabContentsWrapper* left_click_contents = CreateTabContents();
1267  tabstrip.AddTabContents(left_click_contents, -1, PageTransition::LINK,
1268                          TabStripModel::ADD_ACTIVE);
1269
1270  // Verify the state meets our expectations.
1271  EXPECT_EQ(3, tabstrip.count());
1272  EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0));
1273  EXPECT_EQ(left_click_contents, tabstrip.GetTabContentsAt(1));
1274  EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(2));
1275
1276  // The newly created tab should be selected.
1277  EXPECT_EQ(left_click_contents, tabstrip.GetSelectedTabContents());
1278
1279  // After closing the selected tab, the selection should move to the left, to
1280  // the opener.
1281  tabstrip.CloseSelectedTabs();
1282  EXPECT_EQ(homepage_contents, tabstrip.GetSelectedTabContents());
1283
1284  EXPECT_EQ(2, tabstrip.count());
1285
1286  tabstrip.CloseAllTabs();
1287  EXPECT_TRUE(tabstrip.empty());
1288}
1289
1290// Tests whether or not new tabs that should split context (typed pages,
1291// generated urls, also blank tabs) open at the end of the tabstrip instead of
1292// in the middle.
1293TEST_F(TabStripModelTest, AddTabContents_CreateNewBlankTab) {
1294  TabStripDummyDelegate delegate(NULL);
1295  TabStripModel tabstrip(&delegate, profile());
1296  EXPECT_TRUE(tabstrip.empty());
1297
1298  // Open the Home Page
1299  TabContentsWrapper* homepage_contents = CreateTabContents();
1300  tabstrip.AddTabContents(
1301      homepage_contents, -1, PageTransition::AUTO_BOOKMARK,
1302      TabStripModel::ADD_ACTIVE);
1303
1304  // Open some other tab, by user typing.
1305  TabContentsWrapper* typed_page_contents = CreateTabContents();
1306  tabstrip.AddTabContents(
1307      typed_page_contents, -1, PageTransition::TYPED,
1308      TabStripModel::ADD_ACTIVE);
1309
1310  EXPECT_EQ(2, tabstrip.count());
1311
1312  // Re-select the home page.
1313  tabstrip.ActivateTabAt(0, true);
1314
1315  // Open a new blank tab in the foreground.
1316  TabContentsWrapper* new_blank_contents = CreateTabContents();
1317  tabstrip.AddTabContents(new_blank_contents, -1, PageTransition::TYPED,
1318                          TabStripModel::ADD_ACTIVE);
1319
1320  // Verify the state of the tabstrip.
1321  EXPECT_EQ(3, tabstrip.count());
1322  EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0));
1323  EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(1));
1324  EXPECT_EQ(new_blank_contents, tabstrip.GetTabContentsAt(2));
1325
1326  // Now open a couple more blank tabs in the background.
1327  TabContentsWrapper* background_blank_contents1 = CreateTabContents();
1328  tabstrip.AddTabContents(
1329      background_blank_contents1, -1, PageTransition::TYPED,
1330      TabStripModel::ADD_NONE);
1331  TabContentsWrapper* background_blank_contents2 = CreateTabContents();
1332  tabstrip.AddTabContents(
1333      background_blank_contents2, -1, PageTransition::GENERATED,
1334      TabStripModel::ADD_NONE);
1335  EXPECT_EQ(5, tabstrip.count());
1336  EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0));
1337  EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(1));
1338  EXPECT_EQ(new_blank_contents, tabstrip.GetTabContentsAt(2));
1339  EXPECT_EQ(background_blank_contents1, tabstrip.GetTabContentsAt(3));
1340  EXPECT_EQ(background_blank_contents2, tabstrip.GetTabContentsAt(4));
1341
1342  tabstrip.CloseAllTabs();
1343  EXPECT_TRUE(tabstrip.empty());
1344}
1345
1346// Tests whether opener state is correctly forgotten when the user switches
1347// context.
1348TEST_F(TabStripModelTest, AddTabContents_ForgetOpeners) {
1349  TabStripDummyDelegate delegate(NULL);
1350  TabStripModel tabstrip(&delegate, profile());
1351  EXPECT_TRUE(tabstrip.empty());
1352
1353  // Open the Home Page
1354  TabContentsWrapper* homepage_contents = CreateTabContents();
1355  tabstrip.AddTabContents(
1356      homepage_contents, -1, PageTransition::AUTO_BOOKMARK,
1357      TabStripModel::ADD_ACTIVE);
1358
1359  // Open some other tab, by user typing.
1360  TabContentsWrapper* typed_page_contents = CreateTabContents();
1361  tabstrip.AddTabContents(
1362      typed_page_contents, -1, PageTransition::TYPED,
1363      TabStripModel::ADD_ACTIVE);
1364
1365  EXPECT_EQ(2, tabstrip.count());
1366
1367  // Re-select the home page.
1368  tabstrip.ActivateTabAt(0, true);
1369
1370  // Open a bunch of tabs by simulating middle clicking on links on the home
1371  // page.
1372  TabContentsWrapper* middle_click_contents1 = CreateTabContents();
1373  tabstrip.AddTabContents(
1374      middle_click_contents1, -1, PageTransition::LINK,
1375      TabStripModel::ADD_NONE);
1376  TabContentsWrapper* middle_click_contents2 = CreateTabContents();
1377  tabstrip.AddTabContents(
1378      middle_click_contents2, -1, PageTransition::LINK,
1379      TabStripModel::ADD_NONE);
1380  TabContentsWrapper* middle_click_contents3 = CreateTabContents();
1381  tabstrip.AddTabContents(
1382      middle_click_contents3, -1, PageTransition::LINK,
1383      TabStripModel::ADD_NONE);
1384
1385  // Break out of the context by selecting a tab in a different context.
1386  EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(4));
1387  tabstrip.SelectLastTab();
1388  EXPECT_EQ(typed_page_contents, tabstrip.GetSelectedTabContents());
1389
1390  // Step back into the context by selecting a tab inside it.
1391  tabstrip.ActivateTabAt(2, true);
1392  EXPECT_EQ(middle_click_contents2, tabstrip.GetSelectedTabContents());
1393
1394  // Now test that closing tabs selects to the right until there are no more,
1395  // then to the left, as if there were no context (context has been
1396  // successfully forgotten).
1397  tabstrip.CloseSelectedTabs();
1398  EXPECT_EQ(middle_click_contents3, tabstrip.GetSelectedTabContents());
1399  tabstrip.CloseSelectedTabs();
1400  EXPECT_EQ(typed_page_contents, tabstrip.GetSelectedTabContents());
1401  tabstrip.CloseSelectedTabs();
1402  EXPECT_EQ(middle_click_contents1, tabstrip.GetSelectedTabContents());
1403  tabstrip.CloseSelectedTabs();
1404  EXPECT_EQ(homepage_contents, tabstrip.GetSelectedTabContents());
1405
1406  EXPECT_EQ(1, tabstrip.count());
1407
1408  tabstrip.CloseAllTabs();
1409  EXPECT_TRUE(tabstrip.empty());
1410}
1411
1412// Added for http://b/issue?id=958960
1413TEST_F(TabStripModelTest, AppendContentsReselectionTest) {
1414  TabContents* fake_destinations_tab =
1415      new TabContents(profile(), NULL, 0, NULL, NULL);
1416  TabContentsWrapper wrapper(fake_destinations_tab);
1417  TabStripDummyDelegate delegate(&wrapper);
1418  TabStripModel tabstrip(&delegate, profile());
1419  EXPECT_TRUE(tabstrip.empty());
1420
1421  // Open the Home Page
1422  TabContentsWrapper* homepage_contents = CreateTabContents();
1423  tabstrip.AddTabContents(
1424      homepage_contents, -1, PageTransition::AUTO_BOOKMARK,
1425      TabStripModel::ADD_ACTIVE);
1426
1427  // Open some other tab, by user typing.
1428  TabContentsWrapper* typed_page_contents = CreateTabContents();
1429  tabstrip.AddTabContents(
1430      typed_page_contents, -1, PageTransition::TYPED,
1431      TabStripModel::ADD_NONE);
1432
1433  // The selected tab should still be the first.
1434  EXPECT_EQ(0, tabstrip.active_index());
1435
1436  // Now simulate a link click that opens a new tab (by virtue of target=_blank)
1437  // and make sure the right tab gets selected when the new tab is closed.
1438  TabContentsWrapper* target_blank_contents = CreateTabContents();
1439  tabstrip.AppendTabContents(target_blank_contents, true);
1440  EXPECT_EQ(2, tabstrip.active_index());
1441  tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE);
1442  EXPECT_EQ(0, tabstrip.active_index());
1443
1444  // clean up after ourselves
1445  tabstrip.CloseAllTabs();
1446}
1447
1448// Added for http://b/issue?id=1027661
1449TEST_F(TabStripModelTest, ReselectionConsidersChildrenTest) {
1450  TabStripDummyDelegate delegate(NULL);
1451  TabStripModel strip(&delegate, profile());
1452
1453  // Open page A
1454  TabContentsWrapper* page_a_contents = CreateTabContents();
1455  strip.AddTabContents(
1456      page_a_contents, -1, PageTransition::AUTO_BOOKMARK,
1457      TabStripModel::ADD_ACTIVE);
1458
1459  // Simulate middle click to open page A.A and A.B
1460  TabContentsWrapper* page_a_a_contents = CreateTabContents();
1461  strip.AddTabContents(page_a_a_contents, -1, PageTransition::LINK,
1462                       TabStripModel::ADD_NONE);
1463  TabContentsWrapper* page_a_b_contents = CreateTabContents();
1464  strip.AddTabContents(page_a_b_contents, -1, PageTransition::LINK,
1465                       TabStripModel::ADD_NONE);
1466
1467  // Select page A.A
1468  strip.ActivateTabAt(1, true);
1469  EXPECT_EQ(page_a_a_contents, strip.GetSelectedTabContents());
1470
1471  // Simulate a middle click to open page A.A.A
1472  TabContentsWrapper* page_a_a_a_contents = CreateTabContents();
1473  strip.AddTabContents(page_a_a_a_contents, -1, PageTransition::LINK,
1474                       TabStripModel::ADD_NONE);
1475
1476  EXPECT_EQ(page_a_a_a_contents, strip.GetTabContentsAt(2));
1477
1478  // Close page A.A
1479  strip.CloseTabContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1480
1481  // Page A.A.A should be selected, NOT A.B
1482  EXPECT_EQ(page_a_a_a_contents, strip.GetSelectedTabContents());
1483
1484  // Close page A.A.A
1485  strip.CloseTabContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1486
1487  // Page A.B should be selected
1488  EXPECT_EQ(page_a_b_contents, strip.GetSelectedTabContents());
1489
1490  // Close page A.B
1491  strip.CloseTabContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1492
1493  // Page A should be selected
1494  EXPECT_EQ(page_a_contents, strip.GetSelectedTabContents());
1495
1496  // Clean up.
1497  strip.CloseAllTabs();
1498}
1499
1500TEST_F(TabStripModelTest, AddTabContents_NewTabAtEndOfStripInheritsGroup) {
1501  TabStripDummyDelegate delegate(NULL);
1502  TabStripModel strip(&delegate, profile());
1503
1504  // Open page A
1505  TabContentsWrapper* page_a_contents = CreateTabContents();
1506  strip.AddTabContents(page_a_contents, -1, PageTransition::START_PAGE,
1507                       TabStripModel::ADD_ACTIVE);
1508
1509  // Open pages B, C and D in the background from links on page A...
1510  TabContentsWrapper* page_b_contents = CreateTabContents();
1511  TabContentsWrapper* page_c_contents = CreateTabContents();
1512  TabContentsWrapper* page_d_contents = CreateTabContents();
1513  strip.AddTabContents(page_b_contents, -1, PageTransition::LINK,
1514                       TabStripModel::ADD_NONE);
1515  strip.AddTabContents(page_c_contents, -1, PageTransition::LINK,
1516                       TabStripModel::ADD_NONE);
1517  strip.AddTabContents(page_d_contents, -1, PageTransition::LINK,
1518                       TabStripModel::ADD_NONE);
1519
1520  // Switch to page B's tab.
1521  strip.ActivateTabAt(1, true);
1522
1523  // Open a New Tab at the end of the strip (simulate Ctrl+T)
1524  TabContentsWrapper* new_tab_contents = CreateTabContents();
1525  strip.AddTabContents(new_tab_contents, -1, PageTransition::TYPED,
1526                       TabStripModel::ADD_ACTIVE);
1527
1528  EXPECT_EQ(4, strip.GetIndexOfTabContents(new_tab_contents));
1529  EXPECT_EQ(4, strip.active_index());
1530
1531  // Close the New Tab that was just opened. We should be returned to page B's
1532  // Tab...
1533  strip.CloseTabContentsAt(4, TabStripModel::CLOSE_NONE);
1534
1535  EXPECT_EQ(1, strip.active_index());
1536
1537  // Open a non-New Tab tab at the end of the strip, with a TYPED transition.
1538  // This is like typing a URL in the address bar and pressing Alt+Enter. The
1539  // behavior should be the same as above.
1540  TabContentsWrapper* page_e_contents = CreateTabContents();
1541  strip.AddTabContents(page_e_contents, -1, PageTransition::TYPED,
1542                       TabStripModel::ADD_ACTIVE);
1543
1544  EXPECT_EQ(4, strip.GetIndexOfTabContents(page_e_contents));
1545  EXPECT_EQ(4, strip.active_index());
1546
1547  // Close the Tab. Selection should shift back to page B's Tab.
1548  strip.CloseTabContentsAt(4, TabStripModel::CLOSE_NONE);
1549
1550  EXPECT_EQ(1, strip.active_index());
1551
1552  // Open a non-New Tab tab at the end of the strip, with some other
1553  // transition. This is like right clicking on a bookmark and choosing "Open
1554  // in New Tab". No opener relationship should be preserved between this Tab
1555  // and the one that was active when the gesture was performed.
1556  TabContentsWrapper* page_f_contents = CreateTabContents();
1557  strip.AddTabContents(page_f_contents, -1, PageTransition::AUTO_BOOKMARK,
1558                       TabStripModel::ADD_ACTIVE);
1559
1560  EXPECT_EQ(4, strip.GetIndexOfTabContents(page_f_contents));
1561  EXPECT_EQ(4, strip.active_index());
1562
1563  // Close the Tab. The next-adjacent should be selected.
1564  strip.CloseTabContentsAt(4, TabStripModel::CLOSE_NONE);
1565
1566  EXPECT_EQ(3, strip.active_index());
1567
1568  // Clean up.
1569  strip.CloseAllTabs();
1570}
1571
1572// A test of navigations in a tab that is part of a group of opened from some
1573// parent tab. If the navigations are link clicks, the group relationship of
1574// the tab to its parent are preserved. If they are of any other type, they are
1575// not preserved.
1576TEST_F(TabStripModelTest, NavigationForgetsOpeners) {
1577  TabStripDummyDelegate delegate(NULL);
1578  TabStripModel strip(&delegate, profile());
1579
1580  // Open page A
1581  TabContentsWrapper* page_a_contents = CreateTabContents();
1582  strip.AddTabContents(page_a_contents, -1, PageTransition::START_PAGE,
1583                       TabStripModel::ADD_ACTIVE);
1584
1585  // Open pages B, C and D in the background from links on page A...
1586  TabContentsWrapper* page_b_contents = CreateTabContents();
1587  TabContentsWrapper* page_c_contents = CreateTabContents();
1588  TabContentsWrapper* page_d_contents = CreateTabContents();
1589  strip.AddTabContents(page_b_contents, -1, PageTransition::LINK,
1590                       TabStripModel::ADD_NONE);
1591  strip.AddTabContents(page_c_contents, -1, PageTransition::LINK,
1592                       TabStripModel::ADD_NONE);
1593  strip.AddTabContents(page_d_contents, -1, PageTransition::LINK,
1594                       TabStripModel::ADD_NONE);
1595
1596  // Open page E in a different opener group from page A.
1597  TabContentsWrapper* page_e_contents = CreateTabContents();
1598  strip.AddTabContents(page_e_contents, -1, PageTransition::START_PAGE,
1599                       TabStripModel::ADD_NONE);
1600
1601  // Tell the TabStripModel that we are navigating page D via a link click.
1602  strip.ActivateTabAt(3, true);
1603  strip.TabNavigating(page_d_contents, PageTransition::LINK);
1604
1605  // Close page D, page C should be selected. (part of same group).
1606  strip.CloseTabContentsAt(3, TabStripModel::CLOSE_NONE);
1607  EXPECT_EQ(2, strip.active_index());
1608
1609  // Tell the TabStripModel that we are navigating in page C via a bookmark.
1610  strip.TabNavigating(page_c_contents, PageTransition::AUTO_BOOKMARK);
1611
1612  // Close page C, page E should be selected. (C is no longer part of the
1613  // A-B-C-D group, selection moves to the right).
1614  strip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE);
1615  EXPECT_EQ(page_e_contents, strip.GetTabContentsAt(strip.active_index()));
1616
1617  strip.CloseAllTabs();
1618}
1619
1620// A test that the forgetting behavior tested in NavigationForgetsOpeners above
1621// doesn't cause the opener relationship for a New Tab opened at the end of the
1622// TabStrip to be reset (Test 1 below), unless another any other tab is
1623// seelcted (Test 2 below).
1624TEST_F(TabStripModelTest, NavigationForgettingDoesntAffectNewTab) {
1625  TabStripDummyDelegate delegate(NULL);
1626  TabStripModel strip(&delegate, profile());
1627
1628  // Open a tab and several tabs from it, then select one of the tabs that was
1629  // opened.
1630  TabContentsWrapper* page_a_contents = CreateTabContents();
1631  strip.AddTabContents(page_a_contents, -1, PageTransition::START_PAGE,
1632                       TabStripModel::ADD_ACTIVE);
1633
1634  TabContentsWrapper* page_b_contents = CreateTabContents();
1635  TabContentsWrapper* page_c_contents = CreateTabContents();
1636  TabContentsWrapper* page_d_contents = CreateTabContents();
1637  strip.AddTabContents(page_b_contents, -1, PageTransition::LINK,
1638                       TabStripModel::ADD_NONE);
1639  strip.AddTabContents(page_c_contents, -1, PageTransition::LINK,
1640                       TabStripModel::ADD_NONE);
1641  strip.AddTabContents(page_d_contents, -1, PageTransition::LINK,
1642                       TabStripModel::ADD_NONE);
1643
1644  strip.ActivateTabAt(2, true);
1645
1646  // TEST 1: If the user is in a group of tabs and opens a new tab at the end
1647  // of the strip, closing that new tab will select the tab that they were
1648  // last on.
1649
1650  // Now simulate opening a new tab at the end of the TabStrip.
1651  TabContentsWrapper* new_tab_contents1 = CreateTabContents();
1652  strip.AddTabContents(new_tab_contents1, -1, PageTransition::TYPED,
1653                       TabStripModel::ADD_ACTIVE);
1654
1655  // At this point, if we close this tab the last selected one should be
1656  // re-selected.
1657  strip.CloseTabContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1658  EXPECT_EQ(page_c_contents, strip.GetTabContentsAt(strip.active_index()));
1659
1660  // TEST 2: If the user is in a group of tabs and opens a new tab at the end
1661  // of the strip, selecting any other tab in the strip will cause that new
1662  // tab's opener relationship to be forgotten.
1663
1664  // Open a new tab again.
1665  TabContentsWrapper* new_tab_contents2 = CreateTabContents();
1666  strip.AddTabContents(new_tab_contents2, -1, PageTransition::TYPED,
1667                       TabStripModel::ADD_ACTIVE);
1668
1669  // Now select the first tab.
1670  strip.ActivateTabAt(0, true);
1671
1672  // Now select the last tab.
1673  strip.ActivateTabAt(strip.count() - 1, true);
1674
1675  // Now close the last tab. The next adjacent should be selected.
1676  strip.CloseTabContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1677  EXPECT_EQ(page_d_contents, strip.GetTabContentsAt(strip.active_index()));
1678
1679  strip.CloseAllTabs();
1680}
1681
1682// Tests that fast shutdown is attempted appropriately.
1683TEST_F(TabStripModelTest, FastShutdown) {
1684  TabStripDummyDelegate delegate(NULL);
1685  TabStripModel tabstrip(&delegate, profile());
1686  MockTabStripModelObserver observer;
1687  tabstrip.AddObserver(&observer);
1688
1689  EXPECT_TRUE(tabstrip.empty());
1690
1691  // Make sure fast shutdown is attempted when tabs that share a RPH are shut
1692  // down.
1693  {
1694    TabContentsWrapper* contents1 = CreateTabContents();
1695    TabContentsWrapper* contents2 =
1696        CreateTabContentsWithSharedRPH(contents1->tab_contents());
1697
1698    SetID(contents1->tab_contents(), 1);
1699    SetID(contents2->tab_contents(), 2);
1700
1701    tabstrip.AppendTabContents(contents1, true);
1702    tabstrip.AppendTabContents(contents2, true);
1703
1704    // Turn on the fake unload listener so the tabs don't actually get shut
1705    // down when we call CloseAllTabs()---we need to be able to check that
1706    // fast shutdown was attempted.
1707    delegate.set_run_unload_listener(true);
1708    tabstrip.CloseAllTabs();
1709    // On a mock RPH this checks whether we *attempted* fast shutdown.
1710    // A real RPH would reject our attempt since there is an unload handler.
1711    EXPECT_TRUE(contents1->tab_contents()->
1712        GetRenderProcessHost()->fast_shutdown_started());
1713    EXPECT_EQ(2, tabstrip.count());
1714
1715    delegate.set_run_unload_listener(false);
1716    tabstrip.CloseAllTabs();
1717    EXPECT_TRUE(tabstrip.empty());
1718  }
1719
1720  // Make sure fast shutdown is not attempted when only some tabs that share a
1721  // RPH are shut down.
1722  {
1723    TabContentsWrapper* contents1 = CreateTabContents();
1724    TabContentsWrapper* contents2 =
1725        CreateTabContentsWithSharedRPH(contents1->tab_contents());
1726
1727    SetID(contents1->tab_contents(), 1);
1728    SetID(contents2->tab_contents(), 2);
1729
1730    tabstrip.AppendTabContents(contents1, true);
1731    tabstrip.AppendTabContents(contents2, true);
1732
1733    tabstrip.CloseTabContentsAt(1, TabStripModel::CLOSE_NONE);
1734    EXPECT_FALSE(contents1->tab_contents()->
1735        GetRenderProcessHost()->fast_shutdown_started());
1736    EXPECT_EQ(1, tabstrip.count());
1737
1738    tabstrip.CloseAllTabs();
1739    EXPECT_TRUE(tabstrip.empty());
1740  }
1741}
1742
1743// Tests various permutations of apps.
1744TEST_F(TabStripModelTest, Apps) {
1745  TabStripDummyDelegate delegate(NULL);
1746  TabStripModel tabstrip(&delegate, profile());
1747  MockTabStripModelObserver observer;
1748  tabstrip.AddObserver(&observer);
1749
1750  EXPECT_TRUE(tabstrip.empty());
1751
1752  typedef MockTabStripModelObserver::State State;
1753
1754#if defined(OS_WIN)
1755  FilePath path(FILE_PATH_LITERAL("c:\\foo"));
1756#elif defined(OS_POSIX)
1757  FilePath path(FILE_PATH_LITERAL("/foo"));
1758#endif
1759  scoped_refptr<Extension> extension_app(new Extension(path,
1760                                                       Extension::INVALID));
1761  extension_app->launch_web_url_ = "http://www.google.com";
1762  TabContentsWrapper* contents1 = CreateTabContents();
1763  contents1->extension_tab_helper()->SetExtensionApp(extension_app);
1764  TabContentsWrapper* contents2 = CreateTabContents();
1765  contents2->extension_tab_helper()->SetExtensionApp(extension_app);
1766  TabContentsWrapper* contents3 = CreateTabContents();
1767
1768  SetID(contents1->tab_contents(), 1);
1769  SetID(contents2->tab_contents(), 2);
1770  SetID(contents3->tab_contents(), 3);
1771
1772  // Note! The ordering of these tests is important, each subsequent test
1773  // builds on the state established in the previous. This is important if you
1774  // ever insert tests rather than append.
1775
1776  // Initial state, tab3 only and selected.
1777  tabstrip.AppendTabContents(contents3, true);
1778
1779  observer.ClearStates();
1780
1781  // Attempt to insert tab1 (an app tab) at position 1. This isn't a legal
1782  // position and tab1 should end up at position 0.
1783  {
1784    tabstrip.InsertTabContentsAt(1, contents1, TabStripModel::ADD_NONE);
1785
1786    ASSERT_EQ(1, observer.GetStateCount());
1787    State state(contents1, 0, MockTabStripModelObserver::INSERT);
1788    EXPECT_TRUE(observer.StateEquals(0, state));
1789
1790    // And verify the state.
1791    EXPECT_EQ("1ap 3", GetPinnedState(tabstrip));
1792
1793    observer.ClearStates();
1794  }
1795
1796  // Insert tab 2 at position 1.
1797  {
1798    tabstrip.InsertTabContentsAt(1, contents2, TabStripModel::ADD_NONE);
1799
1800    ASSERT_EQ(1, observer.GetStateCount());
1801    State state(contents2, 1, MockTabStripModelObserver::INSERT);
1802    EXPECT_TRUE(observer.StateEquals(0, state));
1803
1804    // And verify the state.
1805    EXPECT_EQ("1ap 2ap 3", GetPinnedState(tabstrip));
1806
1807    observer.ClearStates();
1808  }
1809
1810  // Try to move tab 3 to position 0. This isn't legal and should be ignored.
1811  {
1812    tabstrip.MoveTabContentsAt(2, 0, false);
1813
1814    ASSERT_EQ(0, observer.GetStateCount());
1815
1816    // And verify the state didn't change.
1817    EXPECT_EQ("1ap 2ap 3", GetPinnedState(tabstrip));
1818
1819    observer.ClearStates();
1820  }
1821
1822  // Try to move tab 0 to position 3. This isn't legal and should be ignored.
1823  {
1824    tabstrip.MoveTabContentsAt(0, 2, false);
1825
1826    ASSERT_EQ(0, observer.GetStateCount());
1827
1828    // And verify the state didn't change.
1829    EXPECT_EQ("1ap 2ap 3", GetPinnedState(tabstrip));
1830
1831    observer.ClearStates();
1832  }
1833
1834  // Try to move tab 0 to position 1. This is a legal move.
1835  {
1836    tabstrip.MoveTabContentsAt(0, 1, false);
1837
1838    ASSERT_EQ(1, observer.GetStateCount());
1839    State state(contents1, 1, MockTabStripModelObserver::MOVE);
1840    state.src_index = 0;
1841    EXPECT_TRUE(observer.StateEquals(0, state));
1842
1843    // And verify the state didn't change.
1844    EXPECT_EQ("2ap 1ap 3", GetPinnedState(tabstrip));
1845
1846    observer.ClearStates();
1847  }
1848
1849  // Remove tab3 and insert at position 0. It should be forced to position 2.
1850  {
1851    tabstrip.DetachTabContentsAt(2);
1852    observer.ClearStates();
1853
1854    tabstrip.InsertTabContentsAt(0, contents3, TabStripModel::ADD_NONE);
1855
1856    ASSERT_EQ(1, observer.GetStateCount());
1857    State state(contents3, 2, MockTabStripModelObserver::INSERT);
1858    EXPECT_TRUE(observer.StateEquals(0, state));
1859
1860    // And verify the state didn't change.
1861    EXPECT_EQ("2ap 1ap 3", GetPinnedState(tabstrip));
1862
1863    observer.ClearStates();
1864  }
1865
1866  tabstrip.CloseAllTabs();
1867}
1868
1869// Tests various permutations of pinning tabs.
1870TEST_F(TabStripModelTest, Pinning) {
1871  TabStripDummyDelegate delegate(NULL);
1872  TabStripModel tabstrip(&delegate, profile());
1873  MockTabStripModelObserver observer;
1874  tabstrip.AddObserver(&observer);
1875
1876  EXPECT_TRUE(tabstrip.empty());
1877
1878  typedef MockTabStripModelObserver::State State;
1879
1880  TabContentsWrapper* contents1 = CreateTabContents();
1881  TabContentsWrapper* contents2 = CreateTabContents();
1882  TabContentsWrapper* contents3 = CreateTabContents();
1883
1884  SetID(contents1->tab_contents(), 1);
1885  SetID(contents2->tab_contents(), 2);
1886  SetID(contents3->tab_contents(), 3);
1887
1888  // Note! The ordering of these tests is important, each subsequent test
1889  // builds on the state established in the previous. This is important if you
1890  // ever insert tests rather than append.
1891
1892  // Initial state, three tabs, first selected.
1893  tabstrip.AppendTabContents(contents1, true);
1894  tabstrip.AppendTabContents(contents2, false);
1895  tabstrip.AppendTabContents(contents3, false);
1896
1897  observer.ClearStates();
1898
1899  // Pin the first tab, this shouldn't visually reorder anything.
1900  {
1901    tabstrip.SetTabPinned(0, true);
1902
1903    // As the order didn't change, we should get a pinned notification.
1904    ASSERT_EQ(1, observer.GetStateCount());
1905    State state(contents1, 0, MockTabStripModelObserver::PINNED);
1906    EXPECT_TRUE(observer.StateEquals(0, state));
1907
1908    // And verify the state.
1909    EXPECT_EQ("1p 2 3", GetPinnedState(tabstrip));
1910
1911    observer.ClearStates();
1912  }
1913
1914  // Unpin the first tab.
1915  {
1916    tabstrip.SetTabPinned(0, false);
1917
1918    // As the order didn't change, we should get a pinned notification.
1919    ASSERT_EQ(1, observer.GetStateCount());
1920    State state(contents1, 0, MockTabStripModelObserver::PINNED);
1921    EXPECT_TRUE(observer.StateEquals(0, state));
1922
1923    // And verify the state.
1924    EXPECT_EQ("1 2 3", GetPinnedState(tabstrip));
1925
1926    observer.ClearStates();
1927  }
1928
1929  // Pin the 3rd tab, which should move it to the front.
1930  {
1931    tabstrip.SetTabPinned(2, true);
1932
1933    // The pinning should have resulted in a move and a pinned notification.
1934    ASSERT_EQ(2, observer.GetStateCount());
1935    State state(contents3, 0, MockTabStripModelObserver::MOVE);
1936    state.src_index = 2;
1937    EXPECT_TRUE(observer.StateEquals(0, state));
1938
1939    state = State(contents3, 0, MockTabStripModelObserver::PINNED);
1940    EXPECT_TRUE(observer.StateEquals(1, state));
1941
1942    // And verify the state.
1943    EXPECT_EQ("3p 1 2", GetPinnedState(tabstrip));
1944
1945    observer.ClearStates();
1946  }
1947
1948  // Pin the tab "1", which shouldn't move anything.
1949  {
1950    tabstrip.SetTabPinned(1, true);
1951
1952    // As the order didn't change, we should get a pinned notification.
1953    ASSERT_EQ(1, observer.GetStateCount());
1954    State state(contents1, 1, MockTabStripModelObserver::PINNED);
1955    EXPECT_TRUE(observer.StateEquals(0, state));
1956
1957    // And verify the state.
1958    EXPECT_EQ("3p 1p 2", GetPinnedState(tabstrip));
1959
1960    observer.ClearStates();
1961  }
1962
1963  // Try to move tab "2" to the front, it should be ignored.
1964  {
1965    tabstrip.MoveTabContentsAt(2, 0, false);
1966
1967    // As the order didn't change, we should get a pinned notification.
1968    ASSERT_EQ(0, observer.GetStateCount());
1969
1970    // And verify the state.
1971    EXPECT_EQ("3p 1p 2", GetPinnedState(tabstrip));
1972
1973    observer.ClearStates();
1974  }
1975
1976  // Unpin tab "3", which implicitly moves it to the end.
1977  {
1978    tabstrip.SetTabPinned(0, false);
1979
1980    ASSERT_EQ(2, observer.GetStateCount());
1981    State state(contents3, 1, MockTabStripModelObserver::MOVE);
1982    state.src_index = 0;
1983    EXPECT_TRUE(observer.StateEquals(0, state));
1984
1985    state = State(contents3, 1, MockTabStripModelObserver::PINNED);
1986    EXPECT_TRUE(observer.StateEquals(1, state));
1987
1988    // And verify the state.
1989    EXPECT_EQ("1p 3 2", GetPinnedState(tabstrip));
1990
1991    observer.ClearStates();
1992  }
1993
1994  // Unpin tab "3", nothing should happen.
1995  {
1996    tabstrip.SetTabPinned(1, false);
1997
1998    ASSERT_EQ(0, observer.GetStateCount());
1999
2000    EXPECT_EQ("1p 3 2", GetPinnedState(tabstrip));
2001
2002    observer.ClearStates();
2003  }
2004
2005  // Pin "3" and "1".
2006  {
2007    tabstrip.SetTabPinned(0, true);
2008    tabstrip.SetTabPinned(1, true);
2009
2010    EXPECT_EQ("1p 3p 2", GetPinnedState(tabstrip));
2011
2012    observer.ClearStates();
2013  }
2014
2015  TabContentsWrapper* contents4 = CreateTabContents();
2016  SetID(contents4->tab_contents(), 4);
2017
2018  // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end
2019  // up after them.
2020  {
2021    tabstrip.InsertTabContentsAt(1, contents4, TabStripModel::ADD_NONE);
2022
2023    ASSERT_EQ(1, observer.GetStateCount());
2024    State state(contents4, 2, MockTabStripModelObserver::INSERT);
2025    EXPECT_TRUE(observer.StateEquals(0, state));
2026
2027    EXPECT_EQ("1p 3p 4 2", GetPinnedState(tabstrip));
2028  }
2029
2030  tabstrip.CloseAllTabs();
2031}
2032
2033// Makes sure the TabStripModel calls the right observer methods during a
2034// replace.
2035TEST_F(TabStripModelTest, ReplaceSendsSelected) {
2036  typedef MockTabStripModelObserver::State State;
2037
2038  TabStripDummyDelegate delegate(NULL);
2039  TabStripModel strip(&delegate, profile());
2040
2041  TabContentsWrapper* first_contents = CreateTabContents();
2042  strip.AddTabContents(first_contents, -1, PageTransition::TYPED,
2043                       TabStripModel::ADD_ACTIVE);
2044
2045  MockTabStripModelObserver tabstrip_observer;
2046  strip.AddObserver(&tabstrip_observer);
2047
2048  TabContentsWrapper* new_contents = CreateTabContents();
2049  delete strip.ReplaceTabContentsAt(0, new_contents);
2050
2051  ASSERT_EQ(2, tabstrip_observer.GetStateCount());
2052
2053  // First event should be for replaced.
2054  State state(new_contents, 0, MockTabStripModelObserver::REPLACED);
2055  state.src_contents = first_contents;
2056  EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
2057
2058  // And the second for selected.
2059  state = State(new_contents, 0, MockTabStripModelObserver::SELECT);
2060  state.src_contents = first_contents;
2061  EXPECT_TRUE(tabstrip_observer.StateEquals(1, state));
2062
2063  // Now add another tab and replace it, making sure we don't get a selected
2064  // event this time.
2065  TabContentsWrapper* third_contents = CreateTabContents();
2066  strip.AddTabContents(third_contents, 1, PageTransition::TYPED,
2067                       TabStripModel::ADD_NONE);
2068
2069  tabstrip_observer.ClearStates();
2070
2071  // And replace it.
2072  new_contents = CreateTabContents();
2073  delete strip.ReplaceTabContentsAt(1, new_contents);
2074
2075  ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2076
2077  state = State(new_contents, 1, MockTabStripModelObserver::REPLACED);
2078  state.src_contents = third_contents;
2079  EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
2080
2081  strip.CloseAllTabs();
2082}
2083
2084// Makes sure TabStripModel handles the case of deleting a tab while removing
2085// another tab.
2086TEST_F(TabStripModelTest, DeleteFromDestroy) {
2087  TabStripDummyDelegate delegate(NULL);
2088  TabStripModel strip(&delegate, profile());
2089  TabContentsWrapper* contents1 = CreateTabContents();
2090  TabContentsWrapper* contents2 = CreateTabContents();
2091  strip.AppendTabContents(contents1, true);
2092  strip.AppendTabContents(contents2, true);
2093  // DeleteTabContentsOnDestroyedObserver deletes contents1 when contents2 sends
2094  // out notification that it is being destroyed.
2095  DeleteTabContentsOnDestroyedObserver observer(contents2, contents1);
2096  strip.CloseAllTabs();
2097}
2098
2099TEST_F(TabStripModelTest, MoveSelectedTabsTo) {
2100  struct TestData {
2101    // Number of tabs the tab strip should have.
2102    const int tab_count;
2103
2104    // Number of pinned tabs.
2105    const int pinned_count;
2106
2107    // Index of the tabs to select.
2108    const std::string selected_tabs;
2109
2110    // Index to move the tabs to.
2111    const int target_index;
2112
2113    // Expected state after the move (space separated list of indices).
2114    const std::string state_after_move;
2115  } test_data[] = {
2116    // 1 selected tab.
2117    { 2, 0, "0", 1, "1 0" },
2118    { 3, 0, "0", 2, "1 2 0" },
2119    { 3, 0, "2", 0, "2 0 1" },
2120    { 3, 0, "2", 1, "0 2 1" },
2121    { 3, 0, "0 1", 0, "0 1 2" },
2122
2123    // 2 selected tabs.
2124    { 6, 0, "4 5", 1, "0 4 5 1 2 3" },
2125    { 3, 0, "0 1", 1, "2 0 1" },
2126    { 4, 0, "0 2", 1, "1 0 2 3" },
2127    { 6, 0, "0 1", 3, "2 3 4 0 1 5" },
2128
2129    // 3 selected tabs.
2130    { 6, 0, "0 2 3", 3, "1 4 5 0 2 3" },
2131    { 7, 0, "4 5 6", 1, "0 4 5 6 1 2 3" },
2132    { 7, 0, "1 5 6", 4, "0 2 3 4 1 5 6" },
2133
2134    // 5 selected tabs.
2135    { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" },
2136
2137    // 7 selected tabs
2138    { 16, 0, "0 1 2 3 4 7 9", 8, "5 6 8 10 11 12 13 14 0 1 2 3 4 7 9 15" },
2139
2140    // With pinned tabs.
2141    { 6, 2, "2 3", 2, "0p 1p 2 3 4 5" },
2142    { 6, 2, "0 4", 3, "1p 0p 2 3 4 5" },
2143    { 6, 3, "1 2 4", 0, "1p 2p 0p 4 3 5" },
2144    { 8, 3, "1 3 4", 4, "0p 2p 1p 5 6 3 4 7" },
2145
2146    { 7, 4, "2 3 4", 3, "0p 1p 2p 3p 5 4 6" },
2147  };
2148
2149  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
2150    TabStripDummyDelegate delegate(NULL);
2151    TabStripModel strip(&delegate, profile());
2152    ASSERT_NO_FATAL_FAILURE(
2153        PrepareTabstripForSelectionTest(&strip, test_data[i].tab_count,
2154                                        test_data[i].pinned_count,
2155                                        test_data[i].selected_tabs));
2156    strip.MoveSelectedTabsTo(test_data[i].target_index);
2157    EXPECT_EQ(test_data[i].state_after_move, GetPinnedState(strip)) << i;
2158    strip.CloseAllTabs();
2159  }
2160}
2161
2162TEST_F(TabStripModelTest, CloseSelectedTabs) {
2163  TabStripDummyDelegate delegate(NULL);
2164  TabStripModel strip(&delegate, profile());
2165  TabContentsWrapper* contents1 = CreateTabContents();
2166  TabContentsWrapper* contents2 = CreateTabContents();
2167  TabContentsWrapper* contents3 = CreateTabContents();
2168  strip.AppendTabContents(contents1, true);
2169  strip.AppendTabContents(contents2, true);
2170  strip.AppendTabContents(contents3, true);
2171  strip.ToggleSelectionAt(1);
2172  strip.CloseSelectedTabs();
2173  EXPECT_EQ(1, strip.count());
2174  EXPECT_EQ(0, strip.active_index());
2175  strip.CloseAllTabs();
2176}
2177
2178// Verifies that if we change the selection from a multi selection to a single
2179// selection, but not in a way that changes the selected_index that
2180// TabSelectedAt is still invoked.
2181TEST_F(TabStripModelTest, MultipleToSingle) {
2182  TabStripDummyDelegate delegate(NULL);
2183  TabStripModel strip(&delegate, profile());
2184  TabContentsWrapper* contents1 = CreateTabContents();
2185  TabContentsWrapper* contents2 = CreateTabContents();
2186  strip.AppendTabContents(contents1, false);
2187  strip.AppendTabContents(contents2, false);
2188  strip.ToggleSelectionAt(0);
2189  strip.ToggleSelectionAt(1);
2190
2191  MockTabStripModelObserver observer;
2192  strip.AddObserver(&observer);
2193  // This changes the selection (0 is no longer selected) but the selected_index
2194  // still remains at 1.
2195  strip.ActivateTabAt(1, true);
2196  ASSERT_EQ(1, observer.GetStateCount());
2197  MockTabStripModelObserver::State s(
2198      contents2, 1, MockTabStripModelObserver::SELECT);
2199  s.src_contents = contents2;
2200  s.user_gesture = true;
2201  EXPECT_TRUE(observer.StateEquals(0, s));
2202  strip.RemoveObserver(&observer);
2203  strip.CloseAllTabs();
2204}
2205