aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2023-06-14 10:31:27 +0200
committerAki Tomita <121511582+atomita-ni@users.noreply.github.com>2023-07-14 13:38:44 -0500
commit3b5f89ee7206ec4f40b2cd3c60426855e50ab524 (patch)
treece6fe3fc7ffd1ecda168b82737861574947ace74 /tools
parentdevtest: gpio test updates (diff)
downloaduhd-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.py145
-rw-r--r--tools/changeset_testlist.yaml175
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