/* * 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 "kernel/events.h" #include "services/normal/blob_db/pin_db.h" #include "services/normal/timeline/calendar.h" #include "services/normal/timeline/event.h" #include "services/normal/timeline/timeline.h" #include "system/logging.h" #include "clar.h" // Stubs //////////////////////////////////////////////////////////////// #include "stubs_analytics.h" #include "stubs_ancs.h" #include "stubs_ancs_notifications.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_blob_db_sync_util.h" #include "stubs_event_loop.h" #include "stubs_event_service_client.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_notification_storage.h" #include "stubs_notifications.h" #include "stubs_passert.h" #include "stubs_phone_call_util.h" #include "stubs_prompt.h" #include "stubs_rand_ptr.h" #include "stubs_regular_timer.h" #include "stubs_reminder_db.h" #include "stubs_session.h" #include "stubs_sleep.h" #include "stubs_system_task.h" #include "stubs_task_watchdog.h" #include "stubs_text_layer_flow.h" #include "stubs_timeline.h" #include "stubs_timeline_pin_window.h" #include "stubs_window_stack.h" // Fakes //////////////////////////////////////////////////////////////// #include "fake_new_timer.h" #include "fake_pbl_malloc.h" #include "fake_pebble_tasks.h" #include "fake_rtc.h" #include "fake_spi_flash.h" #include "fake_settings_file.h" #include "fake_events.h" bool calendar_layout_verify(bool existing_attributes[]) { return true; } bool weather_layout_verify(bool existing_attributes[]) { return true; } const TimelineEventImpl *timeline_peek_get_event_service(void) { return NULL; } // Helpers //////////////////////////////////////////////////////////////// static bool s_in_calendar_event = false; static bool prv_get_calendar_ongoing(void) { PebbleEvent event = fake_event_get_last(); if (event.type == PEBBLE_CALENDAR_EVENT) { s_in_calendar_event = event.calendar.is_event_ongoing; } return s_in_calendar_event; } // Fake pins //////////////////////////////////////////////////////////////// static Attribute title_attr = { .id = AttributeIdTitle, .cstring = "title", }; static TimelineItem item1 = { .header = { .id = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, .timestamp = 10*60, .duration = 10, .type = TimelineItemTypePin, .all_day = false, .layout = LayoutIdCalendar, }, .attr_list = { .num_attributes = 1, .attributes = &title_attr, }, }; static TimelineItem item2 = { .header = { .id = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, .timestamp = 15*60, .duration = 20, .type = TimelineItemTypePin, .all_day = false, .layout = LayoutIdCalendar, }, .attr_list = { .num_attributes = 1, .attributes = &title_attr, }, }; static TimelineItem item3 = { .header = { .id = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, .timestamp = 25*60, .duration = 5, .type = TimelineItemTypePin, .all_day = false, .layout = LayoutIdCalendar, }, .attr_list = { .num_attributes = 1, .attributes = &title_attr, }, }; static TimelineItem item4 = { .header = { .id = {0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, .timestamp = 100*60, .duration = 10, .type = TimelineItemTypePin, .all_day = false, .layout = LayoutIdCalendar, }, .attr_list = { .num_attributes = 1, .attributes = &title_attr, }, }; // NOT A CALENDAR PIN static TimelineItem item5 = { .header = { .id = {0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, .timestamp = 10*60, .duration = 10, .type = TimelineItemTypePin, .all_day = false, .layout = LayoutIdWeather, }, .attr_list = { .num_attributes = 1, .attributes = &title_attr, }, }; // ALL DAY PIN static TimelineItem item6 = { .header = { .id = {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, .timestamp = 100*60, .duration = 10, .type = TimelineItemTypePin, .all_day = true, .layout = LayoutIdCalendar, }, .attr_list = { .num_attributes = 1, .attributes = &title_attr, }, }; // Setup //////////////////////////////////////////////////////////////// void test_calendar__initialize(void) { s_in_calendar_event = false; rtc_set_time(0); fake_event_init(); pin_db_init(); } void test_calendar__cleanup(void) { timeline_event_deinit(); stub_new_timer_cleanup(); fake_settings_file_reset(); } // Tests //////////////////////////////////////////////////////////////// void test_calendar__no_events(void) { timeline_event_init(); cl_assert_equal_i(fake_event_get_count(), 1); cl_assert(!prv_get_calendar_ongoing()); TimerID timer_id = stub_new_timer_get_next(); cl_assert_equal_i(timer_id, TIMER_INVALID_ID); } void test_calendar__init_with_future_event(void) { timeline_event_init(); cl_assert_equal_i(fake_event_get_count(), 1); cl_assert(timeline_add(&item1)); timeline_event_handle_blobdb_event(); cl_assert_equal_i(fake_event_get_count(), 2); cl_assert(!prv_get_calendar_ongoing()); TimerID timer_id = stub_new_timer_get_next(); cl_assert(timer_id != TIMER_INVALID_ID); cl_assert(stub_new_timer_is_scheduled(timer_id)); cl_assert_equal_i(10*60, stub_new_timer_timeout(timer_id) / 1000); } void test_calendar_handle__future_event_added_and_removed(void) { timeline_event_init(); cl_assert_equal_i(fake_event_get_count(), 1); cl_assert(!prv_get_calendar_ongoing()); TimerID timer_id = stub_new_timer_get_next(); cl_assert_equal_i(timer_id, TIMER_INVALID_ID); cl_assert(!stub_new_timer_is_scheduled(timer_id)); cl_assert(timeline_add(&item1)); timeline_event_handle_blobdb_event(); cl_assert_equal_i(fake_event_get_count(), 2); cl_assert(!prv_get_calendar_ongoing()); cl_assert(stub_new_timer_is_scheduled(timer_id)); cl_assert_equal_i(10*60, stub_new_timer_timeout(timer_id) / 1000); cl_assert(timeline_remove(&item1.header.id)); timeline_event_handle_blobdb_event(); cl_assert_equal_i(fake_event_get_count(), 3); cl_assert(!prv_get_calendar_ongoing()); cl_assert(!stub_new_timer_is_scheduled(timer_id)); } void test_calendar__init_with_ongoing_event(void) { cl_assert(timeline_add(&item1)); timeline_event_handle_blobdb_event(); rtc_set_time(15 * 60); timeline_event_init(); cl_assert_equal_i(fake_event_get_count(), 1); cl_assert(prv_get_calendar_ongoing()); TimerID timer_id = stub_new_timer_get_next(); cl_assert(timer_id != TIMER_INVALID_ID); cl_assert(stub_new_timer_is_scheduled(timer_id)); cl_assert_equal_i(5*60, stub_new_timer_timeout(timer_id) / 1000); } void test_calendar_handle__ongoing_event_added_and_removed(void) { rtc_set_time(15 * 60); timeline_event_init(); cl_assert_equal_i(fake_event_get_count(), 1); cl_assert(!prv_get_calendar_ongoing()); TimerID timer_id = stub_new_timer_get_next(); cl_assert_equal_i(timer_id, TIMER_INVALID_ID); cl_assert(!stub_new_timer_is_scheduled(timer_id)); cl_assert(timeline_add(&item1)); timeline_event_handle_blobdb_event(); cl_assert_equal_i(fake_event_get_count(), 2); cl_assert(prv_get_calendar_ongoing()); cl_assert(stub_new_timer_is_scheduled(timer_id)); cl_assert_equal_i(5*60, stub_new_timer_timeout(timer_id) / 1000); cl_assert(timeline_remove(&item1.header.id)); timeline_event_handle_blobdb_event(); cl_assert_equal_i(fake_event_get_count(), 3); cl_assert(!prv_get_calendar_ongoing()); cl_assert(!stub_new_timer_is_scheduled(timer_id)); } void test_calendar__init_with_past_event(void) { cl_assert(timeline_add(&item1)); timeline_event_handle_blobdb_event(); rtc_set_time(30 * 60); timeline_event_init(); cl_assert_equal_i(fake_event_get_count(), 1); cl_assert(!prv_get_calendar_ongoing()); TimerID timer_id = stub_new_timer_get_next(); cl_assert_equal_i(timer_id, TIMER_INVALID_ID); cl_assert(!stub_new_timer_is_scheduled(timer_id)); } void test_calendar_handle__past_event_added_and_removed(void) { rtc_set_time(30 * 60); timeline_event_init(); cl_assert_equal_i(fake_event_get_count(), 1); cl_assert(!prv_get_calendar_ongoing()); TimerID timer_id = stub_new_timer_get_next(); cl_assert_equal_i(timer_id, TIMER_INVALID_ID); cl_assert(!stub_new_timer_is_scheduled(timer_id)); cl_assert(timeline_add(&item1)); timeline_event_handle_blobdb_event(); cl_assert_equal_i(fake_event_get_count(), 2); cl_assert(!prv_get_calendar_ongoing()); cl_assert(!stub_new_timer_is_scheduled(timer_id)); cl_assert(timeline_remove(&item1.header.id)); timeline_event_handle_blobdb_event(); cl_assert_equal_i(fake_event_get_count(), 3); cl_assert(!prv_get_calendar_ongoing()); cl_assert(!stub_new_timer_is_scheduled(timer_id)); } void test_calendar__timer_test(void) { cl_assert(timeline_add(&item1)); timeline_event_handle_blobdb_event(); cl_assert(timeline_add(&item2)); timeline_event_handle_blobdb_event(); cl_assert(timeline_add(&item3)); timeline_event_handle_blobdb_event(); cl_assert(timeline_add(&item4)); timeline_event_handle_blobdb_event(); timeline_event_init(); cl_assert_equal_i(fake_event_get_count(), 1); cl_assert(!prv_get_calendar_ongoing()); TimerID timer_id = stub_new_timer_get_next(); cl_assert(timer_id != TIMER_INVALID_ID); cl_assert(stub_new_timer_is_scheduled(timer_id)); cl_assert_equal_i(10*60, stub_new_timer_timeout(timer_id) / 1000); rtc_set_time(10 * 60); cl_assert(stub_new_timer_fire(timer_id)); cl_assert_equal_i(fake_event_get_count(), 2); cl_assert(prv_get_calendar_ongoing()); cl_assert(stub_new_timer_is_scheduled(timer_id)); cl_assert_equal_i(10*60, stub_new_timer_timeout(timer_id) / 1000); rtc_set_time(20 * 60); cl_assert(stub_new_timer_fire(timer_id)); cl_assert_equal_i(fake_event_get_count(), 3); cl_assert(prv_get_calendar_ongoing()); cl_assert(stub_new_timer_is_scheduled(timer_id)); cl_assert_equal_i(15*60, stub_new_timer_timeout(timer_id) / 1000); rtc_set_time(35 * 60); cl_assert(stub_new_timer_fire(timer_id)); cl_assert_equal_i(fake_event_get_count(), 4); cl_assert(!prv_get_calendar_ongoing()); cl_assert(stub_new_timer_is_scheduled(timer_id)); cl_assert_equal_i(65*60, stub_new_timer_timeout(timer_id) / 1000); rtc_set_time(100 * 60); cl_assert(stub_new_timer_fire(timer_id)); cl_assert_equal_i(fake_event_get_count(), 5); cl_assert(prv_get_calendar_ongoing()); cl_assert(stub_new_timer_is_scheduled(timer_id)); cl_assert_equal_i(10*60, stub_new_timer_timeout(timer_id) / 1000); rtc_set_time(110 * 60); cl_assert(stub_new_timer_fire(timer_id)); cl_assert_equal_i(fake_event_get_count(), 6); cl_assert(!prv_get_calendar_ongoing()); cl_assert(!stub_new_timer_is_scheduled(timer_id)); } void test_calendar__handle_non_calendar_pins(void) { // Insert a random pin (non calendar event) cl_assert(timeline_add(&item5)); timeline_event_handle_blobdb_event(); timeline_event_init(); cl_assert_equal_i(fake_event_get_count(), 1); cl_assert(!prv_get_calendar_ongoing()); TimerID timer_id = stub_new_timer_get_next(); cl_assert_equal_i(timer_id, TIMER_INVALID_ID); } void test_calendar__handle_all_day_pins(void) { // Insert an all day pin cl_assert(timeline_add(&item6)); timeline_event_handle_blobdb_event(); timeline_event_init(); cl_assert_equal_i(fake_event_get_count(), 1); cl_assert(!prv_get_calendar_ongoing()); TimerID timer_id = stub_new_timer_get_next(); cl_assert(!stub_new_timer_is_scheduled(timer_id)); }