include-what-you-use/more_tests/iwyu_include_picker_test.cc

399 lines
15 KiB
C++

//===--- iwyu_include_picker_test.cc - test the iwyu include-picker file --===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Tests the internals of iwyu_include_picker.{h,cc}, and a few related
// functions from iwyu_path_util.h
#include "iwyu_include_picker.h"
#include <stddef.h>
#include <algorithm>
#include <string>
#include <vector>
#include "iwyu_globals.h"
#include "iwyu_path_util.h"
#include "testing/base/public/gunit.h"
namespace clang {
class ASTConsumer;
class ASTFrontendAction;
class CompilerInstance;
} // namespace clang
using clang::ASTConsumer;
using clang::ASTFrontendAction;
using clang::CompilerInstance;
namespace include_what_you_use {
namespace {
// Returns a string representing the first element where actual (a vector),
// and expected (an array) differ, or "" if they're identical.
template <size_t kCount> string VectorDiff(const string (&expected)[kCount],
const vector<string>& actual) {
for (int i = 0; i < std::min(kCount, actual.size()); ++i) {
if (expected[i] != actual[i]) {
return ("Differ at #" + std::to_string(i) + ": expected=" + expected[i] +
", actual=" + actual[i]);
}
}
if (kCount < actual.size()) {
return ("Differ at #" + std::to_string(kCount) +
": expected at EOF, actual=" + actual[kCount]);
} else if (actual.size() < kCount) {
return ("Differ at #" + std::to_string(kCount) + ": expected=" +
expected[actual.size()] + ", actual at EOF");
} else {
return "";
}
}
// Different format than usual due to VA_ARGS. First comes the actual
// value (as a string vector), then the expected values (as strings).
#define EXPECT_VECTOR_STREQ(actual, ...) do { \
const string expected[] = { __VA_ARGS__ }; \
EXPECT_EQ("", VectorDiff(expected, actual)); \
} while (0)
TEST(GetCanonicalName, StripsKnownSuffixes) {
EXPECT_EQ("my/path/foo", GetCanonicalName("my/path/foo.cxx"));
EXPECT_EQ("my/path/foo", GetCanonicalName("my/path/foo.cpp"));
EXPECT_EQ("my/path/foo", GetCanonicalName("my/path/foo.cc"));
EXPECT_EQ("my/path/foo", GetCanonicalName("my/path/foo.h"));
EXPECT_EQ("my/path/foo", GetCanonicalName("my/path/foo-inl.h"));
EXPECT_EQ("my/path/foo", GetCanonicalName("my/path/foo_unittest.cc"));
EXPECT_EQ("my/path/foo", GetCanonicalName("my/path/foo_regtest.cc"));
EXPECT_EQ("my/path/foo", GetCanonicalName("my/path/foo_test.cc"));
EXPECT_EQ("my/path/foo", GetCanonicalName("my/path/foo.c"));
EXPECT_EQ("my/path/foo", GetCanonicalName("my/path/foo-inl_unittest.cc"));
EXPECT_EQ("my/path/foo_mytest", GetCanonicalName("my/path/foo_mytest.cc"));
}
TEST(GetCanonicalName, StripsQuotes) {
EXPECT_EQ("set", GetCanonicalName("<set>"));
EXPECT_EQ("bits/stl_set", GetCanonicalName("<bits/stl_set.h>"));
EXPECT_EQ("my/path/foo", GetCanonicalName("\"my/path/foo-inl.h\""));
}
TEST(GetCanonicalName, MapsInternalToPublic) {
EXPECT_EQ("my/public/foo", GetCanonicalName("my/internal/foo.cc"));
EXPECT_EQ("my/public/foo", GetCanonicalName("my/public/foo.cc"));
EXPECT_EQ("my/public/foo", GetCanonicalName("my/internal/foo.h"));
EXPECT_EQ("my/public/foo", GetCanonicalName("my/public/foo.h"));
EXPECT_EQ("internal/foo", GetCanonicalName("internal/foo"));
EXPECT_EQ("path/internal_impl", GetCanonicalName("path/internal_impl.cc"));
}
TEST(IsSystemIncludeFile, Basic) {
EXPECT_FALSE(IsSystemIncludeFile("foo.h"));
EXPECT_TRUE(IsSystemIncludeFile("/usr/include/string.h"));
EXPECT_TRUE(IsSystemIncludeFile("/usr/include/c++/4.3/bits/stl_vector.h"));
}
TEST(ConvertToQuotedInclude, Basic) {
EXPECT_EQ("\"foo.h\"", ConvertToQuotedInclude("foo.h"));
EXPECT_EQ("<string.h>", ConvertToQuotedInclude("/usr/include/string.h"));
EXPECT_EQ("<bits/stl_vector.h>",
ConvertToQuotedInclude("/usr/include/c++/4.3/bits/stl_vector.h"));
}
TEST(DynamicMapping, DoesMapping) {
IncludePicker p;
p.AddDirectInclude("project/public/foo.h", "project/internal/private.h", "");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("project/internal/private.h"),
"\"project/public/foo.h\"");
}
TEST(DynamicMapping, MultiplePublicFiles) {
IncludePicker p;
p.AddDirectInclude("project/public/foo.h", "project/internal/private.h", "");
p.AddDirectInclude("project/public/bar.h", "project/internal/private.h", "");
p.AddDirectInclude("project/public/bar.h", "project/internal/other.h", "");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("project/internal/private.h"),
"\"project/public/foo.h\"", "\"project/public/bar.h\"");
}
TEST(DynamicMapping, TransitiveMapping) {
IncludePicker p;
p.AddDirectInclude("project/public/foo.h", "project/internal/private.h", "");
p.AddDirectInclude("project/internal/private.h", "project/internal/other.h",
"");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("project/internal/other.h"),
"\"project/public/foo.h\"");
}
TEST(DynamicMapping, MultipleTransitiveMapping) {
IncludePicker p;
p.AddDirectInclude("project/public/foo.h", "project/internal/private.h", "");
p.AddDirectInclude("project/public/bar.h", "project/internal/private.h", "");
p.AddDirectInclude("project/public/baz.h", "project/internal/private2.h", "");
p.AddDirectInclude("project/internal/private.h", "project/internal/other.h",
"");
p.AddDirectInclude("project/internal/private2.h", "project/internal/other.h",
"");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("project/internal/other.h"),
"\"project/public/foo.h\"", "\"project/public/bar.h\"",
"\"project/public/baz.h\"");
}
TEST(DynamicMapping, NormalizesAsm) {
IncludePicker p;
p.AddDirectInclude("/usr/include/types.h",
"/usr/include/asm-cris/posix_types.h", "");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("/usr/src/linux-headers-2.6.24-gg23/"
"include/asm-cris/posix_types.h"),
"<asm/posix_types.h>");
}
TEST(DynamicMapping, PrivateToPublicMapping) {
IncludePicker p;
// These names are not the public/internal names that AddInclude looks at.
p.AddMapping("\"project/private/foo.h\"", "\"project/not_private/bar.h\"");
p.MarkIncludeAsPrivate("\"project/private/foo.h\"");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("project/private/foo.h"),
"\"project/not_private/bar.h\"");
}
TEST(GetCandidateHeadersForSymbol, Basic) {
IncludePicker p;
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(p.GetCandidateHeadersForSymbol("dev_t"),
"<sys/types.h>", "<sys/stat.h>");
EXPECT_VECTOR_STREQ(p.GetCandidateHeadersForSymbol("NULL"),
"<stddef.h>", "<cstddef>", "<clocale>", "<cstdio>",
"<cstdlib>", "<cstring>", "<ctime>", "<cwchar>",
"<locale.h>", "<stdio.h>", "<stdlib.h>", "<string.h>",
"<time.h>", "<wchar.h>"
);
EXPECT_VECTOR_STREQ(p.GetCandidateHeadersForSymbol("std::allocator"),
"<memory>", "<string>", "<vector>", "<map>", "<set>");
EXPECT_EQ(0, p.GetCandidateHeadersForSymbol("foo").size());
}
TEST(GetCandidateHeadersForFilepath, C) {
IncludePicker p;
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("/usr/include/bits/dlfcn.h"),
"<dlfcn.h>");
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("/usr/grte/v1/include/assert.h"),
"<assert.h>", "<cassert>");
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("/usr/grte/v1/include/stdarg.h"),
"<stdarg.h>", "<cstdarg>");
}
TEST(GetCandidateHeadersForFilepath, CXX) {
IncludePicker p;
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("/usr/include/c++/4.2/bits/allocator.h"),
"<memory>");
}
TEST(GetCandidateHeadersForFilepath, NoIdentityRegex) {
IncludePicker p;
// Make sure we don't complain when the key of a mapping is a regex
// that includes the value (which would, naively, lead to an identity
// mapping).
p.AddMapping("@\"mydir/.*\\.h\"", "\"mydir/include.h\"");
p.MarkIncludeAsPrivate("@\"mydir/.*\\.h\""); // will *not* apply to include.h!
// Add a direct include that should be mapped, and that already is.
p.AddDirectInclude("a.h", "mydir/internal.h", "");
p.AddDirectInclude("b.h", "mydir/include.h", "");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("mydir/internal.h"),
"\"mydir/include.h\"");
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("mydir/include.h"),
"\"mydir/include.h\"");
}
TEST(GetCandidateHeadersForFilepath, NotInAnyMap) {
IncludePicker p;
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("/usr/grte/v1/include/poll.h"),
"<poll.h>");
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("././././my/dot.h"),
"\"my/dot.h\"");
}
TEST(GetCandidateHeadersForFilepath, IncludeRecursion) {
IncludePicker p;
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("/usr/include/c++/4.2/bits/istream.tcc"),
"<istream>", "<fstream>", "<iostream>", "<sstream>");
}
TEST(GetCandidateHeadersForFilepath, PrivateValueInRecursion) {
IncludePicker p;
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("/usr/include/linux/errno.h"),
"<errno.h>", "<cerrno>");
}
TEST(GetCandidateHeadersForFilepath, NoBuiltin) {
// Make sure we never specify "<built-in>" as an #include mapping.
IncludePicker p;
p.AddDirectInclude("<built-in>", "foo/bar/internal/code.cc", "");
p.AddDirectInclude("foo/bar/internal/code.cc", "foo/qux/internal/lib.h", "");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepath("foo/qux/internal/lib.h"),
"\"foo/qux/internal/lib.h\"");
}
TEST(GetCandidateHeadersForFilepathIncludedFrom, NoInternal) {
IncludePicker p;
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepathIncludedFrom("/usr/include/bits/dlfcn.h",
"mydir/myapp.h"),
"<dlfcn.h>");
}
TEST(GetCandidateHeadersForFilepathIncludedFrom, Internal) {
IncludePicker p;
// clang always has <built-in> #including the file specified on the cmdline.
p.AddDirectInclude("<built-in>", "foo/bar/internal/code.cc", "");
p.AddDirectInclude("foo/bar/internal/code.cc", "foo/bar/public/code.h", "");
p.AddDirectInclude("foo/bar/public/code.h", "foo/bar/internal/hdr.h", "");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepathIncludedFrom("foo/bar/internal/hdr.h",
"foo/bar/internal/code.cc"),
"\"foo/bar/internal/hdr.h\"");
}
TEST(GetCandidateHeadersForFilepathIncludedFrom, OtherInternal) {
IncludePicker p;
p.AddDirectInclude("foo/bar/public/code.h", "foo/bar/internal/hdr.h", "");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepathIncludedFrom("foo/bar/internal/hdr.h",
"baz/internal/code.cc"),
"\"foo/bar/public/code.h\"");
}
TEST(GetCandidateHeadersForFilepathIncludedFrom, PublicToInternal) {
IncludePicker p;
p.AddDirectInclude("foo/bar/public/code.cc", "foo/bar/public/code.h", "");
p.AddDirectInclude("foo/bar/public/code.cc", "foo/bar/public/code2.h", "");
p.AddDirectInclude("foo/bar/public/code.h", "foo/bar/internal/hdr.h", "");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepathIncludedFrom("foo/bar/internal/hdr.h",
"foo/bar/public/code.h"),
"\"foo/bar/internal/hdr.h\"");
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepathIncludedFrom("foo/bar/internal/hdr.h",
"foo/bar/public/code2.h"),
"\"foo/bar/internal/hdr.h\"");
}
TEST(GetCandidateHeadersForFilepathIncludedFrom, FriendRegex) {
IncludePicker p;
p.AddDirectInclude("baz.cc", "baz.h", "");
p.AddDirectInclude("baz.cc", "abcde.h", "");
p.AddDirectInclude("baz.cc", "random.h", "");
p.AddDirectInclude("baz.h", "project/private/bar.h", "");
p.AddDirectInclude("abcde.h", "project/private/bar.h", "");
p.AddDirectInclude("random.h", "project/private/bar.h", "");
p.AddMapping("\"project/private/bar.h\"", "\"foo.h\"");
p.MarkIncludeAsPrivate("\"project/private/bar.h\"");
p.AddFriendRegex("project/private/bar.h", "\"baz.*\"");
p.AddFriendRegex("project/private/bar.h", "\"a.c.+\\.h\"");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepathIncludedFrom("project/private/bar.h",
"random.h"),
"\"foo.h\"");
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepathIncludedFrom("project/private/bar.h",
"baz.h"),
"\"project/private/bar.h\"");
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepathIncludedFrom("project/private/bar.h",
"abcde.h"),
"\"project/private/bar.h\"");
}
TEST(GetCandidateHeadersForFilepathIncludedFrom, PreservesWrittenForm) {
IncludePicker p;
p.AddDirectInclude("baz.cc", "baz.h", "\"./././baz.h\"");
p.FinalizeAddedIncludes();
EXPECT_VECTOR_STREQ(
p.GetCandidateHeadersForFilepathIncludedFrom("baz.h", "baz.cc"),
"\"./././baz.h\"");
}
TEST(HasMapping, IncludeMatch) {
IncludePicker p;
p.FinalizeAddedIncludes();
EXPECT_TRUE(p.HasMapping("/usr/include/stdio.h",
"/usr/include/c++/4.2/cstdio"));
EXPECT_TRUE(p.HasMapping("/usr/include/c++/4.2/bits/stl_deque.h",
"/usr/include/c++/4.2/deque"));
EXPECT_TRUE(p.HasMapping("/usr/include/bits/stat.h",
"/usr/include/sys/stat.h"));
EXPECT_FALSE(p.HasMapping("/usr/include/bits/syscall.h",
"/usr/include/sys/stat.h"));
}
TEST(HasMapping, IncludeMatchIndirectly) {
IncludePicker p;
p.FinalizeAddedIncludes();
EXPECT_TRUE(p.HasMapping("/usr/include/c++/4.2/ios",
"/usr/include/c++/4.2/iostream"));
EXPECT_TRUE(p.HasMapping("/usr/include/linux/errno.h",
"/usr/include/errno.h"));
}
TEST(HasMapping, IncludeMatchDifferentMaps) {
IncludePicker p;
p.FinalizeAddedIncludes();
// Testing when a google path re-exports a c++ system #include.
EXPECT_TRUE(p.HasMapping("/usr/include/c++/4.2/ostream", "base/logging.h"));
// Do some indirect checking too.
EXPECT_TRUE(p.HasMapping("/usr/include/c++/4.2/ios", "base/logging.h"));
}
} // namespace
} // namespace include_what_you_use
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
include_what_you_use::InitGlobalsAndFlagsForTesting();
return RUN_ALL_TESTS();
}