feat(tiny_test_fw): ignore known failure cases result
py
This commit is contained in:
@@ -13,19 +13,30 @@
|
||||
# limitations under the License.
|
||||
|
||||
""" Interface for test cases. """
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
import functools
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
import junit_xml
|
||||
|
||||
from . import Env
|
||||
from . import DUT
|
||||
from . import App
|
||||
from . import Utility
|
||||
from . import DUT, App, Env, Utility
|
||||
from .Utility import format_case_id
|
||||
|
||||
|
||||
class TestCaseFailed(AssertionError):
|
||||
def __init__(self, *cases):
|
||||
"""
|
||||
Raise this exception if one or more test cases fail in a 'normal' way (ie the test runs but fails, no unexpected exceptions)
|
||||
|
||||
This will avoid dumping the Python stack trace, because the assumption is the junit error info and full job log already has
|
||||
enough information for a developer to debug.
|
||||
|
||||
'cases' argument is the names of one or more test cases
|
||||
"""
|
||||
message = 'Test case{} failed: {}'.format('s' if len(cases) > 1 else '', ', '.join(str(c) for c in cases))
|
||||
super(TestCaseFailed, self).__init__(self, message)
|
||||
|
||||
|
||||
class DefaultEnvConfig(object):
|
||||
@@ -87,8 +98,8 @@ class JunitReport(object):
|
||||
@classmethod
|
||||
def output_report(cls, junit_file_path):
|
||||
""" Output current test result to file. """
|
||||
with open(os.path.join(junit_file_path, cls.JUNIT_FILE_NAME), "w") as f:
|
||||
cls.JUNIT_TEST_SUITE.to_file(f, [cls.JUNIT_TEST_SUITE], prettyprint=False)
|
||||
with open(os.path.join(junit_file_path, cls.JUNIT_FILE_NAME), 'w') as f:
|
||||
junit_xml.to_xml_report_file(f, [cls.JUNIT_TEST_SUITE], prettyprint=False)
|
||||
|
||||
@classmethod
|
||||
def get_current_test_case(cls):
|
||||
@@ -184,21 +195,20 @@ def test_method(**kwargs):
|
||||
env_inst = Env.Env(**env_config)
|
||||
|
||||
# prepare for xunit test results
|
||||
junit_file_path = env_inst.app_cls.get_log_folder(env_config["test_suite_name"])
|
||||
junit_test_case = JunitReport.create_test_case(case_info["ID"])
|
||||
junit_file_path = env_inst.app_cls.get_log_folder(env_config['test_suite_name'])
|
||||
junit_test_case = JunitReport.create_test_case(format_case_id(case_info['ID'],
|
||||
target=case_info['chip'].lower()))
|
||||
result = False
|
||||
|
||||
try:
|
||||
Utility.console_log("starting running test: " + test_func.__name__, color="green")
|
||||
# execute test function
|
||||
test_func(env_inst, extra_data)
|
||||
# if finish without exception, test result is True
|
||||
result = True
|
||||
except TestCaseFailed as e:
|
||||
junit_test_case.add_failure_info(str(e))
|
||||
except Exception as e:
|
||||
# handle all the exceptions here
|
||||
traceback.print_exc()
|
||||
# log failure
|
||||
junit_test_case.add_failure_info(str(e) + ":\r\n" + traceback.format_exc())
|
||||
Utility.handle_unexpected_exception(junit_test_case, e)
|
||||
finally:
|
||||
# do close all DUTs, if result is False then print DUT debug info
|
||||
close_errors = env_inst.close(dut_debug=(not result))
|
||||
@@ -210,7 +220,7 @@ def test_method(**kwargs):
|
||||
# and raise exception in DUT close to fail test case if reset detected.
|
||||
if close_errors:
|
||||
for error in close_errors:
|
||||
junit_test_case.add_failure_info("env close error: ".format(error))
|
||||
junit_test_case.add_failure_info(str(error))
|
||||
result = False
|
||||
if not case_info["junit_report_by_case"]:
|
||||
JunitReport.test_case_finish(junit_test_case)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
import traceback
|
||||
|
||||
_COLOR_CODES = {
|
||||
"white": u'\033[0m',
|
||||
@@ -73,3 +74,20 @@ def load_source(path):
|
||||
sys.path.remove(dir)
|
||||
__LOADED_MODULES[path] = ret
|
||||
return ret
|
||||
|
||||
|
||||
def handle_unexpected_exception(junit_test_case, exception):
|
||||
"""
|
||||
Helper to log & add junit result details for an unexpected exception encountered
|
||||
when running a test case.
|
||||
|
||||
Should always be called from inside an except: block
|
||||
"""
|
||||
traceback.print_exc()
|
||||
# AssertionError caused by an 'assert' statement has an empty string as its 'str' form
|
||||
e_str = str(exception) if str(exception) else repr(exception)
|
||||
junit_test_case.add_failure_info('Unexpected exception: {}\n{}'.format(e_str, traceback.format_exc()))
|
||||
|
||||
|
||||
def format_case_id(case_name, target='esp32', config='default'):
|
||||
return '{}.{}.{}'.format(target, config, case_name)
|
||||
|
||||
@@ -21,13 +21,14 @@ Command line interface to run test cases from a given path.
|
||||
Use ``python Runner.py test_case_path -c config_file -e env_config_file`` to run test cases.
|
||||
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import threading
|
||||
from fnmatch import fnmatch
|
||||
|
||||
from tiny_test_fw import TinyFW
|
||||
from tiny_test_fw.Utility import SearchCases, CaseConfig
|
||||
from tiny_test_fw.TinyFW import JunitReport, set_default_config
|
||||
from tiny_test_fw.Utility import CaseConfig, SearchCases, console_log
|
||||
|
||||
|
||||
class Runner(threading.Thread):
|
||||
@@ -37,28 +38,64 @@ class Runner(threading.Thread):
|
||||
:param env_config_file: env config file
|
||||
"""
|
||||
|
||||
def __init__(self, test_case, case_config, env_config_file=None):
|
||||
def __init__(self, test_case, case_config, env_config_file=None, known_failure_cases_file=None):
|
||||
super(Runner, self).__init__()
|
||||
self.setDaemon(True)
|
||||
if case_config:
|
||||
test_suite_name = os.path.splitext(os.path.basename(case_config))[0]
|
||||
else:
|
||||
test_suite_name = "TestRunner"
|
||||
TinyFW.set_default_config(env_config_file=env_config_file, test_suite_name=test_suite_name)
|
||||
set_default_config(env_config_file=env_config_file, test_suite_name=test_suite_name)
|
||||
test_methods = SearchCases.Search.search_test_cases(test_case)
|
||||
self.test_cases = CaseConfig.Parser.apply_config(test_methods, case_config)
|
||||
self.test_result = []
|
||||
self.known_failure_cases = self._get_config_cases(known_failure_cases_file)
|
||||
|
||||
@staticmethod
|
||||
def _get_config_cases(config_file):
|
||||
res = set()
|
||||
if not config_file or not os.path.isfile(config_file):
|
||||
return res
|
||||
|
||||
for line in open(config_file).readlines():
|
||||
if not line:
|
||||
continue
|
||||
if not line.strip():
|
||||
continue
|
||||
without_comments = line.split("#")[0].strip()
|
||||
if without_comments:
|
||||
res.add(without_comments)
|
||||
return res
|
||||
|
||||
def run(self):
|
||||
for case in self.test_cases:
|
||||
result = case.run()
|
||||
self.test_result.append(result)
|
||||
case.run()
|
||||
|
||||
@staticmethod
|
||||
def is_known_issue(tc_name, known_cases):
|
||||
for case in known_cases:
|
||||
if tc_name == case:
|
||||
return True
|
||||
if fnmatch(tc_name, case):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_test_result(self):
|
||||
return self.test_result and all(self.test_result)
|
||||
_res = True
|
||||
console_log("Test Results:")
|
||||
for tc in JunitReport.JUNIT_TEST_SUITE.test_cases:
|
||||
if tc.failures:
|
||||
if self.is_known_issue(tc.name, self.known_failure_cases):
|
||||
console_log(" Known Failure: " + tc.name, color="orange")
|
||||
else:
|
||||
console_log(" Test Fail: " + tc.name, color="red")
|
||||
_res = False
|
||||
else:
|
||||
console_log(" Test Succeed: " + tc.name, color="green")
|
||||
|
||||
return _res
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("test_case",
|
||||
@@ -67,15 +104,17 @@ if __name__ == '__main__':
|
||||
help="case filter/config file")
|
||||
parser.add_argument("--env_config_file", "-e", default=None,
|
||||
help="test env config file")
|
||||
parser.add_argument("--known_failure_cases_file", default=None,
|
||||
help="known failure cases file")
|
||||
args = parser.parse_args()
|
||||
|
||||
runner = Runner(args.test_case, args.case_config, args.env_config_file)
|
||||
runner = Runner(args.test_case, args.case_config, args.env_config_file, args.known_failure_cases_file)
|
||||
runner.start()
|
||||
|
||||
while True:
|
||||
try:
|
||||
runner.join(1)
|
||||
if not runner.isAlive():
|
||||
if not runner.is_alive():
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
print("exit by Ctrl-C")
|
||||
|
||||
@@ -20,10 +20,6 @@ from .IDFDUT import IDFDUT, ESP32DUT, ESP32S2DUT, ESP8266DUT, ESP32QEMUDUT # no
|
||||
from .DebugUtils import OCDProcess, GDBProcess, TelnetProcess, CustomProcess # noqa: export DebugUtils for users
|
||||
|
||||
|
||||
def format_case_id(chip, case_name):
|
||||
return "{}.{}".format(chip, case_name)
|
||||
|
||||
|
||||
def idf_example_test(app=Example, dut=IDFDUT, chip="ESP32", module="examples", execution_time=1,
|
||||
level="example", erase_nvs=True, config_name=None, **kwargs):
|
||||
"""
|
||||
@@ -51,7 +47,6 @@ def idf_example_test(app=Example, dut=IDFDUT, chip="ESP32", module="examples", e
|
||||
|
||||
def test(func):
|
||||
test_func = original_method(func)
|
||||
test_func.case_info["ID"] = format_case_id(chip, test_func.case_info["name"])
|
||||
return test_func
|
||||
|
||||
return test
|
||||
@@ -83,7 +78,6 @@ def idf_unit_test(app=UT, dut=IDFDUT, chip="ESP32", module="unit-test", executio
|
||||
|
||||
def test(func):
|
||||
test_func = original_method(func)
|
||||
test_func.case_info["ID"] = format_case_id(chip, test_func.case_info["name"])
|
||||
return test_func
|
||||
|
||||
return test
|
||||
@@ -118,7 +112,6 @@ def idf_custom_test(app=TestApp, dut=IDFDUT, chip="ESP32", module="misc", execut
|
||||
|
||||
def test(func):
|
||||
test_func = original_method(func)
|
||||
test_func.case_info["ID"] = format_case_id(chip, test_func.case_info["name"])
|
||||
return test_func
|
||||
|
||||
return test
|
||||
|
||||
Reference in New Issue
Block a user