From 9c6cd2b669f4a8ef10fb697afffe8b67f86644a3 Mon Sep 17 00:00:00 2001 From: Filip Jagodzinski Date: Sun, 30 Jun 2019 11:49:09 +0200 Subject: [PATCH] Test: Watchdog: Add new driver tests --- TESTS/mbed_drivers/watchdog/Watchdog_tests.h | 85 +++++ TESTS/mbed_drivers/watchdog/main.cpp | 267 +++++++++++++++ .../watchdog_reset/Watchdog_reset_tests.h | 69 ++++ TESTS/mbed_drivers/watchdog_reset/main.cpp | 321 ++++++++++++++++++ 4 files changed, 742 insertions(+) create mode 100644 TESTS/mbed_drivers/watchdog/Watchdog_tests.h create mode 100644 TESTS/mbed_drivers/watchdog/main.cpp create mode 100644 TESTS/mbed_drivers/watchdog_reset/Watchdog_reset_tests.h create mode 100644 TESTS/mbed_drivers/watchdog_reset/main.cpp diff --git a/TESTS/mbed_drivers/watchdog/Watchdog_tests.h b/TESTS/mbed_drivers/watchdog/Watchdog_tests.h new file mode 100644 index 0000000000..8a0575de34 --- /dev/null +++ b/TESTS/mbed_drivers/watchdog/Watchdog_tests.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018-2019 Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * 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_DRIVERS_WATCHDOG_TESTS_H +#define MBED_DRIVERS_WATCHDOG_TESTS_H + +#if DEVICE_WATCHDOG + +/** Test Watchdog max_timeout validity + * + * Given a device supporting Watchdog driver API, + * when @a Watchdog::get_max_timeout() is called, + * then the returned value is greater than 1. + */ +void test_max_timeout_is_valid(); + +/** Test Watchdog stop + * + * Given a device without a support for the @a disable_watchdog feature, + * when @a Watchdog::stop() is called, + * then false is returned. + * + * Otherwise, given the device with @a disable_watchdog feature support: + * + * Given the Watchdog is *NOT* running, + * when @a Watchdog::stop() is called, + * then false is returned. + * + * Given the Watchdog is running, + * when @a Watchdog::stop() is called before the timeout expires, + * then true is returned and the device is not restarted. + * + * Given the Watchdog is *NOT* running (it has already been stopped), + * when @a Watchdog::stop() is called, + * then false is returned. + */ +void test_stop(); + +/** Test Watchdog start multiple times + * + * Given a set of unique timeout values, + * when Watchdog::start(T) is called for each value T, + * then, for every T, Watchdog::start() returns true + * and Watchdog::get_timeout() returns an actual timeout value R + * and T <= R < 2 * T. + */ +void test_restart(); + +/** Test Watchdog start with a valid config + * + * Given a value of T ms which is within supported Watchdog timeout range, + * when Watchdog::start(T) is called, + * then true is returned + * and Watchdog::get_timeout() returns an actual timeout value R + * and T <= R < 2 * T. + */ +template +void test_start(); + +/** Test Watchdog start with max_timeout + * + * Given max_timeout value returned by @a Watchdog::get_max_timeout(), + * when @a Watchdog::start(max_timeout) is called, + * then true is returned + * and @a Watchdog::get_timeout() returns max_timeout. + */ +void test_start_max_timeout(); + +#endif + +#endif diff --git a/TESTS/mbed_drivers/watchdog/main.cpp b/TESTS/mbed_drivers/watchdog/main.cpp new file mode 100644 index 0000000000..b95d8d8d76 --- /dev/null +++ b/TESTS/mbed_drivers/watchdog/main.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2018-2019 Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "unity/unity.h" +#include "utest/utest.h" +#include "mbed.h" +#include "drivers/Watchdog.h" +#include "hal/watchdog_api.h" +#include "Watchdog_tests.h" +#include + +/* The shortest timeout value, this test suite is able to handle correctly. */ +#define WDG_MIN_TIMEOUT_MS 50UL + +// Do not set watchdog timeout shorter than WDG_MIN_TIMEOUT_MS, as it may +// cause the host-test-runner return 'TIMEOUT' instead of 'FAIL' / 'PASS' +// if watchdog performs reset during test suite teardown. +#define WDG_TIMEOUT_MS 100UL + +#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" + +/* To prevent a loss of Greentea data, the serial buffers have to be flushed + * before the UART peripheral shutdown. The UART shutdown happens when the + * device is entering the deepsleep mode or performing a reset. + * + * With the current API, it is not possible to check if the hardware buffers + * are empty. However, it is possible to determine the time required for the + * buffers to flush. + * + * Take NUMAKER_PFM_NUC472 as an example: + * The UART peripheral has 16-byte Tx FIFO. With a baud rate set to 9600, + * flushing the Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 ms. + * To be on the safe side, set the wait time to 20 ms. + */ +#define SERIAL_FLUSH_TIME_MS 20 + +int CASE_INDEX_START; +int CASE_INDEX_CURRENT; + +using utest::v1::Case; +using utest::v1::Specification; +using utest::v1::Harness; + +using namespace mbed; + +Thread wdg_kicking_thread; +Semaphore kick_wdg_during_test_teardown(0, 1); + +void wdg_kicking_thread_fun() +{ + kick_wdg_during_test_teardown.wait(); + while (true) { + hal_watchdog_kick(); + wait_ms(20); + } +} + +void test_max_timeout_is_valid() +{ + Watchdog& watchdog = Watchdog::get_instance(); + TEST_ASSERT(watchdog.get_max_timeout() > 1UL); +} + +void test_stop() +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + Watchdog& watchdog = Watchdog::get_instance(); + if (!features.disable_watchdog) { + TEST_ASSERT_FALSE(watchdog.stop()); + TEST_IGNORE_MESSAGE("Disabling watchdog not supported for this platform"); + return; + } + + TEST_ASSERT_FALSE(watchdog.is_running()); + TEST_ASSERT_FALSE(watchdog.stop()); + TEST_ASSERT_FALSE(watchdog.is_running()); + + TEST_ASSERT_TRUE(watchdog.start(WDG_TIMEOUT_MS)); + TEST_ASSERT_TRUE(watchdog.is_running()); + TEST_ASSERT_TRUE(watchdog.stop()); + TEST_ASSERT_FALSE(watchdog.is_running()); + // Make sure that a disabled watchdog does not reset the core. + wait_ms(2 * WDG_TIMEOUT_MS); // Watchdog should fire before twice the timeout value. + + TEST_ASSERT_FALSE(watchdog.stop()); +} + + +void test_restart() +{ + 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& watchdog = Watchdog::get_instance(); + uint32_t max_timeout = watchdog.get_max_timeout(); + uint32_t timeouts[] = { + max_timeout / 4, + max_timeout / 8, + max_timeout / 16 + }; + int num_timeouts = sizeof timeouts / sizeof timeouts[0]; + + for (size_t i = 0; i < num_timeouts; i++) { + if (timeouts[i] < WDG_MIN_TIMEOUT_MS) { + TEST_IGNORE_MESSAGE("Requested timeout value is too short -- ignoring test case."); + return; + } + + TEST_ASSERT_TRUE(watchdog.start(timeouts[i])); + TEST_ASSERT_TRUE(watchdog.is_running()); + uint32_t actual_timeout = watchdog.get_timeout(); + TEST_ASSERT_TRUE(watchdog.stop()); + TEST_ASSERT_FALSE(watchdog.is_running()); + // The watchdog should trigger at, or after the timeout value. + TEST_ASSERT(actual_timeout >= timeouts[i]); + // The watchdog should trigger before twice the timeout value. + TEST_ASSERT(actual_timeout < 2 * timeouts[i]); + } +} + +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) +{ + // Unlock kicking the watchdog during teardown. + kick_wdg_during_test_teardown.release(); + 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(SERIAL_FLUSH_TIME_MS); // 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_start() +{ + if (timeout_ms < WDG_MIN_TIMEOUT_MS) { + TEST_IGNORE_MESSAGE("Requested timeout value is too short -- ignoring test case."); + return; + } + Watchdog& watchdog = Watchdog::get_instance(); + TEST_ASSERT_TRUE(watchdog.start(timeout_ms)); + uint32_t actual_timeout = watchdog.get_timeout(); + // The watchdog should trigger at, or after the timeout value. + TEST_ASSERT(actual_timeout >= timeout_ms); + // The watchdog should trigger before twice the timeout value. + TEST_ASSERT(actual_timeout < 2 * timeout_ms); +} + +void test_start_max_timeout() +{ + Watchdog& watchdog = Watchdog::get_instance(); + uint32_t max_timeout = watchdog.get_max_timeout(); + TEST_ASSERT_TRUE(watchdog.start(max_timeout)); + // The watchdog should trigger at, or after the timeout value. + TEST_ASSERT(watchdog.get_timeout() >= max_timeout); +} + +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; + } + + // The thread is started here, but feeding the watchdog will start + // when the semaphore is released during a test case teardown. + wdg_kicking_thread.start(mbed::callback(wdg_kicking_thread_fun)); + + 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("max_timeout is valid", test_max_timeout_is_valid), + Case("Stop", test_stop), + Case("Restart multiple times", + (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, + test_restart, (utest::v1::case_teardown_handler_t) case_teardown_wdg_stop_or_reset), + Case("Start, 100 ms", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, + test_start<100UL>, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset), + Case("Start, max_timeout", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, + test_start_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_drivers/watchdog_reset/Watchdog_reset_tests.h b/TESTS/mbed_drivers/watchdog_reset/Watchdog_reset_tests.h new file mode 100644 index 0000000000..739777ec06 --- /dev/null +++ b/TESTS/mbed_drivers/watchdog_reset/Watchdog_reset_tests.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-2019 Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * 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_DRIVERS_WATCHDOG_RESET_TESTS_H +#define MBED_DRIVERS_WATCHDOG_RESET_TESTS_H + +#if DEVICE_WATCHDOG + +/** Test Watchdog reset + * + * Given a device with a Watchdog started, + * when a Watchdog timeout expires, + * then the device is restarted. + */ +void test_simple_reset(); + +/** Test Watchdog reset in sleep mode + * + * Given a device with a Watchdog started, + * when the Watchdog timeout expires while the device is in sleep mode, + * then the device is restarted. + */ +void test_sleep_reset(); + +/** Test Watchdog reset in deepsleep mode + * + * Given a device with a Watchdog started, + * when the Watchdog timeout expires while the device is in deepsleep mode, + * then the device is restarted. + */ +void test_deepsleep_reset(); + +/** Test Watchdog reset after Watchdog restart + * + * Given a device with a Watchdog started, + * when the Watchdog is stopped before its timeout expires, + * then the device is not restarted. + * When the Watchdog is started again and its timeout expires, + * then the device is restarted. + */ +void test_restart_reset(); + +/** Test Watchdog kick + * + * Given a device with a Watchdog started, + * when the Watchdog is kicked before its timeout expires, + * then the device restart is prevented. + * When the Watchdog is *NOT* kicked again before next timeout expires, + * then the device is restarted. + */ +void test_kick_reset(); + +#endif + +#endif diff --git a/TESTS/mbed_drivers/watchdog_reset/main.cpp b/TESTS/mbed_drivers/watchdog_reset/main.cpp new file mode 100644 index 0000000000..37885dbb45 --- /dev/null +++ b/TESTS/mbed_drivers/watchdog_reset/main.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2018-2019 Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "drivers/Watchdog.h" +#include "Watchdog_reset_tests.h" +#include "mbed.h" + +#define TIMEOUT_MS 100UL +#define KICK_ADVANCE_MS 10UL + +#define MSG_VALUE_DUMMY "0" +#define CASE_DATA_INVALID 0xffffffffUL +#define CASE_DATA_PHASE2_OK 0xfffffffeUL + +#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 "dev_reset" + +/* To prevent a loss of Greentea data, the serial buffers have to be flushed + * before the UART peripheral shutdown. The UART shutdown happens when the + * device is entering the deepsleep mode or performing a reset. + * + * With the current API, it is not possible to check if the hardware buffers + * are empty. However, it is possible to determine the time required for the + * buffers to flush. + * + * Take NUMAKER_PFM_NUC472 as an example: + * The UART peripheral has 16-byte Tx FIFO. With a baud rate set to 9600, + * flushing the Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 ms. + * To be on the safe side, set the wait time to 20 ms. + */ +#define SERIAL_FLUSH_TIME_MS 20 + +using utest::v1::Case; +using utest::v1::Specification; +using utest::v1::Harness; + +using namespace mbed; + +struct testcase_data { + int index; + int start_index; + uint32_t received_data; +}; + +void release_sem(Semaphore *sem) +{ + sem->release(); +} + +testcase_data current_case; + +bool send_reset_notification(testcase_data *tcdata, uint32_t delay_ms) +{ + char msg_value[12]; + int str_len = snprintf(msg_value, sizeof msg_value, "%02x,%08lx", tcdata->start_index + tcdata->index, delay_ms); + if (str_len != (sizeof msg_value) - 1) { + utest_printf("Failed to compose a value string to be sent to host."); + return false; + } + greentea_send_kv(MSG_KEY_DEVICE_RESET, msg_value); + return true; +} + +void test_simple_reset() +{ + // Phase 2. -- verify the test results. + // Verify if this test case passed based on data received from host. + if (current_case.received_data != CASE_DATA_INVALID) { + TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data); + current_case.received_data = CASE_DATA_INVALID; + return; + } + + // Phase 1. -- run the test code. + // Init the watchdog and wait for a device reset. + if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) { + TEST_ASSERT_MESSAGE(0, "Dev-host communication error."); + return; + } + Watchdog& watchdog = Watchdog::get_instance(); + TEST_ASSERT_FALSE(watchdog.is_running()); + TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS)); + TEST_ASSERT_TRUE(watchdog.is_running()); + // Watchdog should fire before twice the timeout value. + wait_ms(2 * TIMEOUT_MS); // Device reset expected. + + // Watchdog reset should have occurred during wait_ms() above; + + watchdog.kick(); // Just to buy some time for testsuite failure handling. + TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); +} + +#if DEVICE_SLEEP +void test_sleep_reset() +{ + // Phase 2. -- verify the test results. + if (current_case.received_data != CASE_DATA_INVALID) { + TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data); + current_case.received_data = CASE_DATA_INVALID; + return; + } + + // Phase 1. -- run the test code. + Semaphore sem(0, 1); + Timeout timeout; + if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) { + TEST_ASSERT_MESSAGE(0, "Dev-host communication error."); + return; + } + Watchdog& watchdog = Watchdog::get_instance(); + TEST_ASSERT_FALSE(watchdog.is_running()); + TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS)); + TEST_ASSERT_TRUE(watchdog.is_running()); + sleep_manager_lock_deep_sleep(); + // Watchdog should fire before twice the timeout value. + timeout.attach_us(mbed::callback(release_sem, &sem), 1000ULL * (2 * TIMEOUT_MS)); + if (sleep_manager_can_deep_sleep()) { + TEST_ASSERT_MESSAGE(0, "Deepsleep should be disallowed."); + return; + } + sem.wait(); // Device reset expected. + sleep_manager_unlock_deep_sleep(); + + // Watchdog reset should have occurred during sem.wait() (sleep) above; + + watchdog.kick(); // Just to buy some time for testsuite failure handling. + TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); +} + +#if DEVICE_LOWPOWERTIMER +void test_deepsleep_reset() +{ + // Phase 2. -- verify the test results. + if (current_case.received_data != CASE_DATA_INVALID) { + TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data); + current_case.received_data = CASE_DATA_INVALID; + return; + } + + // Phase 1. -- run the test code. + Semaphore sem(0, 1); + LowPowerTimeout lp_timeout; + if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) { + TEST_ASSERT_MESSAGE(0, "Dev-host communication error."); + return; + } + Watchdog& watchdog = Watchdog::get_instance(); + TEST_ASSERT_FALSE(watchdog.is_running()); + TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS)); + TEST_ASSERT_TRUE(watchdog.is_running()); + // Watchdog should fire before twice the timeout value. + lp_timeout.attach_us(mbed::callback(release_sem, &sem), 1000ULL * (2 * TIMEOUT_MS)); + wait_ms(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush. + if (!sleep_manager_can_deep_sleep()) { + TEST_ASSERT_MESSAGE(0, "Deepsleep should be allowed."); + } + sem.wait(); // Device reset expected. + + // Watchdog reset should have occurred during sem.wait() (deepsleep) above; + + watchdog.kick(); // Just to buy some time for testsuite failure handling. + TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); +} +#endif +#endif + +void test_restart_reset() +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + if (!features.disable_watchdog) { + TEST_IGNORE_MESSAGE("Disabling Watchdog not supported for this platform"); + return; + } + + // Phase 2. -- verify the test results. + if (current_case.received_data != CASE_DATA_INVALID) { + TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data); + current_case.received_data = CASE_DATA_INVALID; + return; + } + + // Phase 1. -- run the test code. + Watchdog& watchdog = Watchdog::get_instance(); + TEST_ASSERT_FALSE(watchdog.is_running()); + TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS)); + TEST_ASSERT_TRUE(watchdog.is_running()); + wait_ms(TIMEOUT_MS / 2UL); + TEST_ASSERT_TRUE(watchdog.stop()); + TEST_ASSERT_FALSE(watchdog.is_running()); + // Check that stopping the Watchdog prevents a device reset. + // The watchdog should trigger at, or after the timeout value. + // The watchdog should trigger before twice the timeout value. + wait_ms(TIMEOUT_MS / 2UL + TIMEOUT_MS); + + if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) { + TEST_ASSERT_MESSAGE(0, "Dev-host communication error."); + return; + } + TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS)); + TEST_ASSERT_TRUE(watchdog.is_running()); + // Watchdog should fire before twice the timeout value. + wait_ms(2 * TIMEOUT_MS); // Device reset expected. + + // Watchdog reset should have occurred during that wait() above; + + watchdog.kick(); // Just to buy some time for testsuite failure handling. + TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); +} + +void test_kick_reset() +{ + // Phase 2. -- verify the test results. + if (current_case.received_data != CASE_DATA_INVALID) { + TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data); + current_case.received_data = CASE_DATA_INVALID; + return; + } + + // Phase 1. -- run the test code. + Watchdog& watchdog = Watchdog::get_instance(); + TEST_ASSERT_FALSE(watchdog.is_running()); + TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS)); + TEST_ASSERT_TRUE(watchdog.is_running()); + for (int i = 3; i; i--) { + // The reset is prevented as long as the watchdog is kicked + // anytime before the timeout. + wait_ms(TIMEOUT_MS - KICK_ADVANCE_MS); + watchdog.kick(); + } + if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) { + TEST_ASSERT_MESSAGE(0, "Dev-host communication error."); + return; + } + // Watchdog should fire before twice the timeout value. + wait_ms(2 * TIMEOUT_MS); // Device reset expected. + + // Watchdog reset should have occurred during that wait() above; + + watchdog.kick(); // Just to buy some time for testsuite failure handling. + TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); +} + +utest::v1::status_t case_setup(const Case *const source, const size_t index_of_case) +{ + current_case.index = index_of_case; + return utest::v1::greentea_case_setup_handler(source, index_of_case); +} + +int testsuite_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(90, "watchdog_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; + } + + int num_args = sscanf(value, "%02x,%08lx", &(current_case.start_index), &(current_case.received_data)); + if (num_args == 0 || num_args == EOF) { + utest_printf("Invalid data received from host\n"); + return utest::v1::STATUS_ABORT; + } + + utest_printf("This test suite is composed of %i test cases. Starting at index %i.\n", number_of_cases, + current_case.start_index); + return current_case.start_index; +} + +Case cases[] = { + Case("Watchdog reset", case_setup, test_simple_reset), +#if DEVICE_SLEEP + Case("Watchdog reset in sleep mode", case_setup, test_sleep_reset), +#if DEVICE_LOWPOWERTIMER + Case("Watchdog reset in deepsleep mode", case_setup, test_deepsleep_reset), +#endif +#endif + Case("Watchdog started again", case_setup, test_restart_reset), + Case("Kicking the Watchdog prevents reset", case_setup, test_kick_reset), +}; + +Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases); + +int main() +{ + // Harness will start with a test case index provided by host script. + return !Harness::run(specification); +}