360 lines
9.9 KiB
C
360 lines
9.9 KiB
C
/*
|
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/ztest.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <zephyr/net/net_timeout.h>
|
|
|
|
#include "../../../subsys/net/ip/net_timeout.c"
|
|
|
|
#define HALFMAX_S (23U + 60U * (31U + 60U * (20U + 24U * 24U)))
|
|
#define NTO_MAX_S HALFMAX_S
|
|
#define FULLMAX_S (47U + 60U * (2U + 60U * (17U + 24U * 49U)))
|
|
|
|
#if 0
|
|
static void dump_nto(const struct net_timeout *nto)
|
|
{
|
|
uint64_t remaining = nto->timer_timeout;
|
|
uint64_t deadline = nto->timer_start;
|
|
|
|
remaining += NET_TIMEOUT_MAX_VALUE * nto->wrap_counter;
|
|
deadline += remaining;
|
|
|
|
printk("start %u, rem %u * %u + %u = %" PRIu64 ", ends %" PRIu64 "\n",
|
|
nto->timer_start, nto->wrap_counter,
|
|
NET_TIMEOUT_MAX_VALUE, nto->timer_timeout,
|
|
remaining, deadline);
|
|
}
|
|
#endif
|
|
|
|
ZTEST(net_timeout, test_basics)
|
|
{
|
|
zassert_equal(NET_TIMEOUT_MAX_VALUE, INT32_MAX,
|
|
"Max value not as expected");
|
|
zassert_equal(((uint32_t)(INT32_MAX / MSEC_PER_SEC)),
|
|
HALFMAX_S,
|
|
"Half-max constant is wrong");
|
|
zassert_equal((UINT32_MAX / MSEC_PER_SEC),
|
|
FULLMAX_S,
|
|
"Full-max constant is wrong");
|
|
}
|
|
|
|
ZTEST(net_timeout, test_set)
|
|
{
|
|
struct net_timeout nto;
|
|
uint32_t now = 4;
|
|
uint32_t lifetime = 0U;
|
|
|
|
/* Zero is a special case. */
|
|
memset(&nto, 0xa5, sizeof(nto));
|
|
net_timeout_set(&nto, 0, now);
|
|
zassert_equal(nto.timer_start, now);
|
|
zassert_equal(nto.wrap_counter, 0);
|
|
zassert_equal(nto.timer_timeout, 0);
|
|
|
|
/* Less than the max is straightforward. */
|
|
lifetime = NTO_MAX_S / 2;
|
|
++now;
|
|
memset(&nto, 0xa5, sizeof(nto));
|
|
net_timeout_set(&nto, lifetime, now);
|
|
zassert_equal(nto.timer_start, now);
|
|
zassert_equal(nto.wrap_counter, 0);
|
|
zassert_equal(nto.timer_timeout, lifetime * MSEC_PER_SEC,
|
|
NULL);
|
|
|
|
/* Max must not incur wrap, so fraction is not zero. */
|
|
lifetime = NTO_MAX_S;
|
|
++now;
|
|
memset(&nto, 0xa5, sizeof(nto));
|
|
net_timeout_set(&nto, lifetime, now);
|
|
zassert_equal(nto.timer_start, now);
|
|
zassert_equal(nto.wrap_counter, 0);
|
|
zassert_equal(nto.timer_timeout, lifetime * MSEC_PER_SEC,
|
|
NULL);
|
|
|
|
/* Next after max does wrap. */
|
|
lifetime += 1U;
|
|
++now;
|
|
memset(&nto, 0xa5, sizeof(nto));
|
|
net_timeout_set(&nto, lifetime, now);
|
|
zassert_equal(nto.timer_start, now);
|
|
zassert_equal(nto.wrap_counter, 1U);
|
|
zassert_equal(nto.timer_timeout,
|
|
(lifetime * MSEC_PER_SEC) % NET_TIMEOUT_MAX_VALUE,
|
|
NULL);
|
|
|
|
/* Fullmax should be one plus partial */
|
|
lifetime = FULLMAX_S;
|
|
++now;
|
|
memset(&nto, 0xa5, sizeof(nto));
|
|
net_timeout_set(&nto, lifetime, now);
|
|
zassert_equal(nto.timer_start, now);
|
|
zassert_equal(nto.wrap_counter, 1U);
|
|
zassert_equal(nto.timer_timeout,
|
|
(lifetime * (uint64_t)MSEC_PER_SEC) % NET_TIMEOUT_MAX_VALUE,
|
|
NULL);
|
|
|
|
/* Multiples of max must also not have a zero fraction. */
|
|
lifetime = NET_TIMEOUT_MAX_VALUE;
|
|
++now;
|
|
memset(&nto, 0xa5, sizeof(nto));
|
|
net_timeout_set(&nto, lifetime, now);
|
|
zassert_equal(nto.timer_start, now);
|
|
zassert_equal(nto.wrap_counter, MSEC_PER_SEC - 1);
|
|
zassert_equal(nto.timer_timeout, NET_TIMEOUT_MAX_VALUE);
|
|
}
|
|
|
|
ZTEST(net_timeout, test_deadline)
|
|
{
|
|
struct net_timeout nto;
|
|
uint64_t now = 1234;
|
|
uint64_t rollover31 = BIT64(31);
|
|
uint64_t rollover32 = BIT64(32);
|
|
uint32_t lifetime = 562;
|
|
|
|
net_timeout_set(&nto, lifetime, now);
|
|
uint64_t expected = now + lifetime * MSEC_PER_SEC;
|
|
zassert_equal(net_timeout_deadline(&nto, now),
|
|
expected,
|
|
NULL);
|
|
|
|
/* Advancing now has no effect until it wraps. */
|
|
zassert_equal(net_timeout_deadline(&nto, now + 23U),
|
|
expected,
|
|
NULL);
|
|
|
|
/* Advancing by 2^31 is not a wrap. */
|
|
now += rollover31;
|
|
zassert_equal(net_timeout_deadline(&nto, now),
|
|
expected,
|
|
NULL);
|
|
|
|
/* Advancing by 2^32 is a wrap, and should be reflected in the
|
|
* returned deadline
|
|
*/
|
|
now += rollover31;
|
|
expected += rollover32;
|
|
zassert_equal(net_timeout_deadline(&nto, now),
|
|
expected,
|
|
NULL);
|
|
|
|
zassert_equal(net_timeout_deadline(&nto, now + 52),
|
|
expected,
|
|
NULL);
|
|
}
|
|
|
|
ZTEST(net_timeout, test_remaining)
|
|
{
|
|
struct net_timeout nto;
|
|
uint32_t now = 4;
|
|
uint32_t lifetime = 0U;
|
|
|
|
/* Zero is a special case. */
|
|
memset(&nto, 0xa5, sizeof(nto));
|
|
net_timeout_set(&nto, 0, now);
|
|
zassert_equal(net_timeout_remaining(&nto, now), 0U,
|
|
NULL);
|
|
|
|
/* Without wrap is straightforward. */
|
|
lifetime = NTO_MAX_S / 2;
|
|
memset(&nto, 0xa5, sizeof(nto));
|
|
net_timeout_set(&nto, lifetime, now);
|
|
zassert_equal(nto.wrap_counter, 0);
|
|
zassert_equal(net_timeout_remaining(&nto, now), lifetime,
|
|
NULL);
|
|
|
|
/* Estimate rounds down (legacy behavior). */
|
|
zassert_equal(net_timeout_remaining(&nto, now + 1U),
|
|
lifetime - 1U,
|
|
NULL);
|
|
zassert_equal(net_timeout_remaining(&nto, now + MSEC_PER_SEC - 1U),
|
|
lifetime - 1U,
|
|
NULL);
|
|
zassert_equal(net_timeout_remaining(&nto, now + MSEC_PER_SEC),
|
|
lifetime - 1U,
|
|
NULL);
|
|
zassert_equal(net_timeout_remaining(&nto, now + MSEC_PER_SEC + 1U),
|
|
lifetime - 2U,
|
|
NULL);
|
|
|
|
/* Works when wrap is involved */
|
|
lifetime = 4 * FULLMAX_S;
|
|
net_timeout_set(&nto, lifetime, now);
|
|
zassert_equal(nto.wrap_counter, 7);
|
|
zassert_equal(net_timeout_remaining(&nto, now), lifetime,
|
|
NULL);
|
|
}
|
|
|
|
ZTEST(net_timeout, test_evaluate_basic)
|
|
{
|
|
struct net_timeout nto;
|
|
uint64_t now = 0;
|
|
uint32_t half_max = NET_TIMEOUT_MAX_VALUE / 2U;
|
|
uint32_t lifetime = FULLMAX_S + HALFMAX_S;
|
|
uint32_t remainder;
|
|
uint32_t delay;
|
|
uint64_t deadline;
|
|
|
|
net_timeout_set(&nto, lifetime, now);
|
|
zassert_equal(nto.timer_start, now,
|
|
NULL);
|
|
zassert_equal(nto.wrap_counter, 2,
|
|
NULL);
|
|
remainder = 2147482706;
|
|
zassert_equal(nto.timer_timeout, remainder,
|
|
NULL);
|
|
deadline = net_timeout_deadline(&nto, now);
|
|
|
|
/* Evaluation with wrap and no advance returns max value
|
|
* without changing anything. */
|
|
delay = net_timeout_evaluate(&nto, now);
|
|
zassert_equal(delay, NET_TIMEOUT_MAX_VALUE,
|
|
NULL);
|
|
zassert_equal(nto.timer_start, now,
|
|
NULL);
|
|
zassert_equal(nto.wrap_counter, 2,
|
|
NULL);
|
|
zassert_equal(nto.timer_timeout, remainder,
|
|
NULL);
|
|
zassert_equal(net_timeout_deadline(&nto, now), deadline,
|
|
NULL);
|
|
|
|
/* Advance now by half the delay should return the remainder,
|
|
* again without advancing anything.
|
|
*/
|
|
delay = net_timeout_evaluate(&nto, now + half_max);
|
|
zassert_equal(delay, NET_TIMEOUT_MAX_VALUE - half_max,
|
|
NULL);
|
|
zassert_equal(nto.timer_start, now,
|
|
NULL);
|
|
zassert_equal(nto.wrap_counter, 2,
|
|
NULL);
|
|
zassert_equal(nto.timer_timeout, remainder,
|
|
NULL);
|
|
zassert_equal(net_timeout_deadline(&nto, now), deadline,
|
|
NULL);
|
|
|
|
/* Advance now by just below delay still doesn't change
|
|
* anything.
|
|
*/
|
|
delay = net_timeout_evaluate(&nto, now + NET_TIMEOUT_MAX_VALUE - 1U);
|
|
zassert_equal(delay, 1U,
|
|
NULL);
|
|
zassert_equal(nto.timer_start, now,
|
|
NULL);
|
|
zassert_equal(nto.wrap_counter, 2,
|
|
NULL);
|
|
zassert_equal(nto.timer_timeout, remainder,
|
|
NULL);
|
|
zassert_equal(net_timeout_deadline(&nto, now), deadline,
|
|
NULL);
|
|
|
|
/* Advancing by the delay consumes the value of the delay. The
|
|
* deadline is unchanged.
|
|
*/
|
|
now += NET_TIMEOUT_MAX_VALUE;
|
|
delay = net_timeout_evaluate(&nto, now);
|
|
zassert_equal(delay, NET_TIMEOUT_MAX_VALUE,
|
|
NULL);
|
|
zassert_equal(nto.timer_start, now,
|
|
NULL);
|
|
zassert_equal(nto.wrap_counter, 1,
|
|
NULL);
|
|
zassert_equal(nto.timer_timeout, remainder,
|
|
"remainder %u", nto.timer_timeout);
|
|
zassert_equal(net_timeout_deadline(&nto, now), deadline,
|
|
NULL);
|
|
|
|
/* Advancing by more than the delay consumes the value of the delay,
|
|
* with the excess reducing the remainder. The deadline is
|
|
* unchanged.
|
|
*/
|
|
now += NET_TIMEOUT_MAX_VALUE + 1234;
|
|
remainder -= 1234;
|
|
delay = net_timeout_evaluate(&nto, now);
|
|
zassert_equal(delay, remainder,
|
|
NULL);
|
|
zassert_equal(nto.timer_start, (uint32_t)now,
|
|
NULL);
|
|
zassert_equal(nto.wrap_counter, 0,
|
|
NULL);
|
|
zassert_equal(nto.timer_timeout, remainder,
|
|
NULL);
|
|
zassert_equal(net_timeout_deadline(&nto, now), deadline,
|
|
NULL);
|
|
|
|
/* Final advance completes the timeout precisely */
|
|
now += delay;
|
|
delay = net_timeout_evaluate(&nto, now);
|
|
zassert_equal(delay, 0,
|
|
NULL);
|
|
zassert_equal(net_timeout_deadline(&nto, now), deadline,
|
|
NULL);
|
|
}
|
|
|
|
ZTEST(net_timeout, test_evaluate_whitebox)
|
|
{
|
|
/* This explicitly tests the path where subtracting the excess elapsed
|
|
* from the fractional timeout requires reducing the wrap count a
|
|
* second time.
|
|
*/
|
|
struct net_timeout nto;
|
|
uint64_t now = 0;
|
|
uint32_t lifetime = 3 * HALFMAX_S + 2;
|
|
|
|
net_timeout_set(&nto, lifetime, now);
|
|
zassert_equal(nto.timer_start, now);
|
|
zassert_equal(nto.wrap_counter, 3);
|
|
zassert_equal(nto.timer_timeout, 59);
|
|
|
|
/* Preserve the deadline for validation */
|
|
uint64_t deadline = net_timeout_deadline(&nto, now);
|
|
|
|
uint32_t delay = net_timeout_evaluate(&nto, now);
|
|
zassert_equal(delay, NET_TIMEOUT_MAX_VALUE);
|
|
zassert_equal(net_timeout_deadline(&nto, now),
|
|
deadline, NULL);
|
|
|
|
/* Simulate a late evaluation, far enough late that the counter gets
|
|
* wiped out.
|
|
*/
|
|
now += delay + 100U;
|
|
delay = net_timeout_evaluate(&nto, now);
|
|
zassert_equal(nto.timer_start, now);
|
|
zassert_equal(nto.wrap_counter, 1);
|
|
zassert_equal(nto.timer_timeout, 2147483606);
|
|
zassert_equal(net_timeout_deadline(&nto, now),
|
|
deadline, NULL);
|
|
zassert_equal(delay, NET_TIMEOUT_MAX_VALUE);
|
|
|
|
/* Another late evaluation finishes the wrap leaving some extra.
|
|
*/
|
|
now += delay + 123U;
|
|
delay = net_timeout_evaluate(&nto, now);
|
|
zassert_equal(nto.timer_start, (uint32_t)now);
|
|
zassert_equal(nto.wrap_counter, 0);
|
|
zassert_equal(nto.timer_timeout, 2147483483);
|
|
zassert_equal(net_timeout_deadline(&nto, now),
|
|
deadline, NULL);
|
|
zassert_equal(delay, nto.timer_timeout);
|
|
|
|
/* Complete the timeout. This does *not* adjust the internal
|
|
* state.
|
|
*/
|
|
now += delay + 234U;
|
|
delay = net_timeout_evaluate(&nto, now);
|
|
zassert_equal(delay, 0);
|
|
zassert_equal(net_timeout_deadline(&nto, now),
|
|
deadline, NULL);
|
|
}
|
|
|
|
ZTEST(net_timeout, test_nop)
|
|
{
|
|
}
|
|
|
|
ZTEST_SUITE(net_timeout, NULL, NULL, NULL, NULL, NULL);
|