tests/drivers/rtc: Add unit tests for RTC devices

This test suite adds tests for the following:

- Setting and getting time
- Validating time is incrementing correctly
- Validating behavior of alarms with callback disabled
- Validating behavior of alarms with callback enabled
- Validating update callback

The test suite uses the devicetree  alias rtc to find
the device to test.

Signed-off-by: Bjarki Arge Andreasen <baa@trackunit.com>
This commit is contained in:
Bjarki Arge Andreasen 2023-03-11 19:29:20 +01:00 committed by Carles Cufí
commit ac697d153d
14 changed files with 687 additions and 0 deletions

View file

@ -59,3 +59,8 @@ API Reference
************* *************
.. doxygengroup:: rtc_interface .. doxygengroup:: rtc_interface
RTC device driver test suite
****************************
See :ref:`rtc_api_test`

View file

@ -4,4 +4,5 @@
zephyr_library() zephyr_library()
zephyr_library_sources_ifdef(CONFIG_USERSPACE rtc_handlers.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE rtc_handlers.c)
zephyr_library_sources_ifdef(CONFIG_RTC_EMUL rtc_emul.c)

View file

@ -0,0 +1,13 @@
# Copyright (c) 2022 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0
include: base.yaml
description: RTC device common bindings
properties:
alarms-count:
type: int
required: true
description: |
Number of alarms supported by RTC device

View file

@ -0,0 +1,35 @@
# Copyright (c) 2022 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(rtc_api)
target_sources(app PRIVATE
src/main.c
src/test_time_incrementing.c
src/test_time.c
)
if(DEFINED CONFIG_RTC_ALARM)
target_sources(app PRIVATE
src/test_alarm_callback.c
src/test_alarm.c
)
endif()
if(DEFINED CONFIG_RTC_UPDATE)
target_sources(app PRIVATE
src/test_update_callback.c
)
endif()
if(DEFINED CONFIG_RTC_CALIBRATION)
target_sources(app PRIVATE
src/test_calibrate.c
)
endif()
target_include_directories(app PRIVATE inc)

View file

@ -0,0 +1,31 @@
.. _rtc_api_test:
:Author: Bjarki Arge Andreasen
RTC Test suite
###############
Test RTC API implementation for RTC devices.
Overview
********
This suite is design to be portable between boards. It uses the alias
``rtc`` to designate the RTC device to test.
This test suite tests the following:
* Setting and getting the time.
* RTC Time incrementing correctly.
* Alarms if supported by hardware, with and without callback enabled
* Calibration if supported by hardware.
The calibration test tests a range of values which are printed to the
console to be manually compared. The user must review the set and
gotten values to ensure they are valid.
By default, only the mandatory Setting and getting time is enabled
for testing. To test the optional alarms, update event callback
and clock calibration, these must be enabled by selecting
``CONFIG_RTC_ALARM``, ``CONFIG_RTC_UPDATE`` and
``CONFIG_RTC_CALIBRATION``.

View file

@ -0,0 +1,6 @@
# Copyright (c) 2022 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0
CONFIG_RTC=y
CONFIG_ZTEST=y
CONFIG_ZTEST_NEW_API=y

View file

