Skip to content

Commit 15fcb71

Browse files
committed
patchtest2: support multiple test suites
- Add the '--suites' and '--module-paths' arguments to the parser - Modify the patchtest.py and the Patchtest class to load test suites and modules based on these parameters - Move some 'core' tests to 'oe' Signed-off-by: Trevor Gamblin <[email protected]>
1 parent 7079296 commit 15fcb71

File tree

4 files changed

+141
-54
lines changed

4 files changed

+141
-54
lines changed

src/patchtest2/parser.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,20 @@ def get_parser(cls):
6666
"--debug", "-d", action="store_true", help="Enable debug output"
6767
)
6868

69+
# Add --suites/-s argument for comma-separated list of suite names
70+
parser.add_argument(
71+
'--suites', '-s',
72+
type=str,
73+
help='Comma-separated list of test suite module names to run (core suite is always included)'
74+
)
75+
76+
# Add --module-path/-m argument that can be specified multiple times
77+
parser.add_argument(
78+
'--module-paths', '-m',
79+
type=str,
80+
help='Comma-separated list of additional paths to search for test modules (src/patchtest2/tests is always included)'
81+
)
82+
6983
log_type_group.add_argument(
7084
"--log-results",
7185
dest="log_results",

src/patchtest2/tests/core.py

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,6 @@ def test_mbox_unidiff_parse_error(target):
8383

8484
return target.subject, result, reason
8585

86-
@patchtest_result
87-
def test_mbox_commit_message_user_tags(target):
88-
"""Test for GitHub-style username tags (@username) in commit messages"""
89-
result = "PASS"
90-
reason = "Mbox includes one or more GitHub-style username tags. Ensure that any '@' symbols are stripped out of usernames"
91-
92-
if patterns.mbox_github_username.search_string(target.commit_message):
93-
result = "FAIL"
94-
95-
return target.subject, result, reason
96-
9786
@patchtest_result
9887
def test_mbox_bugzilla_entry_format(target):
9988
"""Test for proper Bugzilla entry format in commit messages"""
@@ -123,30 +112,6 @@ def test_mbox_author_valid(target):
123112

124113
return target.subject, result, reason
125114

126-
@patchtest_result
127-
def test_mbox_non_auh_upgrade(target):
128-
"""Test that patch is not from AUH (Auto Upgrade Helper)"""
129-
result = "PASS"
130-
reason = f"Invalid author {patterns.auh_email}. Resend the series with a valid patch author"
131-
132-
if patterns.auh_email in target.commit_message:
133-
result = "FAIL"
134-
135-
return target.subject, result, reason
136-
137-
@patchtest_result
138-
def test_mbox_target_mailing_list_meta_project(target):
139-
"""Check for meta-* project tags in subject line"""
140-
result = "PASS"
141-
reason = "Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists"
142-
143-
# Check for meta-* project indicators in subject
144-
project_regex = pyparsing.Regex(r"\[(?P<project>meta-.+)\]")
145-
if project_regex.search_string(target.subject):
146-
result = "FAIL"
147-
148-
return target.subject, result, reason
149-
150115
@patchtest_result
151116
def test_mbox_revert_signed_off_by_exception(target):
152117
"""Skip signed-off-by test for revert commits"""

src/patchtest2/tests/oe.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import patchtest2.patterns as patterns
2+
import pyparsing
3+
import re
4+
import unidiff
5+
from patchtest2.tests.results import patchtest_result
6+
7+
@patchtest_result
8+
def test_mbox_commit_message_user_tags(target):
9+
"""Test for GitHub-style username tags (@username) in commit messages"""
10+
result = "PASS"
11+
reason = "Mbox includes one or more GitHub-style username tags. Ensure that any '@' symbols are stripped out of usernames"
12+
13+
if patterns.mbox_github_username.search_string(target.commit_message):
14+
result = "FAIL"
15+
16+
return target.subject, result, reason
17+
18+
@patchtest_result
19+
def test_mbox_non_auh_upgrade(target):
20+
"""Test that patch is not from AUH (Auto Upgrade Helper)"""
21+
result = "PASS"
22+
reason = f"Invalid author {patterns.auh_email}. Resend the series with a valid patch author"
23+
24+
if patterns.auh_email in target.commit_message:
25+
result = "FAIL"
26+
27+
return target.subject, result, reason
28+
29+
@patchtest_result
30+
def test_mbox_target_mailing_list_meta_project(target):
31+
"""Check for meta-* project tags in subject line"""
32+
result = "PASS"
33+
reason = "Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists"
34+
35+
# Check for meta-* project indicators in subject
36+
project_regex = pyparsing.Regex(r"\[(?P<project>meta-.+)\]")
37+
if project_regex.search_string(target.subject):
38+
result = "FAIL"
39+
40+
return target.subject, result, reason
41+

src/patchtest2/tests/patchtest.py

Lines changed: 86 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,143 @@
11
import inspect
22
import json
33
import os
4-
import patchtest2.tests.core as core
4+
import sys
5+
import importlib.util
6+
from pathlib import Path
57
from patchtest2.parser import PatchtestParser
68
from patchtest2.mbox import PatchSeries, TargetRepo
79

810

911
class Patchtest:
10-
def __init__(self, target_repo, series):
12+
def __init__(self, target_repo, series, suites=None, module_paths=None):
1113
self.target_repo = target_repo
1214
self.series = series
13-
self.core_results = {
14-
k: self._results(v)
15-
for (k, v) in inspect.getmembers(core, inspect.isfunction)
16-
if k != "patchtest_result"
17-
}
18-
19-
self.results = dict(
20-
[
21-
(
22-
"core",
23-
self.core_results,
24-
),
25-
]
26-
)
15+
16+
# Always include 'core' suite, then add any additional suites
17+
self.suites = ['core']
18+
if suites:
19+
# Add additional suites, avoiding duplicates
20+
for suite in suites:
21+
if suite not in self.suites:
22+
self.suites.append(suite)
23+
24+
# Always include src/patchtest2/tests, then add any additional paths
25+
self.module_paths = ['src/patchtest2/tests']
26+
if module_paths:
27+
# Add additional paths, avoiding duplicates
28+
for path in module_paths:
29+
if path not in self.module_paths:
30+
self.module_paths.append(path)
31+
32+
# Load all test modules and their functions
33+
self.results = {}
34+
self._load_test_modules()
35+
36+
def _load_test_modules(self):
37+
"""Load test functions from all specified suites and module paths"""
38+
for suite_name in self.suites:
39+
suite_results = {}
40+
41+
# Look for the suite module in all specified paths
42+
module_found = False
43+
for module_path in self.module_paths:
44+
module_file = Path(module_path) / f"{suite_name}.py"
45+
46+
if module_file.exists():
47+
module_found = True
48+
# Load the module dynamically
49+
spec = importlib.util.spec_from_file_location(
50+
f"patchtest2.tests.{suite_name}",
51+
module_file
52+
)
53+
module = importlib.util.module_from_spec(spec)
54+
55+
# Add to sys.modules to handle imports within the module
56+
sys.modules[f"patchtest2.tests.{suite_name}"] = module
57+
spec.loader.exec_module(module)
58+
59+
# Extract test functions from the module
60+
test_functions = {
61+
k: v for (k, v) in inspect.getmembers(module, inspect.isfunction)
62+
if k != "patchtest_result" and k.startswith("test_")
63+
}
64+
65+
# Run tests and collect results
66+
for func_name, func in test_functions.items():
67+
suite_results[func_name] = self._results(func)
68+
69+
break # Found the module, no need to check other paths
70+
71+
if not module_found:
72+
print(f"Warning: Suite '{suite_name}' not found in any of the specified module paths")
73+
continue
74+
75+
self.results[suite_name] = suite_results
2776

2877
def _results(self, testname):
78+
"""Run a test function against all patches in the series"""
2979
return [testname(patch) for patch in self.series.patchdata]
3080

3181
def _print_result(self, category, tag):
82+
"""Print results for a specific test function"""
3283
for value in self.results[category][tag]:
3384
print(value)
3485

3586
def _print_results(self, category):
87+
"""Print all results for a specific suite"""
3688
for tag in self.results[category].keys():
3789
self._print_result(category, tag)
3890

3991
def print_results(self):
92+
"""Print all results from all suites"""
4093
for category in self.results.keys():
4194
self._print_results(category)
4295

4396
def _log_results(self, logfile):
97+
"""Log results to a text file"""
4498
result_str = ""
4599
for category in self.results.keys():
46100
for tag in self.results[category].keys():
47101
for value in self.results[category][tag]:
48102
result_str += value + "\n"
49-
50103
with open(logfile + ".testresult", "w") as f:
51104
f.write(result_str)
52105

53106
def _log_json(self, logfile):
107+
"""Log results to a JSON file"""
54108
with open(logfile + ".testresult", "w") as f:
55109
f.write(json.dumps(self.results, indent=4, sort_keys=True))
56110

57111
def log_results(self, logfile, mode=None):
112+
"""Log results in specified format"""
58113
if mode == "json":
59114
self._log_json(logfile)
60115
else:
61116
self._log_results(logfile)
62117

63118

64119
def run():
120+
"""Main entry point for patchtest"""
65121
parser = PatchtestParser.get_parser()
66122
args = parser.parse_args()
123+
124+
# Parse suites argument
125+
suites = None
126+
if hasattr(args, 'suites') and args.suites:
127+
suites = [suite.strip() for suite in args.suites.split(',')]
128+
129+
# Parse module paths argument
130+
module_paths = None
131+
if hasattr(args, 'module_paths') and args.module_paths:
132+
module_paths = [path.strip() for path in args.module_paths.split(',')]
133+
67134
target_repo = TargetRepo(args.repodir)
68135
series = PatchSeries(args.patch_path)
69-
results = Patchtest(target_repo, series)
136+
results = Patchtest(target_repo, series, suites=suites, module_paths=module_paths)
70137

71138
results.print_results()
139+
72140
if args.log_json:
73141
results.log_results(os.path.basename(args.patch_path), mode="json")
74-
75142
if args.log_results:
76143
results.log_results(os.path.basename(args.patch_path))

0 commit comments

Comments
 (0)