blob: 290a97d73668bbfbdbfc7fc85f31012eceb81415 [file] [log] [blame]
/**
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <chrono>
#include <ostream>
#include <vector>
#include <android-base/logging.h>
#include <gtest/gtest.h>
#include <input/Input.h>
namespace android {
namespace {
using ::testing::Matcher;
} // namespace
/**
* This file contains a copy of Matchers from .../inputflinger/tests/TestEventMatchers.h. Ideally,
* implementations must not be duplicated.
* TODO(b/365606513): Find a way to share TestEventMatchers.h between inputflinger and libinput.
*/
struct PointerArgs {
float x{0.0f};
float y{0.0f};
bool isResampled{false};
};
struct Sample {
std::chrono::nanoseconds eventTime{0};
std::vector<PointerArgs> pointers{};
};
class WithDeviceIdMatcher {
public:
using is_gtest_matcher = void;
explicit WithDeviceIdMatcher(DeviceId deviceId) : mDeviceId(deviceId) {}
bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
return mDeviceId == event.getDeviceId();
}
void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
private:
const DeviceId mDeviceId;
};
inline WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
return WithDeviceIdMatcher(deviceId);
}
class WithMotionActionMatcher {
public:
using is_gtest_matcher = void;
explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
bool MatchAndExplain(const MotionEvent& event, testing::MatchResultListener* listener) const {
if (mAction != event.getAction()) {
*listener << "expected " << MotionEvent::actionToString(mAction) << ", but got "
<< MotionEvent::actionToString(event.getAction());
return false;
}
if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL &&
(event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) == 0) {
*listener << "event with CANCEL action is missing FLAG_CANCELED";
return false;
}
return true;
}
void DescribeTo(std::ostream* os) const {
*os << "with motion action " << MotionEvent::actionToString(mAction);
if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
*os << " and FLAG_CANCELED";
}
}
void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
private:
const int32_t mAction;
};
inline WithMotionActionMatcher WithMotionAction(int32_t action) {
return WithMotionActionMatcher(action);
}
class WithSampleCountMatcher {
public:
using is_gtest_matcher = void;
explicit WithSampleCountMatcher(size_t sampleCount) : mExpectedSampleCount{sampleCount} {}
bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream*) const {
return (motionEvent.getHistorySize() + 1) == mExpectedSampleCount;
}
void DescribeTo(std::ostream* os) const { *os << "sample count " << mExpectedSampleCount; }
void DescribeNegationTo(std::ostream* os) const { *os << "different sample count"; }
private:
const size_t mExpectedSampleCount;
};
inline WithSampleCountMatcher WithSampleCount(size_t sampleCount) {
return WithSampleCountMatcher(sampleCount);
}
class WithSampleMatcher {
public:
using is_gtest_matcher = void;
explicit WithSampleMatcher(size_t sampleIndex, const Sample& sample)
: mSampleIndex{sampleIndex}, mSample{sample} {}
bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream* os) const {
if (motionEvent.getHistorySize() < mSampleIndex) {
*os << "sample index out of bounds";
return false;
}
if (motionEvent.getHistoricalEventTime(mSampleIndex) != mSample.eventTime.count()) {
*os << "event time mismatch. sample: "
<< motionEvent.getHistoricalEventTime(mSampleIndex)
<< " expected: " << mSample.eventTime.count();
return false;
}
if (motionEvent.getPointerCount() != mSample.pointers.size()) {
*os << "pointer count mismatch. sample: " << motionEvent.getPointerCount()
<< " expected: " << mSample.pointers.size();
return false;
}
for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
++pointerIndex) {
const PointerCoords& pointerCoords =
*(motionEvent.getHistoricalRawPointerCoords(pointerIndex, mSampleIndex));
if ((pointerCoords.getX() != mSample.pointers[pointerIndex].x) ||
(pointerCoords.getY() != mSample.pointers[pointerIndex].y)) {
*os << "sample coordinates mismatch at pointer index " << pointerIndex
<< ". sample: (" << pointerCoords.getX() << ", " << pointerCoords.getY()
<< ") expected: (" << mSample.pointers[pointerIndex].x << ", "
<< mSample.pointers[pointerIndex].y << ")";
return false;
}
if (motionEvent.isResampled(pointerIndex, mSampleIndex) !=
mSample.pointers[pointerIndex].isResampled) {
*os << "resampling flag mismatch. sample: "
<< motionEvent.isResampled(pointerIndex, mSampleIndex)
<< " expected: " << mSample.pointers[pointerIndex].isResampled;
return false;
}
}
return true;
}
void DescribeTo(std::ostream* os) const { *os << "motion event sample properties match."; }
void DescribeNegationTo(std::ostream* os) const {
*os << "motion event sample properties do not match expected properties.";
}
private:
const size_t mSampleIndex;
const Sample mSample;
};
inline WithSampleMatcher WithSample(size_t sampleIndex, const Sample& sample) {
return WithSampleMatcher(sampleIndex, sample);
}
} // namespace android