pebble/tests/fw/applib/test_app_outbox.c
2025-01-27 11:38:16 -08:00

274 lines
9.4 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "applib/app_outbox.h"
#include "applib/event_service_client.h"
#include "services/normal/app_outbox_service.h"
#include "clar.h"
extern void app_outbox_service_deinit(void);
extern uint32_t app_outbox_service_max_pending_messages(AppOutboxServiceTag tag);
extern uint32_t app_outbox_service_max_message_length(AppOutboxServiceTag tag);
////////////////////////////////////////////////////////////////////////////////////////////////////
// Fakes & Stubs
#include "fake_kernel_malloc.h"
#include "fake_pebble_tasks.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_syscall_internal.h"
static EventServiceInfo s_app_state_app_outbox_subscription_info;
EventServiceInfo *app_state_get_app_outbox_subscription_info(void) {
return &s_app_state_app_outbox_subscription_info;
}
void event_service_client_subscribe(EventServiceInfo * service_info) {
}
void sys_send_pebble_event_to_kernel(PebbleEvent* event) {
cl_assert_equal_i(event->type, PEBBLE_APP_OUTBOX_MSG_EVENT);
event->callback.callback(event->callback.data);
}
static int s_num_app_outbox_events_sent;
bool process_manager_send_event_to_process(PebbleTask task, PebbleEvent* e) {
cl_assert_equal_i(e->type, PEBBLE_APP_OUTBOX_SENT_EVENT);
cl_assert(e->app_outbox_sent.sent_handler);
e->app_outbox_sent.sent_handler(e->app_outbox_sent.status,
e->app_outbox_sent.cb_ctx);
++s_num_app_outbox_events_sent;
return true;
}
void app_message_outbox_handle_app_outbox_message_sent(AppOutboxStatus status, void *cb_ctx) {
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Helpers
static int s_num_message_handler_calls;
static AppOutboxMessage *s_last_message;
static void prv_message_handler(AppOutboxMessage *message) {
s_last_message = message;
++s_num_message_handler_calls;
}
static int s_num_sent_handler_called;
static AppOutboxStatus s_last_sent_status;
static void *s_expected_cb_ctx = (void *)0x77777777;
void test_app_outbox_sent_handler(AppOutboxStatus status, void *cb_ctx) {
cl_assert_equal_p(s_expected_cb_ctx, cb_ctx);
s_last_sent_status = status;
++s_num_sent_handler_called;
}
#define assert_sent_cb_last_status(expected_status) \
cl_assert_equal_i(s_last_sent_status, expected_status)
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tests
static uint8_t *s_test_data;
static size_t s_test_data_length;
void test_app_outbox__initialize(void) {
fake_kernel_malloc_init();
fake_kernel_malloc_enable_stats(true);
s_test_data_length = app_outbox_service_max_message_length(AppOutboxServiceTagUnitTest);
s_test_data = kernel_malloc(s_test_data_length);
memset(s_test_data, 0x88, s_test_data_length);
s_num_sent_handler_called = 0;
s_num_app_outbox_events_sent = 0;
s_num_message_handler_calls = 0;
s_last_message = NULL;
stubs_syscall_init();
s_app_state_app_outbox_subscription_info = (EventServiceInfo) {};
// set to something that is not expected anywhere in the tests:
s_last_sent_status = AppOutboxStatusUserRangeEnd;
app_outbox_service_init();
app_outbox_init();
}
void test_app_outbox__cleanup(void) {
app_outbox_service_deinit();
kernel_free(s_test_data);
}
static size_t s_consumer_data_length = 1;
static void prv_register(void) {
app_outbox_service_register(AppOutboxServiceTagUnitTest,
prv_message_handler,
PebbleTask_KernelMain,
s_consumer_data_length);
}
void test_app_outbox__register_twice_asserts(void) {
prv_register();
cl_assert_passert(prv_register());
}
void test_app_outbox__send_not_user_space_buffer(void) {
// TODO: really implement privilege escalation in unit tests. See PBL-9688
return;
cl_assert_passert(app_outbox_send(NULL, s_test_data_length,
test_app_outbox_sent_handler, s_expected_cb_ctx));
assert_syscall_failed();
}
// Disallowed, because it's not white-listed in app_outbox_service.c
static void prv_disallowed_sent_handler(AppOutboxStatus status, void *cb_ctx) {
}
void test_app_outbox__send_disallowed_sent_handler(void) {
prv_register();
cl_assert_passert(app_outbox_send(s_test_data, s_test_data_length,
prv_disallowed_sent_handler, s_expected_cb_ctx));
assert_syscall_failed();
}
void test_app_outbox__send_max_length_exceeded(void) {
prv_register();
cl_assert_passert(app_outbox_send(s_test_data, s_test_data_length + 1,
test_app_outbox_sent_handler, s_expected_cb_ctx));
assert_syscall_failed();
}
void test_app_outbox__send_but_consumer_not_registered(void) {
prv_register();
app_outbox_service_unregister(AppOutboxServiceTagUnitTest);
app_outbox_send(s_test_data, s_test_data_length,
test_app_outbox_sent_handler, s_expected_cb_ctx);
assert_sent_cb_last_status(AppOutboxStatusConsumerDoesNotExist);
}
void test_app_outbox__send_but_max_pending_messages_reached(void) {
prv_register();
uint32_t max_pending_messages =
app_outbox_service_max_pending_messages(AppOutboxServiceTagUnitTest);
for (uint32_t i = 0; i < max_pending_messages; ++i) {
app_outbox_send(s_test_data, s_test_data_length,
test_app_outbox_sent_handler, s_expected_cb_ctx);
cl_assert_equal_i(s_num_sent_handler_called, 0);
}
app_outbox_send(s_test_data, s_test_data_length,
test_app_outbox_sent_handler, s_expected_cb_ctx);
assert_sent_cb_last_status(AppOutboxStatusOutOfResources);
}
void test_app_outbox__send_but_oom(void) {
prv_register();
fake_kernel_malloc_set_largest_free_block(0);
app_outbox_send(s_test_data, s_test_data_length,
test_app_outbox_sent_handler, s_expected_cb_ctx);
assert_sent_cb_last_status(AppOutboxStatusOutOfMemory);
}
void test_app_outbox__send_but_null_sent_handler(void) {
prv_register();
// Invalid data, so normally an event would get put to invoke the sent_handler,
// but sent handler is NULL. Expect no events to be put.
cl_assert_passert(app_outbox_send(NULL, 0, NULL, s_expected_cb_ctx));
cl_assert_equal_i(s_num_app_outbox_events_sent, 0);
}
void test_app_outbox__send(void) {
fake_kernel_malloc_mark();
prv_register();
uint32_t max_pending_messages =
app_outbox_service_max_pending_messages(AppOutboxServiceTagUnitTest);
AppOutboxMessage *message[max_pending_messages];
for (uint32_t i = 0; i < max_pending_messages; ++i) {
app_outbox_send(s_test_data, s_test_data_length,
test_app_outbox_sent_handler, s_expected_cb_ctx);
cl_assert_equal_i(s_num_app_outbox_events_sent, 0);
cl_assert_equal_i(s_num_message_handler_calls, i + 1);
cl_assert(s_last_message);
cl_assert_equal_p(s_test_data, s_last_message->data);
cl_assert_equal_i(s_test_data_length, s_last_message->length);
cl_assert_equal_b(false, app_outbox_service_is_message_cancelled(s_last_message));
message[i] = s_last_message;
}
for (uint32_t i = 0; i < max_pending_messages; ++i) {
app_outbox_service_consume_message(message[i], AppOutboxStatusSuccess);
cl_assert_equal_i(s_num_app_outbox_events_sent, i + 1);
assert_sent_cb_last_status(AppOutboxStatusSuccess);
}
fake_kernel_malloc_mark_assert_equal();
}
void test_app_outbox__unregister_with_pending_message(void) {
fake_kernel_malloc_mark();
prv_register();
app_outbox_send(s_test_data, s_test_data_length,
test_app_outbox_sent_handler, s_expected_cb_ctx);
cl_assert(s_last_message);
app_outbox_service_unregister(AppOutboxServiceTagUnitTest);
cl_assert_equal_i(s_num_app_outbox_events_sent, 1);
assert_sent_cb_last_status(AppOutboxStatusConsumerDoesNotExist);
cl_assert_equal_b(true, app_outbox_service_is_message_cancelled(s_last_message));
// the consumer must call ..._consume_message(), to free the resources:
app_outbox_service_consume_message(s_last_message, AppOutboxStatusSuccess);
// sent_handler shouldn't get called again, it's already been called:
cl_assert_equal_i(s_num_app_outbox_events_sent, 1);
fake_kernel_malloc_mark_assert_equal();
}
void test_app_outbox__cleanup_all_with_pending_message(void) {
fake_kernel_malloc_mark();
prv_register();
app_outbox_send(s_test_data, s_test_data_length,
test_app_outbox_sent_handler, s_expected_cb_ctx);
cl_assert(s_last_message);
app_outbox_service_cleanup_all_pending_messages();
// sent_handler shouldn't get called when cleaning up:
cl_assert_equal_i(s_num_app_outbox_events_sent, 0);
cl_assert_equal_b(true, app_outbox_service_is_message_cancelled(s_last_message));
// the consumer must call ..._consume_message(), to free the resources:
app_outbox_service_consume_message(s_last_message, AppOutboxStatusSuccess);
fake_kernel_malloc_mark_assert_equal();
}