mirror of
https://github.com/google/pebble.git
synced 2025-07-15 02:26:43 -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
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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue