mbed-os/frameworks\utest/source/harness.cpp

261 lines
8.3 KiB
C++

/****************************************************************************
* 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 "minar/minar.h"
#include "core-util/CriticalSectionLock.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;
control_t case_control = control_t(REPEAT_SETUP_TEARDOWN);
size_t case_repeat_count = 0;
minar::callback_handle_t 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;
}
static void die() {
while(1) ;
}
bool Harness::run(const Specification& specification)
{
return run(specification, 0);
}
bool Harness::run(const Specification& specification, std::size_t start_case)
{
// ignore any invalid start index
if (start_case >= specification.length)
return false;
// check if a specification is currently running
if (is_busy())
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;
case_current = &test_cases[start_case];
if (handlers.test_setup && (handlers.test_setup(test_length) != STATUS_CONTINUE)) {
if (handlers.test_failure) handlers.test_failure(FAILURE_SETUP);
if (handlers.test_teardown) handlers.test_teardown(0, 0, FAILURE_SETUP);
test_cases = NULL;
return true;
}
minar::Scheduler::postCallback(run_next_case);
return true;
}
void Harness::raise_failure(failure_t reason)
{
status_t fail_status = STATUS_ABORT;
{
mbed::util::CriticalSectionLock lock;
if (handlers.test_failure) handlers.test_failure(reason);
if (handlers.case_failure) fail_status = handlers.case_failure(case_current, reason);
if (!(fail_status & STATUS_IGNORE)) case_failed++;
if ((fail_status & STATUS_ABORT) && case_timeout_handle)
{
minar::Scheduler::cancelCallback(case_timeout_handle);
case_timeout_handle = NULL;
}
}
if (fail_status & STATUS_ABORT || reason & FAILURE_SETUP) {
if (handlers.case_teardown && !(reason & FAILURE_TEARDOWN)) {
status_t teardown_status = handlers.case_teardown(case_current, case_passed, case_failed, reason);
if (teardown_status != STATUS_CONTINUE) {
raise_failure(FAILURE_TEARDOWN);
}
else handlers.case_teardown = NULL;
}
}
if (fail_status & STATUS_ABORT) {
test_failed++;
if (handlers.test_teardown) handlers.test_teardown(test_passed, test_failed, reason);
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))) {
if (handlers.case_teardown &&
(handlers.case_teardown(case_current, case_passed, case_failed,
case_failed ? FAILURE_CASES : FAILURE_NONE) != STATUS_CONTINUE)) {
raise_failure(FAILURE_TEARDOWN);
}
}
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_current++;
case_passed = 0;
case_failed = 0;
case_failed_before = 0;
case_repeat_count = 0;
test_index_of_case++;
}
minar::Scheduler::postCallback(run_next_case);
}
void Harness::handle_timeout()
{
{
mbed::util::CriticalSectionLock lock;
if (case_timeout_handle != NULL) {
case_timeout_handle = NULL;
case_timeout_occurred = true;
}
}
if (case_timeout_occurred) {
raise_failure(failure_t(FAILURE_TIMEOUT | ((case_control.repeat & REPEAT_ON_TIMEOUT) ? FAILURE_IGNORE : 0)));
minar::Scheduler::postCallback(schedule_next_case);
}
}
void Harness::validate_callback()
{
mbed::util::CriticalSectionLock lock;
case_validation_count++;
if (case_timeout_handle != NULL || case_control.timeout == TIMEOUT_FOREVER)
{
minar::Scheduler::cancelCallback(case_timeout_handle);
case_timeout_handle = NULL;
case_control.repeat = repeat_t(case_control.repeat & ~REPEAT_ON_TIMEOUT);
minar::Scheduler::postCallback(schedule_next_case);
}
}
bool Harness::is_busy()
{
mbed::util::CriticalSectionLock lock;
if (!test_cases) return false;
if (!case_current) return false;
return (case_current < (test_cases + test_length));
}
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()) {
raise_failure(FAILURE_EMPTY_CASE);
schedule_next_case();
return;
}
{
mbed::util::CriticalSectionLock lock;
case_validation_count = 0;
case_timeout_occurred = false;
}
if (case_control.repeat & REPEAT_SETUP_TEARDOWN) {
case_control = control_t();
if (handlers.case_setup && (handlers.case_setup(case_current, test_index_of_case) != STATUS_CONTINUE)) {
raise_failure(FAILURE_SETUP);
schedule_next_case();
return;
}
}
case_failed_before = case_failed;
if (case_current->handler) {
case_current->handler();
} else if (case_current->control_handler) {
case_control = case_current->control_handler();
} else if (case_current->repeat_count_handler) {
case_control = case_current->repeat_count_handler(case_repeat_count);
}
case_repeat_count++;
{
mbed::util::CriticalSectionLock lock;
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 = minar::Scheduler::postCallback(handle_timeout)
.delay(minar::milliseconds(case_control.timeout))
.getHandle();
}
}
else {
minar::Scheduler::postCallback(schedule_next_case);
}
}
}
else if (handlers.test_teardown) {
handlers.test_teardown(test_passed, test_failed, test_failed ? FAILURE_CASES : FAILURE_NONE);
test_cases = NULL;
}
}