From e5b178a3212f6c0f3e88eff0bd36906e38ccf3a7 Mon Sep 17 00:00:00 2001 From: kishikawa katsumi Date: Thu, 4 Jun 2015 14:15:37 -0700 Subject: [PATCH] Fix failing to generate coverage if the tests include asyc test cases --- .../SwiftCovFramework/CoverageReporter.swift | 20 ++- Source/SwiftCovFramework/SimCtl.swift | 148 ++++++++++++++++++ Source/SwiftCovFramework/coverage.py | 1 + swiftcov.xcodeproj/project.pbxproj | 4 + 4 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 Source/SwiftCovFramework/SimCtl.swift diff --git a/Source/SwiftCovFramework/CoverageReporter.swift b/Source/SwiftCovFramework/CoverageReporter.swift index a9c73ea..b5e5872 100644 --- a/Source/SwiftCovFramework/CoverageReporter.swift +++ b/Source/SwiftCovFramework/CoverageReporter.swift @@ -61,7 +61,7 @@ public class CoverageReporter { let dyldFallbackFrameworkPath = "/Library/Frameworks:/Network/Library/Frameworks:/System/Library/Frameworks:\(xcodePath)/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks:\(xcodePath)/Library/PrivateFrameworks:\(xcodePath)/../OtherFrameworks:\(xcodePath)/../SharedFrameworks:\(xcodePath)/Library/Frameworks:\(xcodePath)/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks" let dyldFallbackLibraryPath = "\(xcodePath)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib" - let env = [ + var env = [ "SWIFTCOV_FILES": join("\n", files ?? []), "SWIFTCOV_SDK_NAME": sdkName, "SWIFTCOV_DYLD_FRAMEWORK_PATH": builtProductsDir, @@ -71,6 +71,24 @@ public class CoverageReporter { "SWIFTCOV_DYLD_ROOT_PATH": sdkroot, "SWIFTCOV_HIT_COUNT": "\(threshold)" ] + + if sdkName.hasPrefix("iphone") { + let bootedDevice = SimCtl(verbose: verbose).list() + .flatMap { SimCtl.parseOutput($0) } + .map { (_, _, devices) -> [Device] in + return devices + } + .map { $0.filter { $0.booted }.first } + + switch bootedDevice { + case let .Success(bootedDevice): + if let bootedDevice = bootedDevice.value { + env["SWIFTCOV_XPC_SIMULATOR_LAUNCHD_NAME"] = "com.apple.CoreSimulator.SimDevice.\(bootedDevice.UDID).launchd_sim" + } + case .Failure: + break + } + } return Shell(commandPath: "/usr/bin/python", arguments: [scriptPath, targetPath, srcroot, outputDir], environment: env, verbose: verbose).run() } diff --git a/Source/SwiftCovFramework/SimCtl.swift b/Source/SwiftCovFramework/SimCtl.swift new file mode 100644 index 0000000..ba655dd --- /dev/null +++ b/Source/SwiftCovFramework/SimCtl.swift @@ -0,0 +1,148 @@ +// +// SimCtl.swift +// swiftcov +// +// Created by Kishikawa Katsumi on 2015/06/04. +// Copyright (c) 2015 Realm. All rights reserved. +// + +import Foundation +import Result + +public struct SimCtl { + private let verbose: Bool + + public init(verbose: Bool = false) { + self.verbose = verbose + } + + public func list() -> Result { + let command = Shell(commandPath: "/usr/bin/xcrun", arguments: ["simctl", "list"], verbose: verbose) + return command.output() + } + + public static func parseOutput(output: String) -> Result<([DeviceType], [Runtime], [Device]), TerminationStatus> { + let whitespaceAndNewlineCharacterSet = NSCharacterSet.whitespaceAndNewlineCharacterSet() + let lines: [String] = { + var lines = [String]() + output.enumerateLines { line, _ in + lines.append(line.stringByTrimmingCharactersInSet(whitespaceAndNewlineCharacterSet)) + } + return lines.filter { !$0.isEmpty } + }() + + enum Mode { + case DeviceType + case Runtime + case Device + } + + var deviceTypes = [DeviceType]() + var runtimes = [Runtime]() + var devices = [Device]() + + var mode = Mode.DeviceType + + var target: String? + var settings = [String: String]() + for line in lines { + switch line { + case "== Device Types ==": + mode = Mode.DeviceType + continue + case "== Runtimes ==": + mode = Mode.Runtime + continue + case "== Devices ==": + mode = Mode.Device + continue + default: + break + } + + switch mode { + case .DeviceType: + if let deviceType = DeviceType.parseLine(line) { + deviceTypes.append(deviceType) + } + case .Runtime: + if let runtime = Runtime.parseLine(line) { + runtimes.append(runtime) + } + case .Device: + if let device = Device.parseLine(line) { + devices.append(device) + } + } + } + + return Result(value: (deviceTypes, runtimes, devices)) + } +} + +public struct DeviceType { + public let name: String + public let identifier: String + + internal static func parseLine(line: String) -> DeviceType? { + let regex = NSRegularExpression(pattern: "^(.+) \\((.+)\\)$", options: nil, error: nil)! + let matches = regex.matchesInString(line, options: nil, range: NSRange(location: 0, length: (line as NSString).length)) + if matches.count == 1 { + for match in matches { + if let match = match as? NSTextCheckingResult { + let name = (line as NSString).substringWithRange(match.rangeAtIndex(1)) + let identifier = (line as NSString).substringWithRange(match.rangeAtIndex(2)) + + return DeviceType(name: name, identifier: identifier) + } + } + } + return nil + } +} + +public struct Runtime { + public let name: String + public let build: String + public let identifier: String + + internal static func parseLine(line: String) -> Runtime? { + let regex = NSRegularExpression(pattern: "^(.+) \\((.+)\\) \\((.+)\\)$", options: nil, error: nil)! + let matches = regex.matchesInString(line, options: nil, range: NSRange(location: 0, length: (line as NSString).length)) + if matches.count == 1 { + for match in matches { + if let match = match as? NSTextCheckingResult { + let name = (line as NSString).substringWithRange(match.rangeAtIndex(1)) + let build = (line as NSString).substringWithRange(match.rangeAtIndex(2)) + let identifier = (line as NSString).substringWithRange(match.rangeAtIndex(3)) + + return Runtime(name: name, build: build, identifier: identifier) + } + } + } + return nil + } +} + +public struct Device { + public let iOSVersion: String + public let UDID: String + public let booted: Bool + + internal static func parseLine(line: String) -> Device? { + let regex = NSRegularExpression(pattern: "^(.+) \\((.+)\\) \\((.+)\\)$", options: nil, error: nil)! + let matches = regex.matchesInString(line, options: nil, range: NSRange(location: 0, length: (line as NSString).length)) + if matches.count == 1 { + for match in matches { + if let match = match as? NSTextCheckingResult { + let iOSVersion = (line as NSString).substringWithRange(match.rangeAtIndex(1)) + let UDID = (line as NSString).substringWithRange(match.rangeAtIndex(2)) + let state = (line as NSString).substringWithRange(match.rangeAtIndex(3)) + + return Device(iOSVersion: iOSVersion, UDID: UDID, booted: state == "Booted") + } + } + } + return nil + } +} diff --git a/Source/SwiftCovFramework/coverage.py b/Source/SwiftCovFramework/coverage.py index 1503f47..24d5dca 100755 --- a/Source/SwiftCovFramework/coverage.py +++ b/Source/SwiftCovFramework/coverage.py @@ -219,6 +219,7 @@ def main(): 'DYLD_FALLBACK_FRAMEWORK_PATH=%s' % os.getenv('SWIFTCOV_DYLD_FALLBACK_FRAMEWORK_PATH'), 'DYLD_FALLBACK_LIBRARY_PATH=%s' % os.getenv('SWIFTCOV_DYLD_FALLBACK_LIBRARY_PATH'), 'DYLD_ROOT_PATH=%s' % os.getenv('SWIFTCOV_DYLD_ROOT_PATH'), + 'XPC_SIMULATOR_LAUNCHD_NAME=%s' % os.getenv('SWIFTCOV_XPC_SIMULATOR_LAUNCHD_NAME'), 'LC_ALL=en_US.UTF-8'] else: environment = ['DYLD_FRAMEWORK_PATH=%s' % os.getenv('SWIFTCOV_DYLD_FRAMEWORK_PATH'), diff --git a/swiftcov.xcodeproj/project.pbxproj b/swiftcov.xcodeproj/project.pbxproj index adef510..9693daf 100644 --- a/swiftcov.xcodeproj/project.pbxproj +++ b/swiftcov.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 1401EBDB1B11657C000D8737 /* coverage.py in Resources */ = {isa = PBXBuildFile; fileRef = 1401EBD61B116504000D8737 /* coverage.py */; }; 1401EBE81B14AFE2000D8737 /* Xcodebuild.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1401EBE71B14AFE2000D8737 /* Xcodebuild.swift */; }; 1401EBEA1B14B009000D8737 /* BuildSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1401EBE91B14B009000D8737 /* BuildSettings.swift */; }; + 1499139B1B20C62B0079325B /* SimCtl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1499139A1B20C62B0079325B /* SimCtl.swift */; }; 14DE82611B1B0AA900E1BAA4 /* ShellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14DE82601B1B0AA900E1BAA4 /* ShellTests.swift */; }; 14DE82631B1B210700E1BAA4 /* XcodebuildTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14DE82621B1B210700E1BAA4 /* XcodebuildTests.swift */; }; 14DE82671B1B4B3D00E1BAA4 /* CoverageReporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14DE82661B1B4B3D00E1BAA4 /* CoverageReporterTests.swift */; }; @@ -80,6 +81,7 @@ 1401EBD61B116504000D8737 /* coverage.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = coverage.py; sourceTree = ""; }; 1401EBE71B14AFE2000D8737 /* Xcodebuild.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Xcodebuild.swift; sourceTree = ""; }; 1401EBE91B14B009000D8737 /* BuildSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BuildSettings.swift; sourceTree = ""; }; + 1499139A1B20C62B0079325B /* SimCtl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimCtl.swift; sourceTree = ""; }; 14DE82601B1B0AA900E1BAA4 /* ShellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShellTests.swift; sourceTree = ""; }; 14DE82621B1B210700E1BAA4 /* XcodebuildTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XcodebuildTests.swift; sourceTree = ""; }; 14DE82661B1B4B3D00E1BAA4 /* CoverageReporterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoverageReporterTests.swift; sourceTree = ""; }; @@ -267,6 +269,7 @@ 14DE826A1B1B4DFA00E1BAA4 /* CoverageReporter.swift */, 1401EBE71B14AFE2000D8737 /* Xcodebuild.swift */, 1401EBE91B14B009000D8737 /* BuildSettings.swift */, + 1499139A1B20C62B0079325B /* SimCtl.swift */, 1401EBD41B11612C000D8737 /* Shell.swift */, 1401EBD61B116504000D8737 /* coverage.py */, D0D1216F19E87B05005E4BAA /* Supporting Files */, @@ -470,6 +473,7 @@ 1401EBE81B14AFE2000D8737 /* Xcodebuild.swift in Sources */, 1401EBD51B11612C000D8737 /* Shell.swift in Sources */, 1401EBEA1B14B009000D8737 /* BuildSettings.swift in Sources */, + 1499139B1B20C62B0079325B /* SimCtl.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };