/* * 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 "test_jerry_port_common.h" #include "test_rocky_common.h" #include "applib/rockyjs/api/rocky_api_global.h" #include "applib/rockyjs/api/rocky_api_tickservice.h" #include "applib/rockyjs/pbl_jerry_port.h" // Standard #include "string.h" // Fakes #include "fake_app_timer.h" #include "fake_logging.h" #if EMSCRIPTEN #include "fake_time_timeshift_js.h" #else #include "fake_time.h" #endif // Stubs #include "stubs_app_manager.h" #include "stubs_app_state.h" #include "stubs_logging.h" #include "stubs_passert.h" #include "stubs_pbl_malloc.h" #include "stubs_resources.h" #include "stubs_sleep.h" #include "stubs_serial.h" #include "stubs_syscalls.h" #include "stubs_sys_exit.h" size_t heap_bytes_free(void) { return 123456; } void tick_timer_service_handle_time_change(void) {} MockCallRecordings s_tick_timer_service_subscribe; void tick_timer_service_subscribe(TimeUnits tick_units, TickHandler handler) { record_mock_call(s_tick_timer_service_subscribe) { .tick_units = tick_units, }; } void test_rocky_api_tickservice__initialize(void) { fake_app_timer_init(); rocky_runtime_context_init(); jerry_init(JERRY_INIT_EMPTY); s_tick_timer_service_subscribe = (MockCallRecordings){0}; s_log_internal__expected = NULL; } void test_rocky_api_tickservice__cleanup(void) { jerry_cleanup(); rocky_runtime_context_deinit(); } static const RockyGlobalAPI *s_api[] = { &TICKSERVICE_APIS, NULL, }; void test_rocky_api_tickservice__provides_events(void) { rocky_global_init(s_api); cl_assert_equal_i(0, s_tick_timer_service_subscribe.call_count); cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange")); cl_assert_equal_b(false, rocky_global_has_event_handlers("minutechange")); cl_assert_equal_b(false, rocky_global_has_event_handlers("hourchange")); cl_assert_equal_b(false, rocky_global_has_event_handlers("daychange")); EXECUTE_SCRIPT("_rocky.on('daychange', function() {});"); cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange")); cl_assert_equal_b(false, rocky_global_has_event_handlers("minutechange")); cl_assert_equal_b(false, rocky_global_has_event_handlers("hourchange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange")); cl_assert_equal_i(1, s_tick_timer_service_subscribe.call_count); cl_assert_equal_i(DAY_UNIT | MONTH_UNIT | YEAR_UNIT, s_tick_timer_service_subscribe.last_call.tick_units); EXECUTE_SCRIPT( "var hourHandler = function() {};\n" "_rocky.on('hourchange', hourHandler);\n" ); cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange")); cl_assert_equal_b(false, rocky_global_has_event_handlers("minutechange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange")); cl_assert_equal_i(2, s_tick_timer_service_subscribe.call_count); cl_assert_equal_i(HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT, s_tick_timer_service_subscribe.last_call.tick_units); EXECUTE_SCRIPT("_rocky.on('minutechange', function() {});"); cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("minutechange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange")); cl_assert_equal_i(3, s_tick_timer_service_subscribe.call_count); cl_assert_equal_i(MINUTE_UNIT | HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT, s_tick_timer_service_subscribe.last_call.tick_units); // register for minute again EXECUTE_SCRIPT("_rocky.on('minutechange', function() {});"); cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("minutechange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange")); cl_assert_equal_i(4, s_tick_timer_service_subscribe.call_count); cl_assert_equal_i(MINUTE_UNIT | HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT, s_tick_timer_service_subscribe.last_call.tick_units); EXECUTE_SCRIPT("_rocky.on('secondchange', function() {});"); cl_assert_equal_b(true, rocky_global_has_event_handlers("secondchange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("minutechange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange")); cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange")); cl_assert_equal_i(5, s_tick_timer_service_subscribe.call_count); cl_assert_equal_i(SECOND_UNIT | MINUTE_UNIT | HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT, s_tick_timer_service_subscribe.last_call.tick_units); } void prv_tick_handler(struct tm *tick_time, TimeUnits units_changed); void test_rocky_api_tickservice__calls_handlers(void) { rocky_global_init(s_api); EXECUTE_SCRIPT( "var s = 0;\n" "var m = 0;\n" "var h = 0;\n" "var d = 0;\n" "_rocky.on('secondchange', function(e) {s++;});" "_rocky.on('minutechange', function(e) {m++;});" "_rocky.on('hourchange', function(e) {h++;});" "_rocky.on('daychange', function(e) {d++;});" ); // subscribing already triggers a call ASSERT_JS_GLOBAL_EQUALS_I("s", 1); ASSERT_JS_GLOBAL_EQUALS_I("m", 1); ASSERT_JS_GLOBAL_EQUALS_I("h", 1); ASSERT_JS_GLOBAL_EQUALS_I("d", 1); // all handlers will be called as year change means minute change prv_tick_handler(NULL, YEAR_UNIT); ASSERT_JS_GLOBAL_EQUALS_I("s", 2); ASSERT_JS_GLOBAL_EQUALS_I("m", 2); ASSERT_JS_GLOBAL_EQUALS_I("h", 2); ASSERT_JS_GLOBAL_EQUALS_I("d", 2); // same here, each time a day changes, a second changes, too prv_tick_handler(NULL, MINUTE_UNIT | DAY_UNIT); ASSERT_JS_GLOBAL_EQUALS_I("s", 3); ASSERT_JS_GLOBAL_EQUALS_I("m", 3); ASSERT_JS_GLOBAL_EQUALS_I("h", 3); ASSERT_JS_GLOBAL_EQUALS_I("d", 3); prv_tick_handler(NULL, HOUR_UNIT); ASSERT_JS_GLOBAL_EQUALS_I("s", 4); ASSERT_JS_GLOBAL_EQUALS_I("m", 4); ASSERT_JS_GLOBAL_EQUALS_I("h", 4); ASSERT_JS_GLOBAL_EQUALS_I("d", 3); prv_tick_handler(NULL, MINUTE_UNIT); ASSERT_JS_GLOBAL_EQUALS_I("s", 5); ASSERT_JS_GLOBAL_EQUALS_I("m", 5); ASSERT_JS_GLOBAL_EQUALS_I("h", 4); ASSERT_JS_GLOBAL_EQUALS_I("d", 3); prv_tick_handler(NULL, SECOND_UNIT); ASSERT_JS_GLOBAL_EQUALS_I("s", 6); ASSERT_JS_GLOBAL_EQUALS_I("m", 5); ASSERT_JS_GLOBAL_EQUALS_I("h", 4); ASSERT_JS_GLOBAL_EQUALS_I("d", 3); } void test_rocky_api_tickservice__event_types(void) { rocky_global_init(s_api); EXECUTE_SCRIPT( "var s = null;\n" "var m = null;\n" "var h = null;\n" "var d = null;\n" "_rocky.on('secondchange', function(e) {s = e.type;});" "_rocky.on('minutechange', function(e) {m = e.type;});" "_rocky.on('hourchange', function(e) {h = e.type;});" "_rocky.on('daychange', function(e) {d = e.type;});" ); // subscribing already triggers a call ASSERT_JS_GLOBAL_EQUALS_S("s", "secondchange"); ASSERT_JS_GLOBAL_EQUALS_S("m", "minutechange"); ASSERT_JS_GLOBAL_EQUALS_S("h", "hourchange"); ASSERT_JS_GLOBAL_EQUALS_S("d", "daychange"); } void test_rocky_api_tickservice__error_in_handler_on_register(void) { rocky_global_init(s_api); s_log_internal__expected = (const char *[]){ "Unhandled exception", " secondchange", NULL }; EXECUTE_SCRIPT( "_rocky.on('secondchange', function(e) { throw e.type; });" ); cl_assert(*s_log_internal__expected == NULL); } void test_rocky_api_tickservice__provides_event_date(void) { rocky_global_init(s_api); s_log_internal__expected = (const char *[]){ NULL }; const time_t cur_time = 1458250851; // Thu Mar 17 21:40:51 2016 UTC // Thu Mar 17 14:40:51 2016 PDT const uint16_t cur_millis = 123; fake_time_init(cur_time, cur_millis); EXECUTE_SCRIPT( "var s = null;\n" "var m = null;\n" "var h = null;\n" "var d = null;\n" "_rocky.on('secondchange', function(e) { s = e.date.getSeconds(); });\n" "_rocky.on('minutechange', function(e) { m = e.date.getMinutes(); });\n" "_rocky.on('hourchange', function(e) { h = e.date.getHours(); });\n" "_rocky.on('daychange', function(e) { d = e.date.getDate(); });\n" ); ASSERT_JS_GLOBAL_EQUALS_D("s", 51.0); ASSERT_JS_GLOBAL_EQUALS_D("m", 40.0); ASSERT_JS_GLOBAL_EQUALS_D("h", 21.0); ASSERT_JS_GLOBAL_EQUALS_D("d", 17.0); EXECUTE_SCRIPT( "s = null;\n" "m = null;\n" "h = null;\n" "d = null;\n" ); struct tm tm = { .tm_sec = 1, .tm_min = 2, .tm_hour = 3, .tm_mday = 4, .tm_mon = 5, .tm_year = 116, // 2016 }; prv_tick_handler(&tm, SECOND_UNIT | MINUTE_UNIT | HOUR_UNIT | DAY_UNIT); ASSERT_JS_GLOBAL_EQUALS_D("s", 1.0); ASSERT_JS_GLOBAL_EQUALS_D("m", 2.0); ASSERT_JS_GLOBAL_EQUALS_D("h", 3.0); ASSERT_JS_GLOBAL_EQUALS_D("d", 4.0); cl_assert(*s_log_internal__expected == NULL); }