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