xref: /llvm-project/clang-tools-extra/clangd/unittests/remote/MarshallingTests.cpp (revision 7d591e123e0eb2d3415840de9c2b45c3742f0eaa)
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 "TestFS.h"
11 #include "index/Index.h"
12 #include "index/Ref.h"
13 #include "index/Serialization.h"
14 #include "index/Symbol.h"
15 #include "index/SymbolID.h"
16 #include "index/SymbolLocation.h"
17 #include "index/remote/marshalling/Marshalling.h"
18 #include "clang/Index/IndexSymbol.h"
19 #include "llvm/ADT/SmallString.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/StringSaver.h"
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include <cstring>
26 
27 namespace clang {
28 namespace clangd {
29 namespace remote {
30 namespace {
31 
32 using llvm::sys::path::convert_to_slash;
33 
34 const char *testPathURI(llvm::StringRef Path,
35                         llvm::UniqueStringSaver &Strings) {
36   auto URI = URI::createFile(testPath(Path));
37   return Strings.save(URI.toString()).begin();
38 }
39 
40 TEST(RemoteMarshallingTest, URITranslation) {
41   llvm::BumpPtrAllocator Arena;
42   llvm::UniqueStringSaver Strings(Arena);
43   Marshaller ProtobufMarshaller(
44       testPath("remote/machine/projects/llvm-project/"),
45       testPath("home/my-projects/llvm-project/"));
46   clangd::Ref Original;
47   Original.Location.FileURI =
48       testPathURI("remote/machine/projects/llvm-project/clang-tools-extra/"
49                   "clangd/unittests/remote/MarshallingTests.cpp",
50                   Strings);
51   auto Serialized = ProtobufMarshaller.toProtobuf(Original);
52   EXPECT_TRUE(Serialized);
53   EXPECT_EQ(Serialized->location().file_path(),
54             "clang-tools-extra/clangd/unittests/remote/MarshallingTests.cpp");
55   auto Deserialized = ProtobufMarshaller.fromProtobuf(*Serialized);
56   EXPECT_TRUE(Deserialized);
57   EXPECT_STREQ(Deserialized->Location.FileURI,
58                testPathURI("home/my-projects/llvm-project/clang-tools-extra/"
59                            "clangd/unittests/remote/MarshallingTests.cpp",
60                            Strings));
61 
62   // Can't have empty paths.
63   *Serialized->mutable_location()->mutable_file_path() = std::string();
64   Deserialized = ProtobufMarshaller.fromProtobuf(*Serialized);
65   EXPECT_FALSE(Deserialized);
66 
67   clangd::Ref WithInvalidURI;
68   // Invalid URI results in serialization failure.
69   WithInvalidURI.Location.FileURI = "This is not a URI";
70   Serialized = ProtobufMarshaller.toProtobuf(WithInvalidURI);
71   EXPECT_FALSE(Serialized);
72 
73   // Can not use URIs with scheme different from "file".
74   auto UnittestURI =
75       URI::create(testPath("project/lib/HelloWorld.cpp"), "unittest");
76   EXPECT_TRUE(bool(UnittestURI));
77   WithInvalidURI.Location.FileURI =
78       Strings.save(UnittestURI->toString()).begin();
79   Serialized = ProtobufMarshaller.toProtobuf(WithInvalidURI);
80   EXPECT_FALSE(Serialized);
81 
82   // Paths transmitted over the wire can not be absolute, they have to be
83   // relative.
84   Ref WithAbsolutePath;
85   *WithAbsolutePath.mutable_location()->mutable_file_path() =
86       "/usr/local/user/home/HelloWorld.cpp";
87   Deserialized = ProtobufMarshaller.fromProtobuf(WithAbsolutePath);
88   EXPECT_FALSE(Deserialized);
89 }
90 
91 TEST(RemoteMarshallingTest, SymbolSerialization) {
92   clangd::Symbol Sym;
93 
94   auto ID = SymbolID::fromStr("057557CEBF6E6B2D");
95   EXPECT_TRUE(bool(ID));
96   Sym.ID = *ID;
97 
98   index::SymbolInfo Info;
99   Info.Kind = index::SymbolKind::Function;
100   Info.SubKind = index::SymbolSubKind::AccessorGetter;
101   Info.Lang = index::SymbolLanguage::CXX;
102   Info.Properties = static_cast<index::SymbolPropertySet>(
103       index::SymbolProperty::TemplateSpecialization);
104   Sym.SymInfo = Info;
105 
106   llvm::BumpPtrAllocator Arena;
107   llvm::UniqueStringSaver Strings(Arena);
108 
109   Sym.Name = Strings.save("Foo");
110   Sym.Scope = Strings.save("llvm::foo::bar::");
111 
112   clangd::SymbolLocation Location;
113   Location.Start.setLine(1);
114   Location.Start.setColumn(15);
115   Location.End.setLine(3);
116   Location.End.setColumn(121);
117   Location.FileURI = testPathURI("home/Definition.cpp", Strings);
118   Sym.Definition = Location;
119 
120   Location.Start.setLine(42);
121   Location.Start.setColumn(31);
122   Location.End.setLine(20);
123   Location.End.setColumn(400);
124   Location.FileURI = testPathURI("home/Declaration.h", Strings);
125   Sym.CanonicalDeclaration = Location;
126 
127   Sym.References = 9000;
128   Sym.Origin = clangd::SymbolOrigin::Static;
129   Sym.Signature = Strings.save("(int X, char Y, Type T)");
130   Sym.TemplateSpecializationArgs = Strings.save("<int, char, bool, Type>");
131   Sym.CompletionSnippetSuffix =
132       Strings.save("({1: int X}, {2: char Y}, {3: Type T})");
133   Sym.Documentation = Strings.save("This is my amazing Foo constructor!");
134   Sym.ReturnType = Strings.save("Foo");
135 
136   Sym.Flags = clangd::Symbol::SymbolFlag::IndexedForCodeCompletion;
137 
138   Marshaller ProtobufMarshaller(testPath("home/"), testPath("home/"));
139 
140   // Check that symbols are exactly the same if the path to indexed project is
141   // the same on indexing machine and the client.
142   auto Serialized = ProtobufMarshaller.toProtobuf(Sym);
143   EXPECT_TRUE(Serialized);
144   auto Deserialized = ProtobufMarshaller.fromProtobuf(*Serialized);
145   EXPECT_TRUE(Deserialized);
146   EXPECT_EQ(toYAML(Sym), toYAML(*Deserialized));
147   // Serialized paths are relative and have UNIX slashes.
148   EXPECT_EQ(convert_to_slash(Serialized->definition().file_path(),
149                              llvm::sys::path::Style::posix),
150             Serialized->definition().file_path());
151   EXPECT_TRUE(
152       llvm::sys::path::is_relative(Serialized->definition().file_path()));
153 
154   // Missing definition is OK.
155   Sym.Definition = clangd::SymbolLocation();
156   Serialized = ProtobufMarshaller.toProtobuf(Sym);
157   EXPECT_TRUE(Serialized);
158   Deserialized = ProtobufMarshaller.fromProtobuf(*Serialized);
159   EXPECT_TRUE(Deserialized);
160 
161   // Relative path is absolute.
162   *Serialized->mutable_canonical_declaration()->mutable_file_path() =
163       convert_to_slash("/path/to/Declaration.h");
164   Deserialized = ProtobufMarshaller.fromProtobuf(*Serialized);
165   EXPECT_FALSE(Deserialized);
166 
167   // Fail with an invalid URI.
168   Location.FileURI = "Not A URI";
169   Sym.Definition = Location;
170   Serialized = ProtobufMarshaller.toProtobuf(Sym);
171   EXPECT_FALSE(Serialized);
172 
173   // Schemes other than "file" can not be used.
174   auto UnittestURI = URI::create(testPath("home/SomePath.h"), "unittest");
175   EXPECT_TRUE(bool(UnittestURI));
176   Location.FileURI = Strings.save(UnittestURI->toString()).begin();
177   Sym.Definition = Location;
178   Serialized = ProtobufMarshaller.toProtobuf(Sym);
179   EXPECT_FALSE(Serialized);
180 
181   // Passing root that is not prefix of the original file path.
182   Location.FileURI = testPathURI("home/File.h", Strings);
183   Sym.Definition = Location;
184   // Check that the symbol is valid and passing the correct path works.
185   Serialized = ProtobufMarshaller.toProtobuf(Sym);
186   EXPECT_TRUE(Serialized);
187   Deserialized = ProtobufMarshaller.fromProtobuf(*Serialized);
188   EXPECT_TRUE(Deserialized);
189   EXPECT_STREQ(Deserialized->Definition.FileURI,
190                testPathURI("home/File.h", Strings));
191   // Fail with a wrong root.
192   Marshaller WrongMarshaller(testPath("nothome/"), testPath("home/"));
193   Serialized = WrongMarshaller.toProtobuf(Sym);
194   EXPECT_FALSE(Serialized);
195 }
196 
197 TEST(RemoteMarshallingTest, RefSerialization) {
198   clangd::Ref Ref;
199   Ref.Kind = clangd::RefKind::Spelled | clangd::RefKind::Declaration;
200 
201   llvm::BumpPtrAllocator Arena;
202   llvm::UniqueStringSaver Strings(Arena);
203 
204   clangd::SymbolLocation Location;
205   Location.Start.setLine(124);
206   Location.Start.setColumn(21);
207   Location.End.setLine(3213);
208   Location.End.setColumn(541);
209   Location.FileURI = testPathURI(
210       "llvm-project/llvm/clang-tools-extra/clangd/Protocol.h", Strings);
211   Ref.Location = Location;
212 
213   Marshaller ProtobufMarshaller(testPath("llvm-project/"),
214                                 testPath("llvm-project/"));
215 
216   auto Serialized = ProtobufMarshaller.toProtobuf(Ref);
217   EXPECT_TRUE(Serialized);
218   auto Deserialized = ProtobufMarshaller.fromProtobuf(*Serialized);
219   EXPECT_TRUE(Deserialized);
220   EXPECT_EQ(toYAML(Ref), toYAML(*Deserialized));
221 }
222 
223 TEST(RemoteMarshallingTest, IncludeHeaderURIs) {
224   llvm::BumpPtrAllocator Arena;
225   llvm::UniqueStringSaver Strings(Arena);
226 
227   llvm::SmallVector<clangd::Symbol::IncludeHeaderWithReferences, 1>
228       ValidHeaders;
229   clangd::Symbol::IncludeHeaderWithReferences Header;
230   Header.IncludeHeader = Strings.save(
231       URI::createFile("/usr/local/user/home/project/Header.h").toString());
232   Header.References = 21;
233   ValidHeaders.push_back(Header);
234   Header.IncludeHeader = Strings.save("<iostream>");
235   Header.References = 100;
236   ValidHeaders.push_back(Header);
237   Header.IncludeHeader = Strings.save("\"cstdio\"");
238   Header.References = 200;
239   ValidHeaders.push_back(Header);
240 
241   llvm::SmallVector<clangd::Symbol::IncludeHeaderWithReferences, 1>
242       InvalidHeaders;
243   // This is an absolute path to a header: can not be transmitted over the wire.
244   Header.IncludeHeader = Strings.save(testPath("project/include/Common.h"));
245   Header.References = 42;
246   InvalidHeaders.push_back(Header);
247   // This is not a valid header: can not be transmitted over the wire;
248   Header.IncludeHeader = Strings.save("NotAHeader");
249   Header.References = 5;
250   InvalidHeaders.push_back(Header);
251 
252   clangd::Symbol Sym;
253   // Fill in definition and declaration, Symbool will be invalid otherwise.
254   clangd::SymbolLocation Location;
255   Location.Start.setLine(1);
256   Location.Start.setColumn(2);
257   Location.End.setLine(3);
258   Location.End.setColumn(4);
259   Location.FileURI = testPathURI("File.h", Strings);
260   Sym.Definition = Location;
261   Sym.CanonicalDeclaration = Location;
262 
263   // Try to serialize all headers but only valid ones will end up in Protobuf
264   // message.
265   auto AllHeaders = ValidHeaders;
266   AllHeaders.insert(AllHeaders.end(), InvalidHeaders.begin(),
267                     InvalidHeaders.end());
268   Sym.IncludeHeaders = AllHeaders;
269 
270   Marshaller ProtobufMarshaller(convert_to_slash("/"), convert_to_slash("/"));
271 
272   auto Serialized = ProtobufMarshaller.toProtobuf(Sym);
273   EXPECT_EQ(static_cast<size_t>(Serialized->headers_size()),
274             ValidHeaders.size());
275   EXPECT_TRUE(Serialized);
276   auto Deserialized = ProtobufMarshaller.fromProtobuf(*Serialized);
277   EXPECT_TRUE(Deserialized);
278 
279   Sym.IncludeHeaders = ValidHeaders;
280   EXPECT_EQ(toYAML(Sym), toYAML(*Deserialized));
281 }
282 
283 TEST(RemoteMarshallingTest, FuzzyFindRequestSerialization) {
284   clangd::FuzzyFindRequest Request;
285   Request.ProximityPaths = {testPath("local/Header.h"),
286                             testPath("local/subdir/OtherHeader.h"),
287                             testPath("remote/File.h"), "Not a Path."};
288   Marshaller ProtobufMarshaller(testPath("remote/"), testPath("local/"));
289   auto Serialized = ProtobufMarshaller.toProtobuf(Request);
290   EXPECT_EQ(Serialized.proximity_paths_size(), 2);
291   auto Deserialized = ProtobufMarshaller.fromProtobuf(&Serialized);
292   EXPECT_THAT(Deserialized.ProximityPaths,
293               testing::ElementsAre(testPath("remote/Header.h"),
294                                    testPath("remote/subdir/OtherHeader.h")));
295 }
296 
297 TEST(RemoteMarshallingTest, RelativePathToURITranslation) {
298   Marshaller ProtobufMarshaller(/*RemoteIndexRoot=*/"",
299                                 /*LocalIndexRoot=*/testPath("home/project/"));
300   EXPECT_TRUE(ProtobufMarshaller.relativePathToURI("lib/File.cpp"));
301   // RelativePath can not be absolute.
302   EXPECT_FALSE(ProtobufMarshaller.relativePathToURI("/lib/File.cpp"));
303   // RelativePath can not be empty.
304   EXPECT_FALSE(ProtobufMarshaller.relativePathToURI(std::string()));
305 }
306 
307 TEST(RemoteMarshallingTest, URIToRelativePathTranslation) {
308   llvm::BumpPtrAllocator Arena;
309   llvm::UniqueStringSaver Strings(Arena);
310   Marshaller ProtobufMarshaller(/*RemoteIndexRoot=*/testPath("remote/project/"),
311                                 /*LocalIndexRoot=*/"");
312   EXPECT_TRUE(ProtobufMarshaller.uriToRelativePath(
313       testPathURI("remote/project/lib/File.cpp", Strings)));
314   // RemoteIndexRoot has to be be a prefix of the file path.
315   Marshaller WrongMarshaller(
316       /*RemoteIndexRoot=*/testPath("remote/other/project/"),
317       /*LocalIndexRoot=*/"");
318   EXPECT_FALSE(WrongMarshaller.uriToRelativePath(
319       testPathURI("remote/project/lib/File.cpp", Strings)));
320 }
321 
322 } // namespace
323 } // namespace remote
324 } // namespace clangd
325 } // namespace clang
326