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