diff --git a/frameworks/unity.lib b/frameworks/unity.lib new file mode 100644 index 0000000000..bd2fffb638 --- /dev/null +++ b/frameworks/unity.lib @@ -0,0 +1 @@ +https://github.com/ARMmbed/unity.git#3b2fef7fcf60abcc diff --git a/frameworks/utest/mbed-utest-shim.cpp b/frameworks/utest/mbed-utest-shim.cpp new file mode 100644 index 0000000000..db664882e8 --- /dev/null +++ b/frameworks/utest/mbed-utest-shim.cpp @@ -0,0 +1,32 @@ + +/* mbed Microcontroller Library + * Copyright (c) 2013-2016 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. + */ + +#include "mbed.h" +#include "utest/utest.h" + +using namespace utest::v1; + +void utest_v1_enter_critical_section(void) +{ + // will replaced by CriticalSectionLock + __disable_irq(); +} + +void utest_v1_leave_critical_section(void) +{ + __enable_irq(); +} diff --git a/frameworks/utest/source/shim.cpp b/frameworks/utest/source/shim.cpp new file mode 100644 index 0000000000..4065a16516 --- /dev/null +++ b/frameworks/utest/source/shim.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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. + **************************************************************************** + */ + +#include "utest/shim.h" + +#if UTEST_SHIM_SCHEDULER_USE_MINAR +#include "minar/minar.h" + +static int32_t utest_minar_init() +{ + return 0; +} +static void *utest_minar_post(const utest_v1_harness_callback_t callback, const uint32_t delay_ms) +{ + void *handle = minar::Scheduler::postCallback(callback).delay(minar::milliseconds(delay_ms)).getHandle(); + return handle; +} +static int32_t utest_minar_cancel(void *handle) +{ + int32_t ret = minar::Scheduler::cancelCallback(handle); + return ret; +} +static int32_t utest_minar_run() +{ + return 0; +} +extern "C" { +static const utest_v1_scheduler_t utest_v1_scheduler = +{ + utest_minar_init, + utest_minar_post, + utest_minar_cancel, + utest_minar_run +}; +utest_v1_scheduler_t utest_v1_get_scheduler() +{ + return utest_v1_scheduler; +} +} + +#elif UTEST_SHIM_SCHEDULER_USE_US_TICKER +#ifdef YOTTA_MBED_HAL_VERSION_STRING +# include "mbed-hal/us_ticker_api.h" +#else +# include "us_ticker_api.h" +#endif +// only one callback is active at any given time +static volatile utest_v1_harness_callback_t minimal_callback; +static volatile utest_v1_harness_callback_t ticker_callback; +static const ticker_data_t *ticker_data; +static ticker_event_t ticker_event; + +static void ticker_handler(uint32_t) +{ + // printf("\t\t>>> Ticker callback fired for %p.\n", ticker_callback); + minimal_callback = ticker_callback; +} + +static int32_t utest_us_ticker_init() +{ + ticker_data = get_us_ticker_data(); + ticker_set_handler(ticker_data, ticker_handler); + return 0; +} +static void *utest_us_ticker_post(const utest_v1_harness_callback_t callback, const uint32_t delay_ms) +{ + // printf("\t\t>>> Schedule %p with %ums delay => %p.\n", callback, (unsigned int)delay_ms, (void*)1); + if (delay_ms) { + ticker_callback = callback; + // fire the interrupt in 1000us * delay_ms + ticker_insert_event(ticker_data, &ticker_event, ticker_read(ticker_data) + delay_ms * 1000, 0); + } else { + minimal_callback = callback; + } + + // return a bogus handle + return (void*)1; +} +static int32_t utest_us_ticker_cancel(void *handle) +{ + // printf("\t\t>>> Cancel %p => %u\n", handle, (unsigned int)0); + (void) handle; + ticker_remove_event(ticker_data, &ticker_event); + return 0; +} +static int32_t utest_us_ticker_run() +{ + while(1) + { + // check if a new callback has been set + if (minimal_callback) + { + // printf("\t\t>>> Firing callback %p\n", minimal_callback); + // copy the callback + utest_v1_harness_callback_t callback = minimal_callback; + // reset the shared callback + minimal_callback = NULL; + // execute the copied callback + callback(); + } + } + return 0; +} +extern "C" { +static const utest_v1_scheduler_t utest_v1_scheduler = +{ + utest_us_ticker_init, + utest_us_ticker_post, + utest_us_ticker_cancel, + utest_us_ticker_run +}; +utest_v1_scheduler_t utest_v1_get_scheduler() +{ + return utest_v1_scheduler; +} +} +#endif + +#ifdef YOTTA_CORE_UTIL_VERSION_STRING +// their functionality is implemented using the CriticalSectionLock class +void utest_v1_enter_critical_section(void) {} +void utest_v1_leave_critical_section(void) {} +#endif diff --git "a/frameworks\\utest/source/case.cpp" "b/frameworks\\utest/source/case.cpp" new file mode 100644 index 0000000000..57395abd4a --- /dev/null +++ "b/frameworks\\utest/source/case.cpp" @@ -0,0 +1,151 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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. + **************************************************************************** + */ + + #include "utest/case.h" + +using namespace utest::v1; + +// normal handler +Case::Case(const char *description, + const case_setup_handler_t setup_handler, + const case_handler_t handler, + const case_teardown_handler_t teardown_handler, + const case_failure_handler_t failure_handler) : + description(description), + handler(handler), + control_handler(ignore_handler), + repeat_count_handler(ignore_handler), + setup_handler(setup_handler), + teardown_handler(teardown_handler), + failure_handler(failure_handler) +{} + +Case::Case(const char *description, + const case_handler_t handler, + const case_teardown_handler_t teardown_handler, + const case_failure_handler_t failure_handler) : + description(description), + handler(handler), + control_handler(ignore_handler), + repeat_count_handler(ignore_handler), + setup_handler(default_handler), + teardown_handler(teardown_handler), + failure_handler(failure_handler) +{} + +Case::Case(const char *description, + const case_handler_t handler, + const case_failure_handler_t failure_handler) : + description(description), + handler(handler), + control_handler(ignore_handler), + repeat_count_handler(ignore_handler), + setup_handler(default_handler), + teardown_handler(default_handler), + failure_handler(failure_handler) +{} + +// control handler +Case::Case(const char *description, + const case_setup_handler_t setup_handler, + const case_control_handler_t handler, + const case_teardown_handler_t teardown_handler, + const case_failure_handler_t failure_handler) : + description(description), + handler(ignore_handler), + control_handler(handler), + repeat_count_handler(ignore_handler), + setup_handler(setup_handler), + teardown_handler(teardown_handler), + failure_handler(failure_handler) +{} + +Case::Case(const char *description, + const case_control_handler_t handler, + const case_teardown_handler_t teardown_handler, + const case_failure_handler_t failure_handler) : + description(description), + handler(ignore_handler), + control_handler(handler), + repeat_count_handler(ignore_handler), + setup_handler(default_handler), + teardown_handler(teardown_handler), + failure_handler(failure_handler) +{} + +Case::Case(const char *description, + const case_control_handler_t handler, + const case_failure_handler_t failure_handler) : + description(description), + handler(ignore_handler), + control_handler(handler), + repeat_count_handler(ignore_handler), + setup_handler(default_handler), + teardown_handler(default_handler), + failure_handler(failure_handler) +{} + +// control flow handler +Case::Case(const char *description, + const case_setup_handler_t setup_handler, + const case_call_count_handler_t case_repeat_count_handler, + const case_teardown_handler_t teardown_handler, + const case_failure_handler_t failure_handler) : + description(description), + handler(ignore_handler), + control_handler(ignore_handler), + repeat_count_handler(case_repeat_count_handler), + setup_handler(setup_handler), + teardown_handler(teardown_handler), + failure_handler(failure_handler) +{} + +Case::Case(const char *description, + const case_call_count_handler_t case_repeat_count_handler, + const case_failure_handler_t failure_handler) : + description(description), + handler(ignore_handler), + control_handler(ignore_handler), + repeat_count_handler(case_repeat_count_handler), + setup_handler(default_handler), + teardown_handler(default_handler), + failure_handler(failure_handler) +{} + +Case::Case(const char *description, + const case_call_count_handler_t case_repeat_count_handler, + const case_teardown_handler_t teardown_handler, + const case_failure_handler_t failure_handler) : + description(description), + handler(ignore_handler), + control_handler(ignore_handler), + repeat_count_handler(case_repeat_count_handler), + setup_handler(default_handler), + teardown_handler(teardown_handler), + failure_handler(failure_handler) +{} + +const char* +Case::get_description() const { + return description; +} + +bool +Case::is_empty() const { + return !(handler || control_handler || repeat_count_handler || setup_handler || teardown_handler); +} diff --git "a/frameworks\\utest/source/default_handlers.cpp" "b/frameworks\\utest/source/default_handlers.cpp" new file mode 100644 index 0000000000..44c0ffae42 --- /dev/null +++ "b/frameworks\\utest/source/default_handlers.cpp" @@ -0,0 +1,94 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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. + **************************************************************************** + */ + +#include "utest/default_handlers.h" +#include "utest/case.h" + +using namespace utest::v1; + +static void test_failure_handler(const failure_t failure); + +const handlers_t utest::v1::verbose_continue_handlers = { + verbose_test_setup_handler, + verbose_test_teardown_handler, + test_failure_handler, + verbose_case_setup_handler, + verbose_case_teardown_handler, + verbose_case_failure_handler +}; + + +// --- SPECIAL HANDLERS --- +static void test_failure_handler(const failure_t failure) { + if (failure.location == LOCATION_TEST_SETUP || failure.location == LOCATION_TEST_TEARDOWN) { + verbose_test_failure_handler(failure); + printf("{{failure}}\n{{end}}\n"); + while(1) ; + } +} + +// --- VERBOSE TEST HANDLERS --- +status_t utest::v1::verbose_test_setup_handler(const size_t number_of_cases) +{ + printf(">>> Running %u test cases...\n", number_of_cases); + return STATUS_CONTINUE; +} + +void utest::v1::verbose_test_teardown_handler(const size_t passed, const size_t failed, const failure_t failure) +{ + printf("\n>>> Test cases: %u passed, %u failed", passed, failed); + if (failure.reason == REASON_NONE) { + printf("\n"); + } else { + printf(" with reason '%s'\n", stringify(failure.reason)); + } + if (failed) printf(">>> TESTS FAILED!\n"); +} + +void utest::v1::verbose_test_failure_handler(const failure_t failure) +{ + printf(">>> failure with reason '%s' during '%s'\n", stringify(failure.reason), stringify(failure.location)); +} + +// --- VERBOSE CASE HANDLERS --- +status_t utest::v1::verbose_case_setup_handler(const Case *const source, const size_t index_of_case) +{ + printf("\n>>> Running case #%u: '%s'...\n", index_of_case + 1, source->get_description()); + return STATUS_CONTINUE; +} + +status_t utest::v1::verbose_case_teardown_handler(const Case *const source, const size_t passed, const size_t failed, const failure_t failure) +{ + printf(">>> '%s': %u passed, %u failed", source->get_description(), passed, failed); + if (failure.reason == REASON_NONE) { + printf("\n"); + } else { + printf(" with reason '%s'\n", stringify(failure.reason)); + } + return STATUS_CONTINUE; +} + +status_t utest::v1::verbose_case_failure_handler(const Case *const /*source*/, const failure_t failure) +{ + if (!(failure.reason & REASON_ASSERTION)) { + verbose_test_failure_handler(failure); + } + if (failure.reason & (REASON_TEST_TEARDOWN | REASON_CASE_TEARDOWN)) return STATUS_ABORT; + if (failure.reason & REASON_IGNORE) return STATUS_IGNORE; + return STATUS_CONTINUE; +} diff --git "a/frameworks\\utest/source/greentea_handlers.cpp" "b/frameworks\\utest/source/greentea_handlers.cpp" new file mode 100644 index 0000000000..e4760287e0 --- /dev/null +++ "b/frameworks\\utest/source/greentea_handlers.cpp" @@ -0,0 +1,127 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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. + **************************************************************************** + */ + +#include "utest/default_handlers.h" +#include "utest/case.h" +#include "greentea-client/test_env.h" + +using namespace utest::v1; + +static status_t unknown_test_setup_handler(const size_t); +static void selftest_failure_handler(const failure_t); +static void test_failure_handler(const failure_t); + + +const handlers_t utest::v1::greentea_abort_handlers = { + unknown_test_setup_handler, + greentea_test_teardown_handler, + test_failure_handler, + greentea_case_setup_handler, + greentea_case_teardown_handler, + greentea_case_failure_abort_handler +}; + +const handlers_t utest::v1::greentea_continue_handlers = { + unknown_test_setup_handler, + greentea_test_teardown_handler, + test_failure_handler, + greentea_case_setup_handler, + greentea_case_teardown_handler, + greentea_case_failure_continue_handler +}; + +const handlers_t utest::v1::selftest_handlers = { + unknown_test_setup_handler, + greentea_test_teardown_handler, + selftest_failure_handler, + greentea_case_setup_handler, + greentea_case_teardown_handler, + greentea_case_failure_continue_handler +}; + + +// --- SPECIAL HANDLERS --- +static status_t unknown_test_setup_handler(const size_t) { + printf(">>> I do not know how to tell greentea that the test started, since\n"); + printf(">>> you forgot to override the `test_setup_handler` in your specification.\n"); + + return STATUS_ABORT; +} + +static void selftest_failure_handler(const failure_t failure) { + if (failure.location == LOCATION_TEST_SETUP || failure.location == LOCATION_TEST_TEARDOWN || failure.reason == REASON_ASSERTION) { + verbose_test_failure_handler(failure); + } + if (failure.reason == REASON_ASSERTION) { + GREENTEA_TESTSUITE_RESULT(false); + while(1) ; + } +} + +static void test_failure_handler(const failure_t failure) { + if (failure.location == LOCATION_TEST_SETUP || failure.location == LOCATION_TEST_TEARDOWN) { + verbose_test_failure_handler(failure); + GREENTEA_TESTSUITE_RESULT(false); + while(1) ; + } +} + +// --- GREENTEA HANDLERS --- +status_t utest::v1::greentea_test_setup_handler(const size_t number_of_cases) +{ + greentea_send_kv(TEST_ENV_TESTCASE_COUNT, number_of_cases); + return verbose_test_setup_handler(number_of_cases); +} + +void utest::v1::greentea_test_teardown_handler(const size_t passed, const size_t failed, const failure_t failure) +{ + verbose_test_teardown_handler(passed, failed, failure); + greentea_send_kv(TEST_ENV_TESTCASE_SUMMARY, passed, failed); + int result = !(failed || (failure.reason && !(failure.reason & REASON_IGNORE))); + GREENTEA_TESTSUITE_RESULT(result); +} + +void utest::v1::greentea_test_failure_handler(const failure_t failure) +{ + verbose_test_failure_handler(failure); +} + +// --- GREENTEA CASE HANDLERS --- +status_t utest::v1::greentea_case_setup_handler(const Case *const source, const size_t index_of_case) +{ + status_t status = verbose_case_setup_handler(source, index_of_case); + greentea_send_kv(TEST_ENV_TESTCASE_START, source->get_description()); + return status; +} + +status_t utest::v1::greentea_case_teardown_handler(const Case *const source, const size_t passed, const size_t failed, const failure_t failure) +{ + greentea_send_kv(TEST_ENV_TESTCASE_FINISH, source->get_description(), passed, failed); + return verbose_case_teardown_handler(source, passed, failed, failure); +} + +status_t utest::v1::greentea_case_failure_abort_handler(const Case *const source, const failure_t failure) +{ + status_t status = verbose_case_failure_handler(source, failure); + return (status == STATUS_IGNORE) ? STATUS_IGNORE : STATUS_ABORT; +} + +status_t utest::v1::greentea_case_failure_continue_handler(const Case *const source, const failure_t failure) +{ + return verbose_case_failure_handler(source, failure); +} diff --git "a/frameworks\\utest/source/harness.cpp" "b/frameworks\\utest/source/harness.cpp" new file mode 100644 index 0000000000..8403bffcd1 --- /dev/null +++ "b/frameworks\\utest/source/harness.cpp" @@ -0,0 +1,342 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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. + **************************************************************************** + */ + +#include "utest/harness.h" +#include + +using namespace utest::v1; + +namespace +{ + const Case *test_cases = NULL; + size_t test_length = 0; + + size_t test_index_of_case = 0; + + size_t test_passed = 0; + size_t test_failed = 0; + + const Case *case_current = NULL; + size_t case_index = 0; + control_t case_control = control_t(REPEAT_SETUP_TEARDOWN); + size_t case_repeat_count = 1; + + void *case_timeout_handle = NULL; + size_t case_validation_count = 0; + bool case_timeout_occurred = false; + + size_t case_passed = 0; + size_t case_failed = 0; + size_t case_failed_before = 0; + + handlers_t defaults = default_handlers; + handlers_t handlers = defaults; + + location_t location = LOCATION_UNKNOWN; + + utest_v1_scheduler_t scheduler = {NULL, NULL, NULL, NULL}; +} + +static void die() { + while(1) ; +} + +static bool is_scheduler_valid(const utest_v1_scheduler_t scheduler) +{ + return (scheduler.init && scheduler.post && scheduler.cancel && scheduler.run); +} + +bool Harness::set_scheduler(const utest_v1_scheduler_t scheduler) +{ + if (is_scheduler_valid(scheduler)) { + ::scheduler = scheduler; + return true; + } + return false; +} + +bool Harness::run(const Specification& specification, size_t) +{ + return run(specification); +} + +bool Harness::run(const Specification& specification) +{ + // check if a specification is currently running + if (is_busy()) + return false; + + // if the scheduler is invalid, this is the first time we are calling + if (!is_scheduler_valid(scheduler)) + scheduler = utest_v1_get_scheduler(); + // if the scheduler is still invalid, abort + if (!is_scheduler_valid(scheduler)) + return false; + // if the scheduler failed to initialize, abort + if (scheduler.init() != 0) + return false; + + test_cases = specification.cases; + test_length = specification.length; + defaults = specification.defaults; + handlers.test_setup = defaults.get_handler(specification.setup_handler); + handlers.test_teardown = defaults.get_handler(specification.teardown_handler); + handlers.test_failure = defaults.get_handler(specification.failure_handler); + + test_index_of_case = 0; + test_passed = 0; + test_failed = 0; + + case_passed = 0; + case_failed = 0; + case_failed_before = 0; + + location = LOCATION_TEST_SETUP; + int setup_status = 0; + failure_t failure(REASON_NONE, location); + + if (handlers.test_setup) { + setup_status = handlers.test_setup(test_length); + if (setup_status == STATUS_CONTINUE) setup_status = 0; + else if (setup_status < STATUS_CONTINUE) failure.reason = REASON_TEST_SETUP; + else if (setup_status > signed(test_length)) failure.reason = REASON_CASE_INDEX; + } + + if (failure.reason != REASON_NONE) { + if (handlers.test_failure) handlers.test_failure(failure); + if (handlers.test_teardown) handlers.test_teardown(0, 0, failure); + test_cases = NULL; + exit(1); + return true; + } + + case_index = setup_status; + case_current = &test_cases[case_index]; + + scheduler.post(run_next_case, 0); + if (scheduler.run() != 0) { + failure.reason = REASON_SCHEDULER; + if (handlers.test_failure) handlers.test_failure(failure); + if (handlers.test_teardown) handlers.test_teardown(0, 0, failure); + test_cases = NULL; + exit(1); + return true; + } + return true; +} + +void Harness::raise_failure(const failure_reason_t reason) +{ + // ignore a failure, if the Harness has not been initialized. + // this allows using unity assertion macros without setting up utest. + if (test_cases == NULL) return; + + status_t fail_status = STATUS_ABORT; + { + UTEST_ENTER_CRITICAL_SECTION; + + if (handlers.test_failure) handlers.test_failure(failure_t(reason, location)); + if (handlers.case_failure) fail_status = handlers.case_failure(case_current, failure_t(reason, location)); + if (fail_status != STATUS_IGNORE) case_failed++; + + if ((fail_status == STATUS_ABORT) && case_timeout_handle) + { + scheduler.cancel(case_timeout_handle); + case_timeout_handle = NULL; + } + UTEST_LEAVE_CRITICAL_SECTION; + } + + if (fail_status == STATUS_ABORT || reason & REASON_CASE_SETUP) { + if (handlers.case_teardown && location != LOCATION_CASE_TEARDOWN) { + location_t fail_loc(location); + location = LOCATION_CASE_TEARDOWN; + + status_t teardown_status = handlers.case_teardown(case_current, case_passed, case_failed, failure_t(reason, fail_loc)); + if (teardown_status < STATUS_CONTINUE) raise_failure(REASON_CASE_TEARDOWN); + else if (teardown_status > signed(test_length)) raise_failure(REASON_CASE_INDEX); + else if (teardown_status >= 0) case_index = teardown_status - 1; + + handlers.case_teardown = NULL; + } + } + if (fail_status == STATUS_ABORT) { + test_failed++; + failure_t fail(reason, location); + location = LOCATION_TEST_TEARDOWN; + if (handlers.test_teardown) handlers.test_teardown(test_passed, test_failed, fail); + exit(test_failed); + die(); + } +} + +void Harness::schedule_next_case() +{ + if (!case_timeout_occurred && case_failed_before == case_failed) { + case_passed++; + } + + if (case_control.repeat & REPEAT_SETUP_TEARDOWN || !(case_control.repeat & (REPEAT_ON_TIMEOUT | REPEAT_ON_VALIDATE))) { + location = LOCATION_CASE_TEARDOWN; + + if (handlers.case_teardown) { + status_t status = handlers.case_teardown(case_current, case_passed, case_failed, + case_failed ? failure_t(REASON_CASES, LOCATION_UNKNOWN) : failure_t(REASON_NONE)); + if (status < STATUS_CONTINUE) raise_failure(REASON_CASE_TEARDOWN); + else if (status > signed(test_length)) raise_failure(REASON_CASE_INDEX); + else if (status >= 0) case_index = status - 1; + } + } + + if (!(case_control.repeat & (REPEAT_ON_TIMEOUT | REPEAT_ON_VALIDATE))) { + if (case_failed > 0) test_failed++; + else test_passed++; + + case_control = control_t(REPEAT_SETUP_TEARDOWN); + case_index++; + case_current = &test_cases[case_index]; + case_passed = 0; + case_failed = 0; + case_failed_before = 0; + case_repeat_count = 1; + test_index_of_case++; + } + scheduler.post(run_next_case, 0); +} + +void Harness::handle_timeout() +{ + { + UTEST_ENTER_CRITICAL_SECTION; + + if (case_timeout_handle != NULL) { + case_timeout_handle = NULL; + case_timeout_occurred = true; + } + UTEST_LEAVE_CRITICAL_SECTION; + } + if (case_timeout_occurred) { + raise_failure(failure_reason_t(REASON_TIMEOUT | ((case_control.repeat & REPEAT_ON_TIMEOUT) ? REASON_IGNORE : 0))); + scheduler.post(schedule_next_case, 0); + } +} + +void Harness::validate_callback(const control_t control) +{ + UTEST_ENTER_CRITICAL_SECTION; + case_validation_count++; + + if (case_timeout_handle != NULL || case_control.timeout == TIMEOUT_FOREVER) + { + scheduler.cancel(case_timeout_handle); + case_timeout_handle = NULL; + control_t merged_control = case_control + control; + case_control.repeat = repeat_t(merged_control.repeat & ~REPEAT_ON_TIMEOUT); + case_control.timeout = TIMEOUT_NONE; + scheduler.post(schedule_next_case, 0); + } + UTEST_LEAVE_CRITICAL_SECTION; +} + +bool Harness::is_busy() +{ + UTEST_ENTER_CRITICAL_SECTION; + if (!test_cases) return false; + if (!case_current) return false; + + bool res = (case_current < (test_cases + test_length)); + UTEST_LEAVE_CRITICAL_SECTION; + return res; +} + +void Harness::run_next_case() +{ + if(case_current < (test_cases + test_length)) + { + handlers.case_setup = defaults.get_handler(case_current->setup_handler); + handlers.case_teardown = defaults.get_handler(case_current->teardown_handler); + handlers.case_failure = defaults.get_handler(case_current->failure_handler); + + if (case_current->is_empty()) { + location = LOCATION_UNKNOWN; + raise_failure(REASON_EMPTY_CASE); + schedule_next_case(); + return; + } + + repeat_t setup_repeat; + { + UTEST_ENTER_CRITICAL_SECTION; + case_validation_count = 0; + case_timeout_occurred = false; + setup_repeat = case_control.repeat; + case_control = control_t(); + UTEST_LEAVE_CRITICAL_SECTION; + } + + if (setup_repeat & REPEAT_SETUP_TEARDOWN) { + location = LOCATION_CASE_SETUP; + if (handlers.case_setup && (handlers.case_setup(case_current, test_index_of_case) != STATUS_CONTINUE)) { + raise_failure(REASON_CASE_SETUP); + schedule_next_case(); + return; + } + } + + case_failed_before = case_failed; + location = LOCATION_CASE_HANDLER; + + if (case_current->handler) { + case_current->handler(); + } else if (case_current->control_handler) { + case_control = case_control + case_current->control_handler(); + } else if (case_current->repeat_count_handler) { + case_control = case_control + case_current->repeat_count_handler(case_repeat_count); + } + case_repeat_count++; + + { + UTEST_ENTER_CRITICAL_SECTION; + if (case_validation_count) case_control.repeat = repeat_t(case_control.repeat & ~REPEAT_ON_TIMEOUT); + + // if timeout valid + if (case_control.timeout < TIMEOUT_UNDECLR && case_validation_count == 0) { + // if await validation _with_ timeout + if (case_control.timeout < TIMEOUT_FOREVER) { + case_timeout_handle = scheduler.post(handle_timeout, case_control.timeout); + if (case_timeout_handle == NULL) { + raise_failure(REASON_SCHEDULER); + schedule_next_case(); + } + } + } + else { + scheduler.post(schedule_next_case, 0); + } + UTEST_LEAVE_CRITICAL_SECTION; + } + } + else if (handlers.test_teardown) { + location = LOCATION_TEST_TEARDOWN; + handlers.test_teardown(test_passed, test_failed, test_failed ? failure_t(REASON_CASES, LOCATION_UNKNOWN) : failure_t(REASON_NONE)); + test_cases = NULL; + exit(test_failed); + } else { + exit(test_failed); + } +} diff --git "a/frameworks\\utest/source/shim.cpp" "b/frameworks\\utest/source/shim.cpp" new file mode 100644 index 0000000000..bb66fff80f --- /dev/null +++ "b/frameworks\\utest/source/shim.cpp" @@ -0,0 +1,138 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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. + **************************************************************************** + */ + +#include "utest/shim.h" + +#if UTEST_SHIM_SCHEDULER_USE_MINAR +#include "minar/minar.h" + +static int32_t utest_minar_init() +{ + return 0; +} +static void *utest_minar_post(const utest_v1_harness_callback_t callback, const uint32_t delay_ms) +{ + void *handle = minar::Scheduler::postCallback(callback).delay(minar::milliseconds(delay_ms)).getHandle(); + return handle; +} +static int32_t utest_minar_cancel(void *handle) +{ + int32_t ret = minar::Scheduler::cancelCallback(handle); + return ret; +} +static int32_t utest_minar_run() +{ + return 0; +} +extern "C" { +static const utest_v1_scheduler_t utest_v1_scheduler = +{ + utest_minar_init, + utest_minar_post, + utest_minar_cancel, + utest_minar_run +}; +utest_v1_scheduler_t utest_v1_get_scheduler() +{ + return utest_v1_scheduler; +} +} + +#elif UTEST_SHIM_SCHEDULER_USE_US_TICKER +#ifdef YOTTA_MBED_HAL_VERSION_STRING +# include "mbed-hal/us_ticker.h" +#else +# include "us_ticker.h" +#endif +// only one callback is active at any given time +static volatile utest_v1_harness_callback_t minimal_callback; +static volatile utest_v1_harness_callback_t ticker_callback; +static const ticker_data_t *ticker_data; +static ticker_event_t ticker_event; + +static void ticker_handler(uint32_t) +{ + // printf("\t\t>>> Ticker callback fired for %p.\n", ticker_callback); + minimal_callback = ticker_callback; +} + +static int32_t utest_us_ticker_init() +{ + ticker_data = get_us_ticker_data(); + ticker_set_handler(ticker_data, ticker_handler); + return 0; +} +static void *utest_us_ticker_post(const utest_v1_harness_callback_t callback, const uint32_t delay_ms) +{ + // printf("\t\t>>> Schedule %p with %ums delay => %p.\n", callback, (unsigned int)delay_ms, (void*)1); + if (delay_ms) { + ticker_callback = callback; + // fire the interrupt in 1000us * delay_ms + ticker_insert_event(ticker_data, &ticker_event, ticker_read(ticker_data) + delay_ms * 1000, 0); + } else { + minimal_callback = callback; + } + + // return a bogus handle + return (void*)1; +} +static int32_t utest_us_ticker_cancel(void *handle) +{ + // printf("\t\t>>> Cancel %p => %u\n", handle, (unsigned int)0); + (void) handle; + ticker_remove_event(ticker_data, &ticker_event); + return 0; +} +static int32_t utest_us_ticker_run() +{ + while(1) + { + // check if a new callback has been set + if (minimal_callback) + { + // printf("\t\t>>> Firing callback %p\n", minimal_callback); + // copy the callback + utest_v1_harness_callback_t callback = minimal_callback; + // reset the shared callback + minimal_callback = NULL; + // execute the copied callback + callback(); + } + } + return 0; +} +extern "C" { +static const utest_v1_scheduler_t utest_v1_scheduler = +{ + utest_us_ticker_init, + utest_us_ticker_post, + utest_us_ticker_cancel, + utest_us_ticker_run +}; +utest_v1_scheduler_t utest_v1_get_scheduler() +{ + return utest_v1_scheduler; +} +} +#endif + +#ifdef YOTTA_CORE_UTIL_VERSION_STRING +// their functionality is implemented using the CriticalSectionLock class +void utest_v1_enter_critical_section(void) {} +void utest_v1_leave_critical_section(void) {} +#endif diff --git "a/frameworks\\utest/source/types.cpp" "b/frameworks\\utest/source/types.cpp" new file mode 100644 index 0000000000..80cf1e743f --- /dev/null +++ "b/frameworks\\utest/source/types.cpp" @@ -0,0 +1,116 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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. + **************************************************************************** + */ + + #include "utest/types.h" + +const char* utest::v1::stringify(utest::v1::failure_reason_t reason) +{ + const char *string; + switch(reason & ~REASON_IGNORE) + { + case REASON_NONE: + string = "Ignored: No Failure"; + break; + case REASON_CASES: + string = "Ignored: Test Cases Failed"; + break; + case REASON_EMPTY_CASE: + string = "Ignored: Test Case is Empty"; + break; + case REASON_TIMEOUT: + string = "Ignored: Timed Out"; + break; + case REASON_ASSERTION: + string = "Ignored: Assertion Failed"; + break; + case REASON_TEST_SETUP: + string = "Ignored: Test Setup Failed"; + break; + case REASON_TEST_TEARDOWN: + string = "Ignored: Test Teardown Failed"; + break; + case REASON_CASE_SETUP: + string = "Ignored: Case Setup Failed"; + break; + case REASON_CASE_HANDLER: + string = "Ignored: Case Handler Failed"; + break; + case REASON_CASE_TEARDOWN: + string = "Ignored: Case Teardown Failed"; + break; + case REASON_CASE_INDEX: + string = "Ignored: Case Index Invalid"; + break; + case REASON_SCHEDULER: + string = "Ignored: Scheduling Asynchronous Callback Failed"; + break; + default: + case REASON_UNKNOWN: + string = "Ignored: Unknown Failure"; + break; + } + if (!(reason & REASON_IGNORE)) string += 9; + return string; +} + +const char* utest::v1::stringify(utest::v1::failure_t failure) +{ + return stringify(failure.reason); +} + +const char* utest::v1::stringify(utest::v1::location_t location) +{ + const char *string; + switch(location) + { + case LOCATION_TEST_SETUP: + string = "Test Setup Handler"; + break; + case LOCATION_TEST_TEARDOWN: + string = "Test Teardown Handler"; + break; + case LOCATION_CASE_SETUP: + string = "Case Setup Handler"; + break; + case LOCATION_CASE_HANDLER: + string = "Case Handler"; + break; + case LOCATION_CASE_TEARDOWN: + string = "Case Teardown Handler"; + break; + default: + case LOCATION_UNKNOWN: + string = "Unknown Location"; + break; + } + return string; +} + +const char* utest::v1::stringify(utest::v1::status_t status) +{ + switch(status) + { + case STATUS_CONTINUE: + return "Continue"; + case STATUS_IGNORE: + return "Ignore"; + case STATUS_ABORT: + return "Abort"; + } + return "Unknown Status"; +} diff --git "a/frameworks\\utest/source/unity_handler.cpp" "b/frameworks\\utest/source/unity_handler.cpp" new file mode 100644 index 0000000000..bc595233aa --- /dev/null +++ "b/frameworks\\utest/source/unity_handler.cpp" @@ -0,0 +1,31 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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. + **************************************************************************** + */ + + #include "utest/harness.h" + +extern "C" +void utest_unity_assert_failure() +{ + utest::v1::Harness::raise_failure(utest::v1::REASON_ASSERTION); +} + +extern "C" +void utest_unity_ignore_failure() +{ + utest::v1::Harness::raise_failure(utest::v1::failure_reason_t(utest::v1::REASON_ASSERTION | utest::v1::REASON_IGNORE)); +} diff --git "a/frameworks\\utest/utest/case.h" "b/frameworks\\utest/utest/case.h" new file mode 100644 index 0000000000..bf9569ddeb --- /dev/null +++ "b/frameworks\\utest/utest/case.h" @@ -0,0 +1,129 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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 UTEST_CASES_H +#define UTEST_CASES_H + +#include +#include +#include +#include "types.h" +#include "default_handlers.h" + + +namespace utest { +namespace v1 { + + /** Test case wrapper class. + * + * This class contains the description of the test case and all handlers + * for setting up, running the test case, tearing down and handling failures. + * + * By default you only need to provide a description and a test case handler. + * You may override the setup, teardown and failure handlers, but you do not have to. + * If you do not override these handler, the specified default handlers will be called. + * + * These constructors are overloaded to allow you a comfortable declaration of all your + * callbacks. + * The order is always: + * - description (required) + * - setup handler (optional) + * - test case handler (required) + * - teardown handler (optional) + * - failure handler (optional) + * + * @note While you can specify an empty test case (ie. use `ignore_handler` for all callbacks), + * the harness will abort the test unconditionally. + */ + class Case + { + public: + // overloads for case_handler_t + Case(const char *description, + const case_setup_handler_t setup_handler, + const case_handler_t case_handler, + const case_teardown_handler_t teardown_handler = default_handler, + const case_failure_handler_t failure_handler = default_handler); + + Case(const char *description, + const case_handler_t case_handler, + const case_failure_handler_t failure_handler = default_handler); + + Case(const char *description, + const case_handler_t case_handler, + const case_teardown_handler_t teardown_handler, + const case_failure_handler_t failure_handler = default_handler); + + // overloads for case_control_handler_t + Case(const char *description, + const case_setup_handler_t setup_handler, + const case_control_handler_t case_handler, + const case_teardown_handler_t teardown_handler = default_handler, + const case_failure_handler_t failure_handler = default_handler); + + Case(const char *description, + const case_control_handler_t case_handler, + const case_failure_handler_t failure_handler = default_handler); + + Case(const char *description, + const case_control_handler_t case_handler, + const case_teardown_handler_t teardown_handler, + const case_failure_handler_t failure_handler = default_handler); + + // overloads for case_call_count_handler_t + Case(const char *description, + const case_setup_handler_t setup_handler, + const case_call_count_handler_t case_handler, + const case_teardown_handler_t teardown_handler = default_handler, + const case_failure_handler_t failure_handler = default_handler); + + Case(const char *description, + const case_call_count_handler_t case_handler, + const case_failure_handler_t failure_handler = default_handler); + + Case(const char *description, + const case_call_count_handler_t case_handler, + const case_teardown_handler_t teardown_handler, + const case_failure_handler_t failure_handler = default_handler); + + + /// @returns the textual description of the test case + const char* get_description() const; + + /// @returns `true` if setup, test and teardown handlers are set to `ignore_handler` + bool is_empty() const; + + private: + const char *description; + + const case_handler_t handler; + const case_control_handler_t control_handler; + const case_call_count_handler_t repeat_count_handler; + + const case_setup_handler_t setup_handler; + const case_teardown_handler_t teardown_handler; + + const case_failure_handler_t failure_handler; + + friend class Harness; + }; + +} // namespace v1 +} // namespace utest + + #endif // UTEST_CASES_H diff --git "a/frameworks\\utest/utest/default_handlers.h" "b/frameworks\\utest/utest/default_handlers.h" new file mode 100644 index 0000000000..fe372e3829 --- /dev/null +++ "b/frameworks\\utest/utest/default_handlers.h" @@ -0,0 +1,176 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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 UTEST_DEFAULT_HANDLER_H +#define UTEST_DEFAULT_HANDLER_H + +#include +#include +#include +#include "types.h" + + +namespace utest { +namespace v1 { + + /** Default handler hint. + * + * Use this handler to indicate the you want the default handler to be called. + * This type automatically casts itself into the appropriate handler type, when possible. + * Use the constants to default a handler unambigously. + */ + static const struct + { + operator test_setup_handler_t() const { return test_setup_handler_t(1); } + operator test_teardown_handler_t() const { return test_teardown_handler_t(1); } + operator test_failure_handler_t() const { return test_failure_handler_t(1); } + + operator case_setup_handler_t() const { return case_setup_handler_t(1); } + operator case_teardown_handler_t() const { return case_teardown_handler_t(1); } + operator case_failure_handler_t() const { return case_failure_handler_t(1); } + } default_handler; + + /** Ignore handler hint. + * + * Use this handler to indicate the you want to ignore this handler and it will not be called. + * This type automatically casts itself into the appropriate handler type, when possible. + * Use the constants to ignore a handler unambigously. + */ + static const struct + { + operator case_handler_t() const { return case_handler_t(NULL); } + operator case_control_handler_t() const { return case_control_handler_t(NULL); } + operator case_call_count_handler_t() const { return case_call_count_handler_t(NULL); } + + operator test_setup_handler_t() const { return test_setup_handler_t(NULL); } + operator test_teardown_handler_t() const { return test_teardown_handler_t(NULL); } + operator test_failure_handler_t() const { return test_failure_handler_t(NULL); } + + operator case_setup_handler_t() const { return case_setup_handler_t(NULL); } + operator case_teardown_handler_t() const { return case_teardown_handler_t(NULL); } + operator case_failure_handler_t() const { return case_failure_handler_t(NULL); } + } ignore_handler; + + /** A table of handlers. + * + * This structure stores all modifyable handlers and provides accessors to + * filter out the default handler. + * So if this structure contains handlers, and you want to use these handlers + * as a default backup, you can use the `get_handler` function to choose the right handler. + * + * Example: + * @code + * const handler_t defaults = { ... }; // your default handlers + * // will return the handler in defaults. + * test_setup_handler_t handler = defaults.get_handler(default_handler); + * // you will still need to manually check the handler before executing it + * if (handler != ignore_handler) handler(...); + * + * extern test_teardown_handler_t custom_handler(...); + * // will return `custom_handler` + * test_teardown_handler_t handler = defaults.get_handler(custom_handler); + * // you will still need to manually check the handler before executing it + * if (handler != ignore_handler) handler(...); + * @endcode + */ + struct handlers_t + { + test_setup_handler_t test_setup; + test_teardown_handler_t test_teardown; + test_failure_handler_t test_failure; + + case_setup_handler_t case_setup; + case_teardown_handler_t case_teardown; + case_failure_handler_t case_failure; + + inline test_setup_handler_t get_handler(test_setup_handler_t handler) const { + if (handler == default_handler) return test_setup; + return handler; + } + inline test_teardown_handler_t get_handler(test_teardown_handler_t handler) const { + if (handler == default_handler) return test_teardown; + return handler; + } + inline test_failure_handler_t get_handler(test_failure_handler_t handler) const { + if (handler == default_handler) return test_failure; + return handler; + } + + inline case_setup_handler_t get_handler(case_setup_handler_t handler) const { + if (handler == default_handler) return case_setup; + return handler; + } + inline case_teardown_handler_t get_handler(case_teardown_handler_t handler) const { + if (handler == default_handler) return case_teardown; + return handler; + } + inline case_failure_handler_t get_handler(case_failure_handler_t handler) const { + if (handler == default_handler) return case_failure; + return handler; + } + }; + + /// Prints the number of tests to run and continues. + status_t verbose_test_setup_handler (const size_t number_of_cases); + /// Prints the number of tests that passed and failed with a reason if provided. + void verbose_test_teardown_handler(const size_t passed, const size_t failed, const failure_t failure); + /// Prints the failure for `REASON_TEST_SETUP` and `REASON_TEST_TEARDOWN` and then dies. + void verbose_test_failure_handler (const failure_t failure); + + /// Prints the index and description of the case being run and continues. + status_t verbose_case_setup_handler (const Case *const source, const size_t index_of_case); + /// Prints the number of tests that passed and failed with a reason if provided within this case and continues. + status_t verbose_case_teardown_handler(const Case *const source, const size_t passed, const size_t failed, const failure_t failure); + /// Prints the reason of the failure and continues, unless the teardown handler failed, for which it aborts. + status_t verbose_case_failure_handler (const Case *const source, const failure_t reason); + + /// Requests the start test case from greentea and continues. + status_t greentea_test_setup_handler (const size_t number_of_cases); + /// Reports the test results to greentea. + void greentea_test_teardown_handler(const size_t passed, const size_t failed, const failure_t failure); + /// Reports the failure for `REASON_TEST_SETUP` and `REASON_TEST_TEARDOWN` to greentea and then dies. + void greentea_test_failure_handler (const failure_t failure); + + /// Registers the test case setup with greentea. + status_t greentea_case_setup_handler (const Case *const source, const size_t index_of_case); + /// Registers the test case teardown with greentea. + status_t greentea_case_teardown_handler(const Case *const source, const size_t passed, const size_t failed, const failure_t failure); + /// Reports the failure to greentea and then aborts. + status_t greentea_case_failure_abort_handler (const Case *const source, const failure_t reason); + /// Reports the failure to greentea and then continues. + status_t greentea_case_failure_continue_handler(const Case *const source, const failure_t reason); + + /// The verbose default handlers that always continue on failure + extern const handlers_t verbose_continue_handlers; + + /// The greentea default handlers that always abort on the first encountered failure + extern const handlers_t greentea_abort_handlers; + + /// The greentea default handlers that always continue on failure + extern const handlers_t greentea_continue_handlers; + + /// The selftest default handlers that always abort on _any_ assertion failure, otherwise continue + extern const handlers_t selftest_handlers; + + /// The greentea aborting handlers are the default + const handlers_t default_handlers = greentea_abort_handlers; + +} // namespace v1 +} // namespace utest + +#endif // UTEST_DEFAULT_HANDLER_H diff --git "a/frameworks\\utest/utest/harness.h" "b/frameworks\\utest/utest/harness.h" new file mode 100644 index 0000000000..9298af9af3 --- /dev/null +++ "b/frameworks\\utest/utest/harness.h" @@ -0,0 +1,99 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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 UTEST_HARNESS_H +#define UTEST_HARNESS_H + +#include +#include +#include + +#include "types.h" +#include "case.h" +#include "default_handlers.h" +#include "specification.h" +#include "scheduler.h" + + +namespace utest { +namespace v1 { + + /** Test Harness. + * + * This class runs a test specification for you and calls all required handlers. + * The harness executes the test specification in an asynchronous fashion, therefore + * `run()` returns immediately. + * + * By default, this harness uses the MINAR scheduler for asynchronous callbacks. + * If you wamt to provide your own custom scheduler, set `config.utest.use_custom_scheduler` to `true` + * inside your yotta config and set a custom scheduler implementation using the `set_scheduler()` function. + * You must set the scheduler before running a specification. + * + * @note In case of an test abort, the harness will busy-wait and never finish. + */ + class Harness + { + public: + /// Runs a test specification + /// @retval `true` if the specification can be run + /// @retval `false` if another specification is currently running + static bool run(const Specification& specification); + + /// @cond + __deprecated_message("Start case selection is done by returning the index from the test setup handler!") + static bool run(const Specification& specification, size_t start_case); + /// @endcond + + /// @returns `true` if a test specification is being executed, `false` otherwise + static bool is_busy(); + + /// Sets the scheduler to be used. + /// @return `true` if scheduler is properly specified (all functions non-null). + static bool set_scheduler(utest_v1_scheduler_t scheduler); + + /** Call this function in the asynchronous callback that you have been waiting for. + * + * You can only validate a callback once, calling this function when no callback is expected + * has no side effects. + * After callback validation, the next test case is scheduled. + * + * You may specify additional test case attributes with this callback. + * So for example, you may delay the decision to repeat an asynchronous test case until the callback + * needs to be validated. + * + * However, be aware, that only the repeat attributes can be modified and the usual arbitration rules apply. + * The modified case attributes are only valid until the case handler returns updated attributes. + * + * @param control the test case attribute to be added to the existing attributes. + */ + static void validate_callback(const control_t control = control_t()); + + /// Raising a failure causes the failure to be counted and the failure handler to be called. + /// Further action then depends on its return state. + static void raise_failure(const failure_reason_t reason); + + protected: + static void run_next_case(); + static void handle_timeout(); + static void schedule_next_case(); + }; + +} // namespace v1 +} // namespace utest + +#endif // UTEST_HARNESS_H diff --git "a/frameworks\\utest/utest/scheduler.h" "b/frameworks\\utest/utest/scheduler.h" new file mode 100644 index 0000000000..55d83e97b2 --- /dev/null +++ "b/frameworks\\utest/utest/scheduler.h" @@ -0,0 +1,103 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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 UTEST_SCHEDULER_H +#define UTEST_SCHEDULER_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The utest harness manages its own state and therefore does not require the scheduler to + * bind any arguments to the scheduled callback. + */ +typedef void (*utest_v1_harness_callback_t)(void); + +/** + * utest calls this function before running the test specification. + * Use this function to initialize your scheduler before the first callback is requested. + * + * @retval `0` if success + * @retval non-zero if failure + */ +typedef int32_t (*utest_v1_scheduler_init_callback_t)(void); + +/** + * utest calls this function when it needs to schedule a callback with a delay in milliseconds. + * `delay_ms` will only be non-zero if an asynchronous test case exists in the test specification. + * @note If your scheduler cannot provide asynchronous callbacks (which probably require a hardware timer), + * then this scheduler may return `NULL` as a handle and `utest` will fail the asynchronous request and move on. + * Note that test cases which do not require asynchronous callback support will still work fine then. + * + * @warning You MUST NOT execute the callback inside this function, even for a delay of 0ms. + * Buffer the callback and call it in your main loop. + * @warning You MUST NOT execute the callback in an interrupt context! + * Buffer the callback and call it in your main loop. + * @note utest only schedules one callback at any given time. + * This should make the implementation of this scheduler a lot simpler for you. + * + * @param callback the pointer to the callback function + * @param delay_ms the delay in milliseconds after which the callback should be executed + * @return A handle to identify the scheduled callback, or `NULL` for failure. + */ +typedef void *(*utest_v1_scheduler_post_callback_t)(const utest_v1_harness_callback_t callback, const uint32_t delay_ms); + +/** + * utest needs to cancel callbacks with a non-zero delay some time later. + * Even though `utest` only schedules one callback at any given time, it can cancel a callback more than once. + * You should therefore make use of the handle to make sure you do not cancel the wrong callback. + * + * @note If your scheduler cannot provide asynchronous callbacks, do nothing in this function and return non-zero. + * + * @param handle the handle returned from the `post` call to identify which callback to be cancelled. + * @retval `0` if success + * @retval non-zero if failure + */ +typedef int32_t (*utest_v1_scheduler_cancel_callback_t)(void *handle); + +/** + * utest calls this function at the end of the `Harness::run()` function, after (!) the first callback has been requested. + * This function is meant to implement an optional event loop, which may very well be blocking (if your scheduler works with that). + * This assumes that `Harness::run()` will be called on the main stack (ie. not in an interrupt!). + * + * @retval `0` if success + * @retval non-zero if failure + */ +typedef int32_t (*utest_v1_scheduler_run_callback_t)(void); + +/** + * The scheduler interface consists out of the `post` and `cancel` functions, + * which you must implement to use `utest`. + */ +typedef struct { + utest_v1_scheduler_init_callback_t init; + utest_v1_scheduler_post_callback_t post; + utest_v1_scheduler_cancel_callback_t cancel; + utest_v1_scheduler_run_callback_t run; +} utest_v1_scheduler_t; + +#ifdef __cplusplus +} +#endif + +#endif // UTEST_SCHEDULER_H diff --git "a/frameworks\\utest/utest/shim.h" "b/frameworks\\utest/utest/shim.h" new file mode 100644 index 0000000000..78a6f6048a --- /dev/null +++ "b/frameworks\\utest/utest/shim.h" @@ -0,0 +1,85 @@ +/**************************************************************************** + * Copyright (c) 2016, ARM Limited, All Rights Reserved + * 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 UTEST_SHIM_H +#define UTEST_SHIM_H + +#include +#include +#include +#include "scheduler.h" + +#ifdef YOTTA_CFG +# include "compiler-polyfill/attributes.h" +#else +# ifndef __deprecated_message +# if defined(__CC_ARM) +# define __deprecated_message(msg) __attribute__((deprecated)) +# else +# define __deprecated_message(msg) __attribute__((deprecated(msg))) +# endif +# endif +#endif + +#ifdef YOTTA_CORE_UTIL_VERSION_STRING +# include "core-util/CriticalSectionLock.h" +# define UTEST_ENTER_CRITICAL_SECTION mbed::util::CriticalSectionLock lock +# define UTEST_LEAVE_CRITICAL_SECTION +#else +# ifndef UTEST_ENTER_CRITICAL_SECTION +# define UTEST_ENTER_CRITICAL_SECTION utest_v1_enter_critical_section() +# endif +# ifndef UTEST_LEAVE_CRITICAL_SECTION +# define UTEST_LEAVE_CRITICAL_SECTION utest_v1_leave_critical_section() +# endif +#endif + +#ifndef YOTTA_CFG_UTEST_USE_CUSTOM_SCHEDULER +# ifdef YOTTA_MINAR_VERSION_STRING +# define UTEST_MINAR_AVAILABLE 1 +# else +# define UTEST_MINAR_AVAILABLE 0 +# endif +# ifndef UTEST_SHIM_SCHEDULER_USE_MINAR +# define UTEST_SHIM_SCHEDULER_USE_MINAR UTEST_MINAR_AVAILABLE +# endif +# ifndef UTEST_SHIM_SCHEDULER_USE_US_TICKER +# ifdef __MBED__ +# define UTEST_SHIM_SCHEDULER_USE_US_TICKER 1 +# else +# define UTEST_SHIM_SCHEDULER_USE_US_TICKER 0 +# endif +# endif +#endif // YOTTA_CFG_UTEST_USE_CUSTOM_SCHEDULER + +#ifdef __cplusplus +extern "C" { +#endif + +/// must be implemented by the port +void utest_v1_enter_critical_section(void); +void utest_v1_leave_critical_section(void); + +/// This is the default scheduler implementation used by the harness. +utest_v1_scheduler_t utest_v1_get_scheduler(void); + +#ifdef __cplusplus +} +#endif + +#endif // UTEST_SHIM_H diff --git "a/frameworks\\utest/utest/specification.h" "b/frameworks\\utest/utest/specification.h" new file mode 100644 index 0000000000..3e0747d2d7 --- /dev/null +++ "b/frameworks\\utest/utest/specification.h" @@ -0,0 +1,139 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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 UTEST_SPECIFICATION_H +#define UTEST_SPECIFICATION_H + +#include +#include +#include +#include "types.h" +#include "case.h" +#include "default_handlers.h" + + +namespace utest { +namespace v1 { + + /** Test specification containing the setup and teardown handlers and test cases. + * + * This class simply holds the test cases and allows you to specify default handlers, and + * override setup and teardown handlers. + * The order of arguments is: + * - test setup handler (optional) + * - array of test cases (required) + * - test teardown handler (optional) + * - default handlers (optional) + * + * @note You cannot set the size of the test case array dynamically, it is template deducted at compile + * time. Creating test specifications for unittests at runtime is explicitly not supported. + */ + class Specification + { + public: + template< size_t N > + Specification(const Case (&cases)[N], + const handlers_t defaults = default_handlers) : + setup_handler(default_handler), teardown_handler(default_handler), failure_handler(default_handler), + cases(cases), length(N), + defaults(defaults) + {} + + template< size_t N > + Specification(const Case (&cases)[N], + const test_failure_handler_t failure_handler, + const handlers_t defaults = default_handlers) : + setup_handler(default_handler), teardown_handler(default_handler), failure_handler(failure_handler), + cases(cases), length(N), + defaults(defaults) + {} + + template< size_t N > + Specification(const Case (&cases)[N], + const test_teardown_handler_t teardown_handler, + const handlers_t defaults = default_handlers) : + setup_handler(default_handler), teardown_handler(teardown_handler), failure_handler(default_handler), + cases(cases), length(N), + defaults(defaults) + {} + + template< size_t N > + Specification(const Case (&cases)[N], + const test_teardown_handler_t teardown_handler, + const test_failure_handler_t failure_handler, + const handlers_t defaults = default_handlers) : + setup_handler(default_handler), teardown_handler(teardown_handler), failure_handler(failure_handler), + cases(cases), length(N), + defaults(defaults) + {} + + template< size_t N > + Specification(const test_setup_handler_t setup_handler, + const Case (&cases)[N], + const handlers_t defaults = default_handlers) : + setup_handler(setup_handler), teardown_handler(default_handler), failure_handler(default_handler), + cases(cases), length(N), + defaults(defaults) + {} + + template< size_t N > + Specification(const test_setup_handler_t setup_handler, + const Case (&cases)[N], + const test_failure_handler_t failure_handler, + const handlers_t defaults = default_handlers) : + setup_handler(setup_handler), teardown_handler(default_handler), failure_handler(failure_handler), + cases(cases), length(N), + defaults(defaults) + {} + + template< size_t N > + Specification(const test_setup_handler_t setup_handler, + const Case (&cases)[N], + const test_teardown_handler_t teardown_handler, + const handlers_t defaults = default_handlers) : + setup_handler(setup_handler), teardown_handler(teardown_handler), failure_handler(default_handler), + cases(cases), length(N), + defaults(defaults) + {} + + template< size_t N > + Specification(const test_setup_handler_t setup_handler, + const Case (&cases)[N], + const test_teardown_handler_t teardown_handler, + const test_failure_handler_t failure_handler, + const handlers_t defaults = default_handlers) : + setup_handler(setup_handler), teardown_handler(teardown_handler), failure_handler(failure_handler), + cases(cases), length(N), + defaults(defaults) + {} + + private: + const test_setup_handler_t setup_handler; + const test_teardown_handler_t teardown_handler; + const test_failure_handler_t failure_handler; + const Case *const cases; + const size_t length; + const handlers_t defaults; + + friend class Harness; + }; + +} // namespace v1 +} // namespace utest + + #endif // UTEST_SPECIFICATION_H diff --git "a/frameworks\\utest/utest/types.h" "b/frameworks\\utest/utest/types.h" new file mode 100644 index 0000000000..ef8c6c3ccf --- /dev/null +++ "b/frameworks\\utest/utest/types.h" @@ -0,0 +1,354 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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 UTEST_TYPES_H +#define UTEST_TYPES_H + +#include +#include +#include +#include "shim.h" + +namespace utest { +namespace v1 { + + enum repeat_t { + REPEAT_UNDECLR = 0, + REPEAT_NONE = 1, ///< continue with the next test case + + REPEAT_ON_TIMEOUT = 2, + REPEAT_ON_VALIDATE = 4, + REPEAT_CASE_ONLY = 8, + REPEAT_SETUP_TEARDOWN = 16, + + REPEAT_MASK = REPEAT_ON_TIMEOUT | REPEAT_ON_VALIDATE | REPEAT_CASE_ONLY | REPEAT_SETUP_TEARDOWN, + + REPEAT_ALL_ON_TIMEOUT = REPEAT_SETUP_TEARDOWN | REPEAT_ON_TIMEOUT, ///< repeat the handler with setup and teardown on timeout + REPEAT_HANDLER_ON_TIMEOUT = REPEAT_CASE_ONLY | REPEAT_ON_TIMEOUT, ///< repeat only the handler on timeout + REPEAT_ALL = REPEAT_SETUP_TEARDOWN | REPEAT_ON_VALIDATE, ///< repeat the handler with setup and teardown + REPEAT_HANDLER = REPEAT_CASE_ONLY | REPEAT_ON_VALIDATE ///< repeat only the handler + }; + + enum status_t { + STATUS_CONTINUE = -1, ///< continues testing + STATUS_IGNORE = -2, ///< ignores failure and continues testing + STATUS_ABORT = -3 ///< stops testing + }; + + enum failure_reason_t { + REASON_NONE = 0, ///< No failure occurred + + REASON_UNKNOWN = (1 << 0), ///< An unknown failure occurred + REASON_CASES = (1 << 1), ///< A failure occurred in at least one test case + REASON_EMPTY_CASE = (1 << 2), ///< The test case contains only empty handlers + REASON_TIMEOUT = (1 << 3), ///< An expected asynchronous call timed out + REASON_ASSERTION = (1 << 4), ///< An assertion failed + + REASON_TEST_SETUP = (1 << 5), ///< Test setup failed + REASON_TEST_TEARDOWN = (1 << 6), ///< Test teardown failed + REASON_CASE_SETUP = (1 << 7), ///< Case setup failed + REASON_CASE_HANDLER = (1 << 8), ///< Case handler failed + REASON_CASE_TEARDOWN = (1 << 9), ///< Case teardown failed + + REASON_CASE_INDEX = (1 << 10), ///< Case index out-of-range + REASON_SCHEDULER = (1 << 11), ///< Asynchronous callback scheduling failed + + REASON_IGNORE = 0x8000 ///< The failure may be ignored + }; + + enum location_t { + LOCATION_NONE = 0, ///< No location information + LOCATION_TEST_SETUP, ///< A failure occurred in the test setup + LOCATION_TEST_TEARDOWN, ///< A failure occurred in the test teardown + LOCATION_CASE_SETUP, ///< A failure occurred in the case setup + LOCATION_CASE_HANDLER, ///< A failure occurred in the case handler + LOCATION_CASE_TEARDOWN, ///< A failure occurred in the case teardown + LOCATION_UNKNOWN ///< A failure occurred in an unknown location + }; + + /// Contains the reason and location of the failure. + struct failure_t { + failure_t() : reason(REASON_NONE), location(LOCATION_NONE) {} + failure_t(failure_reason_t reason) : reason(reason), location(LOCATION_NONE) {} + failure_t(location_t location) : reason(REASON_NONE), location(location) {} + failure_t(failure_reason_t reason, location_t location) : reason(reason), location(location) {} + + /// @returns a copy of the failure with the reason ignored. + failure_t ignored() const { + return failure_t(failure_reason_t(reason | REASON_IGNORE), location); + } + + failure_reason_t reason; + location_t location; + }; + + + enum { + TIMEOUT_NONE = uint32_t(-1), ///< Do not use a timeout + TIMEOUT_UNDECLR = uint32_t(-2), ///< Timeout not explicitly specified, defaults to NONE + TIMEOUT_FOREVER = uint32_t(-3) ///< Never time out + }; + + /// Stringifies a failure reason for understandable error messages. + const char* stringify(failure_reason_t reason); + /// Stringifies a failure for understandable error messages. + const char* stringify(failure_t failure); + /// Stringifies a location. + const char* stringify(location_t location); + /// Stringifies a status. + const char* stringify(status_t status); + + /** Control class for specifying test case attributes + * + * This class encapsulated control information about test cases which, when returned from + * a test case influences the behavior of the test harness. + * Instead of using this class directly it is recommended to use the aliases for clearer + * semantics: + * @code + * control_t test_case(const size_t call_count) { + * // repeat 5 times for a total of 6 calls + * return (call_count < 6) ? CaseRepeatHandler : CaseNext; + * } + * @endcode + * + * This class overloads the `+` operator to implement something similiar to saturated arbitration: + * - The lower timeout value "wins". + * - A more involved repeat "wins" (ie. `ALL` > 'HANDLER' > 'NONE'). + * - Next Case always wins. + * + * You may then add timeouts and repeats together: + * @code + * control_t test_case(const size_t call_count) { + * // repeat 5 times for a total of 6 calls, each with a 500ms asynchronous timeout + * return CaseTimeout(500) + ((call_count < 6) ? CaseRepeatAll : CaseNoRepeat); + * } + * @endcode + * + * In the future, more control information may be added transparently and backwards compatible. + */ + struct control_t + { + control_t() : repeat(REPEAT_UNDECLR), timeout(TIMEOUT_UNDECLR) {} + + control_t(repeat_t repeat, uint32_t timeout_ms) : + repeat(repeat), timeout(timeout_ms) {} + + control_t(repeat_t repeat) : + repeat(repeat), timeout(TIMEOUT_UNDECLR) {} + + control_t(uint32_t timeout_ms) : + repeat(REPEAT_UNDECLR), timeout(timeout_ms) {} + + control_t + inline operator+(const control_t& rhs) const { + control_t result( + repeat_t(this->repeat | rhs.repeat), + (rhs.timeout == TIMEOUT_NONE) ? rhs.timeout : this->timeout); + + if (result.timeout != TIMEOUT_NONE && result.timeout > rhs.timeout) { + result.timeout = rhs.timeout; + } + + if (result.repeat & REPEAT_NONE) { + result.repeat = REPEAT_NONE; + } + else { + if (result.repeat & REPEAT_SETUP_TEARDOWN) { + result.repeat = repeat_t(result.repeat & ~REPEAT_CASE_ONLY); + } + if (result.timeout == TIMEOUT_NONE && result.repeat & REPEAT_ON_TIMEOUT) { + result.repeat = repeat_t(result.repeat & ~REPEAT_ON_TIMEOUT); + } + } + + return result; + } + + repeat_t + inline get_repeat() const { + return repeat; + } + uint32_t + inline get_timeout() const { + return timeout; + } + + private: + repeat_t repeat; + uint32_t timeout; + friend class Harness; + }; + + /// does not repeat this test case and immediately moves on to the next one without timeout + const control_t CaseNext(REPEAT_NONE, TIMEOUT_NONE); + + /// does not repeat this test case, moves on to the next one + const control_t CaseNoRepeat(REPEAT_NONE); + /// repeats the test case handler with calling teardown and setup handlers + const control_t CaseRepeatAll(REPEAT_ALL); + /// repeats only the test case handler without calling teardown and setup handlers + const control_t CaseRepeatHandler(REPEAT_HANDLER); + + /// No timeout, immediately moves on to the next case, but allows repeats + const control_t CaseNoTimeout(TIMEOUT_NONE); + /// Awaits until the callback is validated and never times out. Use with caution! + const control_t CaseAwait(TIMEOUT_FOREVER); + /// Alias class for asynchronous timeout control in milliseconds + inline control_t CaseTimeout(uint32_t ms) { return ms; } + + /// Alias class for asynchronous timeout control in milliseconds and + /// repeats the test case handler with calling teardown and setup handlers + inline control_t CaseRepeatAllOnTimeout(uint32_t ms) { return control_t(REPEAT_ALL_ON_TIMEOUT, ms); } + /// Alias class for asynchronous timeout control in milliseconds and + /// repeats only the test case handler without calling teardown and setup handlers + inline control_t CaseRepeatHandlerOnTimeout(uint32_t ms) { return control_t(REPEAT_HANDLER_ON_TIMEOUT, ms); } + + class Case; // forward declaration + + /** Test setup handler. + * + * This handler is called before execution of any test case and + * allows you to initialize your common test environment. + * + * @param number_of_cases the total number of test cases in the test specification + * + * @returns + * You can return `STATUS_ABORT` if you initialization failed and the test teardown handler will + * then be called with the `REASON_SETUP`. + */ + typedef status_t (*test_setup_handler_t)(const size_t number_of_cases); + + /** Test teardown handler. + * + * This handler is called after execution of all test case or if test execution is aborted. + * You can use this handler to de-initialize your test environment and output test statistics. + * The failure argument contains the immediate reason why this handler is called. + * If the test completed normally without failures, this will contain `REASON_NONE`. + * + * After execution of this handler, the test harness will stop execution. + * + * @param passed the number of cases without failures + * @param failed the number of cases with at least one failure + * @param failure the reason why this handler was called + */ + typedef void (*test_teardown_handler_t)(const size_t passed, const size_t failed, const failure_t failure); + + /** Test failure handler. + * + * This handler is called anytime a failure occurs during the execution of a test speficication. + * The handler only allows logging of failures and cannot influence test execution. + * + * @param failure the reason why this handler was called + */ + typedef void (*test_failure_handler_t)(const failure_t reason); + + /** Test case setup handler. + * + * This handler is called before execution of each test case and + * allows you to modify your environment before each test case. + * + * @param source the test case to be setup + * @param index_of_case the current index of the test case within the specification + * + * @returns + * You can return `STATUS_ABORT` to indicate that your setup failed, which will call the case + * failure handler with `REASON_SETUP` and then the case teardown handler with `REASON_SETUP`. + * This gives the teardown handler a chance to clean up a failed setup. + */ + typedef status_t (*case_setup_handler_t)(const Case *const source, const size_t index_of_case); + + /** Primitive test case handler + * + * This handler is called only if the case setup succeeded and is followed by the test case teardown handler. + * + * @note This handler is executed only once. + */ + typedef void (*case_handler_t)(void); + + /** Complex test case handler + * + * This handler is called only if the case setup succeeded and then may be repeated or + * awaiting a asynchronous callback, depending on the return modifiers. + * + * @returns + * A combination of control modifiers. + */ + typedef control_t (*case_control_handler_t)(void); + + /** Test case handler (repeatable) + * + * This handler is called only if the case setup succeeded and then may be repeated or + * awaiting a asynchronous callback, depending on the return modifiers. + * + * @param call_count starting at `1`, contains the number of times this handler has been called + * + * @returns + * A combination of control modifiers. + */ + typedef control_t (*case_call_count_handler_t)(const size_t call_count); + + /** Test case teardown handler. + * + * This handler is called after execution of each test case or all repeated test cases and + * allows you to reset your environment after each test case. + * + * @param source the test case to be torn down + * @param passed the number of cases without failures (can be >1 for repeated test cases) + * @param failed the number failures (can be larger than the number of (repeated) test cases) + * @param failure the reason why this handler was called + * + * @returns + * You can return `STATUS_ABORT` to indicate that your teardown failed, which will call the case + * failure handler with `REASON_TEARDOWN`. + */ + typedef status_t (*case_teardown_handler_t)(const Case *const source, const size_t passed, const size_t failed, const failure_t reason); + + /** Test case failure handler. + * + * This handler is called whenever a failure occurred during the setup, execution or teardown. + * + * @param source the test case in which the failure occurred + * @param reason the reason why this handler was called + * + * @returns + * You can return `STATUS_ABORT` to indicate that this failure is non-recoverable, which will call the case + * teardown handler with reason. If a failure occurs during teardown, the teardown will not be called again. + * You may return `STATUS_IGNORE` which will cause the harness to ignore and not count the failure. + */ + typedef status_t (*case_failure_handler_t)(const Case *const source, const failure_t reason); + + + // deprecations + __deprecated_message("Use CaseRepeatAll instead.") const control_t CaseRepeat = CaseRepeatAll; + __deprecated_message("Use CaseRepeatHandler instead.") const control_t CaseRepeatHandlerOnly = CaseRepeatHandler; + + __deprecated_message("Use REASON_NONE instead.") const failure_reason_t FAILURE_NONE = REASON_NONE; + __deprecated_message("Use REASON_UNKNOWN instead.") const failure_reason_t FAILURE_UNKNOWN = REASON_UNKNOWN; + __deprecated_message("Use REASON_CASES instead.") const failure_reason_t FAILURE_CASES = REASON_CASES; + __deprecated_message("Use REASON_EMPTY_CASE instead.") const failure_reason_t FAILURE_EMPTY_CASE = REASON_EMPTY_CASE; + __deprecated_message("Use REASON_TIMEOUT instead.") const failure_reason_t FAILURE_TIMEOUT = REASON_TIMEOUT; + __deprecated_message("Use REASON_ASSERTION instead.") const failure_reason_t FAILURE_ASSERTION = REASON_ASSERTION; + __deprecated_message("Use REASON_CASE_SETUP instead.") const failure_reason_t FAILURE_SETUP = REASON_CASE_SETUP; + __deprecated_message("Use REASON_CASE_TEARDOWN instead.") const failure_reason_t FAILURE_TEARDOWN = REASON_CASE_TEARDOWN; + __deprecated_message("Use REASON_IGNORE instead.") const failure_reason_t FAILURE_IGNORE = REASON_IGNORE; + + +} // namespace v1 +} // namespace utest + +#endif // UTEST_TYPES_H diff --git "a/frameworks\\utest/utest/unity_handler.h" "b/frameworks\\utest/utest/unity_handler.h" new file mode 100644 index 0000000000..5982ff88a9 --- /dev/null +++ "b/frameworks\\utest/utest/unity_handler.h" @@ -0,0 +1,30 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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 UTEST_UNITY_ASSERT_FAILURE_H +#define UTEST_UNITY_ASSERT_FAILURE_H + +#include + +/// this function is called from the unity module when an assertion failed. +void utest_unity_assert_failure(); + +/// this function is called from the unity module when an assertion failed, but is ignored. +void utest_unity_ignore_failure(); + +#endif // UTEST_UNITY_ASSERT_FAILURE_H diff --git "a/frameworks\\utest/utest/utest.h" "b/frameworks\\utest/utest/utest.h" new file mode 100644 index 0000000000..98e20055aa --- /dev/null +++ "b/frameworks\\utest/utest/utest.h" @@ -0,0 +1,28 @@ +/**************************************************************************** + * Copyright (c) 2015, ARM Limited, All Rights Reserved + * 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 UTEST_H +#define UTEST_H + +#include "types.h" +#include "case.h" +#include "default_handlers.h" +#include "harness.h" + + +#endif // UTEST_H