1 //===--- MarshallingTests.cpp ------------------------------------*- C++-*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
12 #include "index/Index.h"
13 #include "index/Ref.h"
14 #include "index/Relation.h"
15 #include "index/Serialization.h"
16 #include "index/Symbol.h"
17 #include "index/SymbolID.h"
18 #include "index/SymbolLocation.h"
19 #include "index/SymbolOrigin.h"
20 #include "index/remote/marshalling/Marshalling.h"
21 #include "clang/Index/IndexSymbol.h"
22 #include "llvm/ADT/SmallString.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/ADT/Twine.h"
25 #include "llvm/Support/Error.h"
26 #include "llvm/Support/Path.h"
27 #include "llvm/Support/StringSaver.h"
28 #include "gmock/gmock.h"
29 #include "gtest/gtest.h"
37 using llvm::sys::path::convert_to_slash
;
39 const char *testPathURI(llvm::StringRef Path
,
40 llvm::UniqueStringSaver
&Strings
) {
41 auto URI
= URI::createFile(testPath(Path
));
42 return Strings
.save(URI
.toString()).begin();
45 clangd::Symbol
createSymbol(llvm::StringRef PathPrefix
,
46 llvm::UniqueStringSaver
&Strings
) {
48 Sym
.ID
= llvm::cantFail(SymbolID::fromStr("057557CEBF6E6B2D"));
50 index::SymbolInfo Info
;
51 Info
.Kind
= index::SymbolKind::Function
;
52 Info
.SubKind
= index::SymbolSubKind::AccessorGetter
;
53 Info
.Lang
= index::SymbolLanguage::CXX
;
54 Info
.Properties
= static_cast<index::SymbolPropertySet
>(
55 index::SymbolProperty::TemplateSpecialization
);
58 Sym
.Name
= Strings
.save("Foo");
59 Sym
.Scope
= Strings
.save("llvm::foo::bar::");
61 clangd::SymbolLocation Location
;
62 Location
.Start
.setLine(1);
63 Location
.Start
.setColumn(15);
64 Location
.End
.setLine(3);
65 Location
.End
.setColumn(121);
66 Location
.FileURI
= testPathURI(PathPrefix
.str() + "Definition.cpp", Strings
);
67 Sym
.Definition
= Location
;
69 Location
.Start
.setLine(42);
70 Location
.Start
.setColumn(31);
71 Location
.End
.setLine(20);
72 Location
.End
.setColumn(400);
73 Location
.FileURI
= testPathURI(PathPrefix
.str() + "Declaration.h", Strings
);
74 Sym
.CanonicalDeclaration
= Location
;
76 Sym
.References
= 9000;
77 Sym
.Origin
= clangd::SymbolOrigin::Static
;
78 Sym
.Signature
= Strings
.save("(int X, char Y, Type T)");
79 Sym
.TemplateSpecializationArgs
= Strings
.save("<int, char, bool, Type>");
80 Sym
.CompletionSnippetSuffix
=
81 Strings
.save("({1: int X}, {2: char Y}, {3: Type T})");
82 Sym
.Documentation
= Strings
.save("This is my amazing Foo constructor!");
83 Sym
.ReturnType
= Strings
.save("Foo");
85 Sym
.Flags
= clangd::Symbol::SymbolFlag::IndexedForCodeCompletion
;
90 TEST(RemoteMarshallingTest
, URITranslation
) {
91 llvm::BumpPtrAllocator Arena
;
92 llvm::UniqueStringSaver
Strings(Arena
);
93 Marshaller
ProtobufMarshaller(
94 testPath("remote/machine/projects/llvm-project/"),
95 testPath("home/my-projects/llvm-project/"));
97 Original
.Location
.FileURI
=
98 testPathURI("remote/machine/projects/llvm-project/clang-tools-extra/"
99 "clangd/unittests/remote/MarshallingTests.cpp",
101 auto Serialized
= ProtobufMarshaller
.toProtobuf(Original
);
102 ASSERT_TRUE(bool(Serialized
));
103 EXPECT_EQ(Serialized
->location().file_path(),
104 "clang-tools-extra/clangd/unittests/remote/MarshallingTests.cpp");
105 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(*Serialized
);
106 ASSERT_TRUE(bool(Deserialized
));
107 EXPECT_STREQ(Deserialized
->Location
.FileURI
,
108 testPathURI("home/my-projects/llvm-project/clang-tools-extra/"
109 "clangd/unittests/remote/MarshallingTests.cpp",
112 // Can't have empty paths.
113 *Serialized
->mutable_location()->mutable_file_path() = std::string();
114 Deserialized
= ProtobufMarshaller
.fromProtobuf(*Serialized
);
115 EXPECT_FALSE(bool(Deserialized
));
116 llvm::consumeError(Deserialized
.takeError());
118 clangd::Ref WithInvalidURI
;
119 // Invalid URI results in serialization failure.
120 WithInvalidURI
.Location
.FileURI
= "This is not a URI";
121 auto DeserializedRef
= ProtobufMarshaller
.toProtobuf(WithInvalidURI
);
122 EXPECT_FALSE(bool(DeserializedRef
));
123 llvm::consumeError(DeserializedRef
.takeError());
125 // Can not use URIs with scheme different from "file".
127 URI::create(testPath("project/lib/HelloWorld.cpp"), "unittest");
128 ASSERT_TRUE(bool(UnittestURI
));
129 WithInvalidURI
.Location
.FileURI
=
130 Strings
.save(UnittestURI
->toString()).begin();
131 auto DeserializedSymbol
= ProtobufMarshaller
.toProtobuf(WithInvalidURI
);
132 EXPECT_FALSE(bool(DeserializedSymbol
));
133 llvm::consumeError(DeserializedSymbol
.takeError());
135 // Paths transmitted over the wire can not be absolute, they have to be
137 Ref WithAbsolutePath
;
138 *WithAbsolutePath
.mutable_location()->mutable_file_path() =
139 "/usr/local/user/home/HelloWorld.cpp";
140 Deserialized
= ProtobufMarshaller
.fromProtobuf(WithAbsolutePath
);
141 EXPECT_FALSE(bool(Deserialized
));
142 llvm::consumeError(Deserialized
.takeError());
145 TEST(RemoteMarshallingTest
, SymbolSerialization
) {
146 llvm::BumpPtrAllocator Arena
;
147 llvm::UniqueStringSaver
Strings(Arena
);
149 clangd::Symbol Sym
= createSymbol("home/", Strings
);
150 Marshaller
ProtobufMarshaller(testPath("home/"), testPath("home/"));
152 // Check that symbols are exactly the same if the path to indexed project is
153 // the same on indexing machine and the client.
154 auto Serialized
= ProtobufMarshaller
.toProtobuf(Sym
);
155 ASSERT_TRUE(bool(Serialized
));
156 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(*Serialized
);
157 ASSERT_TRUE(bool(Deserialized
));
158 // Origin is overwritten when deserializing.
159 Sym
.Origin
= SymbolOrigin::Remote
;
160 EXPECT_EQ(toYAML(Sym
), toYAML(*Deserialized
));
161 // Serialized paths are relative and have UNIX slashes.
162 EXPECT_EQ(convert_to_slash(Serialized
->definition().file_path(),
163 llvm::sys::path::Style::posix
),
164 Serialized
->definition().file_path());
166 llvm::sys::path::is_relative(Serialized
->definition().file_path()));
168 // Missing definition is OK.
169 Sym
.Definition
= clangd::SymbolLocation();
170 Serialized
= ProtobufMarshaller
.toProtobuf(Sym
);
171 ASSERT_TRUE(bool(Serialized
));
172 ASSERT_TRUE(bool(ProtobufMarshaller
.fromProtobuf(*Serialized
)));
174 // Relative path is absolute.
175 *Serialized
->mutable_canonical_declaration()->mutable_file_path() =
176 convert_to_slash("/path/to/Declaration.h");
177 Deserialized
= ProtobufMarshaller
.fromProtobuf(*Serialized
);
178 EXPECT_FALSE(bool(Deserialized
));
179 llvm::consumeError(Deserialized
.takeError());
181 // Fail with an invalid URI.
182 Sym
.Definition
.FileURI
= "Not A URI";
183 Serialized
= ProtobufMarshaller
.toProtobuf(Sym
);
184 EXPECT_FALSE(bool(Serialized
));
185 llvm::consumeError(Serialized
.takeError());
187 // Schemes other than "file" can not be used.
188 auto UnittestURI
= URI::create(testPath("home/SomePath.h"), "unittest");
189 ASSERT_TRUE(bool(UnittestURI
));
190 Sym
.Definition
.FileURI
= Strings
.save(UnittestURI
->toString()).begin();
191 Serialized
= ProtobufMarshaller
.toProtobuf(Sym
);
192 EXPECT_FALSE(bool(Serialized
));
193 llvm::consumeError(Serialized
.takeError());
195 // Passing root that is not prefix of the original file path.
196 Sym
.Definition
.FileURI
= testPathURI("home/File.h", Strings
);
197 // Check that the symbol is valid and passing the correct path works.
198 Serialized
= ProtobufMarshaller
.toProtobuf(Sym
);
199 ASSERT_TRUE(bool(Serialized
));
200 Deserialized
= ProtobufMarshaller
.fromProtobuf(*Serialized
);
201 ASSERT_TRUE(bool(Deserialized
));
202 EXPECT_STREQ(Deserialized
->Definition
.FileURI
,
203 testPathURI("home/File.h", Strings
));
204 // Fail with a wrong root.
205 Marshaller
WrongMarshaller(testPath("nothome/"), testPath("home/"));
206 Serialized
= WrongMarshaller
.toProtobuf(Sym
);
207 EXPECT_FALSE(Serialized
);
208 llvm::consumeError(Serialized
.takeError());
211 TEST(RemoteMarshallingTest
, RefSerialization
) {
213 Ref
.Kind
= clangd::RefKind::Spelled
| clangd::RefKind::Declaration
;
215 llvm::BumpPtrAllocator Arena
;
216 llvm::UniqueStringSaver
Strings(Arena
);
218 clangd::SymbolLocation Location
;
219 Location
.Start
.setLine(124);
220 Location
.Start
.setColumn(21);
221 Location
.End
.setLine(3213);
222 Location
.End
.setColumn(541);
223 Location
.FileURI
= testPathURI(
224 "llvm-project/llvm/clang-tools-extra/clangd/Protocol.h", Strings
);
225 Ref
.Location
= Location
;
227 Marshaller
ProtobufMarshaller(testPath("llvm-project/"),
228 testPath("llvm-project/"));
230 auto Serialized
= ProtobufMarshaller
.toProtobuf(Ref
);
231 ASSERT_TRUE(bool(Serialized
));
232 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(*Serialized
);
233 ASSERT_TRUE(bool(Deserialized
));
234 EXPECT_EQ(toYAML(Ref
), toYAML(*Deserialized
));
237 TEST(RemoteMarshallingTest
, IncludeHeaderURIs
) {
238 llvm::BumpPtrAllocator Arena
;
239 llvm::UniqueStringSaver
Strings(Arena
);
241 clangd::Symbol Sym
= createSymbol("remote/", Strings
);
243 clangd::Symbol::IncludeHeaderWithReferences Header
;
244 // Add only valid headers.
245 Header
.IncludeHeader
=
246 Strings
.save(URI::createFile(testPath("project/Header.h")).toString());
247 Header
.References
= 21;
248 Sym
.IncludeHeaders
.push_back(Header
);
249 Header
.IncludeHeader
= Strings
.save("<iostream>");
250 Header
.References
= 100;
251 Sym
.IncludeHeaders
.push_back(Header
);
252 Header
.IncludeHeader
= Strings
.save("\"cstdio\"");
253 Header
.References
= 200;
254 Sym
.IncludeHeaders
.push_back(Header
);
256 Marshaller
ProtobufMarshaller(testPath(""), testPath(""));
258 auto Serialized
= ProtobufMarshaller
.toProtobuf(Sym
);
259 ASSERT_TRUE(bool(Serialized
));
260 EXPECT_EQ(static_cast<size_t>(Serialized
->headers_size()),
261 Sym
.IncludeHeaders
.size());
262 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(*Serialized
);
263 ASSERT_TRUE(bool(Deserialized
));
264 Sym
.Origin
= SymbolOrigin::Remote
;
265 EXPECT_EQ(toYAML(Sym
), toYAML(*Deserialized
));
267 // This is an absolute path to a header: can not be transmitted over the wire.
268 Header
.IncludeHeader
= Strings
.save(testPath("project/include/Common.h"));
269 Header
.References
= 42;
270 Sym
.IncludeHeaders
.push_back(Header
);
271 Serialized
= ProtobufMarshaller
.toProtobuf(Sym
);
272 EXPECT_FALSE(bool(Serialized
));
273 llvm::consumeError(Serialized
.takeError());
275 // Remove last invalid header.
276 Sym
.IncludeHeaders
.pop_back();
277 // This is not a valid header: can not be transmitted over the wire;
278 Header
.IncludeHeader
= Strings
.save("NotAHeader");
279 Header
.References
= 5;
280 Sym
.IncludeHeaders
.push_back(Header
);
281 Serialized
= ProtobufMarshaller
.toProtobuf(Sym
);
282 EXPECT_FALSE(bool(Serialized
));
283 llvm::consumeError(Serialized
.takeError());
285 // Try putting an invalid header into already serialized symbol.
286 Sym
.IncludeHeaders
.pop_back();
287 Serialized
= ProtobufMarshaller
.toProtobuf(Sym
);
288 ASSERT_TRUE(bool(Serialized
));
289 HeaderWithReferences InvalidHeader
;
290 InvalidHeader
.set_header(convert_to_slash("/absolute/path/Header.h"));
291 InvalidHeader
.set_references(9000);
292 *Serialized
->add_headers() = InvalidHeader
;
293 Deserialized
= ProtobufMarshaller
.fromProtobuf(*Serialized
);
294 EXPECT_FALSE(bool(Deserialized
));
295 llvm::consumeError(Deserialized
.takeError());
298 TEST(RemoteMarshallingTest
, LookupRequestSerialization
) {
299 clangd::LookupRequest Request
;
300 Request
.IDs
.insert(llvm::cantFail(SymbolID::fromStr("0000000000000001")));
301 Request
.IDs
.insert(llvm::cantFail(SymbolID::fromStr("0000000000000002")));
303 Marshaller
ProtobufMarshaller(testPath("remote/"), testPath("local/"));
305 auto Serialized
= ProtobufMarshaller
.toProtobuf(Request
);
306 EXPECT_EQ(static_cast<unsigned>(Serialized
.ids_size()), Request
.IDs
.size());
307 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(&Serialized
);
308 ASSERT_TRUE(bool(Deserialized
));
309 EXPECT_EQ(Deserialized
->IDs
, Request
.IDs
);
312 TEST(RemoteMarshallingTest
, LookupRequestFailingSerialization
) {
313 clangd::LookupRequest Request
;
314 Marshaller
ProtobufMarshaller(testPath("remote/"), testPath("local/"));
315 auto Serialized
= ProtobufMarshaller
.toProtobuf(Request
);
316 Serialized
.add_ids("Invalid Symbol ID");
317 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(&Serialized
);
318 EXPECT_FALSE(bool(Deserialized
));
319 llvm::consumeError(Deserialized
.takeError());
322 TEST(RemoteMarshallingTest
, FuzzyFindRequestSerialization
) {
323 clangd::FuzzyFindRequest Request
;
324 Request
.ProximityPaths
= {testPath("local/Header.h"),
325 testPath("local/subdir/OtherHeader.h"),
326 testPath("remote/File.h"), "Not a Path."};
327 Marshaller
ProtobufMarshaller(testPath("remote/"), testPath("local/"));
328 auto Serialized
= ProtobufMarshaller
.toProtobuf(Request
);
329 EXPECT_EQ(Serialized
.proximity_paths_size(), 2);
330 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(&Serialized
);
331 ASSERT_TRUE(bool(Deserialized
));
332 EXPECT_THAT(Deserialized
->ProximityPaths
,
333 testing::ElementsAre(testPath("remote/Header.h"),
334 testPath("remote/subdir/OtherHeader.h")));
337 TEST(RemoteMarshallingTest
, RefsRequestSerialization
) {
338 clangd::RefsRequest Request
;
339 Request
.IDs
.insert(llvm::cantFail(SymbolID::fromStr("0000000000000001")));
340 Request
.IDs
.insert(llvm::cantFail(SymbolID::fromStr("0000000000000002")));
342 Request
.Limit
= 9000;
343 Request
.Filter
= RefKind::Spelled
| RefKind::Declaration
;
345 Marshaller
ProtobufMarshaller(testPath("remote/"), testPath("local/"));
347 auto Serialized
= ProtobufMarshaller
.toProtobuf(Request
);
348 EXPECT_EQ(static_cast<unsigned>(Serialized
.ids_size()), Request
.IDs
.size());
349 EXPECT_EQ(Serialized
.limit(), Request
.Limit
);
350 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(&Serialized
);
351 ASSERT_TRUE(bool(Deserialized
));
352 EXPECT_EQ(Deserialized
->IDs
, Request
.IDs
);
353 ASSERT_TRUE(Deserialized
->Limit
);
354 EXPECT_EQ(*Deserialized
->Limit
, Request
.Limit
);
355 EXPECT_EQ(Deserialized
->Filter
, Request
.Filter
);
358 TEST(RemoteMarshallingTest
, RefsRequestFailingSerialization
) {
359 clangd::RefsRequest Request
;
360 Marshaller
ProtobufMarshaller(testPath("remote/"), testPath("local/"));
361 auto Serialized
= ProtobufMarshaller
.toProtobuf(Request
);
362 Serialized
.add_ids("Invalid Symbol ID");
363 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(&Serialized
);
364 EXPECT_FALSE(bool(Deserialized
));
365 llvm::consumeError(Deserialized
.takeError());
368 TEST(RemoteMarshallingTest
, RelationsRequestSerialization
) {
369 clangd::RelationsRequest Request
;
370 Request
.Subjects
.insert(
371 llvm::cantFail(SymbolID::fromStr("0000000000000001")));
372 Request
.Subjects
.insert(
373 llvm::cantFail(SymbolID::fromStr("0000000000000002")));
375 Request
.Limit
= 9000;
376 Request
.Predicate
= RelationKind::BaseOf
;
378 Marshaller
ProtobufMarshaller(testPath("remote/"), testPath("local/"));
380 auto Serialized
= ProtobufMarshaller
.toProtobuf(Request
);
381 EXPECT_EQ(static_cast<unsigned>(Serialized
.subjects_size()),
382 Request
.Subjects
.size());
383 EXPECT_EQ(Serialized
.limit(), Request
.Limit
);
384 EXPECT_EQ(static_cast<RelationKind
>(Serialized
.predicate()),
386 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(&Serialized
);
387 ASSERT_TRUE(bool(Deserialized
));
388 EXPECT_EQ(Deserialized
->Subjects
, Request
.Subjects
);
389 ASSERT_TRUE(Deserialized
->Limit
);
390 EXPECT_EQ(*Deserialized
->Limit
, Request
.Limit
);
391 EXPECT_EQ(Deserialized
->Predicate
, Request
.Predicate
);
394 TEST(RemoteMarshallingTest
, RelationsRequestFailingSerialization
) {
395 RelationsRequest Serialized
;
396 Serialized
.add_subjects("ZZZZZZZZZZZZZZZZ");
397 Marshaller
ProtobufMarshaller(testPath("remote/"), testPath("local/"));
398 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(&Serialized
);
399 EXPECT_FALSE(bool(Deserialized
));
400 llvm::consumeError(Deserialized
.takeError());
403 TEST(RemoteMarshallingTest
, RelationsSerializion
) {
404 llvm::BumpPtrAllocator Arena
;
405 llvm::UniqueStringSaver
Strings(Arena
);
407 clangd::Symbol Sym
= createSymbol("remote/", Strings
);
408 SymbolID ID
= llvm::cantFail(SymbolID::fromStr("0000000000000002"));
409 Marshaller
ProtobufMarshaller(testPath("remote/"), testPath("local/"));
410 auto Serialized
= ProtobufMarshaller
.toProtobuf(ID
, Sym
);
411 ASSERT_TRUE(bool(Serialized
));
412 auto Deserialized
= ProtobufMarshaller
.fromProtobuf(*Serialized
);
413 ASSERT_TRUE(bool(Deserialized
));
414 EXPECT_THAT(Deserialized
->first
, ID
);
415 EXPECT_THAT(Deserialized
->second
.ID
, Sym
.ID
);
418 TEST(RemoteMarshallingTest
, RelativePathToURITranslation
) {
419 Marshaller
ProtobufMarshaller(/*RemoteIndexRoot=*/"",
420 /*LocalIndexRoot=*/testPath("home/project/"));
421 auto URIString
= ProtobufMarshaller
.relativePathToURI("lib/File.cpp");
422 ASSERT_TRUE(bool(URIString
));
423 // RelativePath can not be absolute.
424 URIString
= ProtobufMarshaller
.relativePathToURI("/lib/File.cpp");
425 EXPECT_FALSE(bool(URIString
));
426 llvm::consumeError(URIString
.takeError());
427 // RelativePath can not be empty.
428 URIString
= ProtobufMarshaller
.relativePathToURI(std::string());
429 EXPECT_FALSE(bool(URIString
));
430 llvm::consumeError(URIString
.takeError());
433 TEST(RemoteMarshallingTest
, URIToRelativePathTranslation
) {
434 llvm::BumpPtrAllocator Arena
;
435 llvm::UniqueStringSaver
Strings(Arena
);
436 Marshaller
ProtobufMarshaller(/*RemoteIndexRoot=*/testPath("remote/project/"),
437 /*LocalIndexRoot=*/"");
438 auto RelativePath
= ProtobufMarshaller
.uriToRelativePath(
439 testPathURI("remote/project/lib/File.cpp", Strings
));
440 ASSERT_TRUE(bool(RelativePath
));
441 // RemoteIndexRoot has to be be a prefix of the file path.
442 Marshaller
WrongMarshaller(
443 /*RemoteIndexRoot=*/testPath("remote/other/project/"),
444 /*LocalIndexRoot=*/"");
445 RelativePath
= WrongMarshaller
.uriToRelativePath(
446 testPathURI("remote/project/lib/File.cpp", Strings
));
447 EXPECT_FALSE(bool(RelativePath
));
448 llvm::consumeError(RelativePath
.takeError());
452 } // namespace remote
453 } // namespace clangd