[llvm][TextAPI] add equality operator for InterfaceFile

This patch adds functionality to compare for the equality between `InterfaceFile`s based on attributes specific to linking.

Reviewed By: cishida, steven_wu

Differential Revision: https://reviews.llvm.org/D96629

GitOrigin-RevId: eb2eeeb76f7a294b2a8ddd6d09aafe1402ed9348
diff --git a/include/llvm/TextAPI/MachO/InterfaceFile.h b/include/llvm/TextAPI/MachO/InterfaceFile.h
index e49c5e5..f77601d 100644
--- a/include/llvm/TextAPI/MachO/InterfaceFile.h
+++ b/include/llvm/TextAPI/MachO/InterfaceFile.h
@@ -336,9 +336,7 @@
   /// Add a library for inlining to top level library.
   ///
   ///\param Document The library to inline with top level library.
-  void addDocument(std::shared_ptr<InterfaceFile> &&Document) {
-    Documents.emplace_back(std::move(Document));
-  }
+  void addDocument(std::shared_ptr<InterfaceFile> &&Document);
 
   /// Get the list of inlined libraries.
   ///
@@ -397,6 +395,14 @@
         fn);
   }
 
+  /// The equality is determined by attributes that impact linking
+  /// compatibilities. UUIDs, Path, & FileKind are irrelevant since these by
+  /// itself should not impact linking.
+  /// This is an expensive operation.
+  bool operator==(const InterfaceFile &O) const;
+
+  bool operator!=(const InterfaceFile &O) const { return !(*this == O); }
+
 private:
   llvm::BumpPtrAllocator Allocator;
   StringRef copyString(StringRef String) {
@@ -427,6 +433,21 @@
   SymbolMapType Symbols;
 };
 
+template <typename DerivedT, typename KeyInfoT, typename BucketT>
+bool operator==(const DenseMapBase<DerivedT, SymbolsMapKey, MachO::Symbol *,
+                                   KeyInfoT, BucketT> &LHS,
+                const DenseMapBase<DerivedT, SymbolsMapKey, MachO::Symbol *,
+                                   KeyInfoT, BucketT> &RHS) {
+  if (LHS.size() != RHS.size())
+    return false;
+  for (auto KV : LHS) {
+    auto I = RHS.find(KV.first);
+    if (I == RHS.end() || *I->second != *KV.second)
+      return false;
+  }
+  return true;
+}
+
 } // end namespace MachO.
 } // end namespace llvm.
 
diff --git a/include/llvm/TextAPI/MachO/Symbol.h b/include/llvm/TextAPI/MachO/Symbol.h
index 1b1632c..839945f 100644
--- a/include/llvm/TextAPI/MachO/Symbol.h
+++ b/include/llvm/TextAPI/MachO/Symbol.h
@@ -104,6 +104,15 @@
   void dump() const { dump(llvm::errs()); }
 #endif
 
+  bool operator==(const Symbol &O) const {
+    return (Kind == O.Kind) && (Name == O.Name) && (Targets == O.Targets) &&
+           (Flags == O.Flags);
+  }
+
+  bool operator!=(const Symbol &O) const {
+    return !(*this == O);
+  }
+
 private:
   StringRef Name;
   TargetList Targets;
diff --git a/lib/TextAPI/MachO/InterfaceFile.cpp b/lib/TextAPI/MachO/InterfaceFile.cpp
index cfc1c58..5b73cfe 100644
--- a/lib/TextAPI/MachO/InterfaceFile.cpp
+++ b/lib/TextAPI/MachO/InterfaceFile.cpp
@@ -117,3 +117,46 @@
     for (const auto &Target : Targets)
       result.first->second->addTarget(Target);
 }
