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/SearchCases.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/example.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)
test_functions = []
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)
if isinstance(mod.__getattribute__(x), types.FunctionType)]:
try:
@ -38,7 +40,7 @@ class Search:
except AttributeError:
continue
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)
test_functions_out = []
@ -56,7 +58,7 @@ class Search:
""" search all test case files recursively of a path """
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):
test_case_files = []
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
import logging
import os.path
import sys
import time
import traceback
from unittest.mock import MagicMock
from .. import Env
@ -63,19 +68,20 @@ def console_log(data, color='white', end='\n'):
pass
__LOADED_MODULES = dict()
__LOADED_MODULES = dict() # type: ignore
# we should only load one module once.
# if we load one module twice,
# 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
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``.
It should only be used when the package path is only available in runtime.
: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
"""
path = os.path.realpath(path)
@ -84,17 +90,29 @@ def load_source(path):
try:
return __LOADED_MODULES[path]
except KeyError:
folder = os.path.dirname(path)
sys.path.append(folder)
from importlib.machinery import SourceFileLoader
try:
dir = os.path.dirname(path)
sys.path.append(dir)
from importlib.machinery import SourceFileLoader
ret = SourceFileLoader(load_name, path).load_module()
except ImportError:
# importlib.machinery doesn't exists in Python 2 so we will use imp (deprecated in Python 3)
import imp
ret = imp.load_source(load_name, path)
except ModuleNotFoundError as e:
if not mock_missing:
raise
# 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:
sys.path.remove(dir)
sys.path.remove(folder)
__LOADED_MODULES[path] = ret
return ret