tooltip_controller_unittest.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
1// Copyright (c) 2013 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 "ui/views/corewm/tooltip_controller.h"
6
7#include "base/strings/utf_string_conversions.h"
8#include "ui/aura/client/cursor_client.h"
9#include "ui/aura/client/screen_position_client.h"
10#include "ui/aura/env.h"
11#include "ui/aura/test/aura_test_base.h"
12#include "ui/aura/test/event_generator.h"
13#include "ui/aura/test/test_screen.h"
14#include "ui/aura/test/test_window_delegate.h"
15#include "ui/aura/window.h"
16#include "ui/aura/window_event_dispatcher.h"
17#include "ui/base/resource/resource_bundle.h"
18#include "ui/gfx/font.h"
19#include "ui/gfx/point.h"
20#include "ui/gfx/screen.h"
21#include "ui/gfx/screen_type_delegate.h"
22#include "ui/gfx/text_elider.h"
23#include "ui/views/corewm/tooltip_aura.h"
24#include "ui/views/corewm/tooltip_controller_test_helper.h"
25#include "ui/views/test/desktop_test_views_delegate.h"
26#include "ui/views/test/test_views_delegate.h"
27#include "ui/views/view.h"
28#include "ui/views/widget/tooltip_manager.h"
29#include "ui/views/widget/widget.h"
30#include "ui/wm/core/default_activation_client.h"
31#include "ui/wm/core/wm_state.h"
32#include "ui/wm/public/tooltip_client.h"
33#include "ui/wm/public/window_types.h"
34
35#if defined(OS_WIN)
36#include "ui/base/win/scoped_ole_initializer.h"
37#endif
38
39#if !defined(OS_CHROMEOS)
40#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
41#include "ui/views/widget/desktop_aura/desktop_screen.h"
42#endif
43
44using base::ASCIIToUTF16;
45
46namespace views {
47namespace corewm {
48namespace test {
49namespace {
50
51views::Widget* CreateWidget(aura::Window* root) {
52  views::Widget* widget = new views::Widget;
53  views::Widget::InitParams params;
54  params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
55  params.accept_events = true;
56  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
57#if defined(OS_CHROMEOS)
58  params.parent = root;
59#else
60  params.native_widget = new DesktopNativeWidgetAura(widget);
61#endif
62  params.bounds = gfx::Rect(0, 0, 200, 100);
63  widget->Init(params);
64  widget->Show();
65  return widget;
66}
67
68TooltipController* GetController(Widget* widget) {
69  return static_cast<TooltipController*>(
70      aura::client::GetTooltipClient(
71          widget->GetNativeWindow()->GetRootWindow()));
72}
73
74}  // namespace
75
76class TooltipControllerTest : public aura::test::AuraTestBase {
77 public:
78  TooltipControllerTest() : view_(NULL) {}
79  virtual ~TooltipControllerTest() {}
80
81  virtual void SetUp() OVERRIDE {
82#if defined(OS_CHROMEOS)
83    views_delegate_.reset(new TestViewsDelegate);
84#else
85    views_delegate_.reset(new DesktopTestViewsDelegate);
86#endif
87
88    aura::test::AuraTestBase::SetUp();
89    new wm::DefaultActivationClient(root_window());
90#if defined(OS_CHROMEOS)
91    controller_.reset(new TooltipController(
92          scoped_ptr<views::corewm::Tooltip>(
93              new views::corewm::TooltipAura(gfx::SCREEN_TYPE_ALTERNATE))));
94    root_window()->AddPreTargetHandler(controller_.get());
95    SetTooltipClient(root_window(), controller_.get());
96#endif
97    widget_.reset(CreateWidget(root_window()));
98    widget_->SetContentsView(new View);
99    view_ = new TooltipTestView;
100    widget_->GetContentsView()->AddChildView(view_);
101    view_->SetBoundsRect(widget_->GetContentsView()->GetLocalBounds());
102    helper_.reset(new TooltipControllerTestHelper(
103                      GetController(widget_.get())));
104    generator_.reset(new aura::test::EventGenerator(GetRootWindow()));
105  }
106
107  virtual void TearDown() OVERRIDE {
108#if defined(OS_CHROMEOS)
109    root_window()->RemovePreTargetHandler(controller_.get());
110    aura::client::SetTooltipClient(root_window(), NULL);
111    controller_.reset();
112#endif
113    generator_.reset();
114    helper_.reset();
115    widget_.reset();
116    aura::test::AuraTestBase::TearDown();
117    views_delegate_.reset();
118  }
119
120 protected:
121  aura::Window* GetWindow() {
122    return widget_->GetNativeWindow();
123  }
124
125  aura::Window* GetRootWindow() {
126    return GetWindow()->GetRootWindow();
127  }
128
129  TooltipTestView* PrepareSecondView() {
130    TooltipTestView* view2 = new TooltipTestView;
131    widget_->GetContentsView()->AddChildView(view2);
132    view_->SetBounds(0, 0, 100, 100);
133    view2->SetBounds(100, 0, 100, 100);
134    return view2;
135  }
136
137  scoped_ptr<views::Widget> widget_;
138  TooltipTestView* view_;
139  scoped_ptr<TooltipControllerTestHelper> helper_;
140  scoped_ptr<aura::test::EventGenerator> generator_;
141
142 private:
143  scoped_ptr<TooltipController> controller_;
144
145  scoped_ptr<views::TestViewsDelegate> views_delegate_;
146
147#if defined(OS_WIN)
148  ui::ScopedOleInitializer ole_initializer_;
149#endif
150
151  DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest);
152};
153
154TEST_F(TooltipControllerTest, ViewTooltip) {
155  view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text"));
156  EXPECT_EQ(base::string16(), helper_->GetTooltipText());
157  EXPECT_EQ(NULL, helper_->GetTooltipWindow());
158  generator_->MoveMouseToCenterOf(GetWindow());
159
160  EXPECT_EQ(GetWindow(), GetRootWindow()->GetEventHandlerForPoint(
161      generator_->current_location()));
162  base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text");
163  EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow()));
164  EXPECT_EQ(base::string16(), helper_->GetTooltipText());
165  EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow());
166
167  // Fire tooltip timer so tooltip becomes visible.
168  helper_->FireTooltipTimer();
169
170  EXPECT_TRUE(helper_->IsTooltipVisible());
171  generator_->MoveMouseBy(1, 0);
172
173  EXPECT_TRUE(helper_->IsTooltipVisible());
174  EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow()));
175  EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
176  EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow());
177}
178
179TEST_F(TooltipControllerTest, TooltipsInMultipleViews) {
180  view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text"));
181  EXPECT_EQ(base::string16(), helper_->GetTooltipText());
182  EXPECT_EQ(NULL, helper_->GetTooltipWindow());
183
184  PrepareSecondView();
185  aura::Window* window = GetWindow();
186  aura::Window* root_window = GetRootWindow();
187
188  // Fire tooltip timer so tooltip becomes visible.
189  generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint());
190  helper_->FireTooltipTimer();
191  EXPECT_TRUE(helper_->IsTooltipVisible());
192  for (int i = 0; i < 49; ++i) {
193    generator_->MoveMouseBy(1, 0);
194    EXPECT_TRUE(helper_->IsTooltipVisible());
195    EXPECT_EQ(window, root_window->GetEventHandlerForPoint(
196            generator_->current_location()));
197    base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text");
198    EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
199    EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
200    EXPECT_EQ(window, helper_->GetTooltipWindow());
201  }
202  for (int i = 0; i < 49; ++i) {
203    generator_->MoveMouseBy(1, 0);
204    EXPECT_FALSE(helper_->IsTooltipVisible());
205    EXPECT_EQ(window, root_window->GetEventHandlerForPoint(
206            generator_->current_location()));
207    base::string16 expected_tooltip;  // = ""
208    EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
209    EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
210    EXPECT_EQ(window, helper_->GetTooltipWindow());
211  }
212}
213
214TEST_F(TooltipControllerTest, EnableOrDisableTooltips) {
215  view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text"));
216  EXPECT_EQ(base::string16(), helper_->GetTooltipText());
217  EXPECT_EQ(NULL, helper_->GetTooltipWindow());
218
219  generator_->MoveMouseRelativeTo(GetWindow(), view_->bounds().CenterPoint());
220  base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text");
221
222  // Fire tooltip timer so tooltip becomes visible.
223  helper_->FireTooltipTimer();
224  EXPECT_TRUE(helper_->IsTooltipVisible());
225
226  // Disable tooltips and check again.
227  helper_->controller()->SetTooltipsEnabled(false);
228  EXPECT_FALSE(helper_->IsTooltipVisible());
229  helper_->FireTooltipTimer();
230  EXPECT_FALSE(helper_->IsTooltipVisible());
231
232  // Enable tooltips back and check again.
233  helper_->controller()->SetTooltipsEnabled(true);
234  EXPECT_FALSE(helper_->IsTooltipVisible());
235  helper_->FireTooltipTimer();
236  EXPECT_TRUE(helper_->IsTooltipVisible());
237}
238
239// Verifies tooltip isn't shown if tooltip text consists entirely of whitespace.
240TEST_F(TooltipControllerTest, DontShowEmptyTooltips) {
241  view_->set_tooltip_text(ASCIIToUTF16("                     "));
242  EXPECT_EQ(base::string16(), helper_->GetTooltipText());
243  EXPECT_EQ(NULL, helper_->GetTooltipWindow());
244
245  generator_->MoveMouseRelativeTo(GetWindow(), view_->bounds().CenterPoint());
246
247  helper_->FireTooltipTimer();
248  EXPECT_FALSE(helper_->IsTooltipVisible());
249}
250
251TEST_F(TooltipControllerTest, TooltipHidesOnKeyPressAndStaysHiddenUntilChange) {
252  view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1"));
253  EXPECT_EQ(base::string16(), helper_->GetTooltipText());
254  EXPECT_EQ(NULL, helper_->GetTooltipWindow());
255
256  TooltipTestView* view2 = PrepareSecondView();
257  view2->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 2"));
258
259  aura::Window* window = GetWindow();
260
261  // Fire tooltip timer so tooltip becomes visible.
262  generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint());
263  helper_->FireTooltipTimer();
264  EXPECT_TRUE(helper_->IsTooltipVisible());
265  EXPECT_TRUE(helper_->IsTooltipShownTimerRunning());
266
267  generator_->PressKey(ui::VKEY_1, 0);
268  EXPECT_FALSE(helper_->IsTooltipVisible());
269  EXPECT_FALSE(helper_->IsTooltipTimerRunning());
270  EXPECT_FALSE(helper_->IsTooltipShownTimerRunning());
271
272  // Moving the mouse inside |view1| should not change the state of the tooltip
273  // or the timers.
274  for (int i = 0; i < 49; i++) {
275    generator_->MoveMouseBy(1, 0);
276    EXPECT_FALSE(helper_->IsTooltipVisible());
277    EXPECT_FALSE(helper_->IsTooltipTimerRunning());
278    EXPECT_FALSE(helper_->IsTooltipShownTimerRunning());
279    EXPECT_EQ(window,
280              GetRootWindow()->GetEventHandlerForPoint(
281                  generator_->current_location()));
282    base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1");
283    EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
284    EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
285    EXPECT_EQ(window, helper_->GetTooltipWindow());
286  }
287
288  // Now we move the mouse on to |view2|. It should re-start the tooltip timer.
289  generator_->MoveMouseBy(1, 0);
290  EXPECT_TRUE(helper_->IsTooltipTimerRunning());
291  helper_->FireTooltipTimer();
292  EXPECT_TRUE(helper_->IsTooltipVisible());
293  EXPECT_TRUE(helper_->IsTooltipShownTimerRunning());
294  base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2");
295  EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
296  EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
297  EXPECT_EQ(window, helper_->GetTooltipWindow());
298}
299
300TEST_F(TooltipControllerTest, TooltipHidesOnTimeoutAndStaysHiddenUntilChange) {
301  view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1"));
302  EXPECT_EQ(base::string16(), helper_->GetTooltipText());
303  EXPECT_EQ(NULL, helper_->GetTooltipWindow());
304
305  TooltipTestView* view2 = PrepareSecondView();
306  view2->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 2"));
307
308  aura::Window* window = GetWindow();
309
310  // Fire tooltip timer so tooltip becomes visible.
311  generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint());
312  helper_->FireTooltipTimer();
313  EXPECT_TRUE(helper_->IsTooltipVisible());
314  EXPECT_TRUE(helper_->IsTooltipShownTimerRunning());
315
316  helper_->FireTooltipShownTimer();
317  EXPECT_FALSE(helper_->IsTooltipVisible());
318  EXPECT_FALSE(helper_->IsTooltipTimerRunning());
319  EXPECT_FALSE(helper_->IsTooltipShownTimerRunning());
320
321  // Moving the mouse inside |view1| should not change the state of the tooltip
322  // or the timers.
323  for (int i = 0; i < 49; ++i) {
324    generator_->MoveMouseBy(1, 0);
325    EXPECT_FALSE(helper_->IsTooltipVisible());
326    EXPECT_FALSE(helper_->IsTooltipTimerRunning());
327    EXPECT_FALSE(helper_->IsTooltipShownTimerRunning());
328    EXPECT_EQ(window, GetRootWindow()->GetEventHandlerForPoint(
329                  generator_->current_location()));
330    base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1");
331    EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
332    EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
333    EXPECT_EQ(window, helper_->GetTooltipWindow());
334  }
335
336  // Now we move the mouse on to |view2|. It should re-start the tooltip timer.
337  generator_->MoveMouseBy(1, 0);
338  EXPECT_TRUE(helper_->IsTooltipTimerRunning());
339  helper_->FireTooltipTimer();
340  EXPECT_TRUE(helper_->IsTooltipVisible());
341  EXPECT_TRUE(helper_->IsTooltipShownTimerRunning());
342  base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2");
343  EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
344  EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
345  EXPECT_EQ(window, helper_->GetTooltipWindow());
346}
347
348// Verifies a mouse exit event hides the tooltips.
349TEST_F(TooltipControllerTest, HideOnExit) {
350  view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text"));
351  generator_->MoveMouseToCenterOf(GetWindow());
352  base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text");
353  EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow()));
354  EXPECT_EQ(base::string16(), helper_->GetTooltipText());
355  EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow());
356
357  // Fire tooltip timer so tooltip becomes visible.
358  helper_->FireTooltipTimer();
359
360  EXPECT_TRUE(helper_->IsTooltipVisible());
361  generator_->SendMouseExit();
362  EXPECT_FALSE(helper_->IsTooltipVisible());
363}
364
365TEST_F(TooltipControllerTest, ReshowOnClickAfterEnterExit) {
366  // Owned by |view_|.
367  TooltipTestView* v1 = new TooltipTestView;
368  TooltipTestView* v2 = new TooltipTestView;
369  view_->AddChildView(v1);
370  view_->AddChildView(v2);
371  gfx::Rect view_bounds(view_->GetLocalBounds());
372  view_bounds.set_height(view_bounds.height() / 2);
373  v1->SetBoundsRect(view_bounds);
374  view_bounds.set_y(view_bounds.height());
375  v2->SetBoundsRect(view_bounds);
376  const base::string16 v1_tt(ASCIIToUTF16("v1"));
377  const base::string16 v2_tt(ASCIIToUTF16("v2"));
378  v1->set_tooltip_text(v1_tt);
379  v2->set_tooltip_text(v2_tt);
380
381  gfx::Point v1_point(1, 1);
382  View::ConvertPointToWidget(v1, &v1_point);
383  generator_->MoveMouseRelativeTo(GetWindow(), v1_point);
384
385  // Fire tooltip timer so tooltip becomes visible.
386  helper_->FireTooltipTimer();
387  EXPECT_TRUE(helper_->IsTooltipVisible());
388  EXPECT_EQ(v1_tt, helper_->GetTooltipText());
389
390  // Press the mouse, move to v2 and back to v1.
391  generator_->ClickLeftButton();
392
393  gfx::Point v2_point(1, 1);
394  View::ConvertPointToWidget(v2, &v2_point);
395  generator_->MoveMouseRelativeTo(GetWindow(), v2_point);
396  generator_->MoveMouseRelativeTo(GetWindow(), v1_point);
397
398  helper_->FireTooltipTimer();
399  EXPECT_TRUE(helper_->IsTooltipVisible());
400  EXPECT_EQ(v1_tt, helper_->GetTooltipText());
401}
402
403namespace {
404
405// Returns the index of |window| in its parent's children.
406int IndexInParent(const aura::Window* window) {
407  aura::Window::Windows::const_iterator i =
408      std::find(window->parent()->children().begin(),
409                window->parent()->children().end(),
410                window);
411  return i == window->parent()->children().end() ? -1 :
412      static_cast<int>(i - window->parent()->children().begin());
413}
414
415class TestScreenPositionClient : public aura::client::ScreenPositionClient {
416 public:
417  TestScreenPositionClient() {}
418  virtual ~TestScreenPositionClient() {}
419
420  // ScreenPositionClient overrides:
421  virtual void ConvertPointToScreen(const aura::Window* window,
422                                    gfx::Point* point) OVERRIDE {
423  }
424  virtual void ConvertPointFromScreen(const aura::Window* window,
425                                      gfx::Point* point) OVERRIDE {
426  }
427  virtual void ConvertHostPointToScreen(aura::Window* root_gwindow,
428                                        gfx::Point* point) OVERRIDE {
429    NOTREACHED();
430  }
431  virtual void SetBounds(aura::Window* window,
432                         const gfx::Rect& bounds,
433                         const gfx::Display& display) OVERRIDE {
434    window->SetBounds(bounds);
435  }
436
437 private:
438  DISALLOW_COPY_AND_ASSIGN(TestScreenPositionClient);
439};
440
441}  // namespace
442
443class TooltipControllerCaptureTest : public TooltipControllerTest {
444 public:
445  TooltipControllerCaptureTest() {}
446  virtual ~TooltipControllerCaptureTest() {}
447
448  virtual void SetUp() OVERRIDE {
449    TooltipControllerTest::SetUp();
450    aura::client::SetScreenPositionClient(GetRootWindow(),
451                                          &screen_position_client_);
452#if !defined(OS_CHROMEOS)
453    desktop_screen_.reset(CreateDesktopScreen());
454    gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE,
455                                   desktop_screen_.get());
456#endif
457  }
458
459  virtual void TearDown() OVERRIDE {
460#if !defined(OS_CHROMEOS)
461    gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen());
462    desktop_screen_.reset();
463#endif
464    aura::client::SetScreenPositionClient(GetRootWindow(), NULL);
465    TooltipControllerTest::TearDown();
466  }
467
468 private:
469  TestScreenPositionClient screen_position_client_;
470  scoped_ptr<gfx::Screen> desktop_screen_;
471
472  DISALLOW_COPY_AND_ASSIGN(TooltipControllerCaptureTest);
473};
474
475// Verifies when capture is released the TooltipController resets state.
476TEST_F(TooltipControllerCaptureTest, CloseOnCaptureLost) {
477  view_->GetWidget()->SetCapture(view_);
478  RunAllPendingInMessageLoop();
479  view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text"));
480  generator_->MoveMouseToCenterOf(GetWindow());
481  base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text");
482  EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow()));
483  EXPECT_EQ(base::string16(), helper_->GetTooltipText());
484  EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow());
485
486  // Fire tooltip timer so tooltip becomes visible.
487  helper_->FireTooltipTimer();
488
489  EXPECT_TRUE(helper_->IsTooltipVisible());
490  view_->GetWidget()->ReleaseCapture();
491  EXPECT_FALSE(helper_->IsTooltipVisible());
492  EXPECT_TRUE(helper_->GetTooltipWindow() == NULL);
493}
494
495// Disabled on linux as DesktopScreenX11::GetWindowAtScreenPoint() doesn't
496// consider z-order.
497#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
498#define MAYBE_Capture DISABLED_Capture
499#else
500#define MAYBE_Capture Capture
501#endif
502// Verifies the correct window is found for tooltips when there is a capture.
503TEST_F(TooltipControllerCaptureTest, MAYBE_Capture) {
504  const base::string16 tooltip_text(ASCIIToUTF16("1"));
505  const base::string16 tooltip_text2(ASCIIToUTF16("2"));
506
507  widget_->SetBounds(gfx::Rect(0, 0, 200, 200));
508  view_->set_tooltip_text(tooltip_text);
509
510  scoped_ptr<views::Widget> widget2(CreateWidget(root_window()));
511  widget2->SetContentsView(new View);
512  TooltipTestView* view2 = new TooltipTestView;
513  widget2->GetContentsView()->AddChildView(view2);
514  view2->set_tooltip_text(tooltip_text2);
515  widget2->SetBounds(gfx::Rect(0, 0, 200, 200));
516  view2->SetBoundsRect(widget2->GetContentsView()->GetLocalBounds());
517
518  widget_->SetCapture(view_);
519  EXPECT_TRUE(widget_->HasCapture());
520  widget2->Show();
521  EXPECT_GE(IndexInParent(widget2->GetNativeWindow()),
522            IndexInParent(widget_->GetNativeWindow()));
523
524  generator_->MoveMouseRelativeTo(widget_->GetNativeWindow(),
525                                  view_->bounds().CenterPoint());
526
527  EXPECT_TRUE(helper_->IsTooltipTimerRunning());
528  helper_->FireTooltipTimer();
529  // Even though the mouse is over a window with a tooltip it shouldn't be
530  // picked up because the windows don't have the same value for
531  // |TooltipManager::kGroupingPropertyKey|.
532  EXPECT_TRUE(helper_->GetTooltipText().empty());
533
534  // Now make both the windows have same transient value for
535  // kGroupingPropertyKey. In this case the tooltip should be picked up from
536  // |widget2| (because the mouse is over it).
537  const int grouping_key = 1;
538  widget_->SetNativeWindowProperty(TooltipManager::kGroupingPropertyKey,
539                                   reinterpret_cast<void*>(grouping_key));
540  widget2->SetNativeWindowProperty(TooltipManager::kGroupingPropertyKey,
541                                   reinterpret_cast<void*>(grouping_key));
542  generator_->MoveMouseBy(1, 10);
543  EXPECT_TRUE(helper_->IsTooltipTimerRunning());
544  helper_->FireTooltipTimer();
545  EXPECT_EQ(tooltip_text2, helper_->GetTooltipText());
546
547  widget2.reset();
548}
549
550namespace {
551
552class TestTooltip : public Tooltip {
553 public:
554  TestTooltip() : is_visible_(false) {}
555  virtual ~TestTooltip() {}
556
557  const base::string16& tooltip_text() const { return tooltip_text_; }
558
559  // Tooltip:
560  virtual void SetText(aura::Window* window,
561                       const base::string16& tooltip_text,
562                       const gfx::Point& location) OVERRIDE {
563    tooltip_text_ = tooltip_text;
564    location_ = location;
565  }
566  virtual void Show() OVERRIDE {
567    is_visible_ = true;
568  }
569  virtual void Hide() OVERRIDE {
570    is_visible_ = false;
571  }
572  virtual bool IsVisible() OVERRIDE {
573    return is_visible_;
574  }
575  const gfx::Point& location() { return location_; }
576
577 private:
578  bool is_visible_;
579  base::string16 tooltip_text_;
580  gfx::Point location_;
581
582  DISALLOW_COPY_AND_ASSIGN(TestTooltip);
583};
584
585}  // namespace
586
587// Use for tests that don't depend upon views.
588class TooltipControllerTest2 : public aura::test::AuraTestBase {
589 public:
590  TooltipControllerTest2() : test_tooltip_(new TestTooltip) {}
591  virtual ~TooltipControllerTest2() {}
592
593  virtual void SetUp() OVERRIDE {
594    wm_state_.reset(new wm::WMState);
595    aura::test::AuraTestBase::SetUp();
596    new wm::DefaultActivationClient(root_window());
597    controller_.reset(new TooltipController(
598                          scoped_ptr<corewm::Tooltip>(test_tooltip_)));
599    root_window()->AddPreTargetHandler(controller_.get());
600    SetTooltipClient(root_window(), controller_.get());
601    helper_.reset(new TooltipControllerTestHelper(controller_.get()));
602    generator_.reset(new aura::test::EventGenerator(root_window()));
603  }
604
605  virtual void TearDown() OVERRIDE {
606    root_window()->RemovePreTargetHandler(controller_.get());
607    aura::client::SetTooltipClient(root_window(), NULL);
608    controller_.reset();
609    generator_.reset();
610    helper_.reset();
611    aura::test::AuraTestBase::TearDown();
612    wm_state_.reset();
613  }
614
615 protected:
616  // Owned by |controller_|.
617  TestTooltip* test_tooltip_;
618  scoped_ptr<TooltipControllerTestHelper> helper_;
619  scoped_ptr<aura::test::EventGenerator> generator_;
620
621 private:
622  scoped_ptr<TooltipController> controller_;
623  scoped_ptr<wm::WMState> wm_state_;
624
625  DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest2);
626};
627
628TEST_F(TooltipControllerTest2, VerifyLeadingTrailingWhitespaceStripped) {
629  aura::test::TestWindowDelegate test_delegate;
630  scoped_ptr<aura::Window> window(
631      CreateNormalWindow(100, root_window(), &test_delegate));
632  window->SetBounds(gfx::Rect(0, 0, 300, 300));
633  base::string16 tooltip_text(ASCIIToUTF16(" \nx  "));
634  aura::client::SetTooltipText(window.get(), &tooltip_text);
635  generator_->MoveMouseToCenterOf(window.get());
636  helper_->FireTooltipTimer();
637  EXPECT_EQ(ASCIIToUTF16("x"), test_tooltip_->tooltip_text());
638}
639
640// Verifies that tooltip is hidden and tooltip window closed upon cancel mode.
641TEST_F(TooltipControllerTest2, CloseOnCancelMode) {
642  aura::test::TestWindowDelegate test_delegate;
643  scoped_ptr<aura::Window> window(
644      CreateNormalWindow(100, root_window(), &test_delegate));
645  window->SetBounds(gfx::Rect(0, 0, 300, 300));
646  base::string16 tooltip_text(ASCIIToUTF16("Tooltip Text"));
647  aura::client::SetTooltipText(window.get(), &tooltip_text);
648  generator_->MoveMouseToCenterOf(window.get());
649
650  // Fire tooltip timer so tooltip becomes visible.
651  helper_->FireTooltipTimer();
652  EXPECT_TRUE(helper_->IsTooltipVisible());
653
654  // Send OnCancelMode event and verify that tooltip becomes invisible and
655  // the tooltip window is closed.
656  ui::CancelModeEvent event;
657  helper_->controller()->OnCancelMode(&event);
658  EXPECT_FALSE(helper_->IsTooltipVisible());
659  EXPECT_TRUE(helper_->GetTooltipWindow() == NULL);
660}
661
662// Use for tests that need both views and a TestTooltip.
663class TooltipControllerTest3 : public aura::test::AuraTestBase {
664 public:
665  TooltipControllerTest3() : test_tooltip_(new TestTooltip) {}
666  virtual ~TooltipControllerTest3() {}
667
668  virtual void SetUp() OVERRIDE {
669    wm_state_.reset(new wm::WMState);
670    aura::test::AuraTestBase::SetUp();
671    new wm::DefaultActivationClient(root_window());
672
673    widget_.reset(CreateWidget(root_window()));
674    widget_->SetContentsView(new View);
675    view_ = new TooltipTestView;
676    widget_->GetContentsView()->AddChildView(view_);
677    view_->SetBoundsRect(widget_->GetContentsView()->GetLocalBounds());
678
679    generator_.reset(new aura::test::EventGenerator(GetRootWindow()));
680    controller_.reset(new TooltipController(
681        scoped_ptr<views::corewm::Tooltip>(test_tooltip_)));
682    GetRootWindow()->RemovePreTargetHandler(
683        static_cast<TooltipController*>(aura::client::GetTooltipClient(
684            widget_->GetNativeWindow()->GetRootWindow())));
685    GetRootWindow()->AddPreTargetHandler(controller_.get());
686    helper_.reset(new TooltipControllerTestHelper(controller_.get()));
687    SetTooltipClient(GetRootWindow(), controller_.get());
688  }
689
690  virtual void TearDown() OVERRIDE {
691    GetRootWindow()->RemovePreTargetHandler(controller_.get());
692    aura::client::SetTooltipClient(GetRootWindow(), NULL);
693
694    controller_.reset();
695    generator_.reset();
696    helper_.reset();
697    widget_.reset();
698    aura::test::AuraTestBase::TearDown();
699    wm_state_.reset();
700  }
701
702  aura::Window* GetWindow() { return widget_->GetNativeWindow(); }
703
704 protected:
705  // Owned by |controller_|.
706  TestTooltip* test_tooltip_;
707  scoped_ptr<TooltipControllerTestHelper> helper_;
708  scoped_ptr<aura::test::EventGenerator> generator_;
709  scoped_ptr<views::Widget> widget_;
710  TooltipTestView* view_;
711
712 private:
713  scoped_ptr<TooltipController> controller_;
714  scoped_ptr<wm::WMState> wm_state_;
715
716#if defined(OS_WIN)
717  ui::ScopedOleInitializer ole_initializer_;
718#endif
719
720  aura::Window* GetRootWindow() { return GetWindow()->GetRootWindow(); }
721
722  DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest3);
723};
724
725TEST_F(TooltipControllerTest3, TooltipPositionChangesOnTwoViewsWithSameLabel) {
726  // Owned by |view_|.
727  // These two views have the same tooltip text
728  TooltipTestView* v1 = new TooltipTestView;
729  TooltipTestView* v2 = new TooltipTestView;
730  // v1_1 is a view inside v1 that has an identical tooltip text to that of v1
731  // and v2
732  TooltipTestView* v1_1 = new TooltipTestView;
733  // v2_1 is a view inside v2 that has an identical tooltip text to that of v1
734  // and v2
735  TooltipTestView* v2_1 = new TooltipTestView;
736  // v2_2 is a view inside v2 with the tooltip text different from all the
737  // others
738  TooltipTestView* v2_2 = new TooltipTestView;
739
740  // Setup all the views' relations
741  view_->AddChildView(v1);
742  view_->AddChildView(v2);
743  v1->AddChildView(v1_1);
744  v2->AddChildView(v2_1);
745  v2->AddChildView(v2_2);
746  const base::string16 reference_string(
747      base::ASCIIToUTF16("Identical Tooltip Text"));
748  const base::string16 alternative_string(
749      base::ASCIIToUTF16("Another Shrubbery"));
750  v1->set_tooltip_text(reference_string);
751  v2->set_tooltip_text(reference_string);
752  v1_1->set_tooltip_text(reference_string);
753  v2_1->set_tooltip_text(reference_string);
754  v2_2->set_tooltip_text(alternative_string);
755
756  // Set views' bounds
757  gfx::Rect view_bounds(view_->GetLocalBounds());
758  view_bounds.set_height(view_bounds.height() / 2);
759  v1->SetBoundsRect(view_bounds);
760  v1_1->SetBounds(0, 0, 3, 3);
761  view_bounds.set_y(view_bounds.height());
762  v2->SetBoundsRect(view_bounds);
763  v2_2->SetBounds(view_bounds.width() - 3, view_bounds.height() - 3, 3, 3);
764  v2_1->SetBounds(0, 0, 3, 3);
765
766  // Test whether a toolbar appears on v1
767  gfx::Point center = v1->bounds().CenterPoint();
768  generator_->MoveMouseRelativeTo(GetWindow(), center);
769  helper_->FireTooltipTimer();
770  EXPECT_TRUE(helper_->IsTooltipVisible());
771  EXPECT_EQ(reference_string, helper_->GetTooltipText());
772  gfx::Point tooltip_bounds1 = test_tooltip_->location();
773
774  // Test whether the toolbar changes position on mouse over v2
775  center = v2->bounds().CenterPoint();
776  generator_->MoveMouseRelativeTo(GetWindow(), center);
777  helper_->FireTooltipTimer();
778  EXPECT_TRUE(helper_->IsTooltipVisible());
779  EXPECT_EQ(reference_string, helper_->GetTooltipText());
780  gfx::Point tooltip_bounds2 = test_tooltip_->location();
781
782  EXPECT_NE(tooltip_bounds1, gfx::Point());
783  EXPECT_NE(tooltip_bounds2, gfx::Point());
784  EXPECT_NE(tooltip_bounds1, tooltip_bounds2);
785
786  // Test if the toolbar does not change position on encountering a contained
787  // view with the same tooltip text
788  center = v2_1->GetLocalBounds().CenterPoint();
789  views::View::ConvertPointToTarget(v2_1, view_, &center);
790  generator_->MoveMouseRelativeTo(GetWindow(), center);
791  helper_->FireTooltipTimer();
792  gfx::Point tooltip_bounds2_1 = test_tooltip_->location();
793
794  EXPECT_NE(tooltip_bounds2, tooltip_bounds2_1);
795  EXPECT_TRUE(helper_->IsTooltipVisible());
796  EXPECT_EQ(reference_string, helper_->GetTooltipText());
797
798  // Test if the toolbar changes position on encountering a contained
799  // view with a different tooltip text
800  center = v2_2->GetLocalBounds().CenterPoint();
801  views::View::ConvertPointToTarget(v2_2, view_, &center);
802  generator_->MoveMouseRelativeTo(GetWindow(), center);
803  helper_->FireTooltipTimer();
804  gfx::Point tooltip_bounds2_2 = test_tooltip_->location();
805
806  EXPECT_NE(tooltip_bounds2_1, tooltip_bounds2_2);
807  EXPECT_TRUE(helper_->IsTooltipVisible());
808  EXPECT_EQ(alternative_string, helper_->GetTooltipText());
809
810  // Test if moving from a view that is contained by a larger view, both with
811  // the same tooltip text, does not change tooltip's position.
812  center = v1_1->GetLocalBounds().CenterPoint();
813  views::View::ConvertPointToTarget(v1_1, view_, &center);
814  generator_->MoveMouseRelativeTo(GetWindow(), center);
815  helper_->FireTooltipTimer();
816  gfx::Point tooltip_bounds1_1 = test_tooltip_->location();
817
818  EXPECT_TRUE(helper_->IsTooltipVisible());
819  EXPECT_EQ(reference_string, helper_->GetTooltipText());
820
821  center = v1->bounds().CenterPoint();
822  generator_->MoveMouseRelativeTo(GetWindow(), center);
823  helper_->FireTooltipTimer();
824  tooltip_bounds1 = test_tooltip_->location();
825
826  EXPECT_NE(tooltip_bounds1_1, tooltip_bounds1);
827  EXPECT_EQ(reference_string, helper_->GetTooltipText());
828}
829
830}  // namespace test
831}  // namespace corewm
832}  // namespace views
833