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

278 lines
8.2 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 "fake_app_manager.h"
#include "fake_new_timer.h"
#include "fake_pbl_malloc.h"
#include "fake_pebble_tasks.h"
#include "fake_system_task.h"
#include "stubs_analytics.h"
#include "stubs_analytics_external.h"
#include "stubs_gettext.h"
#include "stubs_logging.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_persist.h"
#include "stubs_prompt.h"
#include "stubs_queue.h"
#include "stubs_resources.h"
#include "stubs_serial.h"
#include "stubs_syscall_internal.h"
#include "stubs_worker_manager.h"
#include "drivers/accel.h"
#include "services/common/event_service.h"
#include "util/math.h"
#include "util/size.h"
#include <stdio.h>
// helpers from accel manager
extern void test_accel_manager_get_subsample_info(
AccelManagerState *state, uint16_t *num, uint16_t *den, uint16_t *samps_per_update);
extern void test_accel_manager_reset(void);
// stub
void event_service_init(PebbleEventType type,
EventServiceAddSubscriberCallback start_cb,
EventServiceRemoveSubscriberCallback stop_cb) {}
void sys_vibe_history_start_collecting(void) {}
void sys_vibe_history_stop_collecting(void) {}
int32_t sys_vibe_get_vibe_strength(void) {
return 0;
}
void accel_set_shake_sensitivity_high(bool sensitivity_high) {}
QueueHandle_t pebble_task_get_to_queue(PebbleTask task) {
return NULL;
}
// fake accel.h impl
static int s_sampling_interval_us = 1000000 / ACCEL_SAMPLING_25HZ;
static int s_num_samples = 0;
//! If true, ignore attempts to change the sampling interval
static bool s_force_sampling_interval;
uint32_t accel_set_sampling_interval(uint32_t interval_us) {
if (!s_force_sampling_interval) {
s_sampling_interval_us = interval_us;
}
return accel_get_sampling_interval();
}
uint32_t accel_get_sampling_interval(void) {
return s_sampling_interval_us;
}
void accel_set_num_samples(uint32_t num_samples) {
s_num_samples = num_samples;
}
int accel_peek(AccelDriverSample *data) {
return 0;
}
void accel_enable_shake_detection(bool on) {
}
bool accel_get_shake_detection_enabled(void) {
return false;
}
void accel_enable_double_tap_detection(bool on) {
}
bool accel_get_double_tap_detection_enabled(void) {
return false;
}
bool accel_run_selftest(void) {
return true;
}
bool gyro_run_selftest(void) {
return true;
}
bool new_timer_add_work_callback_from_isr(NewTimerWorkCallback cb, void *data) {
return false;
}
bool new_timer_add_work_callback(NewTimerWorkCallback cb, void *data) {
return true;
}
// Unit Test Code
void test_accel_manager__initialize(void) {
accel_manager_init();
s_sampling_interval_us = 1000000 / ACCEL_SAMPLING_25HZ;
s_num_samples = 0;
s_force_sampling_interval = false;
}
void test_accel_manager__cleanup(void) {
test_accel_manager_reset();
}
static void prv_noop_sample_handler(void *context) {
}
static void prv_validate_sample_rates(int *arr, int num_samples) {
for (int i = 0; i < num_samples; i++) {
// force a compiler error if user has not added all possible sample rates to array
switch ((AccelSamplingRate)arr[i]) {
case ACCEL_SAMPLING_10HZ:
case ACCEL_SAMPLING_25HZ:
case ACCEL_SAMPLING_50HZ:
case ACCEL_SAMPLING_100HZ:
break;
default:
cl_assert(0);
}
}
}
static void prv_run_accel_test(int *sample_arr, int num_items) {
PebbleTask tasks[] = { PebbleTask_KernelMain, PebbleTask_Worker, PebbleTask_App };
AccelManagerState* sessions[3];
if (num_items > 3) {
return; // we only support 3 simultaneous subscribers
}
int fastest_rate = 0;
AccelRawData fake_buf[1];
for (int i = 0; i < num_items; i++) {
if (fastest_rate < sample_arr[i]) {
fastest_rate = sample_arr[i];
}
sessions[i] = sys_accel_manager_data_subscribe(
sample_arr[i], prv_noop_sample_handler, NULL, tasks[i]);
// buffer size of 1
sys_accel_manager_set_sample_buffer(sessions[i], fake_buf, 1);
}
// make sure all sampling rates are what they should be
for (int i = 0; i < num_items; i++) {
stub_pebble_tasks_set_current(tasks[i]);
uint16_t num, den, samps_per_update;
test_accel_manager_get_subsample_info(sessions[i], &num, &den, &samps_per_update);
if ((fastest_rate % sample_arr[i]) == 0) {
// the current sample rate is a multiple of the rate we are running at
cl_assert_equal_i(num, 1);
cl_assert_equal_i(den, fastest_rate / sample_arr[i]);
cl_assert_equal_i(samps_per_update, 1);
} else {
// the sample rate is not an even multiple of our fastest rate
uint32_t gcd_of_rates = gcd(fastest_rate, sample_arr[i]);
cl_assert_equal_i(num, sample_arr[i] / gcd_of_rates);
cl_assert_equal_i(den, fastest_rate / gcd_of_rates);
cl_assert_equal_i(samps_per_update, 1);
}
}
cl_assert_equal_i(1000000 / s_sampling_interval_us, fastest_rate);
cl_assert_equal_i(s_num_samples, 1);
for (int i = 0; i < num_items; i++) {
sys_accel_manager_data_unsubscribe(sessions[i]);
stub_pebble_tasks_set_current(tasks[i]);
}
}
// enumerate through all possible sampling rate combinations and confirm
// that the correct frequency is selected
void test_accel_manager__subscription_sampling_rates(void) {
int sample_rates[] = { ACCEL_SAMPLING_10HZ, ACCEL_SAMPLING_25HZ,
ACCEL_SAMPLING_50HZ, ACCEL_SAMPLING_100HZ};
prv_validate_sample_rates(sample_rates, ARRAY_LENGTH(sample_rates));
int poss_rates = ARRAY_LENGTH(sample_rates);
int max_permutations = 0x1 << poss_rates;
for (int mask = 0; mask < max_permutations; mask++) {
int count = __builtin_popcount(mask);
if (count == 0) {
continue; // we don't care about the empty set
}
int test_rates[count];
int idx = 0;
for (int j = 0; j < poss_rates; j++) {
if ((mask & (0x1 << j)) != 0) {
test_rates[idx] = sample_rates[j];
idx++;
}
}
printf("Testing: ");
for (int i = 0; i < count; i++) {
printf("%d ", sample_rates[i]);
}
printf("\n");
prv_run_accel_test(test_rates, count);
}
}
void test_accel_manager__jitterfree(void) {
// Force the fake accel to only support the 125hz sample rate.
s_force_sampling_interval = true;
s_sampling_interval_us = (1000000000 / 125000);
AccelRawData fake_buf[1];
AccelManagerState *state = sys_accel_manager_data_subscribe(
ACCEL_SAMPLING_25HZ, prv_noop_sample_handler, NULL, PebbleTask_KernelMain);
uint32_t resulting_mhz = accel_manager_set_jitterfree_sampling_rate(state, 12500);
sys_accel_manager_set_sample_buffer(state , fake_buf, ARRAY_LENGTH(fake_buf));
cl_assert_equal_i(resulting_mhz, 12500);
uint16_t num, den, samples_per_update;
test_accel_manager_get_subsample_info(state, &num, &den, &samples_per_update);
cl_assert_equal_i(num, 1);
cl_assert_equal_i(den, 10);
cl_assert_equal_i(samples_per_update, ARRAY_LENGTH(fake_buf));
}
void test_accel_manager__batched_samples(void) {
AccelRawData fake_buf[30];
stub_pebble_tasks_set_current(PebbleTask_KernelMain);
AccelManagerState *main_session = sys_accel_manager_data_subscribe(
ACCEL_SAMPLING_10HZ, prv_noop_sample_handler, NULL, PebbleTask_KernelMain);
sys_accel_manager_set_sample_buffer(main_session, fake_buf, 11);
stub_pebble_tasks_set_current(PebbleTask_Worker);
AccelManagerState *worker_session = sys_accel_manager_data_subscribe(
ACCEL_SAMPLING_25HZ, prv_noop_sample_handler, NULL, PebbleTask_KernelMain);
sys_accel_manager_set_sample_buffer(worker_session, fake_buf, 22);
cl_assert_equal_i(s_num_samples, 22);
stub_pebble_tasks_set_current(PebbleTask_KernelMain);
sys_accel_manager_set_sample_buffer(main_session, fake_buf, 3);
cl_assert_equal_i(s_num_samples, 7); /* 300ms / (1000ms / 25 samps) */
}