19 #include "llvm/Support/ScopedPrinter.h"
20 #include "llvm/Support/raw_ostream.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
26 using ::testing::AnyOf;
27 using ::testing::ElementsAre;
28 using ::testing::IsEmpty;
29 using ::testing::UnorderedElementsAre;
40 std::vector<DocID> consumeIDs(Iterator &It) {
42 std::vector<DocID> IDs(IDAndScore.size());
43 for (
size_t I = 0; I < IDAndScore.size(); ++I)
44 IDs[I] = IDAndScore[I].first;
48 TEST(DexIterators, DocumentIterator) {
49 const PostingList L({4, 7, 8, 20, 42, 100});
50 auto DocIterator = L.iterator();
52 EXPECT_EQ(DocIterator->peek(), 4U);
53 EXPECT_FALSE(DocIterator->reachedEnd());
55 DocIterator->advance();
56 EXPECT_EQ(DocIterator->peek(), 7U);
57 EXPECT_FALSE(DocIterator->reachedEnd());
59 DocIterator->advanceTo(20);
60 EXPECT_EQ(DocIterator->peek(), 20U);
61 EXPECT_FALSE(DocIterator->reachedEnd());
63 DocIterator->advanceTo(65);
64 EXPECT_EQ(DocIterator->peek(), 100U);
65 EXPECT_FALSE(DocIterator->reachedEnd());
67 DocIterator->advanceTo(420);
68 EXPECT_TRUE(DocIterator->reachedEnd());
71 TEST(DexIterators, AndTwoLists) {
73 const PostingList L0({0, 5, 7, 10, 42, 320, 9000});
74 const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000});
76 auto And = C.intersect(L1.iterator(), L0.iterator());
78 EXPECT_FALSE(And->reachedEnd());
79 EXPECT_THAT(consumeIDs(*And), ElementsAre(0U, 7U, 10U, 320U, 9000U));
81 And = C.intersect(L0.iterator(), L1.iterator());
84 EXPECT_EQ(And->peek(), 0U);
86 EXPECT_EQ(And->peek(), 7U);
88 EXPECT_EQ(And->peek(), 10U);
90 EXPECT_EQ(And->peek(), 320U);
92 EXPECT_EQ(And->peek(), 9000U);
96 TEST(DexIterators, AndThreeLists) {
98 const PostingList L0({0, 5, 7, 10, 42, 320, 9000});
99 const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000});
100 const PostingList L2({1, 4, 7, 11, 30, 60, 320, 9000});
102 auto And = C.intersect(L0.iterator(), L1.iterator(), L2.iterator());
103 EXPECT_EQ(And->peek(), 7U);
105 EXPECT_EQ(And->peek(), 320U);
106 And->advanceTo(100000);
108 EXPECT_TRUE(And->reachedEnd());
111 TEST(DexIterators, AndEmpty) {
113 const PostingList L1{1};
114 const PostingList L2{2};
116 auto Empty1 = C.intersect(L1.iterator(), L2.iterator());
117 auto Empty2 = C.intersect(L1.iterator(), L2.iterator());
119 auto And = C.intersect(std::move(Empty1), std::move(Empty2));
120 EXPECT_TRUE(And->reachedEnd());
123 TEST(DexIterators, OrTwoLists) {
125 const PostingList L0({0, 5, 7, 10, 42, 320, 9000});
126 const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000});
128 auto Or = C.unionOf(L0.iterator(), L1.iterator());
130 EXPECT_FALSE(Or->reachedEnd());
131 EXPECT_EQ(Or->peek(), 0U);
133 EXPECT_EQ(Or->peek(), 4U);
135 EXPECT_EQ(Or->peek(), 5U);
137 EXPECT_EQ(Or->peek(), 7U);
139 EXPECT_EQ(Or->peek(), 10U);
141 EXPECT_EQ(Or->peek(), 30U);
143 EXPECT_EQ(Or->peek(), 42U);
145 EXPECT_EQ(Or->peek(), 320U);
147 EXPECT_EQ(Or->peek(), 9000U);
149 EXPECT_TRUE(Or->reachedEnd());
151 Or = C.unionOf(L0.iterator(), L1.iterator());
153 EXPECT_THAT(consumeIDs(*Or),
154 ElementsAre(0U, 4U, 5U, 7U, 10U, 30U, 42U, 60U, 320U, 9000U));
157 TEST(DexIterators, OrThreeLists) {
159 const PostingList L0({0, 5, 7, 10, 42, 320, 9000});
160 const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000});
161 const PostingList L2({1, 4, 7, 11, 30, 60, 320, 9000});
163 auto Or = C.unionOf(L0.iterator(), L1.iterator(), L2.iterator());
165 EXPECT_FALSE(Or->reachedEnd());
166 EXPECT_EQ(Or->peek(), 0U);
169 EXPECT_EQ(Or->peek(), 1U);
172 EXPECT_EQ(Or->peek(), 4U);
177 EXPECT_EQ(Or->peek(), 60U);
180 EXPECT_TRUE(Or->reachedEnd());
188 TEST(DexIterators, QueryTree) {
213 const PostingList L0({1, 3, 5, 8, 9});
214 const PostingList L1({1, 5, 7, 9});
215 const PostingList L2({1, 5});
216 const PostingList L3({0, 3, 5});
219 auto Root = C.intersect(
221 C.intersect(L0.iterator(), C.boost(L1.iterator(), 2U)),
223 C.unionOf(C.boost(L2.iterator(), 3U), C.boost(L3.iterator(), 4U)));
225 EXPECT_FALSE(Root->reachedEnd());
226 EXPECT_EQ(Root->peek(), 1U);
231 EXPECT_EQ(Root->peek(), 1U);
232 auto ElementBoost = Root->consume();
233 EXPECT_THAT(ElementBoost, 6);
235 EXPECT_EQ(Root->peek(), 5U);
237 EXPECT_EQ(Root->peek(), 5U);
238 ElementBoost = Root->consume();
239 EXPECT_THAT(ElementBoost, 8);
240 Root->advanceTo(9000);
241 EXPECT_TRUE(Root->reachedEnd());
244 TEST(DexIterators, StringRepresentation) {
246 const PostingList L1({1, 3, 5});
247 const PostingList L2({1, 7, 9});
250 auto I1 = L1.iterator();
251 EXPECT_EQ(llvm::to_string(*I1),
"[1 3 5]");
255 auto I2 = L1.iterator(&Tok);
256 EXPECT_EQ(llvm::to_string(*I2),
"T=L2");
258 auto Tree = C.limit(C.intersect(move(I1), move(I2)), 10);
260 EXPECT_THAT(llvm::to_string(*Tree), AnyOf(
"(LIMIT 10 (& [1 3 5] T=L2))",
261 "(LIMIT 10 (& T=L2 [1 3 5]))"));
264 TEST(DexIterators, Limit) {
266 const PostingList L0({3, 6, 7, 20, 42, 100});
267 const PostingList L1({1, 3, 5, 6, 7, 30, 100});
268 const PostingList L2({0, 3, 5, 7, 8, 100});
270 auto DocIterator = C.limit(L0.iterator(), 42);
271 EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7, 20, 42, 100));
273 DocIterator = C.limit(L0.iterator(), 3);
274 EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7));
276 DocIterator = C.limit(L0.iterator(), 0);
277 EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre());
280 C.intersect(C.limit(C.all(), 343), C.limit(L0.iterator(), 2),
281 C.limit(L1.iterator(), 3), C.limit(L2.iterator(), 42));
282 EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(3, 7));
285 TEST(DexIterators, True) {
286 EXPECT_TRUE(Corpus{0}.all()->reachedEnd());
287 EXPECT_THAT(consumeIDs(*Corpus{4}.all()), ElementsAre(0, 1, 2, 3));
290 TEST(DexIterators, Boost) {
292 auto BoostIterator = C.boost(C.all(), 42U);
293 EXPECT_FALSE(BoostIterator->reachedEnd());
294 auto ElementBoost = BoostIterator->consume();
295 EXPECT_THAT(ElementBoost, 42U);
297 const PostingList L0({2, 4});
298 const PostingList L1({1, 4});
299 auto Root = C.unionOf(C.all(), C.boost(L0.iterator(), 2U),
300 C.boost(L1.iterator(), 3U));
302 ElementBoost = Root->consume();
303 EXPECT_THAT(ElementBoost, 1);
305 EXPECT_THAT(Root->peek(), 1U);
306 ElementBoost = Root->consume();
307 EXPECT_THAT(ElementBoost, 3);
310 EXPECT_THAT(Root->peek(), 2U);
311 ElementBoost = Root->consume();
312 EXPECT_THAT(ElementBoost, 2);
315 ElementBoost = Root->consume();
316 EXPECT_THAT(ElementBoost, 3);
319 TEST(DexIterators, Optimizations) {
321 const PostingList L1{1};
322 const PostingList L2{2};
323 const PostingList L3{3};
326 EXPECT_EQ(llvm::to_string(*C.intersect()),
"true");
327 EXPECT_EQ(llvm::to_string(*C.unionOf()),
"false");
330 EXPECT_EQ(llvm::to_string(*C.intersect(L1.iterator(), C.all())),
"[1]");
331 EXPECT_EQ(llvm::to_string(*C.intersect(L1.iterator(), C.none())),
"false");
333 EXPECT_EQ(llvm::to_string(*C.unionOf(L1.iterator(), C.all())),
335 EXPECT_EQ(llvm::to_string(*C.unionOf(L1.iterator(), C.none())),
"[1]");
338 EXPECT_EQ(llvm::to_string(*C.intersect(
339 L1.iterator(), C.intersect(L1.iterator(), L1.iterator()))),
341 EXPECT_EQ(llvm::to_string(*C.unionOf(
342 L1.iterator(), C.unionOf(L2.iterator(), L3.iterator()))),
346 EXPECT_EQ(llvm::to_string(*C.intersect(
347 C.intersect(L1.iterator(), C.intersect()), C.unionOf(C.all()))),
355 ::testing::Matcher<std::vector<Token>>
357 std::vector<Token> Tokens;
358 for (
const auto &TokenData :
Strings) {
359 Tokens.push_back(Token(
Kind, TokenData));
361 return ::testing::UnorderedElementsAreArray(Tokens);
364 ::testing::Matcher<std::vector<Token>>
365 trigramsAre(std::initializer_list<std::string> Trigrams) {
369 std::vector<Token> identifierTrigramTokens(llvm::StringRef S) {
370 std::vector<Trigram> Trigrams;
372 std::vector<Token> Tokens;
373 for (Trigram T : Trigrams)
378 TEST(DexTrigrams, IdentifierTrigrams) {
379 EXPECT_THAT(identifierTrigramTokens(
"X86"), trigramsAre({
"x86",
"x",
"x8"}));
381 EXPECT_THAT(identifierTrigramTokens(
"nl"), trigramsAre({
"nl",
"n"}));
383 EXPECT_THAT(identifierTrigramTokens(
"n"), trigramsAre({
"n"}));
385 EXPECT_THAT(identifierTrigramTokens(
"clangd"),
386 trigramsAre({
"c",
"cl",
"cla",
"lan",
"ang",
"ngd"}));
388 EXPECT_THAT(identifierTrigramTokens(
"abc_def"),
389 trigramsAre({
"a",
"ab",
"ad",
"abc",
"abd",
"ade",
"bcd",
"bde",
392 EXPECT_THAT(identifierTrigramTokens(
"a_b_c_d_e_"),
393 trigramsAre({
"a",
"a_",
"ab",
"abc",
"bcd",
"cde"}));
395 EXPECT_THAT(identifierTrigramTokens(
"unique_ptr"),
396 trigramsAre({
"u",
"un",
"up",
"uni",
"unp",
"upt",
"niq",
"nip",
397 "npt",
"iqu",
"iqp",
"ipt",
"que",
"qup",
"qpt",
398 "uep",
"ept",
"ptr"}));
401 identifierTrigramTokens(
"TUDecl"),
402 trigramsAre({
"t",
"tu",
"td",
"tud",
"tde",
"ude",
"dec",
"ecl"}));
404 EXPECT_THAT(identifierTrigramTokens(
"IsOK"),
405 trigramsAre({
"i",
"is",
"io",
"iso",
"iok",
"sok"}));
408 identifierTrigramTokens(
"abc_defGhij__klm"),
409 trigramsAre({
"a",
"ab",
"ad",
"abc",
"abd",
"ade",
"adg",
"bcd",
410 "bde",
"bdg",
"cde",
"cdg",
"def",
"deg",
"dgh",
"dgk",
411 "efg",
"egh",
"egk",
"fgh",
"fgk",
"ghi",
"ghk",
"gkl",
412 "hij",
"hik",
"hkl",
"ijk",
"ikl",
"jkl",
"klm"}));
415 TEST(DexTrigrams, QueryTrigrams) {
428 trigramsAre({
"cla",
"lan",
"ang",
"ngd"}));
431 trigramsAre({
"abc",
"bcd",
"cde",
"def"}));
434 trigramsAre({
"abc",
"bcd",
"cde"}));
437 trigramsAre({
"uni",
"niq",
"iqu",
"que",
"uep",
"ept",
"ptr"}));
440 trigramsAre({
"tud",
"ude",
"dec",
"ecl"}));
445 trigramsAre({
"abc",
"bcd",
"cde",
"def",
"efg",
"fgh",
"ghi",
446 "hij",
"ijk",
"jkl",
"klm"}));
449 TEST(DexSearchTokens, SymbolPath) {
451 "unittest:///clang-tools-extra/clangd/index/Token.h"),
452 ElementsAre(
"unittest:///clang-tools-extra/clangd/index/Token.h",
453 "unittest:///clang-tools-extra/clangd/index",
454 "unittest:///clang-tools-extra/clangd",
455 "unittest:///clang-tools-extra",
"unittest:///"));
458 ElementsAre(
"unittest:///a/b/c.h",
"unittest:///a/b",
459 "unittest:///a",
"unittest:///"));
469 EXPECT_THAT(
lookup(*I,
SymbolID(
"ns::abc")), UnorderedElementsAre(
"ns::abc"));
471 UnorderedElementsAre(
"ns::abc",
"ns::xyz"));
473 UnorderedElementsAre(
"ns::xyz"));
474 EXPECT_THAT(
lookup(*I,
SymbolID(
"ns::nonono")), UnorderedElementsAre());
477 TEST(Dex, FuzzyFind) {
480 "ns::nested::ABC",
"other::ABC",
"other::A"}),
481 RefSlab(), RelationSlab());
482 FuzzyFindRequest Req;
484 Req.Scopes = {
"ns::"};
485 EXPECT_THAT(
match(*
Index, Req), UnorderedElementsAre(
"ns::ABC"));
486 Req.Scopes = {
"ns::",
"ns::nested::"};
488 UnorderedElementsAre(
"ns::ABC",
"ns::nested::ABC"));
490 Req.Scopes = {
"other::"};
492 UnorderedElementsAre(
"other::A",
"other::ABC"));
497 UnorderedElementsAre(
"ns::ABC",
"ns::BCD",
"::ABC",
498 "ns::nested::ABC",
"other::ABC",
502 TEST(DexTest, DexLimitedNumMatches) {
504 FuzzyFindRequest Req;
509 auto Matches =
match(*I, Req, &Incomplete);
510 EXPECT_TRUE(Req.Limit);
511 EXPECT_EQ(Matches.size(), *Req.Limit);
512 EXPECT_TRUE(Incomplete);
515 TEST(DexTest, FuzzyMatch) {
517 generateSymbols({
"LaughingOutLoud",
"LionPopulation",
"LittleOldLady"}),
518 RefSlab(), RelationSlab());
519 FuzzyFindRequest Req;
523 EXPECT_THAT(
match(*I, Req),
524 UnorderedElementsAre(
"LaughingOutLoud",
"LittleOldLady"));
527 TEST(DexTest, ShortQuery) {
530 FuzzyFindRequest Req;
534 EXPECT_THAT(
match(*I, Req, &Incomplete), ElementsAre(
"OneTwoThreeFour"));
535 EXPECT_FALSE(Incomplete) <<
"Empty string is not a short query";
538 EXPECT_THAT(
match(*I, Req, &Incomplete), ElementsAre());
539 EXPECT_TRUE(Incomplete) <<
"Short queries have different semantics";
542 EXPECT_THAT(
match(*I, Req, &Incomplete), ElementsAre());
543 EXPECT_TRUE(Incomplete) <<
"Short queries have different semantics";
546 EXPECT_THAT(
match(*I, Req, &Incomplete), ElementsAre(
"OneTwoThreeFour"));
547 EXPECT_FALSE(Incomplete) <<
"3-char string is not a short query";
550 TEST(DexTest, MatchQualifiedNamesWithoutSpecificScope) {
553 FuzzyFindRequest Req;
556 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1",
"b::y2",
"y3"));
559 TEST(DexTest, MatchQualifiedNamesWithGlobalScope) {
562 FuzzyFindRequest Req;
565 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"y3"));
568 TEST(DexTest, MatchQualifiedNamesWithOneScope) {
571 RefSlab(), RelationSlab());
572 FuzzyFindRequest Req;
574 Req.Scopes = {
"a::"};
575 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1",
"a::y2"));
578 TEST(DexTest, MatchQualifiedNamesWithMultipleScopes) {
581 RefSlab(), RelationSlab());
582 FuzzyFindRequest Req;
584 Req.Scopes = {
"a::",
"b::"};
585 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1",
"a::y2",
"b::y3"));
588 TEST(DexTest, NoMatchNestedScopes) {
591 FuzzyFindRequest Req;
593 Req.Scopes = {
"a::"};
594 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1"));
597 TEST(DexTest, WildcardScope) {
599 RefSlab(), RelationSlab());
600 FuzzyFindRequest Req;
603 Req.Scopes = {
"a::"};
604 EXPECT_THAT(
match(*I, Req),
605 UnorderedElementsAre(
"a::y1",
"a::b::y2",
"c::y3"));
608 TEST(DexTest, IgnoreCases) {
611 FuzzyFindRequest Req;
613 Req.Scopes = {
"ns::"};
614 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"ns::ABC",
"ns::abc"));
617 TEST(DexTest, UnknownPostingList) {
621 FuzzyFindRequest Req;
622 Req.Scopes = {
"ns2::"};
623 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre());
626 TEST(DexTest, Lookup) {
629 EXPECT_THAT(
lookup(*I,
SymbolID(
"ns::abc")), UnorderedElementsAre(
"ns::abc"));
631 UnorderedElementsAre(
"ns::abc",
"ns::xyz"));
633 UnorderedElementsAre(
"ns::xyz"));
634 EXPECT_THAT(
lookup(*I,
SymbolID(
"ns::nonono")), UnorderedElementsAre());
637 TEST(DexTest, SymbolIndexOptionsFilter) {
638 auto CodeCompletionSymbol =
symbol(
"Completion");
639 auto NonCodeCompletionSymbol =
symbol(
"NoCompletion");
640 CodeCompletionSymbol.Flags = Symbol::SymbolFlag::IndexedForCodeCompletion;
641 NonCodeCompletionSymbol.Flags = Symbol::SymbolFlag::None;
642 std::vector<Symbol>
Symbols{CodeCompletionSymbol, NonCodeCompletionSymbol};
643 Dex I(
Symbols, RefSlab(), RelationSlab());
644 FuzzyFindRequest Req;
646 Req.RestrictForCodeCompletion =
false;
647 EXPECT_THAT(
match(I, Req), ElementsAre(
"Completion",
"NoCompletion"));
648 Req.RestrictForCodeCompletion =
true;
649 EXPECT_THAT(
match(I, Req), ElementsAre(
"Completion"));
652 TEST(DexTest, ProximityPathsBoosting) {
653 auto RootSymbol =
symbol(
"root::abc");
654 RootSymbol.CanonicalDeclaration.FileURI =
"unittest:///file.h";
655 auto CloseSymbol =
symbol(
"close::abc");
656 CloseSymbol.CanonicalDeclaration.FileURI =
"unittest:///a/b/c/d/e/f/file.h";
658 std::vector<Symbol>
Symbols{CloseSymbol, RootSymbol};
659 Dex I(
Symbols, RefSlab(), RelationSlab());
661 FuzzyFindRequest Req;
669 Req.ProximityPaths = {
testPath(
"a/b/c/d/e/f/file.h")};
670 EXPECT_THAT(
match(I, Req), ElementsAre(
"close::abc"));
674 Req.ProximityPaths = {
testPath(
"file.h")};
675 EXPECT_THAT(
match(I, Req), ElementsAre(
"root::abc"));
679 llvm::DenseMap<SymbolID, std::vector<Ref>>
Refs;
681 auto &SymbolRefs =
Refs[Sym.ID];
682 SymbolRefs.emplace_back();
683 SymbolRefs.back().Kind =
Kind;
684 SymbolRefs.back().Location.FileURI =
Filename;
694 Req.IDs.insert(Foo.ID);
697 std::vector<std::string> Files;
698 EXPECT_FALSE(Dex(std::vector<Symbol>{Foo, Bar},
Refs, RelationSlab())
699 .refs(Req, [&](
const Ref &R) {
700 Files.push_back(R.Location.FileURI);
702 EXPECT_THAT(Files, UnorderedElementsAre(
"foo.h",
"foo.cc"));
706 EXPECT_TRUE(Dex(std::vector<Symbol>{Foo, Bar},
Refs, RelationSlab())
707 .refs(Req, [&](
const Ref &R) {
708 Files.push_back(R.Location.FileURI);
710 EXPECT_THAT(Files, ElementsAre(AnyOf(
"foo.h",
"foo.cc")));
713 TEST(DexTests, Relations) {
715 auto Child1 =
symbol(
"Child1");
716 auto Child2 =
symbol(
"Child2");
723 Dex I{
Symbols, RefSlab(), Relations};
726 RelationsRequest Req;
727 Req.Subjects.insert(
Parent.ID);
729 I.relations(Req, [&](
const SymbolID &Subject,
const Symbol &Object) {
732 EXPECT_THAT(
Results, UnorderedElementsAre(Child1.ID, Child2.ID));
735 TEST(DexTest, PreferredTypesBoosting) {
741 std::vector<Symbol>
Symbols{Sym1, Sym2};
742 Dex I(
Symbols, RefSlab(), RelationSlab());
744 FuzzyFindRequest Req;
750 Req.PreferredTypes = {std::string(Sym1.Type)};
751 EXPECT_THAT(
match(I, Req), ElementsAre(
"t1"));
753 Req.PreferredTypes = {std::string(Sym2.Type)};
754 EXPECT_THAT(
match(I, Req), ElementsAre(
"t2"));
757 TEST(DexTest, TemplateSpecialization) {
760 Symbol S =
symbol(
"TempSpec");
766 S.TemplateSpecializationArgs =
"<int, bool>";
767 S.SymInfo.Properties = static_cast<index::SymbolPropertySet>(
768 index::SymbolProperty::TemplateSpecialization);
773 S.TemplateSpecializationArgs =
"<int, U>";
774 S.SymInfo.Properties = static_cast<index::SymbolPropertySet>(
775 index::SymbolProperty::TemplatePartialSpecialization);
778 auto I =
dex::Dex::build(std::move(B).build(), RefSlab(), RelationSlab());
779 FuzzyFindRequest Req;
782 Req.Query =
"TempSpec";
783 EXPECT_THAT(
match(*I, Req),
784 UnorderedElementsAre(
"TempSpec",
"TempSpec<int, bool>",
785 "TempSpec<int, U>"));
788 Req.Query =
"TempSpec<int";
789 EXPECT_THAT(
match(*I, Req), IsEmpty());