[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