pebble/tests/fw/services/test_touch.c
2025-01-27 11:38:16 -08:00

385 lines
17 KiB
C

/*
* Copyright 2024 Google LLC
*
* 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.
*/
#include "clar.h"
#include "applib/graphics/gtypes.h"
#include "kernel/events.h"
#include "services/common/touch/touch.h"
#include "services/common/touch/touch_event.h"
#include "services/common/touch/touch_client.h"
#include "util/size.h"
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "fake_events.h"
// Stubs
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
bool gpoint_equal(const GPoint * const point_a, const GPoint * const point_b) {
return (point_a->x == point_b->x && point_a->y == point_b->y);
}
void kernel_free(void *p) {
}
extern TouchEvent *touch_event_queue_get_event(TouchIdx touch_idx, uint32_t queue_idx);
extern void touch_set_touch_state(TouchIdx touch_idx, TouchState touch_state, GPoint touch_down_pos,
uint64_t touch_down_time_ms, TouchPressure touch_down_pressure);
// setup and teardown
void test_touch__initialize(void) {
fake_event_init();
touch_reset();
}
void test_touch__cleanup(void) {
}
void prv_test_touch_event(TouchEvent *touch_event, TouchIdx idx, TouchEventType type, GPoint *start_pos,
uint64_t start_time_ms, TouchPressure start_pressure, GPoint *diff_pos,
uint64_t diff_time_ms, TouchPressure diff_pressure, bool test_diff) {
cl_assert(touch_event);
cl_assert_equal_i(touch_event->type, type);
cl_assert_equal_i(touch_event->index, idx);
cl_assert_equal_i(touch_event->start_time_ms, start_time_ms);
cl_assert(gpoint_equal(&touch_event->start_pos, start_pos));
cl_assert_equal_i(touch_event->start_pressure, start_pressure);
if (type != TouchEvent_Touchdown) {
cl_assert_equal_i(touch_event->diff_time_ms, diff_time_ms);
cl_assert(gpoint_equal(&touch_event->diff_pos, diff_pos));
cl_assert_equal_i(touch_event->diff_pressure, diff_pressure);
}
}
// tests
void test_touch__handle_update_touchdown(void) {
touch_handle_update(0, TouchState_FingerDown, &GPoint(15, 100), 3, 3686400);
PebbleEvent event = fake_event_get_last();
cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT);
cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable);
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
prv_test_touch_event(touch_event, 0, TouchEvent_Touchdown, &GPoint(15, 100), 3686400, 3,
&GPointZero, 0, 0, true);
touch_event = touch_event_queue_get_event(1, 0);
cl_assert_equal_p(touch_event, NULL);
// Test second touch
touch_handle_update(1, TouchState_FingerDown, &GPoint(1, 13), 5, 3686401);
event = fake_event_get_last();
cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT);
cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable);
touch_event = touch_event_queue_get_event(1, 0);
prv_test_touch_event(touch_event, 1, TouchEvent_Touchdown, &GPoint(1, 13), 3686401, 5,
&GPointZero, 0, 0, true);
}
void test_touch__handle_update_liftoff(void) {
// Test first touch
touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0);
touch_handle_update(0, TouchState_FingerUp, &GPoint(15, 100), 0, 3686400);
PebbleEvent event = fake_event_get_last();
cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT);
cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable);
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
prv_test_touch_event(touch_event, 0, TouchEvent_Liftoff, &GPointZero, 3686380, 0,
&GPoint(15, 100), 20, 0, true);
// Ensure nothing recorded for second touch
touch_event = touch_event_queue_get_event(1, 0);
cl_assert_equal_p(touch_event, NULL);
// Test second touch
touch_set_touch_state(1, TouchState_FingerDown, GPointZero, 0, 0);
touch_handle_update(1, TouchState_FingerUp, &GPoint(1, 13), 0, 3686401);
event = fake_event_get_last();
cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT);
cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable);
touch_event = touch_event_queue_get_event(1, 0);
prv_test_touch_event(touch_event, 1, TouchEvent_Liftoff, &GPointZero, 0, 0, &GPoint(1, 13),
3686401, 0, true);
}
void test_touch__handle_update_liftoff_null_pos(void) {
touch_handle_update(0, TouchState_FingerDown, &GPoint(1, 13), 5, 3686400);
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
prv_test_touch_event(touch_event, 0, TouchEvent_Touchdown, &GPoint(1, 13), 3686400, 5,
&GPointZero, 0, 0, false);
touch_handle_update(0, TouchState_FingerUp, NULL, 0, 3686410);
touch_event = touch_event_queue_get_event(0, 1);
prv_test_touch_event(touch_event, 0, TouchEvent_Liftoff, &GPoint(1, 13), 3686400, 5, &GPointZero,
10, -5, true);
}
void test_touch__handle_update_position(void) {
touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0);
touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686400);
PebbleEvent event = fake_event_get_last();
cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT);
cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable);
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0,
&GPoint(10, 10), 20, 5, true);
fake_event_reset_count();
touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420);
cl_assert_equal_i(fake_event_get_count(), 0); // no event if previous one not handled
touch_event = touch_event_queue_get_event(0, 1);
prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0,
&GPoint(13, 13), 40, 6, true);
}
void test_touch__handle_update_position_stationary(void) {
touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0);
touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686400);
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0,
&GPoint(10, 10), 20, 5, true);
// No touch event generated when finger remains stationary
touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686420);
touch_event = touch_event_queue_get_event(0, 1);
cl_assert_equal_p(touch_event, NULL);
}
void test_touch__handle_update_merge_position(void) {
touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0);
touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686400);
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0,
&GPoint(10, 10), 20, 5, true);
touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420);
touch_event = touch_event_queue_get_event(0, 1);
prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0,
&GPoint(13, 13), 40, 6, true);
touch_handle_update(0, TouchState_FingerDown, &GPoint(18, 5), 1, 3686440);
// Test the same event (event at index 1): it should update to reflect the difference between this
// and the first event
touch_event = touch_event_queue_get_event(0, 1);
prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0,
&GPoint(18, 5), 60, 1, true);
}
void test_touch__handle_update_merge_liftoff(void) {
touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0);
touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686400);
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0,
&GPoint(10, 10), 20, 5, true);
touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420);
touch_event = touch_event_queue_get_event(0, 1);
prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0,
&GPoint(13, 13), 40, 6, true);
touch_handle_update(0, TouchState_FingerUp, &GPoint(18, 5), 0, 3686440);
// Test the same event (event at index 1): it should update to reflect the difference between this
// and the first event and that it is a liftoff event
touch_event = touch_event_queue_get_event(0, 1);
prv_test_touch_event(touch_event, 0, TouchEvent_Liftoff, &GPointZero, 3686380, 0, &GPoint(18, 5),
60, 0, true);
}
void test_touch__handle_update_merge_liftoff_null_pos(void) {
touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0);
touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686400);
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0,
&GPoint(10, 10), 20, 5, true);
touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420);
touch_event = touch_event_queue_get_event(0, 1);
prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0,
&GPoint(13, 13), 40, 6, true);
touch_handle_update(0, TouchState_FingerUp, NULL, 0, 3686440);
touch_event = touch_event_queue_get_event(0, 1);
prv_test_touch_event(touch_event, 0, TouchEvent_Liftoff, &GPointZero, 3686380, 0,
&GPoint(13, 13), 60, 0, true);
}
void test_touch__assert_null_pos_not_liftoff(void) {
// NULL position not valid for touchdown event
cl_assert_passert(touch_handle_update(0, TouchState_FingerDown, NULL, 5, 3686400));
touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 0, 0);
// NULL position not valid for position update event
cl_assert_passert(touch_handle_update(0, TouchState_FingerDown, NULL, 5, 3686400));
}
void test_touch__handle_update_reset_queue_touchdown(void) {
touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0);
touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420);
touch_handle_update(0, TouchState_FingerUp, &GPoint(15, 100), 0, 3686400);
TouchEvent *touch_event = touch_event_queue_get_event(0, 1);
prv_test_touch_event(touch_event, 0, TouchEvent_Liftoff, &GPointZero, 3686380, 0,
&GPoint(15, 100), 20, 0, true);
// touchdown event should reset the touch event queue regardless of what is in it
touch_handle_update(0, TouchState_FingerDown, &GPoint(31, 1), 6, 3686500);
touch_event = touch_event_queue_get_event(0, 0);
prv_test_touch_event(touch_event, 0, TouchEvent_Touchdown, &GPoint(31, 1), 3686500, 6,
&GPointZero, 0, 0, true);
touch_event = touch_event_queue_get_event(0, 1);
cl_assert_equal_p(touch_event, NULL);
}
void test_touch__handle_update_pressure(void) {
//TODO: We're not passing pressure updates to the UI yet (not so useful?)
}
typedef struct TouchEventContext {
TouchEvent touch_events[4];
uint32_t idx;
} TouchEventContext;
static void prv_touch_event_dispatch_cb(const TouchEvent *event, void *context) {
TouchEventContext *ctx = context;
cl_assert(ctx->idx < ARRAY_LENGTH(ctx->touch_events));
ctx->touch_events[ctx->idx++] = *event;
}
void test_touch__dispatch_touch_events_single_finger(void) {
TouchEventContext ctx = {
.idx = 0
};
touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx);
cl_assert_equal_i(ctx.idx, 0);
touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420);
touch_handle_update(0, TouchState_FingerDown, &GPoint(15, 15), 6, 3686440);
touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx);
cl_assert_equal_i(ctx.idx, 2);
prv_test_touch_event(&ctx.touch_events[0], 0, TouchEvent_Touchdown, &GPoint(13, 13), 3686420, 6,
NULL, 0, 0, false);
prv_test_touch_event(&ctx.touch_events[1], 0, TouchEvent_PositionUpdate, &GPoint(13, 13), 3686420,
6, &GPoint(2, 2), 20, 0, true);
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
cl_assert_equal_p(touch_event, NULL);
}
void test_touch__dispatch_touch_events_two_fingers(void) {
TouchEventContext ctx = {
.idx = 0
};
touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx);
cl_assert_equal_i(ctx.idx, 0);
touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420);
touch_handle_update(0, TouchState_FingerDown, &GPoint(15, 15), 6, 3686440);
touch_handle_update(1, TouchState_FingerDown, &GPoint(55, 55), 2, 3686480);
touch_handle_update(1, TouchState_FingerDown, &GPoint(33, 33), 7, 3686500);
touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx);
cl_assert_equal_i(ctx.idx, 2);
prv_test_touch_event(&ctx.touch_events[0], 0, TouchEvent_Touchdown, &GPoint(13, 13), 3686420, 6,
NULL, 0, 0, false);
prv_test_touch_event(&ctx.touch_events[1], 0, TouchEvent_PositionUpdate, &GPoint(13, 13), 3686420,
6, &GPoint(2, 2), 20, 0, true);
touch_dispatch_touch_events(1, prv_touch_event_dispatch_cb, &ctx);
cl_assert_equal_i(ctx.idx, 4);
prv_test_touch_event(&ctx.touch_events[2], 1, TouchEvent_Touchdown, &GPoint(55, 55), 3686480, 2,
NULL, 0, 0, false);
prv_test_touch_event(&ctx.touch_events[3], 1, TouchEvent_PositionUpdate, &GPoint(55, 55),
3686480, 2, &GPoint(-22, -22), 20, 5, true);
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
cl_assert_equal_p(touch_event, NULL);
}
void test_touch__cancel_touches(void) {
touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420);
touch_handle_update(0, TouchState_FingerDown, &GPoint(15, 15), 6, 3686440);
touch_handle_update(1, TouchState_FingerDown, &GPoint(55, 55), 2, 3686480);
touch_handle_update(1, TouchState_FingerDown, &GPoint(33, 33), 7, 3686500);
touch_handle_driver_event(TouchDriverEvent_ControllerError);
PebbleEvent event = fake_event_get_last();
// Touches cancelled event generated
cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT);
cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesCancelled);
// no more touches
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
cl_assert_equal_p(touch_event, NULL);
touch_event = touch_event_queue_get_event(1, 0);
cl_assert_equal_p(touch_event, NULL);
}
// test that the first dispatch after a cancel event is pended does not return any touches, even
// if new touches have arrived - this is to ensure that the valid new touches are not cancelled
// by the cancellation event if it is pended before previous touches
void test_touch__cancel_touches_handle_first_dispatch(void) {
touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420);
touch_handle_driver_event(TouchDriverEvent_ControllerError);
PebbleEvent event = fake_event_get_last();
cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT);
cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesCancelled);
touch_handle_update(0, TouchState_FingerDown, &GPoint(15, 15), 6, 3686440);
// make sure that another event is, in fact, pended
event = fake_event_get_last();
cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT);
cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable);
TouchEventContext ctx = {
.idx = 0
};
// handle first TouchesAvailable event
touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx);
cl_assert_equal_i(ctx.idx, 0);
// handle second TouchesAvailable event
touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx);
cl_assert_equal_i(ctx.idx, 1);
prv_test_touch_event(&ctx.touch_events[0], 0, TouchEvent_Touchdown, &GPoint(15, 15), 3686440, 6,
NULL, 0, 0, false);
}
static PebbleEvent s_expected_palm_events[2];
static int s_palm_event_count = 0;
static void prv_handle_palm_events(PebbleEvent *e) {
s_expected_palm_events[s_palm_event_count++] = *e;
}
void test_touch__palm_detect_event(void) {
touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420);
touch_handle_update(1, TouchState_FingerDown, &GPoint(55, 55), 2, 3686480);
fake_event_set_callback(prv_handle_palm_events);
touch_handle_driver_event(TouchDriverEvent_PalmDetect);
// Cancelled event, followed by a palm detection event
cl_assert_equal_i(s_expected_palm_events[0].type, PEBBLE_TOUCH_EVENT);
cl_assert_equal_i(s_expected_palm_events[0].touch.type, PebbleTouchEvent_TouchesCancelled);
cl_assert_equal_i(s_expected_palm_events[1].type, PEBBLE_TOUCH_EVENT);
cl_assert_equal_i(s_expected_palm_events[1].touch.type, PebbleTouchEvent_PalmDetected);
TouchEvent *touch_event = touch_event_queue_get_event(0, 0);
cl_assert_equal_p(touch_event, NULL);
touch_event = touch_event_queue_get_event(1, 0);
cl_assert_equal_p(touch_event, NULL);
}