test: support loadable elf test cases:
loadable elf example is different for 2 reasons: 1. loadable elf binary don't have flasher_args.json, so we can't use normal to get from CI artifacts 2. it don't have binary and don't need to downloaded binary to DUT. Some related functions can be ignored
This commit is contained in:
parent
cca08b3d2b
commit
164e8ba21f
3 changed files with 104 additions and 53 deletions
|
@ -121,12 +121,13 @@ class SerialThread(object):
|
||||||
@ttfw_idf.idf_example_test(env_tag="test_jtag_arm")
|
@ttfw_idf.idf_example_test(env_tag="test_jtag_arm")
|
||||||
def test_examples_loadable_elf(env, extra_data):
|
def test_examples_loadable_elf(env, extra_data):
|
||||||
|
|
||||||
idf_path = os.environ['IDF_PATH']
|
|
||||||
rel_project_path = os.path.join('examples', 'get-started', 'hello_world')
|
rel_project_path = os.path.join('examples', 'get-started', 'hello_world')
|
||||||
|
app_files = ['hello-world.elf', 'partition_table/partition-table.bin']
|
||||||
|
example = ttfw_idf.LoadableElfExample(rel_project_path, app_files, target="esp32")
|
||||||
|
idf_path = example.get_sdk_path()
|
||||||
proj_path = os.path.join(idf_path, rel_project_path)
|
proj_path = os.path.join(idf_path, rel_project_path)
|
||||||
example = ttfw_idf.Example(rel_project_path, target="esp32")
|
|
||||||
sdkconfig = example.get_sdkconfig()
|
sdkconfig = example.get_sdkconfig()
|
||||||
elf_path = os.path.join(example.get_binary_path(rel_project_path), 'hello-world.elf')
|
elf_path = os.path.join(example.binary_path, 'hello-world.elf')
|
||||||
esp_log_path = os.path.join(proj_path, 'esp.log')
|
esp_log_path = os.path.join(proj_path, 'esp.log')
|
||||||
|
|
||||||
assert(sdkconfig['CONFIG_IDF_TARGET_ESP32'] == 'y'), "Only ESP32 target is supported"
|
assert(sdkconfig['CONFIG_IDF_TARGET_ESP32'] == 'y'), "Only ESP32 target is supported"
|
||||||
|
|
|
@ -35,6 +35,7 @@ def parse_flash_settings(path):
|
||||||
args = json.load(f)
|
args = json.load(f)
|
||||||
flash_files = [(offs, binary) for (offs, binary) in args["flash_files"].items() if offs != ""]
|
flash_files = [(offs, binary) for (offs, binary) in args["flash_files"].items() if offs != ""]
|
||||||
flash_settings = args["flash_settings"]
|
flash_settings = args["flash_settings"]
|
||||||
|
app_name = os.path.splitext(args["app"]["file"])[0]
|
||||||
else:
|
else:
|
||||||
# GNU Make version uses download.config arguments file
|
# GNU Make version uses download.config arguments file
|
||||||
with open(path, "r") as f:
|
with open(path, "r") as f:
|
||||||
|
@ -48,14 +49,28 @@ def parse_flash_settings(path):
|
||||||
else:
|
else:
|
||||||
# offs, filename
|
# offs, filename
|
||||||
flash_files.append((args[idx], args[idx + 1]))
|
flash_files.append((args[idx], args[idx + 1]))
|
||||||
return flash_files, flash_settings
|
# we can only guess app name in download.config.
|
||||||
|
for p in flash_files:
|
||||||
|
if not os.path.dirname(p[1]) and "partition" not in p[1]:
|
||||||
|
# app bin usually in the same dir with download.config and it's not partition table
|
||||||
|
app_name = os.path.splitext(p[1])[0]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
app_name = None
|
||||||
|
return flash_files, flash_settings, app_name
|
||||||
|
|
||||||
|
|
||||||
class Artifacts(object):
|
class Artifacts(object):
|
||||||
def __init__(self, dest_root_path):
|
def __init__(self, dest_root_path, artifact_index_file, app_path, config_name, target):
|
||||||
assert gitlab_api
|
assert gitlab_api
|
||||||
|
# at least one of app_path or config_name is not None. otherwise we can't match artifact
|
||||||
|
assert app_path or config_name
|
||||||
|
assert os.path.exists(artifact_index_file)
|
||||||
self.gitlab_inst = gitlab_api.Gitlab(os.getenv("CI_PROJECT_ID"))
|
self.gitlab_inst = gitlab_api.Gitlab(os.getenv("CI_PROJECT_ID"))
|
||||||
self.dest_root_path = dest_root_path
|
self.dest_root_path = dest_root_path
|
||||||
|
with open(artifact_index_file, "r") as f:
|
||||||
|
artifact_index = json.load(f)
|
||||||
|
self.artifact_info = self._find_artifact(artifact_index, app_path, config_name, target)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _find_artifact(artifact_index, app_path, config_name, target):
|
def _find_artifact(artifact_index, app_path, config_name, target):
|
||||||
|
@ -74,21 +89,13 @@ class Artifacts(object):
|
||||||
ret = None
|
ret = None
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def download_artifact(self, artifact_index_file, app_path, config_name, target):
|
def download_artifacts(self):
|
||||||
# at least one of app_path or config_name is not None. otherwise we can't match artifact
|
if self.artifact_info:
|
||||||
assert app_path or config_name
|
base_path = os.path.join(self.artifact_info["work_dir"], self.artifact_info["build_dir"])
|
||||||
assert os.path.exists(artifact_index_file)
|
job_id = self.artifact_info["ci_job_id"]
|
||||||
with open(artifact_index_file, "r") as f:
|
|
||||||
artifact_index = json.load(f)
|
|
||||||
|
|
||||||
artifact_info = self._find_artifact(artifact_index, app_path, config_name, target)
|
|
||||||
|
|
||||||
if artifact_info:
|
|
||||||
base_path = os.path.join(artifact_info["work_dir"], artifact_info["build_dir"])
|
|
||||||
job_id = artifact_info["ci_job_id"]
|
|
||||||
|
|
||||||
# 1. download flash args file
|
# 1. download flash args file
|
||||||
if artifact_info["build_system"] == "cmake":
|
if self.artifact_info["build_system"] == "cmake":
|
||||||
flash_arg_file = os.path.join(base_path, "flasher_args.json")
|
flash_arg_file = os.path.join(base_path, "flasher_args.json")
|
||||||
else:
|
else:
|
||||||
flash_arg_file = os.path.join(base_path, "download.config")
|
flash_arg_file = os.path.join(base_path, "download.config")
|
||||||
|
@ -96,14 +103,10 @@ class Artifacts(object):
|
||||||
self.gitlab_inst.download_artifact(job_id, [flash_arg_file], self.dest_root_path)
|
self.gitlab_inst.download_artifact(job_id, [flash_arg_file], self.dest_root_path)
|
||||||
|
|
||||||
# 2. download all binary files
|
# 2. download all binary files
|
||||||
flash_files, flash_settings = parse_flash_settings(os.path.join(self.dest_root_path, flash_arg_file))
|
flash_files, flash_settings, app_name = parse_flash_settings(os.path.join(self.dest_root_path,
|
||||||
artifact_files = []
|
flash_arg_file))
|
||||||
for p in flash_files:
|
artifact_files = [os.path.join(base_path, p[1]) for p in flash_files]
|
||||||
artifact_files.append(os.path.join(base_path, p[1]))
|
artifact_files.append(os.path.join(base_path, app_name + ".elf"))
|
||||||
if not os.path.dirname(p[1]):
|
|
||||||
# find app bin and also download elf
|
|
||||||
elf_file = os.path.splitext(p[1])[0] + ".elf"
|
|
||||||
artifact_files.append(os.path.join(base_path, elf_file))
|
|
||||||
|
|
||||||
self.gitlab_inst.download_artifact(job_id, artifact_files, self.dest_root_path)
|
self.gitlab_inst.download_artifact(job_id, artifact_files, self.dest_root_path)
|
||||||
|
|
||||||
|
@ -114,6 +117,22 @@ class Artifacts(object):
|
||||||
base_path = None
|
base_path = None
|
||||||
return base_path
|
return base_path
|
||||||
|
|
||||||
|
def download_artifact_files(self, file_names):
|
||||||
|
if self.artifact_info:
|
||||||
|
base_path = os.path.join(self.artifact_info["work_dir"], self.artifact_info["build_dir"])
|
||||||
|
job_id = self.artifact_info["ci_job_id"]
|
||||||
|
|
||||||
|
# download all binary files
|
||||||
|
artifact_files = [os.path.join(base_path, fn) for fn in file_names]
|
||||||
|
self.gitlab_inst.download_artifact(job_id, artifact_files, self.dest_root_path)
|
||||||
|
|
||||||
|
# download sdkconfig file
|
||||||
|
self.gitlab_inst.download_artifact(job_id, [os.path.join(os.path.dirname(base_path), "sdkconfig")],
|
||||||
|
self.dest_root_path)
|
||||||
|
else:
|
||||||
|
base_path = None
|
||||||
|
return base_path
|
||||||
|
|
||||||
|
|
||||||
class IDFApp(App.BaseApp):
|
class IDFApp(App.BaseApp):
|
||||||
"""
|
"""
|
||||||
|
@ -132,9 +151,6 @@ class IDFApp(App.BaseApp):
|
||||||
self.binary_path = self.get_binary_path(app_path, config_name, target)
|
self.binary_path = self.get_binary_path(app_path, config_name, target)
|
||||||
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()
|
|
||||||
if "CONFIG_APP_BUILD_GENERATE_BINARIES" in sdkconfig_dict:
|
|
||||||
# There are no flashing targets available when no binaries where generated.
|
|
||||||
if self.IDF_DOWNLOAD_CONFIG_FILE not in os.listdir(self.binary_path):
|
if self.IDF_DOWNLOAD_CONFIG_FILE not in os.listdir(self.binary_path):
|
||||||
if self.IDF_FLASH_ARGS_FILE not in os.listdir(self.binary_path):
|
if self.IDF_FLASH_ARGS_FILE not in os.listdir(self.binary_path):
|
||||||
msg = ("Neither {} nor {} exists. "
|
msg = ("Neither {} nor {} exists. "
|
||||||
|
@ -150,6 +166,7 @@ class IDFApp(App.BaseApp):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_sdk_path(cls):
|
def get_sdk_path(cls):
|
||||||
|
# type: () -> str
|
||||||
idf_path = os.getenv("IDF_PATH")
|
idf_path = os.getenv("IDF_PATH")
|
||||||
assert idf_path
|
assert idf_path
|
||||||
assert os.path.exists(idf_path)
|
assert os.path.exists(idf_path)
|
||||||
|
@ -184,6 +201,7 @@ class IDFApp(App.BaseApp):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def get_binary_path(self, app_path, config_name=None, target=None):
|
def get_binary_path(self, app_path, config_name=None, target=None):
|
||||||
|
# type: (str, str, str) -> str
|
||||||
"""
|
"""
|
||||||
get binary path according to input app_path.
|
get binary path according to input app_path.
|
||||||
|
|
||||||
|
@ -223,7 +241,7 @@ class IDFApp(App.BaseApp):
|
||||||
# GNU Make version uses download.config arguments file
|
# GNU Make version uses download.config arguments file
|
||||||
path = os.path.join(self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE)
|
path = os.path.join(self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE)
|
||||||
|
|
||||||
flash_files, flash_settings = parse_flash_settings(path)
|
flash_files, flash_settings, app_name = parse_flash_settings(path)
|
||||||
# The build metadata file does not currently have details, which files should be encrypted and which not.
|
# The build metadata file does not currently have details, which files should be encrypted and which not.
|
||||||
# Assume that all files should be encrypted if flash encryption is enabled in development mode.
|
# Assume that all files should be encrypted if flash encryption is enabled in development mode.
|
||||||
sdkconfig_dict = self.get_sdkconfig()
|
sdkconfig_dict = self.get_sdkconfig()
|
||||||
|
@ -292,7 +310,7 @@ 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, config_name=None, target=None):
|
def _try_get_binary_from_local_fs(self, app_path, config_name=None, target=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 os.path.exists(path):
|
if os.path.exists(path):
|
||||||
|
@ -309,16 +327,48 @@ class Example(IDFApp):
|
||||||
example_path = os.path.join(self.idf_path, "build_examples")
|
example_path = os.path.join(self.idf_path, "build_examples")
|
||||||
for dirpath in os.listdir(example_path):
|
for dirpath in os.listdir(example_path):
|
||||||
if os.path.basename(dirpath) == app_path_underscored:
|
if os.path.basename(dirpath) == app_path_underscored:
|
||||||
path = os.path.join(example_path, dirpath, config_name, self.target, "build")
|
path = os.path.join(example_path, dirpath, config_name, target, "build")
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
return path
|
return path
|
||||||
else:
|
else:
|
||||||
# app path exists, but config name not exists. try to download artifacts.
|
return None
|
||||||
break
|
|
||||||
|
|
||||||
artifacts = Artifacts(self.idf_path)
|
def get_binary_path(self, app_path, config_name=None, target=None):
|
||||||
path = artifacts.download_artifact(CIAssignExampleTest.ARTIFACT_INDEX_FILE,
|
path = self._try_get_binary_from_local_fs(app_path, config_name, target)
|
||||||
|
if path:
|
||||||
|
return path
|
||||||
|
else:
|
||||||
|
artifacts = Artifacts(self.idf_path, CIAssignExampleTest.ARTIFACT_INDEX_FILE,
|
||||||
app_path, config_name, target)
|
app_path, config_name, target)
|
||||||
|
path = artifacts.download_artifacts()
|
||||||
|
if path:
|
||||||
|
return os.path.join(self.idf_path, path)
|
||||||
|
else:
|
||||||
|
raise OSError("Failed to find example binary")
|
||||||
|
|
||||||
|
|
||||||
|
class LoadableElfExample(Example):
|
||||||
|
def __init__(self, app_path, app_files, config_name=None, target=None):
|
||||||
|
# add arg `app_files` for loadable elf example.
|
||||||
|
# Such examples only build elf files, so it doesn't generate flasher_args.json.
|
||||||
|
# So we can't get app files from config file. Test case should pass it to application.
|
||||||
|
super(IDFApp, self).__init__(app_path)
|
||||||
|
self.app_files = app_files
|
||||||
|
self.config_name = config_name
|
||||||
|
self.target = target
|
||||||
|
self.idf_path = self.get_sdk_path()
|
||||||
|
self.binary_path = self.get_binary_path(app_path, config_name, target)
|
||||||
|
self.elf_file = self._get_elf_file_path(self.binary_path)
|
||||||
|
assert os.path.exists(self.binary_path)
|
||||||
|
|
||||||
|
def get_binary_path(self, app_path, config_name=None, target=None):
|
||||||
|
path = self._try_get_binary_from_local_fs(app_path, config_name, target)
|
||||||
|
if path:
|
||||||
|
return path
|
||||||
|
else:
|
||||||
|
artifacts = Artifacts(self.idf_path, CIAssignExampleTest.ARTIFACT_INDEX_FILE,
|
||||||
|
app_path, config_name, target)
|
||||||
|
path = artifacts.download_artifact_files(self.app_files)
|
||||||
if path:
|
if path:
|
||||||
return os.path.join(self.idf_path, path)
|
return os.path.join(self.idf_path, path)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -15,7 +15,7 @@ import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from tiny_test_fw import TinyFW, Utility
|
from tiny_test_fw import TinyFW, Utility
|
||||||
from IDFApp import IDFApp, Example, UT
|
from IDFApp import IDFApp, Example, LoadableElfExample, UT # noqa: export all Apps for users
|
||||||
from IDFDUT import IDFDUT, ESP32DUT, ESP32S2DUT, ESP8266DUT # noqa: export DUTs for users
|
from IDFDUT import IDFDUT, ESP32DUT, ESP32S2DUT, ESP8266DUT # noqa: export DUTs for users
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue