1 //===-- TextStubV1Tests.cpp - TBD V1 File Test ----------------------------===// 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 "TextStubHelpers.h" 10 #include "llvm/TextAPI/InterfaceFile.h" 11 #include "llvm/TextAPI/TextAPIReader.h" 12 #include "llvm/TextAPI/TextAPIWriter.h" 13 #include "gtest/gtest.h" 14 #include <string> 15 #include <vector> 16 17 using namespace llvm; 18 using namespace llvm::MachO; 19 20 static ExportedSymbol TBDv1Symbols[] = { 21 {SymbolKind::GlobalSymbol, "$ld$hide$os9.0$_sym1", false, false}, 22 {SymbolKind::GlobalSymbol, "_sym1", false, false}, 23 {SymbolKind::GlobalSymbol, "_sym2", false, false}, 24 {SymbolKind::GlobalSymbol, "_sym3", false, false}, 25 {SymbolKind::GlobalSymbol, "_sym4", false, false}, 26 {SymbolKind::GlobalSymbol, "_sym5", false, false}, 27 {SymbolKind::GlobalSymbol, "_tlv1", false, true}, 28 {SymbolKind::GlobalSymbol, "_tlv2", false, true}, 29 {SymbolKind::GlobalSymbol, "_tlv3", false, true}, 30 {SymbolKind::GlobalSymbol, "_weak1", true, false}, 31 {SymbolKind::GlobalSymbol, "_weak2", true, false}, 32 {SymbolKind::GlobalSymbol, "_weak3", true, false}, 33 {SymbolKind::ObjectiveCClass, "class1", false, false}, 34 {SymbolKind::ObjectiveCClass, "class2", false, false}, 35 {SymbolKind::ObjectiveCClass, "class3", false, false}, 36 {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar1", false, false}, 37 {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar2", false, false}, 38 {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar3", false, false}, 39 }; 40 41 namespace TBDv1 { 42 43 TEST(TBDv1, ReadFile) { 44 static const char TBDv1File1[] = 45 "---\n" 46 "archs: [ armv7, armv7s, armv7k, arm64 ]\n" 47 "platform: ios\n" 48 "install-name: Test.dylib\n" 49 "current-version: 2.3.4\n" 50 "compatibility-version: 1.0\n" 51 "swift-version: 1.1\n" 52 "exports:\n" 53 " - archs: [ armv7, armv7s, armv7k, arm64 ]\n" 54 " allowed-clients: [ clientA ]\n" 55 " re-exports: [ /usr/lib/libfoo.dylib ]\n" 56 " symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n" 57 " objc-classes: [ _class1, _class2 ]\n" 58 " objc-ivars: [ _class1._ivar1, _class1._ivar2 ]\n" 59 " weak-def-symbols: [ _weak1, _weak2 ]\n" 60 " thread-local-symbols: [ _tlv1, _tlv2 ]\n" 61 " - archs: [ armv7, armv7s, armv7k ]\n" 62 " symbols: [ _sym5 ]\n" 63 " objc-classes: [ _class3 ]\n" 64 " objc-ivars: [ _class1._ivar3 ]\n" 65 " weak-def-symbols: [ _weak3 ]\n" 66 " thread-local-symbols: [ _tlv3 ]\n" 67 "...\n"; 68 69 Expected<TBDFile> Result = 70 TextAPIReader::get(MemoryBufferRef(TBDv1File1, "Test.tbd")); 71 EXPECT_TRUE(!!Result); 72 TBDFile File = std::move(Result.get()); 73 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 74 auto Archs = AK_armv7 | AK_armv7s | AK_armv7k | AK_arm64; 75 auto Platform = PlatformKind::iOS; 76 TargetList Targets; 77 for (auto &&arch : Archs) 78 Targets.emplace_back(Target(arch, Platform)); 79 EXPECT_EQ(Archs, File->getArchitectures()); 80 EXPECT_EQ(File->getPlatforms().size(), 1U); 81 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 82 EXPECT_EQ(std::string("Test.dylib"), File->getInstallName()); 83 EXPECT_EQ(PackedVersion(2, 3, 4), File->getCurrentVersion()); 84 EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion()); 85 EXPECT_EQ(2U, File->getSwiftABIVersion()); 86 EXPECT_EQ(ObjCConstraintType::None, File->getObjCConstraint()); 87 EXPECT_TRUE(File->isTwoLevelNamespace()); 88 EXPECT_TRUE(File->isApplicationExtensionSafe()); 89 EXPECT_FALSE(File->isInstallAPI()); 90 InterfaceFileRef client("clientA", Targets); 91 InterfaceFileRef reexport("/usr/lib/libfoo.dylib", Targets); 92 EXPECT_EQ(1U, File->allowableClients().size()); 93 EXPECT_EQ(client, File->allowableClients().front()); 94 EXPECT_EQ(1U, File->reexportedLibraries().size()); 95 EXPECT_EQ(reexport, File->reexportedLibraries().front()); 96 97 ExportedSymbolSeq Exports; 98 for (const auto *Sym : File->symbols()) { 99 EXPECT_FALSE(Sym->isWeakReferenced()); 100 EXPECT_FALSE(Sym->isUndefined()); 101 Exports.emplace_back( 102 ExportedSymbol{Sym->getKind(), std::string(Sym->getName()), 103 Sym->isWeakDefined(), Sym->isThreadLocalValue()}); 104 } 105 llvm::sort(Exports.begin(), Exports.end()); 106 107 EXPECT_EQ(sizeof(TBDv1Symbols) / sizeof(ExportedSymbol), Exports.size()); 108 EXPECT_TRUE( 109 std::equal(Exports.begin(), Exports.end(), std::begin(TBDv1Symbols))); 110 111 File->addSymbol(SymbolKind::ObjectiveCClassEHType, "Class1", {Targets[1]}); 112 File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, "Class1._ivar1", 113 {Targets[1]}); 114 } 115 116 TEST(TBDv1, ReadFile2) { 117 static const char TBDv1File2[] = "--- !tapi-tbd-v1\n" 118 "archs: [ armv7, armv7s, armv7k, arm64 ]\n" 119 "platform: ios\n" 120 "install-name: Test.dylib\n" 121 "...\n"; 122 123 Expected<TBDFile> Result = 124 TextAPIReader::get(MemoryBufferRef(TBDv1File2, "Test.tbd")); 125 EXPECT_TRUE(!!Result); 126 TBDFile File = std::move(Result.get()); 127 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 128 auto Archs = AK_armv7 | AK_armv7s | AK_armv7k | AK_arm64; 129 auto Platform = PlatformKind::iOS; 130 TargetList Targets; 131 for (auto &&arch : Archs) 132 Targets.emplace_back(Target(arch, Platform)); 133 EXPECT_EQ(Archs, File->getArchitectures()); 134 EXPECT_EQ(File->getPlatforms().size(), 1U); 135 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 136 EXPECT_EQ(std::string("Test.dylib"), File->getInstallName()); 137 EXPECT_EQ(PackedVersion(1, 0, 0), File->getCurrentVersion()); 138 EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion()); 139 EXPECT_EQ(0U, File->getSwiftABIVersion()); 140 EXPECT_EQ(ObjCConstraintType::None, File->getObjCConstraint()); 141 EXPECT_TRUE(File->isTwoLevelNamespace()); 142 EXPECT_TRUE(File->isApplicationExtensionSafe()); 143 EXPECT_FALSE(File->isInstallAPI()); 144 EXPECT_EQ(0U, File->allowableClients().size()); 145 EXPECT_EQ(0U, File->reexportedLibraries().size()); 146 } 147 148 TEST(TBDv1, WriteFile) { 149 static const char TBDv1File3[] = 150 "---\n" 151 "archs: [ i386, x86_64 ]\n" 152 "platform: macosx\n" 153 "install-name: '/usr/lib/libfoo.dylib'\n" 154 "current-version: 1.2.3\n" 155 "compatibility-version: 0\n" 156 "swift-version: 5\n" 157 "objc-constraint: retain_release\n" 158 "exports:\n" 159 " - archs: [ i386 ]\n" 160 " symbols: [ _sym1 ]\n" 161 " weak-def-symbols: [ _sym2 ]\n" 162 " thread-local-symbols: [ _sym3 ]\n" 163 " - archs: [ x86_64 ]\n" 164 " allowed-clients: [ clientA ]\n" 165 " re-exports: [ '/usr/lib/libfoo.dylib' ]\n" 166 " symbols: [ '_OBJC_EHTYPE_$_Class1' ]\n" 167 " objc-classes: [ _Class1 ]\n" 168 " objc-ivars: [ _Class1._ivar1 ]\n" 169 "...\n"; 170 171 InterfaceFile File; 172 TargetList Targets; 173 for (auto &&arch : AK_i386 | AK_x86_64) 174 Targets.emplace_back(Target(arch, PlatformKind::macOS)); 175 File.setPath("libfoo.dylib"); 176 File.setInstallName("/usr/lib/libfoo.dylib"); 177 File.setFileType(FileType::TBD_V1); 178 File.addTargets(Targets); 179 File.setCurrentVersion(PackedVersion(1, 2, 3)); 180 File.setSwiftABIVersion(5); 181 File.setObjCConstraint(ObjCConstraintType::Retain_Release); 182 File.addAllowableClient("clientA", Targets[1]); 183 File.addReexportedLibrary("/usr/lib/libfoo.dylib", Targets[1]); 184 File.addSymbol(SymbolKind::GlobalSymbol, "_sym1", {Targets[0]}); 185 File.addSymbol(SymbolKind::GlobalSymbol, "_sym2", {Targets[0]}, 186 SymbolFlags::WeakDefined); 187 File.addSymbol(SymbolKind::GlobalSymbol, "_sym3", {Targets[0]}, 188 SymbolFlags::ThreadLocalValue); 189 File.addSymbol(SymbolKind::ObjectiveCClass, "Class1", {Targets[1]}); 190 File.addSymbol(SymbolKind::ObjectiveCClassEHType, "Class1", {Targets[1]}); 191 File.addSymbol(SymbolKind::ObjectiveCInstanceVariable, "Class1._ivar1", 192 {Targets[1]}); 193 194 SmallString<4096> Buffer; 195 raw_svector_ostream OS(Buffer); 196 Error Result = TextAPIWriter::writeToStream(OS, File); 197 EXPECT_FALSE(Result); 198 EXPECT_STREQ(TBDv1File3, Buffer.c_str()); 199 } 200 201 TEST(TBDv1, Platform_macOS) { 202 static const char TBDv1PlatformMacOS[] = "---\n" 203 "archs: [ x86_64 ]\n" 204 "platform: macosx\n" 205 "install-name: Test.dylib\n" 206 "...\n"; 207 208 Expected<TBDFile> Result = 209 TextAPIReader::get(MemoryBufferRef(TBDv1PlatformMacOS, "Test.tbd")); 210 EXPECT_TRUE(!!Result); 211 auto Platform = PlatformKind::macOS; 212 TBDFile File = std::move(Result.get()); 213 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 214 EXPECT_EQ(File->getPlatforms().size(), 1U); 215 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 216 } 217 218 TEST(TBDv1, Platform_iOS) { 219 static const char TBDv1PlatformiOS[] = "---\n" 220 "archs: [ arm64 ]\n" 221 "platform: ios\n" 222 "install-name: Test.dylib\n" 223 "...\n"; 224 225 Expected<TBDFile> Result = 226 TextAPIReader::get(MemoryBufferRef(TBDv1PlatformiOS, "Test.tbd")); 227 EXPECT_TRUE(!!Result); 228 auto Platform = PlatformKind::iOS; 229 TBDFile File = std::move(Result.get()); 230 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 231 EXPECT_EQ(File->getPlatforms().size(), 1U); 232 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 233 } 234 235 TEST(TBDv1, Platform_watchOS) { 236 static const char TBDv1PlatformWatchOS[] = "---\n" 237 "archs: [ armv7k ]\n" 238 "platform: watchos\n" 239 "install-name: Test.dylib\n" 240 "...\n"; 241 242 Expected<TBDFile> Result = 243 TextAPIReader::get(MemoryBufferRef(TBDv1PlatformWatchOS, "Test.tbd")); 244 EXPECT_TRUE(!!Result); 245 auto Platform = PlatformKind::watchOS; 246 TBDFile File = std::move(Result.get()); 247 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 248 EXPECT_EQ(File->getPlatforms().size(), 1U); 249 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 250 } 251 252 TEST(TBDv1, Platform_tvOS) { 253 static const char TBDv1PlatformtvOS[] = "---\n" 254 "archs: [ arm64 ]\n" 255 "platform: tvos\n" 256 "install-name: Test.dylib\n" 257 "...\n"; 258 259 Expected<TBDFile> Result = 260 TextAPIReader::get(MemoryBufferRef(TBDv1PlatformtvOS, "Test.tbd")); 261 EXPECT_TRUE(!!Result); 262 auto Platform = PlatformKind::tvOS; 263 TBDFile File = std::move(Result.get()); 264 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 265 EXPECT_EQ(File->getPlatforms().size(), 1U); 266 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 267 } 268 269 TEST(TBDv1, Platform_bridgeOS) { 270 static const char TBDv1BridgeOS[] = "---\n" 271 "archs: [ armv7k ]\n" 272 "platform: bridgeos\n" 273 "install-name: Test.dylib\n" 274 "...\n"; 275 276 Expected<TBDFile> Result = 277 TextAPIReader::get(MemoryBufferRef(TBDv1BridgeOS, "Test.tbd")); 278 EXPECT_TRUE(!!Result); 279 auto Platform = PlatformKind::bridgeOS; 280 TBDFile File = std::move(Result.get()); 281 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 282 EXPECT_EQ(File->getPlatforms().size(), 1U); 283 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 284 } 285 286 TEST(TBDv1, Swift_1_0) { 287 static const char TBDv1Swift1[] = "---\n" 288 "archs: [ arm64 ]\n" 289 "platform: ios\n" 290 "install-name: Test.dylib\n" 291 "swift-version: 1.0\n" 292 "...\n"; 293 294 Expected<TBDFile> Result = 295 TextAPIReader::get(MemoryBufferRef(TBDv1Swift1, "Test.tbd")); 296 EXPECT_TRUE(!!Result); 297 TBDFile File = std::move(Result.get()); 298 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 299 EXPECT_EQ(1U, File->getSwiftABIVersion()); 300 } 301 302 TEST(TBDv1, Swift_1_1) { 303 static const char TBDv1Swift1dot[] = "---\n" 304 "archs: [ arm64 ]\n" 305 "platform: ios\n" 306 "install-name: Test.dylib\n" 307 "swift-version: 1.1\n" 308 "...\n"; 309 310 Expected<TBDFile> Result = 311 TextAPIReader::get(MemoryBufferRef(TBDv1Swift1dot, "Test.tbd")); 312 EXPECT_TRUE(!!Result); 313 TBDFile File = std::move(Result.get()); 314 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 315 EXPECT_EQ(2U, File->getSwiftABIVersion()); 316 } 317 318 TEST(TBDv1, Swift_2_0) { 319 static const char TBDv1Swift2[] = "---\n" 320 "archs: [ arm64 ]\n" 321 "platform: ios\n" 322 "install-name: Test.dylib\n" 323 "swift-version: 2.0\n" 324 "...\n"; 325 326 Expected<TBDFile> Result = 327 TextAPIReader::get(MemoryBufferRef(TBDv1Swift2, "Test.tbd")); 328 EXPECT_TRUE(!!Result); 329 TBDFile File = std::move(Result.get()); 330 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 331 EXPECT_EQ(3U, File->getSwiftABIVersion()); 332 } 333 334 TEST(TBDv1, Swift_3_0) { 335 static const char TBDv1Swift3[] = "---\n" 336 "archs: [ arm64 ]\n" 337 "platform: ios\n" 338 "install-name: Test.dylib\n" 339 "swift-version: 3.0\n" 340 "...\n"; 341 342 Expected<TBDFile> Result = 343 TextAPIReader::get(MemoryBufferRef(TBDv1Swift3, "Test.tbd")); 344 EXPECT_TRUE(!!Result); 345 TBDFile File = std::move(Result.get()); 346 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 347 EXPECT_EQ(4U, File->getSwiftABIVersion()); 348 } 349 350 TEST(TBDv1, Swift_4_0) { 351 static const char TBDv1Swift4[] = "---\n" 352 "archs: [ arm64 ]\n" 353 "platform: ios\n" 354 "install-name: Test.dylib\n" 355 "swift-version: 4.0\n" 356 "...\n"; 357 358 Expected<TBDFile> Result = 359 TextAPIReader::get(MemoryBufferRef(TBDv1Swift4, "Test.tbd")); 360 EXPECT_FALSE(!!Result); 361 std::string ErrorMessage = toString(Result.takeError()); 362 EXPECT_EQ("malformed file\nTest.tbd:5:16: error: invalid Swift ABI " 363 "version.\nswift-version: 4.0\n ^~~\n", 364 ErrorMessage); 365 } 366 367 TEST(TBDv1, Swift_5) { 368 static const char TBDv1Swift5[] = "---\n" 369 "archs: [ arm64 ]\n" 370 "platform: ios\n" 371 "install-name: Test.dylib\n" 372 "swift-version: 5\n" 373 "...\n"; 374 375 Expected<TBDFile> Result = 376 TextAPIReader::get(MemoryBufferRef(TBDv1Swift5, "Test.tbd")); 377 EXPECT_TRUE(!!Result); 378 TBDFile File = std::move(Result.get()); 379 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 380 EXPECT_EQ(5U, File->getSwiftABIVersion()); 381 } 382 383 TEST(TBDv1, Swift_99) { 384 static const char TBDv1Swift99[] = "---\n" 385 "archs: [ arm64 ]\n" 386 "platform: ios\n" 387 "install-name: Test.dylib\n" 388 "swift-version: 99\n" 389 "...\n"; 390 391 Expected<TBDFile> Result = 392 TextAPIReader::get(MemoryBufferRef(TBDv1Swift99, "Test.tbd")); 393 EXPECT_TRUE(!!Result); 394 TBDFile File = std::move(Result.get()); 395 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 396 EXPECT_EQ(99U, File->getSwiftABIVersion()); 397 } 398 399 TEST(TBDv1, UnknownArchitecture) { 400 static const char TBDv1FileUnknownArch[] = "---\n" 401 "archs: [ foo ]\n" 402 "platform: macosx\n" 403 "install-name: Test.dylib\n" 404 "...\n"; 405 406 Expected<TBDFile> Result = 407 TextAPIReader::get(MemoryBufferRef(TBDv1FileUnknownArch, "Test.tbd")); 408 EXPECT_TRUE(!!Result); 409 } 410 411 TEST(TBDv1, UnknownPlatform) { 412 static const char TBDv1FileUnknownPlatform[] = "---\n" 413 "archs: [ i386 ]\n" 414 "platform: newOS\n" 415 "...\n"; 416 417 Expected<TBDFile> Result = 418 TextAPIReader::get(MemoryBufferRef(TBDv1FileUnknownPlatform, "Test.tbd")); 419 EXPECT_FALSE(!!Result); 420 std::string ErrorMessage = toString(Result.takeError()); 421 EXPECT_EQ("malformed file\nTest.tbd:3:11: error: unknown platform\nplatform: " 422 "newOS\n ^~~~~\n", 423 ErrorMessage); 424 } 425 426 TEST(TBDv1, MalformedFile1) { 427 static const char TBDv1FileMalformed1[] = "---\n" 428 "archs: [ arm64 ]\n" 429 "foobar: \"Unsupported key\"\n" 430 "...\n"; 431 432 Expected<TBDFile> Result = 433 TextAPIReader::get(MemoryBufferRef(TBDv1FileMalformed1, "Test.tbd")); 434 EXPECT_FALSE(!!Result); 435 std::string ErrorMessage = toString(Result.takeError()); 436 ASSERT_EQ("malformed file\nTest.tbd:2:1: error: missing required key " 437 "'platform'\narchs: [ arm64 ]\n^\n", 438 ErrorMessage); 439 } 440 441 TEST(TBDv1, MalformedFile2) { 442 static const char TBDv1FileMalformed2[] = "---\n" 443 "archs: [ arm64 ]\n" 444 "platform: ios\n" 445 "install-name: Test.dylib\n" 446 "foobar: \"Unsupported key\"\n" 447 "...\n"; 448 449 Expected<TBDFile> Result = 450 TextAPIReader::get(MemoryBufferRef(TBDv1FileMalformed2, "Test.tbd")); 451 EXPECT_FALSE(!!Result); 452 std::string ErrorMessage = toString(Result.takeError()); 453 ASSERT_EQ( 454 "malformed file\nTest.tbd:5:1: error: unknown key 'foobar'\nfoobar: " 455 "\"Unsupported key\"\n^~~~~~\n", 456 ErrorMessage); 457 } 458 459 } // end namespace TBDv1. 460