tests: posix: common: separate posix timer tests into a smaller testsuite

posix.common contains testsuites that can be separated into smaller
groups of tests. This change moves clock, sleep, nanosleep and timer
into a singular testsuite at tests/posix/timer app directory.

Signed-off-by: Marvin Ouma <pancakesdeath@protonmail.com>
This commit is contained in:
Marvin Ouma 2024-10-30 16:08:45 +03:00 committed by Carles Cufí
commit 6737f6b283
9 changed files with 340 additions and 245 deletions

View file

@ -11,23 +11,6 @@
#include <zephyr/ztest.h>
#include <zephyr/logging/log.h>
#define SLEEP_SECONDS 1
#define CLOCK_INVALID -1
LOG_MODULE_REGISTER(clock_test, LOG_LEVEL_DBG);
/* Set a particular time. In this case, the output of: `date +%s -d 2018-01-01T15:45:01Z` */
static const struct timespec ref_ts = {1514821501, NSEC_PER_SEC / 2U};
static const clockid_t clocks[] = {
CLOCK_MONOTONIC,
CLOCK_REALTIME,
};
static const bool settable[] = {
false,
true,
};
static inline int64_t ts_to_ns(const struct timespec *ts)
{
return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
@ -47,47 +30,7 @@ static inline void tv_to_ts(const struct timeval *tv, struct timespec *ts)
return _tp_op(_a, _b, _op); \
}
_decl_op(bool, tp_eq, ==); /* a == b */
_decl_op(bool, tp_lt, <); /* a < b */
_decl_op(bool, tp_gt, >); /* a > b */
_decl_op(bool, tp_le, <=); /* a <= b */
_decl_op(bool, tp_ge, >=); /* a >= b */
_decl_op(int64_t, tp_diff, -); /* a - b */
/* lo <= (a - b) < hi */
static inline bool tp_diff_in_range_ns(const struct timespec *a, const struct timespec *b,
int64_t lo, int64_t hi)
{
int64_t diff = tp_diff(a, b);
return diff >= lo && diff < hi;
}
ZTEST(clock, test_clock_gettime)
{
struct timespec ts;
/* ensure argument validation is performed */
errno = 0;
zassert_equal(clock_gettime(CLOCK_INVALID, &ts), -1);
zassert_equal(errno, EINVAL);
if (false) {
/* undefined behaviour */
errno = 0;
zassert_equal(clock_gettime(clocks[0], NULL), -1);
zassert_equal(errno, EINVAL);
}
/* verify that we can call clock_gettime() on supported clocks */
ARRAY_FOR_EACH(clocks, i)
{
ts = (struct timespec){-1, -1};
zassert_ok(clock_gettime(clocks[i], &ts));
zassert_not_equal(ts.tv_sec, -1);
zassert_not_equal(ts.tv_nsec, -1);
}
}
ZTEST(clock, test_gettimeofday)
{
@ -114,161 +57,4 @@ ZTEST(clock, test_gettimeofday)
zassert_true(tp_ge(&rts, &ts));
}
ZTEST(clock, test_clock_settime)
{
int64_t diff_ns;
struct timespec ts = {0};
BUILD_ASSERT(ARRAY_SIZE(settable) == ARRAY_SIZE(clocks));
/* ensure argument validation is performed */
errno = 0;
zassert_equal(clock_settime(CLOCK_INVALID, &ts), -1);
zassert_equal(errno, EINVAL);
if (false) {
/* undefined behaviour */
errno = 0;
zassert_equal(clock_settime(CLOCK_REALTIME, NULL), -1);
zassert_equal(errno, EINVAL);
}
/* verify nanoseconds */
errno = 0;
ts = (struct timespec){0, NSEC_PER_SEC};
zassert_equal(clock_settime(CLOCK_REALTIME, &ts), -1);
zassert_equal(errno, EINVAL);
errno = 0;
ts = (struct timespec){0, -1};
zassert_equal(clock_settime(CLOCK_REALTIME, &ts), -1);
zassert_equal(errno, EINVAL);
ARRAY_FOR_EACH(clocks, i)
{
if (!settable[i]) {
/* should fail attempting to set unsettable clocks */
errno = 0;
zassert_equal(clock_settime(clocks[i], &ts), -1);
zassert_equal(errno, EINVAL);
continue;
}
zassert_ok(clock_settime(clocks[i], &ref_ts));
/* read-back the time */
zassert_ok(clock_gettime(clocks[i], &ts));
/* dt should be >= 0, but definitely <= 1s */
diff_ns = tp_diff(&ts, &ref_ts);
zassert_true(diff_ns >= 0 && diff_ns <= NSEC_PER_SEC);
}
}
ZTEST(clock, test_realtime)
{
struct timespec then, now;
/*
* For calculating cumulative moving average
* Note: we do not want to assert any individual samples due to scheduler noise.
* The CMA filters out the noise so we can make an assertion (on average).
* https://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average
*/
int64_t cma_prev = 0;
int64_t cma;
int64_t x_i;
/* lower and uppoer boundary for assertion */
int64_t lo = CONFIG_TEST_CLOCK_RT_SLEEP_MS;
int64_t hi = CONFIG_TEST_CLOCK_RT_SLEEP_MS + CONFIG_TEST_CLOCK_RT_ERROR_MS;
/* lower and upper watermark */
int64_t lo_wm = INT64_MAX;
int64_t hi_wm = INT64_MIN;
/* Loop n times, sleeping a little bit for each */
(void)clock_gettime(CLOCK_REALTIME, &then);
for (int i = 0; i < CONFIG_TEST_CLOCK_RT_ITERATIONS; ++i) {
zassert_ok(k_usleep(USEC_PER_MSEC * CONFIG_TEST_CLOCK_RT_SLEEP_MS));
(void)clock_gettime(CLOCK_REALTIME, &now);
/* Make the delta milliseconds. */
x_i = tp_diff(&now, &then) / NSEC_PER_MSEC;
then = now;
if (x_i < lo_wm) {
/* update low watermark */
lo_wm = x_i;
}
if (x_i > hi_wm) {
/* update high watermark */
hi_wm = x_i;
}
/* compute cumulative running average */
cma = (x_i + i * cma_prev) / (i + 1);
cma_prev = cma;
}
LOG_INF("n: %d, sleep: %d, margin: %d, lo: %lld, avg: %lld, hi: %lld",
CONFIG_TEST_CLOCK_RT_ITERATIONS, CONFIG_TEST_CLOCK_RT_SLEEP_MS,
CONFIG_TEST_CLOCK_RT_ERROR_MS, lo_wm, cma, hi_wm);
zassert_between_inclusive(cma, lo, hi);
}
ZTEST(clock, test_clock_getcpuclockid)
{
int ret = 0;
clockid_t clock_id = CLOCK_INVALID;
ret = clock_getcpuclockid((pid_t)0, &clock_id);
zassert_equal(ret, 0, "POSIX clock_getcpuclock id failed");
zassert_equal(clock_id, CLOCK_PROCESS_CPUTIME_ID, "POSIX clock_getcpuclock id failed");
ret = clock_getcpuclockid((pid_t)2482, &clock_id);
zassert_equal(ret, EPERM, "POSIX clock_getcpuclock id failed");
}
ZTEST(clock, test_clock_getres)
{
int ret;
struct timespec res;
const struct timespec one_ns = {
.tv_sec = 0,
.tv_nsec = 1,
};
struct arg {
clockid_t clock_id;
struct timespec *res;
int expect;
};
const struct arg args[] = {
/* permuting over "invalid" inputs */
{CLOCK_INVALID, NULL, -1},
{CLOCK_INVALID, &res, -1},
{CLOCK_REALTIME, NULL, 0},
{CLOCK_MONOTONIC, NULL, 0},
{CLOCK_PROCESS_CPUTIME_ID, NULL, 0},
/* all valid inputs */
{CLOCK_REALTIME, &res, 0},
{CLOCK_MONOTONIC, &res, 0},
{CLOCK_PROCESS_CPUTIME_ID, &res, 0},
};
ARRAY_FOR_EACH_PTR(args, arg) {
errno = 0;
res = (struct timespec){0};
ret = clock_getres(arg->clock_id, arg->res);
zassert_equal(ret, arg->expect);
if (ret != 0) {
zassert_equal(errno, EINVAL);
continue;
}
if (arg->res != NULL) {
zassert_true(tp_ge(arg->res, &one_ns));
}
}
}
ZTEST_SUITE(clock, NULL, NULL, NULL, NULL, NULL);

View file

@ -0,0 +1,11 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(posix_timers)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
target_compile_options(app PRIVATE -U_POSIX_C_SOURCE -D_POSIX_C_SOURCE=200809L)

View file

@ -0,0 +1,27 @@
# Copyright (c) 2023, Meta
# SPDX-License-Identifier: Apache-2.0
# Options specific to clock.c / test_realtime
config TEST_CLOCK_RT_ITERATIONS
int "Number of iterations to check clock_gettime() reliability"
range 10 100
default 20
help
This option is specific to posix_apis.test_realtime in clock.c
config TEST_CLOCK_RT_SLEEP_MS
int "Time to sleep between iterations in milliseconds"
range 50 1000
default 100
help
This option is specific to posix_apis.test_realtime in clock.c
config TEST_CLOCK_RT_ERROR_MS
int "Maximum overshoot (error) in milliseconds"
range 10 500
default 10
help
This option is specific to posix_apis.test_realtime in clock.c
source "Kconfig.zephyr"

View file

@ -0,0 +1,5 @@
CONFIG_POSIX_API=y
CONFIG_ZTEST=y
CONFIG_POSIX_AEP_CHOICE_BASE=y
CONFIG_POSIX_TIMERS=y

View file

@ -0,0 +1,246 @@
/*
* Copyright (c) 2018 Intel Corporation
* Copyright (c) 2023, Meta
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <zephyr/ztest.h>
#include <zephyr/logging/log.h>
#define SLEEP_SECONDS 1
#define CLOCK_INVALID -1
LOG_MODULE_REGISTER(clock_test, LOG_LEVEL_DBG);
/* Set a particular time. In this case, the output of: `date +%s -d 2018-01-01T15:45:01Z` */
static const struct timespec ref_ts = {1514821501, NSEC_PER_SEC / 2U};
static const clockid_t clocks[] = {
CLOCK_MONOTONIC,
CLOCK_REALTIME,
};
static const bool settable[] = {
false,
true,
};
static inline int64_t ts_to_ns(const struct timespec *ts)
{
return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
}
static inline void tv_to_ts(const struct timeval *tv, struct timespec *ts)
{
ts->tv_sec = tv->tv_sec;
ts->tv_nsec = tv->tv_usec * NSEC_PER_USEC;
}
#define _tp_op(_a, _b, _op) (ts_to_ns(_a) _op ts_to_ns(_b))
#define _decl_op(_type, _name, _op) \
static inline _type _name(const struct timespec *_a, const struct timespec *_b) \
{ \
return _tp_op(_a, _b, _op); \
}
_decl_op(bool, tp_eq, ==); /* a == b */
_decl_op(bool, tp_lt, <); /* a < b */
_decl_op(bool, tp_gt, >); /* a > b */
_decl_op(bool, tp_le, <=); /* a <= b */
_decl_op(bool, tp_ge, >=); /* a >= b */
_decl_op(int64_t, tp_diff, -); /* a - b */
/* lo <= (a - b) < hi */
static inline bool tp_diff_in_range_ns(const struct timespec *a, const struct timespec *b,
int64_t lo, int64_t hi)
{
int64_t diff = tp_diff(a, b);
return diff >= lo && diff < hi;
}
ZTEST(posix_timers, test_clock_gettime)
{
struct timespec ts;
/* ensure argument validation is performed */
errno = 0;
zassert_equal(clock_gettime(CLOCK_INVALID, &ts), -1);
zassert_equal(errno, EINVAL);
if (false) {
/* undefined behaviour */
errno = 0;
zassert_equal(clock_gettime(clocks[0], NULL), -1);
zassert_equal(errno, EINVAL);
}
/* verify that we can call clock_gettime() on supported clocks */
ARRAY_FOR_EACH(clocks, i) {
ts = (struct timespec){-1, -1};
zassert_ok(clock_gettime(clocks[i], &ts));
zassert_not_equal(ts.tv_sec, -1);
zassert_not_equal(ts.tv_nsec, -1);
}
}
ZTEST(posix_timers, test_clock_settime)
{
int64_t diff_ns;
struct timespec ts = {0};
BUILD_ASSERT(ARRAY_SIZE(settable) == ARRAY_SIZE(clocks));
/* ensure argument validation is performed */
errno = 0;
zassert_equal(clock_settime(CLOCK_INVALID, &ts), -1);
zassert_equal(errno, EINVAL);
if (false) {
/* undefined behaviour */
errno = 0;
zassert_equal(clock_settime(CLOCK_REALTIME, NULL), -1);
zassert_equal(errno, EINVAL);
}
/* verify nanoseconds */
errno = 0;
ts = (struct timespec){0, NSEC_PER_SEC};
zassert_equal(clock_settime(CLOCK_REALTIME, &ts), -1);
zassert_equal(errno, EINVAL);
errno = 0;
ts = (struct timespec){0, -1};
zassert_equal(clock_settime(CLOCK_REALTIME, &ts), -1);
zassert_equal(errno, EINVAL);
ARRAY_FOR_EACH(clocks, i) {
if (!settable[i]) {
/* should fail attempting to set unsettable clocks */
errno = 0;
zassert_equal(clock_settime(clocks[i], &ts), -1);
zassert_equal(errno, EINVAL);
continue;
}
zassert_ok(clock_settime(clocks[i], &ref_ts));
/* read-back the time */
zassert_ok(clock_gettime(clocks[i], &ts));
/* dt should be >= 0, but definitely <= 1s */
diff_ns = tp_diff(&ts, &ref_ts);
zassert_true(diff_ns >= 0 && diff_ns <= NSEC_PER_SEC);
}
}
ZTEST(posix_timers, test_realtime)
{
struct timespec then, now;
/*
* For calculating cumulative moving average
* Note: we do not want to assert any individual samples due to scheduler noise.
* The CMA filters out the noise so we can make an assertion (on average).
* https://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average
*/
int64_t cma_prev = 0;
int64_t cma;
int64_t x_i;
/* lower and uppoer boundary for assertion */
int64_t lo = CONFIG_TEST_CLOCK_RT_SLEEP_MS;
int64_t hi = CONFIG_TEST_CLOCK_RT_SLEEP_MS + CONFIG_TEST_CLOCK_RT_ERROR_MS;
/* lower and upper watermark */
int64_t lo_wm = INT64_MAX;
int64_t hi_wm = INT64_MIN;
/* Loop n times, sleeping a little bit for each */
(void)clock_gettime(CLOCK_REALTIME, &then);
for (int i = 0; i < CONFIG_TEST_CLOCK_RT_ITERATIONS; ++i) {
zassert_ok(k_usleep(USEC_PER_MSEC * CONFIG_TEST_CLOCK_RT_SLEEP_MS));
(void)clock_gettime(CLOCK_REALTIME, &now);
/* Make the delta milliseconds. */
x_i = tp_diff(&now, &then) / NSEC_PER_MSEC;
then = now;
if (x_i < lo_wm) {
/* update low watermark */
lo_wm = x_i;
}
if (x_i > hi_wm) {
/* update high watermark */
hi_wm = x_i;
}
/* compute cumulative running average */
cma = (x_i + i * cma_prev) / (i + 1);
cma_prev = cma;
}
LOG_INF("n: %d, sleep: %d, margin: %d, lo: %lld, avg: %lld, hi: %lld",
CONFIG_TEST_CLOCK_RT_ITERATIONS, CONFIG_TEST_CLOCK_RT_SLEEP_MS,
CONFIG_TEST_CLOCK_RT_ERROR_MS, lo_wm, cma, hi_wm);
zassert_between_inclusive(cma, lo, hi);
}
ZTEST(posix_timers, test_clock_getcpuclockid)
{
int ret = 0;
clockid_t clock_id = CLOCK_INVALID;
ret = clock_getcpuclockid((pid_t)0, &clock_id);
zassert_equal(ret, 0, "POSIX clock_getcpuclock id failed");
zassert_equal(clock_id, CLOCK_PROCESS_CPUTIME_ID, "POSIX clock_getcpuclock id failed");
ret = clock_getcpuclockid((pid_t)2482, &clock_id);
zassert_equal(ret, EPERM, "POSIX clock_getcpuclock id failed");
}
ZTEST(posix_timers, test_clock_getres)
{
int ret;
struct timespec res;
const struct timespec one_ns = {
.tv_sec = 0,
.tv_nsec = 1,
};
struct arg {
clockid_t clock_id;
struct timespec *res;
int expect;
};
const struct arg args[] = {
/* permuting over "invalid" inputs */
{CLOCK_INVALID, NULL, -1},
{CLOCK_INVALID, &res, -1},
{CLOCK_REALTIME, NULL, 0},
{CLOCK_MONOTONIC, NULL, 0},
{CLOCK_PROCESS_CPUTIME_ID, NULL, 0},
/* all valid inputs */
{CLOCK_REALTIME, &res, 0},
{CLOCK_MONOTONIC, &res, 0},
{CLOCK_PROCESS_CPUTIME_ID, &res, 0},
};
ARRAY_FOR_EACH_PTR(args, arg) {
errno = 0;
res = (struct timespec){0};
ret = clock_getres(arg->clock_id, arg->res);
zassert_equal(ret, arg->expect);
if (ret != 0) {
zassert_equal(errno, EINVAL);
continue;
}
if (arg->res != NULL) {
zassert_true(tp_ge(arg->res, &one_ns));
}
}
}

