From 0c94db3df4e00b25b62ef760c1a6993df06e4e15 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 16 Oct 2023 11:08:09 +0200 Subject: [PATCH] tests: add test for ztimer_mbox_get_timeout() --- tests/sys/ztimer_mbox_get_timeout/Makefile | 7 + tests/sys/ztimer_mbox_get_timeout/Makefile.ci | 4 + tests/sys/ztimer_mbox_get_timeout/main.c | 162 ++++++++++++++++++ .../ztimer_mbox_get_timeout/tests/01-run.py | 21 +++ 4 files changed, 194 insertions(+) create mode 100644 tests/sys/ztimer_mbox_get_timeout/Makefile create mode 100644 tests/sys/ztimer_mbox_get_timeout/Makefile.ci create mode 100644 tests/sys/ztimer_mbox_get_timeout/main.c create mode 100755 tests/sys/ztimer_mbox_get_timeout/tests/01-run.py diff --git a/tests/sys/ztimer_mbox_get_timeout/Makefile b/tests/sys/ztimer_mbox_get_timeout/Makefile new file mode 100644 index 000000000000..1e51f832b346 --- /dev/null +++ b/tests/sys/ztimer_mbox_get_timeout/Makefile @@ -0,0 +1,7 @@ +include ../Makefile.sys_common + +USEMODULE += ztimer_usec +USEMODULE += core_thread_flags +USEMODULE += core_mbox + +include $(RIOTBASE)/Makefile.include diff --git a/tests/sys/ztimer_mbox_get_timeout/Makefile.ci b/tests/sys/ztimer_mbox_get_timeout/Makefile.ci new file mode 100644 index 000000000000..363d4f3a79ad --- /dev/null +++ b/tests/sys/ztimer_mbox_get_timeout/Makefile.ci @@ -0,0 +1,4 @@ +BOARD_INSUFFICIENT_MEMORY := \ + atmega8 \ + nucleo-l011k4 \ + # diff --git a/tests/sys/ztimer_mbox_get_timeout/main.c b/tests/sys/ztimer_mbox_get_timeout/main.c new file mode 100644 index 000000000000..03e39cb00f2b --- /dev/null +++ b/tests/sys/ztimer_mbox_get_timeout/main.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2023 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief testing ztimer_mbox_get_timeout function + * + * + * @author Marian Buschsieweke + * + */ + +#include +#include + +#include "mbox.h" +#include "test_utils/expect.h" +#include "time_units.h" +#include "ztimer.h" + +#define MSG_TYPE 42 +#define MSG_VAL 1337 + +static msg_t queue[4]; +static mbox_t mbox = MBOX_INIT(queue, ARRAY_SIZE(queue)); + +static void cb_mbox_put(void *arg) +{ + mbox_try_put(&mbox, arg); +} + +static void test_mbox_already_full(void) +{ + printf("testing mbox already full prior call: "); + msg_t msg = { .type = MSG_TYPE, .content.value = MSG_VAL }; + mbox_put(&mbox, &msg); + uint32_t start = ztimer_now(ZTIMER_USEC); + expect(ztimer_mbox_get_timeout(ZTIMER_USEC, &mbox, &msg, US_PER_SEC) == 0); + uint32_t stop = ztimer_now(ZTIMER_USEC); + /* Returning immediately should with nothing else running in this test app + * should not take more than a millisecond */ + expect(stop - start < US_PER_MS); + printf("OK\n"); +} + +static void test_timeout_reached(void) +{ + const uint32_t timeout_us = 10 * US_PER_MS; + printf("testing timeout is reached: "); + msg_t msg = { .type = MSG_TYPE - 1, .content.value = MSG_VAL - 1 }; + uint32_t start = ztimer_now(ZTIMER_USEC); + expect(ztimer_mbox_get_timeout(ZTIMER_USEC, &mbox, &msg, timeout_us) == -ETIMEDOUT); + uint32_t stop = ztimer_now(ZTIMER_USEC); + /* This may take longer than the timeout due to overhead, background tasks, + * etc. But it MUST NOT return early. */ + expect(stop - start >= timeout_us); + /* But it should also not take way too long */ + expect(stop - start < 2 * timeout_us); + /* msg must not be changed */ + expect((msg.type == MSG_TYPE - 1) && (msg.content.value == MSG_VAL - 1)); + printf("OK\n"); +} + +static void test_msg_prior_timeout(void) +{ + const uint32_t msg_timeout_us = 1 * US_PER_MS; +#if defined(BOARD_NATIVE64) || defined(BOARD_NATIVE) + /* relax timing on native, as background load can mess with timing */ + const uint32_t wait_timeout_us = 100 * US_PER_MS; +#else + const uint32_t wait_timeout_us = 2 * US_PER_MS; +#endif + + msg_t msg = { .type = MSG_TYPE, .content.value = MSG_VAL }; + msg_t got = { .type = 0 }; + ztimer_t t = { + .callback = cb_mbox_put, + .arg = &msg, + }; + + ztimer_set(ZTIMER_USEC, &t, msg_timeout_us); + uint32_t start = ztimer_now(ZTIMER_USEC); + expect(ztimer_mbox_get_timeout(ZTIMER_USEC, &mbox, &got, wait_timeout_us) == 0); + uint32_t stop = ztimer_now(ZTIMER_USEC); + + /* the function should return AFTER the message was send, but BEFORE the + * timeout was triggered */ + expect(stop - start >= msg_timeout_us); + expect(stop - start < wait_timeout_us); + + /* we should have gotten the correct message */ + expect((got.type == msg.type) && (got.content.value == msg.content.value)); +} + +static WORD_ALIGNED char stack_high_prio_thread[THREAD_STACKSIZE_TINY]; +static void * high_prio_thread(void *arg) +{ + uint32_t timeout = (uintptr_t)arg; + + { + msg_t msg = { .type = MSG_TYPE, .content.value = MSG_VAL }; + mbox_put(&mbox, &msg); + } + + ztimer_spin(ZTIMER_USEC, timeout); + return NULL; +} + +static void test_msg_race(void) +{ + printf("testing timeout is reached despite message received (race): "); + const uint32_t wait_timeout_us = 2 * US_PER_MS; + const uintptr_t spin_timeout_us = 2 * wait_timeout_us; + + msg_t msg = { .type = MSG_TYPE, .content.value = MSG_VAL }; + msg_t got = { .type = 0 }; + + thread_create(stack_high_prio_thread, sizeof(stack_high_prio_thread), + THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_WOUT_YIELD, + high_prio_thread, (void *)spin_timeout_us, "high_prio"); + uint32_t start = ztimer_now(ZTIMER_USEC); + expect(ztimer_mbox_get_timeout(ZTIMER_USEC, &mbox, &got, wait_timeout_us) == 0); + uint32_t stop = ztimer_now(ZTIMER_USEC); + + /* the high prio thread should prevent us from running while it spins. It + * will even prevent us from cancelling the timout. We should still receive + * the message (which was received prior to the timeout), even though + * we get CPU time only way after the timeout. */ + expect(stop - start > wait_timeout_us); + + /* we should have gotten the correct message */ + expect((got.type == msg.type) && (got.content.value == msg.content.value)); + printf("OK\n"); +} + +int main(void) +{ + const unsigned repetitions = 1000; + printf("Testing ztimer_mbox_get_timeout()\n" + "=================================\n"); + test_mbox_already_full(); + test_timeout_reached(); + test_msg_race(); + + printf("Running test for reception prior timeout %u times: ", repetitions); + for (unsigned i = 0; i < repetitions; i++) { + test_msg_prior_timeout(); + } + + printf("OK\n"); + + printf("\nALL TESTS SUCCEEDED\n"); + return 0; +} diff --git a/tests/sys/ztimer_mbox_get_timeout/tests/01-run.py b/tests/sys/ztimer_mbox_get_timeout/tests/01-run.py new file mode 100755 index 000000000000..80df7e90d2e0 --- /dev/null +++ b/tests/sys/ztimer_mbox_get_timeout/tests/01-run.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2023 Otto-von-Guericke-Universität Magdeburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +# @author Marian Buschsieweke + +import sys +from testrunner import run + + +def testfunc(child): + child.expect("Testing ztimer_mbox_get_timeout()") + child.expect("ALL TESTS SUCCEEDED") + + +if __name__ == "__main__": + sys.exit(run(testfunc))