twister: Add support for required snippets
Adds support for twister to require using snippets on tests Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
This commit is contained in:
parent
0af7770132
commit
bc97d8fb1e
7 changed files with 86 additions and 1 deletions
|
@ -48,6 +48,7 @@ class TwisterConfigParser:
|
|||
"extra_conf_files": {"type": "list", "default": []},
|
||||
"extra_overlay_confs" : {"type": "list", "default": []},
|
||||
"extra_dtc_overlay_files": {"type": "list", "default": []},
|
||||
"required_snippets": {"type": "list"},
|
||||
"build_only": {"type": "bool", "default": False},
|
||||
"build_on_all": {"type": "bool", "default": False},
|
||||
"skip": {"type": "bool", "default": False},
|
||||
|
|
|
@ -359,6 +359,10 @@ class CMake:
|
|||
cmake_opts = ['-DBOARD={}'.format(self.platform.name)]
|
||||
cmake_args.extend(cmake_opts)
|
||||
|
||||
if self.instance.testsuite.required_snippets:
|
||||
cmake_opts = ['-DSNIPPET={}'.format(';'.join(self.instance.testsuite.required_snippets))]
|
||||
cmake_args.extend(cmake_opts)
|
||||
|
||||
cmake = shutil.which('cmake')
|
||||
cmd = [cmake] + cmake_args
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ import logging
|
|||
import copy
|
||||
import shutil
|
||||
import random
|
||||
import snippets
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger('twister')
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
@ -819,6 +821,46 @@ class TestPlan:
|
|||
if plat.only_tags and not set(plat.only_tags) & ts.tags:
|
||||
instance.add_filter("Excluded tags per platform (only_tags)", Filters.PLATFORM)
|
||||
|
||||
if ts.required_snippets:
|
||||
missing_snippet = False
|
||||
snippet_args = {"snippets": ts.required_snippets}
|
||||
found_snippets = snippets.find_snippets_in_roots(snippet_args, [Path(ZEPHYR_BASE), Path(ts.source_dir)])
|
||||
|
||||
# Search and check that all required snippet files are found
|
||||
for this_snippet in snippet_args['snippets']:
|
||||
if this_snippet not in found_snippets:
|
||||
logger.error(f"Can't find snippet '%s' for test '%s'", this_snippet, ts.name)
|
||||
instance.status = "error"
|
||||
instance.reason = f"Snippet {this_snippet} not found"
|
||||
missing_snippet = True
|
||||
break
|
||||
|
||||
if not missing_snippet:
|
||||
# Look for required snippets and check that they are applicable for these
|
||||
# platforms/boards
|
||||
for this_snippet in found_snippets:
|
||||
matched_snippet_board = False
|
||||
|
||||
# If the "appends" key is present with at least one entry then this
|
||||
# snippet applies to all boards and further platform-specific checks
|
||||
# are not required
|
||||
if found_snippets[this_snippet].appends:
|
||||
continue
|
||||
|
||||
for this_board in found_snippets[this_snippet].board2appends:
|
||||
if this_board.startswith('/'):
|
||||
match = re.search(this_board[1:-1], plat.name)
|
||||
if match is not None:
|
||||
matched_snippet_board = True
|
||||
break
|
||||
elif this_board == plat.name:
|
||||
matched_snippet_board = True
|
||||
break
|
||||
|
||||
if matched_snippet_board is False:
|
||||
instance.add_filter("Snippet not supported", Filters.PLATFORM)
|
||||
break
|
||||
|
||||
# platform_key is a list of unique platform attributes that form a unique key a test
|
||||
# will match against to determine if it should be scheduled to run. A key containing a
|
||||
# field name that the platform does not have will filter the platform.
|
||||
|
|
|
@ -145,6 +145,11 @@ mapping:
|
|||
matching: "all"
|
||||
sequence:
|
||||
- type: str
|
||||
"required_snippets":
|
||||
type: seq
|
||||
required: false
|
||||
sequence:
|
||||
- type: str
|
||||
"tags":
|
||||
type: any
|
||||
required: false
|
||||
|
@ -243,6 +248,11 @@ mapping:
|
|||
"extra_sections":
|
||||
type: any
|
||||
required: false
|
||||
"required_snippets":
|
||||
type: seq
|
||||
required: false
|
||||
sequence:
|
||||
- type: str
|
||||
"filter":
|
||||
type: str
|
||||
required: false
|
||||
|
|
|
@ -238,6 +238,22 @@ def process_snippets(args: argparse.Namespace) -> Snippets:
|
|||
|
||||
return snippets
|
||||
|
||||
def find_snippets_in_roots(requested_snippets, snippet_roots) -> Snippets:
|
||||
'''Process snippet.yml files under each *snippet_root*
|
||||
by recursive search. Return a Snippets object describing
|
||||
the results of the search.
|
||||
'''
|
||||
# This will contain information about all the snippets
|
||||
# we discover in each snippet_root element.
|
||||
snippets = Snippets(requested=requested_snippets)
|
||||
|
||||
# Process each path in snippet_root in order, adjusting
|
||||
# snippets as needed for each one.
|
||||
for root in snippet_roots:
|
||||
process_snippets_in(root, snippets)
|
||||
|
||||
return snippets
|
||||
|
||||
def process_snippets_in(root_dir: Path, snippets: Snippets) -> None:
|
||||
'''Process snippet.yml files in *root_dir*,
|
||||
updating *snippets* as needed.'''
|
||||
|
|
|
@ -44,6 +44,9 @@ pairs:
|
|||
Extra configuration options to be merged with a master prj.conf
|
||||
when building or running the test case.
|
||||
|
||||
required_snippets: <list of snippets>
|
||||
Snippets that must be applied for the test case to run.
|
||||
|
||||
sysbuild: <True|False> (default False)
|
||||
If true, build the sample using the sysbuild infrastructure. Filtering
|
||||
will only be enabled for the main project, and is not supported for
|
||||
|
|
|
@ -293,6 +293,7 @@ class Build(Forceable):
|
|||
extra_dtc_overlay_files = []
|
||||
extra_overlay_confs = []
|
||||
extra_conf_files = []
|
||||
required_snippets = []
|
||||
for section in [common, item]:
|
||||
if not section:
|
||||
continue
|
||||
|
@ -302,7 +303,8 @@ class Build(Forceable):
|
|||
'extra_configs',
|
||||
'extra_conf_files',
|
||||
'extra_overlay_confs',
|
||||
'extra_dtc_overlay_files'
|
||||
'extra_dtc_overlay_files',
|
||||
'required_snippets'
|
||||
]:
|
||||
extra = section.get(data)
|
||||
if not extra:
|
||||
|
@ -325,6 +327,9 @@ class Build(Forceable):
|
|||
elif data == 'extra_dtc_overlay_files':
|
||||
extra_dtc_overlay_files.extend(arg_list)
|
||||
continue
|
||||
elif data == 'required_snippets':
|
||||
required_snippets.extend(arg_list)
|
||||
continue
|
||||
|
||||
if self.args.cmake_opts:
|
||||
self.args.cmake_opts.extend(args)
|
||||
|
@ -343,6 +348,10 @@ class Build(Forceable):
|
|||
|
||||
if extra_overlay_confs:
|
||||
args.append(f"OVERLAY_CONFIG=\"{';'.join(extra_overlay_confs)}\"")
|
||||
|
||||
if required_snippets:
|
||||
args.append(f"SNIPPET=\"{';'.join(required_snippets)}\"")
|
||||
|
||||
# Build the final argument list
|
||||
args_expanded = ["-D{}".format(a.replace('"', '')) for a in args]
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue