1 /*
2 * Copyright 2025 Tenstorrent AI ULC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdbool.h>
8 #include <time.h>
9
10 #include <zephyr/ztest.h>
11 #include <zephyr/sys_clock.h>
12 #include <zephyr/sys/timeutil.h>
13 #include <zephyr/sys/util.h>
14
15 #undef CORRECTABLE
16 #define CORRECTABLE true
17
18 #undef UNCORRECTABLE
19 #define UNCORRECTABLE false
20
21 /* Initialize a struct timespec object from a tick count with additional nanoseconds */
22 #define SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(ticks, ns) \
23 SYS_TIMESPEC(SYS_TICKS_TO_SECS(ticks) + \
24 (SYS_TICKS_TO_NSECS(ticks) + (uint64_t)(ns)) / NSEC_PER_SEC, \
25 (SYS_TICKS_TO_NSECS(ticks) + (uint64_t)(ns)) % NSEC_PER_SEC)
26
27 /*
28 * test spec for simple timespec validation
29 *
30 * If a timespec is expected to be valid, then invalid_ts and valid_ts are equal.
31 *
32 * If a timespec is expected to be invalid, then invalid_ts and valid_ts are not equal.
33 */
34 struct ts_test_spec {
35 struct timespec invalid_ts;
36 struct timespec valid_ts;
37 bool expect_valid;
38 bool correctable;
39 };
40
41 #define DECL_VALID_TS_TEST(sec, nsec) \
42 { \
43 .invalid_ts = {(sec), (nsec)}, \
44 .valid_ts = {(sec), (nsec)}, \
45 .expect_valid = true, \
46 .correctable = false, \
47 }
48
49 /*
50 * Invalid timespecs can usually be converted to valid ones by adding or subtracting
51 * multiples of `NSEC_PER_SEC` from tv_nsec, and incrementing or decrementing tv_sec
52 * proportionately, unless doing so would result in signed integer overflow.
53 *
54 * There are two particular corner cases:
55 * 1. making tv_sec more negative would overflow the tv_sec field in the negative direction
56 * 1. making tv_sec more positive would overflow the tv_sec field in the positive direction
57 */
58 #define DECL_INVALID_TS_TEST(invalid_sec, invalid_nsec, valid_sec, valid_nsec, is_correctable) \
59 { \
60 .valid_ts = {(valid_sec), (valid_nsec)}, \
61 .invalid_ts = {(invalid_sec), (invalid_nsec)}, \
62 .expect_valid = false, \
63 .correctable = (is_correctable), \
64 }
65
66 /*
67 * Easily verifiable tests for both valid and invalid timespecs.
68 */
69 static const struct ts_test_spec ts_tests[] = {
70 /* Valid cases */
71 DECL_VALID_TS_TEST(0, 0),
72 DECL_VALID_TS_TEST(0, 1),
73 DECL_VALID_TS_TEST(1, 0),
74 DECL_VALID_TS_TEST(1, 1),
75 DECL_VALID_TS_TEST(1, NSEC_PER_SEC - 1),
76 DECL_VALID_TS_TEST(-1, 0),
77 DECL_VALID_TS_TEST(-1, 1),
78 DECL_VALID_TS_TEST(-1, 0),
79 DECL_VALID_TS_TEST(-1, 1),
80 DECL_VALID_TS_TEST(-1, NSEC_PER_SEC - 1),
81 DECL_VALID_TS_TEST(SYS_TIME_T_MIN, 0),
82 DECL_VALID_TS_TEST(SYS_TIME_T_MIN, NSEC_PER_SEC - 1),
83 DECL_VALID_TS_TEST(SYS_TIME_T_MAX, 0),
84 DECL_VALID_TS_TEST(SYS_TIME_T_MAX, NSEC_PER_SEC - 1),
85
86 /* Correctable, invalid cases */
87 DECL_INVALID_TS_TEST(0, -2LL * NSEC_PER_SEC + 1, -2, 1, CORRECTABLE),
88 DECL_INVALID_TS_TEST(0, -2LL * NSEC_PER_SEC - 1, -3, NSEC_PER_SEC - 1, CORRECTABLE),
89 DECL_INVALID_TS_TEST(0, -1LL * NSEC_PER_SEC - 1, -2, NSEC_PER_SEC - 1, CORRECTABLE),
90 DECL_INVALID_TS_TEST(0, -1, -1, NSEC_PER_SEC - 1, CORRECTABLE),
91 DECL_INVALID_TS_TEST(0, NSEC_PER_SEC, 1, 0, CORRECTABLE),
92 DECL_INVALID_TS_TEST(0, NSEC_PER_SEC + 1, 1, 1, CORRECTABLE),
93 DECL_INVALID_TS_TEST(1, -1, 0, NSEC_PER_SEC - 1, CORRECTABLE),
94 DECL_INVALID_TS_TEST(1, NSEC_PER_SEC, 2, 0, CORRECTABLE),
95 DECL_INVALID_TS_TEST(-1, -1, -2, NSEC_PER_SEC - 1, CORRECTABLE),
96 DECL_INVALID_TS_TEST(0, NSEC_PER_SEC, 1, 0, CORRECTABLE),
97 DECL_INVALID_TS_TEST(1, -1, 0, NSEC_PER_SEC - 1, CORRECTABLE),
98 DECL_INVALID_TS_TEST(1, NSEC_PER_SEC, 2, 0, CORRECTABLE),
99 DECL_INVALID_TS_TEST(SYS_TIME_T_MIN, NSEC_PER_SEC, SYS_TIME_T_MIN + 1, 0, CORRECTABLE),
100 DECL_INVALID_TS_TEST(SYS_TIME_T_MAX, -1, SYS_TIME_T_MAX - 1, NSEC_PER_SEC - 1, CORRECTABLE),
101 DECL_INVALID_TS_TEST(0, LONG_MIN, (int64_t)LONG_MIN / NSEC_PER_SEC - 1,
102 NSEC_PER_SEC + LONG_MIN % (long long)NSEC_PER_SEC, CORRECTABLE),
103 DECL_INVALID_TS_TEST(0, LONG_MAX, LONG_MAX / NSEC_PER_SEC, LONG_MAX % NSEC_PER_SEC,
104 CORRECTABLE),
105
106 /* Uncorrectable, invalid cases */
107 DECL_INVALID_TS_TEST(SYS_TIME_T_MIN + 2, -2 * (int64_t)NSEC_PER_SEC - 1, 0, 0,
108 UNCORRECTABLE),
109 DECL_INVALID_TS_TEST(SYS_TIME_T_MIN + 1, -(int64_t)NSEC_PER_SEC - 1, 0, 0, UNCORRECTABLE),
110 DECL_INVALID_TS_TEST(SYS_TIME_T_MIN + 1, -(int64_t)NSEC_PER_SEC - 1, 0, 0, UNCORRECTABLE),
111 DECL_INVALID_TS_TEST(SYS_TIME_T_MIN, -1, 0, 0, UNCORRECTABLE),
112 DECL_INVALID_TS_TEST(SYS_TIME_T_MAX, (int64_t)NSEC_PER_SEC, 0, 0, UNCORRECTABLE),
113 DECL_INVALID_TS_TEST(SYS_TIME_T_MAX - 1, 2 * (int64_t)NSEC_PER_SEC, 0, 0, UNCORRECTABLE),
114 };
115
ZTEST(timeutil_api,test_timespec_is_valid)116 ZTEST(timeutil_api, test_timespec_is_valid)
117 {
118 ARRAY_FOR_EACH(ts_tests, i) {
119 const struct ts_test_spec *const tspec = &ts_tests[i];
120 bool valid = timespec_is_valid(&tspec->invalid_ts);
121
122 zexpect_equal(valid, tspec->expect_valid,
123 "%zu: timespec_is_valid({%lld, %lld}) = %s, expected true", i,
124 (long long)tspec->valid_ts.tv_sec, (long long)tspec->valid_ts.tv_nsec,
125 tspec->expect_valid ? "false" : "true");
126 }
127 }
128
ZTEST(timeutil_api,test_timespec_normalize)129 ZTEST(timeutil_api, test_timespec_normalize)
130 {
131 ARRAY_FOR_EACH(ts_tests, i) {
132 bool different, corrected;
133 bool overflow;
134 const struct ts_test_spec *const tspec = &ts_tests[i];
135 struct timespec norm = tspec->invalid_ts;
136
137 TC_PRINT("%zu: timespec_normalize({%lld, %lld})\n", i,
138 (long long)tspec->invalid_ts.tv_sec, (long long)tspec->invalid_ts.tv_nsec);
139
140 overflow = !timespec_normalize(&norm);
141 zexpect_not_equal(tspec->expect_valid || tspec->correctable, overflow,
142 "%zu: timespec_normalize({%lld, %lld}) %s, unexpectedly", i,
143 (long long)tspec->invalid_ts.tv_sec,
144 (long long)tspec->invalid_ts.tv_nsec,
145 tspec->correctable ? "failed" : "succeeded");
146
147 if (!tspec->expect_valid && tspec->correctable) {
148 different = !timespec_equal(&tspec->invalid_ts, &norm);
149 corrected = timespec_equal(&tspec->valid_ts, &norm);
150 zexpect_true(different && corrected,
151 "%zu: {%lld, %lld} is not properly corrected:"
152 "{%lld, %lld} != {%lld, %lld}", i,
153 (long long)tspec->invalid_ts.tv_sec,
154 (long long)tspec->invalid_ts.tv_nsec,
155 (long long)tspec->valid_ts.tv_sec,
156 (long long)tspec->valid_ts.tv_nsec,
157 (long long)norm.tv_sec,
158 (long long)norm.tv_nsec);
159 }
160 }
161 }
162
ZTEST(timeutil_api,test_timespec_add)163 ZTEST(timeutil_api, test_timespec_add)
164 {
165 bool overflow;
166 struct timespec actual;
167 const struct atspec {
168 struct timespec a;
169 struct timespec b;
170 struct timespec result;
171 bool expect;
172 } tspecs[] = {
173 /* non-overflow cases */
174 {.a = {0, 0}, .b = {0, 0}, .result = {0, 0}, .expect = false},
175 {.a = {1, 1}, .b = {1, 1}, .result = {2, 2}, .expect = false},
176 {.a = {-1, 1}, .b = {-1, 1}, .result = {-2, 2}, .expect = false},
177 {.a = {-1, NSEC_PER_SEC - 1}, .b = {0, 1}, .result = {0, 0}, .expect = false},
178 /* overflow cases */
179 {.a = {SYS_TIME_T_MAX, 0}, .b = {1, 0}, .result = {0}, .expect = true},
180 {.a = {SYS_TIME_T_MIN, 0}, .b = {-1, 0}, .result = {0}, .expect = true},
181 {.a = {SYS_TIME_T_MAX, NSEC_PER_SEC - 1}, .b = {1, 1}, .result = {0},
182 .expect = true},
183 {.a = {SYS_TIME_T_MIN, NSEC_PER_SEC - 1}, .b = {-1, 0}, .result = {0},
184 .expect = true},
185 };
186
187 ARRAY_FOR_EACH(tspecs, i) {
188 const struct atspec *const tspec = &tspecs[i];
189
190 actual = tspec->a;
191 overflow = !timespec_add(&actual, &tspec->b);
192
193 zexpect_equal(overflow, tspec->expect,
194 "%zu: timespec_add({%lld, %lld}, {%lld, %lld}) %s, unexpectedly", i,
195 (long long)tspec->a.tv_sec, (long long)tspec->a.tv_nsec,
196 (long long)tspec->b.tv_sec, (long long)tspec->b.tv_nsec,
197 tspec->expect ? "succeeded" : "failed");
198
199 if (!tspec->expect) {
200 zexpect_equal(
201 timespec_equal(&actual, &tspec->result), true,
202 "%zu: {%lld, %lld} and {%lld, %lld} are unexpectedly different", i,
203 (long long)actual.tv_sec, (long long)actual.tv_nsec,
204 (long long)tspec->result.tv_sec, (long long)tspec->result.tv_nsec);
205 }
206 }
207 }
208
ZTEST(timeutil_api,test_timespec_negate)209 ZTEST(timeutil_api, test_timespec_negate)
210 {
211 struct ntspec {
212 struct timespec ts;
213 struct timespec result;
214 bool expect_failure;
215 } tspecs[] = {
216 /* non-overflow cases */
217 {.ts = {0, 0}, .result = {0, 0}, .expect_failure = false},
218 {.ts = {1, 1}, .result = {-2, NSEC_PER_SEC - 1}, .expect_failure = false},
219 {.ts = {-1, 1}, .result = {0, NSEC_PER_SEC - 1}, .expect_failure = false},
220 {.ts = {SYS_TIME_T_MAX, 0}, .result = {SYS_TIME_T_MIN + 1, 0},
221 .expect_failure = false},
222 /* overflow cases */
223 {.ts = {SYS_TIME_T_MIN, 0}, .result = {0}, .expect_failure = true},
224 };
225
226 ARRAY_FOR_EACH(tspecs, i) {
227 bool overflow;
228 const struct ntspec *const tspec = &tspecs[i];
229 struct timespec actual = tspec->ts;
230
231 overflow = !timespec_negate(&actual);
232 zexpect_equal(overflow, tspec->expect_failure,
233 "%zu: timespec_negate({%lld, %lld}) %s, unexpectedly", i,
234 (long long)tspec->ts.tv_sec, (long long)tspec->ts.tv_nsec,
235 tspec->expect_failure ? "did not overflow" : "overflowed");
236
237 if (!tspec->expect_failure) {
238 zexpect_true(
239 timespec_equal(&actual, &tspec->result),
240 "%zu: {%lld, %lld} and {%lld, %lld} are unexpectedly different", i,
241 (long long)actual.tv_sec, (long long)actual.tv_nsec,
242 (long long)tspec->result.tv_sec, (long long)tspec->result.tv_nsec);
243 }
244 }
245 }
246
ZTEST(timeutil_api,test_timespec_sub)247 ZTEST(timeutil_api, test_timespec_sub)
248 {
249 struct timespec a = {0};
250 struct timespec b = {0};
251
252 zexpect_true(timespec_sub(&a, &b));
253 }
254
ZTEST(timeutil_api,test_timespec_compare)255 ZTEST(timeutil_api, test_timespec_compare)
256 {
257 struct timespec a;
258 struct timespec b;
259
260 a = (struct timespec){0};
261 b = (struct timespec){0};
262 zexpect_equal(timespec_compare(&a, &b), 0);
263
264 a = (struct timespec){-1};
265 b = (struct timespec){0};
266 zexpect_equal(timespec_compare(&a, &b), -1);
267
268 a = (struct timespec){1};
269 b = (struct timespec){0};
270 zexpect_equal(timespec_compare(&a, &b), 1);
271 }
272
ZTEST(timeutil_api,test_timespec_equal)273 ZTEST(timeutil_api, test_timespec_equal)
274 {
275 struct timespec a;
276 struct timespec b;
277
278 a = (struct timespec){0};
279 b = (struct timespec){0};
280 zexpect_true(timespec_equal(&a, &b));
281
282 a = (struct timespec){-1};
283 b = (struct timespec){0};
284 zexpect_false(timespec_equal(&a, &b));
285 }
286
ZTEST(timeutil_api,test_SYS_TICKS_TO_SECS)287 ZTEST(timeutil_api, test_SYS_TICKS_TO_SECS)
288 {
289 zexpect_equal(SYS_TICKS_TO_SECS(0), 0);
290 zexpect_equal(SYS_TICKS_TO_SECS(CONFIG_SYS_CLOCK_TICKS_PER_SEC), 1);
291 zexpect_equal(SYS_TICKS_TO_SECS(2 * CONFIG_SYS_CLOCK_TICKS_PER_SEC), 2);
292 zexpect_equal(SYS_TICKS_TO_SECS(K_TICKS_FOREVER), SYS_TIME_T_MAX);
293
294 if (SYS_TIME_T_MAX >= 92233720368547758LL) {
295 /* These checks should only be done if time_t has enough bits to hold K_TS_MAX */
296 zexpect_equal(SYS_TICKS_TO_SECS(K_TICK_MAX), SYS_TIMESPEC_MAX.tv_sec);
297 #if defined(CONFIG_TIMEOUT_64BIT) && (CONFIG_SYS_CLOCK_TICKS_PER_SEC == 100)
298 zexpect_equal(SYS_TIMESPEC_MAX.tv_sec, 92233720368547758LL);
299 #endif
300 }
301
302 #if (CONFIG_SYS_CLOCK_TICKS_PER_SEC == 32768)
303 #if defined(CONFIG_TIMEOUT_64BIT)
304 if (SYS_TIME_T_MAX >= 281474976710655LL) {
305 zexpect_equal(SYS_TIMESPEC_MAX.tv_sec, 281474976710655LL);
306 }
307 #else
308 zexpect_equal(SYS_TIMESPEC_MAX.tv_sec, 131071);
309 #endif
310 #endif
311 }
312
ZTEST(timeutil_api,test_SYS_TICKS_TO_NSECS)313 ZTEST(timeutil_api, test_SYS_TICKS_TO_NSECS)
314 {
315 zexpect_equal(SYS_TICKS_TO_NSECS(0), 0);
316 zexpect_equal(SYS_TICKS_TO_NSECS(1) % NSEC_PER_SEC,
317 (NSEC_PER_SEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC) % NSEC_PER_SEC);
318 zexpect_equal(SYS_TICKS_TO_NSECS(2) % NSEC_PER_SEC,
319 (2 * NSEC_PER_SEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC) % NSEC_PER_SEC);
320 zexpect_equal(SYS_TICKS_TO_NSECS(K_TICK_MAX), SYS_TIMESPEC_MAX.tv_nsec);
321 zexpect_equal(SYS_TICKS_TO_NSECS(K_TICKS_FOREVER), NSEC_PER_SEC - 1);
322
323 #if defined(CONFIG_TIMEOUT_64BIT) && (CONFIG_SYS_CLOCK_TICKS_PER_SEC == 100)
324 zexpect_equal(SYS_TIMESPEC_MAX.tv_nsec, 70000000L);
325 #endif
326
327 #if (CONFIG_SYS_CLOCK_TICKS_PER_SEC == 32768)
328 #if defined(CONFIG_TIMEOUT_64BIT)
329 zexpect_equal(SYS_TIMESPEC_MAX.tv_nsec, 999969482L);
330 #else
331 zexpect_equal(SYS_TIMESPEC_MAX.tv_nsec, 999938964L);
332 #endif
333 #endif
334 }
335
336 /* non-saturating */
337 #define DECL_TOSPEC_TEST(to, ts, sat, neg, round) \
338 { \
339 .timeout = (to), \
340 .tspec = (ts), \
341 .saturation = (sat), \
342 .negative = (neg), \
343 .roundup = (round), \
344 }
345 /* negative timespecs rounded up to K_NO_WAIT */
346 #define DECL_TOSPEC_NEGATIVE_TEST(ts) DECL_TOSPEC_TEST(K_NO_WAIT, (ts), 0, true, false)
347 /* zero-valued timeout */
348 #define DECL_TOSPEC_ZERO_TEST(to) DECL_TOSPEC_TEST((to), SYS_TIMESPEC(0, 0), 0, false, false)
349 /* round up toward K_TICK_MIN */
350 #define DECL_NSAT_TOSPEC_TEST(ts) DECL_TOSPEC_TEST(K_TICKS(K_TICK_MIN), (ts), -1, false, false)
351 /* round up toward next tick boundary */
352 #define DECL_ROUND_TOSPEC_TEST(to, ts) DECL_TOSPEC_TEST((to), (ts), 0, false, true)
353 /* round down toward K_TICK_MAX */
354 #define DECL_PSAT_TOSPEC_TEST(ts) DECL_TOSPEC_TEST(K_TICKS(K_TICK_MAX), (ts), 1, false, false)
355
356 static const struct tospec {
357 k_timeout_t timeout;
358 struct timespec tspec;
359 int saturation;
360 bool negative;
361 bool roundup;
362 } tospecs[] = {
363 /* negative timespecs should round-up to K_NO_WAIT */
364 DECL_TOSPEC_NEGATIVE_TEST(SYS_TIMESPEC(SYS_TIME_T_MIN, 0)),
365 DECL_TOSPEC_NEGATIVE_TEST(SYS_TIMESPEC(-1, 0)),
366 DECL_TOSPEC_NEGATIVE_TEST(SYS_TIMESPEC(-1, NSEC_PER_SEC - 1)),
367
368 /* zero-valued timeouts are equivalent to K_NO_WAIT */
369 DECL_TOSPEC_ZERO_TEST(K_NSEC(0)),
370 DECL_TOSPEC_ZERO_TEST(K_USEC(0)),
371 DECL_TOSPEC_ZERO_TEST(K_MSEC(0)),
372 DECL_TOSPEC_ZERO_TEST(K_SECONDS(0)),
373
374 /* round up to K_TICK_MIN */
375 DECL_NSAT_TOSPEC_TEST(SYS_TIMESPEC(0, 1)),
376 DECL_NSAT_TOSPEC_TEST(SYS_TIMESPEC(0, 2)),
377 #if CONFIG_SYS_CLOCK_TICKS_PER_SEC > 1
378 DECL_NSAT_TOSPEC_TEST(SYS_TIMESPEC(0, SYS_TICKS_TO_NSECS(K_TICK_MIN))),
379 #endif
380
381 #if CONFIG_SYS_CLOCK_TICKS_PER_SEC < MHZ(1)
382 DECL_NSAT_TOSPEC_TEST(SYS_TIMESPEC(0, NSEC_PER_USEC)),
383 #endif
384 #if CONFIG_SYS_CLOCK_TICKS_PER_SEC < KHZ(1)
385 DECL_NSAT_TOSPEC_TEST(SYS_TIMESPEC(0, NSEC_PER_MSEC)),
386 #endif
387
388 /* round to next tick boundary (low-end) */
389 #if CONFIG_SYS_CLOCK_TICKS_PER_SEC > 1
390 DECL_ROUND_TOSPEC_TEST(K_TICKS(2), SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(1, 1)),
391 DECL_ROUND_TOSPEC_TEST(K_TICKS(2),
392 SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(1, SYS_TICKS_TO_NSECS(1) / 2)),
393 DECL_ROUND_TOSPEC_TEST(K_TICKS(2),
394 SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(1, SYS_TICKS_TO_NSECS(1) - 1)),
395 #endif
396
397 /* exact conversions for large timeouts */
398 #ifdef CONFIG_TIMEOUT_64BIT
399 DECL_TOSPEC_TEST(K_NSEC(2000000000), SYS_TIMESPEC(2, 0), 0, false, false),
400 #endif
401 DECL_TOSPEC_TEST(K_USEC(2000000), SYS_TIMESPEC(2, 0), 0, false, false),
402 DECL_TOSPEC_TEST(K_MSEC(2000), SYS_TIMESPEC(2, 0), 0, false, false),
403
404 DECL_TOSPEC_TEST(K_SECONDS(1),
405 SYS_TIMESPEC(1, SYS_TICKS_TO_NSECS(CONFIG_SYS_CLOCK_TICKS_PER_SEC)), 0,
406 false, false),
407 DECL_TOSPEC_TEST(K_SECONDS(2),
408 SYS_TIMESPEC(2, SYS_TICKS_TO_NSECS(2 * CONFIG_SYS_CLOCK_TICKS_PER_SEC)), 0,
409 false, false),
410 DECL_TOSPEC_TEST(K_SECONDS(100),
411 SYS_TIMESPEC(100,
412 SYS_TICKS_TO_NSECS(100 * CONFIG_SYS_CLOCK_TICKS_PER_SEC)),
413 0, false, false),
414
415 DECL_TOSPEC_TEST(K_TICKS(1000), SYS_TICKS_TO_TIMESPEC(1000), 0, false, false),
416
417 /* round to next tick boundary (high-end) */
418 #if CONFIG_SYS_CLOCK_TICKS_PER_SEC > 1
419 DECL_ROUND_TOSPEC_TEST(K_TICKS(1000), SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(999, 1)),
420 DECL_ROUND_TOSPEC_TEST(K_TICKS(1000),
421 SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(999, SYS_TICKS_TO_NSECS(1) / 2)),
422 DECL_ROUND_TOSPEC_TEST(K_TICKS(1000),
423 SYS_TICKS_TO_TIMESPEC_PLUS_NSECS(999, SYS_TICKS_TO_NSECS(1) - 1)),
424 #endif
425
426 /* round down toward K_TICK_MAX */
427 DECL_PSAT_TOSPEC_TEST(SYS_TICKS_TO_TIMESPEC(K_TICK_MAX)),
428
429 /* K_FOREVER <=> SYS_TIMESPEC_FOREVER */
430 DECL_TOSPEC_TEST(K_FOREVER, SYS_TIMESPEC(SYS_TIME_T_MAX, NSEC_PER_SEC - 1), 0, false,
431 false),
432 };
433
ZTEST(timeutil_api,test_timespec_from_timeout)434 ZTEST(timeutil_api, test_timespec_from_timeout)
435 {
436 ARRAY_FOR_EACH(tospecs, i) {
437 const struct tospec *const tspec = &tospecs[i];
438 struct timespec actual;
439
440 /*
441 * In this test we only check exact conversions, so skip negative timespecs that
442 * saturate up to K_NO_WAIT and skip values under SYS_TIMESPEC_MIN and over
443 * SYS_TIMESPEC_MAX. Also, skip "normal" conversions that just round up to the next
444 * tick boundary.
445 */
446 if (tspec->negative || (tspec->saturation != 0) || tspec->roundup) {
447 continue;
448 }
449
450 TC_PRINT("%zu: ticks: {%lld}, timespec: {%lld, %lld}\n", i,
451 (long long)tspec->timeout.ticks, (long long)tspec->tspec.tv_sec,
452 (long long)tspec->tspec.tv_nsec);
453
454 timespec_from_timeout(tspec->timeout, &actual);
455 zexpect_true(timespec_equal(&actual, &tspec->tspec),
456 "%zu: {%lld, %lld} and {%lld, %lld} are unexpectedly different", i,
457 (long long)actual.tv_sec, (long long)actual.tv_nsec,
458 (long long)tspec->tspec.tv_sec, (long long)tspec->tspec.tv_nsec);
459 }
460 }
461
ZTEST(timeutil_api,test_timespec_to_timeout)462 ZTEST(timeutil_api, test_timespec_to_timeout)
463 {
464 ARRAY_FOR_EACH(tospecs, i) {
465 const struct tospec *const tspec = &tospecs[i];
466 k_timeout_t actual;
467 struct timespec tick_ts;
468 struct timespec rem = {};
469
470 TC_PRINT("%zu: ticks: {%lld}, timespec: {%lld, %lld}\n", i,
471 (long long)tspec->timeout.ticks, (long long)tspec->tspec.tv_sec,
472 (long long)tspec->tspec.tv_nsec);
473
474 actual = timespec_to_timeout(&tspec->tspec, &rem);
475 if (tspec->saturation == 0) {
476 /* exact match or rounding up */
477 if (!tspec->negative &&
478 (timespec_compare(&tspec->tspec, &SYS_TIMESPEC_NO_WAIT) != 0) &&
479 (timespec_compare(&tspec->tspec, &SYS_TIMESPEC_FOREVER) != 0)) {
480 __ASSERT(timespec_compare(&tspec->tspec, &SYS_TIMESPEC_MIN) >= 0,
481 "%zu: timespec: {%lld, %lld} is not greater than "
482 "SYS_TIMESPEC_MIN",
483 i, (long long)tspec->tspec.tv_sec,
484 (long long)tspec->tspec.tv_nsec);
485 __ASSERT(timespec_compare(&tspec->tspec, &SYS_TIMESPEC_MAX) <= 0,
486 "%zu: timespec: {%lld, %lld} is not less than "
487 "SYS_TIMESPEC_MAX",
488 i, (long long)tspec->tspec.tv_sec,
489 (long long)tspec->tspec.tv_nsec);
490 }
491 zexpect_equal(actual.ticks, tspec->timeout.ticks,
492 "%zu: {%" PRId64 "} and {%" PRId64
493 "} are unexpectedly different",
494 i, (int64_t)actual.ticks, (int64_t)tspec->timeout.ticks);
495 } else if (tspec->saturation < 0) {
496 /* K_TICK_MIN saturation */
497 __ASSERT(timespec_compare(&tspec->tspec, &SYS_TIMESPEC_MIN) <= 0,
498 "timespec: {%lld, %lld} is not less than or equal to "
499 "SYS_TIMESPEC_MIN "
500 "{%lld, %lld}",
501 (long long)tspec->tspec.tv_sec, (long long)tspec->tspec.tv_nsec,
502 (long long)SYS_TIMESPEC_MIN.tv_sec,
503 (long long)SYS_TIMESPEC_MIN.tv_nsec);
504 zexpect_equal(actual.ticks, K_TICK_MIN,
505 "%zu: {%" PRId64 "} and {%" PRId64
506 "} are unexpectedly different",
507 i, (int64_t)actual.ticks, (int64_t)K_TICK_MIN);
508 } else if (tspec->saturation > 0) {
509 /* K_TICK_MAX saturation */
510 __ASSERT(timespec_compare(&tspec->tspec, &SYS_TIMESPEC_MAX) >= 0,
511 "timespec: {%lld, %lld} is not greater than or equal to "
512 "SYS_TIMESPEC_MAX "
513 "{%lld, %lld}",
514 (long long)tspec->tspec.tv_sec, (long long)tspec->tspec.tv_nsec,
515 (long long)SYS_TIMESPEC_MAX.tv_sec,
516 (long long)SYS_TIMESPEC_MAX.tv_nsec);
517 zexpect_equal(actual.ticks, K_TICK_MAX,
518 "%zu: {%" PRId64 "} and {%" PRId64
519 "} are unexpectedly different",
520 i, (int64_t)actual.ticks, (int64_t)K_TICK_MAX);
521 }
522
523 timespec_from_timeout(tspec->timeout, &tick_ts);
524 timespec_add(&tick_ts, &rem);
525 zexpect_true(timespec_equal(&tick_ts, &tspec->tspec),
526 "%zu: {%lld, %lld} and {%lld, %lld} are unexpectedly different", i,
527 (long long)tick_ts.tv_sec, (long long)tick_ts.tv_nsec,
528 (long long)tspec->tspec.tv_sec, (long long)tspec->tspec.tv_nsec);
529 }
530
531 #if defined(CONFIG_TIMEOUT_64BIT) && (CONFIG_SYS_CLOCK_TICKS_PER_SEC == 100)
532 {
533 struct timespec rem = {};
534 k_timeout_t to = K_TICKS(K_TICK_MAX);
535 /* SYS_TIMESPEC_MAX corresponding K_TICK_MAX with a tick rate of 100 Hz */
536 struct timespec ts = SYS_TIMESPEC(92233720368547758LL, 70000000L);
537
538 zexpect_true(K_TIMEOUT_EQ(timespec_to_timeout(&ts, &rem), to),
539 "timespec_to_timeout(%lld, %lld) != %lld", (long long)ts.tv_sec,
540 (long long)ts.tv_nsec, (long long)to.ticks);
541 zexpect_true(timespec_equal(&rem, &SYS_TIMESPEC_NO_WAIT),
542 "non-zero remainder {%lld, %lld}", (long long)rem.tv_sec,
543 (long long)rem.tv_nsec);
544
545 TC_PRINT("timespec_to_timeout():\nts: {%lld, %lld} => to: {%lld}, rem: {%lld, "
546 "%lld}\n",
547 (long long)ts.tv_sec, (long long)ts.tv_nsec, (long long)to.ticks,
548 (long long)rem.tv_sec, (long long)rem.tv_nsec);
549 }
550 #endif
551 }
552
setup(void)553 static void *setup(void)
554 {
555 TC_PRINT("CONFIG_SYS_CLOCK_TICKS_PER_SEC=%d\n", CONFIG_SYS_CLOCK_TICKS_PER_SEC);
556 TC_PRINT("CONFIG_TIMEOUT_64BIT=%c\n", IS_ENABLED(CONFIG_TIMEOUT_64BIT) ? 'y' : 'n');
557 TC_PRINT("K_TICK_MIN: %lld\n", (long long)K_TICK_MIN);
558 TC_PRINT("K_TICK_MAX: %lld\n", (long long)K_TICK_MAX);
559 TC_PRINT("SYS_TIMESPEC_MIN: {%lld, %lld}\n", (long long)SYS_TIMESPEC_MIN.tv_sec,
560 (long long)SYS_TIMESPEC_MIN.tv_nsec);
561 TC_PRINT("SYS_TIMESPEC_MAX: {%lld, %lld}\n", (long long)SYS_TIMESPEC_MAX.tv_sec,
562 (long long)SYS_TIMESPEC_MAX.tv_nsec);
563 TC_PRINT("INT64_MIN: %lld\n", (long long)INT64_MIN);
564 TC_PRINT("INT64_MAX: %lld\n", (long long)INT64_MAX);
565 PRINT_LINE;
566
567 /* check numerical values corresponding to K_TICK_MAX */
568 zassert_equal(K_TICK_MAX, IS_ENABLED(CONFIG_TIMEOUT_64BIT) ? INT64_MAX : UINT32_MAX - 1);
569
570 return NULL;
571 }
572
573 ZTEST_SUITE(timeutil_api, NULL, setup, NULL, NULL, NULL);
574