blob: 6be46f38cd83ec82a0fba4abb9e1e0a093e41292 [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 "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "content/browser/renderer_host/input/synthetic_gesture.h"
#include "content/browser/renderer_host/input/synthetic_gesture_controller.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target.h"
#include "content/browser/renderer_host/input/synthetic_pinch_gesture.h"
#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
#include "content/browser/renderer_host/input/synthetic_tap_gesture.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/common/input/synthetic_pinch_gesture_params.h"
#include "content/common/input/synthetic_smooth_scroll_gesture_params.h"
#include "content/common/input/synthetic_tap_gesture_params.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "content/test/test_render_view_host.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/gfx/point.h"
#include "ui/gfx/point_f.h"
#include "ui/gfx/vector2d.h"
#include "ui/gfx/vector2d_f.h"
using blink::WebInputEvent;
using blink::WebMouseEvent;
using blink::WebMouseWheelEvent;
using blink::WebTouchEvent;
namespace content {
namespace {
const int kFlushInputRateInMs = 16;
const int kPointerAssumedStoppedTimeMs = 43;
const float kTouchSlopInDips = 7.0f;
const float kMinScalingSpanInDips = 27.5f;
class MockSyntheticGesture : public SyntheticGesture {
public:
MockSyntheticGesture(bool* finished, int num_steps)
: finished_(finished),
num_steps_(num_steps),
step_count_(0) {
*finished_ = false;
}
~MockSyntheticGesture() override {}
Result ForwardInputEvents(const base::TimeTicks& timestamp,
SyntheticGestureTarget* target) override {
step_count_++;
if (step_count_ == num_steps_) {
*finished_ = true;
return SyntheticGesture::GESTURE_FINISHED;
} else if (step_count_ > num_steps_) {
*finished_ = true;
// Return arbitrary failure.
return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED;
}
return SyntheticGesture::GESTURE_RUNNING;
}
protected:
bool* finished_;
int num_steps_;
int step_count_;
};
class MockSyntheticGestureTarget : public SyntheticGestureTarget {
public:
MockSyntheticGestureTarget()
: flush_requested_(false),
pointer_assumed_stopped_time_ms_(kPointerAssumedStoppedTimeMs) {}
~MockSyntheticGestureTarget() override {}
// SyntheticGestureTarget:
void DispatchInputEventToPlatform(const WebInputEvent& event) override {}
void SetNeedsFlush() override { flush_requested_ = true; }
SyntheticGestureParams::GestureSourceType
GetDefaultSyntheticGestureSourceType() const override {
return SyntheticGestureParams::TOUCH_INPUT;
}
base::TimeDelta PointerAssumedStoppedTime() const override {
return base::TimeDelta::FromMilliseconds(pointer_assumed_stopped_time_ms_);
}
void set_pointer_assumed_stopped_time_ms(int time_ms) {
pointer_assumed_stopped_time_ms_ = time_ms;
}
float GetTouchSlopInDips() const override { return kTouchSlopInDips; }
float GetMinScalingSpanInDips() const override {
return kMinScalingSpanInDips;
}
bool flush_requested() const { return flush_requested_; }
void ClearFlushRequest() { flush_requested_ = false; }
private:
bool flush_requested_;
int pointer_assumed_stopped_time_ms_;
};
class MockScrollGestureTarget : public MockSyntheticGestureTarget {
public:
MockScrollGestureTarget() : total_abs_scroll_distance_length_(0) {}
~MockScrollGestureTarget() override {}
gfx::Vector2dF start_to_end_distance() const {
return start_to_end_distance_;
}
float total_abs_scroll_distance_length() const {
return total_abs_scroll_distance_length_;
}
protected:
gfx::Vector2dF start_to_end_distance_;
float total_abs_scroll_distance_length_;
};
class MockScrollMouseTarget : public MockScrollGestureTarget {
public:
MockScrollMouseTarget() {}
~MockScrollMouseTarget() override {}
void DispatchInputEventToPlatform(const WebInputEvent& event) override {
ASSERT_EQ(event.type, WebInputEvent::MouseWheel);
const WebMouseWheelEvent& mouse_wheel_event =
static_cast<const WebMouseWheelEvent&>(event);
gfx::Vector2dF delta(mouse_wheel_event.deltaX, mouse_wheel_event.deltaY);
start_to_end_distance_ += delta;
total_abs_scroll_distance_length_ += delta.Length();
}
};
class MockScrollTouchTarget : public MockScrollGestureTarget {
public:
MockScrollTouchTarget() : started_(false) {}
~MockScrollTouchTarget() override {}
void DispatchInputEventToPlatform(const WebInputEvent& event) override {
ASSERT_TRUE(WebInputEvent::isTouchEventType(event.type));
const WebTouchEvent& touch_event = static_cast<const WebTouchEvent&>(event);
ASSERT_EQ(touch_event.touchesLength, 1U);
if (!started_) {
ASSERT_EQ(touch_event.type, WebInputEvent::TouchStart);
start_.SetPoint(touch_event.touches[0].position.x,
touch_event.touches[0].position.y);
last_touch_point_ = start_;
started_ = true;
} else {
ASSERT_NE(touch_event.type, WebInputEvent::TouchStart);
ASSERT_NE(touch_event.type, WebInputEvent::TouchCancel);
gfx::PointF touch_point(touch_event.touches[0].position.x,
touch_event.touches[0].position.y);
gfx::Vector2dF delta = touch_point - last_touch_point_;
total_abs_scroll_distance_length_ += delta.Length();
if (touch_event.type == WebInputEvent::TouchEnd)
start_to_end_distance_ = touch_point - start_;
last_touch_point_ = touch_point;
}
}
protected:
gfx::Point start_;
gfx::PointF last_touch_point_;
bool started_;
};
class MockSyntheticPinchTouchTarget : public MockSyntheticGestureTarget {
public:
enum ZoomDirection {
ZOOM_DIRECTION_UNKNOWN,
ZOOM_IN,
ZOOM_OUT
};
MockSyntheticPinchTouchTarget()
: initial_pointer_distance_(0),
last_pointer_distance_(0),
zoom_direction_(ZOOM_DIRECTION_UNKNOWN),
started_(false) {}
~MockSyntheticPinchTouchTarget() override {}
void DispatchInputEventToPlatform(const WebInputEvent& event) override {
ASSERT_TRUE(WebInputEvent::isTouchEventType(event.type));
const WebTouchEvent& touch_event = static_cast<const WebTouchEvent&>(event);
ASSERT_EQ(touch_event.touchesLength, 2U);
if (!started_) {
ASSERT_EQ(touch_event.type, WebInputEvent::TouchStart);
start_0_ = gfx::PointF(touch_event.touches[0].position);
start_1_ = gfx::PointF(touch_event.touches[1].position);
last_pointer_distance_ = (start_0_ - start_1_).Length();
initial_pointer_distance_ = last_pointer_distance_;
EXPECT_GE(initial_pointer_distance_, GetMinScalingSpanInDips());
started_ = true;
} else {
ASSERT_NE(touch_event.type, WebInputEvent::TouchStart);
ASSERT_NE(touch_event.type, WebInputEvent::TouchCancel);
gfx::PointF current_0 = gfx::PointF(touch_event.touches[0].position);
gfx::PointF current_1 = gfx::PointF(touch_event.touches[1].position);
float pointer_distance = (current_0 - current_1).Length();
if (last_pointer_distance_ != pointer_distance) {
if (zoom_direction_ == ZOOM_DIRECTION_UNKNOWN)
zoom_direction_ =
ComputeZoomDirection(last_pointer_distance_, pointer_distance);
else
EXPECT_EQ(
zoom_direction_,
ComputeZoomDirection(last_pointer_distance_, pointer_distance));
}
last_pointer_distance_ = pointer_distance;
}
}
ZoomDirection zoom_direction() const { return zoom_direction_; }
float ComputeScaleFactor() const {
switch (zoom_direction_) {
case ZOOM_IN:
return last_pointer_distance_ /
(initial_pointer_distance_ + 2 * GetTouchSlopInDips());
case ZOOM_OUT:
return last_pointer_distance_ /
(initial_pointer_distance_ - 2 * GetTouchSlopInDips());
case ZOOM_DIRECTION_UNKNOWN:
return 1.0f;
default:
NOTREACHED();
return 0.0f;
}
}
private:
ZoomDirection ComputeZoomDirection(float last_pointer_distance,
float current_pointer_distance) {
DCHECK_NE(last_pointer_distance, current_pointer_distance);
return last_pointer_distance < current_pointer_distance ? ZOOM_IN
: ZOOM_OUT;
}
float initial_pointer_distance_;
float last_pointer_distance_;
ZoomDirection zoom_direction_;
gfx::PointF start_0_;
gfx::PointF start_1_;
bool started_;
};
class MockSyntheticTapGestureTarget : public MockSyntheticGestureTarget {
public:
MockSyntheticTapGestureTarget() : state_(NOT_STARTED) {}
~MockSyntheticTapGestureTarget() override {}
bool GestureFinished() const { return state_ == FINISHED; }
gfx::PointF position() const { return position_; }
base::TimeDelta GetDuration() const { return stop_time_ - start_time_; }
protected:
enum GestureState {
NOT_STARTED,
STARTED,
FINISHED
};
gfx::PointF position_;
base::TimeDelta start_time_;
base::TimeDelta stop_time_;
GestureState state_;
};
class MockSyntheticTapTouchTarget : public MockSyntheticTapGestureTarget {
public:
MockSyntheticTapTouchTarget() {}
~MockSyntheticTapTouchTarget() override {}
void DispatchInputEventToPlatform(const WebInputEvent& event) override {
ASSERT_TRUE(WebInputEvent::isTouchEventType(event.type));
const WebTouchEvent& touch_event = static_cast<const WebTouchEvent&>(event);
ASSERT_EQ(touch_event.touchesLength, 1U);
switch (state_) {
case NOT_STARTED:
EXPECT_EQ(touch_event.type, WebInputEvent::TouchStart);
position_ = gfx::PointF(touch_event.touches[0].position);
start_time_ = base::TimeDelta::FromMilliseconds(
static_cast<int64>(touch_event.timeStampSeconds * 1000));
state_ = STARTED;
break;
case STARTED:
EXPECT_EQ(touch_event.type, WebInputEvent::TouchEnd);
EXPECT_EQ(position_, gfx::PointF(touch_event.touches[0].position));
stop_time_ = base::TimeDelta::FromMilliseconds(
static_cast<int64>(touch_event.timeStampSeconds * 1000));
state_ = FINISHED;
break;
case FINISHED:
EXPECT_FALSE(true);
break;
}
}
};
class MockSyntheticTapMouseTarget : public MockSyntheticTapGestureTarget {
public:
MockSyntheticTapMouseTarget() {}
~MockSyntheticTapMouseTarget() override {}
void DispatchInputEventToPlatform(const WebInputEvent& event) override {
ASSERT_TRUE(WebInputEvent::isMouseEventType(event.type));
const WebMouseEvent& mouse_event = static_cast<const WebMouseEvent&>(event);
switch (state_) {
case NOT_STARTED:
EXPECT_EQ(mouse_event.type, WebInputEvent::MouseDown);
EXPECT_EQ(mouse_event.button, WebMouseEvent::ButtonLeft);
EXPECT_EQ(mouse_event.clickCount, 1);
position_ = gfx::PointF(mouse_event.x, mouse_event.y);
start_time_ = base::TimeDelta::FromMilliseconds(
static_cast<int64>(mouse_event.timeStampSeconds * 1000));
state_ = STARTED;
break;
case STARTED:
EXPECT_EQ(mouse_event.type, WebInputEvent::MouseUp);
EXPECT_EQ(mouse_event.button, WebMouseEvent::ButtonLeft);
EXPECT_EQ(mouse_event.clickCount, 1);
EXPECT_EQ(position_, gfx::PointF(mouse_event.x, mouse_event.y));
stop_time_ = base::TimeDelta::FromMilliseconds(
static_cast<int64>(mouse_event.timeStampSeconds * 1000));
state_ = FINISHED;
break;
case FINISHED:
EXPECT_FALSE(true);
break;
}
}
};
class SyntheticGestureControllerTest : public testing::Test {
public:
SyntheticGestureControllerTest() {}
virtual ~SyntheticGestureControllerTest() {}
protected:
template<typename MockGestureTarget>
void CreateControllerAndTarget() {
target_ = new MockGestureTarget();
controller_.reset(new SyntheticGestureController(
scoped_ptr<SyntheticGestureTarget>(target_)));
}
virtual void SetUp() override {
start_time_ = base::TimeTicks::Now();
time_ = start_time_;
num_success_ = 0;
num_failure_ = 0;
}
virtual void TearDown() override {
controller_.reset();
target_ = NULL;
time_ = base::TimeTicks();
}
void QueueSyntheticGesture(scoped_ptr<SyntheticGesture> gesture) {
controller_->QueueSyntheticGesture(gesture.Pass(),
base::Bind(&SyntheticGestureControllerTest::OnSyntheticGestureCompleted,
base::Unretained(this)));
}
void FlushInputUntilComplete() {
while (target_->flush_requested()) {
while (target_->flush_requested()) {
target_->ClearFlushRequest();
time_ += base::TimeDelta::FromMilliseconds(kFlushInputRateInMs);
controller_->Flush(time_);
}
controller_->OnDidFlushInput();
}
}
void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
DCHECK_NE(result, SyntheticGesture::GESTURE_RUNNING);
if (result == SyntheticGesture::GESTURE_FINISHED)
num_success_++;
else
num_failure_++;
}
base::TimeDelta GetTotalTime() const { return time_ - start_time_; }
MockSyntheticGestureTarget* target_;
scoped_ptr<SyntheticGestureController> controller_;
base::TimeTicks start_time_;
base::TimeTicks time_;
int num_success_;
int num_failure_;
};
TEST_F(SyntheticGestureControllerTest, SingleGesture) {
CreateControllerAndTarget<MockSyntheticGestureTarget>();
bool finished = false;
scoped_ptr<MockSyntheticGesture> gesture(
new MockSyntheticGesture(&finished, 3));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
EXPECT_TRUE(finished);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
}
TEST_F(SyntheticGestureControllerTest, GestureFailed) {
CreateControllerAndTarget<MockSyntheticGestureTarget>();
bool finished = false;
scoped_ptr<MockSyntheticGesture> gesture(
new MockSyntheticGesture(&finished, 0));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
EXPECT_TRUE(finished);
EXPECT_EQ(1, num_failure_);
EXPECT_EQ(0, num_success_);
}
TEST_F(SyntheticGestureControllerTest, SuccessiveGestures) {
CreateControllerAndTarget<MockSyntheticGestureTarget>();
bool finished_1 = false;
scoped_ptr<MockSyntheticGesture> gesture_1(
new MockSyntheticGesture(&finished_1, 2));
bool finished_2 = false;
scoped_ptr<MockSyntheticGesture> gesture_2(
new MockSyntheticGesture(&finished_2, 4));
// Queue first gesture and wait for it to finish
QueueSyntheticGesture(gesture_1.Pass());
FlushInputUntilComplete();
EXPECT_TRUE(finished_1);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
// Queue second gesture.
QueueSyntheticGesture(gesture_2.Pass());
FlushInputUntilComplete();
EXPECT_TRUE(finished_2);
EXPECT_EQ(2, num_success_);
EXPECT_EQ(0, num_failure_);
}
TEST_F(SyntheticGestureControllerTest, TwoGesturesInFlight) {
CreateControllerAndTarget<MockSyntheticGestureTarget>();
bool finished_1 = false;
scoped_ptr<MockSyntheticGesture> gesture_1(
new MockSyntheticGesture(&finished_1, 2));
bool finished_2 = false;
scoped_ptr<MockSyntheticGesture> gesture_2(
new MockSyntheticGesture(&finished_2, 4));
QueueSyntheticGesture(gesture_1.Pass());
QueueSyntheticGesture(gesture_2.Pass());
FlushInputUntilComplete();
EXPECT_TRUE(finished_1);
EXPECT_TRUE(finished_2);
EXPECT_EQ(2, num_success_);
EXPECT_EQ(0, num_failure_);
}
TEST_F(SyntheticGestureControllerTest, GestureCompletedOnDidFlushInput) {
CreateControllerAndTarget<MockSyntheticGestureTarget>();
bool finished_1, finished_2;
scoped_ptr<MockSyntheticGesture> gesture_1(
new MockSyntheticGesture(&finished_1, 2));
scoped_ptr<MockSyntheticGesture> gesture_2(
new MockSyntheticGesture(&finished_2, 4));
QueueSyntheticGesture(gesture_1.Pass());
QueueSyntheticGesture(gesture_2.Pass());
while (target_->flush_requested()) {
target_->ClearFlushRequest();
time_ += base::TimeDelta::FromMilliseconds(kFlushInputRateInMs);
controller_->Flush(time_);
}
EXPECT_EQ(0, num_success_);
controller_->OnDidFlushInput();
EXPECT_EQ(1, num_success_);
while (target_->flush_requested()) {
target_->ClearFlushRequest();
time_ += base::TimeDelta::FromMilliseconds(kFlushInputRateInMs);
controller_->Flush(time_);
}
EXPECT_EQ(1, num_success_);
controller_->OnDidFlushInput();
EXPECT_EQ(2, num_success_);
}
gfx::Vector2d AddTouchSlopToVector(const gfx::Vector2d& vector,
SyntheticGestureTarget* target) {
const int kTouchSlop = target->GetTouchSlopInDips();
int x = vector.x();
if (x > 0)
x += kTouchSlop;
else if (x < 0)
x -= kTouchSlop;
int y = vector.y();
if (y > 0)
y += kTouchSlop;
else if (y < 0)
y -= kTouchSlop;
return gfx::Vector2d(x, y);
}
TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchVertical) {
CreateControllerAndTarget<MockScrollTouchTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.anchor.SetPoint(89, 32);
params.distances.push_back(gfx::Vector2d(0, 123));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_EQ(AddTouchSlopToVector(params.distances[0], target_),
scroll_target->start_to_end_distance());
}
TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchHorizontal) {
CreateControllerAndTarget<MockScrollTouchTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.anchor.SetPoint(12, -23);
params.distances.push_back(gfx::Vector2d(-234, 0));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_EQ(AddTouchSlopToVector(params.distances[0], target_),
scroll_target->start_to_end_distance());
}
void CheckIsWithinRangeSingle(float scroll_distance,
int target_distance,
SyntheticGestureTarget* target) {
if (target_distance > 0) {
EXPECT_LE(target_distance, scroll_distance);
EXPECT_LE(scroll_distance, target_distance + target->GetTouchSlopInDips());
} else {
EXPECT_GE(target_distance, scroll_distance);
EXPECT_GE(scroll_distance, target_distance - target->GetTouchSlopInDips());
}
}
void CheckSingleScrollDistanceIsWithinRange(
const gfx::Vector2dF& scroll_distance,
const gfx::Vector2d& target_distance,
SyntheticGestureTarget* target) {
CheckIsWithinRangeSingle(scroll_distance.x(), target_distance.x(), target);
CheckIsWithinRangeSingle(scroll_distance.y(), target_distance.y(), target);
}
TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchDiagonal) {
CreateControllerAndTarget<MockScrollTouchTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.anchor.SetPoint(0, 7);
params.distances.push_back(gfx::Vector2d(413, -83));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
CheckSingleScrollDistanceIsWithinRange(
scroll_target->start_to_end_distance(), params.distances[0], target_);
}
TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchLongStop) {
CreateControllerAndTarget<MockScrollTouchTarget>();
// Create a smooth scroll with a short distance and set the pointer assumed
// stopped time high, so that the stopping should dominate the time the
// gesture is active.
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.anchor.SetPoint(-98, -23);
params.distances.push_back(gfx::Vector2d(21, -12));
params.prevent_fling = true;
target_->set_pointer_assumed_stopped_time_ms(543);
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
CheckSingleScrollDistanceIsWithinRange(
scroll_target->start_to_end_distance(), params.distances[0], target_);
EXPECT_GE(GetTotalTime(), target_->PointerAssumedStoppedTime());
}
TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchFling) {
CreateControllerAndTarget<MockScrollTouchTarget>();
// Create a smooth scroll with a short distance and set the pointer assumed
// stopped time high. Disable 'prevent_fling' and check that the gesture
// finishes without waiting before it stops.
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.anchor.SetPoint(-89, 78);
params.distances.push_back(gfx::Vector2d(-43, 19));
params.prevent_fling = false;
target_->set_pointer_assumed_stopped_time_ms(543);
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
CheckSingleScrollDistanceIsWithinRange(
scroll_target->start_to_end_distance(), params.distances[0], target_);
EXPECT_LE(GetTotalTime(), target_->PointerAssumedStoppedTime());
}
TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchZeroDistance) {
CreateControllerAndTarget<MockScrollTouchTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.anchor.SetPoint(-32, 43);
params.distances.push_back(gfx::Vector2d(0, 0));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_EQ(gfx::Vector2dF(0, 0), scroll_target->start_to_end_distance());
}
TEST_F(SyntheticGestureControllerTest, SingleScrollGestureMouseVertical) {
CreateControllerAndTarget<MockScrollMouseTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
params.anchor.SetPoint(432, 89);
params.distances.push_back(gfx::Vector2d(0, -234));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_EQ(params.distances[0], scroll_target->start_to_end_distance());
}
TEST_F(SyntheticGestureControllerTest, SingleScrollGestureMouseHorizontal) {
CreateControllerAndTarget<MockScrollMouseTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
params.anchor.SetPoint(90, 12);
params.distances.push_back(gfx::Vector2d(345, 0));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_EQ(params.distances[0], scroll_target->start_to_end_distance());
}
TEST_F(SyntheticGestureControllerTest, SingleScrollGestureMouseDiagonal) {
CreateControllerAndTarget<MockScrollMouseTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
params.anchor.SetPoint(90, 12);
params.distances.push_back(gfx::Vector2d(-194, 303));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_EQ(params.distances[0], scroll_target->start_to_end_distance());
}
TEST_F(SyntheticGestureControllerTest, MultiScrollGestureMouse) {
CreateControllerAndTarget<MockScrollMouseTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
params.anchor.SetPoint(90, 12);
params.distances.push_back(gfx::Vector2d(-129, 212));
params.distances.push_back(gfx::Vector2d(8, -9));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_EQ(params.distances[0] + params.distances[1],
scroll_target->start_to_end_distance());
}
TEST_F(SyntheticGestureControllerTest, MultiScrollGestureMouseHorizontal) {
CreateControllerAndTarget<MockScrollMouseTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
params.anchor.SetPoint(90, 12);
params.distances.push_back(gfx::Vector2d(-129, 0));
params.distances.push_back(gfx::Vector2d(79, 0));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
// This check only works for horizontal or vertical scrolls because of
// floating point precision issues with diagonal scrolls.
EXPECT_FLOAT_EQ(params.distances[0].Length() + params.distances[1].Length(),
scroll_target->total_abs_scroll_distance_length());
EXPECT_EQ(params.distances[0] + params.distances[1],
scroll_target->start_to_end_distance());
}
void CheckIsWithinRangeMulti(float scroll_distance,
int target_distance,
SyntheticGestureTarget* target) {
if (target_distance > 0) {
EXPECT_GE(scroll_distance, target_distance - target->GetTouchSlopInDips());
EXPECT_LE(scroll_distance, target_distance + target->GetTouchSlopInDips());
} else {
EXPECT_LE(scroll_distance, target_distance + target->GetTouchSlopInDips());
EXPECT_GE(scroll_distance, target_distance - target->GetTouchSlopInDips());
}
}
void CheckMultiScrollDistanceIsWithinRange(
const gfx::Vector2dF& scroll_distance,
const gfx::Vector2d& target_distance,
SyntheticGestureTarget* target) {
CheckIsWithinRangeMulti(scroll_distance.x(), target_distance.x(), target);
CheckIsWithinRangeMulti(scroll_distance.y(), target_distance.y(), target);
}
TEST_F(SyntheticGestureControllerTest, MultiScrollGestureTouch) {
CreateControllerAndTarget<MockScrollTouchTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.anchor.SetPoint(8, -13);
params.distances.push_back(gfx::Vector2d(234, 133));
params.distances.push_back(gfx::Vector2d(-9, 78));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
CheckMultiScrollDistanceIsWithinRange(
scroll_target->start_to_end_distance(),
params.distances[0] + params.distances[1],
target_);
}
TEST_F(SyntheticGestureControllerTest, MultiScrollGestureTouchVertical) {
CreateControllerAndTarget<MockScrollTouchTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.anchor.SetPoint(234, -13);
params.distances.push_back(gfx::Vector2d(0, 133));
params.distances.push_back(gfx::Vector2d(0, 78));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockScrollGestureTarget* scroll_target =
static_cast<MockScrollGestureTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_FLOAT_EQ(
params.distances[0].Length() + params.distances[1].Length() +
target_->GetTouchSlopInDips(),
scroll_target->total_abs_scroll_distance_length());
EXPECT_EQ(AddTouchSlopToVector(params.distances[0] + params.distances[1],
target_),
scroll_target->start_to_end_distance());
}
TEST_F(SyntheticGestureControllerTest, PinchGestureTouchZoomIn) {
CreateControllerAndTarget<MockSyntheticPinchTouchTarget>();
SyntheticPinchGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.scale_factor = 2.3f;
params.anchor.SetPoint(54, 89);
scoped_ptr<SyntheticPinchGesture> gesture(new SyntheticPinchGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockSyntheticPinchTouchTarget* pinch_target =
static_cast<MockSyntheticPinchTouchTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_EQ(pinch_target->zoom_direction(),
MockSyntheticPinchTouchTarget::ZOOM_IN);
EXPECT_FLOAT_EQ(params.scale_factor, pinch_target->ComputeScaleFactor());
}
TEST_F(SyntheticGestureControllerTest, PinchGestureTouchZoomOut) {
CreateControllerAndTarget<MockSyntheticPinchTouchTarget>();
SyntheticPinchGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.scale_factor = 0.4f;
params.anchor.SetPoint(-12, 93);
scoped_ptr<SyntheticPinchGesture> gesture(new SyntheticPinchGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockSyntheticPinchTouchTarget* pinch_target =
static_cast<MockSyntheticPinchTouchTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_EQ(pinch_target->zoom_direction(),
MockSyntheticPinchTouchTarget::ZOOM_OUT);
EXPECT_FLOAT_EQ(params.scale_factor, pinch_target->ComputeScaleFactor());
}
TEST_F(SyntheticGestureControllerTest, PinchGestureTouchNoScaling) {
CreateControllerAndTarget<MockSyntheticPinchTouchTarget>();
SyntheticPinchGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.scale_factor = 1.0f;
scoped_ptr<SyntheticPinchGesture> gesture(new SyntheticPinchGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockSyntheticPinchTouchTarget* pinch_target =
static_cast<MockSyntheticPinchTouchTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_EQ(pinch_target->zoom_direction(),
MockSyntheticPinchTouchTarget::ZOOM_DIRECTION_UNKNOWN);
EXPECT_EQ(params.scale_factor, pinch_target->ComputeScaleFactor());
}
TEST_F(SyntheticGestureControllerTest, TapGestureTouch) {
CreateControllerAndTarget<MockSyntheticTapTouchTarget>();
SyntheticTapGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.duration_ms = 123;
params.position.SetPoint(87, -124);
scoped_ptr<SyntheticTapGesture> gesture(new SyntheticTapGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockSyntheticTapTouchTarget* tap_target =
static_cast<MockSyntheticTapTouchTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_TRUE(tap_target->GestureFinished());
EXPECT_EQ(tap_target->position(), params.position);
EXPECT_EQ(tap_target->GetDuration().InMilliseconds(), params.duration_ms);
EXPECT_GE(GetTotalTime(),
base::TimeDelta::FromMilliseconds(params.duration_ms));
}
TEST_F(SyntheticGestureControllerTest, TapGestureMouse) {
CreateControllerAndTarget<MockSyntheticTapMouseTarget>();
SyntheticTapGestureParams params;
params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
params.duration_ms = 79;
params.position.SetPoint(98, 123);
scoped_ptr<SyntheticTapGesture> gesture(new SyntheticTapGesture(params));
QueueSyntheticGesture(gesture.Pass());
FlushInputUntilComplete();
MockSyntheticTapMouseTarget* tap_target =
static_cast<MockSyntheticTapMouseTarget*>(target_);
EXPECT_EQ(1, num_success_);
EXPECT_EQ(0, num_failure_);
EXPECT_TRUE(tap_target->GestureFinished());
EXPECT_EQ(tap_target->position(), params.position);
EXPECT_EQ(tap_target->GetDuration().InMilliseconds(), params.duration_ms);
EXPECT_GE(GetTotalTime(),
base::TimeDelta::FromMilliseconds(params.duration_ms));
}
} // namespace
} // namespace content