View file

@ -104,12 +104,12 @@ static void common_errors(int selection, clockid_t clock_id, int flags)
zassert_equal(req.tv_nsec, 0, "actual: %d expected: %d", req.tv_nsec, 0);
}
ZTEST(nanosleep, test_nanosleep_errors_errno)
ZTEST(posix_timers, test_nanosleep_errors_errno)
{
common_errors(SELECT_NANOSLEEP, CLOCK_REALTIME, 0);
}
ZTEST(nanosleep, test_clock_nanosleep_errors_errno)
ZTEST(posix_timers, test_clock_nanosleep_errors_errno)
{
struct timespec rem = {};
struct timespec req = {};
@ -200,7 +200,7 @@ static void common_lower_bound_check(int selection, clockid_t clock_id, int flag
/* TODO: Upper bounds check when hr timers are available */
}
ZTEST(nanosleep, test_nanosleep_execution)
ZTEST(posix_timers, test_nanosleep_execution)
{
/* sleep for 1ns */
common_lower_bound_check(SELECT_NANOSLEEP, 0, 0, 0, 1);
@ -221,7 +221,7 @@ ZTEST(nanosleep, test_nanosleep_execution)
common_lower_bound_check(SELECT_NANOSLEEP, 0, 0, 1, 1001);
}
ZTEST(nanosleep, test_clock_nanosleep_execution)
ZTEST(posix_timers, test_clock_nanosleep_execution)
{
struct timespec ts;
@ -281,5 +281,3 @@ ZTEST(nanosleep, test_clock_nanosleep_execution)
common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME,
ts.tv_sec + 2, 1001);
}
ZTEST_SUITE(nanosleep, NULL, NULL, NULL, NULL, NULL);

