blob: 6d714ae2d772ee1837b866b1c5779e9d81634f37 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/gtest/include/gtest/gtest.h"
#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
#include "base/mac/scoped_nsobject.h"
#import "base/mac/sdk_forward_declarations.h"
#import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/ocmock_extensions.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
@interface HistorySwiper (MacHistorySwiperTest)
- (BOOL)systemSettingsAllowHistorySwiping:(NSEvent*)event;
- (BOOL)browserCanNavigateInDirection:
(history_swiper::NavigationDirection)forward
event:(NSEvent*)event;
- (void)endHistorySwipe;
- (void)beginHistorySwipeInDirection:
(history_swiper::NavigationDirection)goForward
event:(NSEvent*)event;
- (void)navigateBrowserInDirection:(history_swiper::NavigationDirection)forward;
- (void)initiateMagicMouseHistorySwipe:(BOOL)isRightScroll
event:(NSEvent*)event;
@end
class MacHistorySwiperTest : public CocoaTest {
public:
virtual void SetUp() OVERRIDE {
CocoaTest::SetUp();
[HistorySwiper resetMagicMouseState];
view_ = [[NSView alloc] init];
id mockDelegate =
[OCMockObject mockForProtocol:@protocol(HistorySwiperDelegate)];
[[[mockDelegate stub] andReturn:view_] viewThatWantsHistoryOverlay];
[[[mockDelegate stub] andReturnBool:YES] shouldAllowHistorySwiping];
base::scoped_nsobject<HistorySwiper> historySwiper(
[[HistorySwiper alloc] initWithDelegate:mockDelegate]);
id mockHistorySwiper = [OCMockObject partialMockForObject:historySwiper];
[[[mockHistorySwiper stub] andReturnBool:YES]
systemSettingsAllowHistorySwiping:[OCMArg any]];
[[[mockHistorySwiper stub] andReturnBool:YES]
browserCanNavigateInDirection:history_swiper::kForwards
event:[OCMArg any]];
[[[mockHistorySwiper stub] andReturnBool:YES]
browserCanNavigateInDirection:history_swiper::kBackwards
event:[OCMArg any]];
[[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
++begin_count_;
// beginHistorySwipeInDirection: calls endHistorySwipe internally.
--end_count_;
}] andForwardToRealObject]
beginHistorySwipeInDirection:history_swiper::kForwards
event:[OCMArg any]];
[[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
++begin_count_;
// beginHistorySwipeInDirection: calls endHistorySwipe internally.
--end_count_;
}] andForwardToRealObject]
beginHistorySwipeInDirection:history_swiper::kBackwards
event:[OCMArg any]];
[[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
++end_count_;
}] andForwardToRealObject] endHistorySwipe];
[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
navigated_right_ = true;
}] navigateBrowserInDirection:history_swiper::kForwards];
[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
navigated_left_ = true;
}] navigateBrowserInDirection:history_swiper::kBackwards];
[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
magic_mouse_history_swipe_ = true;
}] initiateMagicMouseHistorySwipe:YES event:[OCMArg any]];
[[[mockHistorySwiper stub] andDo:^(NSInvocation* invocation) {
magic_mouse_history_swipe_ = true;
}] initiateMagicMouseHistorySwipe:NO event:[OCMArg any]];
historySwiper_ = [mockHistorySwiper retain];
begin_count_ = 0;
end_count_ = 0;
navigated_right_ = false;
navigated_left_ = false;
magic_mouse_history_swipe_ = false;
}
virtual void TearDown() OVERRIDE {
[view_ release];
[historySwiper_ release];
CocoaTest::TearDown();
}
void startGestureInMiddle();
void moveGestureInMiddle();
void moveGestureAtPoint(NSPoint point);
void momentumMoveGestureAtPoint(NSPoint point);
void endGestureAtPoint(NSPoint point);
void rendererACKForBeganEvent();
HistorySwiper* historySwiper_;
NSView* view_;
int begin_count_;
int end_count_;
bool navigated_right_;
bool navigated_left_;
bool magic_mouse_history_swipe_;
};
NSPoint makePoint(CGFloat x, CGFloat y) {
NSPoint point;
point.x = x;
point.y = y;
return point;
}
id mockEventWithPoint(NSPoint point, NSEventType type) {
id mockEvent = [OCMockObject mockForClass:[NSEvent class]];
id mockTouch = [OCMockObject mockForClass:[NSTouch class]];
[[[mockTouch stub] andReturnNSPoint:point] normalizedPosition];
NSArray* touches = @[mockTouch];
[[[mockEvent stub] andReturn:touches] touchesMatchingPhase:NSTouchPhaseAny
inView:[OCMArg any]];
[[[mockEvent stub] andReturnBool:NO] isDirectionInvertedFromDevice];
[(NSEvent*)[[mockEvent stub] andReturnValue:OCMOCK_VALUE(type)] type];
return mockEvent;
}
id scrollWheelEventWithPhase(NSEventPhase phase,
NSEventPhase momentumPhase,
CGFloat scrollingDeltaX,
CGFloat scrollingDeltaY) {
// The point isn't used, so we pass in bogus data.
id event = mockEventWithPoint(makePoint(0,0), NSScrollWheel);
[(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(phase)] phase];
[(NSEvent*)
[[event stub] andReturnValue:OCMOCK_VALUE(momentumPhase)] momentumPhase];
[(NSEvent*)[[event stub]
andReturnValue:OCMOCK_VALUE(scrollingDeltaX)] scrollingDeltaX];
[(NSEvent*)[[event stub]
andReturnValue:OCMOCK_VALUE(scrollingDeltaY)] scrollingDeltaY];
return event;
}
id scrollWheelEventWithPhase(NSEventPhase phase,
NSEventPhase momentumPhase) {
return scrollWheelEventWithPhase(phase, momentumPhase, 0, 0);
}
id scrollWheelEventWithPhase(NSEventPhase phase) {
return scrollWheelEventWithPhase(phase, NSEventPhaseNone);
}
void MacHistorySwiperTest::startGestureInMiddle() {
NSEvent* event = mockEventWithPoint(makePoint(0.5, 0.5), NSEventTypeGesture);
[historySwiper_ touchesBeganWithEvent:event];
[historySwiper_ beginGestureWithEvent:event];
NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseBegan);
[historySwiper_ handleEvent:scrollEvent];
}
void MacHistorySwiperTest::moveGestureInMiddle() {
moveGestureAtPoint(makePoint(0.5, 0.5));
// Callbacks from blink to set the relevant state for history swiping.
rendererACKForBeganEvent();
}
void MacHistorySwiperTest::moveGestureAtPoint(NSPoint point) {
NSEvent* event = mockEventWithPoint(point, NSEventTypeGesture);
[historySwiper_ touchesMovedWithEvent:event];
NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseChanged);
[historySwiper_ handleEvent:scrollEvent];
}
void MacHistorySwiperTest::momentumMoveGestureAtPoint(NSPoint point) {
NSEvent* event = mockEventWithPoint(point, NSEventTypeGesture);
[historySwiper_ touchesMovedWithEvent:event];
NSEvent* scrollEvent =
scrollWheelEventWithPhase(NSEventPhaseNone, NSEventPhaseChanged);
[historySwiper_ handleEvent:scrollEvent];
}
void MacHistorySwiperTest::endGestureAtPoint(NSPoint point) {
NSEvent* event = mockEventWithPoint(point, NSEventTypeGesture);
[historySwiper_ touchesEndedWithEvent:event];
NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseEnded);
[historySwiper_ handleEvent:scrollEvent];
}
void MacHistorySwiperTest::rendererACKForBeganEvent() {
blink::WebMouseWheelEvent event;
event.phase = blink::WebMouseWheelEvent::PhaseBegan;
[historySwiper_ rendererHandledWheelEvent:event consumed:NO];
}
// Test that a simple left-swipe causes navigation.
TEST_F(MacHistorySwiperTest, SwipeLeft) {
// These tests require 10.7+ APIs.
if (![NSEvent
respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)])
return;
startGestureInMiddle();
moveGestureInMiddle();
EXPECT_EQ(begin_count_, 0);
EXPECT_EQ(end_count_, 0);
moveGestureAtPoint(makePoint(0.2, 0.5));
EXPECT_EQ(begin_count_, 1);
EXPECT_EQ(end_count_, 0);
EXPECT_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_);
endGestureAtPoint(makePoint(0.2, 0.5));
EXPECT_EQ(begin_count_, 1);
EXPECT_EQ(end_count_, 1);
EXPECT_FALSE(navigated_right_);
EXPECT_TRUE(navigated_left_);
}
// Test that a simple right-swipe causes navigation.
TEST_F(MacHistorySwiperTest, SwipeRight) {
// These tests require 10.7+ APIs.
if (![NSEvent
respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)])
return;
startGestureInMiddle();
moveGestureInMiddle();
EXPECT_EQ(begin_count_, 0);
EXPECT_EQ(end_count_, 0);
moveGestureAtPoint(makePoint(0.8, 0.5));
EXPECT_EQ(begin_count_, 1);
EXPECT_EQ(end_count_, 0);
EXPECT_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_);
endGestureAtPoint(makePoint(0.8, 0.5));
EXPECT_EQ(begin_count_, 1);
EXPECT_EQ(end_count_, 1);
EXPECT_TRUE(navigated_right_);
EXPECT_FALSE(navigated_left_);
}
// If the user doesn't swipe enough, the history swiper should begin, but the
// browser should not navigate.
TEST_F(MacHistorySwiperTest, SwipeLeftSmallAmount) {
// These tests require 10.7+ APIs.
if (![NSEvent
respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)])
return;
startGestureInMiddle();
moveGestureInMiddle();
moveGestureAtPoint(makePoint(0.45, 0.5));
endGestureAtPoint(makePoint(0.45, 0.5));
EXPECT_EQ(begin_count_, 1);
EXPECT_EQ(end_count_, 1);
EXPECT_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_);
}
// Diagonal swipes with a slight horizontal bias should not start the history
// swiper.
TEST_F(MacHistorySwiperTest, SwipeDiagonal) {
// These tests require 10.7+ APIs.
if (![NSEvent
respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)])
return;
startGestureInMiddle();
moveGestureInMiddle();
moveGestureAtPoint(makePoint(0.6, 0.59));
endGestureAtPoint(makePoint(0.6, 0.59));
EXPECT_EQ(begin_count_, 1);
EXPECT_EQ(end_count_, 1);
EXPECT_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_);
}
// Swiping left and then down should cancel the history swiper without
// navigating.
TEST_F(MacHistorySwiperTest, SwipeLeftThenDown) {
// These tests require 10.7+ APIs.
if (![NSEvent
respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)])
return;
startGestureInMiddle();
moveGestureInMiddle();
moveGestureAtPoint(makePoint(0.4, 0.5));
moveGestureAtPoint(makePoint(0.4, 0.3));
endGestureAtPoint(makePoint(0.2, 0.2));
EXPECT_EQ(begin_count_, 1);
EXPECT_EQ(end_count_, 1);
EXPECT_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_);
}
// Sometimes Cocoa gets confused and sends us a momentum swipe event instead of
// a swipe gesture event. Momentum events should not cause history swiping.
TEST_F(MacHistorySwiperTest, MomentumSwipeLeft) {
// These tests require 10.7+ APIs.
if (![NSEvent
respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)])
return;
startGestureInMiddle();
// Send a momentum move gesture.
momentumMoveGestureAtPoint(makePoint(0.5, 0.5));
EXPECT_EQ(begin_count_, 0);
EXPECT_EQ(end_count_, 0);
// Callbacks from blink to set the relevant state for history swiping.
rendererACKForBeganEvent();
momentumMoveGestureAtPoint(makePoint(0.2, 0.5));
EXPECT_EQ(begin_count_, 0);
EXPECT_EQ(end_count_, 0);
endGestureAtPoint(makePoint(0.2, 0.5));
EXPECT_EQ(begin_count_, 0);
EXPECT_EQ(end_count_, 0);
}
// Momentum scroll events for magic mouse should not attempt to trigger the
// `trackSwipeEventWithOptions:` api, as that throws an exception.
TEST_F(MacHistorySwiperTest, MagicMouseMomentumSwipe) {
// These tests require 10.7+ APIs.
if (![NSEvent
respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)])
return;
// Magic mouse events don't generate 'touches*' callbacks.
NSEvent* event = mockEventWithPoint(makePoint(0.5, 0.5), NSEventTypeGesture);
[historySwiper_ beginGestureWithEvent:event];
NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseBegan);
[historySwiper_ handleEvent:scrollEvent];
// Callbacks from blink to set the relevant state for history swiping.
rendererACKForBeganEvent();
// Send a momentum move gesture.
scrollEvent =
scrollWheelEventWithPhase(NSEventPhaseNone, NSEventPhaseChanged, 5.0, 0);
[historySwiper_ handleEvent:scrollEvent];
EXPECT_FALSE(magic_mouse_history_swipe_);
}
// User starts a swipe but doesn't move.
TEST_F(MacHistorySwiperTest, NoSwipe) {
// These tests require 10.7+ APIs.
if (![NSEvent
respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)])
return;
startGestureInMiddle();
moveGestureInMiddle();
moveGestureAtPoint(makePoint(0.5, 0.5));
endGestureAtPoint(makePoint(0.5, 0.5));
EXPECT_EQ(begin_count_, 1);
EXPECT_EQ(end_count_, 1);
EXPECT_FALSE(navigated_right_);
EXPECT_FALSE(navigated_left_);
}
// After a gesture is successfully recognized, momentum events should be
// swallowed, but new events should pass through.
TEST_F(MacHistorySwiperTest, TouchEventAfterGestureFinishes) {
// These tests require 10.7+ APIs.
if (![NSEvent
respondsToSelector:@selector(isSwipeTrackingFromScrollEventsEnabled)])
return;
// Successfully pass through a gesture.
startGestureInMiddle();
moveGestureInMiddle();
moveGestureAtPoint(makePoint(0.8, 0.5));
endGestureAtPoint(makePoint(0.8, 0.5));
EXPECT_TRUE(navigated_right_);
// Momentum events should be swallowed.
NSEvent* momentumEvent = scrollWheelEventWithPhase(NSEventPhaseNone,
NSEventPhaseChanged);
EXPECT_TRUE([historySwiper_ handleEvent:momentumEvent]);
// New events should not be swallowed.
NSEvent* beganEvent = scrollWheelEventWithPhase(NSEventPhaseBegan);
EXPECT_FALSE([historySwiper_ handleEvent:beganEvent]);
}