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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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