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

458 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 "BSCAPI.h"
#include "clar.h"
#include "bluetooth/bt_driver_comm.h"
#include "services/common/comm_session/session.h"
#include "services/common/comm_session/session_remote_version.h"
#include "services/common/comm_session/session_send_buffer.h"
#include "services/common/comm_session/session_transport.h"
#include "kernel/events.h"
extern void comm_session_set_capabilities(CommSession *session,
CommSessionCapability capability_flags);
extern bool comm_session_is_valid(const CommSession *session);
extern void comm_session_init(void);
extern void comm_session_deinit(void);
extern void comm_session_send_next_immediately(CommSession *session);
// Stubs
///////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_bt_lock.h"
#include "stubs_bt_stack.h"
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_rand_ptr.h"
#include "stubs_syscall_internal.h"
void comm_session_analytics_open_session(CommSession *session) {
}
void comm_session_analytics_close_session(CommSession *session, CommSessionCloseReason reason) {
}
void comm_session_receive_router_cleanup(CommSession *session) {
}
void comm_session_send_queue_cleanup(CommSession *session) {
}
void bt_persistent_storage_set_cached_system_capabilities(
const PebbleProtocolCapabilities *capabilities) {
}
static uint16_t s_send_queue_length;
size_t comm_session_send_queue_get_length(const CommSession *session) {
return s_send_queue_length;
}
void fake_session_send_queue_set_length(uint32_t length) {
s_send_queue_length = length;
}
static bool s_dls_private_handle_disconnect_called;
void dls_private_handle_disconnect(void *data) {
s_dls_private_handle_disconnect_called = true;
}
static bool s_comm_session_event_put;
void event_put(PebbleEvent* event) {
if (event->type == PEBBLE_COMM_SESSION_EVENT &&
event->bluetooth.comm_session_event.is_system) {
s_comm_session_event_put = true;
};
}
void app_launch_trigger(void) {
}
void session_remote_version_start_requests(CommSession *session) {
}
static int s_send_next_count;
static void prv_send_next(Transport *transport) {
++s_send_next_count;
}
static int s_close_count;
Transport *s_last_closed_transport;
static void prv_close(Transport *transport) {
++s_close_count;
s_last_closed_transport = transport;
}
static int s_reset_count;
static void prv_reset(Transport *transport) {
++s_reset_count;
}
typedef enum {
TransportIDNull,
TransportID1,
TransportID2,
TransportID3,
NumTransportID,
} TransportID;
static Uuid *s_transport_uuid[NumTransportID];
static const Uuid *prv_get_uuid(Transport *transport) {
TransportID i = (uintptr_t)transport;
cl_assert(i >= TransportID1 && i <= TransportID3);
return s_transport_uuid[i];
}
static CommSessionTransportType prv_get_type(struct Transport *transport) {
return CommSessionTransportType_QEMU;
}
static const TransportImplementation s_transport_imp = {
.send_next = prv_send_next,
.close = prv_close,
.reset = prv_reset,
.get_uuid = prv_get_uuid,
.get_type = prv_get_type,
};
// Fakes
///////////////////////////////////////////////////////////
#include "fake_kernel_malloc.h"
#include "fake_session_send_buffer.h"
#include "fake_system_task.h"
#include "fake_app_manager.h"
static void prv_system_task_cb(void *data) {
CommSession *session = (CommSession *)data;
bt_driver_run_send_next_job(session, true);
}
bool bt_driver_comm_schedule_send_next_job(CommSession *data) {
// Implement this API in this test suite using the fake_system_task:
system_task_add_callback(prv_system_task_cb, data);
return true;
}
static bool s_bt_driver_comm_is_current_task_send_next_task;
bool bt_driver_comm_is_current_task_send_next_task(void) {
return s_bt_driver_comm_is_current_task_send_next_task;
}
// Tests
///////////////////////////////////////////////////////////
void test_session__initialize(void) {
stub_app_init();
comm_session_init();
fake_kernel_malloc_init();
fake_kernel_malloc_mark();
fake_session_send_buffer_init();
s_send_queue_length = 0;
memset(s_transport_uuid, 0, sizeof(s_transport_uuid));
s_send_next_count = 0;
s_reset_count = 0;
s_close_count = 0;
s_last_closed_transport = NULL;
s_dls_private_handle_disconnect_called = false;
s_comm_session_event_put = false;
s_bt_driver_comm_is_current_task_send_next_task = false;
}
void test_session__cleanup(void) {
// Check for leaks:
fake_kernel_malloc_mark_assert_equal();
fake_kernel_malloc_deinit();
fake_system_task_callbacks_cleanup();
}
void test_session__get_system_session_disconnected_returns_null(void) {
cl_assert_equal_p(comm_session_get_system_session(), NULL);
}
void test_session__get_app_session_disconnected_returns_null(void) {
cl_assert_equal_p(comm_session_get_current_app_session(), NULL);
}
void test_session__send_data_returns_false_for_null_session(void) {
const uint16_t endpoint_id = 1234;
uint8_t data[] = {1, 2, 3, 4};
cl_assert_equal_b(comm_session_send_data(NULL, endpoint_id,
data, sizeof(data),
COMM_SESSION_DEFAULT_TIMEOUT), false);
}
void test_session__basic_open_close(void) {
Transport *transport = (Transport *) TransportID1;
CommSession *session = comm_session_open(transport, &s_transport_imp,
TransportDestinationSystem);
cl_assert(session);
cl_assert_equal_b(comm_session_is_valid(session), true);
comm_session_close(session, CommSessionCloseReason_UnderlyingDisconnection);
cl_assert_equal_b(comm_session_is_valid(session), false);
}
void test_session__get_type_system(void) {
Transport *transport = (Transport *) TransportID1;
CommSession *session = comm_session_open(transport, &s_transport_imp,
TransportDestinationSystem);
cl_assert_equal_i(comm_session_get_type(session), CommSessionTypeSystem);
cl_assert_equal_p(comm_session_get_system_session(), session);
cl_assert_equal_p(comm_session_get_by_type(CommSessionTypeSystem), session);
comm_session_close(session, CommSessionCloseReason_UnderlyingDisconnection);
fake_system_task_callbacks_invoke_pending(); // get dls called
cl_assert_equal_b(s_dls_private_handle_disconnect_called, true);
cl_assert_equal_b(s_comm_session_event_put, true);
}
void test_session__get_type_app(void) {
Transport *transport = (Transport *) TransportID1;
CommSession *session = comm_session_open(transport, &s_transport_imp,
TransportDestinationApp);
cl_assert_equal_i(comm_session_get_type(session), CommSessionTypeApp);
cl_assert_equal_p(comm_session_get_current_app_session(), session);
cl_assert_equal_p(comm_session_get_by_type(CommSessionTypeApp), session);
comm_session_close(session, CommSessionCloseReason_UnderlyingDisconnection);
fake_system_task_callbacks_invoke_pending(); // get dls called
cl_assert_equal_b(s_dls_private_handle_disconnect_called, false);
cl_assert_equal_b(s_comm_session_event_put, false);
}
void test_session__last_system_session_wins(void) {
Transport *system_transport = (Transport *) TransportID1;
CommSession *system_session = comm_session_open(system_transport, &s_transport_imp,
TransportDestinationSystem);
cl_assert_equal_i(s_close_count, 0);
Transport *system_transport2 = (Transport *) TransportID2;
CommSession *system_session2 = comm_session_open(system_transport2, &s_transport_imp,
TransportDestinationSystem);
cl_assert(system_session2);
cl_assert_equal_p(s_last_closed_transport, system_transport);
cl_assert_equal_i(s_close_count, 1);
comm_session_close(system_session2, CommSessionCloseReason_UnderlyingDisconnection);
// The transport's .close is supposed to call this.
// The stub in this test doens't, so clean up manually:
comm_session_close(system_session, CommSessionCloseReason_UnderlyingDisconnection);
}
void test_session__get_app_session_multiple(void) {
Transport *system_transport = (Transport *) TransportID1;
CommSession *system_session = comm_session_open(system_transport, &s_transport_imp,
TransportDestinationSystem);
Uuid legacy_app_uuid = {
0xff, 0xc5, 0x24, 0x01, 0x4d, 0xbe, 0x40, 0x8b,
0xb7, 0x3a, 0x0e, 0x80, 0xef, 0x09, 0xaf, 0x74};
// Legacy transport (iAP) isn't aware of the app UUID, so don't set anything:
Transport *legacy_transport = (Transport *) TransportID2;
CommSession *legacy_app_session = comm_session_open(legacy_transport, &s_transport_imp,
TransportDestinationApp);
Uuid modern_app_uuid = {
0x04, 0xc5, 0x24, 0x01, 0x4d, 0xbe, 0x40, 0x8b,
0xb7, 0x3a, 0x0e, 0x80, 0xef, 0x09, 0xaf, 0x74};
Transport *modern_transport = (Transport *) TransportID3;
s_transport_uuid[TransportID3] = &modern_app_uuid;
CommSession *modern_app_session = comm_session_open(modern_transport, &s_transport_imp,
TransportDestinationApp);
stub_app_set_uuid(legacy_app_uuid);
cl_assert_equal_p(comm_session_get_current_app_session(), legacy_app_session);
stub_app_set_uuid(modern_app_uuid);
cl_assert_equal_p(comm_session_get_current_app_session(), modern_app_session);
stub_app_set_uuid((Uuid)UUID_INVALID);
cl_assert_equal_p(comm_session_get_current_app_session(), NULL);
stub_app_set_uuid((Uuid)UUID_SYSTEM);
cl_assert_equal_p(comm_session_get_current_app_session(), NULL);
comm_session_close(system_session, CommSessionCloseReason_UnderlyingDisconnection);
comm_session_close(legacy_app_session, CommSessionCloseReason_UnderlyingDisconnection);
comm_session_close(modern_app_session, CommSessionCloseReason_UnderlyingDisconnection);
}
void test_session__assert_if_deinit_and_transport_did_not_clean_up_properly(void) {
Transport *transport = (Transport *) TransportID1;
CommSession *session = comm_session_open(transport, &s_transport_imp,
TransportDestinationSystem);
cl_assert_equal_b(comm_session_is_valid(session), true);
// Expect assert when Transport didn't clean up after itself:
cl_assert_passert(comm_session_deinit());
comm_session_close(session, CommSessionCloseReason_UnderlyingDisconnection);
}
void test_session__send_next_deduping(void) {
Transport *transport = (Transport *) TransportID1;
CommSession *session = comm_session_open(transport, &s_transport_imp,
TransportDestinationSystem);
cl_assert_equal_b(comm_session_is_valid(session), true);
fake_session_send_queue_set_length(1234);
cl_assert_equal_i(fake_system_task_count_callbacks(), 0);
comm_session_send_next(session);
cl_assert_equal_i(fake_system_task_count_callbacks(), 1);
comm_session_send_next(session);
cl_assert_equal_i(fake_system_task_count_callbacks(), 1);
comm_session_send_next(session);
cl_assert_equal_i(fake_system_task_count_callbacks(), 1);
comm_session_send_next(session);
cl_assert_equal_i(fake_system_task_count_callbacks(), 1);
comm_session_send_next(session);
cl_assert_equal_i(fake_system_task_count_callbacks(), 1);
cl_assert_equal_i(s_send_next_count, 0);
fake_system_task_callbacks_invoke_pending();
cl_assert_equal_i(fake_system_task_count_callbacks(), 0);
cl_assert_equal_i(s_send_next_count, 1);
comm_session_send_next(session);
cl_assert_equal_i(fake_system_task_count_callbacks(), 1);
comm_session_send_next(session);
cl_assert_equal_i(fake_system_task_count_callbacks(), 1);
cl_assert_equal_i(s_send_next_count, 1);
fake_system_task_callbacks_invoke_pending();
cl_assert_equal_i(s_send_next_count, 2);
comm_session_close(session, CommSessionCloseReason_UnderlyingDisconnection);
}
void test_session__send_next_not_called_when_session_closed_in_mean_time(void) {
Transport *transport = (Transport *) TransportID1;
CommSession *session = comm_session_open(transport, &s_transport_imp,
TransportDestinationSystem);
cl_assert_equal_b(comm_session_is_valid(session), true);
fake_session_send_queue_set_length(1234);
cl_assert_equal_i(fake_system_task_count_callbacks(), 0);
comm_session_send_next(session);
comm_session_close(session, CommSessionCloseReason_UnderlyingDisconnection);
fake_system_task_callbacks_invoke_pending();
cl_assert_equal_i(fake_system_task_count_callbacks(), 0);
cl_assert_equal_i(s_send_next_count, 0);
}
static bool prv_schedule_send_next(CommSession *session) {
return true;
}
static bool s_is_current_task_schedule_task = false;
static bool prv_is_current_task_schedule_task(Transport *transport) {
return s_is_current_task_schedule_task;
}
void test_session__transport_send_next_task(void) {
TransportImplementation transport_imp = s_transport_imp;
transport_imp.schedule = prv_schedule_send_next;
transport_imp.is_current_task_schedule_task = prv_is_current_task_schedule_task;
extern bool comm_session_is_current_task_send_next_task(CommSession *session);
Transport *transport = (Transport *) TransportID1;
CommSession *session = comm_session_open(transport, &transport_imp,
TransportDestinationSystem);
s_is_current_task_schedule_task = true;
cl_assert_equal_b(comm_session_is_current_task_send_next_task(session), true);
s_is_current_task_schedule_task = false;
cl_assert_equal_b(comm_session_is_current_task_send_next_task(session), false);
comm_session_close(session, CommSessionCloseReason_UnderlyingDisconnection);
transport_imp.schedule = NULL;
transport_imp.is_current_task_schedule_task = NULL;
session = comm_session_open(transport, &transport_imp, TransportDestinationSystem);
s_bt_driver_comm_is_current_task_send_next_task = true;
cl_assert_equal_b(comm_session_is_current_task_send_next_task(session), true);
s_bt_driver_comm_is_current_task_send_next_task = false;
cl_assert_equal_b(comm_session_is_current_task_send_next_task(session), false);
comm_session_close(session, CommSessionCloseReason_UnderlyingDisconnection);
}
void test_session__reset_valid_session(void) {
Transport *transport = (Transport *) TransportID1;
CommSession *session = comm_session_open(transport, &s_transport_imp,
TransportDestinationSystem);
cl_assert_equal_b(comm_session_is_valid(session), true);
cl_assert_equal_i(s_reset_count, 0);
comm_session_reset(session);
cl_assert_equal_i(s_reset_count, 1);
comm_session_close(session, CommSessionCloseReason_UnderlyingDisconnection);
}
void test_session__reset_invalid_session(void) {
CommSession *invalid_session = (CommSession *) TransportID1;
cl_assert_equal_b(comm_session_is_valid(invalid_session), false);
cl_assert_equal_i(s_reset_count, 0);
comm_session_reset(invalid_session);
cl_assert_equal_i(s_reset_count, 0);
}
extern bool comm_session_send_next_is_scheduled(CommSession *session);
void test_session__send_next_is_schedule_flag_not_unset_after_immediate_call(void){
Transport *transport = (Transport *) TransportID1;
CommSession *session = comm_session_open(transport, &s_transport_imp,
TransportDestinationSystem);
cl_assert_equal_b(comm_session_is_valid(session), true);
comm_session_send_next(session);
cl_assert_equal_b(comm_session_send_next_is_scheduled(session), true);
// Calling comm_session_send_next_immediately should NOT result in the flag to get unset
comm_session_send_next_immediately(session);
cl_assert_equal_b(comm_session_send_next_is_scheduled(session), true);
fake_system_task_callbacks_invoke_pending();
cl_assert_equal_b(comm_session_send_next_is_scheduled(session), false);
comm_session_close(session, CommSessionCloseReason_UnderlyingDisconnection);
}
void test_session__capabilities(void) {
Transport *transport = (Transport *) TransportID1;
CommSession *session = comm_session_open(transport, &s_transport_imp,
TransportDestinationSystem);
for (int i = 0; i < (sizeof(int) * 8); ++i) {
CommSessionCapability capability = (1 << i);
cl_assert_equal_b(false, comm_session_has_capability(session, capability));
}
comm_session_set_capabilities(session, ~0);
for (int i = 0; i < (sizeof(int) * 8); ++i) {
CommSessionCapability capability = (1 << i);
cl_assert_equal_b(true, comm_session_has_capability(session, capability));
}
comm_session_close(session, CommSessionCloseReason_UnderlyingDisconnection);
}