941 lines
34 KiB
C++
941 lines
34 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include <initializer_list>
|
|
#include "InputData.h"
|
|
#include "Units.h"
|
|
#include "gtest/gtest.h"
|
|
#include "mozilla/Maybe.h"
|
|
#include "mozilla/TimeStamp.h"
|
|
#include "TouchResampler.h"
|
|
|
|
using namespace mozilla;
|
|
using widget::TouchResampler;
|
|
|
|
class TouchResamplerTest : public ::testing::Test {
|
|
protected:
|
|
virtual void SetUp() { baseTimeStamp = TimeStamp::Now(); }
|
|
|
|
TimeStamp Time(double aMilliseconds) {
|
|
return baseTimeStamp + TimeDuration::FromMilliseconds(aMilliseconds);
|
|
}
|
|
|
|
uint64_t ProcessEvent(
|
|
MultiTouchInput::MultiTouchType aType,
|
|
std::initializer_list<std::pair<TimeStamp, ScreenIntPoint>>
|
|
aHistoricalData,
|
|
const TimeStamp& aTimeStamp, const ScreenIntPoint& aPosition) {
|
|
MultiTouchInput input(aType, 0, aTimeStamp, 0);
|
|
input.mTouches.AppendElement(SingleTouchData(1, aPosition, {}, 0.0f, 0.0f));
|
|
for (const auto& histData : aHistoricalData) {
|
|
input.mTouches[0].mHistoricalData.AppendElement(
|
|
SingleTouchData::HistoricalTouchData{
|
|
histData.first, histData.second, {}, {}, 0.0f, 0.0f});
|
|
}
|
|
return resampler.ProcessEvent(std::move(input));
|
|
}
|
|
|
|
void CheckTime(const TimeStamp& aTimeStamp,
|
|
const TimeStamp& aExpectedTimeStamp) {
|
|
EXPECT_EQ((aTimeStamp - baseTimeStamp).ToMilliseconds(),
|
|
(aExpectedTimeStamp - baseTimeStamp).ToMilliseconds());
|
|
}
|
|
|
|
void CheckEvent(const MultiTouchInput& aEvent,
|
|
MultiTouchInput::MultiTouchType aExpectedType,
|
|
std::initializer_list<std::pair<TimeStamp, ScreenIntPoint>>
|
|
aExpectedHistoricalData,
|
|
const TimeStamp& aExpectedTimeStamp,
|
|
const ScreenIntPoint& aExpectedPosition) {
|
|
EXPECT_EQ(aEvent.mType, aExpectedType);
|
|
EXPECT_EQ(aEvent.mTouches.Length(), size_t(1));
|
|
EXPECT_EQ(aEvent.mTouches[0].mHistoricalData.Length(),
|
|
aExpectedHistoricalData.size());
|
|
for (size_t i = 0; i < aExpectedHistoricalData.size(); i++) {
|
|
CheckTime(aEvent.mTouches[0].mHistoricalData[i].mTimeStamp,
|
|
aExpectedHistoricalData.begin()[i].first);
|
|
EXPECT_EQ(aEvent.mTouches[0].mHistoricalData[i].mScreenPoint,
|
|
aExpectedHistoricalData.begin()[i].second);
|
|
}
|
|
CheckTime(aEvent.mTimeStamp, aExpectedTimeStamp);
|
|
EXPECT_EQ(aEvent.mTouches[0].mScreenPoint, aExpectedPosition);
|
|
}
|
|
|
|
struct ExpectedOutgoingEvent {
|
|
Maybe<uint64_t> mEventId;
|
|
MultiTouchInput::MultiTouchType mType = MultiTouchInput::MULTITOUCH_START;
|
|
std::initializer_list<std::pair<TimeStamp, ScreenIntPoint>> mHistoricalData;
|
|
TimeStamp mTimeStamp;
|
|
ScreenIntPoint mPosition;
|
|
};
|
|
|
|
void CheckOutgoingEvents(
|
|
std::initializer_list<ExpectedOutgoingEvent> aExpectedEvents) {
|
|
auto outgoing = resampler.ConsumeOutgoingEvents();
|
|
EXPECT_EQ(outgoing.size(), aExpectedEvents.size());
|
|
for (const auto& expectedEvent : aExpectedEvents) {
|
|
auto outgoingEvent = std::move(outgoing.front());
|
|
outgoing.pop();
|
|
|
|
EXPECT_EQ(outgoingEvent.mEventId, expectedEvent.mEventId);
|
|
CheckEvent(outgoingEvent.mEvent, expectedEvent.mType,
|
|
expectedEvent.mHistoricalData, expectedEvent.mTimeStamp,
|
|
expectedEvent.mPosition);
|
|
}
|
|
}
|
|
|
|
TimeStamp baseTimeStamp;
|
|
TouchResampler resampler;
|
|
};
|
|
|
|
TEST_F(TouchResamplerTest, BasicExtrapolation) {
|
|
// Execute the following sequence:
|
|
//
|
|
// 0----------10-------16-----20---------------32------------
|
|
// * touchstart at (10, 10)
|
|
// * touchmove at (20, 20)
|
|
// * frame
|
|
// * touchend at (20, 20)
|
|
// * frame
|
|
//
|
|
// And expect the following output:
|
|
//
|
|
// 0----------10-------16-----20---------------32------------
|
|
// * touchstart at (10, 10)
|
|
// * touchmove at (26, 26)
|
|
// * touchmove at (20, 20)
|
|
// * touchend at (20, 20)
|
|
//
|
|
// The first frame should emit an extrapolated touchmove from the position
|
|
// data in the touchstart and touchmove events.
|
|
// The touchend should force a synthesized touchmove that returns back to a
|
|
// non-resampled position.
|
|
|
|
EXPECT_FALSE(resampler.InTouchingState());
|
|
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(10, 10));
|
|
EXPECT_TRUE(resampler.InTouchingState());
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE, {}, Time(10.0),
|
|
ScreenIntPoint(20, 20));
|
|
|
|
resampler.NotifyFrame(Time(16.0));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(10, 10)},
|
|
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(20, 20)}},
|
|
Time(16.0),
|
|
ScreenIntPoint(26, 26)},
|
|
});
|
|
|
|
auto idEnd2 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(20.0),
|
|
ScreenIntPoint(20, 20));
|
|
|
|
EXPECT_FALSE(resampler.InTouchingState());
|
|
|
|
CheckOutgoingEvents({
|
|
{Nothing(),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{},
|
|
Time(16.0),
|
|
ScreenIntPoint(20, 20)},
|
|
|
|
{Some(idEnd2),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(20.0),
|
|
ScreenIntPoint(20, 20)},
|
|
});
|
|
|
|
// No more events should be produced from here on out.
|
|
resampler.NotifyFrame(Time(32.0));
|
|
auto outgoing = resampler.ConsumeOutgoingEvents();
|
|
EXPECT_TRUE(outgoing.empty());
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, BasicInterpolation) {
|
|
// Same test as BasicExtrapolation, but with a frame time that's 10ms earlier.
|
|
//
|
|
// Execute the following sequence:
|
|
//
|
|
// 0------6---10-----------20--22------------30-------------
|
|
// * touchstart at (10, 10)
|
|
// * touchmove at (20, 20)
|
|
// * frame
|
|
// * touchend at (20, 20)
|
|
// * frame
|
|
//
|
|
// And expect the following output:
|
|
//
|
|
// 0------6---10-----------20--22------------30-------------
|
|
// * touchstart at (10, 10)
|
|
// * touchmove (16, 16)
|
|
// * touchmove (20, 20)
|
|
// * touchend at (20, 20)
|
|
//
|
|
// The first frame should emit an interpolated touchmove from the position
|
|
// data in the touchstart and touchmove events.
|
|
// The touchend should create a touchmove that returns back to a non-resampled
|
|
// position.
|
|
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(10, 10));
|
|
EXPECT_TRUE(resampler.InTouchingState());
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE, {}, Time(10.0),
|
|
ScreenIntPoint(20, 20));
|
|
|
|
resampler.NotifyFrame(Time(6.0));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(10, 10)},
|
|
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{},
|
|
Time(6.0),
|
|
ScreenIntPoint(16, 16)},
|
|
});
|
|
|
|
auto idEnd2 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(20.0),
|
|
ScreenIntPoint(20, 20));
|
|
EXPECT_FALSE(resampler.InTouchingState());
|
|
|
|
CheckOutgoingEvents({
|
|
{Nothing(),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{},
|
|
Time(10.0),
|
|
ScreenIntPoint(20, 20)},
|
|
|
|
{Some(idEnd2),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(20.0),
|
|
ScreenIntPoint(20, 20)},
|
|
});
|
|
|
|
// No more events should be produced from here on out.
|
|
resampler.NotifyFrame(Time(22.0));
|
|
auto outgoing = resampler.ConsumeOutgoingEvents();
|
|
EXPECT_TRUE(outgoing.empty());
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, InterpolationFromHistoricalData) {
|
|
// Interpolate from the historical data in a touch move event.
|
|
//
|
|
// Execute the following sequence:
|
|
//
|
|
// 0----------10-------16-----20-----------30--32------------
|
|
// * touchstart at (10, 10)
|
|
// * [hist] at (20, 25) for
|
|
// `---------------* touchmove at (30, 30)
|
|
// * frame
|
|
// * touchend at (30, 30)
|
|
// * frame
|
|
//
|
|
// And expect the following output:
|
|
//
|
|
// 0----------10-------16-----20-----------30--32------------
|
|
// * touchstart at (10, 10)
|
|
// * [hist] at (20, 25) for
|
|
// `--------* touchmove at (26, 28)
|
|
// * touchmove at (30, 30)
|
|
// * touchend at (30, 30)
|
|
//
|
|
// The first frame should emit an interpolated touchmove from the position
|
|
// data in the touchmove event, and integrate the historical data point into
|
|
// the resampled event.
|
|
// The touchend should force a synthesized touchmove that returns back to a
|
|
// non-resampled position.
|
|
|
|
// This also tests that interpolation works for both x and y, by giving the
|
|
// historical datapoint different values for x and y.
|
|
// (26, 28) is 60% of the way from (20, 25) to (30, 30).
|
|
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(10, 10));
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(20, 25)}},
|
|
Time(20.0), ScreenIntPoint(30, 30));
|
|
resampler.NotifyFrame(Time(16.0));
|
|
auto idEnd2 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(30.0),
|
|
ScreenIntPoint(30, 30));
|
|
resampler.NotifyFrame(Time(32.0));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(10, 10)},
|
|
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(20, 25)}},
|
|
Time(16.0),
|
|
ScreenIntPoint(26, 28)},
|
|
|
|
{Nothing(),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{},
|
|
Time(20.0),
|
|
ScreenIntPoint(30, 30)},
|
|
|
|
{Some(idEnd2),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(30.0),
|
|
ScreenIntPoint(30, 30)},
|
|
});
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, MultipleTouches) {
|
|
EXPECT_FALSE(resampler.InTouchingState());
|
|
|
|
// Touch start
|
|
MultiTouchInput inputStart0(MultiTouchInput::MULTITOUCH_START, 0, Time(0.0),
|
|
0);
|
|
inputStart0.mTouches.AppendElement(
|
|
SingleTouchData(1, ScreenIntPoint(10, 10), {}, 0.0f, 0.0f));
|
|
auto idStart0 = resampler.ProcessEvent(std::move(inputStart0));
|
|
EXPECT_TRUE(resampler.InTouchingState());
|
|
|
|
// Touch move
|
|
MultiTouchInput inputMove1(MultiTouchInput::MULTITOUCH_MOVE, 0, Time(20.0),
|
|
0);
|
|
inputMove1.mTouches.AppendElement(
|
|
SingleTouchData(1, ScreenIntPoint(30, 30), {}, 0.0f, 0.0f));
|
|
inputMove1.mTouches[0].mHistoricalData.AppendElement(
|
|
SingleTouchData::HistoricalTouchData{
|
|
Time(10.0), ScreenIntPoint(20, 25), {}, {}, 0.0f, 0.0f});
|
|
auto idMove1 = resampler.ProcessEvent(std::move(inputMove1));
|
|
EXPECT_TRUE(resampler.InTouchingState());
|
|
|
|
// Frame
|
|
resampler.NotifyFrame(Time(16.0));
|
|
|
|
// Touch move
|
|
MultiTouchInput inputMove2(MultiTouchInput::MULTITOUCH_MOVE, 0, Time(30.0),
|
|
0);
|
|
inputMove2.mTouches.AppendElement(
|
|
SingleTouchData(1, ScreenIntPoint(30, 40), {}, 0.0f, 0.0f));
|
|
auto idMove2 = resampler.ProcessEvent(std::move(inputMove2));
|
|
EXPECT_TRUE(resampler.InTouchingState());
|
|
|
|
// Touch start
|
|
MultiTouchInput inputStart3(MultiTouchInput::MULTITOUCH_START, 0, Time(30.0),
|
|
0);
|
|
inputStart3.mTouches.AppendElement(
|
|
SingleTouchData(2, ScreenIntPoint(100, 10), {}, 0.0f, 0.0f));
|
|
inputStart3.mTouches.AppendElement(
|
|
SingleTouchData(1, ScreenIntPoint(30, 40), {}, 0.0f, 0.0f));
|
|
auto idStart3 = resampler.ProcessEvent(std::move(inputStart3));
|
|
EXPECT_TRUE(resampler.InTouchingState());
|
|
|
|
// Touch move
|
|
MultiTouchInput inputMove4(MultiTouchInput::MULTITOUCH_MOVE, 0, Time(40.0),
|
|
0);
|
|
inputMove4.mTouches.AppendElement(
|
|
SingleTouchData(1, ScreenIntPoint(30, 50), {}, 0.0f, 0.0f));
|
|
inputMove4.mTouches.AppendElement(
|
|
SingleTouchData(2, ScreenIntPoint(100, 30), {}, 0.0f, 0.0f));
|
|
auto idMove4 = resampler.ProcessEvent(std::move(inputMove4));
|
|
EXPECT_TRUE(resampler.InTouchingState());
|
|
|
|
// Frame
|
|
resampler.NotifyFrame(Time(32.0));
|
|
|
|
// Touch move
|
|
MultiTouchInput inputMove5(MultiTouchInput::MULTITOUCH_MOVE, 0, Time(50.0),
|
|
0);
|
|
inputMove5.mTouches.AppendElement(
|
|
SingleTouchData(1, ScreenIntPoint(30, 60), {}, 0.0f, 0.0f));
|
|
inputMove5.mTouches.AppendElement(
|
|
SingleTouchData(2, ScreenIntPoint(100, 40), {}, 0.0f, 0.0f));
|
|
auto idMove5 = resampler.ProcessEvent(std::move(inputMove5));
|
|
EXPECT_TRUE(resampler.InTouchingState());
|
|
|
|
// Touch end
|
|
MultiTouchInput inputEnd6(MultiTouchInput::MULTITOUCH_END, 0, Time(50.0), 0);
|
|
// Touch point with identifier 1 is lifted
|
|
inputEnd6.mTouches.AppendElement(
|
|
SingleTouchData(1, ScreenIntPoint(30, 60), {}, 0.0f, 0.0f));
|
|
auto idEnd6 = resampler.ProcessEvent(std::move(inputEnd6));
|
|
EXPECT_TRUE(resampler.InTouchingState());
|
|
|
|
// Frame
|
|
resampler.NotifyFrame(Time(48.0));
|
|
|
|
// Touch move
|
|
MultiTouchInput inputMove7(MultiTouchInput::MULTITOUCH_MOVE, 0, Time(60.0),
|
|
0);
|
|
inputMove7.mTouches.AppendElement(
|
|
SingleTouchData(2, ScreenIntPoint(100, 60), {}, 0.0f, 0.0f));
|
|
auto idMove7 = resampler.ProcessEvent(std::move(inputMove7));
|
|
EXPECT_TRUE(resampler.InTouchingState());
|
|
|
|
// Frame
|
|
resampler.NotifyFrame(Time(64.0));
|
|
|
|
// Touch end
|
|
MultiTouchInput inputEnd8(MultiTouchInput::MULTITOUCH_END, 0, Time(70.0), 0);
|
|
// Touch point with identifier 2 is lifted
|
|
inputEnd8.mTouches.AppendElement(
|
|
SingleTouchData(2, ScreenIntPoint(100, 60), {}, 0.0f, 0.0f));
|
|
auto idEnd8 = resampler.ProcessEvent(std::move(inputEnd8));
|
|
EXPECT_FALSE(resampler.InTouchingState());
|
|
|
|
// Check outgoing events
|
|
auto outgoing = resampler.ConsumeOutgoingEvents();
|
|
EXPECT_EQ(outgoing.size(), size_t(9));
|
|
|
|
auto outgoingStart0 = std::move(outgoing.front());
|
|
outgoing.pop();
|
|
EXPECT_EQ(outgoingStart0.mEventId, Some(idStart0));
|
|
CheckEvent(outgoingStart0.mEvent, MultiTouchInput::MULTITOUCH_START, {},
|
|
Time(0.0), ScreenIntPoint(10, 10));
|
|
|
|
auto outgoingMove1 = std::move(outgoing.front());
|
|
outgoing.pop();
|
|
EXPECT_EQ(outgoingMove1.mEventId, Some(idMove1));
|
|
// (26, 28) is 60% of the way from (20, 25) to (30, 30).
|
|
CheckEvent(outgoingMove1.mEvent, MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(20, 25)}}, Time(16.0),
|
|
ScreenIntPoint(26, 28));
|
|
|
|
auto outgoingMove2 = std::move(outgoing.front());
|
|
outgoing.pop();
|
|
EXPECT_EQ(outgoingMove2.mEventId, Some(idMove2));
|
|
CheckEvent(outgoingMove2.mEvent, MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(20.0), ScreenIntPoint(30, 30)}}, Time(30.0),
|
|
ScreenIntPoint(30, 40));
|
|
|
|
auto outgoingStart3 = std::move(outgoing.front());
|
|
outgoing.pop();
|
|
EXPECT_EQ(outgoingStart3.mEventId, Some(idStart3));
|
|
EXPECT_EQ(outgoingStart3.mEvent.mType, MultiTouchInput::MULTITOUCH_START);
|
|
CheckTime(outgoingStart3.mEvent.mTimeStamp, Time(30.0));
|
|
EXPECT_EQ(outgoingStart3.mEvent.mTouches.Length(), size_t(2));
|
|
// touch order should be taken from the original touch start event
|
|
EXPECT_EQ(outgoingStart3.mEvent.mTouches[0].mIdentifier, 2);
|
|
EXPECT_EQ(outgoingStart3.mEvent.mTouches[0].mScreenPoint,
|
|
ScreenIntPoint(100, 10));
|
|
EXPECT_EQ(outgoingStart3.mEvent.mTouches[1].mIdentifier, 1);
|
|
EXPECT_EQ(outgoingStart3.mEvent.mTouches[1].mScreenPoint,
|
|
ScreenIntPoint(30, 40));
|
|
|
|
auto outgoingMove4 = std::move(outgoing.front());
|
|
outgoing.pop();
|
|
EXPECT_EQ(outgoingMove4.mEventId, Some(idMove4));
|
|
EXPECT_EQ(outgoingMove4.mEvent.mType, MultiTouchInput::MULTITOUCH_MOVE);
|
|
CheckTime(outgoingMove4.mEvent.mTimeStamp, Time(32.0));
|
|
EXPECT_EQ(outgoingMove4.mEvent.mTouches.Length(), size_t(2));
|
|
// Touch order should be taken from the original touch move event.
|
|
// Both touches should be resampled.
|
|
EXPECT_EQ(outgoingMove4.mEvent.mTouches[0].mIdentifier, 1);
|
|
EXPECT_EQ(outgoingMove4.mEvent.mTouches[0].mScreenPoint,
|
|
ScreenIntPoint(30, 42));
|
|
EXPECT_EQ(outgoingMove4.mEvent.mTouches[1].mIdentifier, 2);
|
|
EXPECT_EQ(outgoingMove4.mEvent.mTouches[1].mScreenPoint,
|
|
ScreenIntPoint(100, 14));
|
|
|
|
auto outgoingMove5 = std::move(outgoing.front());
|
|
outgoing.pop();
|
|
EXPECT_EQ(outgoingMove5.mEventId, Some(idMove5));
|
|
EXPECT_EQ(outgoingMove5.mEvent.mType, MultiTouchInput::MULTITOUCH_MOVE);
|
|
CheckTime(outgoingMove5.mEvent.mTimeStamp, Time(50.0));
|
|
EXPECT_EQ(outgoingMove5.mEvent.mTouches.Length(), size_t(2));
|
|
// touch order should be taken from the original touch move event
|
|
EXPECT_EQ(outgoingMove5.mEvent.mTouches[0].mIdentifier, 1);
|
|
EXPECT_EQ(outgoingMove5.mEvent.mTouches[0].mScreenPoint,
|
|
ScreenIntPoint(30, 60));
|
|
EXPECT_EQ(outgoingMove5.mEvent.mTouches[0].mHistoricalData.Length(),
|
|
size_t(1));
|
|
CheckTime(outgoingMove5.mEvent.mTouches[0].mHistoricalData[0].mTimeStamp,
|
|
Time(40.0));
|
|
EXPECT_EQ(outgoingMove5.mEvent.mTouches[0].mHistoricalData[0].mScreenPoint,
|
|
ScreenIntPoint(30, 50));
|
|
EXPECT_EQ(outgoingMove5.mEvent.mTouches[1].mIdentifier, 2);
|
|
EXPECT_EQ(outgoingMove5.mEvent.mTouches[1].mScreenPoint,
|
|
ScreenIntPoint(100, 40));
|
|
EXPECT_EQ(outgoingMove5.mEvent.mTouches[1].mHistoricalData.Length(),
|
|
size_t(1));
|
|
CheckTime(outgoingMove5.mEvent.mTouches[1].mHistoricalData[0].mTimeStamp,
|
|
Time(40.0));
|
|
EXPECT_EQ(outgoingMove5.mEvent.mTouches[1].mHistoricalData[0].mScreenPoint,
|
|
ScreenIntPoint(100, 30));
|
|
|
|
auto outgoingEnd6 = std::move(outgoing.front());
|
|
outgoing.pop();
|
|
EXPECT_EQ(outgoingEnd6.mEventId, Some(idEnd6));
|
|
CheckEvent(outgoingEnd6.mEvent, MultiTouchInput::MULTITOUCH_END, {},
|
|
Time(50.0), ScreenIntPoint(30, 60));
|
|
|
|
auto outgoingMove7 = std::move(outgoing.front());
|
|
outgoing.pop();
|
|
EXPECT_EQ(outgoingMove7.mEventId, Some(idMove7));
|
|
// No extrapolation because the frame at 64.0 cleared the data points because
|
|
// there was no pending touch move event at that point
|
|
CheckEvent(outgoingMove7.mEvent, MultiTouchInput::MULTITOUCH_MOVE, {},
|
|
Time(60.0), ScreenIntPoint(100, 60));
|
|
EXPECT_EQ(outgoingMove7.mEvent.mTouches[0].mIdentifier, 2);
|
|
|
|
auto outgoingEnd8 = std::move(outgoing.front());
|
|
outgoing.pop();
|
|
EXPECT_EQ(outgoingEnd8.mEventId, Some(idEnd8));
|
|
CheckEvent(outgoingEnd8.mEvent, MultiTouchInput::MULTITOUCH_END, {},
|
|
Time(70.0), ScreenIntPoint(100, 60));
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, MovingPauses) {
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(10, 10));
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE, {}, Time(10.0),
|
|
ScreenIntPoint(20, 20));
|
|
resampler.NotifyFrame(Time(16.0));
|
|
auto idMove2 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE, {}, Time(30.0),
|
|
ScreenIntPoint(40, 40));
|
|
resampler.NotifyFrame(Time(32.0));
|
|
auto idMove3 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE, {}, Time(40.0),
|
|
ScreenIntPoint(50, 40));
|
|
resampler.NotifyFrame(Time(48.0));
|
|
resampler.NotifyFrame(Time(64.0));
|
|
auto idEnd4 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(70.0),
|
|
ScreenIntPoint(50, 40));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(10, 10)},
|
|
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(20, 20)}},
|
|
Time(16.0),
|
|
ScreenIntPoint(26, 26)},
|
|
|
|
{Some(idMove2),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(30.0), ScreenIntPoint(40, 40)}},
|
|
Time(32.0),
|
|
ScreenIntPoint(42, 42)},
|
|
|
|
{Some(idMove3),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(40.0), ScreenIntPoint(50, 40)}},
|
|
Time(48.0),
|
|
ScreenIntPoint(58, 40)},
|
|
|
|
// There was no event between two frames here, so we expect a reset event,
|
|
// so that we pause at a non-resampled position.
|
|
{Nothing(),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{},
|
|
Time(48.0),
|
|
ScreenIntPoint(50, 40)},
|
|
|
|
{Some(idEnd4),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(70.0),
|
|
ScreenIntPoint(50, 40)},
|
|
});
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, MixedInterAndExtrapolation) {
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(0, 0));
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE, {}, Time(10.0),
|
|
ScreenIntPoint(0, 10));
|
|
resampler.NotifyFrame(Time(11.0)); // 16 - 5
|
|
auto idMove2 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(20.0), ScreenIntPoint(0, 20)}}, Time(30.0),
|
|
ScreenIntPoint(0, 30));
|
|
resampler.NotifyFrame(Time(27.0)); // 32 - 5
|
|
auto idMove3 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE, {}, Time(40.0),
|
|
ScreenIntPoint(0, 40));
|
|
resampler.NotifyFrame(Time(43.0)); // 48 - 5
|
|
auto idMove4 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(50.0), ScreenIntPoint(0, 50)}}, Time(60.0),
|
|
ScreenIntPoint(0, 60));
|
|
resampler.NotifyFrame(Time(59.0)); // 64 - 5
|
|
auto idEnd5 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(70.0),
|
|
ScreenIntPoint(0, 60));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(0, 0)},
|
|
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(0, 10)}},
|
|
Time(11.0),
|
|
ScreenIntPoint(0, 11)},
|
|
|
|
{Some(idMove2),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(20.0), ScreenIntPoint(0, 20)}},
|
|
Time(27.0),
|
|
ScreenIntPoint(0, 27)},
|
|
|
|
{Some(idMove3),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(30.0), ScreenIntPoint(0, 30)},
|
|
{Time(40.0), ScreenIntPoint(0, 40)}},
|
|
Time(43.0),
|
|
ScreenIntPoint(0, 43)},
|
|
|
|
{Some(idMove4),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(50.0), ScreenIntPoint(0, 50)}},
|
|
Time(59.0),
|
|
ScreenIntPoint(0, 59)},
|
|
|
|
{Nothing(),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{},
|
|
Time(60.0),
|
|
ScreenIntPoint(0, 60)},
|
|
|
|
{Some(idEnd5),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(70.0),
|
|
ScreenIntPoint(0, 60)},
|
|
});
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, MultipleMoveEvents) {
|
|
// Test what happens if multiple touch move events appear between two frames.
|
|
// This scenario shouldn't occur on Android but we should be able to deal with
|
|
// it anyway. Check that we don't discard any event IDs.
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(0, 0));
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE, {}, Time(10.0),
|
|
ScreenIntPoint(0, 10));
|
|
resampler.NotifyFrame(Time(11.0)); // 16 - 5
|
|
auto idMove2 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(20.0), ScreenIntPoint(0, 20)}}, Time(30.0),
|
|
ScreenIntPoint(0, 30));
|
|
auto idMove3 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE, {}, Time(40.0),
|
|
ScreenIntPoint(0, 40));
|
|
auto idMove4 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(45.0), ScreenIntPoint(0, 45)}}, Time(50.0),
|
|
ScreenIntPoint(0, 50));
|
|
auto idMove5 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE, {}, Time(55.0),
|
|
ScreenIntPoint(0, 55));
|
|
resampler.NotifyFrame(Time(43.0)); // 48 - 5
|
|
resampler.NotifyFrame(Time(59.0)); // 64 - 5
|
|
auto idEnd5 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(70.0),
|
|
ScreenIntPoint(0, 60));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(0, 0)},
|
|
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(0, 10)}},
|
|
Time(11.0),
|
|
ScreenIntPoint(0, 11)},
|
|
|
|
{Some(idMove2),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(20.0), ScreenIntPoint(0, 20)}},
|
|
Time(30.0),
|
|
ScreenIntPoint(0, 30)},
|
|
|
|
{Some(idMove3),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{},
|
|
Time(40.0),
|
|
ScreenIntPoint(0, 40)},
|
|
|
|
{Some(idMove4),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{},
|
|
Time(43.0),
|
|
ScreenIntPoint(0, 43)},
|
|
|
|
{Some(idMove5),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(45.0), ScreenIntPoint(0, 45)},
|
|
{Time(50.0), ScreenIntPoint(0, 50)},
|
|
{Time(55.0), ScreenIntPoint(0, 55)}},
|
|
Time(59.0),
|
|
ScreenIntPoint(0, 59)},
|
|
|
|
{Nothing(),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{},
|
|
Time(59.0),
|
|
ScreenIntPoint(0, 55)},
|
|
|
|
{Some(idEnd5),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(70.0),
|
|
ScreenIntPoint(0, 60)},
|
|
});
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, LimitFuturePrediction) {
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(0, 0));
|
|
// Fingers move until time 44, then pause. UI thread is occupied until 64.
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(20.0), ScreenIntPoint(0, 20)},
|
|
{Time(32.0), ScreenIntPoint(0, 32)}},
|
|
Time(44.0), ScreenIntPoint(0, 44));
|
|
resampler.NotifyFrame(Time(59.0)); // 64 - 5
|
|
auto idEnd2 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(70.0),
|
|
ScreenIntPoint(0, 44));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(0, 0)},
|
|
|
|
// kTouchResampleMaxPredictMs == 8
|
|
// Refuse to predict more than 8ms into the future, the fingers might have
|
|
// paused. Make an event for time 52 (= 44 + 8) instead of 59.
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(20.0), ScreenIntPoint(0, 20)},
|
|
{Time(32.0), ScreenIntPoint(0, 32)},
|
|
{Time(44.0), ScreenIntPoint(0, 44)}},
|
|
Time(52.0),
|
|
ScreenIntPoint(0, 52)},
|
|
|
|
{Nothing(),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{},
|
|
Time(52.0),
|
|
ScreenIntPoint(0, 44)},
|
|
|
|
{Some(idEnd2),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(70.0),
|
|
ScreenIntPoint(0, 44)},
|
|
});
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, LimitBacksampling) {
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(0, 0));
|
|
// Fingers move until time 44, then pause. UI thread is occupied until 64.
|
|
// Then we get a frame callback with a very outdated frametime.
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(20.0), ScreenIntPoint(0, 20)},
|
|
{Time(32.0), ScreenIntPoint(0, 32)}},
|
|
Time(44.0), ScreenIntPoint(0, 44));
|
|
resampler.NotifyFrame(Time(11.0)); // 16 - 5
|
|
auto idEnd2 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(70.0),
|
|
ScreenIntPoint(0, 44));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(0, 0)},
|
|
|
|
// kTouchResampleMaxBacksampleMs == 20
|
|
// Refuse to sample further back than 20ms before the last data point.
|
|
// Make an event for time 24 (= 44 - 20) instead of time 11.
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(20.0), ScreenIntPoint(0, 20)}},
|
|
Time(24.0),
|
|
ScreenIntPoint(0, 24)},
|
|
|
|
{Nothing(),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(32.0), ScreenIntPoint(0, 32)}},
|
|
Time(44.0),
|
|
ScreenIntPoint(0, 44)},
|
|
|
|
{Some(idEnd2),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(70.0),
|
|
ScreenIntPoint(0, 44)},
|
|
});
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, DontExtrapolateFromOldTouch) {
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(0, 0));
|
|
// Fingers move until time 40, then pause. UI thread is occupied until 64.
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(20.0), ScreenIntPoint(0, 20)},
|
|
{Time(30.0), ScreenIntPoint(0, 30)}},
|
|
Time(40.0), ScreenIntPoint(0, 40));
|
|
resampler.NotifyFrame(Time(59.0)); // 64 - 5
|
|
auto idEnd2 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(70.0),
|
|
ScreenIntPoint(0, 44));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(0, 0)},
|
|
|
|
// kTouchResampleOldTouchThresholdMs == 17
|
|
// Refuse to extrapolate from a data point that's more than 17ms older
|
|
// than the frame time.
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(20.0), ScreenIntPoint(0, 20)},
|
|
{Time(30.0), ScreenIntPoint(0, 30)}},
|
|
Time(40.0),
|
|
ScreenIntPoint(0, 40)},
|
|
|
|
{Some(idEnd2),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(70.0),
|
|
ScreenIntPoint(0, 44)},
|
|
});
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, DontExtrapolateIfTooOld) {
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(0, 0));
|
|
// Fingers move until time 10, pause, and move again at 55.
|
|
// UI thread is occupied until 64.
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(0, 10)}}, Time(55.0),
|
|
ScreenIntPoint(0, 55));
|
|
resampler.NotifyFrame(Time(59.0)); // 64 - 5
|
|
auto idEnd2 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(70.0),
|
|
ScreenIntPoint(0, 60));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(0, 0)},
|
|
|
|
// kTouchResampleWindowSize == 40
|
|
// Refuse to resample between two data points that are more than 40ms
|
|
// apart.
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(0, 10)}},
|
|
Time(55.0),
|
|
ScreenIntPoint(0, 55)},
|
|
|
|
{Some(idEnd2),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(70.0),
|
|
ScreenIntPoint(0, 60)},
|
|
});
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, DontInterpolateIfTooOld) {
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(0, 0));
|
|
// Fingers move until time 10, pause, and move again at 60.
|
|
// UI thread is occupied until 64.
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(0, 10)}}, Time(60.0),
|
|
ScreenIntPoint(0, 60));
|
|
resampler.NotifyFrame(Time(59.0)); // 64 - 5
|
|
auto idEnd2 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(70.0),
|
|
ScreenIntPoint(0, 60));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(0, 0)},
|
|
|
|
// kTouchResampleWindowSize == 40
|
|
// Refuse to resample between two data points that are more than 40ms
|
|
// apart.
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(0, 10)}},
|
|
Time(60.0),
|
|
ScreenIntPoint(0, 60)},
|
|
|
|
{Some(idEnd2),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(70.0),
|
|
ScreenIntPoint(0, 60)},
|
|
});
|
|
}
|
|
|
|
TEST_F(TouchResamplerTest, DiscardOutdatedHistoricalData) {
|
|
auto idStart0 = ProcessEvent(MultiTouchInput::MULTITOUCH_START, {}, Time(0.0),
|
|
ScreenIntPoint(0, 0));
|
|
auto idMove1 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(0, 10)}}, Time(16.0),
|
|
ScreenIntPoint(0, 16));
|
|
resampler.NotifyFrame(Time(20.0));
|
|
auto idMove2 = ProcessEvent(MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(18.0), ScreenIntPoint(0, 18)}}, Time(25.0),
|
|
ScreenIntPoint(0, 25));
|
|
auto idEnd3 = ProcessEvent(MultiTouchInput::MULTITOUCH_END, {}, Time(35.0),
|
|
ScreenIntPoint(0, 25));
|
|
|
|
CheckOutgoingEvents({
|
|
{Some(idStart0),
|
|
MultiTouchInput::MULTITOUCH_START,
|
|
{},
|
|
Time(0.0),
|
|
ScreenIntPoint(0, 0)},
|
|
|
|
{Some(idMove1),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{{Time(10.0), ScreenIntPoint(0, 10)},
|
|
{Time(16.0), ScreenIntPoint(0, 16)}},
|
|
Time(20.0),
|
|
ScreenIntPoint(0, 20)},
|
|
|
|
// Discard the historical data point from time 18, because we've already
|
|
// sent out an event with time 20 and don't want to go back before that.
|
|
{Some(idMove2),
|
|
MultiTouchInput::MULTITOUCH_MOVE,
|
|
{},
|
|
Time(25.0),
|
|
ScreenIntPoint(0, 25)},
|
|
|
|
{Some(idEnd3),
|
|
MultiTouchInput::MULTITOUCH_END,
|
|
{},
|
|
Time(35.0),
|
|
ScreenIntPoint(0, 25)},
|
|
});
|
|
}
|