mirror of
https://github.com/google/pebble.git
synced 2025-03-21 19:31:20 +00:00
387 lines
14 KiB
C
387 lines
14 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/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);
|
||
|
}
|