Author: sammccall
Date: Mon Dec  4 23:20:26 2017
New Revision: 319741

URL: http://llvm.org/viewvc/llvm-project?rev=319741&view=rev
Log:
[clangd] Split code-completion tests out of ClangdTests. NFC.

Summary:
Common parts are mostly FS related, so pulled out TestFS.h for the common stuff.
Deliberately resisted cleaning up much here, so this is pretty mechanical.

Reviewers: hokein

Subscribers: klimek, mgorny, ilya-biryukov, cfe-commits

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

Added:
    clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
    clang-tools-extra/trunk/unittests/clangd/TestFS.cpp
    clang-tools-extra/trunk/unittests/clangd/TestFS.h
Modified:
    clang-tools-extra/trunk/clangd/CodeComplete.cpp
    clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
    clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp

Modified: clang-tools-extra/trunk/clangd/CodeComplete.cpp
URL: 
http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.cpp?rev=319741&r1=319740&r2=319741&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/CodeComplete.cpp (original)
+++ clang-tools-extra/trunk/clangd/CodeComplete.cpp Mon Dec  4 23:20:26 2017
@@ -163,7 +163,6 @@ getOptionalParameters(const CodeCompleti
   return Result;
 }
 
-
 /// A scored code completion result.
 /// It may be promoted to a CompletionItem if it's among the top-ranked 
results.
 struct CompletionCandidate {
@@ -349,9 +348,8 @@ class PlainTextCompletionItemsCollector
     : public CompletionItemsCollector {
 
 public:
-  PlainTextCompletionItemsCollector(
-      const CodeCompleteOptions &CodeCompleteOpts,
-      CompletionList &Items)
+  PlainTextCompletionItemsCollector(const CodeCompleteOptions 
&CodeCompleteOpts,
+                                    CompletionList &Items)
       : CompletionItemsCollector(CodeCompleteOpts, Items) {}
 
 private:
@@ -386,9 +384,8 @@ private:
 class SnippetCompletionItemsCollector final : public CompletionItemsCollector {
 
 public:
-  SnippetCompletionItemsCollector(
-      const CodeCompleteOptions &CodeCompleteOpts,
-      CompletionList &Items)
+  SnippetCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
+                                  CompletionList &Items)
       : CompletionItemsCollector(CodeCompleteOpts, Items) {}
 
 private:

Modified: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
URL: 
http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt?rev=319741&r1=319740&r2=319741&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Mon Dec  4 23:20:26 
2017
@@ -10,8 +10,10 @@ include_directories(
 
 add_extra_unittest(ClangdTests
   ClangdTests.cpp
+  CodeCompleteTests.cpp
   FuzzyMatchTests.cpp
   JSONExprTests.cpp
+  TestFS.cpp
   TraceTests.cpp
   )
 

Modified: clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp
URL: 
http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp?rev=319741&r1=319740&r2=319741&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp Mon Dec  4 
23:20:26 2017
@@ -10,7 +10,7 @@
 #include "ClangdLSPServer.h"
 #include "ClangdServer.h"
 #include "Logger.h"
-#include "clang/Basic/VirtualFileSystem.h"
+#include "TestFS.h"
 #include "clang/Config/config.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
@@ -27,141 +27,9 @@
 #include <vector>
 
 namespace clang {
-namespace vfs {
-
-/// An implementation of vfs::FileSystem that only allows access to
-/// files and folders inside a set of whitelisted directories.
-///
-/// FIXME(ibiryukov): should it also emulate access to parents of whitelisted
-/// directories with only whitelisted contents?
-class FilteredFileSystem : public vfs::FileSystem {
-public:
-  /// The paths inside \p WhitelistedDirs should be absolute
-  FilteredFileSystem(std::vector<std::string> WhitelistedDirs,
-                     IntrusiveRefCntPtr<vfs::FileSystem> InnerFS)
-      : WhitelistedDirs(std::move(WhitelistedDirs)), InnerFS(InnerFS) {
-    assert(std::all_of(WhitelistedDirs.begin(), WhitelistedDirs.end(),
-                       [](const std::string &Path) -> bool {
-                         return llvm::sys::path::is_absolute(Path);
-                       }) &&
-           "Not all WhitelistedDirs are absolute");
-  }
-
-  virtual llvm::ErrorOr<Status> status(const Twine &Path) {
-    if (!isInsideWhitelistedDir(Path))
-      return llvm::errc::no_such_file_or_directory;
-    return InnerFS->status(Path);
-  }
-
-  virtual llvm::ErrorOr<std::unique_ptr<File>>
-  openFileForRead(const Twine &Path) {
-    if (!isInsideWhitelistedDir(Path))
-      return llvm::errc::no_such_file_or_directory;
-    return InnerFS->openFileForRead(Path);
-  }
-
-  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
-  getBufferForFile(const Twine &Name, int64_t FileSize = -1,
-                   bool RequiresNullTerminator = true,
-                   bool IsVolatile = false) {
-    if (!isInsideWhitelistedDir(Name))
-      return llvm::errc::no_such_file_or_directory;
-    return InnerFS->getBufferForFile(Name, FileSize, RequiresNullTerminator,
-                                     IsVolatile);
-  }
-
-  virtual directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) {
-    if (!isInsideWhitelistedDir(Dir)) {
-      EC = llvm::errc::no_such_file_or_directory;
-      return directory_iterator();
-    }
-    return InnerFS->dir_begin(Dir, EC);
-  }
-
-  virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) {
-    return InnerFS->setCurrentWorkingDirectory(Path);
-  }
-
-  virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const {
-    return InnerFS->getCurrentWorkingDirectory();
-  }
-
-  bool exists(const Twine &Path) {
-    if (!isInsideWhitelistedDir(Path))
-      return false;
-    return InnerFS->exists(Path);
-  }
-
-  std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const {
-    return InnerFS->makeAbsolute(Path);
-  }
-
-private:
-  bool isInsideWhitelistedDir(const Twine &InputPath) const {
-    SmallString<128> Path;
-    InputPath.toVector(Path);
-
-    if (makeAbsolute(Path))
-      return false;
-
-    for (const auto &Dir : WhitelistedDirs) {
-      if (Path.startswith(Dir))
-        return true;
-    }
-    return false;
-  }
-
-  std::vector<std::string> WhitelistedDirs;
-  IntrusiveRefCntPtr<vfs::FileSystem> InnerFS;
-};
-
-/// Create a vfs::FileSystem that has access only to temporary directories
-/// (obtained by calling system_temp_directory).
-IntrusiveRefCntPtr<vfs::FileSystem> getTempOnlyFS() {
-  llvm::SmallString<128> TmpDir1;
-  llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/false, TmpDir1);
-  llvm::SmallString<128> TmpDir2;
-  llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/true, TmpDir2);
-
-  std::vector<std::string> TmpDirs;
-  TmpDirs.push_back(TmpDir1.str());
-  if (TmpDir1 != TmpDir2)
-    TmpDirs.push_back(TmpDir2.str());
-  return new vfs::FilteredFileSystem(std::move(TmpDirs),
-                                     vfs::getRealFileSystem());
-}
-} // namespace vfs
-
 namespace clangd {
 namespace {
 
-struct StringWithPos {
-  std::string Text;
-  clangd::Position MarkerPos;
-};
-
-/// Returns location of "{mark}" substring in \p Text and removes it from \p
-/// Text. Note that \p Text must contain exactly one occurence of "{mark}".
-///
-/// Marker name can be configured using \p MarkerName parameter.
-StringWithPos parseTextMarker(StringRef Text, StringRef MarkerName = "mark") {
-  SmallString<16> Marker;
-  Twine("{" + MarkerName + "}").toVector(/*ref*/ Marker);
-
-  std::size_t MarkerOffset = Text.find(Marker);
-  assert(MarkerOffset != StringRef::npos && "{mark} wasn't found in Text.");
-
-  std::string WithoutMarker;
-  WithoutMarker += Text.take_front(MarkerOffset);
-  WithoutMarker += Text.drop_front(MarkerOffset + Marker.size());
-  assert(StringRef(WithoutMarker).find(Marker) == StringRef::npos &&
-         "There were multiple occurences of {mark} inside Text");
-
-  clangd::Position MarkerPos =
-      clangd::offsetToPosition(WithoutMarker, MarkerOffset);
-  return {std::move(WithoutMarker), MarkerPos};
-}
-
 // Don't wait for async ops in clangd test more than that to avoid blocking
 // indefinitely in case of bugs.
 static const std::chrono::seconds DefaultFutureTimeout =
@@ -203,46 +71,6 @@ private:
   VFSTag LastVFSTag = VFSTag();
 };
 
-class MockCompilationDatabase : public GlobalCompilationDatabase {
-public:
-  MockCompilationDatabase(bool AddFreestandingFlag) {
-    // We have to add -ffreestanding to VFS-specific tests to avoid errors on
-    // implicit includes of stdc-predef.h.
-    if (AddFreestandingFlag)
-      ExtraClangFlags.push_back("-ffreestanding");
-  }
-
-  llvm::Optional<tooling::CompileCommand>
-  getCompileCommand(PathRef File) const override {
-    if (ExtraClangFlags.empty())
-      return llvm::None;
-
-    auto CommandLine = ExtraClangFlags;
-    CommandLine.insert(CommandLine.begin(), "clang");
-    CommandLine.insert(CommandLine.end(), File.str());
-    return {tooling::CompileCommand(llvm::sys::path::parent_path(File),
-                                    llvm::sys::path::filename(File),
-                                    std::move(CommandLine), "")};
-  }
-
-  std::vector<std::string> ExtraClangFlags;
-};
-
-IntrusiveRefCntPtr<vfs::FileSystem>
-buildTestFS(llvm::StringMap<std::string> const &Files) {
-  IntrusiveRefCntPtr<vfs::InMemoryFileSystem> MemFS(
-      new vfs::InMemoryFileSystem);
-  for (auto &FileAndContents : Files)
-    MemFS->addFile(FileAndContents.first(), time_t(),
-                   llvm::MemoryBuffer::getMemBuffer(FileAndContents.second,
-                                                    FileAndContents.first()));
-
-  auto OverlayFS = IntrusiveRefCntPtr<vfs::OverlayFileSystem>(
-      new vfs::OverlayFileSystem(vfs::getTempOnlyFS()));
-  OverlayFS->pushOverlay(std::move(MemFS));
-  return OverlayFS;
-}
-
 class ConstantFSProvider : public FileSystemProvider {
 public:
   ConstantFSProvider(IntrusiveRefCntPtr<vfs::FileSystem> FS,
@@ -259,23 +87,6 @@ private:
   VFSTag Tag;
 };
 
-class MockFSProvider : public FileSystemProvider {
-public:
-  Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
-  getTaggedFileSystem(PathRef File) override {
-    if (ExpectedFile) {
-      EXPECT_EQ(*ExpectedFile, File);
-    }
-
-    auto FS = buildTestFS(Files);
-    return make_tagged(FS, Tag);
-  }
-
-  llvm::Optional<SmallString<32>> ExpectedFile;
-  llvm::StringMap<std::string> Files;
-  VFSTag Tag = VFSTag();
-};
-
 /// Replaces all patterns of the form 0x123abc with spaces
 std::string replacePtrsInDump(std::string const &Dump) {
   llvm::Regex RE("0x[0-9a-fA-F]+");
@@ -304,22 +115,6 @@ std::string dumpASTWithoutMemoryLocs(Cla
 
 class ClangdVFSTest : public ::testing::Test {
 protected:
-  SmallString<16> getVirtualTestRoot() {
-#ifdef LLVM_ON_WIN32
-    return SmallString<16>("C:\\clangd-test");
-#else
-    return SmallString<16>("/clangd-test");
-#endif
-  }
-
-  llvm::SmallString<32> getVirtualTestFilePath(PathRef File) {
-    assert(llvm::sys::path::is_relative(File) && "FileName should be 
relative");
-
-    llvm::SmallString<32> Path;
-    llvm::sys::path::append(Path, getVirtualTestRoot(), File);
-    return Path;
-  }
-
   std::string parseSourceAndDumpAST(
       PathRef SourceFileRelPath, StringRef SourceContents,
       std::vector<std::pair<PathRef, StringRef>> ExtraFiles = {},
@@ -621,352 +416,6 @@ struct bar { T x; };
   EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
 }
 
-class ClangdCompletionTest : public ClangdVFSTest {
-protected:
-  template <class Predicate>
-  bool ContainsItemPred(CompletionList const &Items, Predicate Pred) {
-    for (const auto &Item : Items.items) {
-      if (Pred(Item))
-        return true;
-    }
-    return false;
-  }
-
-  bool ContainsItem(CompletionList const &Items, StringRef Name) {
-    return ContainsItemPred(Items, [Name](clangd::CompletionItem Item) {
-      return Item.insertText == Name;
-    });
-    return false;
-  }
-};
-
-TEST_F(ClangdCompletionTest, CheckContentsOverride) {
-  MockFSProvider FS;
-  ErrorCheckingDiagConsumer DiagConsumer;
-  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
-
-  ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
-                      /*StorePreamblesInMemory=*/true,
-                      clangd::CodeCompleteOptions(),
-                      EmptyLogger::getInstance());
-
-  auto FooCpp = getVirtualTestFilePath("foo.cpp");
-  const auto SourceContents = R"cpp(
-int aba;
-int b =   ;
-)cpp";
-
-  const auto OverridenSourceContents = R"cpp(
-int cbc;
-int b =   ;
-)cpp";
-  // Complete after '=' sign. We need to be careful to keep the SourceContents'
-  // size the same.
-  // We complete on the 3rd line (2nd in zero-based numbering), because raw
-  // string literal of the SourceContents starts with a newline(it's easy to
-  // miss).
-  Position CompletePos = {2, 8};
-  FS.Files[FooCpp] = SourceContents;
-  FS.ExpectedFile = FooCpp;
-
-  // No need to sync reparses here as there are no asserts on diagnostics (or
-  // other async operations).
-  Server.addDocument(FooCpp, SourceContents);
-
-  {
-    auto CodeCompletionResults1 =
-        Server.codeComplete(FooCpp, CompletePos, None).get().Value;
-    EXPECT_TRUE(ContainsItem(CodeCompletionResults1, "aba"));
-    EXPECT_FALSE(ContainsItem(CodeCompletionResults1, "cbc"));
-  }
-
-  {
-    auto CodeCompletionResultsOverriden =
-        Server
-            .codeComplete(FooCpp, CompletePos,
-                          StringRef(OverridenSourceContents))
-            .get()
-            .Value;
-    EXPECT_TRUE(ContainsItem(CodeCompletionResultsOverriden, "cbc"));
-    EXPECT_FALSE(ContainsItem(CodeCompletionResultsOverriden, "aba"));
-  }
-
-  {
-    auto CodeCompletionResults2 =
-        Server.codeComplete(FooCpp, CompletePos, None).get().Value;
-    EXPECT_TRUE(ContainsItem(CodeCompletionResults2, "aba"));
-    EXPECT_FALSE(ContainsItem(CodeCompletionResults2, "cbc"));
-  }
-}
-
-TEST_F(ClangdCompletionTest, Limit) {
-  MockFSProvider FS;
-  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
-  CDB.ExtraClangFlags.push_back("-xc++");
-  ErrorCheckingDiagConsumer DiagConsumer;
-  clangd::CodeCompleteOptions Opts;
-  Opts.Limit = 2;
-  ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
-                      /*StorePreamblesInMemory=*/true, Opts,
-                      EmptyLogger::getInstance());
-
-  auto FooCpp = getVirtualTestFilePath("foo.cpp");
-  FS.Files[FooCpp] = "";
-  FS.ExpectedFile = FooCpp;
-  StringWithPos Completion = parseTextMarker(R"cpp(
-struct ClassWithMembers {
-  int AAA();
-  int BBB();
-  int CCC();
-}
-int main() { ClassWithMembers().{complete} }
-      )cpp",
-                                             "complete");
-  Server.addDocument(FooCpp, Completion.Text);
-
-  /// For after-dot completion we must always get consistent results.
-  auto Results = Server
-                     .codeComplete(FooCpp, Completion.MarkerPos,
-                                   StringRef(Completion.Text))
-                     .get()
-                     .Value;
-
-  EXPECT_TRUE(Results.isIncomplete);
-  EXPECT_EQ(Opts.Limit, Results.items.size());
-  EXPECT_TRUE(ContainsItem(Results, "AAA"));
-  EXPECT_TRUE(ContainsItem(Results, "BBB"));
-  EXPECT_FALSE(ContainsItem(Results, "CCC"));
-}
-
-TEST_F(ClangdCompletionTest, Filter) {
-  MockFSProvider FS;
-  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
-  CDB.ExtraClangFlags.push_back("-xc++");
-  ErrorCheckingDiagConsumer DiagConsumer;
-  clangd::CodeCompleteOptions Opts;
-  ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
-                      /*StorePreamblesInMemory=*/true, Opts,
-                      EmptyLogger::getInstance());
-
-  auto FooCpp = getVirtualTestFilePath("foo.cpp");
-  FS.Files[FooCpp] = "";
-  FS.ExpectedFile = FooCpp;
-  const char *Body = R"cpp(
-    int Abracadabra;
-    int Alakazam;
-    struct S {
-      int FooBar;
-      int FooBaz;
-      int Qux;
-    };
-  )cpp";
-  auto Complete = [&](StringRef Query) {
-    StringWithPos Completion = parseTextMarker(
-        formatv("{0} int main() { {1}{{complete}} }", Body, Query).str(),
-        "complete");
-    Server.addDocument(FooCpp, Completion.Text);
-    return Server
-        .codeComplete(FooCpp, Completion.MarkerPos, StringRef(Completion.Text))
-        .get()
-        .Value;
-  };
-
-  auto Foba = Complete("S().Foba");
-  EXPECT_TRUE(ContainsItem(Foba, "FooBar"));
-  EXPECT_TRUE(ContainsItem(Foba, "FooBaz"));
-  EXPECT_FALSE(ContainsItem(Foba, "Qux"));
-
-  auto FR = Complete("S().FR");
-  EXPECT_TRUE(ContainsItem(FR, "FooBar"));
-  EXPECT_FALSE(ContainsItem(FR, "FooBaz"));
-  EXPECT_FALSE(ContainsItem(FR, "Qux"));
-
-  auto Op = Complete("S().opr");
-  EXPECT_TRUE(ContainsItem(Op, "operator="));
-
-  auto Aaa = Complete("aaa");
-  EXPECT_TRUE(ContainsItem(Aaa, "Abracadabra"));
-  EXPECT_TRUE(ContainsItem(Aaa, "Alakazam"));
-
-  auto UA = Complete("_a");
-  EXPECT_TRUE(ContainsItem(UA, "static_cast"));
-  EXPECT_FALSE(ContainsItem(UA, "Abracadabra"));
-}
-
-TEST_F(ClangdCompletionTest, CompletionOptions) {
-  MockFSProvider FS;
-  ErrorCheckingDiagConsumer DiagConsumer;
-  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
-  CDB.ExtraClangFlags.push_back("-xc++");
-
-  auto FooCpp = getVirtualTestFilePath("foo.cpp");
-  FS.Files[FooCpp] = "";
-  FS.ExpectedFile = FooCpp;
-
-  const auto GlobalCompletionSourceTemplate = R"cpp(
-#define MACRO X
-
-int global_var;
-int global_func();
-
-struct GlobalClass {};
-
-struct ClassWithMembers {
-  /// Doc for method.
-  int method();
-};
-
-int test() {
-  struct LocalClass {};
-
-  /// Doc for local_var.
-  int local_var;
-
-  {complete}
-}
-)cpp";
-  const auto MemberCompletionSourceTemplate = R"cpp(
-#define MACRO X
-
-int global_var;
-
-int global_func();
-
-struct GlobalClass {};
-
-struct ClassWithMembers {
-  /// Doc for method.
-  int method();
-
-  int field;
-private:
-  int private_field;
-};
-
-int test() {
-  struct LocalClass {};
-
-  /// Doc for local_var.
-  int local_var;
-
-  ClassWithMembers().{complete}
-}
-)cpp";
-
-  StringWithPos GlobalCompletion =
-      parseTextMarker(GlobalCompletionSourceTemplate, "complete");
-  StringWithPos MemberCompletion =
-      parseTextMarker(MemberCompletionSourceTemplate, "complete");
-
-  auto TestWithOpts = [&](clangd::CodeCompleteOptions Opts) {
-    ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
-                        /*StorePreamblesInMemory=*/true, Opts,
-                        EmptyLogger::getInstance());
-    // No need to sync reparses here as there are no asserts on diagnostics (or
-    // other async operations).
-    Server.addDocument(FooCpp, GlobalCompletion.Text);
-
-    StringRef MethodItemText = Opts.EnableSnippets ? "method()" : "method";
-    StringRef GlobalFuncItemText =
-        Opts.EnableSnippets ? "global_func()" : "global_func";
-
-    /// For after-dot completion we must always get consistent results.
-    {
-      auto Results = Server
-                         .codeComplete(FooCpp, MemberCompletion.MarkerPos,
-                                       StringRef(MemberCompletion.Text))
-                         .get()
-                         .Value;
-
-      // Class members. The only items that must be present in after-dor
-      // completion.
-      EXPECT_TRUE(ContainsItem(Results, MethodItemText));
-      EXPECT_TRUE(ContainsItem(Results, MethodItemText));
-      EXPECT_TRUE(ContainsItem(Results, "field"));
-      EXPECT_EQ(Opts.IncludeIneligibleResults,
-                ContainsItem(Results, "private_field"));
-      // Global items.
-      EXPECT_FALSE(ContainsItem(Results, "global_var"));
-      EXPECT_FALSE(ContainsItem(Results, GlobalFuncItemText));
-      EXPECT_FALSE(ContainsItem(Results, "GlobalClass"));
-      // A macro.
-      EXPECT_FALSE(ContainsItem(Results, "MACRO"));
-      // Local items.
-      EXPECT_FALSE(ContainsItem(Results, "LocalClass"));
-      // There should be no code patterns (aka snippets) in after-dot
-      // completion. At least there aren't any we're aware of.
-      EXPECT_FALSE(
-          ContainsItemPred(Results, [](clangd::CompletionItem const &Item) {
-            return Item.kind == clangd::CompletionItemKind::Snippet;
-          }));
-      // Check documentation.
-      EXPECT_EQ(
-          Opts.IncludeBriefComments,
-          ContainsItemPred(Results, [](clangd::CompletionItem const &Item) {
-            return !Item.documentation.empty();
-          }));
-    }
-    // Global completion differs based on the Opts that were passed.
-    {
-      auto Results = Server
-                         .codeComplete(FooCpp, GlobalCompletion.MarkerPos,
-                                       StringRef(GlobalCompletion.Text))
-                         .get()
-                         .Value;
-
-      // Class members. Should never be present in global completions.
-      EXPECT_FALSE(ContainsItem(Results, MethodItemText));
-      EXPECT_FALSE(ContainsItem(Results, "field"));
-      // Global items.
-      EXPECT_EQ(ContainsItem(Results, "global_var"), Opts.IncludeGlobals);
-      EXPECT_EQ(ContainsItem(Results, GlobalFuncItemText), 
Opts.IncludeGlobals);
-      EXPECT_EQ(ContainsItem(Results, "GlobalClass"), Opts.IncludeGlobals);
-      // A macro.
-      EXPECT_EQ(ContainsItem(Results, "MACRO"), Opts.IncludeMacros);
-      // Local items. Must be present always.
-      EXPECT_TRUE(ContainsItem(Results, "local_var"));
-      EXPECT_TRUE(ContainsItem(Results, "LocalClass"));
-      // FIXME(ibiryukov): snippets have wrong Item.kind now. Reenable this
-      // check after https://reviews.llvm.org/D38720 makes it in.
-      //
-      // Code patterns (aka snippets).
-      // EXPECT_EQ(
-      //     Opts.IncludeCodePatterns && Opts.EnableSnippets,
-      //     ContainsItemPred(Results, [](clangd::CompletionItem const &Item) {
-      //       return Item.kind == clangd::CompletionItemKind::Snippet;
-      //     }));
-
-      // Check documentation.
-      EXPECT_EQ(
-          Opts.IncludeBriefComments,
-          ContainsItemPred(Results, [](clangd::CompletionItem const &Item) {
-            return !Item.documentation.empty();
-          }));
-    }
-  };
-
-  clangd::CodeCompleteOptions CCOpts;
-  for (bool IncludeMacros : {true, false}){
-    CCOpts.IncludeMacros = IncludeMacros;
-    for (bool IncludeGlobals : {true, false}){
-      CCOpts.IncludeGlobals = IncludeGlobals;
-      for (bool IncludeBriefComments : {true, false}){
-        CCOpts.IncludeBriefComments = IncludeBriefComments;
-        for (bool EnableSnippets : {true, false}){
-          CCOpts.EnableSnippets = EnableSnippets;
-          for (bool IncludeCodePatterns : {true, false}) {
-            CCOpts.IncludeCodePatterns = IncludeCodePatterns;
-            for (bool IncludeIneligibleResults : {true, false}) {
-              CCOpts.IncludeIneligibleResults = IncludeIneligibleResults;
-              TestWithOpts(CCOpts);
-            }
-          }
-        }
-      }
-    }
-  }
-}
-
 class ClangdThreadingTest : public ClangdVFSTest {};
 
 TEST_F(ClangdThreadingTest, StressTest) {

Added: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
URL: 
http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp?rev=319741&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp (added)
+++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp Mon Dec  4 
23:20:26 2017
@@ -0,0 +1,400 @@
+//===-- CodeCompleteTests.cpp -----------------------------------*- C++ 
-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "ClangdServer.h"
+#include "Compiler.h"
+#include "Protocol.h"
+#include "TestFS.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+using namespace llvm;
+
+class IgnoreDiagnostics : public DiagnosticsConsumer {
+  void onDiagnosticsReady(
+      PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) override 
{}
+};
+
+struct StringWithPos {
+  std::string Text;
+  clangd::Position MarkerPos;
+};
+
+/// Returns location of "{mark}" substring in \p Text and removes it from \p
+/// Text. Note that \p Text must contain exactly one occurence of "{mark}".
+///
+/// Marker name can be configured using \p MarkerName parameter.
+StringWithPos parseTextMarker(StringRef Text, StringRef MarkerName = "mark") {
+  SmallString<16> Marker;
+  Twine("{" + MarkerName + "}").toVector(/*ref*/ Marker);
+
+  std::size_t MarkerOffset = Text.find(Marker);
+  assert(MarkerOffset != StringRef::npos && "{mark} wasn't found in Text.");
+
+  std::string WithoutMarker;
+  WithoutMarker += Text.take_front(MarkerOffset);
+  WithoutMarker += Text.drop_front(MarkerOffset + Marker.size());
+  assert(StringRef(WithoutMarker).find(Marker) == StringRef::npos &&
+         "There were multiple occurences of {mark} inside Text");
+
+  clangd::Position MarkerPos =
+      clangd::offsetToPosition(WithoutMarker, MarkerOffset);
+  return {std::move(WithoutMarker), MarkerPos};
+}
+
+class ClangdCompletionTest : public ::testing::Test {
+protected:
+  template <class Predicate>
+  bool ContainsItemPred(CompletionList const &Items, Predicate Pred) {
+    for (const auto &Item : Items.items) {
+      if (Pred(Item))
+        return true;
+    }
+    return false;
+  }
+
+  bool ContainsItem(CompletionList const &Items, StringRef Name) {
+    return ContainsItemPred(Items, [Name](clangd::CompletionItem Item) {
+      return Item.insertText == Name;
+    });
+    return false;
+  }
+};
+
+TEST_F(ClangdCompletionTest, CheckContentsOverride) {
+  MockFSProvider FS;
+  IgnoreDiagnostics DiagConsumer;
+  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+
+  ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
+                      /*StorePreamblesInMemory=*/true,
+                      clangd::CodeCompleteOptions(),
+                      EmptyLogger::getInstance());
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  const auto SourceContents = R"cpp(
+int aba;
+int b =   ;
+)cpp";
+
+  const auto OverridenSourceContents = R"cpp(
+int cbc;
+int b =   ;
+)cpp";
+  // Complete after '=' sign. We need to be careful to keep the SourceContents'
+  // size the same.
+  // We complete on the 3rd line (2nd in zero-based numbering), because raw
+  // string literal of the SourceContents starts with a newline(it's easy to
+  // miss).
+  Position CompletePos = {2, 8};
+  FS.Files[FooCpp] = SourceContents;
+  FS.ExpectedFile = FooCpp;
+
+  // No need to sync reparses here as there are no asserts on diagnostics (or
+  // other async operations).
+  Server.addDocument(FooCpp, SourceContents);
+
+  {
+    auto CodeCompletionResults1 =
+        Server.codeComplete(FooCpp, CompletePos, None).get().Value;
+    EXPECT_TRUE(ContainsItem(CodeCompletionResults1, "aba"));
+    EXPECT_FALSE(ContainsItem(CodeCompletionResults1, "cbc"));
+  }
+
+  {
+    auto CodeCompletionResultsOverriden =
+        Server
+            .codeComplete(FooCpp, CompletePos,
+                          StringRef(OverridenSourceContents))
+            .get()
+            .Value;
+    EXPECT_TRUE(ContainsItem(CodeCompletionResultsOverriden, "cbc"));
+    EXPECT_FALSE(ContainsItem(CodeCompletionResultsOverriden, "aba"));
+  }
+
+  {
+    auto CodeCompletionResults2 =
+        Server.codeComplete(FooCpp, CompletePos, None).get().Value;
+    EXPECT_TRUE(ContainsItem(CodeCompletionResults2, "aba"));
+    EXPECT_FALSE(ContainsItem(CodeCompletionResults2, "cbc"));
+  }
+}
+
+TEST_F(ClangdCompletionTest, Limit) {
+  MockFSProvider FS;
+  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+  CDB.ExtraClangFlags.push_back("-xc++");
+  IgnoreDiagnostics DiagConsumer;
+  clangd::CodeCompleteOptions Opts;
+  Opts.Limit = 2;
+  ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
+                      /*StorePreamblesInMemory=*/true, Opts,
+                      EmptyLogger::getInstance());
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  FS.Files[FooCpp] = "";
+  FS.ExpectedFile = FooCpp;
+  StringWithPos Completion = parseTextMarker(R"cpp(
+struct ClassWithMembers {
+  int AAA();
+  int BBB();
+  int CCC();
+}
+int main() { ClassWithMembers().{complete} }
+      )cpp",
+                                             "complete");
+  Server.addDocument(FooCpp, Completion.Text);
+
+  /// For after-dot completion we must always get consistent results.
+  auto Results = Server
+                     .codeComplete(FooCpp, Completion.MarkerPos,
+                                   StringRef(Completion.Text))
+                     .get()
+                     .Value;
+
+  EXPECT_TRUE(Results.isIncomplete);
+  EXPECT_EQ(Opts.Limit, Results.items.size());
+  EXPECT_TRUE(ContainsItem(Results, "AAA"));
+  EXPECT_TRUE(ContainsItem(Results, "BBB"));
+  EXPECT_FALSE(ContainsItem(Results, "CCC"));
+}
+
+TEST_F(ClangdCompletionTest, Filter) {
+  MockFSProvider FS;
+  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+  CDB.ExtraClangFlags.push_back("-xc++");
+  IgnoreDiagnostics DiagConsumer;
+  clangd::CodeCompleteOptions Opts;
+  ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
+                      /*StorePreamblesInMemory=*/true, Opts,
+                      EmptyLogger::getInstance());
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  FS.Files[FooCpp] = "";
+  FS.ExpectedFile = FooCpp;
+  const char *Body = R"cpp(
+    int Abracadabra;
+    int Alakazam;
+    struct S {
+      int FooBar;
+      int FooBaz;
+      int Qux;
+    };
+  )cpp";
+  auto Complete = [&](StringRef Query) {
+    StringWithPos Completion = parseTextMarker(
+        formatv("{0} int main() { {1}{{complete}} }", Body, Query).str(),
+        "complete");
+    Server.addDocument(FooCpp, Completion.Text);
+    return Server
+        .codeComplete(FooCpp, Completion.MarkerPos, StringRef(Completion.Text))
+        .get()
+        .Value;
+  };
+
+  auto Foba = Complete("S().Foba");
+  EXPECT_TRUE(ContainsItem(Foba, "FooBar"));
+  EXPECT_TRUE(ContainsItem(Foba, "FooBaz"));
+  EXPECT_FALSE(ContainsItem(Foba, "Qux"));
+
+  auto FR = Complete("S().FR");
+  EXPECT_TRUE(ContainsItem(FR, "FooBar"));
+  EXPECT_FALSE(ContainsItem(FR, "FooBaz"));
+  EXPECT_FALSE(ContainsItem(FR, "Qux"));
+
+  auto Op = Complete("S().opr");
+  EXPECT_TRUE(ContainsItem(Op, "operator="));
+
+  auto Aaa = Complete("aaa");
+  EXPECT_TRUE(ContainsItem(Aaa, "Abracadabra"));
+  EXPECT_TRUE(ContainsItem(Aaa, "Alakazam"));
+
+  auto UA = Complete("_a");
+  EXPECT_TRUE(ContainsItem(UA, "static_cast"));
+  EXPECT_FALSE(ContainsItem(UA, "Abracadabra"));
+}
+
+TEST_F(ClangdCompletionTest, CompletionOptions) {
+  MockFSProvider FS;
+  IgnoreDiagnostics DiagConsumer;
+  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+  CDB.ExtraClangFlags.push_back("-xc++");
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  FS.Files[FooCpp] = "";
+  FS.ExpectedFile = FooCpp;
+
+  const auto GlobalCompletionSourceTemplate = R"cpp(
+#define MACRO X
+
+int global_var;
+int global_func();
+
+struct GlobalClass {};
+
+struct ClassWithMembers {
+  /// Doc for method.
+  int method();
+};
+
+int test() {
+  struct LocalClass {};
+
+  /// Doc for local_var.
+  int local_var;
+
+  {complete}
+}
+)cpp";
+  const auto MemberCompletionSourceTemplate = R"cpp(
+#define MACRO X
+
+int global_var;
+
+int global_func();
+
+struct GlobalClass {};
+
+struct ClassWithMembers {
+  /// Doc for method.
+  int method();
+
+  int field;
+private:
+  int private_field;
+};
+
+int test() {
+  struct LocalClass {};
+
+  /// Doc for local_var.
+  int local_var;
+
+  ClassWithMembers().{complete}
+}
+)cpp";
+
+  StringWithPos GlobalCompletion =
+      parseTextMarker(GlobalCompletionSourceTemplate, "complete");
+  StringWithPos MemberCompletion =
+      parseTextMarker(MemberCompletionSourceTemplate, "complete");
+
+  auto TestWithOpts = [&](clangd::CodeCompleteOptions Opts) {
+    ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
+                        /*StorePreamblesInMemory=*/true, Opts,
+                        EmptyLogger::getInstance());
+    // No need to sync reparses here as there are no asserts on diagnostics (or
+    // other async operations).
+    Server.addDocument(FooCpp, GlobalCompletion.Text);
+
+    StringRef MethodItemText = Opts.EnableSnippets ? "method()" : "method";
+    StringRef GlobalFuncItemText =
+        Opts.EnableSnippets ? "global_func()" : "global_func";
+
+    /// For after-dot completion we must always get consistent results.
+    {
+      auto Results = Server
+                         .codeComplete(FooCpp, MemberCompletion.MarkerPos,
+                                       StringRef(MemberCompletion.Text))
+                         .get()
+                         .Value;
+
+      // Class members. The only items that must be present in after-dor
+      // completion.
+      EXPECT_TRUE(ContainsItem(Results, MethodItemText));
+      EXPECT_TRUE(ContainsItem(Results, MethodItemText));
+      EXPECT_TRUE(ContainsItem(Results, "field"));
+      EXPECT_EQ(Opts.IncludeIneligibleResults,
+                ContainsItem(Results, "private_field"));
+      // Global items.
+      EXPECT_FALSE(ContainsItem(Results, "global_var"));
+      EXPECT_FALSE(ContainsItem(Results, GlobalFuncItemText));
+      EXPECT_FALSE(ContainsItem(Results, "GlobalClass"));
+      // A macro.
+      EXPECT_FALSE(ContainsItem(Results, "MACRO"));
+      // Local items.
+      EXPECT_FALSE(ContainsItem(Results, "LocalClass"));
+      // There should be no code patterns (aka snippets) in after-dot
+      // completion. At least there aren't any we're aware of.
+      EXPECT_FALSE(
+          ContainsItemPred(Results, [](clangd::CompletionItem const &Item) {
+            return Item.kind == clangd::CompletionItemKind::Snippet;
+          }));
+      // Check documentation.
+      EXPECT_EQ(
+          Opts.IncludeBriefComments,
+          ContainsItemPred(Results, [](clangd::CompletionItem const &Item) {
+            return !Item.documentation.empty();
+          }));
+    }
+    // Global completion differs based on the Opts that were passed.
+    {
+      auto Results = Server
+                         .codeComplete(FooCpp, GlobalCompletion.MarkerPos,
+                                       StringRef(GlobalCompletion.Text))
+                         .get()
+                         .Value;
+
+      // Class members. Should never be present in global completions.
+      EXPECT_FALSE(ContainsItem(Results, MethodItemText));
+      EXPECT_FALSE(ContainsItem(Results, "field"));
+      // Global items.
+      EXPECT_EQ(ContainsItem(Results, "global_var"), Opts.IncludeGlobals);
+      EXPECT_EQ(ContainsItem(Results, GlobalFuncItemText), 
Opts.IncludeGlobals);
+      EXPECT_EQ(ContainsItem(Results, "GlobalClass"), Opts.IncludeGlobals);
+      // A macro.
+      EXPECT_EQ(ContainsItem(Results, "MACRO"), Opts.IncludeMacros);
+      // Local items. Must be present always.
+      EXPECT_TRUE(ContainsItem(Results, "local_var"));
+      EXPECT_TRUE(ContainsItem(Results, "LocalClass"));
+      // FIXME(ibiryukov): snippets have wrong Item.kind now. Reenable this
+      // check after https://reviews.llvm.org/D38720 makes it in.
+      //
+      // Code patterns (aka snippets).
+      // EXPECT_EQ(
+      //     Opts.IncludeCodePatterns && Opts.EnableSnippets,
+      //     ContainsItemPred(Results, [](clangd::CompletionItem const &Item) {
+      //       return Item.kind == clangd::CompletionItemKind::Snippet;
+      //     }));
+
+      // Check documentation.
+      EXPECT_EQ(
+          Opts.IncludeBriefComments,
+          ContainsItemPred(Results, [](clangd::CompletionItem const &Item) {
+            return !Item.documentation.empty();
+          }));
+    }
+  };
+
+  clangd::CodeCompleteOptions CCOpts;
+  for (bool IncludeMacros : {true, false}) {
+    CCOpts.IncludeMacros = IncludeMacros;
+    for (bool IncludeGlobals : {true, false}) {
+      CCOpts.IncludeGlobals = IncludeGlobals;
+      for (bool IncludeBriefComments : {true, false}) {
+        CCOpts.IncludeBriefComments = IncludeBriefComments;
+        for (bool EnableSnippets : {true, false}) {
+          CCOpts.EnableSnippets = EnableSnippets;
+          for (bool IncludeCodePatterns : {true, false}) {
+            CCOpts.IncludeCodePatterns = IncludeCodePatterns;
+            for (bool IncludeIneligibleResults : {true, false}) {
+              CCOpts.IncludeIneligibleResults = IncludeIneligibleResults;
+              TestWithOpts(CCOpts);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang

Added: clang-tools-extra/trunk/unittests/clangd/TestFS.cpp
URL: 
http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/TestFS.cpp?rev=319741&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/TestFS.cpp (added)
+++ clang-tools-extra/trunk/unittests/clangd/TestFS.cpp Mon Dec  4 23:20:26 2017
@@ -0,0 +1,176 @@
+//===-- TestFS.cpp ----------------------------------------------*- C++ 
-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "TestFS.h"
+#include "llvm/Support/Errc.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+/// An implementation of vfs::FileSystem that only allows access to
+/// files and folders inside a set of whitelisted directories.
+///
+/// FIXME(ibiryukov): should it also emulate access to parents of whitelisted
+/// directories with only whitelisted contents?
+class FilteredFileSystem : public vfs::FileSystem {
+public:
+  /// The paths inside \p WhitelistedDirs should be absolute
+  FilteredFileSystem(std::vector<std::string> WhitelistedDirs,
+                     IntrusiveRefCntPtr<vfs::FileSystem> InnerFS)
+      : WhitelistedDirs(std::move(WhitelistedDirs)), InnerFS(InnerFS) {
+    assert(std::all_of(WhitelistedDirs.begin(), WhitelistedDirs.end(),
+                       [](const std::string &Path) -> bool {
+                         return llvm::sys::path::is_absolute(Path);
+                       }) &&
+           "Not all WhitelistedDirs are absolute");
+  }
+
+  virtual llvm::ErrorOr<vfs::Status> status(const Twine &Path) {
+    if (!isInsideWhitelistedDir(Path))
+      return llvm::errc::no_such_file_or_directory;
+    return InnerFS->status(Path);
+  }
+
+  virtual llvm::ErrorOr<std::unique_ptr<vfs::File>>
+  openFileForRead(const Twine &Path) {
+    if (!isInsideWhitelistedDir(Path))
+      return llvm::errc::no_such_file_or_directory;
+    return InnerFS->openFileForRead(Path);
+  }
+
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+  getBufferForFile(const Twine &Name, int64_t FileSize = -1,
+                   bool RequiresNullTerminator = true,
+                   bool IsVolatile = false) {
+    if (!isInsideWhitelistedDir(Name))
+      return llvm::errc::no_such_file_or_directory;
+    return InnerFS->getBufferForFile(Name, FileSize, RequiresNullTerminator,
+                                     IsVolatile);
+  }
+
+  virtual vfs::directory_iterator dir_begin(const Twine &Dir,
+                                            std::error_code &EC) {
+    if (!isInsideWhitelistedDir(Dir)) {
+      EC = llvm::errc::no_such_file_or_directory;
+      return vfs::directory_iterator();
+    }
+    return InnerFS->dir_begin(Dir, EC);
+  }
+
+  virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) {
+    return InnerFS->setCurrentWorkingDirectory(Path);
+  }
+
+  virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const {
+    return InnerFS->getCurrentWorkingDirectory();
+  }
+
+  bool exists(const Twine &Path) {
+    if (!isInsideWhitelistedDir(Path))
+      return false;
+    return InnerFS->exists(Path);
+  }
+
+  std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const {
+    return InnerFS->makeAbsolute(Path);
+  }
+
+private:
+  bool isInsideWhitelistedDir(const Twine &InputPath) const {
+    SmallString<128> Path;
+    InputPath.toVector(Path);
+
+    if (makeAbsolute(Path))
+      return false;
+
+    for (const auto &Dir : WhitelistedDirs) {
+      if (Path.startswith(Dir))
+        return true;
+    }
+    return false;
+  }
+
+  std::vector<std::string> WhitelistedDirs;
+  IntrusiveRefCntPtr<vfs::FileSystem> InnerFS;
+};
+
+/// Create a vfs::FileSystem that has access only to temporary directories
+/// (obtained by calling system_temp_directory).
+IntrusiveRefCntPtr<vfs::FileSystem> getTempOnlyFS() {
+  llvm::SmallString<128> TmpDir1;
+  llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/false, TmpDir1);
+  llvm::SmallString<128> TmpDir2;
+  llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/true, TmpDir2);
+
+  std::vector<std::string> TmpDirs;
+  TmpDirs.push_back(TmpDir1.str());
+  if (TmpDir1 != TmpDir2)
+    TmpDirs.push_back(TmpDir2.str());
+  return new FilteredFileSystem(std::move(TmpDirs), vfs::getRealFileSystem());
+}
+
+} // namespace
+
+IntrusiveRefCntPtr<vfs::FileSystem>
+buildTestFS(llvm::StringMap<std::string> const &Files) {
+  IntrusiveRefCntPtr<vfs::InMemoryFileSystem> MemFS(
+      new vfs::InMemoryFileSystem);
+  for (auto &FileAndContents : Files)
+    MemFS->addFile(FileAndContents.first(), time_t(),
+                   llvm::MemoryBuffer::getMemBuffer(FileAndContents.second,
+                                                    FileAndContents.first()));
+
+  auto OverlayFS = IntrusiveRefCntPtr<vfs::OverlayFileSystem>(
+      new vfs::OverlayFileSystem(getTempOnlyFS()));
+  OverlayFS->pushOverlay(std::move(MemFS));
+  return OverlayFS;
+}
+
+Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
+MockFSProvider::getTaggedFileSystem(PathRef File) {
+  if (ExpectedFile) {
+    EXPECT_EQ(*ExpectedFile, File);
+  }
+
+  auto FS = buildTestFS(Files);
+  return make_tagged(FS, Tag);
+}
+
+llvm::Optional<tooling::CompileCommand>
+MockCompilationDatabase::getCompileCommand(PathRef File) const {
+  if (ExtraClangFlags.empty())
+    return llvm::None;
+
+  auto CommandLine = ExtraClangFlags;
+  CommandLine.insert(CommandLine.begin(), "clang");
+  CommandLine.insert(CommandLine.end(), File.str());
+  return {tooling::CompileCommand(llvm::sys::path::parent_path(File),
+                                  llvm::sys::path::filename(File),
+                                  std::move(CommandLine), "")};
+}
+
+static const char *getVirtualTestRoot() {
+#ifdef LLVM_ON_WIN32
+  return "C:\\clangd-test";
+#else
+  return "/clangd-test";
+#endif
+}
+
+llvm::SmallString<32> getVirtualTestFilePath(PathRef File) {
+  assert(llvm::sys::path::is_relative(File) && "FileName should be relative");
+
+  llvm::SmallString<32> Path;
+  llvm::sys::path::append(Path, getVirtualTestRoot(), File);
+  return Path;
+}
+
+} // namespace clangd
+} // namespace clang

