From 7cde9bac8b83d1e755ae50f0dc0228a43c25c0a2 Mon Sep 17 00:00:00 2001 From: Jeffrey Macko Date: Tue, 17 Dec 2019 13:00:35 +0100 Subject: [PATCH] On avance vers la 1.0 (#6) * Improve testing * Remove __Snapshots__ * Update Swift Format * Simplification de la configuration swift format * Fix formatage * Add cli interface * Sa avance peniblement * Fix * On avance sur le command parser * Improve tooling * Need to rework the errors * Improve formating * Fix bitrise lookup * bitrise * formmating --- .githooks/pre-commit | 11 +- .swift-format | 48 +----- Makefile | 7 +- Package.resolved | 9 ++ Package.swift | 24 ++- Sources/PlatformLookup/Platform.swift | 4 +- .../PlatformLookup+DeviceFamily.swift | 7 + .../PlatformLookup/PlatformLookup+Error.swift | 125 +++++++++++++-- .../PlatformLookup+Filters.swift | 20 ++- .../PlatformLookup+FindDevices.swift | 147 ++++++++++-------- Sources/PlatformLookup/PlatformLookup.swift | 6 +- Sources/Shell/Shell.swift | 4 +- Sources/cli/Command.swift | 69 ++++++++ Sources/cli/main.swift | 5 + Sources/cli/parser.swift | 115 ++++++++++++++ Sources/cli/run.swift | 46 ++++++ .../PlatformLookupTests.swift | 126 +++++++++++++-- Tests/ShellTests/ShellTests.swift | 7 +- .../ShellTests/test_SimpleTest.1.txt | 1 - bitrisePlatformLookup.swift | 29 ++++ findDevicePlatform.swift | 17 -- 21 files changed, 647 insertions(+), 180 deletions(-) create mode 100644 Sources/cli/Command.swift create mode 100755 Sources/cli/main.swift create mode 100644 Sources/cli/parser.swift create mode 100644 Sources/cli/run.swift delete mode 100644 Tests/ShellTests/__Snapshots__/ShellTests/test_SimpleTest.1.txt create mode 100755 bitrisePlatformLookup.swift delete mode 100755 findDevicePlatform.swift diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 12fab72..23d34dc 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -5,7 +5,9 @@ if ! [ -x "$(command -v swift)" ]; then echo 'Error: swift is not installed.' >&2 echo 'You should install it before continuing' >&2 exit 1 -elif ! [ -x "$(command -v swift-format)" ]; then +fi + +if ! [ -x "$(command -v swift-format)" ]; then echo 'Error: swift-format is not installed.' >&2 git clone https://github.com/apple/swift-format.git /tmp/__hawkci__/swift-format cd /tmp/__hawkci__/swift-format @@ -22,6 +24,13 @@ else echo "You might need to re-commit to commit the updated formmated code" fi +if ! [ -x "$(command -v generate-enum-properties)" ]; then + echo "Installing swift-enum-properties" + git clone https://github.com/pointfreeco/swift-enum-properties.git /tmp/__hawkci__/swift-enum-properties + cd /tmp/__hawkci__/swift-enum-properties + make install +fi + # # An example hook script to verify what is about to be committed. # Called by "git commit" with no arguments. The hook should diff --git a/.swift-format b/.swift-format index 0abec54..010bf8f 100644 --- a/.swift-format +++ b/.swift-format @@ -1,53 +1,7 @@ { - "blankLineBetweenMembers" : { - "ignoreSingleLineProperties" : true - }, - "indentation" : { - "spaces" : 2 - }, - "indentConditionalCompilationBlocks" : true, - "lineBreakBeforeControlFlowKeywords" : false, + "lineLength" : 80, "lineBreakBeforeEachArgument" : true, - "lineLength" : 100, - "maximumBlankLines" : 1, "respectsExistingLineBreaks" : false, - "rules" : { - "AllPublicDeclarationsHaveDocumentation" : true, - "AlwaysUseLowerCamelCase" : true, - "AmbiguousTrailingClosureOverload" : true, - "BeginDocumentationCommentWithOneLineSummary" : true, - "BlankLineBetweenMembers" : false, - "CaseIndentLevelEqualsSwitch" : true, - "DoNotUseSemicolons" : true, - "DontRepeatTypeInStaticProperties" : true, - "FullyIndirectEnum" : true, - "GroupNumericLiterals" : true, - "IdentifiersMustBeASCII" : true, - "MultiLineTrailingCommas" : true, - "NeverForceUnwrap" : true, - "NeverUseForceTry" : true, - "NeverUseImplicitlyUnwrappedOptionals" : true, - "NoAccessLevelOnExtensionDeclaration" : true, - "NoBlockComments" : true, - "NoCasesWithOnlyFallthrough" : true, - "NoEmptyTrailingClosureParentheses" : true, - "NoLabelsInCasePatterns" : true, - "NoLeadingUnderscores" : true, - "NoParensAroundConditions" : true, - "NoVoidReturnOnFunctionSignature" : true, - "OneCasePerLine" : true, - "OneVariableDeclarationPerLine" : true, - "OnlyOneTrailingClosureArgument" : true, - "OrderedImports" : true, - "ReturnVoidInsteadOfEmptyTuple" : true, - "UseEnumForNamespacing" : true, - "UseLetInEveryBoundCaseVariable" : true, - "UseShorthandTypeNames" : true, - "UseSingleLinePropertyGetter" : true, - "UseSynthesizedInitializer" : true, - "UseTripleSlashForDocumentationComments" : true, - "ValidateDocumentationComments" : true - }, "tabWidth" : 4, "version" : 1 } diff --git a/Makefile b/Makefile index d918d12..e545ddf 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,13 @@ build: swift build format: - swift-format -m format --configuration .swift-format -r -i Sources + swift-format -m format --configuration .swift-format -r -i Sources/**/*.swift lint: - swift-format -m lint --configuration .swift-format -r -i Sources + swift-format -m lint --configuration .swift-format -r -i Sources/**/*.swift + +generate-enum-properties: + generate-enum-properties Sources/**/*.swift gitignore-flush: git rm -r --cached . diff --git a/Package.resolved b/Package.resolved index d272995..0654793 100644 --- a/Package.resolved +++ b/Package.resolved @@ -10,6 +10,15 @@ "version": "1.7.0" } }, + { + "package": "swift-tools-support-core", + "repositoryURL": "https://github.com/apple/swift-tools-support-core.git", + "state": { + "branch": "master", + "revision": "edc19d30a674cb9f3311b77ffb406dc7c5d2f540", + "version": null + } + }, { "package": "Version", "repositoryURL": "https://github.com/mrackwitz/Version.git", diff --git a/Package.swift b/Package.swift index b4338d1..abe1dd0 100644 --- a/Package.swift +++ b/Package.swift @@ -7,19 +7,33 @@ let package = Package( name: "PlatformLookup", platforms: [.macOS(.v10_14)], products: [ + .executable(name: "cli", targets: ["cli"]), .library(name: "SimulatorControl", targets: ["SimulatorControl"]), .library(name: "Shell", targets: ["Shell"]), .library(name: "PlatformLookup", targets: ["PlatformLookup"]), ], dependencies: [ + .package( + url: "https://github.com/apple/swift-tools-support-core.git", + .branch("master") + ), // en attendant une release stable .package(url: "https://github.com/mrackwitz/Version.git", from: "0.7.2"), - .package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git", from: "1.7.0"), + .package( + url: "https://github.com/pointfreeco/swift-snapshot-testing.git", + from: "1.7.0" + ), ], targets: [ - .target(name: "PlatformLookup", dependencies: ["SimulatorControl", "Shell"]), - .target(name: "Shell"), .target(name: "SimulatorControl", dependencies: ["Version"]), - .testTarget(name: "PlatformLookupTests", dependencies: ["PlatformLookup", "SnapshotTesting"]), - .testTarget(name: "ShellTests", dependencies: ["Shell", "SnapshotTesting"]), + .target(name: "cli", dependencies: ["PlatformLookup", "SwiftToolsSupport"]), + .target( + name: "PlatformLookup", + dependencies: ["SimulatorControl", "Shell"] + ), .target(name: "Shell"), + .target(name: "SimulatorControl", dependencies: ["Version"]), + .testTarget( + name: "PlatformLookupTests", + dependencies: ["PlatformLookup", "SnapshotTesting"] + ), .testTarget(name: "ShellTests", dependencies: ["Shell"]), ], swiftLanguageVersions: [.v5] ) diff --git a/Sources/PlatformLookup/Platform.swift b/Sources/PlatformLookup/Platform.swift index bd363e7..7e86b75 100644 --- a/Sources/PlatformLookup/Platform.swift +++ b/Sources/PlatformLookup/Platform.swift @@ -3,8 +3,8 @@ import SimulatorControl /// <#Description#> public struct Platform: Equatable, Codable { - let devices: [Device] - let runtime: Runtime + public let devices: [Device] + public let runtime: Runtime /// <#Description#> /// - Parameters: diff --git a/Sources/PlatformLookup/PlatformLookup+DeviceFamily.swift b/Sources/PlatformLookup/PlatformLookup+DeviceFamily.swift index 246a9b2..c8c12fd 100644 --- a/Sources/PlatformLookup/PlatformLookup+DeviceFamily.swift +++ b/Sources/PlatformLookup/PlatformLookup+DeviceFamily.swift @@ -15,5 +15,12 @@ extension PlatformLookup { case .appleWatch: return "watchOS" } } + var simulatorName: String { + switch self { + case .iPhone, .iPad: return "iOS Simulator" + case .appleTV: return "tvOS Simulator" + case .appleWatch: return "watchOS Simulator" + } + } } } diff --git a/Sources/PlatformLookup/PlatformLookup+Error.swift b/Sources/PlatformLookup/PlatformLookup+Error.swift index 214ef47..c7e56a4 100644 --- a/Sources/PlatformLookup/PlatformLookup+Error.swift +++ b/Sources/PlatformLookup/PlatformLookup+Error.swift @@ -4,8 +4,12 @@ extension PlatformLookup { /// <#Description#> public enum PlatformLookupError: Error, LocalizedError { case failedToInitializeDataIsNotValid - case unknowDeviceFamilly(String) + case invalidIndex(file: String, function: String, line: Int) + case noResultForThisCombinaison(device: String, runtime: String) case noRuntimeFound + case thisShouldNeverAppen(file: String, function: String, line: Int) + case unknowDevice(String) + case unknowRuntime(String) /// A localized message describing what error occurred. public var errorDescription: String? { switch self { @@ -14,25 +18,118 @@ extension PlatformLookup { "Invalid data at initilisation of `PlatformLookup`", comment: "Invalid Data" ) - case .unknowDeviceFamilly(let input): - return NSLocalizedString("Device familly \(input) unknown", comment: "Unknown Device") - case .noRuntimeFound: return NSLocalizedString("Runtime unknown", comment: "Unknown Runtime") + case .invalidIndex(let file, let function, let line): + return NSLocalizedString( + "Invalid Index at \(function) in \(file):\(line)", + comment: "Invalid Index" + ) + case .noResultForThisCombinaison(let device, let runtime): + return NSLocalizedString( + "No result found for device: \(device), runtime: \(runtime)", + comment: "Invalid device/runtime combinaison" + ) + case .noRuntimeFound: + return NSLocalizedString( + "No runtime found", + comment: "No runtime found" + ) + case .thisShouldNeverAppen(let file, let function, let line): + let message = + "This should really never happen please look at \(function) in \(file):\(line)" + #if DEBUG + assertionFailure(message) + #endif + return NSLocalizedString(message, comment: "Unknown Runtime") + case .unknowDevice(let device): + return NSLocalizedString("Unknown \(device)", comment: "unknown device") + case .unknowRuntime(let runtime): + return NSLocalizedString( + "Unknown \(runtime)", + comment: "Unknown runtime" + ) } } /// A localized message describing the reason for the failure. public var failureReason: String? { - return "failureReason" // switch self { - // case .failedToInitializeDataIsNotValid: - // <#code#> - // case .unknowDeviceFamilly(_): - // <#code#> - // case .noRuntimeFound: - // <#code#> - // } + return "failureReason - need to be implemented" } /// A localized message describing how one might recover from the failure. - public var recoverySuggestion: String? { return "failureReason" } + public var recoverySuggestion: String? { + return "recoverySuggestion - need to be implemented" + } /// A localized message providing "help" text if the user requests help. - public var helpAnchor: String? { return "failureReason" } + public var helpAnchor: String? { + return "helpAnchor - need to be implemented" + } + public var invalidIndex: (file: String, function: String, line: Int)? { + get { + guard case let .invalidIndex(value) = self else { return nil } + return value + } + set { + guard case .invalidIndex = self, let newValue = newValue else { return } + self = .invalidIndex( + file: newValue.0, + function: newValue.1, + line: newValue.2 + ) + } + } + public var noResultForThisCombinaison: (device: String, runtime: String)? { + get { + guard case let .noResultForThisCombinaison(value) = self else { + return nil + } + return value + } + set { + guard case .noResultForThisCombinaison = self, let newValue = newValue + else { return } + self = .noResultForThisCombinaison( + device: newValue.0, + runtime: newValue.1 + ) + } + } + public var thisShouldNeverAppen: + (file: String, function: String, line: Int)? + { + get { + guard case let .thisShouldNeverAppen(value) = self else { return nil } + return value + } + set { + guard case .thisShouldNeverAppen = self, let newValue = newValue else { + return + } + self = .thisShouldNeverAppen( + file: newValue.0, + function: newValue.1, + line: newValue.2 + ) + } + } + public var unknowDevice: String? { + get { + guard case let .unknowDevice(value) = self else { return nil } + return value + } + set { + guard case .unknowDevice = self, let newValue = newValue else { return } + self = .unknowDevice(newValue) + } + } + public var unknowRuntime: String? { + get { + guard case let .unknowRuntime(value) = self else { return nil } + return value + } + set { + guard case .unknowRuntime = self, let newValue = newValue else { + return + } + self = .unknowRuntime(newValue) + } + } } } diff --git a/Sources/PlatformLookup/PlatformLookup+Filters.swift b/Sources/PlatformLookup/PlatformLookup+Filters.swift index d863b6f..cece840 100644 --- a/Sources/PlatformLookup/PlatformLookup+Filters.swift +++ b/Sources/PlatformLookup/PlatformLookup+Filters.swift @@ -8,7 +8,9 @@ extension PlatformLookup { /// - Parameters: /// - name: <#name description#> /// - version: <#version description#> - static public func filterRuntime(_ name: String, version: String? = nil) -> ((Runtime) -> Bool) { + static public func filterRuntime(_ name: String, version: String? = nil) -> ( + (Runtime) -> Bool + ) { return { runtime in let f1 = runtime.name.contains(name) guard let version = version else { return f1 } return f1 && runtime.version.contains(version) @@ -18,17 +20,19 @@ extension PlatformLookup { // MARK: - Device Filter /// <#Description#> /// - Parameter deviceName: <#deviceName description#> - static public func filterDeviceName(_ deviceName: String) -> ((Device) -> Bool) { - return { $0.name.contains(deviceName) } - } + static public func filterDeviceName(_ deviceName: String) -> ( + (Device) -> Bool + ) { return { $0.name.contains(deviceName) } } /// <#Description#> /// - Parameters: /// - deviceFamily: <#deviceFamily description#> /// - isAvailable: <#isAvailable description#> - static public func filterDeviceFamily(_ deviceFamily: DeviceFamily, isAvailable: Bool? = nil) -> ( - (Device) -> Bool - ) { - return { device in let containExpectedDevice = device.name.contains(deviceFamily.rawValue) + static public func filterDeviceFamily( + _ deviceFamily: DeviceFamily, + isAvailable: Bool? = nil + ) -> ((Device) -> Bool) { + return { device in + let containExpectedDevice = device.name.contains(deviceFamily.rawValue) guard let isAvailable = isAvailable else { return containExpectedDevice } return (containExpectedDevice && isAvailable) } diff --git a/Sources/PlatformLookup/PlatformLookup+FindDevices.swift b/Sources/PlatformLookup/PlatformLookup+FindDevices.swift index bc7f937..47874d0 100644 --- a/Sources/PlatformLookup/PlatformLookup+FindDevices.swift +++ b/Sources/PlatformLookup/PlatformLookup+FindDevices.swift @@ -1,94 +1,115 @@ import Foundation +import SimulatorControl extension PlatformLookup { - // MARK: - Find Devices - - /// Trouve un device pour la derniere version de l'OS par default cherche un iPhone - /// platform=\"iOS Simulator,name=iPhone 11 Pro Max,OS=13.2\" - /// <#Description#> - /// - Parameter deviceFamily: <#deviceFamily description#> - static public func findADeviceForLastOSVersion(_ deviceFamily: DeviceFamily = .iPhone) throws - -> String? - { - return try PlatformLookup.shared?.getPlatform( - with: filterDeviceFamily(deviceFamily), - runtimeFilter: filterRuntime(deviceFamily.os, version: nil) - ) - } - - /// Trouve un device pour la derniere version de l'OS par default cherche un iPhone - /// <#Description#> - /// - Parameter deviceFamily: <#deviceFamily description#> - static public func findADeviceForLastOSVersion(_ deviceFamily: DeviceFamily = .iPhone) throws - -> Platform? + static public func deviceFamilyFrom(_ deviceName: String) throws + -> DeviceFamily { - return try PlatformLookup.shared?.getPlatform( - with: filterDeviceFamily(deviceFamily), - runtimeFilter: filterRuntime(deviceFamily.os, version: nil) - ) + guard + let deviceFamily = DeviceFamily.allCases.first(where: { + deviceName.contains($0.rawValue) + }) + else { throw (PlatformLookupError.unknowDevice(deviceName)) } + return deviceFamily } - - /// Trouve tous les devices pour une version d'os - /// <#Description#> + // MARK: - Find Devices + /// Cherche un device qui correspond a un nom précis /// - Parameters: - /// - deviceFamily: <#deviceFamily description#> + /// - deviceName: <#deviceName description#> /// - version: <#version description#> - static public func findAllDevicesForAnOSVersion( - _ deviceFamily: DeviceFamily, + static public func findAllDeviceNamed( + _ deviceName: String, version: String? = nil ) throws -> [Platform] { - return try PlatformLookup.shared?.getAllDevices( - with: filterDeviceFamily(deviceFamily), + let deviceFamily = try deviceFamilyFrom(deviceName) + let platforms = try PlatformLookup.shared?.getAllDevices( + with: filterDeviceName(deviceName), runtimeFilter: filterRuntime(deviceFamily.os, version: version) - ) ?? [] + ) + guard let validPlatforms = platforms, validPlatforms.isEmpty == false else { + throw ( + PlatformLookupError.noResultForThisCombinaison( + device: deviceName, + runtime: + "os: \(deviceFamily.os), version: \(version ?? "")" + ) + ) + } + return validPlatforms } - /// Trouve tous les devices pour une version d'os - /// <#Description#> + /// Cherche un device qui correspond a un nom précis /// - Parameters: /// - deviceName: <#deviceName description#> /// - version: <#version description#> - static public func findAllDevicesForAnOSVersion(deviceName: String, version: String? = nil) throws - -> [Platform] - { - guard let deviceFamily = DeviceFamily(rawValue: deviceName) else { - throw (PlatformLookupError.unknowDeviceFamilly(deviceName)) + static public func findAllDevice( + _ deviceFamily: DeviceFamily = .iPhone, + version: String? = nil + ) throws -> [Platform] { + let platforms = try PlatformLookup.shared?.getAllDevices( + with: filterDeviceFamily(deviceFamily), + runtimeFilter: filterRuntime(deviceFamily.os, version: version) + ) + guard let validPlatforms = platforms, validPlatforms.isEmpty == false else { + throw ( + PlatformLookupError.noResultForThisCombinaison( + device: deviceFamily.rawValue, + runtime: + "os: \(deviceFamily.os), version: \(version ?? "")" + ) + ) } - return try PlatformLookup.findAllDevicesForAnOSVersion(deviceFamily, version: version) + return validPlatforms } - // MARK: - Private + /// Trouve un device pour la derniere version de l'OS par default cherche un iPhone /// <#Description#> - /// - Parameters: - /// - deviceFilter: <#deviceFilter description#> - /// - runtimeFilter: <#runtimeFilter description#> - private func getPlatform(with deviceFilter: DeviceFilter, runtimeFilter: RuntimeFilter) throws - -> String? + /// - Parameter deviceFamily: <#deviceFamily description#> + static public func findADeviceForLastOSVersion(_ deviceFamily: DeviceFamily) + throws -> Platform { - if let platform = try getAllDevices(with: deviceFilter, runtimeFilter: runtimeFilter).last { - return "iOS Simulator,name=\(platform.devices.last!.name),OS=\(platform.runtime.version)" + let platform = try PlatformLookup.shared?.getAllDevices( + with: filterDeviceFamily(deviceFamily), + runtimeFilter: filterRuntime(deviceFamily.os, version: nil) + ).last + guard let validPlatform = platform else { + throw ( + PlatformLookupError.noResultForThisCombinaison( + device: deviceFamily.rawValue, + runtime: "os: \(deviceFamily.os), version: " + ) + ) } - return nil + return validPlatform } - + // MARK: - Platform /// <#Description#> /// - Parameters: - /// - deviceFilter: <#deviceFilter description#> - /// - runtimeFilter: <#runtimeFilter description#> - private func getPlatform(with deviceFilter: DeviceFilter, runtimeFilter: RuntimeFilter) throws - -> Platform? - { return try getAllDevices(with: deviceFilter, runtimeFilter: runtimeFilter).last } - + /// - platform: <#platform description#> + /// - deviceIndex: <#deviceIndex description#> + static public func format( + _ platform: Platform, + deviceFamily: DeviceFamily, + deviceIndex: Int? = nil + ) throws -> String { + let device: Device + if let idx = deviceIndex { device = platform.devices[idx] } + else { device = platform.devices.last! } + return + "\(deviceFamily.simulatorName),name=\(device.name),OS=\(platform.runtime.version)" + } + // MARK: - Private /// <#Description#> /// - Parameters: /// - deviceFilter: <#deviceFilter description#> /// - runtimeFilter: <#runtimeFilter description#> - private func getAllDevices(with deviceFilter: DeviceFilter, runtimeFilter: RuntimeFilter) throws - -> [Platform] - { - guard let filteredRuntimes = simctl.runtimes?.filter(runtimeFilter) else { - throw (PlatformLookupError.noRuntimeFound) - } + private func getAllDevices( + with deviceFilter: DeviceFilter, + runtimeFilter: RuntimeFilter + ) throws -> [Platform] { + guard let filteredRuntimes = simctl.runtimes?.filter(runtimeFilter), + filteredRuntimes.count > 0 + else { throw (PlatformLookupError.noRuntimeFound) } let sortedRuntimes = filteredRuntimes.sorted(by: <) return sortedRuntimes.compactMap { (runtime) -> Platform? in diff --git a/Sources/PlatformLookup/PlatformLookup.swift b/Sources/PlatformLookup/PlatformLookup.swift index 16ece24..5bf4860 100644 --- a/Sources/PlatformLookup/PlatformLookup.swift +++ b/Sources/PlatformLookup/PlatformLookup.swift @@ -25,8 +25,10 @@ public final class PlatformLookup { /// <#Description#> /// - Parameter data: <#data description#> - private init(_ data: Data?) throws { - guard let data = data else { throw (PlatformLookupError.failedToInitializeDataIsNotValid) } + internal init(_ data: Data?) throws { + guard let data = data else { + throw (PlatformLookupError.failedToInitializeDataIsNotValid) + } simctl = try JSONDecoder().decode(SimulatorControl.self, from: data) } diff --git a/Sources/Shell/Shell.swift b/Sources/Shell/Shell.swift index 5ad38b3..b8b972d 100644 --- a/Sources/Shell/Shell.swift +++ b/Sources/Shell/Shell.swift @@ -16,7 +16,9 @@ public enum ShellError: Error { case pipeOutputFailedToDecode } /// - Parameters: /// - command: <#command description#> /// - arguments: <#arguments description#> -@discardableResult public func shell(_ command: String, arguments: [String]) throws -> String { +@discardableResult public func shell(_ command: String, arguments: [String]) + throws -> String +{ let task = Process() task.launchPath = "/bin/bash" task.arguments = arguments diff --git a/Sources/cli/Command.swift b/Sources/cli/Command.swift new file mode 100644 index 0000000..efc540f --- /dev/null +++ b/Sources/cli/Command.swift @@ -0,0 +1,69 @@ +import Foundation + +enum OSVersionCommand { + case version(String) + case lastOS + + var version: String? { + get { + guard case let .version(value) = self else { return nil } + return value + } + set { + guard case .version = self, let newValue = newValue else { return } + self = .version(newValue) + } + } + + var lastOS: Void? { + guard case .lastOS = self else { return nil } + return () + } +} + +enum Command { + enum CommandError: Error { case toto } + case name(String) + case osVersion(OSVersionCommand) + case version + case help + init(arguments: [String]) throws { + let (parser, binder) = (kArgParser, kArgBinder) + let result = try parser.parse(arguments) + var command = Command.help + try binder.fill(parseResult: result, into: &command) + self = command + } + + var name: String? { + get { + guard case let .name(value) = self else { return nil } + return value + } + set { + guard case .name = self, let newValue = newValue else { return } + self = .name(newValue) + } + } + + var osVersion: OSVersionCommand? { + get { + guard case let .osVersion(value) = self else { return nil } + return value + } + set { + guard case .osVersion = self, let newValue = newValue else { return } + self = .osVersion(newValue) + } + } + + var version: Void? { + guard case .version = self else { return nil } + return () + } + + var help: Void? { + guard case .help = self else { return nil } + return () + } +} diff --git a/Sources/cli/main.swift b/Sources/cli/main.swift new file mode 100755 index 0000000..230a38a --- /dev/null +++ b/Sources/cli/main.swift @@ -0,0 +1,5 @@ +import Foundation + +let version = "1.0.0" + +exit(run(arguments: Array(CommandLine.arguments.dropFirst()))) diff --git a/Sources/cli/parser.swift b/Sources/cli/parser.swift new file mode 100644 index 0000000..d054db6 --- /dev/null +++ b/Sources/cli/parser.swift @@ -0,0 +1,115 @@ +import TSCUtility + +private let mainOverview = """ + PlatformLookup \(version) + A Swift docstring linter, formatter, nitpicky assistant... + DrString helps you locate and fix docstring problems such as missing + documentation or whitespace errors. It help your team to write consistent + docstrings. + Learn more at: https://github.com/dduan/DrString + """ + +private let mainUsage = """ + SUBCOMMAND [OPTIONS]... + EXAMPLES: + Check every Swift source under ./Sources for problems: + drstring check -i "Sources/**/*.swift" + Explain the problem associated with ID `E007`: + drstring explain E007 + Automatically fix formatting issues according to a config file: + drstring format --config-file path/to/.drstring.toml + """ + +private let nameOverview = """ + Check (lint) source files for docstring problems in given paths. + """ + +private let nameUsage = """ + [OPTIONS]... + Paths are specfied using the `-i/--include` option, they can be repeated. + Globstar is supported (see example). + Flags can be used to specify preferred styles for docstrings. + A configuration file can be used instead of command line options to specify + preferred styles. The options are equivalent in both methods. + EXAMPLES: + Examine all Swift files under `./Sources` recursively: + drstring check -i 'Sources/**/*.swift' + Use full name for `-i` (include), exclude some paths from being checked, + ignore throws, require first letter of keywords (e.g. `throws`, `returns`, + etc) to be uppercase: + drstring check \\ + --include 'Sources/**/*.swift' \\ + --include 'Tests/**/*.swift' \\ + --exclude 'Tests/Fixtures/*.swift' \\ + --ignore-throws true \\ + --first-letter uppercase + """ + +private let osVersionOverview = """ + Fix docstring formatting errors for sources in given paths. + """ + +private let osVersionUsage = """ + [OPTIONS]... + Paths are specfied using the `-i/--include` option, they can be repeated. + Globstar is supported (see example). + Flags can be used to specify preferred styles for docstrings. + A configuration file can be used instead of command line options to specify + preferred styles. The options are equivalent in both methods. + EXAMPLES: + Fix all Swift files under `./Sources` recursively: + drstring format -i 'Sources/**/*.swift' + Use full name for `-i` (include), exclude some paths from being checked, + ignore throws, require an empty docstring line after parameters, and + continuation of descriptions to vertical align after ':'s. + drstring format \\ + --include 'Sources/**/*.swift' \\ + --include 'Tests/**/*.swift' \\ + --exclude 'Tests/Fixtures/*.swift' \\ + --needs-separation parameters \\ + --align-after-colon parameters + """ + +let versionOverview = "Print version." +let helpOverview = "Print help." + +let (kArgParser, kArgBinder): (ArgumentParser, ArgumentBinder) = { + let binder = ArgumentBinder() + let main = ArgumentParser(usage: mainUsage, overview: mainOverview) + let name = main.add( + subparser: "name", + usage: nameUsage, + overview: nameOverview + ) + let osVersion = main.add( + subparser: "osVersion", + usage: osVersionUsage, + overview: osVersionOverview + ) + _ = main.add(subparser: "version", usage: "", overview: versionOverview) + _ = main.add(subparser: "help", usage: "", overview: helpOverview) + binder.bind(parser: main) { (commandOption, sub) in // switch subcommand { + // case "name": + // name = .name("toto") + // case "osVersion": + // osVersion = .format(Configuration()) + // case "version": + // command = .version + // default: + // command = .help + // } + } + for parser in [name, osVersion] { + binder.bind( + option: parser.add( + option: "--name", + shortName: "-n", + kind: String.self, + usage: "Parser Usage..... blablabla" + ) + ) { (cmd, pourris) in print(cmd) + print(pourris) + } + } + return (main, binder) +}() diff --git a/Sources/cli/run.swift b/Sources/cli/run.swift new file mode 100644 index 0000000..f6fdb24 --- /dev/null +++ b/Sources/cli/run.swift @@ -0,0 +1,46 @@ +import Foundation +import PlatformLookup + +private func handle(_ error: Command.CommandError) -> Int32 { + switch error { + case .toto: fputs("Config file doesn't exist at.", stderr) + } + return EXIT_FAILURE +} + +func execute(_ command: Command) throws -> Int32 { + if (command.help != nil) { + fputs("USAGE", stdout) + exit(EXIT_SUCCESS) + } + + if (command.version != nil) { + fputs("\(version)", stdout) + exit(EXIT_SUCCESS) + } + + guard case .name(let name) = command else { + fputs("Config file doesn't exist at.", stderr) + exit(EXIT_FAILURE) + } + let platforms = try PlatformLookup.findAllDeviceNamed( + name, + version: command.osVersion?.version + ) + if (command.osVersion?.lastOS != nil), let platform = platforms.last { + let deviceFamily = try PlatformLookup.deviceFamilyFrom(name) + let output = try PlatformLookup.format(platform, deviceFamily: deviceFamily) + fputs(output, stdout) + exit(EXIT_SUCCESS) + } + fputs("🤔", stderr) + exit(EXIT_FAILURE) +} + +func run(arguments: [String]) -> Int32 { + do { return try execute(Command(arguments: arguments)) } + catch let error { + fputs("\(error)", stderr) + return EXIT_FAILURE + } +} diff --git a/Tests/PlatformLookupTests/PlatformLookupTests.swift b/Tests/PlatformLookupTests/PlatformLookupTests.swift index e9bc3e4..4989ef1 100644 --- a/Tests/PlatformLookupTests/PlatformLookupTests.swift +++ b/Tests/PlatformLookupTests/PlatformLookupTests.swift @@ -4,10 +4,15 @@ import XCTest @testable import PlatformLookup final class PlatformLookupTests: XCTestCase { - override func setUp() { try? PlatformLookup.instanciate(SimulatorControlJSONData) } + override func setUp() { + try? PlatformLookup.instanciate(SimulatorControlJSONData) + } func test_decodeXcrunSimctlJSONData() { do { - let simctl = try JSONDecoder().decode(SimulatorControl.self, from: SimulatorControlJSONData) + let simctl = try JSONDecoder().decode( + SimulatorControl.self, + from: SimulatorControlJSONData + ) let runtimesSorted = simctl.runtimes?.sorted(by: >) if let firstRuntime = runtimesSorted?.first { let testRuntime = Runtime( @@ -37,14 +42,18 @@ final class PlatformLookupTests: XCTestCase { let finalDevice = iPhones!.first! XCTAssertEqual(finalDevice, testDevice) } - } catch { print(error) } + } + catch { XCTFail(error.localizedDescription) } } func test_findADeviceForLastOSVersion_string() { - let platform: String? = try? PlatformLookup.findADeviceForLastOSVersion() - XCTAssertEqual(platform, "iOS Simulator,name=iPhone 11 Pro Max,OS=13.2") + let platform = try? PlatformLookup.findADeviceForLastOSVersion(.iPhone) + let output = try? PlatformLookup.format(platform!, deviceFamily: .iPhone) + XCTAssertEqual(output, "iOS Simulator,name=iPhone 11 Pro Max,OS=13.2") } func test_findADeviceForLastOSVersion_platform() { - let platform: Platform? = try? PlatformLookup.findADeviceForLastOSVersion() + let platform: Platform? = try? PlatformLookup.findADeviceForLastOSVersion( + .iPhone + ) let device = Device( state: "Shutdown", isAvailable: true, @@ -82,7 +91,6 @@ final class PlatformLookupTests: XCTestCase { buildversion: "17B84" ) let p1 = Platform(runtime: runtime, device: device) - device = Device( state: "Shutdown", isAvailable: true, @@ -117,7 +125,6 @@ final class PlatformLookupTests: XCTestCase { buildversion: "17K81" ) let p3 = Platform(runtime: runtime, device: device) - device = Device( state: "Shutdown", isAvailable: true, @@ -135,17 +142,108 @@ final class PlatformLookupTests: XCTestCase { buildversion: "17S83" ) let p4 = Platform(runtime: runtime, device: device) - - for (expectedPlatform, aCase) in zip([p1, p2, p3, p4], PlatformLookup.DeviceFamily.allCases) { - let platform: Platform? = try? PlatformLookup.findADeviceForLastOSVersion(aCase) + for (expectedPlatform, aCase) in zip( + [p1, p2, p3, p4], + PlatformLookup.DeviceFamily.allCases + ) { + let platform: Platform? = try? PlatformLookup.findADeviceForLastOSVersion( + aCase + ) XCTAssertEqual(platform?.runtime, expectedPlatform.runtime) XCTAssertEqual(platform?.devices.last, expectedPlatform.devices.last) } } + func test_findAllDeviceNamed() { + do { + var platforms = try PlatformLookup.findAllDeviceNamed( + "iPad Pro (9.7-inch)" + ) + XCTAssertGreaterThan(platforms.count, 0) + platforms = try PlatformLookup.findAllDeviceNamed( + "iPad Pro (9.7-inch)", + version: "13.2" + ) + XCTAssertEqual(platforms.count, 1) + XCTAssertEqual( + platforms.first?.devices.first?.name, + "iPad Pro (9.7-inch)" + ) + XCTAssertEqual(platforms.first?.runtime.version, "13.2") + } + catch { XCTFail(error.localizedDescription) } + } + func test_findAllDevice() { + do { + var platforms = try PlatformLookup.findAllDevice(.appleWatch) + XCTAssertGreaterThan(platforms.count, 0) + + platforms = try PlatformLookup.findAllDevice(.appleWatch, version: "6.1") + XCTAssertGreaterThan(platforms.count, 0) + XCTAssertEqual(platforms.first?.devices.count, 4) + XCTAssertEqual(platforms.first?.runtime.version, "6.1") + } + catch { XCTFail(error.localizedDescription) } + } + func test_errors() { + // PlatformLookupError.unknowDeviceFamilly + do { + let platforms = try PlatformLookup.findAllDeviceNamed("Pikachu 22") + XCTAssertEqual(platforms.count, 0) + } + catch { + XCTAssertEqual( + error.localizedDescription, + PlatformLookup.PlatformLookupError.unknowDevice("Pikachu 22") + .localizedDescription + ) + } + // PlatformLookupError.noRuntimeFound + do { + let platforms = try PlatformLookup.findAllDeviceNamed( + "iPhone", + version: "0.1" + ) + XCTAssertEqual(platforms.count, 0) + } + catch { + XCTAssertEqual( + error.localizedDescription, + PlatformLookup.PlatformLookupError.noRuntimeFound.localizedDescription + ) + } + // PlatformLookupError.failedToInitializeDataIsNotValid + do { _ = try PlatformLookup(nil) } + catch { + XCTAssertEqual( + error.localizedDescription, + PlatformLookup.PlatformLookupError.failedToInitializeDataIsNotValid + .localizedDescription + ) + } + + // not so sure about keeping this one + do { try PlatformLookup.instanciate(Data()) } + catch { + XCTAssertEqual( + error.localizedDescription, + "The data couldn’t be read because it isn’t in the correct format." + ) + } + } static var allTests = [ ("test_decodeXcrunSimctlJSONData", test_decodeXcrunSimctlJSONData), - ("test_findADeviceForLastOSVersion_string", test_findADeviceForLastOSVersion_string), - ("test_findADeviceForLastOSVersion_platform", test_findADeviceForLastOSVersion_platform), - ("test_findADeviceForLastOSVersion_allCases", test_findADeviceForLastOSVersion_allCases), + ( + "test_findADeviceForLastOSVersion_string", + test_findADeviceForLastOSVersion_string + ), + ( + "test_findADeviceForLastOSVersion_platform", + test_findADeviceForLastOSVersion_platform + ), + ( + "test_findADeviceForLastOSVersion_allCases", + test_findADeviceForLastOSVersion_allCases + ), ("test_findAllDeviceNamed", test_findAllDeviceNamed), + ("test_findAllDevice", test_findAllDevice), ] } diff --git a/Tests/ShellTests/ShellTests.swift b/Tests/ShellTests/ShellTests.swift index f92831b..fac5a8e 100644 --- a/Tests/ShellTests/ShellTests.swift +++ b/Tests/ShellTests/ShellTests.swift @@ -1,4 +1,3 @@ -import SnapshotTesting import XCTest @testable import Shell @@ -7,8 +6,10 @@ final class ShellTests: XCTestCase { func test_SimpleTest() { do { try shell("touch testFile.txt") - let lsOutput = try shell("ls testFile.txt") - assertSnapshot(matching: lsOutput, as: .description) + var lsOutput = try shell("ls testFile.txt") + // remove the \n + _ = lsOutput.removeLast() + XCTAssertEqual(lsOutput, "testFile.txt") } catch { XCTAssert(false, error.localizedDescription) } } diff --git a/Tests/ShellTests/__Snapshots__/ShellTests/test_SimpleTest.1.txt b/Tests/ShellTests/__Snapshots__/ShellTests/test_SimpleTest.1.txt deleted file mode 100644 index edc648c..0000000 --- a/Tests/ShellTests/__Snapshots__/ShellTests/test_SimpleTest.1.txt +++ /dev/null @@ -1 +0,0 @@ -testFile.txt diff --git a/bitrisePlatformLookup.swift b/bitrisePlatformLookup.swift new file mode 100755 index 0000000..52c54a5 --- /dev/null +++ b/bitrisePlatformLookup.swift @@ -0,0 +1,29 @@ +import Foundation +import PlatformLookup // mackoj/SimulatorControl +import Shell + +func findDevicePlatform(_ args: [String]) throws { + let platform = try PlatformLookup.findADeviceForLastOSVersion(.iPhone) + _ = try shell( + "envman add --key PLATFORM_LOOKUP_DEVICE_MODEL --value \"\(platform.devices.last!.name)\"" + ) + _ = try shell( + "envman add --key PLATFORM_LOOKUP_OS_VERSION --value \"\(platform.runtime.version)\"" + ) + let platformString = try PlatformLookup.format( + platform, + deviceFamily: .iPhone + ) + _ = try shell( + "envman add --key PLATFORM_LOOKUP_PLATFORM --value \"\(platformString)\"" + ) + print(platformString) + exit(EXIT_SUCCESS) +} +func test() { + do { try findDevicePlatform(CommandLine.arguments) } + catch { + print(error.localizedDescription) + exit(EXIT_FAILURE) + } +} diff --git a/findDevicePlatform.swift b/findDevicePlatform.swift deleted file mode 100755 index 8781217..0000000 --- a/findDevicePlatform.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation -import PlatformLookup // mackoj/SimulatorControl - -func findDevicePlatform(_ args: [String]) throws { - if let platform:String = try PlatformLookup.findADeviceForLastOSVersion() { - // Expecting: iOS Simulator,name=iPhone 11 Pro Max,OS=13.2 - print(platform) - } else { - print("👉 Failure 💩") - exit(EXIT_FAILURE) - } -} - -do { try findDevicePlatform(CommandLine.arguments) } catch { - print(error.localizedDescription) - exit(EXIT_FAILURE) -}