1 /*
2 * Copyright (c) 2020 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <ztest.h>
8 #include <string.h>
9 #include <inttypes.h>
10 #include <net/net_timeout.h>
11
12 #include "../../../subsys/net/ip/net_timeout.c"
13
14 #define HALFMAX_S (23U + 60U * (31U + 60U * (20U + 24U * 24U)))
15 #define NTO_MAX_S HALFMAX_S
16 #define FULLMAX_S (47U + 60U * (2U + 60U * (17U + 24U * 49U)))
17
18 #if 0
19 static void dump_nto(const struct net_timeout *nto)
20 {
21 uint64_t remaining = nto->timer_timeout;
22 uint64_t deadline = nto->timer_start;
23
24 remaining += NET_TIMEOUT_MAX_VALUE * nto->wrap_counter;
25 deadline += remaining;
26
27 printk("start %u, rem %u * %u + %u = %" PRIu64 ", ends %" PRIu64 "\n",
28 nto->timer_start, nto->wrap_counter,
29 NET_TIMEOUT_MAX_VALUE, nto->timer_timeout,
30 remaining, deadline);
31 }
32 #endif
33
test_basics(void)34 static void test_basics(void)
35 {
36 zassert_equal(NET_TIMEOUT_MAX_VALUE, INT32_MAX,
37 "Max value not as expected");
38 zassert_equal(((uint32_t)(INT32_MAX / MSEC_PER_SEC)),
39 HALFMAX_S,
40 "Half-max constant is wrong");
41 zassert_equal((UINT32_MAX / MSEC_PER_SEC),
42 FULLMAX_S,
43 "Full-max constant is wrong");
44 }
45
test_set(void)46 static void test_set(void)
47 {
48 struct net_timeout nto;
49 uint32_t now = 4;
50 uint32_t lifetime = 0U;
51
52 /* Zero is a special case. */
53 memset(&nto, 0xa5, sizeof(nto));
54 net_timeout_set(&nto, 0, now);
55 zassert_equal(nto.timer_start, now, NULL);
56 zassert_equal(nto.wrap_counter, 0, NULL);
57 zassert_equal(nto.timer_timeout, 0, NULL);
58
59 /* Less than the max is straightforward. */
60 lifetime = NTO_MAX_S / 2;
61 ++now;
62 memset(&nto, 0xa5, sizeof(nto));
63 net_timeout_set(&nto, lifetime, now);
64 zassert_equal(nto.timer_start, now, NULL);
65 zassert_equal(nto.wrap_counter, 0, NULL);
66 zassert_equal(nto.timer_timeout, lifetime * MSEC_PER_SEC,
67 NULL);
68
69 /* Max must not incur wrap, so fraction is not zero. */
70 lifetime = NTO_MAX_S;
71 ++now;
72 memset(&nto, 0xa5, sizeof(nto));
73 net_timeout_set(&nto, lifetime, now);
74 zassert_equal(nto.timer_start, now, NULL);
75 zassert_equal(nto.wrap_counter, 0, NULL);
76 zassert_equal(nto.timer_timeout, lifetime * MSEC_PER_SEC,
77 NULL);
78
79 /* Next after max does wrap. */
80 lifetime += 1U;
81 ++now;
82 memset(&nto, 0xa5, sizeof(nto));
83 net_timeout_set(&nto, lifetime, now);
84 zassert_equal(nto.timer_start, now, NULL);
85 zassert_equal(nto.wrap_counter, 1U, NULL);
86 zassert_equal(nto.timer_timeout,
87 (lifetime * MSEC_PER_SEC) % NET_TIMEOUT_MAX_VALUE,
88 NULL);
89
90 /* Fullmax should be one plus partial */
91 lifetime = FULLMAX_S;
92 ++now;
93 memset(&nto, 0xa5, sizeof(nto));
94 net_timeout_set(&nto, lifetime, now);
95 zassert_equal(nto.timer_start, now, NULL);
96 zassert_equal(nto.wrap_counter, 1U, NULL);
97 zassert_equal(nto.timer_timeout,
98 (lifetime * (uint64_t)MSEC_PER_SEC) % NET_TIMEOUT_MAX_VALUE,
99 NULL);
100
101 /* Multiples of max must also not have a zero fraction. */
102 lifetime = NET_TIMEOUT_MAX_VALUE;
103 ++now;
104 memset(&nto, 0xa5, sizeof(nto));
105 net_timeout_set(&nto, lifetime, now);
106 zassert_equal(nto.timer_start, now, NULL);
107 zassert_equal(nto.wrap_counter, MSEC_PER_SEC - 1, NULL);
108 zassert_equal(nto.timer_timeout, NET_TIMEOUT_MAX_VALUE, NULL);
109 }
110
test_deadline(void)111 static void test_deadline(void)
112 {
113 struct net_timeout nto;
114 uint64_t now = 1234;
115 uint64_t rollover31 = BIT64(31);
116 uint64_t rollover32 = BIT64(32);
117 uint32_t lifetime = 562;
118
119 net_timeout_set(&nto, lifetime, now);
120 uint64_t expected = now + lifetime * MSEC_PER_SEC;
121 zassert_equal(net_timeout_deadline(&nto, now),
122 expected,
123 NULL);
124
125 /* Advancing now has no effect until it wraps. */
126 zassert_equal(net_timeout_deadline(&nto, now + 23U),
127 expected,
128 NULL);
129
130 /* Advancing by 2^31 is not a wrap. */
131 now += rollover31;
132 zassert_equal(net_timeout_deadline(&nto, now),
133 expected,
134 NULL);
135
136 /* Advancing by 2^32 is a wrap, and should be reflected in the
137 * returned deadline
138 */
139 now += rollover31;
140 expected += rollover32;
141 zassert_equal(net_timeout_deadline(&nto, now),
142 expected,
143 NULL);
144
145 zassert_equal(net_timeout_deadline(&nto, now + 52),
146 expected,
147 NULL);
148 }
149
test_remaining(void)150 static void test_remaining(void)
151 {
152 struct net_timeout nto;
153 uint32_t now = 4;
154 uint32_t lifetime = 0U;
155
156 /* Zero is a special case. */
157 memset(&nto, 0xa5, sizeof(nto));
158 net_timeout_set(&nto, 0, now);
159 zassert_equal(net_timeout_remaining(&nto, now), 0U,
160 NULL);
161
162 /* Without wrap is straightforward. */
163 lifetime = NTO_MAX_S / 2;
164 memset(&nto, 0xa5, sizeof(nto));
165 net_timeout_set(&nto, lifetime, now);
166 zassert_equal(nto.wrap_counter, 0, NULL);
167 zassert_equal(net_timeout_remaining(&nto, now), lifetime,
168 NULL);
169
170 /* Estimate rounds down (legacy behavior). */
171 zassert_equal(net_timeout_remaining(&nto, now + 1U),
172 lifetime - 1U,
173 NULL);
174 zassert_equal(net_timeout_remaining(&nto, now + MSEC_PER_SEC - 1U),
175 lifetime - 1U,
176 NULL);
177 zassert_equal(net_timeout_remaining(&nto, now + MSEC_PER_SEC),
178 lifetime - 1U,
179 NULL);
180 zassert_equal(net_timeout_remaining(&nto, now + MSEC_PER_SEC + 1U),
181 lifetime - 2U,
182 NULL);
183
184 /* Works when wrap is involved */
185 lifetime = 4 * FULLMAX_S;
186 net_timeout_set(&nto, lifetime, now);
187 zassert_equal(nto.wrap_counter, 7, NULL);
188 zassert_equal(net_timeout_remaining(&nto, now), lifetime,
189 NULL);
190 }
191
test_evaluate_basic(void)192 static void test_evaluate_basic(void)
193 {
194 struct net_timeout nto;
195 uint64_t now = 0;
196 uint32_t half_max = NET_TIMEOUT_MAX_VALUE / 2U;
197 uint32_t lifetime = FULLMAX_S + HALFMAX_S;
198 uint32_t remainder;
199 uint32_t delay;
200 uint64_t deadline;
201
202 net_timeout_set(&nto, lifetime, now);
203 zassert_equal(nto.timer_start, now,
204 NULL);
205 zassert_equal(nto.wrap_counter, 2,
206 NULL);
207 remainder = 2147482706;
208 zassert_equal(nto.timer_timeout, remainder,
209 NULL);
210 deadline = net_timeout_deadline(&nto, now);
211
212 /* Evaluation with wrap and no advance returns max value
213 * without changing anything. */
214 delay = net_timeout_evaluate(&nto, now);
215 zassert_equal(delay, NET_TIMEOUT_MAX_VALUE,
216 NULL);
217 zassert_equal(nto.timer_start, now,
218 NULL);
219 zassert_equal(nto.wrap_counter, 2,
220 NULL);
221 zassert_equal(nto.timer_timeout, remainder,
222 NULL);
223 zassert_equal(net_timeout_deadline(&nto, now), deadline,
224 NULL);
225
226 /* Advance now by half the delay should return the remainder,
227 * again without advancing anything.
228 */
229 delay = net_timeout_evaluate(&nto, now + half_max);
230 zassert_equal(delay, NET_TIMEOUT_MAX_VALUE - half_max,
231 NULL);
232 zassert_equal(nto.timer_start, now,
233 NULL);
234 zassert_equal(nto.wrap_counter, 2,
235 NULL);
236 zassert_equal(nto.timer_timeout, remainder,
237 NULL);
238 zassert_equal(net_timeout_deadline(&nto, now), deadline,
239 NULL);
240
241 /* Advance now by just below delay still doesn't change
242 * anything.
243 */
244 delay = net_timeout_evaluate(&nto, now + NET_TIMEOUT_MAX_VALUE - 1U);
245 zassert_equal(delay, 1U,
246 NULL);
247 zassert_equal(nto.timer_start, now,
248 NULL);
249 zassert_equal(nto.wrap_counter, 2,
250 NULL);
251 zassert_equal(nto.timer_timeout, remainder,
252 NULL);
253 zassert_equal(net_timeout_deadline(&nto, now), deadline,
254 NULL);
255
256 /* Advancing by the delay consumes the value of the delay. The
257 * deadline is unchanged.
258 */
259 now += NET_TIMEOUT_MAX_VALUE;
260 delay = net_timeout_evaluate(&nto, now);
261 zassert_equal(delay, NET_TIMEOUT_MAX_VALUE,
262 NULL);
263 zassert_equal(nto.timer_start, now,
264 NULL);
265 zassert_equal(nto.wrap_counter, 1,
266 NULL);
267 zassert_equal(nto.timer_timeout, remainder,
268 "remainder %u", nto.timer_timeout);
269 zassert_equal(net_timeout_deadline(&nto, now), deadline,
270 NULL);
271
272 /* Advancing by more than the delay consumes the value of the delay,
273 * with the excess reducing the remainder. The deadline is
274 * unchanged.
275 */
276 now += NET_TIMEOUT_MAX_VALUE + 1234;
277 remainder -= 1234;
278 delay = net_timeout_evaluate(&nto, now);
279 zassert_equal(delay, remainder,
280 NULL);
281 zassert_equal(nto.timer_start, (uint32_t)now,
282 NULL);
283 zassert_equal(nto.wrap_counter, 0,
284 NULL);
285 zassert_equal(nto.timer_timeout, remainder,
286 NULL);
287 zassert_equal(net_timeout_deadline(&nto, now), deadline,
288 NULL);
289
290 /* Final advance completes the timeout precisely */
291 now += delay;
292 delay = net_timeout_evaluate(&nto, now);
293 zassert_equal(delay, 0,
294 NULL);
295 zassert_equal(net_timeout_deadline(&nto, now), deadline,
296 NULL);
297 }
298
test_evaluate_whitebox(void)299 static void test_evaluate_whitebox(void)
300 {
301 /* This explicitly tests the path where subtracting the excess elapsed
302 * from the fractional timeout requires reducing the wrap count a
303 * second time.
304 */
305 struct net_timeout nto;
306 uint64_t now = 0;
307 uint32_t lifetime = 3 * HALFMAX_S + 2;
308
309 net_timeout_set(&nto, lifetime, now);
310 zassert_equal(nto.timer_start, now, NULL);
311 zassert_equal(nto.wrap_counter, 3, NULL);
312 zassert_equal(nto.timer_timeout, 59, NULL);
313
314 /* Preserve the deadline for validation */
315 uint64_t deadline = net_timeout_deadline(&nto, now);
316
317 uint32_t delay = net_timeout_evaluate(&nto, now);
318 zassert_equal(delay, NET_TIMEOUT_MAX_VALUE, NULL);
319 zassert_equal(net_timeout_deadline(&nto, now),
320 deadline, NULL);
321
322 /* Simulate a late evaluation, far enough late that the counter gets
323 * wiped out.
324 */
325 now += delay + 100U;
326 delay = net_timeout_evaluate(&nto, now);
327 zassert_equal(nto.timer_start, now, NULL);
328 zassert_equal(nto.wrap_counter, 1, NULL);
329 zassert_equal(nto.timer_timeout, 2147483606, NULL);
330 zassert_equal(net_timeout_deadline(&nto, now),
331 deadline, NULL);
332 zassert_equal(delay, NET_TIMEOUT_MAX_VALUE, NULL);
333
334 /* Another late evaluation finishes the wrap leaving some extra.
335 */
336 now += delay + 123U;
337 delay = net_timeout_evaluate(&nto, now);
338 zassert_equal(nto.timer_start, (uint32_t)now, NULL);
339 zassert_equal(nto.wrap_counter, 0, NULL);
340 zassert_equal(nto.timer_timeout, 2147483483, NULL);
341 zassert_equal(net_timeout_deadline(&nto, now),
342 deadline, NULL);
343 zassert_equal(delay, nto.timer_timeout, NULL);
344
345 /* Complete the timeout. This does *not* adjust the internal
346 * state.
347 */
348 now += delay + 234U;
349 delay = net_timeout_evaluate(&nto, now);
350 zassert_equal(delay, 0, NULL);
351 zassert_equal(net_timeout_deadline(&nto, now),
352 deadline, NULL);
353 }
354
test_nop(void)355 static void test_nop(void)
356 {
357 }
358
359 /*test case main entry*/
test_main(void)360 void test_main(void)
361 {
362
363 ztest_test_suite(test_prf,
364 ztest_unit_test(test_basics),
365 ztest_unit_test(test_set),
366 ztest_unit_test(test_deadline),
367 ztest_unit_test(test_remaining),
368 ztest_unit_test(test_evaluate_basic),
369 ztest_unit_test(test_evaluate_whitebox),
370 ztest_unit_test(test_nop)
371 );
372 ztest_run_test_suite(test_prf);
373 }
374