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 "ui/views/controls/slider.h"
6
7#include <string>
8
9#include "base/memory/scoped_ptr.h"
10#include "base/strings/string_util.h"
11#include "base/strings/utf_string_conversions.h"
12#include "base/time/time.h"
13#include "testing/gtest/include/gtest/gtest.h"
14#include "ui/base/l10n/l10n_util.h"
15#include "ui/events/event.h"
16#include "ui/events/gesture_event_details.h"
17#include "ui/events/test/event_generator.h"
18#include "ui/views/test/slider_test_api.h"
19#include "ui/views/test/views_test_base.h"
20#include "ui/views/view.h"
21#include "ui/views/widget/widget.h"
22#include "ui/views/widget/widget_delegate.h"
23
24namespace {
25
26// A views::SliderListener that tracks simple event call history.
27class TestSliderListener : public views::SliderListener {
28 public:
29  TestSliderListener();
30  virtual ~TestSliderListener();
31
32  int last_event_epoch() {
33    return last_event_epoch_;
34  }
35
36  int last_drag_started_epoch() {
37    return last_drag_started_epoch_;
38  }
39
40  int last_drag_ended_epoch() {
41    return last_drag_ended_epoch_;
42  }
43
44  views::Slider* last_drag_started_sender() {
45    return last_drag_started_sender_;
46  }
47
48  views::Slider* last_drag_ended_sender() {
49    return last_drag_ended_sender_;
50  }
51
52  // Resets the state of this as if it were newly created.
53  virtual void ResetCallHistory();
54
55  // views::SliderListener:
56  virtual void SliderValueChanged(views::Slider* sender,
57                                  float value,
58                                  float old_value,
59                                  views::SliderChangeReason reason) OVERRIDE;
60  virtual void SliderDragStarted(views::Slider* sender) OVERRIDE;
61  virtual void SliderDragEnded(views::Slider* sender) OVERRIDE;
62
63 private:
64  // The epoch of the last event.
65  int last_event_epoch_;
66  // The epoch of the last time SliderDragStarted was called.
67  int last_drag_started_epoch_;
68  // The epoch of the last time SliderDragEnded was called.
69  int last_drag_ended_epoch_;
70  // The sender from the last SliderDragStarted call.
71  views::Slider* last_drag_started_sender_;
72  // The sender from the last SliderDragEnded call.
73  views::Slider* last_drag_ended_sender_;
74
75  DISALLOW_COPY_AND_ASSIGN(TestSliderListener);
76};
77
78TestSliderListener::TestSliderListener()
79  : last_event_epoch_(0),
80    last_drag_started_epoch_(-1),
81    last_drag_ended_epoch_(-1),
82    last_drag_started_sender_(NULL),
83    last_drag_ended_sender_(NULL) {
84}
85
86TestSliderListener::~TestSliderListener() {
87  last_drag_started_sender_ = NULL;
88  last_drag_ended_sender_ = NULL;
89}
90
91void TestSliderListener::ResetCallHistory() {
92  last_event_epoch_ = 0;
93  last_drag_started_epoch_ = -1;
94  last_drag_ended_epoch_ = -1;
95  last_drag_started_sender_ = NULL;
96  last_drag_ended_sender_ = NULL;
97}
98
99void TestSliderListener::SliderValueChanged(views::Slider* sender,
100                                            float value,
101                                            float old_value,
102                                            views::SliderChangeReason reason) {
103  ++last_event_epoch_;
104}
105
106void TestSliderListener::SliderDragStarted(views::Slider* sender) {
107  last_drag_started_sender_ = sender;
108  last_drag_started_epoch_ = ++last_event_epoch_;
109}
110
111void TestSliderListener::SliderDragEnded(views::Slider* sender) {
112  last_drag_ended_sender_ = sender;
113  last_drag_ended_epoch_ = ++last_event_epoch_;
114}
115
116}  // namespace
117
118namespace views {
119
120// Base test fixture for Slider tests.
121class SliderTest : public views::ViewsTestBase {
122 public:
123  explicit SliderTest(Slider::Orientation orientation);
124  virtual ~SliderTest();
125
126 protected:
127  Slider* slider() {
128    return slider_;
129  }
130
131  TestSliderListener& slider_listener() {
132    return slider_listener_;
133  }
134
135  int max_x() {
136    return max_x_;
137  }
138
139  int max_y() {
140    return max_y_;
141  }
142
143  virtual void ClickAt(int x, int y);
144
145  // testing::Test:
146  virtual void SetUp() OVERRIDE;
147  virtual void TearDown() OVERRIDE;
148
149  ui::test::EventGenerator* event_generator() {
150    return event_generator_.get();
151  }
152
153 private:
154  // The Slider's orientation
155  Slider::Orientation orientation_;
156  // The Slider to be tested.
157  Slider* slider_;
158  // A simple SliderListener test double.
159  TestSliderListener slider_listener_;
160  // Stores the default locale at test setup so it can be restored
161  // during test teardown.
162  std::string default_locale_;
163  // The maximum x value within the bounds of the slider.
164  int max_x_;
165  // The maximum y value within the bounds of the slider.
166  int max_y_;
167  // The widget container for the slider being tested.
168  views::Widget* widget_;
169  // An event generator.
170  scoped_ptr<ui::test::EventGenerator> event_generator_;
171
172  DISALLOW_COPY_AND_ASSIGN(SliderTest);
173};
174
175SliderTest::SliderTest(Slider::Orientation orientation)
176  : orientation_(orientation),
177    slider_(NULL),
178    default_locale_(""),
179    max_x_(0),
180    max_y_(0) {
181}
182
183SliderTest::~SliderTest() {
184}
185
186void SliderTest::SetUp() {
187  views::ViewsTestBase::SetUp();
188
189  slider_ = new Slider(NULL, orientation_);
190  View* view = slider_;
191  gfx::Size size = view->GetPreferredSize();
192  view->SetSize(size);
193  max_x_ = size.width() - 1;
194  max_y_ = size.height() - 1;
195  default_locale_ = l10n_util::GetApplicationLocale("");
196
197  views::Widget::InitParams init_params(CreateParams(
198        views::Widget::InitParams::TYPE_WINDOW_FRAMELESS));
199  init_params.bounds = gfx::Rect(size);
200
201  widget_ = new views::Widget();
202  widget_->Init(init_params);
203  widget_->SetContentsView(slider_);
204  widget_->Show();
205
206  event_generator_.reset(new ui::test::EventGenerator(
207      GetContext(), widget_->GetNativeWindow()));
208}
209
210void SliderTest::TearDown() {
211  if (widget_ && !widget_->IsClosed())
212    widget_->Close();
213
214  base::i18n::SetICUDefaultLocale(default_locale_);
215
216  views::ViewsTestBase::TearDown();
217}
218
219void SliderTest::ClickAt(int x, int y) {
220  gfx::Point point(x, y);
221  event_generator_->MoveMouseTo(point);
222  event_generator_->ClickLeftButton();
223}
224
225// Test fixture for horizontally oriented slider tests.
226class HorizontalSliderTest : public SliderTest {
227 public:
228  HorizontalSliderTest();
229  virtual ~HorizontalSliderTest();
230
231 private:
232  DISALLOW_COPY_AND_ASSIGN(HorizontalSliderTest);
233};
234
235HorizontalSliderTest::HorizontalSliderTest()
236  : SliderTest(Slider::HORIZONTAL) {
237}
238
239HorizontalSliderTest::~HorizontalSliderTest() {
240}
241
242// Test fixture for vertically oriented slider tests.
243class VerticalSliderTest : public SliderTest {
244 public:
245  VerticalSliderTest();
246  virtual ~VerticalSliderTest();
247
248 private:
249  DISALLOW_COPY_AND_ASSIGN(VerticalSliderTest);
250};
251
252VerticalSliderTest::VerticalSliderTest()
253  : SliderTest(Slider::VERTICAL) {
254}
255
256VerticalSliderTest::~VerticalSliderTest() {
257}
258
259TEST_F(HorizontalSliderTest, UpdateFromClickHorizontal) {
260  ClickAt(0, 0);
261  EXPECT_EQ(0.0f, slider()->value());
262
263  ClickAt(max_x(), 0);
264  EXPECT_EQ(1.0f, slider()->value());
265}
266
267TEST_F(VerticalSliderTest, UpdateFromClickVertical) {
268  ClickAt(0, 0);
269  EXPECT_EQ(1.0f, slider()->value());
270
271  ClickAt(0, max_y());
272  EXPECT_EQ(0.0f, slider()->value());
273}
274
275TEST_F(HorizontalSliderTest, UpdateFromClickRTLHorizontal) {
276  base::i18n::SetICUDefaultLocale("he");
277
278  ClickAt(0, 0);
279  EXPECT_EQ(1.0f, slider()->value());
280
281  ClickAt(max_x(), 0);
282  EXPECT_EQ(0.0f, slider()->value());
283}
284
285// Test the slider location after a tap gesture.
286TEST_F(HorizontalSliderTest, SliderValueForTapGesture) {
287  // Tap below the minimum.
288  slider()->SetValue(0.5);
289  event_generator()->GestureTapAt(gfx::Point(0, 0));
290  EXPECT_FLOAT_EQ(0, slider()->value());
291
292  // Tap above the maximum.
293  slider()->SetValue(0.5);
294  event_generator()->GestureTapAt(gfx::Point(max_x(), max_y()));
295  EXPECT_FLOAT_EQ(1, slider()->value());
296
297  // Tap somwhere in the middle.
298  slider()->SetValue(0.5);
299  event_generator()->GestureTapAt(gfx::Point(0.75 * max_x(), 0.75 * max_y()));
300  EXPECT_NEAR(0.75, slider()->value(), 0.03);
301}
302
303// Test the slider location after a scroll gesture.
304TEST_F(HorizontalSliderTest, SliderValueForScrollGesture) {
305  // Scroll below the minimum.
306  slider()->SetValue(0.5);
307  event_generator()->GestureScrollSequence(
308    gfx::Point(0.5 * max_x(), 0.5 * max_y()),
309    gfx::Point(0, 0),
310    base::TimeDelta::FromMilliseconds(10),
311    5 /* steps */);
312  EXPECT_EQ(0, slider()->value());
313
314  // Scroll above the maximum.
315  slider()->SetValue(0.5);
316  event_generator()->GestureScrollSequence(
317    gfx::Point(0.5 * max_x(), 0.5 * max_y()),
318    gfx::Point(max_x(), max_y()),
319    base::TimeDelta::FromMilliseconds(10),
320    5 /* steps */);
321  EXPECT_EQ(1, slider()->value());
322
323  // Scroll somewhere in the middle.
324  slider()->SetValue(0.25);
325  event_generator()->GestureScrollSequence(
326    gfx::Point(0.25 * max_x(), 0.25 * max_y()),
327    gfx::Point(0.75 * max_x(), 0.75 * max_y()),
328    base::TimeDelta::FromMilliseconds(10),
329    5 /* steps */);
330  EXPECT_NEAR(0.75, slider()->value(), 0.03);
331}
332
333// Verifies the correct SliderListener events are raised for a tap gesture.
334TEST_F(HorizontalSliderTest, SliderListenerEventsForTapGesture) {
335  test::SliderTestApi slider_test_api(slider());
336  slider_test_api.SetListener(&slider_listener());
337
338  event_generator()->GestureTapAt(gfx::Point(0, 0));
339  EXPECT_EQ(1, slider_listener().last_drag_started_epoch());
340  EXPECT_EQ(2, slider_listener().last_drag_ended_epoch());
341  EXPECT_EQ(slider(), slider_listener().last_drag_started_sender());
342  EXPECT_EQ(slider(), slider_listener().last_drag_ended_sender());
343}
344
345// Verifies the correct SliderListener events are raised for a scroll gesture.
346TEST_F(HorizontalSliderTest, SliderListenerEventsForScrollGesture) {
347  test::SliderTestApi slider_test_api(slider());
348  slider_test_api.SetListener(&slider_listener());
349
350  event_generator()->GestureScrollSequence(
351    gfx::Point(0.25 * max_x(), 0.25 * max_y()),
352    gfx::Point(0.75 * max_x(), 0.75 * max_y()),
353    base::TimeDelta::FromMilliseconds(0),
354    5 /* steps */);
355
356  EXPECT_EQ(1, slider_listener().last_drag_started_epoch());
357  EXPECT_GT(slider_listener().last_drag_ended_epoch(),
358            slider_listener().last_drag_started_epoch());
359  EXPECT_EQ(slider(), slider_listener().last_drag_started_sender());
360  EXPECT_EQ(slider(), slider_listener().last_drag_ended_sender());
361}
362
363// Verifies the correct SliderListener events are raised for a multi
364// finger scroll gesture.
365TEST_F(HorizontalSliderTest, SliderListenerEventsForMultiFingerScrollGesture) {
366  test::SliderTestApi slider_test_api(slider());
367  slider_test_api.SetListener(&slider_listener());
368
369  gfx::Point points[] = {gfx::Point(0, 0.1 * max_y()),
370                         gfx::Point(0, 0.2 * max_y())};
371  event_generator()->GestureMultiFingerScroll(2 /* count */, points,
372      0 /* event_separation_time_ms */, 5 /* steps */,
373      2 /* move_x */, 0 /* move_y */);
374
375  EXPECT_EQ(1, slider_listener().last_drag_started_epoch());
376  EXPECT_GT(slider_listener().last_drag_ended_epoch(),
377            slider_listener().last_drag_started_epoch());
378  EXPECT_EQ(slider(), slider_listener().last_drag_started_sender());
379  EXPECT_EQ(slider(), slider_listener().last_drag_ended_sender());
380}
381
382}  // namespace views
383