pebble/tests/fw/services/timeline/test_timeline_resources.c
2025-01-27 11:38:16 -08:00

359 lines
15 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 "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));
}