208 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			208 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "clang/AST/ASTConsumer.h"
 | |
| #include "clang/AST/DeclCXX.h"
 | |
| #include "clang/AST/DeclGroup.h"
 | |
| #include "clang/Frontend/ASTUnit.h"
 | |
| #include "clang/Frontend/CompilerInstance.h"
 | |
| #include "clang/Frontend/FrontendAction.h"
 | |
| #include "clang/Frontend/FrontendActions.h"
 | |
| #include "clang/Tooling/CompilationDatabase.h"
 | |
| #include "clang/Tooling/Tooling.h"
 | |
| #include "llvm/ADT/STLExtras.h"
 | |
| #include "llvm/Support/FormatVariadic.h"
 | |
| #include "llvm/Support/Path.h"
 | |
| #include "llvm/Support/TargetRegistry.h"
 | |
| #include "llvm/Support/TargetSelect.h"
 | |
| #include "gtest/gtest.h"
 | |
| #include <algorithm>
 | |
| #include <string>
 | |
| 
 | |
| namespace clang {
 | |
| namespace tooling {
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| /// Prints out all of the gathered dependencies into a string.
 | |
| class TestFileCollector : public DependencyFileGenerator {
 | |
| public:
 | |
|   TestFileCollector(DependencyOutputOptions &Opts,
 | |
|                     std::vector<std::string> &Deps)
 | |
|       : DependencyFileGenerator(Opts), Deps(Deps) {}
 | |
| 
 | |
|   void finishedMainFile(DiagnosticsEngine &Diags) override {
 | |
|     auto NewDeps = getDependencies();
 | |
|     Deps.insert(Deps.end(), NewDeps.begin(), NewDeps.end());
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   std::vector<std::string> &Deps;
 | |
| };
 | |
| 
 | |
| class TestDependencyScanningAction : public tooling::ToolAction {
 | |
| public:
 | |
|   TestDependencyScanningAction(std::vector<std::string> &Deps) : Deps(Deps) {}
 | |
| 
 | |
|   bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
 | |
|                      FileManager *FileMgr,
 | |
|                      std::shared_ptr<PCHContainerOperations> PCHContainerOps,
 | |
|                      DiagnosticConsumer *DiagConsumer) override {
 | |
|     CompilerInstance Compiler(std::move(PCHContainerOps));
 | |
|     Compiler.setInvocation(std::move(Invocation));
 | |
|     Compiler.setFileManager(FileMgr);
 | |
| 
 | |
|     Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
 | |
|     if (!Compiler.hasDiagnostics())
 | |
|       return false;
 | |
| 
 | |
|     Compiler.createSourceManager(*FileMgr);
 | |
|     Compiler.addDependencyCollector(std::make_shared<TestFileCollector>(
 | |
|         Compiler.getInvocation().getDependencyOutputOpts(), Deps));
 | |
| 
 | |
|     auto Action = std::make_unique<PreprocessOnlyAction>();
 | |
|     return Compiler.ExecuteAction(*Action);
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   std::vector<std::string> &Deps;
 | |
| };
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| TEST(DependencyScanner, ScanDepsReuseFilemanager) {
 | |
|   std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};
 | |
|   StringRef CWD = "/root";
 | |
|   FixedCompilationDatabase CDB(CWD, Compilation);
 | |
| 
 | |
|   auto VFS = new llvm::vfs::InMemoryFileSystem();
 | |
|   VFS->setCurrentWorkingDirectory(CWD);
 | |
|   auto Sept = llvm::sys::path::get_separator();
 | |
|   std::string HeaderPath =
 | |
|       std::string(llvm::formatv("{0}root{0}header.h", Sept));
 | |
|   std::string SymlinkPath =
 | |
|       std::string(llvm::formatv("{0}root{0}symlink.h", Sept));
 | |
|   std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));
 | |
| 
 | |
|   VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));
 | |
|   VFS->addHardLink(SymlinkPath, HeaderPath);
 | |
|   VFS->addFile(TestPath, 0,
 | |
|                llvm::MemoryBuffer::getMemBuffer(
 | |
|                    "#include \"symlink.h\"\n#include \"header.h\"\n"));
 | |
| 
 | |
|   ClangTool Tool(CDB, {"test.cpp"}, std::make_shared<PCHContainerOperations>(),
 | |
|                  VFS);
 | |
|   Tool.clearArgumentsAdjusters();
 | |
|   std::vector<std::string> Deps;
 | |
|   TestDependencyScanningAction Action(Deps);
 | |
|   Tool.run(&Action);
 | |
|   using llvm::sys::path::convert_to_slash;
 | |
|   // The first invocation should return dependencies in order of access.
 | |
|   ASSERT_EQ(Deps.size(), 3u);
 | |
|   EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[1]), "/root/symlink.h");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[2]), "/root/header.h");
 | |
| 
 | |
|   // The file manager should still have two FileEntries, as one file is a
 | |
|   // hardlink.
 | |
