diff options
Diffstat (limited to 'unittests/Tooling/CompilationDatabaseTest.cpp')
-rw-r--r-- | unittests/Tooling/CompilationDatabaseTest.cpp | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp index fd8afe6b79762..ffc1d5b3a4acb 100644 --- a/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/unittests/Tooling/CompilationDatabaseTest.cpp @@ -626,5 +626,143 @@ TEST(ParseFixedCompilationDatabase, HandlesArgv0) { EXPECT_EQ(2, Argc); } +struct MemCDB : public CompilationDatabase { + using EntryMap = llvm::StringMap<SmallVector<CompileCommand, 1>>; + EntryMap Entries; + MemCDB(const EntryMap &E) : Entries(E) {} + + std::vector<CompileCommand> getCompileCommands(StringRef F) const override { + auto Ret = Entries.lookup(F); + return {Ret.begin(), Ret.end()}; + } + + std::vector<std::string> getAllFiles() const override { + std::vector<std::string> Result; + for (const auto &Entry : Entries) + Result.push_back(Entry.first()); + return Result; + } +}; + +class InterpolateTest : public ::testing::Test { +protected: + // Adds an entry to the underlying compilation database. + // A flag is injected: -D <File>, so the command used can be identified. + void add(llvm::StringRef File, llvm::StringRef Flags = "") { + llvm::SmallVector<StringRef, 8> Argv = {"clang", File, "-D", File}; + llvm::SplitString(Flags, Argv); + llvm::SmallString<32> Dir; + llvm::sys::path::system_temp_directory(false, Dir); + Entries[path(File)].push_back( + {Dir, path(File), {Argv.begin(), Argv.end()}, "foo.o"}); + } + + // Turn a unix path fragment (foo/bar.h) into a native path (C:\tmp\foo\bar.h) + std::string path(llvm::SmallString<32> File) { + llvm::SmallString<32> Dir; + llvm::sys::path::system_temp_directory(false, Dir); + llvm::sys::path::native(File); + llvm::SmallString<64> Result; + llvm::sys::path::append(Result, Dir, File); + return Result.str(); + } + + // Look up the command from a relative path, and return it in string form. + // The input file is not included in the returned command. + std::string getCommand(llvm::StringRef F) { + auto Results = + inferMissingCompileCommands(llvm::make_unique<MemCDB>(Entries)) + ->getCompileCommands(path(F)); + if (Results.empty()) + return "none"; + // drop the input file argument, so tests don't have to deal with path(). + EXPECT_EQ(Results[0].CommandLine.back(), path(F)) + << "Last arg should be the file"; + Results[0].CommandLine.pop_back(); + return llvm::join(Results[0].CommandLine, " "); + } + + MemCDB::EntryMap Entries; +}; + +TEST_F(InterpolateTest, Nearby) { + add("dir/foo.cpp"); + add("dir/bar.cpp"); + add("an/other/foo.cpp"); + + // great: dir and name both match (prefix or full, case insensitive) + EXPECT_EQ(getCommand("dir/f.cpp"), "clang -D dir/foo.cpp"); + EXPECT_EQ(getCommand("dir/FOO.cpp"), "clang -D dir/foo.cpp"); + // no name match. prefer matching dir, break ties by alpha + EXPECT_EQ(getCommand("dir/a.cpp"), "clang -D dir/bar.cpp"); + // an exact name match beats one segment of directory match + EXPECT_EQ(getCommand("some/other/bar.h"), + "clang -D dir/bar.cpp -x c++-header"); + // two segments of directory match beat a prefix name match + EXPECT_EQ(getCommand("an/other/b.cpp"), "clang -D an/other/foo.cpp"); + // if nothing matches at all, we still get the closest alpha match + EXPECT_EQ(getCommand("below/some/obscure/path.cpp"), + "clang -D an/other/foo.cpp"); +} + +TEST_F(InterpolateTest, Language) { + add("dir/foo.cpp", "-std=c++17"); + add("dir/baz.cee", "-x c"); + + // .h is ambiguous, so we add explicit language flags + EXPECT_EQ(getCommand("foo.h"), + "clang -D dir/foo.cpp -x c++-header -std=c++17"); + // and don't add -x if the inferred language is correct. + EXPECT_EQ(getCommand("foo.hpp"), "clang -D dir/foo.cpp -std=c++17"); + // respect -x if it's already there. + EXPECT_EQ(getCommand("baz.h"), "clang -D dir/baz.cee -x c-header"); + // prefer a worse match with the right language + EXPECT_EQ(getCommand("foo.c"), "clang -D dir/baz.cee"); + Entries.erase(path(StringRef("dir/baz.cee"))); + // Now we transfer across languages, so drop -std too. + EXPECT_EQ(getCommand("foo.c"), "clang -D dir/foo.cpp"); +} + +TEST_F(InterpolateTest, Strip) { + add("dir/foo.cpp", "-o foo.o -Wall"); + // the -o option and the input file are removed, but -Wall is preserved. + EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall"); +} + +TEST_F(InterpolateTest, Case) { + add("FOO/BAR/BAZ/SHOUT.cc"); + add("foo/bar/baz/quiet.cc"); + // Case mismatches are completely ignored, so we choose the name match. + EXPECT_EQ(getCommand("foo/bar/baz/shout.C"), "clang -D FOO/BAR/BAZ/SHOUT.cc"); +} + +TEST(CompileCommandTest, EqualityOperator) { + CompileCommand CCRef("/foo/bar", "hello.c", {"a", "b"}, "hello.o"); + CompileCommand CCTest = CCRef; + + EXPECT_TRUE(CCRef == CCTest); + EXPECT_FALSE(CCRef != CCTest); + + CCTest = CCRef; + CCTest.Directory = "/foo/baz"; + EXPECT_FALSE(CCRef == CCTest); + EXPECT_TRUE(CCRef != CCTest); + + CCTest = CCRef; + CCTest.Filename = "bonjour.c"; + EXPECT_FALSE(CCRef == CCTest); + EXPECT_TRUE(CCRef != CCTest); + + CCTest = CCRef; + CCTest.CommandLine.push_back("c"); + EXPECT_FALSE(CCRef == CCTest); + EXPECT_TRUE(CCRef != CCTest); + + CCTest = CCRef; + CCTest.Output = "bonjour.o"; + EXPECT_FALSE(CCRef == CCTest); + EXPECT_TRUE(CCRef != CCTest); +} + } // end namespace tooling } // end namespace clang |