diff --git a/TESTS/host_tests/sync_on_reset.py b/TESTS/host_tests/sync_on_reset.py new file mode 100644 index 0000000000..134f3c5335 --- /dev/null +++ b/TESTS/host_tests/sync_on_reset.py @@ -0,0 +1,72 @@ +""" +mbed SDK +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. +""" +import time +from mbed_host_tests import BaseHostTest + +DEFAULT_CYCLE_PERIOD = 4.0 + +MSG_VALUE_DUMMY = '0' +MSG_KEY_DEVICE_READY = 'ready' +MSG_KEY_START_CASE = 'start_case' +MSG_KEY_DEVICE_RESET = 'reset_on_case_teardown' +MSG_KEY_SYNC = '__sync' + + +class SyncOnReset(BaseHostTest): + """Host side test that handles device reset during case teardown. + + Given a device that performs a reset during a test case teardown. + When the device notifies the host about the reset. + Then the host: + * keeps track of the test case index of the current test suite, + * performs a dev-host handshake, + * advances the test suite to next test case. + + Note: + Developed for a watchdog test, so that it can be run on devices that + do not support watchdog timeout updates after the initial setup. + As a solution, after testing watchdog with one set of settings, the + device performs a reset and notifies the host with the test case number, + so that the test suite may be advanced once the device boots again. + """ + + def __init__(self): + super(SyncOnReset, self).__init__() + self.test_case_num = 0 + cycle_s = self.get_config_item('program_cycle_s') + self.program_cycle_s = cycle_s if cycle_s is not None else DEFAULT_CYCLE_PERIOD + + def setup(self): + self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready) + self.register_callback(MSG_KEY_DEVICE_RESET, self.cb_device_reset) + + def cb_device_ready(self, key, value, timestamp): + """Advance the device test suite to the next test case.""" + self.send_kv(MSG_KEY_START_CASE, self.test_case_num) + + def cb_device_reset(self, key, value, timestamp): + """Wait for the device to boot and perform a handshake. + + Additionally, keep track of the last test case number. + """ + try: + self.test_case_num = int(value) + except ValueError: + pass + self.test_case_num += 1 + time.sleep(self.program_cycle_s) + self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) diff --git a/TESTS/mbed_hal/watchdog/main.cpp b/TESTS/mbed_hal/watchdog/main.cpp new file mode 100644 index 0000000000..bbb2dd7fbb --- /dev/null +++ b/TESTS/mbed_hal/watchdog/main.cpp @@ -0,0 +1,219 @@ +/* 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. + */ +#if !DEVICE_WATCHDOG +#error [NOT_SUPPORTED] Watchdog not supported for this target +#endif + +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "hal/watchdog_api.h" +#include "watchdog_api_tests.h" + +/* This is platform specific and depends on the watchdog timer implementation, + * e.g. STM32F4 uses 32kHz internal RC oscillator to clock the IWDG, so + * when the prescaler divider is set to max value of 256 the resolution + * drops to 8 ms. + */ +#define WORST_TIMEOUT_RESOLUTION_MS 8UL + +#define TIMEOUT_DELTA_MS (WORST_TIMEOUT_RESOLUTION_MS) +#define WDG_TIMEOUT_MS 500UL + +#define MSG_VALUE_DUMMY "0" +#define MSG_VALUE_LEN 24 +#define MSG_KEY_LEN 24 + +#define MSG_KEY_DEVICE_READY "ready" +#define MSG_KEY_START_CASE "start_case" +#define MSG_KEY_DEVICE_RESET "reset_on_case_teardown" + +int CASE_INDEX_START; +int CASE_INDEX_CURRENT; + +using utest::v1::Case; +using utest::v1::Specification; +using utest::v1::Harness; + +const watchdog_config_t WDG_CONFIG_DEFAULT = { .timeout_ms = WDG_TIMEOUT_MS }; + +void test_max_timeout_is_valid() +{ + TEST_ASSERT(hal_watchdog_get_platform_features().max_timeout > 1UL); +} + +void test_restart_is_possible() +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + if (!features.disable_watchdog) { + TEST_IGNORE_MESSAGE("Disabling watchdog not supported for this platform"); + return; + } + TEST_ASSERT(features.update_config); +} + +void test_stop() +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + if (!features.disable_watchdog) { + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_NOT_SUPPORTED, hal_watchdog_stop()); + TEST_IGNORE_MESSAGE("Disabling watchdog not supported for this platform"); + return; + } + + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_stop()); + + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&WDG_CONFIG_DEFAULT)); + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_stop()); + // Make sure that a disabled watchdog does not reset the core. + wait_ms(WDG_TIMEOUT_MS + TIMEOUT_DELTA_MS); + + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_stop()); +} + +void test_update_config() +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + if (!features.update_config) { + TEST_IGNORE_MESSAGE("Updating watchdog config not supported for this platform"); + return; + } + + watchdog_config_t config = WDG_CONFIG_DEFAULT; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, config.timeout_ms, hal_watchdog_get_reload_value()); + + config.timeout_ms = features.max_timeout - 2 * WORST_TIMEOUT_RESOLUTION_MS; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, config.timeout_ms, hal_watchdog_get_reload_value()); + + config.timeout_ms = features.max_timeout; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, config.timeout_ms, hal_watchdog_get_reload_value()); +} + +utest::v1::status_t case_setup_sync_on_reset(const Case * const source, const size_t index_of_case) +{ + CASE_INDEX_CURRENT = index_of_case; + return utest::v1::greentea_case_setup_handler(source, index_of_case); +} + +utest::v1::status_t case_teardown_sync_on_reset(const Case * const source, const size_t passed, const size_t failed, + const utest::v1::failure_t failure) +{ + utest::v1::status_t status = utest::v1::greentea_case_teardown_handler(source, passed, failed, failure); + if (failed) { + /* Return immediately and skip the device reset, if the test case failed. + * Provided that the device won't be restarted by other means (i.e. watchdog timer), + * this should allow the test suite to finish in a defined manner + * and report failure to host. + * In case of watchdog reset during test suite teardown, the loss of serial + * connection is possible, so the host-test-runner may return 'TIMEOUT' + * instead of 'FAIL'. + */ + return status; + } + greentea_send_kv(MSG_KEY_DEVICE_RESET, CASE_INDEX_START + CASE_INDEX_CURRENT); + utest_printf("The device will now restart.\n"); + wait_ms(10); // Wait for the serial buffers to flush. + NVIC_SystemReset(); + return status; // Reset is instant so this line won't be reached. +} + +utest::v1::status_t case_teardown_wdg_stop_or_reset(const Case * const source, const size_t passed, const size_t failed, + const utest::v1::failure_t failure) +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + if (features.disable_watchdog) { + hal_watchdog_stop(); + return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure); + } + + return case_teardown_sync_on_reset(source, passed, failed, failure); +} + +template +void test_init() +{ + watchdog_config_t config = { timeout_ms }; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, timeout_ms, hal_watchdog_get_reload_value()); +} + +void test_init_max_timeout() +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + watchdog_config_t config = { .timeout_ms = features.max_timeout }; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, features.max_timeout, hal_watchdog_get_reload_value()); +} + +int testsuite_setup_sync_on_reset(const size_t number_of_cases) +{ + GREENTEA_SETUP(45, "sync_on_reset"); + utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases); + if (status != utest::v1::STATUS_CONTINUE) { + return status; + } + + char key[MSG_KEY_LEN + 1] = { }; + char value[MSG_VALUE_LEN + 1] = { }; + + greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY); + greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); + + if (strcmp(key, MSG_KEY_START_CASE) != 0) { + utest_printf("Invalid message key.\n"); + return utest::v1::STATUS_ABORT; + } + + char *tailptr = NULL; + CASE_INDEX_START = (int) strtol(value, &tailptr, 10); + if (*tailptr != '\0' || CASE_INDEX_START < 0) { + utest_printf("Invalid start case index received from host\n"); + return utest::v1::STATUS_ABORT; + } + + utest_printf("Starting with test case index %i of all %i defined test cases.\n", CASE_INDEX_START, number_of_cases); + return CASE_INDEX_START; +} + +Case cases[] = { + Case("Platform feature max_timeout is valid", test_max_timeout_is_valid), + Case("Stopped watchdog can be started again", test_restart_is_possible), + Case("Watchdog can be stopped", test_stop), + + Case("Update config with multiple init calls", + (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, + test_update_config, + (utest::v1::case_teardown_handler_t) case_teardown_wdg_stop_or_reset), + + // Do not set watchdog timeout shorter than 500 ms as it may cause the + // host-test-runner return 'TIMEOUT' instead of 'FAIL' / 'PASS' if watchdog + // performs reset during test suite teardown. + Case("Init, 500 ms", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, + test_init<500UL>, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset), + Case("Init, max_timeout", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, + test_init_max_timeout, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset), +}; + +Specification specification((utest::v1::test_setup_handler_t) testsuite_setup_sync_on_reset, cases); + +int main() +{ + // Harness will start with a test case index provided by host script. + return !Harness::run(specification); +} diff --git a/TESTS/mbed_hal/watchdog/watchdog_api_tests.h b/TESTS/mbed_hal/watchdog/watchdog_api_tests.h new file mode 100644 index 0000000000..82dfd5795d --- /dev/null +++ b/TESTS/mbed_hal/watchdog/watchdog_api_tests.h @@ -0,0 +1,101 @@ +/* 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. + */ + +/** + * @addtogroup hal_watchdog_tests + * @{ + */ + +#ifndef MBED_HAL_WATCHDOG_API_TESTS_H +#define MBED_HAL_WATCHDOG_API_TESTS_H + +#if DEVICE_WATCHDOG + +/** Test max_timeout is valid + * + * Given a device supporting watchdog HAL API + * When @a hal_watchdog_get_platform_features() is called + * Then max_timeout member of returned watchdog_features_t struct is greater than 1 + */ +void test_max_timeout_is_valid(); + +/** Test stopped watchdog can be started again + * + * Given a device supporting watchdog HAL API + * When the device supports the @a disable_watchdog feature + * Then the device also supports @a update_config feature + */ +void test_restart_is_possible(); + +/** Test watchdog can be stopped + * + * Given a device without a support for the @a disable_watchdog feature + * When @a hal_watchdog_stop() is called + * Then WATCHDOG_STATUS_NOT_SUPPORTED is returned + * + * Given a device supporting @a disable_watchdog feature + * When watchdog is not running and @a hal_watchdog_stop() is called + * Then WATCHDOG_STATUS_OK is returned + * When watchdog is running + * and @a hal_watchdog_stop() is called before timeout + * Then WATCHDOG_STATUS_OK is returned + * and watchdog does not reset the device + * When watchdog has already been stopped and @a hal_watchdog_stop() is called + * Then WATCHDOG_STATUS_OK is returned + */ +void test_stop(); + +/** Test update config with multiple init calls + * + * Given @a max_timeout value returned by @a hal_watchdog_get_platform_features() + * and @a hal_watchdog_init() called multiple times with same @a config + * When @a config.timeout_ms is set to WDG_CONFIG_DEFAULT + * Then return value of hal_watchdog_init() is @a WATCHDOG_STATUS_OK + * and @a hal_watchdog_get_reload_value() returns WDG_CONFIG_DEFAULT + * When @a config.timeout_ms is set to max_timeout-delta + * Then return value of hal_watchdog_init() is @a WATCHDOG_STATUS_OK + * and @a hal_watchdog_get_reload_value() returns max_timeout-delta + * When @a config.timeout_ms is set to max_timeout + * Then return value of hal_watchdog_init() is @a WATCHDOG_STATUS_OK + * and @a hal_watchdog_get_reload_value() returns max_timeout + */ +void test_update_config(); + +/** Test init with a valid config + * + * Given a device supporting watchdog HAL API + * When @a config.timeout_ms is set to value X within platform's timeout range + * Then return value of hal_watchdog_init() is @a WATCHDOG_STATUS_OK + * and @a hal_watchdog_get_reload_value() returns X + */ +template +void test_init(); + +/** Test init with a max_timeout + * + * Given @a max_timeout value returned by @a hal_watchdog_get_platform_features() + * When @a config.timeout_ms is set to max_timeout + * Then return value of hal_watchdog_init() is @a WATCHDOG_STATUS_OK + * and @a hal_watchdog_get_reload_value() returns max_timeout + */ +void test_init_max_timeout(); + +#endif + +#endif + +/** @}*/ +