pebble/tests/fw/services/blob_db/test_timeline.c
2025-01-27 11:38:16 -08:00

1511 lines
48 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 "util/uuid.h"
#include "services/normal/filesystem/pfs.h"
#include "services/common/regular_timer.h"
#include "services/normal/blob_db/pin_db.h"
#include "services/normal/timeline/timeline.h"
#include "util/list.h"
#include "util/size.h"
// Fixture
////////////////////////////////////////////////////////////////
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_pbl_malloc.h"
#include "fake_rtc.h"
#include "fake_settings_file.h"
static TimezoneInfo tz = {
.tm_gmtoff = -8 * 60 * 60, // PST
};
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_app_cache.h"
#include "stubs_app_install_manager.h"
#include "stubs_app_manager.h"
#include "stubs_blob_db.h"
#include "stubs_blob_db_sync.h"
#include "stubs_calendar.h"
#include "stubs_event_service_client.h"
#include "stubs_events.h"
#include "stubs_fonts.h"
#include "stubs_hexdump.h"
#include "stubs_i18n.h"
#include "stubs_layout_layer.h"
#include "stubs_logging.h"
#include "stubs_modal_manager.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pebble_tasks.h"
#include "stubs_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_regular_timer.h"
#include "stubs_session.h"
#include "stubs_sleep.h"
#include "stubs_task_watchdog.h"
#include "stubs_window_stack.h"
struct TimelineNode {
ListNode node;
int index;
Uuid id;
time_t timestamp;
uint16_t duration;
bool all_day;
};
void ancs_notifications_enable_bulk_action_mode(bool enable) {
return;
}
bool ancs_notifications_is_bulk_action_mode_enabled(void) {
return false;
}
status_t reminder_db_delete_with_parent(const TimelineItemId *id) {
return S_SUCCESS;
}
void timeline_action_endpoint_invoke_action(const Uuid *id,
uint8_t action_id, AttributeList *attributes) {
}
const PebbleProcessMd *timeline_get_app_info(void) {
return NULL;
}
void launcher_task_add_callback(void *data) {
}
void timeline_pin_window_push_modal(TimelineItem *item) {
}
PebblePhoneCaller* phone_call_util_create_caller(const char *number, const char *name) {
return NULL;
}
void ancs_perform_action(uint32_t notification_uid, uint8_t action_id) {
}
void notifications_handle_notification_action_result(
PebbleSysNotificationActionResult *action_result) {
}
void notification_storage_set_status(const Uuid *id, uint8_t status) {
}
void notifications_handle_notification_acted_upon(Uuid *notification_id) {
return;
}
// Data
/////////////////////////
static TimelineItem s_items[] = {
{
.header = { // [0]
.id = {0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb1},
.parent_id = {0},
.timestamp = 1421178061, // Tue Jan 13 11:41:01 2015 PST
.duration = 1,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
}, {
.header = { // [1]
.id = {0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb2},
.parent_id = {0},
.timestamp = 1421183642, // Tue Jan 13 13:14:02 2015 PST
.duration = 10,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
}, {
.header = { // [2]
.id = {0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb3},
.parent_id = {0},
.timestamp = 1421183642, // Tue Jan 13 13:14:02 2015 PST
.duration = 2,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
}, {
.header = { // [3]
.id = {0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4},
.parent_id = {0},
.timestamp = 1421183642, // Tue Jan 13 13:14:02 2015 PST
.duration = 30,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
}, {
.header = { // [4]
.id = {0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb5},
.parent_id = {0},
.timestamp = 1421178061, // Tue Jan 13 11:41:01 2015 PST
.duration = 5,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
}, {
.header = { // [5]
.id = {0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb6},
.parent_id = {0},
.timestamp = 1421183462, // Tue Jan 13 13:11:02 PST 2015
.duration = 4,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
}
};
// items with long duration
static TimelineItem s_long_items[] = {
{
.header = {
.id = {0xaa},
.timestamp = 10000,
.duration = 30,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
}
},
{
.header = {
.id = {0xbb},
.timestamp = 12000,
.duration = 30,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
}
},
{
.header = {
.id = {0xcc},
.timestamp = 14000,
.duration = 30,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
}
},
{
.header = {
.id = {0xdd},
.timestamp = 16000,
.duration = 30,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
}
},
{
.header = {
.id = {0xee},
.timestamp = 18000,
.duration = 30,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
}
}
};
// all day item
static TimelineItem s_all_day_items[] = {
{
.header = {
.id = {0x01},
.parent_id = {0},
.timestamp = 1421020800, // midnight jan 12, 2015 UTC
.duration = MINUTES_PER_DAY,
.type = TimelineItemTypePin,
.all_day = 1,
.layout = LayoutIdTest,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
}, {
.header = {
.id = {0x02},
.parent_id = {0},
.timestamp = 1421107200, // Tue Jan 13 midnight 2015 UTC
.duration = MINUTES_PER_DAY,
.type = TimelineItemTypePin,
.all_day = 1,
.layout = LayoutIdTest,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
}, {
.header = {
.id = {0x03},
.parent_id = {0},
.timestamp = 1421107200, // Tue Jan 13 midnight 2015 UTC
.duration = MINUTES_PER_DAY,
.type = TimelineItemTypePin,
.all_day = 1,
.layout = LayoutIdTest,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
}
};
static const int s_feb_5_midnight = 1423123200; // 2015 PST
static const int s_feb_5_midnight_utc = 1423094400; // 2015 UTC
// extra case -- one all day event, one event from 8:00 to 10:00, and one from 8:15-8:16
// should cover most edge cases since it deals with all day events and skipping events
// that have past by endtime
static TimelineItem s_extra_case_items[] = {
{
.header = {
.id = {0xbb},
.parent_id = {0},
.timestamp = s_feb_5_midnight_utc,
.duration = MINUTES_PER_DAY,
.type = TimelineItemTypePin,
.all_day = 1,
.layout = LayoutIdTest,
},
}, {
.header = {
.id = {0xcc},
.parent_id = {0},
.timestamp = s_feb_5_midnight + 8 * 60 * 60, // 8:00-10:00 am
.duration = 120,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
}
}, {
.header = {
.id = {0xdd},
.parent_id = {0},
.timestamp = s_feb_5_midnight + 8 * 60 * 60 + 15 * 60, // 8:15-8:16 am
.duration = 1,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
}
}
};
// Setup
/////////////////////////
void test_timeline__initialize(void) {
fake_rtc_init(0, 0);
// Note: creating a settings file is going to result in one malloc for the FD name
pin_db_init();
time_util_update_timezone(&tz);
uint8_t num_net_allocs = fake_pbl_malloc_num_net_allocs();
for (unsigned int i = 0; i < ARRAY_LENGTH(s_items); ++i) {
cl_assert_equal_i(pin_db_insert_item(&s_items[i]), 0);
}
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), num_net_allocs);
}
void test_timeline__cleanup(void) {
fake_settings_file_reset();
fake_pbl_malloc_clear_tracking();
}
// Tests
///////////////////////////
void test_timeline__all_forwards(void) {
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
// Note: 1421178000 = Tue Jan 13 11:40:00 PST 2015
// check first
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
1421178000), 0);
cl_assert(uuid_equal(&state.pin.header.id, &s_items[0].header.id));
// check second
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[4].header.id));
// check third
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[5].header.id));
// check second again
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[4].header.id));
// check fourth
cl_assert(iter_next(&iterator));
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[2].header.id));
// check fifth
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[1].header.id));
// check sixth
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[3].header.id));
// check rollover behaviour
cl_assert(!iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[3].header.id));
cl_assert(state.node);
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[1].header.id));
}
void test_timeline__forward_and_back(void) {
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
// Note: 1421178000 = Tue Jan 13 11:40:00 PST 2015
// check first
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
1421178000), 0);
cl_assert(uuid_equal(&state.pin.header.id, &s_items[0].header.id));
// check second
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[4].header.id));
// check first again
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[0].header.id));
cl_assert(!iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[0].header.id));
cl_assert(state.node);
}
void test_timeline__none_forwards(void) {
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
1421188000), 2);
}
void test_timeline__all_backwards(void) {
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
// Note: 1421188000 == Tue Jan 13 14:26:40 PST 2015
// check first
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast,
1421188000), 0);
cl_assert(uuid_equal(&state.pin.header.id, &s_items[3].header.id));
// check second
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[1].header.id));
// check third
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[2].header.id));
// check fourth
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[5].header.id));
// check third again
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[2].header.id));
// check fifth
cl_assert(iter_next(&iterator));
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[4].header.id));
// check sixth
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[0].header.id));
}
void test_timeline__none_backwards(void) {
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast,
1421178000), 2);
}
void test_timeline__middle_forwards(void) {
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
// check first
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
1421183640), 0);
cl_assert(uuid_equal(&state.pin.header.id, &s_items[5].header.id));
cl_assert(iter_next(&iterator));
// check second
cl_assert(uuid_equal(&state.pin.header.id, &s_items[2].header.id));
// check third
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[1].header.id));
// check fourth
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[3].header.id));
// check third again
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[1].header.id));
// check done
cl_assert(iter_next(&iterator));
cl_assert(iter_next(&iterator) == false);
}
void test_timeline__middle_backwards(void) {
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
// check first
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast,
1421183640), 0);
cl_assert(uuid_equal(&state.pin.header.id, &s_items[4].header.id));
// check second
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[0].header.id));
// check done
cl_assert(iter_next(&iterator) == false);
}
static void prv_insert_long_items(void) {
cl_assert_equal_i(pin_db_flush(), 0);
for (unsigned int i = 0; i < ARRAY_LENGTH(s_long_items); i++) {
cl_assert_equal_i(pin_db_insert_item(&s_long_items[i]), 0);
}
}
void test_timeline__long_middle_past(void) {
prv_insert_long_items();
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
// initialize it to be 11 min after item cc has started
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast,
14700), 0);
#if !CAPABILITY_HAS_CORE_NAVIGATION4
cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[2].header.id));
cl_assert(iter_next(&iterator));
#endif
cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[1].header.id));
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[0].header.id));
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[1].header.id));
#if !CAPABILITY_HAS_CORE_NAVIGATION4
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[2].header.id));
#endif
cl_assert(!iter_prev(&iterator));
}
void test_timeline__long_middle_future(void) {
prv_insert_long_items();
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
// initialize it to be 11 min after item cc has started
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
14700), 0);
#if CAPABILITY_HAS_CORE_NAVIGATION4
cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[2].header.id));
cl_assert(iter_next(&iterator));
#endif
cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[3].header.id));
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[4].header.id));
cl_assert(!iter_next(&iterator));
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[3].header.id));
#if CAPABILITY_HAS_CORE_NAVIGATION4
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[2].header.id));
#endif
cl_assert(!iter_prev(&iterator));
}
static int prv_num_items(Iterator iterator) {
int n = 1;
while (iter_next(&iterator)) {
n++;
}
return n;
}
void test_timeline__gc_past(void) {
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
// Tue Jan 13 11:40:00 PST 2015
rtc_set_time(1421178000);
fake_pbl_malloc_clear_tracking();
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), 0);
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
1421178000), 0);
cl_assert_equal_i(prv_num_items(iterator), 6);
// Thursday Jan 16 00:00:00 PST 2015
// No items within window
rtc_set_time(1421395200);
head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast,
1421395200),
S_NO_MORE_ITEMS);
fake_pbl_malloc_clear_tracking();
// Thursday Jan 16 14:00:00 PST 2015
// all items garbage collected
rtc_set_time(1421445600);
head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast,
1421445600),
S_NO_MORE_ITEMS);
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), 0);
}
static void prv_insert_all_day_items(void) {
for (int i = 0; i < ARRAY_LENGTH(s_all_day_items); i++) {
cl_assert_equal_i(pin_db_insert_item(&s_all_day_items[i]), 0);
}
}
void test_timeline__all_day_future(void) {
prv_insert_all_day_items();
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
// start 11:40 AM, earlier than all timed events for that day
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
1421178000), 0);
Uuid first_all_day_event = state.pin.header.id;
cl_assert(uuid_equal(&state.pin.header.id, &s_all_day_items[1].header.id) ||
uuid_equal(&state.pin.header.id, &s_all_day_items[2].header.id));
cl_assert(state.node->all_day);
// check that the item we see is timestamped at local midnight rather than utc midnight
// 1421136000 is midnight Jan 13, PST
cl_assert_equal_i(state.pin.header.timestamp, 1421136000);
// second all day event
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_all_day_items[1].header.id) ||
uuid_equal(&state.pin.header.id, &s_all_day_items[2].header.id));
cl_assert(!uuid_equal(&first_all_day_event, &state.pin.header.id));
cl_assert(state.node->all_day);
cl_assert_equal_i(state.pin.header.timestamp, 1421136000);
// back to the first
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&first_all_day_event, &state.pin.header.id));
cl_assert(state.node->all_day);
// correct end of line behaviour
cl_assert(!iter_prev(&iterator));
}
void test_timeline__all_day_future_with_others(void) {
prv_insert_all_day_items();
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
// start 11:40 AM, earlier than all timed events for that day
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
1421178000), 0);
Uuid first_all_day_event = state.pin.header.id;
cl_assert(uuid_equal(&state.pin.header.id, &s_all_day_items[1].header.id) ||
uuid_equal(&state.pin.header.id, &s_all_day_items[2].header.id));
cl_assert(state.node->all_day);
// check that the item we see is timestamped at local midnight rather than utc midnight
// 1421136000 is midnight Jan 13, PST
cl_assert_equal_i(state.pin.header.timestamp, 1421136000);
// second all day event
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_all_day_items[1].header.id) ||
uuid_equal(&state.pin.header.id, &s_all_day_items[2].header.id));
cl_assert(!uuid_equal(&first_all_day_event, &state.pin.header.id));
cl_assert(state.node->all_day);
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[0].header.id));
// check second
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[4].header.id));
// check third
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[5].header.id));
// check second again
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[4].header.id));
// check fourth
cl_assert(iter_next(&iterator));
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[2].header.id));
// check fifth
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[1].header.id));
// check sixth
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[3].header.id));
// check rollover behaviour
cl_assert(!iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[3].header.id));
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[1].header.id));
}
void test_timeline__all_day_past(void) {
prv_insert_all_day_items();
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
TimelineItem earlier_item = {
.header = {
.id = {0x04},
.parent_id = {0},
.timestamp = 1421049600 + 9 * 60 * 60, // 9am on Jan 12, 2015
.duration = 20,
.type = TimelineItemTypePin,
.flags = 0,
.layout = LayoutIdTest,
}
};
cl_assert_equal_i(pin_db_insert_item(&earlier_item), 0);
// start 11:40 AM, earlier than all timed events for that day
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast,
1421178000), 0);
cl_assert(uuid_equal(&state.pin.header.id, &earlier_item.header.id));
cl_assert(!state.node->all_day);
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_all_day_items[0].header.id));
cl_assert(state.node->all_day);
cl_assert(!iter_next(&iterator));
cl_assert(iter_prev(&iterator));
cl_assert(!iter_prev(&iterator));
}
void test_timeline__all_day_middle_past(void) {
prv_insert_all_day_items();
// 1421183640 is 13:14 on Jan 13, 2015
// after first timed event of the day but not all of them
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast,
1421183640), 0);
cl_assert(uuid_equal(&state.pin.header.id, &s_items[4].header.id));
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[0].header.id));
// check all day events
cl_assert(iter_next(&iterator));
Uuid first_all_day_event = state.pin.header.id;
cl_assert(uuid_equal(&state.pin.header.id, &s_all_day_items[1].header.id) ||
uuid_equal(&state.pin.header.id, &s_all_day_items[2].header.id));
cl_assert(state.node->all_day);
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_all_day_items[1].header.id) ||
uuid_equal(&state.pin.header.id, &s_all_day_items[2].header.id));
cl_assert(!uuid_equal(&first_all_day_event, &state.pin.header.id));
cl_assert(state.node->all_day);
// yesterday's all day event
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_all_day_items[0].header.id));
cl_assert(!iter_next(&iterator));
}
static void prv_insert_extra_case_items(void) {
pin_db_flush();
for (int i = 0; i < ARRAY_LENGTH(s_extra_case_items); i++) {
cl_assert_equal_i(pin_db_insert_item(&s_extra_case_items[i]), 0);
}
}
// 5am, no events passed
void test_timeline__extra_case_forwards(void) {
prv_insert_extra_case_items();
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
s_feb_5_midnight + 5 * 60 * 60), 0);
cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[0].header.id));
// check next
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[1].header.id));
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[2].header.id));
cl_assert(!iter_next(&iterator));
}
// 5am, no events passed
void test_timeline__extra_case_none_backwards(void) {
prv_insert_extra_case_items();
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast,
s_feb_5_midnight + 5 * 60 * 60), 2);
}
// 8:16 am. 8:15 event is in future but not 8:00 event
void test_timeline__extra_case_middle_future(void) {
prv_insert_extra_case_items();
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
s_feb_5_midnight + 8 * SECONDS_PER_HOUR + 16 * SECONDS_PER_MINUTE), 0);
#if CAPABILITY_HAS_CORE_NAVIGATION4
cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[1].header.id));
cl_assert(iter_next(&iterator));
#endif
cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[2].header.id));
cl_assert(!iter_next(&iterator));
}
// 8:16 am, 8:00 event has passed but not 8:15 event
void test_timeline__extra_case_middle_past(void) {
prv_insert_extra_case_items();
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast,
s_feb_5_midnight + 8 * 60 * 60 + 16 * 60), 0);
#if !CAPABILITY_HAS_CORE_NAVIGATION4
cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[1].header.id));
cl_assert(iter_next(&iterator));
#endif
cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[0].header.id));
cl_assert(!iter_next(&iterator));
}
// 11 am, all events passed
void test_timeline__extra_case_backwards(void) {
prv_insert_extra_case_items();
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast,
s_feb_5_midnight + 11 * 60 * 60), 0);
cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[2].header.id));
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[1].header.id));
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[0].header.id));
cl_assert(!iter_next(&iterator));
}
// 11 am
void test_timeline__extra_case_none_forwards(void) {
prv_insert_extra_case_items();
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
s_feb_5_midnight + 11 * 60 * 60), 2);
}
void test_timeline__two_iterators(void) {
uint8_t init_net_allocs = fake_pbl_malloc_num_net_allocs();
Iterator iterator1 = {0};
Iterator iterator2 = {0};
TimelineIterState state1 = {0};
TimelineIterState state2 = {0};
TimelineNode *head = NULL;
// first iterator should alloc all the memory for all items
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator1, &state1, &head, TimelineIterDirectionFuture,
1421178000), 0);
// should have one alloc for each node in list, + 1 for the current timelineitem
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(),
init_net_allocs + ARRAY_LENGTH(s_items) + 1);
// second iterator should not alloc any more memory
cl_assert_equal_i(timeline_iter_init(&iterator2, &state2, &head, TimelineIterDirectionFuture,
1421178000), 0);
// should have one alloc for each node in list, + 1 for the current timelineitem
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(),
init_net_allocs + ARRAY_LENGTH(s_items) + 2);
// deinit should free all the memory
timeline_iter_deinit(&iterator1, &state1, &head);
timeline_iter_deinit(&iterator2, &state2, &head);
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), init_net_allocs);
}
void test_timeline__delete_on_iterator(void) {
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture, 1421178000), 0);
// s_items[0] is the earliest pin, followed by s_items[4]
cl_assert_equal_i(pin_db_delete((uint8_t *)&s_items[0].header.id, sizeof(Uuid)), 0);
// next item should be items 4, going back should skip over items[0]
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[4].header.id));
cl_assert(!iter_prev(&iterator));
timeline_iter_deinit(&iterator, &state, &head);
}
void test_timeline__skip_deleted_item(void) {
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture, 1421178000), 0);
cl_assert_equal_i(pin_db_delete((uint8_t *)&s_items[4].header.id, sizeof(Uuid)), 0);
// next item should be items[5], going back should skip over items[4]
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[5].header.id));
cl_assert(iter_prev(&iterator));
cl_assert(!iter_prev(&iterator));
cl_assert(state.node);
timeline_iter_deinit(&iterator, &state, &head);
}
void test_timeline__delete_last_items(void) {
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
timeline_init(&head);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture, 1421178000), 0);
// delete item 2, iterate to the end, check that everything still works
cl_assert_equal_i(pin_db_delete((uint8_t *)&s_items[2].header.id, sizeof(Uuid)), 0);
cl_assert(uuid_equal(&state.pin.header.id, &s_items[0].header.id));
// check second
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[4].header.id));
// check third
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[5].header.id));
// check second again
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[4].header.id));
// check fourth
cl_assert(iter_next(&iterator));
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[1].header.id));
// check fifth
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[3].header.id));
// check sixth
cl_assert(!iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[3].header.id));
cl_assert(uuid_equal(&state.node->id, &s_items[3].header.id));
cl_assert(state.node);
cl_assert(iter_prev(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &s_items[1].header.id));
timeline_iter_deinit(&iterator, &state, &head);
}
void test_timeline__multiday(void) {
TimelineItem multiday_item = {
.header = {
.id = {0x29, 0xac, 0xd8, 0xb5, 0x9, 0xc7, 0x4c, 0x31, 0xbf,
0x6f, 0x3, 0x64, 0xd0, 0x5b, 0x9b, 0xc2},
.parent_id = {0},
.timestamp = 1425312000, // 8:00 AM March 2 2015 PST
.duration = (16 + (2 * 24) + 13) * MINUTES_PER_HOUR, // lasts until March 5 1pm (4 days total)
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
};
cl_assert(timeline_add(&multiday_item));
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
cl_assert_equal_i(timeline_init(&head), S_SUCCESS);
// 1425272400 is 21:00 March 1 2015 PST
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture, 1425272400),
S_SUCCESS);
time_t midnight_march_2_pst = 1425283200;
// day 1
cl_assert(uuid_equal(&state.pin.header.id, &multiday_item.header.id));
cl_assert(uuid_equal(&state.node->id, &multiday_item.header.id));
cl_assert(!state.node->all_day);
cl_assert_equal_i(state.node->timestamp, 1425312000);
cl_assert_equal_i(state.node->duration, 16 * 60);
cl_assert_equal_i(state.current_day, midnight_march_2_pst);
// day 2
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &multiday_item.header.id));
cl_assert(uuid_equal(&state.node->id, &multiday_item.header.id));
cl_assert(state.node->all_day);
cl_assert_equal_i(state.node->timestamp, 1425369600);
cl_assert_equal_i(state.node->duration, MINUTES_PER_DAY);
cl_assert_equal_i(state.current_day, midnight_march_2_pst + SECONDS_PER_DAY);
// no more
cl_assert(!iter_next(&iterator));
// 4 deletes
cl_assert(timeline_iter_remove_node_with_id(&head, &multiday_item.header.id));
cl_assert(timeline_iter_remove_node_with_id(&head, &multiday_item.header.id));
cl_assert(timeline_iter_remove_node_with_id(&head, &multiday_item.header.id));
cl_assert(timeline_iter_remove_node_with_id(&head, &multiday_item.header.id));
cl_assert(!timeline_iter_remove_node_with_id(&head, &multiday_item.header.id));
}
void test_timeline__all_day_single_day(void) {
const time_t midnight_march_3_utc = 1425340800;
TimelineItem all_day_item = {
.header = {
.id = { 0x29, 0xac, 0xd8, 0xb5, 0x09, 0xc7, 0x4c, 0x31,
0xbf, 0x6f, 0x03, 0x64, 0xd0, 0x5b, 0x9b, 0xc2 },
.timestamp = midnight_march_3_utc,
.duration = MINUTES_PER_DAY,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
.all_day = 1,
},
};
cl_assert(timeline_add(&all_day_item));
Iterator iterator = {};
TimelineIterState state = {};
TimelineNode *head = NULL;
cl_assert_equal_i(timeline_init(&head), S_SUCCESS);
const time_t time_21_00_march_1_pst = 1425272400;
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
time_21_00_march_1_pst), S_SUCCESS);
const time_t midnight_march_3_pst = 1425369600;
// day 1
cl_assert(uuid_equal(&state.pin.header.id, &all_day_item.header.id));
cl_assert(uuid_equal(&state.node->id, &all_day_item.header.id));
cl_assert(state.node->all_day);
cl_assert_equal_i(state.node->timestamp, midnight_march_3_pst);
cl_assert_equal_i(state.node->duration, MINUTES_PER_DAY);
cl_assert_equal_i(state.current_day, midnight_march_3_pst);
// no more
cl_assert(!iter_next(&iterator));
// 1 delete
cl_assert(timeline_iter_remove_node_with_id(&head, &all_day_item.header.id));
cl_assert(!timeline_iter_remove_node_with_id(&head, &all_day_item.header.id));
}
void test_timeline__24h_non_all_day_starting_mid_day(void) {
const time_t midnight_march_3_utc = 1425340800;
TimelineItem all_day_item = {
.header = {
.id = { 0x29, 0xac, 0xd8, 0xb5, 0x09, 0xc7, 0x4c, 0x31,
0xbf, 0x6f, 0x03, 0x64, 0xd0, 0x5b, 0x9b, 0xc2 },
.timestamp = midnight_march_3_utc,
.duration = MINUTES_PER_DAY,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
.all_day = 0, // this is a non-all-day event spanning 24h
},
};
cl_assert(timeline_add(&all_day_item));
Iterator iterator = {};
TimelineIterState state = {};
TimelineNode *head = NULL;
cl_assert_equal_i(timeline_init(&head), S_SUCCESS);
const time_t time_21_00_march_1_pst = 1425272400;
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
time_21_00_march_1_pst), S_SUCCESS);
const time_t midnight_march_2_pst = 1425283200;
// day 1
cl_assert(uuid_equal(&state.pin.header.id, &all_day_item.header.id));
cl_assert(uuid_equal(&state.node->id, &all_day_item.header.id));
cl_assert(!state.node->all_day);
cl_assert_equal_i(state.node->timestamp, midnight_march_3_utc);
cl_assert_equal_i(state.node->duration, 8 * MINUTES_PER_HOUR);
cl_assert_equal_i(state.current_day, midnight_march_2_pst);
// day 2
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &all_day_item.header.id));
cl_assert(uuid_equal(&state.node->id, &all_day_item.header.id));
cl_assert(!state.node->all_day);
cl_assert_equal_i(state.node->timestamp, midnight_march_3_utc + SECONDS_PER_DAY);
cl_assert_equal_i(state.node->duration, 0);
cl_assert_equal_i(state.current_day, midnight_march_2_pst + SECONDS_PER_DAY);
// no more
cl_assert(!iter_next(&iterator));
// 2 deletes
cl_assert(timeline_iter_remove_node_with_id(&head, &all_day_item.header.id));
cl_assert(timeline_iter_remove_node_with_id(&head, &all_day_item.header.id));
cl_assert(!timeline_iter_remove_node_with_id(&head, &all_day_item.header.id));
}
void test_timeline__24h_non_all_day_starting_midnight(void) {
const time_t midnight_march_2_pst = 1425283200;
TimelineItem all_day_item = {
.header = {
.id = { 0x29, 0xac, 0xd8, 0xb5, 0x09, 0xc7, 0x4c, 0x31,
0xbf, 0x6f, 0x03, 0x64, 0xd0, 0x5b, 0x9b, 0xc2 },
.timestamp = midnight_march_2_pst,
.duration = MINUTES_PER_DAY,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
.all_day = 0, // this is a non-all-day event spanning 24h
},
};
cl_assert(timeline_add(&all_day_item));
Iterator iterator = {};
TimelineIterState state = {};
TimelineNode *head = NULL;
cl_assert_equal_i(timeline_init(&head), S_SUCCESS);
const time_t time_21_00_march_1_pst = 1425272400;
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
time_21_00_march_1_pst), S_SUCCESS);
// day 1
cl_assert(uuid_equal(&state.pin.header.id, &all_day_item.header.id));
cl_assert(uuid_equal(&state.node->id, &all_day_item.header.id));
cl_assert(state.node->all_day);
cl_assert_equal_i(state.node->timestamp, midnight_march_2_pst);
cl_assert_equal_i(state.node->duration, MINUTES_PER_DAY);
cl_assert_equal_i(state.current_day, midnight_march_2_pst);
// no more
cl_assert(!iter_next(&iterator));
// 1 delete
cl_assert(timeline_iter_remove_node_with_id(&head, &all_day_item.header.id));
cl_assert(!timeline_iter_remove_node_with_id(&head, &all_day_item.header.id));
}
void test_timeline__all_day_multiday(void) {
TimelineItem multiday_item = {
.header = {
.id = {0x29, 0xac, 0xd8, 0xb5, 0x9, 0xc7, 0x4c, 0x31, 0xbf,
0x6f, 0x3, 0x64, 0xd0, 0x5b, 0x9b, 0xc2},
.parent_id = {0},
.timestamp = 1425254400, // midnight March 2 2015 UTC
.duration = 4 * MINUTES_PER_DAY,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
.all_day = 1,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
};
cl_assert(timeline_add(&multiday_item));
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
cl_assert_equal_i(timeline_init(&head), S_SUCCESS);
// 1425272400 is 21:00 March 1 2015 PST
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture, 1425272400),
S_SUCCESS);
time_t midnight_march_2_pst = 1425283200;
// day 1
cl_assert(uuid_equal(&state.pin.header.id, &multiday_item.header.id));
cl_assert(uuid_equal(&state.node->id, &multiday_item.header.id));
cl_assert(state.node->all_day);
cl_assert_equal_i(state.node->timestamp, midnight_march_2_pst);
cl_assert_equal_i(state.node->duration, MINUTES_PER_DAY);
cl_assert_equal_i(state.current_day, midnight_march_2_pst);
// day 2
cl_assert(iter_next(&iterator));
cl_assert(uuid_equal(&state.pin.header.id, &multiday_item.header.id));
cl_assert(uuid_equal(&state.node->id, &multiday_item.header.id));
cl_assert(state.node->all_day);
cl_assert_equal_i(state.node->timestamp, midnight_march_2_pst + SECONDS_PER_DAY);
cl_assert_equal_i(state.node->duration, MINUTES_PER_DAY);
cl_assert_equal_i(state.current_day, midnight_march_2_pst + SECONDS_PER_DAY);
// no more
cl_assert(!iter_next(&iterator));
// 4 deletes
cl_assert(timeline_iter_remove_node_with_id(&head, &multiday_item.header.id));
cl_assert(timeline_iter_remove_node_with_id(&head, &multiday_item.header.id));
cl_assert(timeline_iter_remove_node_with_id(&head, &multiday_item.header.id));
cl_assert(timeline_iter_remove_node_with_id(&head, &multiday_item.header.id));
cl_assert(!timeline_iter_remove_node_with_id(&head, &multiday_item.header.id));
}
void test_timeline__all_day_ios_bug(void) {
TimelineItem item = {
.header = {
.id = {0x29, 0xac, 0xd8, 0xb5, 0x9, 0xc7, 0x4c, 0x31, 0xbf,
0x6f, 0x3, 0x64, 0xd0, 0x5b, 0x9b, 0xc2},
.parent_id = {0},
.timestamp = 1430236800, // 9am Apr 28, 2015 PDT
.duration = MINUTES_PER_DAY,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
.all_day = 1,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
};
cl_assert_equal_i(pin_db_flush(), 0);
cl_assert(timeline_add(&item));
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
cl_assert_equal_i(timeline_init(&head), S_SUCCESS);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
1430236800 - 60 * 60), S_SUCCESS);
const time_t midnight_apr_28_pst = 1430208000;
cl_assert_equal_i(state.node->timestamp, midnight_apr_28_pst);
}
void test_timeline__all_day_ios_bug_2(void) {
TimelineItem item = {
.header = {
.id = {0x29, 0xac, 0xd8, 0xb5, 0x9, 0xc7, 0x4c, 0x31, 0xbf,
0x6f, 0x3, 0x64, 0xd0, 0x5b, 0x9b, 0xc2},
.parent_id = {0},
.timestamp = 1430200800, // 9am Apr 28, 2015 MSK
.duration = MINUTES_PER_DAY,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
.all_day = 1,
},
.attr_list = {
.num_attributes = 0,
.attributes = NULL,
},
.action_group = {
.num_actions = 0,
.actions = NULL,
},
.allocated_buffer = NULL,
};
TimezoneInfo moscow_tz = {
.tm_gmtoff = 3 * 60 * 60, // MSK
};
time_util_update_timezone(&moscow_tz);
cl_assert_equal_i(pin_db_flush(), 0);
cl_assert(timeline_add(&item));
Iterator iterator = {0};
TimelineIterState state = {0};
TimelineNode *head = NULL;
cl_assert_equal_i(timeline_init(&head), S_SUCCESS);
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
1430200800 - 60 * 60), S_SUCCESS);
const time_t midnight_apr_28_msk = 1430168400;
cl_assert_equal_i(state.node->timestamp, midnight_apr_28_msk);
}
void test_timeline__0_duration_all_day(void) {
const time_t midnight_march_3_utc = 1425340800;
TimelineItem all_day_item = {
.header = {
.id = { 0x29, 0xac, 0xd8, 0xb5, 0x09, 0xc7, 0x4c, 0x31,
0xbf, 0x6f, 0x03, 0x64, 0xd0, 0x5b, 0x9b, 0xc2 },
.timestamp = midnight_march_3_utc,
.duration = 0,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
.all_day = 1,
},
};
cl_assert(timeline_add(&all_day_item));
Iterator iterator = {};
TimelineIterState state = {};
TimelineNode *head = NULL;
cl_assert_equal_i(timeline_init(&head), S_SUCCESS);
const time_t time_21_00_march_1_pst = 1425272400;
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
time_21_00_march_1_pst), S_SUCCESS);
const time_t midnight_march_3_pst = 1425369600;
// day 1
cl_assert(uuid_equal(&state.pin.header.id, &all_day_item.header.id));
cl_assert(uuid_equal(&state.node->id, &all_day_item.header.id));
cl_assert(state.node->all_day);
cl_assert_equal_i(state.node->timestamp, midnight_march_3_pst);
cl_assert_equal_i(state.node->duration, MINUTES_PER_DAY);
cl_assert_equal_i(state.current_day, midnight_march_3_pst);
// no more
cl_assert(!iter_next(&iterator));
// 1 delete
cl_assert(timeline_iter_remove_node_with_id(&head, &all_day_item.header.id));
cl_assert(!timeline_iter_remove_node_with_id(&head, &all_day_item.header.id));
}
void test_timeline__0_duration(void) {
const time_t midnight_march_3_utc = 1425340800;
TimelineItem all_day_item = {
.header = {
.id = { 0x29, 0xac, 0xd8, 0xb5, 0x09, 0xc7, 0x4c, 0x31,
0xbf, 0x6f, 0x03, 0x64, 0xd0, 0x5b, 0x9b, 0xc2 },
.timestamp = midnight_march_3_utc,
.duration = 0,
.type = TimelineItemTypePin,
.layout = LayoutIdTest,
.all_day = 0,
},
};
cl_assert(timeline_add(&all_day_item));
Iterator iterator = {};
TimelineIterState state = {};
TimelineNode *head = NULL;
cl_assert_equal_i(timeline_init(&head), S_SUCCESS);
const time_t time_21_00_march_1_pst = 1425272400;
cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture,
time_21_00_march_1_pst), S_SUCCESS);
const time_t midnight_march_2_pst = 1425283200;
// day 1
cl_assert(uuid_equal(&state.pin.header.id, &all_day_item.header.id));
cl_assert(uuid_equal(&state.node->id, &all_day_item.header.id));
cl_assert(!state.node->all_day);
cl_assert_equal_i(state.node->timestamp, midnight_march_3_utc);
cl_assert_equal_i(state.node->duration, 0);
cl_assert_equal_i(state.current_day, midnight_march_2_pst);
// no more
cl_assert(!iter_next(&iterator));
// 1 delete
cl_assert(timeline_iter_remove_node_with_id(&head, &all_day_item.header.id));
cl_assert(!timeline_iter_remove_node_with_id(&head, &all_day_item.header.id));
}