blob: 4a75ca26d16e023b656b66fdb9065d2a79ad7476 [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/vr/gesture_detector.h"
#include "chrome/browser/vr/input_event.h"
#include "chrome/browser/vr/platform_controller.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr float kDelta = 0.001f;
}
namespace vr {
class MockPlatformController : public PlatformController {
public:
MockPlatformController() = default;
MockPlatformController(bool touching_trackpad,
base::TimeTicks touch_timestamp)
: is_touching_trackpad(touching_trackpad),
last_touch_timestamp(touch_timestamp) {}
bool IsButtonDown(PlatformController::ButtonType type) const override {
switch (type) {
case vr::PlatformController::kButtonSelect:
return is_select_button_down;
case vr::PlatformController::kButtonMenu:
return is_menu_button_down;
default:
return false;
}
}
bool ButtonUpHappened(PlatformController::ButtonType type) const override {
switch (type) {
case vr::PlatformController::kButtonSelect:
return was_select_button_down_ && !is_select_button_down;
case vr::PlatformController::kButtonMenu:
return was_menu_button_down_ && !is_menu_button_down;
default:
return false;
}
}
bool ButtonDownHappened(PlatformController::ButtonType type) const override {
switch (type) {
case vr::PlatformController::kButtonSelect:
return !was_select_button_down_ && is_select_button_down;
case vr::PlatformController::kButtonMenu:
return !was_menu_button_down_ && is_menu_button_down;
default:
return false;
}
}
bool IsTouchingTrackpad() const override { return is_touching_trackpad; }
gfx::PointF GetPositionInTrackpad() const override {
return position_in_trackpad;
}
base::TimeTicks GetLastOrientationTimestamp() const override {
return base::TimeTicks();
}
base::TimeTicks GetLastTouchTimestamp() const override {
return last_touch_timestamp;
}
base::TimeTicks GetLastButtonTimestamp() const override {
return base::TimeTicks();
}
vr::ControllerModel::Handedness GetHandedness() const override {
return vr::ControllerModel::kRightHanded;
}
bool GetRecentered() const override { return false; }
int GetBatteryLevel() const override { return 100; }
// Call before each frame, if the test needs button up/down events.
void Update() {
was_select_button_down_ = is_select_button_down;
was_menu_button_down_ = is_menu_button_down;
}
bool is_touching_trackpad = false;
bool is_select_button_down = false;
bool is_menu_button_down = false;
gfx::PointF position_in_trackpad;
base::TimeTicks last_touch_timestamp;
private:
bool was_select_button_down_ = false;
bool was_menu_button_down_ = false;
};
TEST(GestureDetector, StartTouchWithoutMoving) {
GestureDetector detector;
base::TimeTicks timestamp;
MockPlatformController controller(true, timestamp);
auto gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kFlingCancel);
// A small move doesn't trigger scrolling yet.
timestamp += base::TimeDelta::FromMilliseconds(1);
controller.last_touch_timestamp = timestamp;
controller.position_in_trackpad = {kDelta, kDelta};
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_TRUE(gestures.empty());
}
TEST(GestureDetector, StartTouchMoveAndRelease) {
GestureDetector detector;
base::TimeTicks timestamp;
MockPlatformController controller(true, timestamp);
detector.DetectGestures(controller, timestamp);
// Move to the right.
timestamp += base::TimeDelta::FromMilliseconds(1);
controller.last_touch_timestamp = timestamp;
controller.position_in_trackpad = {0.3f, 0.0f};
auto gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollBegin);
auto* gesture = static_cast<InputEvent*>(gestures.front().get());
EXPECT_GT(gesture->scroll_data.delta_x, 0.0f);
EXPECT_EQ(gesture->scroll_data.delta_y, 0.0f);
// Move slightly up.
timestamp += base::TimeDelta::FromMilliseconds(1);
controller.last_touch_timestamp = timestamp;
controller.position_in_trackpad = {0.3f, 0.01f};
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollUpdate);
gesture = static_cast<InputEvent*>(gestures.front().get());
EXPECT_EQ(gesture->scroll_data.delta_x, 0.0f);
EXPECT_GT(gesture->scroll_data.delta_y, 0.0f);
// Release touch. Scroll is extrapolated for 2 frames.
controller.is_touching_trackpad = false;
timestamp += base::TimeDelta::FromMilliseconds(1);
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollUpdate);
gesture = static_cast<InputEvent*>(gestures.front().get());
EXPECT_GT(gesture->scroll_data.delta_x, 0.0f);
EXPECT_GT(gesture->scroll_data.delta_y, 0.0f);
timestamp += base::TimeDelta::FromMilliseconds(1);
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollUpdate);
timestamp += base::TimeDelta::FromMilliseconds(1);
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollEnd);
}
TEST(GestureDetector, CancelDuringScrolling) {
GestureDetector detector;
base::TimeTicks timestamp;
MockPlatformController controller(true, timestamp);
detector.DetectGestures(controller, timestamp);
// Move to the right.
timestamp += base::TimeDelta::FromMilliseconds(1);
controller.last_touch_timestamp = timestamp;
controller.position_in_trackpad = {0.3f, 0.0f};
auto gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollBegin);
// Button down.
timestamp += base::TimeDelta::FromMilliseconds(1);
controller.last_touch_timestamp = timestamp;
controller.is_select_button_down = true;
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollEnd);
}
TEST(GestureDetector, CancelDuringPostScrolling) {
GestureDetector detector;
base::TimeTicks timestamp;
MockPlatformController controller(true, timestamp);
detector.DetectGestures(controller, timestamp);
// Move to the right.
timestamp += base::TimeDelta::FromMilliseconds(1);
controller.last_touch_timestamp = timestamp;
controller.position_in_trackpad = {0.3f, 0.0f};
auto gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollBegin);
// Release touch. We should see extrapolated scrolling.
timestamp += base::TimeDelta::FromMilliseconds(1);
controller.is_touching_trackpad = false;
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollUpdate);
// Button down.
timestamp += base::TimeDelta::FromMilliseconds(1);
controller.is_select_button_down = true;
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollEnd);
}
TEST(GestureDetector, CancelAndTouchDuringPostScrolling) {
GestureDetector detector;
base::TimeTicks timestamp;
MockPlatformController controller(true, timestamp);
detector.DetectGestures(controller, timestamp);
// Move to the right.
timestamp += base::TimeDelta::FromMilliseconds(1);
controller.last_touch_timestamp = timestamp;
controller.position_in_trackpad = {0.3f, 0.0f};
auto gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollBegin);
// Release touch. We should see extrapolated scrolling.
timestamp += base::TimeDelta::FromMilliseconds(1);
controller.is_touching_trackpad = false;
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollUpdate);
// Touch and button down.
timestamp += base::TimeDelta::FromMilliseconds(1);
controller.is_select_button_down = true;
controller.is_touching_trackpad = true;
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.front()->type(), InputEvent::kScrollEnd);
}
TEST(GestureDetector, ClickMenuButton) {
GestureDetector detector;
base::TimeTicks timestamp;
// Touch down menu button.
MockPlatformController controller;
controller.is_menu_button_down = true;
auto gestures = detector.DetectGestures(controller, timestamp);
EXPECT_TRUE(gestures.empty());
// Release menu button.
controller.Update();
controller.is_menu_button_down = false;
timestamp += base::TimeDelta::FromMilliseconds(1);
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.size(), 1u);
EXPECT_EQ(gestures.front()->type(), InputEvent::kMenuButtonClicked);
}
TEST(GestureDetector, LongPressMenuButton) {
GestureDetector detector;
base::TimeTicks timestamp;
// Touch down menu button.
MockPlatformController controller;
controller.is_menu_button_down = true;
auto gestures = detector.DetectGestures(controller, timestamp);
EXPECT_TRUE(gestures.empty());
// Keep menu button down.
controller.Update();
timestamp += base::TimeDelta::FromSeconds(1);
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.size(), 1u);
EXPECT_EQ(gestures.front()->type(), InputEvent::kMenuButtonLongPressStart);
// Release menu button.
controller.Update();
controller.is_menu_button_down = false;
timestamp += base::TimeDelta::FromSeconds(1);
gestures = detector.DetectGestures(controller, timestamp);
EXPECT_EQ(gestures.size(), 1u);
EXPECT_EQ(gestures.front()->type(), InputEvent::kMenuButtonLongPressEnd);
}
} // namespace vr