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 "ash/shell.h" 6 #include "ash/test/ash_test_base.h" 7 #include "base/strings/utf_string_conversions.h" 8 #include "ui/aura/env.h" 9 #include "ui/aura/window.h" 10 #include "ui/aura/window_event_dispatcher.h" 11 #include "ui/events/test/event_generator.h" 12 #include "ui/gfx/font.h" 13 #include "ui/gfx/point.h" 14 #include "ui/views/corewm/tooltip_controller.h" 15 #include "ui/views/corewm/tooltip_controller_test_helper.h" 16 #include "ui/views/view.h" 17 #include "ui/views/widget/widget.h" 18 #include "ui/wm/public/tooltip_client.h" 19 20 using views::corewm::TooltipController; 21 using views::corewm::test::TooltipTestView; 22 using views::corewm::test::TooltipControllerTestHelper; 23 24 // The tests in this file exercise bits of TooltipController that are hard to 25 // test outside of ash. Meaning these tests require the shell and related things 26 // to be installed. 27 28 namespace ash { 29 namespace test { 30 31 namespace { 32 33 views::Widget* CreateNewWidgetWithBoundsOn(int display, 34 const gfx::Rect& bounds) { 35 views::Widget* widget = new views::Widget; 36 views::Widget::InitParams params; 37 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; 38 params.accept_events = true; 39 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 40 params.context = Shell::GetAllRootWindows().at(display); 41 params.bounds = bounds; 42 widget->Init(params); 43 widget->Show(); 44 return widget; 45 } 46 47 views::Widget* CreateNewWidgetOn(int display) { 48 return CreateNewWidgetWithBoundsOn(display, gfx::Rect()); 49 } 50 51 void AddViewToWidgetAndResize(views::Widget* widget, views::View* view) { 52 if (!widget->GetContentsView()) { 53 views::View* contents_view = new views::View; 54 widget->SetContentsView(contents_view); 55 } 56 57 views::View* contents_view = widget->GetContentsView(); 58 contents_view->AddChildView(view); 59 view->SetBounds(contents_view->width(), 0, 100, 100); 60 gfx::Rect contents_view_bounds = contents_view->bounds(); 61 contents_view_bounds.Union(view->bounds()); 62 contents_view->SetBoundsRect(contents_view_bounds); 63 widget->SetBounds(gfx::Rect(widget->GetWindowBoundsInScreen().origin(), 64 contents_view_bounds.size())); 65 } 66 67 TooltipController* GetController() { 68 return static_cast<TooltipController*>( 69 aura::client::GetTooltipClient(Shell::GetPrimaryRootWindow())); 70 } 71 72 } // namespace 73 74 class TooltipControllerTest : public AshTestBase { 75 public: 76 TooltipControllerTest() {} 77 virtual ~TooltipControllerTest() {} 78 79 virtual void SetUp() OVERRIDE { 80 AshTestBase::SetUp(); 81 helper_.reset(new TooltipControllerTestHelper(GetController())); 82 } 83 84 protected: 85 scoped_ptr<TooltipControllerTestHelper> helper_; 86 87 private: 88 DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest); 89 }; 90 91 TEST_F(TooltipControllerTest, NonNullTooltipClient) { 92 EXPECT_TRUE(aura::client::GetTooltipClient(Shell::GetPrimaryRootWindow()) 93 != NULL); 94 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 95 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 96 EXPECT_FALSE(helper_->IsTooltipVisible()); 97 } 98 99 TEST_F(TooltipControllerTest, HideTooltipWhenCursorHidden) { 100 scoped_ptr<views::Widget> widget(CreateNewWidgetOn(0)); 101 TooltipTestView* view = new TooltipTestView; 102 AddViewToWidgetAndResize(widget.get(), view); 103 view->set_tooltip_text(base::ASCIIToUTF16("Tooltip Text")); 104 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 105 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 106 107 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 108 generator.MoveMouseRelativeTo(widget->GetNativeView(), 109 view->bounds().CenterPoint()); 110 base::string16 expected_tooltip = base::ASCIIToUTF16("Tooltip Text"); 111 112 // Fire tooltip timer so tooltip becomes visible. 113 helper_->FireTooltipTimer(); 114 EXPECT_TRUE(helper_->IsTooltipVisible()); 115 116 // Hide the cursor and check again. 117 ash::Shell::GetInstance()->cursor_manager()->DisableMouseEvents(); 118 helper_->FireTooltipTimer(); 119 EXPECT_FALSE(helper_->IsTooltipVisible()); 120 121 // Show the cursor and re-check. 122 RunAllPendingInMessageLoop(); 123 ash::Shell::GetInstance()->cursor_manager()->EnableMouseEvents(); 124 RunAllPendingInMessageLoop(); 125 helper_->FireTooltipTimer(); 126 EXPECT_TRUE(helper_->IsTooltipVisible()); 127 } 128 129 TEST_F(TooltipControllerTest, TooltipsOnMultiDisplayShouldNotCrash) { 130 if (!SupportsMultipleDisplays()) 131 return; 132 133 UpdateDisplay("1000x600,600x400"); 134 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 135 scoped_ptr<views::Widget> widget1(CreateNewWidgetWithBoundsOn( 136 0, gfx::Rect(10, 10, 100, 100))); 137 TooltipTestView* view1 = new TooltipTestView; 138 AddViewToWidgetAndResize(widget1.get(), view1); 139 view1->set_tooltip_text(base::ASCIIToUTF16("Tooltip Text for view 1")); 140 EXPECT_EQ(widget1->GetNativeView()->GetRootWindow(), root_windows[0]); 141 142 scoped_ptr<views::Widget> widget2(CreateNewWidgetWithBoundsOn( 143 1, gfx::Rect(1200, 10, 100, 100))); 144 TooltipTestView* view2 = new TooltipTestView; 145 AddViewToWidgetAndResize(widget2.get(), view2); 146 view2->set_tooltip_text(base::ASCIIToUTF16("Tooltip Text for view 2")); 147 EXPECT_EQ(widget2->GetNativeView()->GetRootWindow(), root_windows[1]); 148 149 // Show tooltip on second display. 150 ui::test::EventGenerator generator(root_windows[1]); 151 generator.MoveMouseRelativeTo(widget2->GetNativeView(), 152 view2->bounds().CenterPoint()); 153 helper_->FireTooltipTimer(); 154 EXPECT_TRUE(helper_->IsTooltipVisible()); 155 156 // Get rid of secondary display. This destroy's the tooltip's aura window. If 157 // we have handled this case, we will not crash in the following statement. 158 UpdateDisplay("1000x600"); 159 #if !defined(OS_WIN) 160 // TODO(cpu): Detangle the window destruction notification. Currently 161 // the TooltipController::OnWindowDestroyed is not being called then the 162 // display is torn down so the tooltip is is still there. 163 EXPECT_FALSE(helper_->IsTooltipVisible()); 164 #endif 165 EXPECT_EQ(widget2->GetNativeView()->GetRootWindow(), root_windows[0]); 166 167 // The tooltip should create a new aura window for itself, so we should still 168 // be able to show tooltips on the primary display. 169 ui::test::EventGenerator generator1(root_windows[0]); 170 generator1.MoveMouseRelativeTo(widget1->GetNativeView(), 171 view1->bounds().CenterPoint()); 172 helper_->FireTooltipTimer(); 173 EXPECT_TRUE(helper_->IsTooltipVisible()); 174 } 175 176 } // namespace test 177 } // namespace ash 178