summaryrefslogtreecommitdiffstats
path: root/unittests
diff options
context:
space:
mode:
authorSam McCall <sam.mccall@gmail.com>2017-12-08 15:00:59 +0000
committerSam McCall <sam.mccall@gmail.com>2017-12-08 15:00:59 +0000
commite0c4412107cdfb3595eed87683984d3f1a6bf841 (patch)
tree5badc91dc8c07dd755ce4dfd73f9e42a03af51ed /unittests
parent89827634e1c90ed38380cbb2d1d53b69f7a4736d (diff)
[clangd] Convert lit code completion tests to unit-tests. NFC
Summary: This improves readability of tests and error messages. Reviewers: ioeric Subscribers: klimek, ilya-biryukov, cfe-commits Differential Revision: https://reviews.llvm.org/D40952 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@320148 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'unittests')
-rw-r--r--unittests/clangd/CodeCompleteTests.cpp232
-rw-r--r--unittests/clangd/Matchers.h112
2 files changed, 278 insertions, 66 deletions
diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp
index 299bffa4..5c55acd1 100644
--- a/unittests/clangd/CodeCompleteTests.cpp
+++ b/unittests/clangd/CodeCompleteTests.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "ClangdServer.h"
#include "Compiler.h"
+#include "Matchers.h"
#include "Protocol.h"
#include "TestFS.h"
#include "gmock/gmock.h"
@@ -18,7 +19,16 @@ namespace clangd {
// Let GMock print completion items.
void PrintTo(const CompletionItem &I, std::ostream *O) {
llvm::raw_os_ostream OS(*O);
- OS << toJSON(I);
+ OS << I.label << " - " << toJSON(I);
+}
+void PrintTo(const std::vector<CompletionItem> &V, std::ostream *O) {
+ *O << "{\n";
+ for (const auto &I : V) {
+ *O << "\t";
+ PrintTo(I, O);
+ *O << "\n";
+ }
+ *O << "}";
}
namespace {
@@ -26,7 +36,6 @@ using namespace llvm;
using ::testing::AllOf;
using ::testing::Contains;
using ::testing::ElementsAre;
-using ::testing::Matcher;
using ::testing::Not;
class IgnoreDiagnostics : public DiagnosticsConsumer {
@@ -57,22 +66,25 @@ StringWithPos parseTextMarker(StringRef Text) {
// GMock helpers for matching completion items.
MATCHER_P(Named, Name, "") { return arg.insertText == Name; }
+MATCHER_P(Labeled, Label, "") { return arg.label == Label; }
+MATCHER_P(Kind, K, "") { return arg.kind == K; }
+MATCHER_P(PlainText, Text, "") {
+ return arg.insertTextFormat == clangd::InsertTextFormat::PlainText &&
+ arg.insertText == Text;
+}
+MATCHER_P(Snippet, Text, "") {
+ return arg.insertTextFormat == clangd::InsertTextFormat::Snippet &&
+ arg.insertText == Text;
+}
// 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;
+Matcher<const std::vector<CompletionItem> &> Has(std::string Name,
+ CompletionItemKind K) {
+ return Contains(AllOf(Named(std::move(Name)), Kind(K)));
}
-// 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)
+MATCHER(IsDocumented, "") { return !arg.documentation.empty(); }
CompletionList completions(StringRef Text,
clangd::CodeCompleteOptions Opts = {}) {
@@ -133,93 +145,97 @@ TEST(CompletionTest, Filter) {
}
void TestAfterDotCompletion(clangd::CodeCompleteOptions Opts) {
- auto Results = completions(R"cpp(
-#define MACRO X
+ auto Results = completions(
+ R"cpp(
+ #define MACRO X
-int global_var;
+ int global_var;
-int global_func();
+ int global_func();
-struct GlobalClass {};
+ struct GlobalClass {};
-struct ClassWithMembers {
- /// Doc for method.
- int method();
+ struct ClassWithMembers {
+ /// Doc for method.
+ int method();
- int field;
-private:
- int private_field;
-};
+ int field;
+ private:
+ int private_field;
+ };
-int test() {
- struct LocalClass {};
+ int test() {
+ struct LocalClass {};
- /// Doc for local_var.
- int local_var;
+ /// Doc for local_var.
+ int local_var;
- ClassWithMembers().^
-}
-)cpp",
- Opts)
- .items;
+ ClassWithMembers().^
+ }
+ )cpp",
+ Opts);
// 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"));
+ EXPECT_THAT(
+ Results.items,
+ AllOf(Has(Opts.EnableSnippets ? "method()" : "method"), Has("field")));
+ EXPECT_IFF(Opts.IncludeIneligibleResults, Results.items,
+ 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"))));
+ EXPECT_THAT(Results.items, 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())));
+ EXPECT_THAT(Results.items, Not(Contains(Kind(CompletionItemKind::Snippet))));
// Check documentation.
- EXPECT_IFF(Opts.IncludeBriefComments, Results, Contains(IsDocumented()));
+ EXPECT_IFF(Opts.IncludeBriefComments, Results.items,
+ Contains(IsDocumented()));
}
void TestGlobalScopeCompletion(clangd::CodeCompleteOptions Opts) {
- auto Results = completions(R"cpp(
-#define MACRO X
+ auto Results = completions(
+ R"cpp(
+ #define MACRO X
-int global_var;
-int global_func();
+ int global_var;
+ int global_func();
-struct GlobalClass {};
+ struct GlobalClass {};
-struct ClassWithMembers {
- /// Doc for method.
- int method();
-};
+ struct ClassWithMembers {
+ /// Doc for method.
+ int method();
+ };
-int test() {
- struct LocalClass {};
+ int test() {
+ struct LocalClass {};
- /// Doc for local_var.
- int local_var;
+ /// Doc for local_var.
+ int local_var;
- ^
-}
-)cpp",
- Opts)
- .items;
+ ^
+ }
+ )cpp",
+ Opts);
// Class members. Should never be present in global completions.
- EXPECT_THAT(Results,
+ EXPECT_THAT(Results.items,
Not(AnyOf(Has("method"), Has("method()"), Has("field"))));
// Global items.
- EXPECT_IFF(Opts.IncludeGlobals, Results,
+ EXPECT_IFF(Opts.IncludeGlobals, Results.items,
AllOf(Has("global_var"),
Has(Opts.EnableSnippets ? "global_func()" : "global_func"),
Has("GlobalClass")));
// A macro.
- EXPECT_IFF(Opts.IncludeMacros, Results, Has("MACRO"));
+ EXPECT_IFF(Opts.IncludeMacros, Results.items, Has("MACRO"));
// Local items. Must be present always.
- EXPECT_THAT(Results, AllOf(Has("local_var"), Has("LocalClass"),
- Contains(IsSnippet())));
+ EXPECT_THAT(Results.items, AllOf(Has("local_var"), Has("LocalClass"),
+ Contains(Kind(CompletionItemKind::Snippet))));
// Check documentation.
- EXPECT_IFF(Opts.IncludeBriefComments, Results, Contains(IsDocumented()));
+ EXPECT_IFF(Opts.IncludeBriefComments, Results.items,
+ Contains(IsDocumented()));
}
TEST(CompletionTest, CompletionOptions) {
@@ -267,6 +283,90 @@ TEST(CompletionTest, CheckContentsOverride) {
EXPECT_THAT(Results.items, Contains(Named("cbc")));
}
+TEST(CompletionTest, Priorities) {
+ auto Internal = completions(R"cpp(
+ class Foo {
+ public: void pub();
+ protected: void prot();
+ private: void priv();
+ };
+ void Foo::pub() { this->^ }
+ )cpp");
+ EXPECT_THAT(Internal.items,
+ HasSubsequence(Named("priv"), Named("prot"), Named("pub")));
+
+ auto External = completions(R"cpp(
+ class Foo {
+ public: void pub();
+ protected: void prot();
+ private: void priv();
+ };
+ void test() {
+ Foo F;
+ F.^
+ }
+ )cpp");
+ EXPECT_THAT(External.items,
+ AllOf(Has("pub"), Not(Has("prot")), Not(Has("priv"))));
+}
+
+TEST(CompletionTest, Qualifiers) {
+ auto Results = completions(R"cpp(
+ class Foo {
+ public: int foo() const;
+ int bar() const;
+ };
+ class Bar : public Foo {
+ int foo() const;
+ };
+ void test() { Bar().^ }
+ )cpp");
+ EXPECT_THAT(Results.items, HasSubsequence(Labeled("bar() const"),
+ Labeled("Foo::foo() const")));
+ EXPECT_THAT(Results.items, Not(Contains(Labeled("foo() const")))); // private
+}
+
+TEST(CompletionTest, Snippets) {
+ clangd::CodeCompleteOptions Opts;
+ Opts.EnableSnippets = true;
+ auto Results = completions(
+ R"cpp(
+ struct fake {
+ int a;
+ int f(int i, const float f) const;
+ };
+ int main() {
+ fake f;
+ f.^
+ }
+ )cpp",
+ Opts);
+ EXPECT_THAT(Results.items,
+ HasSubsequence(PlainText("a"),
+ Snippet("f(${1:int i}, ${2:const float f})")));
+}
+
+TEST(CompletionTest, Kinds) {
+ auto Results = completions(R"cpp(
+ #define MACRO X
+ int variable;
+ struct Struct {};
+ int function();
+ int X = ^
+ )cpp");
+ EXPECT_THAT(Results.items, Has("function", CompletionItemKind::Function));
+ EXPECT_THAT(Results.items, Has("variable", CompletionItemKind::Variable));
+ EXPECT_THAT(Results.items, Has("int", CompletionItemKind::Keyword));
+ EXPECT_THAT(Results.items, Has("Struct", CompletionItemKind::Class));
+ EXPECT_THAT(Results.items, Has("MACRO", CompletionItemKind::Text));
+
+ clangd::CodeCompleteOptions Opts;
+ Opts.EnableSnippets = true; // Needed for code patterns.
+
+ Results = completions("nam^");
+ EXPECT_THAT(Results.items, Has("namespace", CompletionItemKind::Snippet));
+}
+
} // namespace
} // namespace clangd
} // namespace clang
diff --git a/unittests/clangd/Matchers.h b/unittests/clangd/Matchers.h
new file mode 100644
index 00000000..073a5525
--- /dev/null
+++ b/unittests/clangd/Matchers.h
@@ -0,0 +1,112 @@
+//===-- Matchers.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// GMock matchers that aren't specific to particular tests.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_MATCHERS_H
+#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_MATCHERS_H
+#include "gmock/gmock.h"
+
+namespace clang {
+namespace clangd {
+using ::testing::Matcher;
+
+// EXPECT_IFF expects matcher if condition is true, and Not(matcher) if false.
+// 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)
+
+// HasSubsequence(m1, m2, ...) matches a vector containing elements that match
+// m1, m2 ... in that order.
+//
+// SubsequenceMatcher implements this once the type of vector is known.
+template <typename T>
+class SubsequenceMatcher
+ : public ::testing::MatcherInterface<const std::vector<T> &> {
+ std::vector<Matcher<T>> Matchers;
+
+public:
+ SubsequenceMatcher(std::vector<Matcher<T>> M) : Matchers(M) {}
+
+ void DescribeTo(std::ostream *OS) const override {
+ *OS << "Contains the subsequence [";
+ const char *Sep = "";
+ for (const auto &M : Matchers) {
+ *OS << Sep;
+ M.DescribeTo(OS);
+ Sep = ", ";
+ }
+ *OS << "]";
+ }
+
+ bool MatchAndExplain(const std::vector<T> &V,
+ ::testing::MatchResultListener *L) const override {
+ std::vector<int> Matches(Matchers.size());
+ size_t I = 0;
+ for (size_t J = 0; I < Matchers.size() && J < V.size(); ++J)
+ if (Matchers[I].Matches(V[J]))
+ Matches[I++] = J;
+ if (I == Matchers.size()) // We exhausted all matchers.
+ return true;
+ if (L->IsInterested()) {
+ *L << "\n Matched:";
+ for (size_t K = 0; K < I; ++K) {
+ *L << "\n\t";
+ Matchers[K].DescribeTo(L->stream());
+ *L << " ==> " << ::testing::PrintToString(V[Matches[K]]);
+ }
+ *L << "\n\t";
+ Matchers[I].DescribeTo(L->stream());
+ *L << " ==> no subsequent match";
+ }
+ return false;
+ }
+};
+
+// PolySubsequenceMatcher implements a "polymorphic" SubsequenceMatcher.
+// It captures the types of the element matchers, and can be converted to
+// Matcher<vector<T>> if each matcher can be converted to Matcher<T>.
+// This allows HasSubsequence() to accept polymorphic matchers like Not().
+template <typename... M> class PolySubsequenceMatcher {
+ std::tuple<M...> Matchers;
+
+public:
+ PolySubsequenceMatcher(M &&... Args)
+ : Matchers(std::make_tuple(std::forward<M>(Args)...)) {}
+
+ template <typename T> operator Matcher<const std::vector<T> &>() const {
+ return ::testing::MakeMatcher(new SubsequenceMatcher<T>(
+ TypedMatchers<T>(llvm::index_sequence_for<M...>{})));
+ }
+
+private:
+ template <typename T, size_t... I>
+ std::vector<Matcher<T>> TypedMatchers(llvm::index_sequence<I...>) const {
+ return {std::get<I>(Matchers)...};
+ }
+};
+
+// HasSubsequence(m1, m2, ...) matches a vector containing elements that match
+// m1, m2 ... in that order.
+// The real implementation is in SubsequenceMatcher.
+template <typename... Args>
+PolySubsequenceMatcher<Args...> HasSubsequence(Args &&... M) {
+ return PolySubsequenceMatcher<Args...>(std::forward<Args>(M)...);
+}
+
+} // namespace clangd
+} // namespace clang
+#endif