/* * 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 "applib/rockyjs/rocky_res.h" #include "services/common/comm_session/session.h" #include "services/normal/app_fetch_endpoint.h" #include "system/logging.h" #include "util/attributes.h" #include // Fakes //////////////////////////////////// #include "fake_events.h" #include "fake_new_timer.h" #include "fake_pbl_malloc.h" #include "fake_session.h" #include "fake_system_task.h" // Stubs //////////////////////////////////// #include "stubs_app_cache.h" #include "stubs_bt_lock.h" #include "stubs_hexdump.h" #include "stubs_logging.h" #include "stubs_passert.h" #include "stubs_rand_ptr.h" #include "stubs_queue.h" typedef struct {} EventServiceInfo; void app_event_service_subscribe(EventServiceInfo * service_info) { return; } void put_bytes_cancel(void) { } void put_bytes_expect_init(uint32_t timeout_ms) { } void app_storage_delete_bank(uint32_t bank) { } const PebbleProcessMd *app_install_get_md(AppInstallId id, bool worker) { return NULL; } void app_install_release_md(const PebbleProcessMd *md) { } RockyResourceValidation s_rocky_app_validate_resources__result; RockyResourceValidation rocky_app_validate_resources(const PebbleProcessMd *md) { return s_rocky_app_validate_resources__result; } typedef struct PACKED { uint16_t length; uint16_t endpoint_id; } PebbleProtocolHeader; enum { APP_FETCH_INSTALL_COMMAND = 0x01, } AppFetchCommand; enum { APP_FETCH_INSTALL_RESPONSE = 0x01, } AppFetchResponse; typedef struct AppFetchData { CommSession *session; size_t length; uint8_t data[]; } AppFetchData; typedef struct PACKED { uint8_t command; Uuid uuid; uint32_t app_id; } AppFetchRequest; extern void app_fetch_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length); static const uint16_t APP_FETCH_ENDPOINT_ID = 6001; static const uint32_t app_id_1 = 42; static const Uuid uuid_1 = {0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4}; Transport *s_transport; /* Start of test */ void test_app_fetch_endpoint__initialize(void) { fake_comm_session_init(); s_transport = fake_transport_create(TransportDestinationSystem, NULL, NULL); fake_transport_set_connected(s_transport, true /* connected */); } void test_app_fetch_endpoint__cleanup(void) { fake_transport_destroy(s_transport); s_transport = NULL; fake_comm_session_cleanup(); fake_system_task_callbacks_cleanup(); } /************************************* * Checking for valid INSERT command * *************************************/ static const uint8_t s_app_fetch_success[] = { // Message Header 0x01, // APP_FETCH_INSTALL_RESPONSE 0x01, // ACK: 0x01 }; static void prv_check_valid_app_fetch_request(uint16_t endpoint_id, const uint8_t* data, unsigned int data_length) { // AppFetchRequest request = *(AppFetchRequest *)data; PBL_LOG(LOG_LEVEL_DEBUG, "sizeof: %lu length: %u", sizeof(AppFetchRequest), data_length); AppFetchRequest request = *(AppFetchRequest *)data; cl_assert_equal_i(endpoint_id, APP_FETCH_ENDPOINT_ID); cl_assert_equal_i(request.command, APP_FETCH_INSTALL_COMMAND); cl_assert_equal_i(request.app_id, app_id_1); cl_assert_equal_b(true, uuid_equal(&request.uuid, &uuid_1)); // if anything is planning on being done relating to this ACK, then it will be done after this app_fetch_protocol_msg_callback(comm_session_get_system_session(), s_app_fetch_success, sizeof(s_app_fetch_success)); } void test_app_fetch_endpoint__app_fetch_binaries(void) { // set the function that will validate the data in the send buffer fake_transport_set_sent_cb(s_transport, prv_check_valid_app_fetch_request); // queue of system task to send an app_fetch request app_fetch_binaries(&uuid_1, app_id_1, 0); // process system task events fake_system_task_callbacks_invoke_pending(); fake_comm_session_process_send_next(); // if anything is planning on being done relating to the ACK sent back from phone, // then it should be checked here. } static void prv_fetch_complete_app() { app_fetch_binaries(&uuid_1, app_id_1, false); app_fetch_put_bytes_event_handler(&(PebblePutBytesEvent){ .type = PebblePutBytesEventTypeCleanup, .object_type = ObjectAppResources, .has_cookie = true, }); fake_system_task_callbacks_invoke_pending(); app_fetch_put_bytes_event_handler(&(PebblePutBytesEvent){ .type = PebblePutBytesEventTypeCleanup, .object_type = ObjectWatchApp, .has_cookie = true, }); fake_system_task_callbacks_invoke_pending(); } void test_app_fetch_endpoint__no_incompatible_js(void) { s_rocky_app_validate_resources__result = RockyResourceValidation_Valid; prv_fetch_complete_app(); const PebbleEvent e = fake_event_get_last(); cl_assert_equal_i(PEBBLE_APP_FETCH_EVENT, e.type); cl_assert_equal_i(AppFetchEventTypeFinish, e.app_fetch.type); } void test_app_fetch_endpoint__incompatible_js(void) { s_rocky_app_validate_resources__result = RockyResourceValidation_Invalid; prv_fetch_complete_app(); const PebbleEvent e = fake_event_get_last(); cl_assert_equal_i(PEBBLE_APP_FETCH_EVENT, e.type); cl_assert_equal_i(AppFetchEventTypeError, e.app_fetch.type); cl_assert_equal_i(AppFetchResultIncompatibleJSFailure, e.app_fetch.error_code); }