[clangd] Add a TestWorkspace utility
authorNathan Ridge <zeratul976@hotmail.com>
Tue, 13 Oct 2020 06:09:45 +0000 (02:09 -0400)
committerNathan Ridge <zeratul976@hotmail.com>
Sun, 25 Oct 2020 00:15:17 +0000 (20:15 -0400)
TestWorkspace allows easily writing tests involving multiple
files that can have inclusion relationships between them.

BackgroundIndexTest.RelationsMultiFile is refactored to use
TestWorkspace, and moved to FileIndexTest as it no longer
depends on BackgroundIndex.

Differential Revision: https://reviews.llvm.org/D89297

clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
clang-tools-extra/clangd/unittests/CMakeLists.txt
clang-tools-extra/clangd/unittests/FileIndexTests.cpp
clang-tools-extra/clangd/unittests/TestTU.cpp
clang-tools-extra/clangd/unittests/TestTU.h
clang-tools-extra/clangd/unittests/TestWorkspace.cpp [new file with mode: 0644]
clang-tools-extra/clangd/unittests/TestWorkspace.h [new file with mode: 0644]
llvm/utils/gn/secondary/clang-tools-extra/clangd/unittests/BUILD.gn

index cc0ca6f..b25d3fd 100644 (file)
@@ -230,49 +230,6 @@ TEST_F(BackgroundIndexTest, IndexTwoFiles) {
                        FileURI("unittest:///root/B.cc")}));
 }
 
-TEST_F(BackgroundIndexTest, RelationsMultiFile) {
-  MockFS FS;
-  FS.Files[testPath("root/Base.h")] = "class Base {};";
-  FS.Files[testPath("root/A.cc")] = R"cpp(
-    #include "Base.h"
-    class A : public Base {};
-  )cpp";
-  FS.Files[testPath("root/B.cc")] = R"cpp(
-    #include "Base.h"
-    class B : public Base {};
-  )cpp";
-
-  llvm::StringMap<std::string> Storage;
-  size_t CacheHits = 0;
-  MemoryShardStorage MSS(Storage, CacheHits);
-  OverlayCDB CDB(/*Base=*/nullptr);
-  BackgroundIndex Index(FS, CDB, [&](llvm::StringRef) { return &MSS; },
-                        /*Opts=*/{});
-
-  tooling::CompileCommand Cmd;
-  Cmd.Filename = testPath("root/A.cc");
-  Cmd.Directory = testPath("root");
-  Cmd.CommandLine = {"clang++", Cmd.Filename};
-  CDB.setCompileCommand(testPath("root/A.cc"), Cmd);
-  ASSERT_TRUE(Index.blockUntilIdleForTest());
-
-  Cmd.Filename = testPath("root/B.cc");
-  Cmd.CommandLine = {"clang++", Cmd.Filename};
-  CDB.setCompileCommand(testPath("root/B.cc"), Cmd);
-  ASSERT_TRUE(Index.blockUntilIdleForTest());
-
-  auto HeaderShard = MSS.loadShard(testPath("root/Base.h"));
-  EXPECT_NE(HeaderShard, nullptr);
-  SymbolID Base = findSymbol(*HeaderShard->Symbols, "Base").ID;
-
-  RelationsRequest Req;
-  Req.Subjects.insert(Base);
-  Req.Predicate = RelationKind::BaseOf;
-  uint32_t Results = 0;
-  Index.relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; });
-  EXPECT_EQ(Results, 2u);
-}
-
 TEST_F(BackgroundIndexTest, MainFileRefs) {
   MockFS FS;
   FS.Files[testPath("root/A.h")] = R"cpp(
index de8eaca..bf96448 100644 (file)
@@ -88,6 +88,7 @@ add_unittest(ClangdUnitTests ClangdTests
   TestFS.cpp
   TestIndex.cpp
   TestTU.cpp
+  TestWorkspace.cpp
   TypeHierarchyTests.cpp
   TweakTests.cpp
   TweakTesting.cpp
index 4abe0bf..80c4798 100644 (file)
@@ -14,6 +14,7 @@
 #include "SyncAPI.h"
 #include "TestFS.h"
 #include "TestTU.h"
+#include "TestWorkspace.h"
 #include "URI.h"
 #include "index/CanonicalIncludes.h"
 #include "index/FileIndex.h"
@@ -426,6 +427,33 @@ TEST(FileIndexTest, Relations) {
   EXPECT_EQ(Results, 1u);
 }
 
+TEST(FileIndexTest, RelationsMultiFile) {
+  TestWorkspace Workspace;
+  Workspace.addSource("Base.h", "class Base {};");
+  Workspace.addMainFile("A.cpp", R"cpp(
+    #include "Base.h"
+    class A : public Base {};
+  )cpp");
+  Workspace.addMainFile("B.cpp", R"cpp(
+    #include "Base.h"
+    class B : public Base {};
+  )cpp");
+
+  auto Index = Workspace.index();
+  FuzzyFindRequest FFReq;
+  FFReq.Query = "Base";
+  FFReq.AnyScope = true;
+  SymbolID Base;
+  Index->fuzzyFind(FFReq, [&](const Symbol &S) { Base = S.ID; });
+
+  RelationsRequest Req;
+  Req.Subjects.insert(Base);
+  Req.Predicate = RelationKind::BaseOf;
+  uint32_t Results = 0;
+  Index->relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; });
+  EXPECT_EQ(Results, 2u);
+}
+
 TEST(FileIndexTest, ReferencesInMainFileWithPreamble) {
   TestTU TU;
   TU.HeaderCode = "class Foo{};";
index 81b6b43..d0f011e 100644 (file)
@@ -80,7 +80,8 @@ void deleteModuleCache(const std::string ModuleCachePath) {
   }
 }
 
