From 083c5f7f6249ab75c81857306a71a2ca29c04fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Harter?= Date: Wed, 31 Oct 2018 15:21:06 +0100 Subject: [PATCH 1/3] WIP/POC: start a test where xtimer uses a fake 'periph_timer' mock implementation --- tests/xtimer_mocked_periph_timer/Makefile | 19 ++ tests/xtimer_mocked_periph_timer/main.c | 191 ++++++++++++++++++ .../periph_timer_mock.c | 107 ++++++++++ .../periph_timer_mock.h | 25 +++ 4 files changed, 342 insertions(+) create mode 100644 tests/xtimer_mocked_periph_timer/Makefile create mode 100644 tests/xtimer_mocked_periph_timer/main.c create mode 100644 tests/xtimer_mocked_periph_timer/periph_timer_mock.c create mode 100644 tests/xtimer_mocked_periph_timer/periph_timer_mock.h diff --git a/tests/xtimer_mocked_periph_timer/Makefile b/tests/xtimer_mocked_periph_timer/Makefile new file mode 100644 index 000000000000..13eb2b5952f1 --- /dev/null +++ b/tests/xtimer_mocked_periph_timer/Makefile @@ -0,0 +1,19 @@ +include ../Makefile.tests_common + +TEST_ON_CI_WHITELIST += all + +USEMODULE += embunit + +# Replace enabling xtimer through the build system to manually enabling it +# It allows replacing periph_timer implementation for xtimer by including c +# files +## USEMODULE += xtimer + +# Dependencies +INCLUDES += -I$(RIOTBASE)/sys/xtimer +USEMODULE += div +# Ignore periph_timer dependency it will be mocked + +DISABLE_MODULE += auto_init + +include $(RIOTBASE)/Makefile.include diff --git a/tests/xtimer_mocked_periph_timer/main.c b/tests/xtimer_mocked_periph_timer/main.c new file mode 100644 index 000000000000..36933a286ccf --- /dev/null +++ b/tests/xtimer_mocked_periph_timer/main.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2018 Freie Universität Berlin + * + * 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 xtimer mocked periph_timer test application + * + * Test xtimer using a mocked implementation of periph_timer + * + * @author Gaëtan Harter + * @} + */ +#include + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#include "embUnit.h" + +/* + * Mock 'xtimer' to use our periph_timer mock implementation + * The '.c' files must be included to have access to private functions and + * mock with the macros + */ +#define timer_init timer_init_mock +#define timer_read timer_read_mock +#define timer_set_absolute timer_set_absolute_mock +#include "xtimer.h" +#include "xtimer.c" +#include "xtimer_core.c" + +/* Allow configuring the mock through here */ +#include "periph_timer_mock.h" + + +static void test_timer_cb(void *arg) +{ + printf("test_timer_cb(%p)\n", arg); +} + + + +/* This is a magic that should tell that it is the first call in + * `_xtimer_set` + */ +#define TIMER_SET_TICK_COUNT (1) + +/* + * A magic value that is meant to make it work with the current + * implementation + */ +#define TIMER_SET_XTIMER_SET_OFFSET (XTIMER_BACKOFF) +#define TIMER_SET_DESCHEDULE_TIME_TICK (TIMER_SET_XTIMER_SET_OFFSET +1) + +/* Simulate descheduling the task for XTIMER_BACKOFF after `_xtimer_now` in + * `_xtimer_set_absolute` + * + * Simulating the de-scheduling with this method assumes we are not in masked + * interrupts, which we currently are. + * A proper test should just unlock a thread but this is for next step. + */ +static int timer_set_deschedule_triggered = 0; +static void _simulate_deschedule_in_timer_set(uint32_t now) +{ + if (now == TIMER_SET_TICK_COUNT && !timer_set_deschedule_triggered) { + /* This is countered by being in an interrupt context */ + DEBUG("De-schedule the thread at %" PRIu32 " for %u\n", + now, TIMER_SET_DESCHEDULE_TIME_TICK); + timer_set_deschedule_triggered = 1; + timer_mock_set(now + TIMER_SET_DESCHEDULE_TIME_TICK); + } + +} +static void test_show_xtimer_set_call_target_in_the_past(void) +{ + xtimer_init(); + timer_mock_set_post_step_cb(_simulate_deschedule_in_timer_set); + + xtimer_t timer_low_offset = {.callback = test_timer_cb}; + + /* TODO I only handle Freq 1MHz for now in this test */ + TEST_ASSERT_MESSAGE(XTIMER_HZ == 1000000, + "Only 1Mhz handled here for this first test"); + + + xtimer_set(&timer_low_offset, TIMER_SET_XTIMER_SET_OFFSET); + + /* The test should not be in the `long_list_head` as it is close by */ + int was_in_list = _remove_timer_from_list(&long_list_head, &timer_low_offset); + TEST_ASSERT_MESSAGE(was_in_list == 0, + "The timer has been placed in the long_list_head for a small sleep time"); +} + + + +/* This is a magic that should tell that it is the first call in + * `_xtimer_set_absolute` + */ +#define SET_ABSOLUTE_TICK_COUNT (2) + +/* + * A magic value that is meant to make it work with the current + * implementation + */ +#define SET_ABSOLUTE_XTIMER_SET_OFFSET (XTIMER_BACKOFF) +#define SET_ABSOLUTE_DESCHEDULE_TIME_TICK (SET_ABSOLUTE_XTIMER_SET_OFFSET) + +/* Simulate descheduling the task for XTIMER_BACKOFF after `_xtimer_now` in + * `_xtimer_set_absolute` + * + * Simulating the de-scheduling with this method assumes we are not in masked + * interrupts, which we currently are. + * A proper test should just unlock a thread but this is for next step. + */ +static int set_absolute_deschedule_triggered = 0; +static int set_absolute_full_loop = 0; +static void _simulate_deschedule_in_set_absolute(uint32_t now) +{ + if (now == SET_ABSOLUTE_TICK_COUNT && !set_absolute_deschedule_triggered) { + /* This is countered by being in an interrupt context */ + DEBUG("De-schedule the thread at %" PRIu32 " for %u\n", + now, SET_ABSOLUTE_DESCHEDULE_TIME_TICK); + set_absolute_deschedule_triggered = 1; + timer_mock_set(now + SET_ABSOLUTE_DESCHEDULE_TIME_TICK); + } + + if (now == 200) { + puts("Stuck in xtimer_spin_until for a full loop, jump out"); + set_absolute_full_loop = 1; + /* Step a lot in the future */ + timer_mock_set((((uint64_t)1) << XTIMER_WIDTH) - 2); + } +} + + + +/* This test adds delay after the _xtimer_now() in `_xtimer_set_absolute` as if + * it was de-scheduled enough time to make `target` in the past. + * + * This makes `xtimer_spin_until` be called with a target already elapsed. + */ +static void test_show_xtimer_set_does_not_handle_overhead(void) +{ + xtimer_init(); + timer_mock_set_post_step_cb(_simulate_deschedule_in_set_absolute); + + xtimer_t timer_low_offset = {.callback = test_timer_cb}; + + /* TODO I only handle Freq 1MHz for now in this test */ + TEST_ASSERT_MESSAGE(XTIMER_HZ == 1000000, + "Only 1Mhz handled here for this first test"); + + + xtimer_set(&timer_low_offset, SET_ABSOLUTE_XTIMER_SET_OFFSET); + DEBUG("Outside of xtimer_set\n"); + TEST_ASSERT(!set_absolute_full_loop); +} + + + +static TestRef tests_xtimer_low_level(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_show_xtimer_set_call_target_in_the_past), + new_TestFixture(test_show_xtimer_set_does_not_handle_overhead), + + }; + EMB_UNIT_TESTCALLER(xtimer_low_level, NULL, NULL, fixtures); + + return (Test *)&xtimer_low_level; +} + +int main(void) +{ + printf("XTIMER_WIDTH: %u\n", XTIMER_WIDTH); + printf("XTIMER_HZ: %lu\n", XTIMER_HZ); + printf("XTIMER_BACKOFF: %u\n", XTIMER_BACKOFF); + + TESTS_START(); + TESTS_RUN(tests_xtimer_low_level()); + TESTS_END(); + return 0; +} diff --git a/tests/xtimer_mocked_periph_timer/periph_timer_mock.c b/tests/xtimer_mocked_periph_timer/periph_timer_mock.c new file mode 100644 index 000000000000..477b81769230 --- /dev/null +++ b/tests/xtimer_mocked_periph_timer/periph_timer_mock.c @@ -0,0 +1,107 @@ +#include + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#include "periph/timer.h" +#include "periph_timer_mock.h" + + +static struct { + tim_t dev; + unsigned long freq; + timer_cb_t cb; + void *cb_arg; + /* Handle only one channel for now */ + int channel; + unsigned int timeout_value; + + uint32_t current; + + void (*post_step_cb)(uint32_t current); +} mock_timer; + + +void timer_mock_set_post_step_cb(void (*post_step_cb)(uint32_t current)) +{ + mock_timer.post_step_cb = post_step_cb; +} + + +static void _timer_mock_step_one(void) +{ + /* TODO handle callbacks for timer */ + mock_timer.current = (mock_timer.current == mock_timer.timeout_value ? + 0 : mock_timer.current + 1); + if (mock_timer.current == mock_timer.timeout_value) { + printf("periph_timer_mock: TODO Callback not handled\n"); + } +} + + +uint32_t timer_mock_step(uint32_t ticks) +{ + DEBUG("timer_mock_step(%" PRIu32 ") from %" PRIu32 "\n", ticks, mock_timer.current); + for (uint32_t i = 0; i < ticks; i++) { + _timer_mock_step_one(); + } + + uint32_t now = mock_timer.current; + + + if (mock_timer.post_step_cb) { + mock_timer.post_step_cb(mock_timer.current); + } + + return now; +} + +void timer_mock_set(uint32_t ticks) +{ + DEBUG("timer_mock_set(%" PRIu32 ")\n", ticks); + /* TODO This should be lower than `mock_timer.timeout_value` to be handled + * properly.*/ + mock_timer.current = ticks; +} + + +static void timer_cb_mock(void *arg, int channel) +{ + (void)arg; + (void)channel; +} + +int timer_init_mock(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) +{ + DEBUG("timer_init_mock(%u): %" PRIu32 ", %p, %p\n", dev, freq, cb, arg); + + + mock_timer.dev = dev; + mock_timer.freq = freq; + mock_timer.cb = cb; + mock_timer.cb_arg = arg; + + mock_timer.current = 0; + + mock_timer.post_step_cb = NULL; + + (void)timer_cb_mock; + + return 0; +} + + +unsigned int timer_read_mock(tim_t dev) +{ + DEBUG("timer_read_mock(%u)\n", dev); + return timer_mock_step(1); +} + +int timer_set_absolute_mock(tim_t dev, int channel, unsigned int value) +{ + assert(channel == 0); /* Not handled more than one channel for the moment */ + DEBUG("timer_set_absolute_mock(%u): channel %i, value %u\n", dev, channel, value); + mock_timer.channel = channel; + mock_timer.timeout_value = value; + return 1; +} diff --git a/tests/xtimer_mocked_periph_timer/periph_timer_mock.h b/tests/xtimer_mocked_periph_timer/periph_timer_mock.h new file mode 100644 index 000000000000..f0c5d8ce055a --- /dev/null +++ b/tests/xtimer_mocked_periph_timer/periph_timer_mock.h @@ -0,0 +1,25 @@ +#ifndef PERIPH_TIMER_MOCK_H +#define PERIPH_TIMER_MOCK_H + +/* Put here any functions that could need to help communicate with the mock */ + +/* + * Step the timer counter by 'ticks' and return the current ticks count after. + */ +uint32_t timer_mock_step(uint32_t ticks); + +/* + * Set the internal timer without handling any callbacks + * Should fit the internal precisiong + */ +void timer_mock_set(uint32_t ticks); + +/* + * This simulates a function that would be called after the value of + * `xtimer_now` is calculated + * + * And so deschedule after xtimer_now. + */ +void timer_mock_set_post_step_cb(void (*post_step_cb)(uint32_t current)); + +#endif /* PERIPH_TIMER_MOCK_H */ From 146417a48fffa007cf91702e5dfebf726760b242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Harter?= Date: Mon, 5 Nov 2018 13:35:16 +0100 Subject: [PATCH 2/3] squash! WIP/POC: start a test where xtimer uses a fake 'periph_timer' mock implementation Change that the 'post' callback so it is done after 'timer_read' only. --- tests/xtimer_mocked_periph_timer/main.c | 4 +- .../periph_timer_mock.c | 54 +++++++++---------- .../periph_timer_mock.h | 6 +-- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/tests/xtimer_mocked_periph_timer/main.c b/tests/xtimer_mocked_periph_timer/main.c index 36933a286ccf..fa515eb5662d 100644 --- a/tests/xtimer_mocked_periph_timer/main.c +++ b/tests/xtimer_mocked_periph_timer/main.c @@ -82,7 +82,7 @@ static void _simulate_deschedule_in_timer_set(uint32_t now) static void test_show_xtimer_set_call_target_in_the_past(void) { xtimer_init(); - timer_mock_set_post_step_cb(_simulate_deschedule_in_timer_set); + timer_mock_set_post_read_cb(_simulate_deschedule_in_timer_set); xtimer_t timer_low_offset = {.callback = test_timer_cb}; @@ -150,7 +150,7 @@ static void _simulate_deschedule_in_set_absolute(uint32_t now) static void test_show_xtimer_set_does_not_handle_overhead(void) { xtimer_init(); - timer_mock_set_post_step_cb(_simulate_deschedule_in_set_absolute); + timer_mock_set_post_read_cb(_simulate_deschedule_in_set_absolute); xtimer_t timer_low_offset = {.callback = test_timer_cb}; diff --git a/tests/xtimer_mocked_periph_timer/periph_timer_mock.c b/tests/xtimer_mocked_periph_timer/periph_timer_mock.c index 477b81769230..6fbc5df1a533 100644 --- a/tests/xtimer_mocked_periph_timer/periph_timer_mock.c +++ b/tests/xtimer_mocked_periph_timer/periph_timer_mock.c @@ -18,15 +18,26 @@ static struct { uint32_t current; - void (*post_step_cb)(uint32_t current); + void (*post_read_cb)(uint32_t current); } mock_timer; +static void _timer_mock_step_one(void); -void timer_mock_set_post_step_cb(void (*post_step_cb)(uint32_t current)) + +void timer_mock_set_post_read_cb(void (*post_read_cb)(uint32_t current)) { - mock_timer.post_step_cb = post_step_cb; + mock_timer.post_read_cb = post_read_cb; } +uint32_t timer_mock_step(uint32_t ticks) +{ + DEBUG("timer_mock_step(%" PRIu32 ") from %" PRIu32 "\n", + ticks, mock_timer.current); + for (uint32_t i = 0; i < ticks; i++) { + _timer_mock_step_one(); + } + return mock_timer.current; +} static void _timer_mock_step_one(void) { @@ -38,24 +49,6 @@ static void _timer_mock_step_one(void) } } - -uint32_t timer_mock_step(uint32_t ticks) -{ - DEBUG("timer_mock_step(%" PRIu32 ") from %" PRIu32 "\n", ticks, mock_timer.current); - for (uint32_t i = 0; i < ticks; i++) { - _timer_mock_step_one(); - } - - uint32_t now = mock_timer.current; - - - if (mock_timer.post_step_cb) { - mock_timer.post_step_cb(mock_timer.current); - } - - return now; -} - void timer_mock_set(uint32_t ticks) { DEBUG("timer_mock_set(%" PRIu32 ")\n", ticks); @@ -64,13 +57,15 @@ void timer_mock_set(uint32_t ticks) mock_timer.current = ticks; } - -static void timer_cb_mock(void *arg, int channel) +static void _timer_cb_mock(void *arg, int channel) { (void)arg; (void)channel; } +/* + * Mock of the original functions + */ int timer_init_mock(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) { DEBUG("timer_init_mock(%u): %" PRIu32 ", %p, %p\n", dev, freq, cb, arg); @@ -83,18 +78,23 @@ int timer_init_mock(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) mock_timer.current = 0; - mock_timer.post_step_cb = NULL; + mock_timer.post_read_cb = NULL; - (void)timer_cb_mock; + (void)_timer_cb_mock; return 0; } - unsigned int timer_read_mock(tim_t dev) { DEBUG("timer_read_mock(%u)\n", dev); - return timer_mock_step(1); + uint32_t now = timer_mock_step(1); + + if (mock_timer.post_read_cb) { + mock_timer.post_read_cb(now); + } + + return now; } int timer_set_absolute_mock(tim_t dev, int channel, unsigned int value) diff --git a/tests/xtimer_mocked_periph_timer/periph_timer_mock.h b/tests/xtimer_mocked_periph_timer/periph_timer_mock.h index f0c5d8ce055a..89a968e93f2e 100644 --- a/tests/xtimer_mocked_periph_timer/periph_timer_mock.h +++ b/tests/xtimer_mocked_periph_timer/periph_timer_mock.h @@ -16,10 +16,8 @@ void timer_mock_set(uint32_t ticks); /* * This simulates a function that would be called after the value of - * `xtimer_now` is calculated - * - * And so deschedule after xtimer_now. + * `timer_read` is calculated. It is executed after evaluating the return value. */ -void timer_mock_set_post_step_cb(void (*post_step_cb)(uint32_t current)); +void timer_mock_set_post_read_cb(void (*post_read_cb)(uint32_t current)); #endif /* PERIPH_TIMER_MOCK_H */ From 9508fd27596dec75dcec2ab0c9978fb9baebb407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Harter?= Date: Mon, 5 Nov 2018 15:23:24 +0100 Subject: [PATCH 3/3] fixup! WIP/POC: start a test where xtimer uses a fake 'periph_timer' mock implementation --- tests/xtimer_mocked_periph_timer/periph_timer_mock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/xtimer_mocked_periph_timer/periph_timer_mock.h b/tests/xtimer_mocked_periph_timer/periph_timer_mock.h index 89a968e93f2e..98bddfd8691d 100644 --- a/tests/xtimer_mocked_periph_timer/periph_timer_mock.h +++ b/tests/xtimer_mocked_periph_timer/periph_timer_mock.h @@ -10,7 +10,7 @@ uint32_t timer_mock_step(uint32_t ticks); /* * Set the internal timer without handling any callbacks - * Should fit the internal precisiong + * Should fit the internal precision */ void timer_mock_set(uint32_t ticks);