Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / ash / launcher / launcher_view_unittest.cc
blobd1e2e768c164c6a6df29944b88f0eab248ffcca4
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.
5 #include "ash/launcher/launcher_view.h"
7 #include <algorithm>
8 #include <vector>
10 #include "ash/launcher/launcher.h"
11 #include "ash/launcher/launcher_button.h"
12 #include "ash/launcher/launcher_icon_observer.h"
13 #include "ash/launcher/launcher_model.h"
14 #include "ash/launcher/launcher_tooltip_manager.h"
15 #include "ash/root_window_controller.h"
16 #include "ash/shelf/shelf_layout_manager.h"
17 #include "ash/shelf/shelf_widget.h"
18 #include "ash/shell.h"
19 #include "ash/shell_window_ids.h"
20 #include "ash/test/ash_test_base.h"
21 #include "ash/test/launcher_view_test_api.h"
22 #include "ash/test/shell_test_api.h"
23 #include "ash/test/test_launcher_delegate.h"
24 #include "base/basictypes.h"
25 #include "base/compiler_specific.h"
26 #include "base/memory/scoped_ptr.h"
27 #include "grit/ash_resources.h"
28 #include "ui/aura/root_window.h"
29 #include "ui/aura/test/aura_test_base.h"
30 #include "ui/aura/window.h"
31 #include "ui/base/events/event.h"
32 #include "ui/base/events/event_constants.h"
33 #include "ui/compositor/layer.h"
34 #include "ui/views/widget/widget.h"
35 #include "ui/views/widget/widget_delegate.h"
37 namespace {
38 const int kExpectedAppIndex = 1;
41 namespace ash {
42 namespace test {
44 ////////////////////////////////////////////////////////////////////////////////
45 // LauncherIconObserver tests.
47 class TestLauncherIconObserver : public LauncherIconObserver {
48 public:
49 explicit TestLauncherIconObserver(Launcher* launcher)
50 : launcher_(launcher),
51 change_notified_(false) {
52 if (launcher_)
53 launcher_->AddIconObserver(this);
56 virtual ~TestLauncherIconObserver() {
57 if (launcher_)
58 launcher_->RemoveIconObserver(this);
61 // LauncherIconObserver implementation.
62 virtual void OnLauncherIconPositionsChanged() OVERRIDE {
63 change_notified_ = true;
66 int change_notified() const { return change_notified_; }
67 void Reset() { change_notified_ = false; }
69 private:
70 Launcher* launcher_;
71 bool change_notified_;
73 DISALLOW_COPY_AND_ASSIGN(TestLauncherIconObserver);
76 class LauncherViewIconObserverTest : public ash::test::AshTestBase {
77 public:
78 LauncherViewIconObserverTest() {}
79 virtual ~LauncherViewIconObserverTest() {}
81 virtual void SetUp() OVERRIDE {
82 AshTestBase::SetUp();
83 Launcher* launcher = Launcher::ForPrimaryDisplay();
84 observer_.reset(new TestLauncherIconObserver(launcher));
86 launcher_view_test_.reset(new LauncherViewTestAPI(
87 launcher->GetLauncherViewForTest()));
88 launcher_view_test_->SetAnimationDuration(1);
91 virtual void TearDown() OVERRIDE {
92 observer_.reset();
93 AshTestBase::TearDown();
96 TestLauncherIconObserver* observer() { return observer_.get(); }
98 LauncherViewTestAPI* launcher_view_test() {
99 return launcher_view_test_.get();
102 Launcher* LauncherForSecondaryDisplay() {
103 return Launcher::ForWindow(Shell::GetAllRootWindows()[1]);
106 private:
107 scoped_ptr<TestLauncherIconObserver> observer_;
108 scoped_ptr<LauncherViewTestAPI> launcher_view_test_;
110 DISALLOW_COPY_AND_ASSIGN(LauncherViewIconObserverTest);
113 TEST_F(LauncherViewIconObserverTest, AddRemove) {
114 ash::test::TestLauncherDelegate* launcher_delegate =
115 ash::test::TestLauncherDelegate::instance();
116 ASSERT_TRUE(launcher_delegate);
118 views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
119 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
120 params.bounds = gfx::Rect(0, 0, 200, 200);
121 params.context = CurrentContext();
123 scoped_ptr<views::Widget> widget(new views::Widget());
124 widget->Init(params);
125 launcher_delegate->AddLauncherItem(widget->GetNativeWindow());
126 launcher_view_test()->RunMessageLoopUntilAnimationsDone();
127 EXPECT_TRUE(observer()->change_notified());
128 observer()->Reset();
130 widget->Show();
131 widget->GetNativeWindow()->parent()->RemoveChild(widget->GetNativeWindow());
132 launcher_view_test()->RunMessageLoopUntilAnimationsDone();
133 EXPECT_TRUE(observer()->change_notified());
134 observer()->Reset();
137 // Sometimes fails on trybots on win7_aura. http://crbug.com/177135
138 #if defined(OS_WIN)
139 #define MAYBE_AddRemoveWithMultipleDisplays \
140 DISABLED_AddRemoveWithMultipleDisplays
141 #else
142 #define MAYBE_AddRemoveWithMultipleDisplays \
143 AddRemoveWithMultipleDisplays
144 #endif
145 // Make sure creating/deleting an window on one displays notifies a
146 // launcher on external display as well as one on primary.
147 TEST_F(LauncherViewIconObserverTest, MAYBE_AddRemoveWithMultipleDisplays) {
148 UpdateDisplay("400x400,400x400");
149 TestLauncherIconObserver second_observer(LauncherForSecondaryDisplay());
151 ash::test::TestLauncherDelegate* launcher_delegate =
152 ash::test::TestLauncherDelegate::instance();
153 ASSERT_TRUE(launcher_delegate);
155 views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
156 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
157 params.bounds = gfx::Rect(0, 0, 200, 200);
158 params.context = CurrentContext();
160 scoped_ptr<views::Widget> widget(new views::Widget());
161 widget->Init(params);
162 launcher_delegate->AddLauncherItem(widget->GetNativeWindow());
163 launcher_view_test()->RunMessageLoopUntilAnimationsDone();
164 EXPECT_TRUE(observer()->change_notified());
165 EXPECT_TRUE(second_observer.change_notified());
166 observer()->Reset();
167 second_observer.Reset();
169 widget->GetNativeWindow()->parent()->RemoveChild(widget->GetNativeWindow());
170 launcher_view_test()->RunMessageLoopUntilAnimationsDone();
171 EXPECT_TRUE(observer()->change_notified());
172 EXPECT_TRUE(second_observer.change_notified());
174 observer()->Reset();
175 second_observer.Reset();
178 TEST_F(LauncherViewIconObserverTest, BoundsChanged) {
179 ash::ShelfWidget* shelf = Shell::GetPrimaryRootWindowController()->shelf();
180 Launcher* launcher = Launcher::ForPrimaryDisplay();
181 gfx::Size shelf_size =
182 shelf->GetWindowBoundsInScreen().size();
183 shelf_size.set_width(shelf_size.width() / 2);
184 ASSERT_GT(shelf_size.width(), 0);
185 launcher->SetLauncherViewBounds(gfx::Rect(shelf_size));
186 // No animation happens for LauncherView bounds change.
187 EXPECT_TRUE(observer()->change_notified());
188 observer()->Reset();
191 ////////////////////////////////////////////////////////////////////////////////
192 // LauncherView tests.
194 class LauncherViewTest : public AshTestBase {
195 public:
196 LauncherViewTest() : model_(NULL), launcher_view_(NULL) {}
197 virtual ~LauncherViewTest() {}
199 virtual void SetUp() OVERRIDE {
200 AshTestBase::SetUp();
201 test::ShellTestApi test_api(Shell::GetInstance());
202 model_ = test_api.launcher_model();
203 Launcher* launcher = Launcher::ForPrimaryDisplay();
204 launcher_view_ = launcher->GetLauncherViewForTest();
206 // The bounds should be big enough for 4 buttons + overflow chevron.
207 launcher_view_->SetBounds(0, 0, 500, 50);
209 test_api_.reset(new LauncherViewTestAPI(launcher_view_));
210 test_api_->SetAnimationDuration(1); // Speeds up animation for test.
213 virtual void TearDown() OVERRIDE {
214 test_api_.reset();
215 AshTestBase::TearDown();
218 protected:
219 LauncherID AddAppShortcut() {
220 LauncherItem item;
221 item.type = TYPE_APP_SHORTCUT;
222 item.status = STATUS_CLOSED;
224 LauncherID id = model_->next_id();
225 model_->Add(item);
226 test_api_->RunMessageLoopUntilAnimationsDone();
227 return id;
230 LauncherID AddTabbedBrowserNoWait() {
231 LauncherItem item;
232 item.type = TYPE_TABBED;
233 item.status = STATUS_RUNNING;
235 LauncherID id = model_->next_id();
236 model_->Add(item);
237 return id;
240 LauncherID AddTabbedBrowser() {
241 LauncherID id = AddTabbedBrowserNoWait();
242 test_api_->RunMessageLoopUntilAnimationsDone();
243 return id;
246 LauncherID AddPanel() {
247 LauncherID id = AddPanelNoWait();
248 test_api_->RunMessageLoopUntilAnimationsDone();
249 return id;
252 LauncherID AddPlatformAppNoWait() {
253 LauncherItem item;
254 item.type = TYPE_PLATFORM_APP;
255 item.status = STATUS_RUNNING;
257 LauncherID id = model_->next_id();
258 model_->Add(item);
259 return id;
262 LauncherID AddPanelNoWait() {
263 LauncherItem item;
264 item.type = TYPE_APP_PANEL;
265 item.status = STATUS_RUNNING;
267 LauncherID id = model_->next_id();
268 model_->Add(item);
269 return id;
272 LauncherID AddPlatformApp() {
273 LauncherID id = AddPlatformAppNoWait();
274 test_api_->RunMessageLoopUntilAnimationsDone();
275 return id;
278 void RemoveByID(LauncherID id) {
279 model_->RemoveItemAt(model_->ItemIndexByID(id));
280 test_api_->RunMessageLoopUntilAnimationsDone();
283 internal::LauncherButton* GetButtonByID(LauncherID id) {
284 int index = model_->ItemIndexByID(id);
285 return test_api_->GetButton(index);
288 LauncherItem GetItemByID(LauncherID id) {
289 LauncherItems::const_iterator items = model_->ItemByID(id);
290 return *items;
293 void CheckModelIDs(
294 const std::vector<std::pair<LauncherID, views::View*> >& id_map) {
295 size_t map_index = 0;
296 for (size_t model_index = 0;
297 model_index < model_->items().size();
298 ++model_index) {
299 ash::LauncherItem item = model_->items()[model_index];
300 ash::LauncherID id = item.id;
301 EXPECT_EQ(id_map[map_index].first, id);
302 EXPECT_EQ(id_map[map_index].second, GetButtonByID(id));
303 ++map_index;
305 ASSERT_EQ(map_index, id_map.size());
308 void VerifyLauncherItemBoundsAreValid() {
309 for (int i=0;i <= test_api_->GetLastVisibleIndex(); ++i) {
310 if (test_api_->GetButton(i)) {
311 gfx::Rect launcher_view_bounds = launcher_view_->GetLocalBounds();
312 gfx::Rect item_bounds = test_api_->GetBoundsByIndex(i);
313 EXPECT_TRUE(item_bounds.x() >= 0);
314 EXPECT_TRUE(item_bounds.y() >= 0);
315 EXPECT_TRUE(item_bounds.right() <= launcher_view_bounds.width());
316 EXPECT_TRUE(item_bounds.bottom() <= launcher_view_bounds.height());
321 views::View* SimulateDrag(internal::LauncherButtonHost::Pointer pointer,
322 int button_index,
323 int destination_index) {
324 // Add kExpectedAppIndex to each button index to allow default icons.
325 internal::LauncherButtonHost* button_host = launcher_view_;
327 // Mouse down.
328 views::View* button =
329 test_api_->GetButton(kExpectedAppIndex + button_index);
330 ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED,
331 button->bounds().origin(),
332 button->bounds().origin(), 0);
333 button_host->PointerPressedOnButton(button, pointer, click_event);
335 // Drag.
336 views::View* destination =
337 test_api_->GetButton(kExpectedAppIndex + destination_index);
338 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED,
339 destination->bounds().origin(),
340 destination->bounds().origin(), 0);
341 button_host->PointerDraggedOnButton(button, pointer, drag_event);
342 return button;
345 void SetupForDragTest(
346 std::vector<std::pair<LauncherID, views::View*> >* id_map) {
347 // Initialize |id_map| with the automatically-created launcher buttons.
348 for (size_t i = 0; i < model_->items().size(); ++i) {
349 internal::LauncherButton* button = test_api_->GetButton(i);
350 id_map->push_back(std::make_pair(model_->items()[i].id, button));
352 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(*id_map));
354 // Add 5 app launcher buttons for testing.
355 for (int i = 0; i < 5; ++i) {
356 LauncherID id = AddAppShortcut();
357 id_map->insert(id_map->begin() + (kExpectedAppIndex + i),
358 std::make_pair(id, GetButtonByID(id)));
360 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(*id_map));
363 views::View* GetTooltipAnchorView() {
364 return launcher_view_->tooltip_manager()->anchor_;
367 void ShowTooltip() {
368 launcher_view_->tooltip_manager()->ShowInternal();
371 LauncherModel* model_;
372 internal::LauncherView* launcher_view_;
374 scoped_ptr<LauncherViewTestAPI> test_api_;
376 private:
377 DISALLOW_COPY_AND_ASSIGN(LauncherViewTest);
380 // Checks that the icon positions do not shift with a state change.
381 TEST_F(LauncherViewTest, NoStateChangeIconMovement) {
382 LauncherID last_added = AddAppShortcut();
383 internal::LauncherButton* button = GetButtonByID(last_added);
384 EXPECT_EQ(button->state(), ash::internal::LauncherButton::STATE_NORMAL);
385 gfx::Rect old_bounds = button->GetIconBounds();
387 button->AddState(ash::internal::LauncherButton::STATE_HOVERED);
388 gfx::Rect hovered_bounds = button->GetIconBounds();
389 EXPECT_EQ(old_bounds.ToString(), hovered_bounds.ToString());
392 // Adds browser button until overflow and verifies that the last added browser
393 // button is hidden.
394 TEST_F(LauncherViewTest, AddBrowserUntilOverflow) {
395 // All buttons should be visible.
396 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
397 test_api_->GetButtonCount());
399 // Add tabbed browser until overflow.
400 int items_added = 0;
401 LauncherID last_added = AddTabbedBrowser();
402 while (!test_api_->IsOverflowButtonVisible()) {
403 // Added button is visible after animation while in this loop.
404 EXPECT_TRUE(GetButtonByID(last_added)->visible());
406 last_added = AddTabbedBrowser();
407 ++items_added;
408 ASSERT_LT(items_added, 10000);
411 // The last added button should be invisible.
412 EXPECT_FALSE(GetButtonByID(last_added)->visible());
415 // Adds one browser button then adds app shortcut until overflow. Verifies that
416 // the browser button gets hidden on overflow and last added app shortcut is
417 // still visible.
418 TEST_F(LauncherViewTest, AddAppShortcutWithBrowserButtonUntilOverflow) {
419 // All buttons should be visible.
420 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
421 test_api_->GetButtonCount());
423 LauncherID browser_button_id = AddTabbedBrowser();
425 // Add app shortcut until overflow.
426 int items_added = 0;
427 LauncherID last_added = AddAppShortcut();
428 while (!test_api_->IsOverflowButtonVisible()) {
429 // Added button is visible after animation while in this loop.
430 EXPECT_TRUE(GetButtonByID(last_added)->visible());
432 last_added = AddAppShortcut();
433 ++items_added;
434 ASSERT_LT(items_added, 10000);
437 // The last added app short button should be visible.
438 EXPECT_TRUE(GetButtonByID(last_added)->visible());
439 // And the browser button is invisible.
440 EXPECT_FALSE(GetButtonByID(browser_button_id)->visible());
443 TEST_F(LauncherViewTest, AddPanelHidesTabbedBrowser) {
444 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
445 test_api_->GetButtonCount());
447 // Add tabbed browser until overflow, remember last visible tabbed browser.
448 int items_added = 0;
449 LauncherID first_added = AddTabbedBrowser();
450 EXPECT_TRUE(GetButtonByID(first_added)->visible());
451 LauncherID last_visible = first_added;
452 while (true) {
453 LauncherID added = AddTabbedBrowser();
454 if (test_api_->IsOverflowButtonVisible()) {
455 EXPECT_FALSE(GetButtonByID(added)->visible());
456 break;
458 last_visible = added;
459 ++items_added;
460 ASSERT_LT(items_added, 10000);
463 LauncherID panel = AddPanel();
464 EXPECT_TRUE(GetButtonByID(panel)->visible());
465 EXPECT_FALSE(GetButtonByID(last_visible)->visible());
467 RemoveByID(panel);
468 EXPECT_TRUE(GetButtonByID(last_visible)->visible());
471 // When there are more panels then browsers we should hide panels rather
472 // than browsers.
473 TEST_F(LauncherViewTest, BrowserHidesExcessPanels) {
474 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
475 test_api_->GetButtonCount());
477 // Add tabbed browser.
478 LauncherID browser = AddTabbedBrowser();
479 LauncherID first_panel = AddPanel();
481 EXPECT_TRUE(GetButtonByID(browser)->visible());
482 EXPECT_TRUE(GetButtonByID(first_panel)->visible());
484 // Add panels until there is an overflow.
485 LauncherID last_panel = first_panel;
486 int items_added = 0;
487 while (!test_api_->IsOverflowButtonVisible()) {
488 last_panel = AddPanel();
489 ++items_added;
490 ASSERT_LT(items_added, 10000);
493 // The first panel should now be hidden by the new browsers needing space.
494 EXPECT_FALSE(GetButtonByID(first_panel)->visible());
495 EXPECT_TRUE(GetButtonByID(last_panel)->visible());
496 EXPECT_TRUE(GetButtonByID(browser)->visible());
498 // Adding browsers should eventually begin to hide browsers. We will add
499 // browsers until either the last panel or browser is hidden.
500 items_added = 0;
501 while (GetButtonByID(browser)->visible() &&
502 GetButtonByID(last_panel)->visible()) {
503 browser = AddTabbedBrowser();
504 ++items_added;
505 ASSERT_LT(items_added, 10000);
507 EXPECT_TRUE(GetButtonByID(last_panel)->visible());
508 EXPECT_FALSE(GetButtonByID(browser)->visible());
511 // Adds button until overflow then removes first added one. Verifies that
512 // the last added one changes from invisible to visible and overflow
513 // chevron is gone.
514 TEST_F(LauncherViewTest, RemoveButtonRevealsOverflowed) {
515 // All buttons should be visible.
516 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
517 test_api_->GetButtonCount());
519 // Add tabbed browser until overflow.
520 int items_added = 0;
521 LauncherID first_added = AddTabbedBrowser();
522 LauncherID last_added = first_added;
523 while (!test_api_->IsOverflowButtonVisible()) {
524 last_added = AddTabbedBrowser();
525 ++items_added;
526 ASSERT_LT(items_added, 10000);
529 // Expect add more than 1 button. First added is visible and last is not.
530 EXPECT_NE(first_added, last_added);
531 EXPECT_TRUE(GetButtonByID(first_added)->visible());
532 EXPECT_FALSE(GetButtonByID(last_added)->visible());
534 // Remove first added.
535 RemoveByID(first_added);
537 // Last added button becomes visible and overflow chevron is gone.
538 EXPECT_TRUE(GetButtonByID(last_added)->visible());
539 EXPECT_EQ(1.0f, GetButtonByID(last_added)->layer()->opacity());
540 EXPECT_FALSE(test_api_->IsOverflowButtonVisible());
543 // Verifies that remove last overflowed button should hide overflow chevron.
544 TEST_F(LauncherViewTest, RemoveLastOverflowed) {
545 // All buttons should be visible.
546 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
547 test_api_->GetButtonCount());
549 // Add tabbed browser until overflow.
550 int items_added = 0;
551 LauncherID last_added = AddTabbedBrowser();
552 while (!test_api_->IsOverflowButtonVisible()) {
553 last_added = AddTabbedBrowser();
554 ++items_added;
555 ASSERT_LT(items_added, 10000);
558 RemoveByID(last_added);
559 EXPECT_FALSE(test_api_->IsOverflowButtonVisible());
562 // Adds browser button without waiting for animation to finish and verifies
563 // that all added buttons are visible.
564 TEST_F(LauncherViewTest, AddButtonQuickly) {
565 // All buttons should be visible.
566 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
567 test_api_->GetButtonCount());
569 // Add a few tabbed browser quickly without wait for animation.
570 int added_count = 0;
571 while (!test_api_->IsOverflowButtonVisible()) {
572 AddTabbedBrowserNoWait();
573 ++added_count;
574 ASSERT_LT(added_count, 10000);
577 // LauncherView should be big enough to hold at least 3 new buttons.
578 ASSERT_GE(added_count, 3);
580 // Wait for the last animation to finish.
581 test_api_->RunMessageLoopUntilAnimationsDone();
583 // Verifies non-overflow buttons are visible.
584 for (int i = 0; i <= test_api_->GetLastVisibleIndex(); ++i) {
585 internal::LauncherButton* button = test_api_->GetButton(i);
586 if (button) {
587 EXPECT_TRUE(button->visible()) << "button index=" << i;
588 EXPECT_EQ(1.0f, button->layer()->opacity()) << "button index=" << i;
593 // Check that model changes are handled correctly while a launcher icon is being
594 // dragged.
595 TEST_F(LauncherViewTest, ModelChangesWhileDragging) {
596 internal::LauncherButtonHost* button_host = launcher_view_;
598 std::vector<std::pair<LauncherID, views::View*> > id_map;
599 SetupForDragTest(&id_map);
601 // Dragging changes model order.
602 views::View* dragged_button = SimulateDrag(
603 internal::LauncherButtonHost::MOUSE, 0, 2);
604 std::rotate(id_map.begin() + kExpectedAppIndex,
605 id_map.begin() + kExpectedAppIndex + 1,
606 id_map.begin() + kExpectedAppIndex + 3);
607 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
609 // Cancelling the drag operation restores previous order.
610 button_host->PointerReleasedOnButton(dragged_button,
611 internal::LauncherButtonHost::MOUSE,
612 true);
613 std::rotate(id_map.begin() + kExpectedAppIndex,
614 id_map.begin() + kExpectedAppIndex + 2,
615 id_map.begin() + kExpectedAppIndex + 3);
616 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
618 // Deleting an item keeps the remaining intact.
619 dragged_button = SimulateDrag(internal::LauncherButtonHost::MOUSE, 0, 2);
620 model_->RemoveItemAt(kExpectedAppIndex + 1);
621 id_map.erase(id_map.begin() + kExpectedAppIndex + 1);
622 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
623 button_host->PointerReleasedOnButton(dragged_button,
624 internal::LauncherButtonHost::MOUSE,
625 false);
627 // Adding a launcher item cancels the drag and respects the order.
628 dragged_button = SimulateDrag(internal::LauncherButtonHost::MOUSE, 0, 2);
629 LauncherID new_id = AddAppShortcut();
630 id_map.insert(id_map.begin() + kExpectedAppIndex + 4,
631 std::make_pair(new_id, GetButtonByID(new_id)));
632 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
633 button_host->PointerReleasedOnButton(dragged_button,
634 internal::LauncherButtonHost::MOUSE,
635 false);
637 // Adding a launcher item at the end (i.e. a panel) canels drag and respects
638 // the order.
639 dragged_button = SimulateDrag(internal::LauncherButtonHost::MOUSE, 0, 2);
640 new_id = AddPanel();
641 id_map.insert(id_map.begin() + kExpectedAppIndex + 6,
642 std::make_pair(new_id, GetButtonByID(new_id)));
643 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
644 button_host->PointerReleasedOnButton(dragged_button,
645 internal::LauncherButtonHost::MOUSE,
646 false);
649 // Check that 2nd drag from the other pointer would be ignored.
650 TEST_F(LauncherViewTest, SimultaneousDrag) {
651 internal::LauncherButtonHost* button_host = launcher_view_;
653 std::vector<std::pair<LauncherID, views::View*> > id_map;
654 SetupForDragTest(&id_map);
656 // Start a mouse drag.
657 views::View* dragged_button_mouse = SimulateDrag(
658 internal::LauncherButtonHost::MOUSE, 0, 2);
659 std::rotate(id_map.begin() + kExpectedAppIndex,
660 id_map.begin() + kExpectedAppIndex + 1,
661 id_map.begin() + kExpectedAppIndex + 3);
662 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
663 // Attempt a touch drag before the mouse drag finishes.
664 views::View* dragged_button_touch = SimulateDrag(
665 internal::LauncherButtonHost::TOUCH, 3, 1);
667 // Nothing changes since 2nd drag is ignored.
668 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
670 // Finish the mouse drag.
671 button_host->PointerReleasedOnButton(dragged_button_mouse,
672 internal::LauncherButtonHost::MOUSE,
673 false);
674 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
676 // Now start a touch drag.
677 dragged_button_touch = SimulateDrag(
678 internal::LauncherButtonHost::TOUCH, 3, 1);
679 std::rotate(id_map.begin() + kExpectedAppIndex + 2,
680 id_map.begin() + kExpectedAppIndex + 3,
681 id_map.begin() + kExpectedAppIndex + 4);
682 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
684 // And attempt a mouse drag before the touch drag finishes.
685 dragged_button_mouse = SimulateDrag(
686 internal::LauncherButtonHost::MOUSE, 0, 1);
688 // Nothing changes since 2nd drag is ignored.
689 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
691 button_host->PointerReleasedOnButton(dragged_button_touch,
692 internal::LauncherButtonHost::TOUCH,
693 false);
694 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
697 // Confirm that item status changes are reflected in the buttons.
698 TEST_F(LauncherViewTest, LauncherItemStatus) {
699 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
700 test_api_->GetButtonCount());
702 // Add tabbed browser.
703 LauncherID last_added = AddTabbedBrowser();
704 LauncherItem item = GetItemByID(last_added);
705 int index = model_->ItemIndexByID(last_added);
706 internal::LauncherButton* button = GetButtonByID(last_added);
707 ASSERT_EQ(internal::LauncherButton::STATE_RUNNING, button->state());
708 item.status = ash::STATUS_ACTIVE;
709 model_->Set(index, item);
710 ASSERT_EQ(internal::LauncherButton::STATE_ACTIVE, button->state());
711 item.status = ash::STATUS_ATTENTION;
712 model_->Set(index, item);
713 ASSERT_EQ(internal::LauncherButton::STATE_ATTENTION, button->state());
716 // Confirm that item status changes are reflected in the buttons
717 // for platform apps.
718 TEST_F(LauncherViewTest, LauncherItemStatusPlatformApp) {
719 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
720 test_api_->GetButtonCount());
722 // Add tabbed browser.
723 LauncherID last_added = AddPlatformApp();
724 LauncherItem item = GetItemByID(last_added);
725 int index = model_->ItemIndexByID(last_added);
726 internal::LauncherButton* button = GetButtonByID(last_added);
727 ASSERT_EQ(internal::LauncherButton::STATE_RUNNING, button->state());
728 item.status = ash::STATUS_ACTIVE;
729 model_->Set(index, item);
730 ASSERT_EQ(internal::LauncherButton::STATE_ACTIVE, button->state());
731 item.status = ash::STATUS_ATTENTION;
732 model_->Set(index, item);
733 ASSERT_EQ(internal::LauncherButton::STATE_ATTENTION, button->state());
736 // Confirm that launcher item bounds are correctly updated on shelf changes.
737 TEST_F(LauncherViewTest, LauncherItemBoundsCheck) {
738 internal::ShelfLayoutManager* shelf_layout_manager =
739 Shell::GetPrimaryRootWindowController()->shelf()->shelf_layout_manager();
740 VerifyLauncherItemBoundsAreValid();
741 shelf_layout_manager->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
742 test_api_->RunMessageLoopUntilAnimationsDone();
743 VerifyLauncherItemBoundsAreValid();
744 shelf_layout_manager->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
745 test_api_->RunMessageLoopUntilAnimationsDone();
746 VerifyLauncherItemBoundsAreValid();
749 TEST_F(LauncherViewTest, LauncherTooltipTest) {
750 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
751 test_api_->GetButtonCount());
753 // Prepare some items to the launcher.
754 LauncherID app_button_id = AddAppShortcut();
755 LauncherID tab_button_id = AddTabbedBrowser();
757 internal::LauncherButton* app_button = GetButtonByID(app_button_id);
758 internal::LauncherButton* tab_button = GetButtonByID(tab_button_id);
760 internal::LauncherButtonHost* button_host = launcher_view_;
761 internal::LauncherTooltipManager* tooltip_manager =
762 launcher_view_->tooltip_manager();
764 button_host->MouseEnteredButton(app_button);
765 // There's a delay to show the tooltip, so it's not visible yet.
766 EXPECT_FALSE(tooltip_manager->IsVisible());
767 EXPECT_EQ(app_button, GetTooltipAnchorView());
769 ShowTooltip();
770 EXPECT_TRUE(tooltip_manager->IsVisible());
772 // Once it's visible, it keeps visibility and is pointing to the same
773 // item.
774 button_host->MouseExitedButton(app_button);
775 EXPECT_TRUE(tooltip_manager->IsVisible());
776 EXPECT_EQ(app_button, GetTooltipAnchorView());
778 // When entered to another item, it switches to the new item. There is no
779 // delay for the visibility.
780 button_host->MouseEnteredButton(tab_button);
781 EXPECT_TRUE(tooltip_manager->IsVisible());
782 EXPECT_EQ(tab_button, GetTooltipAnchorView());
784 button_host->MouseExitedButton(tab_button);
785 tooltip_manager->Close();
787 // Next time: enter app_button -> move immediately to tab_button.
788 button_host->MouseEnteredButton(app_button);
789 button_host->MouseExitedButton(app_button);
790 button_host->MouseEnteredButton(tab_button);
791 EXPECT_FALSE(tooltip_manager->IsVisible());
792 EXPECT_EQ(tab_button, GetTooltipAnchorView());
795 TEST_F(LauncherViewTest, ShouldHideTooltipTest) {
796 LauncherID app_button_id = AddAppShortcut();
797 LauncherID tab_button_id = AddTabbedBrowser();
799 // The tooltip shouldn't hide if the mouse is on normal buttons.
800 for (int i = 0; i < test_api_->GetButtonCount(); i++) {
801 internal::LauncherButton* button = test_api_->GetButton(i);
802 if (!button)
803 continue;
805 EXPECT_FALSE(launcher_view_->ShouldHideTooltip(
806 button->GetMirroredBounds().CenterPoint()))
807 << "LauncherView tries to hide on button " << i;
810 // The tooltip should not hide on the app-list button.
811 views::View* app_list_button = launcher_view_->GetAppListButtonView();
812 EXPECT_FALSE(launcher_view_->ShouldHideTooltip(
813 app_list_button->GetMirroredBounds().CenterPoint()));
815 // The tooltip shouldn't hide if the mouse is in the gap between two buttons.
816 gfx::Rect app_button_rect = GetButtonByID(app_button_id)->GetMirroredBounds();
817 gfx::Rect tab_button_rect = GetButtonByID(tab_button_id)->GetMirroredBounds();
818 ASSERT_FALSE(app_button_rect.Intersects(tab_button_rect));
819 EXPECT_FALSE(launcher_view_->ShouldHideTooltip(
820 gfx::UnionRects(app_button_rect, tab_button_rect).CenterPoint()));
822 // The tooltip should hide if it's outside of all buttons.
823 gfx::Rect all_area;
824 for (int i = 0; i < test_api_->GetButtonCount(); i++) {
825 internal::LauncherButton* button = test_api_->GetButton(i);
826 if (!button)
827 continue;
829 all_area.Union(button->GetMirroredBounds());
831 all_area.Union(launcher_view_->GetAppListButtonView()->GetMirroredBounds());
832 EXPECT_FALSE(launcher_view_->ShouldHideTooltip(all_area.origin()));
833 EXPECT_FALSE(launcher_view_->ShouldHideTooltip(
834 gfx::Point(all_area.right() - 1, all_area.bottom() - 1)));
835 EXPECT_TRUE(launcher_view_->ShouldHideTooltip(
836 gfx::Point(all_area.right(), all_area.y())));
837 EXPECT_TRUE(launcher_view_->ShouldHideTooltip(
838 gfx::Point(all_area.x() - 1, all_area.y())));
839 EXPECT_TRUE(launcher_view_->ShouldHideTooltip(
840 gfx::Point(all_area.x(), all_area.y() - 1)));
841 EXPECT_TRUE(launcher_view_->ShouldHideTooltip(
842 gfx::Point(all_area.x(), all_area.bottom())));
845 TEST_F(LauncherViewTest, ShouldHideTooltipWithAppListWindowTest) {
846 Shell::GetInstance()->ToggleAppList(NULL);
847 ASSERT_TRUE(Shell::GetInstance()->GetAppListWindow());
849 // The tooltip shouldn't hide if the mouse is on normal buttons.
850 for (int i = 1; i < test_api_->GetButtonCount(); i++) {
851 internal::LauncherButton* button = test_api_->GetButton(i);
852 if (!button)
853 continue;
855 EXPECT_FALSE(launcher_view_->ShouldHideTooltip(
856 button->GetMirroredBounds().CenterPoint()))
857 << "LauncherView tries to hide on button " << i;
860 // The tooltip should hide on the app-list button.
861 views::View* app_list_button = launcher_view_->GetAppListButtonView();
862 EXPECT_TRUE(launcher_view_->ShouldHideTooltip(
863 app_list_button->GetMirroredBounds().CenterPoint()));
866 // Resizing launcher view while an add animation without fade-in is running,
867 // which happens when overflow happens. App list button should end up in its
868 // new ideal bounds.
869 TEST_F(LauncherViewTest, ResizeDuringOverflowAddAnimation) {
870 // All buttons should be visible.
871 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
872 test_api_->GetButtonCount());
874 // Add buttons until overflow. Let the non-overflow add animations finish but
875 // leave the last running.
876 int items_added = 0;
877 AddTabbedBrowserNoWait();
878 while (!test_api_->IsOverflowButtonVisible()) {
879 test_api_->RunMessageLoopUntilAnimationsDone();
880 AddTabbedBrowserNoWait();
881 ++items_added;
882 ASSERT_LT(items_added, 10000);
885 // Resize launcher view with that animation running and stay overflown.
886 gfx::Rect bounds = launcher_view_->bounds();
887 bounds.set_width(bounds.width() - kLauncherPreferredSize);
888 launcher_view_->SetBoundsRect(bounds);
889 ASSERT_TRUE(test_api_->IsOverflowButtonVisible());
891 // Finish the animation.
892 test_api_->RunMessageLoopUntilAnimationsDone();
894 // App list button should ends up in its new ideal bounds.
895 const int app_list_button_index = test_api_->GetButtonCount() - 1;
896 const gfx::Rect& app_list_ideal_bounds =
897 test_api_->GetIdealBoundsByIndex(app_list_button_index);
898 const gfx::Rect& app_list_bounds =
899 test_api_->GetBoundsByIndex(app_list_button_index);
900 EXPECT_EQ(app_list_bounds, app_list_ideal_bounds);
903 // Check that the first item in the list follows Fitt's law by including the
904 // first pixel and being therefore bigger then the others.
905 TEST_F(LauncherViewTest, CheckFittsLaw) {
906 // All buttons should be visible.
907 ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
908 test_api_->GetButtonCount());
909 gfx::Rect ideal_bounds_0 = test_api_->GetIdealBoundsByIndex(0);
910 gfx::Rect ideal_bounds_1 = test_api_->GetIdealBoundsByIndex(1);
911 EXPECT_GT(ideal_bounds_0.width(), ideal_bounds_1.width());
914 } // namespace test
915 } // namespace ash