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