mirror of
https://github.com/google/pebble.git
synced 2025-03-27 21:47:44 +00:00
337 lines
11 KiB
C
337 lines
11 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "clar.h"
|
|
|
|
// 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();
|
|
}
|
|
|