ttfw: mock missing packages while search cases

This commit is contained in:
Fu Hanxi 2022-07-13 10:35:01 +08:00
parent c9b73a554e
commit 97132c40fd
3 changed files with 34 additions and 13 deletions

View File

@ -173,6 +173,7 @@ tools/ci/python_packages/tiny_test_fw/Utility/CaseConfig.py
tools/ci/python_packages/tiny_test_fw/Utility/GitlabCIJob.py tools/ci/python_packages/tiny_test_fw/Utility/GitlabCIJob.py
tools/ci/python_packages/tiny_test_fw/Utility/SearchCases.py tools/ci/python_packages/tiny_test_fw/Utility/SearchCases.py
tools/ci/python_packages/tiny_test_fw/Utility/TestCase.py tools/ci/python_packages/tiny_test_fw/Utility/TestCase.py
tools/ci/python_packages/tiny_test_fw/Utility/__init__.py
tools/ci/python_packages/tiny_test_fw/bin/Runner.py tools/ci/python_packages/tiny_test_fw/bin/Runner.py
tools/ci/python_packages/tiny_test_fw/bin/example.py tools/ci/python_packages/tiny_test_fw/bin/example.py
tools/ci/python_packages/tiny_test_fw/docs/conf.py tools/ci/python_packages/tiny_test_fw/docs/conf.py

View File

@ -28,7 +28,9 @@ class Search:
print('Try to get cases from: ' + file_name) print('Try to get cases from: ' + file_name)
test_functions = [] test_functions = []
try: try:
mod = load_source(file_name) # search case no need to run the functions
# mock missing modules would help us get the test case function objects
mod = load_source(file_name, mock_missing=True)
for func in [mod.__getattribute__(x) for x in dir(mod) for func in [mod.__getattribute__(x) for x in dir(mod)
if isinstance(mod.__getattribute__(x), types.FunctionType)]: if isinstance(mod.__getattribute__(x), types.FunctionType)]:
try: try:
@ -38,7 +40,7 @@ class Search:
except AttributeError: except AttributeError:
continue continue
except ImportError as e: except ImportError as e:
warning_str = 'ImortError: \r\n\tFile:' + file_name + '\r\n\tError:' + str(e) warning_str = 'ImportError: \r\n\tFile:' + file_name + '\r\n\tError:' + str(e)
cls.missing_import_warnings.append(warning_str) cls.missing_import_warnings.append(warning_str)
test_functions_out = [] test_functions_out = []
@ -56,7 +58,7 @@ class Search:
""" search all test case files recursively of a path """ """ search all test case files recursively of a path """
if not os.path.exists(test_case): if not os.path.exists(test_case):
raise OSError('test case path not exist') raise OSError(f'test case path "{test_case}" not exist')
if os.path.isdir(test_case): if os.path.isdir(test_case):
test_case_files = [] test_case_files = []
for root, _, file_names in os.walk(test_case): for root, _, file_names in os.walk(test_case):

View File

@ -1,9 +1,14 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
from __future__ import print_function from __future__ import print_function
import logging
import os.path import os.path
import sys import sys
import time import time
import traceback import traceback
from unittest.mock import MagicMock
from .. import Env from .. import Env
@ -63,19 +68,20 @@ def console_log(data, color='white', end='\n'):
pass pass
__LOADED_MODULES = dict() __LOADED_MODULES = dict() # type: ignore
# we should only load one module once. # we should only load one module once.
# if we load one module twice, # if we load one module twice,
# python will regard the same object loaded in the first time and second time as different objects. # python will regard the same object loaded in the first time and second time as different objects.
# it will lead to strange errors like `isinstance(object, type_of_this_object)` return False # it will lead to strange errors like `isinstance(object, type_of_this_object)` return False
def load_source(path): def load_source(path, mock_missing=False):
""" """
Dynamic loading python file. Note that this function SHOULD NOT be used to replace ``import``. Dynamic loading python file. Note that this function SHOULD NOT be used to replace ``import``.
It should only be used when the package path is only available in runtime. It should only be used when the package path is only available in runtime.
:param path: The path of python file :param path: The path of python file
:param mock_missing: If True, will mock the module if the module is not found.
:return: Loaded object :return: Loaded object
""" """
path = os.path.realpath(path) path = os.path.realpath(path)
@ -84,17 +90,29 @@ def load_source(path):
try: try:
return __LOADED_MODULES[path] return __LOADED_MODULES[path]
except KeyError: except KeyError:
folder = os.path.dirname(path)
sys.path.append(folder)
from importlib.machinery import SourceFileLoader
try: try:
dir = os.path.dirname(path)
sys.path.append(dir)
from importlib.machinery import SourceFileLoader
ret = SourceFileLoader(load_name, path).load_module() ret = SourceFileLoader(load_name, path).load_module()
except ImportError: except ModuleNotFoundError as e:
# importlib.machinery doesn't exists in Python 2 so we will use imp (deprecated in Python 3) if not mock_missing:
import imp raise
ret = imp.load_source(load_name, path)
# mock the missing third-party libs. Don't use it when real-testing
while True:
sys.modules[e.name] = MagicMock()
logging.warning('Mocking python module %s', e.name)
try:
ret = SourceFileLoader(load_name, path).load_module()
break
except ModuleNotFoundError as f:
e = f # another round
except SyntaxError:
# well, let's ignore it... non of our business
return None
finally: finally:
sys.path.remove(dir) sys.path.remove(folder)
__LOADED_MODULES[path] = ret __LOADED_MODULES[path] = ret
return ret return ret