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 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 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 86 TestSliderListener::~TestSliderListener() { 87 last_drag_started_sender_ = NULL; 88 last_drag_ended_sender_ = NULL; 89 } 90 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 99 void TestSliderListener::SliderValueChanged(views::Slider* sender, 100 float value, 101 float old_value, 102 views::SliderChangeReason reason) { 103 ++last_event_epoch_; 104 } 105 106 void TestSliderListener::SliderDragStarted(views::Slider* sender) { 107 last_drag_started_sender_ = sender; 108 last_drag_started_epoch_ = ++last_event_epoch_; 109 } 110 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: 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 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 183 SliderTest::~SliderTest() { 184 } 185 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 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 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 235 HorizontalSliderTest::HorizontalSliderTest() 236 : SliderTest(Slider::HORIZONTAL) { 237 } 238 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 252 VerticalSliderTest::VerticalSliderTest() 253 : SliderTest(Slider::VERTICAL) { 254 } 255 256 VerticalSliderTest::~VerticalSliderTest() { 257 } 258 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 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 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. 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. 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. 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. 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. 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