summaryrefslogtreecommitdiffstats
path: root/run_iwyu_tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'run_iwyu_tests.py')
-rwxr-xr-xrun_iwyu_tests.py339
1 files changed, 91 insertions, 248 deletions
diff --git a/run_iwyu_tests.py b/run_iwyu_tests.py
index a566949..9e7918f 100755
--- a/run_iwyu_tests.py
+++ b/run_iwyu_tests.py
@@ -13,6 +13,7 @@
__author__ = 'dsturtevant@google.com (Dean Sturtevant)'
+import argparse
import glob
import os
import re
@@ -39,252 +40,75 @@ def Partition(l, delimiter):
return l[:delim_index], l[delim_index+1:]
-class OneIwyuTest(unittest.TestCase):
- """Superclass for tests. A subclass per test-file is created at runtime."""
-
- def CheckAlsoExtension(self, extension):
- """Return a suitable iwyu flag for checking files with the given extension.
- """
- return '--check_also="%s"' % posixpath.join(self.rootdir, '*' + extension)
-
- def MappingFile(self, filename):
- """Return a suitable iwyu flag for adding the given mapping file."""
- return '--mapping_file=%s' % posixpath.join(self.rootdir, filename)
-
- def Include(self, filename):
- """Return a -include switch for clang to force include of file."""
- return '-include %s' % posixpath.join(self.rootdir, filename)
-
- def setUp(self):
- # Iwyu flags for specific tests.
- # Map from filename to flag list. If any test requires special
- # iwyu flags to run properly, add an entry to the map with
- # key=cc-filename (relative to self.rootdir), value=list of flags.
- flags_map = {
- 'backwards_includes.cc': [self.CheckAlsoExtension('-d*.h')],
- 'badinc.cc': [self.MappingFile('badinc.imp')],
- 'builtins_with_mapping.cc': [self.MappingFile('builtins_with_mapping.imp')],
- 'check_also.cc': [self.CheckAlsoExtension('-d1.h')],
- 'implicit_ctor.cc': [self.CheckAlsoExtension('-d1.h')],
- 'iwyu_stricter_than_cpp.cc': [self.CheckAlsoExtension('-autocast.h'),
- self.CheckAlsoExtension('-fnreturn.h'),
- self.CheckAlsoExtension('-typedefs.h'),
- self.CheckAlsoExtension('-d2.h')],
- 'keep_includes.c': ['--keep=tests/c/keep_includes*.h'],
- 'keep_mapping.cc': [self.CheckAlsoExtension('-public.h'),
- self.MappingFile('keep_mapping.imp')],
- 'keep_moc.cc': [self.CheckAlsoExtension('-i1.h')],
- 'macro_location.cc': [self.CheckAlsoExtension('-d2.h')],
- 'mapping_to_self.cc': [self.MappingFile('mapping_to_self.imp')],
- 'non_transitive_include.cc': [self.CheckAlsoExtension('-d*.h'),
- '--transitive_includes_only'],
- 'no_h_includes_cc.cc': [self.CheckAlsoExtension('.c')],
- 'no_comments.cc': ['--no_comments'],
- 'no_fwd_decls.cc': ['--no_fwd_decls'],
- 'overloaded_class.cc': [self.CheckAlsoExtension('-i1.h')],
- 'pch_in_code.cc': ['--pch_in_code', '--prefix_header_includes=remove'],
- 'prefix_header_attribution.cc': ['--prefix_header_includes=remove'],
- 'prefix_header_includes_add.cc': ['--prefix_header_includes=add'],
- 'prefix_header_includes_keep.cc': ['--prefix_header_includes=keep'],
- 'prefix_header_includes_remove.cc': ['--prefix_header_includes=remove'],
- 'prefix_header_operator_new.cc': ['--prefix_header_includes=remove'],
- 'quoted_includes_first.cc': ['--pch_in_code', '--quoted_includes_first'],
- 'relative_exported_mapped_include.cc':
- [self.MappingFile('relative_exported_mapped_include.imp')],
- 'cxx17ns.cc': ['--cxx17ns'],
- }
- prefix_headers = [self.Include('prefix_header_includes-d1.h'),
- self.Include('prefix_header_includes-d2.h'),
- self.Include('prefix_header_includes-d3.h'),
- self.Include('prefix_header_includes-d4.h')]
- clang_flags_map = {
- 'alias_template.cc': ['-std=c++11'],
- 'auto_type_within_template.cc': ['-std=c++11'],
- # MSVC targets need to explicitly enable exceptions, so we do it for all.
- 'catch.cc': ['-fcxx-exceptions', '-fexceptions'],
- 'clmode.cc': ['--driver-mode=cl', '/GF', '/Os', '/W2'],
- 'conversion_ctor.cc': ['-std=c++11'],
- 'deleted_implicit.cc' : ['-std=c++11'],
- 'funcptrs.cc': ['-Wno-unused'],
- 'lambda_fwd_decl.cc': ['-std=c++11'],
- 'lateparsed_template.cc': ['-fdelayed-template-parsing'],
- 'macro_defined_by_includer.cc': [
- '-std=c++11', '-DCOMMAND_LINE_TYPE=double',
- self.Include('macro_defined_by_includer-prefix.h')],
- 'macro_location.cc': ['-Wno-sizeof-pointer-div'],
- 'ms_inline_asm.cc': ['-fms-extensions'],
- 'operator_new.cc': ['-std=c++17'],
- 'placement_new.cc': ['-std=c++17'],
- 'prefix_header_attribution.cc': [self.Include('prefix_header_attribution-d1.h')],
- 'prefix_header_includes_add.cc': prefix_headers,
- 'prefix_header_includes_keep.cc': prefix_headers,
- 'prefix_header_includes_remove.cc': prefix_headers,
- 'range_for.cc': ['-std=c++11'],
- 'typedef_in_template.cc': ['-std=c++11'],
- 'inheriting_ctor.cc': ['-std=c++11'],
- 'cxx17ns.cc': ['-std=c++17'],
- }
- include_map = {
- 'alias_template.cc': ['.'],
- 'array.cc': ['.'],
- 'associated_h_file_heuristic.cc': ['.'],
- 'associated_include.cc': ['.'],
- 'associated_skipped.cc': ['.'],
- 'backwards_includes.cc': ['.'],
- 'badinc.cc': ['.'],
- 'badinc-extradef.cc': ['.'],
- 'binary_type_trait.cc': ['.'],
- 'builtins_with_mapping.cc': ['.'],
- 'funcptrs.cc': ['.'],
- 'casts.cc': ['.'],
- 'catch.cc': ['.'],
- 'check_also.cc': ['.'],
- 'clmode.cc': ['.'],
- 'comment_pragmas.cc': ['.'],
- 'computed_include.cc': ['.'],
- 'conversion_ctor.cc': ['.'],
- 'cvr.cc': ['.'],
- 'default_template_arg_other_file.cc': ['.'],
- 'depopulated_h_file.cc': ['.'],
- 'derived_function_tpl_args.cc': ['.'],
- 'dotdot.cc': ['.'],
- 'double_include.cc': ['.'],
- 'elaborated_struct.c': ['.'],
- 'elaborated_type.cc': ['.'],
- 'enum_base.cc': ['.'],
- 'export_nesting.cc': ['.'],
- 'external_including_internal.cc': ['.'],
- 'forward_declare_in_macro.cc': ['.'],
- 'fullinfo_for_templates.cc': ['.'],
- 'fwd_decl_class_template.cc': ['.'],
- 'fwd_decl_final.cc': ['.'],
- 'fwd_decl_static_member.cc': ['.'],
- 'fwd_decl_with_instantiation.cc': ['.'],
- 'header_in_subdir.cc': ['.'],
- 'implicit_ctor.cc': ['.'],
- 'include_cycle.cc': ['.'],
- 'include_with_using.cc': ['.'],
- 'inline_namespace.cc': ['.'],
- 'internal/internal_files.cc': ['.'],
- 'iwyu_stricter_than_cpp.cc': ['.'],
- 'keep_includes.c': ['.'],
- 'keep_mapping.cc': ['.'],
- 'keep_moc.cc': ['.'],
- 'lateparsed_template.cc': ['.'],
- 'macro_defined_by_includer.cc': ['.'],
- 'macro_location.cc': ['.'],
- 'mapping_to_self.cc': ['.'],
- 'member_expr.cc': ['.'],
- 'multiple_include_paths.cc': ['.'],
- 'new_header_path_provided.cc': ['.'],
- 'no_comments.cc': ['.'],
- 'no_fwd_decl_nested_class.cc': ['.'],
- 'no_fwd_decls.cc': ['.'],
- 'no_h_includes_cc.cc': ['.'],
- 'non_transitive_include.cc': ['.'],
- 'operator_new.cc': ['.'],
- 'overloaded_class.cc': ['.'],
- 'pch_in_code.cc': ['.'],
- 'pointer_arith.cc': ['.'],
- 'placement_new.cc': ['.'],
- 'pragma_associated.cc': ['.'],
- 'precomputed_tpl_args.cc': ['.'],
- 'prefix_header_attribution.cc': ['.'],
- 'prefix_header_includes_add.cc': ['.'],
- 'prefix_header_includes_keep.cc': ['.'],
- 'prefix_header_includes_remove.cc': ['.'],
- 'quoted_includes_first.cc' : ['.'],
- 'range_for.cc': ['.'],
- 're_fwd_decl.cc': ['.'],
- 'redecls.cc': ['.'],
- 'relative_exported_mapped_include.cc': ['tests/cxx/subdir'],
- 'remove_fwd_decl_when_including.cc': ['.'],
- 'self_include.cc': ['.'],
- 'sizeof_in_template_arg.cc': ['.'],
- 'sizeof_reference.cc': ['.'],
- 'specialization_needs_decl.cc': ['.'],
- 'system_namespaces.cc': ['.'],
- 'template_args.cc': ['.'],
- 'templated_constructor.cc': ['.'],
- 'template_specialization.cc': ['.'],
- 'typedef_chain_in_template.cc': ['.'],
- 'typedef_chain_no_follow.cc': ['.'],
- 'typedef_in_template.cc': ['.'],
- 'typedefs_and_resugaring.cc': ['.'],
- 'unused_class_template_ctor.cc': ['.'],
- 'uses_printf.cc': ['.'],
- 'using_aliased_symbol.cc': ['.'],
- 'using_aliased_symbol_unused.cc': ['.'],
- 'varargs_and_references.cc': ['.'],
- 'virtual_tpl_method.cc': ['.'],
- 'cxx17ns.cc': ['.'],
- }
- # Internally, we like it when the paths start with rootdir.
- self._iwyu_flags_map = dict((posixpath.join(self.rootdir, k), v)
- for (k,v) in flags_map.items())
- self._clang_flags_map = dict((posixpath.join(self.rootdir, k), v)
- for (k,v) in clang_flags_map.items())
- self._include_map = dict((posixpath.join(self.rootdir, k), ['-I ' + include for include in v])
- for (k,v) in include_map.items())
-
- def RunOneTest(self, filename):
- logging.info('Testing iwyu on %s', filename)
- # Split full/path/to/foo.cc into full/path/to/foo and .cc.
- (all_but_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 fnmatch(f, self.pattern)]
- 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_flags = self._iwyu_flags_map.get(filename, None)
- clang_flags = self._clang_flags_map.get(filename, [])
- clang_flags.extend(self._include_map.get(filename, []))
- iwyu_test_util.TestIwyuOnRelativeFile(self, filename, files_to_check,
- iwyu_flags, clang_flags, verbose=True)
-
-
-def RegisterFilesForTesting(rootdir, pattern):
- """Create a test-class for every file in rootdir matching pattern."""
- filenames = []
- for (dirpath, dirs, 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
-
- module = sys.modules[__name__]
-
- for filename in filenames:
- all_but_extension = os.path.splitext(filename)[0]
- basename = os.path.basename(all_but_extension)
- class_name = re.sub('[^0-9a-zA-Z_]', '_', basename) # python-clean
- if class_name[0].isdigit(): # classes can't start with a number
- class_name = '_' + class_name
- while class_name in module.__dict__: # already have a class with that name
- class_name += '2' # just append a suffix :-)
-
- logging.info('Registering %s to test %s', class_name, filename)
- test_class = type(class_name, # class name
- (OneIwyuTest,), # superclass
- # and attrs. f=filename is required for proper scoping
- {'runTest': lambda self, f=filename: self.RunOneTest(f),
- 'rootdir': rootdir,
- 'pattern': pattern})
- setattr(module, test_class.__name__, test_class)
+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__':
@@ -292,6 +116,25 @@ if __name__ == '__main__':
if additional_args:
iwyu_test_util.SetIwyuPath(additional_args[0])
- RegisterFilesForTesting('tests/cxx', '*.cc')
- RegisterFilesForTesting('tests/c', '*.c')
+ 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
+
+ if runner_args.list_tests:
+ exit(PrintLoadedTests())
+ elif runner_args.list_test_files:
+ exit(PrintLoadedTestsAndFiles())
+
unittest.main(argv=unittest_args)