chrome_render_widget_host_view_mac_history_swiper_unit_test.mm revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
1// Copyright 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 "testing/gtest/include/gtest/gtest.h" 6#import "chrome/browser/ui/cocoa/cocoa_test_helper.h" 7 8#include "base/mac/scoped_nsobject.h" 9 10#import "base/mac/sdk_forward_declarations.h" 11#import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.h" 12#import "third_party/ocmock/OCMock/OCMock.h" 13#import "third_party/ocmock/ocmock_extensions.h" 14 15@interface HistorySwiper (MacHistorySwiperTest) 16- (BOOL)systemSettingsAllowHistorySwiping:(NSEvent*)event; 17- (BOOL)browserCanNavigateInDirection: 18 (history_swiper::NavigationDirection)forward 19 event:(NSEvent*)event; 20- (void)endHistorySwipe; 21- (void)beginHistorySwipeInDirection: 22 (history_swiper::NavigationDirection)goForward 23 event:(NSEvent*)event; 24- (void)navigateBrowserInDirection:(history_swiper::NavigationDirection)forward; 25- (void)initiateMagicMouseHistorySwipe:(BOOL)isRightScroll 26 event:(NSEvent*)event; 27@end 28 29class MacHistorySwiperTest : public CocoaTest { 30 public: 31 virtual void SetUp() OVERRIDE { 32 CocoaTest::SetUp(); 33 34 [HistorySwiper resetMagicMouseState]; 35 36 view_ = [[NSView alloc] init]; 37 id mockDelegate = 38 [OCMockObject mockForProtocol:@protocol(HistorySwiperDelegate)]; 39 [[[mockDelegate stub] andReturn:view_] viewThatWantsHistoryOverlay]; 40 [[[mockDelegate stub] andReturnBool:YES] shouldAllowHistorySwiping]; 41 42 base::scoped_nsobject<HistorySwiper> historySwiper( 43 [[HistorySwiper alloc] initWithDelegate:mockDelegate]); 44 id mockHistorySwiper = [OCMockObject partialMockForObject:historySwiper]; 45 [[[mockHistorySwiper stub] andReturnBool:YES] 46 systemSettingsAllowHistorySwiping:[OCMArg any]]; 47 [[[mockHistorySwiper stub] andReturnBool:YES] 48 browserCanNavigateInDirection:history_swiper::kForwards 49 event:[OCMArg any]]; 50 [[[mockHistorySwiper stub] andReturnBool:YES] 51 browserCanNavigateInDirection:history_swiper::kBackwards 52 event:[OCMArg any]]; 53 [[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { 54 ++begin_count_; 55 // beginHistorySwipeInDirection: calls endHistorySwipe internally. 56 --end_count_; 57 }] andForwardToRealObject] 58 beginHistorySwipeInDirection:history_swiper::kForwards 59 event:[OCMArg any]]; 60 [[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { 61 ++begin_count_; 62 // beginHistorySwipeInDirection: calls endHistorySwipe internally. 63 --end_count_; 64 }] andForwardToRealObject] 65 beginHistorySwipeInDirection:history_swiper::kBackwards 66 event:[OCMArg any]]; 67 [[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { 68 ++end_count_; 69 }] andForwardToRealObject] endHistorySwipe]; 70 [[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { 71 navigated_right_ = true; 72 }] navigateBrowserInDirection:history_swiper::kForwards]; 73 [[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { 74 navigated_left_ = true; 75 }] navigateBrowserInDirection:history_swiper::kBackwards]; 76 77 [[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { 78 magic_mouse_history_swipe_ = true; 79 }] initiateMagicMouseHistorySwipe:YES event:[OCMArg any]]; 80 [[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) { 81 magic_mouse_history_swipe_ = true; 82 }] initiateMagicMouseHistorySwipe:NO event:[OCMArg any]]; 83 84 historySwiper_ = [mockHistorySwiper retain]; 85 86 begin_count_ = 0; 87 end_count_ = 0; 88 navigated_right_ = false; 89 navigated_left_ = false; 90 magic_mouse_history_swipe_ = false; 91 } 92 93 virtual void TearDown() OVERRIDE { 94 [view_ release]; 95 [historySwiper_ release]; 96 CocoaTest::TearDown(); 97 } 98 99 void startGestureInMiddle(); 100 void moveGestureInMiddle(); 101 void moveGestureAtPoint(NSPoint point); 102 void momentumMoveGestureAtPoint(NSPoint point); 103 void endGestureAtPoint(NSPoint point); 104 105 HistorySwiper* historySwiper_; 106 NSView* view_; 107 int begin_count_; 108 int end_count_; 109 bool navigated_right_; 110 bool navigated_left_; 111 bool magic_mouse_history_swipe_; 112}; 113 114NSPoint makePoint(CGFloat x, CGFloat y) { 115 NSPoint point; 116 point.x = x; 117 point.y = y; 118 return point; 119} 120 121id mockEventWithPoint(NSPoint point, NSEventType type) { 122 id mockEvent = [OCMockObject mockForClass:[NSEvent class]]; 123 id mockTouch = [OCMockObject mockForClass:[NSTouch class]]; 124 [[[mockTouch stub] andReturnNSPoint:point] normalizedPosition]; 125 NSArray* touches = @[mockTouch]; 126 [[[mockEvent stub] andReturn:touches] touchesMatchingPhase:NSTouchPhaseAny 127 inView:[OCMArg any]]; 128 [[[mockEvent stub] andReturnBool:NO] isDirectionInvertedFromDevice]; 129 [(NSEvent*)[[mockEvent stub] andReturnValue:OCMOCK_VALUE(type)] type]; 130 131 return mockEvent; 132} 133 134id scrollWheelEventWithPhase(NSEventPhase phase, 135 NSEventPhase momentumPhase, 136 CGFloat scrollingDeltaX, 137 CGFloat scrollingDeltaY) { 138 // The point isn't used, so we pass in bogus data. 139 id event = mockEventWithPoint(makePoint(0,0), NSScrollWheel); 140 [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(phase)] phase]; 141 [(NSEvent*) 142 [[event stub] andReturnValue:OCMOCK_VALUE(momentumPhase)] momentumPhase]; 143 [(NSEvent*)[[event stub] 144 andReturnValue:OCMOCK_VALUE(scrollingDeltaX)] scrollingDeltaX]; 145 [(NSEvent*)[[event stub] 146 andReturnValue:OCMOCK_VALUE(scrollingDeltaY)] scrollingDeltaY]; 147 return event; 148} 149 150id scrollWheelEventWithPhase(NSEventPhase phase, 151 NSEventPhase momentumPhase) { 152 return scrollWheelEventWithPhase(phase, momentumPhase, 0, 0); 153} 154 155id scrollWheelEventWithPhase(NSEventPhase phase) { 156 return scrollWheelEventWithPhase(phase, NSEventPhaseNone); 157} 158 159void MacHistorySwiperTest::startGestureInMiddle() { 160 NSEvent* event = mockEventWithPoint(makePoint(0.5, 0.5), NSEventTypeGesture); 161 [historySwiper_ touchesBeganWithEvent:event]; 162 [historySwiper_ beginGestureWithEvent:event]; 163 NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseBegan); 164 [historySwiper_ handleEvent:scrollEvent]; 165} 166 167void MacHistorySwiperTest::moveGestureInMiddle() { 168 moveGestureAtPoint(makePoint(0.5, 0.5)); 169 170 // Callbacks from blink to set the relevant state for history swiping. 171 [historySwiper_ gotWheelEventConsumed:NO]; 172} 173 174void MacHistorySwiperTest::moveGestureAtPoint(NSPoint point) { 175 NSEvent* event = mockEventWithPoint(point, NSEventTypeGesture); 176 [historySwiper_ touchesMovedWithEvent:event]; 177 178 NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseChanged); 179 [historySwiper_ handleEvent:scrollEvent]; 180} 181 182void MacHistorySwiperTest::momentumMoveGestureAtPoint(NSPoint point) { 183 NSEvent* event = mockEventWithPoint(point, NSEventTypeGesture); 184 [historySwiper_ touchesMovedWithEvent:event]; 185 186 NSEvent* scrollEvent = 187 scrollWheelEventWithPhase(NSEventPhaseNone, NSEventPhaseChanged); 188 [historySwiper_ handleEvent:scrollEvent]; 189} 190 191void MacHistorySwiperTest::endGestureAtPoint(NSPoint point) { 192 NSEvent* event = mockEventWithPoint(point, NSEventTypeGesture); 193 [historySwiper_ touchesEndedWithEvent:event]; 194 195 NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseEnded); 196 [historySwiper_ handleEvent:scrollEvent]; 197} 198 199// Test that a simple left-swipe causes navigation. 200TEST_F(MacHistorySwiperTest, SwipeLeft) { 201 // These tests require 10.7+ APIs. 202 if (![NSEvent 203 respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]) 204 return; 205 206 startGestureInMiddle(); 207 moveGestureInMiddle(); 208 209 EXPECT_EQ(begin_count_, 0); 210 EXPECT_EQ(end_count_, 0); 211 212 moveGestureAtPoint(makePoint(0.2, 0.5)); 213 EXPECT_EQ(begin_count_, 1); 214 EXPECT_EQ(end_count_, 0); 215 EXPECT_FALSE(navigated_right_); 216 EXPECT_FALSE(navigated_left_); 217 218 endGestureAtPoint(makePoint(0.2, 0.5)); 219 EXPECT_EQ(begin_count_, 1); 220 EXPECT_EQ(end_count_, 1); 221 EXPECT_FALSE(navigated_right_); 222 EXPECT_TRUE(navigated_left_); 223} 224 225// Test that a simple right-swipe causes navigation. 226TEST_F(MacHistorySwiperTest, SwipeRight) { 227 // These tests require 10.7+ APIs. 228 if (![NSEvent 229 respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]) 230 return; 231 232 startGestureInMiddle(); 233 moveGestureInMiddle(); 234 235 EXPECT_EQ(begin_count_, 0); 236 EXPECT_EQ(end_count_, 0); 237 238 moveGestureAtPoint(makePoint(0.8, 0.5)); 239 EXPECT_EQ(begin_count_, 1); 240 EXPECT_EQ(end_count_, 0); 241 EXPECT_FALSE(navigated_right_); 242 EXPECT_FALSE(navigated_left_); 243 244 endGestureAtPoint(makePoint(0.8, 0.5)); 245 EXPECT_EQ(begin_count_, 1); 246 EXPECT_EQ(end_count_, 1); 247 EXPECT_TRUE(navigated_right_); 248 EXPECT_FALSE(navigated_left_); 249} 250 251// If the user doesn't swipe enough, the history swiper should begin, but the 252// browser should not navigate. 253TEST_F(MacHistorySwiperTest, SwipeLeftSmallAmount) { 254 // These tests require 10.7+ APIs. 255 if (![NSEvent 256 respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]) 257 return; 258 259 startGestureInMiddle(); 260 moveGestureInMiddle(); 261 moveGestureAtPoint(makePoint(0.45, 0.5)); 262 endGestureAtPoint(makePoint(0.45, 0.5)); 263 EXPECT_EQ(begin_count_, 1); 264 EXPECT_EQ(end_count_, 1); 265 EXPECT_FALSE(navigated_right_); 266 EXPECT_FALSE(navigated_left_); 267} 268 269// Diagonal swipes with a slight horizontal bias should not start the history 270// swiper. 271TEST_F(MacHistorySwiperTest, SwipeDiagonal) { 272 // These tests require 10.7+ APIs. 273 if (![NSEvent 274 respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]) 275 return; 276 277 startGestureInMiddle(); 278 moveGestureInMiddle(); 279 moveGestureAtPoint(makePoint(0.6, 0.59)); 280 endGestureAtPoint(makePoint(0.6, 0.59)); 281 282 EXPECT_EQ(begin_count_, 0); 283 EXPECT_EQ(end_count_, 0); 284 EXPECT_FALSE(navigated_right_); 285 EXPECT_FALSE(navigated_left_); 286} 287 288// Swiping left and then down should cancel the history swiper without 289// navigating. 290TEST_F(MacHistorySwiperTest, SwipeLeftThenDown) { 291 // These tests require 10.7+ APIs. 292 if (![NSEvent 293 respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]) 294 return; 295 296 startGestureInMiddle(); 297 moveGestureInMiddle(); 298 moveGestureAtPoint(makePoint(0.4, 0.5)); 299 moveGestureAtPoint(makePoint(0.4, 0.3)); 300 endGestureAtPoint(makePoint(0.2, 0.2)); 301 EXPECT_EQ(begin_count_, 1); 302 EXPECT_EQ(end_count_, 1); 303 EXPECT_FALSE(navigated_right_); 304 EXPECT_FALSE(navigated_left_); 305} 306 307// Sometimes Cocoa gets confused and sends us a momentum swipe event instead of 308// a swipe gesture event. We should still function appropriately. 309TEST_F(MacHistorySwiperTest, MomentumSwipeLeft) { 310 // These tests require 10.7+ APIs. 311 if (![NSEvent 312 respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]) 313 return; 314 315 startGestureInMiddle(); 316 317 // Send a momentum move gesture. 318 momentumMoveGestureAtPoint(makePoint(0.5, 0.5)); 319 EXPECT_EQ(begin_count_, 0); 320 EXPECT_EQ(end_count_, 0); 321 322 // Callbacks from blink to set the relevant state for history swiping. 323 [historySwiper_ gotWheelEventConsumed:NO]; 324 325 momentumMoveGestureAtPoint(makePoint(0.2, 0.5)); 326 EXPECT_EQ(begin_count_, 1); 327 EXPECT_EQ(end_count_, 0); 328 EXPECT_FALSE(navigated_right_); 329 EXPECT_FALSE(navigated_left_); 330 331 endGestureAtPoint(makePoint(0.2, 0.5)); 332 EXPECT_EQ(begin_count_, 1); 333 EXPECT_EQ(end_count_, 1); 334 EXPECT_FALSE(navigated_right_); 335 EXPECT_TRUE(navigated_left_); 336} 337 338// Momentum scroll events for magic mouse should not attempt to trigger the 339// `trackSwipeEventWithOptions:` api, as that throws an exception. 340TEST_F(MacHistorySwiperTest, MagicMouseMomentumSwipe) { 341 // These tests require 10.7+ APIs. 342 if (![NSEvent 343 respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]) 344 return; 345 346 // Magic mouse events don't generate 'touches*' callbacks. 347 NSEvent* event = mockEventWithPoint(makePoint(0.5, 0.5), NSEventTypeGesture); 348 [historySwiper_ beginGestureWithEvent:event]; 349 NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseBegan); 350 [historySwiper_ handleEvent:scrollEvent]; 351 352 // Callbacks from blink to set the relevant state for history swiping. 353 [historySwiper_ gotWheelEventConsumed:NO]; 354 355 // Send a momentum move gesture. 356 scrollEvent = 357 scrollWheelEventWithPhase(NSEventPhaseNone, NSEventPhaseChanged, 5.0, 0); 358 [historySwiper_ handleEvent:scrollEvent]; 359 360 EXPECT_FALSE(magic_mouse_history_swipe_); 361} 362 363// User starts a swipe but doesn't move. 364TEST_F(MacHistorySwiperTest, NoSwipe) { 365 // These tests require 10.7+ APIs. 366 if (![NSEvent 367 respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]) 368 return; 369 370 startGestureInMiddle(); 371 moveGestureInMiddle(); 372 moveGestureAtPoint(makePoint(0.5, 0.5)); 373 endGestureAtPoint(makePoint(0.5, 0.5)); 374 375 EXPECT_EQ(begin_count_, 0); 376 EXPECT_EQ(end_count_, 0); 377} 378 379// After a gesture is successfully recognized, momentum events should be 380// swallowed, but new events should pass through. 381TEST_F(MacHistorySwiperTest, TouchEventAfterGestureFinishes) { 382 // These tests require 10.7+ APIs. 383 if (![NSEvent 384 respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]) 385 return; 386 387 // Successfully pass through a gesture. 388 startGestureInMiddle(); 389 moveGestureInMiddle(); 390 moveGestureAtPoint(makePoint(0.8, 0.5)); 391 endGestureAtPoint(makePoint(0.8, 0.5)); 392 EXPECT_TRUE(navigated_right_); 393 394 // Momentum events should be swallowed. 395 NSEvent* momentumEvent = scrollWheelEventWithPhase(NSEventPhaseNone, 396 NSEventPhaseChanged); 397 EXPECT_TRUE([historySwiper_ handleEvent:momentumEvent]); 398 399 // New events should not be swallowed. 400 NSEvent* beganEvent = scrollWheelEventWithPhase(NSEventPhaseBegan); 401 EXPECT_FALSE([historySwiper_ handleEvent:beganEvent]); 402} 403 404// If any event is handled by blink, history swiping should not trigger. 405TEST_F(MacHistorySwiperTest, EventHandledByBlink) { 406 // These tests require 10.7+ APIs. 407 if (![NSEvent 408 respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)]) 409 return; 410 411 startGestureInMiddle(); 412 moveGestureInMiddle(); 413 414 // An event is handled by blink. 415 [historySwiper_ gotWheelEventConsumed:YES]; 416 417 // A new event comes in, that isn't handled by blink. 418 moveGestureAtPoint(makePoint(0.2, 0.5)); 419 [historySwiper_ gotWheelEventConsumed:NO]; 420 421 EXPECT_EQ(begin_count_, 0); 422} 423