| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stddef.h> |
| |
| #include "tools/gn/c_include_iterator.h" |
| #include "tools/gn/input_file.h" |
| #include "tools/gn/location.h" |
| #include "util/test/test.h" |
| |
| namespace { |
| |
| bool RangeIs(const LocationRange& range, |
| int line, |
| int begin_char, |
| int end_char) { |
| return range.begin().line_number() == line && |
| range.end().line_number() == line && |
| range.begin().column_number() == begin_char && |
| range.end().column_number() == end_char; |
| } |
| |
| } // namespace |
| |
| TEST(CIncludeIterator, Basic) { |
| std::string buffer; |
| buffer.append("// Some comment\n"); |
| buffer.append("\n"); |
| buffer.append("#include \"foo/bar.h\"\n"); |
| buffer.append("\n"); |
| buffer.append("#include <stdio.h>\n"); |
| buffer.append("\n"); |
| buffer.append(" #include \"foo/baz.h\"\n"); // Leading whitespace |
| buffer.append("#include \"la/deda.h\"\n"); |
| // Line annotated with "// nogncheck" |
| buffer.append("#include \"should_be_skipped.h\" // nogncheck\n"); |
| buffer.append("#import \"weird_mac_import.h\"\n"); |
| buffer.append("\n"); |
| buffer.append("void SomeCode() {\n"); |
| |
| InputFile file(SourceFile("//foo.cc")); |
| file.SetContents(buffer); |
| |
| CIncludeIterator iter(&file); |
| |
| std::string_view contents; |
| LocationRange range; |
| EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range)); |
| EXPECT_EQ("foo/bar.h", contents); |
| EXPECT_TRUE(RangeIs(range, 3, 11, 20)) << range.begin().Describe(true); |
| |
| EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range)); |
| EXPECT_EQ("foo/baz.h", contents); |
| EXPECT_TRUE(RangeIs(range, 7, 12, 21)) << range.begin().Describe(true); |
| |
| EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range)); |
| EXPECT_EQ("la/deda.h", contents); |
| EXPECT_TRUE(RangeIs(range, 8, 11, 20)) << range.begin().Describe(true); |
| |
| // The line annotated with "nogncheck" should be skipped. |
| |
| EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range)); |
| EXPECT_EQ("weird_mac_import.h", contents); |
| EXPECT_TRUE(RangeIs(range, 10, 10, 28)) << range.begin().Describe(true); |
| |
| EXPECT_FALSE(iter.GetNextIncludeString(&contents, &range)); |
| } |
| |
| // Tests that we don't search for includes indefinitely. |
| TEST(CIncludeIterator, GiveUp) { |
| std::string buffer; |
| for (size_t i = 0; i < 1000; i++) |
| buffer.append("x\n"); |
| buffer.append("#include \"foo/bar.h\"\n"); |
| |
| InputFile file(SourceFile("//foo.cc")); |
| file.SetContents(buffer); |
| |
| std::string_view contents; |
| LocationRange range; |
| |
| CIncludeIterator iter(&file); |
| EXPECT_FALSE(iter.GetNextIncludeString(&contents, &range)); |
| EXPECT_TRUE(contents.empty()); |
| } |
| |
| // Don't count blank lines, comments, and preprocessor when giving up. |
| TEST(CIncludeIterator, DontGiveUp) { |
| std::string buffer; |
| for (size_t i = 0; i < 1000; i++) |
| buffer.push_back('\n'); |
| for (size_t i = 0; i < 1000; i++) |
| buffer.append("// comment\n"); |
| for (size_t i = 0; i < 1000; i++) |
| buffer.append("#preproc\n"); |
| buffer.append("#include \"foo/bar.h\"\n"); |
| |
| InputFile file(SourceFile("//foo.cc")); |
| file.SetContents(buffer); |
| |
| std::string_view contents; |
| LocationRange range; |
| |
| CIncludeIterator iter(&file); |
| EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range)); |
| EXPECT_EQ("foo/bar.h", contents); |
| } |
| |
| // Tests that we'll tolerate some small numbers of non-includes interspersed |
| // with real includes. |
| TEST(CIncludeIterator, TolerateNonIncludes) { |
| const size_t kSkip = CIncludeIterator::kMaxNonIncludeLines - 2; |
| const size_t kGroupCount = 100; |
| |
| std::string include("foo/bar.h"); |
| |
| // Allow a series of includes with blanks in between. |
| std::string buffer; |
| for (size_t group = 0; group < kGroupCount; group++) { |
| for (size_t i = 0; i < kSkip; i++) |
| buffer.append("foo\n"); |
| buffer.append("#include \"" + include + "\"\n"); |
| } |
| |
| InputFile file(SourceFile("//foo.cc")); |
| file.SetContents(buffer); |
| |
| std::string_view contents; |
| LocationRange range; |
| |
| CIncludeIterator iter(&file); |
| for (size_t group = 0; group < kGroupCount; group++) { |
| EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range)); |
| EXPECT_EQ(include, std::string(contents)); |
| } |
| EXPECT_FALSE(iter.GetNextIncludeString(&contents, &range)); |
| } |
| |
| // Tests that comments of the form |
| // /* |
| // * |
| // */ |
| // are not counted toward the non-include line count. |
| TEST(CIncludeIterator, CStyleComments) { |
| std::string buffer("/*"); |
| for (size_t i = 0; i < 1000; i++) |
| buffer.append(" *\n"); |
| buffer.append(" */\n\n"); |
| buffer.append("#include \"foo/bar.h\"\n"); |
| |
| InputFile file(SourceFile("//foo.cc")); |
| file.SetContents(buffer); |
| |
| std::string_view contents; |
| LocationRange range; |
| |
| CIncludeIterator iter(&file); |
| EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range)); |
| EXPECT_EQ("foo/bar.h", contents); |
| } |
| |
| // Tests that spaces between the hash and directive are ignored. |
| TEST(CIncludeIterator, SpacesAfterHash) { |
| std::string buffer("# include \"foo/bar.h\"\n"); |
| |
| InputFile file(SourceFile("//foo.cc")); |
| file.SetContents(buffer); |
| |
| std::string_view contents; |
| LocationRange range; |
| |
| CIncludeIterator iter(&file); |
| EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range)); |
| EXPECT_EQ("foo/bar.h", contents); |
| |
| EXPECT_FALSE(iter.GetNextIncludeString(&contents, &range)); |
| } |