Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View file

@ -0,0 +1,275 @@
/*
* 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_types.h"
#include "process_management/pebble_process_info.h"
#include "process_management/pebble_process_md.h"
#include "services/normal/filesystem/pfs.h"
#include "services/normal/blob_db/app_db.h"
// Fixture
////////////////////////////////////////////////////////////////
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_spi_flash.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_app_cache.h"
#include "stubs_events.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_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_sleep.h"
#include "stubs_task_watchdog.h"
void app_install_clear_app_db(void) {
}
void put_bytes_cancel(void) {
}
typedef void (*InstallCallbackDoneCallback)(void*);
bool app_install_do_callbacks(InstallEventType event_type, AppInstallId install_id, Uuid *uuid,
InstallCallbackDoneCallback done_callback, void* done_callback_data) {
return true;
}
bool app_fetch_in_progress(void) {
return false;
}
void app_fetch_cancel_from_system_task(void) {
}
extern AppInstallId app_db_check_next_unique_id(void);
static const AppDBEntry app1 = {
.name = "Application 1",
.uuid = {0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4},
.app_version = {
.major = 1,
.minor = 1,
},
.sdk_version = {
.major = 1,
.minor = 1,
},
.info_flags = 0,
.icon_resource_id = 0,
};
static const AppDBEntry app2 = {
.name = "Application 2",
.uuid = {0x55, 0xcb, 0x7c, 0x75, 0x8a, 0x35, 0x44, 0x87,
0x90, 0xa4, 0x91, 0x3f, 0x1f, 0xa6, 0x76, 0x01},
.app_version = {
.major = 1,
.minor = 1,
},
.sdk_version = {
.major = 1,
.minor = 1,
},
.info_flags = 0,
.icon_resource_id = 0,
};
static const AppDBEntry app3 = {
.name = "Application 3",
.uuid = {0x7c, 0x65, 0x2e, 0xb9, 0x26, 0xd6, 0x44, 0x2c,
0x98, 0x68, 0xa4, 0x36, 0x79, 0x7d, 0xe2, 0x05},
.app_version = {
.major = 1,
.minor = 1,
},
.sdk_version = {
.major = 1,
.minor = 1,
},
.info_flags = 0,
.icon_resource_id = 0,
};
// Setup
////////////////////////////////////////////////////////////////
void test_app_db__initialize(void) {
fake_spi_flash_init(0, 0x1000000);
pfs_init(false);
app_db_init();
// add all three
cl_assert_equal_i(S_SUCCESS, app_db_insert((uint8_t*)&app1.uuid,
sizeof(Uuid), (uint8_t*)&app1, sizeof(AppDBEntry)));
cl_assert_equal_i(S_SUCCESS, app_db_insert((uint8_t*)&app2.uuid,
sizeof(Uuid), (uint8_t*)&app2, sizeof(AppDBEntry)));
cl_assert_equal_i(S_SUCCESS, app_db_insert((uint8_t*)&app3.uuid,
sizeof(Uuid), (uint8_t*)&app3, sizeof(AppDBEntry)));
}
void test_app_db__cleanup(void) {
//nada
}
// Tests
////////////////////////////////////////////////////////////////
void test_app_db__basic_test(void) {
// confirm all three are there
cl_assert(app_db_get_len((uint8_t*)&app1.uuid, sizeof(Uuid)) > 0);
cl_assert(app_db_get_len((uint8_t*)&app2.uuid, sizeof(Uuid)) > 0);
cl_assert(app_db_get_len((uint8_t*)&app3.uuid, sizeof(Uuid)) > 0);
// remove #1 and confirm it's deleted
cl_assert_equal_i(S_SUCCESS, app_db_delete((uint8_t*)&app1.uuid, sizeof(Uuid)));
cl_assert_equal_i(0, app_db_get_len((uint8_t*)&app1.uuid, sizeof(Uuid)));
// add 1 back so it's clean
cl_assert_equal_i(S_SUCCESS, app_db_insert((uint8_t*)&app1.uuid,
sizeof(Uuid), (uint8_t*)&app1, sizeof(AppDBEntry)));
AppDBEntry temp;
cl_assert_equal_i(S_SUCCESS, app_db_read((uint8_t*)&app1.uuid,
sizeof(Uuid), (uint8_t*)&temp, sizeof(AppDBEntry)));
cl_assert_equal_i(5, app_db_check_next_unique_id());
// check app 1
memset(&temp, 0, sizeof(AppDBEntry));
cl_assert_equal_i(S_SUCCESS, app_db_get_app_entry_for_uuid(&app1.uuid, &temp));
cl_assert_equal_b(true, uuid_equal(&app1.uuid, &temp.uuid));
cl_assert_equal_i(0 , strncmp((char *)&app1.name, (char *)&temp.name, APP_NAME_SIZE_BYTES));
// check app 2
memset(&temp, 0, sizeof(AppDBEntry));
cl_assert_equal_i(S_SUCCESS, app_db_get_app_entry_for_uuid(&app1.uuid, &temp));
cl_assert_equal_b(true, uuid_equal(&app1.uuid, &temp.uuid));
cl_assert_equal_i(0, strncmp((char *)&app1.name, (char *)&temp.name, APP_NAME_SIZE_BYTES));
// check app 3
memset(&temp, 0, sizeof(AppDBEntry));
cl_assert_equal_i(S_SUCCESS, app_db_get_app_entry_for_uuid(&app3.uuid, &temp));
cl_assert_equal_b(true, uuid_equal(&app3.uuid, &temp.uuid));
cl_assert_equal_i(0, strncmp((char *)&app3.name, (char *)&temp.name, APP_NAME_SIZE_BYTES));
}
void test_app_db__retrieve_app_db_entries_by_install_id(void) {
AppDBEntry temp;
// check app_db_get_install_id_for_uuid for app 1
memset(&temp, 0, sizeof(AppDBEntry));
AppInstallId app_one_id = app_db_get_install_id_for_uuid(&app1.uuid);
cl_assert(app_one_id > 0);
cl_assert_equal_i(S_SUCCESS, app_db_get_app_entry_for_install_id(app_one_id, &temp));
cl_assert_equal_b(true, uuid_equal(&app1.uuid, &temp.uuid));
// check app_db_get_install_id_for_uuid for app 2
memset(&temp, 0, sizeof(AppDBEntry));
AppInstallId app_two_id = app_db_get_install_id_for_uuid(&app2.uuid);
cl_assert(app_one_id > 0);
cl_assert_equal_i(S_SUCCESS, app_db_get_app_entry_for_install_id(app_two_id, &temp));
cl_assert_equal_b(true, uuid_equal(&app2.uuid, &temp.uuid));
// check app_db_get_install_id_for_uuid for app 3
memset(&temp, 0, sizeof(AppDBEntry));
AppInstallId app_three_id = app_db_get_install_id_for_uuid(&app3.uuid);
cl_assert(app_one_id > 0);
cl_assert_equal_i(S_SUCCESS, app_db_get_app_entry_for_install_id(app_three_id, &temp));
cl_assert_equal_b(true, uuid_equal(&app3.uuid, &temp.uuid));
}
void test_app_db__retrieve_app_db_entries_by_uuid(void) {
AppDBEntry temp;
// check app_db_get_install_id_for_uuid for app 1
memset(&temp, 0, sizeof(AppDBEntry));
cl_assert_equal_i(S_SUCCESS, app_db_get_app_entry_for_uuid(&app1.uuid, &temp));
cl_assert_equal_b(true, uuid_equal(&app1.uuid, &temp.uuid));
// check app_db_get_install_id_for_uuid for app 2
memset(&temp, 0, sizeof(AppDBEntry));
cl_assert_equal_i(S_SUCCESS, app_db_get_app_entry_for_uuid(&app2.uuid, &temp));
cl_assert_equal_b(true, uuid_equal(&app2.uuid, &temp.uuid));
// check app_db_get_install_id_for_uuid for app 3
memset(&temp, 0, sizeof(AppDBEntry));
cl_assert_equal_i(S_SUCCESS, app_db_get_app_entry_for_uuid(&app3.uuid, &temp));
cl_assert_equal_b(true, uuid_equal(&app3.uuid, &temp.uuid));
}
void test_app_db__overwrite(void) {
// add 3 of the same. Confirm that the entry was overwritten by checking next ID.
cl_assert_equal_i(S_SUCCESS, app_db_insert((uint8_t*)&app1.uuid, sizeof(Uuid), (uint8_t*)&app1,
sizeof(AppDBEntry)));
cl_assert_equal_i(S_SUCCESS, app_db_insert((uint8_t*)&app1.uuid, sizeof(Uuid), (uint8_t*)&app1,
sizeof(AppDBEntry)));
cl_assert_equal_i(S_SUCCESS, app_db_insert((uint8_t*)&app1.uuid, sizeof(Uuid), (uint8_t*)&app1,
sizeof(AppDBEntry)));
cl_assert(app_db_check_next_unique_id() == 4);
// add two more duplicates of a different app. Confirm it only increments by 1.
cl_assert_equal_i(S_SUCCESS, app_db_insert((uint8_t*)&app2.uuid, sizeof(Uuid), (uint8_t*)&app2,
sizeof(AppDBEntry)));
cl_assert_equal_i(S_SUCCESS, app_db_insert((uint8_t*)&app2.uuid, sizeof(Uuid), (uint8_t*)&app2,
sizeof(AppDBEntry)));
cl_assert(app_db_check_next_unique_id() == 4);
}
void test_app_db__test_exists(void) {
cl_assert_equal_b(false, app_db_exists_install_id(-1));
cl_assert_equal_b(false, app_db_exists_install_id(0));
cl_assert_equal_b(true, app_db_exists_install_id(1));
cl_assert_equal_b(true, app_db_exists_install_id(2));
cl_assert_equal_b(true, app_db_exists_install_id(3));
cl_assert_equal_b(false, app_db_exists_install_id(4));
}
static const uint8_t some_data[] = {0x01, 0x02, 0x17, 0x54};
void prv_enumerate_entries(AppInstallId install_id, AppDBEntry *entry, void *data) {
switch(install_id) {
case 1:
cl_assert_equal_m(&app1, entry, sizeof(AppDBEntry));
break;
case 2:
cl_assert_equal_m(&app2, entry, sizeof(AppDBEntry));
break;
case 3:
cl_assert_equal_m(&app3, entry, sizeof(AppDBEntry));
break;
default:
break;
}
cl_assert_equal_m((uint8_t *)some_data, (uint8_t *)data, sizeof(some_data));
}
void test_app_db__enumerate(void) {
app_db_enumerate_entries(prv_enumerate_entries, (void *)&some_data);
}

View file

@ -0,0 +1,568 @@
/*
* 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 "resource/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 "util/uuid.h"
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_settings_file.h"
#include "fake_events.h"
// Stubs
////////////////////////////////////////////////////////////////
#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;
}
static bool s_app_cache_entry_exists = true;
bool app_cache_entry_exists(AppInstallId app_id) {
return s_app_cache_entry_exists;
}
static int s_launch_count = 0;
status_t app_cache_app_launched(AppInstallId app_id) {
s_launch_count++;
return S_SUCCESS;
}
static AppInstallId s_app_install_id = 1;
AppInstallId app_install_get_id_for_uuid(const Uuid *uuid) {
if (!uuid) {
return INSTALL_ID_INVALID;
}
return s_app_install_id;
}
bool app_install_id_from_system(AppInstallId id) {
return (id < INSTALL_ID_INVALID);
}
bool app_install_id_from_app_db(AppInstallId id) {
return (id > INSTALL_ID_INVALID);
}
#define APP_GLANCE_TEST_UUID \
(UuidMake(0x3d, 0xc6, 0xb9, 0x4c, 0x4, 0x2, 0x48, 0xf4, \
0xbe, 0x14, 0x81, 0x17, 0xf1, 0xa, 0xa9, 0xc4))
static const uint8_t s_app_glance_basic[] = {
// Version
APP_GLANCE_DB_CURRENT_VERSION,
// Creation time
0x14, 0x13, 0x4E, 0x57, // 1464734484 (Tue, 31 May 2016 22:41:24 GMT)
// Slice 1
0x22, 0x00, // Total size
0x00, // AppGlanceSliceType - AppGlanceSliceType_IconAndSubtitle
0x03, // Number of attributes
// Slice Attributes
0x25, // Attribute ID - AttributeIdTimestamp
0x04, 0x00, // Attribute Length
// Slice expiration time:
0x94, 0x64, 0x4F, 0x57, // 1464820884 (Wed, 1 June 2016 22:41:24 GMT)
0x30, // Attribute ID - AttributeIdIcon
0x04, 0x00, // Attribute Length
// Slice icon resource ID:
0x69, 0x00, 0x00, 0x00, //
0x2F, // Attribute ID - AttributeIdSubtitleTemplateString
0x0D, 0x00, // Attribute Length
// Slice subtitle:
'T', 'e', 's', 't', ' ', 's', 'u', 'b', 't', 'i', 't', 'l', 'e',
};
// Note that `APP_GLANCE_DB_MAX_SLICES_PER_GLANCE` is reduced for the unit tests!
static const uint8_t s_app_glance_with_too_many_slices[] = {
// Version
APP_GLANCE_DB_CURRENT_VERSION,
// Creation time
0x14, 0x13, 0x4E, 0x57, // 1464734484 (Tue, 31 May 2016 22:41:24 GMT)
// Slice 1
0x0B, 0x00, // Total size
0x00, // AppGlanceSliceType - AppGlanceSliceType_IconAndSubtitle
0x01, // Number of attributes
// Slice Attributes
0x25, // Attribute ID - AttributeIdTimestamp
0x04, 0x00, // Attribute Length
// Slice expiration time:
0x94, 0x64, 0x4F, 0x57, // 1464820884 (Wed, 1 June 2016 22:41:24 GMT)
// Slice 2
0x0B, 0x00, // Total size
0x00, // AppGlanceSliceType - AppGlanceSliceType_IconAndSubtitle
0x01, // Number of attributes
// Slice Attributes
0x25, // Attribute ID - AttributeIdTimestamp
0x04, 0x00, // Attribute Length
// Slice expiration time:
0x95, 0x64, 0x4F, 0x57, // 1464820884 (Wed, 1 June 2016 22:41:25 GMT)
// Slice 3
0x0B, 0x00, // Total size
0x00, // AppGlanceSliceType - AppGlanceSliceType_IconAndSubtitle
0x01, // Number of attributes
// Slice Attributes
0x25, // Attribute ID - AttributeIdTimestamp
0x04, 0x00, // Attribute Length
// Slice expiration time:
0x96, 0x64, 0x4F, 0x57, // 1464820884 (Wed, 1 June 2016 22:41:26 GMT)
};
static const uint8_t s_app_glance_with_invalid_slice_total_sizes[] = {
// Version
APP_GLANCE_DB_CURRENT_VERSION,
// Creation time
0x14, 0x13, 0x4E, 0x57, // 1464734484 (Tue, 31 May 2016 22:41:24 GMT)
// Slice 1 (valid)
0x0B, 0x00, // Total size
0x00, // AppGlanceSliceType - AppGlanceSliceType_IconAndSubtitle
0x01, // Number of attributes
// Slice Attributes
0x25, // Attribute ID - AttributeIdTimestamp
0x04, 0x00, // Attribute Length
// Slice expiration time:
0x94, 0x64, 0x4F, 0x57, // 1464820884 (Wed, 1 June 2016 22:41:24 GMT)
// Slice 2 (invalid total_size)
0xFF, 0x00, // Total size
0x00, // AppGlanceSliceType - AppGlanceSliceType_IconAndSubtitle
0x01, // Number of attributes
// Slice Attributes
0x25, // Attribute ID - AttributeIdTimestamp
0x04, 0x00, // Attribute Length
// Slice expiration time:
0x95, 0x64, 0x4F, 0x57, // 1464820884 (Wed, 1 June 2016 22:41:25 GMT)
};
// Setup
////////////////////////////////////////////////////////////////
void test_app_glance_db__initialize(void) {
s_app_cache_entry_exists = true;
s_app_install_id = 1;
s_launch_count = 0;
fake_event_init();
fake_settings_file_reset();
app_glance_db_init();
}
void app_glance_db_deinit(void);
void test_app_glance_db__cleanup(void) {
app_glance_db_deinit();
}
// Blob Tests
////////////////////////////////////////////////////////////////
void test_app_glance_db__blob_insertion_with_invalid_key_or_val_length_fails(void) {
// Invalid key length should fail
const size_t invalid_key_length = 1337;
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, invalid_key_length,
(uint8_t *)&s_app_glance_basic,
sizeof(s_app_glance_basic)),
E_INVALID_ARGUMENT);
// Invalid val length should fail
const size_t invalid_val_size = sizeof(SerializedAppGlanceHeader) - 1;
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
(uint8_t *)&s_app_glance_basic, invalid_val_size),
E_INVALID_ARGUMENT);
}
void test_app_glance_db__basic_glance_blob_insert_and_read(void) {
const size_t glance_size = sizeof(s_app_glance_basic);
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
(uint8_t *)&s_app_glance_basic, glance_size),
S_SUCCESS);
cl_assert_equal_i(app_glance_db_get_len((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE),
glance_size);
uint8_t *glance_out = kernel_malloc(glance_size);
cl_assert_equal_i(app_glance_db_read((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE, glance_out,
glance_size),
S_SUCCESS);
cl_assert_equal_m(glance_out, (uint8_t *)s_app_glance_basic, glance_size);
kernel_free(glance_out);
}
void test_app_glance_db__blob_read_with_invalid_key_length_or_null_val_out_fails(void) {
// Call the basic glance blob insert test to insert the basic glance blob
test_app_glance_db__basic_glance_blob_insert_and_read();
const size_t glance_size = sizeof(s_app_glance_basic);
uint8_t glance_out[glance_size];
// Trying to read the basic glance blob back with an invalid key length should fail
const size_t invalid_key_length = 1337;
cl_assert_equal_i(app_glance_db_read((uint8_t *)&APP_GLANCE_TEST_UUID, invalid_key_length,
glance_out, glance_size),
E_INVALID_ARGUMENT);
// Trying to read the basic glance blob back with a NULL glance_out argument should fail
uint8_t *invalid_glance_out = NULL;
cl_assert_equal_i(app_glance_db_read((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
invalid_glance_out, glance_size),
E_INVALID_ARGUMENT);
}
void test_app_glance_db__basic_glance_blob_delete(void) {
// Call the basic glance blob insert test to insert the basic glance blob
test_app_glance_db__basic_glance_blob_insert_and_read();
// Delete the basic glance blob
cl_assert_equal_i(app_glance_db_delete((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE),
S_SUCCESS);
const size_t glance_size = sizeof(s_app_glance_basic);
uint8_t glance_out[glance_size];
// Trying to read the basic glance blob now should fail because it should no longer exist
cl_assert_equal_i(app_glance_db_read((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE, glance_out,
glance_size),
E_DOES_NOT_EXIST);
}
void test_app_glance_db__delete_non_existing_blob_does_nothing(void) {
// Trying to delete a glance that is not actually in the database should do nothing
cl_assert_equal_i(app_glance_db_delete((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE),
S_SUCCESS);
}
void test_app_glance_db__delete_blob_with_invalid_key_length_fails(void) {
// Call the basic glance blob insert test to insert the basic glance blob
test_app_glance_db__basic_glance_blob_insert_and_read();
// Trying to delete the basic glance blob with an invalid key length should fail
const size_t invalid_key_length = 1337;
cl_assert_equal_i(app_glance_db_delete((uint8_t *)&APP_GLANCE_TEST_UUID, invalid_key_length),
E_INVALID_ARGUMENT);
}
void test_app_glance_db__glance_blob_with_older_creation_time_than_existing_not_inserted(void) {
// Insert the first glance blob
SerializedAppGlanceHeader app_glance_1 = (SerializedAppGlanceHeader) {
.version = APP_GLANCE_DB_CURRENT_VERSION,
.creation_time = 1464734484, // Tue, 31 May 2016 22:41:24 GMT
};
const size_t glance_1_size = sizeof(app_glance_1);
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
(uint8_t *)&app_glance_1, glance_1_size),
S_SUCCESS);
cl_assert_equal_i(app_glance_db_get_len((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE),
glance_1_size);
// Try to insert a different glance blob with an older creation time; this should fail
SerializedAppGlanceHeader app_glance_2 = (SerializedAppGlanceHeader) {
.version = APP_GLANCE_DB_CURRENT_VERSION,
.creation_time = 1464648084, // Mon, 30 May 2016 22:41:24 GMT
};
const size_t glance_2_size = sizeof(app_glance_2);
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
(uint8_t *)&app_glance_2, glance_2_size),
E_INVALID_ARGUMENT);
}
void test_app_glance_db__glance_blob_with_too_many_slices_inserted_but_trimmed(void) {
const size_t original_glance_size = sizeof(s_app_glance_with_too_many_slices);
const size_t excess_slices_size = 11;
const size_t trimmed_glance_size = original_glance_size - excess_slices_size;
// Insert the glance blob with too many slices; this should succeed
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
(uint8_t *)&s_app_glance_with_too_many_slices,
original_glance_size),
S_SUCCESS);
// But the length we read back should be trimmed of the excess slices
cl_assert_equal_i(app_glance_db_get_len((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE),
trimmed_glance_size);
// The glance blob read back from the database should match everything up to where we trimmed
uint8_t glance_out[trimmed_glance_size];
cl_assert_equal_i(app_glance_db_read((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE, glance_out,
trimmed_glance_size),
S_SUCCESS);
cl_assert_equal_m(glance_out, (uint8_t *)s_app_glance_with_too_many_slices, trimmed_glance_size);
}
static void prv_check_invalid_version_code_blob_not_inserted(uint8_t version) {
const SerializedAppGlanceHeader app_glance = (SerializedAppGlanceHeader) {
.version = version,
};
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
(uint8_t *)&app_glance, sizeof(app_glance)),
E_INVALID_ARGUMENT);
}
void test_app_glance_db__lower_version_blob_not_inserted(void) {
for (uint8_t version = 0; version < APP_GLANCE_DB_CURRENT_VERSION; version++) {
prv_check_invalid_version_code_blob_not_inserted(version);
}
}
void test_app_glance_db__higher_version_not_blob_inserted(void) {
prv_check_invalid_version_code_blob_not_inserted(APP_GLANCE_DB_CURRENT_VERSION + 1);
}
static status_t prv_insert_dummy_glance_blob_with_size(uint16_t blob_size) {
const uint8_t dummy_app_glance[] = {
// Version
APP_GLANCE_DB_CURRENT_VERSION,
// Creation time
0x14, 0x13, 0x4E, 0x57, // 1464734484 (Tue, 31 May 2016 22:41:24 GMT)
// Slice 1
(uint8_t)(blob_size & 0xFF), (uint8_t)(blob_size >> 8), // Total size
};
return app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
(uint8_t *)&dummy_app_glance, sizeof(dummy_app_glance));
}
void test_app_glance_db__check_too_small_blob_not_inserted(void) {
cl_assert_equal_i(prv_insert_dummy_glance_blob_with_size(APP_GLANCE_DB_SLICE_MIN_SIZE - 1),
E_INVALID_ARGUMENT);
}
void test_app_glance_db__check_too_large_blob_not_inserted(void) {
cl_assert_equal_i(prv_insert_dummy_glance_blob_with_size(APP_GLANCE_DB_SLICE_MAX_SIZE + 1),
E_INVALID_ARGUMENT);
}
void test_app_glance_db__check_invalid_slice_total_sizes_blob_not_inserted(void) {
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
(uint8_t *)&s_app_glance_with_invalid_slice_total_sizes,
sizeof(s_app_glance_with_invalid_slice_total_sizes)),
E_INVALID_ARGUMENT);
}
status_t app_glance_db_insert_stale(const uint8_t *key, int key_len, const uint8_t *val,
int val_len);
void test_app_glance_db__read_stale_glance_blob(void) {
// Force the insertion of a stale glance blob (outdated version)
const SerializedAppGlanceHeader app_glance = (SerializedAppGlanceHeader) {
.version = APP_GLANCE_DB_CURRENT_VERSION - 1,
};
cl_assert_equal_i(app_glance_db_insert_stale((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
(uint8_t *)&app_glance, sizeof(app_glance)),
S_SUCCESS);
// Verify that trying to read the blob back fails due to it not existing
SerializedAppGlanceHeader glance_out = {};
cl_assert_equal_i(app_glance_db_read((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
(uint8_t *)&glance_out, sizeof(SerializedAppGlanceHeader)),
E_DOES_NOT_EXIST);
}
void test_app_glance_db__glance_blob_with_slice_missing_expiration_time_gets_default_value(void) {
const uint8_t app_glance_with_slice_missing_expiration_time[] = {
// Version
APP_GLANCE_DB_CURRENT_VERSION,
// Creation time
0x14, 0x13, 0x4E, 0x57, // 1464734484 (Tue, 31 May 2016 22:41:24 GMT)
// Slice 1
0x1B, 0x00, // Total size
0x00, // AppGlanceSliceType - AppGlanceSliceType_IconAndSubtitle
0x02, // Number of attributes
// Slice Attributes
0x30, // Attribute ID - AttributeIdIcon
0x04, 0x00, // Attribute Length
// Slice icon resource ID:
0x69, 0x00, 0x00, 0x00, //
0x2F, // Attribute ID - AttributeIdSubtitleTemplateString
0x0D, 0x00, // Attribute Length
// Slice subtitle:
'T', 'e', 's', 't', ' ', 's', 'u', 'b', 't', 'i', 't', 'l', 'e',
};
const size_t app_glance_with_slice_missing_expiration_time_size =
sizeof(app_glance_with_slice_missing_expiration_time);
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
(uint8_t *)&app_glance_with_slice_missing_expiration_time,
app_glance_with_slice_missing_expiration_time_size),
S_SUCCESS);
AppGlance read_back_glance = {};
cl_assert_equal_i(app_glance_db_read_glance(&APP_GLANCE_TEST_UUID, &read_back_glance), S_SUCCESS);
cl_assert_equal_i(read_back_glance.slices[0].expiration_time, APP_GLANCE_SLICE_NO_EXPIRATION);
}
// Glance Tests
////////////////////////////////////////////////////////////////
void test_app_glance_db__basic_glance_insert_and_read(void) {
const AppGlance glance = (AppGlance) {
.num_slices = 2,
.slices = {
{
.expiration_time = 1464734484, // (Tue, 31 May 2016 22:41:24 GMT)
.type = AppGlanceSliceType_IconAndSubtitle,
.icon_and_subtitle = {
.icon_resource_id = RESOURCE_ID_SETTINGS_ICON_AIRPLANE,
.template_string = "Test subtitle",
},
},
{
.expiration_time = 1465579430, // (Fri, 10 Jun 2016 17:23:50 GMT)
.type = AppGlanceSliceType_IconAndSubtitle,
.icon_and_subtitle = {
.icon_resource_id = RESOURCE_ID_SETTINGS_ICON_BLUETOOTH,
.template_string = "Test subtitle 2",
},
},
},
};
cl_assert_equal_i(app_glance_db_insert_glance(&APP_GLANCE_TEST_UUID, &glance), S_SUCCESS);
AppGlance read_back_glance = {};
cl_assert_equal_i(app_glance_db_read_glance(&APP_GLANCE_TEST_UUID, &read_back_glance),
S_SUCCESS);
cl_assert_equal_m(&glance, &read_back_glance, sizeof(AppGlance));
}
void test_app_glance_db__reading_nonexistent_glance_returns_does_not_exist(void) {
AppGlance glance = {};
cl_assert_equal_i(app_glance_db_read_glance(&UUID_INVALID, &glance), E_DOES_NOT_EXIST);
}
void test_app_glance_db__reading_glance_with_invalid_arguments_fails(void) {
// NULL UUID fails
AppGlance glance_out = {};
cl_assert_equal_i(app_glance_db_read_glance(NULL, &glance_out), E_INVALID_ARGUMENT);
// NULL glance_out fails
cl_assert_equal_i(app_glance_db_read_glance(&APP_GLANCE_TEST_UUID, NULL), E_INVALID_ARGUMENT);
}
void test_app_glance_db__inserting_glance_with_invalid_arguments_fails(void) {
// NULL UUID fails
const AppGlance glance = {};
cl_assert_equal_i(app_glance_db_insert_glance(NULL, &glance), E_INVALID_ARGUMENT);
// NULL glance fails
cl_assert_equal_i(app_glance_db_insert_glance(&APP_GLANCE_TEST_UUID, NULL), E_INVALID_ARGUMENT);
// Glance with too many slices fails
const AppGlance glance_with_too_many_slices = (AppGlance) {
.num_slices = 1337,
};
cl_assert(glance_with_too_many_slices.num_slices > APP_GLANCE_DB_MAX_SLICES_PER_GLANCE);
cl_assert_equal_i(app_glance_db_insert_glance(&APP_GLANCE_TEST_UUID,
&glance_with_too_many_slices), E_INVALID_ARGUMENT);
// Glance containing a slice with an invalid type fails
const AppGlance glance_containing_slice_with_invalid_type = (AppGlance) {
.num_slices = 1,
.slices = {
{
.expiration_time = 1464734484, // (Tue, 31 May 2016 22:41:24 GMT)
.type = (AppGlanceSliceType)200,
},
},
};
cl_assert(glance_containing_slice_with_invalid_type.slices[0].type >= AppGlanceSliceTypeCount);
cl_assert_equal_i(app_glance_db_insert_glance(&APP_GLANCE_TEST_UUID,
&glance_containing_slice_with_invalid_type),
E_INVALID_ARGUMENT);
}
void test_app_glance_db__read_glance_creation_time(void) {
time_t time_out;
// Reading the creation time of a glance that doesn't exist should return "does not exist"
cl_assert_equal_i(app_glance_db_read_creation_time(&APP_GLANCE_TEST_UUID, &time_out),
E_DOES_NOT_EXIST);
// Insert a glance and check that the creation time we read back matches
test_app_glance_db__basic_glance_blob_insert_and_read();
cl_assert_equal_i(app_glance_db_read_creation_time(&APP_GLANCE_TEST_UUID, &time_out),
S_SUCCESS);
cl_assert_equal_i(time_out, 1464734484);
}
void test_app_glance_db__read_glance_creation_time_with_invalid_arguments_fails(void) {
// NULL UUID fails
time_t time_out;
cl_assert_equal_i(app_glance_db_read_creation_time(NULL, &time_out), E_INVALID_ARGUMENT);
// NULL time_out fails
cl_assert_equal_i(app_glance_db_read_creation_time(&APP_GLANCE_TEST_UUID, NULL),
E_INVALID_ARGUMENT);
}
void test_app_glance_db__empty_glance_insert_after_basic_glance_insert_succeeds(void) {
// Call the basic glance insert test to insert the basic glance
test_app_glance_db__basic_glance_insert_and_read();
// Let some time pass so the creation time of this next glance insertion is newer
rtc_set_time(rtc_get_time() + 10);
// Try inserting an empty glance; this should succeed and clear the glance
AppGlance empty_glance = {};
cl_assert_equal_i(app_glance_db_insert_glance(&APP_GLANCE_TEST_UUID, &empty_glance), S_SUCCESS);
AppGlance read_back_glance = {};
cl_assert_equal_i(app_glance_db_read_glance(&APP_GLANCE_TEST_UUID, &read_back_glance),
S_SUCCESS);
cl_assert_equal_m(&empty_glance, &read_back_glance, sizeof(AppGlance));
}
void test_app_glance_db__insert_no_app_installed(void) {
s_app_install_id = INSTALL_ID_INVALID;
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
s_app_glance_basic, sizeof(s_app_glance_basic)), E_DOES_NOT_EXIST);
}
void test_app_glance_db__insert_app_not_in_cache(void) {
s_app_install_id = 10;
s_app_cache_entry_exists = false;
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
s_app_glance_basic, sizeof(s_app_glance_basic)), S_SUCCESS);
cl_assert_equal_i(fake_event_get_count(), 1);
PebbleEvent e = fake_event_get_last();
cl_assert_equal_i(e.type, PEBBLE_APP_FETCH_REQUEST_EVENT);
cl_assert(!e.app_fetch_request.with_ui);
cl_assert_equal_i(e.app_fetch_request.id, 10);
}
void test_app_glance_db__insert_app_in_cache(void) {
cl_assert_equal_i(app_glance_db_insert((uint8_t *)&APP_GLANCE_TEST_UUID, UUID_SIZE,
s_app_glance_basic, sizeof(s_app_glance_basic)), S_SUCCESS);
cl_assert_equal_i(s_launch_count, 1);
}

View file

@ -0,0 +1,302 @@
/*
* 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 "services/common/comm_session/session.h"
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
#include "services/normal/blob_db/api.h"
#include "services/normal/blob_db/endpoint.h"
#include "services/normal/blob_db/sync.h"
#include <stdio.h>
// Fakes
////////////////////////////////////
#include "fake_system_task.h"
// Stubs
////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_app_cache.h"
#include "stubs_ios_notif_pref_db.h"
#include "stubs_app_db.h"
#include "stubs_contacts_db.h"
#include "stubs_notif_db.h"
#include "stubs_pin_db.h"
#include "stubs_prefs_db.h"
#include "stubs_reminder_db.h"
#include "stubs_watch_app_prefs_db.h"
#include "stubs_weather_db.h"
#include "stubs_bt_lock.h"
#include "stubs_evented_timer.h"
#include "stubs_events.h"
#include "stubs_hexdump.h"
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_reminders.h"
#include "stubs_regular_timer.h"
CommSession *comm_session_get_system_session(void) {
return (CommSession *)1 ;
}
void blob_db_set_accepting_messages(bool ehh) {
}
static const uint8_t *s_expected_msg;
static bool did_sync_next = false;
static bool did_sync_cancel = false;
static bool did_sync_db = false;
static uint8_t sendbuffer[100];
static int sendbuffer_length;
static int sendbuffer_write_index;
extern void blob_db2_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length);
typedef void SendBuffer;
SendBuffer *comm_session_send_buffer_begin_write(CommSession *session, uint16_t endpoint_id,
size_t required_payload_length,
uint32_t timeout_ms) {
cl_assert(required_payload_length < 100);
sendbuffer_length = required_payload_length;
sendbuffer_write_index = 0;
return (void*)(uintptr_t)1;
}
bool comm_session_send_buffer_write(SendBuffer *sb, const uint8_t *data, size_t length) {
memcpy(&sendbuffer[sendbuffer_write_index], data, length);
sendbuffer_write_index += length;
return true;
}
void comm_session_send_buffer_end_write(SendBuffer *sb) {
cl_assert(sendbuffer_length > 0);
cl_assert_equal_m(sendbuffer, s_expected_msg, sendbuffer_length);
}
bool comm_session_send_data(CommSession *session, uint16_t endpoint_id,
const uint8_t *data, size_t length, uint32_t timeout_ms) {
cl_assert_equal_m(data, s_expected_msg, length);
return true;
}
extern void prv_send_response(CommSession *session, uint8_t *response, uint8_t response_length) {
cl_assert_equal_m(response, s_expected_msg, response_length);
}
static const BlobDBToken token = 0x22;
extern BlobDBToken prv_new_token(void) {
return token;
}
void blob_db_sync_next(BlobDBSyncSession *session) {
did_sync_next = true;
}
void blob_db_sync_cancel(BlobDBSyncSession *session) {
did_sync_cancel = true;
}
status_t blob_db_sync_db(BlobDBId db_id) {
did_sync_db = true;
return S_SUCCESS;
}
BlobDBSyncSession *blob_db_sync_get_session_for_token(BlobDBToken token) {
// Don't return NULL
return (BlobDBSyncSession *)1;
}
extern void blob_db2_set_accepting_messages(bool ehh);
void test_blob_db2_endpoint__initialize(void) {
blob_db2_set_accepting_messages(true);
did_sync_next = false;
did_sync_cancel = false;
did_sync_db = false;
sendbuffer_length = 0;
sendbuffer_write_index = 0;
memset(sendbuffer, 0, sizeof(sendbuffer));
}
void test_blob_db2_endpoint__cleanup(void) {
}
static const uint8_t s_dirty_dbs_request[] = {
BLOB_DB_COMMAND_DIRTY_DBS, // cmd
0x12, 0x34, // token
};
static const uint8_t s_dirty_dbs_response[] = {
BLOB_DB_COMMAND_DIRTY_DBS_RESPONSE, // cmd
0x12, 0x34, // token
BLOB_DB_SUCCESS, // status
0x01, // num db_ids
BlobDBIdiOSNotifPref // dirty db
};
void test_blob_db2_endpoint__handle_dirty_dbs_request(void) {
s_expected_msg = s_dirty_dbs_response;
blob_db2_protocol_msg_callback(NULL, s_dirty_dbs_request, sizeof(s_dirty_dbs_request));
}
static const uint8_t s_start_sync_request[] = {
BLOB_DB_COMMAND_START_SYNC, // cmd
0x12, 0x34, // token
BlobDBIdiOSNotifPref, // db id
};
static const uint8_t s_start_sync_response[] = {
BLOB_DB_COMMAND_START_SYNC_RESPONSE, // cmd
0x12, 0x34, // token
BLOB_DB_SUCCESS, // status
};
void test_blob_db2_endpoint__handle_start_sync_request(void) {
s_expected_msg = s_start_sync_response;
blob_db2_protocol_msg_callback(NULL, s_start_sync_request, sizeof(s_start_sync_request));
cl_assert(did_sync_db);
}
static const uint8_t s_start_write_response_success[] = {
BLOB_DB_COMMAND_WRITE_RESPONSE, // cmd
0x12, 0x34, // token
BLOB_DB_SUCCESS, // response
};
static const uint8_t s_start_write_response_error[] = {
BLOB_DB_COMMAND_WRITE_RESPONSE, // cmd
0x56, 0x78, // token
BLOB_DB_GENERAL_FAILURE, // response
};
void test_blob_db2_endpoint__handle_write_response(void) {
blob_db2_protocol_msg_callback(NULL, s_start_write_response_success,
sizeof(s_start_write_response_success));
cl_assert(did_sync_next);
blob_db2_protocol_msg_callback(NULL, s_start_write_response_error,
sizeof(s_start_write_response_error));
cl_assert(did_sync_cancel);
}
static const uint8_t s_start_writeback_response_success[] = {
BLOB_DB_COMMAND_WRITEBACK_RESPONSE, // cmd
0x12, 0x34, // token
BLOB_DB_SUCCESS, // response
};
static const uint8_t s_start_writeback_response_error[] = {
BLOB_DB_COMMAND_WRITEBACK_RESPONSE, // cmd
0x56, 0x78, // token
BLOB_DB_GENERAL_FAILURE, // response
};
void test_blob_db2_endpoint__handle_writeback_response(void) {
blob_db2_protocol_msg_callback(NULL, s_start_writeback_response_success,
sizeof(s_start_writeback_response_success));
cl_assert(did_sync_next);
blob_db2_protocol_msg_callback(NULL, s_start_writeback_response_error,
sizeof(s_start_writeback_response_error));
cl_assert(did_sync_cancel);
}
static const uint8_t s_sync_done_response[] = {
BLOB_DB_COMMAND_SYNC_DONE_RESPONSE, // cmd
0x56, 0x78, // token
BLOB_DB_SUCCESS, // response
};
void test_blob_db2_endpoint__handle_sync_done_response(void) {
blob_db2_protocol_msg_callback(NULL, s_sync_done_response, sizeof(s_sync_done_response));
// We currently don't do anything with this message
}
const uint8_t INVALID_CMD = 123;
static const uint8_t s_invalid_cmd[] = {
INVALID_CMD, // invalid cmd
0x56, 0x78, // token
BLOB_DB_SUCCESS, // response
};
static const uint8_t s_invalid_cmd_response[] = {
INVALID_CMD | RESPONSE_MASK, // cmd
0x56, 0x78, // token
BLOB_DB_INVALID_OPERATION, // status
};
void test_blob_db2_endpoint__handle_unknown_cmd_id(void) {
s_expected_msg = s_invalid_cmd_response;
blob_db2_protocol_msg_callback(NULL, s_invalid_cmd, sizeof(s_invalid_cmd));
}
static const uint8_t s_sync_done_message[] = {
BLOB_DB_COMMAND_SYNC_DONE, // cmd
0x22, 0x00, // token
BlobDBIdiOSNotifPref, // db id
};
void test_blob_db2_endpoint__send_sync_done(void) {
s_expected_msg = s_sync_done_message;
blob_db_endpoint_send_sync_done(BlobDBIdiOSNotifPref);
}
static const time_t last_updated = 1;
static const uint8_t key = 9;
static const uint8_t val = 2;
static const uint8_t s_writeback_message[] = {
BLOB_DB_COMMAND_WRITEBACK, // cmd
0x22, 0x00, // token
BlobDBIdiOSNotifPref, // db id
0x01, 0x00, 0x00, 0x00, // last updated
0x01, // key_len
key, // key
0x01, 0x00, // val_len
val, // val
};
void test_blob_db2_endpoint__send_writeback(void) {
s_expected_msg = s_writeback_message;
blob_db_endpoint_send_writeback(BlobDBIdiOSNotifPref, last_updated, &key, 1, &val, 1);
}
static const uint8_t s_write_message[] = {
BLOB_DB_COMMAND_WRITE, // cmd
0x22, 0x00, // token
BlobDBIdiOSNotifPref, // db id
0x01, 0x00, 0x00, 0x00, // last updated
0x01, // key_len
key, // key
0x01, 0x00, // val_len
val, // val
};
void test_blob_db2_endpoint__send_write(void) {
s_expected_msg = s_write_message;
blob_db_endpoint_send_write(BlobDBIdiOSNotifPref, last_updated, &key, 1, &val, 1);
}

View file

@ -0,0 +1,563 @@
/*
* 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 "services/common/comm_session/session.h"
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
#include "services/normal/blob_db/api.h"
#include "services/normal/blob_db/endpoint.h"
#include "util/attributes.h"
#include <stdio.h>
// Fakes
////////////////////////////////////
#include "fake_system_task.h"
#include "fake_session.h"
// Stubs
////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_app_cache.h"
#include "stubs_ios_notif_pref_db.h"
#include "stubs_app_db.h"
#include "stubs_contacts_db.h"
#include "stubs_notif_db.h"
#include "stubs_pin_db.h"
#include "stubs_prefs_db.h"
#include "stubs_reminder_db.h"
#include "stubs_watch_app_prefs_db.h"
#include "stubs_weather_db.h"
#include "stubs_bt_lock.h"
#include "stubs_evented_timer.h"
#include "stubs_events.h"
#include "stubs_hexdump.h"
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_reminders.h"
#include "stubs_regular_timer.h"
void bt_persistent_storage_set_unfaithful(bool is_unfaithful) {
return;
}
void blob_db2_set_accepting_messages(bool ehh) {
}
typedef struct PACKED {
uint16_t length;
uint16_t endpoint_id;
} PebbleProtocolHeader;
extern void blob_db_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length);
static const uint16_t BLOB_DB_ENDPOINT_ID = 0xb1db;
// used to test the filling of the system_task queue
static uint32_t system_task_queue_size = 7;
// used to examine sending buffer for valid contents
static uint8_t s_data[1024];
static uint16_t s_sending_endpoint_id = 0;
static uint32_t s_sending_data_length = 0;
static CommSession *s_session;
static const uint8_t TEST_KEY_SIZE = 16;
static const uint16_t TEST_VALUE_SIZE = 320;
static const uint8_t TEST_DB_ID = 0x01;
/* Helper functions */
static void prv_sent_data_cb(uint16_t endpoint_id,
const uint8_t* data, unsigned int data_length) {
s_sending_endpoint_id = endpoint_id;
s_sending_data_length = data_length;
memcpy(s_data, data, data_length);
}
static uint8_t *process_blob_db_command(const uint8_t *command, uint32_t length) {
s_sending_endpoint_id = 0;
s_sending_data_length = 0;
blob_db_protocol_msg_callback(s_session, command, length);
fake_system_task_callbacks_invoke_pending();
fake_comm_session_process_send_next();
cl_assert(BLOB_DB_ENDPOINT_ID == s_sending_endpoint_id);
return s_data;
}
/* Start of test */
extern void blob_db_set_accepting_messages(bool enabled);
void test_blob_db_endpoint__initialize(void) {
blob_db_set_accepting_messages(true);
fake_comm_session_init();
Transport *transport = fake_transport_create(TransportDestinationSystem, NULL, prv_sent_data_cb);
s_session = fake_transport_set_connected(transport, true /* connected */);
system_task_set_available_space(system_task_queue_size);
}
void test_blob_db_endpoint__cleanup(void) {
fake_comm_session_cleanup();
}
/*************************************
* Checking for valid INSERT command *
*************************************/
static const uint8_t s_insert_cmd_success[] = {
// Message Header
0x01, // Pebble protocol message ID: INSERT
0x17, 0x00, // Token
TEST_DB_ID, // BlobDBDatabaseId: 0x01
0x10, // Size primary key: sizeof(Uuid) = 16 = 0x10
// Primary Key: UUID:16
0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x40, 0x01, // Size value
// value (made up for now)
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4, 0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
};
void test_blob_db_endpoint__handle_insert_command_success(void) {
uint8_t *cmd_ptr = (uint8_t*)s_insert_cmd_success;
// check for INSERT
cl_assert(BLOB_DB_COMMAND_INSERT == *(uint8_t*)cmd_ptr);
cmd_ptr += sizeof(uint8_t);
// check for token
uint16_t token = *(uint16_t*)cmd_ptr;
cl_assert(0 < token);
cmd_ptr += sizeof(uint16_t);
// check for a Database entry
cl_assert(TEST_DB_ID == *(uint8_t*)cmd_ptr);
cmd_ptr += sizeof(uint8_t);
// check for a key size
cl_assert(TEST_KEY_SIZE == *(uint8_t*)cmd_ptr);
cmd_ptr += sizeof(uint8_t);
// go past key_bytes
cmd_ptr += TEST_KEY_SIZE;
// size value
cl_assert(TEST_VALUE_SIZE == *(uint16_t*)cmd_ptr);
cmd_ptr += sizeof(uint16_t);
// go past value_bytes
cmd_ptr += TEST_VALUE_SIZE;
cl_assert((cmd_ptr - s_insert_cmd_success) == sizeof(s_insert_cmd_success));
// Process Command
uint8_t *resp_ptr = process_blob_db_command(s_insert_cmd_success, sizeof(s_insert_cmd_success));
// Check Response
cl_assert(token == *(uint16_t*)resp_ptr);
resp_ptr += sizeof(uint16_t);
cl_assert(BLOB_DB_SUCCESS == *resp_ptr);
resp_ptr += sizeof(uint8_t);
cl_assert((resp_ptr - s_data) == s_sending_data_length);
}
/**************************************************
* Checking for key size zero INSERT command *
**************************************************/
static const uint8_t s_insert_cmd_zero_key_size[] = {
// Message Header
0x01, // Pebble protocol message ID: INSERT
0x17, 0x00, // Token
TEST_DB_ID, // BlobDBDatabaseId: 0x01
0x00, // Size primary key: sizeof(Uuid) = 16 = 0x10
// garbage data to put message above minimum
0x01, 0x02, 0x03, 0x04, 0x05,
// no value size
};
void test_blob_db_endpoint__handle_insert_command_zero_key_size(void) {
// Ensure key length is 0
cl_assert(s_insert_cmd_zero_key_size[2] == 0);
// Process Command
uint8_t *resp_ptr = process_blob_db_command(s_insert_cmd_zero_key_size, sizeof(s_insert_cmd_zero_key_size));
// Check Response
cl_assert(0 < *(uint16_t*)resp_ptr);
resp_ptr += sizeof(uint16_t);
cl_assert(BLOB_DB_INVALID_DATA == *resp_ptr);
resp_ptr += sizeof(uint8_t);
cl_assert((resp_ptr - s_data) == s_sending_data_length);
}
/**************************************************
* Checking for value size of zero INSERT command *
* This is valid because one can ZERO out a value *
**************************************************/
static const uint8_t s_insert_cmd_zero_value_size[] = {
// Message Header
0x01, // Pebble protocol message ID: INSERT
0x17, 0x00, // Token
TEST_DB_ID, // BlobDBDatabaseId: 0x01
0x10, // Size primary key: sizeof(Uuid) = 16 = 0x10
// Primary Key: UUID:16
0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x00, 0x00, // Size value
};
void test_blob_db_endpoint__handle_insert_command_zero_value_size(void) {
// Process Command
uint8_t *resp_ptr = process_blob_db_command(s_insert_cmd_zero_value_size, sizeof(s_insert_cmd_zero_value_size));
// Check Response
cl_assert(0 < *(uint16_t*)resp_ptr);
resp_ptr += sizeof(uint16_t);
cl_assert(BLOB_DB_INVALID_DATA == *resp_ptr);
resp_ptr += sizeof(uint8_t);
cl_assert((resp_ptr - s_data) == s_sending_data_length);
}
/**************************************************
* Checking for below minimum size INSERT command *
**************************************************/
static const uint8_t s_insert_cmd_no_value_size[] = {
// Message Header
0x01, // Pebble protocol message ID: INSERT
0x17, 0x00, // Token
TEST_DB_ID, // BlobDBDatabaseId: 0x01
0x01, // Size primary key: 1 byte
// Primary Key: byte
0x6b
// no value size
};
void test_blob_db_endpoint__handle_insert_command_no_value_size(void) {
// Ensure packet is too small
cl_assert(sizeof(s_insert_cmd_no_value_size) < 8);
// Process Command
uint8_t *resp_ptr = process_blob_db_command(s_insert_cmd_no_value_size, sizeof(s_insert_cmd_no_value_size));
// Check Response
cl_assert(0 < *(uint16_t*)resp_ptr);
resp_ptr += sizeof(uint16_t);
cl_assert(BLOB_DB_INVALID_DATA == *resp_ptr);
resp_ptr += sizeof(uint8_t);
cl_assert((resp_ptr - s_data) == s_sending_data_length);
}
/**************************************************
* Checking for length data mismatch INSERT command *
**************************************************/
static const uint8_t s_insert_cmd_size_value_wrong[] = {
// Message Header
0x01, // Pebble protocol message ID: INSERT
0x17, 0x00, // Token
TEST_DB_ID, // BlobDBDatabaseId: 0x01
0x10, // Size primary key: sizeof(Uuid) = 16 = 0x10
// Primary Key: UUID:16
0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x21, 0x00, // Size value is 1 more than it should be
// value (made up for now)
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
};
void test_blob_db_endpoint__handle_insert_command_length_data_mismatch(void) {
// Process Command
uint8_t *resp_ptr = process_blob_db_command(s_insert_cmd_size_value_wrong, sizeof(s_insert_cmd_size_value_wrong));
// Check Response
cl_assert(0 < *(uint16_t*)resp_ptr);
resp_ptr += sizeof(uint16_t);
cl_assert(BLOB_DB_INVALID_DATA == *resp_ptr);
resp_ptr += sizeof(uint8_t);
cl_assert((resp_ptr - s_data) == s_sending_data_length);
}
/**************************************************
* Checking for smallest possible valid INSERT command *
**************************************************/
static const uint8_t s_insert_cmd_smallest_length[] = {
// Message Header
0x01, // Pebble protocol message ID: INSERT
0x17, 0x00, // Token
TEST_DB_ID, // BlobDBDatabaseId: 0x01
0x01, // Size primary key: 1 byte
// Primary Key: 1 byte
0x00,
0x01, 0x00, // Size value is 1 more than it should be
// value (made up for now)
0x00
};
void test_blob_db_endpoint__handle_insert_command_smallest_length(void) {
// Process Command
uint8_t *resp_ptr = process_blob_db_command(s_insert_cmd_smallest_length, sizeof(s_insert_cmd_smallest_length));
// Check Response
cl_assert(0 < *(uint16_t*)resp_ptr);
resp_ptr += sizeof(uint16_t);
cl_assert(BLOB_DB_SUCCESS == *resp_ptr);
resp_ptr += sizeof(uint8_t);
cl_assert((resp_ptr - s_data) == s_sending_data_length);
}
/*************************************
* Checking for valid DELETE command *
*************************************/
static const uint8_t s_delete_cmd_success[] = {
// Message Header
0x04, // Pebble protocol message ID: DELETE
0x17, 0x00, // Token
TEST_DB_ID, // BlobDBDatabaseId: 0x01
0x10, // Size primary key: sizeof(Uuid) = 16 = 0x10
// Primary Key: UUID:16
0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4,
};
void test_blob_db_endpoint__handle_delete_command_success(void) {
uint8_t *cmd_ptr = (uint8_t*)s_delete_cmd_success;
// check for DELETE
cl_assert(BLOB_DB_COMMAND_DELETE == *(uint8_t*)cmd_ptr);
cmd_ptr += sizeof(uint8_t);
// check for token
uint16_t token = *(uint16_t*)cmd_ptr;
cl_assert(0 < token);
cmd_ptr += sizeof(uint16_t);
// check for a Database entry
cl_assert(TEST_DB_ID == *(uint8_t*)cmd_ptr);
cmd_ptr += sizeof(uint8_t);
// check for a key size
cl_assert(TEST_KEY_SIZE == *(uint8_t*)cmd_ptr);
cmd_ptr += sizeof(uint8_t);
// go past key_bytes
cmd_ptr += TEST_KEY_SIZE;
cl_assert((cmd_ptr - s_delete_cmd_success) == sizeof(s_delete_cmd_success)) ;
// Process Command
uint8_t *resp_ptr = process_blob_db_command(s_delete_cmd_success, sizeof(s_delete_cmd_success));
// Check Response
cl_assert(token == *(uint16_t*)resp_ptr);
resp_ptr += sizeof(uint16_t);
cl_assert(BLOB_DB_SUCCESS == *resp_ptr);
resp_ptr += sizeof(uint8_t);
cl_assert((resp_ptr - s_data) == s_sending_data_length);
}
/*************************************
* Checking for valid CLEAR command *
*************************************/
static const uint8_t s_clear_cmd_success[] = {
// Message Header
0x05, // Pebble protocol message ID: DELETE
0x17, 0x00, // Token
TEST_DB_ID, // BlobDBDatabaseId: 0x01
};
void test_blob_db_endpoint__handle_clear_command_success(void) {
uint8_t *cmd_ptr = (uint8_t*)s_clear_cmd_success;
// check for CLEAR
cl_assert(BLOB_DB_COMMAND_CLEAR == *(uint8_t*)cmd_ptr);
cmd_ptr += sizeof(uint8_t);
// check for token
uint16_t token = *(uint16_t*)cmd_ptr;
cl_assert(0 < token);
cmd_ptr += sizeof(uint16_t);
// check for a Database entry
cl_assert(TEST_DB_ID == *(uint8_t*)cmd_ptr);
cmd_ptr += sizeof(uint8_t);
cl_assert((cmd_ptr - s_clear_cmd_success) == sizeof(s_clear_cmd_success)) ;
// Process Command
uint8_t *resp_ptr = process_blob_db_command(s_clear_cmd_success, sizeof(s_clear_cmd_success));
// Check Response
cl_assert_equal_i(token, *(uint16_t*)resp_ptr);
resp_ptr += sizeof(uint16_t);
cl_assert(BLOB_DB_SUCCESS == *resp_ptr);
resp_ptr += sizeof(uint8_t);
cl_assert_equal_i((resp_ptr - s_data), s_sending_data_length);
}
/*************************************
* Checking for invalid operation *
*************************************/
static const uint8_t s_invalid_operation_cmd[] = {
// Message Header
0x42, // Pebble protocol message ID: This does not exist
0x17, 0x00, // Token
TEST_DB_ID, // BlobDBDatabaseId: 0x01
};
void test_blob_db_endpoint__handle_invalid_operation_command(void) {
uint8_t *cmd_ptr = (uint8_t*)s_invalid_operation_cmd;
cmd_ptr += sizeof(uint8_t);
// check for token
uint16_t token = *(uint16_t*)cmd_ptr;
cl_assert(0 < token);
cmd_ptr += sizeof(uint16_t);
// check for a Database entry
cl_assert(TEST_DB_ID == *(uint8_t*)cmd_ptr);
cmd_ptr += sizeof(uint8_t);
cl_assert((cmd_ptr - s_invalid_operation_cmd) == sizeof(s_invalid_operation_cmd)) ;
// Process Command
uint8_t *resp_ptr = process_blob_db_command(s_invalid_operation_cmd, sizeof(s_invalid_operation_cmd));
// Check Response
cl_assert(token == *(uint16_t*)resp_ptr);
resp_ptr += sizeof(uint16_t);
cl_assert(BLOB_DB_INVALID_OPERATION == *resp_ptr);
resp_ptr += sizeof(uint8_t);
cl_assert((resp_ptr - s_data) == s_sending_data_length);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// BLOBDB SYNC TESTS
///////////////////////////////////////////////////////////////////////////////////////////////////
static const uint8_t *s_expected_data;
extern void prv_send_v2_response(CommSession *session, uint8_t *response, uint8_t response_length) {
cl_assert_equal_m(response, s_expected_data, response_length);
}
static const uint8_t s_dirty_dbs_request[] = {
BLOB_DB_COMMAND_DIRTY_DBS, // cmd
0x12, 0x34, // token
};
static const uint8_t s_dirty_dbs_response[] = {
BLOB_DB_COMMAND_DIRTY_DBS_RESPONSE, // cmd
0x12, 0x34, // token
BLOB_DB_SUCCESS, // status
0x01, // num db_ids
BlobDBIdiOSNotifPref // dirty db
};
void test_blob_db_endpoint__handle_dirty_dbs_request(void) {
s_expected_data = s_dirty_dbs_response;
blob_db_protocol_msg_callback(NULL, s_dirty_dbs_request, sizeof(s_dirty_dbs_request));
}
static const uint8_t s_start_sync_request[] = {
BLOB_DB_COMMAND_START_SYNC, // cmd
0x12, 0x34, // token
};
static const uint8_t s_start_sync_response[] = {
BLOB_DB_COMMAND_START_SYNC_RESPONSE, // cmd
0x12, 0x34, // token
BLOB_DB_SUCCESS, // status
};
void test_blob_db_endpoint__handle_start_sync_request(void) {
s_expected_data = s_start_sync_response;
blob_db_protocol_msg_callback(NULL, s_start_sync_request, sizeof(s_start_sync_request));
}

View file

@ -0,0 +1,337 @@
/*
* 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"
// Fakes
/////////////
#include "fake_system_task.h"
#include "fake_regular_timer.h"
#include "fake_blobdb.h"
// Stubs
/////////////
#include "stubs_pbl_malloc.h"
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_session.h"
// FW Includes
///////////////
#include "services/normal/blob_db/api.h"
#include "services/normal/blob_db/util.h"
#include "services/normal/blob_db/sync.h"
#include "util/size.h"
// Writebacks counter
////////////////////////
static int s_num_writebacks;
static int s_num_until_timeout;
void blob_db_endpoint_send_sync_done(BlobDBId db_id) {
return;
}
static void prv_handle_response_from_phone(void *data) {
s_num_writebacks++;
BlobDBSyncSession *session = data;
blob_db_sync_next(session);
}
static void prv_generate_responses_from_phone(void) {
while (fake_system_task_count_callbacks()) {
fake_system_task_callbacks_invoke_pending();
}
}
BlobDBToken blob_db_endpoint_send_writeback(BlobDBId db_id,
time_t last_updated,
const void *key,
int key_len,
const void *val,
int val_len) {
BlobDBSyncSession *session = blob_db_sync_get_session_for_id(db_id);
cl_assert(session != NULL);
if (s_num_until_timeout != 0 && s_num_writebacks >= s_num_until_timeout) {
fake_regular_timer_trigger(&session->timeout_timer);
} else {
system_task_add_callback(prv_handle_response_from_phone, session);
}
return 12345;
}
BlobDBToken blob_db_endpoint_send_write(BlobDBId db_id,
time_t last_updated,
const void *key,
int key_len,
const void *val,
int val_len) {
return 0;
}
// Tests
////////////////////////
void test_blob_db_sync__initialize(void) {
fake_blob_db_set_id(BlobDBIdTest);
blob_db_init_dbs();
s_num_until_timeout = 0;
s_num_writebacks = 0;
}
void test_blob_db_sync__cleanup(void) {
blob_db_flush(BlobDBIdTest);
fake_system_task_callbacks_cleanup();
}
void test_blob_db_sync__no_dirty(void) {
uint8_t ids[NumBlobDBs];
uint8_t num_ids;
blob_db_get_dirty_dbs(ids, &num_ids);
cl_assert_equal_i(num_ids, 0);
cl_assert(blob_db_get_dirty_list(BlobDBIdTest) == NULL);
// insert one
char *key = "key";
char *value = "value";
cl_assert_equal_i(S_SUCCESS, blob_db_insert(BlobDBIdTest,
(uint8_t *)key,
strlen(key),
(uint8_t *)value,
strlen(value)));
blob_db_get_dirty_dbs(ids, &num_ids);
cl_assert_equal_i(num_ids, 1);
cl_assert(blob_db_get_dirty_list(BlobDBIdTest) != NULL);
// mark it synced
cl_assert_equal_i(S_SUCCESS, blob_db_mark_synced(BlobDBIdTest, (uint8_t *)key, strlen(key)));
blob_db_get_dirty_dbs(ids, &num_ids);
cl_assert_equal_i(num_ids, 0);
cl_assert(blob_db_get_dirty_list(BlobDBIdTest) == NULL);
}
static bool prv_list_key_comparator(ListNode *cur_node, void *data) {
BlobDBDirtyItem *dirty_item = (BlobDBDirtyItem *)cur_node;
char *key = data;
return (memcmp(dirty_item->key, key, dirty_item->key_len) == 0);
}
void test_blob_db_sync__dirty_list(void) {
uint8_t ids[NumBlobDBs];
uint8_t num_ids;
blob_db_get_dirty_dbs(ids, &num_ids);
cl_assert_equal_i(num_ids, 0);
BlobDBDirtyItem *dirty_list = blob_db_get_dirty_list(BlobDBIdTest);
cl_assert(dirty_list == NULL);
blob_db_util_free_dirty_list(dirty_list);
char *keys[] = { "key1", "key2", "key3", "key4", "key5" };
int key_len = strlen(keys[0]);
char *values[] = { "val1", "val2", "val3", "val4", "val5" };
int value_len = strlen(values[0]);
// insert all keys
for (int i = 0; i < ARRAY_LENGTH(keys); ++i) {
blob_db_insert(BlobDBIdTest, (uint8_t *)keys[i], key_len, (uint8_t *)values[i], value_len);
}
// check the dirty list
blob_db_get_dirty_dbs(ids, &num_ids);
cl_assert_equal_i(num_ids, 1);
dirty_list = blob_db_get_dirty_list(BlobDBIdTest);
cl_assert_equal_i(list_count(&dirty_list->node), ARRAY_LENGTH(keys));
for (int i = 0; i < ARRAY_LENGTH(keys); ++i) {
cl_assert(list_find(&dirty_list->node, prv_list_key_comparator, keys[i]) != NULL);
}
blob_db_util_free_dirty_list(dirty_list);
// mark one as synced and re-check
for (int synced_idx = 1; synced_idx < ARRAY_LENGTH(keys); ++synced_idx) {
blob_db_mark_synced(BlobDBIdTest, (uint8_t *)keys[synced_idx - 1], key_len);
dirty_list = blob_db_get_dirty_list(BlobDBIdTest);
cl_assert_equal_i(list_count(&dirty_list->node), ARRAY_LENGTH(keys) - synced_idx);
for (int i = synced_idx; i < ARRAY_LENGTH(keys); ++i) {
cl_assert(list_find(&dirty_list->node, prv_list_key_comparator, keys[i]) != NULL);
}
blob_db_util_free_dirty_list(dirty_list);
}
}
void test_blob_db_sync__sync_all(void) {
char *keys[] = { "key1", "key2", "key3", "key4", "key5" };
int key_len = strlen(keys[0]);
char *values[] = { "val1", "val2", "val3", "val4", "val5" };
int value_len = strlen(values[0]);
// insert all keys
for (int i = 0; i < ARRAY_LENGTH(keys); ++i) {
blob_db_insert(BlobDBIdTest, (uint8_t *)keys[i], key_len, (uint8_t *)values[i], value_len);
}
cl_assert(blob_db_sync_db(BlobDBIdTest) == S_SUCCESS);
prv_generate_responses_from_phone();
cl_assert_equal_i(s_num_writebacks, 5);
}
void test_blob_db_sync__sync_oom(void) {
char *keys[] = { "key1", "key2", "key3", "key4", "key5" };
int key_len = strlen(keys[0]);
char *values[] = { "val1", "val2", "val3", "val4", "val5" };
int value_len = strlen(values[0]);
// insert all keys
for (int i = 0; i < ARRAY_LENGTH(keys); ++i) {
blob_db_insert(BlobDBIdTest, (uint8_t *)keys[i], key_len, (uint8_t *)values[i], value_len);
}
cl_assert(blob_db_sync_db(BlobDBIdTest) == S_SUCCESS);
// We have built the dirty list, add more entries.
// This mimics us performing writes while the sync is ongoing or not having enough memory to
// build the initial list
char *extra_keys[] = { "keyA", "keyB" };
char *extra_values[] = { "valA", "valB" };
for (int i = 0; i < ARRAY_LENGTH(extra_keys); ++i) {
blob_db_insert(BlobDBIdTest, (uint8_t *)extra_keys[i], key_len,
(uint8_t *)extra_values[i], value_len);
}
prv_generate_responses_from_phone();
cl_assert_equal_i(s_num_writebacks, 7);
}
void test_blob_db_sync__sync_some(void) {
char *keys[] = { "key1", "key2", "key3", "key4", "key5" };
int key_len = strlen(keys[0]);
char *values[] = { "val1", "val2", "val3", "val4", "val5" };
int value_len = strlen(values[0]);
// insert all keys, mark some as synced
for (int i = 0; i < ARRAY_LENGTH(keys); ++i) {
blob_db_insert(BlobDBIdTest, (uint8_t *)keys[i], key_len, (uint8_t *)values[i], value_len);
// choose two that are not consecutive, that leaves 3 still to be synced
if (i == 1 || i == 3) {
blob_db_mark_synced(BlobDBIdTest, (uint8_t *)keys[i], key_len);
}
}
cl_assert(blob_db_sync_db(BlobDBIdTest) == S_SUCCESS);
prv_generate_responses_from_phone();
cl_assert_equal_i(s_num_writebacks, 3);
}
void test_blob_db_sync__timeout_and_retry(void) {
char *keys[] = { "key1", "key2", "key3", "key4", "key5" };
int key_len = strlen(keys[0]);
char *values[] = { "val1", "val2", "val3", "val4", "val5" };
int value_len = strlen(values[0]);
// insert all keys, mark some as synced
for (int i = 0; i < ARRAY_LENGTH(keys); ++i) {
blob_db_insert(BlobDBIdTest, (uint8_t *)keys[i], key_len, (uint8_t *)values[i], value_len);
}
s_num_until_timeout = 3;
s_num_writebacks = 0;
cl_assert(blob_db_sync_db(BlobDBIdTest) == S_SUCCESS);
prv_generate_responses_from_phone();
cl_assert_equal_i(s_num_writebacks, s_num_until_timeout);
s_num_until_timeout = 0;
cl_assert(blob_db_sync_db(BlobDBIdTest) == S_SUCCESS);
prv_generate_responses_from_phone();
cl_assert_equal_i(s_num_writebacks, 5);
}
void test_blob_db_sync__sync_while_syncing(void) {
char *keys[] = { "key1", "key2", "key3", "key4", "key5" };
int key_len = strlen(keys[0]);
char *values[] = { "val1", "val2", "val3", "val4", "val5" };
int value_len = strlen(values[0]);
for (int i = 0; i < ARRAY_LENGTH(keys); ++i) {
blob_db_insert(BlobDBIdTest, (uint8_t *)keys[i], key_len, (uint8_t *)values[i], value_len);
}
cl_assert(blob_db_sync_db(BlobDBIdTest) == S_SUCCESS);
// We should throw an error if we get a sync while a db sync in in progress
cl_assert(blob_db_sync_db(BlobDBIdTest) == E_BUSY);
// Generate some responses so the sync session gets cleaned up
prv_generate_responses_from_phone();
}
static void prv_fill_stop_return_session(BlobDBId id) {
char *keys[] = { "key1", "key2", "key3", "key4", "key5" };
int key_len = strlen(keys[0]);
char *values[] = { "val1", "val2", "val3", "val4", "val5" };
int value_len = strlen(values[0]);
fake_blob_db_set_id(id);
blob_db_init_dbs();
for (int i = 0; i < ARRAY_LENGTH(keys); ++i) {
blob_db_insert(id, (uint8_t *)keys[i], key_len, (uint8_t *)values[i], value_len);
}
s_num_writebacks = 0;
cl_assert(blob_db_sync_db(id) == S_SUCCESS);
}
void test_blob_db_sync__find_session(void) {
// create a few sync sessions
prv_fill_stop_return_session(BlobDBIdTest);
prv_fill_stop_return_session(BlobDBIdPins);
prv_fill_stop_return_session(BlobDBIdReminders);
// check we can conjure them by id
BlobDBSyncSession *test_session = blob_db_sync_get_session_for_id(BlobDBIdTest);
cl_assert(test_session);
cl_assert_equal_i(test_session->db_id, BlobDBIdTest);
BlobDBSyncSession *pins_session = blob_db_sync_get_session_for_id(BlobDBIdPins);
cl_assert(pins_session);
cl_assert_equal_i(pins_session->db_id, BlobDBIdPins);
BlobDBSyncSession *reminders_session = blob_db_sync_get_session_for_id(BlobDBIdReminders);
cl_assert(reminders_session);
cl_assert_equal_i(reminders_session->db_id, BlobDBIdReminders);
test_session->current_token = 1;
pins_session->current_token = 2;
reminders_session->current_token = 3;
// check we can conjure them by token
cl_assert(test_session == blob_db_sync_get_session_for_token(1));
cl_assert(pins_session == blob_db_sync_get_session_for_token(2));
cl_assert(reminders_session == blob_db_sync_get_session_for_token(3));
// Cancel the sync sessions so they get cleaned up
blob_db_sync_cancel(test_session);
blob_db_sync_cancel(pins_session);
blob_db_sync_cancel(reminders_session);
// reset fake blob db so cleanup doesn't assert
fake_blob_db_set_id(BlobDBIdTest);
blob_db_init_dbs();
}

View file

@ -0,0 +1,162 @@
/*
* 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 "services/normal/filesystem/pfs.h"
#include "services/normal/blob_db/contacts_db.h"
#include "services/normal/contacts/contacts.h"
#include "util/uuid.h"
// Fixture
////////////////////////////////////////////////////////////////
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_spi_flash.h"
#include "fake_system_task.h"
#include "fake_kernel_services_notifications.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_hexdump.h"
#include "stubs_layout_layer.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_sleep.h"
#include "stubs_task_watchdog.h"
#define CONTACT_1_UUID 0x0a, 0x04, 0x98, 0x00, 0x39, 0x18, 0x47, 0xaa, 0x9c, 0x16, 0x8e, 0xa0, 0xa8, 0x2a, 0x2e, 0xb8
#define ADDRESS_1_UUID 0xd3, 0x72, 0x2d, 0x75, 0x6b, 0x21, 0x49, 0x2a, 0x9c, 0xc7, 0x5f, 0xf8, 0x4d, 0xd2, 0x5a, 0x9c
#define ADDRESS_2_UUID 0x43, 0x03, 0x91, 0x06, 0x80, 0x39, 0x48, 0xea, 0x92, 0x72, 0xf3, 0x4c, 0xd5, 0x35, 0x9c, 0xcf
static const uint8_t s_contact_1[] = {
// Uuid
CONTACT_1_UUID,
// Flags
0x00, 0x00, 0x00, 0x00,
// Number of Attributes,
0x01,
// Number of Addresses,
0x02,
// Attribute 1
0x01, // Attribute ID - Title
0x08, 0x00, // Attribute Length
// Attribute text: "John Doe"
'J', 'o', 'h', 'n', ' ', 'D', 'o', 'e',
// Address 1
// Uuid
ADDRESS_1_UUID,
0x01, // AddressType - PhoneNumber
0x02, // Number of attributes
// Address Attributes
0x01, // Attribute ID - Title
0x06, 0x00, // Attribute Length
// Attribute text:
'm', 'o', 'b', 'i', 'l', 'e',
0x27, // Attribute ID - Address
0x0a, 0x00, // Attribute Length
// Attribute text:
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
// Address 2
// Uuid
ADDRESS_2_UUID,
0x02, // AddressType - Email
0x02, // Number of attributes
// Address Attributes
0x01, // Attribute ID - Title
0x04, 0x00, // Attribute Length
// Attribute text:
'h', 'o', 'm', 'e',
0x27, // Attribute ID - Address
0x10, 0x00, // Attribute Length
// Attribute text:
'n', 'a', 'm', 'e', '@', 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm',
};
// Setup
////////////////////////////////////////////////////////////////
void test_contacts_db__initialize(void) {
fake_spi_flash_init(0, 0x1000000);
pfs_init(false);
contacts_db_init();
}
void test_contacts_db__cleanup(void) {
}
// Tests
////////////////////////////////////////////////////////////////
void test_contacts_db__insert(void) {
uint8_t contact[sizeof(s_contact_1)];
memcpy(contact, s_contact_1, sizeof(s_contact_1));
cl_assert_equal_i(contacts_db_insert((uint8_t *)&contact, UUID_SIZE,
(uint8_t *)&contact, sizeof(contact)), 0);
cl_assert_equal_i(contacts_db_get_len((uint8_t *)&contact, UUID_SIZE), sizeof(contact));
uint8_t *contact_out = kernel_malloc(sizeof(contact));
cl_assert_equal_i(contacts_db_read((uint8_t *)&contact, UUID_SIZE,
contact_out, sizeof(contact)), 0);
cl_assert_equal_m((uint8_t *)s_contact_1, contact_out, sizeof(contact));
kernel_free(contact_out);
}
void test_contacts_db__insert_remove(void) {
uint8_t contact[sizeof(s_contact_1)];
memcpy(contact, s_contact_1, sizeof(s_contact_1));
cl_assert_equal_i(contacts_db_insert((uint8_t *)&contact, UUID_SIZE, (uint8_t *)&contact, sizeof(contact)), 0);
cl_assert_equal_i(contacts_db_delete((uint8_t *)&contact, UUID_SIZE), 0);
cl_assert_equal_i(contacts_db_get_len((uint8_t *)&contact, UUID_SIZE), 0);
}
void test_contacts_db__flush(void) {
uint8_t contact[sizeof(s_contact_1)];
memcpy(contact, s_contact_1, sizeof(s_contact_1));
cl_assert_equal_i(contacts_db_insert((uint8_t *)&contact, UUID_SIZE, (uint8_t *)&contact, sizeof(contact)), 0);
cl_assert_equal_i(contacts_db_flush(), 0);
fake_system_task_callbacks_invoke_pending();
cl_assert_equal_i(contacts_db_get_len((uint8_t *)&contact, UUID_SIZE), 0);
}
void test_contacts_db__get_serialized_contact(void) {
uint8_t contact[sizeof(s_contact_1)];
memcpy(contact, s_contact_1, sizeof(s_contact_1));
cl_assert_equal_i(contacts_db_insert((uint8_t *)&contact, UUID_SIZE,
(uint8_t *)&contact, sizeof(contact)), 0);
cl_assert_equal_i(contacts_db_get_len((uint8_t *)&contact, UUID_SIZE), sizeof(contact));
SerializedContact *serialized_contact;
contacts_db_get_serialized_contact((Uuid *)&contact, &serialized_contact);
cl_assert_equal_i(serialized_contact->flags, 0);
cl_assert_equal_i(serialized_contact->num_attributes, 1);
cl_assert_equal_i(serialized_contact->num_addresses, 2);
contacts_db_free_serialized_contact(serialized_contact);
}

View file

@ -0,0 +1,467 @@
/*
* 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 "services/normal/filesystem/pfs.h"
#include "services/normal/blob_db/health_db.h"
#include "util/size.h"
#include <limits.h>
#include "fake_settings_file.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_app_state.h"
#include "stubs_hexdump.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pebble_tasks.h"
#include "stubs_pbl_malloc.h"
#include "stubs_prompt.h"
#include "stubs_worker_state.h"
status_t pfs_remove(const char *name) {
fake_settings_file_reset();
return S_SUCCESS;
}
status_t blob_db_insert(BlobDBId db_id,
const uint8_t *key, int key_len, const uint8_t *val, int val_len) {
return settings_file_set(NULL, key, key_len, val, val_len);
}
RtcTicks rtc_get_ticks(void) {
return 0;
}
// Fakes
////////////////////////////////////////////////////////////////
static const time_t NOW = 1471269600; // Mon, 15 Aug 2016 14:00:00 GMT
time_t rtc_get_time(void) {
return NOW;
}
bool activity_get_metric(ActivityMetric metric, uint32_t history_len, int32_t *history) {
*history = 0;
return true;
}
int s_metric_updated_count = 0;
void activity_metrics_prv_set_metric(ActivityMetric metric, DayInWeek day, int32_t value) {
printf("s_metric_updated_count: %d\n", s_metric_updated_count);
s_metric_updated_count++;
}
// Setup
////////////////////////////////////////////////////////////////
void test_health_db__initialize(void) {
fake_settings_file_reset();
health_db_init();
s_metric_updated_count = 0;
}
void test_health_db__cleanup(void) {
}
// Dummy Data
////////////////////////////////////////////////////////////////
#define NUM_CURRENT_MOVEMENT_METRICS 5
#define NUM_CURRENT_SLEEP_METRICS 4
#define NUM_CURRENT_HR_ZONE_METRICS 3
typedef enum MovementDataFields {
MD_Version,
MD_Timestamp,
MD_Steps,
MD_ActiveKCalories,
MD_RestingKCalories,
MD_Distance,
MD_ActiveTime
} MovementDataFields;
static uint32_t s_movement_data[] = {
1, // Version
NOW, // Timestamp
1234, // Steps
1111, // Active K Calories
2222, // Resting K Calories
3333, // Distance
4444, // Active Time
};
static uint32_t s_old_movement_data[] = {
1, // Version
NOW - (7 * SECONDS_PER_DAY),
1234, // Steps
1111, // Active K Calories
2222, // Resting K Calories
3333, // Distance
4444, // Active Time
};
static uint32_t s_future_movement_data[] = {
1, // Version
NOW + SECONDS_PER_DAY,
1234, // Steps
1111, // Active K Calories
2222, // Resting K Calories
3333, // Distance
4444, // Active Time
};
typedef enum SleepDataFields {
SD_Version,
SD_Timestamp,
SD_SleepDuration,
SD_DeepSleepDuration,
SD_FallAsleepTime,
SD_WakeupTime,
SD_TypicalSleepDuration,
SD_TypicalDeepSleepDuration,
SD_TypicalFallAsleepTime,
SD_TypicalWakeupTime,
} SleepDataFields;
static uint32_t s_sleep_data[] = {
1, // Version
NOW, // Timestamp
1234, // Sleep Duration
1111, // Deep Sleep Duration
2222, // Fall Asleep Time
3333, // Wakeup Time
4444, // Active Time
5555, // Typical sleep duration
6666, // Typical deep sleep duration
7777, // Typical fall asleep time
8888, // Typical wakeup time
};
static uint32_t s_old_sleep_data[] = {
1, // Version
NOW - (7 * SECONDS_PER_DAY),
1234, // Sleep Duration
1111, // Deep Sleep Duration
2222, // Fall Asleep Time
3333, // Wakeup Time
4444, // Active Time
5555, // Typical sleep duration
6666, // Typical deep sleep duration
7777, // Typical fall asleep time
8888, // Typical wakeup time
};
static uint32_t s_invalid_sleep_data[] = {
5, // Version
NOW, // Timestamp
1234, // Sleep Duration
1111, // Deep Sleep Duration
2222, // Fall Asleep Time
3333, // Wakeup Time
4444, // Active Time
5555, // Typical sleep duration
6666, // Typical deep sleep duration
7777, // Typical fall asleep time
8888, // Typical wakeup time
};
static uint32_t s_hr_zone_data[] = {
1, // Version
NOW, // Timestamp
3, // Number of zones
60, // Minutes in zone 1
30, // Minutes in zone 2
15, // Minutes in zone 3
};
// Tests
////////////////////////////////////////////////////////////////
void test_health_db__blob_db_api(void) {
const char *key = "monday_sleepData";
// insert one
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_sleep_data, sizeof(s_sleep_data)),
S_SUCCESS);
// check
int32_t val_out;
cl_assert(health_db_get_typical_value(ActivityMetricSleepTotalSeconds, Monday, &val_out));
cl_assert_equal_i(val_out, s_sleep_data[SD_TypicalSleepDuration]);
// delete
cl_assert_equal_i(health_db_delete((uint8_t *)key, strlen(key)),
S_SUCCESS);
// check
cl_assert(!health_db_get_typical_value(ActivityMetricSleepTotalSeconds, Monday, &val_out));
// insert again
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_sleep_data, sizeof(s_sleep_data)),
S_SUCCESS);
// check
cl_assert(health_db_get_typical_value(ActivityMetricSleepTotalSeconds, Monday, &val_out));
cl_assert_equal_i(val_out, s_sleep_data[SD_TypicalSleepDuration]);
// flush
cl_assert_equal_i(health_db_flush(), S_SUCCESS);
// check
cl_assert(!health_db_get_typical_value(ActivityMetricSleepTotalSeconds, Monday, &val_out));
// insert something with an older version (this will succeed)
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_invalid_sleep_data, sizeof(s_sleep_data)),
S_SUCCESS);
// check
cl_assert(!health_db_get_typical_value(ActivityMetricSleepTotalSeconds, Monday, &val_out));
}
void test_health_db__movement_data(void) {
const char *key = "monday_movementData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_movement_data, sizeof(s_movement_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, NUM_CURRENT_MOVEMENT_METRICS);
// check typicals (not stored)
int32_t val_out;
cl_assert(!health_db_get_typical_value(ActivityMetricStepCount, Monday, &val_out));
cl_assert(!health_db_get_typical_value(ActivityMetricActiveSeconds, Monday, &val_out));
cl_assert(!health_db_get_typical_value(ActivityMetricRestingKCalories, Monday, &val_out));
cl_assert(!health_db_get_typical_value(ActivityMetricActiveKCalories, Monday, &val_out));
cl_assert(!health_db_get_typical_value(ActivityMetricDistanceMeters, Monday, &val_out));
}
void test_health_db__sleep_data(void) {
const char *key = "monday_sleepData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_sleep_data, sizeof(s_sleep_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, NUM_CURRENT_SLEEP_METRICS);
// check typicals
int32_t val_out;
cl_assert(health_db_get_typical_value(ActivityMetricSleepTotalSeconds, Monday, &val_out));
cl_assert_equal_i(val_out, s_sleep_data[SD_TypicalSleepDuration]);
cl_assert(health_db_get_typical_value(ActivityMetricSleepRestfulSeconds, Monday, &val_out));
cl_assert_equal_i(val_out, s_sleep_data[SD_TypicalDeepSleepDuration]);
cl_assert(health_db_get_typical_value(ActivityMetricSleepEnterAtSeconds, Monday, &val_out));
cl_assert_equal_i(val_out, s_sleep_data[SD_TypicalFallAsleepTime]);
cl_assert(health_db_get_typical_value(ActivityMetricSleepExitAtSeconds, Monday, &val_out));
cl_assert_equal_i(val_out, s_sleep_data[SD_TypicalWakeupTime]);
}
void test_health_db__hr_zone_data(void) {
const char *key = "monday_heartRateZoneData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_hr_zone_data, sizeof(s_hr_zone_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, NUM_CURRENT_HR_ZONE_METRICS);
// check typicals (not stored)
int32_t val_out;
cl_assert(!health_db_get_typical_value(ActivityMetricHeartRateZone1Minutes, Monday, &val_out));
cl_assert(!health_db_get_typical_value(ActivityMetricHeartRateZone2Minutes, Monday, &val_out));
cl_assert(!health_db_get_typical_value(ActivityMetricHeartRateZone3Minutes, Monday, &val_out));
}
void test_health_db__step_averages(void) {
const struct {
const char *key;
const char *val;
} entries[] = {
{
.key = "sunday_steps",
.val = "l4tHpFsFGE6UINneFPMnf2lgINlYuXlDS6xh6vizK9jbDen5mHQgWF6E8jOzBVnEdV0j2DNOzONfJbsWoSWH0QoQpPmm1NSW" \
"l4tHpFsFGE6UINneFPMnf2lgINlYuXlDS6xh6vizK9jbDen5mHQgWF6E8jOzBVnEdV0j2DNOzONfJbsWoSWH0QoQpPmm1NSW",
}, {
.key = "monday_steps",
.val = "Rhgc3Q7ajjydH8CA9qxVJH0FpVDjdGwwoKCLE2F55x62EZZ6MCIjUMynVq13U8vOHhaWoygDf0zwOIdAEUOrZRwvJmYVzW7J" \
"Rhgc3Q7ajjydH8CA9qxVJH0FpVDjdGwwoKCLE2F55x62EZZ6MCIjUMynVq13U8vOHhaWoygDf0zwOIdAEUOrZRwvJmYVzW7J",
}, {
.key = "tuesday_steps",
.val = "V6PrBVc4suqCYjLceUl6a1UXYO8qwL5w3WZY00KeGoHAcuST7OxGnMBVCEskty0q4OIdTeyyZOljrGif09kZOFldu3BjJqJO" \
"V6PrBVc4suqCYjLceUl6a1UXYO8qwL5w3WZY00KeGoHAcuST7OxGnMBVCEskty0q4OIdTeyyZOljrGif09kZOFldu3BjJqJO",
}, {
.key = "wednesday_steps",
.val = "wufD6hzhFUrkZkLObfn2dFKUDs0kNNWp6CFiS2XBS3spSFDQUnFLuxWPEq7Dql2HjdkVobMcOA8DiOcanhZvziN6hbteMbg8" \
"wufD6hzhFUrkZkLObfn2dFKUDs0kNNWp6CFiS2XBS3spSFDQUnFLuxWPEq7Dql2HjdkVobMcOA8DiOcanhZvziN6hbteMbg8",
}, {
.key = "thursday_steps",
.val = "FXKAfWwOueL4jLJfZRxzINDITxaThvFIpOrzYfgPVmqbbYoCZKkKkbgyvP1UaCEstr9WjptLszgMocgGSEsqmoipqqWdk7dq" \
"FXKAfWwOueL4jLJfZRxzINDITxaThvFIpOrzYfgPVmqbbYoCZKkKkbgyvP1UaCEstr9WjptLszgMocgGSEsqmoipqqWdk7dq",
}, {
.key = "friday_steps",
.val = "uxFhoWTzJxDOmyBX2g3n7wdoPKxeleBR7iwKGn7utn8qTEj0tB7aw65EEFZ5QldgAkg6lctSmamf2p95l2CpHXNgVL22hQFx" \
"uxFhoWTzJxDOmyBX2g3n7wdoPKxeleBR7iwKGn7utn8qTEj0tB7aw65EEFZ5QldgAkg6lctSmamf2p95l2CpHXNgVL22hQFx",
}, {
.key = "saturday_steps",
.val = "SSxw7WtwGnhobAOXwqbvGDDwElpRG6cll8CwM9Wysh01Mj0aFWxEVN0z5w7yQHt8bwiWVabrMeUUAek2J5zCoXiGIkav4cW8" \
"SSxw7WtwGnhobAOXwqbvGDDwElpRG6cll8CwM9Wysh01Mj0aFWxEVN0z5w7yQHt8bwiWVabrMeUUAek2J5zCoXiGIkav4cW8",
},
};
for (int i = 0; i < ARRAY_LENGTH(entries); ++i) {
cl_assert_equal_i(health_db_insert((uint8_t *)entries[i].key,
strlen(entries[i].key),
(uint8_t *)entries[i].val,
strlen(entries[i].val)),
S_SUCCESS);
ActivityMetricAverages averages;
health_db_get_typical_step_averages(i, &averages);
int idx = i * 10;
uint16_t val_expected = ((uint16_t *)entries[i].val)[idx];
cl_assert_equal_i(averages.average[idx], val_expected);
}
}
void test_health_db__monthly_averages(void) {
int32_t val_out;
const char *average_step_key = "average_dailySteps";
int32_t average_steps_val = 123456;
cl_assert_equal_i(health_db_insert((uint8_t *)average_step_key, strlen(average_step_key),
(uint8_t *)&average_steps_val, sizeof(average_steps_val)),
S_SUCCESS);
cl_assert(health_db_get_monthly_average_value(ActivityMetricStepCount, &val_out));
cl_assert_equal_i(val_out, average_steps_val);
const char *average_sleep_key = "average_sleepDuration";
int32_t average_sleep_val = 654321;
cl_assert_equal_i(health_db_insert((uint8_t *)average_sleep_key, strlen(average_sleep_key),
(uint8_t *)&average_sleep_val, sizeof(average_sleep_val)),
S_SUCCESS);
cl_assert(health_db_get_monthly_average_value(ActivityMetricSleepTotalSeconds, &val_out));
cl_assert_equal_i(val_out, average_sleep_val);
}
void test_health_db__notify_listeners(void) {
char *key;
s_metric_updated_count = 0;
key = "tuesday_sleepData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_sleep_data, sizeof(s_sleep_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, NUM_CURRENT_SLEEP_METRICS);
s_metric_updated_count = 0;
key = "wednesday_movementData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_movement_data, sizeof(s_movement_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, NUM_CURRENT_MOVEMENT_METRICS);
s_metric_updated_count = 0;
key = "thursday_sleepData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_sleep_data, sizeof(s_sleep_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, NUM_CURRENT_SLEEP_METRICS);
s_metric_updated_count = 0;
key = "friday_movementData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_movement_data, sizeof(s_movement_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, NUM_CURRENT_MOVEMENT_METRICS);
s_metric_updated_count = 0;
key = "saturday_sleepData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_sleep_data, sizeof(s_sleep_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, NUM_CURRENT_SLEEP_METRICS);
s_metric_updated_count = 0;
key = "sunday_movementData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_movement_data, sizeof(s_movement_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, NUM_CURRENT_MOVEMENT_METRICS);
s_metric_updated_count = 0;
key = "monday_movementData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_movement_data, sizeof(s_movement_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, NUM_CURRENT_MOVEMENT_METRICS);
s_metric_updated_count = 0;
key = "monday_sleepData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_sleep_data, sizeof(s_sleep_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, NUM_CURRENT_SLEEP_METRICS);
// Test inserting something which is more than a week old.
// We shouldn't update our internal storage
s_metric_updated_count = 0;
key = "monday_movementData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_old_movement_data, sizeof(s_old_movement_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, 0);
s_metric_updated_count = 0;
key = "monday_sleepData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_old_sleep_data, sizeof(s_old_sleep_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, 0);
// Insert something which has a future timestamp
s_metric_updated_count = 0;
key = "monday_movementData";
cl_assert_equal_i(health_db_insert((uint8_t *)key, strlen(key),
(uint8_t *)s_future_movement_data, sizeof(s_movement_data)),
S_SUCCESS);
cl_assert_equal_i(s_metric_updated_count, 0);
}

View file

@ -0,0 +1,240 @@
/*
* 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 "services/normal/blob_db/ios_notif_pref_db.h"
#include "util/size.h"
// Fixture
////////////////////////////////////////////////////////////////
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_system_task.h"
#include "fake_settings_file.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_layout_layer.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_rand_ptr.h"
#include "stubs_pfs.h"
#include "stubs_blob_db_sync.h"
#include "stubs_prompt.h"
extern const char *iOS_NOTIF_PREF_DB_FILE_NAME;
extern const int iOS_NOTIF_PREF_MAX_SIZE;
//! Data from iOS notif pref INSERT
//! 00 00 00 00 00 01 02 0d 03 01 05 00 52 65 70 6c ........ ....Repl
//! 79 08 71 00 4f 6b 00 59 65 73 00 4e 6f 00 43 61 y.q.Ok.Y es.No.Ca
//! 6c 6c 20 6d 65 00 43 61 6c 6c 20 79 6f 75 20 6c ll me.Ca ll you l
//! 61 74 65 72 00 54 68 61 6e 6b 20 79 6f 75 00 53 ater.Tha nk you.S
//! 65 65 20 79 6f 75 20 73 6f 6f 6e 00 52 75 6e 6e ee you s oon.Runn
//! 69 6e 67 20 6c 61 74 65 00 4f 6e 20 6d 79 20 77 ing late .On my w
//! 61 79 00 42 75 73 79 20 72 69 67 68 74 20 6e 6f ay.Busy right no
//! 77 20 2d 20 67 69 76 65 20 6d 65 20 61 20 73 65 w - give me a se
//! 63 6f 6e 64 3f 21 01 00 00 cond?!.. .
static const uint8_t s_ios_pref_db_insert_dict[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0d, 0x03, 0x01, 0x05, 0x00, 0x52, 0x65, 0x70, 0x6c,
0x79, 0x08, 0x71, 0x00, 0x4f, 0x6b, 0x00, 0x59, 0x65, 0x73, 0x00, 0x4e, 0x6f, 0x00, 0x43, 0x61,
0x6c, 0x6c, 0x20, 0x6d, 0x65, 0x00, 0x43, 0x61, 0x6c, 0x6c, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6c,
0x61, 0x74, 0x65, 0x72, 0x00, 0x54, 0x68, 0x61, 0x6e, 0x6b, 0x20, 0x79, 0x6f, 0x75, 0x00, 0x53,
0x65, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x73, 0x6f, 0x6f, 0x6e, 0x00, 0x52, 0x75, 0x6e, 0x6e,
0x69, 0x6e, 0x67, 0x20, 0x6c, 0x61, 0x74, 0x65, 0x00, 0x4f, 0x6e, 0x20, 0x6d, 0x79, 0x20, 0x77,
0x61, 0x79, 0x00, 0x42, 0x75, 0x73, 0x79, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x6e, 0x6f,
0x77, 0x20, 0x2d, 0x20, 0x67, 0x69, 0x76, 0x65, 0x20, 0x6d, 0x65, 0x20, 0x61, 0x20, 0x73, 0x65,
0x63, 0x6f, 0x6e, 0x64, 0x3f, 0x21, 0x01, 0x00, 0x00
};
const uint8_t key[] = { 0x01, 0x02, 0x03 };
void test_ios_notif_pref_db__initialize(void) {
}
void test_ios_notif_pref_db__cleanup(void) {
fake_settings_file_reset();
}
void test_ios_notif_pref_db__insert_inverts_flags(void) {
// Make a local copy of dict since ios_notif_pref_db_insert() modifies val
uint8_t val[sizeof(s_ios_pref_db_insert_dict)];
memcpy(val, s_ios_pref_db_insert_dict, sizeof(s_ios_pref_db_insert_dict));
ios_notif_pref_db_insert(key, sizeof(key), val, sizeof(val));
SettingsFile file;
settings_file_open(&file, iOS_NOTIF_PREF_DB_FILE_NAME, iOS_NOTIF_PREF_MAX_SIZE);
const unsigned prefs_len = settings_file_get_len(&file, key, sizeof(key));
void *prefs_out = kernel_zalloc(prefs_len);
settings_file_get(&file, key, sizeof(key), prefs_out, prefs_len);
settings_file_close(&file);
uint32_t flags = *((uint32_t *) prefs_out);
cl_assert_equal_i(flags, ~0);
kernel_free(prefs_out);
}
void test_ios_notif_pref_db__read_flags(void) {
// Make a local copy of dict since ios_notif_pref_db_insert() modifies val
uint8_t val[sizeof(s_ios_pref_db_insert_dict)];
memcpy(val, s_ios_pref_db_insert_dict, sizeof(s_ios_pref_db_insert_dict));
ios_notif_pref_db_insert(key, sizeof(key), val, sizeof(val));
uint32_t flags = ios_notif_pref_db_get_flags(key, sizeof(key));
cl_assert_equal_i(flags, 0);
}
void test_ios_notif_pref_db__store_prefs(void) {
// Create an attribute list and action group
AttributeList attr_list;
attribute_list_init_list(0, &attr_list);
attribute_list_add_cstring(&attr_list, AttributeIdShortTitle, "Title");
attribute_list_add_uint8(&attr_list, AttributeIdMuteDayOfWeek, 0x1F);
attribute_list_add_cstring(&attr_list, AttributeIdAppName, "GMail");
TimelineItemActionGroup action_group = {
.num_actions = 0,
};
// Store them in the DB
char *key = "key1";
int key_len = strlen(key);
ios_notif_pref_db_store_prefs((uint8_t *)key, key_len, &attr_list, &action_group);
// Make sure we can get the data back
iOSNotifPrefs *notif_prefs = ios_notif_pref_db_get_prefs((uint8_t *)key, key_len);
cl_assert(notif_prefs);
Attribute *title = attribute_find(&notif_prefs->attr_list, AttributeIdShortTitle);
cl_assert(title);
cl_assert_equal_s(title->cstring, "Title");
Attribute *mute = attribute_find(&notif_prefs->attr_list, AttributeIdMuteDayOfWeek);
cl_assert(mute);
cl_assert_equal_i(mute->uint8, 0x1F);
Attribute *name = attribute_find(&notif_prefs->attr_list, AttributeIdAppName);
cl_assert(name);
cl_assert_equal_s(name->cstring, "GMail");
// Update the current entry with a new attribute
attribute_list_add_uint32(&attr_list, AttributeIdLastUpdated, 123456);
ios_notif_pref_db_store_prefs((uint8_t *)key, key_len, &attr_list, &action_group);
// Make sure we can get all the data back
notif_prefs = ios_notif_pref_db_get_prefs((uint8_t *)key, key_len);
cl_assert(notif_prefs);
title = attribute_find(&notif_prefs->attr_list, AttributeIdShortTitle);
cl_assert(title);
cl_assert_equal_s(title->cstring, "Title");
mute = attribute_find(&notif_prefs->attr_list, AttributeIdMuteDayOfWeek);
cl_assert(mute);
cl_assert_equal_i(mute->uint8, 0x1F);
name = attribute_find(&notif_prefs->attr_list, AttributeIdAppName);
cl_assert(name);
cl_assert_equal_s(name->cstring, "GMail");
Attribute *updated = attribute_find(&notif_prefs->attr_list, AttributeIdLastUpdated);
cl_assert(updated);
cl_assert_equal_i(updated->uint32, 123456);
attribute_list_destroy_list(&attr_list);
ios_notif_pref_db_free_prefs(notif_prefs);
}
void test_ios_notif_pref_db__store_empty_prefs(void) {
// Store empty prefs
char *key = "key1";
int key_len = strlen(key);
ios_notif_pref_db_store_prefs((uint8_t *)key, key_len, NULL, NULL);
// Read them back
iOSNotifPrefs *notif_prefs = ios_notif_pref_db_get_prefs((uint8_t *)key, key_len);
cl_assert(notif_prefs);
cl_assert_equal_i(notif_prefs->attr_list.num_attributes, 0);
cl_assert_equal_i(notif_prefs->action_group.num_actions, 0);
ios_notif_pref_db_free_prefs(notif_prefs);
}
void test_ios_notif_pref_db__is_dirty_insert_from_phone(void) {
char *keys[] = { "key1", "key2", "key3", "key4", "key5" };
int key_len = strlen(keys[0]);
char *values[] = { "val1", "val2", "val3", "val4", "val5" };
int value_len = strlen(values[0]);
// Insert a bunch of known apps "from the phone"
// They should NOT be dirty (the phone is the source of truth)
for (int i = 0; i < ARRAY_LENGTH(keys); ++i) {
ios_notif_pref_db_insert((uint8_t *)keys[i], key_len, (uint8_t *)values[i], value_len);
}
bool is_dirty = true;
cl_assert_equal_i(ios_notif_pref_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(!is_dirty);
BlobDBDirtyItem *dirty_list = ios_notif_pref_db_get_dirty_list();
cl_assert(!dirty_list);
}
void test_ios_notif_pref_db__is_dirty_insert_locally(void) {
char *keys[] = { "key1", "key2", "key3", "key4", "key5" };
int key_len = strlen(keys[0]);
// Insert a bunch of known apps "from the watch"
// These should be dirty (the phone is the source of truth)
for (int i = 0; i < ARRAY_LENGTH(keys); ++i) {
ios_notif_pref_db_store_prefs((uint8_t *)keys[i], key_len, NULL, NULL);
}
bool is_dirty = false;
cl_assert_equal_i(ios_notif_pref_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(is_dirty);
BlobDBDirtyItem *dirty_list = ios_notif_pref_db_get_dirty_list();
cl_assert(dirty_list);
cl_assert(list_count((ListNode *)dirty_list) == ARRAY_LENGTH(keys));
// Mark some items as synced
ios_notif_pref_db_mark_synced((uint8_t *)keys[0], key_len);
ios_notif_pref_db_mark_synced((uint8_t *)keys[1], key_len);
ios_notif_pref_db_mark_synced((uint8_t *)keys[2], key_len);
// We should now only have 2 dirty items
cl_assert_equal_i(ios_notif_pref_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(is_dirty);
dirty_list = ios_notif_pref_db_get_dirty_list();
cl_assert(dirty_list);
cl_assert_equal_i(list_count((ListNode *)dirty_list), 2);
// Mark the final 2 items as synced
ios_notif_pref_db_mark_synced((uint8_t *)keys[3], key_len);
ios_notif_pref_db_mark_synced((uint8_t *)keys[4], key_len);
// And nothing should be dirty
cl_assert_equal_i(ios_notif_pref_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(!is_dirty);
dirty_list = ios_notif_pref_db_get_dirty_list();
cl_assert(!dirty_list);
}

View file

@ -0,0 +1,123 @@
/*
* 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 "util/uuid.h"
#include "services/normal/filesystem/pfs.h"
#include "services/normal/blob_db/notif_db.h"
#include "services/normal/notifications/notification_storage.h"
// Fixture
////////////////////////////////////////////////////////////////
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_spi_flash.h"
#include "fake_system_task.h"
#include "fake_kernel_services_notifications.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_hexdump.h"
#include "stubs_layout_layer.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_sleep.h"
#include "stubs_task_watchdog.h"
void test_notif_db__initialize(void) {
fake_spi_flash_init(0, 0x1000000);
pfs_init(false);
notification_storage_reset();
}
void test_notif_db__cleanup(void) {
}
void test_notif_db__get_length(void) {
SerializedTimelineItemHeader hdr = {
.common = {
.ancs_uid = 1,
.layout = 0,
.flags = 0,
.timestamp = 0,
},
};
uuid_generate(&hdr.common.id);
cl_assert_equal_i(notif_db_insert((uint8_t *)&hdr, UUID_SIZE, (uint8_t *)&hdr, sizeof(hdr)), 0);
cl_assert_equal_i(notif_db_get_len((uint8_t *)&hdr, UUID_SIZE), sizeof(hdr));
}
void test_notif_db__insert_remove(void) {
SerializedTimelineItemHeader hdr = {
.common = {
.ancs_uid = 1,
.layout = 0,
.flags = 0,
.timestamp = 0,
},
};
uuid_generate(&hdr.common.id);
cl_assert_equal_i(notif_db_insert((uint8_t *)&hdr, UUID_SIZE, (uint8_t *)&hdr, sizeof(hdr)), 0);
cl_assert_equal_i(notif_db_delete((uint8_t *)&hdr, UUID_SIZE), 0);
cl_assert_equal_i(notif_db_get_len((uint8_t *)&hdr, UUID_SIZE), 0);
}
void test_notif_db__flush(void) {
SerializedTimelineItemHeader hdr1 = {
.common = {
.ancs_uid = 1,
.layout = 0,
.flags = 0,
.timestamp = 0,
},
};
uuid_generate(&hdr1.common.id);
SerializedTimelineItemHeader hdr2 = {
.common = {
.ancs_uid = 1,
.layout = 0,
.flags = 0,
.timestamp = 0,
},
};
uuid_generate(&hdr2.common.id);
SerializedTimelineItemHeader hdr3 = {
.common = {
.ancs_uid = 1,
.layout = 0,
.flags = 0,
.timestamp = 0,
},
};
uuid_generate(&hdr3.common.id);
cl_assert_equal_i(notif_db_insert((uint8_t *)&hdr1, UUID_SIZE, (uint8_t *)&hdr1, sizeof(hdr1)), 0);
cl_assert_equal_i(notif_db_insert((uint8_t *)&hdr2, UUID_SIZE, (uint8_t *)&hdr2, sizeof(hdr2)), 0);
cl_assert_equal_i(notif_db_insert((uint8_t *)&hdr3, UUID_SIZE, (uint8_t *)&hdr3, sizeof(hdr3)), 0);
cl_assert_equal_i(notif_db_flush(), 0);
fake_system_task_callbacks_invoke_pending();
cl_assert_equal_i(notif_db_get_len((uint8_t *)&hdr1, UUID_SIZE), 0);
cl_assert_equal_i(notif_db_get_len((uint8_t *)&hdr2, UUID_SIZE), 0);
cl_assert_equal_i(notif_db_get_len((uint8_t *)&hdr3, UUID_SIZE), 0);
}

View file

@ -0,0 +1,200 @@
/*
* 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 "services/normal/blob_db/pin_db.h"
#include "services/normal/timeline/timeline.h"
// Fixture
////////////////////////////////////////////////////////////////
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_settings_file.h"
#include "fake_system_task.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_app_cache.h"
#include "stubs_app_install_manager.h"
#include "stubs_blob_db.h"
#include "stubs_blob_db_sync.h"
#include "stubs_bt_lock.h"
#include "stubs_events.h"
#include "stubs_hexdump.h"
#include "stubs_layout_layer.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_regular_timer.h"
#include "stubs_reminder_db.h"
#include "stubs_sleep.h"
#include "stubs_task_watchdog.h"
const char *timeline_get_private_data_source(Uuid *parent_id) {
return NULL;
}
static TimelineItem item1 = {
.header = {
.id = {0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4},
.parent_id = {0xff, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0x01},
.timestamp = 1,
.duration = 0,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
// don't care about the rest
}
};
static TimelineItem item2 = {
.header = {
.id = {0x55, 0xcb, 0x7c, 0x75, 0x8a, 0x35, 0x44, 0x87,
0x90, 0xa4, 0x91, 0x3f, 0x1f, 0xa6, 0x76, 0x01},
.parent_id = {0xff, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0x01},
.timestamp = 3,
.duration = 0,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
},
};
static TimelineItem item3 = {
.header = {
.id = {0x7c, 0x65, 0x2e, 0xb9, 0x26, 0xd6, 0x44, 0x2c,
0x98, 0x68, 0xa4, 0x36, 0x79, 0x7d, 0xe2, 0x05},
.parent_id = {0xff, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0x02},
.timestamp = 4,
.duration = 0,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
}
};
static TimelineItem item4 = {
.header = {
.id = {0x8c, 0x65, 0x2e, 0xb9, 0x26, 0xd6, 0x44, 0x2c,
0x98, 0x68, 0xa4, 0x36, 0x79, 0x7d, 0xe2, 0x05},
.parent_id = {0xff, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0x03},
.timestamp = 4,
.duration = 0,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
}
};
static TimelineItem reminder_app_item = {
.header = {
.id = {0x9c, 0x65, 0x2e, 0xb9, 0x26, 0xd6, 0x44, 0x2c,
0x98, 0x68, 0xa4, 0x36, 0x79, 0x7d, 0xe2, 0x05},
.parent_id = UUID_REMINDERS_DATA_SOURCE,
.timestamp = 4,
.duration = 0,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
}
};
// Setup
////////////////////////////////////////////////////////////////
void test_pin_db__initialize(void) {
pin_db_init();
}
void test_pin_db__cleanup(void) {
pin_db_deinit();
fake_settings_file_reset();
}
// Tests
////////////////////////////////////////////////////////////////
void test_pin_db__is_dirty_insert_from_phone(void) {
// Insert a bunch of pins "from the phone"
// They should NOT be dirty (the phone is the source of truth)
pin_db_insert((uint8_t *)&item1.header.id, sizeof(TimelineItemId),
(uint8_t *)&item1, sizeof(TimelineItem));
pin_db_insert((uint8_t *)&item2.header.id, sizeof(TimelineItemId),
(uint8_t *)&item2, sizeof(TimelineItem));
pin_db_insert((uint8_t *)&item3.header.id, sizeof(TimelineItemId),
(uint8_t *)&item3, sizeof(TimelineItem));
pin_db_insert((uint8_t *)&item4.header.id, sizeof(TimelineItemId),
(uint8_t *)&item4, sizeof(TimelineItem));
bool is_dirty = true;
cl_assert_equal_i(pin_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(!is_dirty);
BlobDBDirtyItem *dirty_list = pin_db_get_dirty_list();
cl_assert(!dirty_list);
}
void test_pin_db__is_dirty_insert_locally(void) {
// Insert a bunch of pins "from the watch"
// These should not be dirty because they are not from the reminders app
pin_db_insert_item(&item1);
pin_db_insert_item(&item2);
pin_db_insert_item(&item3);
pin_db_insert_item(&item4);
bool is_dirty = true;
cl_assert_equal_i(pin_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(!is_dirty);
BlobDBDirtyItem *dirty_list = pin_db_get_dirty_list();
cl_assert(!dirty_list);
pin_db_insert_item(&reminder_app_item);
cl_assert_equal_i(pin_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(is_dirty);
dirty_list = pin_db_get_dirty_list();
cl_assert(dirty_list);
cl_assert(list_count((ListNode *)dirty_list) == 1);
// Mark the reminder item as synced
pin_db_mark_synced((uint8_t *)&reminder_app_item.header.id, sizeof(TimelineItemId));
// And nothing should be dirty
cl_assert_equal_i(pin_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(!is_dirty);
dirty_list = pin_db_get_dirty_list();
cl_assert(!dirty_list);
}
void test_pin_db__set_status_bits(void) {
pin_db_insert_item(&item1);
TimelineItem item;
cl_must_pass(pin_db_read_item_header(&item, &item1.header.id));
cl_assert_equal_i(item.header.status, 0);
cl_must_pass(pin_db_set_status_bits(&item1.header.id, TimelineItemStatusDismissed));
cl_must_pass(pin_db_read_item_header(&item, &item1.header.id));
cl_assert_equal_i(item.header.status, TimelineItemStatusDismissed);
}

View file

@ -0,0 +1,141 @@
/*
* 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 "util/uuid.h"
#include "services/normal/filesystem/pfs.h"
#include "services/normal/blob_db/prefs_db.h"
#include "shell/prefs.h"
#include "shell/prefs_private.h"
// Fixture
////////////////////////////////////////////////////////////////
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_spi_flash.h"
#include "fake_system_task.h"
#include "fake_kernel_services_notifications.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_app_install_manager.h"
#include "stubs_hexdump.h"
#include "stubs_logging.h"
#include "stubs_mfg_info.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_sleep.h"
#include "stubs_system_theme.h"
#include "stubs_task_watchdog.h"
void i18n_enable(bool enable) {
}
void display_set_offset(GPoint offset) {
}
void test_prefs_db__initialize(void) {
fake_spi_flash_init(0, 0x1000000);
pfs_init(false);
}
void test_prefs_db__cleanup(void) {
}
void test_prefs_db__get_length(void) {
Uuid uuid = {0, 1, 2, 3};
const char *key = "workerId";
int key_len = strlen(key);
cl_assert_equal_i(prefs_db_insert((uint8_t *)key, key_len, (uint8_t *)&uuid, sizeof(uuid)), 0);
cl_assert_equal_i(prefs_db_get_len((uint8_t *)key, key_len), sizeof(uuid));
}
void test_prefs_db__insert_and_read(void) {
uint32_t set_value = 42;
// NOTE: We intentionally put one garbage character after the key to catch errors
// that assume the key is 0 terminated
const char *key = "lightTimeoutMsX";
int key_len = strlen(key) - 1;
// Set initial value
backlight_set_timeout_ms(set_value + 1);
// Insert and check the length
cl_assert_equal_i(prefs_db_insert((uint8_t *)key, key_len, (uint8_t *)&set_value,
sizeof(set_value)), 0);
cl_assert_equal_i(prefs_db_get_len((uint8_t *)key, key_len), sizeof(set_value));
// Read it back
uint32_t get_value;
cl_assert_equal_i(prefs_db_read((uint8_t *)key, key_len, (uint8_t *)&get_value,
sizeof(get_value)), 0);
cl_assert_equal_i(set_value, get_value);
// If we get the pref setting now, it should still be the old value because we haven't
// issued the blob_db update event yet
uint32_t get_pref = backlight_get_timeout_ms();
cl_assert_equal_i(get_pref, set_value + 1);
// Issue the blob_db update event
PebbleBlobDBEvent event = (PebbleBlobDBEvent) {
.db_id = BlobDBIdPrefs,
.type = BlobDBEventTypeInsert,
.key = (uint8_t *)key,
.key_len = key_len,
};
prefs_private_handle_blob_db_event(&event);
get_pref = backlight_get_timeout_ms();
cl_assert(get_pref == get_value);
// Set new value using the set call and read it back using prefs_db
uint32_t new_set_value = 4242;
backlight_set_timeout_ms(new_set_value);
cl_assert_equal_i(prefs_db_read((uint8_t *)key, key_len, (uint8_t *)&get_value,
sizeof(get_value)), 0);
cl_assert_equal_i(new_set_value, get_value);
// Try and insert an unknown key. It should fail
const char *bad_key = "bad_key";
int bad_key_len = strlen(bad_key);
cl_assert(prefs_db_insert((uint8_t *)bad_key, bad_key_len, (uint8_t *)&set_value,
sizeof(set_value)) < 0);
cl_assert(prefs_db_get_len((uint8_t *)bad_key, bad_key_len) < 0);
cl_assert(prefs_db_read((uint8_t *)bad_key, bad_key_len, (uint8_t *)&get_value,
sizeof(get_value)) < 0);
// Try and insert the wrong size for a known key, it should fail
cl_assert(prefs_db_insert((uint8_t *)key, key_len, (uint8_t *)&set_value,
sizeof(set_value) + 1) < 0);
// Read it back
cl_assert(prefs_db_read((uint8_t *)key, key_len, (uint8_t *)&get_value,
sizeof(get_value) + 1) < 0);
}

View file

@ -0,0 +1,390 @@
/*
* 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 "services/normal/blob_db/reminder_db.h"
// Fixture
////////////////////////////////////////////////////////////////
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_settings_file.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_blob_db_sync.h"
#include "stubs_hexdump.h"
#include "stubs_layout_layer.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_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_regular_timer.h"
#include "stubs_reminders.h"
#include "stubs_sleep.h"
#include "stubs_task_watchdog.h"
void reminders_handle_reminder_removed(const Uuid *reminder_id) {
}
static TimelineItem item1 = {
.header = {
.id = {0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4},
.parent_id = {0xff, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0x01},
.timestamp = 1,
.duration = 0,
.type = TimelineItemTypeReminder,
.layout = LayoutIdTest,
// don't care about the rest
}
};
static TimelineItem item2 = {
.header = {
.id = {0x55, 0xcb, 0x7c, 0x75, 0x8a, 0x35, 0x44, 0x87,
0x90, 0xa4, 0x91, 0x3f, 0x1f, 0xa6, 0x76, 0x01},
.parent_id = {0xff, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0x01},
.timestamp = 3,
.duration = 0,
.type = TimelineItemTypeReminder,
.layout = LayoutIdTest,
},
};
static TimelineItem item3 = {
.header = {
.id = {0x7c, 0x65, 0x2e, 0xb9, 0x26, 0xd6, 0x44, 0x2c,
0x98, 0x68, 0xa4, 0x36, 0x79, 0x7d, 0xe2, 0x05},
.parent_id = {0xff, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0x02},
.timestamp = 4,
.duration = 0,
.type = TimelineItemTypeReminder,
.layout = LayoutIdTest,
}
};
static TimelineItem item4 = {
.header = {
.id = {0x8c, 0x65, 0x2e, 0xb9, 0x26, 0xd6, 0x44, 0x2c,
0x98, 0x68, 0xa4, 0x36, 0x79, 0x7d, 0xe2, 0x05},
.parent_id = {0xff, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0x03},
.timestamp = 4,
.duration = 0,
.type = TimelineItemTypeReminder,
.layout = LayoutIdTest,
}
};
static SerializedTimelineItemHeader bad_item = {
.common = {
.id = {0x8c, 0x65, 0x2e, 0xb9, 0x26, 0xd6, 0x42, 0x2c,
0x98, 0x68, 0xa4, 0x36, 0x79, 0x7d, 0xe2, 0x05},
.timestamp = 3,
.duration = 0,
.type = TimelineItemTypeReminder,
.layout = LayoutIdTest,
},
.num_attributes = 3,
};
static TimelineItem title_item1 = {
.header = {
.id = {0x9c, 0x65, 0x2e, 0xb9, 0x26, 0xd6, 0x44, 0x2c,
0x98, 0x68, 0xa4, 0x36, 0x79, 0x7d, 0xe2, 0x05},
.timestamp = 1,
.duration = 0,
.type = TimelineItemTypeReminder,
.layout = LayoutIdTest,
},
.attr_list = (AttributeList) {
.num_attributes = 1,
.attributes = (Attribute[1]) {{ .id = AttributeIdTitle, .cstring = "test 1" }}
}
};
static TimelineItem title_item2 = {
.header = {
.id = {0xac, 0x65, 0x2e, 0xb9, 0x26, 0xd6, 0x44, 0x2c,
0x98, 0x68, 0xa4, 0x36, 0x79, 0x7d, 0xe2, 0x05},
.timestamp = 1,
.duration = 0,
.type = TimelineItemTypeReminder,
.layout = LayoutIdTest,
},
.attr_list = (AttributeList) {
.num_attributes = 1,
.attributes = (Attribute[1]) {{ .id = AttributeIdTitle, .cstring = "test 2" }}
}
};
static void prv_insert_default_reminders(void) {
// add all four explicitly out of order
cl_assert_equal_i(S_SUCCESS, reminder_db_insert_item(&item4));
cl_assert_equal_i(S_SUCCESS, reminder_db_insert_item(&item2));
cl_assert_equal_i(S_SUCCESS, reminder_db_insert_item(&item1));
cl_assert_equal_i(S_SUCCESS, reminder_db_insert_item(&item3));
}
// Setup
////////////////////////////////////////////////////////////////
void test_reminder_db__initialize(void) {
reminder_db_init();
}
void test_reminder_db__cleanup(void) {
reminder_db_deinit();
fake_settings_file_reset();
}
// Tests
////////////////////////////////////////////////////////////////
void test_reminder_db__basic_test(void) {
prv_insert_default_reminders();
// confirm all three are there
cl_assert(reminder_db_get_len((uint8_t*)&item1.header.id, sizeof(Uuid)) > 0);
cl_assert(reminder_db_get_len((uint8_t*)&item2.header.id, sizeof(Uuid)) > 0);
cl_assert(reminder_db_get_len((uint8_t*)&item3.header.id, sizeof(Uuid)) > 0);
// remove #1 and confirm it's deleted
cl_assert(S_SUCCESS == reminder_db_delete((uint8_t*)&item1.header.id, sizeof(Uuid)));
cl_assert(reminder_db_get_len((uint8_t *)&item1.header.id, sizeof(Uuid)) == 0);
// add 1 back so it's clean
cl_assert(S_SUCCESS == reminder_db_insert_item(&item1));
TimelineItem temp = {{{0}}};
cl_assert(S_SUCCESS == reminder_db_read((uint8_t*)&item1.header.id, sizeof(Uuid), (uint8_t*)&temp,
sizeof(CommonTimelineItemHeader)));
// Note: we set things to null because it makes it easier to compare two
// TimelineItems with memcmp
// check item 1
memset(&temp, 0, sizeof(TimelineItem));
cl_assert(S_SUCCESS == reminder_db_next_item_header(&temp));
cl_assert(uuid_equal(&item1.header.id, &temp.header.id));
temp.attr_list.attributes = NULL;
cl_assert(memcmp(&item1, &temp, sizeof(TimelineItem)) == 0);
cl_assert(S_SUCCESS == reminder_db_delete_item(&temp.header.id, true /* send_event */));
cl_assert(reminder_db_get_len((uint8_t *)&item1.header.id, sizeof(Uuid)) == 0);
// check item 2
memset(&temp, 0, sizeof(TimelineItem));
cl_assert(S_SUCCESS == reminder_db_next_item_header(&temp));
cl_assert(uuid_equal(&item2.header.id, &temp.header.id));
temp.attr_list.attributes = NULL;
cl_assert(memcmp(&item2, &temp, sizeof(TimelineItem)) == 0);
cl_assert(S_SUCCESS == reminder_db_delete_item(&temp.header.id, true /* send_event */));
cl_assert(reminder_db_get_len((uint8_t *)&item2.header.id, sizeof(Uuid)) == 0);
// check item 3 or 4
memset(&temp, 0, sizeof(TimelineItem));
cl_assert(S_SUCCESS == reminder_db_next_item_header(&temp));
if (uuid_equal(&item3.header.id, &temp.header.id)) {
temp.attr_list.attributes = NULL;
timeline_item_free_allocated_buffer(&temp);
cl_assert(memcmp(&item3, &temp, sizeof(TimelineItem)) == 0);
cl_assert(S_SUCCESS == reminder_db_delete_item(&temp.header.id, true /* send_event */));
cl_assert(reminder_db_get_len((uint8_t *) &item3, sizeof(Uuid)) == 0);
memset(&temp, 0, sizeof(TimelineItem));
cl_assert(S_SUCCESS == reminder_db_next_item_header(&temp));
temp.attr_list.attributes = NULL;
cl_assert(memcmp(&item4, &temp, sizeof(TimelineItem)) == 0);
cl_assert(S_SUCCESS == reminder_db_delete_item(&temp.header.id, true /* send_event */));
cl_assert(reminder_db_get_len((uint8_t *)&item4.header.id, sizeof(Uuid)) == 0);
} else {
temp.attr_list.attributes = NULL;
cl_assert(memcmp(&item4, &temp, sizeof(TimelineItem)) == 0);
cl_assert(S_SUCCESS == reminder_db_delete_item(&temp.header.id, true /* send_event */));
cl_assert(reminder_db_get_len((uint8_t *) &item4, sizeof(Uuid)) == 0);
memset(&temp, 0, sizeof(TimelineItem));
cl_assert(S_SUCCESS == reminder_db_next_item_header(&temp));
temp.attr_list.attributes = NULL;
cl_assert(memcmp(&item3, &temp, sizeof(TimelineItem)) == 0);
cl_assert(S_SUCCESS == reminder_db_delete_item(&temp.header.id, true /* send_event */));
cl_assert(reminder_db_get_len((uint8_t *)&item3.header.id, sizeof(Uuid)) == 0);
}
cl_assert(S_NO_MORE_ITEMS == reminder_db_next_item_header(&temp));
}
void test_reminder_db__size_test(void) {
prv_insert_default_reminders();
cl_assert(sizeof(SerializedTimelineItemHeader) == reminder_db_get_len((uint8_t*) &item1.header.id, sizeof(TimelineItemId)));
cl_assert(sizeof(SerializedTimelineItemHeader) == reminder_db_get_len((uint8_t*) &item2.header.id, sizeof(TimelineItemId)));
cl_assert(sizeof(SerializedTimelineItemHeader) == reminder_db_get_len((uint8_t*) &item3.header.id, sizeof(TimelineItemId)));
}
void test_reminder_db__wrong_type_test(void) {
TimelineItem not_a_reminder = {
.header = {
.id = {0x99, 0xcb, 0x7c, 0x75, 0x8a, 0x35, 0x44, 0x87,
0x90, 0xa4, 0x91, 0x3f, 0x1f, 0xa6, 0x76, 0x01},
.timestamp = 0,
.duration = 0,
.type = TimelineItemTypeNotification
}
};
cl_assert(E_INVALID_ARGUMENT == reminder_db_insert_item(&not_a_reminder));
}
void test_reminder_db__delete_parent(void) {
prv_insert_default_reminders();
const TimelineItemId *parent_id = &item1.header.parent_id;
// cnfirm the two are here
cl_assert(reminder_db_get_len((uint8_t *)&item1.header.id, sizeof(Uuid)) > 0);
cl_assert(reminder_db_get_len((uint8_t *)&item2.header.id, sizeof(Uuid)) > 0);
// remove the two that share a parent
cl_assert_equal_i(reminder_db_delete_with_parent(parent_id), S_SUCCESS);
// confirm the two are gone
cl_assert(reminder_db_get_len((uint8_t *)&item1.header.id, sizeof(Uuid)) == 0);
cl_assert(reminder_db_get_len((uint8_t *)&item2.header.id, sizeof(Uuid)) == 0);
// confirm the others are still here
cl_assert(reminder_db_get_len((uint8_t*)&item3.header.id, sizeof(Uuid)) > 0);
cl_assert(reminder_db_get_len((uint8_t*)&item4.header.id, sizeof(Uuid)) > 0);
}
void test_reminder_db__bad_item(void) {
cl_assert(S_SUCCESS != reminder_db_insert((uint8_t *)&bad_item.common.id, UUID_SIZE, (uint8_t *)&bad_item, sizeof(bad_item)));
}
void test_reminder_db__read_nonexistant(void) {
TimelineItem item = {{{0}}};
cl_assert_equal_i(E_DOES_NOT_EXIST, reminder_db_read_item(&item, &bad_item.common.id));
}
void test_reminder_db__find_by_timestamp_title(void) {
prv_insert_default_reminders();
// Add items with title attributes for searching (out of order for worst-case scenario)
cl_assert(S_SUCCESS == reminder_db_insert_item(&title_item2));
cl_assert(S_SUCCESS == reminder_db_insert_item(&title_item1));
TimelineItem reminder;
// Test non-matching title and timestamp
cl_assert_equal_b(reminder_db_find_by_timestamp_title(0, "nonexistent title", NULL, &reminder),
false);
// Test matching timstamp, but not title
cl_assert_equal_b(reminder_db_find_by_timestamp_title(title_item1.header.timestamp,
"nonexistent title", NULL, &reminder), false);
// Test matching title, but not timestamp
cl_assert_equal_b(reminder_db_find_by_timestamp_title(0,
title_item1.attr_list.attributes[0].cstring, NULL, &reminder), false);
// Confirm proper item is returned for search criteria
cl_assert_equal_b(reminder_db_find_by_timestamp_title(title_item1.header.timestamp,
title_item1.attr_list.attributes[0].cstring, NULL, &reminder), true);
cl_assert(uuid_equal(&reminder.header.id, &title_item1.header.id));
}
void test_reminder_db__is_dirty_insert_from_phone(void) {
// Insert a bunch of reminders "from the phone"
// They should NOT be dirty (the phone is the source of truth)
reminder_db_insert((uint8_t *)&item1.header.id, sizeof(TimelineItemId),
(uint8_t *)&item1, sizeof(TimelineItem));
reminder_db_insert((uint8_t *)&item2.header.id, sizeof(TimelineItemId),
(uint8_t *)&item2, sizeof(TimelineItem));
reminder_db_insert((uint8_t *)&item3.header.id, sizeof(TimelineItemId),
(uint8_t *)&item3, sizeof(TimelineItem));
reminder_db_insert((uint8_t *)&item4.header.id, sizeof(TimelineItemId),
(uint8_t *)&item4, sizeof(TimelineItem));
bool is_dirty = true;
cl_assert_equal_i(reminder_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(!is_dirty);
BlobDBDirtyItem *dirty_list = reminder_db_get_dirty_list();
cl_assert(!dirty_list);
}
void test_reminder_db__is_dirty_insert_locally(void) {
// Insert a bunch of reminders "from the watch"
// These should be dirty (the phone is the source of truth)
const int num_reminders = 4;
reminder_db_insert_item(&item1);
reminder_db_insert_item(&item2);
reminder_db_insert_item(&item3);
reminder_db_insert_item(&item4);
bool is_dirty = false;
cl_assert_equal_i(reminder_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(is_dirty);
BlobDBDirtyItem *dirty_list = reminder_db_get_dirty_list();
cl_assert(dirty_list);
cl_assert(list_count((ListNode *)dirty_list) == num_reminders);
// Mark some items as synced
reminder_db_mark_synced((uint8_t *)&item1.header.id, sizeof(TimelineItemId));
reminder_db_mark_synced((uint8_t *)&item3.header.id, sizeof(TimelineItemId));
// We should now only have 2 dirty items
cl_assert_equal_i(reminder_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(is_dirty);
dirty_list = reminder_db_get_dirty_list();
cl_assert(dirty_list);
cl_assert_equal_i(list_count((ListNode *)dirty_list), 2);
// Mark the final 2 items as synced
reminder_db_mark_synced((uint8_t *)&item2.header.id, sizeof(TimelineItemId));
reminder_db_mark_synced((uint8_t *)&item4.header.id, sizeof(TimelineItemId));
// And nothing should be dirty
cl_assert_equal_i(reminder_db_is_dirty(&is_dirty), S_SUCCESS);
cl_assert(!is_dirty);
dirty_list = reminder_db_get_dirty_list();
cl_assert(!dirty_list);
}
void test_reminder_db__set_status_bits(void) {
reminder_db_insert_item(&item1);
SerializedTimelineItemHeader item;
cl_must_pass(reminder_db_read((uint8_t *)&item1.header.id, sizeof(Uuid), (uint8_t *)&item,
sizeof(SerializedTimelineItemHeader)));
cl_assert_equal_i(item.common.status & 0xFF, 0);
cl_must_pass(reminder_db_set_status_bits(&item1.header.id, TimelineItemStatusReminded));
cl_must_pass(reminder_db_read((uint8_t *)&item1.header.id, sizeof(Uuid), (uint8_t *)&item,
sizeof(SerializedTimelineItemHeader)));
cl_assert_equal_i(item.common.status & 0xFF, TimelineItemStatusReminded);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,194 @@
/*
* 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 "services/normal/blob_db/watch_app_prefs_db.h"
#include "services/normal/filesystem/pfs.h"
#include "services/normal/weather/weather_service_private.h"
#include "util/uuid.h"
// Fixture
////////////////////////////////////////////////////////////////
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_spi_flash.h"
#include "fake_system_task.h"
#include "fake_kernel_services_notifications.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_hexdump.h"
#include "stubs_layout_layer.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_sleep.h"
#include "stubs_task_watchdog.h"
extern const char *PREF_KEY_SEND_TEXT_APP;
#define SEND_TEXT_KEY ((uint8_t *)PREF_KEY_SEND_TEXT_APP)
#define SEND_TEXT_KEY_LEN (strlen(PREF_KEY_SEND_TEXT_APP))
#define INVALID_KEY ((uint8_t *)"thisIsNotAnApp")
#define INVALID_KEY_LEN strlen((char *)INVALID_KEY)
#define NUM_SEND_TEXT_CONTACTS (5)
#define SEND_TEXT_DATA_LEN (sizeof(SerializedSendTextPrefs) + \
(sizeof(SerializedSendTextContact) * NUM_SEND_TEXT_CONTACTS))
static uint8_t s_send_text_prefs[SEND_TEXT_DATA_LEN];
#define WEATHER_KEY ((uint8_t *)PREF_KEY_WEATHER_APP)
#define WEATHER_KEY_LEN (strlen(PREF_KEY_WEATHER_APP))
#define NUM_WEATHER_LOCATIONS (4)
#define WEATHER_DATA_SIZE (sizeof(SerializedWeatherAppPrefs) + \
(sizeof(Uuid) * NUM_WEATHER_LOCATIONS))
static uint8_t s_weather_prefs[WEATHER_DATA_SIZE];
// Setup
////////////////////////////////////////////////////////////////
void test_watch_app_prefs_db__initialize(void) {
fake_spi_flash_init(0, 0x1000000);
pfs_init(false);
watch_app_prefs_db_init();
// Set up our test prefs with some random data
SerializedSendTextPrefs *prefs = (SerializedSendTextPrefs *)s_send_text_prefs;
prefs->num_contacts = NUM_SEND_TEXT_CONTACTS;
for (int i = 0; i < NUM_SEND_TEXT_CONTACTS; ++i) {
prefs->contacts[i] = (SerializedSendTextContact) {
.is_fav = (i < (NUM_SEND_TEXT_CONTACTS/2)),
};
uuid_generate(&prefs->contacts[i].contact_uuid);
uuid_generate(&prefs->contacts[i].address_uuid);
}
SerializedWeatherAppPrefs *weather_prefs = (SerializedWeatherAppPrefs *)s_weather_prefs;
weather_prefs->num_locations = NUM_WEATHER_LOCATIONS;
for (int i = 0; i < NUM_WEATHER_LOCATIONS; i++) {
uuid_generate(&weather_prefs->locations[i]);
}
}
void test_watch_app_prefs_db__cleanup(void) {
}
// Tests
////////////////////////////////////////////////////////////////
void test_watch_app_prefs_db__insert_send_text(void) {
const int data_len = sizeof(s_send_text_prefs);
cl_assert_equal_i(
watch_app_prefs_db_insert(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN, s_send_text_prefs, data_len),
S_SUCCESS);
cl_assert_equal_i(watch_app_prefs_db_get_len(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN), data_len);
// Make sure we get back the correct data
uint8_t send_text_prefs_out[data_len];
cl_assert_equal_i(
watch_app_prefs_db_read(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN, send_text_prefs_out, data_len),
S_SUCCESS);
cl_assert_equal_m(s_send_text_prefs, send_text_prefs_out, data_len);
// Make sure we reject malformed data (not aligned to list size)
cl_assert_equal_i(watch_app_prefs_db_insert(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN, s_send_text_prefs,
(data_len + 1)), E_INVALID_ARGUMENT);
// Make sure we reject data that is too small to hold all entries
cl_assert_equal_i(watch_app_prefs_db_insert(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN, s_send_text_prefs,
(data_len - sizeof(SerializedSendTextContact))), E_INVALID_ARGUMENT);
// Make sure we reject keys we don't recognize
cl_assert_equal_i(watch_app_prefs_db_insert(INVALID_KEY, INVALID_KEY_LEN, s_send_text_prefs,
data_len), E_INVALID_ARGUMENT);
}
void test_watch_app_prefs_db__insert_weather(void) {
const int data_len = sizeof(s_weather_prefs);
cl_assert_equal_i(
watch_app_prefs_db_insert(WEATHER_KEY, WEATHER_KEY_LEN, s_weather_prefs, data_len),
S_SUCCESS);
cl_assert_equal_i(watch_app_prefs_db_get_len(WEATHER_KEY, WEATHER_KEY_LEN), data_len);
// Make sure we get back the correct data
uint8_t weather_prefs_out[data_len];
cl_assert_equal_i(
watch_app_prefs_db_read(WEATHER_KEY, WEATHER_KEY_LEN, weather_prefs_out, data_len),
S_SUCCESS);
cl_assert_equal_m(s_weather_prefs, weather_prefs_out, data_len);
// Make sure we reject malformed data (not aligned to list size)
cl_assert_equal_i(watch_app_prefs_db_insert(WEATHER_KEY, WEATHER_KEY_LEN, s_weather_prefs,
(data_len + 1)), E_INVALID_ARGUMENT);
// Make sure we reject data that is too small to hold all entries
cl_assert_equal_i(watch_app_prefs_db_insert(WEATHER_KEY, WEATHER_KEY_LEN, s_weather_prefs,
(data_len - sizeof(SerializedWeatherAppPrefs))), E_INVALID_ARGUMENT);
}
void test_watch_app_prefs_db__insert_remove(void) {
cl_assert_equal_i(watch_app_prefs_db_insert(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN, s_send_text_prefs,
sizeof(s_send_text_prefs)), S_SUCCESS);
cl_assert_equal_i(watch_app_prefs_db_delete(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN), S_SUCCESS);
cl_assert_equal_i(watch_app_prefs_db_get_len(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN), 0);
cl_assert_equal_i(watch_app_prefs_db_insert(WEATHER_KEY, WEATHER_KEY_LEN, s_weather_prefs,
sizeof(s_weather_prefs)), S_SUCCESS);
cl_assert_equal_i(watch_app_prefs_db_delete(WEATHER_KEY, WEATHER_KEY_LEN), S_SUCCESS);
cl_assert_equal_i(watch_app_prefs_db_get_len(WEATHER_KEY, WEATHER_KEY_LEN), 0);
}
void test_watch_app_prefs_db__flush(void) {
cl_assert_equal_i(watch_app_prefs_db_insert(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN, s_send_text_prefs,
sizeof(s_send_text_prefs)), S_SUCCESS);
cl_assert_equal_i(watch_app_prefs_db_insert(WEATHER_KEY, WEATHER_KEY_LEN, s_weather_prefs,
sizeof(s_weather_prefs)), S_SUCCESS);
cl_assert_equal_i(watch_app_prefs_db_flush(), S_SUCCESS);
fake_system_task_callbacks_invoke_pending();
cl_assert_equal_i(watch_app_prefs_db_get_len(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN), 0);
}
void test_watch_app_prefs_db__get_send_text(void) {
const int data_len = sizeof(s_send_text_prefs);
cl_assert_equal_i(watch_app_prefs_db_insert(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN,
s_send_text_prefs, data_len), S_SUCCESS);
cl_assert_equal_i(watch_app_prefs_db_get_len(SEND_TEXT_KEY, SEND_TEXT_KEY_LEN), data_len);
SerializedSendTextPrefs *send_text_prefs_out = watch_app_prefs_get_send_text();
cl_assert_equal_m(s_send_text_prefs, send_text_prefs_out, data_len);
task_free(send_text_prefs_out);
}
void test_watch_app_prefs_db__get_weather(void) {
const int data_len = sizeof(s_weather_prefs);
cl_assert_equal_i(watch_app_prefs_db_insert(WEATHER_KEY, WEATHER_KEY_LEN,
s_weather_prefs, data_len), S_SUCCESS);
cl_assert_equal_i(watch_app_prefs_db_get_len(WEATHER_KEY, WEATHER_KEY_LEN), data_len);
SerializedWeatherAppPrefs *weather_prefs_out = watch_app_prefs_get_weather();
cl_assert_equal_m(s_weather_prefs, weather_prefs_out, data_len);
watch_app_prefs_destroy_weather(weather_prefs_out);
}

View file

@ -0,0 +1,174 @@
/*
* 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 "util/attributes.h"
#include "util/pstring.h"
#include "services/normal/blob_db/weather_db.h"
#include "services/normal/filesystem/pfs.h"
#include "services/normal/weather/weather_types.h"
#include "weather_data_shared.h"
// Fixture
////////////////////////////////////////////////////////////////
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_pbl_malloc.h"
#include "fake_spi_flash.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_hexdump.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_prompt.h"
#include "stubs_task_watchdog.h"
#include "stubs_pebble_tasks.h"
#include "stubs_sleep.h"
bool weather_service_supported_by_phone(void) {
return true;
}
// Setup
////////////////////////////////////////////////////////////////
void test_weather_db__initialize(void) {
fake_spi_flash_init(0, 0x1000000);
pfs_init(false);
weather_db_init();
weather_shared_data_init();
}
void test_weather_db__cleanup(void) {
weather_shared_data_cleanup();
}
// Tests
////////////////////////////////////////////////////////////////
static void prv_db_iterator_cb(WeatherDBKey *key, WeatherDBEntry *entry, void *unused) {
weather_shared_data_assert_entries_equal(key, entry,
weather_shared_data_get_entry(weather_shared_data_get_index_of_key(key)));
}
void test_weather_db__get_entries(void) {
cl_assert_equal_i(S_SUCCESS, weather_db_for_each(prv_db_iterator_cb, NULL));
}
void test_weather_db__check_records_in_db(void) {
for (int index = 0; index < WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES; index++) {
WeatherDBEntry *to_check = task_zalloc_check(weather_shared_data_get_entry_size(index));
const WeatherDBKey *key = weather_shared_data_get_key(index);
cl_assert_equal_i(S_SUCCESS, weather_db_read((uint8_t*)key,
sizeof(WeatherDBKey),
(uint8_t*)to_check,
weather_shared_data_get_entry_size(index)));
WeatherDBEntry *original = weather_shared_data_get_entry(index);
weather_shared_data_assert_entries_equal(key, to_check, original);
task_free(to_check);
}
}
void test_weather_db__check_small_record_not_inserted(void) {
const size_t entry_size = MIN_ENTRY_SIZE - 1;
void *entry = task_zalloc_check(entry_size);
WeatherDBKey key = (WeatherDBKey) {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5
};
cl_assert_equal_i(E_INVALID_ARGUMENT, weather_db_insert((uint8_t*)&key,
sizeof(WeatherDBKey),
(uint8_t*)entry,
entry_size));
task_free(entry);
}
void test_weather_db__check_too_large_record_not_inserted(void) {
const size_t entry_size = MAX_ENTRY_SIZE + 1;
void *entry = task_zalloc_check(entry_size);
WeatherDBKey key = (WeatherDBKey) {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5
};
cl_assert_equal_i(E_INVALID_ARGUMENT, weather_db_insert((uint8_t*)&key,
sizeof(WeatherDBKey),
(uint8_t*)entry,
entry_size));
task_free(entry);
}
static void prv_check_invalid_version_code_not_inserted(uint8_t version) {
const WeatherDBEntry *existing_entry = weather_shared_data_get_entry(0);
const size_t entry_size = sizeof(*existing_entry);
WeatherDBEntry *new_entry = task_zalloc_check(entry_size);
*new_entry = *existing_entry;
new_entry->version = version;
WeatherDBKey key = (WeatherDBKey) {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5
};
cl_assert_equal_i(E_INVALID_ARGUMENT, weather_db_insert((uint8_t*)&key,
sizeof(WeatherDBKey),
(uint8_t*)new_entry,
entry_size));
task_free(new_entry);
}
void test_weather_db__lower_version_not_inserted(void) {
for (size_t version = 0; version < WEATHER_DB_CURRENT_VERSION; version++) {
prv_check_invalid_version_code_not_inserted(version);
}
}
void test_weather_db__higher_version_not_inserted(void) {
prv_check_invalid_version_code_not_inserted(WEATHER_DB_CURRENT_VERSION + 1);
}
status_t weather_db_get_num_keys(uint16_t *val_out);
void test_weather_db__test_get_num_keys(void) {
uint16_t num_keys;
cl_assert_equal_i(S_SUCCESS, weather_db_get_num_keys(&num_keys));
cl_assert_equal_i(num_keys, WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES);
}
status_t weather_db_get_keys(WeatherDBKey *keys);
void test_weather_db__test_get_keys(void) {
WeatherDBKey keys[WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES];
cl_assert_equal_i(S_SUCCESS, weather_db_get_keys(keys));
for(int x = 0; x < WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES; x++) {
cl_assert(weather_shared_data_get_key_exists(&keys[x]));
}
}
void test_weather_db__read_stale_entries(void) {
WeatherDBKey key = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
size_t entry_size = weather_shared_data_insert_stale_entry(&key);
uint8_t *buf = task_zalloc_check(entry_size);
cl_assert_equal_i(E_DOES_NOT_EXIST, weather_db_read((uint8_t*)&key,
sizeof(WeatherDBKey),
buf,
entry_size));
}

View file

@ -0,0 +1,303 @@
/*
* 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 "weather_data_shared.h"
#include "clar_asserts.h"
#include "drivers/rtc.h"
#include "kernel/pbl_malloc.h"
#include "services/normal/blob_db/watch_app_prefs_db.h"
#include "services/normal/blob_db/weather_db.h"
#include "services/normal/weather/weather_service_private.h"
#define WEATHER_PREFS_DATA_SIZE (sizeof(SerializedWeatherAppPrefs) + \
(sizeof(Uuid) * WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES))
static uint8_t s_weather_app_prefs[WEATHER_PREFS_DATA_SIZE];
static const WeatherDBKey s_keys[] = {
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2
},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3
},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4
},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5
},
};
static WeatherDBEntry *s_entries[WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES];
static size_t s_entry_sizes[WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES];
static char *s_entry_names[] = {
TEST_WEATHER_DB_LOCATION_PALO_ALTO,
TEST_WEATHER_DB_LOCATION_KITCHENER,
TEST_WEATHER_DB_LOCATION_WATERLOO,
TEST_WEATHER_DB_LOCATION_RWC,
TEST_WEATHER_DB_LOCATION_SF,
};
static char *s_entry_phrases[] = {
TEST_WEATHER_DB_SHORT_PHRASE_SUNNY,
TEST_WEATHER_DB_SHORT_PHRASE_PARTLY_CLOUDY,
TEST_WEATHER_DB_SHORT_PHRASE_HEAVY_SNOW,
TEST_WEATHER_DB_SHORT_PHRASE_HEAVY_RAIN,
TEST_WEATHER_DB_SHORT_PHRASE_PARTLY_CLOUDY,
};
static const WeatherDBEntry s_entry_bases[] = {
{
.version = WEATHER_DB_CURRENT_VERSION,
.is_current_location = true,
.current_temp = 68,
.current_weather_type = WeatherType_Sun,
.today_high_temp = 68,
.today_low_temp = 52,
.tomorrow_weather_type = WeatherType_CloudyDay,
.tomorrow_high_temp = 70,
.tomorrow_low_temp = 60,
},
{
.version = WEATHER_DB_CURRENT_VERSION,
.is_current_location = false,
.current_temp = -10,
.current_weather_type = WeatherType_PartlyCloudy,
.today_high_temp = 0,
.today_low_temp = -11,
.tomorrow_weather_type = WeatherType_CloudyDay,
.tomorrow_high_temp = 2,
.tomorrow_low_temp = -3,
},
{
.version = WEATHER_DB_CURRENT_VERSION,
.is_current_location = false,
.current_temp = -99,
.current_weather_type = WeatherType_HeavySnow,
.today_high_temp = -98,
.today_low_temp = -99,
.tomorrow_weather_type = WeatherType_Sun,
.tomorrow_high_temp = 2,
.tomorrow_low_temp = 1,
},
{
.version = WEATHER_DB_CURRENT_VERSION,
.is_current_location = true,
.current_temp = 60,
.current_weather_type = WeatherType_HeavyRain,
.today_high_temp = 70,
.today_low_temp = 50,
.tomorrow_weather_type = WeatherType_PartlyCloudy,
.tomorrow_high_temp = 70,
.tomorrow_low_temp = 60,
},
{
.version = WEATHER_DB_CURRENT_VERSION,
.is_current_location = true,
.current_temp = 60,
.current_weather_type = WeatherType_PartlyCloudy,
.today_high_temp = 70,
.today_low_temp = 50,
.tomorrow_weather_type = WeatherType_PartlyCloudy,
.tomorrow_high_temp = 70,
.tomorrow_low_temp = 60,
}
};
// Fake out watch_app_prefs calls
void watch_app_prefs_destroy_weather(SerializedWeatherAppPrefs *prefs) {}
SerializedWeatherAppPrefs *watch_app_prefs_get_weather(void) {
SerializedWeatherAppPrefs *prefs = (SerializedWeatherAppPrefs *) s_weather_app_prefs;
prefs->num_locations = WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES;
for (int idx = 0; idx < WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES; idx++) {
prefs->locations[idx] = s_keys[idx];
}
return prefs;
}
static WeatherDBEntry *prv_create_entry(const WeatherDBEntry *base_entry, char *location,
char *phrase, size_t *size_out) {
PascalString16List pstring16_list;
PascalString16 *location_name;
PascalString16 *short_phrase;
size_t data_size;
location_name = pstring_create_pstring16_from_string(location);
short_phrase = pstring_create_pstring16_from_string(phrase);
data_size = strlen(location) +
strlen(phrase) +
sizeof(uint16_t) * 2; // One for each string
const size_t entry_size = sizeof(WeatherDBEntry) + data_size;
WeatherDBEntry *entry = task_zalloc_check(entry_size);
*entry = *base_entry;
entry->pstring16s.data_size = data_size;
entry->last_update_time_utc = rtc_get_time();
pstring_project_list_on_serialized_array(&pstring16_list, &entry->pstring16s);
pstring_add_pstring16_to_list(&pstring16_list, location_name);
pstring_add_pstring16_to_list(&pstring16_list, short_phrase);
pstring_destroy_pstring16(location_name);
pstring_destroy_pstring16(short_phrase);
*size_out = entry_size;
return entry;
}
static void prv_initialize_entries(void) {
for (int idx = 0; idx < WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES; idx++) {
WeatherDBEntry *entry = prv_create_entry(&s_entry_bases[idx],
s_entry_names[idx],
s_entry_phrases[idx],
&s_entry_sizes[idx]);
// Make the last entry contain a timestamp that is too old to be included in weather_service
// forecast list
if (idx == WEATHER_DATA_SHARED_NUM_VALID_TIMESTAMP_ENTRIES) {
entry->last_update_time_utc = (time_start_of_today() - SECONDS_PER_DAY - 1);
}
cl_assert_equal_i(S_SUCCESS, weather_db_insert((uint8_t*)&s_keys[idx],
sizeof(WeatherDBKey),
(uint8_t*)entry,
s_entry_sizes[idx]));
s_entries[idx] = entry;
}
}
void weather_shared_data_init(void) {
rtc_set_time(1461765790); // 2016-04-27T14:03:10+00:00
prv_initialize_entries();
}
void weather_shared_data_cleanup(void) {
for (int i = 0; i < WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES; i++) {
if (s_entries[i]) {
task_free(s_entries[i]);
s_entries[i] = NULL;
}
}
// Flush DB
cl_assert_equal_i(S_SUCCESS, weather_db_flush());
}
const WeatherDBKey *weather_shared_data_get_key(int index) {
return &s_keys[index];
}
WeatherDBEntry *weather_shared_data_get_entry(int index) {
return s_entries[index];
}
size_t weather_shared_data_get_entry_size(int index) {
return s_entry_sizes[index];
}
char *weather_shared_data_get_entry_name(int index) {
return s_entry_names[index];
}
char *weather_shared_data_get_entry_phrase(int index) {
return s_entry_phrases[index];
}
int weather_shared_data_get_index_of_key(const WeatherDBKey *key) {
for (int idx = 0; idx < WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES; idx++) {
if (uuid_equal(key, &s_keys[idx])) {
return idx;
}
}
return -1;
}
void weather_shared_data_assert_entries_equal(const WeatherDBKey *key, WeatherDBEntry *to_check,
WeatherDBEntry *original) {
cl_assert_equal_i(to_check->version, original->version);
cl_assert_equal_b(to_check->is_current_location, original->is_current_location);
cl_assert_equal_i(to_check->current_temp, original->current_temp);
cl_assert_equal_i(to_check->current_weather_type, original->current_weather_type);
cl_assert_equal_i(to_check->today_high_temp, original->today_high_temp);
cl_assert_equal_i(to_check->today_low_temp, original->today_low_temp);
cl_assert_equal_i(to_check->tomorrow_weather_type, original->tomorrow_weather_type);
cl_assert_equal_i(to_check->tomorrow_high_temp, original->tomorrow_high_temp);
cl_assert_equal_i(to_check->tomorrow_low_temp, original->tomorrow_low_temp);
cl_assert_equal_i(to_check->last_update_time_utc, original->last_update_time_utc);
PascalString16List pstring16_list;
pstring_project_list_on_serialized_array(&pstring16_list, &to_check->pstring16s);
cl_assert_equal_i(pstring16_list.count, 2);
PascalString16 *pstring;
pstring = pstring_get_pstring16_from_list(&pstring16_list, 0);
int index = weather_shared_data_get_index_of_key(key);
if (index == -1) {
cl_fail("key not found!");
}
cl_assert_equal_i(pstring->str_length, strlen(s_entry_names[index]));
char loc[WEATHER_SERVICE_MAX_WEATHER_LOCATION_BUFFER_SIZE];
pstring_pstring16_to_string(pstring, loc);
cl_assert_equal_s(loc, s_entry_names[index]);
pstring = pstring_get_pstring16_from_list(&pstring16_list, 1);
cl_assert_equal_i(pstring->str_length, strlen(s_entry_phrases[index]));
char phrase[WEATHER_SERVICE_MAX_SHORT_PHRASE_BUFFER_SIZE];
pstring_pstring16_to_string(pstring, phrase);
cl_assert_equal_s(phrase, s_entry_phrases[index]);
}
bool weather_shared_data_get_key_exists(WeatherDBKey *key) {
return weather_shared_data_get_index_of_key(key) != -1;
}
status_t weather_db_insert_stale(const uint8_t *key, int key_len, const uint8_t *val, int val_len);
size_t weather_shared_data_insert_stale_entry(WeatherDBKey *key) {
const WeatherDBEntry stale_entry = {
.version = WEATHER_DB_CURRENT_VERSION - 1,
.is_current_location = true,
.current_temp = 68,
.current_weather_type = WeatherType_Sun,
.today_high_temp = 68,
.today_low_temp = 52,
.tomorrow_weather_type = WeatherType_CloudyDay,
.tomorrow_high_temp = 70,
.tomorrow_low_temp = 60,
};
WeatherDBEntry *entry = prv_create_entry(&stale_entry,
s_entry_names[0],
s_entry_phrases[0],
&s_entry_sizes[0]);
cl_assert_equal_i(S_SUCCESS, weather_db_insert_stale((uint8_t*)key,
sizeof(WeatherDBKey),
(uint8_t*)entry,
s_entry_sizes[0]));
return s_entry_sizes[0];
}

View file

@ -0,0 +1,60 @@
/*
* 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.
*/
#pragma once
#include "services/normal/blob_db/weather_db.h"
#define WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES (5)
#define WEATHER_DATA_SHARED_NUM_VALID_TIMESTAMP_ENTRIES \
(WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES - 1)
#define TEST_WEATHER_DB_LOCATION_PALO_ALTO "Palo Alto"
#define TEST_WEATHER_DB_LOCATION_KITCHENER "Kitchener"
#define TEST_WEATHER_DB_LOCATION_WATERLOO "Waterloo"
#define TEST_WEATHER_DB_LOCATION_RWC "Redwood City"
#define TEST_WEATHER_DB_LOCATION_SF "San Francisco"
#define TEST_WEATHER_DB_SHORT_PHRASE_SUNNY "Sunny"
#define TEST_WEATHER_DB_SHORT_PHRASE_PARTLY_CLOUDY "Partly Cloudy"
#define TEST_WEATHER_DB_SHORT_PHRASE_HEAVY_SNOW "Heavy Snow"
#define TEST_WEATHER_DB_SHORT_PHRASE_HEAVY_RAIN "Heavy Rain"
void weather_shared_data_initialize_locations_order(void);
void weather_shared_data_init(void);
void weather_shared_data_cleanup(void);
int weather_shared_data_get_index_of_key(const WeatherDBKey *key);
const WeatherDBKey *weather_shared_data_get_key(int index);
WeatherDBEntry *weather_shared_data_get_entry(int index);
size_t weather_shared_data_get_entry_size(int index);
char *weather_shared_data_get_entry_name(int index);
char *weather_shared_data_get_entry_phrase(int index);
void weather_shared_data_assert_entries_equal(const WeatherDBKey *key, WeatherDBEntry *to_check,
WeatherDBEntry *original);
bool weather_shared_data_get_key_exists(WeatherDBKey *key);
size_t weather_shared_data_insert_stale_entry(WeatherDBKey *key);

View file

@ -0,0 +1,254 @@
from waftools.pebble_test import clar
def build(ctx):
def test_timeline_service(test_name, defines=None):
clar(ctx,
test_name=test_name,
defines=defines,
sources_ant_glob=(
"src/fw/services/normal/blob_db/pin_db.c "
"src/fw/services/normal/blob_db/sync_util.c "
"src/fw/services/normal/blob_db/timeline_item_storage.c "
"src/fw/services/normal/timeline/attribute.c "
"src/fw/services/normal/timeline/attribute_group.c "
"src/fw/services/normal/timeline/attributes_actions.c "
"src/fw/services/normal/timeline/item.c "
"src/fw/services/normal/timeline/timeline.c "
"src/fw/util/crc8.c "
"src/fw/util/time/mktime.c "
"src/fw/util/time/time.c "
"tests/fakes/fake_rtc.c "
"tests/fakes/fake_settings_file.c "
),
test_sources_ant_glob="test_timeline.c",
override_includes=['dummy_board'])
test_timeline_service(test_name='test_timeline')
test_timeline_service(test_name='test_timeline~ux4',
defines=['CAPABILITY_HAS_CORE_NAVIGATION4=1'])
clar(ctx,
sources_ant_glob=(
"src/fw/services/normal/blob_db/pin_db.c "
"src/fw/services/normal/blob_db/sync_util.c "
"src/fw/services/normal/blob_db/timeline_item_storage.c "
"src/fw/services/normal/blob_db/util.c "
"src/fw/services/normal/timeline/attribute.c "
"src/fw/services/normal/timeline/attribute_group.c "
"src/fw/services/normal/timeline/attributes_actions.c "
"src/fw/services/normal/timeline/item.c "
"src/fw/util/crc8.c "
"src/fw/util/time/mktime.c "
"src/fw/util/time/time.c "
"tests/fakes/fake_blobdb.c "
"tests/fakes/fake_rtc.c "
"tests/fakes/fake_session.c "
"tests/fakes/fake_settings_file.c "
"tests/fakes/ram_storage.c "
"tests/fakes/test_db.c "
),
test_sources_ant_glob="test_pin_db.c")
clar(ctx,
sources_ant_glob = \
" src/fw/services/normal/blob_db/util.c" \
" src/fw/services/normal/blob_db/sync.c" \
" tests/fakes/ram_storage.c" \
" tests/fakes/test_db.c" \
" tests/fakes/fake_blobdb.c",
test_sources_ant_glob = "test_blob_db_sync.c")
clar(ctx,
sources_ant_glob = \
" src/fw/services/normal/blob_db/api.c" \
" src/fw/services/normal/blob_db/endpoint.c " \
" src/fw/services/normal/blob_db/endpoint_private.c " \
" src/fw/services/normal/blob_db/util.c" \
" tests/fakes/fake_session.c",
test_sources_ant_glob = "test_blob_db_endpoint.c")
clar(ctx,
sources_ant_glob = \
" src/fw/services/normal/blob_db/api.c" \
" src/fw/services/normal/blob_db/endpoint2.c " \
" src/fw/services/normal/blob_db/endpoint_private.c " \
" src/fw/services/normal/blob_db/util.c",
test_sources_ant_glob = "test_blob_db2_endpoint.c")
clar(ctx,
sources_ant_glob = \
" src/fw/util/crc8.c" \
" src/fw/util/legacy_checksum.c" \
" tests/fakes/fake_spi_flash.c" \
" src/fw/util/rand/rand.c" \
" src/fw/vendor/tinymt32/tinymt32.c" \
" tests/fakes/fake_rtc.c" \
" src/fw/flash_region/flash_region.c" \
" src/fw/flash_region/filesystem_regions.c" \
" src/fw/services/normal/settings/settings_file.c" \
" src/fw/services/normal/settings/settings_raw_iter.c" \
" src/fw/services/normal/filesystem/flash_translation.c" \
" src/fw/services/normal/filesystem/pfs.c" \
" src/fw/services/normal/blob_db/app_db.c",
test_sources_ant_glob = "test_app_db.c",
override_includes=['dummy_board'])
clar(ctx,
sources_ant_glob = \
" src/fw/services/normal/blob_db/app_glance_db.c" \
" src/fw/services/normal/timeline/attribute.c" \
" src/fw/util/crc8.c" \
" tests/fakes/fake_rtc.c" \
" tests/fakes/fake_settings_file.c" \
" tests/fakes/fake_events.c" \
"",
test_sources_ant_glob = "test_app_glance_db.c",
override_includes=['dummy_board'])
clar(ctx,
sources_ant_glob = \
" src/fw/util/crc8.c" \
" src/fw/util/legacy_checksum.c" \
" tests/fakes/fake_spi_flash.c" \
" src/fw/util/rand/rand.c" \
" src/fw/vendor/tinymt32/tinymt32.c" \
" tests/fakes/fake_rtc.c" \
" src/fw/flash_region/flash_region.c" \
" src/fw/flash_region/filesystem_regions.c" \
" src/fw/services/normal/settings/settings_file.c" \
" src/fw/services/normal/settings/settings_raw_iter.c" \
" src/fw/services/normal/filesystem/flash_translation.c" \
" src/fw/services/normal/filesystem/pfs.c" \
" src/fw/services/normal/blob_db/contacts_db.c",
test_sources_ant_glob = "test_contacts_db.c",
override_includes=['dummy_board'])
clar(ctx,
sources_ant_glob = \
" src/fw/util/crc8.c" \
" src/fw/util/legacy_checksum.c" \
" tests/fakes/fake_spi_flash.c" \
" src/fw/util/rand/rand.c" \
" src/fw/vendor/tinymt32/tinymt32.c" \
" tests/fakes/fake_rtc.c" \
" src/fw/flash_region/flash_region.c" \
" src/fw/flash_region/filesystem_regions.c" \
" src/fw/services/normal/settings/settings_file.c" \
" src/fw/services/normal/settings/settings_raw_iter.c" \
" src/fw/services/normal/filesystem/flash_translation.c" \
" src/fw/services/normal/filesystem/pfs.c" \
" src/fw/services/normal/blob_db/watch_app_prefs_db.c",
test_sources_ant_glob = "test_watch_app_prefs_db.c",
override_includes=['dummy_board'])
clar(ctx,
sources_ant_glob = \
" src/fw/util/crc8.c" \
" src/fw/util/rand/rand.c" \
" src/fw/vendor/tinymt32/tinymt32.c" \
" tests/fakes/fake_rtc.c" \
" src/fw/util/time/time.c" \
" src/fw/services/normal/blob_db/timeline_item_storage.c" \
" tests/fakes/fake_settings_file.c" \
" src/fw/services/normal/blob_db/reminder_db.c" \
" src/fw/services/normal/blob_db/sync_util.c" \
" src/fw/services/normal/timeline/attribute.c" \
" src/fw/services/normal/timeline/attributes_actions.c" \
" src/fw/services/normal/timeline/attribute_group.c" \
" src/fw/services/normal/timeline/item.c",
test_sources_ant_glob = "test_reminder_db.c",
override_includes=['dummy_board'])
clar(ctx,
sources_ant_glob = \
" src/fw/util/crc8.c" \
" src/fw/util/legacy_checksum.c" \
" tests/fakes/fake_spi_flash.c" \
" tests/fakes/fake_kernel_services_notifications.c" \
" src/fw/util/rand/rand.c" \
" src/fw/vendor/tinymt32/tinymt32.c" \
" tests/fakes/fake_rtc.c" \
" src/fw/flash_region/filesystem_regions.c" \
" src/fw/flash_region/flash_region.c" \
" src/fw/util/time/time.c" \
" src/fw/services/normal/filesystem/flash_translation.c" \
" src/fw/services/normal/filesystem/pfs.c" \
" src/fw/services/normal/notifications/notification_storage.c" \
" src/fw/services/normal/timeline/attribute.c" \
" src/fw/services/normal/timeline/item.c" \
" src/fw/services/normal/timeline/attributes_actions.c" \
" src/fw/services/normal/timeline/attribute_group.c" \
" src/fw/services/normal/blob_db/notif_db.c",
test_sources_ant_glob = "test_notif_db.c",
override_includes=['dummy_board'])
clar(ctx,
sources_ant_glob = \
" src/fw/util/pstring.c" \
" src/fw/util/time/time.c" \
" src/fw/util/crc8.c" \
" src/fw/util/legacy_checksum.c" \
" tests/fakes/fake_rtc.c" \
" tests/fakes/fake_spi_flash.c" \
" src/fw/flash_region/flash_region.c" \
" src/fw/flash_region/filesystem_regions.c" \
" src/fw/services/normal/settings/settings_file.c" \
" src/fw/services/normal/settings/settings_raw_iter.c" \
" src/fw/services/normal/filesystem/flash_translation.c" \
" src/fw/services/normal/filesystem/pfs.c" \
" src/fw/services/normal/blob_db/weather_db.c" \
" tests/fw/services/blob_db/weather_data_shared.c",
test_sources_ant_glob = "test_weather_db.c",
override_includes=['dummy_board'])
clar(ctx,
sources_ant_glob = \
" src/fw/util/rand/rand.c" \
" src/fw/vendor/tinymt32/tinymt32.c" \
" src/fw/util/time/time.c" \
" src/fw/flash_region/filesystem_regions.c" \
" src/fw/flash_region/flash_region.c" \
" src/fw/services/normal/filesystem/flash_translation.c" \
" src/fw/services/normal/filesystem/pfs.c" \
" src/fw/services/normal/settings/settings_file.c" \
" src/fw/services/normal/settings/settings_raw_iter.c" \
" src/fw/services/normal/blob_db/prefs_db.c" \
" src/fw/shell/normal/prefs.c" \
" src/fw/util/crc8.c" \
" src/fw/util/legacy_checksum.c" \
" tests/fakes/fake_spi_flash.c" \
" tests/fakes/fake_rtc.c",
test_sources_ant_glob = "test_prefs_db.c",
override_includes=['dummy_board'])
clar(ctx,
sources_ant_glob = \
" src/fw/util/crc8.c" \
" src/libutil/uuid.c" \
" src/fw/util/rand/rand.c" \
" src/fw/vendor/tinymt32/tinymt32.c" \
" tests/fakes/fake_rtc.c" \
" tests/fakes/fake_settings_file.c" \
" src/fw/util/time/time.c" \
" src/fw/services/normal/timeline/attribute.c" \
" src/fw/services/normal/timeline/item.c" \
" src/fw/services/normal/timeline/attributes_actions.c" \
" src/fw/services/normal/timeline/attribute_group.c"
" src/fw/services/normal/blob_db/sync_util.c" \
" src/fw/services/normal/blob_db/ios_notif_pref_db.c" ,
test_sources_ant_glob = "test_ios_notif_pref_db.c",
override_includes=['dummy_board'])
clar(ctx,
sources_ant_glob = \
" src/fw/util/crc8.c" \
" src/fw/util/time/time.c" \
" tests/fakes/fake_settings_file.c" \
" src/fw/services/normal/blob_db/health_db.c" ,
test_sources_ant_glob = "test_health_db.c",
override_includes=['dummy_board'])
# vim:filetype=python