Added: clang-tools-extra/trunk/unittests/clangd/TestFS.h
URL: 
http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/TestFS.h?rev=319741&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/TestFS.h (added)
+++ clang-tools-extra/trunk/unittests/clangd/TestFS.h Mon Dec  4 23:20:26 2017
@@ -0,0 +1,57 @@
+//===-- TestFS.h ------------------------------------------------*- C++ 
-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Allows setting up fake filesystem environments for tests.
+//
+//===----------------------------------------------------------------------===//
+#include "ClangdServer.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace clangd {
+
+// Builds a VFS that provides access to the provided files, plus temporary
+// directories.
+llvm::IntrusiveRefCntPtr<vfs::FileSystem>
+buildTestFS(llvm::StringMap<std::string> const &Files);
+
+// A VFS provider that returns TestFSes containing a provided set of files.
+class MockFSProvider : public FileSystemProvider {
+public:
+  Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
+  getTaggedFileSystem(PathRef File) override;
+
+  llvm::Optional<SmallString<32>> ExpectedFile;
+  llvm::StringMap<std::string> Files;
+  VFSTag Tag = VFSTag();
+};
+
+// A Compilation database that returns a fixed set of compile flags.
+class MockCompilationDatabase : public GlobalCompilationDatabase {
+public:
+  MockCompilationDatabase(bool AddFreestandingFlag) {
+    // We have to add -ffreestanding to VFS-specific tests to avoid errors on
+    // implicit includes of stdc-predef.h.
+    if (AddFreestandingFlag)
+      ExtraClangFlags.push_back("-ffreestanding");
+  }
+
+  llvm::Optional<tooling::CompileCommand>
+  getCompileCommand(PathRef File) const override;
+
+  std::vector<std::string> ExtraClangFlags;
+};
+
+// Returns a suitable absolute path for this OS.
+llvm::SmallString<32> getVirtualTestFilePath(PathRef File);
+
+} // namespace clangd
+} // namespace clang


_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to