mirror of
https://github.com/google/pebble.git
synced 2025-08-18 04:16:32 -04:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
272
tests/fw/services/comm_session/test_default_kernel_receiver.c
Normal file
272
tests/fw/services/comm_session/test_default_kernel_receiver.c
Normal file
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* 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 "services/common/comm_session/session_receive_router.h"
|
||||
#include "services/common/system_task.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// Stubs
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
|
||||
typedef void (*CallbackEventCallback)(void *data);
|
||||
|
||||
static int s_kernel_main_schedule_count;
|
||||
void launcher_task_add_callback(CallbackEventCallback callback, void *data) {
|
||||
++s_kernel_main_schedule_count;
|
||||
system_task_add_callback(callback, data);
|
||||
}
|
||||
|
||||
// Fakes
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#include "../../fakes/fake_pbl_malloc.h"
|
||||
#include "../../fakes/fake_system_task.h"
|
||||
|
||||
#define FAKE_COMM_SESSION (CommSession *)1
|
||||
|
||||
// Helpers
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
extern const ReceiverImplementation g_default_kernel_receiver_implementation;
|
||||
extern const PebbleTask g_default_kernel_receiver_opt_bg;
|
||||
extern const PebbleTask g_default_kernel_receiver_opt_main;
|
||||
|
||||
typedef enum {
|
||||
HandlerA = 0,
|
||||
HandlerB,
|
||||
HandlerC,
|
||||
NumHandlers
|
||||
} FakeProtocolHandlers;
|
||||
|
||||
static int s_handler_call_count[NumHandlers] = { 0 };
|
||||
|
||||
typedef struct {
|
||||
size_t len;
|
||||
uint8_t *buf;
|
||||
} ExpectedData;
|
||||
|
||||
static ExpectedData s_expected_data;
|
||||
|
||||
static void prv_set_expected_data(void *data, size_t len) {
|
||||
s_expected_data.buf = data;
|
||||
s_expected_data.len = len;
|
||||
}
|
||||
|
||||
static void prv_assert_data_matches_expected(const void *data, size_t len) {
|
||||
cl_assert_equal_i(len, s_expected_data.len);
|
||||
cl_assert(memcmp(s_expected_data.buf, data, len) == 0);
|
||||
}
|
||||
|
||||
static void prv_assert_no_handler_calls(void) {
|
||||
for (int i = 0; i < NumHandlers; i++) {
|
||||
cl_assert_equal_i(s_handler_call_count[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_endpoint_handler_a(
|
||||
CommSession *session, const uint8_t *data, size_t length) {
|
||||
s_handler_call_count[HandlerA]++;
|
||||
prv_assert_data_matches_expected(data, length);
|
||||
}
|
||||
|
||||
static void prv_endpoint_handler_b(
|
||||
CommSession *session, const uint8_t *data, size_t length) {
|
||||
s_handler_call_count[HandlerB]++;
|
||||
prv_assert_data_matches_expected(data, length);
|
||||
}
|
||||
|
||||
static void prv_endpoint_handler_c(
|
||||
CommSession *session, const uint8_t *data, size_t length) {
|
||||
s_handler_call_count[HandlerC]++;
|
||||
prv_assert_data_matches_expected(data, length);
|
||||
}
|
||||
|
||||
static const PebbleProtocolEndpoint s_endpoints[NumHandlers] = {
|
||||
[0] = {
|
||||
.handler = prv_endpoint_handler_a,
|
||||
.receiver_opt = &g_default_kernel_receiver_opt_bg,
|
||||
},
|
||||
[1] = {
|
||||
.handler = prv_endpoint_handler_b,
|
||||
.receiver_opt = &g_default_kernel_receiver_opt_bg,
|
||||
},
|
||||
[2] = {
|
||||
.handler = prv_endpoint_handler_c,
|
||||
.receiver_opt = &g_default_kernel_receiver_opt_main,
|
||||
},
|
||||
};
|
||||
|
||||
// Tests
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
void test_default_kernel_receiver__cleanup(void) {
|
||||
memset(s_handler_call_count, 0x0, sizeof(s_handler_call_count));
|
||||
s_kernel_main_schedule_count = 0;
|
||||
}
|
||||
|
||||
|
||||
//! With one event in flight, walk through the prepare, write, finish happy
|
||||
//! path. Ensure that the endpoint handler CB is run from kernel BG and that
|
||||
//! we don't leak memory
|
||||
void test_default_kernel_receiver__prepare_write_finish_single(void) {
|
||||
char *data = "helloworld";
|
||||
|
||||
Receiver *receiver = g_default_kernel_receiver_implementation.prepare(
|
||||
FAKE_COMM_SESSION, &s_endpoints[0], strlen(data));
|
||||
cl_assert(receiver != NULL);
|
||||
|
||||
g_default_kernel_receiver_implementation.write(receiver, (uint8_t *)data, strlen(data));
|
||||
prv_assert_no_handler_calls();
|
||||
|
||||
g_default_kernel_receiver_implementation.finish(receiver);
|
||||
|
||||
// CBs shouldn't immediately execute since they are pended on KernelBG
|
||||
prv_assert_no_handler_calls();
|
||||
|
||||
prv_set_expected_data(data, strlen(data));
|
||||
fake_system_task_callbacks_invoke_pending();
|
||||
cl_assert_equal_i(s_handler_call_count[HandlerA], 1);
|
||||
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), 0);
|
||||
}
|
||||
|
||||
//! Multiple sessions should be able to be transmitting messages concurrently
|
||||
void test_default_kernel_receiver__prepare_write_finish_multiple_sessions(void) {
|
||||
Receiver *receiver[NumHandlers];
|
||||
CommSession *session[NumHandlers] = {
|
||||
(CommSession *)1,
|
||||
(CommSession *)2,
|
||||
(CommSession *)3,
|
||||
};
|
||||
|
||||
char *data[NumHandlers] = {
|
||||
"Session 1 Data!!",
|
||||
"This is Session 2 Data!",
|
||||
"Session 3"
|
||||
};
|
||||
|
||||
for (int i = 0; i < NumHandlers; i++) {
|
||||
receiver[i] = g_default_kernel_receiver_implementation.prepare(
|
||||
session[i], &s_endpoints[i], strlen(data[i]));
|
||||
cl_assert(receiver[i] != NULL);
|
||||
|
||||
for (int j = 0; j < strlen(data[i]); j++) {
|
||||
g_default_kernel_receiver_implementation.write(
|
||||
receiver[i], (uint8_t *)&data[i][j], 1);
|
||||
}
|
||||
}
|
||||
|
||||
prv_assert_no_handler_calls();
|
||||
|
||||
for (int i = NumHandlers - 1; i >= 0; i--) {
|
||||
int kernel_main_schedule_count_before = s_kernel_main_schedule_count;
|
||||
|
||||
g_default_kernel_receiver_implementation.finish(receiver[i]);
|
||||
cl_assert_equal_i(s_handler_call_count[i], 0);
|
||||
|
||||
bool should_execute_on_kernel_main =
|
||||
(s_endpoints[i].receiver_opt == &g_default_kernel_receiver_opt_main);
|
||||
if (should_execute_on_kernel_main) {
|
||||
cl_assert_equal_i(kernel_main_schedule_count_before + 1, s_kernel_main_schedule_count);
|
||||
}
|
||||
|
||||
prv_set_expected_data(data[i], strlen(data[i]));
|
||||
fake_system_task_callbacks_invoke_pending();
|
||||
cl_assert_equal_i(s_handler_call_count[i], 1);
|
||||
}
|
||||
|
||||
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), 0);
|
||||
}
|
||||
|
||||
//! It's possible the same session can receiver multiple messages before any
|
||||
//! are processed on kernel BG. Make sure they do not interfere with one
|
||||
//! another
|
||||
void test_default_kernel_receiver__same_session_batched(void) {
|
||||
const int batch_num = 10;
|
||||
char data = 'a';
|
||||
for (int i = 0; i < batch_num; i++) {
|
||||
Receiver *receiver = g_default_kernel_receiver_implementation.prepare(
|
||||
FAKE_COMM_SESSION, &s_endpoints[0], 1);
|
||||
cl_assert(receiver != NULL);
|
||||
|
||||
g_default_kernel_receiver_implementation.write(
|
||||
receiver, (uint8_t *)&data, 1);
|
||||
|
||||
g_default_kernel_receiver_implementation.finish(receiver);
|
||||
|
||||
data += 1;
|
||||
}
|
||||
|
||||
prv_assert_no_handler_calls();
|
||||
|
||||
char expected_data = 'a';
|
||||
for (int i = 0; i < batch_num; i++) {
|
||||
prv_set_expected_data(&expected_data, 1);
|
||||
fake_system_task_callbacks_invoke(1);
|
||||
cl_assert_equal_i(s_handler_call_count[HandlerA], i + 1);
|
||||
expected_data += 1;
|
||||
}
|
||||
|
||||
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), 0);
|
||||
}
|
||||
|
||||
//! Make sure that if cleanup runs and we are partially through updating a
|
||||
//! buffer the callback does not get called
|
||||
void test_default_kernel_receiver__receiver_cleanup(void) {
|
||||
char *data = "cleanup test!";
|
||||
|
||||
Receiver *receiver = g_default_kernel_receiver_implementation.prepare(
|
||||
FAKE_COMM_SESSION, &s_endpoints[0], strlen(data));
|
||||
cl_assert(receiver != NULL);
|
||||
|
||||
g_default_kernel_receiver_implementation.write(receiver, (uint8_t *)data, strlen(data));
|
||||
prv_assert_no_handler_calls();
|
||||
|
||||
|
||||
g_default_kernel_receiver_implementation.cleanup(receiver);
|
||||
|
||||
fake_system_task_callbacks_invoke_pending();
|
||||
prv_assert_no_handler_calls();
|
||||
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), 0);
|
||||
}
|
||||
|
||||
//! Test the case where a callback has been pended to kernelBG and we get a cleanup
|
||||
void test_default_kernel_receiver__race_condition(void) {
|
||||
char *data = "cleanup but finish ran first!";
|
||||
|
||||
Receiver *receiver = g_default_kernel_receiver_implementation.prepare(
|
||||
FAKE_COMM_SESSION, &s_endpoints[0], strlen(data));
|
||||
cl_assert(receiver != NULL);
|
||||
|
||||
g_default_kernel_receiver_implementation.write(receiver, (uint8_t *)data, strlen(data));
|
||||
prv_assert_no_handler_calls();
|
||||
|
||||
g_default_kernel_receiver_implementation.finish(receiver);
|
||||
g_default_kernel_receiver_implementation.cleanup(receiver);
|
||||
|
||||
// our msg should not have been freed since it was offloaded to kernelBG
|
||||
cl_assert(fake_pbl_malloc_num_net_allocs() != 0);
|
||||
|
||||
prv_set_expected_data(data, strlen(data));
|
||||
fake_system_task_callbacks_invoke_pending();
|
||||
cl_assert_equal_i(s_handler_call_count[HandlerA], 1);
|
||||
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), 0);
|
||||
}
|
110
tests/fw/services/comm_session/test_meta_endpoint.c
Normal file
110
tests/fw/services/comm_session/test_meta_endpoint.c
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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 "services/common/comm_session/meta_endpoint.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
// Stubs
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#include "stubs_bt_lock.h"
|
||||
#include "stubs_hexdump.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_print.h"
|
||||
|
||||
// Fakes
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#include "fake_kernel_malloc.h"
|
||||
#include "fake_session.h"
|
||||
#include "fake_system_task.h"
|
||||
|
||||
static Transport *s_transport;
|
||||
static CommSession *s_session;
|
||||
|
||||
// Helpers
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
static void prv_process_and_assert_sent(const uint8_t data[], uint16_t length) {
|
||||
const uint16_t meta_endpoint_id = 0;
|
||||
fake_system_task_callbacks_invoke_pending();
|
||||
fake_comm_session_process_send_next();
|
||||
fake_transport_assert_sent(s_transport, 0 /* index */, meta_endpoint_id, data, length);
|
||||
}
|
||||
|
||||
// Tests
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
void test_meta_endpoint__initialize(void) {
|
||||
fake_kernel_malloc_init();
|
||||
fake_kernel_malloc_mark();
|
||||
fake_comm_session_init();
|
||||
|
||||
s_transport = fake_transport_create(TransportDestinationSystem, NULL, NULL);
|
||||
s_session = fake_transport_set_connected(s_transport, true);
|
||||
}
|
||||
|
||||
void test_meta_endpoint__cleanup(void) {
|
||||
// Check for leaks:
|
||||
fake_kernel_malloc_mark_assert_equal();
|
||||
fake_kernel_malloc_deinit();
|
||||
|
||||
fake_comm_session_cleanup();
|
||||
}
|
||||
|
||||
void test_meta_endpoint__send_meta_corrupted_message(void) {
|
||||
const MetaResponseInfo meta_response_info = {
|
||||
.session = s_session,
|
||||
.payload = {
|
||||
.error_code = MetaResponseCodeCorruptedMessage,
|
||||
},
|
||||
};
|
||||
meta_endpoint_send_response_async(&meta_response_info);
|
||||
|
||||
const uint8_t expected_payload[] = { 0xd0 };
|
||||
prv_process_and_assert_sent(expected_payload, sizeof(expected_payload));
|
||||
}
|
||||
|
||||
void test_meta_endpoint__send_meta_disallowed_message(void) {
|
||||
const MetaResponseInfo meta_response_info = {
|
||||
.session = s_session,
|
||||
.payload = {
|
||||
.error_code = MetaResponseCodeDisallowed,
|
||||
.endpoint_id = 0xabcd,
|
||||
},
|
||||
};
|
||||
meta_endpoint_send_response_async(&meta_response_info);
|
||||
|
||||
const uint8_t expected_payload[] = { 0xdd, 0xab, 0xcd };
|
||||
prv_process_and_assert_sent(expected_payload, sizeof(expected_payload));
|
||||
}
|
||||
|
||||
void test_meta_endpoint__send_meta_unhandled_message(void) {
|
||||
const MetaResponseInfo meta_response_info = {
|
||||
.session = s_session,
|
||||
.payload = {
|
||||
.error_code = MetaResponseCodeUnhandled,
|
||||
.endpoint_id = 0x1234,
|
||||
},
|
||||
};
|
||||
meta_endpoint_send_response_async(&meta_response_info);
|
||||
|
||||
const uint8_t expected_payload[] = { 0xdc, 0x12, 0x34 };
|
||||
prv_process_and_assert_sent(expected_payload, sizeof(expected_payload));
|
||||
}
|
||||
|
458
tests/fw/services/comm_session/test_session.c
Normal file
458
tests/fw/services/comm_session/test_session.c
Normal file
|
@ -0,0 +1,458 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
382
tests/fw/services/comm_session/test_session_receive_router.c
Normal file
382
tests/fw/services/comm_session/test_session_receive_router.c
Normal file
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
* 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 "kernel/events.h"
|
||||
#include "services/common/comm_session/meta_endpoint.h"
|
||||
#include "services/common/comm_session/session_receive_router.h"
|
||||
#include "services/common/comm_session/session_remote_version.h"
|
||||
#include "services/common/comm_session/session_transport.h"
|
||||
#include "services/common/comm_session/test_endpoint_ids.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
// Stubs
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#include "stubs_analytics.h"
|
||||
#include "stubs_bt_lock.h"
|
||||
#include "stubs_bt_stack.h"
|
||||
#include "stubs_events.h"
|
||||
#include "stubs_hexdump.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_rand_ptr.h"
|
||||
#include "stubs_syscall_internal.h"
|
||||
|
||||
void app_launch_trigger(void) {
|
||||
}
|
||||
|
||||
bool bt_driver_comm_schedule_send_next_job(CommSession *data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bt_driver_comm_is_current_task_send_next_task(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void comm_session_analytics_inc_bytes_received(CommSession *session, uint16_t length) {
|
||||
}
|
||||
|
||||
void comm_session_analytics_open_session(CommSession *session) {
|
||||
}
|
||||
|
||||
void comm_session_analytics_close_session(CommSession *session, CommSessionCloseReason reason) {
|
||||
}
|
||||
|
||||
void comm_session_send_queue_cleanup(CommSession *session) {
|
||||
}
|
||||
|
||||
size_t comm_session_send_queue_get_length(const CommSession *session) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dls_private_handle_disconnect(void *data) {
|
||||
}
|
||||
|
||||
void session_remote_version_start_requests(CommSession *session) {
|
||||
}
|
||||
|
||||
void bt_persistent_storage_set_cached_system_capabilities(
|
||||
const PebbleProtocolCapabilities *capabilities) {
|
||||
}
|
||||
|
||||
// Fakes
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#include "fake_kernel_malloc.h"
|
||||
#include "fake_session_send_buffer.h"
|
||||
#include "fake_system_task.h"
|
||||
#include "fake_app_manager.h"
|
||||
|
||||
MetaResponseInfo s_last_meta_response_info;
|
||||
|
||||
void meta_endpoint_send_response_async(const MetaResponseInfo *meta_response_info) {
|
||||
s_last_meta_response_info = *meta_response_info;
|
||||
}
|
||||
|
||||
#define assert_meta_response_sent(e) \
|
||||
cl_assert_equal_i(e, s_last_meta_response_info.payload.error_code)
|
||||
|
||||
// Helpers
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#define PP_MSG_BYTES(endpoint_id, length, ...) \
|
||||
{ \
|
||||
length & 0xff, length >> 8, \
|
||||
endpoint_id && 0xff, endpoint_id >> 8, \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
|
||||
#define RECEIVE(...) \
|
||||
{ \
|
||||
const uint8_t partial_data[] = { __VA_ARGS__ }; \
|
||||
comm_session_receive_router_write(s_session, partial_data, sizeof(partial_data)); \
|
||||
}
|
||||
|
||||
static void prv_send_next(Transport *transport);
|
||||
static void prv_reset(Transport *transport);
|
||||
static void prv_set_connection_responsiveness(Transport *transport,
|
||||
BtConsumer consumer,
|
||||
ResponseTimeState state,
|
||||
uint16_t max_period_secs,
|
||||
ResponsivenessGrantedHandler granted_handler);
|
||||
|
||||
CommSession *s_session;
|
||||
|
||||
Transport *s_transport = (Transport *) ~0;
|
||||
|
||||
const TransportImplementation s_transport_implementation = {
|
||||
.send_next = prv_send_next,
|
||||
.reset = prv_reset,
|
||||
.set_connection_responsiveness = prv_set_connection_responsiveness,
|
||||
};
|
||||
|
||||
static void prv_send_next(Transport *transport) {
|
||||
|
||||
}
|
||||
|
||||
static void prv_reset(Transport *transport) {
|
||||
|
||||
}
|
||||
|
||||
static void prv_set_connection_responsiveness(Transport *transport,
|
||||
BtConsumer consumer,
|
||||
ResponseTimeState state,
|
||||
uint16_t max_period_secs,
|
||||
ResponsivenessGrantedHandler granted_handler) {
|
||||
|
||||
}
|
||||
|
||||
// Referenced from protocol_endppints_table.auto.h override header:
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
typedef enum {
|
||||
TestEndpointPrivate,
|
||||
TestEndpointPublic,
|
||||
TestEndpointAny,
|
||||
NumTestEndpoint,
|
||||
} TestEndpoint;
|
||||
|
||||
static int s_protocol_callback_counts[NumTestEndpoint];
|
||||
|
||||
void private_test_protocol_msg_callback(CommSession *session,
|
||||
const uint8_t* data, size_t length) {
|
||||
++s_protocol_callback_counts[TestEndpointPrivate];
|
||||
}
|
||||
|
||||
void public_test_protocol_msg_callback(CommSession *session,
|
||||
const uint8_t* data, size_t length) {
|
||||
++s_protocol_callback_counts[TestEndpointPublic];
|
||||
}
|
||||
|
||||
void any_test_protocol_msg_callback(CommSession *session,
|
||||
const uint8_t* data, size_t length) {
|
||||
++s_protocol_callback_counts[TestEndpointAny];
|
||||
}
|
||||
|
||||
static struct {
|
||||
int foo;
|
||||
} s_test_receiver_ctx;
|
||||
|
||||
static int s_prepare_count = 0;
|
||||
static int s_finish_count = 0;
|
||||
static int s_cleanup_count = 0;
|
||||
|
||||
static uint8_t s_write_buffer[1024];
|
||||
static uint16_t s_write_length;
|
||||
|
||||
static bool s_prepare_return_null = false;
|
||||
|
||||
static Receiver * prv_system_test_receiver_prepare(CommSession *session,
|
||||
const PebbleProtocolEndpoint *endpoint,
|
||||
size_t total_msg_length) {
|
||||
++s_prepare_count;
|
||||
if (s_prepare_return_null) {
|
||||
return NULL;
|
||||
}
|
||||
return (Receiver *) &s_test_receiver_ctx;
|
||||
}
|
||||
|
||||
static void prv_system_test_receiver_write(Receiver *receiver,
|
||||
const uint8_t *data, size_t length) {
|
||||
cl_assert(s_write_length + length < sizeof(s_write_buffer));
|
||||
memcpy(s_write_buffer + s_write_length, data, length);
|
||||
s_write_length += length;
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Wrote %zu bytes", length);
|
||||
}
|
||||
|
||||
static void prv_system_test_receiver_finish(Receiver *receiver) {
|
||||
++s_finish_count;
|
||||
}
|
||||
|
||||
static void prv_system_test_receiver_cleanup(Receiver *receiver) {
|
||||
++s_cleanup_count;
|
||||
}
|
||||
|
||||
const ReceiverImplementation g_system_test_receiver_imp = {
|
||||
.prepare = prv_system_test_receiver_prepare,
|
||||
.write = prv_system_test_receiver_write,
|
||||
.finish = prv_system_test_receiver_finish,
|
||||
.cleanup = prv_system_test_receiver_cleanup,
|
||||
};
|
||||
|
||||
// Tests
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void test_session_receive_router__initialize(void) {
|
||||
s_session = comm_session_open(s_transport, &s_transport_implementation,
|
||||
TransportDestinationSystem);
|
||||
memset(s_protocol_callback_counts, 0, sizeof(s_protocol_callback_counts));
|
||||
s_prepare_count = 0;
|
||||
s_finish_count = 0;
|
||||
s_cleanup_count = 0;
|
||||
s_write_length = 0;
|
||||
s_last_meta_response_info = (const MetaResponseInfo) {};
|
||||
s_prepare_return_null = false;
|
||||
}
|
||||
|
||||
void test_session_receive_router__cleanup(void) {
|
||||
if (s_session) {
|
||||
comm_session_close(s_session, CommSessionCloseReason_UnderlyingDisconnection);
|
||||
s_session = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void test_session_receive_router__header_byte_by_byte(void) {
|
||||
// Expect callback to "prepare" only after complete Pebble Protocol header has been received.
|
||||
|
||||
// Length high byte (big endian!)
|
||||
RECEIVE(0x00);
|
||||
cl_assert_equal_i(s_prepare_count, 0);
|
||||
|
||||
// Length low byte (big endian!)
|
||||
RECEIVE(0x01);
|
||||
cl_assert_equal_i(s_prepare_count, 0);
|
||||
|
||||
// Endpoint low byte (big endian!)
|
||||
RECEIVE(PRIVATE_TEST_ENDPOINT_ID >> 8);
|
||||
cl_assert_equal_i(s_prepare_count, 0);
|
||||
|
||||
// Endpoint low byte (big endian!)
|
||||
RECEIVE(PRIVATE_TEST_ENDPOINT_ID & 0xff);
|
||||
cl_assert_equal_i(s_prepare_count, 1);
|
||||
}
|
||||
|
||||
void test_session_receive_router__unhandled_endpoint(void) {
|
||||
// Expect "Unhandled" meta message to be replied to a message for an unknown endpoint.
|
||||
// The message should get eaten and not interfere with whatever comes next.
|
||||
|
||||
// Length: 1, Endpoint ID: NON_EXISTENT_ENDPOINT_ID, Payload: 0x55
|
||||
RECEIVE(0x00, 0x01, NON_EXISTENT_ENDPOINT_ID >> 8, NON_EXISTENT_ENDPOINT_ID & 0xff, 0x55);
|
||||
cl_assert_equal_i(s_prepare_count, 0);
|
||||
assert_meta_response_sent(MetaResponseCodeUnhandled);
|
||||
|
||||
// Length: 1, Endpoint ID: OTHER_NON_EXISTENT_ENDPOINT_ID, Payload: 0x55
|
||||
RECEIVE(0x00, 0x01,
|
||||
OTHER_NON_EXISTENT_ENDPOINT_ID >> 8, OTHER_NON_EXISTENT_ENDPOINT_ID & 0xff, 0x55);
|
||||
cl_assert_equal_i(s_prepare_count, 0);
|
||||
assert_meta_response_sent(MetaResponseCodeUnhandled);
|
||||
|
||||
// Length: 1, Endpoint ID: PRIVATE_TEST_ENDPOINT_ID, Payload: 0xaa
|
||||
RECEIVE(0x00, 0x01, PRIVATE_TEST_ENDPOINT_ID >> 8, PRIVATE_TEST_ENDPOINT_ID & 0xff, 0xaa);
|
||||
cl_assert_equal_i(s_prepare_count, 1);
|
||||
}
|
||||
|
||||
void test_session_receive_router__unhandled_and_supported_concat(void) {
|
||||
// Expect "Unhandled" meta message to be replied to a message for an unknown endpoint,
|
||||
// the message should get eaten even if a supported message immediately follows.
|
||||
|
||||
// Length: 1, Endpoint ID: NON_EXISTENT_ENDPOINT_ID, Payload: 0x55
|
||||
RECEIVE(0x00, 0x01, NON_EXISTENT_ENDPOINT_ID >> 8, NON_EXISTENT_ENDPOINT_ID & 0xff, 0x55,
|
||||
0x00, 0x01, PRIVATE_TEST_ENDPOINT_ID >> 8, PRIVATE_TEST_ENDPOINT_ID & 0xff, 0xaa);
|
||||
assert_meta_response_sent(MetaResponseCodeUnhandled);
|
||||
cl_assert_equal_i(s_prepare_count, 1);
|
||||
}
|
||||
|
||||
void test_session_receive_router__system_disallowed_endpoint(void) {
|
||||
// Expect "Disallowed" meta message to be replied to a message for an endpoint that is disallowed
|
||||
// by use over a system session.
|
||||
// The message should get eaten and not interfere with whatever comes next.
|
||||
|
||||
// Length: 1, Endpoint ID: PUBLIC_TEST_ENDPOINT_ID, Payload: 0x55
|
||||
RECEIVE(0x00, 0x01, PUBLIC_TEST_ENDPOINT_ID >> 8, PUBLIC_TEST_ENDPOINT_ID & 0xff, 0x55);
|
||||
cl_assert_equal_i(s_prepare_count, 0);
|
||||
assert_meta_response_sent(MetaResponseCodeDisallowed);
|
||||
|
||||
// Length: 1, Endpoint ID: PRIVATE_TEST_ENDPOINT_ID, Payload: 0xaa
|
||||
RECEIVE(0x00, 0x01, PRIVATE_TEST_ENDPOINT_ID >> 8, PRIVATE_TEST_ENDPOINT_ID & 0xff, 0xaa);
|
||||
cl_assert_equal_i(s_prepare_count, 1);
|
||||
}
|
||||
|
||||
void test_session_receive_router__app_disallowed_endpoint(void) {
|
||||
// Expect "Disallowed" meta message to be replied to a message for an endpoint that is disallowed
|
||||
// by use over a app session.
|
||||
// The message should get eaten and not interfere with whatever comes next.
|
||||
|
||||
comm_session_close(s_session, CommSessionCloseReason_UnderlyingDisconnection);
|
||||
s_session = comm_session_open(s_transport, &s_transport_implementation,
|
||||
TransportDestinationApp);
|
||||
|
||||
// Length: 1, Endpoint ID: PUBLIC_TEST_ENDPOINT_ID, Payload: 0xaa
|
||||
RECEIVE(0x00, 0x01, PRIVATE_TEST_ENDPOINT_ID >> 8, PRIVATE_TEST_ENDPOINT_ID & 0xff, 0xaa);
|
||||
cl_assert_equal_i(s_prepare_count, 0);
|
||||
assert_meta_response_sent(MetaResponseCodeDisallowed);
|
||||
|
||||
// Length: 1, Endpoint ID: PUBLIC_TEST_ENDPOINT_ID, Payload: 0x55
|
||||
RECEIVE(0x00, 0x01, PUBLIC_TEST_ENDPOINT_ID >> 8, PUBLIC_TEST_ENDPOINT_ID & 0xff, 0x55);
|
||||
cl_assert_equal_i(s_prepare_count, 1);
|
||||
}
|
||||
|
||||
void test_session_receive_router__ignore_message_if_no_receiver_could_be_prepared(void) {
|
||||
// Expect an inbound message to be skipped/ignored if no Receiver could be prepared.
|
||||
// The message should get eaten and not interfere with whatever comes next.
|
||||
|
||||
s_prepare_return_null = true;
|
||||
// Length: 1, Endpoint ID: PRIVATE_TEST_ENDPOINT_ID, Payload: 0xaa
|
||||
RECEIVE(0x00, 0x01, PRIVATE_TEST_ENDPOINT_ID >> 8, PRIVATE_TEST_ENDPOINT_ID & 0xff, 0xaa);
|
||||
cl_assert_equal_i(s_prepare_count, 1);
|
||||
cl_assert_equal_i(s_finish_count, 0);
|
||||
s_prepare_return_null = false;
|
||||
|
||||
// Length: 1, Endpoint ID: PRIVATE_TEST_ENDPOINT_ID, Payload: 0xaa
|
||||
RECEIVE(0x00, 0x01, PRIVATE_TEST_ENDPOINT_ID >> 8, PRIVATE_TEST_ENDPOINT_ID & 0xff, 0xaa);
|
||||
cl_assert_equal_i(s_prepare_count, 2);
|
||||
cl_assert_equal_i(s_finish_count, 1);
|
||||
}
|
||||
|
||||
void test_session_receive_router__cleanup_receiver_if_session_is_closed(void) {
|
||||
// Expect that when a partial message has been received, but then the session gets closed,
|
||||
// that "cleanup" is called.
|
||||
|
||||
// Length: 1, Endpoint ID: PRIVATE_TEST_ENDPOINT_ID, Payload: not received yet
|
||||
RECEIVE(0x00, 0x01, PRIVATE_TEST_ENDPOINT_ID >> 8, PRIVATE_TEST_ENDPOINT_ID & 0xff);
|
||||
cl_assert_equal_i(s_prepare_count, 1);
|
||||
|
||||
comm_session_close(s_session, CommSessionCloseReason_UnderlyingDisconnection);
|
||||
s_session = NULL;
|
||||
|
||||
cl_assert_equal_i(s_cleanup_count, 1);
|
||||
}
|
||||
|
||||
void test_session_receive_router__payload_in_pieces(void) {
|
||||
// Expect that when a message's payload is received in pieces, the complete payload will be
|
||||
// written and finish will only be called after the whole payload has been received.
|
||||
|
||||
const uint8_t expected_payload[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
|
||||
|
||||
// Length: 5, Endpoint ID: PRIVATE_TEST_ENDPOINT_ID, Payload: 0xaa (byte 1)
|
||||
RECEIVE(0x00, sizeof(expected_payload),
|
||||
PRIVATE_TEST_ENDPOINT_ID >> 8, PRIVATE_TEST_ENDPOINT_ID & 0xff,
|
||||
0xaa);
|
||||
cl_assert_equal_i(s_write_length, 1);
|
||||
cl_assert_equal_i(s_finish_count, 0);
|
||||
|
||||
// Payload bytes 2, 3 and 4
|
||||
RECEIVE(0xbb, 0xcc, 0xdd);
|
||||
cl_assert_equal_i(s_write_length, 4);
|
||||
cl_assert_equal_i(s_finish_count, 0);
|
||||
|
||||
// Last payload byte
|
||||
RECEIVE(0xee,
|
||||
// New message partial header: Length: 5, Endpoint ID: PRIVATE_TEST_ENDPOINT_ID
|
||||
0x00, 0x01, PRIVATE_TEST_ENDPOINT_ID >> 8);
|
||||
cl_assert_equal_i(s_finish_count, 1);
|
||||
cl_assert_equal_i(s_write_length, sizeof(expected_payload));
|
||||
cl_assert_equal_m(s_write_buffer, expected_payload, sizeof(expected_payload));
|
||||
}
|
171
tests/fw/services/comm_session/test_session_remote_version.c
Normal file
171
tests/fw/services/comm_session/test_session_remote_version.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* 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 "services/common/bluetooth/bluetooth_persistent_storage.h"
|
||||
#include "services/common/comm_session/session_internal.h"
|
||||
#include "services/common/comm_session/session_remote_os.h"
|
||||
#include "services/common/comm_session/session_remote_version.h"
|
||||
#include "util/net.h"
|
||||
|
||||
static CommSession s_session;
|
||||
|
||||
extern void session_remote_version_protocol_msg_callback(CommSession *session,
|
||||
const uint8_t *data, size_t length);
|
||||
|
||||
// Fakes & Stubs
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "fake_pbl_malloc.h"
|
||||
#include "fake_events.h"
|
||||
#include "fake_new_timer.h"
|
||||
|
||||
#include "stubs_bt_lock.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_mutex.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_rtc.h"
|
||||
|
||||
void bt_driver_reconnect_notify_platform_bitfield(uint32_t p) {}
|
||||
|
||||
static bool s_session_is_system;
|
||||
bool comm_session_is_system(CommSession *session) {
|
||||
return s_session_is_system;
|
||||
}
|
||||
|
||||
static bool s_session_is_valid;
|
||||
bool comm_session_is_valid(const CommSession *session) {
|
||||
return (session == &s_session) && s_session_is_valid;
|
||||
}
|
||||
|
||||
static bool s_data_sent;
|
||||
bool comm_session_send_data(CommSession *session, uint16_t endpoint_id,
|
||||
const uint8_t* data, size_t length, uint32_t timeout_ms) {
|
||||
// The request is just a single 0x00 byte to endpoint 0x11:
|
||||
cl_assert_equal_i(endpoint_id, 0x11);
|
||||
cl_assert_equal_i(length, 1);
|
||||
cl_assert_equal_i(*data, 0x00 /* Command ID 'Request' */);
|
||||
s_data_sent = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
CommSessionCapability s_capability_flags;
|
||||
void comm_session_set_capabilities(CommSession *session, CommSessionCapability capability_flags) {
|
||||
cl_assert_equal_p(session, &s_session);
|
||||
s_capability_flags = capability_flags;
|
||||
}
|
||||
|
||||
BTBondingID bt_persistent_storage_store_bt_classic_pairing(BTDeviceAddress *address, SM128BitKey *link_key,
|
||||
char *name, uint8_t *platform_bits) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void (*s_launcher_task_callback)(void *data);
|
||||
static void *s_launcher_task_callback_data;
|
||||
|
||||
void launcher_task_add_callback(void (*callback)(void *data), void *data) {
|
||||
// Simple fake, can only handle one call
|
||||
cl_assert_equal_p(s_launcher_task_callback, NULL);
|
||||
|
||||
s_launcher_task_callback = callback;
|
||||
s_launcher_task_callback_data = data;
|
||||
}
|
||||
|
||||
static void prv_process_and_assert_sent_request_data(bool expect_request_data_sent) {
|
||||
s_data_sent = false;
|
||||
s_launcher_task_callback(s_launcher_task_callback_data);
|
||||
cl_assert_equal_b(s_data_sent, expect_request_data_sent);
|
||||
}
|
||||
|
||||
static void prv_receive_v3_response(uint8_t major, uint8_t minor, uint8_t bugfix,
|
||||
CommSessionCapability protocol_capabilities) {
|
||||
union {
|
||||
CommSessionCapability protocol_capabilities;
|
||||
uint8_t byte[8]; // Little-endian!
|
||||
} capabilities;
|
||||
capabilities.protocol_capabilities = protocol_capabilities;
|
||||
|
||||
uint8_t response_data[] = {
|
||||
0x01, // Command ID 'Response'
|
||||
0x00, 0x00, 0x00, 0x00, // Deprecated library version
|
||||
0x00, 0x00, 0x00, 0x00, // Deprecated capabilities
|
||||
0x00, 0x00, 0x00, 0x00, // Platform (OS) bitfield
|
||||
0x02, // Response version
|
||||
major, minor, bugfix,
|
||||
capabilities.byte[0],
|
||||
capabilities.byte[1],
|
||||
capabilities.byte[2],
|
||||
capabilities.byte[3],
|
||||
capabilities.byte[4],
|
||||
capabilities.byte[5],
|
||||
capabilities.byte[7],
|
||||
};
|
||||
session_remote_version_protocol_msg_callback(&s_session, response_data, sizeof(response_data));
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define MAX_ATTEMPTS (3)
|
||||
|
||||
void test_session_remote_version__initialize(void) {
|
||||
fake_event_init();
|
||||
s_session = (CommSession) {};
|
||||
s_data_sent = false;
|
||||
s_session_is_valid = true;
|
||||
s_session_is_system = true;
|
||||
s_capability_flags = 0;
|
||||
session_remote_version_start_requests(&s_session);
|
||||
}
|
||||
|
||||
void test_session_remote_version__cleanup(void) {
|
||||
s_launcher_task_callback = NULL;
|
||||
s_launcher_task_callback_data = NULL;
|
||||
|
||||
fake_pbl_malloc_clear_tracking();
|
||||
}
|
||||
|
||||
void test_session_remote_version__receive_invalid_msg(void) {
|
||||
uint8_t invalid_msg = 0xff;
|
||||
session_remote_version_protocol_msg_callback(&s_session, &invalid_msg, sizeof(invalid_msg));
|
||||
cl_assert_equal_i(fake_event_get_count(), 0);
|
||||
}
|
||||
|
||||
static const CommSessionCapability s_expected_capabilities =
|
||||
(CommSessionAppMessage8kSupport |
|
||||
CommSessionVoiceApiSupport);
|
||||
|
||||
void test_session_remote_version__system_session(void) {
|
||||
s_session_is_system = true;
|
||||
prv_receive_v3_response(3, 2, 1, s_expected_capabilities);
|
||||
// Triggers PEBBLE_REMOTE_APP_INFO_EVENT:
|
||||
cl_assert_equal_i(fake_event_get_count(), 1);
|
||||
PebbleEvent e = fake_event_get_last();
|
||||
cl_assert_equal_i(e.type, PEBBLE_REMOTE_APP_INFO_EVENT);
|
||||
cl_assert_equal_i(s_capability_flags, s_expected_capabilities);
|
||||
}
|
||||
|
||||
void test_session_remote_version__app_session(void) {
|
||||
s_session_is_system = false;
|
||||
prv_receive_v3_response(3, 2, 1, s_expected_capabilities);
|
||||
// Triggers no event:
|
||||
cl_assert_equal_i(fake_event_get_count(), 0);
|
||||
cl_assert_equal_i(s_capability_flags, s_expected_capabilities);
|
||||
}
|
377
tests/fw/services/comm_session/test_session_send_buffer.c
Normal file
377
tests/fw/services/comm_session/test_session_send_buffer.c
Normal file
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
* 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 "services/common/comm_session/default_kernel_sender.h"
|
||||
#include "services/common/comm_session/session_send_buffer.h"
|
||||
#include "services/common/comm_session/session_transport.h"
|
||||
#include "services/common/comm_session/session_internal.h"
|
||||
#include "services/common/comm_session/session_send_queue.h"
|
||||
#include "services/common/comm_session/protocol.h"
|
||||
|
||||
#include "util/net.h"
|
||||
#include "util/size.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "semphr.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
extern SendBuffer * comm_session_send_buffer_create(bool is_system);
|
||||
extern void comm_session_send_buffer_destroy(SendBuffer *sb);
|
||||
extern SemaphoreHandle_t comm_session_send_buffer_write_semaphore(void);
|
||||
extern T_STATIC const SessionSendJobImpl s_default_kernel_send_job_impl;
|
||||
extern void comm_default_kernel_sender_deinit(void);
|
||||
extern void comm_session_send_queue_cleanup(CommSession *session);
|
||||
|
||||
// Stubs
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#include "stubs_bt_lock.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_analytics.h"
|
||||
|
||||
void comm_session_analytics_inc_bytes_sent(CommSession *session, uint16_t length) {
|
||||
}
|
||||
|
||||
// Fakes
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#include "fake_kernel_malloc.h"
|
||||
#include "fake_queue.h"
|
||||
#include "fake_rtc.h"
|
||||
|
||||
static CommSession s_session;
|
||||
|
||||
static CommSession *s_valid_session;
|
||||
|
||||
static void prv_cleanup_send_buffer(SendBuffer *sb) {
|
||||
s_default_kernel_send_job_impl.free((SessionSendQueueJob *)sb);
|
||||
}
|
||||
|
||||
bool comm_session_is_valid(const CommSession *session) {
|
||||
if (!session) {
|
||||
return false;
|
||||
}
|
||||
return (s_valid_session == session);
|
||||
}
|
||||
|
||||
static int s_send_next_count = 0;
|
||||
void comm_session_send_next(CommSession *session) {
|
||||
++s_send_next_count;
|
||||
}
|
||||
|
||||
void comm_session_send_next_immediately(CommSession *session) {
|
||||
// Pretend to send out all the data:
|
||||
size_t read_space = comm_session_send_queue_get_length(session);
|
||||
comm_session_send_queue_consume(session, read_space);
|
||||
}
|
||||
|
||||
static bool s_is_current_task_send_next_task = false;
|
||||
bool comm_session_is_current_task_send_next_task(CommSession *session) {
|
||||
return s_is_current_task_send_next_task;
|
||||
}
|
||||
|
||||
// Tests
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
static const uint16_t ENDPOINT_ID = 1234;
|
||||
static const uint32_t TIMEOUT_MS = 500;
|
||||
|
||||
void test_session_send_buffer__initialize(void) {
|
||||
s_is_current_task_send_next_task = false;
|
||||
s_session = (const CommSession) {};
|
||||
fake_kernel_malloc_init();
|
||||
fake_kernel_malloc_enable_stats(true);
|
||||
fake_kernel_malloc_mark();
|
||||
s_send_next_count = 0;
|
||||
comm_default_kernel_sender_init();
|
||||
}
|
||||
|
||||
void test_session_send_buffer__cleanup(void) {
|
||||
comm_default_kernel_sender_deinit();
|
||||
|
||||
// Check for leaks:
|
||||
fake_kernel_malloc_mark_assert_equal();
|
||||
fake_kernel_malloc_deinit();
|
||||
}
|
||||
|
||||
void test_session_send_buffer__null_session(void) {
|
||||
cl_assert_equal_p(NULL, comm_session_send_buffer_begin_write(NULL, ENDPOINT_ID, 1, TIMEOUT_MS));
|
||||
}
|
||||
|
||||
void test_session_send_buffer__begin_write_with_more_than_max_payload(void) {
|
||||
s_valid_session = &s_session;
|
||||
|
||||
size_t max_length = comm_session_send_buffer_get_max_payload_length(&s_session);
|
||||
SendBuffer *write_sb = comm_session_send_buffer_begin_write(&s_session, ENDPOINT_ID,
|
||||
max_length + 1,
|
||||
TIMEOUT_MS);
|
||||
cl_assert_equal_p(write_sb, NULL);
|
||||
}
|
||||
|
||||
TickType_t prv_session_closed_yield_cb(QueueHandle_t handle) {
|
||||
if (s_valid_session) {
|
||||
comm_session_send_queue_cleanup(s_valid_session);
|
||||
s_valid_session = NULL;
|
||||
}
|
||||
return 10;
|
||||
}
|
||||
|
||||
TickType_t prv_receive_but_no_bytes_freed_yield_cb(QueueHandle_t handle) {
|
||||
fake_rtc_increment_ticks(100);
|
||||
xSemaphoreGive(handle);
|
||||
return 100;
|
||||
}
|
||||
|
||||
void test_session_send_buffer__not_enough_space_in_time(void) {
|
||||
s_valid_session = &s_session;
|
||||
|
||||
// Fill the send buffer completely:
|
||||
const size_t max_length = comm_session_send_buffer_get_max_payload_length(&s_session);
|
||||
SendBuffer *write_sb = comm_session_send_buffer_begin_write(&s_session, ENDPOINT_ID,
|
||||
max_length /* required_free_length */,
|
||||
TIMEOUT_MS);
|
||||
cl_assert(write_sb);
|
||||
uint8_t fake_data[max_length];
|
||||
memset(fake_data, 0, max_length);
|
||||
comm_session_send_buffer_write(write_sb, fake_data, max_length);
|
||||
comm_session_send_buffer_end_write(write_sb);
|
||||
|
||||
// Set a yield callback that gives the semph in time but does not clear out the send buffer:
|
||||
SemaphoreHandle_t write_semph = comm_session_send_buffer_write_semaphore();
|
||||
fake_queue_set_yield_callback(write_semph, prv_receive_but_no_bytes_freed_yield_cb);
|
||||
|
||||
// Try to begin writing again, requesting only one byte:
|
||||
SendBuffer *write_sb2 = comm_session_send_buffer_begin_write(&s_session, ENDPOINT_ID,
|
||||
1 /* required_free_length */,
|
||||
TIMEOUT_MS);
|
||||
cl_assert_equal_p(write_sb2, NULL);
|
||||
|
||||
prv_cleanup_send_buffer(write_sb);
|
||||
}
|
||||
|
||||
void test_session_send_buffer__multiple_smaller_messages(void) {
|
||||
s_valid_session = &s_session;
|
||||
|
||||
// This length excludes the sizeof(PebbleProtocolHeader).
|
||||
size_t bytes_free = comm_session_send_buffer_get_max_payload_length(&s_session);
|
||||
bytes_free += sizeof(PebbleProtocolHeader);
|
||||
|
||||
const int num_sbs = 1 + (bytes_free / (sizeof(PebbleProtocolHeader) + 1 /* payload_length */));
|
||||
SendBuffer *write_sb[num_sbs];
|
||||
memset(write_sb, 0, sizeof(write_sb));
|
||||
|
||||
for (int i = 0; bytes_free > 0 && i < ARRAY_LENGTH(write_sb); ++i) {
|
||||
size_t payload_length = 1;
|
||||
|
||||
bytes_free -= sizeof(PebbleProtocolHeader) + payload_length;
|
||||
|
||||
// If we cannot fit another message after this one, increment the length to use up the space:
|
||||
if (bytes_free <= (sizeof(PebbleProtocolHeader) + payload_length)) {
|
||||
payload_length += bytes_free;
|
||||
bytes_free = 0;
|
||||
}
|
||||
|
||||
write_sb[i] = comm_session_send_buffer_begin_write(&s_session, ENDPOINT_ID,
|
||||
payload_length, TIMEOUT_MS);
|
||||
uint8_t fake_data[payload_length];
|
||||
memset(fake_data, 0, payload_length);
|
||||
comm_session_send_buffer_write(write_sb[i], fake_data, payload_length);
|
||||
comm_session_send_buffer_end_write(write_sb[i]);
|
||||
}
|
||||
|
||||
// Can't write another message:
|
||||
cl_assert_equal_p(NULL, comm_session_send_buffer_begin_write(&s_session, ENDPOINT_ID,
|
||||
1 /* length */, TIMEOUT_MS));
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(write_sb); ++i) {
|
||||
if (!write_sb[i]) {
|
||||
break;
|
||||
}
|
||||
prv_cleanup_send_buffer(write_sb[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void test_session_send_buffer__not_enough_space_kernel_bg(void) {
|
||||
s_valid_session = &s_session;
|
||||
|
||||
// Fill the send buffer completely:
|
||||
const size_t max_length = comm_session_send_buffer_get_max_payload_length(&s_session);
|
||||
SendBuffer *write_sb = comm_session_send_buffer_begin_write(&s_session, ENDPOINT_ID,
|
||||
max_length /* required_free_length */,
|
||||
TIMEOUT_MS);
|
||||
uint8_t fake_data[max_length];
|
||||
memset(fake_data, 0, max_length);
|
||||
comm_session_send_buffer_write(write_sb, fake_data, max_length);
|
||||
comm_session_send_buffer_end_write(write_sb);
|
||||
|
||||
// Pretend the current task is the same task that processes "send_next".
|
||||
// Pretend to execute a callback that was scheduled already before the previous write caused
|
||||
// a "send_next" callback to be scheduled.
|
||||
s_is_current_task_send_next_task = true;
|
||||
|
||||
// Set a yield callback that gives the semph in time but does not clear out the send buffer:
|
||||
SemaphoreHandle_t write_semph = comm_session_send_buffer_write_semaphore();
|
||||
fake_queue_set_yield_callback(write_semph, prv_receive_but_no_bytes_freed_yield_cb);
|
||||
|
||||
// Try to begin writing again, requesting only one byte:
|
||||
SendBuffer *write_sb2 = comm_session_send_buffer_begin_write(&s_session, ENDPOINT_ID,
|
||||
1 /* required_free_length */,
|
||||
TIMEOUT_MS);
|
||||
|
||||
// Because the ..._begin_write() call happened from the BT02 task, expect the data to be
|
||||
// sent out immediately (we'd timeout or deadlock if an infinite timeout was set)
|
||||
cl_assert(write_sb2);
|
||||
comm_session_send_buffer_end_write(write_sb2);
|
||||
|
||||
// write_sb is already cleaned up because it got sent out
|
||||
prv_cleanup_send_buffer(write_sb2);
|
||||
}
|
||||
|
||||
void test_session_send_buffer__writing_but_then_session_closed(void) {
|
||||
s_valid_session = &s_session;
|
||||
|
||||
// Fill the send buffer completely:
|
||||
const size_t max_length = comm_session_send_buffer_get_max_payload_length(&s_session);
|
||||
SendBuffer *write_sb = comm_session_send_buffer_begin_write(&s_session, ENDPOINT_ID,
|
||||
max_length /* required_free_length */,
|
||||
TIMEOUT_MS);
|
||||
uint8_t fake_data[max_length];
|
||||
memset(fake_data, 0, max_length);
|
||||
comm_session_send_buffer_write(write_sb, fake_data, max_length);
|
||||
comm_session_send_buffer_end_write(write_sb);
|
||||
|
||||
// Set a yield callback that gives the semph in time but closes the session:
|
||||
SemaphoreHandle_t write_semph = comm_session_send_buffer_write_semaphore();
|
||||
fake_queue_set_yield_callback(write_semph, prv_session_closed_yield_cb);
|
||||
|
||||
// Try to begin writing again, requesting only one byte:
|
||||
write_sb = comm_session_send_buffer_begin_write(&s_session, ENDPOINT_ID,
|
||||
1 /* required_free_length */,
|
||||
TIMEOUT_MS);
|
||||
cl_assert_equal_p(write_sb, NULL);
|
||||
|
||||
// ..send_buffer_destroy() is already called in the yield cb
|
||||
}
|
||||
|
||||
void test_session_send_buffer__write_beyond_available_space(void) {
|
||||
s_valid_session = &s_session;
|
||||
|
||||
// Fill the send buffer completely:
|
||||
const size_t max_length = comm_session_send_buffer_get_max_payload_length(&s_session);
|
||||
SendBuffer *write_sb = comm_session_send_buffer_begin_write(&s_session, ENDPOINT_ID,
|
||||
max_length /* required_free_length */,
|
||||
TIMEOUT_MS);
|
||||
uint8_t fake_data[max_length];
|
||||
memset(fake_data, 0, max_length);
|
||||
cl_assert_equal_b(comm_session_send_buffer_write(write_sb, fake_data, max_length), true);
|
||||
|
||||
|
||||
// Try writing another byte (expect false returned):
|
||||
cl_assert_equal_b(comm_session_send_buffer_write(write_sb, fake_data, max_length), false);
|
||||
comm_session_send_buffer_end_write(write_sb);
|
||||
|
||||
prv_cleanup_send_buffer(write_sb);
|
||||
}
|
||||
|
||||
void test_session_send_buffer__send_queue_interface(void) {
|
||||
s_valid_session = &s_session;
|
||||
|
||||
// Fill the send buffer completely:
|
||||
const size_t max_payload_length = comm_session_send_buffer_get_max_payload_length(&s_session);
|
||||
SendBuffer *write_sb = comm_session_send_buffer_begin_write(&s_session, ENDPOINT_ID,
|
||||
max_payload_length /* required_free_length */,
|
||||
TIMEOUT_MS);
|
||||
uint8_t fake_data_payload[max_payload_length];
|
||||
for (int i = 0; i < max_payload_length; ++i) {
|
||||
fake_data_payload[i] = i % 0xff;
|
||||
}
|
||||
// Write in two parts:
|
||||
size_t second_write_length = max_payload_length - (max_payload_length / 2);
|
||||
cl_assert_equal_b(comm_session_send_buffer_write(write_sb,
|
||||
fake_data_payload,
|
||||
max_payload_length - second_write_length), true);
|
||||
cl_assert_equal_b(comm_session_send_buffer_write(write_sb,
|
||||
fake_data_payload + second_write_length,
|
||||
second_write_length), true);
|
||||
|
||||
cl_assert_equal_i(s_send_next_count, 0);
|
||||
comm_session_send_buffer_end_write(write_sb);
|
||||
// Expect comm_session_send_next to be called to trigger the transport:
|
||||
cl_assert_equal_i(s_send_next_count, 1);
|
||||
|
||||
// Exercise the transport interface:
|
||||
const SessionSendQueueJob *job = (const SessionSendQueueJob *)write_sb;
|
||||
// ..._get_read_space_remaining():
|
||||
size_t expected_bytes_incl_pebble_protocol_header =
|
||||
max_payload_length + sizeof(PebbleProtocolHeader);
|
||||
size_t length = s_default_kernel_send_job_impl.get_length(job);
|
||||
cl_assert_equal_i(length, expected_bytes_incl_pebble_protocol_header);
|
||||
|
||||
// ..._copy():
|
||||
uint8_t pp_data_out[expected_bytes_incl_pebble_protocol_header];
|
||||
size_t bytes_copied =
|
||||
s_default_kernel_send_job_impl.copy(job, 0,
|
||||
expected_bytes_incl_pebble_protocol_header,
|
||||
pp_data_out);
|
||||
cl_assert_equal_i(bytes_copied, expected_bytes_incl_pebble_protocol_header);
|
||||
PebbleProtocolHeader *header = (PebbleProtocolHeader *) pp_data_out;
|
||||
cl_assert_equal_i(header->length, htons(max_payload_length));
|
||||
cl_assert_equal_i(header->endpoint_id, htons(ENDPOINT_ID));
|
||||
cl_assert_equal_i(memcmp(pp_data_out + sizeof(PebbleProtocolHeader),
|
||||
fake_data_payload, max_payload_length), 0);
|
||||
|
||||
// ..._copy() with offset:
|
||||
int offset = 2;
|
||||
bytes_copied =
|
||||
s_default_kernel_send_job_impl.copy(job, offset,
|
||||
expected_bytes_incl_pebble_protocol_header,
|
||||
pp_data_out);
|
||||
cl_assert_equal_i(bytes_copied, expected_bytes_incl_pebble_protocol_header - offset);
|
||||
header = (PebbleProtocolHeader *) (pp_data_out - offset);
|
||||
cl_assert_equal_i(header->endpoint_id, htons(ENDPOINT_ID));
|
||||
cl_assert_equal_i(memcmp(pp_data_out + sizeof(PebbleProtocolHeader) - offset,
|
||||
fake_data_payload, max_payload_length - offset), 0);
|
||||
|
||||
|
||||
// ..._get_read_pointer():
|
||||
uint16_t bytes_read = 0;
|
||||
uint16_t read_space;
|
||||
const uint8_t *data_out;
|
||||
while ((read_space = s_default_kernel_send_job_impl.get_read_pointer(job, &data_out))) {
|
||||
PebbleProtocolHeader *header = (PebbleProtocolHeader *) data_out;
|
||||
if (bytes_read == 0) {
|
||||
cl_assert(read_space >= sizeof(PebbleProtocolHeader));
|
||||
cl_assert_equal_i(header->length, htons(max_payload_length));
|
||||
cl_assert_equal_i(header->endpoint_id, htons(ENDPOINT_ID));
|
||||
cl_assert_equal_i(memcmp(data_out + sizeof(PebbleProtocolHeader),
|
||||
fake_data_payload,
|
||||
read_space - sizeof(PebbleProtocolHeader)), 0);
|
||||
} else {
|
||||
cl_assert_equal_i(memcmp(data_out,
|
||||
fake_data_payload + bytes_read - sizeof(PebbleProtocolHeader),
|
||||
read_space), 0);
|
||||
}
|
||||
s_default_kernel_send_job_impl.consume(job, read_space);
|
||||
bytes_read += read_space;
|
||||
}
|
||||
cl_assert_equal_i(bytes_read, expected_bytes_incl_pebble_protocol_header);
|
||||
|
||||
cl_assert_equal_i(comm_session_send_queue_get_length(s_valid_session), 0);
|
||||
|
||||
prv_cleanup_send_buffer(write_sb);
|
||||
}
|
303
tests/fw/services/comm_session/test_session_send_queue.c
Normal file
303
tests/fw/services/comm_session/test_session_send_queue.c
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* 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 "services/common/comm_session/session_internal.h"
|
||||
#include "services/common/comm_session/session_send_queue.h"
|
||||
#include "util/math.h"
|
||||
|
||||
extern void comm_session_send_queue_cleanup(CommSession *session);
|
||||
|
||||
// Fakes & Stubs
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "fake_kernel_malloc.h"
|
||||
|
||||
#include "stubs_bt_lock.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
|
||||
static CommSession s_session;
|
||||
static CommSession *s_valid_session;
|
||||
|
||||
void comm_session_analytics_inc_bytes_sent(CommSession *session, uint16_t length) {
|
||||
}
|
||||
|
||||
bool comm_session_is_valid(const CommSession *session) {
|
||||
if (!session) {
|
||||
return false;
|
||||
}
|
||||
return (s_valid_session == session);
|
||||
}
|
||||
|
||||
void comm_session_send_next(CommSession *session) {
|
||||
}
|
||||
|
||||
|
||||
// Helpers
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct {
|
||||
SessionSendQueueJob job;
|
||||
size_t consumed_length;
|
||||
size_t length;
|
||||
uint8_t data[];
|
||||
} TestSendJob;
|
||||
|
||||
static size_t prv_get_length(const TestSendJob *sb) {
|
||||
return (sb->length - sb->consumed_length);
|
||||
}
|
||||
|
||||
static const uint8_t *prv_get_read_pointer(const TestSendJob *sb) {
|
||||
return (sb->data + sb->consumed_length);
|
||||
}
|
||||
|
||||
static size_t prv_send_job_impl_get_length(const SessionSendQueueJob *send_job) {
|
||||
return prv_get_length((TestSendJob *)send_job);
|
||||
}
|
||||
|
||||
size_t prv_send_job_impl_copy(const SessionSendQueueJob *send_job, int start_offset,
|
||||
size_t length, uint8_t *data_out) {
|
||||
TestSendJob *sb = (TestSendJob *)send_job;
|
||||
const size_t length_remaining = prv_get_length(sb);
|
||||
const size_t length_after_offset = (length_remaining - start_offset);
|
||||
const size_t length_to_copy = MIN(length_after_offset, length);
|
||||
memcpy(data_out, prv_get_read_pointer(sb) + start_offset, length_to_copy);
|
||||
return length_to_copy;
|
||||
}
|
||||
|
||||
size_t prv_send_job_impl_get_read_pointer(const SessionSendQueueJob *send_job,
|
||||
const uint8_t **data_out) {
|
||||
TestSendJob *sb = (TestSendJob *)send_job;
|
||||
*data_out = prv_get_read_pointer(sb);
|
||||
return prv_get_length(sb);
|
||||
}
|
||||
|
||||
void prv_send_job_impl_consume(const SessionSendQueueJob *send_job, size_t length) {
|
||||
TestSendJob *sb = (TestSendJob *)send_job;
|
||||
sb->consumed_length += length;
|
||||
}
|
||||
|
||||
static int s_free_count;
|
||||
|
||||
void prv_send_job_impl_free(SessionSendQueueJob *send_job) {
|
||||
kernel_free(send_job);
|
||||
++s_free_count;
|
||||
}
|
||||
|
||||
static const SessionSendJobImpl s_test_job_impl = {
|
||||
.get_length = prv_send_job_impl_get_length,
|
||||
.copy = prv_send_job_impl_copy,
|
||||
.get_read_pointer = prv_send_job_impl_get_read_pointer,
|
||||
.consume = prv_send_job_impl_consume,
|
||||
.free = prv_send_job_impl_free,
|
||||
};
|
||||
|
||||
SessionSendQueueJob *prv_create_test_job(const uint8_t *data, size_t length) {
|
||||
TestSendJob *job = kernel_malloc(sizeof(TestSendJob) + length);
|
||||
cl_assert(job);
|
||||
*job = (const TestSendJob) {
|
||||
.job = {
|
||||
.impl = &s_test_job_impl,
|
||||
},
|
||||
.length = length,
|
||||
};
|
||||
if (length && data) {
|
||||
memcpy(job->data, data, length);
|
||||
}
|
||||
return (SessionSendQueueJob *)job;
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const uint8_t TEST_DATA[] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
|
||||
|
||||
void test_session_send_queue__initialize(void) {
|
||||
s_valid_session = &s_session;
|
||||
s_free_count = 0;
|
||||
fake_kernel_malloc_init();
|
||||
fake_kernel_malloc_enable_stats(true);
|
||||
fake_kernel_malloc_mark();
|
||||
s_session = (const CommSession) {};
|
||||
}
|
||||
|
||||
void test_session_send_queue__cleanup(void) {
|
||||
if (s_valid_session) {
|
||||
comm_session_send_queue_cleanup(s_valid_session);
|
||||
}
|
||||
|
||||
// Check for leaks:
|
||||
fake_kernel_malloc_mark_assert_equal();
|
||||
fake_kernel_malloc_deinit();
|
||||
}
|
||||
|
||||
void test_session_send_queue__get_length_returns_summed_length_of_all_jobs(void) {
|
||||
cl_assert_equal_i(0, comm_session_send_queue_get_length(s_valid_session));
|
||||
|
||||
int num_jobs = 3;
|
||||
for (int i = 0; i < num_jobs; ++i) {
|
||||
SessionSendQueueJob *job = prv_create_test_job(TEST_DATA, sizeof(TEST_DATA));
|
||||
comm_session_send_queue_add_job(s_valid_session, &job);
|
||||
cl_assert(job);
|
||||
cl_assert_equal_i((i + 1) * sizeof(TEST_DATA),
|
||||
comm_session_send_queue_get_length(s_valid_session));
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_add_jobs(int num_jobs) {
|
||||
for (int i = 0; i < num_jobs; ++i) {
|
||||
SessionSendQueueJob *job = prv_create_test_job(TEST_DATA, sizeof(TEST_DATA));
|
||||
comm_session_send_queue_add_job(s_valid_session, &job);
|
||||
cl_assert(job);
|
||||
}
|
||||
}
|
||||
|
||||
void test_session_send_queue__copy_empty_queue(void) {
|
||||
uint8_t data_out[2];
|
||||
cl_assert_equal_i(0,
|
||||
comm_session_send_queue_copy(s_valid_session, 0, sizeof(data_out), data_out));
|
||||
}
|
||||
|
||||
void test_session_send_queue__copy_less_than_head_job_zero_offset(void) {
|
||||
int num_jobs = 3;
|
||||
prv_add_jobs(num_jobs);
|
||||
|
||||
uint8_t data_out[2];
|
||||
memset(data_out, 0, sizeof(data_out));
|
||||
cl_assert_equal_i(sizeof(data_out),
|
||||
comm_session_send_queue_copy(s_valid_session, 0, sizeof(data_out), data_out));
|
||||
cl_assert_equal_m(data_out, TEST_DATA, sizeof(data_out));
|
||||
}
|
||||
|
||||
void test_session_send_queue__copy_less_than_head_job_with_offset_shorter_than_job(void) {
|
||||
int num_jobs = 3;
|
||||
prv_add_jobs(num_jobs);
|
||||
|
||||
uint8_t data_out[1];
|
||||
memset(data_out, 0, sizeof(data_out));
|
||||
int offset = 1;
|
||||
cl_assert_equal_i(sizeof(data_out),
|
||||
comm_session_send_queue_copy(s_valid_session, offset,
|
||||
sizeof(data_out), data_out));
|
||||
cl_assert_equal_m(data_out, TEST_DATA + offset, sizeof(data_out));
|
||||
}
|
||||
|
||||
void test_session_send_queue__copy_less_than_head_job_with_offset_longer_than_job(void) {
|
||||
int num_jobs = 3;
|
||||
prv_add_jobs(num_jobs);
|
||||
|
||||
uint8_t data_out[sizeof(TEST_DATA) - 1];
|
||||
memset(data_out, 0, sizeof(data_out));
|
||||
int offset = sizeof(TEST_DATA) + 1;
|
||||
cl_assert_equal_i(sizeof(data_out),
|
||||
comm_session_send_queue_copy(s_valid_session, offset,
|
||||
sizeof(data_out), data_out));
|
||||
cl_assert_equal_m(data_out, TEST_DATA + (offset % sizeof(TEST_DATA)), sizeof(data_out));
|
||||
}
|
||||
|
||||
void test_session_send_queue__copy_overlapping_multiple_jobs_with_offset(void) {
|
||||
int num_jobs = 3;
|
||||
prv_add_jobs(num_jobs);
|
||||
|
||||
uint8_t data_out[2 * sizeof(TEST_DATA)];
|
||||
memset(data_out, 0, sizeof(data_out));
|
||||
int offset = 1;
|
||||
cl_assert_equal_i(sizeof(data_out),
|
||||
comm_session_send_queue_copy(s_valid_session, offset,
|
||||
sizeof(data_out), data_out));
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
cl_assert_equal_m(data_out + (i * sizeof(TEST_DATA)),
|
||||
TEST_DATA + offset, sizeof(TEST_DATA) - offset);
|
||||
cl_assert_equal_m(data_out + ((i + 1) * sizeof(TEST_DATA)) - offset,
|
||||
TEST_DATA, offset);
|
||||
}
|
||||
}
|
||||
|
||||
void test_session_send_queue__get_read_pointer(void) {
|
||||
cl_assert_equal_i(s_free_count, 0);
|
||||
|
||||
int num_jobs = 3;
|
||||
prv_add_jobs(num_jobs);
|
||||
const uint8_t *data_out = NULL;
|
||||
|
||||
for (int consumed = 0; consumed < sizeof(TEST_DATA); ++consumed) {
|
||||
cl_assert_equal_i(comm_session_send_queue_get_read_pointer(s_valid_session, &data_out),
|
||||
sizeof(TEST_DATA) - consumed);
|
||||
cl_assert_equal_m(data_out, TEST_DATA + consumed, sizeof(TEST_DATA) - consumed);
|
||||
|
||||
comm_session_send_queue_consume(s_valid_session, 1);
|
||||
}
|
||||
|
||||
// Expect head to be free'd:
|
||||
cl_assert_equal_i(s_free_count, 1);
|
||||
|
||||
// Next job can be read:
|
||||
cl_assert_equal_i(comm_session_send_queue_get_read_pointer(s_valid_session, &data_out),
|
||||
sizeof(TEST_DATA));
|
||||
cl_assert_equal_m(data_out, TEST_DATA, sizeof(TEST_DATA));
|
||||
}
|
||||
|
||||
void test_session_send_queue__get_read_pointer_no_jobs(void) {
|
||||
const uint8_t *data_out = NULL;
|
||||
cl_assert_equal_i(0, comm_session_send_queue_get_read_pointer(s_valid_session, &data_out));
|
||||
}
|
||||
|
||||
void test_session_send_queue__consume_more_than_one_job(void) {
|
||||
int num_jobs = 3;
|
||||
prv_add_jobs(num_jobs);
|
||||
|
||||
size_t consumed = sizeof(TEST_DATA) + 1;
|
||||
comm_session_send_queue_consume(s_valid_session, consumed);
|
||||
|
||||
// Expect head to be free'd:
|
||||
cl_assert_equal_i(s_free_count, 1);
|
||||
|
||||
cl_assert_equal_i((num_jobs * sizeof(TEST_DATA)) - consumed,
|
||||
comm_session_send_queue_get_length(s_valid_session));
|
||||
}
|
||||
|
||||
void test_session_send_queue__consume_all(void) {
|
||||
int num_jobs = 3;
|
||||
prv_add_jobs(num_jobs);
|
||||
|
||||
comm_session_send_queue_consume(s_valid_session, UINT32_MAX);
|
||||
|
||||
cl_assert_equal_i(s_free_count, num_jobs);
|
||||
}
|
||||
|
||||
void test_session_send_queue__cleanup_calls_free_on_all_jobs(void) {
|
||||
cl_assert_equal_i(s_free_count, 0);
|
||||
|
||||
// Add a couple jobs:
|
||||
int num_jobs = 3;
|
||||
prv_add_jobs(num_jobs);
|
||||
|
||||
// When the session is disconnected, comm_session_send_queue_cleanup() is called:
|
||||
comm_session_send_queue_cleanup(s_valid_session);
|
||||
|
||||
cl_assert_equal_i(s_free_count, num_jobs);
|
||||
}
|
||||
|
||||
void test_session_send_queue__session_closed_when_add_is_called(void) {
|
||||
s_valid_session = NULL;
|
||||
|
||||
SessionSendQueueJob *job = prv_create_test_job(TEST_DATA, sizeof(TEST_DATA));
|
||||
comm_session_send_queue_add_job(s_valid_session, &job);
|
||||
cl_assert(!job);
|
||||
cl_assert_equal_i(s_free_count, 1);
|
||||
}
|
64
tests/fw/services/comm_session/wscript
Normal file
64
tests/fw/services/comm_session/wscript
Normal file
|
@ -0,0 +1,64 @@
|
|||
from waftools.pebble_test import clar
|
||||
|
||||
|
||||
def build(bld):
|
||||
clar(bld,
|
||||
sources_ant_glob=(
|
||||
"src/fw/services/common/comm_session/meta_endpoint.c "
|
||||
"tests/fakes/fake_session.c "
|
||||
),
|
||||
test_sources_ant_glob="test_meta_endpoint.c")
|
||||
|
||||
clar(bld,
|
||||
sources_ant_glob=(
|
||||
"src/fw/util/rand/rand.c "
|
||||
"src/fw/vendor/tinymt32/tinymt32.c "
|
||||
"src/fw/services/common/comm_session/session.c "
|
||||
"tests/fakes/fake_session_send_buffer.c"
|
||||
),
|
||||
test_sources_ant_glob="test_session.c",
|
||||
override_includes=['dummy_board'])
|
||||
|
||||
clar(bld,
|
||||
sources_ant_glob=(
|
||||
"src/fw/services/common/comm_session/default_kernel_sender.c "
|
||||
"src/fw/services/common/comm_session/session_send_queue.c "
|
||||
"tests/fakes/fake_queue.c "
|
||||
"tests/fakes/fake_rtc.c"
|
||||
),
|
||||
test_sources_ant_glob="test_session_send_buffer.c")
|
||||
|
||||
clar(bld,
|
||||
sources_ant_glob=(
|
||||
"src/fw/services/common/comm_session/session_send_queue.c "
|
||||
),
|
||||
test_sources_ant_glob="test_session_send_queue.c")
|
||||
|
||||
clar(bld,
|
||||
sources_ant_glob=(
|
||||
"src/fw/util/rand/rand.c "
|
||||
"src/fw/vendor/tinymt32/tinymt32.c "
|
||||
"src/fw/services/common/comm_session/session.c "
|
||||
"src/fw/services/common/comm_session/session_receive_router.c "
|
||||
"tests/fakes/fake_session_send_buffer.c "
|
||||
),
|
||||
test_sources_ant_glob="test_session_receive_router.c",
|
||||
override_includes=['dummy_board', 'pp_endpoints'])
|
||||
|
||||
clar(bld,
|
||||
sources_ant_glob=(
|
||||
"src/fw/services/common/comm_session/session_remote_version.c "
|
||||
"tests/fakes/fake_events.c "
|
||||
),
|
||||
test_sources_ant_glob="test_session_remote_version.c",
|
||||
override_includes=['dummy_board'])
|
||||
|
||||
clar(bld,
|
||||
sources_ant_glob=(
|
||||
"src/fw/services/common/comm_session/default_kernel_receiver.c "
|
||||
),
|
||||
test_sources_ant_glob="test_default_kernel_receiver.c",
|
||||
override_includes=['dummy_board'])
|
||||
|
||||
|
||||
# vim:filetype=python
|
Loading…
Add table
Add a link
Reference in a new issue