1 // Copyright 2021 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <gtest/gtest.h>
6
7 #include "include/haptic_button_generator_filter_interpreter.h"
8 #include "include/unittest_util.h"
9
10 namespace gestures {
11
12 namespace {
13 class HapticButtonGeneratorFilterInterpreterTest : public ::testing::Test {};
14
15 class HapticButtonGeneratorFilterInterpreterTestInterpreter :
16 public Interpreter {
17 public:
HapticButtonGeneratorFilterInterpreterTestInterpreter()18 HapticButtonGeneratorFilterInterpreterTestInterpreter()
19 : Interpreter(NULL, NULL, false) {}
SyncInterpret(HardwareState * hwstate,stime_t * timeout)20 virtual void SyncInterpret(HardwareState* hwstate, stime_t* timeout) {
21 if (return_value_.type != kGestureTypeNull)
22 ProduceGesture(return_value_);
23 }
24
HandleTimer(stime_t now,stime_t * timeout)25 virtual void HandleTimer(stime_t now, stime_t* timeout) {
26 ADD_FAILURE() << "HandleTimer on the next interpreter shouldn't be called";
27 }
28
29 Gesture return_value_;
30 };
31
32 struct GestureTestInputs {
33 stime_t time;
34 short touch_count; // -1 for timer callback
35 FingerState* fs;
36 Gesture gesture;
37 stime_t expected_button;
38 };
39
40 } // namespace {}
41
TEST(HapticButtonGeneratorFilterInterpreterTest,SimpleTest)42 TEST(HapticButtonGeneratorFilterInterpreterTest, SimpleTest) {
43 HapticButtonGeneratorFilterInterpreterTestInterpreter* base_interpreter =
44 new HapticButtonGeneratorFilterInterpreterTestInterpreter;
45 HapticButtonGeneratorFilterInterpreter interpreter(
46 NULL, base_interpreter, NULL);
47 HardwareProperties hwprops = {
48 0, 0, 100, 100, // left, top, right, bottom
49 10, // x res (pixels/mm)
50 10, // y res (pixels/mm)
51 133, 133, // scrn DPI X, Y
52 -1, // orientation minimum
53 2, // orientation maximum
54 2, 5, // max fingers, max_touch
55 0, 0, 0, // t5r2, semi, button pad
56 0, 0, // has wheel, vertical wheel is high resolution
57 1, // haptic pad
58 };
59 TestInterpreterWrapper wrapper(&interpreter, &hwprops);
60
61 interpreter.enabled_.val_ = true;
62
63 FingerState fs[] = {
64 // TM, Tm, WM, Wm, pr, orient, x, y, id, flag
65 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
66 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
67 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
68
69 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
70 { 0, 0, 0, 0, 120, 0, 10, 1, 1, 0 },
71 { 0, 0, 0, 0, 160, 0, 10, 1, 1, 0 },
72
73 { 0, 0, 0, 0, 160, 0, 10, 1, 1, 0 },
74 { 0, 0, 0, 0, 120, 0, 10, 1, 1, 0 },
75 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
76
77 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
78 { 0, 0, 0, 0, 50, 0, 10, 1, 2, 0 },
79 { 0, 0, 0, 0, 80, 0, 10, 1, 1, 0 },
80 { 0, 0, 0, 0, 80, 0, 10, 1, 2, 0 },
81
82 { 0, 0, 0, 0, 160, 0, 10, 1, 1, 0 },
83 { 0, 0, 0, 0, 80, 0, 10, 1, 2, 0 },
84 };
85 HardwareState hs[] = {
86 // Expect to remove button press generated by firmare
87 make_hwstate(1.01, 0, 1, 1, &fs[0]),
88 make_hwstate(1.02, GESTURES_BUTTON_LEFT, 1, 1, &fs[1]),
89 make_hwstate(1.03, 0, 1, 1, &fs[2]),
90
91 // Expect to set button down when going above 'down force threshold' (130)
92 make_hwstate(2.01, 0, 1, 1, &fs[3]),
93 make_hwstate(2.03, 0, 1, 1, &fs[4]),
94 make_hwstate(2.05, 0, 1, 1, &fs[5]),
95
96 // Expect to set button up when going below 'up force threshold' (105)
97 make_hwstate(3.01, 0, 1, 1, &fs[6]),
98 make_hwstate(3.03, 0, 1, 1, &fs[7]),
99 make_hwstate(3.05, 0, 1, 1, &fs[8]),
100
101 // Expect not to set button down when no individual finger goes above the
102 // 'down force threshold' (130), even if multiple combined do
103 make_hwstate(4.01, 0, 2, 2, &fs[9]),
104 make_hwstate(4.03, 0, 2, 2, &fs[11]),
105
106 // Expect to set button down when one of multiple fingers goes above the
107 // 'down force threshold'
108 make_hwstate(4.05, 0, 2, 2, &fs[13]),
109
110 // Expect to set button up after all fingers leave
111 make_hwstate(5.01, 0, 0, 0, NULL),
112 };
113
114 stime_t expected_buttons[] = {
115 0, 0, 0,
116 0, 0, GESTURES_BUTTON_LEFT,
117 GESTURES_BUTTON_LEFT, GESTURES_BUTTON_LEFT, 0,
118 0, 0,
119 GESTURES_BUTTON_LEFT,
120 0
121 };
122
123 for (size_t i = 0; i < arraysize(hs); i++) {
124 stime_t timeout = NO_DEADLINE;
125 wrapper.SyncInterpret(&hs[i], &timeout);
126 EXPECT_EQ(hs[i].buttons_down, expected_buttons[i]);
127 }
128 }
129
TEST(HapticButtonGeneratorFilterInterpreterTest,NotHapticTest)130 TEST(HapticButtonGeneratorFilterInterpreterTest, NotHapticTest) {
131 HapticButtonGeneratorFilterInterpreterTestInterpreter* base_interpreter =
132 new HapticButtonGeneratorFilterInterpreterTestInterpreter;
133 HapticButtonGeneratorFilterInterpreter interpreter(
134 NULL, base_interpreter, NULL);
135 HardwareProperties hwprops = {
136 0, 0, 100, 100, // left, top, right, bottom
137 10, // x res (pixels/mm)
138 10, // y res (pixels/mm)
139 133, 133, // scrn DPI X, Y
140 -1, // orientation minimum
141 2, // orientation maximum
142 2, 5, // max fingers, max_touch
143 0, 0, 0, // t5r2, semi, button pad
144 0, 0, // has wheel, vertical wheel is high resolution
145 0, // haptic pad
146 };
147 TestInterpreterWrapper wrapper(&interpreter, &hwprops);
148
149 interpreter.enabled_.val_ = true;
150
151 FingerState fs[] = {
152 // TM, Tm, WM, Wm, pr, orient, x, y, id, flag
153 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
154 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
155 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
156
157 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
158 { 0, 0, 0, 0, 120, 0, 10, 1, 1, 0 },
159 { 0, 0, 0, 0, 160, 0, 10, 1, 1, 0 },
160 };
161 HardwareState hs[] = {
162 // Expect to keep button press generated by firmware
163 make_hwstate(1.01, 0, 1, 1, &fs[0]),
164 make_hwstate(1.02, GESTURES_BUTTON_LEFT, 1, 1, &fs[1]),
165 make_hwstate(1.03, 0, 1, 1, &fs[2]),
166
167 // Expect to not generate a button pres
168 make_hwstate(2.01, 0, 1, 1, &fs[3]),
169 make_hwstate(2.03, 0, 1, 1, &fs[4]),
170 make_hwstate(2.05, 0, 1, 1, &fs[5]),
171 };
172
173 stime_t expected_buttons[] = {
174 0, GESTURES_BUTTON_LEFT, 0,
175 0, 0, 0
176 };
177
178 for (size_t i = 0; i < arraysize(hs); i++) {
179 stime_t timeout = NO_DEADLINE;
180 wrapper.SyncInterpret(&hs[i], &timeout);
181 EXPECT_EQ(hs[i].buttons_down, expected_buttons[i]);
182 }
183 }
184
TEST(HapticButtonGeneratorFilterInterpreterTest,GesturePreventsButtonDownTest)185 TEST(HapticButtonGeneratorFilterInterpreterTest,
186 GesturePreventsButtonDownTest) {
187 HapticButtonGeneratorFilterInterpreterTestInterpreter* base_interpreter =
188 new HapticButtonGeneratorFilterInterpreterTestInterpreter;
189 HapticButtonGeneratorFilterInterpreter interpreter(
190 NULL, base_interpreter, NULL);
191 HardwareProperties hwprops = {
192 0, 0, 100, 100, // left, top, right, bottom
193 10, // x res (pixels/mm)
194 10, // y res (pixels/mm)
195 133, 133, // scrn DPI X, Y
196 -1, // orientation minimum
197 2, // orientation maximum
198 2, 5, // max fingers, max_touch
199 0, 0, 0, // t5r2, semi, button pad
200 0, 0, // has wheel, vertical wheel is high resolution
201 1, // haptic pad
202 };
203 TestInterpreterWrapper wrapper(&interpreter, &hwprops);
204
205 interpreter.enabled_.val_ = true;
206
207 // TM, Tm, WM, Wm, pr, orient, x, y, id, flag
208 FingerState fs_low_force[] = {
209 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
210 { 0, 0, 0, 0, 50, 0, 10, 1, 2, 0 },
211 };
212 FingerState fs_high_force[] = {
213 { 0, 0, 0, 0, 160, 0, 10, 1, 1, 0 },
214 { 0, 0, 0, 0, 160, 0, 10, 1, 2, 0 },
215 };
216
217 const Gesture kNull = Gesture();
218 const Gesture kScroll = Gesture(kGestureScroll, 0, 0, 20, 0);
219 const Gesture kFling = Gesture(kGestureFling, 0, 0, 20, 0,
220 GESTURES_FLING_START);
221
222 GestureTestInputs inputs[] = {
223 // Don't set the button down if a gesture is active.
224 {1.00, 2, fs_low_force, kScroll, GESTURES_BUTTON_NONE},
225 {1.01, 2, fs_high_force, kFling, GESTURES_BUTTON_NONE},
226
227 // If the button is down before a gesture starts, don't prevent the button
228 // from going back up.
229 {2.000, 2, fs_low_force, kNull, GESTURES_BUTTON_NONE},
230 {2.010, 2, fs_high_force, kNull, GESTURES_BUTTON_LEFT},
231 {2.030, 2, fs_high_force, kScroll, GESTURES_BUTTON_LEFT},
232 {2.040, 2, fs_low_force, kScroll, GESTURES_BUTTON_NONE},
233 {2.050, 2, fs_low_force, kFling, GESTURES_BUTTON_NONE},
234
235 // If there is no "ending" gesture event, allow button clicks after a short
236 // timeout.
237 {3.000, 2, fs_low_force, kScroll, GESTURES_BUTTON_NONE},
238 {3.010, 2, fs_high_force, kNull, GESTURES_BUTTON_NONE},
239 {3.011, 2, fs_high_force, kNull, GESTURES_BUTTON_NONE},
240 {3.011 + interpreter.active_gesture_timeout_, -1, NULL, kNull, 0},
241 {3.200 + interpreter.active_gesture_timeout_,
242 2, fs_high_force, kNull, GESTURES_BUTTON_LEFT},
243 };
244
245 for (size_t i = 0; i < arraysize(inputs); i++) {
246 GestureTestInputs input = inputs[i];
247 base_interpreter->return_value_ = input.gesture;
248 stime_t timeout = NO_DEADLINE;
249 if (input.touch_count == -1) {
250 wrapper.HandleTimer(input.time, &timeout);
251 } else {
252 unsigned short touch_count =
253 static_cast<unsigned short>(input.touch_count);
254 HardwareState hs = make_hwstate(input.time, 0, touch_count, touch_count,
255 input.fs);
256 wrapper.SyncInterpret(&hs, &timeout);
257 EXPECT_EQ(hs.buttons_down, input.expected_button);
258 }
259 }
260 }
261
TEST(HapticButtonGeneratorFilterInterpreterTest,DynamicThresholdTest)262 TEST(HapticButtonGeneratorFilterInterpreterTest, DynamicThresholdTest) {
263 HapticButtonGeneratorFilterInterpreterTestInterpreter* base_interpreter =
264 new HapticButtonGeneratorFilterInterpreterTestInterpreter;
265 HapticButtonGeneratorFilterInterpreter interpreter(
266 NULL, base_interpreter, NULL);
267 HardwareProperties hwprops = {
268 0, 0, 100, 100, // left, top, right, bottom
269 10, // x res (pixels/mm)
270 10, // y res (pixels/mm)
271 133, 133, // scrn DPI X, Y
272 -1, // orientation minimum
273 2, // orientation maximum
274 2, 5, // max fingers, max_touch
275 0, 0, 0, // t5r2, semi, button pad
276 0, 0, // has wheel, vertical wheel is high resolution
277 1, // haptic pad
278 };
279 TestInterpreterWrapper wrapper(&interpreter, &hwprops);
280
281 interpreter.enabled_.val_ = true;
282 interpreter.use_dynamic_thresholds_.val_ = true;
283
284 FingerState fs[] = {
285 // TM, Tm, WM, Wm, pr, orient, x, y, id, flag
286 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
287 { 0, 0, 0, 0, 120, 0, 10, 1, 1, 0 },
288 { 0, 0, 0, 0, 160, 0, 10, 1, 1, 0 },
289
290 { 0, 0, 0, 0, 500, 0, 10, 1, 1, 0 },
291 { 0, 0, 0, 0, 300, 0, 10, 1, 1, 0 },
292 { 0, 0, 0, 0, 200, 0, 10, 1, 1, 0 },
293
294 { 0, 0, 0, 0, 220, 0, 10, 1, 1, 0 },
295 { 0, 0, 0, 0, 250, 0, 10, 1, 1, 0 },
296
297 { 0, 0, 0, 0, 10, 0, 10, 1, 1, 0 },
298 { 0, 0, 0, 0, 120, 0, 10, 1, 1, 0 },
299 { 0, 0, 0, 0, 140, 0, 10, 1, 1, 0 },
300
301 { 0, 0, 0, 0, 110, 0, 10, 1, 1, 0 },
302 { 0, 0, 0, 0, 100, 0, 10, 1, 1, 0 },
303 };
304
305 std::pair<HardwareState, int> hs[] = {
306 // Expect to set button down when going above 'down force threshold' (130)
307 std::make_pair(make_hwstate(1.01, 0, 1, 1, &fs[0]), 0),
308 std::make_pair(make_hwstate(1.03, 0, 1, 1, &fs[1]), 0),
309 std::make_pair(make_hwstate(1.05, 0, 1, 1, &fs[2]), GESTURES_BUTTON_LEFT),
310
311 // Expect to increase button up threshold after seeing a very high force.
312 // Default 'up force threshold' is 105, but it increases to half of the max
313 // force seen.
314 std::make_pair(make_hwstate(2.01, 0, 1, 1, &fs[3]), GESTURES_BUTTON_LEFT),
315 std::make_pair(make_hwstate(2.03, 0, 1, 1, &fs[4]), GESTURES_BUTTON_LEFT),
316 std::make_pair(make_hwstate(2.05, 0, 1, 1, &fs[5]), 0),
317
318 // Expect to increase 'button down threshold' after seeing a very high
319 // force.
320 std::make_pair(make_hwstate(3.01, 0, 1, 1, &fs[6]), 0),
321 std::make_pair(make_hwstate(3.03, 0, 1, 1, &fs[7]), GESTURES_BUTTON_LEFT),
322
323 // Expect 'button down threshold' to return to normal (130) after seeing a
324 // low pressure value.
325 std::make_pair(make_hwstate(4.01, 0, 1, 1, &fs[8]), 0),
326 std::make_pair(make_hwstate(4.03, 0, 1, 1, &fs[9]), 0),
327 std::make_pair(make_hwstate(4.05, 0, 1, 1, &fs[10]), GESTURES_BUTTON_LEFT),
328
329 // Expect 'button up threshold' to return to normal after seeing a low
330 // pressure value.
331 std::make_pair(make_hwstate(5.01, 0, 1, 1, &fs[11]), GESTURES_BUTTON_LEFT),
332 std::make_pair(make_hwstate(5.03, 0, 1, 1, &fs[12]), 0),
333 };
334
335 for (size_t i = 0; i < arraysize(hs); i++) {
336 stime_t timeout = NO_DEADLINE;
337 wrapper.SyncInterpret(&hs[i].first, &timeout);
338 EXPECT_EQ(hs[i].first.buttons_down, hs[i].second);
339 }
340 }
341
TEST(HapticButtonGeneratorFilterInterpreterTest,PalmTest)342 TEST(HapticButtonGeneratorFilterInterpreterTest, PalmTest) {
343 HapticButtonGeneratorFilterInterpreterTestInterpreter* base_interpreter =
344 new HapticButtonGeneratorFilterInterpreterTestInterpreter;
345 HapticButtonGeneratorFilterInterpreter interpreter(
346 NULL, base_interpreter, NULL);
347 HardwareProperties hwprops = {
348 0, 0, 100, 100, // left, top, right, bottom
349 10, // x res (pixels/mm)
350 10, // y res (pixels/mm)
351 133, 133, // scrn DPI X, Y
352 -1, // orientation minimum
353 2, // orientation maximum
354 2, 5, // max fingers, max_touch
355 0, 0, 0, // t5r2, semi, button pad
356 0, 0, // has wheel, vertical wheel is high resolution
357 1, // haptic pad
358 };
359 TestInterpreterWrapper wrapper(&interpreter, &hwprops);
360
361 interpreter.enabled_.val_ = true;
362
363 FingerState fs[] = {
364 // TM, Tm, WM, Wm, pr, orient, x, y, id, flag
365 { 0, 0, 0, 0, 50, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
366 { 0, 0, 0, 0, 160, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
367 { 0, 0, 0, 0, 50, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
368
369 { 0, 0, 0, 0, 50, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
370 { 0, 0, 0, 0, 50, 0, 10, 1, 2, 0 },
371 { 0, 0, 0, 0, 160, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
372 { 0, 0, 0, 0, 80, 0, 10, 1, 2, 0 },
373
374 { 0, 0, 0, 0, 160, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
375 { 0, 0, 0, 0, 160, 0, 10, 1, 2, 0 },
376 };
377 HardwareState hs[] = {
378 // Expect not to set button down when a lone palm goes above 'down force
379 // threshold'
380 make_hwstate(2.01, 0, 1, 1, &fs[0]),
381 make_hwstate(2.03, 0, 1, 1, &fs[1]),
382 make_hwstate(2.05, 0, 1, 1, &fs[2]),
383
384 // Expect not to set button down when there are multiple fingers and only a
385 // palm goes above 'down force threshold'
386 make_hwstate(4.01, 0, 2, 2, &fs[3]),
387 make_hwstate(4.03, 0, 2, 2, &fs[5]),
388
389 // Expect to set button down when there are multiple fingers and a non-palm
390 // goes above 'down force threshold'
391 make_hwstate(4.05, 0, 2, 2, &fs[7]),
392 };
393
394 stime_t expected_buttons[] = {
395 0, 0, 0,
396 0, 0,
397 GESTURES_BUTTON_LEFT,
398 };
399
400 for (size_t i = 0; i < arraysize(hs); i++) {
401 stime_t timeout = NO_DEADLINE;
402 wrapper.SyncInterpret(&hs[i], &timeout);
403 EXPECT_EQ(hs[i].buttons_down, expected_buttons[i]);
404 }
405 }
406
407 } // namespace gestures
408