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