From 82f781ce3da094f32b26a67d907766ffabd67745 Mon Sep 17 00:00:00 2001 From: Filip Jagodzinski Date: Tue, 12 Sep 2017 16:41:01 +0200 Subject: [PATCH] Tests: Drivers: Timeout: Update unit tests Added unit tests for Timeout API. Added unit tests for LowPowerTimeout API. Moved all existing test cases to a header file. --- TESTS/mbed_drivers/lp_timeout/main.cpp | 165 +++----- TESTS/mbed_drivers/timeout/main.cpp | 108 +++--- TESTS/mbed_drivers/timeout/timeout_tests.h | 415 +++++++++++++++++++++ 3 files changed, 518 insertions(+), 170 deletions(-) create mode 100644 TESTS/mbed_drivers/timeout/timeout_tests.h diff --git a/TESTS/mbed_drivers/lp_timeout/main.cpp b/TESTS/mbed_drivers/lp_timeout/main.cpp index 9a16d6bdfb..eaccab90c9 100644 --- a/TESTS/mbed_drivers/lp_timeout/main.cpp +++ b/TESTS/mbed_drivers/lp_timeout/main.cpp @@ -15,138 +15,79 @@ */ #if !DEVICE_LOWPOWERTIMER - #error [NOT_SUPPORTED] Low power timer not supported for this target +#error [NOT_SUPPORTED] Low power timer not supported for this target #endif +#include "mbed.h" +#include "greentea-client/test_env.h" #include "utest/utest.h" #include "unity/unity.h" -#include "greentea-client/test_env.h" - -#include "mbed.h" +#include "../timeout/timeout_tests.h" using namespace utest::v1; -volatile static bool complete; -static LowPowerTimeout lpt; - -/* Timeouts are quite arbitrary due to large number of boards with varying level of accuracy */ -#define LONG_TIMEOUT (100000) -#define SHORT_TIMEOUT (600) - -void cb_done() { - complete = true; -} - -#if DEVICE_SLEEP -void lp_timeout_1s_deepsleep(void) +utest::v1::status_t greentea_failure_handler(const Case * const source, const failure_t reason) { - complete = false; - LowPowerTimer timer; - - /* - * Since deepsleep() may shut down the UART peripheral, we wait for 10ms - * to allow for hardware serial buffers to completely flush. - - * This should be replaced with a better function that checks if the - * hardware buffers are empty. However, such an API does not exist now, - * so we'll use the wait_ms() function for now. - */ - wait_ms(10); - - /* - * We use here the low power timer instead of microsecond timer for start and - * end because the microseconds timer might be disable during deepsleep. - */ - timer.start(); - lpt.attach(&cb_done, 1); - /* Make sure deepsleep is allowed, to go to deepsleep */ - bool deep_sleep_allowed = sleep_manager_can_deep_sleep(); - TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed"); - sleep(); - while (!complete); - - /* It takes longer to wake up from deep sleep */ - TEST_ASSERT_UINT32_WITHIN(LONG_TIMEOUT, 1000000, timer.read_us()); - TEST_ASSERT_TRUE(complete); -} - -void lp_timeout_1s_sleep(void) -{ - complete = false; - Timer timer; - timer.start(); - - sleep_manager_lock_deep_sleep(); - lpt.attach(&cb_done, 1); - bool deep_sleep_allowed = sleep_manager_can_deep_sleep(); - TEST_ASSERT_FALSE_MESSAGE(deep_sleep_allowed, "Deep sleep should be disallowed"); - sleep(); - while (!complete); - sleep_manager_unlock_deep_sleep(); - - TEST_ASSERT_UINT32_WITHIN(LONG_TIMEOUT, 1000000, timer.read_us()); - TEST_ASSERT_TRUE(complete); -} -#endif /* DEVICE_SLEEP */ - -void lp_timeout_us(uint32_t delay_us, uint32_t tolerance) -{ - complete = false; - Timer timer; - timer.start(); - - lpt.attach_us(&cb_done, delay_us); - while (!complete); - - /* Using RTC which is less accurate */ - TEST_ASSERT_UINT32_WITHIN(tolerance, delay_us, timer.read_us()); - TEST_ASSERT_TRUE(complete); -} - -void lp_timeout_5s(void) -{ - lp_timeout_us(5000000, LONG_TIMEOUT); -} - -void lp_timeout_1s(void) -{ - lp_timeout_us(1000000, LONG_TIMEOUT); -} - -void lp_timeout_1ms(void) -{ - lp_timeout_us(1000, SHORT_TIMEOUT); -} - -void lp_timeout_500us(void) -{ - lp_timeout_us(500, SHORT_TIMEOUT); - -} - -utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason) { greentea_case_failure_abort_handler(source, reason); return STATUS_CONTINUE; } Case cases[] = { - Case("500us LowPowerTimeout", lp_timeout_500us, greentea_failure_handler), - Case("1ms LowPowerTimeout", lp_timeout_1ms, greentea_failure_handler), - Case("1sec LowPowerTimeout", lp_timeout_1s, greentea_failure_handler), - Case("5sec LowPowerTimeout", lp_timeout_5s, greentea_failure_handler), + Case("Callback called once (attach)", test_single_call >), + Case("Callback called once (attach_us)", test_single_call >), + + Case("Callback not called when cancelled (attach)", test_cancel >), + Case("Callback not called when cancelled (attach_us)", test_cancel >), + + Case("Callback override (attach)", test_override >), + Case("Callback override (attach_us)", test_override >), + + Case("Multiple timeouts running in parallel (attach)", test_multiple >), + Case("Multiple timeouts running in parallel (attach_us)", test_multiple >), + + Case("Zero delay (attach)", test_no_wait >), + Case("Zero delay (attach_us)", test_no_wait >), + + Case("10 ms delay accuracy (attach)", test_delay_accuracy, 10000, SHORT_DELTA_US>, + greentea_failure_handler), + Case("10 ms delay accuracy (attach_us)", test_delay_accuracy, 10000, SHORT_DELTA_US>, + greentea_failure_handler), + + Case("1 s delay accuracy (attach)", test_delay_accuracy, 1000000, LONG_DELTA_US>, + greentea_failure_handler), + Case("1 s delay accuracy (attach_us)", test_delay_accuracy, 1000000, LONG_DELTA_US>, + greentea_failure_handler), + + Case("5 s delay accuracy (attach)", test_delay_accuracy, 5000000, LONG_DELTA_US>, + greentea_failure_handler), + Case("5 s delay accuracy (attach_us)", test_delay_accuracy, 5000000, LONG_DELTA_US>, + greentea_failure_handler), + #if DEVICE_SLEEP - Case("1sec LowPowerTimeout from sleep", lp_timeout_1s_sleep, greentea_failure_handler), - Case("1sec LowPowerTimeout from deepsleep", lp_timeout_1s_deepsleep, greentea_failure_handler), -#endif /* DEVICE_SLEEP */ + Case("1 s delay during sleep (attach)", test_sleep, 1000000, LONG_DELTA_US>, + greentea_failure_handler), + Case("1 s delay during sleep (attach_us)", test_sleep, 1000000, LONG_DELTA_US>, + greentea_failure_handler), + + Case("1 s delay during deepsleep (attach)", test_deepsleep, 1000000, LONG_DELTA_US>, + greentea_failure_handler), + Case("1 s delay during deepsleep (attach_us)", test_deepsleep, 1000000, LONG_DELTA_US>, + greentea_failure_handler), +#endif + + Case("Timing drift (attach)", test_drift >), + Case("Timing drift (attach_us)", test_drift >), }; -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) { - GREENTEA_SETUP(20, "default_auto"); +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(240, "timing_drift_auto"); return greentea_test_setup_handler(number_of_cases); } Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); -int main() { +int main() +{ Harness::run(specification); } diff --git a/TESTS/mbed_drivers/timeout/main.cpp b/TESTS/mbed_drivers/timeout/main.cpp index 851b947020..acab4fda5c 100644 --- a/TESTS/mbed_drivers/timeout/main.cpp +++ b/TESTS/mbed_drivers/timeout/main.cpp @@ -1,91 +1,83 @@ -/* - * Copyright (c) 2013-2016, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 +/* 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. + * 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. */ - - -/* - * Tests is to measure the accuracy of Timeout over a period of time - * - * - * 1) DUT would start to update callback_trigger_count every milli sec - * 2) Host would query what is current count base_time, Device responds by the callback_trigger_count - * 3) Host after waiting for measurement stretch. It will query for device time again final_time. - * 4) Host computes the drift considering base_time, final_time, transport delay and measurement stretch - * 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" +#include "timeout_tests.h" using namespace utest::v1; -#define PERIOD_US 10000 - -volatile int ticker_count = 0; -volatile uint32_t callback_trigger_count = 0; -static const int test_timeout = 240; -Timeout timeout; - -void set_incremeant_count() { - timeout.attach_us(set_incremeant_count, PERIOD_US); - ++callback_trigger_count; +utest::v1::status_t greentea_failure_handler(const Case * const source, const failure_t reason) +{ + greentea_case_failure_abort_handler(source, reason); + return STATUS_CONTINUE; } -void test_case_timeout() { +Case cases[] = { + Case("Callback called once (attach)", test_single_call >), + Case("Callback called once (attach_us)", test_single_call >), - char _key[11] = { }; - char _value[128] = { }; - int expected_key = 1; - uint8_t results_size = 0; + Case("Callback not called when cancelled (attach)", test_cancel >), + Case("Callback not called when cancelled (attach_us)", test_cancel >), - greentea_send_kv("timing_drift_check_start", 0); - timeout.attach_us(set_incremeant_count, PERIOD_US); + Case("Callback override (attach)", test_override >), + Case("Callback override (attach_us)", test_override >), - // wait for 1st signal from host - do { - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - expected_key = strcmp(_key, "base_time"); - } while (expected_key); + Case("Multiple timeouts running in parallel (attach)", test_multiple >), + Case("Multiple timeouts running in parallel (attach_us)", test_multiple >), - greentea_send_kv(_key, callback_trigger_count * PERIOD_US); + Case("Zero delay (attach)", test_no_wait >), + Case("Zero delay (attach_us)", test_no_wait >), - // wait for 2nd signal from host - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - greentea_send_kv(_key, callback_trigger_count * PERIOD_US); + Case("10 ms delay accuracy (attach)", test_delay_accuracy, 10000, SHORT_DELTA_US>, + greentea_failure_handler), + Case("10 ms delay accuracy (attach_us)", test_delay_accuracy, 10000, SHORT_DELTA_US>, + greentea_failure_handler), - //get the results from host - greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + Case("1 s delay accuracy (attach)", test_delay_accuracy, 1000000, LONG_DELTA_US>, + greentea_failure_handler), + Case("1 s delay accuracy (attach_us)", test_delay_accuracy, 1000000, LONG_DELTA_US>, + greentea_failure_handler), - TEST_ASSERT_EQUAL_STRING_MESSAGE("pass", _key,"Host side script reported a fail..."); -} + Case("5 s delay accuracy (attach)", test_delay_accuracy, 5000000, LONG_DELTA_US>, + greentea_failure_handler), + Case("5 s delay accuracy (attach_us)", test_delay_accuracy, 5000000, LONG_DELTA_US>, + greentea_failure_handler), -// Test casess -Case cases[] = { Case("Timers: toggle on/off", test_case_timeout), }; +#if DEVICE_SLEEP + Case("1 s delay during sleep (attach)", test_sleep, 1000000, LONG_DELTA_US>, + greentea_failure_handler), + Case("1 s delay during sleep (attach_us)", test_sleep, 1000000, LONG_DELTA_US>, + greentea_failure_handler), +#endif -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) { - GREENTEA_SETUP(test_timeout, "timing_drift_auto"); + Case("Timing drift (attach)", test_drift >), + Case("Timing drift (attach_us)", test_drift >), +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(240, "timing_drift_auto"); return greentea_test_setup_handler(number_of_cases); } Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); -int main() { +int main() +{ Harness::run(specification); } diff --git a/TESTS/mbed_drivers/timeout/timeout_tests.h b/TESTS/mbed_drivers/timeout/timeout_tests.h new file mode 100644 index 0000000000..2920e8c243 --- /dev/null +++ b/TESTS/mbed_drivers/timeout/timeout_tests.h @@ -0,0 +1,415 @@ +/* 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. + */ +#ifndef MBED_TIMEOUT_TESTS_H +#define MBED_TIMEOUT_TESTS_H + +#include "mbed.h" +#include "unity/unity.h" + +#define NUM_TIMEOUTS 16 +#define DRIFT_TEST_PERIOD_US 10000 + +const float TEST_DELAY_S = 0.01; +const uint32_t TEST_DELAY_MS = 1000.0F * TEST_DELAY_S; +const us_timestamp_t TEST_DELAY_US = 1000000.0F * TEST_DELAY_S; + +/* Timeouts are quite arbitrary due to large number of boards with varying level of accuracy */ +#define LONG_DELTA_US (100000) +#define SHORT_DELTA_US (2000) + +void sem_callback(Semaphore *sem) +{ + sem->release(); +} + +void cnt_callback(volatile uint32_t *cnt) +{ + (*cnt)++; +} + +template +class AttachTester: public TimeoutType { +public: + void attach_callback(Callback func, us_timestamp_t delay_us) + { + TimeoutType::attach(func, (float) delay_us / 1000000.0f); + } +}; + +template +class AttachUSTester: public TimeoutType { +public: + void attach_callback(Callback func, us_timestamp_t delay_us) + { + TimeoutType::attach_us(func, delay_us); + } +}; + +/** Template for tests: callback called once + * + * Test callback called once + * Given a Timeout object with a callback attached with @a attach() + * When given time elapses + * Then the callback is called exactly one time + * + * Test callback called once + * Given a Timeout object with a callback attached with @a attach_us() + * When given time elapses + * Then the callback is called exactly one time + */ +template +void test_single_call(void) +{ + Semaphore sem(0, 1); + T timeout; + + timeout.attach_callback(mbed::callback(sem_callback, &sem), TEST_DELAY_US); + + int32_t sem_slots = sem.wait(0); + TEST_ASSERT_EQUAL(0, sem_slots); + + sem_slots = sem.wait(TEST_DELAY_MS + 1); + TEST_ASSERT_EQUAL(1, sem_slots); + + sem_slots = sem.wait(TEST_DELAY_MS + 1); + TEST_ASSERT_EQUAL(0, sem_slots); + + timeout.detach(); +} + +/** Template for tests: callback not called when cancelled + * + * Test callback not called when cancelled + * Given a Timeout object with a callback attached with @a attach() + * When the callback is detached before being called + * Then the callback is never called + * + * Test callback not called when cancelled + * Given a Timeout object with a callback attached with @a attach_us() + * When the callback is detached before being called + * Then the callback is never called + */ +template +void test_cancel(void) +{ + Semaphore sem(0, 1); + T timeout; + + timeout.attach_callback(mbed::callback(sem_callback, &sem), 2.0f * TEST_DELAY_US); + + int32_t sem_slots = sem.wait(TEST_DELAY_MS); + TEST_ASSERT_EQUAL(0, sem_slots); + timeout.detach(); + + sem_slots = sem.wait(TEST_DELAY_MS + 1); + TEST_ASSERT_EQUAL(0, sem_slots); +} + +/** Template for tests: callback override + * + * Test callback override + * Given a Timeout object with a callback attached with @a attach() + * When another callback is attached before first one is called + * and second callback's delay elapses + * Then the second callback is called + * and the first callback is never called + * + * Test callback override + * Given a Timeout object with a callback attached with @a attach_us() + * When another callback is attached before first one is called + * and second callback's delay elapses + * Then the second callback is called + * and the first callback is never called + */ +template +void test_override(void) +{ + Semaphore sem1(0, 1); + Semaphore sem2(0, 1); + T timeout; + + timeout.attach_callback(mbed::callback(sem_callback, &sem1), 2.0f * TEST_DELAY_US); + + int32_t sem_slots = sem1.wait(TEST_DELAY_MS); + TEST_ASSERT_EQUAL(0, sem_slots); + timeout.attach_callback(mbed::callback(sem_callback, &sem2), 2.0f * TEST_DELAY_US); + + sem_slots = sem2.wait(2 * TEST_DELAY_MS + 1); + TEST_ASSERT_EQUAL(1, sem_slots); + sem_slots = sem1.wait(0); + TEST_ASSERT_EQUAL(0, sem_slots); + + timeout.detach(); +} + +/** Template for tests: multiple Timeouts + * + * Test multiple Timeouts + * Given multiple separate Timeout objects + * When a callback is attached to all of these Timeout objects with @a attach() + * and delay for every Timeout elapses + * Then all callbacks are called + * + * Test multiple Timeouts + * Given multiple separate Timeout objects + * When a callback is attached to all of these Timeout objects with @a attach_us() + * and delay for every Timeout elapses + * Then all callbacks are called + */ +template +void test_multiple(void) +{ + volatile uint32_t callback_count = 0; + T timeouts[NUM_TIMEOUTS]; + for (size_t i = 0; i < NUM_TIMEOUTS; i++) { + timeouts[i].attach_callback(mbed::callback(cnt_callback, &callback_count), TEST_DELAY_US); + } + Thread::wait(TEST_DELAY_MS + 1); + TEST_ASSERT_EQUAL(NUM_TIMEOUTS, callback_count); +} + +/** Template for tests: zero delay + * + * Test zero delay + * Given a Timeout object + * When a callback is attached with 0.0 s delay, with @a attach() + * Then the callback is called instantly + * + * Test zero delay + * Given a Timeout object + * When a callback is attached with 0.0 s delay, with @a attach_us() + * Then the callback is called instantly + */ +template +void test_no_wait(void) +{ + Semaphore sem(0, 1); + T timeout; + timeout.attach_callback(mbed::callback(sem_callback, &sem), 0ULL); + + int32_t sem_slots = sem.wait(0); + TEST_ASSERT_EQUAL(1, sem_slots); + timeout.detach(); +} + +/** Template for tests: accuracy of timeout delay + * + * Test delay accuracy + * Given a Timeout object with a callback attached with @a attach() + * When the callback is called + * Then elapsed time matches given delay + * + * Test delay accuracy + * Given a Timeout object with a callback attached with @a attach_us() + * When the callback is called + * Then elapsed time matches given delay + */ +template +void test_delay_accuracy(void) +{ + Semaphore sem(0, 1); + T timeout; + Timer timer; + + timer.start(); + timeout.attach_callback(mbed::callback(sem_callback, &sem), delay_us); + + int32_t sem_slots = sem.wait(osWaitForever); + timer.stop(); + TEST_ASSERT_EQUAL(1, sem_slots); + TEST_ASSERT_UINT64_WITHIN(delta_us, delay_us, timer.read_high_resolution_us()); + + timeout.detach(); +} + +#if DEVICE_SLEEP +/** Template for tests: timeout during sleep + * + * Test timeout during sleep + * Given a Timeout object with a callback attached with @a attach() + * and the uC in a sleep mode + * When given delay elapses + * Then the callback is called + * and elapsed time matches given delay + * + * Test timeout during sleep + * Given a Timeout object with a callback attached with @a attach_us() + * and the uC in a sleep mode + * When given delay elapses + * Then the callback is called + * and elapsed time matches given delay + */ +template +void test_sleep(void) +{ + Semaphore sem(0, 1); + T timeout; + Timer timer; + + sleep_manager_lock_deep_sleep(); + timer.start(); + timeout.attach_callback(mbed::callback(sem_callback, &sem), delay_us); + + bool deep_sleep_allowed = sleep_manager_can_deep_sleep(); + TEST_ASSERT_FALSE_MESSAGE(deep_sleep_allowed, "Deep sleep should be disallowed"); + while (sem.wait(0) != 1) { + sleep(); + } + timer.stop(); + + sleep_manager_unlock_deep_sleep(); + TEST_ASSERT_UINT64_WITHIN(delta_us, delay_us, timer.read_high_resolution_us()); + + timeout.detach(); +} + +#if DEVICE_LOWPOWERTIMER +/** Template for tests: timeout during deepsleep + * + * Test timeout during deepsleep + * Given a LowPowerTimeout object with a callback attached with @a attach() + * and the uC in a deepsleep mode + * When given delay elapses + * Then the callback is called + * and elapsed time matches given delay + * + * Test timeout during deepsleep + * Given a LowPowerTimeout object with a callback attached with @a attach_us() + * and the uC in a deepsleep mode + * When given delay elapses + * Then the callback is called + * and elapsed time matches given delay + */ +template +void test_deepsleep(void) +{ + Semaphore sem(0, 1); + T timeout; + /* + * We use here the low power timer instead of microsecond timer for start and + * end because the microseconds timer might be disabled during deepsleep. + */ + LowPowerTimer timer; + + /* + * Since deepsleep() may shut down the UART peripheral, we wait for 10ms + * to allow for hardware serial buffers to completely flush. + + * This should be replaced with a better function that checks if the + * hardware buffers are empty. However, such an API does not exist now, + * so we'll use the wait_ms() function for now. + */ + wait_ms(10); + + timer.start(); + timeout.attach_callback(mbed::callback(sem_callback, &sem), delay_us); + + bool deep_sleep_allowed = sleep_manager_can_deep_sleep(); + TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed"); + while (sem.wait(0) != 1) { + sleep(); + } + timer.stop(); + + TEST_ASSERT_UINT64_WITHIN(delta_us, delay_us, timer.read_high_resolution_us()); + + timeout.detach(); +} +#endif +#endif + +template +class TimeoutDriftTester { +public: + TimeoutDriftTester(us_timestamp_t period = 1000) : + _callback_count(0), _period(period), _timeout() + { + } + + void reschedule_callback(void) + { + _timeout.attach_callback(mbed::callback(this, &TimeoutDriftTester::reschedule_callback), _period); + _callback_count++; + } + + void detach_callback(void) + { + _timeout.detach(); + } + + uint32_t get_callback_count(void) + { + return _callback_count; + } + +private: + volatile uint32_t _callback_count; + us_timestamp_t _period; + TimeoutTesterType _timeout; +}; + +/** Template for tests: accuracy of timeout delay scheduled repeatedly + * + * Test time drift -- device part + * Given a Timeout object with a callback repeatedly attached with @a attach() + * When the testing host computes test duration based on values received from uC + * Then computed time and actual time measured by host are equal within given tolerance + * + * Test time drift -- device part + * Given a Timeout object with a callback repeatedly attached with @a attach_us() + * When the testing host computes test duration based on values received from uC + * Then computed time and actual time measured by host are equal within given tolerance + * + * Original description: + * 1) DUT would start to update callback_trigger_count every milli sec + * 2) Host would query what is current count base_time, Device responds by the callback_trigger_count + * 3) Host after waiting for measurement stretch. It will query for device time again final_time. + * 4) Host computes the drift considering base_time, final_time, transport delay and measurement stretch + * 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 + */ +template +void test_drift(void) +{ + char _key[11] = { }; + char _value[128] = { }; + int expected_key = 1; + TimeoutDriftTester timeout(DRIFT_TEST_PERIOD_US); + + greentea_send_kv("timing_drift_check_start", 0); + timeout.reschedule_callback(); + + // wait for 1st signal from host + do { + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + expected_key = strcmp(_key, "base_time"); + } while (expected_key); + + greentea_send_kv(_key, timeout.get_callback_count() * DRIFT_TEST_PERIOD_US); + + // wait for 2nd signal from host + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + greentea_send_kv(_key, timeout.get_callback_count() * DRIFT_TEST_PERIOD_US); + + timeout.detach_callback(); + + //get the results from host + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + + TEST_ASSERT_EQUAL_STRING_MESSAGE("pass", _key, "Host script reported a failure"); +} + +#endif