diff --git a/TESTS/storage_abstraction/basicAPI/basicAPI.cpp b/TESTS/storage_abstraction/basicAPI/basicAPI.cpp index bc31905e3e4..05fbe6d896f 100644 --- a/TESTS/storage_abstraction/basicAPI/basicAPI.cpp +++ b/TESTS/storage_abstraction/basicAPI/basicAPI.cpp @@ -135,7 +135,7 @@ control_t test_initialize(const size_t call_count) TEST_ASSERT(rc >= ARM_DRIVER_OK); if (rc == ARM_DRIVER_OK) { TEST_ASSERT_EQUAL(1, capabilities.asynchronous_ops); - return (call_count < REPEAT_INSTANCES) ? (CaseTimeout(200) + CaseRepeatAll) : CaseNext; + return (call_count < REPEAT_INSTANCES) ? (CaseTimeout(200) + CaseRepeatAll) : (control_t) CaseNext; } TEST_ASSERT(rc == 1); diff --git a/features/frameworks/utest/source/utest_default_handlers.cpp b/features/frameworks/utest/source/utest_default_handlers.cpp index 7124af76f28..f487e2dcea6 100644 --- a/features/frameworks/utest/source/utest_default_handlers.cpp +++ b/features/frameworks/utest/source/utest_default_handlers.cpp @@ -34,6 +34,7 @@ const handlers_t utest::v1::verbose_continue_handlers = { verbose_case_failure_handler }; +const handlers_t& utest::v1::default_handlers = greentea_abort_handlers; // --- SPECIAL HANDLERS --- static void test_failure_handler(const failure_t failure) { diff --git a/features/frameworks/utest/source/utest_harness.cpp b/features/frameworks/utest/source/utest_harness.cpp index a796c020f0b..52b8513bcd2 100644 --- a/features/frameworks/utest/source/utest_harness.cpp +++ b/features/frameworks/utest/source/utest_harness.cpp @@ -36,7 +36,7 @@ namespace const Case *case_current = NULL; size_t case_index = 0; - control_t case_control = control_t(REPEAT_SETUP_TEARDOWN); + base_control_t case_control = { REPEAT_SETUP_TEARDOWN, TIMEOUT_UNDECLR }; size_t case_repeat_count = 1; void *case_timeout_handle = NULL; @@ -47,8 +47,13 @@ namespace size_t case_failed = 0; size_t case_failed_before = 0; - handlers_t defaults = default_handlers; - handlers_t handlers = defaults; + struct DefaultHandlers : public handlers_t { + DefaultHandlers() : handlers_t(default_handlers) { } + DefaultHandlers(const handlers_t& other) : handlers_t(other) { } + }; + + SingletonPtr defaults; + SingletonPtr handlers; location_t location = LOCATION_UNKNOWN; @@ -110,10 +115,10 @@ bool Harness::run(const Specification& specification) 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); + *defaults.get() = 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; @@ -127,16 +132,16 @@ bool Harness::run(const Specification& specification) int setup_status = 0; failure_t failure(REASON_NONE, location); - if (handlers.test_setup) { - setup_status = handlers.test_setup(test_length); + 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); + 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; @@ -150,8 +155,8 @@ bool Harness::run(const Specification& specification) 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); + 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; @@ -167,8 +172,8 @@ void Harness::raise_failure(const failure_reason_t reason) if (test_cases == NULL) return; utest::v1::status_t fail_status = STATUS_ABORT; - 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 (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)); { UTEST_ENTER_CRITICAL_SECTION; @@ -184,25 +189,25 @@ void Harness::raise_failure(const failure_reason_t reason) } if (fail_status == STATUS_ABORT || reason & REASON_CASE_SETUP) { - if (handlers.case_teardown && location != LOCATION_CASE_TEARDOWN) { + if (handlers->case_teardown && location != LOCATION_CASE_TEARDOWN) { location_t fail_loc(location); location = LOCATION_CASE_TEARDOWN; - utest::v1::status_t teardown_status = handlers.case_teardown(case_current, case_passed, case_failed, failure_t(reason, fail_loc)); + utest::v1::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; // Restore case failure location once we have dealt with case teardown location = fail_loc; - handlers.case_teardown = NULL; + 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); + if (handlers->test_teardown) handlers->test_teardown(test_passed, test_failed, fail); exit(test_failed); die(); } @@ -218,8 +223,8 @@ void Harness::schedule_next_case() if (case_control.repeat & REPEAT_SETUP_TEARDOWN || !(case_control.repeat & (REPEAT_ON_TIMEOUT | REPEAT_ON_VALIDATE))) { location = LOCATION_CASE_TEARDOWN; - if (handlers.case_teardown) { - utest::v1::status_t status = handlers.case_teardown(case_current, case_passed, case_failed, + if (handlers->case_teardown) { + utest::v1::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); @@ -298,9 +303,9 @@ void Harness::run_next_case() UTEST_LOG_FUNCTION(); 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); + 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; @@ -321,7 +326,7 @@ void Harness::run_next_case() 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)) { + if (handlers->case_setup && (handlers->case_setup(case_current, test_index_of_case) != STATUS_CONTINUE)) { raise_failure(REASON_CASE_SETUP); schedule_next_case(); return; @@ -361,9 +366,9 @@ void Harness::run_next_case() UTEST_LEAVE_CRITICAL_SECTION; } } - else if (handlers.test_teardown) { + 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)); + 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 { diff --git a/features/frameworks/utest/source/utest_shim.cpp b/features/frameworks/utest/source/utest_shim.cpp index ae5b47179a5..6b5ab7596a9 100644 --- a/features/frameworks/utest/source/utest_shim.cpp +++ b/features/frameworks/utest/source/utest_shim.cpp @@ -66,7 +66,7 @@ static volatile utest_v1_harness_callback_t minimal_callback; static volatile utest_v1_harness_callback_t ticker_callback; // Timeout object used to control the scheduling of test case callbacks -Timeout utest_timeout_object; +SingletonPtr utest_timeout_object; static void ticker_handler() { @@ -77,7 +77,9 @@ static void ticker_handler() static int32_t utest_us_ticker_init() { UTEST_LOG_FUNCTION(); - // Ticker scheduler does not require any initialisation so return immediately + // initialize the Timeout object to makes sure it is not initialized in + // interrupt context. + utest_timeout_object.get(); return 0; } static void *utest_us_ticker_post(const utest_v1_harness_callback_t callback, timestamp_t delay_ms) @@ -88,7 +90,7 @@ static void *utest_us_ticker_post(const utest_v1_harness_callback_t callback, ti if (delay_ms) { ticker_callback = callback; // fire the interrupt in 1000us * delay_ms - utest_timeout_object.attach_us(ticker_handler, delay_us); + utest_timeout_object->attach_us(ticker_handler, delay_us); } else { @@ -102,7 +104,7 @@ static int32_t utest_us_ticker_cancel(void *handle) { UTEST_LOG_FUNCTION(); (void) handle; - utest_timeout_object.detach(); + utest_timeout_object->detach(); return 0; } static int32_t utest_us_ticker_run() diff --git a/features/frameworks/utest/source/utest_types.cpp b/features/frameworks/utest/source/utest_types.cpp index f9d6ed55cf2..16a753248eb 100644 --- a/features/frameworks/utest/source/utest_types.cpp +++ b/features/frameworks/utest/source/utest_types.cpp @@ -114,3 +114,22 @@ const char* utest::v1::stringify(utest::v1::status_t status) } return "Unknown Status"; } + + +const utest::v1::base_control_t utest::v1::CaseNext = { REPEAT_NONE, TIMEOUT_NONE }; + +const utest::v1::base_control_t utest::v1::CaseNoRepeat = { REPEAT_NONE, TIMEOUT_UNDECLR }; + +const utest::v1::base_control_t utest::v1::CaseRepeatAll = { REPEAT_ALL, TIMEOUT_UNDECLR }; + +const utest::v1::base_control_t utest::v1::CaseRepeatHandler = { REPEAT_HANDLER, TIMEOUT_UNDECLR }; + +const utest::v1::base_control_t utest::v1::CaseNoTimeout = { REPEAT_UNDECLR, TIMEOUT_NONE }; + +const utest::v1::base_control_t utest::v1::CaseAwait = { REPEAT_UNDECLR, TIMEOUT_FOREVER }; + +// equal to CaeReapeatAll +const utest::v1::base_control_t utest::v1::CaseRepeat = { REPEAT_ALL, TIMEOUT_UNDECLR }; + +// equal to CaseRepeatHandler +const utest::v1::base_control_t utest::v1::CaseRepeatHandlerOnly = { REPEAT_HANDLER, TIMEOUT_UNDECLR }; diff --git a/features/frameworks/utest/utest/utest_default_handlers.h b/features/frameworks/utest/utest/utest_default_handlers.h index 46e907a13a1..965e089c60e 100644 --- a/features/frameworks/utest/utest/utest_default_handlers.h +++ b/features/frameworks/utest/utest/utest_default_handlers.h @@ -185,7 +185,7 @@ namespace v1 { extern const handlers_t selftest_handlers; /// The greentea aborting handlers are the default - const handlers_t default_handlers = greentea_abort_handlers; + extern const handlers_t& default_handlers; } // namespace v1 } // namespace utest diff --git a/features/frameworks/utest/utest/utest_types.h b/features/frameworks/utest/utest/utest_types.h index f7b66825283..37cf64294de 100644 --- a/features/frameworks/utest/utest/utest_types.h +++ b/features/frameworks/utest/utest/utest_types.h @@ -120,6 +120,23 @@ namespace v1 { /// Stringifies a status. const char* stringify(utest::v1::status_t status); + /** POD version of the class control_t. + * It is used to instantiate const control_t objects as PODs + * and prevent them to be included in the final binary. + * @note: control_pod_t can be converted to control_t by copy construction. + */ + struct base_control_t { + repeat_t repeat; + uint32_t timeout; + + repeat_t inline get_repeat() const { + return repeat; + } + uint32_t inline get_timeout() const { + return timeout; + } + }; + /** Control class for specifying test case attributes * * This class encapsulated control information about test cases which, when returned from @@ -148,24 +165,26 @@ namespace v1 { * * In the future, more control information may be added transparently and backwards compatible. */ - struct control_t + struct control_t : private base_control_t { - control_t() : repeat(REPEAT_UNDECLR), timeout(TIMEOUT_UNDECLR) {} + control_t() : base_control_t(make_base_control_t(REPEAT_UNDECLR, TIMEOUT_UNDECLR)) {} control_t(repeat_t repeat, uint32_t timeout_ms) : - repeat(repeat), timeout(timeout_ms) {} + base_control_t(make_base_control_t(repeat, timeout_ms)) {} control_t(repeat_t repeat) : - repeat(repeat), timeout(TIMEOUT_UNDECLR) {} + base_control_t(make_base_control_t(repeat, TIMEOUT_UNDECLR)) {} control_t(uint32_t timeout_ms) : - repeat(REPEAT_UNDECLR), timeout(timeout_ms) {} + base_control_t(make_base_control_t(REPEAT_UNDECLR, timeout_ms)) {} - control_t - inline operator+(const control_t& rhs) const { + control_t(const base_control_t& other) : + base_control_t(other) {} + + friend control_t operator+(const control_t& lhs, const control_t& rhs) { control_t result( - repeat_t(this->repeat | rhs.repeat), - (rhs.timeout == TIMEOUT_NONE) ? rhs.timeout : this->timeout); + repeat_t(lhs.repeat | rhs.repeat), + (rhs.timeout == TIMEOUT_NONE) ? rhs.timeout : lhs.timeout); if (result.timeout != TIMEOUT_NONE && result.timeout > rhs.timeout) { result.timeout = rhs.timeout; @@ -196,25 +215,46 @@ namespace v1 { } private: - repeat_t repeat; - uint32_t timeout; + static base_control_t make_base_control_t(repeat_t repeat, uint32_t timeout) { + base_control_t result = { + repeat, + timeout + }; + return result; + } + friend class Harness; }; + /// @see operator+ in control_t + inline control_t operator+(const base_control_t& lhs, const base_control_t& rhs) { + return control_t(lhs) + control_t(rhs); + } + + /// @see operator+ in control_t + inline control_t operator+(const base_control_t& lhs, const control_t& rhs) { + return control_t(lhs) + rhs; + } + + /// @see operator+ in control_t + inline control_t operator+(const control_t& lhs, const base_control_t& rhs) { + return lhs + control_t(rhs); + } + /// does not repeat this test case and immediately moves on to the next one without timeout - const control_t CaseNext(REPEAT_NONE, TIMEOUT_NONE); + extern const base_control_t CaseNext; /// does not repeat this test case, moves on to the next one - const control_t CaseNoRepeat(REPEAT_NONE); + extern const base_control_t CaseNoRepeat; /// repeats the test case handler with calling teardown and setup handlers - const control_t CaseRepeatAll(REPEAT_ALL); + extern const base_control_t CaseRepeatAll; /// repeats only the test case handler without calling teardown and setup handlers - const control_t CaseRepeatHandler(REPEAT_HANDLER); + extern const base_control_t CaseRepeatHandler; /// No timeout, immediately moves on to the next case, but allows repeats - const control_t CaseNoTimeout(TIMEOUT_NONE); + extern const base_control_t CaseNoTimeout; /// Awaits until the callback is validated and never times out. Use with caution! - const control_t CaseAwait(TIMEOUT_FOREVER); + extern const base_control_t CaseAwait; /// Alias class for asynchronous timeout control in milliseconds inline control_t CaseTimeout(uint32_t ms) { return ms; } @@ -341,8 +381,11 @@ namespace v1 { // deprecations - __deprecated_message("Use CaseRepeatAll instead.") const control_t CaseRepeat = CaseRepeatAll; - __deprecated_message("Use CaseRepeatHandler instead.") const control_t CaseRepeatHandlerOnly = CaseRepeatHandler; + __deprecated_message("Use CaseRepeatAll instead.") + extern const base_control_t CaseRepeat; + + __deprecated_message("Use CaseRepeatHandler instead.") + extern const base_control_t CaseRepeatHandlerOnly; __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;