@ -0,0 +1,9 @@
/*
* Copyright 2022 Bjarki Arge Andreasen
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
ZTEST_SUITE(rtc_api, NULL, NULL, NULL, NULL, NULL);

View file

@ -0,0 +1,143 @@
/*
* Copyright 2022 Bjarki Arge Andreasen
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/device.h>
#include <zephyr/drivers/rtc.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/timeutil.h>
#include <time.h>
/* Fri Jan 01 2021 13:29:50 GMT+0000 */
#define RTC_TEST_ALARM_SET_TIME (1609507790)
#define RTC_TEST_ALARM_TEST_NOT_PENDING_DELAY (3)
#define RTC_TEST_ALARM_TEST_PENDING_DELAY (10)
#define RTC_TEST_ALARM_TIME_MINUTE (30)
#define RTC_TEST_ALARM_TIME_HOUR (13)
static const struct device *rtc = DEVICE_DT_GET(DT_ALIAS(rtc));
static const uint16_t alarms_count = DT_PROP(DT_ALIAS(rtc), alarms_count);
ZTEST(rtc_api, test_alarm)
{
int ret;
time_t timer_set;
struct rtc_time time_set;
struct rtc_time alarm_time_set;
uint16_t alarm_time_mask_supported;
uint16_t alarm_time_mask_set;
struct rtc_time alarm_time_get;
uint16_t alarm_time_mask_get;
/* Clear alarm alarm time */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_set_time(rtc, i, 0, NULL);
zassert_true(ret == 0, "Failed to clear alarm time");
}
/* Disable alarm callback */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_set_callback(rtc, i, NULL, NULL);
zassert_true((ret == 0) || (ret == -ENOTSUP),
"Failed to clear and disable alarm callback");
}
/* Validate alarms supported fields */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_get_supported_fields(rtc, i, &alarm_time_mask_supported);
zassert_true(ret == 0, "Failed to get supported alarm fields");
/* Skip test if alarm does not support the minute and hour fields */
if (((RTC_ALARM_TIME_MASK_MINUTE & alarm_time_mask_supported) == 0) ||
((RTC_TEST_ALARM_TIME_HOUR & alarm_time_mask_supported) == 0)) {
ztest_test_skip();
}
}
/* Set alarm time */
alarm_time_set.tm_min = RTC_TEST_ALARM_TIME_MINUTE;
alarm_time_set.tm_hour = RTC_TEST_ALARM_TIME_HOUR;
alarm_time_mask_set = (RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR);
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_set_time(rtc, i, alarm_time_mask_set, &alarm_time_set);
zassert_true(ret == 0, "Failed to set alarm time");
}
/* Validate alarm time */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_get_time(rtc, i, &alarm_time_mask_get, &alarm_time_get);
zassert_true(ret == 0, "Failed to set alarm time");
zassert_true(alarm_time_mask_get == alarm_time_mask_set,
"Incorrect alarm time mask");
zassert_true(alarm_time_get.tm_min == alarm_time_get.tm_min,
"Incorrect alarm time minute field");
zassert_true(alarm_time_get.tm_hour == alarm_time_get.tm_hour,
"Incorrect alarm time hour field");
}
/* Initialize RTC time to set */
timer_set = RTC_TEST_ALARM_SET_TIME;
gmtime_r(&timer_set, (struct tm *)(&time_set));
time_set.tm_isdst = -1;
time_set.tm_nsec = 0;
for (uint8_t i = 0; i < 2; i++) {
/* Set RTC time */
ret = rtc_set_time(rtc, &time_set);
zassert_true(ret == 0, "Failed to set time");
/* Clear alarm pending status */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_is_pending(rtc, i);
zassert_true(ret > -1, "Failed to clear alarm pending status");
}
/* Wait before validating alarm pending status has not been set prematurely */
k_sleep(K_SECONDS(RTC_TEST_ALARM_TEST_NOT_PENDING_DELAY));
/* Validate alarm are not pending */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_is_pending(rtc, i);
zassert_true(ret == 0, "Alarm should not be pending");
}
/* Wait for alarm to trigger */
k_sleep(K_SECONDS(RTC_TEST_ALARM_TEST_PENDING_DELAY));
/* Validate alarm is pending */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_is_pending(rtc, i);
zassert_true(ret == 1, "Alarm should be pending");
}
}
/* Disable and clear alarms */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_set_time(rtc, i, 0, NULL);
zassert_true(ret == 0, "Failed to disable alarm");
ret = rtc_alarm_is_pending(rtc, i);
zassert_true(ret > -1, "Failed to clear alarm pending state");
}
}

View file

