1 /**
2 * Copyright 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <chrono>
20 #include <cmath>
21 #include <ostream>
22 #include <vector>
23
24 #include <android-base/logging.h>
25 #include <gtest/gtest.h>
26 #include <input/Input.h>
27
28 namespace android {
29
30 namespace {
31
32 using ::testing::Matcher;
33
34 } // namespace
35
36 /**
37 * This file contains a copy of Matchers from .../inputflinger/tests/TestEventMatchers.h. Ideally,
38 * implementations must not be duplicated.
39 * TODO(b/365606513): Find a way to share TestEventMatchers.h between inputflinger and libinput.
40 */
41
42 struct PointerArgs {
43 float x{0.0f};
44 float y{0.0f};
45 bool isResampled{false};
46 };
47
48 struct Sample {
49 std::chrono::nanoseconds eventTime{0};
50 std::vector<PointerArgs> pointers{};
51 };
52
53 class WithDeviceIdMatcher {
54 public:
55 using is_gtest_matcher = void;
WithDeviceIdMatcher(DeviceId deviceId)56 explicit WithDeviceIdMatcher(DeviceId deviceId) : mDeviceId(deviceId) {}
57
MatchAndExplain(const InputEvent & event,std::ostream *)58 bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
59 return mDeviceId == event.getDeviceId();
60 }
61
DescribeTo(std::ostream * os)62 void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
63
DescribeNegationTo(std::ostream * os)64 void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
65
66 private:
67 const DeviceId mDeviceId;
68 };
69
WithDeviceId(int32_t deviceId)70 inline WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
71 return WithDeviceIdMatcher(deviceId);
72 }
73
74 class WithMotionActionMatcher {
75 public:
76 using is_gtest_matcher = void;
WithMotionActionMatcher(int32_t action)77 explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
78
MatchAndExplain(const MotionEvent & event,testing::MatchResultListener * listener)79 bool MatchAndExplain(const MotionEvent& event, testing::MatchResultListener* listener) const {
80 if (mAction != event.getAction()) {
81 *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got "
82 << MotionEvent::actionToString(event.getAction());
83 return false;
84 }
85 if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL &&
86 (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) == 0) {
87 *listener << "event with CANCEL action is missing FLAG_CANCELED";
88 return false;
89 }
90 return true;
91 }
92
DescribeTo(std::ostream * os)93 void DescribeTo(std::ostream* os) const {
94 *os << "with motion action " << MotionEvent::actionToString(mAction);
95 if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
96 *os << " and FLAG_CANCELED";
97 }
98 }
99
DescribeNegationTo(std::ostream * os)100 void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
101
102 private:
103 const int32_t mAction;
104 };
105
WithMotionAction(int32_t action)106 inline WithMotionActionMatcher WithMotionAction(int32_t action) {
107 return WithMotionActionMatcher(action);
108 }
109
110 class WithSampleCountMatcher {
111 public:
112 using is_gtest_matcher = void;
WithSampleCountMatcher(size_t sampleCount)113 explicit WithSampleCountMatcher(size_t sampleCount) : mExpectedSampleCount{sampleCount} {}
114
MatchAndExplain(const MotionEvent & motionEvent,std::ostream *)115 bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream*) const {
116 return (motionEvent.getHistorySize() + 1) == mExpectedSampleCount;
117 }
118
DescribeTo(std::ostream * os)119 void DescribeTo(std::ostream* os) const { *os << "sample count " << mExpectedSampleCount; }
120
DescribeNegationTo(std::ostream * os)121 void DescribeNegationTo(std::ostream* os) const { *os << "different sample count"; }
122
123 private:
124 const size_t mExpectedSampleCount;
125 };
126
WithSampleCount(size_t sampleCount)127 inline WithSampleCountMatcher WithSampleCount(size_t sampleCount) {
128 return WithSampleCountMatcher(sampleCount);
129 }
130
131 class WithSampleMatcher {
132 public:
133 using is_gtest_matcher = void;
WithSampleMatcher(size_t sampleIndex,const Sample & sample)134 explicit WithSampleMatcher(size_t sampleIndex, const Sample& sample)
135 : mSampleIndex{sampleIndex}, mSample{sample} {}
136
MatchAndExplain(const MotionEvent & motionEvent,std::ostream * os)137 bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream* os) const {
138 if (motionEvent.getHistorySize() < mSampleIndex) {
139 *os << "sample index out of bounds";
140 return false;
141 }
142
143 if (motionEvent.getHistoricalEventTime(mSampleIndex) != mSample.eventTime.count()) {
144 *os << "event time mismatch. sample: "
145 << motionEvent.getHistoricalEventTime(mSampleIndex)
146 << " expected: " << mSample.eventTime.count();
147 return false;
148 }
149
150 if (motionEvent.getPointerCount() != mSample.pointers.size()) {
151 *os << "pointer count mismatch. sample: " << motionEvent.getPointerCount()
152 << " expected: " << mSample.pointers.size();
153 return false;
154 }
155
156 for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
157 ++pointerIndex) {
158 const PointerCoords& pointerCoords =
159 *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, mSampleIndex));
160
161 if ((std::abs(pointerCoords.getX() - mSample.pointers[pointerIndex].x) >
162 MotionEvent::ROUNDING_PRECISION) ||
163 (std::abs(pointerCoords.getY() - mSample.pointers[pointerIndex].y) >
164 MotionEvent::ROUNDING_PRECISION)) {
165 *os << "sample coordinates mismatch at pointer index " << pointerIndex
166 << ". sample: (" << pointerCoords.getX() << ", " << pointerCoords.getY()
167 << ") expected: (" << mSample.pointers[pointerIndex].x << ", "
168 << mSample.pointers[pointerIndex].y << ")";
169 return false;
170 }
171
172 if (motionEvent.isResampled(pointerIndex, mSampleIndex) !=
173 mSample.pointers[pointerIndex].isResampled) {
174 *os << "resampling flag mismatch. sample: "
175 << motionEvent.isResampled(pointerIndex, mSampleIndex)
176 << " expected: " << mSample.pointers[pointerIndex].isResampled;
177 return false;
178 }
179 }
180 return true;
181 }
182
DescribeTo(std::ostream * os)183 void DescribeTo(std::ostream* os) const { *os << "motion event sample properties match."; }
184
DescribeNegationTo(std::ostream * os)185 void DescribeNegationTo(std::ostream* os) const {
186 *os << "motion event sample properties do not match expected properties.";
187 }
188
189 private:
190 const size_t mSampleIndex;
191 const Sample mSample;
192 };
193
WithSample(size_t sampleIndex,const Sample & sample)194 inline WithSampleMatcher WithSample(size_t sampleIndex, const Sample& sample) {
195 return WithSampleMatcher(sampleIndex, sample);
196 }
197
198 } // namespace android
199