tab_strip_unittest.cc revision 9ab5563a3196760eb381d102cbb2bc0f7abc6a50
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/views/tabs/tab_strip.h" 6 7#include "base/message_loop/message_loop.h" 8#include "chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h" 9#include "chrome/browser/ui/views/tabs/tab_strip.h" 10#include "chrome/browser/ui/views/tabs/tab_strip_controller.h" 11#include "chrome/browser/ui/views/tabs/tab_strip_observer.h" 12#include "chrome/test/base/testing_profile.h" 13#include "testing/gtest/include/gtest/gtest.h" 14 15namespace { 16 17// Walks up the views hierarchy until it finds a tab view. It returns the 18// found tab view, on NULL if none is found. 19views::View* FindTabView(views::View* view) { 20 views::View* current = view; 21 while (current && strcmp(current->GetClassName(), Tab::kViewClassName)) { 22 current = current->parent(); 23 } 24 return current; 25} 26 27} // namespace 28 29class TestTabStripObserver : public TabStripObserver { 30 public: 31 explicit TestTabStripObserver(TabStrip* tab_strip) 32 : tab_strip_(tab_strip), 33 last_tab_added_(-1), 34 last_tab_removed_(-1), 35 last_tab_moved_from_(-1), 36 last_tab_moved_to_(-1), 37 tabstrip_deleted_(false) { 38 tab_strip_->AddObserver(this); 39 } 40 41 virtual ~TestTabStripObserver() { 42 if (tab_strip_) 43 tab_strip_->RemoveObserver(this); 44 } 45 46 int last_tab_added() const { return last_tab_added_; } 47 int last_tab_removed() const { return last_tab_removed_; } 48 int last_tab_moved_from() const { return last_tab_moved_from_; } 49 int last_tab_moved_to() const { return last_tab_moved_to_; } 50 bool tabstrip_deleted() const { return tabstrip_deleted_; } 51 52 private: 53 // TabStripObserver overrides. 54 virtual void TabStripAddedTabAt(TabStrip* tab_strip, int index) OVERRIDE { 55 last_tab_added_ = index; 56 } 57 58 virtual void TabStripMovedTab(TabStrip* tab_strip, 59 int from_index, 60 int to_index) OVERRIDE { 61 last_tab_moved_from_ = from_index; 62 last_tab_moved_to_ = to_index; 63 } 64 65 virtual void TabStripRemovedTabAt(TabStrip* tab_strip, int index) OVERRIDE { 66 last_tab_removed_ = index; 67 } 68 69 virtual void TabStripDeleted(TabStrip* tab_strip) OVERRIDE { 70 tabstrip_deleted_ = true; 71 tab_strip_ = NULL; 72 } 73 74 TabStrip* tab_strip_; 75 int last_tab_added_; 76 int last_tab_removed_; 77 int last_tab_moved_from_; 78 int last_tab_moved_to_; 79 bool tabstrip_deleted_; 80 81 DISALLOW_COPY_AND_ASSIGN(TestTabStripObserver); 82}; 83 84class TabStripTest : public testing::Test { 85 public: 86 TabStripTest() 87 : controller_(new FakeBaseTabStripController) { 88 tab_strip_ = new TabStrip(controller_); 89 controller_->set_tab_strip(tab_strip_); 90 // Do this to force TabStrip to create the buttons. 91 parent_.AddChildView(tab_strip_); 92 } 93 94 protected: 95 // Checks whether |tab| contains |point_in_tabstrip_coords|, where the point 96 // is in |tab_strip_| coordinates. 97 bool IsPointInTab(Tab* tab, const gfx::Point& point_in_tabstrip_coords) { 98 gfx::Point point_in_tab_coords(point_in_tabstrip_coords); 99 views::View::ConvertPointToTarget(tab_strip_, tab, &point_in_tab_coords); 100 return tab->HitTestPoint(point_in_tab_coords); 101 } 102 103 base::MessageLoopForUI ui_loop_; 104 // Owned by TabStrip. 105 FakeBaseTabStripController* controller_; 106 // Owns |tab_strip_|. 107 views::View parent_; 108 TabStrip* tab_strip_; 109 110 private: 111 DISALLOW_COPY_AND_ASSIGN(TabStripTest); 112}; 113 114TEST_F(TabStripTest, GetModelCount) { 115 EXPECT_EQ(0, tab_strip_->GetModelCount()); 116} 117 118TEST_F(TabStripTest, IsValidModelIndex) { 119 EXPECT_FALSE(tab_strip_->IsValidModelIndex(0)); 120} 121 122TEST_F(TabStripTest, tab_count) { 123 EXPECT_EQ(0, tab_strip_->tab_count()); 124} 125 126TEST_F(TabStripTest, CreateTabForDragging) { 127 // Any result is good, as long as it doesn't crash. 128 scoped_ptr<Tab> tab(tab_strip_->CreateTabForDragging()); 129} 130 131TEST_F(TabStripTest, AddTabAt) { 132 TestTabStripObserver observer(tab_strip_); 133 tab_strip_->AddTabAt(0, TabRendererData(), false); 134 ASSERT_EQ(1, tab_strip_->tab_count()); 135 EXPECT_EQ(0, observer.last_tab_added()); 136 Tab* tab = tab_strip_->tab_at(0); 137 EXPECT_FALSE(tab == NULL); 138} 139 140// Confirms that TabStripObserver::TabStripDeleted() is sent. 141TEST_F(TabStripTest, TabStripDeleted) { 142 FakeBaseTabStripController* controller = new FakeBaseTabStripController; 143 TabStrip* tab_strip = new TabStrip(controller); 144 controller->set_tab_strip(tab_strip); 145 TestTabStripObserver observer(tab_strip); 146 delete tab_strip; 147 EXPECT_TRUE(observer.tabstrip_deleted()); 148} 149 150TEST_F(TabStripTest, MoveTab) { 151 TestTabStripObserver observer(tab_strip_); 152 tab_strip_->AddTabAt(0, TabRendererData(), false); 153 tab_strip_->AddTabAt(1, TabRendererData(), false); 154 tab_strip_->AddTabAt(2, TabRendererData(), false); 155 ASSERT_EQ(3, tab_strip_->tab_count()); 156 EXPECT_EQ(2, observer.last_tab_added()); 157 Tab* tab = tab_strip_->tab_at(0); 158 tab_strip_->MoveTab(0, 1, TabRendererData()); 159 EXPECT_EQ(0, observer.last_tab_moved_from()); 160 EXPECT_EQ(1, observer.last_tab_moved_to()); 161 EXPECT_EQ(tab, tab_strip_->tab_at(1)); 162} 163 164// Verifies child views are deleted after an animation completes. 165TEST_F(TabStripTest, RemoveTab) { 166 TestTabStripObserver observer(tab_strip_); 167 controller_->AddTab(0, false); 168 controller_->AddTab(1, false); 169 const int child_view_count = tab_strip_->child_count(); 170 EXPECT_EQ(2, tab_strip_->tab_count()); 171 controller_->RemoveTab(0); 172 EXPECT_EQ(0, observer.last_tab_removed()); 173 // When removing a tab the tabcount should immediately decrement. 174 EXPECT_EQ(1, tab_strip_->tab_count()); 175 // But the number of views should remain the same (it's animatining closed). 176 EXPECT_EQ(child_view_count, tab_strip_->child_count()); 177 tab_strip_->SetBounds(0, 0, 200, 20); 178 // Layout at a different size should force the animation to end and delete 179 // the tab that was removed. 180 tab_strip_->Layout(); 181 EXPECT_EQ(child_view_count - 1, tab_strip_->child_count()); 182 183 // Remove the last tab to make sure things are cleaned up correctly when 184 // the TabStrip is destroyed and an animation is ongoing. 185 controller_->RemoveTab(0); 186 EXPECT_EQ(0, observer.last_tab_removed()); 187} 188 189TEST_F(TabStripTest, ImmersiveMode) { 190 // Immersive mode defaults to off. 191 EXPECT_FALSE(tab_strip_->IsImmersiveStyle()); 192 193 // Tab strip defaults to normal tab height. 194 int normal_height = Tab::GetMinimumUnselectedSize().height(); 195 EXPECT_EQ(normal_height, tab_strip_->GetPreferredSize().height()); 196 197 // Tab strip can toggle immersive mode. 198 tab_strip_->SetImmersiveStyle(true); 199 EXPECT_TRUE(tab_strip_->IsImmersiveStyle()); 200 201 // Now tabs have the immersive height. 202 int immersive_height = Tab::GetImmersiveHeight(); 203 EXPECT_EQ(immersive_height, tab_strip_->GetPreferredSize().height()); 204 205 // Sanity-check immersive tabs are shorter than normal tabs. 206 EXPECT_LT(immersive_height, normal_height); 207} 208 209TEST_F(TabStripTest, GetEventHandlerForOverlappingArea) { 210 tab_strip_->SetBounds(0, 0, 1000, 20); 211 212 controller_->AddTab(0, false); 213 controller_->AddTab(1, true); 214 controller_->AddTab(2, false); 215 controller_->AddTab(3, false); 216 ASSERT_EQ(4, tab_strip_->tab_count()); 217 218 // Verify that the active tab will be a tooltip handler for points that hit 219 // it. 220 Tab* left_tab = tab_strip_->tab_at(0); 221 left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20))); 222 223 Tab* active_tab = tab_strip_->tab_at(1); 224 active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20))); 225 ASSERT_TRUE(active_tab->IsActive()); 226 227 Tab* right_tab = tab_strip_->tab_at(2); 228 right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20))); 229 230 Tab* most_right_tab = tab_strip_->tab_at(3); 231 most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0), 232 gfx::Size(200, 20))); 233 234 // Test that active tabs gets events from area in which it overlaps with its 235 // left neighbour. 236 gfx::Point left_overlap( 237 (active_tab->x() + left_tab->bounds().right() + 1) / 2, 238 active_tab->bounds().bottom() - 1); 239 240 // Sanity check that the point is in both active and left tab. 241 ASSERT_TRUE(IsPointInTab(active_tab, left_overlap)); 242 ASSERT_TRUE(IsPointInTab(left_tab, left_overlap)); 243 244 EXPECT_EQ(active_tab, 245 FindTabView(tab_strip_->GetEventHandlerForPoint(left_overlap))); 246 247 // Test that active tabs gets events from area in which it overlaps with its 248 // right neighbour. 249 gfx::Point right_overlap((active_tab->bounds().right() + right_tab->x()) / 2, 250 active_tab->bounds().bottom() - 1); 251 252 // Sanity check that the point is in both active and right tab. 253 ASSERT_TRUE(IsPointInTab(active_tab, right_overlap)); 254 ASSERT_TRUE(IsPointInTab(right_tab, right_overlap)); 255 256 EXPECT_EQ(active_tab, 257 FindTabView(tab_strip_->GetEventHandlerForPoint(right_overlap))); 258 259 // Test that if neither of tabs is active, the left one is selected. 260 gfx::Point unactive_overlap( 261 (right_tab->x() + most_right_tab->bounds().right() + 1) / 2, 262 right_tab->bounds().bottom() - 1); 263 264 // Sanity check that the point is in both active and left tab. 265 ASSERT_TRUE(IsPointInTab(right_tab, unactive_overlap)); 266 ASSERT_TRUE(IsPointInTab(most_right_tab, unactive_overlap)); 267 268 EXPECT_EQ(right_tab, 269 FindTabView(tab_strip_->GetEventHandlerForPoint(unactive_overlap))); 270} 271 272TEST_F(TabStripTest, GetTooltipHandler) { 273 tab_strip_->SetBounds(0, 0, 1000, 20); 274 275 controller_->AddTab(0, false); 276 controller_->AddTab(1, true); 277 controller_->AddTab(2, false); 278 controller_->AddTab(3, false); 279 ASSERT_EQ(4, tab_strip_->tab_count()); 280 281 // Verify that the active tab will be a tooltip handler for points that hit 282 // it. 283 Tab* left_tab = tab_strip_->tab_at(0); 284 left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20))); 285 286 Tab* active_tab = tab_strip_->tab_at(1); 287 active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20))); 288 ASSERT_TRUE(active_tab->IsActive()); 289 290 Tab* right_tab = tab_strip_->tab_at(2); 291 right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20))); 292 293 Tab* most_right_tab = tab_strip_->tab_at(3); 294 most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0), 295 gfx::Size(200, 20))); 296 297 // Test that active_tab handles tooltips from area in which it overlaps with 298 // its left neighbour. 299 gfx::Point left_overlap( 300 (active_tab->x() + left_tab->bounds().right() + 1) / 2, 301 active_tab->bounds().bottom() - 1); 302 303 // Sanity check that the point is in both active and left tab. 304 ASSERT_TRUE(IsPointInTab(active_tab, left_overlap)); 305 ASSERT_TRUE(IsPointInTab(left_tab, left_overlap)); 306 307 EXPECT_EQ(active_tab, 308 FindTabView(tab_strip_->GetTooltipHandlerForPoint(left_overlap))); 309 310 // Test that active_tab handles tooltips from area in which it overlaps with 311 // its right neighbour. 312 gfx::Point right_overlap((active_tab->bounds().right() + right_tab->x()) / 2, 313 active_tab->bounds().bottom() - 1); 314 315 // Sanity check that the point is in both active and right tab. 316 ASSERT_TRUE(IsPointInTab(active_tab, right_overlap)); 317 ASSERT_TRUE(IsPointInTab(right_tab, right_overlap)); 318 319 EXPECT_EQ(active_tab, 320 FindTabView(tab_strip_->GetTooltipHandlerForPoint(right_overlap))); 321 322 // Test that if neither of tabs is active, the left one is selected. 323 gfx::Point unactive_overlap( 324 (right_tab->x() + most_right_tab->bounds().right() + 1) / 2, 325 right_tab->bounds().bottom() - 1); 326 327 // Sanity check that the point is in both active and left tab. 328 ASSERT_TRUE(IsPointInTab(right_tab, unactive_overlap)); 329 ASSERT_TRUE(IsPointInTab(most_right_tab, unactive_overlap)); 330 331 EXPECT_EQ( 332 right_tab, 333 FindTabView(tab_strip_->GetTooltipHandlerForPoint(unactive_overlap))); 334 335 // Confirm that tab strip doe not return tooltip handler for points that 336 // don't hit it. 337 EXPECT_FALSE(tab_strip_->GetTooltipHandlerForPoint(gfx::Point(-1, 2))); 338} 339