diff options
author | 2023-06-14 10:31:27 +0200 | |
---|---|---|
committer | 2023-07-14 13:38:44 -0500 | |
commit | 3b5f89ee7206ec4f40b2cd3c60426855e50ab524 (patch) | |
tree | ce6fe3fc7ffd1ecda168b82737861574947ace74 /tools | |
parent | devtest: gpio test updates (diff) | |
download | uhd-3b5f89ee7206ec4f40b2cd3c60426855e50ab524.tar.xz uhd-3b5f89ee7206ec4f40b2cd3c60426855e50ab524.zip |
tools: Add changeset analyzer
This tool compares two git branches of UHD and produces a list of tests
required to verify the changeset. The intended use case is to be able to
verify branches that are being created for pull requests.
For example, say a pull requests is based on a branch that only modifies
files in host/lib/usrp/x300. Then it is only necessary to run hardware
tests on X300, running tests on other USRPs would be a waste of time.
This commit contains two files: The utility itself (a Python script),
and a rule file (a YAML file). The former uses the latter to map
a changeset to a list of tests.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/changeset_testlist.py | 145 | ||||
-rw-r--r-- | tools/changeset_testlist.yaml | 175 |
2 files changed, 320 insertions, 0 deletions
diff --git a/tools/changeset_testlist.py b/tools/changeset_testlist.py new file mode 100644 index 000000000..59c9d2caa --- /dev/null +++ b/tools/changeset_testlist.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# +# Copyright 2023 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Changeset analyzer: Reads changes made on a particular branch compared to +another and computes a list of tests that need to be run to validate this branch. +""" + +import os +import pathlib +import re +import sys +import subprocess +import shutil +import argparse +from ruamel import yaml + +def parse_args(): + """ + Parse and return args. + """ + parser = argparse.ArgumentParser( + description=__doc__, + ) + parser.add_argument( + '--source-branch', + help="Source branch off diff. Defaults to the current branch.") + parser.add_argument( + '--target-branch', default='master', + help="Target branch off diff. Defaults to 'master'.") + parser.add_argument( + '--repo-path', default='.', + help="Path to the UHD git repository. Defaults to the current directory.") + parser.add_argument( + '--rule-file', + help="Path to rules file.") + parser.add_argument( + '--set-azdo-var', + help="Generate output to set an AzDO variable") + parser.add_argument( + '--list-tests', action='store_true', + help="Show the generated test-list.") + parser.add_argument( + '--include-target', action='store_true', + help="Include changes that originate from the target branch.") + parser.add_argument( + '-v', '--verbose', action='store_true', + help="Verbose output") + return parser.parse_args() + +def get_changed_files(repo_path, target_branch, source_branch, include_target): + """ + Returns a list of paths in the UHD repository that have are different between + two branches. + """ + assert target_branch + # If include_target is false, then current (unstaged/uncommited) changes are + # not included. If we want to change this, then couple this with + # git diff --name-only (no further arguments) + if not include_target: + target_branch += '...' + git_cmd = shutil.which('git') + get_diff_args = [ + git_cmd, + 'diff', + '--name-only', + target_branch] + if source_branch: + get_diff_args.append(source_branch) + files = subprocess.check_output(get_diff_args, cwd=repo_path, encoding='utf-8') + return files.strip().split("\n") + +def load_rules(rule_file): + """ + Return the rules as a Python list. + """ + with open(rule_file, 'r', encoding='utf-8') as rfd: + return yaml.safe_load(rfd) + +class RuleApplier: + """ + Helper class to update an internal test list based on a set of rules. + """ + def __init__(self, rules): + self.rules = rules + self.test_list = set() + + def apply(self, filename, verbose=False): + """ + Apply rules against a file. + """ + for rule in self.rules: + if self._apply_rule(rule, filename, verbose): + break + + def _apply_rule(self, rule, filename, verbose=False): + """ + Helper: Apply a single rule. + + Returns True if you can stop applying rules against this filename. + """ + # First: Check if this rule even applies to this file + if 're' in rule and not re.search(rule['re'], filename) \ + or 'name' in rule and rule['name'] != filename: + return False + if verbose: + sys.stderr.write(f"Filename {filename} matches rule: {rule}\n") + if 'add' in rule: + self.test_list.update(rule['add']) + # If stop is specified as False, then we can still apply more rules. + if 'stop' in rule and not rule['stop']: + return False + return True + +def main(): + """ + Gogogo! + """ + args = parse_args() + rule_file = args.rule_file + if not rule_file: + rule_file = os.path.join(pathlib.Path(__file__).parent.resolve(), 'changeset_testlist.yaml') + file_list = get_changed_files( + args.repo_path, + args.target_branch, + args.source_branch, + args.include_target, + ) + rule_applier = RuleApplier(load_rules(rule_file)) + for filename in file_list: + rule_applier.apply(filename, args.verbose) + if args.set_azdo_var: + print( + f"##vso[task.setvariable variable={args.set_azdo_var};isoutput=true]" + \ + ';'.join(rule_applier.test_list)) + if args.list_tests: + print("Required tests:") + print("---------------", end='') + print('\n* '.join(sorted([''] + list(rule_applier.test_list)))) + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/changeset_testlist.yaml b/tools/changeset_testlist.yaml new file mode 100644 index 000000000..e6ecb8e7b --- /dev/null +++ b/tools/changeset_testlist.yaml @@ -0,0 +1,175 @@ +# Test targets: +# - uhd.build.$PLATFORM: Build UHD on given platforms. This includes running unit tests. +# Valid values for $PLATFORM are: all, linux, windows, mac. +# - hw.streaming.$DEVICE: Run streaming tests for $DEVICE. +# - hw.rf.$DEVICE: Run RF tests for $DEVICE. +# - devtest.$DEVICE: Run devtests for $DEVICE. + + +############################################################################### +# HOST CHANGES (UHD) +############################################################################### +# If only a unit test changed, then we need to re-run unit tests, but no HW tests +- re: ^host/tests/[^/]+(cpp|py)$|^host/tests/CMakeLists.txt + add: + - uhd.build.all + - uhd.utest.all +# Documentation changes +- re: ^host/docs/ + add: + - uhd.docs +# Device-specific changes. These should trigger HW tests only on those devices +# they affect. We start with daughterboard rules, then motherboard rules. +- re: host/lib/usrp/dboard/zbx/ + add: + - uhd.build.all + - hw.rf.x410 + - hw.streaming.x410 + - devtest.x410 +- re: host/lib/usrp/dboard/e3xx + add: + - uhd.build.all + - hw.rf.e3xx + - hw.streaming.e3xx + - devtest.e3xx +- re: host/lib/usrp/dboard/magnesium + add: + - uhd.build.all + - hw.rf.n310 + - devtest.n310 +- re: host/lib/usrp/dboard/rhodium + add: + - uhd.build.all + - hw.rf.n320 + - devtest.n320 +- re: host/lib/usrp/dboard/twinrx/ + name: host/lib/usrp/dboard/db_twinrx.cpp + add: + - uhd.build.all + - hw.rf.x310.twinrx + - hw.rf.x300.twinrx +- re: host/lib/usrp/dboard/.*CMakeLists.txt + add: + - uhd.build.all +- re: host/lib/usrp/dboard/db_.+pp$ + add: + - uhd.build.all + - hw.rf.x3xx + - hw.rf.n2xx +- re: host/lib/usrp/b200/ + add: + - uhd.build.all + - hw.rf.b2xx + - hw.streaming.b2xx + - devtest.b2xx +- re: host/lib/usrp/b100/ + add: + - uhd.build.all + - hw.rf.b1xx + - hw.streaming.b1xx + - devtest.b1xx +- re: host/lib/usrp/x300/ + add: + - uhd.build.all + - hw.rf.x3xx + - hw.streaming.x3xx + - devtest.x3xx.all +- re: host/lib/usrp/x400/ + add: + - uhd.build.all + - hw.rf.x4xx + - hw.rf.n3xx + - hw.streaming.x4xx + - devtest.x4xx +- re: host/lib/usrp/mpmd/ + add: + - uhd.build.all + - hw.rf.x4xx + - hw.rf.n3xx + - hw.rf.e3xx + - hw.streaming.x4xx + - hw.streaming.n3xx + - hw.streaming.e3xx + - devtest.x4xx + - devtest.e3xx + - devtest.n3xx +- re: host/lib/usrp/usrp2 + add: + - uhd.build.all + - hw.rf.n2xx +# Catchall rule for UHD changes +- re: host/.+cpp$|host/.+CMakeLists.txt|host/.+hpp$|host/.+ipp$|host/.+c$|host/.+h$|host/.+py$ + add: + - uhd.build.all + - uhd.utest.all + - hw.streaming.all + - hw.rf.all + - devtest.all + +############################################################################### +# MPM CHANGES +############################################################################### +# When any code file changes, we want to run the unit tests, but run the +# other tests, too +- re: ^mpm/.+hpp$|^mpm/.+cpp$|^mpm/.+py$|^mpm/.+/tests/|^mpm/.+CMakeLists.txt$ + add: + - mpm.utest.all + stop: False +- re: ^mpm/.+/ad9361|^mpm/.+catalina$|^mpm/.+/dboard_manager/ad936x_db.py + add: + - mpm.build.e3xx + - hw.rf.e3xx + - devtest.e3xx +- re: ^mpm/.+/periph_manager/n3xx + add: + - mpm.build.n3xx + - hw.rf.n3xx + - hw.streaming.n3xx + - devtest.n3xx +- re: ^mpm/.+/ad937x|^mpm/.+/mykonos|^mpm/.+/dboard_manager/mg_|magnesium_manager..pp$ + add: + - mpm.build.n310 + - hw.rf.n310 + - devtest.n310 +- re: ^mpm/.+/dboard_manager/rh_|^mpm/.+/dboard_manager/..._rh.py + add: + - mpm.build.n320 + - hw.rf.n320 + - devtest.n320 +- re: ^mpm/.+periph_manager/x4xx + add: + - hw.streaming.x4xx + stop: False +- re: ^mpm/.+/rfdc|^mpm/.+_manager/x4xx + add: + - mpm.build.x4xx + - hw.rf.x4xx + - devtest.x4xx +- re: fbx.py$ + add: + - mpm.build.x4xx + - hw.rf.x440 + - devtest.x440 +- re: zbx.py + add: + - mpm.build.x4xx + - hw.rf.x410 + - devtest.x410 +- re: e31x_db_manager..pp$|^mpm/.+/periph_manager/e31x|e31x_db.py$ + add: + - mpm.build.e310 + - hw.rf.e310 + - devtest.e310 +- re: neon_manager..pp$|^mpm/.+/periph_manager/e320 + add: + - mpm.build.e320 + - hw.rf.e320 + - devtest.e320 +# Catchall rule for MPM changes +- re: ^mpm/ + add: + - mpm.build.all + - mpm.utest.all + - hw.streaming.all + - hw.rf.all + - devtest.all |