15#include "clang/Tooling/CompilationDatabase.h"
16#include "llvm/ADT/StringExtras.h"
17#include "llvm/Config/llvm-config.h"
18#include "llvm/Support/Compression.h"
19#include "llvm/Support/Error.h"
20#include "llvm/Support/ScopedPrinter.h"
21#include "gmock/gmock.h"
22#include "gtest/gtest.h"
24#include <sys/resource.h>
27using ::testing::ElementsAre;
29using ::testing::UnorderedElementsAre;
30using ::testing::UnorderedElementsAreArray;
46 FileURI: file:///path/foo.h
55Documentation: 'Foo doc'
60 Directives: [ Include ]
63 Directives: [ Import ]
66 Directives: [ Include, Import ]
80 FileURI: file:///path/bar.h
89CompletionSnippetSuffix: '-snippet'
96 FileURI: file:///path/foo.cc
118URI: 'file:///path/source1.cpp'
120Digest: EED8F5EAF25C453C
122 - 'file:///path/inc1.h'
123 - 'file:///path/inc2.h'
128MATCHER_P(qName, Name,
"") {
return (arg.Scope + arg.Name).str() == Name; }
129MATCHER_P3(IncludeHeaderWithRefAndDirectives, IncludeHeader,
References,
130 SupportedDirectives,
"") {
131 return (arg.IncludeHeader == IncludeHeader) &&
133 (arg.SupportedDirectives == SupportedDirectives);
140TEST(SerializationTest, NoCrashOnEmptyYAML) {
144TEST(SerializationTest, YAMLConversions) {
146 ASSERT_TRUE(
bool(ParsedYAML)) << ParsedYAML.takeError();
147 ASSERT_TRUE(
bool(ParsedYAML->Symbols));
149 *ParsedYAML->Symbols,
150 UnorderedElementsAre(
id(
"057557CEBF6E6B2D"),
id(
"057557CEBF6E6B2E")));
152 auto Sym1 = *ParsedYAML->Symbols->find(
154 auto Sym2 = *ParsedYAML->Symbols->find(
157 EXPECT_THAT(Sym1, qName(
"clang::Foo1"));
158 EXPECT_EQ(Sym1.Signature,
"");
159 EXPECT_EQ(Sym1.Documentation,
"Foo doc");
160 EXPECT_EQ(Sym1.ReturnType,
"int");
161 EXPECT_EQ(StringRef(Sym1.CanonicalDeclaration.FileURI),
"file:///path/foo.h");
163 EXPECT_EQ(
static_cast<uint8_t
>(Sym1.Flags), 129);
167 EXPECT_EQ(Sym1.Tags, 258u);
172 UnorderedElementsAre(
174 IncludeHeaderWithRefAndDirectives(
"include2", 3u,
Symbol::Import),
175 IncludeHeaderWithRefAndDirectives(
"include3", 2u,
179 EXPECT_THAT(Sym2, qName(
"clang::Foo2"));
180 EXPECT_EQ(Sym2.Signature,
"-sig");
181 EXPECT_EQ(Sym2.ReturnType,
"");
182 EXPECT_EQ(llvm::StringRef(Sym2.CanonicalDeclaration.FileURI),
183 "file:///path/bar.h");
186 EXPECT_EQ(Sym2.Tags, 0u);
188 ASSERT_TRUE(
bool(ParsedYAML->Refs));
192 ::testing::SizeIs(1))));
193 auto Ref1 = ParsedYAML->Refs->begin()->second.front();
195 EXPECT_EQ(StringRef(Ref1.Location.FileURI),
"file:///path/foo.cc");
199 ASSERT_TRUE(
bool(ParsedYAML->Relations));
201 *ParsedYAML->Relations,
204 ASSERT_TRUE(
bool(ParsedYAML->Cmd));
205 auto &Cmd = *ParsedYAML->Cmd;
206 ASSERT_EQ(Cmd.Directory,
"testdir");
207 EXPECT_THAT(Cmd.CommandLine, ElementsAre(
"cmd1",
"cmd2"));
209 ASSERT_TRUE(
bool(ParsedYAML->Sources));
210 const auto *
URI =
"file:///path/source1.cpp";
211 ASSERT_TRUE(ParsedYAML->Sources->count(
URI));
212 auto IGNDeserialized = ParsedYAML->Sources->lookup(
URI);
213 EXPECT_EQ(llvm::toHex(IGNDeserialized.Digest),
"EED8F5EAF25C453C");
214 EXPECT_THAT(IGNDeserialized.DirectIncludes,
215 ElementsAre(
"file:///path/inc1.h",
"file:///path/inc2.h"));
216 EXPECT_EQ(IGNDeserialized.URI,
URI);
220std::vector<std::string> yamlFromSymbols(
const SymbolSlab &Slab) {
221 std::vector<std::string> Result;
222 for (
const auto &Sym : Slab)
223 Result.push_back(
toYAML(Sym));
226std::vector<std::string> yamlFromRefs(
const RefSlab &Slab) {
227 std::vector<std::string> Result;
228 for (
const auto &Refs : Slab)
229 Result.push_back(
toYAML(Refs));
233std::vector<std::string> yamlFromRelations(
const RelationSlab &Slab) {
234 std::vector<std::string> Result;
235 for (
const auto &Rel : Slab)
236 Result.push_back(
toYAML(Rel));
240TEST(SerializationTest, BinaryConversions) {
242 EXPECT_TRUE(
bool(In)) << In.takeError();
247 std::string Serialized = llvm::to_string(Out);
250 ASSERT_TRUE(
bool(In2)) << In2.takeError();
251 ASSERT_TRUE(In2->Symbols);
252 ASSERT_TRUE(In2->Refs);
253 ASSERT_TRUE(In2->Relations);
256 EXPECT_THAT(yamlFromSymbols(*In2->Symbols),
257 UnorderedElementsAreArray(yamlFromSymbols(*In->Symbols)));
258 EXPECT_THAT(yamlFromRefs(*In2->Refs),
259 UnorderedElementsAreArray(yamlFromRefs(*In->Refs)));
260 EXPECT_THAT(yamlFromRelations(*In2->Relations),
261 UnorderedElementsAreArray(yamlFromRelations(*In->Relations)));
266TEST(SerializationTest, YAMLSymbolKindRoundTrip) {
268 for (
unsigned K =
static_cast<unsigned>(index::SymbolKind::Unknown);
269 K <= static_cast<unsigned>(index::SymbolKind::Concept); ++K) {
270 std::string Name =
"Sym" + std::to_string(K);
272 Sym.SymInfo.Kind =
static_cast<index::SymbolKind
>(K);
282 ASSERT_TRUE(
bool(In)) << In.takeError();
283 ASSERT_TRUE(In->Symbols);
285 auto It = In->Symbols->find(Sym.ID);
286 ASSERT_NE(It, In->Symbols->end()) << Sym.Name;
287 EXPECT_EQ(It->SymInfo.Kind, Sym.SymInfo.Kind) << Sym.Name;
291TEST(SerializationTest, SrcsTest) {
293 EXPECT_TRUE(
bool(In)) << In.takeError();
295 std::string TestContent(
"TestContent");
298 IGN.DirectIncludes = {
"inc1",
"inc2"};
303 Sources[IGN.URI] = IGN;
307 Out.Sources = &Sources;
309 std::string Serialized = llvm::to_string(Out);
312 ASSERT_TRUE(
bool(In)) << In.takeError();
313 ASSERT_TRUE(In->Symbols);
314 ASSERT_TRUE(In->Refs);
315 ASSERT_TRUE(In->Sources);
316 ASSERT_TRUE(In->Sources->count(IGN.URI));
318 EXPECT_THAT(yamlFromSymbols(*In->Symbols),
319 UnorderedElementsAreArray(yamlFromSymbols(*In->Symbols)));
320 EXPECT_THAT(yamlFromRefs(*In->Refs),
321 UnorderedElementsAreArray(yamlFromRefs(*In->Refs)));
322 auto IGNDeserialized = In->Sources->lookup(IGN.URI);
323 EXPECT_EQ(IGNDeserialized.Digest, IGN.Digest);
324 EXPECT_EQ(IGNDeserialized.DirectIncludes, IGN.DirectIncludes);
325 EXPECT_EQ(IGNDeserialized.URI, IGN.URI);
326 EXPECT_EQ(IGNDeserialized.Flags, IGN.Flags);
332TEST(SerializationTest, TagsRoundTrip) {
337 const char *TaggedYAML = R
"(
347 FileURI: file:///tmp/tagged.h
362 constexpr unsigned ExpectedTagMask = 258u;
363 EXPECT_EQ(ExpectedTags, ExpectedTagMask);
367 ASSERT_TRUE(
bool(FromYAML)) << FromYAML.takeError();
368 ASSERT_TRUE(
bool(FromYAML->Symbols));
370 auto It = FromYAML->Symbols->find(TaggedID);
371 ASSERT_NE(It, FromYAML->Symbols->end())
372 <<
"symbol not found after YAML parse";
373 EXPECT_EQ(It->Tags, ExpectedTags)
374 <<
"Tags lost during YAML deserialization";
380 std::string Serialized = llvm::to_string(Out);
383 ASSERT_TRUE(
bool(FromRIFF)) << FromRIFF.takeError();
384 ASSERT_TRUE(
bool(FromRIFF->Symbols));
386 auto It = FromRIFF->Symbols->find(TaggedID);
387 ASSERT_NE(It, FromRIFF->Symbols->end())
388 <<
"symbol not found after RIFF roundtrip";
389 EXPECT_EQ(It->Tags, ExpectedTags) <<
"Tags lost during RIFF serialization";
397 const char *UntaggedYAML = R
"(
407 FileURI: file:///tmp/untagged.h
418 ASSERT_TRUE(
bool(FromUntaggedYAML)) << FromUntaggedYAML.takeError();
419 ASSERT_TRUE(
bool(FromUntaggedYAML->Symbols));
422 auto FromUntaggedRIFF =
readIndexFile(llvm::to_string(Out2));
423 ASSERT_TRUE(
bool(FromUntaggedRIFF)) << FromUntaggedRIFF.takeError();
424 ASSERT_TRUE(
bool(FromUntaggedRIFF->Symbols));
427 auto It = FromUntaggedRIFF->Symbols->find(UntaggedID);
428 ASSERT_NE(It, FromUntaggedRIFF->Symbols->end());
429 EXPECT_EQ(It->Tags, 0u) <<
"Tags must be zero for untagged symbol";
433TEST(SerializationTest, CmdlTest) {
435 EXPECT_TRUE(
bool(In)) << In.takeError();
437 tooling::CompileCommand Cmd;
438 Cmd.Directory =
"testdir";
439 Cmd.CommandLine.push_back(
"cmd1");
440 Cmd.CommandLine.push_back(
"cmd2");
441 Cmd.Filename =
"ignored";
442 Cmd.Heuristic =
"ignored";
443 Cmd.Output =
"ignored";
449 std::string Serialized = llvm::to_string(Out);
452 ASSERT_TRUE(
bool(In)) << In.takeError();
453 ASSERT_TRUE(In->Cmd);
455 const tooling::CompileCommand &SerializedCmd = *In->Cmd;
456 EXPECT_EQ(SerializedCmd.CommandLine, Cmd.CommandLine);
457 EXPECT_EQ(SerializedCmd.Directory, Cmd.Directory);
458 EXPECT_NE(SerializedCmd.Filename, Cmd.Filename);
459 EXPECT_NE(SerializedCmd.Heuristic, Cmd.Heuristic);
460 EXPECT_NE(SerializedCmd.Output, Cmd.Output);
466#if LLVM_ON_UNIX && defined(RLIMIT_AS) && !LLVM_ADDRESS_SANITIZER_BUILD && \
467 !LLVM_MEMORY_SANITIZER_BUILD && !LLVM_THREAD_SANITIZER_BUILD
468class ScopedMemoryLimit {
469 struct rlimit OriginalLimit;
470 bool Succeeded =
false;
473 ScopedMemoryLimit(rlim_t Bytes) {
474 if (!getrlimit(RLIMIT_AS, &OriginalLimit)) {
475 struct rlimit NewLimit = OriginalLimit;
476 NewLimit.rlim_cur = Bytes;
477 Succeeded = !setrlimit(RLIMIT_AS, &NewLimit);
480 log(
"Failed to set rlimit");
483 ~ScopedMemoryLimit() {
485 setrlimit(RLIMIT_AS, &OriginalLimit);
489class ScopedMemoryLimit {
491 ScopedMemoryLimit(
unsigned Bytes) {
log(
"rlimit unsupported"); }
497TEST(SerializationTest, NoCrashOnBadArraySize) {
501 ASSERT_FALSE(!In) << In.takeError();
504 std::string Serialized = llvm::to_string(Out);
508 ASSERT_FALSE(!Parsed) << Parsed.takeError();
509 auto Srcs = llvm::find_if(Parsed->Chunks, [](
riff::Chunk C) {
510 return C.ID == riff::fourCC(
"srcs");
512 ASSERT_NE(Srcs, Parsed->Chunks.end());
523 std::string
FileDigest = llvm::fromHex(
"EED8F5EAF25C453C");
524 unsigned Pos = Srcs->Data.find_first_of(
FileDigest);
525 ASSERT_NE(Pos, StringRef::npos) <<
"Couldn't locate file digest";
530 std::string CorruptSrcs =
531 (Srcs->Data.take_front(Pos) + llvm::fromHex(
"ffffffff0f") +
532 "some_random_garbage")
534 Srcs->Data = CorruptSrcs;
537 ScopedMemoryLimit MemLimit(1000 * 1024 * 1024);
539 std::string CorruptFile = llvm::to_string(*Parsed);
541 ASSERT_TRUE(!CorruptParsed);
542 EXPECT_EQ(llvm::toString(CorruptParsed.takeError()),
543 "malformed or truncated include uri");
548TEST(SerializationTest, NoCrashOnBadStringTableSize) {
549 if (!llvm::compression::zlib::isAvailable()) {
550 log(
"skipping test, no zlib");
556 ASSERT_FALSE(!In) << In.takeError();
559 std::string Serialized = llvm::to_string(Out);
563 ASSERT_FALSE(!Parsed) << Parsed.takeError();
564 auto Stri = llvm::find_if(Parsed->Chunks, [](
riff::Chunk C) {
565 return C.ID == riff::fourCC(
"stri");
567 ASSERT_NE(Stri, Parsed->Chunks.end());
571 std::string CorruptStri =
572 (llvm::fromHex(
"ffffffff") + Stri->Data.drop_front(4)).str();
573 Stri->Data = CorruptStri;
576 ScopedMemoryLimit MemLimit(1000 * 1024 * 1024);
578 std::string CorruptFile = llvm::to_string(*Parsed);
580 ASSERT_TRUE(!CorruptParsed);
581 EXPECT_THAT(llvm::toString(CorruptParsed.takeError()),
582 testing::HasSubstr(
"bytes is implausible"));
An efficient structure of storing large set of symbol references in memory.
static llvm::Expected< SymbolID > fromStr(llvm::StringRef)
SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
An immutable symbol container that stores a set of symbols.
A URI describes the location of a source file.
llvm::Expected< File > readFile(llvm::StringRef Stream)
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
llvm::Expected< IndexFileIn > readIndexFile(llvm::StringRef Data, SymbolOrigin Origin)
uint32_t SymbolTags
A bitmask type representing symbol tags supported by LSP.
FileDigest digest(llvm::StringRef Content)
SymbolTags toSymbolTagBitmask(const SymbolTag ST)
Converts a single SymbolTag to a bitmask.
TEST(BackgroundQueueTest, Priority)
Symbol symbol(llvm::StringRef QName)
llvm::StringMap< IncludeGraphNode > IncludeGraph
void log(const char *Fmt, Ts &&... Vals)
std::string toYAML(const Symbol &)
std::array< uint8_t, 8 > FileDigest
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
const SymbolSlab * Symbols
Represents a relation between two symbols.
Ensure we have enough bits to represent all SymbolTag values.
@ IndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
@ Deprecated
Indicates if the symbol is deprecated.
@ Include
#include "header.h"
@ Import
#import "header.h"