diff --git a/TESTS/mbedmicro-rtos-mbed/rtostimer/main.cpp b/TESTS/mbedmicro-rtos-mbed/rtostimer/main.cpp new file mode 100644 index 0000000000..581336bcdc --- /dev/null +++ b/TESTS/mbedmicro-rtos-mbed/rtostimer/main.cpp @@ -0,0 +1,367 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "rtos.h" + +using namespace utest::v1; + +#define TEST_DELAY_MS 50 +#define TEST_DELAY2_MS 30 +#define TEST_DELAY_MS_DELTA 1 +#define TEST_RESTART_DELAY_MS 10 + +#if TEST_RESTART_DELAY_MS >= TEST_DELAY_MS +#error invalid TEST_RESTART_DELAY_MS value +#endif + +void timer_callback(void const *arg) +{ + Semaphore *sem = (Semaphore *) arg; + sem->release(); +} + +/* In order to successfully run this test suite when compiled with --profile=debug + * error() has to be redefined as noop. + * + * RtosTimer calls RTX API which uses Event Recorder functionality. When compiled + * with MBED_TRAP_ERRORS_ENABLED=1 (set in debug profile) EvrRtxTimerError() calls error() + * which aborts test program. + */ +#if defined(MBED_TRAP_ERRORS_ENABLED) && MBED_TRAP_ERRORS_ENABLED +void error(const char* format, ...) { + (void) format; +} +#endif + +/** Test one-shot not restarted when elapsed + * + * Given a one-shot timer + * When the timer is started + * and given time elapses + * Then timer stops + * and elapsed time matches given delay + * and timer stays stopped + */ +void test_oneshot_not_restarted() +{ + Semaphore sem(1); + RtosTimer timer(mbed::callback(timer_callback, (void const *) &sem), osTimerOnce); + osStatus stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); + + Timer t; + sem.wait(0); + stat = timer.start(TEST_DELAY_MS); + t.start(); + TEST_ASSERT_EQUAL(osOK, stat); + + int32_t slots = sem.wait(TEST_DELAY_MS + 1); + t.stop(); + TEST_ASSERT_EQUAL(1, slots); + TEST_ASSERT_INT_WITHIN(TEST_DELAY_MS_DELTA * 1000, TEST_DELAY_MS * 1000, t.read_us()); + + slots = sem.wait(TEST_DELAY_MS + 1); + TEST_ASSERT_EQUAL(0, slots); + + stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); +} + +/** Test periodic repeats continuously + * + * Given a periodic timer + * When timer is started + * and given time elapses + * Then timer repeats its operation + * When timer is stopped + * Then timer stops operation + */ +void test_periodic_repeats() +{ + Semaphore sem(1); + RtosTimer timer(mbed::callback(timer_callback, (void const *) &sem), osTimerPeriodic); + osStatus stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); + + Timer t; + sem.wait(0); + stat = timer.start(TEST_DELAY_MS); + t.start(); + TEST_ASSERT_EQUAL(osOK, stat); + + int32_t slots = sem.wait(TEST_DELAY_MS + 1); + int t1 = t.read_us(); + TEST_ASSERT_EQUAL(1, slots); + TEST_ASSERT_INT_WITHIN(TEST_DELAY_MS_DELTA * 1000, TEST_DELAY_MS * 1000, t1); + + slots = sem.wait(TEST_DELAY_MS + 1); + t.stop(); + TEST_ASSERT_EQUAL(1, slots); + TEST_ASSERT_INT_WITHIN(TEST_DELAY_MS_DELTA * 1000, TEST_DELAY_MS * 1000, t.read_us() - t1); + + stat = timer.stop(); + TEST_ASSERT_EQUAL(osOK, stat); + + slots = sem.wait(TEST_DELAY_MS + 1); + TEST_ASSERT_EQUAL(0, slots); + + stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); +} + +/** Test timer can be restarted + * + * Given a one-shot timer + * When the timer is started + * and @a start is called again before given time elapses + * and given time elapses + * Then timer stops + * and elapsed time is greater than original delay + */ +void test_restart() +{ + Semaphore sem(1); + RtosTimer timer(mbed::callback(timer_callback, (void const *) &sem), osTimerOnce); + osStatus stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); + + Timer t; + sem.wait(0); + stat = timer.start(TEST_DELAY_MS); + t.start(); + TEST_ASSERT_EQUAL(osOK, stat); + + int32_t slots = sem.wait(TEST_RESTART_DELAY_MS); + TEST_ASSERT_EQUAL(0, slots); + + stat = timer.start(TEST_DELAY_MS); + TEST_ASSERT_EQUAL(osOK, stat); + + slots = sem.wait(TEST_DELAY_MS + 1); + t.stop(); + TEST_ASSERT_EQUAL(1, slots); + TEST_ASSERT_INT_WITHIN(TEST_DELAY_MS_DELTA * 1000, (TEST_DELAY_MS + TEST_RESTART_DELAY_MS) * 1000, t.read_us()); + + stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); +} + +/** Test timer can be started again + * + * Given a one-shot timer + * When the timer is started + * and given time elapses + * Then timer stops + * When the timer is started again + * and given time elapses + * Then timer stops again + */ +void test_start_again() +{ + Semaphore sem(1); + RtosTimer timer(mbed::callback(timer_callback, (void const *) &sem), osTimerOnce); + osStatus stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); + + sem.wait(0); + stat = timer.start(TEST_DELAY_MS); + TEST_ASSERT_EQUAL(osOK, stat); + + int32_t slots = sem.wait(TEST_DELAY_MS + 1); + TEST_ASSERT_EQUAL(1, slots); + + stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); + + stat = timer.start(TEST_DELAY_MS); + TEST_ASSERT_EQUAL(osOK, stat); + + slots = sem.wait(TEST_DELAY_MS + 1); + TEST_ASSERT_EQUAL(1, slots); + + stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); +} + +/** Test timer restart updates delay + * + * Given a one-shot timer + * When the timer is started + * and @a start is called again with a different delay before given time elapses + * and updated delay elapses + * Then timer stops + * and time elapsed since the second @a start call matches updated delay + */ +void test_restart_updates_delay() +{ + Semaphore sem(1); + RtosTimer timer(mbed::callback(timer_callback, (void const *) &sem), osTimerOnce); + osStatus stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); + + sem.wait(0); + stat = timer.start(TEST_DELAY_MS); + TEST_ASSERT_EQUAL(osOK, stat); + + int32_t slots = sem.wait(TEST_RESTART_DELAY_MS); + TEST_ASSERT_EQUAL(0, slots); + + Timer t; + stat = timer.start(TEST_DELAY2_MS); + t.start(); + TEST_ASSERT_EQUAL(osOK, stat); + + slots = sem.wait(TEST_DELAY2_MS + 1); + t.stop(); + TEST_ASSERT_EQUAL(1, slots); + TEST_ASSERT_INT_WITHIN(TEST_DELAY_MS_DELTA * 1000, TEST_DELAY2_MS * 1000, t.read_us()); + + stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); +} + +/** Test timer is created in stopped state + * + * Given a one-shot timer + * When the timer has not been started + * Then the timer is stopped + */ +void test_created_stopped() +{ + RtosTimer timer(mbed::callback(timer_callback, (void const *) NULL), osTimerOnce); + osStatus stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); +} + +/** Test one-shot can be stopped + * + * Given a one-shot timer + * When the timer is started + * and timer is stopped while still running + * Then timer stops operation + */ +void test_stop() +{ + Semaphore sem(1); + RtosTimer timer(mbed::callback(timer_callback, (void const *) &sem), osTimerOnce); + osStatus stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); + + sem.wait(0); + stat = timer.start(TEST_DELAY_MS); + TEST_ASSERT_EQUAL(osOK, stat); + + int32_t slots = sem.wait(TEST_RESTART_DELAY_MS); + TEST_ASSERT_EQUAL(0, slots); + + stat = timer.stop(); + TEST_ASSERT_EQUAL(osOK, stat); + + slots = sem.wait(TEST_DELAY_MS + 1); + TEST_ASSERT_EQUAL(0, slots); + + stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); +} + +/** Test timer started with infinite delay + * + * Given a one-shot timer + * When the timer is started with @a osWaitForever delay + * Then @a start return status is @a osOK + */ +void test_wait_forever() +{ + RtosTimer timer(mbed::callback(timer_callback, (void const *) NULL), osTimerOnce); + + osStatus stat = timer.start(osWaitForever); + TEST_ASSERT_EQUAL(osOK, stat); + + stat = timer.stop(); + TEST_ASSERT_EQUAL(osOK, stat); +} + +/** Test timer started with zero delay + * + * Given a one-shot timer + * When the timer is started with 0 delay + * Then @a start return status is @a osErrorParameter + */ +void test_no_wait() +{ + RtosTimer timer(mbed::callback(timer_callback, (void const *) NULL), osTimerOnce); + + osStatus stat = timer.start(0); + TEST_ASSERT_EQUAL(osErrorParameter, stat); + + stat = timer.stop(); + TEST_ASSERT_EQUAL(osErrorResource, stat); +} + +void timer_isr_call(void const *arg) +{ + RtosTimer *timer = (RtosTimer *) arg; + osStatus stat = timer->start(TEST_DELAY_MS); + TEST_ASSERT_EQUAL(osErrorISR, stat); + + stat = timer->stop(); + TEST_ASSERT_EQUAL(osErrorISR, stat); +} + +/** Test timer method calls from an ISR fail + * + * Given a one-shot timer + * When a timer method is called from an ISR + * Then method return status is @a osErrorISR + */ +void test_isr_calls_fail() +{ + RtosTimer timer(mbed::callback(timer_callback, (void const *) NULL), osTimerOnce); + + Ticker ticker; + ticker.attach(mbed::callback(timer_isr_call, (void const *) &timer), (float) TEST_DELAY_MS / 1000.0); + + wait_ms(TEST_DELAY_MS + 1); +} + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(5, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Test one-shot not restarted when elapsed", test_oneshot_not_restarted), + Case("Test periodic repeats continuously", test_periodic_repeats), + Case("Test timer can be restarted while running", test_restart), + Case("Test stopped timer can be started again", test_start_again), + Case("Test restart changes timeout", test_restart_updates_delay), + Case("Test can be stopped", test_stop), + Case("Test timer is created in stopped state", test_created_stopped), + Case("Test timer started with infinite delay", test_wait_forever), + Case("Test timer started with zero delay", test_no_wait), + Case("Test calls from ISR fail", test_isr_calls_fail) +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} diff --git a/rtos/RtosTimer.h b/rtos/RtosTimer.h index 3abef47c94..d96de829e1 100644 --- a/rtos/RtosTimer.h +++ b/rtos/RtosTimer.h @@ -131,13 +131,21 @@ public: } /** Stop the timer. - @return status code that indicates the execution status of the function. + @return status code that indicates the execution status of the function: + @a osOK the timer has been stopped. + @a osErrorISR @a stop cannot be called from interrupt service routines. + @a osErrorParameter internal error. + @a osErrorResource the timer is not running. */ osStatus stop(void); - /** Start the timer. - @param millisec time delay value of the timer. - @return status code that indicates the execution status of the function. + /** Start or restart the timer. + @param millisec non-zero value of the timer. + @return status code that indicates the execution status of the function: + @a osOK the timer has been started or restarted. + @a osErrorISR @a start cannot be called from interrupt service routines. + @a osErrorParameter internal error or incorrect parameter value. + @a osErrorResource internal error (the timer is in an invalid timer state). */ osStatus start(uint32_t millisec);