[clang-tools-extra] r319820 - [clangd] Clean up code complete unit tests. NFC

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Tue Dec 5 12:11:29 PST 2017


Author: sammccall
Date: Tue Dec  5 12:11:29 2017
New Revision: 319820

URL: http://llvm.org/viewvc/llvm-project?rev=319820&view=rev
Log:
[clangd] Clean up code complete unit tests. NFC

Modified:
    clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp

Modified: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp?rev=319820&r1=319819&r2=319820&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp Tue Dec  5 12:11:29 2017
@@ -10,12 +10,24 @@
 #include "Compiler.h"
 #include "Protocol.h"
 #include "TestFS.h"
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 namespace clang {
 namespace clangd {
+// Let GMock print completion items.
+void PrintTo(const CompletionItem &I, std::ostream *O) {
+  llvm::raw_os_ostream OS(*O);
+  OS << toJSON(I);
+}
+
 namespace {
 using namespace llvm;
+using ::testing::AllOf;
+using ::testing::Contains;
+using ::testing::ElementsAre;
+using ::testing::Matcher;
+using ::testing::Not;
 
 class IgnoreDiagnostics : public DiagnosticsConsumer {
   void onDiagnosticsReady(
@@ -27,161 +39,74 @@ struct StringWithPos {
   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.");
+/// Accepts a source file with a cursor marker ^.
+/// Returns the source file with the marker removed, and the marker position.
+StringWithPos parseTextMarker(StringRef Text) {
+  std::size_t MarkerOffset = Text.find('^');
+  assert(MarkerOffset != StringRef::npos && "^ 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");
+  WithoutMarker += Text.drop_front(MarkerOffset + 1);
+  assert(StringRef(WithoutMarker).find('^') == StringRef::npos &&
+         "There were multiple occurences of ^ inside Text");
 
-  clangd::Position MarkerPos =
-      clangd::offsetToPosition(WithoutMarker, MarkerOffset);
+  auto MarkerPos = 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;
-
-  ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
-                      /*StorePreamblesInMemory=*/true,
-                      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";
-
-  // Use default options.
-  CodeCompleteOptions CCOpts;
-  // 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, CCOpts, None).get().Value;
-    EXPECT_TRUE(ContainsItem(CodeCompletionResults1, "aba"));
-    EXPECT_FALSE(ContainsItem(CodeCompletionResults1, "cbc"));
-  }
-
-  {
-    auto CodeCompletionResultsOverriden =
-        Server
-            .codeComplete(FooCpp, CompletePos, CCOpts,
-                          StringRef(OverridenSourceContents))
-            .get()
-            .Value;
-    EXPECT_TRUE(ContainsItem(CodeCompletionResultsOverriden, "cbc"));
-    EXPECT_FALSE(ContainsItem(CodeCompletionResultsOverriden, "aba"));
-  }
-
-  {
-    auto CodeCompletionResults2 =
-        Server.codeComplete(FooCpp, CompletePos, CCOpts, None).get().Value;
-    EXPECT_TRUE(ContainsItem(CodeCompletionResults2, "aba"));
-    EXPECT_FALSE(ContainsItem(CodeCompletionResults2, "cbc"));
-  }
+// GMock helpers for matching completion items.
+MATCHER_P(Named, Name, "") { return arg.insertText == Name; }
+// Shorthand for Contains(Named(Name)).
+Matcher<const std::vector<CompletionItem> &> Has(std::string Name) {
+  return Contains(Named(std::move(Name)));
+}
+MATCHER(IsDocumented, "") { return !arg.documentation.empty(); }
+MATCHER(IsSnippet, "") {
+  return arg.kind == clangd::CompletionItemKind::Snippet;
 }
+// This is hard to write as a function, because matchers may be polymorphic.
+#define EXPECT_IFF(condition, value, matcher)                                  \
+  do {                                                                         \
+    if (condition)                                                             \
+      EXPECT_THAT(value, matcher);                                             \
+    else                                                                       \
+      EXPECT_THAT(value, ::testing::Not(matcher));                             \
+  } while (0)
 
-TEST_F(ClangdCompletionTest, Limit) {
+CompletionList completions(StringRef Text,
+                           clangd::CodeCompleteOptions Opts = {}) {
   MockFSProvider FS;
   MockCompilationDatabase CDB;
-  CDB.ExtraClangFlags.push_back("-xc++");
   IgnoreDiagnostics DiagConsumer;
   ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
                       /*StorePreamblesInMemory=*/true,
                       EmptyLogger::getInstance());
+  auto File = getVirtualTestFilePath("foo.cpp");
+  auto Test = parseTextMarker(Text);
+  Server.addDocument(File, Test.Text);
+  return Server.codeComplete(File, Test.MarkerPos, Opts).get().Value;
+}
 
-  auto FooCpp = getVirtualTestFilePath("foo.cpp");
-  FS.Files[FooCpp] = "";
-  FS.ExpectedFile = FooCpp;
-  StringWithPos Completion = parseTextMarker(R"cpp(
+TEST(CompletionTest, Limit) {
+  clangd::CodeCompleteOptions Opts;
+  Opts.Limit = 2;
+  auto Results = completions(R"cpp(
 struct ClassWithMembers {
   int AAA();
   int BBB();
   int CCC();
 }
-int main() { ClassWithMembers().{complete} }
+int main() { ClassWithMembers().^ }
       )cpp",
-                                             "complete");
-  Server.addDocument(FooCpp, Completion.Text);
-
-  clangd::CodeCompleteOptions Opts;
-  Opts.Limit = 2;
-
-  /// For after-dot completion we must always get consistent results.
-  auto Results = Server
-                     .codeComplete(FooCpp, Completion.MarkerPos, Opts,
-                                   StringRef(Completion.Text))
-                     .get()
-                     .Value;
+                             Opts);
 
   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"));
+  EXPECT_THAT(Results.items, ElementsAre(Named("AAA"), Named("BBB")));
 }
 
-TEST_F(ClangdCompletionTest, Filter) {
-  MockFSProvider FS;
-  MockCompilationDatabase CDB;
-  CDB.ExtraClangFlags.push_back("-xc++");
-  IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
-                      /*StorePreamblesInMemory=*/true,
-                      EmptyLogger::getInstance());
-
-  auto FooCpp = getVirtualTestFilePath("foo.cpp");
-  FS.Files[FooCpp] = "";
-  FS.ExpectedFile = FooCpp;
-  const char *Body = R"cpp(
+TEST(CompletionTest, Filter) {
+  std::string Body = R"cpp(
     int Abracadabra;
     int Alakazam;
     struct S {
@@ -190,54 +115,29 @@ TEST_F(ClangdCompletionTest, Filter) {
       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,
-                      clangd::CodeCompleteOptions(), 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;
-  CDB.ExtraClangFlags.push_back("-xc++");
+  EXPECT_THAT(completions(Body + "int main() { S().Foba^ }").items,
+              AllOf(Has("FooBar"), Has("FooBaz"), Not(Has("Qux"))));
 
-  auto FooCpp = getVirtualTestFilePath("foo.cpp");
-  FS.Files[FooCpp] = "";
-  FS.ExpectedFile = FooCpp;
+  EXPECT_THAT(completions(Body + "int main() { S().FR^ }").items,
+              AllOf(Has("FooBar"), Not(Has("FooBaz")), Not(Has("Qux"))));
+
+  EXPECT_THAT(completions(Body + "int main() { S().opr^ }").items,
+              Has("operator="));
+
+  EXPECT_THAT(completions(Body + "int main() { aaa^ }").items,
+              AllOf(Has("Abracadabra"), Has("Alakazam")));
+
+  EXPECT_THAT(completions(Body + "int main() { _a^ }").items,
+              AllOf(Has("static_cast"), Not(Has("Abracadabra"))));
+}
 
-  const auto GlobalCompletionSourceTemplate = R"cpp(
+void TestAfterDotCompletion(clangd::CodeCompleteOptions Opts) {
+  auto Results = completions(R"cpp(
 #define MACRO X
 
 int global_var;
+
 int global_func();
 
 struct GlobalClass {};
@@ -245,6 +145,10 @@ struct GlobalClass {};
 struct ClassWithMembers {
   /// Doc for method.
   int method();
+
+  int field;
+private:
+  int private_field;
 };
 
 int test() {
@@ -253,14 +157,33 @@ int test() {
   /// Doc for local_var.
   int local_var;
 
-  {complete}
+  ClassWithMembers().^
 }
-)cpp";
-  const auto MemberCompletionSourceTemplate = R"cpp(
+)cpp",
+                             Opts)
+                     .items;
+
+  // Class members. The only items that must be present in after-dot
+  // completion.
+  EXPECT_THAT(Results, AllOf(Has(Opts.EnableSnippets ? "method()" : "method"),
+                             Has("field")));
+  EXPECT_IFF(Opts.IncludeIneligibleResults, Results, Has("private_field"));
+  // Global items.
+  EXPECT_THAT(Results, Not(AnyOf(Has("global_var"), Has("global_func"),
+                                 Has("global_func()"), Has("GlobalClass"),
+                                 Has("MACRO"), Has("LocalClass"))));
+  // There should be no code patterns (aka snippets) in after-dot
+  // completion. At least there aren't any we're aware of.
+  EXPECT_THAT(Results, Not(Contains(IsSnippet())));
+  // Check documentation.
+  EXPECT_IFF(Opts.IncludeBriefComments, Results, Contains(IsDocumented()));
+}
+
+void TestGlobalScopeCompletion(clangd::CodeCompleteOptions Opts) {
+  auto Results = completions(R"cpp(
 #define MACRO X
 
 int global_var;
-
 int global_func();
 
 struct GlobalClass {};
@@ -268,10 +191,6 @@ struct GlobalClass {};
 struct ClassWithMembers {
   /// Doc for method.
   int method();
-
-  int field;
-private:
-  int private_field;
 };
 
 int test() {
@@ -280,116 +199,45 @@ int test() {
   /// Doc for local_var.
   int local_var;
 
-  ClassWithMembers().{complete}
+  ^
+}
+)cpp",
+                             Opts)
+                     .items;
+
+  // Class members. Should never be present in global completions.
+  EXPECT_THAT(Results,
+              Not(AnyOf(Has("method"), Has("method()"), Has("field"))));
+  // Global items.
+  EXPECT_IFF(Opts.IncludeGlobals, Results,
+             AllOf(Has("global_var"),
+                   Has(Opts.EnableSnippets ? "global_func()" : "global_func"),
+                   Has("GlobalClass")));
+  // A macro.
+  EXPECT_IFF(Opts.IncludeMacros, Results, Has("MACRO"));
+  // Local items. Must be present always.
+  EXPECT_THAT(Results, AllOf(Has("local_var"), Has("LocalClass"),
+                             Contains(IsSnippet())));
+  // Check documentation.
+  EXPECT_IFF(Opts.IncludeBriefComments, Results, Contains(IsDocumented()));
 }
-)cpp";
-
-  StringWithPos GlobalCompletion =
-      parseTextMarker(GlobalCompletionSourceTemplate, "complete");
-  StringWithPos MemberCompletion =
-      parseTextMarker(MemberCompletionSourceTemplate, "complete");
-
-  auto TestWithOpts = [&](clangd::CodeCompleteOptions Opts) {
-    ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
-                        /*StorePreamblesInMemory=*/true,
-                        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, Opts,
-                                       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, Opts,
-                                       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;
+TEST(CompletionTest, CompletionOptions) {
+  clangd::CodeCompleteOptions Opts;
   for (bool IncludeMacros : {true, false}) {
-    CCOpts.IncludeMacros = IncludeMacros;
+    Opts.IncludeMacros = IncludeMacros;
     for (bool IncludeGlobals : {true, false}) {
-      CCOpts.IncludeGlobals = IncludeGlobals;
+      Opts.IncludeGlobals = IncludeGlobals;
       for (bool IncludeBriefComments : {true, false}) {
-        CCOpts.IncludeBriefComments = IncludeBriefComments;
+        Opts.IncludeBriefComments = IncludeBriefComments;
         for (bool EnableSnippets : {true, false}) {
-          CCOpts.EnableSnippets = EnableSnippets;
+          Opts.EnableSnippets = EnableSnippets;
           for (bool IncludeCodePatterns : {true, false}) {
-            CCOpts.IncludeCodePatterns = IncludeCodePatterns;
+            Opts.IncludeCodePatterns = IncludeCodePatterns;
             for (bool IncludeIneligibleResults : {true, false}) {
-              CCOpts.IncludeIneligibleResults = IncludeIneligibleResults;
-              TestWithOpts(CCOpts);
+              Opts.IncludeIneligibleResults = IncludeIneligibleResults;
+              TestAfterDotCompletion(Opts);
+              TestGlobalScopeCompletion(Opts);
             }
           }
         }
@@ -398,6 +246,27 @@ int test() {
   }
 }
 
+// Check code completion works when the file contents are overridden.
+TEST(CompletionTest, CheckContentsOverride) {
+  MockFSProvider FS;
+  IgnoreDiagnostics DiagConsumer;
+  MockCompilationDatabase CDB;
+  ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
+                      /*StorePreamblesInMemory=*/true,
+                      EmptyLogger::getInstance());
+  auto File = getVirtualTestFilePath("foo.cpp");
+  Server.addDocument(File, "ignored text!");
+
+  auto Example = parseTextMarker("int cbc; int b = ^;");
+  auto Results =
+      Server
+          .codeComplete(File, Example.MarkerPos, clangd::CodeCompleteOptions(),
+                        StringRef(Example.Text))
+          .get()
+          .Value;
+  EXPECT_THAT(Results.items, Contains(Named("cbc")));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang




More information about the cfe-commits mailing list