Improve error output

This commit is contained in:
Pedro 2018-05-12 00:25:45 +02:00
parent 85a124a339
commit abcc73bb74
24 changed files with 129 additions and 83 deletions

View File

@ -841,7 +841,7 @@
};
B94C7F962062B829009BF596 = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Manual;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 1;
@ -1342,7 +1342,7 @@
BUGSNAG_API_KEY = "$(BUGSNAG_API_KEY)";
CODE_SIGN_ENTITLEMENTS = App/xcbuddy.entitlements;
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = U6LC622NKF;
FRAMEWORK_SEARCH_PATHS = (
@ -1357,8 +1357,8 @@
);
PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.xcbuddy;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "743ba204-b728-43bd-9155-013830238edc";
PROVISIONING_PROFILE_SPECIFIER = "xcbuddy dev";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_INCLUDE_PATHS = .build/debug/;
SWIFT_VERSION = 4.0;
};
@ -1371,8 +1371,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
BUGSNAG_API_KEY = "$(BUGSNAG_API_KEY)";
CODE_SIGN_ENTITLEMENTS = App/xcbuddy.entitlements;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = U6LC622NKF;
FRAMEWORK_SEARCH_PATHS = (
@ -1387,8 +1387,8 @@
);
PRODUCT_BUNDLE_IDENTIFIER = es.ppinera.xcbuddy;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "1c8827f5-51d9-4f3e-867c-00b9b3a7b642";
PROVISIONING_PROFILE_SPECIFIER = "xcbuddy dist";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_INCLUDE_PATHS = .build/debug/;
SWIFT_VERSION = 4.0;
};

View File

@ -47,8 +47,8 @@
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "generate -p /Users/pedropinera/Desktop/Test"
isEnabled = "NO">
argument = "generate -p /Users/pepibumur/Desktop/Test"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<AdditionalOptions>

View File