+
+void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
+  auto Pos = llvm::lower_bound(Documents, Document,
+                               [](const std::shared_ptr<InterfaceFile> &LHS,
+                                  const std::shared_ptr<InterfaceFile> &RHS) {
+                                 return LHS->InstallName < RHS->InstallName;
+                               });
+  Documents.insert(Pos, Document);
+}
+
+bool InterfaceFile::operator==(const InterfaceFile &O) const {
+    if (Targets != O.Targets)
+      return false;
+    if (InstallName != O.InstallName)
+      return false;
+    if ((CurrentVersion != O.CurrentVersion) ||
+        (CompatibilityVersion != O.CompatibilityVersion))
+      return false;
+    if (SwiftABIVersion != O.SwiftABIVersion)
+      return false;
+    if (IsTwoLevelNamespace != O.IsTwoLevelNamespace)
+      return false;
+    if (IsAppExtensionSafe != O.IsAppExtensionSafe)
+      return false;
+    if (IsInstallAPI != O.IsInstallAPI)
+      return false;
+    if (ParentUmbrellas != O.ParentUmbrellas)
+      return false;
+    if (AllowableClients != O.AllowableClients)
+      return false;
+    if (ReexportedLibraries != O.ReexportedLibraries)
+      return false;
+    if (Symbols != O.Symbols)
+      return false;
+    if (!std::equal(Documents.begin(), Documents.end(), O.Documents.begin(),
+                    O.Documents.end(),
+                    [](const std::shared_ptr<InterfaceFile> LHS,
+                        const std::shared_ptr<InterfaceFile> RHS) {
+                      return *LHS == *RHS;
+                    }))
+      return false;
+    return true;
+}
diff --git a/unittests/TextAPI/TextStubHelpers.h b/unittests/TextAPI/TextStubHelpers.h
index cf93504..183dbf0 100644
--- a/unittests/TextAPI/TextStubHelpers.h
+++ b/unittests/TextAPI/TextStubHelpers.h
@@ -40,5 +40,24 @@
   S.erase(std::remove_if(S.begin(), S.end(), ::isspace), S.end());
   return S;
 }
+
+// This will transform a single InterfaceFile then compare against the other
+// InterfaceFile then transform the second InterfaceFile in the same way to
+// regain equality.
+inline bool
+checkEqualityOnTransform(MachO::InterfaceFile &FileA,
+                         MachO::InterfaceFile &FileB,
+                         void (*Transform)(MachO::InterfaceFile *)) {
+  Transform(&FileA);
+  // Files should not be equal.
+  if (FileA == FileB)
+    return false;
+  Transform(&FileB);
+  // Files should be equal.
+  if (FileA != FileB)
+    return false;
+  return true;
+}
+
 } // namespace llvm
 #endif
diff --git a/unittests/TextAPI/TextStubV3Tests.cpp b/unittests/TextAPI/TextStubV3Tests.cpp
index 2881c95..8841b20 100644
--- a/unittests/TextAPI/TextStubV3Tests.cpp
+++ b/unittests/TextAPI/TextStubV3Tests.cpp
@@ -836,4 +836,114 @@
       ErrorMessage);
 }
 
