diff --git a/Tools/scripts/patchcheck.py b/Tools/scripts/patchcheck.py --- a/Tools/scripts/patchcheck.py +++ b/Tools/scripts/patchcheck.py @@ -5,6 +5,10 @@ import os.path import subprocess import sysconfig +import linecache +import types +import functools +from collections import Counter import reindent import untabify @@ -60,16 +64,21 @@ return [x.decode().rstrip() for x in st.stdout] -def report_modified_files(file_paths): - count = len(file_paths) +def report_list(items, report_count): + count = len(items) if count == 0: - return n_files_str(count) + return report_count(count) else: - lines = ["{}:".format(n_files_str(count))] - for path in file_paths: - lines.append(" {}".format(path)) + lines = ["{}:".format(report_count(count))] + for item in items: + lines.append(" {}".format(item)) return "\n".join(lines) +report_modified_files = functools.partial(report_list, + report_count=n_files_str) +report_test_names = functools.partial(report_list, report_count=lambda x: + "{} test{}".format(x, "s" if x != 1 else "")) + @status("Fixing whitespace", info=report_modified_files) def normalize_whitespace(file_paths): @@ -115,6 +124,32 @@ return fixed +def testmethod_names(code, name=[]): + name = name + [code.co_name] + for c in code.co_consts: + if isinstance(c, types.CodeType): + for item in testmethod_names(c, name): + yield item + if name[-1].startswith('test_'): + yield '.'.join(name[1:]) + + +@status("Duplicate test names", info=report_test_names) +def duplicate_test_names(file_paths): + duplicates = [] + for path in file_paths: + abspath = os.path.join(SRCDIR, path) + try: + source_lines = linecache.getlines(abspath) + code = compile(''.join(source_lines), abspath, 'exec') + except: + continue + duplicates.extend("{} in file {}".format(name, path) for + name, cnt in + Counter(testmethod_names(code)).items() if cnt > 1) + return duplicates + + @status("Docs modified", modal=True) def docs_modified(file_paths): """Report if any file in the Doc directory has been changed.""" @@ -151,6 +186,8 @@ def main(): file_paths = changed_files() python_files = [fn for fn in file_paths if fn.endswith('.py')] + test_files = [fn for fn in python_files if + fn.startswith(os.path.join('Lib', 'test'))] c_files = [fn for fn in file_paths if fn.endswith(('.c', '.h'))] doc_files = [fn for fn in file_paths if fn.startswith('Doc')] special_files = {'Misc/ACKS', 'Misc/NEWS'} & set(file_paths) @@ -160,6 +197,8 @@ normalize_c_whitespace(c_files) # Doc whitespace enforcement. normalize_docs_whitespace(doc_files) + # Duplicate test names. + duplicate_test_names(test_files) # Docs updated. docs_modified(doc_files) # Misc/ACKS changed.