/* * 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 "comm/remote.h" #include "process_management/app_run_state.h" #include "services/common/comm_session/protocol.h" #include "system/passert.h" #include "util/attributes.h" #include "util/list.h" #include // Stubs /////////////////////////////////////// #include "stubs_logging.h" #include "stubs_passert.h" #include "stubs_pbl_malloc.h" #include "stubs_rand_ptr.h" // Fakes /////////////////////////////////////// #include "fake_app_manager.h" #include "fake_pebble_tasks.h" // Structures /////////////////////////////////////// typedef struct PACKED { uint8_t command; Uuid uuid; } AppStateMessage; struct CommSession { }; typedef struct PACKED { AppState state:8; Uuid uuid; } AppRunState; // Globals /////////////////////////////////////// CommSession *s_session = NULL; static uint8_t s_launcher_deprecated_messages = 0; static uint8_t s_app_run_state_messages = 0; static uint8_t s_free_count = 0; static uint8_t s_malloc_count = 0; static void *s_ptr = NULL; static uint8_t s_app_state; static uint64_t s_flags = 0; static const Uuid s_app_uuid = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5 }; // Helpers /////////////////////////////////////// static void prv_set_remote_active(void) { static const int deadbeef = 0xdeadbeef; s_session = (void*)&deadbeef; s_flags = 0; } static void prv_set_expected(AppState app_state) { s_app_state = app_state; } static void prv_set_remote_capability(CommSessionCapability c) { s_flags |= c; } bool comm_session_has_capability(CommSession *session, CommSessionCapability c) { return (s_flags & c) != 0; } void app_install_unmark_prioritized(const Uuid *uuid) { return; } bool app_install_is_app_running(AppInstallId id) { return true; } void app_install_mark_prioritized(AppInstallId install_id, bool can_expire) { } bool system_task_add_callback(void(*cb)(void *data), void *data) { cb(data); return true; } status_t app_cache_app_launched(AppInstallId id) { return 0; } void app_manager_put_launch_app_event(const AppLaunchEventConfig *config) { app_run_state_send_update(&app_manager_get_current_app_md()->uuid, RUNNING); } void process_manager_put_kill_process_event(PebbleTask task, bool gracefully) { app_run_state_send_update(&app_manager_get_current_app_md()->uuid, NOT_RUNNING); } CommSession *comm_session_get_system_session(void) { return s_session; } void launcher_app_message_send_app_state_deprecated(const Uuid *uuid, bool running) { s_launcher_deprecated_messages++; cl_assert(running == (s_app_state == RUNNING ? true : false)); } bool comm_session_send_data(CommSession *session, uint16_t endpoint_id, const uint8_t *data, size_t length, uint32_t timeout_ms) { AppRunState *state = (AppRunState*)data; s_app_run_state_messages++; cl_assert(state->state == s_app_state); return true; } void bt_lock(void) { return; } void bt_unlock(void) { return; } // Tests /////////////////////////////////////// extern void app_run_state_protocol_msg_callback(CommSession*, const uint8_t*, size_t); void test_app_run_state__initialize(void) { s_launcher_deprecated_messages = 0; s_app_run_state_messages = 0; s_session = NULL; s_flags = 0; s_malloc_count = 0; s_free_count = 0; stub_app_init(); } void test_app_run_state__cleanup(void) { // Always ensure that after any test, all malloc'd data has been freed cl_assert_equal_i(s_malloc_count, s_free_count); } void test_app_run_state__send_update(void) { // Tests that when app_run_state_send_update is called, that the proper // callback is triggered depending on the device / version app_run_state_send_update(&s_app_uuid, RUNNING); // When no remote is set, this should just not run cl_assert_equal_i(s_launcher_deprecated_messages, 0); // Set the remote as being active prv_set_remote_active(); stub_app_set_uuid(s_app_uuid); // When app_run_state is not supported, should use launcher_app_message prv_set_expected(RUNNING); app_run_state_send_update(&s_app_uuid, RUNNING); cl_assert_equal_i(s_launcher_deprecated_messages, 1); // When app_run_state is supported, should use app_run_state prv_set_expected(NOT_RUNNING); prv_set_remote_capability(CommSessionRunState); app_run_state_send_update(&s_app_uuid, NOT_RUNNING); cl_assert_equal_i(s_launcher_deprecated_messages, 1); cl_assert_equal_i(s_app_run_state_messages, 1); // Changing the remote should change the flags and use launcher_app_message // if app_run_state not supported prv_set_remote_active(); app_run_state_send_update(&s_app_uuid, NOT_RUNNING); cl_assert_equal_i(s_launcher_deprecated_messages, 2); cl_assert_equal_i(s_app_run_state_messages, 1); } void test_app_run_state__protocol_msg_callback(void) { // Tests app_run_state_procotol_msg_callback which should take data // from a source and perform the appropriate command prv_set_remote_active(); prv_set_remote_capability(CommSessionRunState); stub_app_set_uuid(s_app_uuid); stub_app_set_install_id(1337); CommSession session; AppStateMessage msg; msg.command = APP_RUN_STATE_INVALID_COMMAND; memcpy(&msg.uuid, &s_app_uuid, sizeof(s_app_uuid)); app_run_state_protocol_msg_callback(&session, (uint8_t*)&msg, sizeof(msg)); // This should be a noop since the key is invalid cl_assert_equal_i(s_launcher_deprecated_messages, 0); cl_assert_equal_i(s_app_run_state_messages, 0); AppRunStateCommand commands[] = { APP_RUN_STATE_INVALID_COMMAND, APP_RUN_STATE_RUN_COMMAND, APP_RUN_STATE_STOP_COMMAND, APP_RUN_STATE_STATUS_COMMAND }; AppState expected[] = { RUNNING, RUNNING, NOT_RUNNING, RUNNING }; // Since our version is >= 2.2, this should use the new endpoint // And check that we're getting back the right state for (int msg_count = 0; msg_count < 4; msg_count++) { msg.command = commands[msg_count]; prv_set_expected(expected[msg_count]); app_run_state_protocol_msg_callback(&session, (uint8_t*)&msg, sizeof(msg)); cl_assert_equal_i(s_launcher_deprecated_messages, 0); cl_assert_equal_i(s_app_run_state_messages, msg_count); } app_run_state_protocol_msg_callback(NULL, (uint8_t*)&msg, sizeof(msg)); cl_assert_equal_i(s_launcher_deprecated_messages, 1); }