+TEST(TBDv3, InterfaceEquality) {
+  static const char TBDv3File[] =
+      "--- !tapi-tbd-v3\n"
+      "archs: [ armv7, arm64 ]\n"
+      "uuids: [ 'armv7: 00000000-0000-0000-0000-000000000000',\n"
+      "         'arm64: 11111111-1111-1111-1111-111111111111']\n"
+      "platform: ios\n"
+      "flags: [ installapi ]\n"
+      "install-name: Test.dylib\n"
+      "current-version: 2.3.4\n"
+      "compatibility-version: 1.0\n"
+      "swift-abi-version: 1.1\n"
+      "parent-umbrella: Umbrella.dylib\n"
+      "exports:\n"
+      "  - archs: [ armv7, arm64 ]\n"
+      "    allowable-clients: [ clientA ]\n"
+      "    re-exports: [ /usr/lib/libfoo.dylib ]\n"
+      "    symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n"
+      "    objc-classes: [ class1, class2 ]\n"
+      "    objc-eh-types: [ class1 ]\n"
+      "    objc-ivars: [ class1._ivar1, class1._ivar2 ]\n"
+      "    weak-def-symbols: [ _weak1, _weak2 ]\n"
+      "    thread-local-symbols: [ _tlv1, _tlv3 ]\n"
+      "  - archs: [ armv7 ]\n"
+      "    symbols: [ _sym5 ]\n"
+      "    objc-classes: [ class3 ]\n"
+      "    objc-ivars: [ class1._ivar3 ]\n"
+      "    weak-def-symbols: [ _weak3 ]\n"
+      "    thread-local-symbols: [ _tlv3 ]\n"
+      "--- !tapi-tbd-v3\n"
+      "archs:           [ i386 ]\n"
+      "platform:        macosx\n"
+      "install-name:    '/usr/lib/libbar.dylib'\n"
+      "current-version: 0\n"
+      "compatibility-version: 0\n"
+      "swift-abi-version: 5\n"
+      "objc-constraint: none\n"
+      "exports:\n"
+      "  - archs:           [ i386 ]\n"
+      "    symbols:         [ _sym3, _sym4 ]\n"
+      "...\n";
+  Expected<TBDFile> ResultA =
+      TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestA.tbd"));
+  EXPECT_TRUE(!!ResultA);
+  InterfaceFile FileA = std::move(*ResultA.get());
+  Expected<TBDFile> ResultB =
+      TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestB.tbd"));
+  EXPECT_TRUE(!!ResultB);
+  InterfaceFile FileB = std::move(*ResultB.get());
+  EXPECT_FALSE(FileA.getPath() == FileB.getPath());
+  EXPECT_TRUE(FileA == FileB);
+}
+
+
+
+TEST(TBDv3, InterfaceInequality) {
+  static const char TBDv3File[] = "--- !tapi-tbd-v3\n"
+                                  "archs: [ armv7, arm64 ]\n"
+                                  "platform: ios\n"
+                                  "install-name: Test.dylib\n"
+                                  "...\n";
+
+  Expected<TBDFile> ResultA =
+      TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestA.tbd"));
+  EXPECT_TRUE(!!ResultA);
+  InterfaceFile FileA = std::move(*ResultA.get());
+  Expected<TBDFile> ResultB =
+      TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestB.tbd"));
+  EXPECT_TRUE(!!ResultB);
+  InterfaceFile FileB = std::move(*ResultB.get());
+
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->addTarget(Target(AK_x86_64, PlatformKind::iOS));
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->setCurrentVersion(PackedVersion(1, 2, 3));
+    File->setCompatibilityVersion(PackedVersion(1, 0, 0));
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(
+      FileA, FileB, [](InterfaceFile *File) { File->setSwiftABIVersion(5); }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->setTwoLevelNamespace(false);
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(
+      FileA, FileB, [](InterfaceFile *File) { File->setInstallAPI(true); }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->setApplicationExtensionSafe(false);
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->addParentUmbrella(Target(AK_armv7, PlatformKind::iOS), "Umbrella.dylib");
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->addAllowableClient("ClientA", Target(AK_armv7, PlatformKind::iOS));
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->addReexportedLibrary("/System/Library/Frameworks/A.framework/A",
+                              Target(AK_armv7, PlatformKind::iOS));
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->addSymbol(SymbolKind::GlobalSymbol, "_symA", {Target(AK_arm64, PlatformKind::iOS)});
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    InterfaceFile Document;
+    Document.addTargets(TargetList{Target(AK_armv7, PlatformKind::iOS),
+                      Target(AK_arm64, PlatformKind::iOS)});
+    Document.setInstallName("/System/Library/Frameworks/A.framework/A");
+    File->addDocument(std::make_shared<InterfaceFile>(std::move(Document)));
+  }));
+}
+
 } // namespace TBDv3
