1//===--- MarshallingTests.cpp ------------------------------------*- C++-*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "../TestTU.h"
10#include "Index.pb.h"
11#include "TestFS.h"
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"
30#include <cstring>
31
32namespace clang {
33namespace clangd {
34namespace remote {
35namespace {
36
37using llvm::sys::path::convert_to_slash;
38
39const char *testPathURI(llvm::StringRef Path,
40 llvm::UniqueStringSaver &Strings) {
41 auto URI = URI::createFile(AbsolutePath: testPath(File: Path));
42 return Strings.save(S: URI.toString()).begin();
43}
44
45clangd::Symbol createSymbol(llvm::StringRef PathPrefix,
46 llvm::UniqueStringSaver &Strings) {
47 clangd::Symbol Sym;
48 Sym.ID = llvm::cantFail(ValOrErr: SymbolID::fromStr("057557CEBF6E6B2D"));
49
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);
56 Sym.SymInfo = Info;
57
58 Sym.Name = Strings.save(S: "Foo");
59 Sym.Scope = Strings.save(S: "llvm::foo::bar::");
60
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(Path: PathPrefix.str() + "Definition.cpp", Strings);
67 Sym.Definition = Location;
68
69 Location.Start.setLine(42);
70 Location.Start.setColumn(31);
71 Location.End.setLine(20);
72 Location.End.setColumn(400);
73 Location.FileURI = testPathURI(Path: PathPrefix.str() + "Declaration.h", Strings);
74 Sym.CanonicalDeclaration = Location;
75
76 Sym.References = 9000;
77 Sym.Origin = clangd::SymbolOrigin::Static;
78 Sym.Signature = Strings.save(S: "(int X, char Y, Type T)");
79 Sym.TemplateSpecializationArgs = Strings.save(S: "<int, char, bool, Type>");
80 Sym.CompletionSnippetSuffix =
81 Strings.save(S: "({1: int X}, {2: char Y}, {3: Type T})");
82 Sym.Documentation = Strings.save(S: "This is my amazing Foo constructor!");
83 Sym.ReturnType = Strings.save(S: "Foo");
84
85 Sym.Flags = clangd::Symbol::SymbolFlag::IndexedForCodeCompletion;
86
87 return Sym;
88}
89
90TEST(RemoteMarshallingTest, URITranslation) {
91 llvm::BumpPtrAllocator Arena;
92 llvm::UniqueStringSaver Strings(Arena);
93 Marshaller ProtobufMarshaller(
94 testPath(File: "remote/machine/projects/llvm-project/"),
95 testPath(File: "home/my-projects/llvm-project/"));
96 clangd::Ref Original;
97 Original.Location.FileURI =
98 testPathURI(Path: "remote/machine/projects/llvm-project/clang-tools-extra/"
99 "clangd/unittests/remote/MarshallingTests.cpp",
100 Strings);
101 auto Serialized = ProtobufMarshaller.toProtobuf(From: 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(Message: *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",
110 Strings));
111
112 // Can't have empty paths.
113 *Serialized->mutable_location()->mutable_file_path() = std::string();
114 Deserialized = ProtobufMarshaller.fromProtobuf(Message: *Serialized);
115 EXPECT_FALSE(bool(Deserialized));
116 llvm::consumeError(Err: Deserialized.takeError());
117
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(From: WithInvalidURI);
122 EXPECT_FALSE(bool(DeserializedRef));
123 llvm::consumeError(Err: DeserializedRef.takeError());
124
125 // Can not use URIs with scheme different from "file".
126 auto UnittestURI =
127 URI::create(AbsolutePath: testPath(File: "project/lib/HelloWorld.cpp"), Scheme: "unittest");
128 ASSERT_TRUE(bool(UnittestURI));
129 WithInvalidURI.Location.FileURI =
130 Strings.save(S: UnittestURI->toString()).begin();
131 auto DeserializedSymbol = ProtobufMarshaller.toProtobuf(From: WithInvalidURI);
132 EXPECT_FALSE(bool(DeserializedSymbol));
133 llvm::consumeError(Err: DeserializedSymbol.takeError());
134
135 // Paths transmitted over the wire can not be absolute, they have to be
136 // relative.
137 Ref WithAbsolutePath;
138 *WithAbsolutePath.mutable_location()->mutable_file_path() =
139 "/usr/local/user/home/HelloWorld.cpp";
140 Deserialized = ProtobufMarshaller.fromProtobuf(Message: WithAbsolutePath);
141 EXPECT_FALSE(bool(Deserialized));
142 llvm::consumeError(Err: Deserialized.takeError());
143}
144
145TEST(RemoteMarshallingTest, SymbolSerialization) {
146 llvm::BumpPtrAllocator Arena;
147 llvm::UniqueStringSaver Strings(Arena);
148
149 clangd::Symbol Sym = createSymbol(PathPrefix: "home/", Strings);
150 Marshaller ProtobufMarshaller(testPath(File: "home/"), testPath(File: "home/"));
151
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(From: Sym);
155 ASSERT_TRUE(bool(Serialized));
156 auto Deserialized = ProtobufMarshaller.fromProtobuf(Message: *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());
165 EXPECT_TRUE(
166 llvm::sys::path::is_relative(Serialized->definition().file_path()));
167
168 // Missing definition is OK.
169 Sym.Definition = clangd::SymbolLocation();
170 Serialized = ProtobufMarshaller.toProtobuf(From: Sym);
171 ASSERT_TRUE(bool(Serialized));
172 ASSERT_TRUE(bool(ProtobufMarshaller.fromProtobuf(*Serialized)));
173
174 // Relative path is absolute.
175 *Serialized->mutable_canonical_declaration()->mutable_file_path() =
176 convert_to_slash(path: "/path/to/Declaration.h");
177 Deserialized = ProtobufMarshaller.fromProtobuf(Message: *Serialized);
178 EXPECT_FALSE(bool(Deserialized));
179 llvm::consumeError(Err: Deserialized.takeError());
180
181 // Fail with an invalid URI.
182 Sym.Definition.FileURI = "Not A URI";
183 Serialized = ProtobufMarshaller.toProtobuf(From: Sym);
184 EXPECT_FALSE(bool(Serialized));
185 llvm::consumeError(Err: Serialized.takeError());
186
187 // Schemes other than "file" can not be used.
188 auto UnittestURI = URI::create(AbsolutePath: testPath(File: "home/SomePath.h"), Scheme: "unittest");
189 ASSERT_TRUE(bool(UnittestURI));
190 Sym.Definition.FileURI = Strings.save(S: UnittestURI->toString()).begin();
191 Serialized = ProtobufMarshaller.toProtobuf(From: Sym);
192 EXPECT_FALSE(bool(Serialized));
193 llvm::consumeError(Err: Serialized.takeError());
194
195 // Passing root that is not prefix of the original file path.
196 Sym.Definition.FileURI = testPathURI(Path: "home/File.h", Strings);
197 // Check that the symbol is valid and passing the correct path works.
198 Serialized = ProtobufMarshaller.toProtobuf(From: Sym);
199 ASSERT_TRUE(bool(Serialized));
200 Deserialized = ProtobufMarshaller.fromProtobuf(Message: *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(File: "nothome/"), testPath(File: "home/"));
206 Serialized = WrongMarshaller.toProtobuf(From: Sym);
207 EXPECT_FALSE(Serialized);
208 llvm::consumeError(Err: Serialized.takeError());
209}
210
211TEST(RemoteMarshallingTest, RefSerialization) {
212 clangd::Ref Ref;
213 Ref.Kind = clangd::RefKind::Spelled | clangd::RefKind::Declaration;
214
215 llvm::BumpPtrAllocator Arena;
216 llvm::UniqueStringSaver Strings(Arena);
217
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 Path: "llvm-project/llvm/clang-tools-extra/clangd/Protocol.h", Strings);
225 Ref.Location = Location;
226
227 Marshaller ProtobufMarshaller(testPath(File: "llvm-project/"),
228 testPath(File: "llvm-project/"));
229
230 auto Serialized = ProtobufMarshaller.toProtobuf(From: Ref);
231 ASSERT_TRUE(bool(Serialized));
232 auto Deserialized = ProtobufMarshaller.fromProtobuf(Message: *Serialized);
233 ASSERT_TRUE(bool(Deserialized));
234 EXPECT_EQ(toYAML(Ref), toYAML(*Deserialized));
235}
236
237TEST(RemoteMarshallingTest, IncludeHeaderURIs) {
238 llvm::BumpPtrAllocator Arena;
239 llvm::UniqueStringSaver Strings(Arena);
240
241 clangd::Symbol Sym = createSymbol(PathPrefix: "remote/", Strings);
242
243 clangd::Symbol::IncludeHeaderWithReferences Header;
244 // Add only valid headers.
245 Header.IncludeHeader =
246 Strings.save(S: URI::createFile(AbsolutePath: testPath(File: "project/Header.h")).toString());
247 Header.References = 21;
248 Sym.IncludeHeaders.push_back(Elt: Header);
249 Header.IncludeHeader = Strings.save(S: "<iostream>");
250 Header.References = 100;
251 Sym.IncludeHeaders.push_back(Elt: Header);
252 Header.IncludeHeader = Strings.save(S: "\"cstdio\"");
253 Header.References = 200;
254 Sym.IncludeHeaders.push_back(Elt: Header);
255
256 Marshaller ProtobufMarshaller(testPath(File: ""), testPath(File: ""));
257
258 auto Serialized = ProtobufMarshaller.toProtobuf(From: 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(Message: *Serialized);
263 ASSERT_TRUE(bool(Deserialized));
264 Sym.Origin = SymbolOrigin::Remote;
265 EXPECT_EQ(toYAML(Sym), toYAML(*Deserialized));
266
267 // This is an absolute path to a header: can not be transmitted over the wire.
268 Header.IncludeHeader = Strings.save(S: testPath(File: "project/include/Common.h"));
269 Header.References = 42;
270 Sym.IncludeHeaders.push_back(Elt: Header);
271 Serialized = ProtobufMarshaller.toProtobuf(From: Sym);
272 EXPECT_FALSE(bool(Serialized));
273 llvm::consumeError(Err: Serialized.takeError());
274
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(S: "NotAHeader");
279 Header.References = 5;
280 Sym.IncludeHeaders.push_back(Elt: Header);
281 Serialized = ProtobufMarshaller.toProtobuf(From: Sym);
282 EXPECT_FALSE(bool(Serialized));
283 llvm::consumeError(Err: Serialized.takeError());
284
285 // Try putting an invalid header into already serialized symbol.
286 Sym.IncludeHeaders.pop_back();
287 Serialized = ProtobufMarshaller.toProtobuf(From: Sym);
288 ASSERT_TRUE(bool(Serialized));
289 HeaderWithReferences InvalidHeader;
290 InvalidHeader.set_header(convert_to_slash(path: "/absolute/path/Header.h"));
291 InvalidHeader.set_references(9000);
292 *Serialized->add_headers() = InvalidHeader;
293 Deserialized = ProtobufMarshaller.fromProtobuf(Message: *Serialized);
294 EXPECT_FALSE(bool(Deserialized));
295 llvm::consumeError(Err: Deserialized.takeError());
296}
297
298TEST(RemoteMarshallingTest, LookupRequestSerialization) {
299 clangd::LookupRequest Request;
300 Request.IDs.insert(V: llvm::cantFail(ValOrErr: SymbolID::fromStr("0000000000000001")));
301 Request.IDs.insert(V: llvm::cantFail(ValOrErr: SymbolID::fromStr("0000000000000002")));
302
303 Marshaller ProtobufMarshaller(testPath(File: "remote/"), testPath(File: "local/"));
304
305 auto Serialized = ProtobufMarshaller.toProtobuf(From: Request);
306 EXPECT_EQ(static_cast<unsigned>(Serialized.ids_size()), Request.IDs.size());
307 auto Deserialized = ProtobufMarshaller.fromProtobuf(Message: &Serialized);
308 ASSERT_TRUE(bool(Deserialized));
309 EXPECT_EQ(Deserialized->IDs, Request.IDs);
310}
311
312TEST(RemoteMarshallingTest, LookupRequestFailingSerialization) {
313 clangd::LookupRequest Request;
314 Marshaller ProtobufMarshaller(testPath(File: "remote/"), testPath(File: "local/"));
315 auto Serialized = ProtobufMarshaller.toProtobuf(From: Request);
316 Serialized.add_ids("Invalid Symbol ID");
317 auto Deserialized = ProtobufMarshaller.fromProtobuf(Message: &Serialized);
318 EXPECT_FALSE(bool(Deserialized));
319 llvm::consumeError(Err: Deserialized.takeError());
320}
321
322TEST(RemoteMarshallingTest, FuzzyFindRequestSerialization) {
323 clangd::FuzzyFindRequest Request;
324 Request.ProximityPaths = {testPath(File: "local/Header.h"),
325 testPath(File: "local/subdir/OtherHeader.h"),
326 testPath(File: "remote/File.h"), "Not a Path."};
327 Marshaller ProtobufMarshaller(testPath(File: "remote/"), testPath(File: "local/"));
328 auto Serialized = ProtobufMarshaller.toProtobuf(From: Request);
329 EXPECT_EQ(Serialized.proximity_paths_size(), 2);
330 auto Deserialized = ProtobufMarshaller.fromProtobuf(Message: &Serialized);
331 ASSERT_TRUE(bool(Deserialized));
332 EXPECT_THAT(Deserialized->ProximityPaths,
333 testing::ElementsAre(testPath("remote/Header.h"),
334 testPath("remote/subdir/OtherHeader.h")));
335}
336
337TEST(RemoteMarshallingTest, RefsRequestSerialization) {
338 clangd::RefsRequest Request;
339 Request.IDs.insert(V: llvm::cantFail(ValOrErr: SymbolID::fromStr("0000000000000001")));
340 Request.IDs.insert(V: llvm::cantFail(ValOrErr: SymbolID::fromStr("0000000000000002")));
341
342 Request.Limit = 9000;
343 Request.Filter = RefKind::Spelled | RefKind::Declaration;
344
345 Marshaller ProtobufMarshaller(testPath(File: "remote/"), testPath(File: "local/"));
346
347 auto Serialized = ProtobufMarshaller.toProtobuf(From: 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(Message: &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);
356}
357
358TEST(RemoteMarshallingTest, RefsRequestFailingSerialization) {
359 clangd::RefsRequest Request;
360 Marshaller ProtobufMarshaller(testPath(File: "remote/"), testPath(File: "local/"));
361 auto Serialized = ProtobufMarshaller.toProtobuf(From: Request);
362 Serialized.add_ids("Invalid Symbol ID");
363 auto Deserialized = ProtobufMarshaller.fromProtobuf(Message: &Serialized);
364 EXPECT_FALSE(bool(Deserialized));
365 llvm::consumeError(Err: Deserialized.takeError());
366}
367
368TEST(RemoteMarshallingTest, RelationsRequestSerialization) {
369 clangd::RelationsRequest Request;
370 Request.Subjects.insert(
371 V: llvm::cantFail(ValOrErr: SymbolID::fromStr("0000000000000001")));
372 Request.Subjects.insert(
373 V: llvm::cantFail(ValOrErr: SymbolID::fromStr("0000000000000002")));
374
375 Request.Limit = 9000;
376 Request.Predicate = RelationKind::BaseOf;
377
378 Marshaller ProtobufMarshaller(testPath(File: "remote/"), testPath(File: "local/"));
379
380 auto Serialized = ProtobufMarshaller.toProtobuf(From: 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()),
385 Request.Predicate);
386 auto Deserialized = ProtobufMarshaller.fromProtobuf(Message: &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);
392}
393
394TEST(RemoteMarshallingTest, RelationsRequestFailingSerialization) {
395 RelationsRequest Serialized;
396 Serialized.add_subjects("ZZZZZZZZZZZZZZZZ");
397 Marshaller ProtobufMarshaller(testPath(File: "remote/"), testPath(File: "local/"));
398 auto Deserialized = ProtobufMarshaller.fromProtobuf(Message: &Serialized);
399 EXPECT_FALSE(bool(Deserialized));
400 llvm::consumeError(Err: Deserialized.takeError());
401}
402
403TEST(RemoteMarshallingTest, RelationsSerializion) {
404 llvm::BumpPtrAllocator Arena;
405 llvm::UniqueStringSaver Strings(Arena);
406
407 clangd::Symbol Sym = createSymbol(PathPrefix: "remote/", Strings);
408 SymbolID ID = llvm::cantFail(ValOrErr: SymbolID::fromStr("0000000000000002"));
409 Marshaller ProtobufMarshaller(testPath(File: "remote/"), testPath(File: "local/"));
410 auto Serialized = ProtobufMarshaller.toProtobuf(Subject: ID, Object: Sym);
411 ASSERT_TRUE(bool(Serialized));
412 auto Deserialized = ProtobufMarshaller.fromProtobuf(Message: *Serialized);
413 ASSERT_TRUE(bool(Deserialized));
414 EXPECT_THAT(Deserialized->first, ID);
415 EXPECT_THAT(Deserialized->second.ID, Sym.ID);
416}
417
418TEST(RemoteMarshallingTest, RelativePathToURITranslation) {
419 Marshaller ProtobufMarshaller(/*RemoteIndexRoot=*/"",
420 /*LocalIndexRoot=*/testPath(File: "home/project/"));
421 auto URIString = ProtobufMarshaller.relativePathToURI(RelativePath: "lib/File.cpp");
422 ASSERT_TRUE(bool(URIString));
423 // RelativePath can not be absolute.
424 URIString = ProtobufMarshaller.relativePathToURI(RelativePath: "/lib/File.cpp");
425 EXPECT_FALSE(bool(URIString));
426 llvm::consumeError(Err: URIString.takeError());
427 // RelativePath can not be empty.
428 URIString = ProtobufMarshaller.relativePathToURI(RelativePath: std::string());
429 EXPECT_FALSE(bool(URIString));
430 llvm::consumeError(Err: URIString.takeError());
431}
432
433TEST(RemoteMarshallingTest, URIToRelativePathTranslation) {
434 llvm::BumpPtrAllocator Arena;
435 llvm::UniqueStringSaver Strings(Arena);
436 Marshaller ProtobufMarshaller(/*RemoteIndexRoot=*/testPath(File: "remote/project/"),
437 /*LocalIndexRoot=*/"");
438 auto RelativePath = ProtobufMarshaller.uriToRelativePath(
439 URI: testPathURI(Path: "remote/project/lib/File.cpp", Strings));
440 ASSERT_TRUE(bool(RelativePath));
441 // RemoteIndexRoot has to be a prefix of the file path.
442 Marshaller WrongMarshaller(
443 /*RemoteIndexRoot=*/testPath(File: "remote/other/project/"),
444 /*LocalIndexRoot=*/"");
445 RelativePath = WrongMarshaller.uriToRelativePath(
446 URI: testPathURI(Path: "remote/project/lib/File.cpp", Strings));
447 EXPECT_FALSE(bool(RelativePath));
448 llvm::consumeError(Err: RelativePath.takeError());
449}
450
451} // namespace
452} // namespace remote
453} // namespace clangd
454} // namespace clang
455

source code of clang-tools-extra/clangd/unittests/remote/MarshallingTests.cpp