blob: 66fdaa452028c466096e53fc66fc6558f450db12 [file] [log] [blame]
/*
* Copyright (C) 2022 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 <cmath>
#include <compare>
#include <android-base/stringprintf.h>
#include <android/input.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/Input.h>
#include <input/PrintTools.h>
#include "NotifyArgs.h"
#include "TestConstants.h"
namespace android {
struct PointF {
float x;
float y;
auto operator<=>(const PointF&) const = default;
};
inline std::string pointFToString(const PointF& p) {
return std::string("(") + std::to_string(p.x) + ", " + std::to_string(p.y) + ")";
}
/// Source
class WithSourceMatcher {
public:
using is_gtest_matcher = void;
explicit WithSourceMatcher(uint32_t source) : mSource(source) {}
bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
return mSource == args.source;
}
bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
return mSource == args.source;
}
bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
return mSource == event.getSource();
}
void DescribeTo(std::ostream* os) const {
*os << "with source " << inputEventSourceToString(mSource);
}
void DescribeNegationTo(std::ostream* os) const { *os << "wrong source"; }
private:
const uint32_t mSource;
};
inline WithSourceMatcher WithSource(uint32_t source) {
return WithSourceMatcher(source);
}
/// Key action
class WithKeyActionMatcher {
public:
using is_gtest_matcher = void;
explicit WithKeyActionMatcher(int32_t action) : mAction(action) {}
bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
return mAction == args.action;
}
bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
return mAction == event.getAction();
}
void DescribeTo(std::ostream* os) const {
*os << "with key action " << KeyEvent::actionToString(mAction);
}
void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
private:
const int32_t mAction;
};
inline WithKeyActionMatcher WithKeyAction(int32_t action) {
return WithKeyActionMatcher(action);
}
/// Motion action
class WithMotionActionMatcher {
public:
using is_gtest_matcher = void;
explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
bool matches = mAction == args.action;
if (args.action == AMOTION_EVENT_ACTION_CANCEL) {
matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
}
return matches;
}
bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
bool matches = mAction == event.getAction();
if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
}
return matches;
}
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);
}
/// Display Id
class WithDisplayIdMatcher {
public:
using is_gtest_matcher = void;
explicit WithDisplayIdMatcher(int32_t displayId) : mDisplayId(displayId) {}
bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
return mDisplayId == args.displayId;
}
bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
return mDisplayId == args.displayId;
}
bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
return mDisplayId == event.getDisplayId();
}
void DescribeTo(std::ostream* os) const { *os << "with display id " << mDisplayId; }
void DescribeNegationTo(std::ostream* os) const { *os << "wrong display id"; }
private:
const int32_t mDisplayId;
};
inline WithDisplayIdMatcher WithDisplayId(int32_t displayId) {
return WithDisplayIdMatcher(displayId);
}
/// Device Id
class WithDeviceIdMatcher {
public:
using is_gtest_matcher = void;
explicit WithDeviceIdMatcher(int32_t deviceId) : mDeviceId(deviceId) {}
bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
return mDeviceId == args.deviceId;
}
bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
return mDeviceId == args.deviceId;
}
bool MatchAndExplain(const NotifyDeviceResetArgs& args, std::ostream*) const {
return mDeviceId == args.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 int32_t mDeviceId;
};
inline WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
return WithDeviceIdMatcher(deviceId);
}
/// Flags
class WithFlagsMatcher {
public:
using is_gtest_matcher = void;
explicit WithFlagsMatcher(int32_t flags) : mFlags(flags) {}
bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
return mFlags == args.flags;
}
bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
return mFlags == args.flags;
}
bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
return mFlags == event.getFlags();
}
bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
return mFlags == event.getFlags();
}
void DescribeTo(std::ostream* os) const {
*os << "with flags " << base::StringPrintf("0x%x", mFlags);
}
void DescribeNegationTo(std::ostream* os) const { *os << "wrong flags"; }
private:
const int32_t mFlags;
};
inline WithFlagsMatcher WithFlags(int32_t flags) {
return WithFlagsMatcher(flags);
}
/// DownTime
class WithDownTimeMatcher {
public:
using is_gtest_matcher = void;
explicit WithDownTimeMatcher(nsecs_t downTime) : mDownTime(downTime) {}
bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
return mDownTime == args.downTime;
}
bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
return mDownTime == args.downTime;
}
bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
return mDownTime == event.getDownTime();
}
bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
return mDownTime == event.getDownTime();
}
void DescribeTo(std::ostream* os) const { *os << "with down time " << mDownTime; }
void DescribeNegationTo(std::ostream* os) const { *os << "wrong down time"; }
private:
const nsecs_t mDownTime;
};
inline WithDownTimeMatcher WithDownTime(nsecs_t downTime) {
return WithDownTimeMatcher(downTime);
}
/// Coordinate matcher
class WithCoordsMatcher {
public:
using is_gtest_matcher = void;
explicit WithCoordsMatcher(size_t pointerIndex, float x, float y)
: mPointerIndex(pointerIndex), mX(x), mY(y) {}
bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
if (mPointerIndex >= event.getPointerCount()) {
*os << "Pointer index " << mPointerIndex << " is out of bounds";
return false;
}
bool matches = mX == event.getX(mPointerIndex) && mY == event.getY(mPointerIndex);
if (!matches) {
*os << "expected coords (" << mX << ", " << mY << ") at pointer index " << mPointerIndex
<< ", but got (" << event.getX(mPointerIndex) << ", " << event.getY(mPointerIndex)
<< ")";
}
return matches;
}
bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
if (mPointerIndex >= event.pointerCoords.size()) {
*os << "Pointer index " << mPointerIndex << " is out of bounds";
return false;
}
bool matches = mX == event.pointerCoords[mPointerIndex].getX() &&
mY == event.pointerCoords[mPointerIndex].getY();
if (!matches) {
*os << "expected coords (" << mX << ", " << mY << ") at pointer index " << mPointerIndex
<< ", but got (" << event.pointerCoords[mPointerIndex].getX() << ", "
<< event.pointerCoords[mPointerIndex].getY() << ")";
}
return matches;
}
void DescribeTo(std::ostream* os) const {
*os << "with coords (" << mX << ", " << mY << ") at pointer index " << mPointerIndex;
}
void DescribeNegationTo(std::ostream* os) const { *os << "wrong coords"; }
private:
const size_t mPointerIndex;
const float mX;
const float mY;
};
inline WithCoordsMatcher WithCoords(float x, float y) {
return WithCoordsMatcher(0, x, y);
}
inline WithCoordsMatcher WithPointerCoords(size_t pointerIndex, float x, float y) {
return WithCoordsMatcher(pointerIndex, x, y);
}
/// Raw coordinate matcher
class WithRawCoordsMatcher {
public:
using is_gtest_matcher = void;
explicit WithRawCoordsMatcher(size_t pointerIndex, float rawX, float rawY)
: mPointerIndex(pointerIndex), mRawX(rawX), mRawY(rawY) {}
bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
if (mPointerIndex >= event.getPointerCount()) {
*os << "Pointer index " << mPointerIndex << " is out of bounds";
return false;
}
bool matches =
mRawX == event.getRawX(mPointerIndex) && mRawY == event.getRawY(mPointerIndex);
if (!matches) {
*os << "expected raw coords (" << mRawX << ", " << mRawY << ") at pointer index "
<< mPointerIndex << ", but got (" << event.getRawX(mPointerIndex) << ", "
<< event.getRawY(mPointerIndex) << ")";
}
return matches;
}
void DescribeTo(std::ostream* os) const {
*os << "with raw coords (" << mRawX << ", " << mRawY << ") at pointer index "
<< mPointerIndex;
}
void DescribeNegationTo(std::ostream* os) const { *os << "wrong raw coords"; }
private:
const size_t mPointerIndex;
const float mRawX;
const float mRawY;
};
inline WithRawCoordsMatcher WithRawCoords(float rawX, float rawY) {
return WithRawCoordsMatcher(0, rawX, rawY);
}
inline WithRawCoordsMatcher WithPointerRawCoords(size_t pointerIndex, float rawX, float rawY) {
return WithRawCoordsMatcher(pointerIndex, rawX, rawY);
}
/// Pointer count
class WithPointerCountMatcher {
public:
using is_gtest_matcher = void;
explicit WithPointerCountMatcher(size_t pointerCount) : mPointerCount(pointerCount) {}
bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
if (event.getPointerCount() != mPointerCount) {
*os << "expected pointer count " << mPointerCount << ", but got "
<< event.getPointerCount();
return false;
}
return true;
}
bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
if (event.pointerCoords.size() != mPointerCount) {
*os << "expected pointer count " << mPointerCount << ", but got "
<< event.pointerCoords.size();
return false;
}
return true;
}
void DescribeTo(std::ostream* os) const { *os << "with pointer count " << mPointerCount; }
void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointer count"; }
private:
const size_t mPointerCount;
};
inline WithPointerCountMatcher WithPointerCount(size_t pointerCount) {
return WithPointerCountMatcher(pointerCount);
}
/// Pointers matcher
class WithPointersMatcher {
public:
using is_gtest_matcher = void;
explicit WithPointersMatcher(std::map<int32_t, PointF> pointers) : mPointers(pointers) {}
bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
std::map<int32_t, PointF> actualPointers;
for (size_t pointerIndex = 0; pointerIndex < event.getPointerCount(); pointerIndex++) {
const int32_t pointerId = event.getPointerId(pointerIndex);
actualPointers[pointerId] = {event.getX(pointerIndex), event.getY(pointerIndex)};
}
if (mPointers != actualPointers) {
*os << "expected pointers " << dumpMap(mPointers, constToString, pointFToString)
<< ", but got " << dumpMap(actualPointers, constToString, pointFToString);
return false;
}
return true;
}
bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
std::map<int32_t, PointF> actualPointers;
for (size_t pointerIndex = 0; pointerIndex < event.pointerCoords.size(); pointerIndex++) {
const int32_t pointerId = event.pointerProperties[pointerIndex].id;
actualPointers[pointerId] = {event.pointerCoords[pointerIndex].getX(),
event.pointerCoords[pointerIndex].getY()};
}
if (mPointers != actualPointers) {
*os << "expected pointers " << dumpMap(mPointers, constToString, pointFToString)
<< ", but got " << dumpMap(actualPointers, constToString, pointFToString);
return false;
}
return true;
}
void DescribeTo(std::ostream* os) const {
*os << "with pointers " << dumpMap(mPointers, constToString, pointFToString);
}
void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointers"; }
private:
const std::map<int32_t, PointF> mPointers;
};
inline WithPointersMatcher WithPointers(
const std::map<int32_t /*id*/, PointF /*coords*/>& pointers) {
return WithPointersMatcher(pointers);
}
/// Key code
class WithKeyCodeMatcher {
public:
using is_gtest_matcher = void;
explicit WithKeyCodeMatcher(int32_t keyCode) : mKeyCode(keyCode) {}
bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
return mKeyCode == args.keyCode;
}
bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
return mKeyCode == event.getKeyCode();
}
void DescribeTo(std::ostream* os) const {
*os << "with key code " << KeyEvent::getLabel(mKeyCode);
}
void DescribeNegationTo(std::ostream* os) const { *os << "wrong key code"; }
private:
const int32_t mKeyCode;
};
inline WithKeyCodeMatcher WithKeyCode(int32_t keyCode) {
return WithKeyCodeMatcher(keyCode);
}
/// EventId
class WithEventIdMatcher {
public:
using is_gtest_matcher = void;
explicit WithEventIdMatcher(int32_t eventId) : mEventId(eventId) {}
bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
return mEventId == args.id;
}
bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
return mEventId == args.id;
}
bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
return mEventId == event.getId();
}
void DescribeTo(std::ostream* os) const { *os << "with eventId 0x" << std::hex << mEventId; }
void DescribeNegationTo(std::ostream* os) const {
*os << "with eventId not equal to 0x" << std::hex << mEventId;
}
private:
const int32_t mEventId;
};
inline WithEventIdMatcher WithEventId(int32_t eventId) {
return WithEventIdMatcher(eventId);
}
/// EventIdSource
class WithEventIdSourceMatcher {
public:
using is_gtest_matcher = void;
explicit WithEventIdSourceMatcher(IdGenerator::Source eventIdSource)
: mEventIdSource(eventIdSource) {}
bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
return mEventIdSource == IdGenerator::getSource(args.id);
}
bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
return mEventIdSource == IdGenerator::getSource(args.id);
}
bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
return mEventIdSource == IdGenerator::getSource(event.getId());
}
void DescribeTo(std::ostream* os) const {
*os << "with eventId from source 0x" << std::hex << ftl::to_underlying(mEventIdSource);
}
void DescribeNegationTo(std::ostream* os) const { *os << "wrong event from source"; }
private:
const IdGenerator::Source mEventIdSource;
};
inline WithEventIdSourceMatcher WithEventIdSource(IdGenerator::Source eventIdSource) {
return WithEventIdSourceMatcher(eventIdSource);
}
MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
return arg.getRepeatCount() == repeatCount;
}
MATCHER_P2(WithPointerId, index, id, "MotionEvent with specified pointer ID for pointer index") {
const auto argPointerId = arg.pointerProperties[index].id;
*result_listener << "expected pointer with index " << index << " to have ID " << argPointerId;
return argPointerId == id;
}
MATCHER_P2(WithCursorPosition, x, y, "InputEvent with specified cursor position") {
const auto argX = arg.xCursorPosition;
const auto argY = arg.yCursorPosition;
*result_listener << "expected cursor position (" << x << ", " << y << "), but got (" << argX
<< ", " << argY << ")";
return (isnan(x) ? isnan(argX) : x == argX) && (isnan(y) ? isnan(argY) : y == argY);
}
MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion") {
const auto argX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const auto argY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
*result_listener << "expected relative motion (" << x << ", " << y << "), but got (" << argX
<< ", " << argY << ")";
return argX == x && argY == y;
}
MATCHER_P3(WithGestureOffset, dx, dy, epsilon,
"InputEvent with specified touchpad gesture offset") {
const auto argGestureX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET);
const auto argGestureY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET);
const double xDiff = fabs(argGestureX - dx);
const double yDiff = fabs(argGestureY - dy);
*result_listener << "expected gesture offset (" << dx << ", " << dy << ") within " << epsilon
<< ", but got (" << argGestureX << ", " << argGestureY << ")";
return xDiff <= epsilon && yDiff <= epsilon;
}
MATCHER_P3(WithGestureScrollDistance, x, y, epsilon,
"InputEvent with specified touchpad gesture scroll distance") {
const auto argXDistance =
arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE);
const auto argYDistance =
arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE);
const double xDiff = fabs(argXDistance - x);
const double yDiff = fabs(argYDistance - y);
*result_listener << "expected gesture offset (" << x << ", " << y << ") within " << epsilon
<< ", but got (" << argXDistance << ", " << argYDistance << ")";
return xDiff <= epsilon && yDiff <= epsilon;
}
MATCHER_P2(WithGesturePinchScaleFactor, factor, epsilon,
"InputEvent with specified touchpad pinch gesture scale factor") {
const auto argScaleFactor =
arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR);
*result_listener << "expected gesture scale factor " << factor << " within " << epsilon
<< " but got " << argScaleFactor;
return fabs(argScaleFactor - factor) <= epsilon;
}
MATCHER_P(WithGestureSwipeFingerCount, count,
"InputEvent with specified touchpad swipe finger count") {
const auto argFingerCount =
arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT);
*result_listener << "expected gesture swipe finger count " << count << " but got "
<< argFingerCount;
return fabs(argFingerCount - count) <= EPSILON;
}
MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
*result_listener << "expected pressure " << pressure << ", but got " << argPressure;
return argPressure == pressure;
}
MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") {
const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
*result_listener << "expected touch dimensions " << maj << " major x " << min
<< " minor, but got " << argMajor << " major x " << argMinor << " minor";
return argMajor == maj && argMinor == min;
}
MATCHER_P2(WithToolDimensions, maj, min, "InputEvent with specified tool dimensions") {
const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR);
const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR);
*result_listener << "expected tool dimensions " << maj << " major x " << min
<< " minor, but got " << argMajor << " major x " << argMinor << " minor";
return argMajor == maj && argMinor == min;
}
MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
const auto argToolType = arg.pointerProperties[0].toolType;
*result_listener << "expected tool type " << ftl::enum_string(toolType) << ", but got "
<< ftl::enum_string(argToolType);
return argToolType == toolType;
}
MATCHER_P2(WithPointerToolType, pointer, toolType,
"InputEvent with specified tool type for pointer") {
const auto argToolType = arg.pointerProperties[pointer].toolType;
*result_listener << "expected pointer " << pointer << " to have tool type "
<< ftl::enum_string(toolType) << ", but got " << ftl::enum_string(argToolType);
return argToolType == toolType;
}
MATCHER_P(WithMotionClassification, classification,
"InputEvent with specified MotionClassification") {
*result_listener << "expected classification " << motionClassificationToString(classification)
<< ", but got " << motionClassificationToString(arg.classification);
return arg.classification == classification;
}
MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") {
*result_listener << "expected button state " << buttons << ", but got " << arg.buttonState;
return arg.buttonState == buttons;
}
MATCHER_P(WithActionButton, actionButton, "InputEvent with specified action button") {
*result_listener << "expected action button " << actionButton << ", but got "
<< arg.actionButton;
return arg.actionButton == actionButton;
}
MATCHER_P(WithEventTime, eventTime, "InputEvent with specified eventTime") {
*result_listener << "expected event time " << eventTime << ", but got " << arg.eventTime;
return arg.eventTime == eventTime;
}
MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
*result_listener << "expected down time " << downTime << ", but got " << arg.downTime;
return arg.downTime == downTime;
}
MATCHER_P2(WithPrecision, xPrecision, yPrecision, "MotionEvent with specified precision") {
*result_listener << "expected x-precision " << xPrecision << " and y-precision " << yPrecision
<< ", but got " << arg.xPrecision << " and " << arg.yPrecision;
return arg.xPrecision == xPrecision && arg.yPrecision == yPrecision;
}
} // namespace android