diff --git a/TESTS/mbed_drivers/ticker/main.cpp b/TESTS/mbed_drivers/ticker/main.cpp index 9e12041de5..abbcdb32ea 100644 --- a/TESTS/mbed_drivers/ticker/main.cpp +++ b/TESTS/mbed_drivers/ticker/main.cpp @@ -1,24 +1,110 @@ -/* - * Copyright (c) 2013-2016, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 +/* mbed Microcontroller Library + * Copyright (c) 2013-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. + * 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 + * 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. + * 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 "utest/utest.h" +#include "unity/unity.h" -/* - * Tests is to measure the accuracy of Ticker over a period of time - * +using utest::v1::Case; + +#define ONE_MILLI_SEC 1000 +#define TICKER_COUNT 16 +#define MULTI_TICKER_TIME_MS 100 +volatile uint32_t callback_trigger_count = 0; +static const int test_timeout = 240; +static const int total_ticks = 10; + + +/* Tolerance is quite arbitrary due to large number of boards with varying level of accuracy */ +#define TOLERANCE_US 1000 + +volatile uint32_t ticker_callback_flag; +volatile uint32_t multi_counter; + +DigitalOut led1(LED1); +DigitalOut led2(LED2); + +Ticker *volatile ticker1; +Ticker *volatile ticker2; +Timer gtimer; + +volatile int ticker_count = 0; +volatile bool print_tick = false; + +void ticker_callback_1_switch_to_2(void); +void ticker_callback_2_switch_to_1(void); + +void increment_ticker_counter(void) +{ + ++callback_trigger_count; +} + +void switch_led1_state(void) +{ + led1 = !led1; +} + +void switch_led2_state(void) +{ + led2 = !led2; +} + +void ticker_callback_1_switch_to_2(void) +{ + ++callback_trigger_count; + // If ticker is NULL then it is being or has been deleted + if (ticker1) { + ticker1->detach(); + ticker1->attach_us(ticker_callback_2_switch_to_1, ONE_MILLI_SEC); + } + switch_led1_state(); +} + +void ticker_callback_2_switch_to_1(void) +{ + ++callback_trigger_count; + // If ticker is NULL then it is being or has been deleted + if (ticker2) { + ticker2->detach(); + ticker2->attach_us(ticker_callback_1_switch_to_2, ONE_MILLI_SEC); + } + switch_led2_state(); +} + + +void sem_release(Semaphore *sem) +{ + sem->release(); +} + + +void stop_gtimer_set_flag(void) +{ + gtimer.stop(); + core_util_atomic_incr_u32((uint32_t*)&ticker_callback_flag, 1); +} + +void increment_multi_counter(void) +{ + core_util_atomic_incr_u32((uint32_t*)&multi_counter, 1); +} + + +/* Tests is to measure the accuracy of Ticker over a period of time * * 1) DUT would start to update callback_trigger_count every milli sec, in 2x callback we use 2 tickers * to update the count alternatively. @@ -28,81 +114,14 @@ * 5) Finally host send the results back to device pass/fail based on tolerance. * 6) More details on tests can be found in timing_drift_auto.py */ - -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "utest/utest.h" -#include "unity/unity.h" - -using namespace utest::v1; - -#define ONE_MILLI_SEC 1000 -volatile uint32_t callback_trigger_count = 0; -static const int test_timeout = 240; -static const int total_ticks = 10; - -DigitalOut led1(LED1); -DigitalOut led2(LED2); - -Ticker *volatile ticker1; -Ticker *volatile ticker2; - -volatile int ticker_count = 0; -volatile bool print_tick = false; - -void ticker_callback_1_switch_to_2(void); -void ticker_callback_2_switch_to_1(void); - -void ticker_callback_0(void) { - ++callback_trigger_count; -} - -void ticker_callback_1_led(void) { - led1 = !led1; -} - -void ticker_callback_2_led(void) { - led2 = !led2; -} - -void ticker_callback_1_switch_to_2(void) { - ++callback_trigger_count; - // If ticker is NULL then it is being or has been deleted - if (ticker1) { - ticker1->detach(); - ticker1->attach_us(ticker_callback_2_switch_to_1, ONE_MILLI_SEC); - } - ticker_callback_1_led(); -} - -void ticker_callback_2_switch_to_1(void) { - ++callback_trigger_count; - // If ticker is NULL then it is being or has been deleted - if (ticker2) { - ticker2->detach(); - ticker2->attach_us(ticker_callback_1_switch_to_2, ONE_MILLI_SEC); - } - ticker_callback_2_led(); -} - -void wait_and_print() { - while (ticker_count <= total_ticks) { - if (print_tick) { - print_tick = false; - greentea_send_kv("tick", ticker_count++); - } - } -} - -void test_case_1x_ticker() { - +void test_case_1x_ticker() +{ char _key[11] = { }; char _value[128] = { }; - uint8_t results_size = 0; int expected_key = 1; greentea_send_kv("timing_drift_check_start", 0); - ticker1->attach_us(&ticker_callback_0, ONE_MILLI_SEC); + ticker1->attach_us(&increment_ticker_counter, ONE_MILLI_SEC); // wait for 1st signal from host do { @@ -119,13 +138,12 @@ void test_case_1x_ticker() { greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); TEST_ASSERT_EQUAL_STRING_MESSAGE("pass", _key,"Host side script reported a fail..."); - } -void test_case_2x_callbacks() { +void test_case_2x_callbacks() +{ char _key[11] = { }; char _value[128] = { }; - uint8_t results_size = 0; int expected_key = 1; led1 = 0; @@ -150,50 +168,198 @@ void test_case_2x_callbacks() { greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); TEST_ASSERT_EQUAL_STRING_MESSAGE("pass", _key,"Host side script reported a fail..."); - } -utest::v1::status_t one_ticker_case_setup_handler_t(const Case *const source, const size_t index_of_case) { +/** Test many tickers run one after the other + + Given many Tickers + When schedule them one after the other with the same time intervals + Then tickers properly execute callbacks + When schedule them one after the other with the different time intervals + Then tickers properly execute callbacks + */ +void test_multi_ticker(void) +{ + Ticker ticker[TICKER_COUNT]; + const uint32_t extra_wait = 5; // extra 5ms wait time + + multi_counter = 0; + for (int i = 0; i < TICKER_COUNT; i++) { + ticker[i].attach_us(callback(increment_multi_counter), MULTI_TICKER_TIME_MS * 1000); + } + + Thread::wait(MULTI_TICKER_TIME_MS + extra_wait); + for (int i = 0; i < TICKER_COUNT; i++) { + ticker[i].detach(); + } + TEST_ASSERT_EQUAL(TICKER_COUNT, multi_counter); + + multi_counter = 0; + for (int i = 0; i < TICKER_COUNT; i++) { + ticker[i].attach_us(callback(increment_multi_counter), (MULTI_TICKER_TIME_MS + i) * 1000); + } + + Thread::wait(MULTI_TICKER_TIME_MS + TICKER_COUNT + extra_wait); + for (int i = 0; i < TICKER_COUNT; i++) { + ticker[i].detach(); + } + TEST_ASSERT_EQUAL(TICKER_COUNT, multi_counter); +} + +/** Test multi callback time + + Given a Ticker + When the callback is attached multiple times + Then ticker properly execute callback multiple times + */ +void test_multi_call_time(void) +{ + Ticker ticker; + int time_diff; + const int attach_count = 10; + + for (int i = 0; i < attach_count; i++) { + ticker_callback_flag = 0; + gtimer.reset(); + + gtimer.start(); + ticker.attach_us(callback(stop_gtimer_set_flag), MULTI_TICKER_TIME_MS * 1000); + while(!ticker_callback_flag); + time_diff = gtimer.read_us(); + + TEST_ASSERT_UINT32_WITHIN(TOLERANCE_US, MULTI_TICKER_TIME_MS * 1000, time_diff); + } +} + +/** Test if detach cancel scheduled callback event + + Given a Ticker with callback attached + When the callback is detached + Then the callback is not being called + */ +void test_detach(void) +{ + Ticker ticker; + int32_t ret; + const float ticker_time_s = 0.1f; + const uint32_t wait_time_ms = 500; + Semaphore sem(0, 1); + + ticker.attach(callback(sem_release, &sem), ticker_time_s); + + ret = sem.wait(); + TEST_ASSERT_TRUE(ret > 0); + + ret = sem.wait(); + ticker.detach(); /* cancel */ + TEST_ASSERT_TRUE(ret > 0); + + ret = sem.wait(wait_time_ms); + TEST_ASSERT_EQUAL(0, ret); +} + +/** Test single callback time via attach + + Given a Ticker + When callback attached with time interval specified + Then ticker properly executes callback within a specified time interval + */ +template +void test_attach_time(void) +{ + Ticker ticker; + ticker_callback_flag = 0; + + gtimer.reset(); + gtimer.start(); + ticker.attach(callback(stop_gtimer_set_flag), ((float)DELAY_US) / 1000000.0f); + while(!ticker_callback_flag); + ticker.detach(); + const int time_diff = gtimer.read_us(); + + TEST_ASSERT_UINT64_WITHIN(TOLERANCE_US, DELAY_US, time_diff); +} + +/** Test single callback time via attach_us + + Given a Ticker + When callback attached with time interval specified + Then ticker properly executes callback within a specified time interval + */ +template +void test_attach_us_time(void) +{ + Ticker ticker; + ticker_callback_flag = 0; + + gtimer.reset(); + gtimer.start(); + ticker.attach_us(callback(stop_gtimer_set_flag), DELAY_US); + while(!ticker_callback_flag); + ticker.detach(); + const int time_diff = gtimer.read_us(); + + TEST_ASSERT_UINT64_WITHIN(TOLERANCE_US, DELAY_US, time_diff); +} + + +utest::v1::status_t one_ticker_case_setup_handler_t(const Case *const source, const size_t index_of_case) +{ ticker1 = new Ticker(); return greentea_case_setup_handler(source, index_of_case); } -utest::v1::status_t two_ticker_case_setup_handler_t(const Case *const source, const size_t index_of_case) { +utest::v1::status_t two_ticker_case_setup_handler_t(const Case *const source, const size_t index_of_case) +{ ticker1 = new Ticker(); ticker2 = new Ticker(); - return greentea_case_setup_handler(source, index_of_case); + return utest::v1::greentea_case_setup_handler(source, index_of_case); } -utest::v1::status_t one_ticker_case_teardown_handler_t(const Case *const source, const size_t passed, const size_t failed, const failure_t reason) { +utest::v1::status_t one_ticker_case_teardown_handler_t(const Case *const source, const size_t passed, const size_t failed, const utest::v1::failure_t reason) +{ Ticker *temp1 = ticker1; ticker1 = NULL; delete temp1; - return greentea_case_teardown_handler(source, passed, failed, reason); + return utest::v1::greentea_case_teardown_handler(source, passed, failed, reason); } -utest::v1::status_t two_ticker_case_teardown_handler_t(const Case *const source, const size_t passed, const size_t failed, const failure_t reason) { +utest::v1::status_t two_ticker_case_teardown_handler_t(const Case *const source, const size_t passed, const size_t failed, const utest::v1::failure_t reason) +{ Ticker *temp1 = ticker1; Ticker *temp2 = ticker2; ticker1 = NULL; ticker2 = NULL; delete temp1; delete temp2; - return greentea_case_teardown_handler(source, passed, failed, reason); + return utest::v1::greentea_case_teardown_handler(source, passed, failed, reason); } + // Test cases Case cases[] = { - Case("Timers: 1x ticker", one_ticker_case_setup_handler_t,test_case_1x_ticker, one_ticker_case_teardown_handler_t), - Case("Timers: 2x callbacks", two_ticker_case_setup_handler_t,test_case_2x_callbacks, two_ticker_case_teardown_handler_t), + Case("Test attach for 0.01s and time measure", test_attach_time<10000>), + Case("Test attach_us for 10ms and time measure", test_attach_us_time<10000>), + Case("Test attach for 0.1s and time measure", test_attach_time<100000>), + Case("Test attach_us for 100ms and time measure", test_attach_us_time<100000>), + Case("Test attach for 0.5s and time measure", test_attach_time<500000>), + Case("Test attach_us for 500ms and time measure", test_attach_us_time<500000>), + Case("Test detach", test_detach), + Case("Test multi call and time measure", test_multi_call_time), + Case("Test multi ticker", test_multi_ticker), + Case("Test timers: 1x ticker", one_ticker_case_setup_handler_t,test_case_1x_ticker, one_ticker_case_teardown_handler_t), + Case("Test timers: 2x callbacks", two_ticker_case_setup_handler_t,test_case_2x_callbacks, two_ticker_case_teardown_handler_t) }; -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) { +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ GREENTEA_SETUP(test_timeout, "timing_drift_auto"); - return greentea_test_setup_handler(number_of_cases); + return utest::v1::greentea_test_setup_handler(number_of_cases); } -Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); +utest::v1::Specification specification(greentea_test_setup, cases, utest::v1::greentea_test_teardown_handler); -int main() { - Harness::run(specification); +int main() +{ + utest::v1::Harness::run(specification); }