mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
tiny-test-fw: add build config and target options
This commit is contained in:
parent
7ac8f28dda
commit
0e6e7f49be
@ -38,9 +38,11 @@ class BaseApp(object):
|
|||||||
Also implements some common methods.
|
Also implements some common methods.
|
||||||
|
|
||||||
:param app_path: the path for app.
|
:param app_path: the path for app.
|
||||||
|
:param config_name: app configuration to be tested
|
||||||
|
:param target: build target
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, app_path):
|
def __init__(self, app_path, config_name=None, target=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -275,6 +275,7 @@ class BaseDUT(object):
|
|||||||
DEFAULT_EXPECT_TIMEOUT = 10
|
DEFAULT_EXPECT_TIMEOUT = 10
|
||||||
MAX_EXPECT_FAILURES_TO_SAVED = 10
|
MAX_EXPECT_FAILURES_TO_SAVED = 10
|
||||||
RECV_THREAD_CLS = RecvThread
|
RECV_THREAD_CLS = RecvThread
|
||||||
|
TARGET = None
|
||||||
""" DUT subclass can specify RECV_THREAD_CLS to do add some extra stuff when receive data.
|
""" DUT subclass can specify RECV_THREAD_CLS to do add some extra stuff when receive data.
|
||||||
For example, DUT can implement exception detect & analysis logic in receive thread subclass. """
|
For example, DUT can implement exception detect & analysis logic in receive thread subclass. """
|
||||||
LOG_THREAD = _LogThread()
|
LOG_THREAD = _LogThread()
|
||||||
@ -377,15 +378,14 @@ class BaseDUT(object):
|
|||||||
|
|
||||||
# methods that need to be overwritten by Tool
|
# methods that need to be overwritten by Tool
|
||||||
@classmethod
|
@classmethod
|
||||||
def confirm_dut(cls, port, app, **kwargs):
|
def confirm_dut(cls, port, **kwargs):
|
||||||
"""
|
"""
|
||||||
confirm if it's a DUT, usually used by auto detecting DUT in by Env config.
|
confirm if it's a DUT, usually used by auto detecting DUT in by Env config.
|
||||||
|
|
||||||
subclass (tool) must overwrite this method.
|
subclass (tool) must overwrite this method.
|
||||||
|
|
||||||
:param port: comport
|
:param port: comport
|
||||||
:param app: app instance
|
:return: tuple of result (bool), and target (str)
|
||||||
:return: True or False
|
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ class Env(object):
|
|||||||
self.lock = threading.RLock()
|
self.lock = threading.RLock()
|
||||||
|
|
||||||
@_synced
|
@_synced
|
||||||
def get_dut(self, dut_name, app_path, dut_class=None, app_class=None, **dut_init_args):
|
def get_dut(self, dut_name, app_path, dut_class=None, app_class=None, app_config_name=None, **dut_init_args):
|
||||||
"""
|
"""
|
||||||
get_dut(dut_name, app_path, dut_class=None, app_class=None)
|
get_dut(dut_name, app_path, dut_class=None, app_class=None)
|
||||||
|
|
||||||
@ -70,6 +70,7 @@ class Env(object):
|
|||||||
:param app_path: application path, app instance will use this path to process application info
|
:param app_path: application path, app instance will use this path to process application info
|
||||||
:param dut_class: dut class, if not specified will use default dut class of env
|
:param dut_class: dut class, if not specified will use default dut class of env
|
||||||
:param app_class: app class, if not specified will use default app of env
|
:param app_class: app class, if not specified will use default app of env
|
||||||
|
:param app_config_name: app build config
|
||||||
:keyword dut_init_args: extra kwargs used when creating DUT instance
|
:keyword dut_init_args: extra kwargs used when creating DUT instance
|
||||||
:return: dut instance
|
:return: dut instance
|
||||||
"""
|
"""
|
||||||
@ -80,7 +81,7 @@ class Env(object):
|
|||||||
dut_class = self.default_dut_cls
|
dut_class = self.default_dut_cls
|
||||||
if app_class is None:
|
if app_class is None:
|
||||||
app_class = self.app_cls
|
app_class = self.app_cls
|
||||||
app_inst = app_class(app_path)
|
detected_target = None
|
||||||
try:
|
try:
|
||||||
port = self.config.get_variable(dut_name)
|
port = self.config.get_variable(dut_name)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@ -89,10 +90,19 @@ class Env(object):
|
|||||||
available_ports = dut_class.list_available_ports()
|
available_ports = dut_class.list_available_ports()
|
||||||
for port in available_ports:
|
for port in available_ports:
|
||||||
if port not in allocated_ports:
|
if port not in allocated_ports:
|
||||||
if dut_class.confirm_dut(port, app_inst):
|
result, detected_target = dut_class.confirm_dut(port)
|
||||||
|
if result:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
port = None
|
port = None
|
||||||
|
|
||||||
|
app_target = dut_class.TARGET
|
||||||
|
if not app_target:
|
||||||
|
app_target = detected_target
|
||||||
|
if not app_target:
|
||||||
|
raise ValueError("DUT class doesn't specify the target, and autodetection failed")
|
||||||
|
app_inst = app_class(app_path, app_config_name, app_target)
|
||||||
|
|
||||||
if port:
|
if port:
|
||||||
try:
|
try:
|
||||||
dut_config = self.get_variable(dut_name + "_port_config")
|
dut_config = self.get_variable(dut_name + "_port_config")
|
||||||
|
@ -29,10 +29,12 @@ class IDFApp(App.BaseApp):
|
|||||||
IDF_DOWNLOAD_CONFIG_FILE = "download.config"
|
IDF_DOWNLOAD_CONFIG_FILE = "download.config"
|
||||||
IDF_FLASH_ARGS_FILE = "flasher_args.json"
|
IDF_FLASH_ARGS_FILE = "flasher_args.json"
|
||||||
|
|
||||||
def __init__(self, app_path):
|
def __init__(self, app_path, config_name=None, target=None):
|
||||||
super(IDFApp, self).__init__(app_path)
|
super(IDFApp, self).__init__(app_path)
|
||||||
|
self.config_name = config_name
|
||||||
|
self.target = target
|
||||||
self.idf_path = self.get_sdk_path()
|
self.idf_path = self.get_sdk_path()
|
||||||
self.binary_path = self.get_binary_path(app_path)
|
self.binary_path = self.get_binary_path(app_path, config_name)
|
||||||
self.elf_file = self._get_elf_file_path(self.binary_path)
|
self.elf_file = self._get_elf_file_path(self.binary_path)
|
||||||
assert os.path.exists(self.binary_path)
|
assert os.path.exists(self.binary_path)
|
||||||
sdkconfig_dict = self.get_sdkconfig()
|
sdkconfig_dict = self.get_sdkconfig()
|
||||||
@ -87,13 +89,14 @@ class IDFApp(App.BaseApp):
|
|||||||
d[configs[0]] = configs[1].rstrip()
|
d[configs[0]] = configs[1].rstrip()
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def get_binary_path(self, app_path):
|
def get_binary_path(self, app_path, config_name=None):
|
||||||
"""
|
"""
|
||||||
get binary path according to input app_path.
|
get binary path according to input app_path.
|
||||||
|
|
||||||
subclass must overwrite this method.
|
subclass must overwrite this method.
|
||||||
|
|
||||||
:param app_path: path of application
|
:param app_path: path of application
|
||||||
|
:param config_name: name of the application build config
|
||||||
:return: abs app binary path
|
:return: abs app binary path
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
@ -206,59 +209,65 @@ class Example(IDFApp):
|
|||||||
"""
|
"""
|
||||||
return [os.path.join(self.binary_path, "..", "sdkconfig")]
|
return [os.path.join(self.binary_path, "..", "sdkconfig")]
|
||||||
|
|
||||||
def get_binary_path(self, app_path):
|
def get_binary_path(self, app_path, config_name=None):
|
||||||
# build folder of example path
|
# build folder of example path
|
||||||
path = os.path.join(self.idf_path, app_path, "build")
|
path = os.path.join(self.idf_path, app_path, "build")
|
||||||
if not os.path.exists(path):
|
if os.path.exists(path):
|
||||||
# search for CI build folders
|
return path
|
||||||
app = os.path.basename(app_path)
|
|
||||||
example_path = os.path.join(self.idf_path, "build_examples", "example_builds")
|
if not config_name:
|
||||||
# example_path has subdirectories named after targets. So we need to look into only the right
|
config_name = "default"
|
||||||
# subdirectory. Currently, the target is not known at this moment.
|
|
||||||
for dirpath, dirnames, files in os.walk(example_path):
|
# Search for CI build folders.
|
||||||
if dirnames:
|
# Path format: $IDF_PATH/build_examples/app_path_with_underscores/config/target
|
||||||
if dirnames[0] == app:
|
# (see tools/ci/build_examples_cmake.sh)
|
||||||
path = os.path.join(example_path, dirpath, dirnames[0], "build")
|
# For example: $IDF_PATH/build_examples/examples_get-started_blink/default/esp32
|
||||||
break
|
app_path_underscored = app_path.replace(os.path.sep, "_")
|
||||||
else:
|
example_path = os.path.join(self.idf_path, "build_examples")
|
||||||
raise OSError("Failed to find example binary")
|
for dirpath in os.listdir(example_path):
|
||||||
return path
|
if os.path.basename(dirpath) == app_path_underscored:
|
||||||
|
path = os.path.join(example_path, dirpath, config_name, self.target, "build")
|
||||||
|
return path
|
||||||
|
|
||||||
|
raise OSError("Failed to find example binary")
|
||||||
|
|
||||||
|
|
||||||
class UT(IDFApp):
|
class UT(IDFApp):
|
||||||
def get_binary_path(self, app_path):
|
def get_binary_path(self, app_path, config_name=None):
|
||||||
"""
|
"""
|
||||||
:param app_path: app path or app config
|
:param app_path: app path
|
||||||
|
:param config_name: config name
|
||||||
:return: binary path
|
:return: binary path
|
||||||
"""
|
"""
|
||||||
if not app_path:
|
if not config_name:
|
||||||
app_path = "default"
|
config_name = "default"
|
||||||
|
|
||||||
path = os.path.join(self.idf_path, app_path)
|
path = os.path.join(self.idf_path, app_path)
|
||||||
if not os.path.exists(path):
|
default_build_path = os.path.join(path, "build")
|
||||||
while True:
|
if os.path.exists(default_build_path):
|
||||||
# try to get by config
|
return path
|
||||||
if app_path == "default":
|
|
||||||
# it's default config, we first try to get form build folder of unit-test-app
|
# first try to get from build folder of unit-test-app
|
||||||
path = os.path.join(self.idf_path, "tools", "unit-test-app", "build")
|
path = os.path.join(self.idf_path, "tools", "unit-test-app", "build")
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
# found, use bin in build path
|
# found, use bin in build path
|
||||||
break
|
return path
|
||||||
# ``make ut-build-all-configs`` or ``make ut-build-CONFIG`` will copy binary to output folder
|
|
||||||
path = os.path.join(self.idf_path, "tools", "unit-test-app", "output", app_path)
|
# ``make ut-build-all-configs`` or ``make ut-build-CONFIG`` will copy binary to output folder
|
||||||
if os.path.exists(path):
|
path = os.path.join(self.idf_path, "tools", "unit-test-app", "output", config_name)
|
||||||
break
|
if os.path.exists(path):
|
||||||
raise OSError("Failed to get unit-test-app binary path")
|
return path
|
||||||
return path
|
|
||||||
|
raise OSError("Failed to get unit-test-app binary path")
|
||||||
|
|
||||||
|
|
||||||
class SSC(IDFApp):
|
class SSC(IDFApp):
|
||||||
def get_binary_path(self, app_path):
|
def get_binary_path(self, app_path, config_name=None):
|
||||||
# TODO: to implement SSC get binary path
|
# TODO: to implement SSC get binary path
|
||||||
return app_path
|
return app_path
|
||||||
|
|
||||||
|
|
||||||
class AT(IDFApp):
|
class AT(IDFApp):
|
||||||
def get_binary_path(self, app_path):
|
def get_binary_path(self, app_path, config_name=None):
|
||||||
# TODO: to implement AT get binary path
|
# TODO: to implement AT get binary path
|
||||||
return app_path
|
return app_path
|
||||||
|
@ -152,7 +152,6 @@ class IDFDUT(DUT.SerialDUT):
|
|||||||
# if need to erase NVS partition in start app
|
# if need to erase NVS partition in start app
|
||||||
ERASE_NVS = True
|
ERASE_NVS = True
|
||||||
RECV_THREAD_CLS = IDFRecvThread
|
RECV_THREAD_CLS = IDFRecvThread
|
||||||
TOOLCHAIN_PREFIX = "xtensa-esp32-elf-"
|
|
||||||
|
|
||||||
def __init__(self, name, port, log_file, app, allow_dut_exception=False, **kwargs):
|
def __init__(self, name, port, log_file, app, allow_dut_exception=False, **kwargs):
|
||||||
super(IDFDUT, self).__init__(name, port, log_file, app, **kwargs)
|
super(IDFDUT, self).__init__(name, port, log_file, app, **kwargs)
|
||||||
@ -174,6 +173,7 @@ class IDFDUT(DUT.SerialDUT):
|
|||||||
:param port: serial port as string
|
:param port: serial port as string
|
||||||
:return: MAC address or None
|
:return: MAC address or None
|
||||||
"""
|
"""
|
||||||
|
esp = None
|
||||||
try:
|
try:
|
||||||
esp = cls._get_rom()(port)
|
esp = cls._get_rom()(port)
|
||||||
esp.connect()
|
esp.connect()
|
||||||
@ -181,12 +181,13 @@ class IDFDUT(DUT.SerialDUT):
|
|||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
return None
|
return None
|
||||||
finally:
|
finally:
|
||||||
# do hard reset after use esptool
|
if esp:
|
||||||
esp.hard_reset()
|
# do hard reset after use esptool
|
||||||
esp._port.close()
|
esp.hard_reset()
|
||||||
|
esp._port.close()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def confirm_dut(cls, port, app, **kwargs):
|
def confirm_dut(cls, port, **kwargs):
|
||||||
inst = None
|
inst = None
|
||||||
try:
|
try:
|
||||||
expected_rom_class = cls._get_rom()
|
expected_rom_class = cls._get_rom()
|
||||||
@ -199,9 +200,9 @@ class IDFDUT(DUT.SerialDUT):
|
|||||||
inst = esptool.ESPLoader.detect_chip(port)
|
inst = esptool.ESPLoader.detect_chip(port)
|
||||||
if expected_rom_class and type(inst) != expected_rom_class:
|
if expected_rom_class and type(inst) != expected_rom_class:
|
||||||
raise RuntimeError("Target not expected")
|
raise RuntimeError("Target not expected")
|
||||||
return inst.read_mac() is not None
|
return inst.read_mac() is not None, get_target_by_rom_class(type(inst))
|
||||||
except(esptool.FatalError, RuntimeError):
|
except(esptool.FatalError, RuntimeError):
|
||||||
return False
|
return False, None
|
||||||
finally:
|
finally:
|
||||||
if inst is not None:
|
if inst is not None:
|
||||||
inst._port.close()
|
inst._port.close()
|
||||||
@ -415,18 +416,31 @@ class IDFDUT(DUT.SerialDUT):
|
|||||||
|
|
||||||
|
|
||||||
class ESP32DUT(IDFDUT):
|
class ESP32DUT(IDFDUT):
|
||||||
|
TARGET = "esp32"
|
||||||
|
TOOLCHAIN_PREFIX = "xtensa-esp32-elf-"
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_rom(cls):
|
def _get_rom(cls):
|
||||||
return esptool.ESP32ROM
|
return esptool.ESP32ROM
|
||||||
|
|
||||||
|
|
||||||
class ESP32S2DUT(IDFDUT):
|
class ESP32S2DUT(IDFDUT):
|
||||||
|
TARGET = "esp32s2beta"
|
||||||
|
TOOLCHAIN_PREFIX = "xtensa-esp32s2-elf-"
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_rom(cls):
|
def _get_rom(cls):
|
||||||
return esptool.ESP32S2ROM
|
return esptool.ESP32S2ROM
|
||||||
|
|
||||||
|
|
||||||
class ESP8266DUT(IDFDUT):
|
class ESP8266DUT(IDFDUT):
|
||||||
|
TARGET = "esp8266"
|
||||||
|
TOOLCHAIN_PREFIX = "xtensa-lx106-elf-"
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_rom(cls):
|
def _get_rom(cls):
|
||||||
return esptool.ESP8266ROM
|
return esptool.ESP8266ROM
|
||||||
|
|
||||||
|
|
||||||
|
def get_target_by_rom_class(cls):
|
||||||
|
for c in [ESP32DUT, ESP32S2DUT, ESP8266DUT]:
|
||||||
|
if c._get_rom() == cls:
|
||||||
|
return c.TARGET
|
||||||
|
return None
|
||||||
|
@ -25,7 +25,7 @@ def format_case_id(chip, case_name):
|
|||||||
|
|
||||||
|
|
||||||
def idf_example_test(app=Example, dut=IDFDUT, chip="ESP32", module="examples", execution_time=1,
|
def idf_example_test(app=Example, dut=IDFDUT, chip="ESP32", module="examples", execution_time=1,
|
||||||
level="example", erase_nvs=True, **kwargs):
|
level="example", erase_nvs=True, config_name=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
decorator for testing idf examples (with default values for some keyword args).
|
decorator for testing idf examples (with default values for some keyword args).
|
||||||
|
|
||||||
@ -36,6 +36,7 @@ def idf_example_test(app=Example, dut=IDFDUT, chip="ESP32", module="examples", e
|
|||||||
:param execution_time: execution time in minutes, int
|
:param execution_time: execution time in minutes, int
|
||||||
:param level: test level, could be used to filter test cases, string
|
:param level: test level, could be used to filter test cases, string
|
||||||
:param erase_nvs: if need to erase_nvs in DUT.start_app()
|
:param erase_nvs: if need to erase_nvs in DUT.start_app()
|
||||||
|
:param config_name: if specified, name of the app configuration
|
||||||
:param kwargs: other keyword args
|
:param kwargs: other keyword args
|
||||||
:return: test method
|
:return: test method
|
||||||
"""
|
"""
|
||||||
|
@ -56,6 +56,7 @@ FINISH_PATTERN = re.compile(r"1 Tests (\d) Failures (\d) Ignored")
|
|||||||
END_LIST_STR = r'\r?\nEnter test for running'
|
END_LIST_STR = r'\r?\nEnter test for running'
|
||||||
TEST_PATTERN = re.compile(r'\((\d+)\)\s+"([^"]+)" ([^\r\n]+)\r?\n(' + END_LIST_STR + r')?')
|
TEST_PATTERN = re.compile(r'\((\d+)\)\s+"([^"]+)" ([^\r\n]+)\r?\n(' + END_LIST_STR + r')?')
|
||||||
TEST_SUBMENU_PATTERN = re.compile(r'\s+\((\d+)\)\s+"[^"]+"\r?\n(?=(?=\()|(' + END_LIST_STR + r'))')
|
TEST_SUBMENU_PATTERN = re.compile(r'\s+\((\d+)\)\s+"[^"]+"\r?\n(?=(?=\()|(' + END_LIST_STR + r'))')
|
||||||
|
UT_APP_PATH = "tools/unit-test-app"
|
||||||
|
|
||||||
SIMPLE_TEST_ID = 0
|
SIMPLE_TEST_ID = 0
|
||||||
MULTI_STAGE_ID = 1
|
MULTI_STAGE_ID = 1
|
||||||
@ -284,7 +285,7 @@ def run_unit_test_cases(env, extra_data):
|
|||||||
|
|
||||||
for ut_config in case_config:
|
for ut_config in case_config:
|
||||||
Utility.console_log("Running unit test for config: " + ut_config, "O")
|
Utility.console_log("Running unit test for config: " + ut_config, "O")
|
||||||
dut = env.get_dut("unit-test-app", app_path=ut_config, allow_dut_exception=True)
|
dut = env.get_dut("unit-test-app", app_path=UT_APP_PATH, app_config_name=ut_config, allow_dut_exception=True)
|
||||||
if len(case_config[ut_config]) > 0:
|
if len(case_config[ut_config]) > 0:
|
||||||
replace_app_bin(dut, "unit-test-app", case_config[ut_config][0].get('app_bin'))
|
replace_app_bin(dut, "unit-test-app", case_config[ut_config][0].get('app_bin'))
|
||||||
dut.start_app()
|
dut.start_app()
|
||||||
@ -423,7 +424,7 @@ def get_dut(duts, env, name, ut_config, app_bin=None):
|
|||||||
if name in duts:
|
if name in duts:
|
||||||
dut = duts[name]
|
dut = duts[name]
|
||||||
else:
|
else:
|
||||||
dut = env.get_dut(name, app_path=ut_config, allow_dut_exception=True)
|
dut = env.get_dut(name, app_path=UT_APP_PATH, app_config_name=ut_config, allow_dut_exception=True)
|
||||||
duts[name] = dut
|
duts[name] = dut
|
||||||
replace_app_bin(dut, "unit-test-app", app_bin)
|
replace_app_bin(dut, "unit-test-app", app_bin)
|
||||||
dut.start_app() # download bin to board
|
dut.start_app() # download bin to board
|
||||||
@ -638,7 +639,7 @@ def run_multiple_stage_cases(env, extra_data):
|
|||||||
|
|
||||||
for ut_config in case_config:
|
for ut_config in case_config:
|
||||||
Utility.console_log("Running unit test for config: " + ut_config, "O")
|
Utility.console_log("Running unit test for config: " + ut_config, "O")
|
||||||
dut = env.get_dut("unit-test-app", app_path=ut_config, allow_dut_exception=True)
|
dut = env.get_dut("unit-test-app", app_path=UT_APP_PATH, app_config_name=ut_config, allow_dut_exception=True)
|
||||||
if len(case_config[ut_config]) > 0:
|
if len(case_config[ut_config]) > 0:
|
||||||
replace_app_bin(dut, "unit-test-app", case_config[ut_config][0].get('app_bin'))
|
replace_app_bin(dut, "unit-test-app", case_config[ut_config][0].get('app_bin'))
|
||||||
dut.start_app()
|
dut.start_app()
|
||||||
@ -671,7 +672,7 @@ def detect_update_unit_test_info(env, extra_data, app_bin):
|
|||||||
case_config = format_test_case_config(extra_data)
|
case_config = format_test_case_config(extra_data)
|
||||||
|
|
||||||
for ut_config in case_config:
|
for ut_config in case_config:
|
||||||
dut = env.get_dut("unit-test-app", app_path=ut_config)
|
dut = env.get_dut("unit-test-app", app_path=UT_APP_PATH, app_config_name=ut_config)
|
||||||
replace_app_bin(dut, "unit-test-app", app_bin)
|
replace_app_bin(dut, "unit-test-app", app_bin)
|
||||||
dut.start_app()
|
dut.start_app()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user