diff --git a/Package.swift b/Package.swift index 4d0b47a..b32a508 100644 --- a/Package.swift +++ b/Package.swift @@ -10,6 +10,7 @@ let package = Package( .executable(name: "jungle", targets: ["jungle"]), .library(name: "PodExtractor", targets: ["PodExtractor"]), .library(name: "DependencyGraph", targets: ["DependencyGraph"]), + .library(name: "Shell", targets: ["Shell"]) ], dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser", from: "1.1.3"), @@ -23,7 +24,8 @@ let package = Package( dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), .target(name: "PodExtractor"), - .target(name: "DependencyGraph") + .target(name: "DependencyGraph"), + .target(name: "Shell") ] ), .testTarget( @@ -36,6 +38,7 @@ let package = Package( name: "PodExtractor", dependencies: [ .target(name: "DependencyModule"), + .target(name: "Shell"), .product(name: "Yams", package: "Yams") ] ), @@ -57,6 +60,11 @@ let package = Package( // DependencyModule .target( name: "DependencyModule" + ), + + // Shell + .target( + name: "Shell" ) ] ) diff --git a/README.md b/README.md index 4e5a0b3..f66ea4a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A Swift command line tool to extract dependency information from a CocoaPods-bas - Number of dependant modules - Compare stats between different branches or even through the git history -You can read more information about dependency complexity in our Technical article ["How to control your dependencies"](https://medium.com/@OswaldoRubio/how-to-control-your-dependencies-7690cc7b1c40). +You can read more information about dependency complexity in our Technical article ["How to control your dependencies"](https://tech.xing.com/how-to-control-your-ios-dependencies-7690cc7b1c40). ## Table of contents @@ -48,14 +48,15 @@ swift build -c release ```shell OVERVIEW: Displays historic complexity of the dependency graph -USAGE: jungle history [--since ] [--pod ] [--output-format ] [] +USAGE: jungle history [--since ] [--pod ] --target [--output-format ] [] ARGUMENTS: Path to the directory where Podfile.lock is located (default: .) OPTIONS: --since Equivalent to git-log --since: Eg: '6 months ago' (default: 6 months ago) - --pod The Pod to generate a report for. Omitting this generates a report for a virtual `App` target that imports all Pods + --pod The Pod to generate a report for. Specifying a pod disregards the target parameter + --target The target in your Podfile file to be used --output-format csv or json (default: csv) --version Show the version. @@ -66,7 +67,7 @@ OPTIONS: Example: ```shell -jungle history ProjectDirectory/ --since '1 week ago' +jungle history --target App ProjectDirectory/ --since '1 week ago' 2022-08-30T15:12:14+02:00;cdb9d2ce64a;124;21063;Author;commit message 2022-09-02T11:02:12+02:00;4fdf3a157a4;124;21063;Author;commit message @@ -78,14 +79,15 @@ Now;Current;124;21063;; ```shell OVERVIEW: Compares the current complexity of the dependency graph to others versions in git -USAGE: jungle compare [--to ...] [--pod ] [] +USAGE: jungle compare [--to ...] [--pod ] --target [] ARGUMENTS: Path to the directory where Podfile.lock is located (default: .) OPTIONS: - --to The git objects to compare the current graph to. Eg: - 'main', 'my_branch', 'some_commit_hash'. (default: HEAD, main, master) - --pod The Pod to compare. Omitting this generates compares a virtual `App` target that imports all Pods + --to The git objects to compare the current graph to. Eg: - 'main', 'my_branch', 'some_commit_hash'. (default: HEAD, main) + --pod The Pod to compare. Specifying a pod disregards the target parameter + --target The target in your Podfile file to be used --version Show the version. -h, --help Show help information. ``` @@ -94,7 +96,7 @@ Example: ```shell -jungle compare ProjectDirectory/ --to main +jungle compare --target App ProjectDirectory/ --to main [ { "modules" : 124, @@ -116,16 +118,17 @@ jungle compare ProjectDirectory/ --to main ```shell OVERVIEW: Outputs the dependency graph in DOT format -USAGE: jungle graph [--of ] [--pod ] [] +USAGE: jungle graph [--of ] [--pod ] --target [] ARGUMENTS: Path to the directory where Podfile.lock is located (default: .) OPTIONS: --of A git object representing the version to draw the graph for. Eg: - 'main', 'my_branch', 'some_commit_hash'. - --pod The Pod to graph. Omitting this generates compares a virtual `App` target that imports all Pods + --pod The Pod to compare. Specifying a pod disregards the target parameter + --target The target in your Podfile file to be used --version Show the version. - -h, --help Show help information. + -h, --help Show help information ``` @@ -137,8 +140,8 @@ Outputs DOT format which can be viewed using http://viz-js.com 💡 Copy CSV (to paste in a spreadsheet) or DOT (to paste at http://viz-js.com) to the clipboard using `pbcopy` ```shell -jungle graph | pbcopy -jungle history | pbcopy +jungle graph --target App ProjectDirectory/ | pbcopy +jungle history --target App ProjectDirectory/ | pbcopy ``` @@ -146,7 +149,7 @@ jungle history | pbcopy ```shell brew install graphviz -jungle graph | dot -Tpng -o graph.png && open graph.png +jungle graph --target App ProjectDirectory/ | dot -Tpng -o graph.png && open graph.png ``` ## Contributing diff --git a/Sources/DependencyGraph/Graph+Make.swift b/Sources/DependencyGraph/Graph+Make.swift index c4aa91e..bdba360 100644 --- a/Sources/DependencyGraph/Graph+Make.swift +++ b/Sources/DependencyGraph/Graph+Make.swift @@ -6,7 +6,11 @@ public enum GraphError: Error { } public extension Graph { - static func makeForVirtualAppModule(name: String, dependencies: [Module]) throws -> Graph { + static func make(rootTargetName name: String, dependencies: [Module], targetDependencies: [String]?) throws -> Graph { + + let dependencies = dependencies + .filter { targetDependencies?.contains($0.name) ?? true } + let appModule = Module(name: name, dependencies: dependencies.map(\.name)) return try makeForModule(name: name, dependencies: [appModule] + dependencies) diff --git a/Sources/PodExtractor/Module+Podfile.swift b/Sources/PodExtractor/Module+Podfile.swift index 97d63c0..1038a7b 100644 --- a/Sources/PodExtractor/Module+Podfile.swift +++ b/Sources/PodExtractor/Module+Podfile.swift @@ -1,15 +1,90 @@ import Yams import DependencyModule +import Foundation +import Shell public enum PodError: Error { case yamlParsingFailed case missingPodsDictionary case missingSpecReposDictionary - case failedParsingPod + case failedParsingPod(String) case failedParsingPodName + case podTargetNotFound } -public func extractModulesFromPodfile(_ contents: String) throws -> [Module] { + +struct Podfile: Decodable { + let sources: [String]? + let targetDefinitions: [TargetDefinition] + + struct TargetDefinition: Decodable { + let abstract: Bool + let name: String + let children: [ChildrenDefinition] + } + + struct ChildrenDefinition: Decodable { + let name: String + let dependencies: [Dependency] + let children: [ChildrenDefinition]? + var asTarget: [Module] { + let target = Module(name: name, dependencies: dependencies.compactMap(\.name)) + let children = children ?? [] + return children.reduce([target]) { $0 + $1.asTarget } + } + + struct Dependency: Decodable { + let name: String? + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if let name = try? container.decode(String.self) { + self.name = name + } else if let keyName = try? container.decode([String: [String]].self).keys.first { + self.name = keyName + + } else if let keyName = try? container.decode([String: [[String: String]]].self).keys.first { + self.name = keyName + } else { + self.name = nil + } + } + + } + } +} + +public func moduleFromPodfile(_ contents: String, on target: String) throws -> Module? { + let tmp_podfile = try shell("mktemp PodfileXXXX").trimmingCharacters(in: .newlines) + try contents.write(toFile: tmp_podfile, atomically: true, encoding: .utf8) + let podfileJSON = try shell("pod ipc podfile-json \(tmp_podfile) --silent") + _ = try shell("rm \(tmp_podfile)") + return try moduleFromJSONPodfile(podfileJSON, onTarget: target) +} + +public func moduleFromJSONPodfile(_ contents: String, onTarget target: String) throws -> Module? { + try modulesFromJSONPodfile(contents) + .first(where: { $0.name == target }) +} + +public func modulesFromJSONPodfile(_ contents: String) throws -> [Module] { + let decoder = JSONDecoder() + decoder.keyDecodingStrategy = .convertFromSnakeCase + guard let data = contents.data(using: .utf8), + let pod = try? decoder.decode(Podfile.self, from: data) + else { + throw PodError.failedParsingPod(contents) + } + //first target is always Pods + guard let targetsRaw = pod.targetDefinitions.first?.children + else { + throw PodError.podTargetNotFound + } + + return targetsRaw.flatMap(\.asTarget) +} + +public func extractModulesFromPodfileLock(_ contents: String) throws -> [Module] { // parse YAML to JSON guard let yaml = try? Yams.load(yaml: contents) else { throw PodError.yamlParsingFailed @@ -51,7 +126,7 @@ private func extractPodFromJSON(_ json: Any) throws -> Module { ) } else { - throw PodError.failedParsingPod + throw PodError.failedParsingPod(json as? String ?? "") } } diff --git a/Sources/jungle/Commands/Shell.swift b/Sources/Shell/Shell.swift similarity index 57% rename from Sources/jungle/Commands/Shell.swift rename to Sources/Shell/Shell.swift index 380e24d..479b440 100644 --- a/Sources/jungle/Commands/Shell.swift +++ b/Sources/Shell/Shell.swift @@ -1,16 +1,18 @@ import Foundation -func shell(_ command: String, at currentDirectoryURL: URL) throws -> String { +public func shell(_ command: String, at currentDirectoryURL: URL? = nil) throws -> String { let task = Process() let pipe = Pipe() task.standardOutput = pipe task.standardError = pipe - task.arguments = ["-c", command] + task.arguments = ["--login", "-c", command] task.launchPath = "/bin/zsh" task.standardInput = nil - task.currentDirectoryURL = currentDirectoryURL - + if let currentDirectoryURL = currentDirectoryURL { + task.currentDirectoryURL = currentDirectoryURL + } + try task.run() let data = pipe.fileHandleForReading.readDataToEndOfFile() @@ -18,3 +20,4 @@ func shell(_ command: String, at currentDirectoryURL: URL) throws -> String { return output } + diff --git a/Sources/jungle/Commands/CompareCommand.swift b/Sources/jungle/Commands/CompareCommand.swift index d5f01bd..4c1abca 100644 --- a/Sources/jungle/Commands/CompareCommand.swift +++ b/Sources/jungle/Commands/CompareCommand.swift @@ -2,6 +2,21 @@ import ArgumentParser import Foundation import DependencyGraph import PodExtractor +import DependencyModule +import Shell + +public enum CompareError: Error { + case targetNotFound(target: String) +} + +extension CompareError: CustomStringConvertible { + public var description: String { + switch self { + case .targetNotFound(let target): + return "\"\(target)\" target not found!. Please, provide an existent target in your Podfile." + } + } +} struct CompareCommand: ParsableCommand { static var configuration = CommandConfiguration( @@ -16,8 +31,11 @@ struct CompareCommand: ParsableCommand { ) var gitObjects: [String] = ["HEAD", "main", "master"] - @Option(help: "The Pod to compare. Omitting this generates compares a virtual `App` target that imports all Pods") + @Option(help: "The Pod to compare. If you specify something, target parameter will be ommited") var pod: String? + + @Option(help: "The target in your Podfile file to be used") + var target: String @Argument(help: "Path to the directory where Podfile.lock is located") var directoryPath: String = "." @@ -26,17 +44,33 @@ struct CompareCommand: ParsableCommand { let directoryPath = (directoryPath as NSString).expandingTildeInPath let directoryURL = URL(fileURLWithPath: directoryPath, isDirectory: true) + // Choose the target to analyze + let podfileJSON = try shell("pod ipc podfile-json Podfile --silent", at: directoryURL) + + guard let currentTargetDependencies = try moduleFromJSONPodfile(podfileJSON, onTarget: target) else { + throw CompareError.targetNotFound(target: target) + } + let current = try process( label: "Current", pod: pod, - podfile: String(contentsOf: directoryURL.appendingPathComponent("Podfile.lock")) + podfile: String(contentsOf: directoryURL.appendingPathComponent("Podfile.lock")), + target: currentTargetDependencies ) let outputs = [current] + gitObjects.compactMap { - try? process( + guard + let podfile = try? shell("git show \($0):Podfile"), + let entryTargetDependencies = try? moduleFromPodfile(podfile, on: target) + else { + return nil + } + + return try? process( label: $0, pod: pod, - podfile: shell("git show \($0):Podfile.lock", at: directoryURL) + podfile: shell("git show \($0):Podfile.lock", at: directoryURL), + target: entryTargetDependencies ) } @@ -48,14 +82,14 @@ struct CompareCommand: ParsableCommand { } } -func process(label: String, pod: String?, podfile: String) throws -> CompareStatsOutput { - let dependencies = try extractModulesFromPodfile(podfile) +func process(label: String, pod: String?, podfile: String, target: Module) throws -> CompareStatsOutput { + let dependencies = try extractModulesFromPodfileLock(podfile) let graph: Graph if let pod = pod { graph = try Graph.makeForModule(name: pod, dependencies: dependencies) } else { - graph = try Graph.makeForVirtualAppModule(name: "App", dependencies: dependencies) + graph = try Graph.make(rootTargetName: target.name, dependencies: dependencies, targetDependencies: target.dependencies) } return CompareStatsOutput(label: label, graph: graph) diff --git a/Sources/jungle/Commands/GraphCommand.swift b/Sources/jungle/Commands/GraphCommand.swift index c749070..cb395be 100644 --- a/Sources/jungle/Commands/GraphCommand.swift +++ b/Sources/jungle/Commands/GraphCommand.swift @@ -2,6 +2,8 @@ import ArgumentParser import Foundation import DependencyGraph import PodExtractor +import DependencyModule +import Shell struct GraphCommand: ParsableCommand { static var configuration = CommandConfiguration( @@ -15,41 +17,59 @@ struct GraphCommand: ParsableCommand { ) var gitObject: String? - @Option(help: "The Pod to graph. Omitting this generates compares a virtual `App` target that imports all Pods") + @Option(help: "The Pod to compare. If you specify something, target parameter will be ommited") var pod: String? + @Option(help: "The target in your Podfile file to be used") + var target: String + @Argument(help: "Path to the directory where Podfile.lock is located") var directoryPath: String = "." func run() throws { let directoryPath = (directoryPath as NSString).expandingTildeInPath let directoryURL = URL(fileURLWithPath: directoryPath, isDirectory: true) - if let gitObject = gitObject { + guard + let podfile = try? shell("git show \(gitObject):Podfile", at: directoryURL), + let gitObjectTargetDependencies = try? moduleFromPodfile(podfile, on: target) + else { + throw CompareError.targetNotFound(target: target) + } + try print( makeDOT( podfile: shell("git show \(gitObject):Podfile.lock", at: directoryURL), - label: gitObject + label: gitObject, + target: gitObjectTargetDependencies ) ) } else { + // Choose the target to analyze + let podfileJSON = try shell("pod ipc podfile-json Podfile --silent", at: directoryURL) + + guard let targetWithDependencies = try moduleFromJSONPodfile(podfileJSON, onTarget: target) else { + throw CompareError.targetNotFound(target: target) + } + print( try makeDOT( podfile: String(contentsOf: directoryURL.appendingPathComponent("Podfile.lock")), - label: "Current" + label: "Current", + target: targetWithDependencies ) ) } } - private func makeDOT(podfile: String, label: String) throws -> String { - let dependencies = try extractModulesFromPodfile(podfile) + private func makeDOT(podfile: String, label: String, target: Module) throws -> String { + let dependencies = try extractModulesFromPodfileLock(podfile) let graph: Graph if let pod = pod { graph = try Graph.makeForModule(name: pod, dependencies: dependencies) } else { - graph = try Graph.makeForVirtualAppModule(name: "App", dependencies: dependencies) + graph = try Graph.make(rootTargetName: target.name, dependencies: dependencies, targetDependencies: target.dependencies) } return graph.multiEdgeDOT diff --git a/Sources/jungle/Commands/HistoryCommand.swift b/Sources/jungle/Commands/HistoryCommand.swift index 53f9ddb..e5aa8ca 100644 --- a/Sources/jungle/Commands/HistoryCommand.swift +++ b/Sources/jungle/Commands/HistoryCommand.swift @@ -2,6 +2,8 @@ import ArgumentParser import Foundation import DependencyGraph import PodExtractor +import DependencyModule +import Shell struct HistoryCommand: AsyncParsableCommand { @@ -18,8 +20,11 @@ struct HistoryCommand: AsyncParsableCommand { @Option(help: "Equivalent to git-log --since: Eg: '6 months ago'") var since: String = "6 months ago" - @Option(help: "The Pod to generate a report for. Omitting this generates a report for a virtual `App` target that imports all Pods") + @Option(help: "The Pod to compare. If you specify something, target parameter will be ommited") var pod: String? + + @Option(help: "The target in your Podfile file to be used") + var target: String @Option(help: "csv or json") var outputFormat: OutputFormat = .csv @@ -32,6 +37,14 @@ struct HistoryCommand: AsyncParsableCommand { let directoryURL = URL(fileURLWithPath: directoryPath, isDirectory: true) let podfileURL = directoryURL.appendingPathComponent("Podfile.lock") + + // Choose the target to analyze + let podfileJSON = try shell("pod ipc podfile-json Podfile --silent", at: directoryURL) + + guard let currentTargetDependencies = try moduleFromJSONPodfile(podfileJSON, onTarget: target) else { + throw CompareError.targetNotFound(target: target) + } + // retrieve logs let gitLog = "git log --since='\(since)' --first-parent --format='%h;%aI;%an;%s' -- Podfile.lock" let logs = try shell(gitLog, at: directoryURL) @@ -41,16 +54,19 @@ struct HistoryCommand: AsyncParsableCommand { .map(GitLogEntry.parse) // process Podfile.lock in current directory - let current = try await GitLogEntry.current.process(pod: pod, podfile: String(contentsOf: podfileURL)) + let current = try await GitLogEntry.current.process(pod: pod, podfile: String(contentsOf: podfileURL), target: currentTargetDependencies) // process Podfile.lock for past commits let output = try await withThrowingTaskGroup(of: HistoryStatsOutput.self, returning: [HistoryStatsOutput].self) { group in for entry in logs.lazy { group.addTask { - try await entry.process( + let podfile = try shell("git show \(entry.revision):Podfile", at: directoryURL) + let entryTargetDependencies = try moduleFromPodfile(podfile, on: target) ?? .init(name: target, dependencies: []) + return try await entry.process( pod: pod, - podfile: shell("git show \(entry.revision):Podfile.lock", at: directoryURL) + podfile: shell("git show \(entry.revision):Podfile.lock", at: directoryURL), + target: entryTargetDependencies ) } } @@ -78,14 +94,14 @@ struct HistoryCommand: AsyncParsableCommand { } extension GitLogEntry { - func process(pod: String?, podfile: String) async throws -> HistoryStatsOutput { - let dependencies = try extractModulesFromPodfile(podfile) + func process(pod: String?, podfile: String, target: Module) async throws -> HistoryStatsOutput { + let dependencies = try extractModulesFromPodfileLock(podfile) let graph: Graph if let pod = pod { graph = try Graph.makeForModule(name: pod, dependencies: dependencies) } else { - graph = try Graph.makeForVirtualAppModule(name: "App", dependencies: dependencies) + graph = try Graph.make(rootTargetName: target.name, dependencies: dependencies, targetDependencies: target.dependencies) } return HistoryStatsOutput(entry: self, graph: graph) diff --git a/Tests/DependencyGraphTests/DependencyGraphTests.swift b/Tests/DependencyGraphTests/DependencyGraphTests.swift index af4ee20..090dc8b 100644 --- a/Tests/DependencyGraphTests/DependencyGraphTests.swift +++ b/Tests/DependencyGraphTests/DependencyGraphTests.swift @@ -36,7 +36,7 @@ final class DependencyGraphTests: XCTestCase { let b = Module(name: "B", dependencies: ["B1", "B2", "C"]) let c = Module(name: "C", dependencies: ["C1", "C2"]) - let graph = try Graph.makeForVirtualAppModule(name: "Top", dependencies: [a, b, c]) + let graph = try Graph.make(rootTargetName: "Top", dependencies: [a, b, c], targetDependencies: nil) XCTAssertEqual(graph.multiEdges.count, 15) XCTAssertEqual(graph.uniqueEdges.count, 11) XCTAssertEqual(graph.nodes.count, 10) diff --git a/Tests/PodExtractorTests/PodExtractorTests.swift b/Tests/PodExtractorTests/PodExtractorTests.swift index e566648..417fe2d 100644 --- a/Tests/PodExtractorTests/PodExtractorTests.swift +++ b/Tests/PodExtractorTests/PodExtractorTests.swift @@ -12,7 +12,7 @@ final class PodExtractorTests: XCTestCase { - D (1.0.0) """ - let modules = try extractModulesFromPodfile(podfile) + let modules = try extractModulesFromPodfileLock(podfile) XCTAssertEqual(modules.count, 2) } @@ -24,7 +24,7 @@ final class PodExtractorTests: XCTestCase { - B/Tests (1.0.0) """ - let modules = try extractModulesFromPodfile(podfile) + let modules = try extractModulesFromPodfileLock(podfile) XCTAssertEqual(modules.count, 1) } @@ -45,7 +45,7 @@ final class PodExtractorTests: XCTestCase { - ACPCore """ - let modules = try extractModulesFromPodfile(podfile) + let modules = try extractModulesFromPodfileLock(podfile) XCTAssertEqual(modules.count, 1) } @@ -75,8 +75,171 @@ final class PodExtractorTests: XCTestCase { COCOAPODS: 1.11.3 """ - let modules = try extractModulesFromPodfile(podfile) + let modules = try extractModulesFromPodfileLock(podfile) XCTAssertEqual(modules.count, 2) } + + func testTargetModulesFromPodfileLock() throws { + + // Example Podfile from https://github.com/artsy/eidolon + let podfile = """ + { + "sources" : [ + "https://github.com/artsy/Specs.git", + "https://cdn.cocoapods.org/" + ], + "target_definitions" : [ + { + "abstract" : true, + "children" : [ + { + "children" : [ + { + "abstract" : false, + "dependencies" : [ + "FBSnapshotTestCase", + "Nimble-Snapshots", + "Quick", + "Nimble", + "RxNimble", + "Forgeries", + "RxBlocking" + ], + "inheritance" : "search_paths", + "name" : "KioskTests" + } + ], + "dependencies" : [ + "Artsy+UIColors", + "Artsy+UILabels", + "Artsy-UIButtons", + "Artsy+OSSUIFonts", + { + "FLKAutoLayout" : [ + "0.1.1" + ] + }, + { + "ARCollectionViewMasonryLayout" : [ + "~> 2.0.0" + ] + }, + { + "SDWebImage" : [ + "~> 3.7" + ] + }, + "SVProgressHUD", + { + "HockeySDK-Source" : [ + { + "git" : "https://github.com/bitstadium/HockeySDK-iOS.git" + } + ] + }, + "ARAnalytics/Segmentio", + "ARAnalytics/HockeyApp", + "CardFlight-v4", + { + "Stripe" : [ + "14.0.1" + ] + }, + "ECPhoneNumberFormatter", + { + "UIImageViewAligned" : [ + { + "git" : "https://github.com/ashfurrow/UIImageViewAligned.git" + } + ] + }, + { + "DZNWebViewController" : [ + { + "git" : "https://github.com/orta/DZNWebViewController.git" + } + ] + }, + "ReachabilitySwift", + "UIView+BooleanAnimations", + "ARTiledImageView", + "XNGMarkdownParser", + "ISO8601DateFormatter", + "SwiftyJSON", + "RxSwift", + "RxCocoa", + "RxOptional", + "Moya/RxSwift", + "NSObject+Rx", + "Action" + ], + "name" : "Kiosk" + } + ], + "inhibit_warnings" : { + "all" : true + }, + "name" : "Pods", + "platform" : { + "ios" : "10.0" + }, + "uses_frameworks" : { + "linkage" : "dynamic", + "packaging" : "framework" + } + } + ] + } + """ + + let targets = try modulesFromJSONPodfile(podfile) + + XCTAssertEqual(targets.count, 2) + let expectedDependencies = ["Artsy+UIColors", + "Artsy+UILabels", + "Artsy-UIButtons", + "Artsy+OSSUIFonts", + "FLKAutoLayout", + "ARCollectionViewMasonryLayout", + "SDWebImage", + "SVProgressHUD", + "HockeySDK-Source", + "ARAnalytics/Segmentio", + "ARAnalytics/HockeyApp", + "CardFlight-v4", + "Stripe", + "ECPhoneNumberFormatter", + "UIImageViewAligned", + "DZNWebViewController", + "ReachabilitySwift", + "UIView+BooleanAnimations", + "ARTiledImageView", + "XNGMarkdownParser", + "ISO8601DateFormatter", + "SwiftyJSON", + "RxSwift", + "RxCocoa", + "RxOptional", + "Moya/RxSwift", + "NSObject+Rx", + "Action"] + + let firstTarget = try XCTUnwrap(targets.first) + XCTAssertEqual(firstTarget.name, "Kiosk") + XCTAssertEqual(firstTarget.dependencies, expectedDependencies) + + let secondTarget = try XCTUnwrap(targets.last) + + + XCTAssertEqual(secondTarget.name, "KioskTests") + XCTAssertEqual(secondTarget.dependencies, ["FBSnapshotTestCase", + "Nimble-Snapshots", + "Quick", + "Nimble", + "RxNimble", + "Forgeries", + "RxBlocking"] + ) + } } diff --git a/Tests/jungleTests/GitLogEntryTests.swift b/Tests/jungleTests/GitLogEntryTests.swift index 3715879..26cf895 100644 --- a/Tests/jungleTests/GitLogEntryTests.swift +++ b/Tests/jungleTests/GitLogEntryTests.swift @@ -1,4 +1,5 @@ import XCTest +import DependencyModule @testable import jungle final class GitLogEntryTests: XCTestCase { @@ -32,8 +33,10 @@ final class GitLogEntryTests: XCTestCase { from: "abbd80e;2022-07-09T21:29:20+02:00;Shammi Didla;restructure \n something" ) - let row = try await entry.process(pod: nil, podfile: podfile).csv - XCTAssertEqual(row.description, "2022-07-09T21:29:20+02:00;abbd80e;5;1;Shammi Didla;restructure . something") + let target: Module = .init(name: "T", dependencies: ["A", "C"]) + + let row = try await entry.process(pod: nil, podfile: podfile, target: target).csv + XCTAssertEqual(row.description, "2022-07-09T21:29:20+02:00;abbd80e;4;1;Shammi Didla;restructure . something") } }