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