pebble/tests/fw/applib/test_persist.c
2025-01-27 11:38:16 -08:00

293 lines
11 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 <stdio.h>
#include <string.h>
#include "applib/persist.h"
#include "flash_region/flash_region.h"
#include "process_management/app_install_manager.h"
#include "process_management/pebble_process_md.h"
#include "services/normal/filesystem/pfs.h"
#include "services/normal/persist.h"
#include "system/logging.h"
// Stubs
////////////////////////////////////
#include "fake_rtc.h"
#include "fake_spi_flash.h"
#include "stubs_analytics.h"
#include "stubs_hexdump.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_pebble_tasks.h"
#include "stubs_print.h"
#include "stubs_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_serial.h"
#include "stubs_sleep.h"
#include "stubs_system_reset.h"
#include "stubs_task_watchdog.h"
static PebbleProcessMd __pbl_app_info;
const PebbleProcessMd* sys_process_manager_get_current_process_md(void) {
return &__pbl_app_info;
}
// Tests
////////////////////////////////////
#define TEST_UUID_A { 0x2F, 0xF7, 0xFA, 0x04, 0x60, 0x11, 0x4A, 0x98, 0x8A, 0x3B, 0xA8, 0x26, 0xA4, 0xB8, 0x99, 0xF8 }
static const int system_uuid_id = 0;
static const Uuid system_uuid = UUID_SYSTEM;
static const int test_uuid_a_id = 1;
static const Uuid test_uuid_a = TEST_UUID_A;
static const int test_uuid_b_id = 2;
static const Uuid test_uuid_b = { 0xC3, 0x0D, 0xBA, 0xF1, 0x5F, 0x6F, 0x4F, 0x22, 0xBA, 0xAA, 0x8C, 0x2A, 0x96, 0x8C, 0xFC, 0x28 };
static const int test_uuid_c_id = 3;
static const Uuid test_uuid_c = { 0x1D, 0x6C, 0x7F, 0x01, 0xD9, 0x48, 0x42, 0xA6, 0xAA, 0x4E, 0xB2, 0x08, 0x42, 0x10, 0xEB, 0xBC };
static PebbleProcessMd __pbl_app_info = {
.uuid = TEST_UUID_A,
};
const char lipsum[] = "Lorem ipsum dolor sit amet, consectetur "
"adipiscing elit. Nam dignissim ullamcorper sollicitudin. Suspendisse at "
"urna suscipit, congue purus a, posuere eros. Nulla eros urna, vestibulum "
"a dictum a, maximus sed nibh. Ut ut dui finibus, tincidunt ligula quis, "
"ornare mi. Pellentesque sagittis suscipit lacus nec consectetur. Nunc et "
"commodo neque. Vestibulum vitae dignissim sapien. Nulla scelerisque "
"finibus nisl. Suspendisse ac massa lacus. In hac habitasse platea "
"dictumst. Ut condimentum urna eros. Fusce ipsum metus, vehicula eu tortor "
"sed, congue tempus mauris. Maecenas mollis lacus non cursus bibendum. "
"Etiam id dolor lorem. Aenean scelerisque nulla sed tristique posuere. "
"Proin dui magna, gravida faucibus ultricies non, tincidunt id metus. "
"Integer a laoreet dolor, eu vulputate enim. Ut vitae hendrerit nunc, in "
"bibendum eros. Pellentesque congue ut quam id sollicitudin. Cras "
"malesuada arcu nec imperdiet cursus. Donec vitae ex eget mi imperdiet "
"efficitur id eu velit. Proin pretium ipsum sed convallis efficitur. Morbi "
"non feugiat erat. Ut ut efficitur massa. Sed eu auctor felis. Vestibulum "
"magna orci, placerat nec risus nec, ultricies congue ex. Morbi in "
"vestibulum leo. Nullam non dapibus lorem. Suspendisse blandit diam "
"posuere suscipit malesuada. Maecenas vehicula felis eu posuere euismod. "
"Fusce at velit ultrices, sagittis enim ac, ultrices lorem. Quisque "
"tincidunt fringilla suscipit. Curabitur tempus lorem metus, sed venenatis "
"augue maximus a. Duis venenatis tortor sit amet justo sodales suscipit. "
"Morbi tincidunt rutrum nisl, eget placerat nisi condimentum a. Vestibulum "
"ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia "
"Curae; Cras varius sagittis mauris, in consequat sapien tincidunt vitae. "
"Duis ipsum nunc, tristique sit amet blandit non, scelerisque non diam. "
"Etiam condimentum aliquam dictum. Nam nisi ex, cursus in ligula sit amet, "
"ultricies egestas libero. Aliquam luctus, metus quis ultricies sagittis, "
"nisi orci viverra felis, vitae luctus massa dolor sit amet dolor. Cras "
"mattis velit vitae pretium pulvinar. Pellentesque auctor, turpis at cras "
"amet.";
_Static_assert(sizeof(lipsum) > PERSIST_STRING_MAX_LENGTH,
"lipsum string is not long enough for persist tests");
void test_persist__initialize(void) {
fake_spi_flash_init(0, 0x1000000);
pfs_init(false);
persist_service_init();
persist_service_client_open(&test_uuid_a);
}
void test_persist__cleanup(void) {
persist_service_client_close(&test_uuid_a);
}
void test_persist__int(void) {
const uint32_t key = 0;
const uint32_t value = ~0;
cl_assert_equal_i(persist_read_int(key), 0);
cl_assert_equal_i(persist_write_int(key, value), sizeof(int));
cl_assert_equal_i(persist_get_size(key), sizeof(int));
cl_assert_equal_i(persist_read_int(key), value);
}
void test_persist__bool(void) {
const uint32_t key = 0;
cl_assert_equal_i(persist_read_bool(key), false);
cl_assert_equal_i(persist_write_bool(key, true), sizeof(bool));
cl_assert_equal_i(persist_get_size(key), sizeof(bool));
cl_assert_equal_i(persist_read_bool(key), true);
}
void test_persist__data(void) {
const uint32_t key = 0;
const int size = sizeof(test_uuid_a);
Uuid uuid_buffer;
cl_assert_equal_i(persist_read_data(key, &uuid_buffer, sizeof(uuid_buffer)), E_DOES_NOT_EXIST);
cl_assert_equal_i(persist_write_data(key, &test_uuid_a, sizeof(test_uuid_a)), size);
cl_assert_equal_i(persist_get_size(key), size);
cl_assert_equal_i(persist_read_data(key, &uuid_buffer, sizeof(uuid_buffer)), size);
cl_assert(uuid_equal(&test_uuid_a, &uuid_buffer));
}
void test_persist__data_too_big(void) {
char buf[PERSIST_DATA_MAX_LENGTH+2];
memset(buf, '~', sizeof(buf));
cl_assert_equal_i(persist_write_data(0, lipsum, sizeof(lipsum)),
PERSIST_DATA_MAX_LENGTH);
cl_assert_equal_i(persist_read_data(0, buf, sizeof(buf)),
PERSIST_DATA_MAX_LENGTH);
cl_assert(memcmp(lipsum, buf, PERSIST_DATA_MAX_LENGTH) == 0);
for (size_t i = PERSIST_DATA_MAX_LENGTH; i < sizeof(buf); ++i) {
cl_assert_(buf[i] == '~',
"persist_read_data writes past the end of destination buffer");
}
}
void test_persist__string_does_not_exist(void) {
char string_buffer[PERSIST_STRING_MAX_LENGTH];
memset(string_buffer, '~', sizeof(string_buffer));
cl_assert_equal_i(
persist_read_string(0, string_buffer, sizeof(string_buffer)),
E_DOES_NOT_EXIST);
for (size_t i = 0; i < sizeof(string_buffer); ++i) {
if (string_buffer[i] != '~') {
char error_msg[132];
snprintf(error_msg, sizeof(error_msg), "persist_read_string clobbers "
"destination buffer at %zd when key does not exist", i);
cl_fail(error_msg);
}
}
}
void test_persist__string_write_unterminated_string(void) {
char string_buffer[PERSIST_STRING_MAX_LENGTH + 2];
memset(string_buffer, '~', sizeof(string_buffer));
cl_assert_equal_i(persist_write_string(0, lipsum), PERSIST_STRING_MAX_LENGTH);
cl_assert_equal_i(persist_get_size(0), PERSIST_STRING_MAX_LENGTH);
cl_assert_equal_i(
persist_read_string(0, string_buffer, sizeof(string_buffer)),
PERSIST_STRING_MAX_LENGTH);
cl_assert_equal_i(string_buffer[PERSIST_STRING_MAX_LENGTH - 1], '\0');
cl_assert(strncmp(lipsum, string_buffer, PERSIST_STRING_MAX_LENGTH - 1) == 0);
for (size_t i = PERSIST_STRING_MAX_LENGTH; i < sizeof(string_buffer); ++i) {
cl_assert_(string_buffer[i] == '~',
"persist_read_string writes past the end of destination buffer");
}
}
void test_persist__size_of_nonexistent_key(void) {
cl_assert_equal_i(persist_get_size(0), E_DOES_NOT_EXIST);
}
void test_persist__size(void) {
char data[] = { 1, 2, 3, 4, 5, 6 };
cl_assert_equal_i(persist_write_data(0, data, sizeof(data)), sizeof(data));
cl_assert_equal_i(persist_get_size(0), sizeof(data));
}
void test_persist__exists(void) {
cl_assert_equal_i(persist_exists(0), S_FALSE);
cl_assert(PASSED(persist_write_int(0, 0)));
cl_assert_equal_i(persist_exists(0), S_TRUE);
}
void test_persist__delete(void) {
cl_assert_equal_i(persist_delete(0), E_DOES_NOT_EXIST);
cl_assert(PASSED(persist_write_int(0, 0)));
cl_assert_equal_i(persist_delete(0), S_TRUE);
cl_assert_equal_i(persist_delete(0), E_DOES_NOT_EXIST);
}
/*
* Confirm that fields can be reassigned values.
*/
void test_persist__overwrite(void) {
const uint32_t key = 0;
cl_assert_equal_i(persist_write_int(key, 1), sizeof(int));
cl_assert_equal_i(persist_read_int(key), 1);
cl_assert_equal_i(persist_write_int(key, 2), sizeof(int));
cl_assert_equal_i(persist_read_int(key), 2);
}
/*
* Confirm that overwriting with a smaller data size does not break tuple finding.
*/
void test_persist__overwrite_shrink(void) {
cl_assert(PASSED(persist_write_int(0, 1)));
cl_assert(PASSED(persist_write_bool(0, false)));
cl_assert(PASSED(persist_write_int(1, 2)));
cl_assert(persist_read_int(1) == 2);
}
/*
* Confirm that loading a smaller amount of data, then a larger amount of data,
* always returns the appropriate amount of data.
*/
void test_persist__partial_read_extension(void) {
char buffer[] = "Hello thar";
// Write out data
cl_assert_equal_i(persist_write_string(0, buffer), sizeof(buffer));
// Clear the cache (which has the entire string right now)
persist_service_client_close(&test_uuid_a);
persist_service_client_open(&test_uuid_a);
// Reset out buffer so we can check the read's honesty.
memset(buffer, 0, strlen(buffer));
// Read part of the data we wrote
cl_assert_equal_i(persist_read_string(0, buffer, 2), 2);
cl_assert_equal_i(strlen(buffer), 2 - 1); // -1 because null termination
cl_assert_equal_i(buffer[0], 'H');
// Then attempt to read back the entire thing
cl_assert_equal_i(persist_read_string(0, buffer, sizeof(buffer)), sizeof(buffer));
cl_assert_equal_i(strlen(buffer), 10);
cl_assert(strcmp(buffer, "Hello thar") == 0);
}
void test_persist__legacy2_max_usage(void) {
uint8_t buffer[256];
memset(buffer, 1, sizeof(buffer));
// The maximum amount of 'buffer' sized fields allowed by the old persist
// storage backend.
int n = (4 * 1024) / (9 + sizeof(buffer));
PBL_LOG_VERBOSE("n = %d", n);
for (int i = 0; i < n; ++i) {
PBL_LOG(LOG_LEVEL_DEBUG, "i = %d", i);
cl_assert_equal_i(persist_write_data(i, &buffer, sizeof(buffer)), sizeof(buffer));
}
// Don't be too strict about preventing apps from using more persist than they
// had available under the old implementation.
// cl_assert_equal_i(persist_write_data(n + 1, &buffer, sizeof(buffer)),
// E_OUT_OF_STORAGE);
}