diff --git a/unittests/TextAPI/TextStubV4Tests.cpp b/unittests/TextAPI/TextStubV4Tests.cpp
index 403e2d6..87dae84 100644
--- a/unittests/TextAPI/TextStubV4Tests.cpp
+++ b/unittests/TextAPI/TextStubV4Tests.cpp
@@ -938,4 +938,238 @@
             ErrorMessage);
 }
 
+TEST(TBDv4, InterfaceEquality) {
+  static const char TBDv4File[] =
+      "--- !tapi-tbd\n"
+      "tbd-version: 4\n"
+      "targets:  [ i386-macos, x86_64-macos, x86_64-ios ]\n"
+      "uuids:\n"
+      "  - target: i386-macos\n"
+      "    value: 00000000-0000-0000-0000-000000000000\n"
+      "  - target: x86_64-macos\n"
+      "    value: 11111111-1111-1111-1111-111111111111\n"
+      "  - target: x86_64-ios\n"
+      "    value: 11111111-1111-1111-1111-111111111111\n"
+      "flags: [ flat_namespace, installapi ]\n"
+      "install-name: Umbrella.framework/Umbrella\n"
+      "current-version: 1.2.3\n"
+      "compatibility-version: 1.2\n"
+      "swift-abi-version: 5\n"
+      "parent-umbrella:\n"
+      "  - targets: [ i386-macos, x86_64-macos, x86_64-ios ]\n"
+      "    umbrella: System\n"
+      "allowable-clients:\n"
+      "  - targets: [ i386-macos, x86_64-macos, x86_64-ios ]\n"
+      "    clients: [ ClientA ]\n"
+      "reexported-libraries:\n"
+      "  - targets: [ i386-macos ]\n"
+      "    libraries: [ /System/Library/Frameworks/A.framework/A ]\n"
+      "exports:\n"
+      "  - targets: [ i386-macos ]\n"
+      "    symbols: [ _symA ]\n"
+      "    objc-classes: []\n"
+      "    objc-eh-types: []\n"
+      "    objc-ivars: []\n"
+      "    weak-symbols: []\n"
+      "    thread-local-symbols: []\n"
+      "  - targets: [ x86_64-ios ]\n"
+      "    symbols: [_symB]\n"
+      "  - targets: [ x86_64-macos, x86_64-ios ]\n"
+      "    symbols: [_symAB]\n"
+      "reexports:\n"
+      "  - targets: [ i386-macos ]\n"
+      "    symbols: [_symC]\n"
+      "    objc-classes: []\n"
+      "    objc-eh-types: []\n"
+      "    objc-ivars: []\n"
+      "    weak-symbols: []\n"
+      "    thread-local-symbols: []\n"
+      "undefineds:\n"
+      "  - targets: [ i386-macos ]\n"
+      "    symbols: [ _symD ]\n"
+      "    objc-classes: []\n"
+      "    objc-eh-types: []\n"
+      "    objc-ivars: []\n"
+      "    weak-symbols: []\n"
+      "    thread-local-symbols: []\n"
+      "tbd-version:     4\n"
+      "targets:         [ i386-maccatalyst, x86_64-maccatalyst ]\n"
+      "uuids:\n"
+      "  - target:          i386-maccatalyst\n"
+      "    value:           00000000-0000-0000-0000-000000000000\n"
+      "  - target:          x86_64-maccatalyst\n"
+      "    value:           11111111-1111-1111-1111-111111111111\n"
+      "install-name:    '/System/Library/Frameworks/A.framework/A'\n"
+      "exports:\n"
+      "  - targets:         [ i386-maccatalyst ]\n"
+      "    weak-symbols:    [ _symC ]\n"
+      "  - targets:         [ i386-maccatalyst, x86_64-maccatalyst ]\n"
+      "    symbols:         [ _symA ]\n"
+      "    objc-classes:    [ Class1 ]\n"
+      "  - targets:         [ x86_64-maccatalyst ]\n"
+      "    symbols:         [ _symAB ]\n"
+      "...\n";
+
+  Expected<TBDFile> ResultA =
+      TextAPIReader::get(MemoryBufferRef(TBDv4File, "TestA.tbd"));
+  EXPECT_TRUE(!!ResultA);
+  InterfaceFile FileA = std::move(*ResultA.get());
+  Expected<TBDFile> ResultB =
+      TextAPIReader::get(MemoryBufferRef(TBDv4File, "TestB.tbd"));
+  EXPECT_TRUE(!!ResultB);
+  InterfaceFile FileB = std::move(*ResultB.get());
+  EXPECT_TRUE(FileA == FileB);
+}
+
+TEST(TBDv4, InterfaceDiffVersionsEquality) {
+  static const char TBDv4File[] =
+      "--- !tapi-tbd\n"
+      "tbd-version: 4\n"
+      "targets:  [ i386-macos, x86_64-macos ]\n"
+      "uuids:\n"
+      "  - target: i386-macos\n"
+      "    value: 00000000-0000-0000-0000-000000000000\n"
+      "  - target: x86_64-macos\n"
+      "    value: 11111111-1111-1111-1111-111111111111\n"
+      "flags: [ installapi ]\n"
+      "install-name: Umbrella.framework/Umbrella\n"
+      "current-version: 1.2.3\n"
+      "compatibility-version: 1.0\n"
+      "swift-abi-version: 5\n"
+      "parent-umbrella:\n"
+      "  - targets: [ i386-macos, x86_64-macos ]\n"
+      "    umbrella: System\n"
+      "allowable-clients:\n"
+      "  - targets: [ i386-macos, x86_64-macos ]\n"
+      "    clients: [ ClientA ]\n"
+      "reexported-libraries:\n"
+      "  - targets: [ i386-macos ]\n"
+      "    libraries: [ /System/Library/Frameworks/A.framework/A ]\n"
+      "exports:\n"
+      "  - targets: [ i386-macos ]\n"
+      "    symbols: [ _sym5 ]\n"
+      "    objc-classes: [ class3]\n"
+      "    objc-eh-types: []\n"
+      "    objc-ivars: [ class1._ivar3 ]\n"
+      "    weak-symbols: [ _weak3 ]\n"
+      "  - targets: [ x86_64-macos ]\n"
+      "    symbols: [_symAB]\n"
+      "  - targets: [ i386-macos, x86_64-macos ]\n"
+      "    symbols: [_symA]\n"
+      "    objc-classes: [ class1, class2 ]\n"
+      "    objc-eh-types: [ class1 ]\n"
+      "    objc-ivars: [ class1._ivar1, class1._ivar2 ]\n"
+      "    weak-symbols: [ _weak1, _weak2 ]\n"
+      "    thread-local-symbols: [ _tlv1, _tlv3 ]\n"
+      "undefineds:\n"
+      "  - targets: [ i386-macos ]\n"
+      "    symbols: [ _symC ]\n"
+      "    objc-classes: []\n"
+      "    objc-eh-types: []\n"
+      "    objc-ivars: []\n"
+      "    weak-symbols: []\n"
+      "    thread-local-symbols: []\n"
+      "...\n";
+
+  static const char TBDv3File[] =
+      "--- !tapi-tbd-v3\n"
+      "archs: [ i386, x86_64 ]\n"
+      "uuids: [ 'i386: 00000000-0000-0000-0000-000000000000',\n"
+      "         'x86_64: 22222222-2222-2222-2222-222222222222']\n"
+      "platform: macosx\n"
+      "flags: [ installapi ]\n"
+      "install-name: Umbrella.framework/Umbrella\n"
+      "current-version: 1.2.3\n"
+      "compatibility-version: 1.0\n"
+      "swift-abi-version: 5\n"
+      "parent-umbrella: System\n"
+      "exports:\n"
+      "  - archs: [ i386, x86_64 ]\n"
+      "    allowable-clients: [ ClientA ]\n"
+      "    symbols: [ _symA ]\n"
+      "    objc-classes: [ class1, class2 ]\n"
+      "    objc-eh-types: [ class1 ]\n"
+      "    objc-ivars: [ class1._ivar1, class1._ivar2 ]\n"
+      "    weak-def-symbols: [ _weak1, _weak2 ]\n"
+      "    thread-local-symbols: [ _tlv1, _tlv3 ]\n"
+      "  - archs: [ i386 ]\n"
+      "    re-exports: [ /System/Library/Frameworks/A.framework/A ]\n"
+      "    symbols: [ _sym5 ]\n"
+      "    objc-classes: [ class3 ]\n"
+      "    objc-ivars: [ class1._ivar3 ]\n"
+      "    weak-def-symbols: [ _weak3 ]\n"
+      "  - archs: [ x86_64 ]\n"
+      "    symbols: [ _symAB ]\n"
+      "undefineds:\n"
+      "  - archs: [ i386 ]\n"
+      "    symbols: [ _symC ]\n"
+      "...\n";
+
+  Expected<TBDFile> ResultA =
+      TextAPIReader::get(MemoryBufferRef(TBDv4File, "TestA.tbd"));
+  EXPECT_TRUE(!!ResultA);
+  InterfaceFile FileA = std::move(*ResultA.get());
+  Expected<TBDFile> ResultB =
+      TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestB.tbd"));
+  EXPECT_TRUE(!!ResultB);
+  InterfaceFile FileB = std::move(*ResultB.get());
+  EXPECT_NE(FileA.uuids(), FileB.uuids());
+  EXPECT_TRUE(FileA == FileB);
+}
+
+TEST(TBDv4, InterfaceInequality) {
+  static const char TBDv4File[] = "--- !tapi-tbd\n"
+                                  "tbd-version: 4\n"
+                                  "targets:  [ i386-macos, x86_64-macos ]\n"
+                                  "install-name: Umbrella.framework/Umbrella\n"
+                                  "...\n";
+
+  Expected<TBDFile> ResultA =
+      TextAPIReader::get(MemoryBufferRef(TBDv4File, "TestA.tbd"));
+  EXPECT_TRUE(!!ResultA);
+  InterfaceFile FileA = std::move(*ResultA.get());
+  Expected<TBDFile> ResultB =
+      TextAPIReader::get(MemoryBufferRef(TBDv4File, "TestB.tbd"));
+  EXPECT_TRUE(!!ResultB);
+  InterfaceFile FileB = std::move(*ResultB.get());
+
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->addTarget(Target(AK_x86_64, PlatformKind::iOS));
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->setCurrentVersion(PackedVersion(1, 2, 3));
+    File->setCompatibilityVersion(PackedVersion(1, 0, 0));
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(
+      FileA, FileB, [](InterfaceFile *File) { File->setSwiftABIVersion(5); }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->setTwoLevelNamespace(false);
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(
+      FileA, FileB, [](InterfaceFile *File) { File->setInstallAPI(true); }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->setApplicationExtensionSafe(false);
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->addParentUmbrella(Target(AK_x86_64, PlatformKind::macOS), "System.dylib");
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->addAllowableClient("ClientA", Target(AK_i386, PlatformKind::macOS));
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->addReexportedLibrary("/System/Library/Frameworks/A.framework/A",
+                               Target(AK_i386, PlatformKind::macOS));
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    File->addSymbol(SymbolKind::GlobalSymbol, "_symA", {Target(AK_x86_64, PlatformKind::macOS)});
+  }));
+  EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
+    InterfaceFile Document;
+    Document.addTargets(TargetList {Target(AK_i386, PlatformKind::macOS),
+                      Target(AK_x86_64, PlatformKind::macOS)});
+    Document.setInstallName("/System/Library/Frameworks/A.framework/A");
+    File->addDocument(std::make_shared<InterfaceFile>(std::move(Document)));
+  }));
+}
+
 } // end namespace TBDv4