From 26849e86c2bb8f873e3e68936751c8ed91c06e5b Mon Sep 17 00:00:00 2001 From: Andrea Bizzotto Date: Mon, 3 Oct 2016 18:50:15 +0100 Subject: [PATCH] Improved error handling. Add unit tests --- AcceptanceMark.xcodeproj/project.pbxproj | 112 +++++++++++++++++- .../xcschemes/AcceptanceMarkDemo.xcscheme | 0 .../AcceptanceMarkDemoTests.xcscheme | 0 .../xcschemes/amtool-tests.xcscheme} | 52 ++++---- .../xcschemes/xcschememanagement.plist | 17 ++- Sources/MarkdownTableParser.swift | 94 ++++++++++++--- Tests/Info.plist | 22 ++++ Tests/MarkdownTableParser+Equatable.swift | 77 ++++++++++++ Tests/MarkdownTableParserTests.swift | 102 ++++++++++++++++ 9 files changed, 426 insertions(+), 50 deletions(-) rename AcceptanceMark.xcodeproj/{xcuserdata/andrea.xcuserdatad => xcshareddata}/xcschemes/AcceptanceMarkDemo.xcscheme (100%) rename AcceptanceMark.xcodeproj/{xcuserdata/andrea.xcuserdatad => xcshareddata}/xcschemes/AcceptanceMarkDemoTests.xcscheme (100%) rename AcceptanceMark.xcodeproj/{xcuserdata/andrea.xcuserdatad/xcschemes/AcceptanceMark.xcscheme => xcshareddata/xcschemes/amtool-tests.xcscheme} (69%) create mode 100644 Tests/Info.plist create mode 100644 Tests/MarkdownTableParser+Equatable.swift create mode 100644 Tests/MarkdownTableParserTests.swift diff --git a/AcceptanceMark.xcodeproj/project.pbxproj b/AcceptanceMark.xcodeproj/project.pbxproj index fd5fae2..bc1ff37 100644 --- a/AcceptanceMark.xcodeproj/project.pbxproj +++ b/AcceptanceMark.xcodeproj/project.pbxproj @@ -25,6 +25,11 @@ C3ACE5731D951DB500971FD6 /* String+Trimming.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ACE56C1D951DB500971FD6 /* String+Trimming.swift */; }; C3ACE5741D951DB500971FD6 /* TestGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ACE56D1D951DB500971FD6 /* TestGenerator.swift */; }; C3ACE5751D951DB500971FD6 /* TestSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ACE56E1D951DB500971FD6 /* TestSpec.swift */; }; + C3F0353E1DA29FD000431364 /* MarkdownTableParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0353D1DA29FD000431364 /* MarkdownTableParserTests.swift */; }; + C3F035401DA2A3A100431364 /* MarkdownTableParser+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0353F1DA2A3A100431364 /* MarkdownTableParser+Equatable.swift */; }; + C3F035431DA2A88B00431364 /* MarkdownTableParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ACE56B1D951DB500971FD6 /* MarkdownTableParser.swift */; }; + C3F035441DA2A8ED00431364 /* TestSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ACE56E1D951DB500971FD6 /* TestSpec.swift */; }; + C3F035451DA2A90F00431364 /* String+Trimming.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ACE56C1D951DB500971FD6 /* String+Trimming.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -78,6 +83,10 @@ C3ACE56C1D951DB500971FD6 /* String+Trimming.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Trimming.swift"; sourceTree = ""; }; C3ACE56D1D951DB500971FD6 /* TestGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestGenerator.swift; sourceTree = ""; }; C3ACE56E1D951DB500971FD6 /* TestSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSpec.swift; sourceTree = ""; }; + C3F035301DA29F3600431364 /* amtool-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "amtool-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + C3F0353A1DA29F8500431364 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C3F0353D1DA29FD000431364 /* MarkdownTableParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTableParserTests.swift; sourceTree = ""; }; + C3F0353F1DA2A3A100431364 /* MarkdownTableParser+Equatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MarkdownTableParser+Equatable.swift"; sourceTree = ""; }; D128B03E3CD2CA3711855BDB /* Pods-amtool.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-amtool.release.xcconfig"; path = "Pods/Target Support Files/Pods-amtool/Pods-amtool.release.xcconfig"; sourceTree = ""; }; EF93B81C245EAE4A66E2804E /* Pods-amtool.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-amtool.debug.xcconfig"; path = "Pods/Target Support Files/Pods-amtool/Pods-amtool.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -104,6 +113,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C3F0352D1DA29F3600431364 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -131,6 +147,7 @@ 65BCCFEE1D5A29340062E238 /* amtool-spec.md */, 65BCCFEF1D5A29340062E238 /* README.md */, C3ACE5671D951DB500971FD6 /* Sources */, + C3F035381DA29F5300431364 /* Tests */, 65C051E31D58AC330008892C /* AcceptanceMarkDemo */, 65C051D31D58ABE80008892C /* AcceptanceMarkDemoTests */, 65C051C41D58ABAD0008892C /* Products */, @@ -145,6 +162,7 @@ 65C051D21D58ABE80008892C /* AcceptanceMarkDemoTests.xctest */, 65C051E21D58AC330008892C /* AcceptanceMarkDemo.app */, 65BCCFE71D59E7E30062E238 /* amtool */, + C3F035301DA29F3600431364 /* amtool-tests.xctest */, ); name = Products; sourceTree = ""; @@ -191,6 +209,16 @@ path = Sources; sourceTree = ""; }; + C3F035381DA29F5300431364 /* Tests */ = { + isa = PBXGroup; + children = ( + C3F0353F1DA2A3A100431364 /* MarkdownTableParser+Equatable.swift */, + C3F0353D1DA29FD000431364 /* MarkdownTableParserTests.swift */, + C3F0353A1DA29F8500431364 /* Info.plist */, + ); + path = Tests; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -248,6 +276,23 @@ productReference = 65C051E21D58AC330008892C /* AcceptanceMarkDemo.app */; productType = "com.apple.product-type.application"; }; + C3F0352F1DA29F3600431364 /* amtool-tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = C3F035371DA29F3600431364 /* Build configuration list for PBXNativeTarget "amtool-tests" */; + buildPhases = ( + C3F0352C1DA29F3600431364 /* Sources */, + C3F0352D1DA29F3600431364 /* Frameworks */, + C3F0352E1DA29F3600431364 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "amtool-tests"; + productName = "amtool-tests"; + productReference = C3F035301DA29F3600431364 /* amtool-tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -274,6 +319,10 @@ DevelopmentTeam = M54ZVB688G; ProvisioningStyle = Automatic; }; + C3F0352F1DA29F3600431364 = { + CreatedOnToolsVersion = 8.0; + ProvisioningStyle = Automatic; + }; }; }; buildConfigurationList = 65C051BD1D58ABAD0008892C /* Build configuration list for PBXProject "AcceptanceMark" */; @@ -292,6 +341,7 @@ 65C051D11D58ABE80008892C /* AcceptanceMarkDemoTests */, 65C051E11D58AC330008892C /* AcceptanceMarkDemo */, 65BCCFE61D59E7E30062E238 /* amtool */, + C3F0352F1DA29F3600431364 /* amtool-tests */, ); }; /* End PBXProject section */ @@ -321,6 +371,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C3F0352E1DA29F3600431364 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -376,6 +433,18 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C3F0352C1DA29F3600431364 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C3F035441DA2A8ED00431364 /* TestSpec.swift in Sources */, + C3F0353E1DA29FD000431364 /* MarkdownTableParserTests.swift in Sources */, + C3F035451DA2A90F00431364 /* String+Trimming.swift in Sources */, + C3F035401DA2A3A100431364 /* MarkdownTableParser+Equatable.swift in Sources */, + C3F035431DA2A88B00431364 /* MarkdownTableParser.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -411,7 +480,7 @@ buildSettings = { CODE_SIGN_IDENTITY = "-"; DEVELOPMENT_TEAM = M54ZVB688G; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.11; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SWIFT_VERSION = 3.0; @@ -423,7 +492,7 @@ buildSettings = { CODE_SIGN_IDENTITY = "-"; DEVELOPMENT_TEAM = M54ZVB688G; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.11; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SWIFT_VERSION = 3.0; @@ -581,6 +650,36 @@ }; name = Release; }; + C3F035351DA29F3600431364 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_BUNDLE_IDENTIFIER = "com.musevisions.amtool-tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + C3F035361DA29F3600431364 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_BUNDLE_IDENTIFIER = "com.musevisions.amtool-tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -620,6 +719,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + C3F035371DA29F3600431364 /* Build configuration list for PBXNativeTarget "amtool-tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C3F035351DA29F3600431364 /* Debug */, + C3F035361DA29F3600431364 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 65C051BA1D58ABAD0008892C /* Project object */; diff --git a/AcceptanceMark.xcodeproj/xcuserdata/andrea.xcuserdatad/xcschemes/AcceptanceMarkDemo.xcscheme b/AcceptanceMark.xcodeproj/xcshareddata/xcschemes/AcceptanceMarkDemo.xcscheme similarity index 100% rename from AcceptanceMark.xcodeproj/xcuserdata/andrea.xcuserdatad/xcschemes/AcceptanceMarkDemo.xcscheme rename to AcceptanceMark.xcodeproj/xcshareddata/xcschemes/AcceptanceMarkDemo.xcscheme diff --git a/AcceptanceMark.xcodeproj/xcuserdata/andrea.xcuserdatad/xcschemes/AcceptanceMarkDemoTests.xcscheme b/AcceptanceMark.xcodeproj/xcshareddata/xcschemes/AcceptanceMarkDemoTests.xcscheme similarity index 100% rename from AcceptanceMark.xcodeproj/xcuserdata/andrea.xcuserdatad/xcschemes/AcceptanceMarkDemoTests.xcscheme rename to AcceptanceMark.xcodeproj/xcshareddata/xcschemes/AcceptanceMarkDemoTests.xcscheme diff --git a/AcceptanceMark.xcodeproj/xcuserdata/andrea.xcuserdatad/xcschemes/AcceptanceMark.xcscheme b/AcceptanceMark.xcodeproj/xcshareddata/xcschemes/amtool-tests.xcscheme similarity index 69% rename from AcceptanceMark.xcodeproj/xcuserdata/andrea.xcuserdatad/xcschemes/AcceptanceMark.xcscheme rename to AcceptanceMark.xcodeproj/xcshareddata/xcschemes/amtool-tests.xcscheme index 9b0ff18..8e598f4 100644 --- a/AcceptanceMark.xcodeproj/xcuserdata/andrea.xcuserdatad/xcschemes/AcceptanceMark.xcscheme +++ b/AcceptanceMark.xcodeproj/xcshareddata/xcschemes/amtool-tests.xcscheme @@ -5,22 +5,6 @@ - - - - - - + + + + + + + + @@ -42,15 +45,16 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> - + - + @@ -63,9 +67,9 @@ diff --git a/AcceptanceMark.xcodeproj/xcuserdata/andrea.xcuserdatad/xcschemes/xcschememanagement.plist b/AcceptanceMark.xcodeproj/xcuserdata/andrea.xcuserdatad/xcschemes/xcschememanagement.plist index a3fd822..2d2ff63 100644 --- a/AcceptanceMark.xcodeproj/xcuserdata/andrea.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/AcceptanceMark.xcodeproj/xcuserdata/andrea.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,20 +4,20 @@ SchemeUserState - AcceptanceMark.xcscheme + AcceptanceMarkDemo.xcscheme_^#shared#^_ orderHint - 0 + 2 - AcceptanceMarkDemo.xcscheme + AcceptanceMarkDemoTests.xcscheme_^#shared#^_ orderHint - 2 + 1 - AcceptanceMarkDemoTests.xcscheme + amtool-tests.xcscheme_^#shared#^_ orderHint - 1 + 4 amtool.xcscheme_^#shared#^_ @@ -47,6 +47,11 @@ primary + C3F0352F1DA29F3600431364 + + primary + + diff --git a/Sources/MarkdownTableParser.swift b/Sources/MarkdownTableParser.swift index 2840c8e..75a1876 100644 --- a/Sources/MarkdownTableParser.swift +++ b/Sources/MarkdownTableParser.swift @@ -9,11 +9,40 @@ import Cocoa +enum MarkdownTableParserError { + case noInputsNoOutputs(line: String) + case missingStartPipe(line: String) + case missingEndPipe(line: String) + case invalidHeader(line: String, message: String) + case contentInvalidComponentCount(line: String, message: String) + case invalidSeparator(line: String, message: String) + + var message: String { + switch self { + case .noInputsNoOutputs(let line): return errorString(line: line, message: "Table row has no inputs and no outputs") + case .missingStartPipe(let line): return errorString(line: line, message: "Table row does not start with `|` character") + case .missingEndPipe(let line): return errorString(line: line, message: "Table row does not end with `|` character") + case .invalidHeader(let line, let message): return errorString(line: line, message: message) + case .contentInvalidComponentCount(let line, let message): return errorString(line: line, message: message) + case .invalidSeparator(let line, let message): return errorString(line: line, message: message) + } + } + + private func errorString(line: String, message: String) -> String { + return "\(message):\n\(line)" + } +} + enum MarkdownTableParserState { case outside case header(inputVars: [TestSpec.Variable], outputVars: [TestSpec.Variable]) case separator case content(data: TestSpec.TestData) + case error(error: MarkdownTableParserError) +} + +enum VariablesOrError { + case variables(variables: [TestSpec.Variable]) case error(message: String) } @@ -42,8 +71,8 @@ class MarkdownTableParser: NSObject { self.inputVars = inputVars self.outputVars = outputVars } - if case .error(let message) = state { - print("\(message)") + if case .error(let error) = state { + print("\(error.message)") } case .header(_, _): @@ -79,18 +108,44 @@ class MarkdownTableParser: NSObject { private func parseTableHeader(line: String) -> MarkdownTableParserState { - let (inputs, outputs) = getInputsOutputs(line: line) - - guard let inputVars = parseTableHeaders(strings: inputs), - let outputVars = parseTableHeaders(strings: outputs) else { - return .error(message: "error parsing line:\n\(line)") + let trimmed = (line as NSString).trimmingCharacters(in: CharacterSet.whitespaces) + if let error = errorMessageForTable(line: trimmed) { + return .error(error: error) } - if inputVars.count == inputs.count && outputVars.count == outputs.count { - return .header(inputVars: inputVars, outputVars: outputVars) + + let (inputs, outputs) = getInputsOutputs(line: trimmed) + if inputs.count == 0 && outputs.count == 0 { + return .error(error: .noInputsNoOutputs(line: line)) + } + + let inputHeaders = parseTableHeaders(strings: inputs) + let outputHeaders = parseTableHeaders(strings: outputs) + switch (inputHeaders, outputHeaders) { + case (.error(let inputMessage), .error(_)): return .error(error: .invalidHeader(line: line, message: inputMessage)) + case (.error(let inputMessage), .variables(_)): return .error(error: .invalidHeader(line: line, message: inputMessage)) + case (.variables(_), .error(let outputMessage)): return .error(error: .invalidHeader(line: line, message: outputMessage)) + case (.variables(let inputVars), .variables(let outputVars)): + + if inputVars.count == inputs.count && outputVars.count == outputs.count { + return .header(inputVars: inputVars, outputVars: outputVars) + } + else { + return .error(error: .invalidHeader(line: line, message: "Invalid input / outputs")) + } } - return .error(message: "error parsing line:\n\(line)\nInput vars: \(inputVars), output vars: \(outputVars)") } + private func errorMessageForTable(line: String) -> MarkdownTableParserError? { + + guard let first = line.characters.first, first == "|" else { + return .missingStartPipe(line: line) + } + guard let last = line.characters.last, last == "|" else { + return .missingEndPipe(line: line) + } + return nil + } + private func getInputsOutputs(line: String) -> ([String], [String]) { let values = components(for: line) @@ -109,7 +164,7 @@ class MarkdownTableParser: NSObject { } - func parseTableHeaders(strings: [String]) -> [TestSpec.Variable]? { + func parseTableHeaders(strings: [String]) -> VariablesOrError { var variables: [TestSpec.Variable] = [] for string in strings { @@ -125,23 +180,26 @@ class MarkdownTableParser: NSObject { variables.append(TestSpec.Variable(name: name, type: type)) } else { - print("\(components.first!): Unrecognized variable type: \"\(components.last!)\".\nOnly Bool, Int, Float, String are supported.") - return nil + return .error(message: "\(components.first!): Unrecognized variable type: \"\(components.last!)\".\nOnly Bool, Int, Float, String are supported") } } else { - print("invalid table header format: \"\(strings)\"") - return nil + return .error(message: "invalid table header format: \"\(strings)\"") } } - return variables + return .variables(variables: variables) } func parseContent(line: String) -> MarkdownTableParserState { - let values = components(for: line) + let trimmed = (line as NSString).trimmingCharacters(in: CharacterSet.whitespaces) + if let errorMessage = errorMessageForTable(line: trimmed) { + return .error(error: errorMessage) + } + + let values = components(for: trimmed) if values.count - 1 != inputVars.count + outputVars.count { - return .error(message: "Invalid number of components in table row. Should be \(inputVars.count + outputVars.count + 1), found: \(values.count)") + return .error(error: .contentInvalidComponentCount(line: line, message: "Invalid number of components in table row. Should be \(inputVars.count + outputVars.count + 1), found: \(values.count)")) } let ioSeparatorIndex = inputVars.count diff --git a/Tests/Info.plist b/Tests/Info.plist new file mode 100644 index 0000000..6c6c23c --- /dev/null +++ b/Tests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Tests/MarkdownTableParser+Equatable.swift b/Tests/MarkdownTableParser+Equatable.swift new file mode 100644 index 0000000..d384cd3 --- /dev/null +++ b/Tests/MarkdownTableParser+Equatable.swift @@ -0,0 +1,77 @@ +// +// MarkdownTableParserStateEquatable.swift +// AcceptanceMark +// +// Created by Andrea Bizzotto on 03/10/2016. +// Copyright © 2016 musevisions. All rights reserved. +// + +@testable import amtool + + +extension MarkdownTableParserState: Equatable { + +} + +extension TestSpec.Variable: Equatable { + +} + +func == (lhs: TestSpec.Variable, rhs: TestSpec.Variable) -> Bool { + + return lhs.name == rhs.name && lhs.type == rhs.type +} + +//extension TestSpec.VariableType: Equatable { +// +//} +// +//func == (lhs: TestSpec.VariableType, rhs: TestSpec.VariableType) -> Bool { +// +// switch (lhs, rhs) { +// case (.bool, .bool): return true +// case (.int, .int): return true +// case (.float, .float): return true +// case (.string, .string): return true +// default: return false +// } +//} + + +extension TestSpec.TestData: Equatable { + +} + +func == (lhs: TestSpec.TestData, rhs: TestSpec.TestData) -> Bool { + + return lhs.inputs == rhs.inputs && lhs.outputs == rhs.outputs +} + +func == (lhs: MarkdownTableParserState, rhs: MarkdownTableParserState) -> Bool { + + switch (lhs, rhs) { + case (.outside, .outside): return true + case (let .header(lhsInputVars, lhsOutputVars), let .header(rhsInputVars, rhsOutputVars)): return lhsInputVars == rhsInputVars && lhsOutputVars == rhsOutputVars + case (.separator, .separator): return true + case (let .content(lhsData), let .content(rhsData)): return lhsData == rhsData + case (let .error(lhsMessage), let .error(rhsMessage)): return lhsMessage == rhsMessage + default: return false + } +} + +extension MarkdownTableParserError: Equatable { + +} + +func == (lhs: MarkdownTableParserError, rhs: MarkdownTableParserError) -> Bool { + switch (lhs, rhs) { + case (let .noInputsNoOutputs(leftLine), let .noInputsNoOutputs(rightLine)): return leftLine == rightLine + case (let .missingStartPipe(leftLine), let .missingStartPipe(rightLine)): return leftLine == rightLine + case (let .missingEndPipe(leftLine), let .missingEndPipe(rightLine)): return leftLine == rightLine + case (let .invalidHeader(leftLine, leftMessage), let .invalidHeader(rightLine, rightMessage)): return leftLine == rightLine && leftMessage == rightMessage + case (let .contentInvalidComponentCount(leftLine, leftMessage), let .contentInvalidComponentCount(rightLine, rightMessage)): return leftLine == rightLine && leftMessage == rightMessage + case (let .invalidSeparator(leftLine, leftMessage), let .invalidSeparator(rightLine, rightMessage)): return leftLine == rightLine && leftMessage == rightMessage + default: return false + } +} + diff --git a/Tests/MarkdownTableParserTests.swift b/Tests/MarkdownTableParserTests.swift new file mode 100644 index 0000000..1b25d52 --- /dev/null +++ b/Tests/MarkdownTableParserTests.swift @@ -0,0 +1,102 @@ +// +// MarkdownTableParserTests.swift +// AcceptanceMark +// +// Created by Andrea Bizzotto on 03/10/2016. +// Copyright © 2016 musevisions. All rights reserved. +// + +import XCTest +@testable import amtool + +class MarkdownTableParserTests: XCTestCase { + + // Initialisation Tests + func testParser_InitialState_OutsideTable() { + + let parser = MarkdownTableParser() + + XCTAssertEqual(parser.state, .outside) + } + + // MARK: Header tests + func testLineHeader_OneInputOneOutput_ParseCorrectly() { + + let headerLine = "| name:String || loaded:Bool |" + + let parser = MarkdownTableParser() + + let state = parser.parseTable(line: headerLine) + + let expectedState = MarkdownTableParserState.header(inputVars: [ TestSpec.Variable(name: "name", type: .string) ], outputVars: [ TestSpec.Variable(name: "loaded", type: .bool) ]) + + XCTAssertEqual(state, expectedState) + } + + func testLineHeader_AnotherHeader_ErrorState() { + + let headerLine = "| name:String || loaded:Bool |" + + let parser = MarkdownTableParser() + + let _ = parser.parseTable(line: headerLine) + + let state = parser.parseTable(line: headerLine) + + let expectedState = MarkdownTableParserState.error(error: .invalidSeparator(line: headerLine, message: "")) + + XCTAssertEqual(state, expectedState) + } + + func testLineHeader_NoInputsNoOutputs_ErrorState() { + + let headerLine = "||" + + let parser = MarkdownTableParser() + + let state = parser.parseTable(line: headerLine) + + let expectedState = MarkdownTableParserState.error(error: .noInputsNoOutputs(line: headerLine)) + + XCTAssertEqual(state, expectedState) + } + + func testLineHeader_NoInputsOneOutput_ParseAsOutput() { + + let headerLine = "|valid:Bool|" + + let parser = MarkdownTableParser() + + let state = parser.parseTable(line: headerLine) + + let expectedState = MarkdownTableParserState.header(inputVars: [ ], outputVars: [ TestSpec.Variable(name: "valid", type: .bool) ]) + + XCTAssertEqual(state, expectedState) + } + + func testLineHeader_MissingStartPipe_Error() { + + let headerLine = "name:String || loaded:Bool |" + + let parser = MarkdownTableParser() + + let state = parser.parseTable(line: headerLine) + + let expectedState = MarkdownTableParserState.error(error: .missingStartPipe(line: headerLine)) + + XCTAssertEqual(state, expectedState) + } + func testLineHeader_MissingEndPipe_Error() { + + let headerLine = "| name:String || loaded:Bool" + + let parser = MarkdownTableParser() + + let state = parser.parseTable(line: headerLine) + + let expectedState = MarkdownTableParserState.error(error: .missingEndPipe(line: headerLine)) + + XCTAssertEqual(state, expectedState) + } + +}