#!/usr/bin/env python3 ##===--- fix_includes_test.py - test for fix_includes.py ------------------===## # # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. # ##===----------------------------------------------------------------------===## from __future__ import print_function """Test for fix_includes.py Test test test! """ __author__ = 'csilvers@google.com (Craig Silverstein)' try: from cStringIO import StringIO except ImportError: from io import StringIO import re import sys # I use unittest instead of googletest to ease opensourcing. import unittest import fix_includes class FakeFlags(object): def __init__(self): self.blank_lines = False self.comments = True self.update_comments = False self.dry_run = False self.ignore_re = None self.only_re = None self.safe_headers = False self.separate_project_includes = None self.keep_iwyu_namespace_format = False self.reorder = True self.basedir = None class FixIncludesBase(unittest.TestCase): """Does setup that every test will want.""" def _ReadFile(self, filename, fileinfo): assert filename in self.before_map, filename return self.before_map[filename] def _ParseFileInfo(self, filename): return fix_includes.FileInfo('\n', 'utf-8') def _WriteFile(self, filename, fileinfo, contents): return self.actual_after_contents.extend(contents) def setUp(self): self.flags = FakeFlags() # Map from filename to its contents (a list of lines) before fixing. self.before_map = {} # Map from filename to the 'correct' contents it should have after fixing. self.expected_after_map = {} # INPUT: fix_includes._ReadFile takes a filename # and returns the contents of filename (as a list). # FileInfo controls encoding details of the file, # wire it to return something that agrees with the # tests. fix_includes._ReadFile = self._ReadFile fix_includes.FileInfo.parse = self._ParseFileInfo # OUTPUT: Instead of writing to file, save full output. self.actual_after_contents = [] fix_includes._WriteFile = self._WriteFile # Stub out stdout self.stdout_stub = StringIO() fix_includes.sys.stdout = self.stdout_stub def RegisterFileContents(self, file_contents_map): """Parses and stores the given map from filename to file-contents. The values of the map are file 'contents', written in a simple markup language that allows us to encode both the 'before' and expected 'after' contents of a file. Every line is taken literally to be in both the before and after, with the following exceptions: 1) Lines that look like '///+foo' are removed from 'before', and replaced by 'foo' in 'after'. (This is an 'add' instruction.) 2) Lines that end in '///-' are removed from both 'after' and the '\s*///-' suffix is removed from 'before'. (This is a 'remove' instruction.) This function processes the input map to produce self.before_map and self.expected_after_map. Arguments: file_contents_map: a map from filename to 'contents'. Contents is a string, having the format mentioned above. """ remove_re = re.compile('\s*///-$') for (filename, contents) in file_contents_map.items(): before_contents = [] expected_after_contents = [] for line in contents.splitlines(True): m = remove_re.search(line) if m: # The trailing line separator is stripped, so append a '\n'. before_contents.append(line[:m.start()] + '\n') elif line.startswith('///+'): expected_after_contents.append(line[len('///+'):]) else: before_contents.append(line) expected_after_contents.append(line) self.before_map[filename] = before_contents self.expected_after_map[filename] = expected_after_contents def ProcessAndTest(self, iwyu_output, cmdline_files=None, unedited_files=[], expected_num_modified_files=None, cwd=None): """For all files mentioned in iwyu_output, compare expected and actual. Arguments: iwyu_output: the output the iwyu script gave when run over the set of input files. cmdline_files: files to pass in to ProcessIWYUOutput (that, in an actual fix_includes run, would come from the commandline). These limit what files fix_includes chooses to edit. unedited_files: the list of files that are listed in iwyu_output, but fix_files has chosen not to edit for some reason. expected_num_modified_files: what we expect ProcessIWYUOutput to return. If None, suppress this check. cwd: working directory passed to ProcessIWYUOutput, used to normalize paths in cmdline_files. If None, no normalization occurs. """ filenames = re.findall('^(\S+) should add these lines:', iwyu_output, re.M) if not filenames: # This is the other possible starting-line filenames = re.findall('^\((\S+) has correct #includes/fwd-decls\)', iwyu_output, re.M) expected_after = [] for filename in fix_includes.OrderedSet(filenames): # uniquify filename = fix_includes.NormalizeFilePath(self.flags.basedir, filename) if filename not in unedited_files: expected_after.extend(self.expected_after_map[filename]) iwyu_output_as_file = StringIO(iwyu_output) num_modified_files = fix_includes.ProcessIWYUOutput(iwyu_output_as_file, cmdline_files, self.flags, cwd=cwd) if expected_after != self.actual_after_contents: print("=== Expected:") for line in expected_after: print(line) print("=== Got:") for line in self.actual_after_contents: print(line) print("===") self.assertListEqual(expected_after, self.actual_after_contents) if expected_num_modified_files is not None: self.assertEqual(expected_num_modified_files, num_modified_files) class FixIncludesTest(FixIncludesBase): """End-to-end tests from input file to output file.""" def testSimple(self): infile = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" int main() { return 0; } """ iwyu_output = """\ simple should add these lines: #include #include "used2.h" simple should remove these lines: - #include // lines 3-3 The full include-list for simple: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'simple': infile}) self.ProcessAndTest(iwyu_output, expected_num_modified_files=1) def testUnifiedDiffOutput(self): """Test the unified diff output generated by dry runs.""" infile = """ #include int main() { return 0; } """ iwyu_output = """\ unified_diff.cc should add these lines: unified_diff.cc should remove these lines: - #include // lines 2-2 The full include-list for unified_diff.cc: --- """ diff_expect = """\ >>> Fixing #includes in 'unified_diff.cc' @@ -1,4 +1,2 @@ - -#include int main() { return 0; } IWYU edited 1 files on your behalf. """ self.flags.dry_run = True self.RegisterFileContents({'unified_diff.cc': infile}) self.ProcessAndTest(iwyu_output, unedited_files=['unified_diff.cc']) self.assertEqual(self.stdout_stub.getvalue(), diff_expect) def testNodiffOutput(self): """Tests handling of the '( has correct #includes)' iwyu output.""" infile = """\ // Copyright 2010 #include #include // iwyu will not reorder, even though non-alphabetical namespace Foo; namespace Bar; int main() { return 0; } """ iwyu_output = "(nodiffs.h has correct #includes/fwd-decls)\n" self.RegisterFileContents({'nodiffs.h': infile}) # fix_includes gives special output when there are no changes, so # we can't use the normal ProcessAndTest. iwyu_output_as_file = StringIO(iwyu_output) num_modified_files = fix_includes.ProcessIWYUOutput(iwyu_output_as_file, None, self.flags, None) self.assertListEqual([], self.actual_after_contents) # 'no diffs' self.assertEqual(0, num_modified_files) def testNodiffOutputWithNoSorting(self): """Tests 'correct #includes' iwyu output, but does not need reordering.""" infile = """\ // Copyright 2010 #include #include namespace Foo; namespace Bar; int main() { return 0; } """ iwyu_output = "(nodiffs_nosorting.h has correct #includes/fwd-decls)\n" self.RegisterFileContents({'nodiffs_nosorting.h': infile}) # fix_includes gives special output when there are no changes, so # we can't use the normal ProcessAndTest. iwyu_output_as_file = StringIO(iwyu_output) num_modified_files = fix_includes.ProcessIWYUOutput(iwyu_output_as_file, None, self.flags, None) self.assertListEqual([], self.actual_after_contents) # 'no diffs' self.assertEqual(0, num_modified_files) def testRemoveEmptyNamespace(self): """Tests we remove a namespace if we remove all fwd-decls inside it.""" infile = """\ // Copyright 2010 #include namespace ns { ///- class Foo; ///- namespace ns2 { ///- namespace ns3 { ///- class Bar; ///- } } ///- class Baz; ///- } ///- ///- int main() { return 0; } """ iwyu_output = """\ empty_namespace should add these lines: empty_namespace should remove these lines: - class Foo; // lines 6-6 - namespace ns { namespace ns2 { namespace ns3 { class Bar; } } } // lines 9-9 - namespace ns { class Baz; } } // lines 11-11 The full include-list for empty_namespace: #include --- """ self.RegisterFileContents({'empty_namespace': infile}) self.ProcessAndTest(iwyu_output) def testRemoveEmptyAllmanNamespace(self): """Tests we remove a namespace with Allman braces if we remove all fwd-decls inside it.""" infile = """\ // Copyright 2010 #include namespace ns ///- { ///- class Foo; ///- namespace ns2 ///- { ///- namespace ns3 ///- { ///- class Bar; ///- } ///- } ///- class Baz; ///- } ///- ///- int main() { return 0; } """ iwyu_output = """\ empty_namespace should add these lines: empty_namespace should remove these lines: - class Foo; // lines 7-7 - namespace ns { namespace ns2 { namespace ns3 { class Bar; } } } // lines 12-12 - namespace ns { class Baz; } } // lines 15-15 The full include-list for empty_namespace: #include --- """ self.RegisterFileContents({'empty_namespace': infile}) self.ProcessAndTest(iwyu_output) def testRemoveEmptyMixedNamespace(self): """Tests we remove a namespace with mixed braces if we remove all fwd-decls inside it.""" infile = """\ // Copyright 2010 #include namespace ns ///- { ///- class Foo; ///- namespace ns2 { namespace ns3 ///- { ///- class Bar; ///- } ///- } ///- class Baz; ///- } ///- ///- int main() { return 0; } """ iwyu_output = """\ empty_namespace should add these lines: empty_namespace should remove these lines: - class Foo; // lines 7-7 - namespace ns { namespace ns2 { namespace ns3 { class Bar; } } } // lines 10-10 - namespace ns { class Baz; } } // lines 13-13 The full include-list for empty_namespace: #include --- """ self.RegisterFileContents({'empty_namespace': infile}) self.ProcessAndTest(iwyu_output) def testCXX17NS(self): """Tests handling of output using the --cxx17ns switch.""" infile = """\ #include "cxx17ns-i1.h"///- ///+ ///+namespace a::b::c { ///+struct One; ///+} // namespace a::b::c ///+namespace a::b { ///+struct One2; ///+} // namespace a::b ///+namespace a { ///+struct One4; ///+struct One3; ///+} // namespace a struct Two { Two(a::b::c::One& one); Two(a::b::One2& one); Two(a::One3& one); Two(a::One4& one); }; """ iwyu_output = """\ cxx17ns.cc should add these lines: namespace a { namespace { struct One4; } } namespace a { struct One3; } namespace a::b { struct One2; } namespace a::b::c { struct One; } cxx17ns.cc should remove these lines: - #include "cxx17ns-i1.h" // lines 1-1 The full include-list for cxx17ns.cc: namespace a { namespace { struct One4; } } namespace a { struct One3; } namespace a::b { struct One2; } namespace a::b::c { struct One; } --- """ self.RegisterFileContents({'cxx17ns.cc': infile}) self.ProcessAndTest(iwyu_output) def testNamespaceAlias(self): """Tests we leave namespace aliases alone.""" infile = """\ #include ///- namespace outer { namespace middle { namespace inner { enum Values { VAL }; } // namespace inner } // namespace middle // This alias should not be mistaken for an Allman namespace dfn namespace inner = middle::inner; } // namespace outer """ iwyu_output = """\ namespace_alias.cc should add these lines: namespace_alias.cc should remove these lines: - #include // lines 1-1 The full include-list for namespace_alias.cc: --- """ self.RegisterFileContents({'namespace_alias.cc': infile}) self.ProcessAndTest(iwyu_output) def testRemovePartOfEmptyNamespace(self): """Tests we remove a namespace if empty, but not enclosing namespaces.""" infile = """\ // Copyright 2010 namespace maps_transit_realtime { namespace service_alerts { class StaticServiceAlertStore; namespace trigger { ///- class Trigger; ///- } // namespace trigger ///- namespace ui { ///- class Alert; ///- } // namespace ui ///- } // namespace service_alerts } // namespace maps_transit_realtime int main() { return 0; } """ iwyu_output = """\ empty_internal_namespace should add these lines: empty_internal_namespace should remove these lines: - namespace maps_transit_realtime { namespace service_alerts { namespace trigger { class Trigger; } } } // lines 7-7 - namespace maps_transit_realtime { namespace service_alerts { namespace ui { class Alert; } } } // lines 10-10 The full include-list for empty_internal_namespace: namespace maps_transit_realtime { namespace service_alerts { class StaticServiceAlertStore; } } // lines 5-5 --- """ self.RegisterFileContents({'empty_internal_namespace': infile}) self.ProcessAndTest(iwyu_output) def testRemovePartOfEmptyAllmanNamespace(self): """Tests we remove a namespace with Allman braces if empty, but not enclosing namespaces.""" infile = """\ // Copyright 2010 namespace maps_transit_realtime { namespace service_alerts { class StaticServiceAlertStore; namespace trigger ///- { ///- class Trigger; ///- } // namespace trigger ///- namespace ui ///- { ///- class Alert; ///- } // namespace ui ///- } // namespace service_alerts } // namespace maps_transit_realtime int main() { return 0; } """ iwyu_output = """\ empty_internal_namespace should add these lines: empty_internal_namespace should remove these lines: - namespace maps_transit_realtime { namespace service_alerts { namespace trigger { class Trigger; } } } // lines 10-10 - namespace maps_transit_realtime { namespace service_alerts { namespace ui { class Alert; } } } // lines 14-14 The full include-list for empty_internal_namespace: namespace maps_transit_realtime { namespace service_alerts { class StaticServiceAlertStore; } } // lines 7-7 --- """ self.RegisterFileContents({'empty_internal_namespace': infile}) self.ProcessAndTest(iwyu_output) def testRemovePartOfEmptyMixedNamespace(self): """Tests we remove a namespace with mixed braces if empty, but not enclosing namespaces.""" infile = """\ // Copyright 2010 namespace maps_transit_realtime { class StaticServiceAlertStore; namespace service_alerts { namespace trigger ///- { ///- class Trigger; ///- } // namespace trigger ///- namespace ui ///- { ///- class Alert; ///- } // namespace ui ///- } // namespace service_alerts ///- } // namespace maps_transit_realtime int main() { return 0; } """ iwyu_output = """\ empty_internal_namespace should add these lines: empty_internal_namespace should remove these lines: - namespace maps_transit_realtime { namespace service_alerts { namespace trigger { class Trigger; } } } // lines 8-8 - namespace maps_transit_realtime { namespace service_alerts { namespace ui { class Alert; } } } // lines 12-12 The full include-list for empty_internal_namespace: namespace maps_transit_realtime { class StaticServiceAlertStore; } // lines 5-5 --- """ self.RegisterFileContents({'empty_internal_namespace': infile}) self.ProcessAndTest(iwyu_output) def testRemoveEmptyIfdef(self): """Tests we remove an #ifdef if we remove all #includes inside it.""" # Also makes sure we reorder properly around the removed ifdef. infile = """\ // Copyright 2010 #include ///+#include // Only on windows. ///- #ifdef OS_WINDOWS ///- #include ///- #include ///- #endif ///- #include "used.h" #include ///- ///+#include "used2.h" int main() { return 0; } """ iwyu_output = """\ empty_ifdef should add these lines: #include "used2.h" empty_ifdef should remove these lines: - #include // lines 6-6 - #include // lines 7-7 The full include-list for empty_ifdef: #include #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'empty_ifdef': infile}) self.ProcessAndTest(iwyu_output) def testRemoveEmptyNestedIfdef(self): """Tests we remove an empty #ifdef inside a non-empty #ifdef.""" infile = """\ // Copyright 2010 #include #ifdef NDEBUG // Only on windows. ///- # ifdef OS_WINDOWS ///- # include ///- # include ///- # endif ///- # undef VERBOSE_LOGGING #endif ///+#include #include "used.h" #include ///- ///+#include "used2.h" int main() { return 0; } """ iwyu_output = """\ empty_nested_ifdef should add these lines: #include "used2.h" empty_nested_ifdef should remove these lines: - #include // lines 7-7 - #include // lines 8-8 The full include-list for empty_nested_ifdef: #include #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'empty_nested_ifdef': infile}) self.ProcessAndTest(iwyu_output) def testNonEmptyIfdef(self): """Tests we keep an #ifdef if we don't remove all #includes inside it.""" infile = """\ // Copyright 2010 #include #ifdef OS_WINDOWS #include ///- #include #endif #include "used.h" ///+#include "used2.h" int main() { return 0; } """ iwyu_output = """\ nonempty_ifdef should add these lines: #include "used2.h" nonempty_ifdef should remove these lines: - #include // lines 5-5 The full include-list for nonempty_ifdef: #include #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'nonempty_ifdef': infile}) self.ProcessAndTest(iwyu_output) def testKeepIfdefsWithNonIncludes(self): """Tests we keep an #ifdef if we have a non-#include inside it.""" infile = """\ // Copyright 2010 #include #ifdef OS_WINDOWS #define IN_WINDOWS #include ///- #endif #include "used.h" ///+#include "used2.h" int main() { return 0; } """ iwyu_output = """\ nonempty_ifdef should add these lines: #include "used2.h" nonempty_ifdef should remove these lines: - #include // lines 6-6 The full include-list for nonempty_ifdef: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'nonempty_ifdef': infile}) self.ProcessAndTest(iwyu_output) def testRemoveComments(self): """Tests we remove comments above #includes.""" infile = """\ // Copyright 2010 #include // This file is not used. ///- #include ///- ///- // This file is not used either. ///- // It's not used. ///- // Not used at all. ///- #include ///- ///- #include "notused3.h" ///- // This comment should stay, it's not before an #include. const int kInt = 5; // This file is used. // It's definitedly used. #include "used.h" ///+#include "used2.h" const int kInt2 = 6; ///- // This forward-declare is in a reorder_span all by itself. ///- class NotUsed; ///- // This comment should stay, it's not before an #include. int main() { return 0; } """ iwyu_output = """\ remove_comments should add these lines: #include "used2.h" remove_comments should remove these lines: - #include // lines 5-5 - #include // lines 10-10 - #include "notused3.h" // lines 12-12 - class NotUsed; // lines 23-23 The full include-list for remove_comments: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'remove_comments': infile}) self.ProcessAndTest(iwyu_output) def testNoBlankLineAfterTopOfFileCxxComments(self): """Tests we don't remove top-of-file c++ comments right before #includes.""" infile = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" int main() { return 0; } """ iwyu_output = """\ top_of_file_comments.cc should add these lines: #include #include "used2.h" top_of_file_comments.cc should remove these lines: - #include // lines 2-2 The full include-list for top_of_file_comments.cc: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'top_of_file_comments.cc': infile}) self.ProcessAndTest(iwyu_output) def testNoBlankLineAfterTopOfFileCComments(self): """Tests we don't remove top-of-file c comments right before #includes.""" infile = """\ /* * Copyright 2010 */ #include ///- /* This is a one-line c-style comment. */ ///- #include /* this is a c-style comment after a line */ ///- ///+#include #include "used.h" ///+#include "used2.h" int main() { return 0; } """ iwyu_output = """\ top_of_file_comments.c should add these lines: #include #include "used2.h" top_of_file_comments.c should remove these lines: - #include // lines 4-4 - #include // lines 6-6 The full include-list for top_of_file_comments.c: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'top_of_file_comments.c': infile}) self.ProcessAndTest(iwyu_output) def testNotFullLineCComments(self): """Tests that we treat lines with c comments then code as code-lines.""" infile = """\ // Copyright 2010 ///+#include ///+ /* code here */ x = 4; int main() { return 0; } """ iwyu_output = """\ not_full_line_c_comments.c should add these lines: #include not_full_line_c_comments.c should remove these lines: The full include-list for not_full_line_c_comments.c: #include --- """ self.RegisterFileContents({'not_full_line_c_comments.c': infile}) self.ProcessAndTest(iwyu_output) def testUnusualHFileNames(self): """Tests we treat .pb.h files as header files.""" infile = """\ /* * Copyright 2010 */ #include ///- ///+#include #include "used.pb.h" ///+#include "used2.pb.h" int main() { return 0; } """ iwyu_output = """\ pb.h.cc should add these lines: #include #include "used2.pb.h" pb.h.cc should remove these lines: - #include // lines 4-4 The full include-list for pb.h.cc: #include #include "used.pb.h" #include "used2.pb.h" --- """ self.RegisterFileContents({'pb.h.cc': infile}) self.ProcessAndTest(iwyu_output) def testFwdDeclLines(self): """Tests that we keep or remove forward declares based on iwyu output.""" infile = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" struct KeepStruct; class NoKeepClass; ///- template class KeepTplClass; int main() { return 0; } """ iwyu_output = """\ fwd_decl should add these lines: #include #include "used2.h" fwd_decl should remove these lines: - #include // lines 3-3 - class NoKeepClass; // lines 7-7 The full include-list for fwd_decl: #include #include "used.h" #include "used2.h" struct KeepStruct; // lines 6-6 template class KeepTplClass; // lines 8-8 --- """ self.RegisterFileContents({'fwd_decl': infile}) self.ProcessAndTest(iwyu_output) def testMultiLineFwdDecls(self): """Tests we keep forward-decls that span more than one line.""" infile = """\ // Copyright 2010 struct KeepStruct; class NoKeepClass; ///- template class Keep2LineTplClass; template ///- class NoKeep2LineTplClass; ///- template class Keep3LineTplClass; template ///- class NoKeep3LineTplClass; ///- int main() { return 0; } """ iwyu_output = """\ multiline_fwd_decl should add these lines: multiline_fwd_decl should remove these lines: - class NoKeepClass; // lines 4-4 - template class NoKeep2LineTplClass; // lines 7-8 - template class NoKeep3LineTplClass; // lines 12-14 The full include-list for multiline_fwd_decl: struct KeepStruct; // lines 3-3 template class Keep2LineTplClass; // lines 5-6 template class Keep3LineTplClass; // lines 9-11 --- """ self.RegisterFileContents({'multiline_fwd_decl': infile}) self.ProcessAndTest(iwyu_output) def testKeepExplicitSpecializations(self): """Tests we don't interpret an explicit spec. as a forward-declare.""" infile = """\ // Copyright 2010 struct KeepStruct; class NoKeepClass; ///- template class KeepTplClass; ///+ template<> class KeepTplClass; template void TplFn(); int main() { return 0; } """ iwyu_output = """\ explicit_specialization should add these lines: explicit_specialization should remove these lines: - class NoKeepClass; // lines 4-4 The full include-list for explicit_specialization: struct KeepStruct; // lines 3-3 template class KeepTplClass; // lines 5-5 --- """ self.RegisterFileContents({'explicit_specialization': infile}) self.ProcessAndTest(iwyu_output) def testKeepNestedForwardDeclares(self): """Tests that we don't remove forward-declares inside classes/structs.""" infile = """\ // Copyright 2010 class Keep; class NoKeep; ///- ///+ class Nest { class NestedClass; ///+ class NestedClass { }; class NestedClass2 { }; // looks just like a fwd declare, except for the {} template class NestedTplClass; // test multi-line nested classes as well ///+ friend class NoKeep; template friend class NoKeepTpl; }; int main() { return 0; } """ iwyu_output = """\ nested_fwd_decl should add these lines: nested_fwd_decl should remove these lines: - class NoKeep; // lines 4-4 The full include-list for nested_fwd_decl: class Keep; // lines 3-3 class Nest::NestedClass; // lines 6-6 template class Nest::NestedTplClass; // lines 11-11 --- """ self.RegisterFileContents({'nested_fwd_decl': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareBeforeUsingStatement(self): """Tests we never add a forward-declare after a contentful line.""" infile = """\ // Copyright 2010 #include "foo.h" ///+namespace Bar { ///+class Baz; ///+} // namespace Bar ///+ using Bar::baz; namespace Foo { class Bang; } ///- ///+namespace Foo { ///+class Bang; ///+} // namespace Foo int main() { return 0; } """ iwyu_output = """\ add_fwd_decl_before_using should add these lines: namespace Bar { class Baz; } add_fwd_decl_before_using should remove these lines: The full include-list for add_fwd_decl_before_using: #include "foo.h" namespace Bar { class Baz; } namespace Foo { class Bang; } // lines 7-7 --- """ self.RegisterFileContents({'add_fwd_decl_before_using': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInNamespace(self): """Make sure we normalize namespaces properly.""" infile = """\ // Copyright 2010 #include "foo.h" ///+namespace ns { ///+class Foo; ///+namespace ns2 { ///+namespace ns3 { ///+class Bar; ///+template class Bang; ///+} // namespace ns3 ///+} // namespace ns2 ///+namespace ns4 { ///+class Baz; ///+} // namespace ns4 ///+} // namespace ns ///+ int main() { return 0; } """ iwyu_output = """\ add_fwd_decl_inside_namespace should add these lines: namespace ns { class Foo; } namespace ns { namespace ns2 { namespace ns3 { class Bar; } } } namespace ns { namespace ns2 { namespace ns3 { template class Bang; } } } namespace ns { namespace ns4 { class Baz; } } add_fwd_decl_inside_namespace should remove these lines: The full include-list for add_fwd_decl_inside_namespace: #include "foo.h" // lines 3-3 namespace ns { class Foo; } namespace ns { namespace ns2 { namespace ns3 { class Bar; } } } namespace ns { namespace ns2 { namespace ns3 { template class Bang; } } } namespace ns { namespace ns4 { class Baz; } } --- """ self.RegisterFileContents({'add_fwd_decl_inside_namespace': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInsideNamespaceSometimes(self): """Tests that in special situations, we will put fwd-decls inside a ns.""" infile = """\ // Copyright 2010 #include "foo.h" class Bar; template class Baz; namespace ns { namespace ns2 { // we sure do love nesting our namespaces! class NsFoo; ///+namespace ns3 { ///+class NsBang; ///+template class NsBaz; ///+} // namespace ns3 template class NsBar; } } int main() { return 0; } """ iwyu_output = """\ add_fwd_decl_inside_namespace should add these lines: namespace ns { namespace ns2 { namespace ns3 { class NsBang; } } } namespace ns { namespace ns2 { namespace ns3 { template class NsBaz; } } } add_fwd_decl_inside_namespace should remove these lines: The full include-list for add_fwd_decl_inside_namespace: #include "foo.h" // lines 3-3 class Bar; // lines 5-5 namespace ns { namespace ns2 { class NsFoo; } } // lines 12-12 namespace ns { namespace ns2 { namespace ns3 { class NsBang; } } } namespace ns { namespace ns2 { namespace ns3 { template class NsBaz; } } } namespace ns { namespace ns2 { template class NsBar; } } // lines 13-13 template class Baz; // lines 6-6 --- """ self.RegisterFileContents({'add_fwd_decl_inside_namespace': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInsideNamespaceWithHeaderGuard(self): """Tests that the header guard doesn't confuse our in-ns algorithm.""" infile = """\ // Copyright 2010 #ifndef HDR_GUARD #define HDR_GUARD #include "foo.h" class Bar; template class Baz; namespace ns { namespace ns2 { // we sure do love nesting our namespaces! class NsFoo; ///+namespace ns3 { ///+class NsBang; ///+template class NsBaz; ///+} // namespace ns3 template class NsBar; } } #endif // HDR_GUARD """ iwyu_output = """\ add_fwd_decl_with_hdr_guard should add these lines: namespace ns { namespace ns2 { namespace ns3 { class NsBang; } } } namespace ns { namespace ns2 { namespace ns3 { template class NsBaz; } } } add_fwd_decl_with_hdr_guard should remove these lines: The full include-list for add_fwd_decl_with_hdr_guard: #include "foo.h" // lines 6-6 class Bar; // lines 8-8 namespace ns { namespace ns2 { class NsFoo; } } // lines 15-15 namespace ns { namespace ns2 { namespace ns3 { class NsBang; } } } namespace ns { namespace ns2 { namespace ns3 { template class NsBaz; } } } namespace ns { namespace ns2 { template class NsBar; } } // lines 16-16 template class Baz; // lines 9-9 --- """ self.RegisterFileContents({'add_fwd_decl_with_hdr_guard': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInsideNamespaceWithIfDef(self): """Tests that ifdef blocks are ignored when finding namespaces.""" infile = """\ // Copyright 2010 #include "foo.h" class Bar; template class Baz; #ifdef THIS_IS_A_CONTENTFUL_LINE #include "bar.h" #endif namespace ns { namespace ns2 { ///+class NsBang; class NsFoo; template class NsBar; ///+template class NsBaz; } } int main() { return 0; } """ iwyu_output = """\ add_forward_declares_after_ifdef_code should add these lines: namespace ns { namespace ns2 { class NsBang; } } namespace ns { namespace ns2 { template class NsBaz; } } add_forward_declares_after_ifdef_code should remove these lines: The full include-list for add_forward_declares_after_ifdef_code: #include "foo.h" // lines 3-3 class Bar; // lines 5-5 namespace ns { namespace ns2 { class NsBang; } } namespace ns { namespace ns2 { class NsFoo; } } // lines 16-16 namespace ns { namespace ns2 { template class NsBar; } } // lines 17-17 namespace ns { namespace ns2 { template class NsBaz; } } template class Baz; // lines 6-6 --- """ self.RegisterFileContents({'add_forward_declares_after_ifdef_code': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInsideNamespaceWithoutForwardDeclaresAlready(self): """Tests we put fwd-decls inside an ns even if the ns has no fwd-decl.""" infile = """\ // Copyright 2010 #include "foo.h" class Bar; template class Baz; namespace ns { namespace ns2 { // we sure do love nesting our namespaces! ///- ///+namespace ns3 { ///+class NsBang; ///+template class NsBaz; ///+} // namespace ns3 ///+ int MyFunction() { } } } int main() { return 0; } """ iwyu_output = """\ add_fwd_decl_inside_namespace_without_fwd_decl should add these lines: namespace ns { namespace ns2 { namespace ns3 { class NsBang; } } } namespace ns { namespace ns2 { namespace ns3 { template class NsBaz; } } } add_fwd_decl_inside_namespace_without_fwd_decl should remove these lines: The full include-list for add_fwd_decl_inside_namespace_without_fwd_decl: #include "foo.h" // lines 3-3 class Bar; // lines 5-5 namespace ns { namespace ns2 { namespace ns3 { class NsBang; } } } namespace ns { namespace ns2 { namespace ns3 { template class NsBaz; } } } template class Baz; // lines 6-6 --- """ self.RegisterFileContents({'add_fwd_decl_inside_namespace_without_fwd_decl': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInsideNamespaceWithCompactEndings(self): """Tests we put fwd-decls inside an ns when using compact namespace endings.""" infile = """\ // Copyright 2010 namespace ns { namespace ns1 { namespace ns2 { class Ns2Bang; }} // namespace ns2 // namespace ns1 ///+class NsBar; class NsBaz; ///- namespace ns3 { namespace ns4 { ///- class Ns4Bye; ///- }} // namespace ns4 // namespace ns3 ///- } // namespace ns int main() { return 0; } """ iwyu_output = """\ add_fwd_decl_inside_namespace_without_compact_endings should add these lines: namespace ns { class NsBar; } add_fwd_decl_inside_namespace_without_compact_endings should remove these lines: - namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { namespace ns4 { class Ns4Bye; } } } } } // lines 9-9 The full include-list for add_fwd_decl_inside_namespace_without_compact_endings: namespace ns { namespace ns1 { namespace ns2 { class Ns2Bang; } } } // lines 4-4 namespace ns { class NsBaz; } // lines 6-6 --- """ self.RegisterFileContents({'add_fwd_decl_inside_namespace_without_compact_endings': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInsideNamespaceWithUnnamedNamespace(self): """Tests that unnamed namespaces do not mess up our in-ns calculation.""" infile = """\ // Copyright 2010 #include "foo.h" class Bar; namespace ns { ///+class NsBang; ///+template class NsBaz; namespace { class NsFoo; template class NsBar; } } int main() { return 0; } """ iwyu_output = """\ add_fwd_decl_inside_namespace_unnamed_ns should add these lines: namespace ns { class NsBang; } namespace ns { template class NsBaz; } add_fwd_decl_inside_namespace_unnamed_ns should remove these lines: The full include-list for add_fwd_decl_inside_namespace_unnamed_ns: #include "foo.h" // lines 3-3 class Bar; // lines 5-5 namespace ns { namespace { class NsFoo; } } // lines 10-10 namespace ns { class NsBang; } namespace ns { template class NsBaz; } namespace ns { namespace { template class NsBar; } } // lines 11-11 --- """ self.RegisterFileContents({'add_fwd_decl_inside_namespace_unnamed_ns': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInsideNamespacesWithUnnamedNamespaceAndContent(self): """Tests that nested namespaces with forward declares still get new additions.""" infile = """\ // Copyright 2010 #include "foo.h" class Bar; ///+class Baz; namespace ns { ///+class NsBang; ///+template class NsBaz; namespace { ///+class NsBaz; class NsFoo; template class NsBar; } namespace ns1 { ///+class Ns1Bar; ///+class Ns1Baz; class Ns1Foo; int ns_int = 5; // here's my contentful line } } int main() { return 0; } """ iwyu_output = """\ add_fwd_decl_inside_namespaces_with_existing_content should add these lines: class Baz; namespace ns { class NsBang; } namespace ns { template class NsBaz; } namespace ns { namespace { class NsBaz; } } namespace ns { namespace ns1 { class Ns1Bar; } } namespace ns { namespace ns1 { class Ns1Baz; } } add_fwd_decl_inside_namespaces_with_existing_content should remove these lines: The full include-list for add_fwd_decl_inside_namespaces_with_existing_content: #include "foo.h" // lines 3-3 class Bar; // lines 5-5 class Baz; namespace ns { namespace { class NsFoo; } } // lines 10-10 namespace ns { namespace { class NsBaz; } } namespace ns { class NsBang; } namespace ns { template class NsBaz; } namespace ns { namespace { template class NsBar; } } // lines 11-11 namespace ns { namespace ns1 { class Ns1Foo; } } // lines 15-15 namespace ns { namespace ns1 { class Ns1Bar; } } namespace ns { namespace ns1 { class Ns1Baz; } } --- """ self.RegisterFileContents({'add_fwd_decl_inside_namespaces_with_existing_content': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInsideAllmanNamespacesWithUnnamedNamespaceAndContent(self): """Tests that nested Allman namespaces with forward declares still get new additions.""" infile = """\ // Copyright 2010 #include "foo.h" class Bar; ///+class Baz; namespace ns { ///+class NsBang; ///+template class NsBaz; namespace { ///+class NsBaz; class NsFoo; template class NsBar; } namespace ns1 { ///+class Ns1Bar; ///+class Ns1Baz; class Ns1Foo; int ns_int = 5; // here's my contentful line } } int main() { return 0; } """ iwyu_output = """\ add_fwd_decl_inside_allman_namespaces_with_existing_content should add these lines: class Baz; namespace ns { class NsBang; } namespace ns { template class NsBaz; } namespace ns { namespace { class NsBaz; } } namespace ns { namespace ns1 { class Ns1Bar; } } namespace ns { namespace ns1 { class Ns1Baz; } } add_fwd_decl_inside_allman_namespaces_with_existing_content should remove these lines: The full include-list for add_fwd_decl_inside_allman_namespaces_with_existing_content: #include "foo.h" // lines 3-3 class Bar; // lines 5-5 class Baz; namespace ns { namespace { class NsFoo; } } // lines 12-12 namespace ns { namespace { class NsBaz; } } namespace ns { class NsBang; } namespace ns { template class NsBaz; } namespace ns { namespace { template class NsBar; } } // lines 13-13 namespace ns { namespace ns1 { class Ns1Foo; } } // lines 18-18 namespace ns { namespace ns1 { class Ns1Bar; } } namespace ns { namespace ns1 { class Ns1Baz; } } --- """ self.RegisterFileContents({'add_fwd_decl_inside_allman_namespaces_with_existing_content': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInsideMixedNamespacesWithUnnamedNamespaceAndContent(self): """Tests that nested mixed namespaces with forward declares still get new additions.""" infile = """\ // Copyright 2010 #include "bar.h" ///+ ///+class Baz; ///+ namespace ns { namespace ns1 { namespace ns2 { ///+class Ns2Bang; ///+template class Ns2Baz; ///+ namespace { ///+class NsaBaz; class NsaFoo; template class NsaBar; } // namespace namespace ns3 { ///+class Ns3Bar; ///+class Ns3Baz; class Ns3Foo; ///+ int ns3_int = 5; // here's my contentful line } // namespace ns3 } // namespace ns2 } // namespace ns1 } // namespace ns int main() { return 0; } """ iwyu_output = """\ add_fwd_decl_inside_mixed_namespaces_with_existing_content should add these lines: class Baz; namespace ns { namespace ns1 { namespace ns2 { class Ns2Bang; } } } namespace ns { namespace ns1 { namespace ns2 { template class Ns2Baz; } } } namespace ns { namespace ns1 { namespace ns2 { namespace { class NsaBaz; } } } } namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Bar; } } } } namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Baz; } } } } add_fwd_decl_inside_mixed_namespaces_with_existing_content should remove these lines: The full include-list for add_fwd_decl_inside_mixed_namespaces_with_existing_content: #include "bar.h" // lines 3-3 #include "foo.h" class Baz; namespace ns { namespace ns1 { namespace ns2 { class Ns2Bang; } } } namespace ns { namespace ns1 { namespace ns2 { template class Ns2Baz; } } } namespace ns { namespace ns1 { namespace ns2 { namespace { class NsaFoo; } } } } // lines 8-8 namespace ns { namespace ns1 { namespace ns2 { namespace { template class NsaBar; } } } } // lines 9-9 namespace ns { namespace ns1 { namespace ns2 { namespace { class NsaBaz; } } } } namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Bar; } } } } namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Baz; } } } } namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Foo; } } } } // lines 12-12 --- """ self.RegisterFileContents({'add_fwd_decl_inside_mixed_namespaces_with_existing_content': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInsideNestedNamespacesAndTopLevelForComplexNamespaces(self): """Tests that nested namespaces still get new additions while putting hard to resolve forward declares at the top.""" infile = """\ // Copyright 2010 ///+namespace ns { ///+class NsBang; ///+namespace ns1 { ///+Ns1Bang; ///+} // namespace ns1 ///+} // namespace ns ///+ namespace ns { namespace ns1 { namespace ns2 { class Ns2Bang; class Ns2Bar; ///- ///+class Ns2Baz; ///+ namespace ns3 { ///+class Ns3Bang; class Ns3Baz; } // namespace ns3 } // namespace ns2 } // namespace ns1 } // namespace ns int main() { return 0; } """ iwyu_output = """\ add_fwd_decl_inside_nested_namespaces_and_top_level_for_complex_namespaces should add these lines: namespace ns { class NsBang; } namespace ns { namespace ns1 { Ns1Bang; } namespace ns { namespace ns1 { namespace ns2 { class Ns2Baz; } } } namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Bang; } } } } add_fwd_decl_inside_nested_namespaces_and_top_level_for_complex_namespaces should remove these lines: - namespace ns { namespace ns1 { namespace ns2 { class Ns2Bar; } } } // lines 5-5 The full include-list for add_fwd_decl_inside_nested_namespaces_and_top_level_for_complex_namespaces: namespace ns { class NsBang; } namespace ns { namespace ns1 { Ns1Bang; } namespace ns { namespace ns1 { namespace ns2 { class Ns2Bang; } } } // lines 4-4 namespace ns { namespace ns1 { namespace ns2 { class Ns2Baz; } } } namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Bang; } } } } namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Baz; } } } } // lines 7-7 --- """ self.RegisterFileContents({'add_fwd_decl_inside_nested_namespaces_and_top_level_for_complex_namespaces': infile}) self.ProcessAndTest(iwyu_output) def testRemoveNamespaces(self): """Tests that we keep or remove ns's based on fwd decl content.""" infile = """\ // Copyright 2010 namespace ns1 { struct KeepStruct; class NoKeepClass; ///- template class KeepTplClass; } ///- namespace ns1 { ///- namespace ns2 { ///- // This should all go away. ///- // Even with the multi-line comment here. ///- template class NoKeepTplClass; ///- } ///- } ///- int main() { return 0; } """ iwyu_output = """\ ns_fwd_decl should add these lines: ns_fwd_decl should remove these lines: - class NoKeepClass; // lines 5-5 - namespace ns1 { namespace ns2 { template class NoKeepTplClass; } } // lines 13-13 The full include-list for ns_fwd_decl: namespace ns1 { struct KeepStruct; } // lines 4-4 namespace ns1 { template class KeepTplClass; } // lines 6-6 --- """ self.RegisterFileContents({'ns_fwd_decl': infile}) self.ProcessAndTest(iwyu_output) def testKeepNamespacesWithNonForwardDecls(self): """Tests we never remove a ns with 'real' content in it.""" infile = """\ // Copyright 2010 namespace ns1 { int ns_int = 5; class NoKeepClass; ///- } int main() { return 0; } """ iwyu_output = """\ keep_ns_fwd_decl should add these lines: keep_ns_fwd_decl should remove these lines: - class NoKeepClass; // lines 5-5 The full include-list for keep_ns_fwd_decl: --- """ self.RegisterFileContents({'keep_ns_fwd_decl': infile}) self.ProcessAndTest(iwyu_output) def testICUNamespaces(self): """Tests we treat the icu namespace macros as namespaces.""" infile = """\ // Copyright 2010 U_NAMESPACE_BEGIN // macro from icu struct KeepStruct; class NoKeepClass; ///- template class KeepTplClass; U_NAMESPACE_END ///- U_NAMESPACE_BEGIN ///- template class NoKeepTplClass; ///- U_NAMESPACE_END ///- int main() { return 0; } """ iwyu_output = """\ icu_namespace should add these lines: icu_namespace should remove these lines: - class NoKeepClass; // lines 5-5 - template class NoKeepTplClass; // lines 10-10 The full include-list for icu_namespace: namespace ns1 { struct KeepStruct; } // lines 4-4 namespace ns1 { template class KeepTplClass; } // lines 6-6 --- """ self.RegisterFileContents({'icu_namespace': infile}) self.ProcessAndTest(iwyu_output) def testHashNamespaces(self): """Tests we treat the hash namespace macros as namespaces.""" infile = """\ // Copyright 2010 HASH_NAMESPACE_DECLARATION_START // macro from hash.h struct KeepStruct; class NoKeepClass; ///- template class KeepTplClass; HASH_NAMESPACE_DECLARATION_END ///- HASH_NAMESPACE_DECLARATION_START ///- template class NoKeepTplClass; ///- HASH_NAMESPACE_DECLARATION_END ///- int main() { return 0; } """ iwyu_output = """\ hash_namespace should add these lines: hash_namespace should remove these lines: - class NoKeepClass; // lines 5-5 - template class NoKeepTplClass; // lines 10-10 The full include-list for hash_namespace: namespace ns1 { struct KeepStruct; } // lines 4-4 namespace ns1 { template class KeepTplClass; } // lines 6-6 --- """ self.RegisterFileContents({'hash_namespace': infile}) self.ProcessAndTest(iwyu_output) def testElaboratedClasses(self): """Tests we don't remove lines like 'class Foo* fooptr'.""" infile = """\ // Copyright 2010 struct KeepStruct; class NoKeepClass; ///- ///+ struct NoKeepStruct* s; struct NoKeepStruct& t; int main() { return 0; } """ iwyu_output = """\ elaborated_class should add these lines: elaborated_class should remove these lines: - class NoKeepClass; // lines 4-4 The full include-list for elaborated_class: struct KeepStruct; // lines 3-3 --- """ self.RegisterFileContents({'elaborated_class': infile}) self.ProcessAndTest(iwyu_output) def testBFlag(self): """Tests that --b properly separates sections.""" self.flags.blank_lines = True infile = """\ // Copyright 2010 #include ///- ///+#include "subdir/bflag.h" ///+ ///+#include ///+ #include "used.h" ///+#include "used2.h" int main() { return 0; } """ iwyu_output = """\ subdir/bflag.cc should add these lines: #include "subdir/bflag.h" #include #include "used2.h" subdir/bflag.cc should remove these lines: - #include // lines 3-3 The full include-list for subdir/bflag.cc: #include "subdir/bflag.h" #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'subdir/bflag.cc': infile}) self.ProcessAndTest(iwyu_output) def testSafeHeadersFlag(self): """Tests that --safe_headers causes us to not delete lines.""" self.flags.safe_headers = True infile = """\ // Copyright 2010 #include #include // Hello! ///+#include #include "used.h" ///+#include "used2.h" class Foo; template class Bar; int main() { return 0; } """ iwyu_output = """\ safe_flag.h should add these lines: #include #include "used2.h" safe_flag.h should remove these lines: - #include // lines 3-3 - #include // lines 4-4 - class Foo // lines 7-7 - class Bar // lines 8-9 The full include-list for safe_flag.h: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'safe_flag.h': infile}) self.ProcessAndTest(iwyu_output) def testSafeHeadersFlagTwice(self): """Tests running --safe_headers 2ce in a row doesn't duplicate comments.""" self.flags.safe_headers = True infile = """\ // Copyright 2010 #include // iwyu says this can be removed #include // Hello!; iwyu says this can be removed ///+#include #include "used.h" ///+#include "used2.h" class Foo; // iwyu says this can be removed template // iwyu says this can be removed class Bar; // iwyu says this can be removed int main() { return 0; } """ iwyu_output = """\ safe_flag_twice.h should add these lines: #include #include "used2.h" safe_flag_twice.h should remove these lines: - #include // lines 3-3 - #include // lines 4-4 - class Foo // lines 7-7 - class Bar // lines 8-9 The full include-list for safe_flag_twice.h: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'safe_flag_twice.h': infile}) self.ProcessAndTest(iwyu_output) def testSafeHeadersFlagOnCcFiles(self): """Tests that we delete even in --safe_headers mode, on .cc files.""" self.flags.safe_headers = True infile = """\ // Copyright 2010 #include ///- #include // Hello! ///- ///+#include #include "used.h" ///+#include "used2.h" class Foo; ///- template ///- class Bar; ///- ///- int main() { return 0; } """ iwyu_output = """\ safe_flag.cc should add these lines: #include #include "used2.h" safe_flag.cc should remove these lines: - #include // lines 3-3 - #include // lines 4-4 - class Foo // lines 7-7 - class Bar // lines 8-9 The full include-list for safe_flag.cc: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'safe_flag.cc': infile}) self.ProcessAndTest(iwyu_output) def testIncludeComments(self): """Tests that we properly include comments on #include lines.""" infile = """\ // Copyright 2010 #include ///- ///+#include "subdir/include_comments.h" ///+#include // for printf(), etc. #include "used.h" ///- ///+#include "used.h" // for Used ///+#include "used2.h" // for Used2, Used2::Used2, Used2::~Used2, Used2::Used_Enum, operator==() int main() { return 0; } """ iwyu_output = """\ subdir/include_comments.cc should add these lines: #include "subdir/include_comments.h" #include // for printf(), etc. #include "used2.h" // for Used2, Used2::Used2, Used2::~Used2, Used2::Used_Enum, operator==() subdir/include_comments.cc should remove these lines: - #include // lines 3-3 The full include-list for subdir/include_comments.cc: #include "subdir/include_comments.h" #include // for printf(), etc. #include "used.h" // for Used #include "used2.h" // for Used2, Used2::Used2, Used2::~Used2, Used2::Used_Enum, operator==() --- """ self.RegisterFileContents({'subdir/include_comments.cc': infile}) self.ProcessAndTest(iwyu_output) def testNocommentsFlag(self): """Tests we properly don't include/modify comments with --nocomments.""" self.flags.comments = False infile = """\ // Copyright 2010 #include ///- ///+#include "subdir/include_comments.h" ///+#include #include "used.h" // my favorite #include! ///+#include "used2.h" int main() { return 0; } """ iwyu_output = """\ subdir/include_comments.cc should add these lines: #include "subdir/include_comments.h" #include // for printf(), etc. #include "used2.h" // for Used2, Used2::Used2, Used2::~Used2, Used2::Used_Enum, operator==() subdir/include_comments.cc should remove these lines: - #include // lines 3-3 The full include-list for subdir/include_comments.cc: #include "subdir/include_comments.h" #include // for printf(), etc. #include "used.h" // for Used #include "used2.h" // for Used2, Used2::Used2, Used2::~Used2, Used2::Used_Enum, operator==() --- """ self.RegisterFileContents({'subdir/include_comments.cc': infile}) self.ProcessAndTest(iwyu_output) def testUpdateCommentsFlag(self): """Tests we update comments with --update_comments.""" self.flags.update_comments = True infile = """\ #include "must_keep.h" // IWYU pragma: keep #include "used.h" // for SomethingElse ///- ///+#include "used.h" // for Used Used used; int main() { return 0; } """ iwyu_output = """\ subdir/include_comments.cc should add these lines: subdir/include_comments.cc should remove these lines: The full include-list for subdir/include_comments.cc: #include "must_keep.h" #include "used.h" // for Used --- """ self.RegisterFileContents({'subdir/include_comments.cc': infile}) self.ProcessAndTest(iwyu_output) def testNoUpdateCommentsFlag(self): """Tests we don't update comments with --noupdate_comments.""" self.flags.update_comments = False infile = """\ #include "must_keep.h" // IWYU pragma: keep #include "used.h" // for SomethingElse Used used; int main() { return 0; } """ iwyu_output = """\ subdir/include_comments.cc should add these lines: subdir/include_comments.cc should remove these lines: The full include-list for subdir/include_comments.cc: #include "must_keep.h" #include "used.h" // for Used --- """ self.RegisterFileContents({'subdir/include_comments.cc': infile}) self.ProcessAndTest(iwyu_output, unedited_files=['subdir/include_comments.cc']) def testFixingTwoFiles(self): """Make sure data for one fix doesn't overlap with a second.""" file_a = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" #include "used_only_in_file_a.h" #include "used_only_in_file_b.h" ///- class FileAClass; // kept for file A, not for file B class FileBClass; // kept for file B, not for file A ///- """ file_b = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" #include "used_only_in_file_a.h" ///- #include "used_only_in_file_b.h" class FileAClass; // kept for file A, not for file B ///- class FileBClass; // kept for file B, not for file A """ iwyu_output = """\ file_a.cc should add these lines: #include #include "used2.h" file_a.cc should remove these lines: - #include // lines 3-3 - #include "used_only_in_file_b.h" // lines 6-6 - class FileBClass; // lines 9-9 The full include-list for file_a.cc: #include #include "used.h" #include "used2.h" #include "used_only_in_file_a.h" class FileAClass; // lines 8-8 --- file_b.cc should add these lines: #include #include "used2.h" file_b.cc should remove these lines: - #include // lines 3-3 - #include "used_only_in_file_a.h" // lines 5-5 - class FileAClass; // lines 8-8 The full include-list for file_b.cc: #include #include "used.h" #include "used2.h" #include "used_only_in_file_b.h" class FileBClass; // lines 9-9 --- """ self.RegisterFileContents({'file_a.cc': file_a, 'file_b.cc': file_b}) self.ProcessAndTest(iwyu_output) def testListingTheSameFileTwice(self): """Test when foo.cc is specified twice. It should fix conservatively.""" infile = """\ // Copyright 2010 #include ///- ///+#include ///+#include "include_this_file" // for reason 2 #include "used.h" ///+#include "used2.h" #include "used_only_in_file_a.h" #include "used_only_in_file_b.h" ///- class FileAClass; // kept for file A, not for file B class FileBClass; // kept for file B, not for file A ///- ///+namespace foo { ///+template ClassTemplate; ///+} // namespace foo ///+template ClassTemplate; """ iwyu_output = """\ twice.cc should add these lines: #include #include "include_this_file" // for reason 1 namespace foo { template ClassTemplate; } template ClassTemplate; twice.cc should remove these lines: - #include // lines 3-3 - #include "used_only_in_file_a.h" // lines 5-5 - #include "used_only_in_file_b.h" // lines 6-6 - class FileAClass; // lines 8-8 - class FileBClass; // lines 9-9 The full include-list for twice.cc: #include #include "include_this_file" // for reason 1 #include "used.h" namespace foo { template ClassTemplate; } template ClassTemplate; --- twice.cc should add these lines: #include "used2.h" #include "include_this_file" // for reason 2 namespace foo { template ClassTemplate; } template ClassTemplate; twice.cc should remove these lines: - #include // lines 3-3 - #include "used_only_in_file_b.h" // lines 6-6 - class FileBClass; // lines 9-9 The full include-list for twice.cc: #include "include_this_file" // for reason 2 #include "used.h" #include "used2.h" #include "used_only_in_file_a.h" class FileAClass; // lines 8-8 namespace foo { template ClassTemplate; } template ClassTemplate; --- """ self.RegisterFileContents({'twice.cc': infile}) self.ProcessAndTest(iwyu_output) def testListingTheSameFileTwiceAndOnceIsANoop(self): """Test when foo.cc is specified twice, once with 'all correct'.""" infile = """\ // Copyright 2010 #include ///+#include ///+#include "include_this_file" // for reason 1 #include "used.h" #include "used_only_in_file_a.h" class FileAClass; ///+namespace foo { ///+template ClassTemplate; ///+} // namespace foo ///+template ClassTemplate; """ iwyu_output = """\ twice.cc should add these lines: #include #include "include_this_file" // for reason 1 namespace foo { template ClassTemplate; } template ClassTemplate; twice.cc should remove these lines: - #include // lines 3-3 - #include "used_only_in_file_a.h" // lines 5-5 - class FileAClass; // lines 7-7 The full include-list for twice.cc: #include #include "include_this_file" // for reason 1 #include "used.h" namespace foo { template ClassTemplate; } template ClassTemplate; --- (twice.cc has correct #includes/fwd-decls) """ self.RegisterFileContents({'twice.cc': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclare(self): """Test adding a forward-declare, rather than keeping one.""" infile = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" ///+ ///+struct NotUsed; int main() { return 0; } """ iwyu_output = """\ new_fwd_decl should add these lines: #include #include "used2.h" struct NotUsed; new_fwd_decl should remove these lines: - #include // lines 3-3 The full include-list for new_fwd_decl: #include #include "used.h" #include "used2.h" struct NotUsed; --- """ self.RegisterFileContents({'new_fwd_decl': infile}) self.ProcessAndTest(iwyu_output) def testAddAndKeepForwardDeclare(self): """Test adding a forward-declare in addition to keeping one.""" infile = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" class ForwardDeclClass; ///+struct NotUsed; int main() { return 0; } """ iwyu_output = """\ new_and_keep_fwd_decl should add these lines: #include #include "used2.h" struct NotUsed; new_and_keep_fwd_decl should remove these lines: - #include // lines 3-3 The full include-list for new_and_keep_fwd_decl: #include #include "used.h" #include "used2.h" class ForwardDeclareClass; // lines 6-6 struct NotUsed; --- """ self.RegisterFileContents({'new_and_keep_fwd_decl': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeToFileThatHasOnlyForwardDeclarations(self): """Tests we add an #include in an appropriate place if none exist.""" infile = """\ // Copyright 2010 ///+#include ///+#include "used.h" ///+ const int kFoo = 5; // we should insert before the contentful line. class Foo; int main() { return 0; } """ iwyu_output = """\ no_include_fwd_decl should add these lines: #include #include "used.h" no_include_fwd_decl should remove these lines: The full include-list for no_include_fwd_decl: #include #include "used.h" class Foo; // lines 5-5 --- """ self.RegisterFileContents({'no_include_fwd_decl': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclarationToFileThatHasOnlyIncludes(self): """Tests we add a forward-declare in an appropriate place if none exist.""" infile = """\ // Copyright 2010 const int kFoo = 5; // make sure we don't just insert at the beginning #include #include "used.h" ///+ ///+class Foo; int main() { return 0; } """ iwyu_output = """\ no_fwd_decl_include should add these lines: class Foo; no_fwd_decl_include should remove these lines: The full include-list for no_fwd_decl_include: #include #include "used.h" class Foo; --- """ self.RegisterFileContents({'no_fwd_decl_include': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeToContentlessFile(self): """Tests we add an #include ok to a basically empty file..""" infile = """\ // Copyright 2010 ///+#include ///+#include "used.h" ///+ ///+class Foo; """ iwyu_output = """\ no_include should add these lines: #include #include "used.h" class Foo; no_include should remove these lines: The full include-list for no_include: #include #include "used.h" class Foo; --- """ self.RegisterFileContents({'no_include': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeToEmptyFile(self): """Tests we add an #include ok to an empty file..""" infile = '' iwyu_output = """\ empty_file should add these lines: #include #include "used.h" class Foo; empty_file should remove these lines: The full include-list for empty_file: #include #include "used.h" class Foo; --- """ self.RegisterFileContents({'empty_file': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeToOnlyOneContentfulLineFile(self): """Prevent regression when the only contentful line was the last.""" infile = """\ // Copyright 2010 ///+ ///+#include int main() { return 0; } """ iwyu_output = """\ only_one_contentful_line.c should add these lines: #include only_one_contentful_line.c should remove these lines: The full include-list for only_one_contentful_line.c: #include --- """ self.RegisterFileContents({'only_one_contentful_line.c': infile}) self.ProcessAndTest(iwyu_output) def testCommentsAtEndOfFile(self): """Tests we don't crash if a file ends with #includs and then a comment.""" infile = """\ // Copyright 2010 const int kFoo = 5; // make sure we don't just insert at the beginning #include #include "used.h" ///+ ///+class Foo; // Comments, and then...nothing """ iwyu_output = """\ comments_at_end_of_file should add these lines: class Foo; comments_at_end_of_file should remove these lines: The full include-list for comments_at_end_of_file: #include #include "used.h" class Foo; --- """ self.RegisterFileContents({'comments_at_end_of_file': infile}) self.ProcessAndTest(iwyu_output) def testAddSystemIncludeToFileWithoutAny(self): """Tests we add a system #include to a non-sys location when needed.""" infile = """\ // Copyright 2010 #ifdef HAVE_TYPE_TRAITS_H #include #endif ///+#include #include "used.h" int main() { return 0; } """ iwyu_output = """\ system_include should add these lines: #include system_include should remove these lines: The full include-list for system_include: #include #include "used.h" --- """ self.RegisterFileContents({'system_include': infile}) self.ProcessAndTest(iwyu_output) def testAddNonSystemHeaderUnderMainCUHeader(self): """Tests we distinguish main-cu headers from other non-system headers.""" infile = """\ // Copyright 2010 ///+#include "main_cu.h" #include "main_cu-inl.h" ///- #include ///+#include #ifdef WINDOWS #include #endif #include "used.h" ///+#include "used2.h" int main() { return 0; } """ iwyu_output = """\ main_cu_test.cc should add these lines: #include #include "main_cu.h" #include "used2.h" main_cu_test.cc should remove these lines: The full include-list for main_cu_test.cc: #include "main_cu.h" #include "main_cu-inl.h" #include #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'main_cu_test.cc': infile}) self.ProcessAndTest(iwyu_output) def testAddWithNearestIncludes(self): """Tests we add "includes" with when there's a choice.""" infile = """\ // Copyright 2010 #include "nearest_include.h" static int x = 6; #include ///+#include "used.h" static int y = 7; class Foo; int main() { return 0; } """ iwyu_output = """\ nearest_include.cc should add these lines: #include "used.h" nearest_include.cc should remove these lines: The full include-list for nearest_include.cc: #include "nearest_include.h" #include #include "used.h" class Foo; // lines 9-9 --- """ self.RegisterFileContents({'nearest_include.cc': infile}) self.ProcessAndTest(iwyu_output) def testFalseAlarmHeaderGuard(self): """Tests we calculate top-level-ness even in face of a fake header-guard.""" infile = """\ // Copyright 2010 #include "nearest_toplevel_include.h" static int x = 6; #include ///+#include "used.h" #ifndef MAP_ANONYMOUS // This is the fake header guard! # define MAP_ANONYMOUS MAP_ANON #endif #ifdef FOO #include #endif #if defined(BAR) #include #endif static int y = 7; class Foo; int main() { return 0; } """ iwyu_output = """\ nearest_toplevel_include.cc should add these lines: #include "used.h" nearest_toplevel_include.cc should remove these lines: The full include-list for nearest_toplevel_include.cc: #include "nearest_toplevel_include.h" #include #include #include #include "used.h" class Foo; // lines 9-9 --- """ self.RegisterFileContents({'nearest_toplevel_include.cc': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeAfterHeaderGuard(self): """Test that we are willing to insert .h's inside a header guard.""" infile = """\ // Copyright 2010 #ifndef SIMPLE_H_ #define SIMPLE_H_ #include ///- ///+#include #include "used.h" ///+#include "used2.h" #endif """ iwyu_output = """\ simple.h should add these lines: #include #include "used2.h" simple.h should remove these lines: - #include // lines 6-6 The full include-list for simple.h: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'simple.h': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeAfterSoloPragmaOnce(self): """Test that we are willing to insert .h's after #pragma once.""" infile = """\ // Copyright 2010 #pragma once #include ///- ///+#include #include "used.h" ///+#include "used2.h" """ iwyu_output = """\ pragma_once.h should add these lines: #include #include "used2.h" pragma_once.h should remove these lines: - #include // lines 5-5 The full include-list for pragma_once.h: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'pragma_once.h': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeAfterPragmaOnceWithHeaderGuard(self): """Test that we are willing to insert .h's after #pragma once and header guard.""" infile = """\ // Copyright 2010 #pragma once #ifndef PRAGMA_ONCE_H_ #define PRAGMA_ONCE_H_ #include ///- ///+#include #include "used.h" ///+#include "used2.h" #endif """ iwyu_output = """\ pragma_once_with_guard.h should add these lines: #include #include "used2.h" pragma_once_with_guard.h should remove these lines: - #include // lines 7-7 The full include-list for pragma_once_with_guard.h: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'pragma_once_with_guard.h': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeAfterEarlyPragmaOnce(self): """Test that we are willing to insert .h's after early #pragma once.""" infile = """\ #pragma once // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" """ iwyu_output = """\ early_pragma_once.h should add these lines: #include #include "used2.h" early_pragma_once.h should remove these lines: - #include // lines 4-4 The full include-list for early_pragma_once.h: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'early_pragma_once.h': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeAfterEarlyPragmaOnceWithHeaderGuard(self): """Test that we are willing to insert .h's after early #pragma once and header guard.""" infile = """\ #pragma once // Copyright 2010 #ifndef PRAGMA_ONCE_H_ #define PRAGMA_ONCE_H_ #include ///- ///+#include #include "used.h" ///+#include "used2.h" #endif """ iwyu_output = """\ early_pragma_once_with_guard.h should add these lines: #include #include "used2.h" early_pragma_once_with_guard.h should remove these lines: - #include // lines 7-7 The full include-list for early_pragma_once_with_guard.h: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'early_pragma_once_with_guard.h': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeAfterWeirdPragmaOnce(self): """Test that we are willing to insert .h's after creatively formatted #pragma once.""" infile = """\ # pragma once #include ///- ///+#include """ iwyu_output = """\ weird_pragma_once.h should add these lines: #include weird_pragma_once.h should remove these lines: - #include // lines 3-3 The full include-list for weird_pragma_once.h: #include --- """ self.RegisterFileContents({'weird_pragma_once.h': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeBeforePragmaMessage(self): """Test that non-once #pragmas are pushed after the #includes.""" infile = """\ ///+#include ///+ #pragma message "Hello world!" #include ///- """ iwyu_output = """\ weird_pragma_once.h should add these lines: #include weird_pragma_once.h should remove these lines: - #include // lines 3-3 The full include-list for weird_pragma_once.h: #include --- """ self.RegisterFileContents({'weird_pragma_once.h': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeAfterWeirdHeaderGuard(self): """Test that we are willing to insert .h's inside a non-standard h-guard.""" infile = """\ // Copyright 2010 #if ! defined (SIMPLE_H_) #define SIMPLE_H_ #include ///- ///+#include #include "used.h" ///+#include "used2.h" #endif """ iwyu_output = """\ simple.h should add these lines: #include #include "used2.h" simple.h should remove these lines: - #include // lines 6-6 The full include-list for simple.h: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'simple.h': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeAfterHeaderGuardLikeIfdef(self): """Test that we are willing to insert .h's inside a h-guard-*like* line.""" infile = """\ // Copyright 2010 #ifdef __linux // serves the same role as a header guard #include ///- ///+#include #include "used.h" ///+#include "used2.h" #endif // Comments are allowed after the header guard. """ iwyu_output = """\ os_header_guard.h should add these lines: #include #include "used2.h" os_header_guard.h should remove these lines: - #include // lines 5-5 The full include-list for os_header_guard.h: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'os_header_guard.h': infile}) self.ProcessAndTest(iwyu_output) def testAddIncludeAfterHeaderGuardButBeforeComments(self): """Test that we introduce new #includes right after a header guard.""" infile = """\ // Copyright 2010 #ifndef SIMPLE_WITH_COMMENT_H_ #define SIMPLE_WITH_COMMENT_H_ ///+#include ///+#include "used.h" ///+ // This is a comment void ForThisFunction(); #endif """ iwyu_output = """\ simple_with_comment.h should add these lines: #include #include "used.h" simple_with_comment.h should remove these lines: The full include-list for simple_with_comment.h: #include #include "used.h" --- """ self.RegisterFileContents({'simple_with_comment.h': infile}) self.ProcessAndTest(iwyu_output) def testIdentifyingHeaderGuardLines(self): """Test that not all #defines look like header guards.""" infile = """\ // Copyright 2010 #ifndef IDENTIFYING_HEADER_GUARD_LINES_H_ #define IDENTIFYING_HEADER_GUARD_LINES_H_ namespace foo { ///+namespace bar { ///+class Baz; ///+} // namespace bar ///+ // The namespace decl should come before this #define, not after. // It will, unless we wrongly say the #define is a header-guard define. #define NOT_A_HEADER_GUARD_LINE 1 } #endif """ iwyu_output = """\ identifying_header_guard_lines.h should add these lines: namespace foo { namespace bar { class Baz; } } identifying_header_guard_lines.h should remove these lines: The full include-list for identifying_header_guard_lines.h: namespace foo { namespace bar { class Baz; } } --- """ self.RegisterFileContents({'identifying_header_guard_lines.h': infile}) self.ProcessAndTest(iwyu_output) def testIncludeOfCcFile(self): """Test that iwyu leaves .cc #includes alone.""" infile = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" int kFoo = 5; #include "not_mentioned.cc" int main() { return 0; } """ iwyu_output = """\ cc_include should add these lines: #include #include "used2.h" cc_include should remove these lines: - #include // lines 3-3 The full include-list for cc_include: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'cc_include': infile}) self.ProcessAndTest(iwyu_output) def testCommentsBeforeIncludeLines(self): infile = """\ // Copyright 2010 #include ///- ///+// This is the first include. ///+// Or it will be, after we reorder. ///+#include // This is the second include. // Or *it* will be, after we reorder. #include "used.h" // This is the first include. ///- // Or it will be, after we reorder. ///- #include ///- int main() { return 0; } """ iwyu_output = """\ comments_with_includes should add these lines: comments_with_includes should remove these lines: - #include // lines 3-3 The full include-list for comments_with_includes: #include #include "used.h" --- """ self.RegisterFileContents({'comments_with_includes': infile}) self.ProcessAndTest(iwyu_output) def testRemoveDuplicateIncludes(self): """Tests we uniquify if an #include is in there twice.""" infile = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" #include "used2.h" #include "used.h" // same line even though it has a comment ///- // Even though these two comment-lines are the same, they won't get de-duped. // Even though these two comment-lines are the same, they won't get de-duped. #ifdef _WINDOWS // But keep this one because it's in an #ifdef. #include "used.h" #endif int main() { return 0; } """ iwyu_output = """\ remove_duplicate_includes should add these lines: #include remove_duplicate_includes should remove these lines: - #include // lines 3-3 The full include-list for remove_duplicate_includes: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'remove_duplicate_includes': infile}) self.ProcessAndTest(iwyu_output) def testRemoveDuplicateForwardDeclarations(self): """Tests we uniquify if an #include is in there twice.""" infile = """\ #include ///- class A; template // Comment in the middle not a problem class B; class A; ///- template ///- class B; ///- template class B; ///- int main() { return 0; } """ iwyu_output = """\ remove_duplicate_forward_declarations should add these lines: remove_duplicate_forward_declarations should remove these lines: - #include // lines 1-1 The full include-list for remove_duplicate_forward_declarations: class A; // lines 2-2 template class B; // lines 3-4 class A; // lines 5-5 template class B; // lines 6-7 template class B; // lines 8-8 --- """ self.RegisterFileContents({'remove_duplicate_forward_declarations': infile}) self.ProcessAndTest(iwyu_output) def testDontRemoveTemplateLines(self): """Tests we don't accidentally think repeated template lines are dupes.""" infile = """\ #include ///- template class A; template class B; void f(A&, B&); """ iwyu_output = """\ dont_remove_template_lines should add these lines: dont_remove_template_lines should remove these lines: - #include // lines 1-1 The full include-list for dont_remove_template_lines: template class A; // lines 2-3 template class B; // lines 4-5 --- """ self.RegisterFileContents({'dont_remove_template_lines': infile}) self.ProcessAndTest(iwyu_output) def testDontRemoveSimilarNestedDeclarations(self): """Tests we don't accidentally think repeated nested forward declarations are dupes.""" infile = """\ #include ///- class A { class Inner; }; class B { class Inner; }; """ iwyu_output = """\ dont_remove_similar_nested should add these lines: dont_remove_similar_nested should remove these lines: - #include // lines 1-1 The full include-list for dont_remove_similar_nested: class A::Inner; // lines 4-4 class B::Inner; // lines 8-8 --- """ self.RegisterFileContents({'dont_remove_similar_nested': infile}) self.ProcessAndTest(iwyu_output) def testNestedNamespaces(self): infile = """\ // Copyright 2010 ///+#include ///+ namespace X { class OneA ///+ namespace Y { ///+class TwoA; class TwoB; class TwoA; ///- }} class Toplevel; ///- namespace A { ///- namespace B { namespace C { ///- class Delete1; ///- }}} ///- ///- namespace A { namespace B { class Delete2; } } ///- ///- namespace A { ///- namespace B { ///- class Delete3; ///- } ///- } // namespace A ///- int main() { return 0; } """ iwyu_output = """\ many_namespaces should add these lines: #include many_namespaces should remove these lines: - class Delete1; // lines 13-13 - class Delete2; // lines 16-16 - class Delete3; // lines 20-20 The full include-list for many_namespaces: #include class Toplevel; // lines 9-9 class TwoA; // lines 7-7 class TwoB; // lines 6-6 class OneA; // lines 4-4 --- """ self.RegisterFileContents({'many_namespaces': infile}) self.ProcessAndTest(iwyu_output) def testDoNotInsertIncludeIntoAClass(self): infile = """\ // Copyright 2010 ///+#include ///+ class Foo { }; class Bar { class FwdDecl; FwdDecl* f; } """ iwyu_output = """\ include_not_in_class should add these lines: #include include_not_in_class should remove these lines: The full include-list for include_not_in_class: #include class FwdDecl; // lines 7-7 --- """ self.RegisterFileContents({'include_not_in_class': infile}) self.ProcessAndTest(iwyu_output) def testIdenticalForwardDeclaredNamesInDifferentNamespaces(self): infile = """\ // Copyright 2010 ///+namespace ns1 { ///+class ForwardDeclared; ///+} // namespace ns1 ///+namespace ns2 { ///+class ForwardDeclared; ///+} // namespace ns2 """ iwyu_output = """\ identical_names should add these lines: namespace ns1 { class ForwardDeclared; } namespace ns2 { class ForwardDeclared; } identical_names should remove these lines: The full include-list for identical_names: namespace ns1 { class ForwardDeclared; } namespace ns2 { class ForwardDeclared; } --- """ self.RegisterFileContents({'identical_names': infile}) self.ProcessAndTest(iwyu_output) def testIterativeNamespaceDelete(self): """Tests deleting a namespace with an emptied #ifdef inside it.""" infile = """\ // Copyright 2010 ///- namespace foo { ///- #ifdef FWD_DECL ///- class Bar; ///- #endif ///- } ///- int main() { return 0; } """ iwyu_output = """\ iterative_namespace should add these lines: iterative_namespace should remove these lines: - class Bar; // lines 5-5 The full include-list for iterative_namespace: --- """ self.RegisterFileContents({'iterative_namespace': infile}) self.ProcessAndTest(iwyu_output) def testIterativeAllmanNamespaceDelete(self): """Tests deleting an Allman namespace with an emptied #ifdef inside it.""" infile = """\ // Copyright 2010 ///- namespace foo ///- { ///- #ifdef FWD_DECL ///- class Bar; ///- #endif ///- } ///- int main() { return 0; } """ iwyu_output = """\ iterative_namespace should add these lines: iterative_namespace should remove these lines: - class Bar; // lines 6-6 The full include-list for iterative_namespace: --- """ self.RegisterFileContents({'iterative_namespace': infile}) self.ProcessAndTest(iwyu_output) def testIterativeMixedNamespaceDelete(self): """Tests deleting a namespace with mixed braces with an emptied #ifdef inside it.""" infile = """\ // Copyright 2010 ///- namespace foo { namespace baz ///- { ///- #ifdef FWD_DECL ///- class Bar; ///- #endif ///- } ///- } ///- int main() { return 0; } """ iwyu_output = """\ iterative_namespace should add these lines: iterative_namespace should remove these lines: - class Bar; // lines 6-6 The full include-list for iterative_namespace: --- """ self.RegisterFileContents({'iterative_namespace': infile}) self.ProcessAndTest(iwyu_output) def testIterativeIfdefDelete(self): """Tests deleting an ifdef with an emptied namespace inside it.""" infile = """\ // Copyright 2010 ///- #ifdef FWD_DECL ///- namespace foo { ///- class Bar; ///- } ///- #endif ///- int main() { return 0; } """ iwyu_output = """\ iterative_ifdef should add these lines: iterative_ifdef should remove these lines: - class Bar; // lines 5-5 The full include-list for iterative_ifdef: --- """ self.RegisterFileContents({'iterative_ifdef': infile}) self.ProcessAndTest(iwyu_output) def testOutOfRangeLineNumber(self): """Test we skip editing completely if iwyu has a really big line number.""" # fix_includes skips the file-editing if it detects a problem, as # in this test case. The way that skipping is evidenced in the # test, is the output is empty. infile = """\ // Copyright 2010 ///- ///- #include ///- #include "used.h" ///- ///- int main() { return 0; } ///- """ iwyu_output = """\ out_of_range should add these lines: #include #include "used2.h" out_of_range should remove these lines: - #include // lines 3-3 - #include // lines 3000-3000 The full include-list for out_of_range: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'out_of_range': infile}) self.ProcessAndTest(iwyu_output) def testDeleteExtraneousBlankLines(self): """Test we delete blank lines around deleted spans correctly.""" infile = """\ // Copyright 2010 class Foo { }; ///- class Bar; ///- class Baz { }; class Bang; ///- ///- class Qux { }; int main() { return 0; } """ iwyu_output = """\ extraneous_blank_lines should add these lines: extraneous_blank_lines should remove these lines: - class Bar; // lines 5-5 - class Bang; // lines 11-11 The full include-list for extraneous_blank_lines: --- """ self.RegisterFileContents({'extraneous_blank_lines': infile}) self.ProcessAndTest(iwyu_output) def testKeepNolintComment(self): """Test we keep a nolint comment.""" infile = """\ // Copyright 2010 #include "bar.h" // NOLINT(iwyu) #include "baz.h" // NOLINT(iwyu): blah blah int main() { return 0; } """ iwyu_output = """\ keep_nolint should add these lines: keep_nolint should remove these lines: The full include-list for keep_nolint: #include "bar.h" // lines 3-3 #include "baz.h" // lines 4-4 --- """ self.RegisterFileContents({'keep_nolint': infile}) # No files are written, because there are no changes. self.ProcessAndTest(iwyu_output, unedited_files=['keep_nolint']) def testKeepNolintCommentInNocommentMode(self): """Test we keep a nolint comment even with --nocomments.""" self.flags.comments = False self.testKeepNolintComment() # Test the IWYUOutputParser method _MatchSectionHeading. def testIWYUOutputParserMatchSectionHeadingSuccess(self): parser = fix_includes.IWYUOutputParser() self.assertEqual(None, parser.current_section) self.assertEqual('', parser.filename) self.assertTrue(parser._ProcessOneLine('')) self.assertEqual(None, parser.current_section) self.assertEqual('', parser.filename) self.assertTrue(parser._ProcessOneLine( 'myfile.cc should add these lines:')) self.assertEqual(parser._ADD_SECTION_RE, parser.current_section) self.assertEqual('add', parser._RE_TO_NAME[parser.current_section]) self.assertEqual('myfile.cc', parser.filename) self.assertTrue(parser._ProcessOneLine( 'myfile.cc should remove these lines:')) self.assertEqual(parser._REMOVE_SECTION_RE, parser.current_section) self.assertEqual('remove', parser._RE_TO_NAME[parser.current_section]) self.assertEqual('myfile.cc', parser.filename) self.assertTrue(parser._ProcessOneLine( 'The full include-list for myfile.cc:')) self.assertEqual(parser._TOTAL_SECTION_RE, parser.current_section) self.assertEqual('total', parser._RE_TO_NAME[parser.current_section]) self.assertEqual('myfile.cc', parser.filename) self.assertTrue(not parser._ProcessOneLine('---')) self.assertEqual(parser._SECTION_END_RE, parser.current_section) self.assertEqual('end', parser._RE_TO_NAME[parser.current_section]) self.assertEqual('myfile.cc', parser.filename) def testIWYUOutputParserMatchSectionHeadingWindowsPaths(self): # Windows path names can contain the ':' character, so make sure that parses # correctly. IWYU uses POSIX-style forward slashes consistently, so follow # suit here. parser = fix_includes.IWYUOutputParser() self.assertTrue(parser._ProcessOneLine( 'C:/src/myfile.cc should add these lines:')) self.assertEqual(parser._ADD_SECTION_RE, parser.current_section) self.assertEqual('add', parser._RE_TO_NAME[parser.current_section]) self.assertEqual('C:/src/myfile.cc', parser.filename) self.assertTrue(parser._ProcessOneLine( 'C:/src/myfile.cc should remove these lines:')) self.assertEqual(parser._REMOVE_SECTION_RE, parser.current_section) self.assertEqual('remove', parser._RE_TO_NAME[parser.current_section]) self.assertEqual('C:/src/myfile.cc', parser.filename) self.assertTrue(parser._ProcessOneLine( 'The full include-list for C:/src/myfile.cc:')) self.assertEqual(parser._TOTAL_SECTION_RE, parser.current_section) self.assertEqual('total', parser._RE_TO_NAME[parser.current_section]) self.assertEqual('C:/src/myfile.cc', parser.filename) def testIWYUOutputParserProcessOneLineProcessNoEditsHeader(self): parser = fix_includes.IWYUOutputParser() line = '(myfile.cc has correct #includes/fwd-decls)' self.assertTrue(not parser._ProcessOneLine(line)) self.assertEqual(parser._NO_EDITS_RE, parser.current_section) self.assertEqual('no_edits', parser._RE_TO_NAME[parser.current_section]) self.assertEqual('myfile.cc', parser.filename) def testIWYUOutputParserProcessOneLineAddNotSeenFirst(self): parser = fix_includes.IWYUOutputParser() self.assertRaises(fix_includes.FixIncludesError, parser._ProcessOneLine, 'myfile.cc should remove these lines:') def testIWYUOutputParserProcessOneLineOutOfOrder(self): parser = fix_includes.IWYUOutputParser() self.assertTrue(parser._ProcessOneLine( 'myfile.cc should add these lines:')) self.assertRaises(fix_includes.FixIncludesError, parser._ProcessOneLine, 'The full include-list for myfile.cc:') def testIWYUOutputParserProcessOneLineIncorrectFilename(self): parser = fix_includes.IWYUOutputParser() self.assertTrue(parser._ProcessOneLine( 'myfile.cc should add these lines:')) self.assertRaises(fix_includes.FixIncludesError, parser._ProcessOneLine, 'not_myfile.cc should remove these lines:') def testIWYUOutputParserProcessOneLineNoMatcher(self): parser = fix_includes.IWYUOutputParser() # We successfully process this not-in-any-section line, but update no data. self.assertTrue(parser._ProcessOneLine('#include ')) self.assertEqual(None, parser.current_section) self.assertEqual('', parser.filename) def testIWYUOutputParserSuccess(self): """Tests the IWYUOutputParser method ParseOneRecord.""" iwyu_output = """\ simple should add these lines: #include #include "used2.h" namespace ns {class ForwardDeclared;} simple should remove these lines: - #include // lines 3-3 The full include-list for simple: #include #include "used.h" #include "used2.h" namespace ns {class ForwardDeclared;} --- """ parser = fix_includes.IWYUOutputParser() record = parser.ParseOneRecord(iwyu_output.splitlines(), self.flags) self.assertEqual('simple', record.filename) self.assertSetEqual(set([3]), record.lines_to_delete) self.assertSetEqual(set(('#include ', '#include "used2.h"', 'namespace ns {class ForwardDeclared;}')), record.includes_and_forward_declares_to_add) def testIWYUOutputParserRemoveLineNoComment(self): iwyu_output = """\ no_comment should add these lines: no_comment should remove these lines: - #include The full include-list for no_key: --- """ parser = fix_includes.IWYUOutputParser() self.assertRaises(fix_includes.FixIncludesError, parser.ParseOneRecord, iwyu_output.splitlines(), self.flags) def testFileSpecifiedOnCommandline(self): """Test we limit editing to files specified on the commandline.""" changed_infile = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" int main() { return 0; } """ unchanged_infile = """\ // Copyright 2010 #include #include "used.h" int main() { return 0; } """ iwyu_output = """\ changed should add these lines: #include #include "used2.h" changed should remove these lines: - #include // lines 3-3 The full include-list for changed: #include #include "used.h" #include "used2.h" --- """ # Have the exact same iwyu output for 'unchanged' as for 'changed'. iwyu_output += iwyu_output.replace('changed', 'unchanged') self.RegisterFileContents({'changed': changed_infile, 'unchanged': unchanged_infile}) # unchanged should not be edited, since it is not listed on the 'cmdline'. self.ProcessAndTest(iwyu_output, cmdline_files=['changed'], unedited_files=['unchanged']) def testIgnoreRe(self): """Test the behavior of the --ignore_re flag.""" changed_infile = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" int main() { return 0; } """ unchanged_infile = """\ // Copyright 2010 #include #include "used.h" int main() { return 0; } """ iwyu_output = """\ changed should add these lines: #include #include "used2.h" changed should remove these lines: - #include // lines 3-3 The full include-list for changed: #include #include "used.h" #include "used2.h" --- """ # Have the exact same iwyu output for 'unchanged' as for 'changed'. iwyu_output += iwyu_output.replace('changed', 'unchanged') self.RegisterFileContents({'changed': changed_infile, 'unchanged': unchanged_infile}) # unchanged should not be edited, since it matches ignore_re. self.flags.ignore_re = 'nch' self.ProcessAndTest(iwyu_output, unedited_files=['unchanged']) def testOnlyRe(self): """Test the behavior of the --only_re flag.""" changed_infile = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" int main() { return 0; } """ unchanged_infile = """\ // Copyright 2010 #include #include "used.h" int main() { return 0; } """ iwyu_output = """\ output should add these lines: #include #include "used2.h" output should remove these lines: - #include // lines 3-3 The full include-list for output: #include #include "used.h" #include "used2.h" --- """ # Have the exact same iwyu output for 'alice.cpp' as for 'bob.cpp'. iwyu_output = (iwyu_output.replace('output', 'alice.cpp') + iwyu_output.replace('output', 'bob.cpp')) self.RegisterFileContents({'alice.cpp': changed_infile, 'bob.cpp': unchanged_infile}) # only alice.cpp should be edited, since it matches only_re. self.flags.only_re = 'lice' self.ProcessAndTest(iwyu_output, unedited_files=['bob.cpp']) def testIgnoreAndOnlyRe(self): """Test the behavior of both --ignore_re and --only_re flags.""" changed_infile = """\ // Copyright 2010 #include ///- ///+#include #include "used.h" ///+#include "used2.h" int main() { return 0; } """ unchanged_infile = """\ // Copyright 2010 #include #include "used.h" int main() { return 0; } """ iwyu_output = """\ output should add these lines: #include #include "used2.h" output should remove these lines: - #include // lines 3-3 The full include-list for output: #include #include "used.h" #include "used2.h" --- """ # Have the exact same iwyu output for 'alice.cpp' as for 'bob.cpp' and 'charlie.cpp' iwyu_output = (iwyu_output.replace('output', 'alice.cpp') + iwyu_output.replace('output', 'bob.cpp') + iwyu_output.replace('output', 'charlie.cpp')) self.RegisterFileContents({'alice.cpp': changed_infile, 'bob.cpp': unchanged_infile, 'charlie.cpp': changed_infile}) # only alice.cpp should be edited, since it matches only_re and not ignore_re self.flags.only_re = 'li' self.flags.ignore_re = 'char' self.ProcessAndTest(iwyu_output, unedited_files=['bob.cpp', 'charlie.cpp']) def testSortIncludes(self): """Test sorting includes only -- like running fix_includes.py -s.""" infile = """\ // Copyright 2010 #include // This file is not used. #include // This file is not used either. // It's not used. // Not used at all. #include #include "notused3.h" // This comment should stay, it's not before an #include. const int kInt = 5; // This file is used. // It's definitedly used. #include "used.h" #include const int kInt2 = 6; #include "foo.cc" // This comment should stay, it's not before an #include. int main() { return 0; } """ expected_output = """\ // Copyright 2010 // This file is not used. #include // This file is not used either. // It's not used. // Not used at all. #include #include #include "notused3.h" // This comment should stay, it's not before an #include. const int kInt = 5; #include // This file is used. // It's definitedly used. #include "used.h" const int kInt2 = 6; #include "foo.cc" // This comment should stay, it's not before an #include. int main() { return 0; } """ self.RegisterFileContents({'sort': infile}) num_files_modified = fix_includes.SortIncludesInFiles(['sort'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(1, num_files_modified) def testSortingMultipleFiles(self): """Tests passing more than one argument to SortIncludesInFiles().""" infile1 = """\ #include #include #include """ infile2 = """\ #include "z.h" #include "y.h" #include "x.y" """ expected_output = """\ #include #include #include #include "x.y" #include "y.h" #include "z.h" """ self.RegisterFileContents({'f1': infile1, 'f2': infile2}) num_files_modified = fix_includes.SortIncludesInFiles(['f1', 'f2'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(2, num_files_modified) def testSortingIncludesAlreadySorted(self): """Tests sorting includes only, when includes are already sorted.""" infile = """\ // Copyright 2010 #include #include namespace Foo; // fwd-decls are out of order, but sorter ignores them namespace Bar; int main() { return 0; } """ self.RegisterFileContents({'sort_nosorting.h': infile}) num_files_modified = fix_includes.SortIncludesInFiles(['sort_nosorting.h'], self.flags) self.assertListEqual([], self.actual_after_contents) self.assertEqual(0, num_files_modified) def testBarrierIncludes(self): """Tests that we correctly sort 'around' _BARRIER_INCLUDES.""" infile = """\ // Copyright 2010 #include #include ///- #include ///- #include ///+#include #include "user/include.h" ///+#include "user/new_include.h" #include #include #include #include ///+#include #include int main() { return 0; } """ iwyu_output = """\ barrier_includes.h should add these lines: #include "user/new_include.h" #include barrier_includes.h should remove these lines: - #include // lines 5-5 The full include-list for barrier_includes.h: #include "user/include.h" #include "user/new_include.h" #include #include #include #include #include #include #include #include #include --- """ self.RegisterFileContents({'barrier_includes.h': infile}) self.ProcessAndTest(iwyu_output) def testSortingMainCUIncludeInSameDirectory(self): """Check that we identify when first .h file is a main-cu #include.""" infile = """\ #include #include "me/subdir0/foo.h" #include "other/baz.h" """ expected_output = """\ #include "me/subdir0/foo.h" #include #include "other/baz.h" """ self.RegisterFileContents({'me/subdir0/foo.cc': infile}) num_files_modified = fix_includes.SortIncludesInFiles( ['me/subdir0/foo.cc'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(1, num_files_modified) def testSortingMainCUIncludeViaPragma(self): """Check that we treat (potentially multiple) associated headers as main-cu #includes.""" infile = """\ #include #include "other/dir/bar.h" // IWYU pragma: associated #include "other/baz.h" // IWYU pragma: associated """ expected_output = """\ #include "other/baz.h" // IWYU pragma: associated #include "other/dir/bar.h" // IWYU pragma: associated #include """ self.RegisterFileContents({'me/subdir0/foo.cc': infile}) num_files_modified = fix_includes.SortIncludesInFiles( ['me/subdir0/foo.cc'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(1, num_files_modified) def testSortingMainCUIncludeWithUpperCaseH(self): """Check that we identify when first .H file is a main-cu #include.""" infile = """\ #include #include "foo.H" """ expected_output = """\ #include "foo.H" #include """ self.RegisterFileContents({'foo.cc': infile}) num_files_modified = fix_includes.SortIncludesInFiles( ['foo.cc'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(1, num_files_modified) def testSortingMainCUIncludeWithHpp(self): """Check that we identify when first .hpp file is a main-cu #include.""" infile = """\ #include #include "foo.hpp" """ expected_output = """\ #include "foo.hpp" #include """ self.RegisterFileContents({'foo.cc': infile}) num_files_modified = fix_includes.SortIncludesInFiles( ['foo.cc'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(1, num_files_modified) def testSortingMainCUIncludeWithMixedCaseInl(self): """Check that we identify when first -inl.hpp file with mixed case is a main-cu #include.""" infile = """\ #include #include "foo-InL.h" """ expected_output = """\ #include "foo-InL.h" #include """ self.RegisterFileContents({'foo.cc': infile}) num_files_modified = fix_includes.SortIncludesInFiles( ['foo.cc'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(1, num_files_modified) def testSortingMainCUIncludeInSameDirectoryWithInl(self): """Check that we identify when first -inl.h file is a main-cu #include.""" infile = """\ #include #include "me/subdir0/foo-inl.h" #include "other/baz.h" """ expected_output = """\ #include "me/subdir0/foo-inl.h" #include #include "other/baz.h" """ self.RegisterFileContents({'me/subdir0/foo.cc': infile}) num_files_modified = fix_includes.SortIncludesInFiles( ['me/subdir0/foo.cc'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(1, num_files_modified) def testSortingMainCUIncludeInDifferentDirectory(self): """Check that we identify when first .h file is a main-cu #include.""" infile = """\ #include "me/subdir0/foo.h" #include #include "other/baz.h" """ self.RegisterFileContents({'me/other_subdir/foo.cc': infile}) num_files_modified = fix_includes.SortIncludesInFiles( ['me/other_subdir/foo.cc'], self.flags) self.assertListEqual([], self.actual_after_contents) self.assertEqual(0, num_files_modified) def testSortingMainCUIncludeInDifferentDirectoryWhenNotFirst(self): """Check that we don't let second .h be a main-cu #include.""" infile = """\ #include #include "me/subdir0/foo.h" #include "other/baz.h" """ self.RegisterFileContents({'me/other_subdir/foo.cc': infile}) num_files_modified = fix_includes.SortIncludesInFiles( ['me/other_subdir/foo.cc'], self.flags) self.assertListEqual([], self.actual_after_contents) self.assertEqual(0, num_files_modified) def testSortingProjectIncludesAuto(self): """Check that project includes can be sorted separately.""" infile = """\ #include "me/subdir0/foo.h" #include #include "me/subdir2/bar.h" #include "me/subdir1/bar.h" #include "me/subdir0/bar.h" #include "other/baz.h" """ expected_output = """\ #include "me/subdir0/foo.h" #include #include "other/baz.h" #include "me/subdir0/bar.h" #include "me/subdir1/bar.h" #include "me/subdir2/bar.h" """ self.RegisterFileContents({'me/subdir0/foo.cc': infile}) self.flags.separate_project_includes = '' num_files_modified = fix_includes.SortIncludesInFiles(['me/subdir0/foo.cc'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(1, num_files_modified) def testSortingProjectIncludesUserSpecified(self): """Test user-specified project directory name.""" infile = """\ #include "me/subdir0/foo.h" #include #include "me/subdir2/bar.h" #include "me/subdir1/bar.h" #include "me/subdir0/bar.h" #include "other/baz.h" """ expected_output = """\ #include "me/subdir0/foo.h" #include #include "me/subdir1/bar.h" #include "me/subdir2/bar.h" #include "other/baz.h" #include "me/subdir0/bar.h" """ self.RegisterFileContents({'me/subdir0/foo.cc': infile}) self.flags.separate_project_includes = 'me/subdir0' num_files_modified = fix_includes.SortIncludesInFiles(['me/subdir0/foo.cc'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(1, num_files_modified) def testAddingNewIncludesAfterRemovingOldOnes(self): infile = """\ // Copyright 2008 Google Inc. All Rights Reserved. // Author: zhifengc@google.com (Zhifeng Chen) #ifndef STRUCTUREDSEARCH_COMMON_INTERNAL_DFS_H_ #define STRUCTUREDSEARCH_COMMON_INTERNAL_DFS_H_ #include "util/task/status.h" ///- #include "strings/stringpiece.h" ///- ///+#include // for string ///+#include "base/macros.h" // for DISALLOW_COPY_AND_ASSIGN ///+#include "base/scoped_ptr.h" // for scoped_ptr class Query; ///+namespace util { ///+class Status; ///+} // namespace util namespace structuredsearch { ///+class FieldSpecification; class FieldTokenizer; class FieldSpecification; ///- class TokenizationSpec; class QueryXlator { ... }; #endif // #define STRUCTUREDSEARCH_COMMON_INTERNAL_DFS_H_ """ iwyu_output = """\ structuredsearch/common/internal/query_field_xlate.h should add these lines: #include // for string #include "base/macros.h" // for DISALLOW_COPY_AND_ASSIGN #include "base/scoped_ptr.h" // for scoped_ptr namespace util { class Status; } structuredsearch/common/internal/query_field_xlate.h should remove these lines: - #include "strings/stringpiece.h" // lines 8-8 - #include "util/task/status.h" // lines 7-7 The full include-list for structuredsearch/common/internal/query_field_xlate.h: #include // for string #include "base/macros.h" // for DISALLOW_COPY_AND_ASSIGN #include "base/scoped_ptr.h" // for scoped_ptr class Query; // lines 10-10 namespace structuredsearch { class FieldSpecification; } // lines 15-15 namespace structuredsearch { class FieldTokenizer; } // lines 14-14 namespace structuredsearch { class TokenizationSpec; } // lines 16-16 namespace util { class Status; } --- """ self.RegisterFileContents( {'structuredsearch/common/internal/query_field_xlate.h': infile}) self.ProcessAndTest(iwyu_output) def testDryRun(self): """Tests that --dry_run mode does not modify files.""" self.flags.dry_run = True infile = """\ // Copyright 2010 #include #include #include "used.h" #include "used2.h" int main() { return 0; } """ iwyu_output = """\ dry_run should add these lines: #include #include "used2.h" dry_run should remove these lines: - #include // lines 3-3 The full include-list for dry_run: #include #include "used.h" #include "used2.h" --- """ self.RegisterFileContents({'dry_run': infile}) num_modified_files = fix_includes.ProcessIWYUOutput( StringIO(iwyu_output), ['dry_run'], self.flags, None) self.assertListEqual([], self.actual_after_contents) self.assertEqual(1, num_modified_files) def testAddForwardDeclareAndKeepIwyuNamespaceFormat(self): """Tests that --keep_iwyu_namespace_format writes namespace lines using the IWYU one-line format. Input code similar to case testAddForwardDeclareInNamespace.""" self.flags.keep_iwyu_namespace_format = True infile = """\ // Copyright 2010 #include "foo.h" ///+namespace ns { class Foo; } ///+namespace ns { namespace ns2 { namespace ns3 { class Bar; } } } ///+namespace ns { namespace ns2 { namespace ns3 { template class Bang; } } } ///+namespace ns { namespace ns4 { class Baz; } } ///+ int main() { return 0; } """ iwyu_output = """\ add_fwd_declare_keep_iwyu_namespace should add these lines: namespace ns { class Foo; } namespace ns { namespace ns2 { namespace ns3 { class Bar; } } } namespace ns { namespace ns2 { namespace ns3 { template class Bang; } } } namespace ns { namespace ns4 { class Baz; } } add_fwd_declare_keep_iwyu_namespace should remove these lines: The full include-list for add_fwd_declare_keep_iwyu_namespace: #include "foo.h" // lines 3-3 namespace ns { class Foo; } namespace ns { namespace ns2 { namespace ns3 { class Bar; } } } namespace ns { namespace ns2 { namespace ns3 { template class Bang; } } } namespace ns { namespace ns4 { class Baz; } } --- """ self.RegisterFileContents({'add_fwd_declare_keep_iwyu_namespace': infile}) self.ProcessAndTest(iwyu_output, expected_num_modified_files=1) def testAddNestedForwardDeclaresWithKeepIwyuNamespaceFormat(self): """Tests that --keep_iwyu_namespace_format writes namespace lines using the IWYU one-line format. Input code similar to case testAddForwardDeclareInsideNamespaceWithoutForwardDeclaresAlready.""" self.flags.keep_iwyu_namespace_format = True infile = """\ // Copyright 2010 #include "foo.h" class Bar; ///+class Foo; ///+namespace ns1 { class NsFoo; } ///+namespace ns1 { namespace ns2 { namespace ns3 { class NsBaz; } } } ///+namespace ns1 { namespace ns2 { namespace ns3 { template class NsBang; } } } template class Baz; namespace ns { ///- ///+class NsFoo; ///+namespace ns2 { namespace ns3 { class NsBaz; } } ///+namespace ns2 { namespace ns3 { template class NsBang; } } ///+ class NsBar; namespace ns2 { // we sure do love nesting our namespaces! int MyFunction() { } } } int main() { return 0; } """ iwyu_output = """\ add_fwd_decl_with_keep_iwyu_format should add these lines: class Foo; namespace ns { class NsFoo; } namespace ns { namespace ns2 { namespace ns3 { class NsBaz; } } } namespace ns { namespace ns2 { namespace ns3 { template class NsBang; } } } namespace ns1 { class NsFoo; } namespace ns1 { namespace ns2 { namespace ns3 { class NsBaz; } } } namespace ns1 { namespace ns2 { namespace ns3 { template class NsBang; } } } add_fwd_decl_with_keep_iwyu_format should remove these lines: The full include-list for add_fwd_decl_with_keep_iwyu_format: #include "foo.h" // lines 3-3 class Bar; // lines 5-5 class Foo; namespace ns { class NsFoo; } namespace ns { namespace ns2 { class NsBar; } } namespace ns { namespace ns2 { namespace ns3 { class NsBaz; } } } namespace ns { namespace ns2 { namespace ns3 { template class NsBang; } } } namespace ns1 { class NsFoo; } namespace ns1 { namespace ns2 { namespace ns3 { class NsBaz; } } } namespace ns1 { namespace ns2 { namespace ns3 { template class NsBang; } } } template class Baz; // lines 6-6 --- """ self.RegisterFileContents({'add_fwd_decl_with_keep_iwyu_format': infile}) self.ProcessAndTest(iwyu_output) def testAddForwardDeclareInNamespaceWithKeepIwyuNamespaceFormat(self): """Tests that --keep_iwyu_namespace_format writes namespace lines using the IWYU one-line format. Input code similar to case testAddForwardDeclareInNamespace.""" self.flags.keep_iwyu_namespace_format = True infile = """\ // Copyright 2010 #include "foo.h" ///+namespace ns { class Foo; } ///+namespace ns { namespace ns2 { namespace ns3 { class Bar; } } } ///+namespace ns { namespace ns2 { namespace ns3 { template class Bang; } } } ///+namespace ns { namespace ns4 { class Baz; } } ///+ int main() { return 0; } """ iwyu_output = """\ add_fwd_declare_keep_iwyu_namespace should add these lines: namespace ns { class Foo; } namespace ns { namespace ns2 { namespace ns3 { class Bar; } } } namespace ns { namespace ns2 { namespace ns3 { template class Bang; } } } namespace ns { namespace ns4 { class Baz; } } add_fwd_declare_keep_iwyu_namespace should remove these lines: The full include-list for add_fwd_declare_keep_iwyu_namespace: #include "foo.h" // lines 3-3 namespace ns { class Foo; } namespace ns { namespace ns2 { namespace ns3 { class Bar; } } } namespace ns { namespace ns2 { namespace ns3 { template class Bang; } } } namespace ns { namespace ns4 { class Baz; } } --- """ self.RegisterFileContents({'add_fwd_declare_keep_iwyu_namespace': infile}) self.ProcessAndTest(iwyu_output, expected_num_modified_files=1) def testBasedir(self): self.flags.basedir = "/project/build/" iwyu_output = """\ ../src/source.cc should add these lines: ../src/source.cc should remove these lines: - #include // lines 1-1 The full include-list for ../src/source.cc: #include --- """ infile = """\ #include ///- #include int main() { return 0; } """ self.RegisterFileContents({'/project/src/source.cc': infile}) self.ProcessAndTest(iwyu_output, expected_num_modified_files=1) def testBasedirWithFilesToProcess(self): self.flags.basedir = "/project/build/" iwyu_output = """\ ../src/changed.cc should add these lines: ../src/changed.cc should remove these lines: - #include // lines 1-1 The full include-list for ../src/changed.cc: #include --- """ changed_file = """\ #include ///- #include int main() { return 0; } """ unchanged_file = """\ #include #include int main() { return 0; } """ iwyu_output += iwyu_output.replace('changed.cc', 'unchanged.cc') self.RegisterFileContents({ '/project/src/changed.cc': changed_file, '/project/src/unchanged.cc': unchanged_file }) self.ProcessAndTest(iwyu_output, cmdline_files=['/project/src/changed.cc'], unedited_files=['/project/src/unchanged.cc']) def testBasedirWithRelativeCmdlineFiles(self): self.flags.basedir = "/project/build/" iwyu_output = """\ ../src/changed.cc should add these lines: ../src/changed.cc should remove these lines: - #include // lines 1-1 The full include-list for ../src/changed.cc: #include --- """ changed_file = """\ #include ///- #include int main() { return 0; } """ self.RegisterFileContents({ # File path is normalized to absolute by ProcessIWYUOutput. '/project/src/changed.cc': changed_file, }) self.ProcessAndTest(iwyu_output, cmdline_files=['changed.cc'], cwd='/project/src') def testMain(self): """Make sure calling main doesn't crash. Inspired by a syntax-error bug.""" # Give an empty stdin so we don't actually try to parse anything. old_stdin = sys.stdin try: sys.stdin = StringIO() fix_includes.main(['fix_includes.py']) # argv[0] doesn't really matter finally: sys.stdin = old_stdin def testFilenamesForSortingInMain(self): """Make sure if we use s, we have a filename specified, in main().""" # -s without any files to sort. self.assertRaises(SystemExit, fix_includes.main, ['fix_includes.py', '-s']) def testReorderingInclusions(self): """Show that the --reorder flag causes #includes to be sorted.""" infile = """\ // namespace B namespace B { class BC; } // B // namespace A namespace A { class AC; } // A // b #include "b" // b // c #include // c // a #include // a // a #include "a" // a // asdf #ifdef asdf // x #include // x // endif #endif """ expected_output = """\ // namespace B namespace B { class BC; } // B // namespace A namespace A { class AC; } // A // a #include // a // c #include // c // a #include "a" // a // b #include "b" // b // asdf #ifdef asdf // x #include // x // endif #endif """ self.RegisterFileContents({'inclusions_reordered.cc': infile}) self.flags.reorder = True num_files_modified = fix_includes.SortIncludesInFiles( ['inclusions_reordered.cc'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(1, num_files_modified) def testNoReorderingInclusions(self): """Show that the --noreorder flag causes #includes not to be sorted.""" infile = """\ // namespace B namespace B { class BC; } // B // namespace A namespace A { class AC; } // A // b #include "b" // b // c #include // c // a #include // a // a #include "a" // a // asdf #ifdef asdf // x #include // x // endif #endif """ expected_output = """\ // namespace B namespace B { class BC; } // B // namespace A namespace A { class AC; } // A // c #include // c // a #include // a // b #include "b" // b // a #include "a" // a // asdf #ifdef asdf // x #include // x // endif #endif """ self.RegisterFileContents({'inclusions_not_reordered.cc': infile}) self.flags.reorder = False num_files_modified = fix_includes.SortIncludesInFiles( ['inclusions_not_reordered.cc'], self.flags) self.assertListEqual(expected_output.splitlines(True), self.actual_after_contents) self.assertEqual(1, num_files_modified) class FileInfoTest(unittest.TestCase): """ Unit test for file info detection """ def testEndingsWindows(self): buf = b'first\r\nsecond\r\nthird\r\n' self.assertEqual('\r\n', fix_includes.FileInfo.guess_linesep(buf)) def testEndingsUnix(self): buf = b'first\nsecond\nthird\n' self.assertEqual('\n', fix_includes.FileInfo.guess_linesep(buf)) def testEndingsMixedUnixMajority(self): buf = b'first\nsecond\nsecond-and-a-half\r\nthird\nfourth\r\n' self.assertEqual('\n', fix_includes.FileInfo.guess_linesep(buf)) def testEndingsMixedWindowsMajority(self): buf = b'first\nsecond\r\nsecond-and-a-half\r\nthird\nfourth\r\n' self.assertEqual('\r\n', fix_includes.FileInfo.guess_linesep(buf)) def testEndingsMixedTie(self): buf = b'first\nsecond\nthird\r\nfourth\r\n' self.assertEqual(fix_includes.FileInfo.DEFAULT_LINESEP, fix_includes.FileInfo.guess_linesep(buf)) def testEncodingASCII(self): buf = b'abcdefgh' self.assertEqual('ascii', fix_includes.FileInfo.guess_encoding(buf)) def testEncodingUTF8BOM(self): buf = b'\xef\xbb\xbfSomeASCIIButWithTheBOM' self.assertEqual('utf-8-sig', fix_includes.FileInfo.guess_encoding(buf)) def testEncodingUTF8NoBOM(self): # This is a recurring test input in Swedish, translates to "shrimp sandwich" # and contains all three Swedish exotic characters. buf = b'r\xc3\xa4ksm\xc3\xb6rg\xc3\xa5s' self.assertEqual('utf-8', fix_includes.FileInfo.guess_encoding(buf)) def testEncodingISO8859_1(self): # Yours truly buf = b'Kim Gr\xe4sman' self.assertEqual('windows-1250', fix_includes.FileInfo.guess_encoding(buf)) if __name__ == '__main__': unittest.main()