blob: b3ab77a5583c8f8d32555e3fe1dcaeed8b02cb8b [file] [log] [blame]
/*
* Copyright 2025 Tenstorrent AI ULC
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <time.h>
#include <zephyr/ztest.h>
#include <zephyr/sys_clock.h>
#include <zephyr/sys/timeutil.h>
#include <zephyr/sys/util.h>
#undef CORRECTABLE
#define CORRECTABLE true
#undef UNCORRECTABLE
#define UNCORRECTABLE false
/* Initialize a struct timespec object from a tick count with additional nanoseconds */
#define SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(ticks, ns) \
SYS_TIMESPEC(SYS_TICKS_TO_SECS(ticks) + \
(SYS_TICKS_TO_NSECS(ticks) + (uint64_t)(ns)) / NSEC_PER_SEC, \
(SYS_TICKS_TO_NSECS(ticks) + (uint64_t)(ns)) % NSEC_PER_SEC)
/*
* test spec for simple timespec validation
*
* If a timespec is expected to be valid, then invalid_ts and valid_ts are equal.
*
* If a timespec is expected to be invalid, then invalid_ts and valid_ts are not equal.
*/
struct ts_test_spec {
struct timespec invalid_ts;
struct timespec valid_ts;
bool expect_valid;
bool correctable;
};
#define DECL_VALID_TS_TEST(sec, nsec) \
{ \
.invalid_ts = {(sec), (nsec)}, \
.valid_ts = {(sec), (nsec)}, \
.expect_valid = true, \
.correctable = false, \
}
/*
* Invalid timespecs can usually be converted to valid ones by adding or subtracting
* multiples of `NSEC_PER_SEC` from tv_nsec, and incrementing or decrementing tv_sec
* proportionately, unless doing so would result in signed integer overflow.
*
* There are two particular corner cases:
* 1. making tv_sec more negative would overflow the tv_sec field in the negative direction
* 1. making tv_sec more positive would overflow the tv_sec field in the positive direction
*/
#define DECL_INVALID_TS_TEST(invalid_sec, invalid_nsec, valid_sec, valid_nsec, is_correctable) \
{ \
.valid_ts = {(valid_sec), (valid_nsec)}, \
.invalid_ts = {(invalid_sec), (invalid_nsec)}, \
.expect_valid = false, \
.correctable = (is_correctable), \
}
/*
* Easily verifiable tests for both valid and invalid timespecs.
*/
static const struct ts_test_spec ts_tests[] = {
/* Valid cases */
DECL_VALID_TS_TEST(0, 0),
DECL_VALID_TS_TEST(0, 1),
DECL_VALID_TS_TEST(1, 0),
DECL_VALID_TS_TEST(1, 1),
DECL_VALID_TS_TEST(1, NSEC_PER_SEC - 1),
DECL_VALID_TS_TEST(-1, 0),
DECL_VALID_TS_TEST(-1, 1),
DECL_VALID_TS_TEST(-1, 0),
DECL_VALID_TS_TEST(-1, 1),
DECL_VALID_TS_TEST(-1, NSEC_PER_SEC - 1),
DECL_VALID_TS_TEST(SYS_TIME_T_MIN, 0),
DECL_VALID_TS_TEST(SYS_TIME_T_MIN, NSEC_PER_SEC - 1),
DECL_VALID_TS_TEST(SYS_TIME_T_MAX, 0),
DECL_VALID_TS_TEST(SYS_TIME_T_MAX, NSEC_PER_SEC - 1),
/* Correctable, invalid cases */
DECL_INVALID_TS_TEST(0, -2LL * NSEC_PER_SEC + 1, -2, 1, CORRECTABLE),
DECL_INVALID_TS_TEST(0, -2LL * NSEC_PER_SEC - 1, -3, NSEC_PER_SEC - 1, CORRECTABLE),
DECL_INVALID_TS_TEST(0, -1LL * NSEC_PER_SEC - 1, -2, NSEC_PER_SEC - 1, CORRECTABLE),
DECL_INVALID_TS_TEST(0, -1, -1, NSEC_PER_SEC - 1, CORRECTABLE),
DECL_INVALID_TS_TEST(0, NSEC_PER_SEC, 1, 0, CORRECTABLE),
DECL_INVALID_TS_TEST(0, NSEC_PER_SEC + 1, 1, 1, CORRECTABLE),
DECL_INVALID_TS_TEST(1, -1, 0, NSEC_PER_SEC - 1, CORRECTABLE),
DECL_INVALID_TS_TEST(1, NSEC_PER_SEC, 2, 0, CORRECTABLE),
DECL_INVALID_TS_TEST(-1, -1, -2, NSEC_PER_SEC - 1, CORRECTABLE),
DECL_INVALID_TS_TEST(0, NSEC_PER_SEC, 1, 0, CORRECTABLE),
DECL_INVALID_TS_TEST(1, -1, 0, NSEC_PER_SEC - 1, CORRECTABLE),
DECL_INVALID_TS_TEST(1, NSEC_PER_SEC, 2, 0, CORRECTABLE),
DECL_INVALID_TS_TEST(SYS_TIME_T_MIN, NSEC_PER_SEC, SYS_TIME_T_MIN + 1, 0, CORRECTABLE),
DECL_INVALID_TS_TEST(SYS_TIME_T_MAX, -1, SYS_TIME_T_MAX - 1, NSEC_PER_SEC - 1, CORRECTABLE),
DECL_INVALID_TS_TEST(0, LONG_MIN, (int64_t)LONG_MIN / NSEC_PER_SEC - 1,
NSEC_PER_SEC + LONG_MIN % (long long)NSEC_PER_SEC, CORRECTABLE),
DECL_INVALID_TS_TEST(0, LONG_MAX, LONG_MAX / NSEC_PER_SEC, LONG_MAX % NSEC_PER_SEC,
CORRECTABLE),
/* Uncorrectable, invalid cases */
DECL_INVALID_TS_TEST(SYS_TIME_T_MIN + 2, -2 * (int64_t)NSEC_PER_SEC - 1, 0, 0,
UNCORRECTABLE),
DECL_INVALID_TS_TEST(SYS_TIME_T_MIN + 1, -(int64_t)NSEC_PER_SEC - 1, 0, 0, UNCORRECTABLE),
DECL_INVALID_TS_TEST(SYS_TIME_T_MIN + 1, -(int64_t)NSEC_PER_SEC - 1, 0, 0, UNCORRECTABLE),
DECL_INVALID_TS_TEST(SYS_TIME_T_MIN, -1, 0, 0, UNCORRECTABLE),
DECL_INVALID_TS_TEST(SYS_TIME_T_MAX, (int64_t)NSEC_PER_SEC, 0, 0, UNCORRECTABLE),
DECL_INVALID_TS_TEST(SYS_TIME_T_MAX - 1, 2 * (int64_t)NSEC_PER_SEC, 0, 0, UNCORRECTABLE),
};
ZTEST(timeutil_api, test_timespec_is_valid)
{
ARRAY_FOR_EACH(ts_tests, i) {
const struct ts_test_spec *const tspec = &ts_tests[i];
bool valid = timespec_is_valid(&tspec->invalid_ts);
zexpect_equal(valid, tspec->expect_valid,
"%d: timespec_is_valid({%ld, %ld}) = %s, expected true", i,
tspec->valid_ts.tv_sec, tspec->valid_ts.tv_nsec,
tspec->expect_valid ? "false" : "true");
}
}
ZTEST(timeutil_api, test_timespec_normalize)
{
ARRAY_FOR_EACH(ts_tests, i) {
bool different, corrected;
bool overflow;
const struct ts_test_spec *const tspec = &ts_tests[i];
struct timespec norm = tspec->invalid_ts;
TC_PRINT("%zu: timespec_normalize({%lld, %lld})\n", i,
(long long)tspec->invalid_ts.tv_sec, (long long)tspec->invalid_ts.tv_nsec);
overflow = !timespec_normalize(&norm);
zexpect_not_equal(tspec->expect_valid || tspec->correctable, overflow,
"%d: timespec_normalize({%lld, %lld}) %s, unexpectedly", i,
(long long)tspec->invalid_ts.tv_sec,
(long long)tspec->invalid_ts.tv_nsec,
tspec->correctable ? "failed" : "succeeded");
if (!tspec->expect_valid && tspec->correctable) {
different = !timespec_equal(&tspec->invalid_ts, &norm);
corrected = timespec_equal(&tspec->valid_ts, &norm);
zexpect_true(different && corrected,
"%d: {%lld, %lld} is not properly corrected:"
"{%lld, %lld} != {%lld, %lld}", i,
(long long)tspec->invalid_ts.tv_sec,
(long long)tspec->invalid_ts.tv_nsec,
(long long)tspec->valid_ts.tv_sec,
(long long)tspec->valid_ts.tv_nsec,
(long long)norm.tv_sec,
(long long)norm.tv_nsec);
}
}
}
ZTEST(timeutil_api, test_timespec_add)
{
bool overflow;
struct timespec actual;
const struct atspec {
struct timespec a;
struct timespec b;
struct timespec result;
bool expect;
} tspecs[] = {
/* non-overflow cases */
{.a = {0, 0}, .b = {0, 0}, .result = {0, 0}, .expect = false},
{.a = {1, 1}, .b = {1, 1}, .result = {2, 2}, .expect = false},
{.a = {-1, 1}, .b = {-1, 1}, .result = {-2, 2}, .expect = false},
{.a = {-1, NSEC_PER_SEC - 1}, .b = {0, 1}, .result = {0, 0}, .expect = false},
/* overflow cases */
{.a = {SYS_TIME_T_MAX, 0}, .b = {1, 0}, .result = {0}, .expect = true},
{.a = {SYS_TIME_T_MIN, 0}, .b = {-1, 0}, .result = {0}, .expect = true},
{.a = {SYS_TIME_T_MAX, NSEC_PER_SEC - 1}, .b = {1, 1}, .result = {0},
.expect = true},
{.a = {SYS_TIME_T_MIN, NSEC_PER_SEC - 1}, .b = {-1, 0}, .result = {0},
.expect = true},
};
ARRAY_FOR_EACH(tspecs, i) {
const struct atspec *const tspec = &tspecs[i];
actual = tspec->a;
overflow = !timespec_add(&actual, &tspec->b);
zexpect_equal(overflow, tspec->expect,
"%d: timespec_add({%ld, %ld}, {%ld, %ld}) %s, unexpectedly", i,
tspec->a.tv_sec, tspec->a.tv_nsec, tspec->b.tv_sec, tspec->b.tv_nsec,
tspec->expect ? "succeeded" : "failed");
if (!tspec->expect) {
zexpect_equal(timespec_equal(&actual, &tspec->result), true,
"%d: {%ld, %ld} and {%ld, %ld} are unexpectedly different", i,
actual.tv_sec, actual.tv_nsec, tspec->result.tv_sec,
tspec->result.tv_nsec);
}
}
}
ZTEST(timeutil_api, test_timespec_negate)
{
struct ntspec {
struct timespec ts;
struct timespec result;
bool expect_failure;
} tspecs[] = {
/* non-overflow cases */
{.ts = {0, 0}, .result = {0, 0}, .expect_failure = false},
{.ts = {1, 1}, .result = {-2, NSEC_PER_SEC - 1}, .expect_failure = false},
{.ts = {-1, 1}, .result = {0, NSEC_PER_SEC - 1}, .expect_failure = false},
{.ts = {SYS_TIME_T_MAX, 0}, .result = {SYS_TIME_T_MIN + 1, 0},
.expect_failure = false},
/* overflow cases */
{.ts = {SYS_TIME_T_MIN, 0}, .result = {0}, .expect_failure = true},
};
ARRAY_FOR_EACH(tspecs, i) {
bool overflow;
const struct ntspec *const tspec = &tspecs[i];
struct timespec actual = tspec->ts;
overflow = !timespec_negate(&actual);
zexpect_equal(overflow, tspec->expect_failure,
"%d: timespec_negate({%ld, %ld}) %s, unexpectedly", i,
tspec->ts.tv_sec, tspec->ts.tv_nsec,
tspec->expect_failure ? "did not overflow" : "overflowed");
if (!tspec->expect_failure) {
zexpect_true(timespec_equal(&actual, &tspec->result),
"%d: {%ld, %ld} and {%ld, %ld} are unexpectedly different", i,
actual.tv_sec, actual.tv_nsec, tspec->result.tv_sec,
tspec->result.tv_nsec);
}
}
}
ZTEST(timeutil_api, test_timespec_sub)
{
struct timespec a = {0};
struct timespec b = {0};
zexpect_true(timespec_sub(&a, &b));
}
ZTEST(timeutil_api, test_timespec_compare)
{
struct timespec a;
struct timespec b;
a = (struct timespec){0};
b = (struct timespec){0};
zexpect_equal(timespec_compare(&a, &b), 0);
a = (struct timespec){-1};
b = (struct timespec){0};
zexpect_equal(timespec_compare(&a, &b), -1);
a = (struct timespec){1};
b = (struct timespec){0};
zexpect_equal(timespec_compare(&a, &b), 1);
}
ZTEST(timeutil_api, test_timespec_equal)
{
struct timespec a;
struct timespec b;
a = (struct timespec){0};
b = (struct timespec){0};
zexpect_true(timespec_equal(&a, &b));
a = (struct timespec){-1};
b = (struct timespec){0};
zexpect_false(timespec_equal(&a, &b));
}
ZTEST(timeutil_api, test_SYS_TICKS_TO_SECS)
{
zexpect_equal(SYS_TICKS_TO_SECS(0), 0);
zexpect_equal(SYS_TICKS_TO_SECS(CONFIG_SYS_CLOCK_TICKS_PER_SEC), 1);
zexpect_equal(SYS_TICKS_TO_SECS(2 * CONFIG_SYS_CLOCK_TICKS_PER_SEC), 2);
zexpect_equal(SYS_TICKS_TO_SECS(K_TICKS_FOREVER), SYS_TIME_T_MAX);
if (SYS_TIME_T_MAX >= 92233720368547758LL) {
/* These checks should only be done if time_t has enough bits to hold K_TS_MAX */
zexpect_equal(SYS_TICKS_TO_SECS(K_TICK_MAX), SYS_TIMESPEC_MAX.tv_sec);
#if defined(CONFIG_TIMEOUT_64BIT) && (CONFIG_SYS_CLOCK_TICKS_PER_SEC == 100)
zexpect_equal(SYS_TIMESPEC_MAX.tv_sec, 92233720368547758LL);
#endif
}
#if (CONFIG_SYS_CLOCK_TICKS_PER_SEC == 32768)
#if defined(CONFIG_TIMEOUT_64BIT)
if (SYS_TIME_T_MAX >= 281474976710655LL) {
zexpect_equal(SYS_TIMESPEC_MAX.tv_sec, 281474976710655LL);
}
#else
zexpect_equal(SYS_TIMESPEC_MAX.tv_sec, 131071);
#endif
#endif
}
ZTEST(timeutil_api, test_SYS_TICKS_TO_NSECS)
{
zexpect_equal(SYS_TICKS_TO_NSECS(0), 0);
zexpect_equal(SYS_TICKS_TO_NSECS(1) % NSEC_PER_SEC,
(NSEC_PER_SEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC) % NSEC_PER_SEC);
zexpect_equal(SYS_TICKS_TO_NSECS(2) % NSEC_PER_SEC,
(2 * NSEC_PER_SEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC) % NSEC_PER_SEC);
zexpect_equal(SYS_TICKS_TO_NSECS(K_TICK_MAX), SYS_TIMESPEC_MAX.tv_nsec);
zexpect_equal(SYS_TICKS_TO_NSECS(K_TICKS_FOREVER), NSEC_PER_SEC - 1);
#if defined(CONFIG_TIMEOUT_64BIT) && (CONFIG_SYS_CLOCK_TICKS_PER_SEC == 100)
zexpect_equal(SYS_TIMESPEC_MAX.tv_nsec, 70000000L);
#endif
#if (CONFIG_SYS_CLOCK_TICKS_PER_SEC == 32768)
#if defined(CONFIG_TIMEOUT_64BIT)
zexpect_equal(SYS_TIMESPEC_MAX.tv_nsec, 999969482L);
#else
zexpect_equal(SYS_TIMESPEC_MAX.tv_nsec, 999938964L);
#endif
#endif
}
/* non-saturating */
#define DECL_TOSPEC_TEST(to, ts, sat, neg, round) \
{ \
.timeout = (to), \
.tspec = (ts), \
.saturation = (sat), \
.negative = (neg), \
.roundup = (round), \
}
/* negative timespecs rounded up to K_NO_WAIT */
#define DECL_TOSPEC_NEGATIVE_TEST(ts) DECL_TOSPEC_TEST(K_NO_WAIT, (ts), 0, true, false)
/* zero-valued timeout */
#define DECL_TOSPEC_ZERO_TEST(to) DECL_TOSPEC_TEST((to), SYS_TIMESPEC(0, 0), 0, false, false)
/* round up toward K_TICK_MIN */
#define DECL_NSAT_TOSPEC_TEST(ts) DECL_TOSPEC_TEST(K_TICKS(K_TICK_MIN), (ts), -1, false, false)
/* round up toward next tick boundary */
#define DECL_ROUND_TOSPEC_TEST(to, ts) DECL_TOSPEC_TEST((to), (ts), 0, false, true)
/* round down toward K_TICK_MAX */
#define DECL_PSAT_TOSPEC_TEST(ts) DECL_TOSPEC_TEST(K_TICKS(K_TICK_MAX), (ts), 1, false, false)
static const struct tospec {
k_timeout_t timeout;
struct timespec tspec;
int saturation;
bool negative;
bool roundup;
} tospecs[] = {
/* negative timespecs should round-up to K_NO_WAIT */
DECL_TOSPEC_NEGATIVE_TEST(SYS_TIMESPEC(SYS_TIME_T_MIN, 0)),
DECL_TOSPEC_NEGATIVE_TEST(SYS_TIMESPEC(-1, 0)),
DECL_TOSPEC_NEGATIVE_TEST(SYS_TIMESPEC(-1, NSEC_PER_SEC - 1)),
/* zero-valued timeouts are equivalent to K_NO_WAIT */
DECL_TOSPEC_ZERO_TEST(K_NSEC(0)),
DECL_TOSPEC_ZERO_TEST(K_USEC(0)),
DECL_TOSPEC_ZERO_TEST(K_MSEC(0)),
DECL_TOSPEC_ZERO_TEST(K_SECONDS(0)),
/* round up to K_TICK_MIN */
DECL_NSAT_TOSPEC_TEST(SYS_TIMESPEC(0, 1)),
DECL_NSAT_TOSPEC_TEST(SYS_TIMESPEC(0, 2)),
#if CONFIG_SYS_CLOCK_TICKS_PER_SEC > 1
DECL_NSAT_TOSPEC_TEST(SYS_TIMESPEC(0, SYS_TICKS_TO_NSECS(K_TICK_MIN))),
#endif
#if CONFIG_SYS_CLOCK_TICKS_PER_SEC < MHZ(1)
DECL_NSAT_TOSPEC_TEST(SYS_TIMESPEC(0, NSEC_PER_USEC)),
#endif
#if CONFIG_SYS_CLOCK_TICKS_PER_SEC < KHZ(1)
DECL_NSAT_TOSPEC_TEST(SYS_TIMESPEC(0, NSEC_PER_MSEC)),
#endif
/* round to next tick boundary (low-end) */
#if CONFIG_SYS_CLOCK_TICKS_PER_SEC > 1
DECL_ROUND_TOSPEC_TEST(K_TICKS(2), SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(1, 1)),
DECL_ROUND_TOSPEC_TEST(K_TICKS(2),
SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(1, SYS_TICKS_TO_NSECS(1) / 2)),
DECL_ROUND_TOSPEC_TEST(K_TICKS(2),
SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(1, SYS_TICKS_TO_NSECS(1) - 1)),
#endif
/* exact conversions for large timeouts */
#ifdef CONFIG_TIMEOUT_64BIT
DECL_TOSPEC_TEST(K_NSEC(2000000000), SYS_TIMESPEC(2, 0), 0, false, false),
#endif
DECL_TOSPEC_TEST(K_USEC(2000000), SYS_TIMESPEC(2, 0), 0, false, false),
DECL_TOSPEC_TEST(K_MSEC(2000), SYS_TIMESPEC(2, 0), 0, false, false),
DECL_TOSPEC_TEST(K_SECONDS(1),
SYS_TIMESPEC(1, SYS_TICKS_TO_NSECS(CONFIG_SYS_CLOCK_TICKS_PER_SEC)), 0,
false, false),
DECL_TOSPEC_TEST(K_SECONDS(2),
SYS_TIMESPEC(2, SYS_TICKS_TO_NSECS(2 * CONFIG_SYS_CLOCK_TICKS_PER_SEC)), 0,
false, false),
DECL_TOSPEC_TEST(K_SECONDS(100),
SYS_TIMESPEC(100,
SYS_TICKS_TO_NSECS(100 * CONFIG_SYS_CLOCK_TICKS_PER_SEC)),
0, false, false),
DECL_TOSPEC_TEST(K_TICKS(1000), SYS_TICKS_TO_TIMESPEC(1000), 0, false, false),
/* round to next tick boundary (high-end) */
#if CONFIG_SYS_CLOCK_TICKS_PER_SEC > 1
DECL_ROUND_TOSPEC_TEST(K_TICKS(1000), SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(999, 1)),
DECL_ROUND_TOSPEC_TEST(K_TICKS(1000),
SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(999, SYS_TICKS_TO_NSECS(1) / 2)),
DECL_ROUND_TOSPEC_TEST(K_TICKS(1000),
SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(999, SYS_TICKS_TO_NSECS(1) - 1)),
#endif
/* round down toward K_TICK_MAX */
DECL_PSAT_TOSPEC_TEST(SYS_TICKS_TO_TIMESPEC(K_TICK_MAX)),
/* K_FOREVER <=> SYS_TIMESPEC_FOREVER */
DECL_TOSPEC_TEST(K_FOREVER, SYS_TIMESPEC(SYS_TIME_T_MAX, NSEC_PER_SEC - 1), 0, false,
false),
};
ZTEST(timeutil_api, test_timespec_from_timeout)
{
ARRAY_FOR_EACH(tospecs, i) {
const struct tospec *const tspec = &tospecs[i];
struct timespec actual;
/*
* In this test we only check exact conversions, so skip negative timespecs that
* saturate up to K_NO_WAIT and skip values under SYS_TIMESPEC_MIN and over
* SYS_TIMESPEC_MAX. Also, skip "normal" conversions that just round up to the next
* tick boundary.
*/
if (tspec->negative || (tspec->saturation != 0) || tspec->roundup) {
continue;
}
TC_PRINT("%zu: ticks: {%lld}, timespec: {%lld, %lld}\n", i,
(long long)tspec->timeout.ticks, (long long)tspec->tspec.tv_sec,
(long long)tspec->tspec.tv_nsec);
timespec_from_timeout(tspec->timeout, &actual);
zexpect_true(timespec_equal(&actual, &tspec->tspec),
"%d: {%ld, %ld} and {%ld, %ld} are unexpectedly different", i,
actual.tv_sec, actual.tv_nsec, tspec->tspec.tv_sec,
tspec->tspec.tv_nsec);
}
}
ZTEST(timeutil_api, test_timespec_to_timeout)
{
ARRAY_FOR_EACH(tospecs, i) {
const struct tospec *const tspec = &tospecs[i];
k_timeout_t actual;
struct timespec tick_ts;
struct timespec rem = {};
TC_PRINT("%zu: ticks: {%lld}, timespec: {%lld, %lld}\n", i,
(long long)tspec->timeout.ticks, (long long)tspec->tspec.tv_sec,
(long long)tspec->tspec.tv_nsec);
actual = timespec_to_timeout(&tspec->tspec, &rem);
if (tspec->saturation == 0) {
/* exact match or rounding up */
if (!tspec->negative &&
(timespec_compare(&tspec->tspec, &SYS_TIMESPEC_NO_WAIT) != 0) &&
(timespec_compare(&tspec->tspec, &SYS_TIMESPEC_FOREVER) != 0)) {
__ASSERT(timespec_compare(&tspec->tspec, &SYS_TIMESPEC_MIN) >= 0,
"%zu: timespec: {%lld, %lld} is not greater than "
"SYS_TIMESPEC_MIN",
i, (long long)tspec->tspec.tv_sec,
(long long)tspec->tspec.tv_nsec);
__ASSERT(timespec_compare(&tspec->tspec, &SYS_TIMESPEC_MAX) <= 0,
"%zu: timespec: {%lld, %lld} is not less than "
"SYS_TIMESPEC_MAX",
i, (long long)tspec->tspec.tv_sec,
(long long)tspec->tspec.tv_nsec);
}
zexpect_equal(actual.ticks, tspec->timeout.ticks,
"%d: {%" PRId64 "} and {%" PRId64
"} are unexpectedly different",
i, (int64_t)actual.ticks, (int64_t)tspec->timeout.ticks);
} else if (tspec->saturation < 0) {
/* K_TICK_MIN saturation */
__ASSERT(timespec_compare(&tspec->tspec, &SYS_TIMESPEC_MIN) <= 0,
"timespec: {%lld, %lld} is not less than or equal to "
"SYS_TIMESPEC_MIN "
"{%lld, %lld}",
(long long)tspec->tspec.tv_sec, (long long)tspec->tspec.tv_nsec,
(long long)SYS_TIMESPEC_MIN.tv_sec,
(long long)SYS_TIMESPEC_MIN.tv_nsec);
zexpect_equal(actual.ticks, K_TICK_MIN,
"%d: {%" PRId64 "} and {%" PRId64
"} are unexpectedly different",
i, (int64_t)actual.ticks, (int64_t)K_TICK_MIN);
} else if (tspec->saturation > 0) {
/* K_TICK_MAX saturation */
__ASSERT(timespec_compare(&tspec->tspec, &SYS_TIMESPEC_MAX) >= 0,
"timespec: {%lld, %lld} is not greater than or equal to "
"SYS_TIMESPEC_MAX "
"{%lld, %lld}",
(long long)tspec->tspec.tv_sec, (long long)tspec->tspec.tv_nsec,
(long long)SYS_TIMESPEC_MAX.tv_sec,
(long long)SYS_TIMESPEC_MAX.tv_nsec);
zexpect_equal(actual.ticks, K_TICK_MAX,
"%d: {%" PRId64 "} and {%" PRId64
"} are unexpectedly different",
i, (int64_t)actual.ticks, (int64_t)K_TICK_MAX);
}
timespec_from_timeout(tspec->timeout, &tick_ts);
timespec_add(&tick_ts, &rem);
zexpect_true(timespec_equal(&tick_ts, &tspec->tspec),
"%d: {%ld, %ld} and {%ld, %ld} are unexpectedly different", i,
tick_ts.tv_sec, tick_ts.tv_nsec, tspec->tspec.tv_sec,
tspec->tspec.tv_nsec);
}
#if defined(CONFIG_TIMEOUT_64BIT) && (CONFIG_SYS_CLOCK_TICKS_PER_SEC == 100)
{
struct timespec rem = {};
k_timeout_t to = K_TICKS(K_TICK_MAX);
/* SYS_TIMESPEC_MAX corresponding K_TICK_MAX with a tick rate of 100 Hz */
struct timespec ts = SYS_TIMESPEC(92233720368547758LL, 70000000L);
zexpect_true(K_TIMEOUT_EQ(timespec_to_timeout(&ts, &rem), to),
"timespec_to_timeout(%lld, %lld) != %lld", (long long)ts.tv_sec,
(long long)ts.tv_nsec, (long long)to.ticks);
zexpect_true(timespec_equal(&rem, &SYS_TIMESPEC_NO_WAIT),
"non-zero remainder {%lld, %lld}", (long long)rem.tv_sec,
(long long)rem.tv_nsec);
TC_PRINT("timespec_to_timeout():\nts: {%lld, %lld} => to: {%lld}, rem: {%lld, "
"%lld}\n",
(long long)ts.tv_sec, (long long)ts.tv_nsec, (long long)to.ticks,
(long long)rem.tv_sec, (long long)rem.tv_nsec);
}
#endif
}
static void *setup(void)
{
TC_PRINT("CONFIG_SYS_CLOCK_TICKS_PER_SEC=%d\n", CONFIG_SYS_CLOCK_TICKS_PER_SEC);
TC_PRINT("CONFIG_TIMEOUT_64BIT=%c\n", IS_ENABLED(CONFIG_TIMEOUT_64BIT) ? 'y' : 'n');
TC_PRINT("K_TICK_MIN: %lld\n", (long long)K_TICK_MIN);
TC_PRINT("K_TICK_MAX: %lld\n", (long long)K_TICK_MAX);
TC_PRINT("SYS_TIMESPEC_MIN: {%lld, %lld}\n", (long long)SYS_TIMESPEC_MIN.tv_sec,
(long long)SYS_TIMESPEC_MIN.tv_nsec);
TC_PRINT("SYS_TIMESPEC_MAX: {%lld, %lld}\n", (long long)SYS_TIMESPEC_MAX.tv_sec,
(long long)SYS_TIMESPEC_MAX.tv_nsec);
TC_PRINT("INT64_MIN: %lld\n", (long long)INT64_MIN);
TC_PRINT("INT64_MAX: %lld\n", (long long)INT64_MAX);
PRINT_LINE;
/* check numerical values corresponding to K_TICK_MAX */
zassert_equal(K_TICK_MAX, IS_ENABLED(CONFIG_TIMEOUT_64BIT) ? INT64_MAX : UINT32_MAX - 1);
return NULL;
}
ZTEST_SUITE(timeutil_api, NULL, setup, NULL, NULL, NULL);