/* * 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 "process_management/app_install_manager.h" #include "resource/resource_ids.auto.h" #include "services/normal/timeline/timeline_resources.h" #include "system/passert.h" #include "util/struct.h" // Stubs ///////////////////////// #include "stubs_kino_reel.h" #include "stubs_logging.h" #include "stubs_passert.h" #include "stubs_pbl_malloc.h" #include "stubs_resources.h" #include "stubs_syscalls.h" // Test Data ///////////////////////// typedef enum TimelineResourceTestAppTimelineId { // We start at 1 because TIMELINE_RESOURCE_INVALID = 0 TimelineResourceTestTimelineId_AlarmClock = 1, TimelineResourceTestTimelineId_Basketball, TimelineResourceTestTimelineIdCount } TimelineResourceTestTimelineId; static const uint32_t s_app_lut[TimelineResourceTestTimelineIdCount][TimelineResourceSizeCount] = { [TIMELINE_RESOURCE_INVALID] = { RESOURCE_ID_INVALID, RESOURCE_ID_INVALID, RESOURCE_ID_INVALID }, [TimelineResourceTestTimelineId_AlarmClock] = { RESOURCE_ID_ALARM_CLOCK_TINY, RESOURCE_ID_ALARM_CLOCK_SMALL, RESOURCE_ID_ALARM_CLOCK_LARGE }, [TimelineResourceTestTimelineId_Basketball] = { RESOURCE_ID_BASKETBALL_TINY, RESOURCE_ID_BASKETBALL_SMALL, RESOURCE_ID_BASKETBALL_LARGE }, }; typedef struct TimelineResourceTestAppData { AppInstallEntry install_entry; const uint32_t (*resource_lut)[TimelineResourceSizeCount]; } TimelineResourceTestAppData; typedef enum TimelineResourceTestAppId { // We start from 1 because INSTALL_ID_INVALID = 0 TimelineResourceTestAppId_AppWithInvalidLUT = 1, TimelineResourceTestAppId_AppWithInvalidSDKVersion, TimelineResourceTestAppId_ValidApp, TimelineResourceTestAppIdInvalid, TimelineResourceTestAppIdCount = TimelineResourceTestAppIdInvalid - 1 } TimelineResourceTestAppId; static const TimelineResourceTestAppData s_test_apps[TimelineResourceTestAppIdCount] = { { .install_entry = { .install_id = TimelineResourceTestAppId_AppWithInvalidLUT, .uuid = {0x3c, 0x6e, 0x2e, 0x1d, 0x61, 0x7d, 0x4d, 0x17, 0x97, 0xa1, 0xbc, 0x43, 0x2d, 0x87, 0x4c, 0xed}, .sdk_version = {TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MAJOR, TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MINOR}, }, // No resource_lut specified because this app has an "invalid" lut }, { .install_entry = { .install_id = TimelineResourceTestAppId_AppWithInvalidSDKVersion, .uuid = {0x37, 0xe7, 0x64, 0x5e, 0xd, 0x6a, 0x41, 0xfe, 0xb8, 0x80, 0xea, 0x47, 0x5a, 0x5f, 0x34, 0x34}, // We set the SDK version to one earlier than the first version supporting timeline resources .sdk_version = {TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MAJOR, TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MINOR - 1}, }, .resource_lut = s_app_lut, }, { .install_entry = { .install_id = TimelineResourceTestAppId_ValidApp, .uuid = {0x9e, 0x95, 0x8b, 0xfe, 0xd, 0xbd, 0x4d, 0xf2, 0xbe, 0xbc, 0xf3, 0x77, 0x5d, 0x8d, 0x9f, 0x95}, .sdk_version = {TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MAJOR, TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MINOR}, }, .resource_lut = s_app_lut, }, }; static const TimelineResourceTestAppData *prv_get_data_for_app_with_id(AppInstallId install_id) { for (int i = 0; i < TimelineResourceTestAppIdCount; i++) { const TimelineResourceTestAppData *data = &s_test_apps[i]; if (data->install_entry.install_id == install_id) { return data; } } return NULL; } static const TimelineResourceTestAppData *prv_get_data_for_app_with_uuid(const Uuid *uuid) { for (int i = 0; i < TimelineResourceTestAppIdCount; i++) { const TimelineResourceTestAppData *data = &s_test_apps[i]; if (uuid_equal(uuid, &data->install_entry.uuid)) { return data; } } return NULL; } // Fakes ///////////////////////// bool prv_validate_lut(ResAppNum res_app_num) { // Just check if the .resource_lut pointer for the provided res_app_num is non-NULL const TimelineResourceTestAppData *data = prv_get_data_for_app_with_id(res_app_num); return data ? (data->resource_lut != NULL) : false; } uint32_t prv_get_app_resource_id(ResAppNum res_app_num, TimelineResourceId timeline_id, TimelineResourceSize size) { // Size must be valid if (size >= TimelineResourceSizeCount) { return RESOURCE_ID_INVALID; } // This only supports valid non-system apps const TimelineResourceTestAppData *data = prv_get_data_for_app_with_id(res_app_num); if (!data) { return RESOURCE_ID_INVALID; } // The app must have a valid LUT if (!data->resource_lut) { return RESOURCE_ID_INVALID; } return data->resource_lut[timeline_id][size]; } static bool s_is_app_published_resource_invalid; bool prv_is_app_published_resource_valid(const AppResourceInfo *res_info) { return !s_is_app_published_resource_invalid; } AppInstallId app_install_get_id_for_uuid(const Uuid *uuid) { const TimelineResourceTestAppData *data = prv_get_data_for_app_with_uuid(uuid); return NULL_SAFE_FIELD_ACCESS(data, install_entry.install_id, INSTALL_ID_INVALID); } bool app_install_get_entry_for_install_id(AppInstallId install_id, AppInstallEntry *entry) { if (!entry) { return false; } const TimelineResourceTestAppData *data = prv_get_data_for_app_with_id(install_id); if (!data) { return false; } *entry = data->install_entry; return true; } ResAppNum app_install_get_app_icon_bank(const AppInstallEntry *entry) { PBL_ASSERTN(entry); if (uuid_equal(&entry->uuid, &(Uuid)UUID_SYSTEM)) { return SYSTEM_APP; } else { return entry->install_id; } } // Setup ///////////////////////// void test_timeline_resources__initialize(void) { s_is_app_published_resource_invalid = false; } // Tests ///////////////////////// void test_timeline_resources__get_id_system(void) { AppResourceInfo res_info; // Calling the function with an invalid TimelineResourceId should return false cl_assert(!timeline_resources_get_id_system(TIMELINE_RESOURCE_INVALID, TimelineResourceSizeTiny, TimelineResourceTestAppId_ValidApp, &res_info)); // Calling the function with an invalid size should return false cl_assert(!timeline_resources_get_id_system( (TimelineResourceId)TimelineResourceTestTimelineId_AlarmClock, TimelineResourceSizeCount, TimelineResourceTestAppId_ValidApp, &res_info)); // Calling the function with the ResAppNum of an app with an invalid LUT should return false cl_assert(!timeline_resources_get_id_system( (TimelineResourceId)TimelineResourceTestTimelineId_AlarmClock, TimelineResourceSizeTiny, TimelineResourceTestAppId_AppWithInvalidLUT, &res_info)); // Calling the function for an invalid resource (e.g. dimensions too large) should return false s_is_app_published_resource_invalid = true; cl_assert(!timeline_resources_get_id_system( (TimelineResourceId)TimelineResourceTestTimelineId_AlarmClock, TimelineResourceSizeTiny, TimelineResourceTestAppId_ValidApp, &res_info)); s_is_app_published_resource_invalid = false; // Calling the function with valid args should return true and set the correct values in res_info cl_assert(timeline_resources_get_id_system( (TimelineResourceId)TimelineResourceTestTimelineId_AlarmClock, TimelineResourceSizeTiny, TimelineResourceTestAppId_ValidApp, &res_info)); cl_assert_equal_i(res_info.res_app_num, TimelineResourceTestAppId_ValidApp); cl_assert_equal_i(res_info.res_id, RESOURCE_ID_ALARM_CLOCK_TINY); // Calling the function with valid args should return true even if no AppResourceInfo is provided cl_assert(timeline_resources_get_id_system( (TimelineResourceId)TimelineResourceTestTimelineId_AlarmClock, TimelineResourceSizeTiny, TimelineResourceTestAppId_ValidApp, NULL)); // Calling the function with a valid system TimelineResourceId should return true and set res_info cl_assert(timeline_resources_get_id_system(TIMELINE_RESOURCE_AUDIO_CASSETTE, TimelineResourceSizeSmall, SYSTEM_APP, &res_info)); cl_assert_equal_i(res_info.res_app_num, SYSTEM_APP); cl_assert_equal_i(res_info.res_id, RESOURCE_ID_AUDIO_CASSETTE_SMALL); // Even if the provided ResAppNum != SYSTEM_APP cl_assert(timeline_resources_get_id_system(TIMELINE_RESOURCE_AUDIO_CASSETTE, TimelineResourceSizeSmall, TIMELINE_RESOURCE_INVALID, &res_info)); cl_assert_equal_i(res_info.res_app_num, SYSTEM_APP); cl_assert_equal_i(res_info.res_id, RESOURCE_ID_AUDIO_CASSETTE_SMALL); } void test_timeline_resources__get_id(void) { TimelineResourceInfo timeline_res_info; AppResourceInfo res_info; const TimelineResourceTestAppData *valid_app_data = prv_get_data_for_app_with_id(TimelineResourceTestAppId_ValidApp); PBL_ASSERTN(valid_app_data); // Calling the function with an invalid TimelineResourceId should set res_info to the fallback timeline_res_info = (TimelineResourceInfo) { .app_id = &valid_app_data->install_entry.uuid, .res_id = TIMELINE_RESOURCE_INVALID, .fallback_id = TIMELINE_RESOURCE_BIRTHDAY_EVENT, }; timeline_resources_get_id(&timeline_res_info, TimelineResourceSizeLarge, &res_info); cl_assert_equal_i(res_info.res_app_num, SYSTEM_APP); cl_assert_equal_i(res_info.res_id, RESOURCE_ID_BIRTHDAY_EVENT_LARGE); // Set the TimelineResourceInfo to valid values timeline_res_info = (TimelineResourceInfo) { .app_id = &valid_app_data->install_entry.uuid, .res_id = (TimelineResourceId)TimelineResourceTestTimelineId_AlarmClock, .fallback_id = TIMELINE_RESOURCE_BIRTHDAY_EVENT, }; // Calling the function with an invalid size, no TimelineResourceInfo, or no AppResourceInfo // should assert cl_assert_passert(timeline_resources_get_id(&timeline_res_info, TimelineResourceSizeCount, &res_info)); cl_assert_passert(timeline_resources_get_id(NULL, TimelineResourceSizeTiny, &res_info)); cl_assert_passert(timeline_resources_get_id(&timeline_res_info, TimelineResourceSizeTiny, NULL)); // Set the TimelineResourceInfo to have the UUID of an app with an invalid LUT const TimelineResourceTestAppData *app_with_invalid_lut_data = prv_get_data_for_app_with_id(TimelineResourceTestAppId_AppWithInvalidLUT); PBL_ASSERTN(app_with_invalid_lut_data); timeline_res_info = (TimelineResourceInfo) { .app_id = &app_with_invalid_lut_data->install_entry.uuid, .res_id = (TimelineResourceId)TimelineResourceTestTimelineId_AlarmClock, .fallback_id = TIMELINE_RESOURCE_BIRTHDAY_EVENT, }; // Calling the function with the UUID of an app with an invalid LUT should set res_info to the // fallback timeline_resources_get_id(&timeline_res_info, TimelineResourceSizeLarge, &res_info); cl_assert_equal_i(res_info.res_app_num, SYSTEM_APP); cl_assert_equal_i(res_info.res_id, RESOURCE_ID_BIRTHDAY_EVENT_LARGE); // Set the TimelineResourceInfo to valid values timeline_res_info = (TimelineResourceInfo) { .app_id = &valid_app_data->install_entry.uuid, .res_id = (TimelineResourceId)TimelineResourceTestTimelineId_AlarmClock, .fallback_id = TIMELINE_RESOURCE_BIRTHDAY_EVENT, }; // Calling the function for an invalid resource (e.g. dimensions too large) should set res_info // to the fallback s_is_app_published_resource_invalid = true; timeline_resources_get_id(&timeline_res_info, TimelineResourceSizeLarge, &res_info); cl_assert_equal_i(res_info.res_app_num, SYSTEM_APP); cl_assert_equal_i(res_info.res_id, RESOURCE_ID_BIRTHDAY_EVENT_LARGE); s_is_app_published_resource_invalid = false; // Calling the function with valid args should return true and set the correct values in res_info timeline_resources_get_id(&timeline_res_info, TimelineResourceSizeLarge, &res_info); cl_assert_equal_i(res_info.res_app_num, TimelineResourceTestAppId_ValidApp); cl_assert_equal_i(res_info.res_id, RESOURCE_ID_ALARM_CLOCK_LARGE); // Set the TimelineResourceInfo to have the UUID of an app with an unsupported SDK version const TimelineResourceTestAppData *app_with_invalid_sdk_version = prv_get_data_for_app_with_id(TimelineResourceTestAppId_AppWithInvalidSDKVersion); PBL_ASSERTN(app_with_invalid_sdk_version); timeline_res_info = (TimelineResourceInfo) { .app_id = &app_with_invalid_sdk_version->install_entry.uuid, .res_id = (TimelineResourceId)TimelineResourceTestTimelineId_Basketball, .fallback_id = TIMELINE_RESOURCE_BIRTHDAY_EVENT, }; // Calling the function with the UUID of an app with an unsupported SDK version should set // res_info to the fallback timeline_resources_get_id(&timeline_res_info, TimelineResourceSizeTiny, &res_info); cl_assert_equal_i(res_info.res_app_num, SYSTEM_APP); cl_assert_equal_i(res_info.res_id, RESOURCE_ID_BIRTHDAY_EVENT_TINY); // Set the TimelineResourceInfo to valid values but with a system TimelineResourceId requested timeline_res_info = (TimelineResourceInfo) { .app_id = &(Uuid)UUID_SYSTEM, .res_id = TIMELINE_RESOURCE_HOTEL_RESERVATION, .fallback_id = TIMELINE_RESOURCE_BIRTHDAY_EVENT, }; // Calling the function with a valid system TimelineResourceId should set the correct values in // res_info timeline_resources_get_id(&timeline_res_info, TimelineResourceSizeSmall, &res_info); cl_assert_equal_i(res_info.res_app_num, SYSTEM_APP); cl_assert_equal_i(res_info.res_id, RESOURCE_ID_HOTEL_RESERVATION_SMALL); // Even if the provided app UUID != UUID_SYSTEM timeline_res_info.app_id = &valid_app_data->install_entry.uuid; timeline_resources_get_id(&timeline_res_info, TimelineResourceSizeSmall, &res_info); cl_assert_equal_i(res_info.res_app_num, SYSTEM_APP); cl_assert_equal_i(res_info.res_id, RESOURCE_ID_HOTEL_RESERVATION_SMALL); } void test_timeline_resources__is_system(void) { // System TimelineResourceIds should return true cl_assert(timeline_resources_is_system(TIMELINE_RESOURCE_AUDIO_CASSETTE)); // Others should return false cl_assert(!timeline_resources_is_system(TIMELINE_RESOURCE_INVALID)); cl_assert(!timeline_resources_is_system(NUM_TIMELINE_RESOURCES)); }