From 01c9b0086e43104ea4615e1d05debf32144b4918 Mon Sep 17 00:00:00 2001 From: Maciej Perkowski Date: Fri, 21 Jul 2023 16:09:39 +0200 Subject: [PATCH 1/8] twister: Add option to not include paths in test suites names Test suites names are not being handled uniformly for tests not in zephyr tree. Their names depend on -T arg used in twister's CLI. The newly added option make twister not adding paths to suite names. This is needed if test plans are to be used for tests outside of zephyr tree. Signed-off-by: Maciej Perkowski --- .../pylib/twister/twisterlib/environment.py | 4 ++ scripts/pylib/twister/twisterlib/testplan.py | 2 +- scripts/pylib/twister/twisterlib/testsuite.py | 12 ++++-- scripts/tests/twister/test_testsuite.py | 39 +++++++++++++++++++ 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/scripts/pylib/twister/twisterlib/environment.py b/scripts/pylib/twister/twisterlib/environment.py index 160233a66fe8b..73777c0a1b5a3 100644 --- a/scripts/pylib/twister/twisterlib/environment.py +++ b/scripts/pylib/twister/twisterlib/environment.py @@ -437,6 +437,10 @@ def add_parse_arguments(parser = None): help="Re-use the outdir before building. Will result in " "faster compilation since builds will be incremental.") + parser.add_argument( + "--no-path-name", action="store_true", + help="Don't put paths into test suites' names ") + # To be removed in favor of --detailed-skipped-report parser.add_argument( "--no-skipped-report", action="store_true", diff --git a/scripts/pylib/twister/twisterlib/testplan.py b/scripts/pylib/twister/twisterlib/testplan.py index 85340885dbb6d..8bd8db84b17d4 100755 --- a/scripts/pylib/twister/twisterlib/testplan.py +++ b/scripts/pylib/twister/twisterlib/testplan.py @@ -522,7 +522,7 @@ def add_testsuites(self, testsuite_filter=[]): for name in parsed_data.scenarios.keys(): suite_dict = parsed_data.get_scenario(name) - suite = TestSuite(root, suite_path, name, data=suite_dict) + suite = TestSuite(root, suite_path, name, data=suite_dict, no_path_name=self.options.no_path_name) suite.add_subcases(suite_dict, subcases, ztest_suite_names) if testsuite_filter: if suite.name and suite.name in testsuite_filter: diff --git a/scripts/pylib/twister/twisterlib/testsuite.py b/scripts/pylib/twister/twisterlib/testsuite.py index 3f4a71e4a8c5f..11e0e5a7ba8ac 100644 --- a/scripts/pylib/twister/twisterlib/testsuite.py +++ b/scripts/pylib/twister/twisterlib/testsuite.py @@ -348,7 +348,7 @@ class TestSuite(DisablePyTestCollectionMixin): """Class representing a test application """ - def __init__(self, suite_root, suite_path, name, data=None): + def __init__(self, suite_root, suite_path, name, data=None, no_path_name=False): """TestSuite constructor. This gets called by TestPlan as it finds and reads test yaml files. @@ -369,7 +369,9 @@ def __init__(self, suite_root, suite_path, name, data=None): """ workdir = os.path.relpath(suite_path, suite_root) - self.name = self.get_unique(suite_root, workdir, name) + + assert self.check_suite_name(name, suite_root, workdir) + self.name = name if no_path_name else self.get_unique(suite_root, workdir, name) self.id = name self.source_dir = suite_path @@ -425,10 +427,14 @@ def get_unique(testsuite_root, workdir, name): # workdir can be "." unique = os.path.normpath(os.path.join(relative_ts_root, workdir, name)) + return unique + + @staticmethod + def check_suite_name(name, testsuite_root, workdir): check = name.split(".") if len(check) < 2: raise TwisterException(f"""bad test name '{name}' in {testsuite_root}/{workdir}. \ Tests should reference the category and subsystem with a dot as a separator. """ ) - return unique + return True diff --git a/scripts/tests/twister/test_testsuite.py b/scripts/tests/twister/test_testsuite.py index c954208d75a7b..45495cc2aa190 100644 --- a/scripts/tests/twister/test_testsuite.py +++ b/scripts/tests/twister/test_testsuite.py @@ -110,3 +110,42 @@ def test_get_unique(testsuite_root, suite_path, name, expected): '''Test to check if the unique name is given for each testsuite root and workdir''' suite = TestSuite(testsuite_root, suite_path, name) assert suite.name == expected + +TESTDATA_2 = [ + ( + ZEPHYR_BASE + '/scripts/tests/twister/test_data/testsuites', + ZEPHYR_BASE + '/scripts/tests/twister/test_data/testsuites/tests/test_a', + 'test_a.check_1', + 'test_a.check_1' + ), + ( + ZEPHYR_BASE, + ZEPHYR_BASE, + 'test_a.check_1', + 'test_a.check_1' + ), + ( + ZEPHYR_BASE, + ZEPHYR_BASE + '/scripts/tests/twister/test_data/testsuites/test_b', + 'test_b.check_1', + 'test_b.check_1' + ), + ( + os.path.join(ZEPHYR_BASE, 'scripts/tests'), + os.path.join(ZEPHYR_BASE, 'scripts/tests'), + 'test_b.check_1', + 'test_b.check_1' + ), + ( + ZEPHYR_BASE, + ZEPHYR_BASE, + 'test_a.check_1.check_2', + 'test_a.check_1.check_2' + ), +] +@pytest.mark.parametrize("testsuite_root, suite_path, name, expected", TESTDATA_2) +def test_get_no_path_name(testsuite_root, suite_path, name, expected): + '''Test to check if the name without path is given for each testsuite''' + suite = TestSuite(testsuite_root, suite_path, name, no_path_name=True) + print(suite.name) + assert suite.name == expected From 741b7aa3561015706b5798abd0742d069bfb13c6 Mon Sep 17 00:00:00 2001 From: Maciej Perkowski Date: Mon, 24 Jul 2023 13:24:51 +0200 Subject: [PATCH 2/8] scripts: Add --no-path-name arg to test_plan.py script An option --no-path-name was added to twister to help align names for test outside of zephyr tree. This commit add this arg to test_plan.py script which is then propagated to twister. Signed-off-by: Maciej Perkowski --- scripts/ci/test_plan.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/ci/test_plan.py b/scripts/ci/test_plan.py index 8f6f8eb170b3b..5d9b82f270f89 100755 --- a/scripts/ci/test_plan.py +++ b/scripts/ci/test_plan.py @@ -86,7 +86,7 @@ def __repr__(self): return "".format(self.name) class Filters: - def __init__(self, modified_files, pull_request=False, platforms=[]): + def __init__(self, modified_files, pull_request=False, platforms=[], no_path_name = False): self.modified_files = modified_files self.twister_options = [] self.full_twister = False @@ -95,6 +95,7 @@ def __init__(self, modified_files, pull_request=False, platforms=[]): self.pull_request = pull_request self.platforms = platforms self.default_run = False + self.no_path_name = no_path_name def process(self): self.find_modules() @@ -112,6 +113,8 @@ def process(self): def get_plan(self, options, integration=False): fname = "_test_plan_partial.json" cmd = ["scripts/twister", "-c"] + options + ["--save-tests", fname ] + if self.no_path_name: + cmd += ["--no-path-name"] if integration: cmd.append("--integration") @@ -349,6 +352,8 @@ def parse_args(): help="Number of tests per builder") parser.add_argument('-n', '--default-matrix', default=10, type=int, help="Number of tests per builder") + parser.add_argument('--no-path-name', action="store_true", + help="Don't put paths into test suites' names ") return parser.parse_args() @@ -371,8 +376,7 @@ def parse_args(): print("\n".join(files)) print("=========") - - f = Filters(files, args.pull_request, args.platform) + f = Filters(files, args.pull_request, args.platform, args.no_path_name) f.process() # remove dupes and filtered cases From 4f29893ac22ac8553c507fd2e3d2f9b0daeeefc8 Mon Sep 17 00:00:00 2001 From: Maciej Perkowski Date: Mon, 31 Jul 2023 13:05:35 +0200 Subject: [PATCH 3/8] scripts: Allow test_plan.py to work with other than zephyr repos The test_plan.py script has a path to repository to be scanned for changes hard coded to zephyr. This patch separates zephyr path from such repository's path and adds an arg to pass repo to scan Signed-off-by: Maciej Perkowski --- scripts/ci/test_plan.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/scripts/ci/test_plan.py b/scripts/ci/test_plan.py index 5d9b82f270f89..8c78ca1a44080 100755 --- a/scripts/ci/test_plan.py +++ b/scripts/ci/test_plan.py @@ -19,10 +19,10 @@ if "ZEPHYR_BASE" not in os.environ: exit("$ZEPHYR_BASE environment variable undefined.") -repository_path = Path(os.environ['ZEPHYR_BASE']) +zephyr_base = Path(os.environ['ZEPHYR_BASE']) logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) -sys.path.append(os.path.join(repository_path, 'scripts')) +sys.path.append(os.path.join(zephyr_base, 'scripts')) import list_boards def _get_match_fn(globs, regexes): @@ -112,7 +112,7 @@ def process(self): def get_plan(self, options, integration=False): fname = "_test_plan_partial.json" - cmd = ["scripts/twister", "-c"] + options + ["--save-tests", fname ] + cmd = [f"{zephyr_base}/scripts/twister", "-c"] + options + ["--save-tests", fname ] if self.no_path_name: cmd += ["--no-path-name"] if integration: @@ -131,7 +131,7 @@ def find_modules(self): if 'west.yml' in self.modified_files: print(f"Manifest file 'west.yml' changed") print("=========") - old_manifest_content = repo.git.show(f"{args.commits[:-2]}:west.yml") + old_manifest_content = repo_to_scan.git.show(f"{args.commits[:-2]}:west.yml") with open("west_old.yml", "w") as manifest: manifest.write(old_manifest_content) old_manifest = Manifest.from_file("west_old.yml") @@ -208,8 +208,12 @@ def find_boards(self): if p and p.groups(): boards.add(p.group(1)) - # Limit search to $ZEPHYR_BASE since this is where the changed files are - lb_args = argparse.Namespace(**{ 'arch_roots': [repository_path], 'board_roots': [repository_path] }) + roots = [zephyr_base] + if repository_path != zephyr_base: + roots.append(repository_path) + + # Look for boards in monitored repositories + lb_args = argparse.Namespace(**{ 'arch_roots': roots, 'board_roots': roots}) known_boards = list_boards.find_boards(lb_args) for b in boards: name_re = re.compile(b) @@ -264,7 +268,7 @@ def find_tests(self): def find_tags(self): - tag_cfg_file = os.path.join(repository_path, 'scripts', 'ci', 'tags.yaml') + tag_cfg_file = os.path.join(zephyr_base, 'scripts', 'ci', 'tags.yaml') with open(tag_cfg_file, 'r') as ymlfile: tags_config = yaml.safe_load(ymlfile) @@ -352,6 +356,8 @@ def parse_args(): help="Number of tests per builder") parser.add_argument('-n', '--default-matrix', default=10, type=int, help="Number of tests per builder") + parser.add_argument('-r', '--repo-to-scan', default=None, + help="Repo to scan") parser.add_argument('--no-path-name', action="store_true", help="Don't put paths into test suites' names ") @@ -363,9 +369,12 @@ def parse_args(): args = parse_args() files = [] errors = 0 + repository_path = zephyr_base + if args.repo_to_scan: + repository_path = Path(args.repo_to_scan) if args.commits: - repo = Repo(repository_path) - commit = repo.git.diff("--name-only", args.commits) + repo_to_scan = Repo(repository_path) + commit = repo_to_scan.git.diff("--name-only", args.commits) files = commit.split("\n") elif args.modified_files: with open(args.modified_files, "r") as fp: From 156a7a306204639b204b43e9e3cb8d60342d9c77 Mon Sep 17 00:00:00 2001 From: Maciej Perkowski Date: Tue, 25 Jul 2023 16:45:55 +0200 Subject: [PATCH 4/8] scripts: Allow using alternative ignore-patters in test_plan.py Expand test_plan.py args with --ignore-path. This allows to provide an alternative lists of patterns for the script. Signed-off-by: Maciej Perkowski --- scripts/ci/test_plan.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/ci/test_plan.py b/scripts/ci/test_plan.py index 8c78ca1a44080..622bbd3769683 100755 --- a/scripts/ci/test_plan.py +++ b/scripts/ci/test_plan.py @@ -86,7 +86,7 @@ def __repr__(self): return "".format(self.name) class Filters: - def __init__(self, modified_files, pull_request=False, platforms=[], no_path_name = False): + def __init__(self, modified_files, pull_request=False, platforms=[], no_path_name = False, ignore_path=None): self.modified_files = modified_files self.twister_options = [] self.full_twister = False @@ -96,6 +96,9 @@ def __init__(self, modified_files, pull_request=False, platforms=[], no_path_nam self.platforms = platforms self.default_run = False self.no_path_name = no_path_name + self.ignore_path = f"{zephyr_base}/scripts/ci/twister_ignore.txt" + if ignore_path: + self.ignore_path = ignore_path def process(self): self.find_modules() @@ -306,7 +309,7 @@ def find_tags(self): logging.info(f'Potential tag based filters: {exclude_tags}') def find_excludes(self, skip=[]): - with open("scripts/ci/twister_ignore.txt", "r") as twister_ignore: + with open(self.ignore_path, "r") as twister_ignore: ignores = twister_ignore.read().splitlines() ignores = filter(lambda x: not x.startswith("#"), ignores) @@ -360,6 +363,8 @@ def parse_args(): help="Repo to scan") parser.add_argument('--no-path-name', action="store_true", help="Don't put paths into test suites' names ") + parser.add_argument('--ignore-path', default=None, + help="Path to a text file with patterns of files to be matched against changed files") return parser.parse_args() @@ -385,7 +390,7 @@ def parse_args(): print("\n".join(files)) print("=========") - f = Filters(files, args.pull_request, args.platform, args.no_path_name) + f = Filters(files, args.pull_request, args.platform, args.no_path_name, args.ignore_path) f.process() # remove dupes and filtered cases From a42452e531500f680fee601a02d4bd7d3fc3f770 Mon Sep 17 00:00:00 2001 From: Maciej Perkowski Date: Wed, 2 Aug 2023 14:36:03 +0200 Subject: [PATCH 5/8] scripts: Add arg to set_plan.py for alternative tag relation list Add --alt-tag arg for test_plan.py script. User can use it and point to an alternative file with tag-directories relations. If so, such file will be used instead of the default one. Signed-off-by: Maciej Perkowski --- scripts/ci/test_plan.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/ci/test_plan.py b/scripts/ci/test_plan.py index 622bbd3769683..449b2e74def96 100755 --- a/scripts/ci/test_plan.py +++ b/scripts/ci/test_plan.py @@ -86,7 +86,7 @@ def __repr__(self): return "".format(self.name) class Filters: - def __init__(self, modified_files, pull_request=False, platforms=[], no_path_name = False, ignore_path=None): + def __init__(self, modified_files, pull_request=False, platforms=[], no_path_name = False, ignore_path=None, alt_tags=None): self.modified_files = modified_files self.twister_options = [] self.full_twister = False @@ -96,6 +96,9 @@ def __init__(self, modified_files, pull_request=False, platforms=[], no_path_nam self.platforms = platforms self.default_run = False self.no_path_name = no_path_name + self.tag_cfg_file = os.path.join(zephyr_base, 'scripts', 'ci', 'tags.yaml') + if alt_tags: + self.tag_cfg_file = alt_tags self.ignore_path = f"{zephyr_base}/scripts/ci/twister_ignore.txt" if ignore_path: self.ignore_path = ignore_path @@ -271,8 +274,7 @@ def find_tests(self): def find_tags(self): - tag_cfg_file = os.path.join(zephyr_base, 'scripts', 'ci', 'tags.yaml') - with open(tag_cfg_file, 'r') as ymlfile: + with open(self.tag_cfg_file, 'r') as ymlfile: tags_config = yaml.safe_load(ymlfile) tags = {} @@ -365,6 +367,8 @@ def parse_args(): help="Don't put paths into test suites' names ") parser.add_argument('--ignore-path', default=None, help="Path to a text file with patterns of files to be matched against changed files") + parser.add_argument('--alt-tags', default=None, + help="Path to a file describing relations between directories and tags") return parser.parse_args() @@ -390,7 +394,7 @@ def parse_args(): print("\n".join(files)) print("=========") - f = Filters(files, args.pull_request, args.platform, args.no_path_name, args.ignore_path) + f = Filters(files, args.pull_request, args.platform, args.no_path_name, args.ignore_path, args.alt_tags) f.process() # remove dupes and filtered cases From aff7a7b8d2eb91e1682f932efcedb4ff27312439 Mon Sep 17 00:00:00 2001 From: Maciej Perkowski Date: Wed, 2 Aug 2023 16:43:54 +0200 Subject: [PATCH 6/8] scripts: Add arg to test_plan.py for alternative test locations The arg --testsuite-root was copied from twister. When it is used for test_plan.py it will be propagated to twister calls. This allows to make alternative test locations (e.g. from another repo) to work with test_plan.py Signed-off-by: Maciej Perkowski --- scripts/ci/test_plan.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/scripts/ci/test_plan.py b/scripts/ci/test_plan.py index 449b2e74def96..1b61eb950c6a9 100755 --- a/scripts/ci/test_plan.py +++ b/scripts/ci/test_plan.py @@ -86,8 +86,9 @@ def __repr__(self): return "".format(self.name) class Filters: - def __init__(self, modified_files, pull_request=False, platforms=[], no_path_name = False, ignore_path=None, alt_tags=None): + def __init__(self, modified_files, pull_request=False, platforms=[], no_path_name = False, ignore_path=None, alt_tags=None, testsuite_root=None): self.modified_files = modified_files + self.testsuite_root = testsuite_root self.twister_options = [] self.full_twister = False self.all_tests = [] @@ -116,9 +117,12 @@ def process(self): else: self.find_excludes() - def get_plan(self, options, integration=False): + def get_plan(self, options, integration=False, use_testsuite_root=True): fname = "_test_plan_partial.json" cmd = [f"{zephyr_base}/scripts/twister", "-c"] + options + ["--save-tests", fname ] + if self.testsuite_root and use_testsuite_root: + for root in self.testsuite_root: + cmd+=["-T", root] if self.no_path_name: cmd += ["--no-path-name"] if integration: @@ -270,7 +274,7 @@ def find_tests(self): _options.extend(["-p", platform]) else: _options.append("--all") - self.get_plan(_options) + self.get_plan(_options, use_testsuite_root=False) def find_tags(self): @@ -369,6 +373,12 @@ def parse_args(): help="Path to a text file with patterns of files to be matched against changed files") parser.add_argument('--alt-tags', default=None, help="Path to a file describing relations between directories and tags") + parser.add_argument( + "-T", "--testsuite-root", action="append", default=[], + help="Base directory to recursively search for test cases. All " + "testcase.yaml files under here will be processed. May be " + "called multiple times. Defaults to the 'samples/' and " + "'tests/' directories at the base of the Zephyr tree.") return parser.parse_args() @@ -394,7 +404,7 @@ def parse_args(): print("\n".join(files)) print("=========") - f = Filters(files, args.pull_request, args.platform, args.no_path_name, args.ignore_path, args.alt_tags) + f = Filters(files, args.pull_request, args.platform, args.no_path_name, args.ignore_path, args.alt_tags, args.testsuite_root) f.process() # remove dupes and filtered cases From ea2cdd28e634787d93a0d644f3745f85cca220ec Mon Sep 17 00:00:00 2001 From: Maciej Perkowski Date: Thu, 17 Aug 2023 14:34:31 +0200 Subject: [PATCH 7/8] scripts: Make workflow of test_plan.py script more robust The script was not resolving all detected changes uniformly: find_excludes() could skip or not certain patterns based on required testing scope. The idea was to not include files that were already handled by find_test() and find_boards() workflows. However, only boards and tests folders could be removed but not samples. This also led to blind spots: changes in some files were not triggering any tests. E.g. change in a test/common, where no corresponding yaml can be found by find_tests() which is also ignored by find_excludes(). In the new workflow a list of resolved files (for which find_arch(), find_tests() or find_boards() found scope) is created. Instead of using skip in find_excludes, files are excluded only if they were resolved. Signed-off-by: Maciej Perkowski --- scripts/ci/test_plan.py | 39 +++++++++++++++++++++-------------- scripts/ci/twister_ignore.txt | 19 ----------------- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/scripts/ci/test_plan.py b/scripts/ci/test_plan.py index 1b61eb950c6a9..909fcbdfedf82 100755 --- a/scripts/ci/test_plan.py +++ b/scripts/ci/test_plan.py @@ -88,6 +88,7 @@ def __repr__(self): class Filters: def __init__(self, modified_files, pull_request=False, platforms=[], no_path_name = False, ignore_path=None, alt_tags=None, testsuite_root=None): self.modified_files = modified_files + self.resolved_files = [] self.testsuite_root = testsuite_root self.twister_options = [] self.full_twister = False @@ -95,7 +96,6 @@ def __init__(self, modified_files, pull_request=False, platforms=[], no_path_nam self.tag_options = [] self.pull_request = pull_request self.platforms = platforms - self.default_run = False self.no_path_name = no_path_name self.tag_cfg_file = os.path.join(zephyr_base, 'scripts', 'ci', 'tags.yaml') if alt_tags: @@ -111,11 +111,7 @@ def process(self): if not self.platforms: self.find_archs() self.find_boards() - - if self.default_run: - self.find_excludes(skip=["tests/*", "boards/*/*/*"]) - else: - self.find_excludes() + self.find_excludes() def get_plan(self, options, integration=False, use_testsuite_root=True): fname = "_test_plan_partial.json" @@ -192,6 +188,8 @@ def find_archs(self): archs.add('riscv64') else: archs.add(p.group(1)) + # Modified file is treated as resolved, since a matching scope was found + self.resolved_files.append(f) _options = [] for arch in archs: @@ -210,6 +208,7 @@ def find_archs(self): def find_boards(self): boards = set() all_boards = set() + resolved = [] for f in self.modified_files: if f.endswith(".rst") or f.endswith(".png") or f.endswith(".jpg"): @@ -217,6 +216,7 @@ def find_boards(self): p = re.match(r"^boards\/[^/]+\/([^/]+)\/", f) if p and p.groups(): boards.add(p.group(1)) + resolved.append(f) roots = [zephyr_base] if repository_path != zephyr_base: @@ -231,10 +231,16 @@ def find_boards(self): if name_re.search(kb.name): all_boards.add(kb.name) + # If modified file is catched by "find_boards" workflow (change in "boards" dir AND board recognized) + # it means a proper testing scope for this file was found and this file can be removed + # from further consideration + for board in all_boards: + self.resolved_files.extend(list(filter(lambda f: board in f, resolved))) + _options = [] if len(all_boards) > 20: logging.warning(f"{len(boards)} boards changed, this looks like a global change, skipping test handling, revert to default.") - self.default_run = True + self.full_twister = True return for board in all_boards: @@ -254,6 +260,8 @@ def find_tests(self): if os.path.exists(os.path.join(d, "testcase.yaml")) or \ os.path.exists(os.path.join(d, "sample.yaml")): tests.add(d) + # Modified file is treated as resolved, since a matching scope was found + self.resolved_files.append(f) break else: d = os.path.dirname(d) @@ -264,7 +272,7 @@ def find_tests(self): if len(tests) > 20: logging.warning(f"{len(tests)} tests changed, this looks like a global change, skipping test handling, revert to default") - self.default_run = True + self.full_twister = True return if _options: @@ -320,21 +328,22 @@ def find_excludes(self, skip=[]): ignores = filter(lambda x: not x.startswith("#"), ignores) found = set() - files = list(filter(lambda x: x, self.modified_files)) + files_not_resolved = list(filter(lambda x: x not in self.resolved_files, self.modified_files)) for pattern in ignores: - if pattern in skip: - continue if pattern: - found.update(fnmatch.filter(files, pattern)) + found.update(fnmatch.filter(files_not_resolved, pattern)) logging.debug(found) - logging.debug(files) + logging.debug(files_not_resolved) - if sorted(files) != sorted(found): + # Full twister run can be ordered by detecting great number of tests/boards changed + # or if not all modified files were resolved (corresponding scope found) + self.full_twister = self.full_twister or sorted(files_not_resolved) != sorted(found) + + if self.full_twister: _options = [] logging.info(f'Need to run full or partial twister...') - self.full_twister = True if self.platforms: for platform in self.platforms: _options.extend(["-p", platform]) diff --git a/scripts/ci/twister_ignore.txt b/scripts/ci/twister_ignore.txt index 59f735495ca02..4a0589f291cca 100644 --- a/scripts/ci/twister_ignore.txt +++ b/scripts/ci/twister_ignore.txt @@ -17,25 +17,6 @@ CODEOWNERS MAINTAINERS.yml LICENSE Makefile -tests/* -samples/* -boards/*/*/* -arch/xtensa/* -arch/x86/* -arch/posix/* -arch/arc/* -arch/sparc/* -arch/arm/* -arch/nios2/* -arch/riscv/* -include/arch/xtensa/* -include/arch/x86/* -include/arch/posix/* -include/arch/arc/* -include/arch/sparc/* -include/arch/arm/* -include/arch/nios2/* -include/arch/riscv/* doc/* # GH action have no impact on code .github/* From 74e4d2695a67dc80a8159391a0b30c7a2b9c0e19 Mon Sep 17 00:00:00 2001 From: Maciej Perkowski Date: Fri, 18 Aug 2023 16:02:12 +0200 Subject: [PATCH 8/8] scripts: Add workflow for "common" directories in find_tests() Some tests use a "common" directory to store pieces of code which are reused by different scenarios. In those cases, no test yaml is found within such director nor within its parents. If no test yaml is found in a directory, and the directory is called common, also look in collocated directories if they have test yamls. If so, add all those locations to the scope. E.g. tests/bluetooth/controller/common Signed-off-by: Maciej Perkowski --- scripts/ci/test_plan.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/ci/test_plan.py b/scripts/ci/test_plan.py index 909fcbdfedf82..fb9e6768651c6 100755 --- a/scripts/ci/test_plan.py +++ b/scripts/ci/test_plan.py @@ -12,6 +12,7 @@ import json import logging import sys +import glob from pathlib import Path from git import Repo from west.manifest import Manifest @@ -256,13 +257,25 @@ def find_tests(self): if f.endswith(".rst"): continue d = os.path.dirname(f) - while d: + scope_found = False + while not scope_found and d: + head, tail = os.path.split(d) if os.path.exists(os.path.join(d, "testcase.yaml")) or \ os.path.exists(os.path.join(d, "sample.yaml")): tests.add(d) # Modified file is treated as resolved, since a matching scope was found self.resolved_files.append(f) - break + scope_found = True + elif tail == "common": + # Look for yamls in directories collocated with common + + yamls_found = [yaml for yaml in glob.iglob(head + '/**/testcase.yaml', recursive=True)] + yamls_found.extend([yaml for yaml in glob.iglob(head + '/**/sample.yaml', recursive=True)]) + if yamls_found: + for yaml in yamls_found: + tests.add(os.path.dirname(yaml)) + self.resolved_files.append(f) + scope_found = True else: d = os.path.dirname(d)