diff --git a/doc/nrf/libraries/others/date_time.rst b/doc/nrf/libraries/others/date_time.rst index 51351395cbb..76e446702e2 100644 --- a/doc/nrf/libraries/others/date_time.rst +++ b/doc/nrf/libraries/others/date_time.rst @@ -57,6 +57,13 @@ See the API documentation for more information on these functions. If an application has time-dependent operations immediately after connecting to the LTE network, it should wait for a confirmation indicating that time has been updated. If the :kconfig:option:`CONFIG_DATE_TIME_AUTO_UPDATE` option is not set, the first date-time update cycle (after boot) does not occur until the time set by the :kconfig:option:`CONFIG_DATE_TIME_UPDATE_INTERVAL_SECONDS` option has elapsed. +.. note:: + + Exceptions to the regular date-time update interval set by the :kconfig:option:`CONFIG_DATE_TIME_UPDATE_INTERVAL_SECONDS` Kconfig option occur when + the :c:func:`date_time_update_async` function is called and a new date-time update is triggered and scheduled. + Either retry or regular update interval is used depending on the outcome of the date-time update procedure. + Date-time update from modem through an ``AT%XTIME`` notification does not disturb the regular update interval. + Configuration ************* diff --git a/lib/date_time/date_time_core.c b/lib/date_time/date_time_core.c index 0953e6e062b..3fbef57ba68 100644 --- a/lib/date_time/date_time_core.c +++ b/lib/date_time/date_time_core.c @@ -59,6 +59,8 @@ static void date_time_core_notify_event(enum date_time_evt_type time_source) if (app_evt_handler != NULL) { app_evt_handler(&evt); + } else { + LOG_DBG("No date-time event handler registered"); } } @@ -241,6 +243,8 @@ int date_time_core_now_local(int64_t *local_time_ms) int date_time_core_update_async(date_time_evt_handler_t evt_handler) { + LOG_DBG("Requesting date-time update asynchronously"); + if (evt_handler) { app_evt_handler = evt_handler; } else if (app_evt_handler == NULL) { diff --git a/lib/date_time/date_time_modem.c b/lib/date_time/date_time_modem.c index 17fb8fb1b8d..8568ceaa90c 100644 --- a/lib/date_time/date_time_modem.c +++ b/lib/date_time/date_time_modem.c @@ -73,7 +73,7 @@ int date_time_modem_get(int64_t *date_time_ms, int *date_time_tz) /* Want to match 6 or 7 args */ if (rc != 6 && rc != 7) { LOG_WRN("Did not get time from cellular network (error: %d). " - "This is normal as some cellular networks don't provide it or " + "This may be normal as some cellular networks don't provide it or " "time may not be available yet.", rc); return -ENODATA; } @@ -122,9 +122,6 @@ static void date_time_at_xtime_handler(const char *notif) int err; int tz; - if (notif == NULL) { - return; - } modem_valid_network_time = true; /* Check if current time is valid */ @@ -160,7 +157,8 @@ static void date_time_at_xtime_handler(const char *notif) time_buf_len = hex2bin(time_str_start, 14, time_buf, sizeof(time_buf)); if (time_buf_len < sizeof(time_buf)) { - LOG_ERR("%%XTIME notification decoding failed (ret=%d): %s", time_buf_len, notif); + LOG_ERR("Time value decoding failed from %%XTIME notification (ret=%d): %s", + time_buf_len, notif); return; } @@ -171,6 +169,12 @@ static void date_time_at_xtime_handler(const char *notif) date_time.tm_min = semioctet_to_dec(time_buf[4]); date_time.tm_sec = semioctet_to_dec(time_buf[5]); + /* 3GPP TS 23.040 Section 9.2.3.11 says about the time zone as follows: + * The Time Zone indicates the difference, expressed in quarters of an hour, + * between the local time and GMT. In the first of the two semi octets, + * the first bit (bit 3 of the seventh octet of the TP Service Centre Time Stamp field) + * represents the algebraic sign of this difference (0: positive, 1: negative). + */ tz = semioctet_to_dec(time_buf[6] & 0xF7); if (time_buf[6] & 0x08) { tz = -tz; @@ -235,9 +239,13 @@ void date_time_modem_xtime_subscribe_work_fn(struct k_work *work_item) } } +#if defined(CONFIG_UNITY) +void date_time_modem_on_cfun(int mode, void *ctx) +#else NRF_MODEM_LIB_ON_CFUN(date_time_cfun_hook, date_time_modem_on_cfun, NULL); static void date_time_modem_on_cfun(int mode, void *ctx) +#endif { ARG_UNUSED(ctx); diff --git a/tests/lib/date_time/prj.conf b/tests/lib/date_time/prj.conf index 6753a689cb5..7b3f8b41c67 100644 --- a/tests/lib/date_time/prj.conf +++ b/tests/lib/date_time/prj.conf @@ -14,6 +14,11 @@ CONFIG_MOCK_NRF_MODEM_AT=y CONFIG_LTE_LINK_CONTROL=y CONFIG_POSIX_API=y +CONFIG_DATE_TIME_UPDATE_INTERVAL_SECONDS=3 +CONFIG_DATE_TIME_TOO_OLD_SECONDS=1 +CONFIG_DATE_TIME_RETRY_COUNT=2 +CONFIG_DATE_TIME_RETRY_INTERVAL_SECONDS=1 + # Enable logs if you want to explore them CONFIG_LOG=n CONFIG_DATE_TIME_LOG_LEVEL_DBG=n diff --git a/tests/lib/date_time/src/main.c b/tests/lib/date_time/src/main.c index 1d271c6252d..67a13ddf3d0 100644 --- a/tests/lib/date_time/src/main.c +++ b/tests/lib/date_time/src/main.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -16,17 +18,15 @@ #include #include "cmock_nrf_modem_at.h" +#include "cmock_socket.h" +#include "cmock_sntp.h" -#define TEST_EVENT_MAX_COUNT 10 - -struct test_date_time_cb { - enum date_time_evt_type type; -}; - -struct test_date_time_cb test_date_time_cb_data[TEST_EVENT_MAX_COUNT] = {0}; +/* NOTE: These tests run for few tens of seconds because we are waiting for the + * date-time update and retry intervals. + */ -static int date_time_cb_count_occurred; -static int date_time_cb_count_expected; +#define TEST_EVENT_MAX_COUNT 10 +#define TEST_CB_WAIT_TIME (CONFIG_DATE_TIME_UPDATE_INTERVAL_SECONDS * 1000 + 10000) static void date_time_callback(const struct date_time_evt *evt); @@ -40,15 +40,78 @@ extern void at_monitor_dispatch(const char *at_notif); */ extern void date_time_modem_on_cfun(int mode, void *ctx); -K_SEM_DEFINE(date_time_callback_sem, 0, 1); +struct test_date_time_cb { + enum date_time_evt_type type; + int64_t uptime_start; + int64_t uptime_current; + int64_t time_expected; +}; + +static struct test_date_time_cb test_date_time_cb_data[TEST_EVENT_MAX_COUNT] = {0}; + +static int date_time_cb_count_occurred; +static int date_time_cb_count_expected; + +static K_SEM_DEFINE(date_time_callback_sem, 0, 1); + +static char at_notif[128]; + +/** Fri Aug 07 2020 15:11:30 UTC. */ +static struct tm date_time_global = { + .tm_year = 120, + .tm_mon = 7, + .tm_mday = 7, + .tm_hour = 15, + .tm_min = 11, + .tm_sec = 30 +}; +/** UNIX timestamp equivalent to tm structure date_time_global. */ +static int64_t date_time_global_unix = 1596813090000; + +static struct sockaddr ai_addr_uio_ntp = { + .sa_family = AF_INET, + .data = "100.101.102.103" +}; + +static struct sockaddr ai_addr_google_ntp = { + .sa_family = AF_INET, + .data = "1.2.3.4" +}; + +static struct zsock_addrinfo res_data_uio_ntp = { + .ai_addr = &ai_addr_uio_ntp, + .ai_addrlen = sizeof(struct sockaddr), +}; + +static struct zsock_addrinfo res_data_google_ntp = { + .ai_addr = &ai_addr_google_ntp, + .ai_addrlen = sizeof(struct sockaddr), +}; + +static struct zsock_addrinfo *res_uio_ntp = &res_data_uio_ntp; + +static struct zsock_addrinfo *res_google_ntp = &res_data_google_ntp; + +static struct zsock_addrinfo hints = { + .ai_flags = AI_NUMERICSERV, + .ai_family = AF_UNSPEC /* Allow both IPv4 and IPv6 addresses */ +}; + +static struct sntp_time sntp_time_value = { + .seconds = 42900180919421 +}; void setUp(void) { mock_nrf_modem_at_Init(); + memset(test_date_time_cb_data, 0, sizeof(test_date_time_cb_data)); + date_time_cb_count_occurred = 0; date_time_cb_count_expected = 0; + test_date_time_cb_data[0].uptime_start = k_uptime_get(); + date_time_register_handler(date_time_callback); } @@ -67,12 +130,22 @@ static void date_time_callback(const struct date_time_evt *evt) TEST_ASSERT_MESSAGE(date_time_cb_count_occurred < date_time_cb_count_expected, "date-time event callback called more times than expected"); + TEST_ASSERT_MESSAGE(date_time_cb_count_occurred < TEST_EVENT_MAX_COUNT, + "date-time event callback called more times than TEST_EVENT_MAX_COUNT"); + TEST_ASSERT_EQUAL(test_date_time_cb_data[date_time_cb_count_occurred].type, evt->type); + /* Check time when callback occurred compared to expected time since the test case start */ + TEST_ASSERT_INT64_WITHIN( + 50, + test_date_time_cb_data[date_time_cb_count_occurred].time_expected, + k_uptime_get() - test_date_time_cb_data[0].uptime_start); + date_time_cb_count_occurred++; k_sem_give(&date_time_callback_sem); } +/** Reset to: Fri Aug 07 2020 15:11:30 UTC. */ static void reset_to_valid_time(struct tm *time) { time->tm_year = 120; @@ -83,7 +156,7 @@ static void reset_to_valid_time(struct tm *time) time->tm_sec = 30; } -void test_date_time_invalid_input(void) +void test_date_time_set_invalid_input_fail(void) { int ret; struct tm date_time_dummy; @@ -180,7 +253,7 @@ void test_date_time_invalid_input(void) } -void test_date_time_null_input(void) +void test_date_time_null_input_fail(void) { int ret; @@ -200,7 +273,7 @@ void test_date_time_null_input(void) TEST_ASSERT_EQUAL(-EINVAL, ret); } -void test_date_time_premature_request(void) +void test_date_time_no_valid_time_fail(void) { int ret; int64_t ts_unix_ms = 0; @@ -209,47 +282,39 @@ void test_date_time_premature_request(void) TEST_ASSERT_EQUAL(-ENODATA, ret); TEST_ASSERT_EQUAL(0, ts_unix_ms); - ret = date_time_uptime_to_unix_time_ms(&ts_unix_ms); + ret = date_time_now_local(&ts_unix_ms); TEST_ASSERT_EQUAL(-ENODATA, ret); TEST_ASSERT_EQUAL(0, ts_unix_ms); + ret = date_time_uptime_to_unix_time_ms(&ts_unix_ms); + TEST_ASSERT_EQUAL(-ENODATA, ret); + TEST_ASSERT_EQUAL(0, ts_unix_ms); } -void test_date_time_already_converted(void) +void test_date_time_uptime_to_unix_time_ms_already_converted(void) { + int ret; + /* Wait to get uptime non-zero for date_time_uptime_to_unix_time_ms() call */ k_sleep(K_MSEC(10)); date_time_register_handler(NULL); date_time_clear(); - int ret; - - date_time_register_handler(NULL); - - struct tm date_time_dummy = { - .tm_year = 120, - .tm_mon = 7, - .tm_mday = 7, - .tm_hour = 15, - .tm_min = 11, - .tm_sec = 30 - }; __mock_nrf_modem_at_printf_ExpectAndReturn("AT+CCLK=\"20/08/07,15:11:30+99\"", 0); - ret = date_time_set(&date_time_dummy); + ret = date_time_set(&date_time_global); TEST_ASSERT_EQUAL(0, ret); /** Fri Aug 07 2020 15:11:30 UTC. */ - int64_t ts_unix_ms = 1596813090000; - int64_t ts_unix_ms_prev = 1596813090000; + int64_t ts_unix_ms = date_time_global_unix; ret = date_time_uptime_to_unix_time_ms(&ts_unix_ms); TEST_ASSERT_EQUAL(-EINVAL, ret); - TEST_ASSERT_EQUAL(ts_unix_ms_prev, ts_unix_ms); + TEST_ASSERT_EQUAL(date_time_global_unix, ts_unix_ms); } -void test_date_time_negative_uptime(void) +void test_date_time_uptime_to_unix_time_ms_negative_fail(void) { int ret; @@ -259,7 +324,7 @@ void test_date_time_negative_uptime(void) TEST_ASSERT_EQUAL(-EINVAL, ret); } -void test_date_time_clear(void) +void test_date_time_timestamp_clear(void) { int ret; @@ -275,38 +340,26 @@ void test_date_time_clear(void) TEST_ASSERT_EQUAL(0, ts_unix_ms); } -void test_date_time_conversion(void) +void test_date_time_uptime_to_unix_time_ms(void) { - date_time_register_handler(NULL); - date_time_clear(); - int ret; - struct tm date_time_dummy = { - .tm_year = 120, - .tm_mon = 7, - .tm_mday = 7, - .tm_hour = 15, - .tm_min = 11, - .tm_sec = 30 - }; - - /** UNIX timestamp equivalent to tm structure date_time_dummy. */ - /** Fri Aug 07 2020 15:11:30 UTC. */ - int64_t date_time_utc_unix = 1596813090000; - int64_t date_time_utc_unix_origin = k_uptime_get(); + int64_t date_time_global_unix_origin = k_uptime_get(); int64_t uptime = k_uptime_get(); int64_t ts_unix_ms = 0; int64_t ts_expect = 0; + date_time_register_handler(NULL); + date_time_clear(); + __mock_nrf_modem_at_printf_ExpectAndReturn("AT+CCLK=\"20/08/07,15:11:30+99\"", 0); - ret = date_time_set(&date_time_dummy); + ret = date_time_set(&date_time_global); TEST_ASSERT_EQUAL(0, ret); ret = date_time_now(&ts_unix_ms); TEST_ASSERT_EQUAL(0, ret); - ts_expect = date_time_utc_unix - date_time_utc_unix_origin + k_uptime_get(); + ts_expect = date_time_global_unix - date_time_global_unix_origin + k_uptime_get(); /* We cannot compare exact conversions given by the date time library due to the fact that * the comparing values are based on k_uptime_get(). Use range instead and compare agains an @@ -324,7 +377,7 @@ void test_date_time_conversion(void) ret = date_time_uptime_to_unix_time_ms(&uptime); TEST_ASSERT_EQUAL(0, ret); - ts_expect = date_time_utc_unix - date_time_utc_unix_origin; + ts_expect = date_time_global_unix - date_time_global_unix_origin; TEST_ASSERT_INT64_WITHIN(100, ts_expect, uptime); @@ -333,7 +386,7 @@ void test_date_time_conversion(void) ret = date_time_uptime_to_unix_time_ms(&uptime); TEST_ASSERT_EQUAL(0, ret); - ts_expect = date_time_utc_unix - date_time_utc_unix_origin + k_uptime_get(); + ts_expect = date_time_global_unix - date_time_global_unix_origin + k_uptime_get(); TEST_ASSERT_INT64_WITHIN(100, ts_expect, uptime); @@ -344,40 +397,577 @@ void test_date_time_conversion(void) ret = date_time_uptime_to_unix_time_ms(&uptime); TEST_ASSERT_EQUAL(0, ret); - ts_expect = date_time_utc_unix - date_time_utc_unix_origin + k_uptime_get(); + ts_expect = date_time_global_unix - date_time_global_unix_origin + k_uptime_get(); TEST_ASSERT_INT64_WITHIN(100, ts_expect, uptime); } -void test_date_time_validity(void) +void test_date_time_is_valid(void) { + int ret; + int64_t ts_unix_ms = 0; + /* Wait to get uptime non-zero for last date_time_is_valid() call */ k_sleep(K_MSEC(10)); date_time_register_handler(NULL); date_time_clear(); - int ret; - struct tm date_time_dummy = { - .tm_year = 120, - .tm_mon = 7, - .tm_mday = 7, - .tm_hour = 15, - .tm_min = 11, - .tm_sec = 30 - }; - ret = date_time_is_valid(); TEST_ASSERT_EQUAL(false, ret); + /* Local time not valid here because there is no valid time */ + ret = date_time_is_valid_local(); + TEST_ASSERT_EQUAL(false, ret); + __mock_nrf_modem_at_printf_ExpectAndReturn("AT+CCLK=\"20/08/07,15:11:30+99\"", 0); - /** UNIX timestamp equavivalent to tm structure date_time_dummy. */ - /** Fri Aug 07 2020 15:11:30 UTC. */ - ret = date_time_set(&date_time_dummy); + ret = date_time_set(&date_time_global); + TEST_ASSERT_EQUAL(0, ret); + + ret = date_time_is_valid(); + TEST_ASSERT_EQUAL(true, ret); + + /* Local time still not valid here because timezone is not set with date_time_set() + * but it requires modem time so that's checked elsewhere. + */ + ret = date_time_is_valid_local(); + TEST_ASSERT_EQUAL(false, ret); + + ret = date_time_now(&ts_unix_ms); TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_INT64_WITHIN(100, date_time_global_unix, ts_unix_ms); + + ts_unix_ms = 0; + ret = date_time_now_local(&ts_unix_ms); + TEST_ASSERT_EQUAL(-EAGAIN, ret); + TEST_ASSERT_EQUAL(0, ts_unix_ms); +} + +void test_date_time_xtime_subscribe_fail(void) +{ + /* %XTIME subscription not sent with wrong functional mode */ + date_time_modem_on_cfun(LTE_LC_FUNC_MODE_OFFLINE, NULL); + k_sleep(K_MSEC(1)); + + /* %XTIME subscription fails */ + __mock_nrf_modem_at_printf_ExpectAndReturn("AT%XTIME=1", -ENOMEM); + date_time_modem_on_cfun(LTE_LC_FUNC_MODE_NORMAL, NULL); + k_sleep(K_MSEC(1)); +} + +/** + * Test initial auto update with NTP. Modem time is not queried because NTP is configured and + * %XTIME notification hasn't been received. + * + * Note: This test must be before any %XTIME notification tests because it will set + * modem_valid_network_time and we cannot revert it back to 'false' anymore. + */ +void test_date_time_initial_auto_update_ntp(void) +{ + date_time_cb_count_expected = 1; + test_date_time_cb_data[0].type = DATE_TIME_OBTAINED_NTP; + test_date_time_cb_data[0].time_expected = 1000; + + date_time_clear(); + + __mock_nrf_modem_at_printf_ExpectAndReturn("AT%XTIME=1", 0); + date_time_modem_on_cfun(LTE_LC_FUNC_MODE_NORMAL, NULL); + + k_sleep(K_MSEC(1)); + + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CEREG?", "+CEREG: %*u,%hu,%*[^,],\"%x\",", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint16(LTE_LC_NW_REG_REGISTERED_HOME); + + __cmock_zsock_getaddrinfo_ExpectAndReturn("ntp.uio.no", "123", &hints, NULL, 0); + __cmock_zsock_getaddrinfo_IgnoreArg_res(); + __cmock_zsock_getaddrinfo_ReturnThruPtr_res(&res_uio_ntp); + + __cmock_sntp_init_ExpectAndReturn(NULL, &ai_addr_uio_ntp, sizeof(struct sockaddr), 0); + __cmock_sntp_init_IgnoreArg_ctx(); + + __cmock_sntp_query_ExpectAndReturn(NULL, 5000, NULL, 0); + __cmock_sntp_query_IgnoreArg_ctx(); + __cmock_sntp_query_IgnoreArg_time(); + __cmock_sntp_query_ReturnThruPtr_time(&sntp_time_value); + + __cmock_zsock_freeaddrinfo_ExpectAnyArgs(); + __cmock_sntp_close_ExpectAnyArgs(); + + /* Setting modem time fails */ + __mock_nrf_modem_at_printf_ExpectAndReturn("AT+CCLK=\"22/08/13,18:03:41+99\"", -ENOMEM); + + strcpy(at_notif, "+CEREG: 1,\"002F\",\"0012BEEF\",7,,,\"00000101\",\"00010011\"\r\n"); + at_monitor_dispatch(at_notif); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); +} + +/** Test initial auto update from modem. */ +void test_date_time_initial_auto_update_xtime_notif(void) +{ + int ret; + + date_time_cb_count_expected = 1; + test_date_time_cb_data[0].type = DATE_TIME_OBTAINED_MODEM; + + date_time_clear(); + + ret = date_time_is_valid(); + TEST_ASSERT_EQUAL(false, ret); + + ret = date_time_is_valid_local(); + TEST_ASSERT_EQUAL(false, ret); + + __mock_nrf_modem_at_printf_ExpectAndReturn("AT%XTIME=1", 0); + date_time_modem_on_cfun(LTE_LC_FUNC_MODE_ACTIVATE_LTE, NULL); + + k_sleep(K_MSEC(1)); + + /* Negative timezone. */ + strcpy(at_notif, "%XTIME: \"0A\",\"4290018091940A\",\"01\"\r\n"); + at_monitor_dispatch(at_notif); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); ret = date_time_is_valid(); TEST_ASSERT_EQUAL(true, ret); + + ret = date_time_is_valid_local(); + TEST_ASSERT_EQUAL(true, ret); +} + +/* This is after XTIME test because we get timezone there so local time works. */ +void test_date_time_now_local(void) +{ + int ret; + int64_t ts_unix_ms = 0; + + ret = date_time_is_valid_local(); + TEST_ASSERT_EQUAL(true, ret); + + ret = date_time_now_local(&ts_unix_ms); + TEST_ASSERT_EQUAL(0, ret); + + /* TODO: Verify the time */ +} + +/** Test initial auto update from modem. */ +void test_date_time_use_previously_obtained_time(void) +{ + /* Previously found time is available and source is the same */ + date_time_cb_count_expected = 1; + test_date_time_cb_data[0].type = DATE_TIME_OBTAINED_MODEM; + + date_time_update_async(NULL); + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); +} + +K_SEM_DEFINE(date_time_callback_custom_sem, 0, 1); + +/** Callback that date_time library will call when an event is received. */ +static void date_time_callback_custom(const struct date_time_evt *evt) +{ + /* Verify that type is previously returned type, i.e., modem */ + TEST_ASSERT_EQUAL(DATE_TIME_OBTAINED_MODEM, evt->type); + k_sem_give(&date_time_callback_custom_sem); +} + +void test_date_time_update_async_different_handler(void) +{ + /* Normal handler not used in this test case so no callbacks there */ + date_time_cb_count_expected = 0; + + /* Custom date-time callback handler */ + date_time_update_async(date_time_callback_custom); + k_sem_take(&date_time_callback_custom_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* No date-time callback handler at all. We cannot really verify that the procedure has + * been executed properly but at least we can check that it doesn't crash or send events. + */ + date_time_register_handler(NULL); + date_time_update_async(NULL); + k_sleep(K_MSEC(1)); +} + +/** Test update from modem. */ +void test_date_time_1st_update_modem(void) +{ + date_time_cb_count_expected = 1; + test_date_time_cb_data[0].type = DATE_TIME_OBTAINED_MODEM; + test_date_time_cb_data[0].time_expected = 3000; + + /* Not home or roaming so ignored. */ + strcpy(at_notif, "+CEREG: 2\r\n"); + at_monitor_dispatch(at_notif); + k_sleep(K_MSEC(1)); + + strcpy(at_notif, "+CEREG: 5,\"002F\",\"0012BEEF\",7,,,\"00000101\",\"00010011\"\r\n"); + at_monitor_dispatch(at_notif); + k_sleep(K_MSEC(1)); + + /* %XTIME notification ignored because current time is not too old */ + k_sleep(K_MSEC(300)); + strcpy(at_notif, "%XTIME: ,\"42900180919421\",\"01\"\r\n"); + at_monitor_dispatch(at_notif); + k_sleep(K_MSEC(1)); + + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", 7); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(24); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(30); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(20); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(40); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(52); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(12); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); +} + +void test_date_time_set_dont_set_modem_time_when_it_exists(void) +{ + int ret; + int64_t ts_unix_ms = 0; + + date_time_cb_count_expected = 1; + test_date_time_cb_data[0].type = DATE_TIME_OBTAINED_EXT; + test_date_time_cb_data[0].time_expected = 0; + + /* Modem has time so check that modem time is not set anymore with "AT+CCLK="*/ + ret = date_time_set(&date_time_global); + TEST_ASSERT_EQUAL(0, ret); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + ret = date_time_now(&ts_unix_ms); + TEST_ASSERT_EQUAL(0, ret); + + TEST_ASSERT_INT64_WITHIN(100, date_time_global_unix, ts_unix_ms); +} + +void test_date_time_update_cycle_and_async(void) +{ + date_time_cb_count_expected = 6; + test_date_time_cb_data[0].type = DATE_TIME_OBTAINED_MODEM; + test_date_time_cb_data[0].time_expected = 3000; + test_date_time_cb_data[1].type = DATE_TIME_OBTAINED_MODEM; + test_date_time_cb_data[1].time_expected = 3500; + test_date_time_cb_data[2].type = DATE_TIME_OBTAINED_MODEM; + test_date_time_cb_data[2].time_expected = 5000; + test_date_time_cb_data[3].type = DATE_TIME_OBTAINED_MODEM; + test_date_time_cb_data[3].time_expected = 8000; + test_date_time_cb_data[4].type = DATE_TIME_OBTAINED_MODEM; + test_date_time_cb_data[4].time_expected = 9000; + test_date_time_cb_data[5].type = DATE_TIME_OBTAINED_MODEM; + test_date_time_cb_data[5].time_expected = 11000; + + /* Normal cycle at 3s */ + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", 7); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(24); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(30); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(20); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(40); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(52); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(12); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* Asynchronous update with previous time at 3.5s */ + k_sleep(K_MSEC(500)); + date_time_update_async(NULL); + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* Asynchronous update at 5s*/ + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", 6); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(24); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(30); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(20); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(40); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(52); + + k_sleep(K_MSEC(1500)); + date_time_update_async(NULL); + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* Normal cycle at 8s*/ + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", 7); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(24); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(30); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(20); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(40); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(52); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(12); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* Time taken into use from %XTIME notification at 9s */ + k_sleep(K_MSEC(1000)); + strcpy(at_notif, "%XTIME: ,\"42900180919421\",\"01\"\r\n"); + at_monitor_dispatch(at_notif); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* XTIME doesn't delay normal cycle unlike date_time_update_async */ + + /* Normal cycle at 11s */ + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", 7); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(24); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(30); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(20); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(40); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(52); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(12); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); +} + +void test_date_time_update_cycle_with_failing_async(void) +{ + date_time_cb_count_expected = 5; + test_date_time_cb_data[0].type = DATE_TIME_OBTAINED_MODEM; + test_date_time_cb_data[0].time_expected = 3000; + test_date_time_cb_data[1].type = DATE_TIME_NOT_OBTAINED; + test_date_time_cb_data[1].time_expected = 4000; + test_date_time_cb_data[2].type = DATE_TIME_NOT_OBTAINED; + test_date_time_cb_data[2].time_expected = 5000; + test_date_time_cb_data[3].type = DATE_TIME_NOT_OBTAINED; + test_date_time_cb_data[3].time_expected = 6000; + test_date_time_cb_data[4].type = DATE_TIME_OBTAINED_MODEM; + test_date_time_cb_data[4].time_expected = 9000; + + /* Normal cycle at 3s */ + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", 7); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(24); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(30); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(20); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(40); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(52); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(12); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* Failing asynchronous update at 4s */ + k_sleep(K_MSEC(1000)); + + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", -ENOMEM); + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CEREG?", "+CEREG: %*u,%hu,%*[^,],\"%x\",", -ENOMEM); + __mock_nrf_modem_at_scanf_ReturnVarg_uint16(LTE_LC_NW_REG_UNKNOWN); + + date_time_update_async(NULL); + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* Failing retry after asynchronous update at 5s */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", -ENOMEM); + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CEREG?", "+CEREG: %*u,%hu,%*[^,],\"%x\",", -ENOMEM); + __mock_nrf_modem_at_scanf_ReturnVarg_uint16(LTE_LC_NW_REG_UNKNOWN); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* Failing retry after asynchronous update at 6s */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", -ENOMEM); + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CEREG?", "+CEREG: %*u,%hu,%*[^,],\"%x\",", -ENOMEM); + __mock_nrf_modem_at_scanf_ReturnVarg_uint16(LTE_LC_NW_REG_UNKNOWN); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* Normal cycle at 6s*/ + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", 7); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(24); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(30); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(20); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(40); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(52); + __mock_nrf_modem_at_scanf_ReturnVarg_uint32(12); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); +} + +/** Test all retries failing with different NTP failures. */ +void test_date_time_update_all_retry_ntp_fail(void) +{ + date_time_cb_count_expected = 3; + test_date_time_cb_data[0].type = DATE_TIME_NOT_OBTAINED; + test_date_time_cb_data[0].time_expected = 3000; + test_date_time_cb_data[1].type = DATE_TIME_NOT_OBTAINED; + test_date_time_cb_data[1].time_expected = 4000; + test_date_time_cb_data[2].type = DATE_TIME_NOT_OBTAINED; + /* TODO: The 2nd retry is already 4.5 due to async call. Is that intentional? */ + test_date_time_cb_data[2].time_expected = 4500; + + /* 1st date-time update */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", -ENOMEM); + + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CEREG?", "+CEREG: %*u,%hu,%*[^,],\"%x\",", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint16(LTE_LC_NW_REG_REGISTERED_HOME); + + /* NTP fails in NTP query for UIO */ + __cmock_zsock_getaddrinfo_ExpectAndReturn("ntp.uio.no", "123", &hints, NULL, 0); + __cmock_zsock_getaddrinfo_IgnoreArg_res(); + __cmock_zsock_getaddrinfo_ReturnThruPtr_res(&res_uio_ntp); + + __cmock_sntp_init_ExpectAnyArgsAndReturn(0); + __cmock_sntp_query_ExpectAndReturn(NULL, 5000, NULL, -1); + __cmock_sntp_query_IgnoreArg_ctx(); + __cmock_sntp_query_IgnoreArg_time(); + __cmock_sntp_query_ReturnThruPtr_time(&sntp_time_value); + + __cmock_zsock_freeaddrinfo_ExpectAnyArgs(); + __cmock_sntp_close_ExpectAnyArgs(); + + /* NTP fails in DNS query for Google */ + __cmock_zsock_getaddrinfo_ExpectAndReturn("time.google.com", "123", &hints, NULL, -1); + __cmock_zsock_getaddrinfo_IgnoreArg_res(); + __cmock_zsock_getaddrinfo_ReturnThruPtr_res(&res_google_ntp); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* 1st retry */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", -ENOMEM); + + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CEREG?", "+CEREG: %*u,%hu,%*[^,],\"%x\",", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint16(LTE_LC_NW_REG_REGISTERED_ROAMING); + + /* NTP fails in SNTP init for UIO */ + __cmock_zsock_getaddrinfo_ExpectAndReturn("ntp.uio.no", "123", &hints, NULL, 0); + __cmock_zsock_getaddrinfo_IgnoreArg_res(); + __cmock_zsock_getaddrinfo_ReturnThruPtr_res(&res_uio_ntp); + + __cmock_sntp_init_ExpectAnyArgsAndReturn(-1); + + __cmock_zsock_freeaddrinfo_ExpectAnyArgs(); + __cmock_sntp_close_ExpectAnyArgs(); + + /* NTP fails in NTP query for Google */ + __cmock_zsock_getaddrinfo_ExpectAndReturn("time.google.com", "123", &hints, NULL, 0); + __cmock_zsock_getaddrinfo_IgnoreArg_res(); + __cmock_zsock_getaddrinfo_ReturnThruPtr_res(&res_google_ntp); + + __cmock_sntp_init_ExpectAnyArgsAndReturn(0); + __cmock_sntp_query_ExpectAndReturn(NULL, 5000, NULL, -1); + __cmock_sntp_query_IgnoreArg_ctx(); + __cmock_sntp_query_IgnoreArg_time(); + __cmock_sntp_query_ReturnThruPtr_time(&sntp_time_value); + + __cmock_zsock_freeaddrinfo_ExpectAnyArgs(); + __cmock_sntp_close_ExpectAnyArgs(); + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* 2nd retry with asynchronous update at 4.5s */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", -ENOMEM); + + /* NTP not even tried because no connection to LTE network */ + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CEREG?", "+CEREG: %*u,%hu,%*[^,],\"%x\",", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint16(LTE_LC_NW_REG_REGISTERED_HOME); + + /* NTP fails in NTP query for UIO */ + __cmock_zsock_getaddrinfo_ExpectAndReturn("ntp.uio.no", "123", &hints, NULL, 0); + __cmock_zsock_getaddrinfo_IgnoreArg_res(); + __cmock_zsock_getaddrinfo_ReturnThruPtr_res(&res_uio_ntp); + + __cmock_sntp_init_ExpectAnyArgsAndReturn(0); + __cmock_sntp_query_ExpectAndReturn(NULL, 5000, NULL, -1); + __cmock_sntp_query_IgnoreArg_ctx(); + __cmock_sntp_query_IgnoreArg_time(); + __cmock_sntp_query_ReturnThruPtr_time(&sntp_time_value); + + __cmock_zsock_freeaddrinfo_ExpectAnyArgs(); + __cmock_sntp_close_ExpectAnyArgs(); + + /* NTP fails in NTP query for Google */ + __cmock_zsock_getaddrinfo_ExpectAndReturn("time.google.com", "123", &hints, NULL, 0); + __cmock_zsock_getaddrinfo_IgnoreArg_res(); + __cmock_zsock_getaddrinfo_ReturnThruPtr_res(&res_google_ntp); + + __cmock_sntp_init_ExpectAnyArgsAndReturn(0); + __cmock_sntp_query_ExpectAndReturn(NULL, 5000, NULL, -1); + __cmock_sntp_query_IgnoreArg_ctx(); + __cmock_sntp_query_IgnoreArg_time(); + __cmock_sntp_query_ReturnThruPtr_time(&sntp_time_value); + + __cmock_zsock_freeaddrinfo_ExpectAnyArgs(); + __cmock_sntp_close_ExpectAnyArgs(); + + k_sleep(K_MSEC(500)); + date_time_update_async(NULL); + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); +} + +/* Test all retries failing due to LTE status checked by NTP procedure. */ +void test_date_time_update_all_retry_ntp_ltestatus_fail(void) +{ + date_time_cb_count_expected = 3; + test_date_time_cb_data[0].type = DATE_TIME_NOT_OBTAINED; + test_date_time_cb_data[0].time_expected = 3000; + test_date_time_cb_data[1].type = DATE_TIME_NOT_OBTAINED; + test_date_time_cb_data[1].time_expected = 4000; + test_date_time_cb_data[2].type = DATE_TIME_NOT_OBTAINED; + test_date_time_cb_data[2].time_expected = 5000; + + /* 1st trial */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", -ENOMEM); + + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CEREG?", "+CEREG: %*u,%hu,%*[^,],\"%x\",", -ENOMEM); + __mock_nrf_modem_at_scanf_ReturnVarg_uint16(LTE_LC_NW_REG_UNKNOWN); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* 1st retry */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", -ENOMEM); + + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CEREG?", "+CEREG: %*u,%hu,%*[^,],\"%x\",", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint16(LTE_LC_NW_REG_UNKNOWN); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); + + /* 2nd retry */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CCLK?", "+CCLK: \"%u/%u/%u,%u:%u:%u%d", -ENOMEM); + + __mock_nrf_modem_at_scanf_ExpectAndReturn("AT+CEREG?", "+CEREG: %*u,%hu,%*[^,],\"%x\",", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_uint16(LTE_LC_NW_REG_SEARCHING); + + k_sem_take(&date_time_callback_sem, K_MSEC(TEST_CB_WAIT_TIME)); +} + +/** Test initial auto update from modem. */ +void test_date_time_xtime_notif_fail(void) +{ + /* No comma */ + strcpy(at_notif, "%XTIME: \"42900180919421\" \"01\"\r\n"); + at_monitor_dispatch(at_notif); + k_sleep(K_MSEC(1)); + + /* Too short */ + strcpy(at_notif, "%XTIME: ,\"321\",\"01\"\r\n"); + at_monitor_dispatch(at_notif); + k_sleep(K_MSEC(1)); + + /* Time value contains letters */ + strcpy(at_notif, "%XTIME: ,\"42900180moikka\",\"01\"\r\n"); + at_monitor_dispatch(at_notif); + k_sleep(K_MSEC(1)); + + /* No double quotes for time value */ + strcpy(at_notif, "%XTIME: ,42900180919421,\"01\"\r\n"); + at_monitor_dispatch(at_notif); + k_sleep(K_MSEC(1)); } /* This is needed because AT Monitor library is initialized in SYS_INIT. */