/* * Copyright 2025 Tenstorrent AI ULC * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #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, "%zu: timespec_is_valid({%lld, %lld}) = %s, expected true", i, (long long)tspec->valid_ts.tv_sec, (long long)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, "%zu: 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, "%zu: {%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, "%zu: timespec_add({%lld, %lld}, {%lld, %lld}) %s, unexpectedly", i, (long long)tspec->a.tv_sec, (long long)tspec->a.tv_nsec, (long long)tspec->b.tv_sec, (long long)tspec->b.tv_nsec, tspec->expect ? "succeeded" : "failed"); if (!tspec->expect) { zexpect_equal( timespec_equal(&actual, &tspec->result), true, "%zu: {%lld, %lld} and {%lld, %lld} are unexpectedly different", i, (long long)actual.tv_sec, (long long)actual.tv_nsec, (long long)tspec->result.tv_sec, (long long)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, "%zu: timespec_negate({%lld, %lld}) %s, unexpectedly", i, (long long)tspec->ts.tv_sec, (long long)tspec->ts.tv_nsec, tspec->expect_failure ? "did not overflow" : "overflowed"); if (!tspec->expect_failure) { zexpect_true( timespec_equal(&actual, &tspec->result), "%zu: {%lld, %lld} and {%lld, %lld} are unexpectedly different", i, (long long)actual.tv_sec, (long long)actual.tv_nsec, (long long)tspec->result.tv_sec, (long long)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), "%zu: {%lld, %lld} and {%lld, %lld} are unexpectedly different", i, (long long)actual.tv_sec, (long long)actual.tv_nsec, (long long)tspec->tspec.tv_sec, (long long)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, "%zu: {%" 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, "%zu: {%" 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, "%zu: {%" 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), "%zu: {%lld, %lld} and {%lld, %lld} are unexpectedly different", i, (long long)tick_ts.tv_sec, (long long)tick_ts.tv_nsec, (long long)tspec->tspec.tv_sec, (long long)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);