Browse Source

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>
pull/62354/head
Jamie McCrae 2 years ago committed by Carles Cufí
parent
commit
bc97d8fb1e
  1. 1
      scripts/pylib/twister/twisterlib/config_parser.py
  2. 4
      scripts/pylib/twister/twisterlib/runner.py
  3. 42
      scripts/pylib/twister/twisterlib/testplan.py
  4. 10
      scripts/schemas/twister/testsuite-schema.yaml
  5. 16
      scripts/snippets.py
  6. 3
      scripts/twister
  7. 11
      scripts/west_commands/build.py

1
scripts/pylib/twister/twisterlib/config_parser.py

@ -48,6 +48,7 @@ class TwisterConfigParser:
"extra_conf_files": {"type": "list", "default": []}, "extra_conf_files": {"type": "list", "default": []},
"extra_overlay_confs" : {"type": "list", "default": []}, "extra_overlay_confs" : {"type": "list", "default": []},
"extra_dtc_overlay_files": {"type": "list", "default": []}, "extra_dtc_overlay_files": {"type": "list", "default": []},
"required_snippets": {"type": "list"},
"build_only": {"type": "bool", "default": False}, "build_only": {"type": "bool", "default": False},
"build_on_all": {"type": "bool", "default": False}, "build_on_all": {"type": "bool", "default": False},
"skip": {"type": "bool", "default": False}, "skip": {"type": "bool", "default": False},

4
scripts/pylib/twister/twisterlib/runner.py

@ -359,6 +359,10 @@ class CMake:
cmake_opts = ['-DBOARD={}'.format(self.platform.name)] cmake_opts = ['-DBOARD={}'.format(self.platform.name)]
cmake_args.extend(cmake_opts) 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') cmake = shutil.which('cmake')
cmd = [cmake] + cmake_args cmd = [cmake] + cmake_args

42
scripts/pylib/twister/twisterlib/testplan.py

@ -16,6 +16,8 @@ import logging
import copy import copy
import shutil import shutil
import random import random
import snippets
from pathlib import Path
logger = logging.getLogger('twister') logger = logging.getLogger('twister')
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
@ -819,6 +821,46 @@ class TestPlan:
if plat.only_tags and not set(plat.only_tags) & ts.tags: if plat.only_tags and not set(plat.only_tags) & ts.tags:
instance.add_filter("Excluded tags per platform (only_tags)", Filters.PLATFORM) 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 # 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 # 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. # field name that the platform does not have will filter the platform.

10
scripts/schemas/twister/testsuite-schema.yaml

@ -145,6 +145,11 @@ mapping:
matching: "all" matching: "all"
sequence: sequence:
- type: str - type: str
"required_snippets":
type: seq
required: false
sequence:
- type: str
"tags": "tags":
type: any type: any
required: false required: false
@ -243,6 +248,11 @@ mapping:
"extra_sections": "extra_sections":
type: any type: any
required: false required: false
"required_snippets":
type: seq
required: false
sequence:
- type: str
"filter": "filter":
type: str type: str
required: false required: false

16
scripts/snippets.py

@ -238,6 +238,22 @@ def process_snippets(args: argparse.Namespace) -> Snippets:
return 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: def process_snippets_in(root_dir: Path, snippets: Snippets) -> None:
'''Process snippet.yml files in *root_dir*, '''Process snippet.yml files in *root_dir*,
updating *snippets* as needed.''' updating *snippets* as needed.'''

3
scripts/twister

@ -44,6 +44,9 @@ pairs:
Extra configuration options to be merged with a master prj.conf Extra configuration options to be merged with a master prj.conf
when building or running the test case. 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) sysbuild: <True|False> (default False)
If true, build the sample using the sysbuild infrastructure. Filtering If true, build the sample using the sysbuild infrastructure. Filtering
will only be enabled for the main project, and is not supported for will only be enabled for the main project, and is not supported for

11
scripts/west_commands/build.py

@ -293,6 +293,7 @@ class Build(Forceable):
extra_dtc_overlay_files = [] extra_dtc_overlay_files = []
extra_overlay_confs = [] extra_overlay_confs = []
extra_conf_files = [] extra_conf_files = []
required_snippets = []
for section in [common, item]: for section in [common, item]:
if not section: if not section:
continue continue
@ -302,7 +303,8 @@ class Build(Forceable):
'extra_configs', 'extra_configs',
'extra_conf_files', 'extra_conf_files',
'extra_overlay_confs', 'extra_overlay_confs',
'extra_dtc_overlay_files' 'extra_dtc_overlay_files',
'required_snippets'
]: ]:
extra = section.get(data) extra = section.get(data)
if not extra: if not extra:
@ -325,6 +327,9 @@ class Build(Forceable):
elif data == 'extra_dtc_overlay_files': elif data == 'extra_dtc_overlay_files':
extra_dtc_overlay_files.extend(arg_list) extra_dtc_overlay_files.extend(arg_list)
continue continue
elif data == 'required_snippets':
required_snippets.extend(arg_list)
continue
if self.args.cmake_opts: if self.args.cmake_opts:
self.args.cmake_opts.extend(args) self.args.cmake_opts.extend(args)
@ -343,6 +348,10 @@ class Build(Forceable):
if extra_overlay_confs: if extra_overlay_confs:
args.append(f"OVERLAY_CONFIG=\"{';'.join(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 # Build the final argument list
args_expanded = ["-D{}".format(a.replace('"', '')) for a in args] args_expanded = ["-D{}".format(a.replace('"', '')) for a in args]

Loading…
Cancel
Save