mirror of
https://github.com/google/pebble.git
synced 2025-03-20 11:01:20 +00:00
274 lines
9.4 KiB
C
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();
|
|
}
|
|
|