mirror of https://github.com/ARMmbed/mbed-os.git
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"pull/7582/head
parent
fd4f47d18f
commit
f2c222be15
|
|
@ -16,46 +16,210 @@
|
||||||
#include "utest/utest.h"
|
#include "utest/utest.h"
|
||||||
#include "unity/unity.h"
|
#include "unity/unity.h"
|
||||||
#include "greentea-client/test_env.h"
|
#include "greentea-client/test_env.h"
|
||||||
|
#include <limits.h>
|
||||||
|
#include "mbed.h"
|
||||||
|
#include "mbed_lp_ticker_wrapper.h"
|
||||||
|
#include "sleep_manager_api_tests.h"
|
||||||
|
|
||||||
#if !DEVICE_SLEEP
|
#if !DEVICE_SLEEP
|
||||||
#error [NOT_SUPPORTED] test not supported
|
#error [NOT_SUPPORTED] test not supported
|
||||||
#endif
|
#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();
|
(void) error_status;
|
||||||
TEST_ASSERT_TRUE(deep_sleep_allowed);
|
(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();
|
sleep_manager_lock_deep_sleep();
|
||||||
deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
||||||
TEST_ASSERT_FALSE(deep_sleep_allowed);
|
|
||||||
|
|
||||||
sleep_manager_unlock_deep_sleep();
|
sleep_manager_unlock_deep_sleep();
|
||||||
deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||||
TEST_ASSERT_TRUE(deep_sleep_allowed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
uint32_t expected_err_count = num_test_errors + 1;
|
||||||
return STATUS_CONTINUE;
|
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");
|
uint32_t lock_count = 0;
|
||||||
return greentea_test_setup_handler(number_of_cases);
|
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 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()
|
int main()
|
||||||
{
|
{
|
||||||
Harness::run(specification);
|
return !Harness::run(specification);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
/** @}*/
|
||||||
|
|
@ -163,6 +163,10 @@ void sleep_manager_lock_deep_sleep_internal(void)
|
||||||
if (deep_sleep_lock == USHRT_MAX) {
|
if (deep_sleep_lock == USHRT_MAX) {
|
||||||
core_util_critical_section_exit();
|
core_util_critical_section_exit();
|
||||||
MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_HAL, MBED_ERROR_CODE_OVERFLOW), "DeepSleepLock overflow (> USHRT_MAX)", deep_sleep_lock);
|
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_atomic_incr_u16(&deep_sleep_lock, 1);
|
||||||
core_util_critical_section_exit();
|
core_util_critical_section_exit();
|
||||||
|
|
@ -174,6 +178,10 @@ void sleep_manager_unlock_deep_sleep_internal(void)
|
||||||
if (deep_sleep_lock == 0) {
|
if (deep_sleep_lock == 0) {
|
||||||
core_util_critical_section_exit();
|
core_util_critical_section_exit();
|
||||||
MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_HAL, MBED_ERROR_CODE_UNDERFLOW), "DeepSleepLock underflow (< 0)", deep_sleep_lock);
|
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_atomic_decr_u16(&deep_sleep_lock, 1);
|
||||||
core_util_critical_section_exit();
|
core_util_critical_section_exit();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue