// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/graphics/gestures/cast_system_gesture_dispatcher.h"
#include <memory>
#include "base/test/simple_test_tick_clock.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/point.h"
using testing::_;
using testing::Return;
namespace chromecast {
namespace {
constexpr base::TimeDelta kTimeoutWindow = base::Seconds(3);
const size_t kMaxSwipesWithinTimeout = 3;
} // namespace
class MockCastGestureHandler : public CastGestureHandler {
public:
~MockCastGestureHandler() override = default;
MOCK_METHOD0(GetPriority, Priority());
MOCK_METHOD1(CanHandleSwipe, bool(CastSideSwipeOrigin origin));
MOCK_METHOD3(HandleSideSwipe,
void(CastSideSwipeEvent event,
CastSideSwipeOrigin swipe_origin,
const gfx::Point& touch_location));
MOCK_METHOD1(HandleTapDownGesture, void(const gfx::Point& touch_location));
MOCK_METHOD1(HandleTapGesture, void(const gfx::Point& touch_location));
};
class CastSystemGestureDispatcherTest : public testing::Test {
public:
~CastSystemGestureDispatcherTest() override = default;
void SetUp() override {
test_clock_ = std::make_unique<base::SimpleTestTickClock>();
gesture_dispatcher_ =
std::make_unique<CastSystemGestureDispatcher>(test_clock_.get());
}
void TearDown() override { gesture_dispatcher_.reset(); }
protected:
std::unique_ptr<CastSystemGestureDispatcher> gesture_dispatcher_;
std::unique_ptr<base::SimpleTestTickClock> test_clock_;
};
TEST_F(CastSystemGestureDispatcherTest, SingleHandler) {
MockCastGestureHandler handler;
gesture_dispatcher_->AddGestureHandler(&handler);
ON_CALL(handler, GetPriority())
.WillByDefault(Return(CastGestureHandler::Priority::MAIN_ACTIVITY));
// Test when a handler is able to handle a swipe event.
ON_CALL(handler, CanHandleSwipe(_)).WillByDefault(Return(true));
CastSideSwipeEvent event = CastSideSwipeEvent::BEGIN;
CastSideSwipeOrigin origin = CastSideSwipeOrigin::TOP;
gfx::Point point(0, 0);
EXPECT_CALL(handler, HandleSideSwipe(event, origin, point));
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
// Test when a handler cannot handle a swipe event.
ON_CALL(handler, CanHandleSwipe(CastSideSwipeOrigin::BOTTOM))
.WillByDefault(Return(false));
EXPECT_CALL(handler, HandleSideSwipe(_, _, _)).Times(0);
gesture_dispatcher_->HandleSideSwipe(event, CastSideSwipeOrigin::BOTTOM,
point);
gesture_dispatcher_->RemoveGestureHandler(&handler);
}
TEST_F(CastSystemGestureDispatcherTest, SingleHandlerPriorityNone) {
MockCastGestureHandler handler;
gesture_dispatcher_->AddGestureHandler(&handler);
ON_CALL(handler, GetPriority())
.WillByDefault(Return(CastGestureHandler::Priority::NONE));
// Test when a handler is able to handle a swipe event.
ON_CALL(handler, CanHandleSwipe(_)).WillByDefault(Return(true));
CastSideSwipeEvent event = CastSideSwipeEvent::BEGIN;
CastSideSwipeOrigin origin = CastSideSwipeOrigin::TOP;
gfx::Point point(0, 0);
// The gesture event will not be sent since the handler's priority is NONE.
EXPECT_CALL(handler, HandleSideSwipe(_, _, _)).Times(0);
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
gesture_dispatcher_->RemoveGestureHandler(&handler);
}
TEST_F(CastSystemGestureDispatcherTest, MultipleHandlersByPriority) {
MockCastGestureHandler handler_1;
MockCastGestureHandler handler_2;
ON_CALL(handler_1, GetPriority())
.WillByDefault(Return(CastGestureHandler::Priority::ROOT_UI));
ON_CALL(handler_2, GetPriority())
.WillByDefault(Return(CastGestureHandler::Priority::MAIN_ACTIVITY));
gesture_dispatcher_->AddGestureHandler(&handler_1);
gesture_dispatcher_->AddGestureHandler(&handler_2);
// Higher priority handler should get the event.
ON_CALL(handler_1, CanHandleSwipe(_)).WillByDefault(Return(true));
ON_CALL(handler_2, CanHandleSwipe(_)).WillByDefault(Return(true));
CastSideSwipeEvent event = CastSideSwipeEvent::BEGIN;
CastSideSwipeOrigin origin = CastSideSwipeOrigin::TOP;
gfx::Point point(0, 0);
EXPECT_CALL(handler_1, HandleSideSwipe(_, _, _)).Times(0);
EXPECT_CALL(handler_2, HandleSideSwipe(event, origin, point));
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
test_clock_->Advance(kTimeoutWindow);
// Test when the higher priority handler can't handle the event. Lower
// priority handler should get it instead.
origin = CastSideSwipeOrigin::BOTTOM;
ON_CALL(handler_2, CanHandleSwipe(origin)).WillByDefault(Return(false));
EXPECT_CALL(handler_1, HandleSideSwipe(event, origin, point));
EXPECT_CALL(handler_2, HandleSideSwipe(_, _, _)).Times(0);
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
test_clock_->Advance(kTimeoutWindow);
// Test when no handlers can handle the event.
ON_CALL(handler_1, CanHandleSwipe(origin)).WillByDefault(Return(false));
EXPECT_CALL(handler_1, HandleSideSwipe(_, _, _)).Times(0);
EXPECT_CALL(handler_2, HandleSideSwipe(_, _, _)).Times(0);
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
test_clock_->Advance(kTimeoutWindow);
gesture_dispatcher_->RemoveGestureHandler(&handler_2);
gesture_dispatcher_->RemoveGestureHandler(&handler_1);
}
TEST_F(CastSystemGestureDispatcherTest, MultipleBackSwipesToRootUi) {
MockCastGestureHandler handler_1;
MockCastGestureHandler handler_2;
ON_CALL(handler_1, GetPriority())
.WillByDefault(Return(CastGestureHandler::Priority::ROOT_UI));
ON_CALL(handler_2, GetPriority())
.WillByDefault(Return(CastGestureHandler::Priority::MAIN_ACTIVITY));
gesture_dispatcher_->AddGestureHandler(&handler_1);
gesture_dispatcher_->AddGestureHandler(&handler_2);
// Higher priority handler should get the event.
ON_CALL(handler_1, CanHandleSwipe(_)).WillByDefault(Return(true));
ON_CALL(handler_2, CanHandleSwipe(_)).WillByDefault(Return(true));
CastSideSwipeEvent event = CastSideSwipeEvent::BEGIN;
CastSideSwipeOrigin origin = CastSideSwipeOrigin::TOP;
gfx::Point point(0, 0);
// Swipe gestures from any other edge but LEFT will always hit the main target
// handler.
for (size_t i = 0; i < 2 * kMaxSwipesWithinTimeout; ++i) {
EXPECT_CALL(handler_1, HandleSideSwipe(_, _, _)).Times(0);
EXPECT_CALL(handler_2, HandleSideSwipe(event, origin, point));
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
}
// Now test LEFT events.
origin = CastSideSwipeOrigin::LEFT;
// Trigger N - 1 events within the recent events timeout window.
for (size_t i = 0; i < kMaxSwipesWithinTimeout - 1; ++i) {
EXPECT_CALL(handler_1, HandleSideSwipe(_, _, _)).Times(0);
EXPECT_CALL(handler_2, HandleSideSwipe(event, origin, point));
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
base::TimeDelta time_between_events =
(kTimeoutWindow - base::Seconds(1)) / (kMaxSwipesWithinTimeout - 1);
test_clock_->Advance(time_between_events);
}
// The Nth event will go to the root UI, since there were N BEGIN events that
// happened in rapid succession. This means the main activity is probably not
// handling the events properly, and we should defer to the root UI which is
// assumed to behave properly.
event = CastSideSwipeEvent::BEGIN;
EXPECT_CALL(handler_1, HandleSideSwipe(event, origin, point));
EXPECT_CALL(handler_2, HandleSideSwipe(_, _, _)).Times(0);
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
// CONTINUE events still go to the root UI.
event = CastSideSwipeEvent::CONTINUE;
EXPECT_CALL(handler_1, HandleSideSwipe(event, origin, point));
EXPECT_CALL(handler_2, HandleSideSwipe(_, _, _)).Times(0);
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
// All events will go to the root UI until the next BEGIN event after the
// 3-event timeout.
event = CastSideSwipeEvent::CONTINUE;
EXPECT_CALL(handler_1, HandleSideSwipe(event, origin, point));
EXPECT_CALL(handler_2, HandleSideSwipe(_, _, _)).Times(0);
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
event = CastSideSwipeEvent::END;
EXPECT_CALL(handler_1, HandleSideSwipe(event, origin, point));
EXPECT_CALL(handler_2, HandleSideSwipe(_, _, _)).Times(0);
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
// The next event will behave as normal; the timeout period restarts after
// the END swipe event.
event = CastSideSwipeEvent::BEGIN;
EXPECT_CALL(handler_1, HandleSideSwipe(_, _, _)).Times(0);
EXPECT_CALL(handler_2, HandleSideSwipe(event, origin, point));
gesture_dispatcher_->HandleSideSwipe(event, origin, point);
gesture_dispatcher_->RemoveGestureHandler(&handler_2);
gesture_dispatcher_->RemoveGestureHandler(&handler_1);
}
} // namespace chromecast