/* * 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)); }