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
24 namespace {
25
26 // A views::SliderListener that tracks simple event call history.
27 class TestSliderListener : public views::SliderListener {
28 public:
29 TestSliderListener();
30 virtual ~TestSliderListener();
31
last_event_epoch()32 int last_event_epoch() {
33 return last_event_epoch_;
34 }
35
last_drag_started_epoch()36 int last_drag_started_epoch() {
37 return last_drag_started_epoch_;
38 }
39
last_drag_ended_epoch()40 int last_drag_ended_epoch() {
41 return last_drag_ended_epoch_;
42 }
43
last_drag_started_sender()44 views::Slider* last_drag_started_sender() {
45 return last_drag_started_sender_;
46 }
47
last_drag_ended_sender()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
TestSliderListener()78 TestSliderListener::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
~TestSliderListener()86 TestSliderListener::~TestSliderListener() {
87 last_drag_started_sender_ = NULL;
88 last_drag_ended_sender_ = NULL;
89 }
90
ResetCallHistory()91 void 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
SliderValueChanged(views::Slider * sender,float value,float old_value,views::SliderChangeReason reason)99 void TestSliderListener::SliderValueChanged(views::Slider* sender,
100 float value,
101 float old_value,
102 views::SliderChangeReason reason) {
103 ++last_event_epoch_;
104 }
105
SliderDragStarted(views::Slider * sender)106 void TestSliderListener::SliderDragStarted(views::Slider* sender) {
107 last_drag_started_sender_ = sender;
108 last_drag_started_epoch_ = ++last_event_epoch_;
109 }
110
SliderDragEnded(views::Slider * sender)111 void TestSliderListener::SliderDragEnded(views::Slider* sender) {
112 last_drag_ended_sender_ = sender;
113 last_drag_ended_epoch_ = ++last_event_epoch_;
114 }
115
116 } // namespace
117
118 namespace views {
119
120 // Base test fixture for Slider tests.
121 class SliderTest : public views::ViewsTestBase {
122 public:
123 explicit SliderTest(Slider::Orientation orientation);
124 virtual ~SliderTest();
125
126 protected:
slider()127 Slider* slider() {
128 return slider_;
129 }
130
slider_listener()131 TestSliderListener& slider_listener() {
132 return slider_listener_;
133 }
134
max_x()135 int max_x() {
136 return max_x_;
137 }
138
max_y()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
event_generator()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
SliderTest(Slider::Orientation orientation)175 SliderTest::SliderTest(Slider::Orientation orientation)
176 : orientation_(orientation),
177 slider_(NULL),
178 default_locale_(""),
179 max_x_(0),
180 max_y_(0) {
181 }
182
~SliderTest()183 SliderTest::~SliderTest() {
184 }
185
SetUp()186 void 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
TearDown()210 void SliderTest::TearDown() {
211 if (widget_ && !widget_->IsClosed())
212 widget_->Close();
213
214 base::i18n::SetICUDefaultLocale(default_locale_);
215
216 views::ViewsTestBase::TearDown();
217 }
218
ClickAt(int x,int y)219 void 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.
226 class HorizontalSliderTest : public SliderTest {
227 public:
228 HorizontalSliderTest();
229 virtual ~HorizontalSliderTest();
230
231 private:
232 DISALLOW_COPY_AND_ASSIGN(HorizontalSliderTest);
233 };
234
HorizontalSliderTest()235 HorizontalSliderTest::HorizontalSliderTest()
236 : SliderTest(Slider::HORIZONTAL) {
237 }
238
~HorizontalSliderTest()239 HorizontalSliderTest::~HorizontalSliderTest() {
240 }
241
242 // Test fixture for vertically oriented slider tests.
243 class VerticalSliderTest : public SliderTest {
244 public:
245 VerticalSliderTest();
246 virtual ~VerticalSliderTest();
247
248 private:
249 DISALLOW_COPY_AND_ASSIGN(VerticalSliderTest);
250 };
251
VerticalSliderTest()252 VerticalSliderTest::VerticalSliderTest()
253 : SliderTest(Slider::VERTICAL) {
254 }
255
~VerticalSliderTest()256 VerticalSliderTest::~VerticalSliderTest() {
257 }
258
TEST_F(HorizontalSliderTest,UpdateFromClickHorizontal)259 TEST_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
TEST_F(VerticalSliderTest,UpdateFromClickVertical)267 TEST_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
TEST_F(HorizontalSliderTest,UpdateFromClickRTLHorizontal)275 TEST_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.
TEST_F(HorizontalSliderTest,SliderValueForTapGesture)286 TEST_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.
TEST_F(HorizontalSliderTest,SliderValueForScrollGesture)304 TEST_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.
TEST_F(HorizontalSliderTest,SliderListenerEventsForTapGesture)334 TEST_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.
TEST_F(HorizontalSliderTest,SliderListenerEventsForScrollGesture)346 TEST_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.
TEST_F(HorizontalSliderTest,SliderListenerEventsForMultiFingerScrollGesture)365 TEST_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