|   FileManager &Files = Tool.getFiles();
 | |
|   EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u);
 | |
| 
 | |
|   Deps.clear();
 | |
|   Tool.run(&Action);
 | |
|   // The second invocation should have the same order of dependencies.
 | |
|   ASSERT_EQ(Deps.size(), 3u);
 | |
|   EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[1]), "/root/symlink.h");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[2]), "/root/header.h");
 | |
| 
 | |
|   EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u);
 | |
| }
 | |
| 
 | |
| TEST(DependencyScanner, ScanDepsReuseFilemanagerSkippedFile) {
 | |
|   std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};
 | |
|   StringRef CWD = "/root";
 | |
|   FixedCompilationDatabase CDB(CWD, Compilation);
 | |
| 
 | |
|   auto VFS = new llvm::vfs::InMemoryFileSystem();
 | |
|   VFS->setCurrentWorkingDirectory(CWD);
 | |
|   auto Sept = llvm::sys::path::get_separator();
 | |
|   std::string HeaderPath =
 | |
|       std::string(llvm::formatv("{0}root{0}header.h", Sept));
 | |
|   std::string SymlinkPath =
 | |
|       std::string(llvm::formatv("{0}root{0}symlink.h", Sept));
 | |
|   std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));
 | |
|   std::string Test2Path =
 | |
|       std::string(llvm::formatv("{0}root{0}test2.cpp", Sept));
 | |
| 
 | |
|   VFS->addFile(HeaderPath, 0,
 | |
|                llvm::MemoryBuffer::getMemBuffer("#pragma once\n"));
 | |
|   VFS->addHardLink(SymlinkPath, HeaderPath);
 | |
|   VFS->addFile(TestPath, 0,
 | |
|                llvm::MemoryBuffer::getMemBuffer(
 | |
|                    "#include \"header.h\"\n#include \"symlink.h\"\n"));
 | |
|   VFS->addFile(Test2Path, 0,
 | |
|                llvm::MemoryBuffer::getMemBuffer(
 | |
|                    "#include \"symlink.h\"\n#include \"header.h\"\n"));
 | |
| 
 | |
|   ClangTool Tool(CDB, {"test.cpp", "test2.cpp"},
 | |
|                  std::make_shared<PCHContainerOperations>(), VFS);
 | |
|   Tool.clearArgumentsAdjusters();
 | |
|   std::vector<std::string> Deps;
 | |
|   TestDependencyScanningAction Action(Deps);
 | |
|   Tool.run(&Action);
 | |
|   using llvm::sys::path::convert_to_slash;
 | |
|   ASSERT_EQ(Deps.size(), 6u);
 | |
|   EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[1]), "/root/header.h");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[2]), "/root/symlink.h");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[3]), "/root/test2.cpp");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[4]), "/root/symlink.h");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[5]), "/root/header.h");
 | |
| }
 | |
| 
 | |
| TEST(DependencyScanner, ScanDepsReuseFilemanagerHasInclude) {
 | |
|   std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};
 | |
|   StringRef CWD = "/root";
 | |
|   FixedCompilationDatabase CDB(CWD, Compilation);
 | |
| 
 | |
|   auto VFS = new llvm::vfs::InMemoryFileSystem();
 | |
|   VFS->setCurrentWorkingDirectory(CWD);
 | |
|   auto Sept = llvm::sys::path::get_separator();
 | |
|   std::string HeaderPath =
 | |
|       std::string(llvm::formatv("{0}root{0}header.h", Sept));
 | |
|   std::string SymlinkPath =
 | |
|       std::string(llvm::formatv("{0}root{0}symlink.h", Sept));
 | |
|   std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));
 | |
| 
 | |
|   VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));
 | |
|   VFS->addHardLink(SymlinkPath, HeaderPath);
 | |
|   VFS->addFile(
 | |
|       TestPath, 0,
 | |
|       llvm::MemoryBuffer::getMemBuffer("#if __has_include(\"header.h\") && "
 | |
|                                        "__has_include(\"symlink.h\")\n#endif"));
 | |
| 
 | |
|   ClangTool Tool(CDB, {"test.cpp", "test.cpp"},
 | |
|                  std::make_shared<PCHContainerOperations>(), VFS);
 | |
|   Tool.clearArgumentsAdjusters();
 | |
|   std::vector<std::string> Deps;
 | |
|   TestDependencyScanningAction Action(Deps);
 | |
|   Tool.run(&Action);
 | |
|   using llvm::sys::path::convert_to_slash;
 | |
|   ASSERT_EQ(Deps.size(), 6u);
 | |
|   EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[1]), "/root/header.h");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[2]), "/root/symlink.h");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[3]), "/root/test.cpp");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[4]), "/root/header.h");
 | |
|   EXPECT_EQ(convert_to_slash(Deps[5]), "/root/symlink.h");
 | |
| }
 | |
| 
 | |
| } // end namespace tooling
 | |
| } // end namespace clang
 |