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(nullptr, nullptr, false) {}
20
SyncInterpret(HardwareState & hwstate,stime_t * timeout)21 virtual void SyncInterpret(HardwareState& hwstate, stime_t* timeout) {
22 if (return_value_.type != kGestureTypeNull)
23 ProduceGesture(return_value_);
24 }
25
HandleTimer(stime_t now,stime_t * timeout)26 virtual void HandleTimer(stime_t now, stime_t* timeout) {
27 ADD_FAILURE() << "HandleTimer on the next interpreter shouldn't be called";
28 }
29
30 Gesture return_value_;
31 };
32
33 struct GestureTestInputs {
34 stime_t time;
35 short touch_count; // -1 for timer callback
36 FingerState* fs;
37 Gesture gesture;
38 stime_t expected_button;
39 };
40
41 } // namespace {}
42
TEST(HapticButtonGeneratorFilterInterpreterTest,SimpleTest)43 TEST(HapticButtonGeneratorFilterInterpreterTest, SimpleTest) {
44 HapticButtonGeneratorFilterInterpreterTestInterpreter* base_interpreter =
45 new HapticButtonGeneratorFilterInterpreterTestInterpreter;
46 HapticButtonGeneratorFilterInterpreter interpreter(
47 nullptr, base_interpreter, nullptr);
48 HardwareProperties hwprops = {
49 .right = 100, .bottom = 100,
50 .res_x = 10,
51 .res_y = 10,
52 .orientation_minimum = -1,
53 .orientation_maximum = 2,
54 .max_finger_cnt = 2, .max_touch_cnt = 5,
55 .supports_t5r2 = 0, .support_semi_mt = 0, .is_button_pad = 0,
56 .has_wheel = 0, .wheel_is_hi_res = 0,
57 .is_haptic_pad = 1,
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, nullptr),
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 nullptr, base_interpreter, nullptr);
135 HardwareProperties hwprops = {
136 .right = 100, .bottom = 100,
137 .res_x = 10,
138 .res_y = 10,
139 .orientation_minimum = -1,
140 .orientation_maximum = 2,
141 .max_finger_cnt = 2, .max_touch_cnt = 5,
142 .supports_t5r2 = 0, .support_semi_mt = 0, .is_button_pad = 0,
143 .has_wheel = 0, .wheel_is_hi_res = 0,
144 .is_haptic_pad = 0,
145 };
146 TestInterpreterWrapper wrapper(&interpreter, &hwprops);
147
148 interpreter.enabled_.val_ = true;
149
150 FingerState fs[] = {
151 // TM, Tm, WM, Wm, pr, orient, x, y, id, flag
152 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
153 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
154 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
155
156 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
157 { 0, 0, 0, 0, 120, 0, 10, 1, 1, 0 },
158 { 0, 0, 0, 0, 160, 0, 10, 1, 1, 0 },
159 };
160 HardwareState hs[] = {
161 // Expect to keep button press generated by firmware
162 make_hwstate(1.01, 0, 1, 1, &fs[0]),
163 make_hwstate(1.02, GESTURES_BUTTON_LEFT, 1, 1, &fs[1]),
164 make_hwstate(1.03, 0, 1, 1, &fs[2]),
165
166 // Expect to not generate a button pres
167 make_hwstate(2.01, 0, 1, 1, &fs[3]),
168 make_hwstate(2.03, 0, 1, 1, &fs[4]),
169 make_hwstate(2.05, 0, 1, 1, &fs[5]),
170 };
171
172 stime_t expected_buttons[] = {
173 0, GESTURES_BUTTON_LEFT, 0,
174 0, 0, 0
175 };
176
177 for (size_t i = 0; i < arraysize(hs); i++) {
178 stime_t timeout = NO_DEADLINE;
179 wrapper.SyncInterpret(hs[i], &timeout);
180 EXPECT_EQ(hs[i].buttons_down, expected_buttons[i]);
181 }
182 }
183
TEST(HapticButtonGeneratorFilterInterpreterTest,NotHapticConsumeGestureTest)184 TEST(HapticButtonGeneratorFilterInterpreterTest, NotHapticConsumeGestureTest) {
185 HapticButtonGeneratorFilterInterpreter interpreter(
186 nullptr, nullptr, nullptr);
187 interpreter.is_haptic_pad_ = false;
188 interpreter.active_gesture_deadline_ = 0.0;
189 interpreter.release_suppress_factor_ = 0.0;
190
191 const Gesture kFling(kGestureFling, 0, 0, 20, 0, GESTURES_FLING_START);
192 const Gesture kMove(kGestureMove, 2, 3, 4.0, 5.0);
193 const Gesture kScroll(kGestureScroll, 0, 0, 20, 0);
194
195 // Verify no state change happens when a Fling is sent to haptics
196 // when this is not a haptics device
197 interpreter.active_gesture_ = false;
198 interpreter.ConsumeGesture(kFling);
199 EXPECT_FALSE(interpreter.active_gesture_);
200 EXPECT_EQ(interpreter.active_gesture_deadline_, 0.0);
201 EXPECT_EQ(interpreter.release_suppress_factor_, 0.0);
202
203 interpreter.active_gesture_ = true;
204 interpreter.ConsumeGesture(kFling);
205 EXPECT_TRUE(interpreter.active_gesture_);
206 EXPECT_EQ(interpreter.active_gesture_deadline_, 0.0);
207 EXPECT_EQ(interpreter.release_suppress_factor_, 0.0);
208
209 // Verify no state change happens when a MOVE is sent to haptics
210 // when this is not a haptics device
211 interpreter.active_gesture_ = false;
212 interpreter.ConsumeGesture(kMove);
213 EXPECT_FALSE(interpreter.active_gesture_);
214 EXPECT_EQ(interpreter.active_gesture_deadline_, 0.0);
215 EXPECT_EQ(interpreter.release_suppress_factor_, 0.0);
216
217 interpreter.active_gesture_ = true;
218 interpreter.ConsumeGesture(kMove);
219 EXPECT_TRUE(interpreter.active_gesture_);
220 EXPECT_EQ(interpreter.active_gesture_deadline_, 0.0);
221 EXPECT_EQ(interpreter.release_suppress_factor_, 0.0);
222
223 // Verify no state change happens when a Scroll is sent to haptics
224 // when this is not a haptics device
225 interpreter.active_gesture_ = false;
226 interpreter.ConsumeGesture(kScroll);
227 EXPECT_FALSE(interpreter.active_gesture_);
228 EXPECT_EQ(interpreter.active_gesture_deadline_, 0.0);
229 EXPECT_EQ(interpreter.release_suppress_factor_, 0.0);
230
231 interpreter.active_gesture_ = true;
232 interpreter.ConsumeGesture(kScroll);
233 EXPECT_TRUE(interpreter.active_gesture_);
234 EXPECT_EQ(interpreter.active_gesture_deadline_, 0.0);
235 EXPECT_EQ(interpreter.release_suppress_factor_, 0.0);
236 }
237
TEST(HapticButtonGeneratorFilterInterpreterTest,GesturePreventsButtonDownTest)238 TEST(HapticButtonGeneratorFilterInterpreterTest,
239 GesturePreventsButtonDownTest) {
240 HapticButtonGeneratorFilterInterpreterTestInterpreter* base_interpreter =
241 new HapticButtonGeneratorFilterInterpreterTestInterpreter;
242 HapticButtonGeneratorFilterInterpreter interpreter(
243 nullptr, base_interpreter, nullptr);
244 HardwareProperties hwprops = {
245 .right = 100, .bottom = 100,
246 .res_x = 10,
247 .res_y = 10,
248 .orientation_minimum = -1,
249 .orientation_maximum = 2,
250 .max_finger_cnt = 2, .max_touch_cnt = 5,
251 .supports_t5r2 = 0, .support_semi_mt = 0, .is_button_pad = 0,
252 .has_wheel = 0, .wheel_is_hi_res = 0,
253 .is_haptic_pad = 1,
254 };
255 TestInterpreterWrapper wrapper(&interpreter, &hwprops);
256
257 interpreter.enabled_.val_ = true;
258
259 // TM, Tm, WM, Wm, pr, orient, x, y, id, flag
260 FingerState fs_low_force[] = {
261 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
262 { 0, 0, 0, 0, 50, 0, 10, 1, 2, 0 },
263 };
264 FingerState fs_high_force[] = {
265 { 0, 0, 0, 0, 160, 0, 10, 1, 1, 0 },
266 { 0, 0, 0, 0, 160, 0, 10, 1, 2, 0 },
267 };
268
269 const Gesture kNull = Gesture();
270 const Gesture kScroll = Gesture(kGestureScroll, 0, 0, 20, 0);
271 const Gesture kFling = Gesture(kGestureFling, 0, 0, 20, 0,
272 GESTURES_FLING_START);
273
274 GestureTestInputs inputs[] = {
275 // Don't set the button down if a gesture is active.
276 {1.00, 2, fs_low_force, kScroll, GESTURES_BUTTON_NONE},
277 {1.01, 2, fs_high_force, kFling, GESTURES_BUTTON_NONE},
278
279 // If the button is down before a gesture starts, don't prevent the button
280 // from going back up.
281 {2.000, 2, fs_low_force, kNull, GESTURES_BUTTON_NONE},
282 {2.010, 2, fs_high_force, kNull, GESTURES_BUTTON_LEFT},
283 {2.030, 2, fs_high_force, kScroll, GESTURES_BUTTON_LEFT},
284 {2.040, 2, fs_low_force, kScroll, GESTURES_BUTTON_NONE},
285 {2.050, 2, fs_low_force, kFling, GESTURES_BUTTON_NONE},
286
287 // If there is no "ending" gesture event, allow button clicks after a short
288 // timeout.
289 {3.000, 2, fs_low_force, kScroll, GESTURES_BUTTON_NONE},
290 {3.010, 2, fs_high_force, kNull, GESTURES_BUTTON_NONE},
291 {3.011, 2, fs_high_force, kNull, GESTURES_BUTTON_NONE},
292 {3.011 + interpreter.active_gesture_timeout_, -1, nullptr, kNull, 0},
293 {3.200 + interpreter.active_gesture_timeout_,
294 2, fs_high_force, kNull, GESTURES_BUTTON_LEFT},
295 };
296
297 for (size_t i = 0; i < arraysize(inputs); i++) {
298 GestureTestInputs input = inputs[i];
299 base_interpreter->return_value_ = input.gesture;
300 stime_t timeout = NO_DEADLINE;
301 if (input.touch_count == -1) {
302 wrapper.HandleTimer(input.time, &timeout);
303 } else {
304 unsigned short touch_count =
305 static_cast<unsigned short>(input.touch_count);
306 HardwareState hs = make_hwstate(input.time, 0, touch_count, touch_count,
307 input.fs);
308 wrapper.SyncInterpret(hs, &timeout);
309 EXPECT_EQ(hs.buttons_down, input.expected_button);
310 }
311 }
312 }
313
TEST(HapticButtonGeneratorFilterInterpreterTest,DynamicThresholdTest)314 TEST(HapticButtonGeneratorFilterInterpreterTest, DynamicThresholdTest) {
315 HapticButtonGeneratorFilterInterpreterTestInterpreter* base_interpreter =
316 new HapticButtonGeneratorFilterInterpreterTestInterpreter;
317 HapticButtonGeneratorFilterInterpreter interpreter(
318 nullptr, base_interpreter, nullptr);
319 HardwareProperties hwprops = {
320 .right = 100, .bottom = 100,
321 .res_x = 10,
322 .res_y = 10,
323 .orientation_minimum = -1,
324 .orientation_maximum = 2,
325 .max_finger_cnt = 2, .max_touch_cnt = 5,
326 .supports_t5r2 = 0, .support_semi_mt = 0, .is_button_pad = 0,
327 .has_wheel = 0, .wheel_is_hi_res = 0,
328 .is_haptic_pad = 1,
329 };
330 TestInterpreterWrapper wrapper(&interpreter, &hwprops);
331
332 interpreter.enabled_.val_ = true;
333 interpreter.use_dynamic_thresholds_.val_ = true;
334
335 FingerState fs[] = {
336 // TM, Tm, WM, Wm, pr, orient, x, y, id, flag
337 { 0, 0, 0, 0, 50, 0, 10, 1, 1, 0 },
338 { 0, 0, 0, 0, 120, 0, 10, 1, 1, 0 },
339 { 0, 0, 0, 0, 160, 0, 10, 1, 1, 0 },
340
341 { 0, 0, 0, 0, 500, 0, 10, 1, 1, 0 },
342 { 0, 0, 0, 0, 300, 0, 10, 1, 1, 0 },
343 { 0, 0, 0, 0, 200, 0, 10, 1, 1, 0 },
344
345 { 0, 0, 0, 0, 220, 0, 10, 1, 1, 0 },
346 { 0, 0, 0, 0, 250, 0, 10, 1, 1, 0 },
347
348 { 0, 0, 0, 0, 10, 0, 10, 1, 1, 0 },
349 { 0, 0, 0, 0, 120, 0, 10, 1, 1, 0 },
350 { 0, 0, 0, 0, 140, 0, 10, 1, 1, 0 },
351
352 { 0, 0, 0, 0, 110, 0, 10, 1, 1, 0 },
353 { 0, 0, 0, 0, 100, 0, 10, 1, 1, 0 },
354 };
355
356 std::pair<HardwareState, int> hs[] = {
357 // Expect to set button down when going above 'down force threshold' (130)
358 std::make_pair(make_hwstate(1.01, 0, 1, 1, &fs[0]), 0),
359 std::make_pair(make_hwstate(1.03, 0, 1, 1, &fs[1]), 0),
360 std::make_pair(make_hwstate(1.05, 0, 1, 1, &fs[2]), GESTURES_BUTTON_LEFT),
361
362 // Expect to increase button up threshold after seeing a very high force.
363 // Default 'up force threshold' is 105, but it increases to half of the max
364 // force seen.
365 std::make_pair(make_hwstate(2.01, 0, 1, 1, &fs[3]), GESTURES_BUTTON_LEFT),
366 std::make_pair(make_hwstate(2.03, 0, 1, 1, &fs[4]), GESTURES_BUTTON_LEFT),
367 std::make_pair(make_hwstate(2.05, 0, 1, 1, &fs[5]), 0),
368
369 // Expect to increase 'button down threshold' after seeing a very high
370 // force.
371 std::make_pair(make_hwstate(3.01, 0, 1, 1, &fs[6]), 0),
372 std::make_pair(make_hwstate(3.03, 0, 1, 1, &fs[7]), GESTURES_BUTTON_LEFT),
373
374 // Expect 'button down threshold' to return to normal (130) after seeing a
375 // low pressure value.
376 std::make_pair(make_hwstate(4.01, 0, 1, 1, &fs[8]), 0),
377 std::make_pair(make_hwstate(4.03, 0, 1, 1, &fs[9]), 0),
378 std::make_pair(make_hwstate(4.05, 0, 1, 1, &fs[10]), GESTURES_BUTTON_LEFT),
379
380 // Expect 'button up threshold' to return to normal after seeing a low
381 // pressure value.
382 std::make_pair(make_hwstate(5.01, 0, 1, 1, &fs[11]), GESTURES_BUTTON_LEFT),
383 std::make_pair(make_hwstate(5.03, 0, 1, 1, &fs[12]), 0),
384 };
385
386 for (size_t i = 0; i < arraysize(hs); i++) {
387 stime_t timeout = NO_DEADLINE;
388 wrapper.SyncInterpret(hs[i].first, &timeout);
389 EXPECT_EQ(hs[i].first.buttons_down, hs[i].second);
390 }
391 }
392
TEST(HapticButtonGeneratorFilterInterpreterTest,PalmTest)393 TEST(HapticButtonGeneratorFilterInterpreterTest, PalmTest) {
394 HapticButtonGeneratorFilterInterpreterTestInterpreter* base_interpreter =
395 new HapticButtonGeneratorFilterInterpreterTestInterpreter;
396 HapticButtonGeneratorFilterInterpreter interpreter(
397 nullptr, base_interpreter, nullptr);
398 HardwareProperties hwprops = {
399 .right = 100, .bottom = 100,
400 .res_x = 10,
401 .res_y = 10,
402 .orientation_minimum = -1,
403 .orientation_maximum = 2,
404 .max_finger_cnt = 2, .max_touch_cnt = 5,
405 .supports_t5r2 = 0, .support_semi_mt = 0, .is_button_pad = 0,
406 .has_wheel = 0, .wheel_is_hi_res = 0,
407 .is_haptic_pad = 1,
408 };
409 TestInterpreterWrapper wrapper(&interpreter, &hwprops);
410
411 interpreter.enabled_.val_ = true;
412
413 FingerState fs[] = {
414 // TM, Tm, WM, Wm, pr, orient, x, y, id, flag
415 { 0, 0, 0, 0, 50, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
416 { 0, 0, 0, 0, 160, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
417 { 0, 0, 0, 0, 50, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
418
419 { 0, 0, 0, 0, 50, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
420 { 0, 0, 0, 0, 50, 0, 10, 1, 2, 0 },
421 { 0, 0, 0, 0, 160, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
422 { 0, 0, 0, 0, 80, 0, 10, 1, 2, 0 },
423
424 { 0, 0, 0, 0, 160, 0, 10, 1, 1, GESTURES_FINGER_LARGE_PALM },
425 { 0, 0, 0, 0, 160, 0, 10, 1, 2, 0 },
426 };
427 HardwareState hs[] = {
428 // Expect not to set button down when a lone palm goes above 'down force
429 // threshold'
430 make_hwstate(2.01, 0, 1, 1, &fs[0]),
431 make_hwstate(2.03, 0, 1, 1, &fs[1]),
432 make_hwstate(2.05, 0, 1, 1, &fs[2]),
433
434 // Expect not to set button down when there are multiple fingers and only a
435 // palm goes above 'down force threshold'
436 make_hwstate(4.01, 0, 2, 2, &fs[3]),
437 make_hwstate(4.03, 0, 2, 2, &fs[5]),
438
439 // Expect to set button down when there are multiple fingers and a non-palm
440 // goes above 'down force threshold'
441 make_hwstate(4.05, 0, 2, 2, &fs[7]),
442 };
443
444 stime_t expected_buttons[] = {
445 0, 0, 0,
446 0, 0,
447 GESTURES_BUTTON_LEFT,
448 };
449
450 for (size_t i = 0; i < arraysize(hs); i++) {
451 stime_t timeout = NO_DEADLINE;
452 wrapper.SyncInterpret(hs[i], &timeout);
453 EXPECT_EQ(hs[i].buttons_down, expected_buttons[i]);
454 }
455 }
456
457 } // namespace gestures
458