#!/usr/bin/env python3 ##===--- run_iwyu_tests.py - include-what-you-use test framework driver ---===## # # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. # ##===----------------------------------------------------------------------===## """A test harness for IWYU testing.""" __author__ = 'dsturtevant@google.com (Dean Sturtevant)' import argparse import glob import os import re import sys import unittest import logging logging.basicConfig(level=logging.INFO) import posixpath from fnmatch import fnmatch import iwyu_test_util def PosixPath(path): """Normalize Windows path separators to POSIX path separators.""" return path.replace('\\', '/') def Partition(l, delimiter): try: delim_index = l.index(delimiter) except ValueError: return l, [] return l[:delim_index], l[delim_index+1:] def TestIwyuOnRelevantFiles(filename): logging.info('Testing iwyu on %s', filename) # Split full/path/to/foo.cc into full/path/to/foo and .cc. (all_but_extension, extension) = os.path.splitext(filename) (dirname, basename) = os.path.split(all_but_extension) # Generate diagnostics on all foo-* files (well, not other # foo-*.cc files, which is not kosher but is legal), in addition # to foo.h (if present) and foo.cc. all_files = (glob.glob('%s-*' % all_but_extension) + glob.glob('%s/*/%s-*' % (dirname, basename)) + glob.glob('%s.h' % all_but_extension) + glob.glob('%s/*/%s.h' % (dirname, basename))) files_to_check = [f for f in all_files if not f.endswith(extension)] files_to_check.append(filename) # IWYU emits summaries with canonicalized filepaths, where all the # directory separators are set to '/'. In order for the testsuite to # correctly match up file summaries, we must canonicalize the filepaths # in the same way here. files_to_check = [PosixPath(f) for f in files_to_check] iwyu_test_util.TestIwyuOnRelativeFile(filename, files_to_check, verbose=True) def GenerateTests(rootdir, pattern): def _AddTestFunctions(cls): filenames = [] test_files = {} for (dirpath, _, files) in os.walk(rootdir): dirpath = PosixPath(dirpath) # Normalize path separators. filenames.extend(posixpath.join(dirpath, f) for f in files if fnmatch(f, pattern)) if not filenames: print('No tests found in %s!' % os.path.abspath(rootdir)) return for filename in filenames: all_but_extension = os.path.splitext(filename)[0] basename = os.path.basename(all_but_extension) test_name = re.sub('[^0-9a-zA-Z_]', '_', basename) # python-clean test_name = 'test_%s' % test_name while hasattr(cls, test_name): # already have a class with that name test_name += '2' # just append a suffix :-) setattr(cls, test_name, lambda x, f=filename: TestIwyuOnRelevantFiles(f)) test_files[test_name] = filename setattr(cls, 'test_files', test_files) return cls return _AddTestFunctions def EnumerateLoadedTests(): for suite in unittest.defaultTestLoader.loadTestsFromModule(sys.modules[__name__]): for test in suite: yield (test.__class__, test._testMethodName) def PrintLoadedTests(): for (cls, test) in EnumerateLoadedTests(): print('%s.%s' % (cls.__name__, test)) def PrintLoadedTestsAndFiles(): for (cls, test) in EnumerateLoadedTests(): print('%s.%s:%s' % (cls.__name__, test, cls.test_files[test])) if __name__ == '__main__': unittest_args, additional_args = Partition(sys.argv, '--') if additional_args: iwyu_test_util.SetIwyuPath(additional_args[0]) parser = argparse.ArgumentParser(add_help=False, usage=argparse.SUPPRESS) group = parser.add_mutually_exclusive_group() group.add_argument('--list', dest='list_tests', action='store_true') group.add_argument('--list-test-files', action='store_true') group.add_argument('--run-test-file') (runner_args, _) = parser.parse_known_args(unittest_args) if runner_args.run_test_file: exit(TestIwyuOnRelevantFiles(runner_args.run_test_file)) @GenerateTests(rootdir='tests/c', pattern='*.c') class c(unittest.TestCase): pass @GenerateTests(rootdir='tests/cxx', pattern='*.cc') class cxx(unittest.TestCase): pass @GenerateTests(rootdir='tests/driver', pattern='*.c') class driver(unittest.TestCase): pass if runner_args.list_tests: exit(PrintLoadedTests()) elif runner_args.list_test_files: exit(PrintLoadedTestsAndFiles()) unittest.main(argv=unittest_args)