@ -5,9 +5,9 @@ import Utility
/// Dump command error.
///
/// - manifestNotFound: thrown when the manifest cannot be found at the given path.
enum DumpCommandError: Error, CustomStringConvertible, Equatable {
enum DumpCommandError: Error, ErrorStringConvertible, Equatable {
case manifestNotFound(AbsolutePath)
var description: String {
var errorDescription: String {
switch self {
case let .manifestNotFound(path):
return "Couldn't find Project.swift, Workspace.swift, or Config.swift in the directory \(path.asString)"

View File

@ -2,12 +2,12 @@ import Basic
import Foundation
import Utility
enum GenerateCommandError: Error, CustomStringConvertible, Equatable {
enum GenerateCommandError: Error, ErrorStringConvertible, Equatable {
static func == (_: GenerateCommandError, _: GenerateCommandError) -> Bool {
return true
}
var description: String {
var errorDescription: String {
return ""
}
}

View File

@ -5,10 +5,10 @@ import Utility
/// Init command error
///
/// - alreadyExists: when a file already exists.
enum InitCommandError: Error, CustomStringConvertible {
enum InitCommandError: Error, ErrorStringConvertible {
case alreadyExists(AbsolutePath)
case ungettableProjectName(AbsolutePath)
var description: String {
var errorDescription: String {
switch self {
case let .alreadyExists(path):
return "\(path.asString) already exists"

View File

@ -71,7 +71,11 @@ final class ErrorHandler: ErrorHandling {
do {
try closure()
} catch {
fatal(error: .abort(error as Error & CustomStringConvertible))
if let error = error as? (Error & ErrorStringConvertible) {
fatal(error: .abort(error))
} else {
fatal(error: .abortSilent(error as Error))
}
}
}
@ -80,8 +84,8 @@ final class ErrorHandler: ErrorHandling {
///
/// - Parameter error: error.
func fatal(error: FatalError) {
if let description = error.description {
printer.print(errorMessage: description)
if !error.errorDescription.isEmpty {
printer.print(errorMessage: error.errorDescription)
} else {
let message = """
An unexpected error happened. We've opened an issue to fix it as soon as possible.

View File

@ -1,26 +1,32 @@
import Foundation
/// Protocol that should be conformed by errors that can provide a printable description.
protocol ErrorStringConvertible {
/// Error description.
var errorDescription: String { get }
}
/// Fatal errors that can be thrown at any point of the execution.
///
/// - abort: used when something unexpected happens and the user should be alerted.
/// - bug: like abort, but it also reports the event to Sentry.
/// - abortSilent: like abort, but it doesn't print anything in the console.
/// - bugSilent: like bug, but it doesn't print anything in the console.
enum FatalError: Error {
case abort(Error & CustomStringConvertible)
case bug(Error & CustomStringConvertible)
enum FatalError: Error, ErrorStringConvertible {
case abort(Error & ErrorStringConvertible)
case bug(Error & ErrorStringConvertible)
case abortSilent(Error)
case bugSilent(Error)
/// Returns the error description
var description: String? {
var errorDescription: String {
switch self {
case let .abort(error):
return error.description
return error.errorDescription
case let .bug(error):
return error.description
return error.errorDescription
default:
return nil
return ""
}
}

View File

@ -1,7 +1,7 @@
import Foundation
enum GeneratorError: Error, CustomStringConvertible {
var description: String {
enum GeneratorError: Error, ErrorStringConvertible {
var errorDescription: String {
return ""
}
}

View File

@ -17,19 +17,47 @@ final class ProjectGenerator: ProjectGenerating {
context.printer.print("Generating project \(project.name)")
let workspaceData = XCWorkspaceData(children: [])
let workspace = XCWorkspace(data: workspaceData)
let pbxProj = PBXProj(rootObject: nil)
// let configurationList = XCConfigurationList(buildConfigurations: [])
// let configurationListReference = pbxProj.objects.generateReference(configurationList, project.name)
// pbxProj.objects.addObject(configurationList, reference: configurationListReference)
// let mainGroup = PBXGroup(children: [])
// let mainGroupReference = pbxProj.objects.generateReference(mainGroup, project.name)
// let pbxProject = PBXProject(name: project.name,
// buildConfigurationList: configurationListReference,
// compatibilityVersion: "Xcode 8.0",
// mainGroup: mainGroupReference)
// let projectReference = pbxProj.objects.generateReference(pbxProject, project.name)
// pbxProj.rootObject = projectReference
let xcodeproj = XcodeProj(workspace: workspace, pbxproj: pbxProj)
let pbxproj = PBXProj(objectVersion: Xcode.Default.objectVersion,
archiveVersion: Xcode.LastKnown.archiveVersion,
classes: [:])
/// Configurations.
let debugConfiguration = XCBuildConfiguration(name: "Debug")
let debugConfigurationReference = pbxproj.objects.addObject(debugConfiguration)
let releaseConfiguration = XCBuildConfiguration(name: "Release")
let releaseConfigurationReference = pbxproj.objects.addObject(releaseConfiguration)
let configurationList = XCConfigurationList(buildConfigurations: [])
let configurationListReference = pbxproj.objects.addObject(configurationList)
configurationList.buildConfigurations.append(debugConfigurationReference)
configurationList.buildConfigurations.append(releaseConfigurationReference)
/// Project groups.
let mainGroup = PBXGroup(children: [], sourceTree: .group)
let mainGroupReference = pbxproj.objects.addObject(mainGroup)
let productsGroup = PBXGroup(children: [], sourceTree: .buildProductsDir, name: "Products")
let productsGroupReference = pbxproj.objects.addObject(productsGroup)
mainGroup.children.append(productsGroupReference)
/// Generate project object.
let pbxProject = PBXProject(name: project.name,
buildConfigurationList: configurationListReference,
compatibilityVersion: Xcode.Default.compatibilityVersion,
mainGroup: mainGroupReference,
developmentRegion: Xcode.Default.developmentRegion,
hasScannedForEncodings: 0,
knownRegions: ["en"],
productRefGroup: productsGroupReference,
projectDirPath: "",
projectReferences: [],
projectRoots: [],
targets: [],
attributes: [:])
let projectReference = pbxproj.objects.addObject(pbxProject)
pbxproj.rootObject = projectReference
/// Write.
let xcodeproj = XcodeProj(workspace: workspace, pbxproj: pbxproj)
let xcodeprojPath = project.path.appending(component: "\(project.name).xcodeproj")
try xcodeproj.write(path: xcodeprojPath)
return xcodeprojPath

View File

@ -7,7 +7,7 @@ import Foundation
/// - targetNotFound: error thrown when a target has a dependency with another target that doesn't exist.
/// - manifestNotFound: error thrown when a manifest cannot be found.
/// - unexpected: unexpected error.
enum GraphLoadingError: Error, Equatable, CustomStringConvertible {
enum GraphLoadingError: Error, Equatable, ErrorStringConvertible {
case missingFile(AbsolutePath)
case targetNotFound(String, AbsolutePath)
case manifestNotFound(AbsolutePath)
@ -37,7 +37,7 @@ enum GraphLoadingError: Error, Equatable, CustomStringConvertible {
}
}
var description: String {
var errorDescription: String {
switch self {
case let .manifestNotFound(path):
return "Couldn't find manifest at path: '\(path.asString)'"

View File

@ -14,13 +14,13 @@ protocol GraphManifestLoading {
/// - frameworksFolderNotFound: error thrown when the frameworks fodler that contains the ProjectDescription.framework cannot be found.
/// - swiftNotFound: error thrown when Swift is not found in the system.
/// - unexpectedOutput: error throw when we get an unexpected output trying to compile the manifest.
enum GraphManifestLoaderError: Error, CustomStringConvertible, Equatable {
enum GraphManifestLoaderError: Error, ErrorStringConvertible, Equatable {
case projectDescriptionNotFound(AbsolutePath)
case frameworksFolderNotFound
case swiftNotFound
case unexpectedOutput(AbsolutePath)
var description: String {
var errorDescription: String {
switch self {
case let .projectDescriptionNotFound(path):
return "Couldn't find ProjectDescription.framework at path \(path.asString)."

View File

@ -96,11 +96,11 @@ class TargetNode: GraphNode {
/// Precompiled node errors.
///
/// - architecturesNotFound: thrown when the architectures cannot be found.
enum PrecompiledNodeError: Error, CustomStringConvertible, Equatable {
enum PrecompiledNodeError: Error, ErrorStringConvertible, Equatable {
case architecturesNotFound(AbsolutePath)
/// Error description.
var description: String {
var errorDescription: String {
switch self {
case let .architecturesNotFound(path):
return "Couldn't find architectures for binary at path \(path.asString)"

View File

@ -27,9 +27,9 @@ protocol ResourceLocating: AnyObject {
/// Resource locating error.
///
/// - notFound: thrown then the resource cannot be found.
enum ResourceLocatingError: Error, CustomStringConvertible, Equatable {
enum ResourceLocatingError: Error, ErrorStringConvertible, Equatable {
case notFound(String)
var description: String {
var errorDescription: String {
switch self {
case let .notFound(name):
return "Couldn't find \(name)"

View File

@ -3,8 +3,8 @@ import Foundation
import Utility
/// Shell error.
struct ShellError: Error, CustomStringConvertible, Equatable {
let description: String
struct ShellError: Error, ErrorStringConvertible, Equatable {
let errorDescription: String
}
/// Protocol that represents a shell interface.
@ -29,7 +29,7 @@ class Shell: Shelling {
if result.exitStatus == .terminated(code: 0) {
return try result.utf8Output()
} else {
throw ShellError(description: try result.utf8stderrOutput())
throw ShellError(errorDescription: try result.utf8stderrOutput())
}
}
}

View File

@ -1,9 +1,9 @@
import Basic
import Foundation
enum ProjectValidationError: Error, CustomStringConvertible, Equatable {
enum ProjectValidationError: Error, ErrorStringConvertible, Equatable {
case duplicatedTargets([String], AbsolutePath)
var description: String {
var errorDescription: String {
switch self {
case let .duplicatedTargets(targets, projectPath):
return "Targets \(targets.joined(separator: ", ")) from project at \(projectPath.asString) have duplicates."

View File

@ -23,7 +23,7 @@ final class DumpCommandTests: XCTestCase {
func test_dumpCommandError_returns_the_right_description_when_manifestNotFound() {
let error = DumpCommandError.manifestNotFound(AbsolutePath("/test"))
XCTAssertEqual(error.description, "Couldn't find Project.swift, Workspace.swift, or Config.swift in the directory /test")
XCTAssertEqual(error.errorDescription, "Couldn't find Project.swift, Workspace.swift, or Config.swift in the directory /test")
}
func test_name() {

View File

@ -20,12 +20,12 @@ final class InitCommandTests: XCTestCase {
func test_initCommandError_has_the_right_description_when_alreadyExists() {
let error = InitCommandError.alreadyExists(AbsolutePath("/path"))
XCTAssertEqual(error.description, "/path already exists")
XCTAssertEqual(error.errorDescription, "/path already exists")
}
func test_initCommandError_has_the_right_description_when_ungettableProjectName() {
let error = InitCommandError.ungettableProjectName(AbsolutePath("/path"))
XCTAssertEqual(error.description, "Couldn't infer the project name from path /path")
XCTAssertEqual(error.errorDescription, "Couldn't infer the project name from path /path")
}
func test_init_registersTheSubparser() {

View File

@ -6,27 +6,27 @@ import XCTest
final class GraphLoadingErrorTests: XCTestCase {
func test_description_returns_the_right_value_when_manifestNotFound() {
let path = AbsolutePath("/test/Project.swift")
XCTAssertEqual(GraphLoadingError.manifestNotFound(path).description, "Couldn't find manifest at path: '/test/Project.swift'")
XCTAssertEqual(GraphLoadingError.manifestNotFound(path).errorDescription, "Couldn't find manifest at path: '/test/Project.swift'")
}
func test_description_returns_the_right_value_when_targetNotFound() {
let path = AbsolutePath("/test/Project.swift")
XCTAssertEqual(GraphLoadingError.targetNotFound("Target", path).description, "Couldn't find target 'Target' at '/test/Project.swift'")
XCTAssertEqual(GraphLoadingError.targetNotFound("Target", path).errorDescription, "Couldn't find target 'Target' at '/test/Project.swift'")
}
func test_description_returns_the_right_value_when_missingFile() {
let path = AbsolutePath("/path/file.swift")
XCTAssertEqual(GraphLoadingError.missingFile(path).description, "Couldn't find file at path '/path/file.swift'")
XCTAssertEqual(GraphLoadingError.missingFile(path).errorDescription, "Couldn't find file at path '/path/file.swift'")
}
func test_description_returns_the_right_value_when_unexpected() {
XCTAssertEqual(GraphLoadingError.unexpected("message").description, "message")
XCTAssertEqual(GraphLoadingError.unexpected("message").errorDescription, "message")
}
func test_description_returns_the_right_value_when_circularDependency() {
let from = GraphCircularDetectorNode(path: AbsolutePath("/from"), name: "FromTarget")
let to = GraphCircularDetectorNode(path: AbsolutePath("/to"), name: "ToTarget")
let error = GraphLoadingError.circularDependency(from, to)
XCTAssertEqual(error.description, "Found circular dependency between the target 'FromTarget' at '/from' and the target 'ToTarget' at '/to'")
XCTAssertEqual(error.errorDescription, "Found circular dependency between the target 'FromTarget' at '/from' and the target 'ToTarget' at '/to'")
}
}

View File

@ -3,28 +3,28 @@ import Foundation
@testable import xcbuddykit
import XCTest
fileprivate struct TestError: Error, CustomStringConvertible {
let description: String
fileprivate struct TestError: Error, ErrorStringConvertible {
let errorDescription: String
}
final class GraphManifestLoaderErrorTests: XCTestCase {
func test_description_when_projectDescriptionNotFound() {
let error = GraphManifestLoaderError.projectDescriptionNotFound(AbsolutePath("/test"))
XCTAssertEqual(error.description, "Couldn't find ProjectDescription.framework at path /test.")
XCTAssertEqual(error.errorDescription, "Couldn't find ProjectDescription.framework at path /test.")
}
func test_description_when_frameworksFolderNotFound() {
let error = GraphManifestLoaderError.frameworksFolderNotFound
XCTAssertEqual(error.description, "Couldn't find the Frameworks folder in the bundle that contains the ProjectDescription.framework.")
XCTAssertEqual(error.errorDescription, "Couldn't find the Frameworks folder in the bundle that contains the ProjectDescription.framework.")
}
func test_description_when_swiftNotFound() {
let error = GraphManifestLoaderError.swiftNotFound
XCTAssertEqual(error.description, "Couldn't find Swift on your environment. Run 'xcode-select -p' to see if the Xcode path is properly setup.")
XCTAssertEqual(error.errorDescription, "Couldn't find Swift on your environment. Run 'xcode-select -p' to see if the Xcode path is properly setup.")
}
func test_description_when_unexpectedOutput() {
let error = GraphManifestLoaderError.unexpectedOutput(AbsolutePath("/test/"))
XCTAssertEqual(error.description, "Unexpected output trying to parse the manifest at path /test.")
XCTAssertEqual(error.errorDescription, "Unexpected output trying to parse the manifest at path /test.")
}
}

View File

@ -3,6 +3,10 @@ import Sentry
@testable import xcbuddykit
import XCTest
fileprivate struct TestError: Error, ErrorStringConvertible {
var errorDescription: String { return "Error" }
}
fileprivate final class MockSentryClient: SentryClienting {
var startCrashHandlerCount: UInt = 0
var startCrashHandlerStub: Error?
@ -41,19 +45,19 @@ final class ErrorHandlerTests: XCTestCase {
}
func test_fatalError_printsTheDescription_whenPrintableError() {
let error = NSError(domain: "domain", code: 20, userInfo: nil)
let error = TestError()
subject.fatal(error: .abort(error))
XCTAssertEqual(printer.printErrorMessageArgs.first, error.description)
XCTAssertEqual(printer.printErrorMessageArgs.first, error.errorDescription)
}
func test_fatalError_exitsWith1() {
let error = NSError(domain: "domain", code: 20, userInfo: nil)
let error = TestError()
subject.fatal(error: .abort(error))
XCTAssertEqual(exited, 1)
}
func test_fatalError_reports_whenBug() {
let error = NSError(domain: "domain", code: 20, userInfo: nil)
let error = TestError()
var sentEvent: Event?
client.sendEventStub = { event, completion in
sentEvent = event

View File

@ -2,20 +2,24 @@ import Foundation
@testable import xcbuddykit
import XCTest
fileprivate struct TestError: Error, ErrorStringConvertible {
var errorDescription: String { return "" }
}
final class FatalErrorTests: XCTestCase {
func test_description() {
let error = NSError(domain: "test", code: 1, userInfo: nil)
XCTAssertEqual(FatalError.abort(error).description, error.description)
XCTAssertEqual(FatalError.bug(error).description, error.description)
XCTAssertNil(FatalError.abortSilent(error).description)
XCTAssertNil(FatalError.bugSilent(error).description)
let error = TestError()
XCTAssertEqual(FatalError.abort(error).errorDescription, error.errorDescription)
XCTAssertEqual(FatalError.bug(error).errorDescription, error.errorDescription)
XCTAssertTrue(FatalError.abortSilent(error).errorDescription.isEmpty)
XCTAssertTrue(FatalError.bugSilent(error).errorDescription.isEmpty)
}
func test_bug() {
let error = NSError(domain: "test", code: 1, userInfo: nil)
let error = TestError()
XCTAssertNil(FatalError.abort(error).bug)
XCTAssertNil(FatalError.abortSilent(error).bug)
XCTAssertEqual(FatalError.bug(error).bug as NSError?, error)
XCTAssertEqual(FatalError.bugSilent(error).bug as NSError?, error)
XCTAssertEqual(FatalError.bug(error).bug as NSError?, error as NSError)
XCTAssertEqual(FatalError.bugSilent(error).bug as NSError?, error as NSError)
}
}

View File

@ -6,7 +6,7 @@ import XCTest
final class ProjectValidationErrorTests: XCTestCase {
func test_description_whenDuplicatedTargets() {
let error = ProjectValidationError.duplicatedTargets(["A", "B"], AbsolutePath("/test"))
XCTAssertEqual(error.description, "Targets A, B from project at /test have duplicates.")
XCTAssertEqual(error.errorDescription, "Targets A, B from project at /test have duplicates.")
}
}

View File

@ -24,7 +24,7 @@
"repositoryURL": "git@github.com:xcbuddy/xcodeproj.git",
"state": {
"branch": null,
"revision": "b32d1da90732bea8a0be8ffb205a0bcdbb406a68",
"revision": "3d981ee376466cd19565a1d7a416597886cd2e13",
"version": null
}
}

View File

@ -5,7 +5,7 @@ import PackageDescription
let package = Package(
name: "xcbuddy",
dependencies: [
.package(url: "git@github.com:xcbuddy/xcodeproj.git", .revision("b32d1da90732bea8a0be8ffb205a0bcdbb406a68")),
.package(url: "git@github.com:xcbuddy/xcodeproj.git", .revision("3d981ee376466cd19565a1d7a416597886cd2e13")),
.package(url: "git@github.com:apple/swift-package-manager.git", .revision("26ab3997142ef464090c63f963d1dafef79a7c06")),
],
targets: [