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