mirror of
https://github.com/google/pebble.git
synced 2025-03-20 11:01:20 +00:00
319 lines
12 KiB
C
319 lines
12 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 "applib/app_glance.h"
|
|
#include "drivers/rtc.h"
|
|
#include "kernel/pbl_malloc.h"
|
|
#include "process_management/app_install_manager.h"
|
|
#include "resource/resource_ids.auto.h"
|
|
#include "resource/timeline_resource_ids.auto.h"
|
|
#include "services/normal/app_glances/app_glance_service.h"
|
|
#include "services/normal/blob_db/app_glance_db.h"
|
|
#include "services/normal/blob_db/app_glance_db_private.h"
|
|
#include "services/normal/filesystem/pfs.h"
|
|
#include "services/normal/timeline/timeline_resources.h"
|
|
#include "util/uuid.h"
|
|
|
|
#define APP_GLANCE_TEST_UUID \
|
|
(UuidMake(0x3d, 0xc6, 0xb9, 0x4c, 0x4, 0x2, 0x48, 0xf4, \
|
|
0xbe, 0x14, 0x81, 0x17, 0xf1, 0xa, 0xa9, 0xc4))
|
|
|
|
// Fakes
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#include "fake_rtc.h"
|
|
#include "fake_settings_file.h"
|
|
|
|
void sys_get_app_uuid(Uuid *uuid) {
|
|
if (uuid) {
|
|
*uuid = APP_GLANCE_TEST_UUID;
|
|
}
|
|
}
|
|
|
|
typedef struct AppGlanceTestState {
|
|
bool resource_is_valid;
|
|
void *context;
|
|
bool reload_callback_was_called;
|
|
} AppGlanceTestState;
|
|
|
|
static AppGlanceTestState s_test_state;
|
|
|
|
ResAppNum sys_get_current_resource_num(void) {
|
|
return 0;
|
|
}
|
|
|
|
void sys_timeline_resources_get_id(const TimelineResourceInfo *timeline_res,
|
|
TimelineResourceSize size, AppResourceInfo *res_info) {
|
|
if (!res_info) {
|
|
return;
|
|
}
|
|
// Just fill the output resource ID with some number so it's considered "valid"
|
|
res_info->res_id = s_test_state.resource_is_valid ? 1337 : 0;
|
|
}
|
|
|
|
|
|
// Stubs
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#include "stubs_app_cache.h"
|
|
#include "stubs_app_install_manager.h"
|
|
#include "stubs_app_state.h"
|
|
#include "stubs_events.h"
|
|
#include "stubs_event_service_client.h"
|
|
#include "stubs_i18n.h"
|
|
#include "stubs_logging.h"
|
|
#include "stubs_mutex.h"
|
|
#include "stubs_passert.h"
|
|
#include "stubs_pbl_malloc.h"
|
|
|
|
status_t pfs_remove(const char *name) {
|
|
fake_settings_file_reset();
|
|
return S_SUCCESS;
|
|
}
|
|
|
|
// Setup
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void test_app_glance__initialize(void) {
|
|
fake_rtc_init(0, 1337);
|
|
fake_settings_file_reset();
|
|
app_glance_db_init();
|
|
app_glance_service_init();
|
|
|
|
s_test_state = (AppGlanceTestState) {};
|
|
}
|
|
|
|
void app_glance_db_deinit(void);
|
|
|
|
void test_app_glance__cleanup(void) {
|
|
app_glance_db_deinit();
|
|
}
|
|
|
|
void prv_basic_reload_cb(AppGlanceReloadSession *session, size_t limit, void *context) {
|
|
s_test_state.reload_callback_was_called = true;
|
|
|
|
s_test_state.resource_is_valid = true;
|
|
AppGlanceSlice slice = (AppGlanceSlice) {
|
|
.expiration_time = rtc_get_time() + 10,
|
|
.layout.icon = TIMELINE_RESOURCE_HOTEL_RESERVATION,
|
|
.layout.subtitle_template_string = "Test subtitle",
|
|
};
|
|
cl_assert_equal_i(app_glance_add_slice(session, slice), APP_GLANCE_RESULT_SUCCESS);
|
|
|
|
slice = (AppGlanceSlice) {
|
|
.expiration_time = rtc_get_time() + 20,
|
|
.layout.icon = APP_GLANCE_SLICE_DEFAULT_ICON,
|
|
};
|
|
cl_assert_equal_i(app_glance_add_slice(session, slice), APP_GLANCE_RESULT_SUCCESS);
|
|
}
|
|
|
|
void test_app_glance__basic_reload(void) {
|
|
// Reload the glance with two slices
|
|
app_glance_reload(prv_basic_reload_cb, s_test_state.context);
|
|
cl_assert_equal_b(s_test_state.reload_callback_was_called, true);
|
|
|
|
// Read the glance back
|
|
AppGlance glance = {};
|
|
cl_assert_equal_i(app_glance_db_read_glance(&APP_GLANCE_TEST_UUID, &glance), S_SUCCESS);
|
|
|
|
// Compare the glance read back with the expected glance below
|
|
AppGlance expected_glance = (AppGlance) {
|
|
.num_slices = 2,
|
|
.slices = {
|
|
{
|
|
.expiration_time = rtc_get_time() + 10,
|
|
.type = AppGlanceSliceType_IconAndSubtitle,
|
|
.icon_and_subtitle.icon_resource_id = TIMELINE_RESOURCE_HOTEL_RESERVATION,
|
|
.icon_and_subtitle.template_string = "Test subtitle",
|
|
},
|
|
{
|
|
.expiration_time = rtc_get_time() + 20,
|
|
.type = AppGlanceSliceType_IconAndSubtitle,
|
|
.icon_and_subtitle.icon_resource_id = APP_GLANCE_SLICE_DEFAULT_ICON,
|
|
}
|
|
},
|
|
};
|
|
cl_assert_equal_m(&glance, &expected_glance, sizeof(AppGlance));
|
|
}
|
|
|
|
void prv_reload_with_validation_cb(AppGlanceReloadSession *session, size_t limit, void *context) {
|
|
s_test_state.reload_callback_was_called = true;
|
|
|
|
// Check that the context here is the context we passed to `app_glance_reload()`
|
|
cl_assert_equal_p(context, s_test_state.context);
|
|
|
|
// Check that the limit passed in matches the max slices per glance
|
|
cl_assert_equal_i(limit, APP_GLANCE_DB_MAX_SLICES_PER_GLANCE);
|
|
|
|
unsigned int num_slices_added = 0;
|
|
AppGlanceSlice slice = {};
|
|
|
|
// Check that using a bogus session variable fails
|
|
AppGlanceReloadSession bogus_session;
|
|
cl_assert(app_glance_add_slice(&bogus_session, slice) & APP_GLANCE_RESULT_INVALID_SESSION);
|
|
|
|
// Check that adding a slice with APP_GLANCE_SLICE_DEFAULT_ICON as the icon succeeds
|
|
slice = (AppGlanceSlice) {
|
|
.expiration_time = APP_GLANCE_SLICE_NO_EXPIRATION,
|
|
.layout.icon = APP_GLANCE_SLICE_DEFAULT_ICON,
|
|
.layout.subtitle_template_string = "Test subtitle {time_until(500)|format('%uS')}",
|
|
};
|
|
cl_assert_equal_i(app_glance_add_slice(session, slice), APP_GLANCE_RESULT_SUCCESS);
|
|
num_slices_added++;
|
|
|
|
// Check that adding a slice with a NULL subtitle succeeds
|
|
s_test_state.resource_is_valid = true;
|
|
slice = (AppGlanceSlice) {
|
|
.expiration_time = APP_GLANCE_SLICE_NO_EXPIRATION,
|
|
.layout.icon = TIMELINE_RESOURCE_BIRTHDAY_EVENT,
|
|
.layout.subtitle_template_string = NULL,
|
|
};
|
|
cl_assert_equal_i(app_glance_add_slice(session, slice), APP_GLANCE_RESULT_SUCCESS);
|
|
num_slices_added++;
|
|
|
|
// Check that adding a slice with an invalid icon fails
|
|
s_test_state.resource_is_valid = false;
|
|
slice = (AppGlanceSlice) {
|
|
.expiration_time = APP_GLANCE_SLICE_NO_EXPIRATION,
|
|
.layout.icon = RESOURCE_ID_SETTINGS_ICON_AIRPLANE,
|
|
.layout.subtitle_template_string = "Test subtitle",
|
|
};
|
|
cl_assert(app_glance_add_slice(session, slice) & APP_GLANCE_RESULT_INVALID_ICON);
|
|
|
|
// Check that adding a slice with a subtitle that's too long fails
|
|
const char *really_long_subtitle = "This is a really really really really really really really "
|
|
"really really really really really really really really "
|
|
"really really really really really really really really "
|
|
"really really really really really really really really "
|
|
"really long subtitle.";
|
|
slice = (AppGlanceSlice) {
|
|
.expiration_time = APP_GLANCE_SLICE_NO_EXPIRATION,
|
|
.layout.icon = APP_GLANCE_SLICE_DEFAULT_ICON,
|
|
.layout.subtitle_template_string = really_long_subtitle,
|
|
};
|
|
cl_assert(app_glance_add_slice(session, slice) & APP_GLANCE_RESULT_TEMPLATE_STRING_TOO_LONG);
|
|
|
|
// Check that adding a slice with a bad template string fails
|
|
const char *invalid_template_subtitle = "How much time? {time_until(500)|format('%uS',)}";
|
|
slice = (AppGlanceSlice) {
|
|
.expiration_time = APP_GLANCE_SLICE_NO_EXPIRATION,
|
|
.layout.icon = APP_GLANCE_SLICE_DEFAULT_ICON,
|
|
.layout.subtitle_template_string = invalid_template_subtitle,
|
|
};
|
|
cl_assert(app_glance_add_slice(session, slice) & APP_GLANCE_RESULT_INVALID_TEMPLATE_STRING);
|
|
|
|
// Check that adding a slice that expires in the past fails
|
|
slice = (AppGlanceSlice) {
|
|
.expiration_time = rtc_get_time() - 10,
|
|
.layout.icon = APP_GLANCE_SLICE_DEFAULT_ICON,
|
|
.layout.subtitle_template_string = "Test subtitle",
|
|
};
|
|
cl_assert(app_glance_add_slice(session, slice) & APP_GLANCE_RESULT_EXPIRES_IN_THE_PAST);
|
|
|
|
// At this point we've actually filled up the glance to the capacity
|
|
cl_assert_equal_i(num_slices_added, limit);
|
|
|
|
// So adding one more slice to the glance should fail
|
|
s_test_state.resource_is_valid = true;
|
|
slice = (AppGlanceSlice) {
|
|
.expiration_time = APP_GLANCE_SLICE_NO_EXPIRATION,
|
|
.layout.subtitle_template_string = NULL,
|
|
};
|
|
cl_assert(app_glance_add_slice(session, slice) & APP_GLANCE_RESULT_SLICE_CAPACITY_EXCEEDED);
|
|
|
|
// Check that we can get reports of multiple kinds of failures at the same time
|
|
s_test_state.resource_is_valid = false;
|
|
slice = (AppGlanceSlice) {
|
|
.expiration_time = rtc_get_time() - 10,
|
|
.layout.icon = RESOURCE_ID_SETTINGS_ICON_AIRPLANE,
|
|
.layout.subtitle_template_string = really_long_subtitle,
|
|
};
|
|
const AppGlanceResult result = app_glance_add_slice(session, slice);
|
|
cl_assert(result & APP_GLANCE_RESULT_EXPIRES_IN_THE_PAST);
|
|
cl_assert(result & APP_GLANCE_RESULT_SLICE_CAPACITY_EXCEEDED);
|
|
cl_assert(result & APP_GLANCE_RESULT_INVALID_ICON);
|
|
cl_assert(result & APP_GLANCE_RESULT_TEMPLATE_STRING_TOO_LONG);
|
|
}
|
|
|
|
void test_app_glance__reload_with_validation_callback(void) {
|
|
app_glance_reload(prv_reload_with_validation_cb, s_test_state.context);
|
|
cl_assert_equal_b(s_test_state.reload_callback_was_called, true);
|
|
}
|
|
|
|
static void prv_glance_clear_test(AppGlanceReloadCallback reload_cb) {
|
|
// Insert some slices for the glance
|
|
const AppGlance glance = (AppGlance) {
|
|
.num_slices = 2,
|
|
.slices = {
|
|
{
|
|
.expiration_time = 1464734504, // (Tue, 31 May 2016 22:41:44 GMT)
|
|
.type = AppGlanceSliceType_IconAndSubtitle,
|
|
.icon_and_subtitle = {
|
|
.template_string = "Test subtitle 2",
|
|
},
|
|
},
|
|
{
|
|
.expiration_time = 1464734484, // (Tue, 31 May 2016 22:41:24 GMT)
|
|
.type = AppGlanceSliceType_IconAndSubtitle,
|
|
.icon_and_subtitle = {
|
|
.template_string = "Test subtitle 1",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
cl_assert_equal_i(app_glance_db_insert_glance(&APP_GLANCE_TEST_UUID, &glance), S_SUCCESS);
|
|
|
|
// Request the current slice for this glance; this should match the earliest-expiring slice in
|
|
// the glance we just inserted above
|
|
AppGlanceSliceInternal slice_out;
|
|
cl_assert_equal_b(app_glance_service_get_current_slice(&APP_GLANCE_TEST_UUID, &slice_out), true);
|
|
cl_assert_equal_m(&slice_out, &glance.slices[1], sizeof(slice_out));
|
|
|
|
// Let some time "pass" so that the creation time of this next reload doesn't get ignored
|
|
fake_rtc_increment_time(10);
|
|
|
|
// Reload the glance using the provided callback; this should empty the slices in the glance
|
|
app_glance_reload(reload_cb, NULL);
|
|
|
|
// Read the glance back and check that it doesn't have any slices anymore
|
|
AppGlance glance_read = {};
|
|
cl_assert_equal_i(app_glance_db_read_glance(&APP_GLANCE_TEST_UUID, &glance_read), S_SUCCESS);
|
|
cl_assert_equal_i(glance_read.num_slices, 0);
|
|
for (unsigned int i = 0; i < sizeof(glance_read.slices); i++) {
|
|
const uint8_t byte = ((uint8_t *)glance_read.slices)[i];
|
|
cl_assert_equal_i(byte, 0);
|
|
}
|
|
|
|
// Request the current slice for this glance again; this should return false since there aren't
|
|
// any slices in the glance anymore
|
|
cl_assert_equal_b(app_glance_service_get_current_slice(&APP_GLANCE_TEST_UUID, &slice_out), false);
|
|
}
|
|
|
|
void test_app_glance__reload_with_null_callback_empties_slices(void) {
|
|
prv_glance_clear_test(NULL);
|
|
}
|
|
|
|
static void prv_reload_with_no_slices_added_cb(AppGlanceReloadSession *session, size_t limit,
|
|
void *context) {
|
|
// We don't add any slices in this callback on purpose
|
|
return;
|
|
}
|
|
|
|
void test_app_glance__reload_with_no_slices_added_empties_slices(void) {
|
|
prv_glance_clear_test(prv_reload_with_no_slices_added_cb);
|
|
}
|