• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
32 namespace clang {
33 namespace clangd {
34 namespace remote {
35 namespace {
36 
37 using llvm::sys::path::convert_to_slash;
38 
testPathURI(llvm::StringRef Path,llvm::UniqueStringSaver & Strings)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();
43 }
44 
createSymbol(llvm::StringRef PathPrefix,llvm::UniqueStringSaver & Strings)45 clangd::Symbol createSymbol(llvm::StringRef PathPrefix,
46                             llvm::UniqueStringSaver &Strings) {
47   clangd::Symbol Sym;
48   Sym.ID = llvm::cantFail(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("Foo");
59   Sym.Scope = Strings.save("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(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(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("(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");
84 
85   Sym.Flags = clangd::Symbol::SymbolFlag::IndexedForCodeCompletion;
86 
87   return Sym;
88 }
89 
TEST(RemoteMarshallingTest,URITranslation)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/"));
96   clangd::Ref Original;
97   Original.Location.FileURI =
98       testPathURI("remote/machine/projects/llvm-project/clang-tools-extra/"
99                   "clangd/unittests/remote/MarshallingTests.cpp",
100                   Strings);
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",
110                            Strings));
111 
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());
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(WithInvalidURI);
122   EXPECT_FALSE(bool(DeserializedRef));
123   llvm::consumeError(DeserializedRef.takeError());
124 
125   // Can not use URIs with scheme different from "file".
126   auto UnittestURI =
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());
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(WithAbsolutePath);
141   EXPECT_FALSE(bool(Deserialized));
142   llvm::consumeError(Deserialized.takeError());
143 }
144 
TEST(RemoteMarshallingTest,SymbolSerialization)145 TEST(RemoteMarshallingTest, SymbolSerialization) {
146   llvm::BumpPtrAllocator Arena;
147   llvm::UniqueStringSaver Strings(Arena);
148 
149   clangd::Symbol Sym = createSymbol("home/", Strings);
150   Marshaller ProtobufMarshaller(testPath("home/"), testPath("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(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());
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(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/to/Declaration.h");
177   Deserialized = ProtobufMarshaller.fromProtobuf(*Serialized);
178   EXPECT_FALSE(bool(Deserialized));
179   llvm::consumeError(Deserialized.takeError());
180 
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());
186 
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());
194 
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());
209 }
210 
TEST(RemoteMarshallingTest,RefSerialization)211 TEST(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       "llvm-project/llvm/clang-tools-extra/clangd/Protocol.h", Strings);
225   Ref.Location = Location;
226 
227   Marshaller ProtobufMarshaller(testPath("llvm-project/"),
228                                 testPath("llvm-project/"));
229 
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));
235 }
236 
TEST(RemoteMarshallingTest,IncludeHeaderURIs)237 TEST(RemoteMarshallingTest, IncludeHeaderURIs) {
238   llvm::BumpPtrAllocator Arena;
239   llvm::UniqueStringSaver Strings(Arena);
240 
241   clangd::Symbol Sym = createSymbol("remote/", Strings);
242 
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);
255 
256   Marshaller ProtobufMarshaller(testPath(""), testPath(""));
257 
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));
266 
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());
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("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());
284 
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());
296 }
297 
TEST(RemoteMarshallingTest,LookupRequestSerialization)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")));
302 
303   Marshaller ProtobufMarshaller(testPath("remote/"), testPath("local/"));
304 
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);
310 }
311 
TEST(RemoteMarshallingTest,LookupRequestFailingSerialization)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());
320 }
321 
TEST(RemoteMarshallingTest,FuzzyFindRequestSerialization)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")));
335 }
336 
TEST(RemoteMarshallingTest,RefsRequestSerialization)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")));
341 
342   Request.Limit = 9000;
343   Request.Filter = RefKind::Spelled | RefKind::Declaration;
344 
345   Marshaller ProtobufMarshaller(testPath("remote/"), testPath("local/"));
346 
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);
356 }
357 
TEST(RemoteMarshallingTest,RefsRequestFailingSerialization)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());
366 }
367 
TEST(RemoteMarshallingTest,RelationsRequestSerialization)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")));
374 
375   Request.Limit = 9000;
376   Request.Predicate = RelationKind::BaseOf;
377 
378   Marshaller ProtobufMarshaller(testPath("remote/"), testPath("local/"));
379 
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()),
385             Request.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);
392 }
393 
TEST(RemoteMarshallingTest,RelationsRequestFailingSerialization)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());
401 }
402 
TEST(RemoteMarshallingTest,RelationsSerializion)403 TEST(RemoteMarshallingTest, RelationsSerializion) {
404   llvm::BumpPtrAllocator Arena;
405   llvm::UniqueStringSaver Strings(Arena);
406 
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);
416 }
417 
TEST(RemoteMarshallingTest,RelativePathToURITranslation)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());
431 }
432 
TEST(RemoteMarshallingTest,URIToRelativePathTranslation)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());
449 }
450 
451 } // namespace
452 } // namespace remote
453 } // namespace clangd
454 } // namespace clang
455