@ -0,0 +1,169 @@
/*
* Copyright 2022 Bjarki Arge Andreasen
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/device.h>
#include <zephyr/drivers/rtc.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/timeutil.h>
#include <time.h>
/* Fri Jan 01 2021 13:29:50 GMT+0000 */
#define RTC_TEST_ALARM_SET_TIME (1609507790)
#define RTC_TEST_ALARM_TEST_NOT_CALLED_DELAY (3)
#define RTC_TEST_ALARM_TEST_CALLED_DELAY (10)
#define RTC_TEST_ALARM_TIME_MINUTE (30)
#define RTC_TEST_ALARM_TIME_HOUR (13)
static const struct device *rtc = DEVICE_DT_GET(DT_ALIAS(rtc));
static const uint16_t alarms_count = DT_PROP(DT_ALIAS(rtc), alarms_count);
static uint32_t callback_user_data_odd = 0x4321;
static uint32_t callback_user_data_even = 0x1234;
static atomic_t callback_called_mask_odd;
static atomic_t callback_called_mask_even;
static void test_rtc_alarm_callback_handler_odd(const struct device *dev, uint16_t id,
void *user_data)
{
atomic_set_bit(&callback_called_mask_odd, id);
}
static void test_rtc_alarm_callback_handler_even(const struct device *dev, uint16_t id,
void *user_data)
{
atomic_set_bit(&callback_called_mask_even, id);
}
ZTEST(rtc_api, test_alarm_callback)
{
int ret;
time_t timer_set;
struct rtc_time time_set;
struct rtc_time alarm_time_set;
uint16_t alarm_time_mask_supported;
uint16_t alarm_time_mask_set;
atomic_val_t callback_called_mask_status_odd;
atomic_val_t callback_called_mask_status_even;
bool callback_called_status;
/* Disable alarm callback */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_set_callback(rtc, i, NULL, NULL);
zassert_true(ret == 0, "Failed to clear and disable alarm");
}
/* Validate alarms supported fields */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_get_supported_fields(rtc, i, &alarm_time_mask_supported);
zassert_true(ret == 0, "Failed to get supported alarm fields");
/* Skip test if alarm does not support the minute and hour fields */
if (((RTC_ALARM_TIME_MASK_MINUTE & alarm_time_mask_supported) == 0) ||
((RTC_TEST_ALARM_TIME_HOUR & alarm_time_mask_supported) == 0)) {
ztest_test_skip();
}
}
/* Set alarm time */
alarm_time_set.tm_min = RTC_TEST_ALARM_TIME_MINUTE;
alarm_time_set.tm_hour = RTC_TEST_ALARM_TIME_HOUR;
alarm_time_mask_set = (RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR);
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_set_time(rtc, i, alarm_time_mask_set, &alarm_time_set);
zassert_true(ret == 0, "Failed to set alarm time");
}
/* Initialize RTC time to set */
timer_set = RTC_TEST_ALARM_SET_TIME;
gmtime_r(&timer_set, (struct tm *)(&time_set));
time_set.tm_isdst = -1;
time_set.tm_nsec = 0;
/* Set RTC time */
ret = rtc_set_time(rtc, &time_set);
zassert_true(ret == 0, "Failed to set time");
/* Clear alarm pending status */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_is_pending(rtc, i);
zassert_true(ret > -1, "Failed to clear alarm pending status");
}
/* Set and enable alarm callback */
for (uint16_t i = 0; i < alarms_count; i++) {
if (i % 2) {
ret = rtc_alarm_set_callback(rtc, i,
test_rtc_alarm_callback_handler_odd,
&callback_user_data_odd);
} else {
ret = rtc_alarm_set_callback(rtc, i,
test_rtc_alarm_callback_handler_even,
&callback_user_data_even);
}
zassert_true(ret == 0, "Failed to set alarm callback");
}
for (uint8_t i = 0; i < 2; i++) {
/* Clear callback called atomics */
atomic_set(&callback_called_mask_odd, 0);
atomic_set(&callback_called_mask_even, 0);
/* Wait before validating alarm callbacks have not been called prematurely */
k_sleep(K_SECONDS(RTC_TEST_ALARM_TEST_NOT_CALLED_DELAY));
/* Validate alarm callbacks have not been called prematurely */
callback_called_mask_status_odd = atomic_get(&callback_called_mask_odd);
callback_called_mask_status_even = atomic_get(&callback_called_mask_even);
zassert_true(callback_called_mask_status_odd == 0,
"Alarm callback called prematurely");
zassert_true(callback_called_mask_status_even == 0,
"Alarm callback called prematurely");
/* Wait for alarm to trigger */
k_sleep(K_SECONDS(RTC_TEST_ALARM_TEST_CALLED_DELAY));
/* Validate alarm callback called */
for (uint16_t i = 0; i < alarms_count; i++) {
callback_called_status =
(i % 2) ? atomic_test_bit(&callback_called_mask_odd, i)
: atomic_test_bit(&callback_called_mask_even, i);
zassert_true(callback_called_status == true,
"Alarm callback should have been called");
}
/* Reset RTC time */
ret = rtc_set_time(rtc, &time_set);
zassert_true(ret == 0, "Failed to set time");
}
/* Disable and clear alarms */
for (uint16_t i = 0; i < alarms_count; i++) {
ret = rtc_alarm_set_callback(rtc, i, NULL, NULL);
zassert_true(ret == 0, "Failed to disable alarm callback");
ret = rtc_alarm_set_time(rtc, i, 0, NULL);
zassert_true(ret == 0, "Failed to disable alarm");
ret = rtc_alarm_is_pending(rtc, i);
zassert_true(ret > -1, "Failed to clear alarm pending state");
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright 2022 Bjarki Arge Andreasen
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/rtc.h>
#define RTC_TEST_CAL_RANGE_LIMIT (200000)
#define RTC_TEST_CAL_RANGE_STEP (10000)
static const struct device *rtc = DEVICE_DT_GET(DT_ALIAS(rtc));
static int test_set_get_calibration(int32_t calibrate_set)
{
int32_t calibrate_get;
int ret;
ret = rtc_set_calibration(rtc, calibrate_set);
/* Check if calibration is within limits of hardware */
if (ret == -EINVAL) {
/* skip */
return -EINVAL;
}
/* Validate calibration was set */
zassert_true(ret == 0, "Failed to set calibration");
ret = rtc_get_calibration(rtc, &calibrate_get);
/* Validate calibration was gotten */
zassert_true(ret == 0, "Failed to get calibration");
/* Print comparison between set and get values */
printk("Calibrate (set,get): %i, %i\n", calibrate_set, calibrate_get);
return 0;
}
ZTEST(rtc_api, set_get_calibration)
{
int32_t calibrate_get;
int ret;
int32_t range_limit;
int32_t range_step;
ret = rtc_set_calibration(rtc, 0);
/* Validate calibration was set */
zassert_true(ret == 0, "Failed to set calibration");
ret = rtc_get_calibration(rtc, &calibrate_get);
/* Validate calibration was gotten */
zassert_true(ret == 0, "Failed to get calibration");
/* Validate edge values (0 already tested) */
test_set_get_calibration(1);
test_set_get_calibration(-1);
range_limit = RTC_TEST_CAL_RANGE_LIMIT;
range_step = RTC_TEST_CAL_RANGE_STEP;
/* Validate over negative range */
for (int32_t set = range_step; set <= range_limit; set += range_step) {
ret = test_set_get_calibration(-set);
if (ret < 0) {
/* Limit of hardware capabilties reached */
break;
}
}
/* Validate over positive range */
for (int32_t set = range_step; set <= range_limit; set += range_step) {
ret = test_set_get_calibration(set);
if (ret < 0) {
/* Limit of hardware capabilties reached */
break;
}
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright 2022 Bjarki Arge Andreasen
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/device.h>
#include <zephyr/drivers/rtc.h>
#include <zephyr/sys/timeutil.h>
#include <time.h>
#include <string.h>
/* Wed Dec 31 2025 23:59:55 GMT+0000 */
#define RTC_TEST_GET_SET_TIME (1767225595UL)
#define RTC_TEST_GET_SET_TIME_TOL (1UL)
static const struct device *rtc = DEVICE_DT_GET(DT_ALIAS(rtc));
ZTEST(rtc_api, test_set_get_time)
{
struct rtc_time datetime_set;
struct rtc_time datetime_get;
time_t timer_get;
time_t timer_set = RTC_TEST_GET_SET_TIME;
gmtime_r(&timer_set, (struct tm *)(&datetime_set));
memset(&datetime_get, 0xFF, sizeof(datetime_get));
zassert_true(rtc_set_time(rtc, &datetime_set) == 0, "Failed to set time");
zassert_true(rtc_get_time(rtc, &datetime_get) == 0,
"Failed to get time using rtc_time_get()");
zassert_true((datetime_get.tm_sec > -1) && (datetime_get.tm_sec < 60),
"Invalid tm_sec");
zassert_true((datetime_get.tm_min > -1) && (datetime_get.tm_min < 60),
"Invalid tm_min");
zassert_true((datetime_get.tm_hour > -1) && (datetime_get.tm_hour < 24),
"Invalid tm_hour");
zassert_true((datetime_get.tm_mday > 0) && (datetime_get.tm_mday < 32),
"Invalid tm_mday");
zassert_true((datetime_get.tm_year > 124) && (datetime_get.tm_year < 127),
"Invalid tm_year");
zassert_true((datetime_get.tm_wday > -2) && (datetime_get.tm_wday < 7),
"Invalid tm_wday");
zassert_true((datetime_get.tm_yday > -2) && (datetime_get.tm_yday < 366),
"Invalid tm_yday");
zassert_true((datetime_get.tm_isdst == -1), "Invalid tm_isdst");
zassert_true((datetime_get.tm_nsec > -1) && (datetime_get.tm_yday < 1000000000),
"Invalid tm_yday");
timer_get = timeutil_timegm((struct tm *)(&datetime_get));
zassert_true((timer_get >= RTC_TEST_GET_SET_TIME) &&
(timer_get <= (RTC_TEST_GET_SET_TIME + RTC_TEST_GET_SET_TIME_TOL)),
"Got unexpected time");
}

View file

@ -0,0 +1,57 @@
/*
* Copyright 2022 Bjarki Arge Andreasen
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/device.h>
#include <zephyr/drivers/rtc.h>
#include <zephyr/sys/timeutil.h>
#include <time.h>
/* Wed Dec 31 2025 23:59:55 GMT+0000 */
#define RTC_TEST_TIME_COUNTING_SET_TIME (1767225595UL)
#define RTC_TEST_TIME_COUNTING_LIMIT (RTC_TEST_TIME_COUNTING_SET_TIME + 10UL)
#define RTC_TEST_TIME_COUNTING_POLL_LIMIT (30U)
static const struct device *rtc = DEVICE_DT_GET(DT_ALIAS(rtc));
ZTEST(rtc_api, test_time_counting)
{
struct rtc_time datetime_set;
struct rtc_time datetime_get;
time_t timer_get;
time_t timer_set = RTC_TEST_TIME_COUNTING_SET_TIME;
time_t timer_get_last = timer_set;
uint8_t i;
gmtime_r(&timer_set, (struct tm *)(&datetime_set));
zassert_true(rtc_set_time(rtc, &datetime_set) == 0, "Failed to set time");
for (i = 0; i < RTC_TEST_TIME_COUNTING_POLL_LIMIT; i++) {
/* Get time */
zassert_true(rtc_get_time(rtc, &datetime_get) == 0, "Failed to get time");
timer_get = timeutil_timegm((struct tm *)(&datetime_get));
/* Validate if time incrementing */
zassert_true(timer_get_last <= timer_get, "Time is decrementing");
/* Check if limit reached */
if (timer_get == RTC_TEST_TIME_COUNTING_LIMIT) {
break;
}
/* Save last timer get */
timer_get_last = timer_get;
/* Limit polling rate */
k_msleep(500);
}
zassert_true(i < RTC_TEST_TIME_COUNTING_POLL_LIMIT,
"Timeout occurred waiting for time to increment");
}

View file

@ -0,0 +1,55 @@
/*
* Copyright 2022 Bjarki Arge Andreasen
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/device.h>
#include <zephyr/drivers/rtc.h>
#include <zephyr/sys/atomic.h>
static const struct device *rtc = DEVICE_DT_GET(DT_ALIAS(rtc));
static atomic_t callback_called_counter;
static atomic_t callback_test_user_data_address;
static uint32_t test_user_data = 0x1234;
static void test_rtc_update_callback_handler(const struct device *dev, void *user_data)
{
atomic_inc(&callback_called_counter);
atomic_set(&callback_test_user_data_address, (uint32_t)user_data);
}
ZTEST(rtc_api, test_update_callback)
{
int ret;
uint32_t counter;
uint32_t address;
ret = rtc_update_set_callback(rtc, NULL, NULL);
zassert_true(ret == 0, "Failed to clear and disable update callback");
atomic_set(&callback_called_counter, 0);
k_msleep(5000);
counter = atomic_get(&callback_called_counter);
zassert_true(counter == 0, "Update callback should not have been called");
ret = rtc_update_set_callback(rtc, test_rtc_update_callback_handler, &test_user_data);
zassert_true(ret == 0, "Failed to set and enable update callback");
k_msleep(10000);
counter = atomic_get(&callback_called_counter);
address = atomic_get(&callback_test_user_data_address);
zassert_true(counter < 12 && counter > 8, "Invalid update callback called counter");
zassert_true(address == ((uint32_t)(&test_user_data)), "Incorrect user data");
}

View file

@ -0,0 +1,8 @@
# Copyright (c) 2022 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0
tests:
drivers.rtc.rtc_api:
tags: drivers rtc api
filter: dt_alias_exists("rtc")
depends_on: rtc