mbed-os for GR-LYCHEE

Dependents:   mbed-os-example-blinky-gr-lychee GR-Boads_Camera_sample GR-Boards_Audio_Recoder GR-Boads_Camera_DisplayApp ... more

Revision:
0:f782d9c66c49
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/features/frameworks/utest/source/utest_harness.cpp	Fri Feb 02 05:42:23 2018 +0000
@@ -0,0 +1,375 @@
+/****************************************************************************
+ * 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/utest_harness.h"
+#include "utest/utest_stack_trace.h"
+#include "utest/utest_serial.h"
+
+#include <stdlib.h>
+
+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;
+    base_control_t case_control =  { REPEAT_SETUP_TEARDOWN, TIMEOUT_UNDECLR };
+    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;
+
+    struct DefaultHandlers : public handlers_t { 
+        DefaultHandlers() : handlers_t(default_handlers) { }
+        DefaultHandlers(const handlers_t& other) : handlers_t(other) { }
+    };
+
+    SingletonPtr<DefaultHandlers> defaults;
+    SingletonPtr<DefaultHandlers> handlers;
+
+    location_t location = LOCATION_UNKNOWN;
+
+    utest_v1_scheduler_t scheduler = {NULL, NULL, NULL, NULL};
+}
+
+static void die() {
+    UTEST_LOG_FUNCTION();
+    while(1) ;
+}
+
+static bool is_scheduler_valid(const utest_v1_scheduler_t scheduler)
+{
+    UTEST_LOG_FUNCTION();
+    return (scheduler.init && scheduler.post && scheduler.cancel && scheduler.run);
+}
+
+bool Harness::set_scheduler(const utest_v1_scheduler_t scheduler)
+{
+    UTEST_LOG_FUNCTION();
+    if (is_scheduler_valid(scheduler)) {
+        ::scheduler = scheduler;
+        return true;
+    }
+    return false;
+}
+
+
+void Harness::notify_testcases()
+{
+    for(unsigned i = 0; i < test_length; i++) {
+        utest::v1::greentea_testcase_notification_handler(test_cases[i].get_description());
+    }
+}
+
+bool Harness::run(const Specification& specification, size_t)
+{
+    UTEST_LOG_FUNCTION();
+    return run(specification);
+}
+
+bool Harness::run(const Specification& specification)
+{
+    UTEST_LOG_FUNCTION();
+    // 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.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;
+    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);
+    }
+
+    notify_testcases();
+
+    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;
+}
+
+void Harness::raise_failure(const failure_reason_t reason)
+{
+    UTEST_LOG_FUNCTION();
+    // 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;
+
+    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));
+
+    {
+        UTEST_ENTER_CRITICAL_SECTION;
+
+        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;
+
+            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;
+        }
+    }
+    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()
+{
+    UTEST_LOG_FUNCTION();
+    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) {
+            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);
+            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_LOG_FUNCTION();
+    {
+        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_LOG_FUNCTION();
+    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_LOG_FUNCTION();
+    UTEST_ENTER_CRITICAL_SECTION;
+    bool res = false;
+    
+    if (test_cases && case_current) {    
+        res = (case_current < (test_cases + test_length));
+    }
+    
+    UTEST_LEAVE_CRITICAL_SECTION;
+    return res;
+}
+
+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);
+
+        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);
+    }
+}