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