View file

@ -24,7 +24,7 @@ static void waker_func(struct k_work *work)
}
K_WORK_DELAYABLE_DEFINE(waker, waker_func);
ZTEST(sleep, test_sleep)
ZTEST(posix_timers, test_sleep)
{
uint32_t then;
uint32_t now;
@ -55,7 +55,7 @@ ZTEST(sleep, test_sleep)
zassert_true(sleep(sleep_max_s) >= sleep_rem_s);
}
ZTEST(sleep, test_usleep)
ZTEST(posix_timers, test_usleep)
{
uint32_t then;
uint32_t now;
@ -86,5 +86,3 @@ ZTEST(sleep, test_usleep)
zassert_equal(-1, usleep(USEC_PER_SEC - 1));
zassert_equal(EINTR, errno);
}
ZTEST_SUITE(sleep, NULL, NULL, NULL, NULL, NULL);

View file

@ -71,38 +71,37 @@ void test_timer(clockid_t clock_id, int sigev_notify)
secs_elapsed = (te.tv_sec - ts.tv_sec - 1);
}
uint64_t elapsed = secs_elapsed*NSEC_PER_SEC + nsecs_elapsed;
uint64_t elapsed = secs_elapsed * NSEC_PER_SEC + nsecs_elapsed;
uint64_t first_sig = value.it_value.tv_sec * NSEC_PER_SEC + value.it_value.tv_nsec;
uint64_t sig_interval = value.it_interval.tv_sec * NSEC_PER_SEC + value.it_interval.tv_nsec;
int expected_signal_count = (elapsed - first_sig) / sig_interval + 1;
/*TESTPOINT: Check if POSIX timer test passed*/
zassert_within(exp_count, expected_signal_count, 1,
"POSIX timer test has failed %i != %i",
zassert_within(exp_count, expected_signal_count, 1, "POSIX timer test has failed %i != %i",
exp_count, expected_signal_count);
}
ZTEST(timer, test_CLOCK_REALTIME__SIGEV_SIGNAL)
ZTEST(posix_timers, test_CLOCK_REALTIME__SIGEV_SIGNAL)
{
test_timer(CLOCK_REALTIME, SIGEV_SIGNAL);
}
ZTEST(timer, test_CLOCK_REALTIME__SIGEV_THREAD)
ZTEST(posix_timers, test_CLOCK_REALTIME__SIGEV_THREAD)
{
test_timer(CLOCK_REALTIME, SIGEV_THREAD);
}
ZTEST(timer, test_CLOCK_MONOTONIC__SIGEV_SIGNAL)
ZTEST(posix_timers, test_CLOCK_MONOTONIC__SIGEV_SIGNAL)
{
test_timer(CLOCK_MONOTONIC, SIGEV_SIGNAL);
}
ZTEST(timer, test_CLOCK_MONOTONIC__SIGEV_THREAD)
ZTEST(posix_timers, test_CLOCK_MONOTONIC__SIGEV_THREAD)
{
test_timer(CLOCK_MONOTONIC, SIGEV_THREAD);
}
ZTEST(timer, test_timer_overrun)
ZTEST(posix_timers, test_timer_overrun)
{
struct sigevent sig = {0};
struct itimerspec value;
@ -122,7 +121,7 @@ ZTEST(timer, test_timer_overrun)
zassert_equal(timer_getoverrun(timerid), 4, "Number of overruns is incorrect");
}
ZTEST(timer, test_one_shot__SIGEV_SIGNAL)
ZTEST(posix_timers, test_one_shot__SIGEV_SIGNAL)
{
struct sigevent sig = {0};
struct itimerspec value;
@ -165,4 +164,4 @@ static void before(void *arg)
}
}
ZTEST_SUITE(timer, NULL, NULL, before, after, NULL);
ZTEST_SUITE(posix_timers, NULL, NULL, before, after, NULL);

View file

@ -0,0 +1,25 @@
common:
filter: not CONFIG_NATIVE_LIBC
tags:
- posix
- timers
# 1 tier0 platform per supported architecture
platform_key:
- arch
- simulation
min_flash: 64
min_ram: 32
tests:
portability.posix.timers: {}
portability.posix.timers.minimal:
extra_configs:
- CONFIG_MINIMAL_LIBC=y
portability.posix.timers.newlib:
filter: TOOLCHAIN_HAS_NEWLIB == 1
extra_configs:
- CONFIG_NEWLIB_LIBC=y
portability.posix.timers.picolibc:
tags: picolibc
filter: CONFIG_PICOLIBC_SUPPORTED
extra_configs:
- CONFIG_PICOLIBC=y