From f2c222be1595d879b16b41defa93f38482dd5ce3 Mon Sep 17 00:00:00 2001 From: Filip Jagodzinski Date: Wed, 18 Jul 2018 16:19:06 +0200 Subject: [PATCH] Tests: SleepManager: extend test suite New test cases: * "deep sleep lock/unlock" * "deep sleep unbalanced unlock" * "deep sleep locked USHRT_MAX times" * "deep sleep locked more than USHRT_MAX times" * "sleep_auto calls sleep/deep sleep based on lock" --- TESTS/mbed_hal/sleep_manager/main.cpp | 198 ++++++++++++++++-- .../sleep_manager/sleep_manager_api_tests.h | 84 ++++++++ hal/mbed_sleep_manager.c | 8 + 3 files changed, 273 insertions(+), 17 deletions(-) create mode 100644 TESTS/mbed_hal/sleep_manager/sleep_manager_api_tests.h diff --git a/TESTS/mbed_hal/sleep_manager/main.cpp b/TESTS/mbed_hal/sleep_manager/main.cpp index 3d3e392b50..0717c12f76 100644 --- a/TESTS/mbed_hal/sleep_manager/main.cpp +++ b/TESTS/mbed_hal/sleep_manager/main.cpp @@ -16,46 +16,210 @@ #include "utest/utest.h" #include "unity/unity.h" #include "greentea-client/test_env.h" +#include +#include "mbed.h" +#include "mbed_lp_ticker_wrapper.h" +#include "sleep_manager_api_tests.h" #if !DEVICE_SLEEP #error [NOT_SUPPORTED] test not supported #endif -using namespace utest::v1; +#define SLEEP_DURATION_US 100000ULL +#define SERIAL_FLUSH_TIME_MS 20 -void sleep_manager_deepsleep_counter_test() +using utest::v1::Case; +using utest::v1::Specification; +using utest::v1::Harness; + +static uint32_t num_test_errors = 0UL; + +mbed_error_status_t mbed_error(mbed_error_status_t error_status, const char *error_msg, unsigned int error_value, + const char *filename, int line_number) { - bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check(); - TEST_ASSERT_TRUE(deep_sleep_allowed); + (void) error_status; + (void) error_msg; + (void) error_value; + (void) filename; + (void) line_number; + + num_test_errors++; + return MBED_SUCCESS; +} + +void test_lock_unlock() +{ + TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep()); sleep_manager_lock_deep_sleep(); - deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check(); - TEST_ASSERT_FALSE(deep_sleep_allowed); + TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep()); sleep_manager_unlock_deep_sleep(); - deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check(); - TEST_ASSERT_TRUE(deep_sleep_allowed); + TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep()); } -utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason) +void test_lone_unlock() { - greentea_case_failure_abort_handler(source, reason); - return STATUS_CONTINUE; + uint32_t expected_err_count = num_test_errors + 1; + sleep_manager_unlock_deep_sleep(); + TEST_ASSERT_EQUAL_UINT32(expected_err_count, num_test_errors); + + // Make sure upcoming tests won't be broken. + TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep()); } -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +void test_lock_eq_ushrt_max() { - GREENTEA_SETUP(20, "default_auto"); - return greentea_test_setup_handler(number_of_cases); + uint32_t lock_count = 0; + while (lock_count < USHRT_MAX) { + sleep_manager_lock_deep_sleep(); + lock_count++; + TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep()); + } + while (lock_count > 1) { + sleep_manager_unlock_deep_sleep(); + lock_count--; + TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep()); + } + sleep_manager_unlock_deep_sleep(); + TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep()); +} + +void test_lock_gt_ushrt_max() +{ + uint32_t lock_count = 0; + while (lock_count < USHRT_MAX) { + sleep_manager_lock_deep_sleep(); + lock_count++; + TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep()); + } + + uint32_t expected_err_count = num_test_errors + 1; + sleep_manager_lock_deep_sleep(); + TEST_ASSERT_EQUAL_UINT32(expected_err_count, num_test_errors); + + // Make sure upcoming tests won't be broken. + while (lock_count > 0) { + sleep_manager_unlock_deep_sleep(); + lock_count--; + } + TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep()); +} + +#if DEVICE_LPTICKER && DEVICE_USTICKER +void wakeup_callback(volatile int *wakeup_flag) +{ + (*wakeup_flag)++; +} + +/* This test is based on the fact that the high-speed clocks are turned off + * in deep sleep mode but remain on in the ordinary sleep mode. Low-speed + * clocks stay on for both sleep and deep sleep modes. + * + * The type of sleep that was actually used by sleep_manager_sleep_auto() + * can be detected by comparing times measured by us and lp tickers. + */ +void test_sleep_auto() +{ + const ticker_data_t *const us_ticker = get_us_ticker_data(); + const ticker_data_t *const lp_ticker = get_lp_ticker_data(); + us_timestamp_t us_ts, lp_ts, us_diff1, us_diff2, lp_diff1, lp_diff2; + LowPowerTimeout lp_timeout; + + sleep_manager_lock_deep_sleep(); + volatile int wakeup_flag = 0; + lp_timeout.attach_us(mbed::callback(wakeup_callback, &wakeup_flag), SLEEP_DURATION_US); + us_ts = ticker_read_us(us_ticker); + lp_ts = ticker_read_us(lp_ticker); + + while (wakeup_flag == 0) { + sleep_manager_sleep_auto(); + } + us_diff1 = ticker_read_us(us_ticker) - us_ts; + lp_diff1 = ticker_read_us(lp_ticker) - lp_ts; + + // Deep sleep locked -- ordinary sleep mode used: + // * us_ticker powered ON, + // * lp_ticker powered ON, + // so both should increment equally. + + // Verify us and lp tickers incremented equally, with 10% tolerance. + TEST_ASSERT_UINT64_WITHIN_MESSAGE( + SLEEP_DURATION_US / 10ULL, lp_diff1, us_diff1, + "Deep sleep mode locked, but still used"); + + sleep_manager_unlock_deep_sleep(); + TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep()); + + // Wait for hardware serial buffers to flush. + wait_ms(SERIAL_FLUSH_TIME_MS); + + wakeup_flag = 0; + lp_timeout.attach_us(mbed::callback(wakeup_callback, &wakeup_flag), SLEEP_DURATION_US); + us_ts = ticker_read_us(us_ticker); + lp_ts = ticker_read_us(lp_ticker); + + while (wakeup_flag == 0) { + sleep_manager_sleep_auto(); + } + us_diff2 = ticker_read_us(us_ticker) - us_ts; + lp_diff2 = ticker_read_us(lp_ticker) - lp_ts; + + // Deep sleep unlocked -- deep sleep mode used: + // * us_ticker powered OFF, + // * lp_ticker powered ON. + // The us_ticker increment represents only the code execution time + // and should be much shorter than both: + // 1. current lp_ticker increment, + // 2. previous us_ticker increment (locked sleep test above) + + // Verify that the current us_ticker increment: + // 1. is at most 10% of lp_ticker increment + // 2. is at most 10% of previous us_ticker increment. + TEST_ASSERT_MESSAGE(us_diff2 < lp_diff2 / 10ULL, "Deep sleep mode unlocked, but not used"); + TEST_ASSERT_MESSAGE(us_diff2 < us_diff1 / 10ULL, "Deep sleep mode unlocked, but not used"); +} +#endif + +utest::v1::status_t testsuite_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(10, "default_auto"); + // Suspend the RTOS kernel scheduler to prevent interference with duration of sleep. + osKernelSuspend(); +#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) + // Suspend the low power ticker wrapper to prevent interference with deep sleep lock. + lp_ticker_wrapper_suspend(); +#endif +#if DEVICE_LPTICKER && DEVICE_USTICKER + // Make sure HAL tickers are initialized. + us_ticker_init(); + lp_ticker_init(); +#endif + return utest::v1::greentea_test_setup_handler(number_of_cases); +} + +void testsuite_teardown(const size_t passed, const size_t failed, const utest::v1::failure_t failure) +{ +#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) + lp_ticker_wrapper_resume(); +#endif + osKernelResume(0); + utest::v1::greentea_test_teardown_handler(passed, failed, failure); } Case cases[] = { - Case("sleep manager - deep sleep counter", sleep_manager_deepsleep_counter_test, greentea_failure_handler), + Case("deep sleep lock/unlock", test_lock_unlock), + Case("deep sleep unbalanced unlock", test_lone_unlock), + Case("deep sleep locked USHRT_MAX times", test_lock_eq_ushrt_max), + Case("deep sleep locked more than USHRT_MAX times", test_lock_gt_ushrt_max), +#if DEVICE_LPTICKER && DEVICE_USTICKER + Case("sleep_auto calls sleep/deep sleep based on lock", test_sleep_auto), +#endif }; -Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); +Specification specification(testsuite_setup, cases, testsuite_teardown); int main() { - Harness::run(specification); + return !Harness::run(specification); } diff --git a/TESTS/mbed_hal/sleep_manager/sleep_manager_api_tests.h b/TESTS/mbed_hal/sleep_manager/sleep_manager_api_tests.h new file mode 100644 index 0000000000..c99754a084 --- /dev/null +++ b/TESTS/mbed_hal/sleep_manager/sleep_manager_api_tests.h @@ -0,0 +1,84 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 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. + */ + +/** + * @addtogroup hal_sleep_manager_tests + * @{ + */ + +#ifndef MBED_HAL_SLEEP_MANAGER_API_TESTS_H +#define MBED_HAL_SLEEP_MANAGER_API_TESTS_H + +#if DEVICE_SLEEP + +/** Test lock/unlock + * + * Given no prior calls to lock/unlock + * When the deep sleep status is checked + * Then the deep sleep is allowed + * + * When the lock function is called + * Then the deep sleep is not allowed + * + * When the unlock function is called + * Then the deep sleep is allowed again + */ +void test_lock_unlock(); + +/** Test an unbalanced unlock call + * + * Given the deep sleep has not been locked + * When the deep sleep mode is unlocked + * Then an mbed_error is raised + */ +void test_lone_unlock(); + +/** Test lock USHRT_MAX times + * + * Given a device with sleep mode support + * When deep sleep mode is locked USHRT_MAX times + * Then the deep sleep mode is locked + * + * When unlock is called repeatedly + * Then deep sleep mode stays locked until the number + * of unlock calls is equal to number of lock calls + */ +void test_lock_eq_ushrt_max(); + +/** Test lock more than USHRT_MAX times + * + * Given the deep sleep has already been locked USHRT_MAX times + * When the deep sleep mode is locked again + * Then an mbed_error is raised + */ +void test_lock_gt_ushrt_max(); + +/** Test sleep_auto calls sleep and deep sleep based on lock + * + * Given a device with sleep mode support + * When the deep sleep mode is locked + * Then sleep_auto uses sleep mode + * + * When the deep sleep mode is unlocked + * Then sleep_auto uses deep sleep mode + */ +void test_sleep_auto(); + +#endif + +#endif + +/** @}*/ diff --git a/hal/mbed_sleep_manager.c b/hal/mbed_sleep_manager.c index 03f678e826..e0ce00c216 100644 --- a/hal/mbed_sleep_manager.c +++ b/hal/mbed_sleep_manager.c @@ -163,6 +163,10 @@ void sleep_manager_lock_deep_sleep_internal(void) if (deep_sleep_lock == USHRT_MAX) { core_util_critical_section_exit(); MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_HAL, MBED_ERROR_CODE_OVERFLOW), "DeepSleepLock overflow (> USHRT_MAX)", deep_sleep_lock); + // When running sleep_manager tests, the mbed_error() is overridden + // and no longer calls mbed_halt_system(). Return to prevent + // execution of the following code. + return; } core_util_atomic_incr_u16(&deep_sleep_lock, 1); core_util_critical_section_exit(); @@ -174,6 +178,10 @@ void sleep_manager_unlock_deep_sleep_internal(void) if (deep_sleep_lock == 0) { core_util_critical_section_exit(); MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_HAL, MBED_ERROR_CODE_UNDERFLOW), "DeepSleepLock underflow (< 0)", deep_sleep_lock); + // When running sleep_manager tests, the mbed_error() is overridden + // and no longer calls mbed_halt_system(). Return to prevent + // execution of the following code. + return; } core_util_atomic_decr_u16(&deep_sleep_lock, 1); core_util_critical_section_exit();