/* * 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/event_service_client.h" #include "services/normal/accessory/smartstrap_attribute.h" #include "kernel/pbl_malloc.h" #include "fake_smartstrap_profiles.h" #include "fake_smartstrap_state.h" #include "fake_system_task.h" #include "stubs_app_state.h" #include "stubs_logging.h" #include "stubs_mutex.h" #include "stubs_passert.h" #include "stubs_pbl_malloc.h" #include "stubs_serial.h" #define NON_NULL_MBUF ((MBuf *)1) #define assert_result_ok(result) cl_assert(result == SmartstrapResultOk) #define assert_result_invalid(result) cl_assert(result == SmartstrapResultInvalidArgs) #define assert_result_busy(result) cl_assert(result == SmartstrapResultBusy) typedef void (*EventServiceEventHandler)(PebbleEvent *e, void *context); typedef struct { bool active; SmartstrapAttribute *attribute; size_t length; } PendingInfo; static EventServiceEventHandler s_event_handler; static PendingInfo s_pending_did_read; static PendingInfo s_pending_did_write; static PendingInfo s_pending_notified; // Stubs / fakes void event_service_client_subscribe(EventServiceInfo *info) { cl_assert(info->type == PEBBLE_SMARTSTRAP_EVENT); s_event_handler = info->handler; } void event_service_client_unsubscribe(EventServiceInfo *info) { cl_assert(info->type == PEBBLE_SMARTSTRAP_EVENT); s_event_handler = NULL; } bool process_manager_send_event_to_process(PebbleTask task, PebbleEvent *e) { cl_assert(task == PebbleTask_App); cl_assert(e->type == PEBBLE_SMARTSTRAP_EVENT); cl_assert(s_event_handler); s_event_handler(e, NULL); return true; } void smartstrap_cancel_send(void) { } // Helper functions static void prv_prepare_for_did_read(SmartstrapAttribute *attribute, uint16_t read_length) { cl_assert(!s_pending_did_read.active); s_pending_did_read = (PendingInfo) { .active = true, .attribute = attribute, .length = read_length }; } static void prv_did_read_handler(SmartstrapAttribute *attribute, SmartstrapResult result, const uint8_t *data, size_t length) { cl_assert(data == (uint8_t *)attribute); cl_assert(result == SmartstrapResultOk); cl_assert(s_pending_did_read.active); cl_assert(s_pending_did_read.attribute == attribute); cl_assert(s_pending_did_read.length == length); s_pending_did_read.active = false; } static void prv_prepare_for_did_write(SmartstrapAttribute *attribute) { cl_assert(!s_pending_did_write.active); s_pending_did_write = (PendingInfo) { .active = true, .attribute = attribute }; } static void prv_did_write_handler(SmartstrapAttribute *attribute, SmartstrapResult result) { cl_assert(result == SmartstrapResultOk); cl_assert(s_pending_did_write.active); cl_assert(s_pending_did_write.attribute == attribute); s_pending_did_write.active = false; } static void prv_prepare_for_notified(SmartstrapAttribute *attribute) { cl_assert(!s_pending_notified.active); s_pending_notified = (PendingInfo) { .active = true, .attribute = attribute }; } static void prv_notified_handler(SmartstrapAttribute *attribute) { cl_assert(s_pending_notified.active); cl_assert(s_pending_notified.attribute == attribute); s_pending_notified.active = false; } // Setup void test_app_smartstrap__initialize(void) { smartstrap_attribute_init(); app_smartstrap_subscribe((SmartstrapHandlers) { .did_read = prv_did_read_handler, .did_write = prv_did_write_handler, .notified = prv_notified_handler }); } void test_app_smartstrap__cleanup(void) { } // Tests void test_app_smartstrap__invalid_args(void) { // create test attribute SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); // smartstrap_attribute_create() cl_assert(app_smartstrap_attribute_create(0x1111, 0x2222, 0) == NULL); // smartstrap_attribute_destroy() app_smartstrap_attribute_destroy(NULL); // smartstrap_attribute_get_*_id() cl_assert(app_smartstrap_attribute_get_service_id(NULL) == 0); cl_assert(app_smartstrap_attribute_get_attribute_id(NULL) == 0); // smartstrap_attribute_begin_write() uint8_t *buffer; size_t buffer_len; assert_result_invalid(app_smartstrap_attribute_begin_write(NULL, NULL, NULL)); assert_result_invalid(app_smartstrap_attribute_begin_write(NULL, &buffer, NULL)); assert_result_invalid(app_smartstrap_attribute_begin_write(NULL, NULL, &buffer_len)); assert_result_invalid(app_smartstrap_attribute_begin_write(NULL, &buffer, &buffer_len)); assert_result_invalid(app_smartstrap_attribute_begin_write(attr, NULL, NULL)); assert_result_invalid(app_smartstrap_attribute_begin_write(attr, &buffer, NULL)); assert_result_invalid(app_smartstrap_attribute_begin_write(attr, NULL, &buffer_len)); // smartstrap_attribute_end_write() assert_result_invalid(app_smartstrap_attribute_end_write(NULL, 0, false)); assert_result_invalid(app_smartstrap_attribute_end_write(NULL, 0, true)); assert_result_invalid(app_smartstrap_attribute_end_write(NULL, 100, false)); assert_result_invalid(app_smartstrap_attribute_end_write(NULL, 100, true)); assert_result_invalid(app_smartstrap_attribute_end_write(attr, 0, false)); assert_result_invalid(app_smartstrap_attribute_end_write(attr, 0, true)); assert_result_invalid(app_smartstrap_attribute_end_write(attr, 100, false)); assert_result_invalid(app_smartstrap_attribute_end_write(attr, 100, true)); // smartstrap_attribute_read() assert_result_invalid(app_smartstrap_attribute_read(NULL)); // destroy test attribute app_smartstrap_attribute_destroy(attr); } void test_app_smartstrap__check_ids(void) { // craete an attribute SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); cl_assert(attr != NULL); // verify the ids cl_assert(app_smartstrap_attribute_get_service_id(attr) == 0x1111); cl_assert(app_smartstrap_attribute_get_attribute_id(attr) == 0x2222); // destroy the attribute app_smartstrap_attribute_destroy(attr); // verify that we can no longer get the ids cl_assert(app_smartstrap_attribute_get_service_id(attr) == 0); cl_assert(app_smartstrap_attribute_get_attribute_id(attr) == 0); } void test_app_smartstrap__create_duplicate(void) { // create the attribute once SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); cl_assert(attr != NULL); // try to create it again SmartstrapAttribute *attr_dup = app_smartstrap_attribute_create(0x1111, 0x2222, 100); cl_assert(attr_dup == NULL); // destroy the attribute app_smartstrap_attribute_destroy(attr); // destroy again (shouldn't do anything bad) app_smartstrap_attribute_destroy(attr); } void test_app_smartstrap__read(void) { // create the attribute SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); // start a read request stub_pebble_tasks_set_current(PebbleTask_App); assert_result_ok(app_smartstrap_attribute_read(attr)); // attempt to issue another read request assert_result_busy(app_smartstrap_attribute_read(attr)); // trigger the read request to be sent and expect a did_write handler call stub_pebble_tasks_set_current(PebbleTask_KernelBackground); prv_prepare_for_did_write(attr); cl_assert(smartstrap_attribute_send_pending()); cl_assert(!s_pending_did_write.active); // attempt to issue another read request (should report busy) assert_result_busy(app_smartstrap_attribute_read(attr)); // check that it was sent successfully SmartstrapRequest request = { .service_id = 0x1111, .attribute_id = 0x2222, .write_mbuf = NULL, .read_mbuf = NON_NULL_MBUF, .timeout_ms = SMARTSTRAP_TIMEOUT_DEFAULT }; fake_smartstrap_profiles_check_request_params(&request); // fake the response and expect a did_read handler call prv_prepare_for_did_read(attr, 10); smartstrap_attribute_send_event(SmartstrapDataReceivedEvent, SmartstrapProfileGenericService, SmartstrapResultOk, 0x1111, 0x2222, 10); cl_assert(!s_pending_did_read.active); // destroy the attribute app_smartstrap_attribute_destroy(attr); } void test_app_smartstrap__write(void) { // create the attribute SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); // start a write request stub_pebble_tasks_set_current(PebbleTask_App); uint8_t *write_buffer = NULL; size_t write_length = 0; assert_result_ok(app_smartstrap_attribute_begin_write(attr, &write_buffer, &write_length)); cl_assert(write_buffer == (uint8_t *)attr); cl_assert(write_length == 100); // attempt to start another write request assert_result_busy(app_smartstrap_attribute_read(attr)); uint8_t *write_buffer2 = NULL; size_t write_length2 = 0; assert_result_busy(app_smartstrap_attribute_begin_write(attr, &write_buffer2, &write_length2)); cl_assert(write_buffer2 == NULL); cl_assert(write_length2 == 0); // end the write request without sending anything assert_result_invalid(app_smartstrap_attribute_end_write(attr, 0, false)); // start the write request again write_buffer = NULL; write_length = 0; assert_result_ok(app_smartstrap_attribute_begin_write(attr, &write_buffer, &write_length)); cl_assert(write_buffer == (uint8_t *)attr); cl_assert(write_length == 100); // end the write request assert_result_ok(app_smartstrap_attribute_end_write(attr, 100, false)); // trigger the write request to be sent stub_pebble_tasks_set_current(PebbleTask_KernelBackground); cl_assert(smartstrap_attribute_send_pending()); // check that it was sent successfully SmartstrapRequest request = { .service_id = 0x1111, .attribute_id = 0x2222, .write_mbuf = NON_NULL_MBUF, .read_mbuf = NULL, .timeout_ms = SMARTSTRAP_TIMEOUT_DEFAULT }; // fake the ACK and expect a did_write handler call fake_smartstrap_profiles_check_request_params(&request); prv_prepare_for_did_write(attr); smartstrap_attribute_send_event(SmartstrapDataReceivedEvent, SmartstrapProfileGenericService, SmartstrapResultOk, 0x1111, 0x2222, 100); cl_assert(!s_pending_did_write.active); // destroy the attribute app_smartstrap_attribute_destroy(attr); } void test_app_smartstrap__write_read(void) { // create the attribute SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); // start a write request stub_pebble_tasks_set_current(PebbleTask_App); uint8_t *write_buffer = NULL; size_t write_length = 0; assert_result_ok(app_smartstrap_attribute_begin_write(attr, &write_buffer, &write_length)); cl_assert(write_buffer == (uint8_t *)attr); cl_assert(write_length == 100); // end the write request without sending anything assert_result_invalid(app_smartstrap_attribute_end_write(attr, 0, true)); // start the write request again write_buffer = NULL; write_length = 0; assert_result_ok(app_smartstrap_attribute_begin_write(attr, &write_buffer, &write_length)); cl_assert(write_buffer == (uint8_t *)attr); cl_assert(write_length == 100); // end the write request with request_read=true assert_result_ok(app_smartstrap_attribute_end_write(attr, 100, true)); // trigger the write request to be sent and expect a did_write handler call stub_pebble_tasks_set_current(PebbleTask_KernelBackground); prv_prepare_for_did_write(attr); cl_assert(smartstrap_attribute_send_pending()); cl_assert(!s_pending_did_write.active); // check that it was sent successfully SmartstrapRequest request = { .service_id = 0x1111, .attribute_id = 0x2222, .write_mbuf = NON_NULL_MBUF, .read_mbuf = NON_NULL_MBUF, .timeout_ms = SMARTSTRAP_TIMEOUT_DEFAULT }; // fake the response and expect a did_write and a did_read handler call fake_smartstrap_profiles_check_request_params(&request); prv_prepare_for_did_read(attr, 100); smartstrap_attribute_send_event(SmartstrapDataReceivedEvent, SmartstrapProfileGenericService, SmartstrapResultOk, 0x1111, 0x2222, 100); cl_assert(!s_pending_did_read.active); // destroy the attribute app_smartstrap_attribute_destroy(attr); } void test_app_smartstrap__notify(void) { // create the attribute SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); // send a notification and expect a notified handler call prv_prepare_for_notified(attr); smartstrap_attribute_send_event(SmartstrapNotifyEvent, SmartstrapProfileGenericService, SmartstrapResultOk, 0x1111, 0x2222, 0); cl_assert(!s_pending_notified.active); // send a notification for a non-created attribute which shouldn't cause a notified handler call smartstrap_attribute_send_event(SmartstrapNotifyEvent, SmartstrapProfileGenericService, SmartstrapResultOk, 0x1111, 0x3333, 0); // destroy the attribute app_smartstrap_attribute_destroy(attr); // send a notification for the destroyed attribute which shouldn't cause a notified handler call smartstrap_attribute_send_event(SmartstrapNotifyEvent, SmartstrapProfileGenericService, SmartstrapResultOk, 0x1111, 0x2222, 0); }