From b4a401036b6026f701b86e76cf0b6df84880e574 Mon Sep 17 00:00:00 2001 From: Alex Guretzki Date: Thu, 19 Sep 2024 14:10:51 +0200 Subject: [PATCH] Improving SwiftPackage analyzing (#15) * Improving SwiftPackage analyzing * Fixing some leftovers * Removing commented out code * Surfacing swift package warnings * Renaming and better warning formatting * Fixing tests * Update README.md (#18) * Adding a comment for clarification --------- Co-authored-by: Alex Guretzki --- README.md | 3 +- .../SDKDump/SDKDump+Element+Description.swift | 2 +- .../Models/SwiftPackageDescription.swift | 191 ++++++- ...per.swift => SwiftPackageFileHelper.swift} | 30 +- .../Modules/ABIGenerator/ABIGenerator.swift | 2 +- .../ABIGenerator/PackageABIGenerator.swift | 2 +- Sources/Pipeline/Modules/ProjectBuilder.swift | 4 +- .../Modules/SwiftPackageFileAnalyzer.swift | 402 +++++++++++++- Sources/Pipeline/Pipeline.swift | 26 +- Tests/UnitTests/ABIGeneratorTests.swift | 3 +- Tests/UnitTests/PipelineTests.swift | 7 +- Tests/UnitTests/ProjectBuilderTests.swift | 3 +- .../Resources/dummi-abi-flat-definition.md | 496 +++++++++--------- Tests/UnitTests/SDKDumpAnalyzerTests.swift | 4 +- .../SwiftPackageFileAnalyzerTests.swift | 145 ++++- 15 files changed, 975 insertions(+), 345 deletions(-) rename Sources/Helpers/{PackageFileHelper.swift => SwiftPackageFileHelper.swift} (83%) diff --git a/README.md b/README.md index 14258c5..7e3507e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ Alternatively you could use `xcrun swift-api-digester -diagnose-sdk` and pass th ## How it works -![image](https://github.com/user-attachments/assets/6f8b8927-d08b-487d-9d80-e5ee1b8d8302) +![image](https://github.com/user-attachments/assets/cc04d21a-06f6-42bc-8e73-4aef7af21d7a) + ### Project Builder diff --git a/Sources/Helpers/Models/SDKDump/SDKDump+Element+Description.swift b/Sources/Helpers/Models/SDKDump/SDKDump+Element+Description.swift index ff1e6f9..58a48fa 100644 --- a/Sources/Helpers/Models/SDKDump/SDKDump+Element+Description.swift +++ b/Sources/Helpers/Models/SDKDump/SDKDump+Element+Description.swift @@ -56,7 +56,7 @@ private extension SDKDump.Element { if isObjcAccessible { components += ["@objc"] } if isInlinable { components += ["@inlinable"] } if isOverride { components += ["override"] } - if declKind != .import { + if declKind != .import && declKind != .case { if isOpen { components += ["open"] } else if isInternal { diff --git a/Sources/Helpers/Models/SwiftPackageDescription.swift b/Sources/Helpers/Models/SwiftPackageDescription.swift index 3bb1635..7410d2a 100644 --- a/Sources/Helpers/Models/SwiftPackageDescription.swift +++ b/Sources/Helpers/Models/SwiftPackageDescription.swift @@ -6,43 +6,150 @@ import Foundation -struct SwiftPackageDescription: Codable { +// See: https://docs.swift.org/package-manager/PackageDescription/index.html +// See: https://github.com/swiftlang/swift-package-manager/blob/main/Sources/PackageDescription/PackageDescriptionSerialization.swift + +struct SwiftPackageDescription: Codable, Equatable { + let name: String + let platforms: [Platform] let defaultLocalization: String? - let products: [Product] + let targets: [Target] + let products: [Product] + let dependencies: [Dependency] + let toolsVersion: String var warnings = [String]() + init( + defaultLocalization: String?, + name: String, + platforms: [Platform] = [], + products: [Product] = [], + targets: [Target] = [], + dependencies: [Dependency] = [], + toolsVersion: String, + warnings: [String] = [] + ) { + self.defaultLocalization = defaultLocalization + self.name = name + self.platforms = platforms + self.products = products + self.targets = targets + self.dependencies = dependencies + self.toolsVersion = toolsVersion + self.warnings = warnings + } + enum CodingKeys: String, CodingKey { case defaultLocalization = "default_localization" + case name + case platforms case products case targets + case dependencies case toolsVersion = "tools_version" } } extension SwiftPackageDescription { - struct Product: Codable { + struct Platform: Codable, Equatable, Hashable { + + let name: String + let version: String + } +} + +extension SwiftPackageDescription.Platform: CustomStringConvertible { + var description: String { + "\(name)(\(version))" + } +} + +extension SwiftPackageDescription { + + struct Product: Codable, Equatable, Hashable { + + // TODO: Add `rule` property let name: String let targets: [String] } } +extension SwiftPackageDescription.Product: CustomStringConvertible { + var description: String { + let targetsDescription = targets.map { "\"\($0)\"" }.joined(separator: ", ") + return ".library(name: \"\(name)\", targets: [\(targetsDescription)])" + } +} + +extension SwiftPackageDescription { + + struct Dependency: Codable, Equatable { + + let identity: String + let requirement: Requirement + let type: String + let url: String? + } +} + +extension SwiftPackageDescription.Dependency: CustomStringConvertible { + + var description: String { + var description = ".package(" + + var fields = [String]() + + if let url { + fields += ["url: \"\(url)\""] + } + + fields += [requirement.description] + + description += fields.joined(separator: ", ") + + description += ")" + return description + } +} + +extension SwiftPackageDescription.Dependency { + + struct Requirement: Codable, Equatable { + + // TODO: Which other requirements exist? + + let exact: [String]? + } +} + +extension SwiftPackageDescription.Dependency.Requirement: CustomStringConvertible { + + var description: String { + if let exactVersion = exact?.first { + return "exact: \"\(exactVersion)\"" + } + + return "UNKNOWN_REQUIREMENT" + } +} + extension SwiftPackageDescription { - struct Target: Codable { + struct Target: Codable, Equatable { - enum ModuleType: String, Codable { + enum ModuleType: String, Codable, Equatable { case swiftTarget = "SwiftTarget" case binaryTarget = "BinaryTarget" case clangTarget = "ClangTarget" } - enum TargetType: String, Codable { + enum TargetType: String, Codable, Equatable { case library = "library" case binary = "binary" case test = "test" @@ -53,30 +160,88 @@ extension SwiftPackageDescription { let path: String let moduleType: ModuleType + /// `.product(name: ...)` dependency let productDependencies: [String]? - let productMemberships: [String]? - let sources: [String] + /// `.target(name: ...) dependency let targetDependencies: [String]? - let resources: [Resource]? + // Ignoring following properties for now as they are not handled in the `PackageAnalyzer` + // and thus would produce changes that are not visible + // + // let productMemberships: [String]? + // let sources: [String] + // let resources: [Resource]? + + init( + name: String, + type: TargetType, + path: String, + moduleType: ModuleType, + productDependencies: [String]? = nil, + targetDependencies: [String]? = nil + ) { + self.name = name + self.type = type + self.path = path + self.moduleType = moduleType + self.productDependencies = productDependencies + self.targetDependencies = targetDependencies + } enum CodingKeys: String, CodingKey { case moduleType = "module_type" case name case productDependencies = "product_dependencies" - case productMemberships = "product_memberships" - case sources case targetDependencies = "target_dependencies" case type case path - case resources } } } +extension SwiftPackageDescription.Target.TargetType: CustomStringConvertible { + var description: String { + switch self { + case .binary: "binaryTarget" + case .library: "target" + case .test: "testTarget" + } + } +} + +extension SwiftPackageDescription.Target: CustomStringConvertible { + + var description: String { + var description = ".\(type.description)(name: \"\(name)\"" + + var dependencyDescriptions = [String]() + + if let targetDependenciesDescriptions = targetDependencies?.map({ ".target(name: \"\($0)\")" }) { + dependencyDescriptions += targetDependenciesDescriptions + } + + if let productDependenciesDescriptions = productDependencies?.map({ ".product(name: \"\($0)\", ...)" }) { + dependencyDescriptions += productDependenciesDescriptions + } + + if !dependencyDescriptions.isEmpty { + // `, dependencies: [.target(name: ...), .target(name: ...), .product(name: ...), ...]` + description += ", dependencies: [\(dependencyDescriptions.joined(separator: ", "))]" + } + + description += ", path: \"\(path)\"" + + description += ")" + + return description + } +} + extension SwiftPackageDescription.Target { - struct Resource: Codable { + struct Resource: Codable, Equatable { + + // TODO: Add `rule` property let path: String } diff --git a/Sources/Helpers/PackageFileHelper.swift b/Sources/Helpers/SwiftPackageFileHelper.swift similarity index 83% rename from Sources/Helpers/PackageFileHelper.swift rename to Sources/Helpers/SwiftPackageFileHelper.swift index 61ebfe4..1f45386 100644 --- a/Sources/Helpers/PackageFileHelper.swift +++ b/Sources/Helpers/SwiftPackageFileHelper.swift @@ -6,7 +6,7 @@ import Foundation -enum PackageFileHelperError: LocalizedError { +enum SwiftPackageFileHelperError: LocalizedError { case packageDescriptionError(_ description: String) case couldNotGeneratePackageDescription case couldNotConsolidateTargetsInPackageFile @@ -23,7 +23,7 @@ enum PackageFileHelperError: LocalizedError { } } -struct PackageFileHelper { +struct SwiftPackageFileHelper { private let fileHandler: FileHandling private let xcodeTools: XcodeTools @@ -89,7 +89,7 @@ struct PackageFileHelper { // MARK: Generate Package Description -private extension PackageFileHelper { +private extension SwiftPackageFileHelper { func generatePackageDescription(at projectDirectoryPath: String) throws -> SwiftPackageDescription { @@ -109,7 +109,7 @@ private extension PackageFileHelper { // That we have to get rid of first to generate the description object if firstLine.starts(with: errorTag) { - throw PackageFileHelperError.packageDescriptionError(result) + throw SwiftPackageFileHelperError.packageDescriptionError(result) } if firstLine.starts(with: warningTag) { @@ -120,23 +120,29 @@ private extension PackageFileHelper { warnings += [warning] } - if firstLine.starts(with: "{"), - let packageDescriptionData = packageDescriptionLines.joined(separator: newLine).data(using: .utf8) { - var packageDescription = try JSONDecoder().decode(SwiftPackageDescription.self, from: packageDescriptionData) - packageDescription.warnings = warnings - return packageDescription + if + firstLine.starts(with: "{"), + let packageDescriptionData = packageDescriptionLines.joined(separator: newLine).data(using: .utf8) + { + return try decodePackageDescription(from: packageDescriptionData, warnings: warnings) } packageDescriptionLines.removeFirst() } - throw PackageFileHelperError.couldNotGeneratePackageDescription + throw SwiftPackageFileHelperError.couldNotGeneratePackageDescription + } + + func decodePackageDescription(from packageDescriptionData: Data, warnings: [String]) throws -> SwiftPackageDescription { + var packageDescription = try JSONDecoder().decode(SwiftPackageDescription.self, from: packageDescriptionData) + packageDescription.warnings = warnings + return packageDescription } } // MARK: Update Package Content -private extension PackageFileHelper { +private extension SwiftPackageFileHelper { /// Generates a library entry from the name and available target names to be inserted into the `Package.swift` file func consolidatedLibraryEntry( @@ -162,7 +168,7 @@ private extension PackageFileHelper { if let productsRange = packageContent.range(of: "products: [", options: .caseInsensitive) { updatedContent.insert(contentsOf: consolidatedEntry, at: productsRange.upperBound) } else { - throw PackageFileHelperError.couldNotConsolidateTargetsInPackageFile + throw SwiftPackageFileHelperError.couldNotConsolidateTargetsInPackageFile } return updatedContent } diff --git a/Sources/Pipeline/Modules/ABIGenerator/ABIGenerator.swift b/Sources/Pipeline/Modules/ABIGenerator/ABIGenerator.swift index c1e1ea4..a39df1f 100644 --- a/Sources/Pipeline/Modules/ABIGenerator/ABIGenerator.swift +++ b/Sources/Pipeline/Modules/ABIGenerator/ABIGenerator.swift @@ -10,7 +10,7 @@ struct ABIGenerator: ABIGenerating { private let shell: ShellHandling private let xcodeTools: XcodeTools - private let packageFileHelper: PackageFileHelper + private let packageFileHelper: SwiftPackageFileHelper private let fileHandler: FileHandling private let logger: Logging? diff --git a/Sources/Pipeline/Modules/ABIGenerator/PackageABIGenerator.swift b/Sources/Pipeline/Modules/ABIGenerator/PackageABIGenerator.swift index 30d6ada..5bbf6df 100644 --- a/Sources/Pipeline/Modules/ABIGenerator/PackageABIGenerator.swift +++ b/Sources/Pipeline/Modules/ABIGenerator/PackageABIGenerator.swift @@ -10,7 +10,7 @@ struct PackageABIGenerator: ABIGenerating { let fileHandler: FileHandling let xcodeTools: XcodeTools - let packageFileHelper: PackageFileHelper + let packageFileHelper: SwiftPackageFileHelper let logger: Logging? func generate( diff --git a/Sources/Pipeline/Modules/ProjectBuilder.swift b/Sources/Pipeline/Modules/ProjectBuilder.swift index 54e8327..0339da2 100644 --- a/Sources/Pipeline/Modules/ProjectBuilder.swift +++ b/Sources/Pipeline/Modules/ProjectBuilder.swift @@ -179,10 +179,10 @@ private extension ProjectBuilder { // If a project scheme was provided we just try to build it schemeToBuild = scheme } else { - // Creating an `.library(name: "_allTargets", targets: [ALL_TARGETS])` + // Creating an `.library(name: "_AllTargets", targets: [ALL_TARGETS])` // so we only have to build once and then can generate ABI files for every module from a single build schemeToBuild = "_AllTargets" - let packageFileHelper = PackageFileHelper(fileHandler: fileHandler, xcodeTools: xcodeTools) + let packageFileHelper = SwiftPackageFileHelper(fileHandler: fileHandler, xcodeTools: xcodeTools) try packageFileHelper.preparePackageWithConsolidatedLibrary(named: schemeToBuild, at: projectDirectoryPath) } diff --git a/Sources/Pipeline/Modules/SwiftPackageFileAnalyzer.swift b/Sources/Pipeline/Modules/SwiftPackageFileAnalyzer.swift index e4d374c..f403eb5 100644 --- a/Sources/Pipeline/Modules/SwiftPackageFileAnalyzer.swift +++ b/Sources/Pipeline/Modules/SwiftPackageFileAnalyzer.swift @@ -11,6 +11,13 @@ struct SwiftPackageFileAnalyzer: ProjectAnalyzing { let fileHandler: FileHandling let xcodeTools: XcodeTools + private enum Constants { + static let packageFileName = "Package.swift" + static func packageFileName(child: String) -> String { + "\(packageFileName) / \(child)" + } + } + init( fileHandler: FileHandling = FileManager.default, xcodeTools: XcodeTools = XcodeTools() @@ -24,11 +31,11 @@ struct SwiftPackageFileAnalyzer: ProjectAnalyzing { let oldProjectPath = oldProjectUrl.path() let newProjectPath = newProjectUrl.path() - let oldPackagePath = PackageFileHelper.packagePath(for: oldProjectPath) - let newPackagePath = PackageFileHelper.packagePath(for: newProjectPath) + let oldPackagePath = SwiftPackageFileHelper.packagePath(for: oldProjectPath) + let newPackagePath = SwiftPackageFileHelper.packagePath(for: newProjectPath) if fileHandler.fileExists(atPath: oldPackagePath), fileHandler.fileExists(atPath: newPackagePath) { - let packageHelper = PackageFileHelper( + let packageHelper = SwiftPackageFileHelper( fileHandler: fileHandler, xcodeTools: xcodeTools ) @@ -38,42 +45,385 @@ struct SwiftPackageFileAnalyzer: ProjectAnalyzing { new: packageHelper.packageDescription(at: newProjectPath) ) } else { - return .init( - changes: [], - warnings: [] - ) + return .init(changes: [], warnings: []) } } +} + +private extension SwiftPackageFileAnalyzer { private func analyze( - old oldPackageDescription: SwiftPackageDescription, - new newPackageDescription: SwiftPackageDescription + old: SwiftPackageDescription, + new: SwiftPackageDescription ) throws -> ProjectAnalyzerResult { - let oldLibraries = Set(oldPackageDescription.products.map(\.name)) - let newLibraries = Set(newPackageDescription.products.map(\.name)) + guard old != new else { return .init(changes: [], warnings: []) } + + var changes = [Change]() + changes += try analyzeToolsVersion(old: old.toolsVersion, new: new.toolsVersion) + + changes += try analyzeDefaultLocalization(old: old.defaultLocalization, new: new.defaultLocalization) + changes += try analyzeName(old: old.name, new: new.name) + + changes += try analyzePlatforms(old: old.platforms, new: new.platforms) + changes += try analyzeProducts(old: old.products, new: new.products) + changes += try analyzeTargets(old: old.targets, new: new.targets) + changes += try analyzeDependencies(old: old.dependencies, new: new.dependencies) + + return .init(changes: changes, warnings: new.warnings) + } + + // MARK: - Default Localization + + private func analyzeDefaultLocalization( + old: String?, + new: String? + ) throws -> [Change] { + guard old != new else { return [] } + + let keyName = "defaultLocalization" + + if let old, new == nil { + return [.init( + changeType: .removal(description: "\(keyName): \"\(old)\""), + parentName: Constants.packageFileName + )] + } + + if let new, old == nil { + return [.init( + changeType: .addition(description: "\(keyName): \"\(new)\""), + parentName: Constants.packageFileName + )] + } + + guard let new, let old else { return [] } + + return [.init( + changeType: .change( + oldDescription: "\(keyName): \"\(old)\"", + newDescription: "\(keyName): \"\(new)\"" + ), + parentName: Constants.packageFileName + )] + } + + // MARK: - Name + + private func analyzeName( + old: String, + new: String + ) throws -> [Change] { + guard old != new else { return [] } + + let keyName = "name" + + return [.init( + changeType: .change( + oldDescription: "\(keyName): \"\(old)\"", + newDescription: "\(keyName): \"\(new)\"" + ), + parentName: Constants.packageFileName + )] + } + + // MARK: - Platforms + + private func analyzePlatforms( + old: [SwiftPackageDescription.Platform], + new: [SwiftPackageDescription.Platform] + ) throws -> [Change] { + guard old != new else { return [] } + + let oldPlatformNames = Set(old.map(\.name)) + let newPlatformNames = Set(new.map(\.name)) - let removedLibaries = oldLibraries.subtracting(newLibraries) - var packageChanges = [Change]() + var listOfChanges = [String]() - packageChanges += removedLibaries.map { - .init( - changeType: .removal(description: ".library(name: \"\($0)\", ...)"), - parentName: "" + let added = newPlatformNames.subtracting(oldPlatformNames) + let removed = oldPlatformNames.subtracting(newPlatformNames) + let consistent = oldPlatformNames.intersection(newPlatformNames) + + listOfChanges += added.compactMap { platformName in + guard let addedPlatform = new.first(where: { $0.name == platformName }) else { return nil } + return "Added \(addedPlatform.description)" + } + + listOfChanges += consistent.compactMap { platformName in + guard + let newPlatform = new.first(where: { $0.name == platformName }), + let oldPlatform = old.first(where: { $0.name == platformName }) + else { return nil } + + return "Changed from \(oldPlatform.description) to \(newPlatform.description)" + } + + listOfChanges += removed.compactMap { platformName in + guard let removedPlatform = old.first(where: { $0.name == platformName }) else { return nil } + return "Removed \(removedPlatform.description)" + } + + let oldPlatformsString = old.map { "\($0.description)" }.joined(separator: ", ") + let newPlatformsString = new.map { "\($0.description)" }.joined(separator: ", ") + + return [.init( + changeType: .change( + oldDescription: "platforms: [\(oldPlatformsString)]", + newDescription: "platforms: [\(newPlatformsString)]" + ), + parentName: Constants.packageFileName, + listOfChanges: listOfChanges + )] + } + + // MARK: - Products + + private func analyzeProducts( + old: [SwiftPackageDescription.Product], + new: [SwiftPackageDescription.Product] + ) throws -> [Change] { + guard old != new else { return [] } + + let oldProductNames = Set(old.map(\.name)).filter { $0 != "_AllTargets" } + let newProductNames = Set(new.map(\.name)).filter { $0 != "_AllTargets" } + + let added = newProductNames.subtracting(oldProductNames) + let removed = oldProductNames.subtracting(newProductNames) + let consistent = Set(oldProductNames).intersection(Set(newProductNames)) + + var changes = [Change]() + + changes += added.compactMap { addition in + guard let addedProduct = new.first(where: { $0.name == addition }) else { return nil } + return .init( + changeType: .addition(description: addedProduct.description), + parentName: Constants.packageFileName(child: "products") + ) + } + + try consistent.forEach { productName in + guard + let oldProduct = old.first(where: { $0.name == productName }), + let newProduct = new.first(where: { $0.name == productName }) + else { return } + + changes += try analyzeProduct( + old: oldProduct, + new: newProduct ) } - let addedLibraries = newLibraries.subtracting(oldLibraries) - packageChanges += addedLibraries.map { - .init( - changeType: .addition(description: ".library(name: \"\($0)\", ...)"), - parentName: "" + changes += removed.compactMap { removal in + guard let removedProduct = old.first(where: { $0.name == removal }) else { return nil } + return .init( + changeType: .removal(description: removedProduct.description), + parentName: Constants.packageFileName(child: "products") ) } - return .init( - changes: packageChanges, - warnings: newPackageDescription.warnings - ) + return changes + } + + private func analyzeProduct( + old oldProduct: SwiftPackageDescription.Product, + new newProduct: SwiftPackageDescription.Product + ) throws -> [Change] { + guard oldProduct != newProduct else { return [] } + + let oldTargetNames = Set(oldProduct.targets) + let newTargetNames = Set(newProduct.targets) + + let added = newTargetNames.subtracting(oldTargetNames) + let removed = oldTargetNames.subtracting(newTargetNames) + + var listOfChanges = [String]() + listOfChanges += added.map { "Added target \"\($0)\"" } + listOfChanges += removed.map { "Removed target \"\($0)\"" } + + return [.init( + changeType: .change( + oldDescription: oldProduct.description, + newDescription: newProduct.description + ), + parentName: Constants.packageFileName(child: "products"), + listOfChanges: listOfChanges + )] + } + + // MARK: - Targets + + private func analyzeTargets( + old: [SwiftPackageDescription.Target], + new: [SwiftPackageDescription.Target] + ) throws -> [Change] { + guard old != new else { return [] } + + let oldTargetNames = Set(old.map(\.name)) + let newTargetNames = Set(new.map(\.name)) + + let added = newTargetNames.subtracting(oldTargetNames) + let removed = oldTargetNames.subtracting(newTargetNames) + let consistent = Set(oldTargetNames).intersection(Set(newTargetNames)) + + var changes = [Change]() + + changes += added.compactMap { addition in + guard let addedTarget = new.first(where: { $0.name == addition }) else { return nil } + return .init( + changeType: .addition(description: addedTarget.description), + parentName: Constants.packageFileName(child: "targets") + ) + } + + try consistent.forEach { productName in + guard + let oldTarget = old.first(where: { $0.name == productName }), + let newTarget = new.first(where: { $0.name == productName }) + else { return } + + changes += try analyzeTarget( + oldTarget: oldTarget, + newTarget: newTarget + ) + } + + changes += removed.compactMap { removal in + guard let removedTarget = old.first(where: { $0.name == removal }) else { return nil } + return .init( + changeType: .removal(description: removedTarget.description), + parentName: Constants.packageFileName(child: "targets") + ) + } + + return changes + } + + private func analyzeTarget( + oldTarget: SwiftPackageDescription.Target, + newTarget: SwiftPackageDescription.Target + ) throws -> [Change] { + guard oldTarget != newTarget else { return [] } + + // MARK: Target Dependencies + + let oldTargetDependencies = Set(oldTarget.targetDependencies ?? []) + let newTargetDependencies = Set(newTarget.targetDependencies ?? []) + + let addedTargetDependencies = newTargetDependencies.subtracting(oldTargetDependencies) + let removedTargetDependencies = oldTargetDependencies.subtracting(newTargetDependencies) + + // MARK: Product Dependencies + + let oldProductDependencies = Set(oldTarget.productDependencies ?? []) + let newProductDependencies = Set(newTarget.productDependencies ?? []) + + let addedProductDependencies = newProductDependencies.subtracting(oldProductDependencies) + let removedProductDependencies = oldProductDependencies.subtracting(newProductDependencies) + + var listOfChanges = [String]() + listOfChanges += addedTargetDependencies.map { "Added dependency .target(name: \"\($0)\")" } + listOfChanges += addedProductDependencies.map { "Added dependency .product(name: \"\($0)\", ...)" } + + if oldTarget.path != newTarget.path { + listOfChanges += ["Changed path from \"\(oldTarget.path)\" to \"\(newTarget.path)\""] + } + + if oldTarget.type != newTarget.type { + listOfChanges += ["Changed type from `.\(oldTarget.type.description)` to `.\(newTarget.type.description)`"] + } + + listOfChanges += removedTargetDependencies.map { "Removed dependency .target(name: \"\($0)\")" } + listOfChanges += removedProductDependencies.map { "Removed dependency .product(name: \"\($0)\", ...)" } + + return [.init( + changeType: .change( + oldDescription: oldTarget.description, + newDescription: newTarget.description + ), + parentName: Constants.packageFileName(child: "targets"), + listOfChanges: listOfChanges + )] + + } + + // MARK: - Dependencies + + private func analyzeDependencies( + old: [SwiftPackageDescription.Dependency], + new: [SwiftPackageDescription.Dependency] + ) throws -> [Change] { + guard old != new else { return [] } + + let oldDependencies = Set(old.map(\.identity)) + let newDependencies = Set(new.map(\.identity)) + + let addedDependencies = newDependencies.subtracting(oldDependencies) + let removedDependencies = oldDependencies.subtracting(newDependencies) + let consistentDependencies = oldDependencies.intersection(newDependencies) + + var changes = [Change]() + + changes += addedDependencies.compactMap { addition in + guard let addedDependency = new.first(where: { $0.identity == addition }) else { return nil } + return .init( + changeType: .addition(description: addedDependency.description), + parentName: Constants.packageFileName(child: "dependencies") + ) + } + + try consistentDependencies.forEach { dependencyIdentity in + guard + let oldDependency = old.first(where: { $0.identity == dependencyIdentity }), + let newDependency = new.first(where: { $0.identity == dependencyIdentity }) + else { return } + + changes += try analyzeDependency( + oldDependency: oldDependency, + newDependency: newDependency + ) + } + + changes += removedDependencies.compactMap { addition in + guard let removedDependency = old.first(where: { $0.identity == addition }) else { return nil } + return .init( + changeType: .removal(description: removedDependency.description), + parentName: Constants.packageFileName(child: "dependencies") + ) + } + + return changes + } + + private func analyzeDependency( + oldDependency: SwiftPackageDescription.Dependency, + newDependency: SwiftPackageDescription.Dependency + ) throws -> [Change] { + guard oldDependency != newDependency else { return [] } + + return [.init( + changeType: .change( + oldDescription: oldDependency.description, + newDescription: newDependency.description + ), + parentName: Constants.packageFileName(child: "dependencies"), + listOfChanges: [] // TODO: Improvement: Provide a `listOfChanges` + )] + } + + // MARK: - Tools Version + + private func analyzeToolsVersion( + old: String, + new: String + ) throws -> [Change] { + guard old != new else { return [] } + + return [.init( + changeType: .change( + oldDescription: "// swift-tools-version: \(old)", + newDescription: "// swift-tools-version: \(new)" + ), + parentName: Constants.packageFileName + )] } } diff --git a/Sources/Pipeline/Pipeline.swift b/Sources/Pipeline/Pipeline.swift index 8ada744..ade10fa 100644 --- a/Sources/Pipeline/Pipeline.swift +++ b/Sources/Pipeline/Pipeline.swift @@ -55,7 +55,7 @@ struct Pipeline { var changes = [String: [Change]]() var warnings = [String]() - try analyzeLibraryChanges( + try analyzeProjectChanges( oldProjectUrl: oldProjectUrl, newProjectUrl: newProjectUrl, changes: &changes, @@ -127,8 +127,8 @@ private extension Pipeline { return allTargets.sorted() } - func analyzeLibraryChanges(oldProjectUrl: URL, newProjectUrl: URL, changes: inout [String: [Change]], warnings: inout [String]) throws { - // Analyzing if there are any changes in available libraries between the project versions + func analyzeProjectChanges(oldProjectUrl: URL, newProjectUrl: URL, changes: inout [String: [Change]], warnings: inout [String]) throws { + let projectChanges = try projectAnalyzer.analyze( oldProjectUrl: oldProjectUrl, newProjectUrl: newProjectUrl @@ -156,13 +156,8 @@ private extension Pipeline { // Goes through all the available abi files and compares them try allTargets.forEach { target in - guard let oldAbiJson = oldAbiFiles.abiJsonFileUrl(for: target) else { - return changes[target] = [.removedTarget] - } - - guard let newAbiJson = newAbiFiles.abiJsonFileUrl(for: target) else { - return changes[target] = [.addedTarget] - } + guard let oldAbiJson = oldAbiFiles.abiJsonFileUrl(for: target) else { return } + guard let newAbiJson = newAbiFiles.abiJsonFileUrl(for: target) else { return } /* // Using `xcrun --sdk iphoneos swift-api-digester -diagnose-sdk` instead of the custom parser @@ -196,17 +191,6 @@ private extension Pipeline { // MARK: - Convenience -private extension Change { - - static var removedTarget: Self { - .init(changeType: .removal(description: "Target was removed"), parentName: "") - } - - static var addedTarget: Self { - .init(changeType: .addition(description: "Target was added"), parentName: "") - } -} - private extension [String: [Change]] { mutating func append(change: Change, to moduleName: String) { diff --git a/Tests/UnitTests/ABIGeneratorTests.swift b/Tests/UnitTests/ABIGeneratorTests.swift index 9f7a39b..c1c2632 100644 --- a/Tests/UnitTests/ABIGeneratorTests.swift +++ b/Tests/UnitTests/ABIGeneratorTests.swift @@ -15,8 +15,7 @@ class ABIGeneratorTests: XCTestCase { shell.handleExecute = { _ in let packageDescription = SwiftPackageDescription( defaultLocalization: "en-en", - products: [], - targets: [], + name: "Name", toolsVersion: "1.0" ) let encodedPackageDescription = try! JSONEncoder().encode(packageDescription) diff --git a/Tests/UnitTests/PipelineTests.swift b/Tests/UnitTests/PipelineTests.swift index bdfce9f..0c32d3c 100644 --- a/Tests/UnitTests/PipelineTests.swift +++ b/Tests/UnitTests/PipelineTests.swift @@ -85,7 +85,12 @@ class PipelineTests: XCTestCase { projectAnalyzerExpectation.fulfill() return .init( - changes: [.init(changeType: .addition(description: "A Library was added"), parentName: "Parent")], + changes: [.init( + changeType: .addition( + description: "A Library was added" + ), + parentName: "Parent" + )], warnings: [] ) }), diff --git a/Tests/UnitTests/ProjectBuilderTests.swift b/Tests/UnitTests/ProjectBuilderTests.swift index 604a35d..f0942cb 100644 --- a/Tests/UnitTests/ProjectBuilderTests.swift +++ b/Tests/UnitTests/ProjectBuilderTests.swift @@ -50,8 +50,7 @@ class ProjectBuilderTests: XCTestCase { if command == "cd \(baseWorkingDirectoryPath)/\(randomString); swift package describe --type json" { let packageDescription = SwiftPackageDescription( defaultLocalization: "en-en", - products: [], - targets: [], + name: "Name", toolsVersion: "1.0" ) let encodedPackageDescription = try! JSONEncoder().encode(packageDescription) diff --git a/Tests/UnitTests/Resources/dummi-abi-flat-definition.md b/Tests/UnitTests/Resources/dummi-abi-flat-definition.md index 494bbdb..1ae76c2 100644 --- a/Tests/UnitTests/Resources/dummi-abi-flat-definition.md +++ b/Tests/UnitTests/Resources/dummi-abi-flat-definition.md @@ -72,11 +72,11 @@ import _SwiftConcurrencyShims ``` ```javascript // Parent: AnalyticsFlavor -@_spi(AdyenInternal) public case components(type: Adyen.PaymentMethodType) +@_spi(AdyenInternal) case components(type: Adyen.PaymentMethodType) ``` ```javascript // Parent: AnalyticsFlavor -@_spi(AdyenInternal) public case dropIn(type: Swift.String, paymentMethods: [Swift.String]) +@_spi(AdyenInternal) case dropIn(type: Swift.String, paymentMethods: [Swift.String]) ``` ```javascript // Parent: AnalyticsFlavor @@ -136,59 +136,59 @@ import _SwiftConcurrencyShims ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case cardNumber +@_spi(AdyenInternal) case cardNumber ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case expiryDate +@_spi(AdyenInternal) case expiryDate ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case securityCode +@_spi(AdyenInternal) case securityCode ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case holderName +@_spi(AdyenInternal) case holderName ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case dualBrand +@_spi(AdyenInternal) case dualBrand ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case boletoSocialSecurityNumber +@_spi(AdyenInternal) case boletoSocialSecurityNumber ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case taxNumber +@_spi(AdyenInternal) case taxNumber ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case authPassWord +@_spi(AdyenInternal) case authPassWord ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case addressStreet +@_spi(AdyenInternal) case addressStreet ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case addressHouseNumber +@_spi(AdyenInternal) case addressHouseNumber ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case addressCity +@_spi(AdyenInternal) case addressCity ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case addressPostalCode +@_spi(AdyenInternal) case addressPostalCode ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case issuerList +@_spi(AdyenInternal) case issuerList ``` ```javascript // Parent: AnalyticsEventTarget -@_spi(AdyenInternal) public case listSearch +@_spi(AdyenInternal) case listSearch ``` ```javascript // Parent: AnalyticsEventTarget @@ -332,15 +332,15 @@ public init() -> Adyen.AnalyticsConfiguration ``` ```javascript // Parent: AnalyticsContext.Platform -@_spi(AdyenInternal) public case iOS +@_spi(AdyenInternal) case iOS ``` ```javascript // Parent: AnalyticsContext.Platform -@_spi(AdyenInternal) public case reactNative +@_spi(AdyenInternal) case reactNative ``` ```javascript // Parent: AnalyticsContext.Platform -@_spi(AdyenInternal) public case flutter +@_spi(AdyenInternal) case flutter ``` ```javascript // Parent: AnalyticsContext.Platform @@ -388,31 +388,31 @@ public init() -> Adyen.AnalyticsConfiguration ``` ```javascript // Parent: AnalyticsEventError.ErrorType -@_spi(AdyenInternal) public case network +@_spi(AdyenInternal) case network ``` ```javascript // Parent: AnalyticsEventError.ErrorType -@_spi(AdyenInternal) public case implementation +@_spi(AdyenInternal) case implementation ``` ```javascript // Parent: AnalyticsEventError.ErrorType -@_spi(AdyenInternal) public case internal +@_spi(AdyenInternal) case internal ``` ```javascript // Parent: AnalyticsEventError.ErrorType -@_spi(AdyenInternal) public case api +@_spi(AdyenInternal) case api ``` ```javascript // Parent: AnalyticsEventError.ErrorType -@_spi(AdyenInternal) public case sdk +@_spi(AdyenInternal) case sdk ``` ```javascript // Parent: AnalyticsEventError.ErrorType -@_spi(AdyenInternal) public case thirdParty +@_spi(AdyenInternal) case thirdParty ``` ```javascript // Parent: AnalyticsEventError.ErrorType -@_spi(AdyenInternal) public case generic +@_spi(AdyenInternal) case generic ``` ```javascript // Parent: AnalyticsEventError.ErrorType @@ -484,27 +484,27 @@ public init() -> Adyen.AnalyticsConfiguration ``` ```javascript // Parent: AnalyticsEventInfo.InfoType -@_spi(AdyenInternal) public case selected +@_spi(AdyenInternal) case selected ``` ```javascript // Parent: AnalyticsEventInfo.InfoType -@_spi(AdyenInternal) public case focus +@_spi(AdyenInternal) case focus ``` ```javascript // Parent: AnalyticsEventInfo.InfoType -@_spi(AdyenInternal) public case unfocus +@_spi(AdyenInternal) case unfocus ``` ```javascript // Parent: AnalyticsEventInfo.InfoType -@_spi(AdyenInternal) public case validationError +@_spi(AdyenInternal) case validationError ``` ```javascript // Parent: AnalyticsEventInfo.InfoType -@_spi(AdyenInternal) public case rendered +@_spi(AdyenInternal) case rendered ``` ```javascript // Parent: AnalyticsEventInfo.InfoType -@_spi(AdyenInternal) public case input +@_spi(AdyenInternal) case input ``` ```javascript // Parent: AnalyticsEventInfo.InfoType @@ -564,19 +564,19 @@ public init() -> Adyen.AnalyticsConfiguration ``` ```javascript // Parent: AnalyticsEventLog.LogType -@_spi(AdyenInternal) public case action +@_spi(AdyenInternal) case action ``` ```javascript // Parent: AnalyticsEventLog.LogType -@_spi(AdyenInternal) public case submit +@_spi(AdyenInternal) case submit ``` ```javascript // Parent: AnalyticsEventLog.LogType -@_spi(AdyenInternal) public case redirect +@_spi(AdyenInternal) case redirect ``` ```javascript // Parent: AnalyticsEventLog.LogType -@_spi(AdyenInternal) public case threeDS2 +@_spi(AdyenInternal) case threeDS2 ``` ```javascript // Parent: AnalyticsEventLog.LogType @@ -596,51 +596,51 @@ public init() -> Adyen.AnalyticsConfiguration ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case threeDS2 +@_spi(AdyenInternal) case threeDS2 ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case redirect +@_spi(AdyenInternal) case redirect ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case voucher +@_spi(AdyenInternal) case voucher ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case await +@_spi(AdyenInternal) case await ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case qrCode +@_spi(AdyenInternal) case qrCode ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case bankTransfer +@_spi(AdyenInternal) case bankTransfer ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case sdk +@_spi(AdyenInternal) case sdk ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case fingerprintSent +@_spi(AdyenInternal) case fingerprintSent ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case fingerprintComplete +@_spi(AdyenInternal) case fingerprintComplete ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case challengeDataSent +@_spi(AdyenInternal) case challengeDataSent ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case challengeDisplayed +@_spi(AdyenInternal) case challengeDisplayed ``` ```javascript // Parent: AnalyticsEventLog.LogSubType -@_spi(AdyenInternal) public case challengeComplete +@_spi(AdyenInternal) case challengeComplete ``` ```javascript // Parent: AnalyticsEventLog.LogSubType @@ -1652,35 +1652,35 @@ public init(environment: any AdyenNetworking.AnyAPIEnvironment, clientKey: Swift ``` ```javascript // Parent: AnalyticsEnvironment -@_spi(AdyenInternal) public case test +@_spi(AdyenInternal) case test ``` ```javascript // Parent: AnalyticsEnvironment -@_spi(AdyenInternal) public case liveEurope +@_spi(AdyenInternal) case liveEurope ``` ```javascript // Parent: AnalyticsEnvironment -@_spi(AdyenInternal) public case liveAustralia +@_spi(AdyenInternal) case liveAustralia ``` ```javascript // Parent: AnalyticsEnvironment -@_spi(AdyenInternal) public case liveUnitedStates +@_spi(AdyenInternal) case liveUnitedStates ``` ```javascript // Parent: AnalyticsEnvironment -@_spi(AdyenInternal) public case liveApse +@_spi(AdyenInternal) case liveApse ``` ```javascript // Parent: AnalyticsEnvironment -@_spi(AdyenInternal) public case liveIndia +@_spi(AdyenInternal) case liveIndia ``` ```javascript // Parent: AnalyticsEnvironment -@_spi(AdyenInternal) public case beta +@_spi(AdyenInternal) case beta ``` ```javascript // Parent: AnalyticsEnvironment -@_spi(AdyenInternal) public case local +@_spi(AdyenInternal) case local ``` ```javascript // Parent: AnalyticsEnvironment @@ -1800,11 +1800,11 @@ public static let liveIndia: Adyen.Environment { get } ``` ```javascript // Parent: AppleWalletPassRequest.CodingKeys -@_spi(AdyenInternal) public case platform +@_spi(AdyenInternal) case platform ``` ```javascript // Parent: AppleWalletPassRequest.CodingKeys -@_spi(AdyenInternal) public case passToken +@_spi(AdyenInternal) case passToken ``` ```javascript // Parent: AppleWalletPassRequest.CodingKeys @@ -2076,39 +2076,39 @@ public static let liveIndia: Adyen.Environment { get } ``` ```javascript // Parent: PaymentResultCode -@_spi(AdyenInternal) public case authorised +@_spi(AdyenInternal) case authorised ``` ```javascript // Parent: PaymentResultCode -@_spi(AdyenInternal) public case refused +@_spi(AdyenInternal) case refused ``` ```javascript // Parent: PaymentResultCode -@_spi(AdyenInternal) public case pending +@_spi(AdyenInternal) case pending ``` ```javascript // Parent: PaymentResultCode -@_spi(AdyenInternal) public case cancelled +@_spi(AdyenInternal) case cancelled ``` ```javascript // Parent: PaymentResultCode -@_spi(AdyenInternal) public case error +@_spi(AdyenInternal) case error ``` ```javascript // Parent: PaymentResultCode -@_spi(AdyenInternal) public case received +@_spi(AdyenInternal) case received ``` ```javascript // Parent: PaymentResultCode -@_spi(AdyenInternal) public case redirectShopper +@_spi(AdyenInternal) case redirectShopper ``` ```javascript // Parent: PaymentResultCode -@_spi(AdyenInternal) public case identifyShopper +@_spi(AdyenInternal) case identifyShopper ``` ```javascript // Parent: PaymentResultCode -@_spi(AdyenInternal) public case challengeShopper +@_spi(AdyenInternal) case challengeShopper ``` ```javascript // Parent: PaymentResultCode @@ -2168,31 +2168,31 @@ public convenience init(apiContext: Adyen.APIContext, payment: Adyen.Payment?, a ``` ```javascript // Parent: PersonalInformation -@_spi(AdyenInternal) public case firstName +@_spi(AdyenInternal) case firstName ``` ```javascript // Parent: PersonalInformation -@_spi(AdyenInternal) public case lastName +@_spi(AdyenInternal) case lastName ``` ```javascript // Parent: PersonalInformation -@_spi(AdyenInternal) public case email +@_spi(AdyenInternal) case email ``` ```javascript // Parent: PersonalInformation -@_spi(AdyenInternal) public case phone +@_spi(AdyenInternal) case phone ``` ```javascript // Parent: PersonalInformation -@_spi(AdyenInternal) public case address +@_spi(AdyenInternal) case address ``` ```javascript // Parent: PersonalInformation -@_spi(AdyenInternal) public case deliveryAddress +@_spi(AdyenInternal) case deliveryAddress ``` ```javascript // Parent: PersonalInformation -@_spi(AdyenInternal) public case custom(any Adyen.FormItemInjector) +@_spi(AdyenInternal) case custom(any Adyen.FormItemInjector) ``` ```javascript // Parent: PersonalInformation @@ -2428,15 +2428,15 @@ public enum PartialPaymentError : Equatable, Error, Hashable, LocalizedError, Se ``` ```javascript // Parent: PartialPaymentError -public case zeroRemainingAmount +case zeroRemainingAmount ``` ```javascript // Parent: PartialPaymentError -public case missingOrderData +case missingOrderData ``` ```javascript // Parent: PartialPaymentError -public case notSupportedForComponent +case notSupportedForComponent ``` ```javascript // Parent: PartialPaymentError @@ -3148,11 +3148,11 @@ public func didCancel() -> Swift.Void ``` ```javascript // Parent: NavigationBarType -@_spi(AdyenInternal) public case regular +@_spi(AdyenInternal) case regular ``` ```javascript // Parent: NavigationBarType -@_spi(AdyenInternal) public case custom(any Adyen.AnyNavigationBar) +@_spi(AdyenInternal) case custom(any Adyen.AnyNavigationBar) ``` ```javascript // Parent: Root @@ -3316,31 +3316,31 @@ public func disable(stored ``` ```javascript // Parent: APIErrorType -@_spi(AdyenInternal) public case internal +@_spi(AdyenInternal) case internal ``` ```javascript // Parent: APIErrorType -@_spi(AdyenInternal) public case validation +@_spi(AdyenInternal) case validation ``` ```javascript // Parent: APIErrorType -@_spi(AdyenInternal) public case security +@_spi(AdyenInternal) case security ``` ```javascript // Parent: APIErrorType -@_spi(AdyenInternal) public case configuration +@_spi(AdyenInternal) case configuration ``` ```javascript // Parent: APIErrorType -@_spi(AdyenInternal) public case urlError +@_spi(AdyenInternal) case urlError ``` ```javascript // Parent: APIErrorType -@_spi(AdyenInternal) public case noInternet +@_spi(AdyenInternal) case noInternet ``` ```javascript // Parent: APIErrorType -@_spi(AdyenInternal) public case sessionExpired +@_spi(AdyenInternal) case sessionExpired ``` ```javascript // Parent: APIErrorType @@ -3360,7 +3360,7 @@ public enum AppleWalletError : Equatable, Error, Hashable, LocalizedError, Senda ``` ```javascript // Parent: AppleWalletError -public case failedToAddToAppleWallet +case failedToAddToAppleWallet ``` ```javascript // Parent: AppleWalletError @@ -3380,11 +3380,11 @@ public enum ComponentError : Equatable, Error, Hashable, Sendable ``` ```javascript // Parent: ComponentError -public case cancelled +case cancelled ``` ```javascript // Parent: ComponentError -public case paymentMethodNotSupported +case paymentMethodNotSupported ``` ```javascript // Parent: ComponentError @@ -3416,179 +3416,179 @@ public enum CardType : Decodable, Encodable, Equatable, Hashable, RawRepresentab ``` ```javascript // Parent: CardType -public case accel +case accel ``` ```javascript // Parent: CardType -public case alphaBankBonusMasterCard +case alphaBankBonusMasterCard ``` ```javascript // Parent: CardType -public case alphaBankBonusVISA +case alphaBankBonusVISA ``` ```javascript // Parent: CardType -public case argencard +case argencard ``` ```javascript // Parent: CardType -public case americanExpress +case americanExpress ``` ```javascript // Parent: CardType -public case bcmc +case bcmc ``` ```javascript // Parent: CardType -public case bijenkorfCard +case bijenkorfCard ``` ```javascript // Parent: CardType -public case cabal +case cabal ``` ```javascript // Parent: CardType -public case carteBancaire +case carteBancaire ``` ```javascript // Parent: CardType -public case cencosud +case cencosud ``` ```javascript // Parent: CardType -public case chequeDejeneur +case chequeDejeneur ``` ```javascript // Parent: CardType -public case chinaUnionPay +case chinaUnionPay ``` ```javascript // Parent: CardType -public case codensa +case codensa ``` ```javascript // Parent: CardType -public case creditUnion24 +case creditUnion24 ``` ```javascript // Parent: CardType -public case dankort +case dankort ``` ```javascript // Parent: CardType -public case dankortVISA +case dankortVISA ``` ```javascript // Parent: CardType -public case diners +case diners ``` ```javascript // Parent: CardType -public case discover +case discover ``` ```javascript // Parent: CardType -public case elo +case elo ``` ```javascript // Parent: CardType -public case forbrugsforeningen +case forbrugsforeningen ``` ```javascript // Parent: CardType -public case hiper +case hiper ``` ```javascript // Parent: CardType -public case hipercard +case hipercard ``` ```javascript // Parent: CardType -public case jcb +case jcb ``` ```javascript // Parent: CardType -public case karenMillen +case karenMillen ``` ```javascript // Parent: CardType -public case kcp +case kcp ``` ```javascript // Parent: CardType -public case koreanLocalCard +case koreanLocalCard ``` ```javascript // Parent: CardType -public case laser +case laser ``` ```javascript // Parent: CardType -public case maestro +case maestro ``` ```javascript // Parent: CardType -public case maestroUK +case maestroUK ``` ```javascript // Parent: CardType -public case masterCard +case masterCard ``` ```javascript // Parent: CardType -public case mir +case mir ``` ```javascript // Parent: CardType -public case naranja +case naranja ``` ```javascript // Parent: CardType -public case netplus +case netplus ``` ```javascript // Parent: CardType -public case nyce +case nyce ``` ```javascript // Parent: CardType -public case oasis +case oasis ``` ```javascript // Parent: CardType -public case pulse +case pulse ``` ```javascript // Parent: CardType -public case shopping +case shopping ``` ```javascript // Parent: CardType -public case solo +case solo ``` ```javascript // Parent: CardType -public case star +case star ``` ```javascript // Parent: CardType -public case troy +case troy ``` ```javascript // Parent: CardType -public case uatp +case uatp ``` ```javascript // Parent: CardType -public case visa +case visa ``` ```javascript // Parent: CardType -public case warehouse +case warehouse ``` ```javascript // Parent: CardType -public case other(named: Swift.String) +case other(named: Swift.String) ``` ```javascript // Parent: CardType @@ -3668,11 +3668,11 @@ public enum ShopperInteraction : Decodable, Encodable, Equatable, Hashable, RawR ``` ```javascript // Parent: ShopperInteraction -public case shopperPresent +case shopperPresent ``` ```javascript // Parent: ShopperInteraction -public case shopperNotPresent +case shopperNotPresent ``` ```javascript // Parent: ShopperInteraction @@ -3780,11 +3780,11 @@ public enum CardFundingSource : Decodable, Encodable, Equatable, Hashable, RawRe ``` ```javascript // Parent: CardFundingSource -public case debit +case debit ``` ```javascript // Parent: CardFundingSource -public case credit +case credit ``` ```javascript // Parent: CardFundingSource @@ -3804,231 +3804,231 @@ public enum PaymentMethodType : Decodable, Encodable, Equatable, Hashable, RawRe ``` ```javascript // Parent: PaymentMethodType -public case card +case card ``` ```javascript // Parent: PaymentMethodType -public case scheme +case scheme ``` ```javascript // Parent: PaymentMethodType -public case ideal +case ideal ``` ```javascript // Parent: PaymentMethodType -public case entercash +case entercash ``` ```javascript // Parent: PaymentMethodType -public case eps +case eps ``` ```javascript // Parent: PaymentMethodType -public case dotpay +case dotpay ``` ```javascript // Parent: PaymentMethodType -public case onlineBankingPoland +case onlineBankingPoland ``` ```javascript // Parent: PaymentMethodType -public case openBankingUK +case openBankingUK ``` ```javascript // Parent: PaymentMethodType -public case molPayEBankingFPXMY +case molPayEBankingFPXMY ``` ```javascript // Parent: PaymentMethodType -public case molPayEBankingTH +case molPayEBankingTH ``` ```javascript // Parent: PaymentMethodType -public case molPayEBankingVN +case molPayEBankingVN ``` ```javascript // Parent: PaymentMethodType -public case sepaDirectDebit +case sepaDirectDebit ``` ```javascript // Parent: PaymentMethodType -public case applePay +case applePay ``` ```javascript // Parent: PaymentMethodType -public case payPal +case payPal ``` ```javascript // Parent: PaymentMethodType -public case bcmc +case bcmc ``` ```javascript // Parent: PaymentMethodType -public case bcmcMobile +case bcmcMobile ``` ```javascript // Parent: PaymentMethodType -public case qiwiWallet +case qiwiWallet ``` ```javascript // Parent: PaymentMethodType -public case weChatPaySDK +case weChatPaySDK ``` ```javascript // Parent: PaymentMethodType -public case mbWay +case mbWay ``` ```javascript // Parent: PaymentMethodType -public case blik +case blik ``` ```javascript // Parent: PaymentMethodType -public case dokuWallet +case dokuWallet ``` ```javascript // Parent: PaymentMethodType -public case dokuAlfamart +case dokuAlfamart ``` ```javascript // Parent: PaymentMethodType -public case dokuIndomaret +case dokuIndomaret ``` ```javascript // Parent: PaymentMethodType -public case giftcard +case giftcard ``` ```javascript // Parent: PaymentMethodType -public case doku +case doku ``` ```javascript // Parent: PaymentMethodType -public case econtextSevenEleven +case econtextSevenEleven ``` ```javascript // Parent: PaymentMethodType -public case econtextStores +case econtextStores ``` ```javascript // Parent: PaymentMethodType -public case econtextATM +case econtextATM ``` ```javascript // Parent: PaymentMethodType -public case econtextOnline +case econtextOnline ``` ```javascript // Parent: PaymentMethodType -public case boleto +case boleto ``` ```javascript // Parent: PaymentMethodType -public case affirm +case affirm ``` ```javascript // Parent: PaymentMethodType -public case oxxo +case oxxo ``` ```javascript // Parent: PaymentMethodType -public case bacsDirectDebit +case bacsDirectDebit ``` ```javascript // Parent: PaymentMethodType -public case achDirectDebit +case achDirectDebit ``` ```javascript // Parent: PaymentMethodType -public case multibanco +case multibanco ``` ```javascript // Parent: PaymentMethodType -public case atome +case atome ``` ```javascript // Parent: PaymentMethodType -public case onlineBankingCZ +case onlineBankingCZ ``` ```javascript // Parent: PaymentMethodType -public case onlineBankingSK +case onlineBankingSK ``` ```javascript // Parent: PaymentMethodType -public case mealVoucherNatixis +case mealVoucherNatixis ``` ```javascript // Parent: PaymentMethodType -public case mealVoucherGroupeUp +case mealVoucherGroupeUp ``` ```javascript // Parent: PaymentMethodType -public case mealVoucherSodexo +case mealVoucherSodexo ``` ```javascript // Parent: PaymentMethodType -public case upi +case upi ``` ```javascript // Parent: PaymentMethodType -public case cashAppPay +case cashAppPay ``` ```javascript // Parent: PaymentMethodType -public case twint +case twint ``` ```javascript // Parent: PaymentMethodType -public case other(Swift.String) +case other(Swift.String) ``` ```javascript // Parent: PaymentMethodType -public case bcmcMobileQR +case bcmcMobileQR ``` ```javascript // Parent: PaymentMethodType -public case weChatMiniProgram +case weChatMiniProgram ``` ```javascript // Parent: PaymentMethodType -public case weChatQR +case weChatQR ``` ```javascript // Parent: PaymentMethodType -public case weChatPayWeb +case weChatPayWeb ``` ```javascript // Parent: PaymentMethodType -public case googlePay +case googlePay ``` ```javascript // Parent: PaymentMethodType -public case afterpay +case afterpay ``` ```javascript // Parent: PaymentMethodType -public case androidPay +case androidPay ``` ```javascript // Parent: PaymentMethodType -public case amazonPay +case amazonPay ``` ```javascript // Parent: PaymentMethodType -public case upiCollect +case upiCollect ``` ```javascript // Parent: PaymentMethodType -public case upiIntent +case upiIntent ``` ```javascript // Parent: PaymentMethodType -public case upiQr +case upiQr ``` ```javascript // Parent: PaymentMethodType -public case bizum +case bizum ``` ```javascript // Parent: PaymentMethodType @@ -5440,11 +5440,11 @@ open func sanitizedValue(for: Swift.String) -> Swift.String ``` ```javascript // Parent: AdyenScope.LayoutAnchorSource -@_spi(AdyenInternal) public case view(UIKit.UIView) +@_spi(AdyenInternal) case view(UIKit.UIView) ``` ```javascript // Parent: AdyenScope.LayoutAnchorSource -@_spi(AdyenInternal) public case layoutGuide(UIKit.UILayoutGuide) +@_spi(AdyenInternal) case layoutGuide(UIKit.UILayoutGuide) ``` ```javascript // Parent: AdyenScope @@ -5752,7 +5752,7 @@ public enum DecodingError : Equatable, Error, Hashable, LocalizedError, Sendable ``` ```javascript // Parent: DelegatedAuthenticationData.DecodingError -public case invalidDelegatedAuthenticationData +case invalidDelegatedAuthenticationData ``` ```javascript // Parent: DelegatedAuthenticationData.DecodingError @@ -5772,11 +5772,11 @@ public var hashValue: Swift.Int { get } ``` ```javascript // Parent: DelegatedAuthenticationData -public case sdkOutput(Swift.String) +case sdkOutput(Swift.String) ``` ```javascript // Parent: DelegatedAuthenticationData -public case sdkInput(Swift.String) +case sdkInput(Swift.String) ``` ```javascript // Parent: DelegatedAuthenticationData @@ -5796,11 +5796,11 @@ public enum Plan : Equatable, Hashable, RawRepresentable ``` ```javascript // Parent: Installments.Plan -public case regular +case regular ``` ```javascript // Parent: Installments.Plan -public case revolving +case revolving ``` ```javascript // Parent: Installments.Plan @@ -6484,31 +6484,31 @@ public init() -> Adyen.AddressStyle ``` ```javascript // Parent: AddressField -@_spi(AdyenInternal) public case street +@_spi(AdyenInternal) case street ``` ```javascript // Parent: AddressField -@_spi(AdyenInternal) public case houseNumberOrName +@_spi(AdyenInternal) case houseNumberOrName ``` ```javascript // Parent: AddressField -@_spi(AdyenInternal) public case apartment +@_spi(AdyenInternal) case apartment ``` ```javascript // Parent: AddressField -@_spi(AdyenInternal) public case postalCode +@_spi(AdyenInternal) case postalCode ``` ```javascript // Parent: AddressField -@_spi(AdyenInternal) public case city +@_spi(AdyenInternal) case city ``` ```javascript // Parent: AddressField -@_spi(AdyenInternal) public case stateOrProvince +@_spi(AdyenInternal) case stateOrProvince ``` ```javascript // Parent: AddressField -@_spi(AdyenInternal) public case country +@_spi(AdyenInternal) case country ``` ```javascript // Parent: AddressField @@ -6540,11 +6540,11 @@ public init() -> Adyen.AddressStyle ``` ```javascript // Parent: AddressFormScheme -@_spi(AdyenInternal) public case item(Adyen.AddressField) +@_spi(AdyenInternal) case item(Adyen.AddressField) ``` ```javascript // Parent: AddressFormScheme -@_spi(AdyenInternal) public case split(Adyen.AddressField, Adyen.AddressField) +@_spi(AdyenInternal) case split(Adyen.AddressField, Adyen.AddressField) ``` ```javascript // Parent: Root @@ -6708,11 +6708,11 @@ public init() -> Adyen.AddressStyle ``` ```javascript // Parent: FormAddressPickerItem.AddressType -@_spi(AdyenInternal) public case billing +@_spi(AdyenInternal) case billing ``` ```javascript // Parent: FormAddressPickerItem.AddressType -@_spi(AdyenInternal) public case delivery +@_spi(AdyenInternal) case delivery ``` ```javascript // Parent: FormAddressPickerItem.AddressType @@ -7708,19 +7708,19 @@ public init() -> Adyen.FormTextItemStyle ``` ```javascript // Parent: FormTextItemView.AccessoryType -@_spi(AdyenInternal) public case invalid +@_spi(AdyenInternal) case invalid ``` ```javascript // Parent: FormTextItemView.AccessoryType -@_spi(AdyenInternal) public case valid +@_spi(AdyenInternal) case valid ``` ```javascript // Parent: FormTextItemView.AccessoryType -@_spi(AdyenInternal) public case customView(UIKit.UIView) +@_spi(AdyenInternal) case customView(UIKit.UIView) ``` ```javascript // Parent: FormTextItemView.AccessoryType -@_spi(AdyenInternal) public case none +@_spi(AdyenInternal) case none ``` ```javascript // Parent: FormTextItemView.AccessoryType @@ -8404,11 +8404,11 @@ public var title: Adyen.TextStyle { get } ``` ```javascript // Parent: ListItem.Icon.Location -@_spi(AdyenInternal) public case local(image: UIKit.UIImage) +@_spi(AdyenInternal) case local(image: UIKit.UIImage) ``` ```javascript // Parent: ListItem.Icon.Location -@_spi(AdyenInternal) public case remote(url: Foundation.URL?) +@_spi(AdyenInternal) case remote(url: Foundation.URL?) ``` ```javascript // Parent: ListItem.Icon.Location @@ -8544,11 +8544,11 @@ public static func ==(_: Adyen.ListItemStyle, _: Adyen.ListItemStyle) -> Swift.B ``` ```javascript // Parent: EditingStyle -@_spi(AdyenInternal) public case delete +@_spi(AdyenInternal) case delete ``` ```javascript // Parent: EditingStyle -@_spi(AdyenInternal) public case none +@_spi(AdyenInternal) case none ``` ```javascript // Parent: EditingStyle @@ -8868,15 +8868,15 @@ public enum CornerRounding : Equatable ``` ```javascript // Parent: CornerRounding -public case none +case none ``` ```javascript // Parent: CornerRounding -public case fixed(CoreGraphics.CGFloat) +case fixed(CoreGraphics.CGFloat) ``` ```javascript // Parent: CornerRounding -public case percent(CoreGraphics.CGFloat) +case percent(CoreGraphics.CGFloat) ``` ```javascript // Parent: CornerRounding @@ -9036,15 +9036,15 @@ public enum CancelButtonStyle ``` ```javascript // Parent: CancelButtonStyle -public case system +case system ``` ```javascript // Parent: CancelButtonStyle -public case legacy +case legacy ``` ```javascript // Parent: CancelButtonStyle -public case custom(UIKit.UIImage) +case custom(UIKit.UIImage) ``` ```javascript // Parent: Root @@ -9052,15 +9052,15 @@ public enum ToolbarMode : Equatable, Hashable ``` ```javascript // Parent: ToolbarMode -public case leftCancel +case leftCancel ``` ```javascript // Parent: ToolbarMode -public case rightCancel +case rightCancel ``` ```javascript // Parent: ToolbarMode -public case natural +case natural ``` ```javascript // Parent: ToolbarMode @@ -9672,11 +9672,11 @@ public var backgroundColor: UIKit.UIColor { get set } ``` ```javascript // Parent: Analytics.Flavor -@_spi(AdyenInternal) public case components +@_spi(AdyenInternal) case components ``` ```javascript // Parent: Analytics.Flavor -@_spi(AdyenInternal) public case dropin +@_spi(AdyenInternal) case dropin ``` ```javascript // Parent: Analytics.Flavor @@ -9828,15 +9828,15 @@ public enum Size : Equatable, Hashable, RawRepresentable ``` ```javascript // Parent: LogoURLProvider.Size -public case small +case small ``` ```javascript // Parent: LogoURLProvider.Size -public case medium +case medium ``` ```javascript // Parent: LogoURLProvider.Size -public case large +case large ``` ```javascript // Parent: LogoURLProvider.Size @@ -9856,15 +9856,15 @@ public var rawValue: Swift.String { get } ``` ```javascript // Parent: PhoneNumberPaymentMethod -@_spi(AdyenInternal) public case qiwiWallet +@_spi(AdyenInternal) case qiwiWallet ``` ```javascript // Parent: PhoneNumberPaymentMethod -@_spi(AdyenInternal) public case mbWay +@_spi(AdyenInternal) case mbWay ``` ```javascript // Parent: PhoneNumberPaymentMethod -@_spi(AdyenInternal) public case generic +@_spi(AdyenInternal) case generic ``` ```javascript // Parent: PhoneNumberPaymentMethod @@ -9956,11 +9956,11 @@ public var rawValue: Swift.String { get } ``` ```javascript // Parent: PaymentStyle -@_spi(AdyenInternal) public case needsRedirectToThirdParty(Swift.String) +@_spi(AdyenInternal) case needsRedirectToThirdParty(Swift.String) ``` ```javascript // Parent: PaymentStyle -@_spi(AdyenInternal) public case immediate +@_spi(AdyenInternal) case immediate ``` ```javascript // Parent: Root @@ -10160,7 +10160,7 @@ public var hashValue: Swift.Int { get } ``` ```javascript // Parent: PublicKeyProvider.Error -@_spi(AdyenInternal) public case invalidClientKey +@_spi(AdyenInternal) case invalidClientKey ``` ```javascript // Parent: PublicKeyProvider.Error @@ -10204,11 +10204,11 @@ public var hashValue: Swift.Int { get } ``` ```javascript // Parent: AddressAnalyticsValidationError -@_spi(AdyenInternal) public case postalCodeEmpty +@_spi(AdyenInternal) case postalCodeEmpty ``` ```javascript // Parent: AddressAnalyticsValidationError -@_spi(AdyenInternal) public case postalCodePartial +@_spi(AdyenInternal) case postalCodePartial ``` ```javascript // Parent: AddressAnalyticsValidationError @@ -10240,11 +10240,11 @@ public var hashValue: Swift.Int { get } ``` ```javascript // Parent: BalanceChecker.Error -@_spi(AdyenInternal) public case unexpectedCurrencyCode +@_spi(AdyenInternal) case unexpectedCurrencyCode ``` ```javascript // Parent: BalanceChecker.Error -@_spi(AdyenInternal) public case zeroBalance +@_spi(AdyenInternal) case zeroBalance ``` ```javascript // Parent: BalanceChecker.Error @@ -10316,7 +10316,7 @@ public enum ClientKeyError : Equatable, Error, Hashable, LocalizedError, Sendabl ``` ```javascript // Parent: ClientKeyError -public case invalidClientKey +case invalidClientKey ``` ```javascript // Parent: ClientKeyError @@ -10400,7 +10400,7 @@ public var hashValue: Swift.Int { get } ``` ```javascript // Parent: DateValidator.Format -@_spi(AdyenInternal) public case kcpFormat +@_spi(AdyenInternal) case kcpFormat ``` ```javascript // Parent: DateValidator.Format @@ -10548,11 +10548,11 @@ public var hashValue: Swift.Int { get } ``` ```javascript // Parent: ValidationStatus -@_spi(AdyenInternal) public case valid +@_spi(AdyenInternal) case valid ``` ```javascript // Parent: ValidationStatus -@_spi(AdyenInternal) public case invalid(any Adyen.ValidationError) +@_spi(AdyenInternal) case invalid(any Adyen.ValidationError) ``` ```javascript // Parent: ValidationStatus diff --git a/Tests/UnitTests/SDKDumpAnalyzerTests.swift b/Tests/UnitTests/SDKDumpAnalyzerTests.swift index d2578dc..ee70d59 100644 --- a/Tests/UnitTests/SDKDumpAnalyzerTests.swift +++ b/Tests/UnitTests/SDKDumpAnalyzerTests.swift @@ -137,9 +137,9 @@ class SDKDumpAnalyzerTests: XCTestCase { .init(changeType: .removal(description: "public struct childPrintedName"), parentName: "parent"), .init(changeType: .removal(description: "public class spiChildPrintedName"), parentName: "parent"), .init(changeType: .change(oldDescription: "public static var staticLetPrintedName: UNKNOWN_TYPE", newDescription: "public static let staticLetPrintedName: UNKNOWN_TYPE"), parentName: "parent.enumChild"), - .init(changeType: .removal(description: "public case oldCasePrintedName"), parentName: "parent.enumChild"), + .init(changeType: .removal(description: "case oldCasePrintedName"), parentName: "parent.enumChild"), .init(changeType: .addition(description: "public class childPrintedName"), parentName: "parent"), - .init(changeType: .addition(description: "public case newCasePrintedName"), parentName: "parent.enumChild") + .init(changeType: .addition(description: "case newCasePrintedName"), parentName: "parent.enumChild") ] let analyzer = SDKDumpAnalyzer() diff --git a/Tests/UnitTests/SwiftPackageFileAnalyzerTests.swift b/Tests/UnitTests/SwiftPackageFileAnalyzerTests.swift index 3642b03..5bf575e 100644 --- a/Tests/UnitTests/SwiftPackageFileAnalyzerTests.swift +++ b/Tests/UnitTests/SwiftPackageFileAnalyzerTests.swift @@ -23,8 +23,7 @@ class SwiftPackageFileAnalyzerTests: XCTestCase { shell.handleExecute = { _ in let packageDescription = SwiftPackageDescription( defaultLocalization: "en-en", - products: [], - targets: [], + name: "Name", toolsVersion: "1.0" ) let encodedPackageDescription = try! JSONEncoder().encode(packageDescription) @@ -66,17 +65,47 @@ class SwiftPackageFileAnalyzerTests: XCTestCase { if command.range(of: "NewPackage") != nil { packageDescription = SwiftPackageDescription( - defaultLocalization: "en-en", - products: [.init(name: "NewLibrary", targets: [])], - targets: [], + defaultLocalization: "en-us", + name: "New Name", + platforms: [.init(name: "iOS", version: "15.0"), .init(name: "visionOS", version: "1.0")], + products: [ + .init(name: "New Library", targets: ["New Target"]), + .init(name: "Some Library", targets: ["Some Target", "New Target"]) + ], + targets: [ + .init(name: "New Target", type: .binary, path: "new/path", moduleType: .swiftTarget), + .init( + name: "Some Target", + type: .library, + path: "some/new/path", + moduleType: .swiftTarget, + productDependencies: ["Some Product Dependency", "New Product Dependency"], + targetDependencies: ["Some Target Dependency", "New Target Dependency"] + ), + ], toolsVersion: "1.0" ) } else { packageDescription = SwiftPackageDescription( - defaultLocalization: "en-en", - products: [.init(name: "OldLibrary", targets: [])], - targets: [], - toolsVersion: "1.0" + defaultLocalization: "nl-nl", + name: "Old Name", + platforms: [.init(name: "iOS", version: "12.0"), .init(name: "macOS", version: "10.0")], + products: [ + .init(name: "Old Library", targets: ["Old Target"]), + .init(name: "Some Library", targets: ["Some Target", "Old Target"]) + ], + targets: [ + .init(name: "Old Target", type: .test, path: "old/path", moduleType: .swiftTarget), + .init( + name: "Some Target", + type: .binary, + path: "some/old/path", + moduleType: .swiftTarget, + productDependencies: ["Some Product Dependency", "Old Product Dependency"], + targetDependencies: ["Some Target Dependency", "Old Target Dependency"] + ), + ], + toolsVersion: "2.0" ) } @@ -86,7 +115,10 @@ class SwiftPackageFileAnalyzerTests: XCTestCase { let xcodeTools = XcodeTools(shell: shell) - let projectAnalyzer = SwiftPackageFileAnalyzer(fileHandler: fileHandler, xcodeTools: xcodeTools) + let projectAnalyzer = SwiftPackageFileAnalyzer( + fileHandler: fileHandler, + xcodeTools: xcodeTools + ) let changes = try projectAnalyzer.analyze( oldProjectUrl: URL(filePath: "OldPackage"), @@ -94,9 +126,98 @@ class SwiftPackageFileAnalyzerTests: XCTestCase { ) let expectedChanges: [Change] = [ - .init(changeType: .removal(description: ".library(name: \"OldLibrary\", ...)"), parentName: ""), - .init(changeType: .addition(description: ".library(name: \"NewLibrary\", ...)"), parentName: "") + .init( + changeType: .change( + oldDescription: "// swift-tools-version: 2.0", + newDescription: "// swift-tools-version: 1.0" + ), + parentName: "Package.swift", + listOfChanges: [] + ), + .init( + changeType: .change( + oldDescription: "defaultLocalization: \"nl-nl\"", + newDescription: "defaultLocalization: \"en-us\"" + ), + parentName: "Package.swift", + listOfChanges: [] + ), + .init( + changeType: .change( + oldDescription: "name: \"Old Name\"", + newDescription: "name: \"New Name\"" + ), + parentName: "Package.swift", + listOfChanges: [] + ), + .init( + changeType: .change( + oldDescription: "platforms: [iOS(12.0), macOS(10.0)]", + newDescription: "platforms: [iOS(15.0), visionOS(1.0)]" + ), + parentName: "Package.swift", + listOfChanges: [ + "Added visionOS(1.0)", + "Changed from iOS(12.0) to iOS(15.0)", + "Removed macOS(10.0)" + ] + ), + .init( + changeType: .addition( + description: ".library(name: \"New Library\", targets: [\"New Target\"])" + ), + parentName: "Package.swift / products", + listOfChanges: [] + ), + .init( + changeType: .change( + oldDescription: ".library(name: \"Some Library\", targets: [\"Some Target\", \"Old Target\"])", + newDescription: ".library(name: \"Some Library\", targets: [\"Some Target\", \"New Target\"])" + ), + parentName: "Package.swift / products", + listOfChanges: [ + "Added target \"New Target\"", + "Removed target \"Old Target\"" + ] + ), + .init( + changeType: .removal( + description: ".library(name: \"Old Library\", targets: [\"Old Target\"])" + ), + parentName: "Package.swift / products", + listOfChanges: [] + ), + .init( + changeType: .addition( + description: ".binaryTarget(name: \"New Target\", path: \"new/path\")" + ), + parentName: "Package.swift / targets", + listOfChanges: [] + ), + .init( + changeType: .change( + oldDescription: ".binaryTarget(name: \"Some Target\", dependencies: [.target(name: \"Some Target Dependency\"), .target(name: \"Old Target Dependency\"), .product(name: \"Some Product Dependency\", ...), .product(name: \"Old Product Dependency\", ...)], path: \"some/old/path\")", + newDescription: ".target(name: \"Some Target\", dependencies: [.target(name: \"Some Target Dependency\"), .target(name: \"New Target Dependency\"), .product(name: \"Some Product Dependency\", ...), .product(name: \"New Product Dependency\", ...)], path: \"some/new/path\")" + ), + parentName: "Package.swift / targets", + listOfChanges: [ + "Added dependency .target(name: \"New Target Dependency\")", + "Added dependency .product(name: \"New Product Dependency\", ...)", + "Changed path from \"some/old/path\" to \"some/new/path\"", + "Changed type from `.binaryTarget` to `.target`", + "Removed dependency .target(name: \"Old Target Dependency\")", + "Removed dependency .product(name: \"Old Product Dependency\", ...)" + ] + ), + .init( + changeType: .removal( + description: ".testTarget(name: \"Old Target\", path: \"old/path\")" + ), + parentName: "Package.swift / targets", + listOfChanges: [] + ) ] + XCTAssertEqual(changes.changes, expectedChanges) waitForExpectations(timeout: 1)