/* * 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(); }