-std::shared_ptr<const PreambleData> TestTU::preamble() const {
+std::shared_ptr<const PreambleData>
+TestTU::preamble(PreambleParsedCallback PreambleCallback) const {
   MockFS FS;
   auto Inputs = inputs(FS);
   IgnoreDiagnostics Diags;
@@ -91,8 +92,7 @@ std::shared_ptr<const PreambleData> TestTU::preamble() const {
   auto ModuleCacheDeleter = llvm::make_scope_exit(
       std::bind(deleteModuleCache, CI->getHeaderSearchOpts().ModuleCachePath));
   return clang::clangd::buildPreamble(testPath(Filename), *CI, Inputs,
-                                      /*StoreInMemory=*/true,
-                                      /*PreambleCallback=*/nullptr);
+                                      /*StoreInMemory=*/true, PreambleCallback);
 }
 
 ParsedAST TestTU::build() const {
index dd0cecc..f383e69 100644 (file)
@@ -79,7 +79,8 @@ struct TestTU {
   // By default, build() will report Error diagnostics as GTest errors.
   // Suppress this behavior by adding an 'error-ok' comment to the code.
   ParsedAST build() const;
-  std::shared_ptr<const PreambleData> preamble() const;
+  std::shared_ptr<const PreambleData>
+  preamble(PreambleParsedCallback PreambleCallback = nullptr) const;
   ParseInputs inputs(MockFS &FS) const;
   SymbolSlab headerSymbols() const;
   RefSlab headerRefs() const;
diff --git a/clang-tools-extra/clangd/unittests/TestWorkspace.cpp b/clang-tools-extra/clangd/unittests/TestWorkspace.cpp
new file mode 100644 (file)
index 0000000..52cf45f
--- /dev/null
@@ -0,0 +1,49 @@
+//===--- TestWorkspace.cpp - Utility for writing multi-file tests -*- C++-*===//
+//
+// 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 "TestWorkspace.h"
+
+namespace clang {
+namespace clangd {
+
+std::unique_ptr<SymbolIndex> TestWorkspace::index() {
+  auto Index = std::make_unique<FileIndex>();
+  for (const auto &Input : Inputs) {
+    if (!Input.second.IsMainFile)
+      continue;
+    TU.Code = Input.second.Code;
+    TU.Filename = Input.first().str();
+    TU.preamble([&](ASTContext &Ctx, std::shared_ptr<clang::Preprocessor> PP,
+                    const CanonicalIncludes &CanonIncludes) {
+      Index->updatePreamble(testPath(Input.first()), "null", Ctx, PP,
+                            CanonIncludes);
+    });
+    ParsedAST MainAST = TU.build();
+    Index->updateMain(testPath(Input.first()), MainAST);
+  }
+  return Index;
+}
+
+Optional<ParsedAST> TestWorkspace::openFile(llvm::StringRef Filename) {
+  auto It = Inputs.find(Filename);
+  if (It == Inputs.end()) {
+    ADD_FAILURE() << "Accessing non-existing file: " << Filename;
+    return llvm::None;
+  }
+  TU.Code = It->second.Code;
+  TU.Filename = It->first().str();
+  return TU.build();
+}
+
+void TestWorkspace::addInput(llvm::StringRef Filename,
+                             const SourceFile &Input) {
+  Inputs.insert(std::make_pair(Filename, Input));
+  TU.AdditionalFiles.insert(std::make_pair(Filename, Input.Code));
+}
+} // namespace clangd
+} // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clangd/unittests/TestWorkspace.h b/clang-tools-extra/clangd/unittests/TestWorkspace.h
new file mode 100644 (file)
index 0000000..eb5112f
--- /dev/null
@@ -0,0 +1,59 @@
+//===--- TestWorkspace.h - Utility for writing multi-file tests --*- C++-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// TestWorkspace builds on TestTU to provide a way to write tests involving
+// several related files with inclusion relationships between them.
+//
+// The tests can exercise both index and AST based operations.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTWORKSPACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTWORKSPACE_H
+
+#include "TestFS.h"
+#include "TestTU.h"
+#include "index/FileIndex.h"
+#include "index/Index.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace clangd {
+
+class TestWorkspace {
+public:
+  // The difference between addSource() and addMainFile() is that only main
+  // files will be indexed.
+  void addSource(llvm::StringRef Filename, llvm::StringRef Code) {
+    addInput(Filename.str(), {Code.str(), /*IsMainFile=*/false});
+  }
+  void addMainFile(llvm::StringRef Filename, llvm::StringRef Code) {
+    addInput(Filename.str(), {Code.str(), /*IsMainFile=*/true});
+  }
+
+  std::unique_ptr<SymbolIndex> index();
+
+  Optional<ParsedAST> openFile(llvm::StringRef Filename);
+
+private:
+  struct SourceFile {
+    std::string Code;
+    bool IsMainFile = false;
+  };
+  llvm::StringMap<SourceFile> Inputs;
+  TestTU TU;
+
+  void addInput(llvm::StringRef Filename, const SourceFile &Input);
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTWORKSPACE_H
index 8872b36..4e8ac31 100644 (file)
@@ -96,6 +96,7 @@ unittest("ClangdTests") {
     "TestFS.cpp",
     "TestIndex.cpp",
     "TestTU.cpp",
+    "TestWorkspace.cpp",
     "TweakTesting.cpp",
     "TweakTests.cpp",
     "TypeHierarchyTests.cpp",