diff --git a/.swift-version b/.swift-version new file mode 100644 index 00000000..6131bd77 --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +3.0-PREVIEW-6 diff --git a/.swiftlint.yml b/.swiftlint.yml index 27a4cc2a..2e01fff7 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,6 +1,7 @@ opt_in_rules: - empty_count - missing_docs + - valid_docs excluded: - Carthage diff --git a/.travis.yml b/.travis.yml index bfdf7454..97a2eacb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,32 @@ -language: objective-c -xcode_workspace: SWXMLHash.xcworkspace -xcode_scheme: SWXMLHash iOS -osx_image: xcode7.3 - -script: - - ./Scripts/build.sh +matrix: + include: + - script: + - swiftlint + - set -o pipefail + - xcodebuild $XCODE_ACTION $WORKSPACE -scheme "SWXMLHash OSX" | xcpretty + - xcodebuild $XCODE_ACTION $WORKSPACE -scheme "SWXMLHash iOS" -sdk iphonesimulator -destination "OS=10.0,name=iPhone 6S" | xcpretty + - xcodebuild $XCODE_ACTION $WORKSPACE -scheme "SWXMLHash tvOS" -sdk appletvsimulator -destination "name=Apple TV 1080p" | xcpretty + - xcodebuild build $WORKSPACE -scheme "SWXMLHash watchOS" -sdk watchsimulator | xcpretty + env: + - JOB=Xcode + - WORKSPACE="-workspace SWXMLHash.xcworkspace" + - XCODE_ACTION="build-for-testing test-without-building" + os: osx + osx_image: xcode8 + language: objective-c + - script: + - swift build + - swift test + env: JOB=SPM + os: osx + osx_image: xcode8 + language: objective-c + - script: + - swift build + # - swift test + env: JOB=Linux + sudo: required + dist: trusty + language: generic + install: + - eval "$(curl -sL https://gist.githubusercontent.com/kylef/5c0475ff02b7c7671d2a/raw/9f442512a46d7a2af7b850d65a7e9bd31edfb09b/swiftenv-install.sh)" diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ef20f5e..90a972a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## v3.0.0 (September 13, 2016) + +* Official support for Xcode 8.0 and Swift 3.0 + * See corresponding [PR #78](https://github.com/drmohundro/SWXMLHash/pull/78) +* `XMLIndexer.Error` was renamed to `IndexingError` because of a naming conflict with the built-in `Error` type. +* Linux support is partially available and there is a Travis CI build for it as well. + * Currently failing functionality is because of https://bugs.swift.org/browse/SR-2301. + ## v2.5.1 (August 23, 2016) * Support Swift 2.3 on Xcode 8 diff --git a/README.md b/README.md index 44000071..750cdec2 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,7 @@ The API takes a lot of inspiration from [SwiftyJSON](https://github.com/SwiftyJS ## Requirements - iOS 8.0+ / Mac OS X 10.9+ / tvOS 9.0+ / watchOS 2.0+ -- Xcode 7.1+ - -(note that Xcode 8 beta and Swift 3 support are being tracked in [PR 78](https://github.com/drmohundro/SWXMLHash/pull/78)) +- Xcode 8.0+ ## Installation @@ -47,7 +45,7 @@ Then create a `Podfile` with the following contents: source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' -pod 'SWXMLHash', '~> 2.5.0' +pod 'SWXMLHash', '~> 3.0.0' ``` Finally, run the following command to install it: @@ -68,7 +66,7 @@ $ brew install carthage Then add the following line to your `Cartfile`: ``` -github "drmohundro/SWXMLHash" ~> 2.5 +github "drmohundro/SWXMLHash" ~> 3.0 ``` ### Manual Installation @@ -273,8 +271,8 @@ Using Swift 2.0's new error handling feature: ```swift do { try xml!.byKey("root").byKey("what").byKey("header").byKey("foo") -} catch let error as XMLIndexer.Error { - // error is an XMLIndexer.Error instance that you can deal with +} catch let error as IndexerError { + // error is an IndexerError instance that you can deal with } ``` @@ -285,7 +283,7 @@ switch xml["root"]["what"]["header"]["foo"] { case .Element(let elem): // everything is good, code away! case .XMLError(let error): - // error is an XMLIndexer.Error instance that you can deal with + // error is an IndexerError instance that you can deal with } ``` @@ -327,7 +325,7 @@ struct Book: XMLIndexerDeserializable { let amount: Int? let isbn: Int - static func deserialize(node: XMLIndexer) throws -> Book { + static func deserialize(_ node: XMLIndexer) throws -> Book { return try Book( title: node["title"].value(), price: node["price"].value(), diff --git a/Rakefile b/Rakefile index 1db8e75f..6195245b 100644 --- a/Rakefile +++ b/Rakefile @@ -7,13 +7,13 @@ task :test do |t| xctool_build_cmd = './scripts/build.sh' xcode_build_cmd = 'xcodebuild -workspace SWXMLHash.xcworkspace -scheme "SWXMLHash iOS" clean build test -sdk iphonesimulator' - if system('which xctool') - run xctool_build_cmd - else + #if system('which xctool') + #run xctool_build_cmd + #else if system('which xcpretty') run "#{xcode_build_cmd} | xcpretty -c" else run xcode_build_cmd end - end + #end end diff --git a/SWXMLHash.podspec b/SWXMLHash.podspec index 86e10b9a..f66f236f 100644 --- a/SWXMLHash.podspec +++ b/SWXMLHash.podspec @@ -1,19 +1,20 @@ Pod::Spec.new do |s| s.name = 'SWXMLHash' - s.version = '2.5.1' + s.version = '3.0.0' s.summary = 'Simple XML parsing in Swift' s.homepage = 'https://github.com/drmohundro/SWXMLHash' s.license = { type: 'MIT' } s.authors = { 'David Mohundro' => 'david@mohundro.com' } s.requires_arc = true - s.pod_target_xcconfig = { 'SWIFT_VERSION' => '2.3' } + s.pod_target_xcconfig = { 'SWIFT_VERSION' => '3.0' } s.osx.deployment_target = '10.9' s.ios.deployment_target = '8.0' s.watchos.deployment_target = '2.0' s.tvos.deployment_target = '9.0' - s.source = { git: 'https://github.com/drmohundro/SWXMLHash.git', tag: '2.5.1' } + s.source = { git: 'https://github.com/drmohundro/SWXMLHash.git', + tag: '3.0.0' } s.source_files = 'Source/*.swift' end diff --git a/SWXMLHash.xcodeproj/project.pbxproj b/SWXMLHash.xcodeproj/project.pbxproj index 6cda58e2..7b04f99e 100644 --- a/SWXMLHash.xcodeproj/project.pbxproj +++ b/SWXMLHash.xcodeproj/project.pbxproj @@ -116,7 +116,9 @@ /* Begin PBXFileReference section */ 54B83CC41C849D9B00D588B5 /* SWXMLHash+TypeConversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SWXMLHash+TypeConversion.swift"; sourceTree = ""; }; - CD4B5F3919E2C42D005C1F33 /* test.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = test.xml; path = Tests/test.xml; sourceTree = SOURCE_ROOT; }; + 6C0CE0F01D7440F8005F1248 /* LinuxShims.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinuxShims.swift; sourceTree = ""; }; + 6C477A9C1D702C0900D76FCA /* LinuxMain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LinuxMain.swift; path = Tests/LinuxMain.swift; sourceTree = SOURCE_ROOT; }; + CD4B5F3919E2C42D005C1F33 /* test.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = test.xml; sourceTree = ""; }; CD6083EF196CA106000B4F8D /* SWXMLHash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SWXMLHash.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CD6083F3196CA106000B4F8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CD6083F4196CA106000B4F8D /* SWXMLHash.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SWXMLHash.h; sourceTree = ""; }; @@ -267,8 +269,11 @@ CDC6D13E1D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift */, CDC6D11E1D32D70800570DE5 /* WhiteSpaceParsingTests.swift */, CDC6D11A1D32D6CE00570DE5 /* XMLParsingTests.swift */, + 6C0CE0F01D7440F8005F1248 /* LinuxShims.swift */, + 6C477A9C1D702C0900D76FCA /* LinuxMain.swift */, ); - path = Tests; + name = Tests; + path = Tests/SWXMLHashTests; sourceTree = ""; }; CD6083FF196CA106000B4F8D /* Supporting Files */ = { @@ -734,7 +739,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -777,7 +782,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -827,6 +832,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "drmohundro.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SWXMLHash; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -841,7 +847,7 @@ "DEBUG=1", "$(inherited)", ); - INFOPLIST_FILE = Tests/Info.plist; + INFOPLIST_FILE = Tests/SWXMLHashTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "drmohundro.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -855,10 +861,11 @@ "$(PLATFORM_DIR)/Developer/Library/Frameworks", "$(inherited)", ); - INFOPLIST_FILE = Tests/Info.plist; + INFOPLIST_FILE = Tests/SWXMLHashTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "drmohundro.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -910,6 +917,7 @@ PRODUCT_NAME = SWXMLHash; SDKROOT = macosx; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -925,7 +933,7 @@ "DEBUG=1", "$(inherited)", ); - INFOPLIST_FILE = Tests/Info.plist; + INFOPLIST_FILE = Tests/SWXMLHashTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "drmohundro.${PRODUCT_NAME:rfc1034identifier}"; @@ -943,12 +951,13 @@ "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); - INFOPLIST_FILE = Tests/Info.plist; + INFOPLIST_FILE = Tests/SWXMLHashTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "drmohundro.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -992,6 +1001,7 @@ PRODUCT_NAME = SWXMLHash; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; }; @@ -1003,7 +1013,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = Tests/Info.plist; + INFOPLIST_FILE = Tests/SWXMLHashTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "drmohundro.SWXMLHash-tvOS-Tests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1019,11 +1029,12 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = Tests/Info.plist; + INFOPLIST_FILE = Tests/SWXMLHashTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "drmohundro.SWXMLHash-tvOS-Tests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Release; @@ -1070,6 +1081,7 @@ PRODUCT_NAME = SWXMLHash; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = 4; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; diff --git a/SWXMLHashPlayground.playground/section-1.swift b/SWXMLHashPlayground.playground/section-1.swift index 0db313d8..e00ecdcd 100644 --- a/SWXMLHashPlayground.playground/section-1.swift +++ b/SWXMLHashPlayground.playground/section-1.swift @@ -35,11 +35,11 @@ func enumerate(indexer: XMLIndexer, level: Int) { let name = child.element!.name print("\(level) \(name)") - enumerate(child, level: level + 1) + enumerate(indexer: child, level: level + 1) } } -enumerate(xml, level: 0) +enumerate(indexer: xml, level: 0) // enumerate all child elements (functionally) @@ -78,7 +78,7 @@ struct Book: XMLIndexerDeserializable { let year: Int let amount: Int? - static func deserialize(node: XMLIndexer) throws -> Book { + static func deserialize(_ node: XMLIndexer) throws -> Book { return try Book( title: node["title"].value(), price: node["price"].value(), diff --git a/SWXMLHashPlayground.playground/timeline.xctimeline b/SWXMLHashPlayground.playground/timeline.xctimeline deleted file mode 100644 index bf468afe..00000000 --- a/SWXMLHashPlayground.playground/timeline.xctimeline +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/Scripts/build.sh b/Scripts/build.sh index c76df1cb..c3b5475f 100755 --- a/Scripts/build.sh +++ b/Scripts/build.sh @@ -3,4 +3,4 @@ set -ev #xctool -scheme "SWXMLHash iOS" clean build test -sdk iphonesimulator -set -o pipefail && xcodebuild -workspace SWXMLHash.xcworkspace -scheme "SWXMLHash iOS" clean build test -sdk iphonesimulator | xcpretty +set -o pipefail && xcodebuild -workspace SWXMLHash.xcworkspace -scheme "SWXMLHash iOS" -destination "OS=10.0,name=iPhone 6S" clean build test -sdk iphonesimulator | xcpretty diff --git a/Source/SWXMLHash+TypeConversion.swift b/Source/SWXMLHash+TypeConversion.swift index 0394f866..a478e1d2 100644 --- a/Source/SWXMLHash+TypeConversion.swift +++ b/Source/SWXMLHash+TypeConversion.swift @@ -6,6 +6,7 @@ // // +// swiftlint:disable line_length // swiftlint:disable file_length import Foundation @@ -14,7 +15,8 @@ import Foundation /// Provides XMLIndexer deserialization / type transformation support public protocol XMLIndexerDeserializable { - static func deserialize(element: XMLIndexer) throws -> Self + /// Method for deserializing elements from XMLIndexer + static func deserialize(_ element: XMLIndexer) throws -> Self } /// Provides XMLIndexer deserialization / type transformation support @@ -27,7 +29,7 @@ public extension XMLIndexerDeserializable { - throws: an XMLDeserializationError.ImplementationIsMissing if no implementation is found - returns: this won't ever return because of the error being thrown */ - static func deserialize(element: XMLIndexer) throws -> Self { + static func deserialize(_ element: XMLIndexer) throws -> Self { throw XMLDeserializationError.ImplementationIsMissing( method: "XMLIndexerDeserializable.deserialize(element: XMLIndexer)") } @@ -38,7 +40,8 @@ public extension XMLIndexerDeserializable { /// Provides XMLElement deserialization / type transformation support public protocol XMLElementDeserializable { - static func deserialize(element: XMLElement) throws -> Self + /// Method for deserializing elements from XMLElement + static func deserialize(_ element: XMLElement) throws -> Self } /// Provides XMLElement deserialization / type transformation support @@ -51,7 +54,7 @@ public extension XMLElementDeserializable { - throws: an XMLDeserializationError.ImplementationIsMissing if no implementation is found - returns: this won't ever return because of the error being thrown */ - static func deserialize(element: XMLElement) throws -> Self { + static func deserialize(_ element: XMLElement) throws -> Self { throw XMLDeserializationError.ImplementationIsMissing( method: "XMLElementDeserializable.deserialize(element: XMLElement)") } @@ -61,7 +64,7 @@ public extension XMLElementDeserializable { /// Provides XMLAttribute deserialization / type transformation support public protocol XMLAttributeDeserializable { - static func deserialize(attribute: XMLAttribute) throws -> Self + static func deserialize(_ attribute: XMLAttribute) throws -> Self } /// Provides XMLAttribute deserialization / type transformation support @@ -322,7 +325,7 @@ public extension XMLIndexer { - returns: the deserialized `[T]` value - throws: an XMLDeserializationError is there is a problem with deserialization */ - func value() throws -> [T] { + func value() throws -> [T] where T: XMLIndexerDeserializable { switch self { case .List(let elements): return try elements.map { try T.deserialize( XMLIndexer($0) ) } @@ -413,17 +416,19 @@ extension XMLElement { - throws: XMLDeserializationError.NodeHasNoValue if the element text is empty - returns: The element text */ - private func nonEmptyTextOrThrow() throws -> String { - if let text = self.text where !text.characters.isEmpty { - return text - } else { throw XMLDeserializationError.NodeHasNoValue } + internal func nonEmptyTextOrThrow() throws -> String { + if let textVal = text, !textVal.characters.isEmpty { + return textVal + } + + throw XMLDeserializationError.NodeHasNoValue } } // MARK: - XMLDeserializationError /// The error that is thrown if there is a problem with deserialization -public enum XMLDeserializationError: ErrorType, CustomStringConvertible { +public enum XMLDeserializationError: Error, CustomStringConvertible { case ImplementationIsMissing(method: String) case NodeIsInvalid(node: XMLIndexer) case NodeHasNoValue @@ -462,7 +467,7 @@ extension String: XMLElementDeserializable, XMLAttributeDeserializable { - throws: an XMLDeserializationError.TypeConversionFailed if the element cannot be deserialized - returns: the deserialized String value */ - public static func deserialize(element: XMLElement) throws -> String { + public static func deserialize(_ element: XMLElement) throws -> String { guard let text = element.text else { throw XMLDeserializationError.TypeConversionFailed(type: "String", element: element) } @@ -475,7 +480,7 @@ extension String: XMLElementDeserializable, XMLAttributeDeserializable { - parameter attribute: the XMLAttribute to be deserialized - returns: the deserialized String value */ - public static func deserialize(attribute: XMLAttribute) -> String { + public static func deserialize(_ attribute: XMLAttribute) -> String { return attribute.text } } @@ -489,7 +494,7 @@ extension Int: XMLElementDeserializable, XMLAttributeDeserializable { - throws: an XMLDeserializationError.TypeConversionFailed if the element cannot be deserialized - returns: the deserialized Int value */ - public static func deserialize(element: XMLElement) throws -> Int { + public static func deserialize(_ element: XMLElement) throws -> Int { guard let value = Int(try element.nonEmptyTextOrThrow()) else { throw XMLDeserializationError.TypeConversionFailed(type: "Int", element: element) } @@ -504,7 +509,7 @@ extension Int: XMLElementDeserializable, XMLAttributeDeserializable { deserialized - returns: the deserialized Int value */ - public static func deserialize(attribute: XMLAttribute) throws -> Int { + public static func deserialize(_ attribute: XMLAttribute) throws -> Int { guard let value = Int(attribute.text) else { throw XMLDeserializationError.AttributeDeserializationFailed( type: "Int", attribute: attribute) @@ -522,7 +527,7 @@ extension Double: XMLElementDeserializable, XMLAttributeDeserializable { - throws: an XMLDeserializationError.TypeConversionFailed if the element cannot be deserialized - returns: the deserialized Double value */ - public static func deserialize(element: XMLElement) throws -> Double { + public static func deserialize(_ element: XMLElement) throws -> Double { guard let value = Double(try element.nonEmptyTextOrThrow()) else { throw XMLDeserializationError.TypeConversionFailed(type: "Double", element: element) } @@ -537,7 +542,7 @@ extension Double: XMLElementDeserializable, XMLAttributeDeserializable { deserialized - returns: the deserialized Double value */ - public static func deserialize(attribute: XMLAttribute) throws -> Double { + public static func deserialize(_ attribute: XMLAttribute) throws -> Double { guard let value = Double(attribute.text) else { throw XMLDeserializationError.AttributeDeserializationFailed( type: "Double", attribute: attribute) @@ -555,7 +560,7 @@ extension Float: XMLElementDeserializable, XMLAttributeDeserializable { - throws: an XMLDeserializationError.TypeConversionFailed if the element cannot be deserialized - returns: the deserialized Float value */ - public static func deserialize(element: XMLElement) throws -> Float { + public static func deserialize(_ element: XMLElement) throws -> Float { guard let value = Float(try element.nonEmptyTextOrThrow()) else { throw XMLDeserializationError.TypeConversionFailed(type: "Float", element: element) } @@ -570,7 +575,7 @@ extension Float: XMLElementDeserializable, XMLAttributeDeserializable { deserialized - returns: the deserialized Float value */ - public static func deserialize(attribute: XMLAttribute) throws -> Float { + public static func deserialize(_ attribute: XMLAttribute) throws -> Float { guard let value = Float(attribute.text) else { throw XMLDeserializationError.AttributeDeserializationFailed( type: "Float", attribute: attribute) @@ -590,7 +595,7 @@ extension Bool: XMLElementDeserializable, XMLAttributeDeserializable { - throws: an XMLDeserializationError.TypeConversionFailed if the element cannot be deserialized - returns: the deserialized Bool value */ - public static func deserialize(element: XMLElement) throws -> Bool { + public static func deserialize(_ element: XMLElement) throws -> Bool { let value = Bool(NSString(string: try element.nonEmptyTextOrThrow()).boolValue) return value } @@ -604,7 +609,7 @@ extension Bool: XMLElementDeserializable, XMLAttributeDeserializable { deserialized - returns: the deserialized Bool value */ - public static func deserialize(attribute: XMLAttribute) throws -> Bool { + public static func deserialize(_ attribute: XMLAttribute) throws -> Bool { let value = Bool(NSString(string: attribute.text).boolValue) return value } diff --git a/Source/SWXMLHash.swift b/Source/SWXMLHash.swift index dd9e4f52..4ba739b4 100644 --- a/Source/SWXMLHash.swift +++ b/Source/SWXMLHash.swift @@ -39,7 +39,7 @@ public class SWXMLHashOptions { public var shouldProcessLazily = false /// determines whether to parse XML namespaces or not (forwards to - /// `NSXMLParser.shouldProcessNamespaces`) + /// `XMLParser.shouldProcessNamespaces`) public var shouldProcessNamespaces = false } @@ -59,7 +59,7 @@ public class SWXMLHash { options to be set - returns: an `SWXMLHash` instance */ - class public func config(configAction: (SWXMLHashOptions) -> ()) -> SWXMLHash { + class public func config(_ configAction: (SWXMLHashOptions) -> ()) -> SWXMLHash { let opts = SWXMLHashOptions() configAction(opts) return SWXMLHash(opts) @@ -73,21 +73,21 @@ public class SWXMLHash { string containing XML. - returns: an `XMLIndexer` instance that can be iterated over */ - public func parse(xml: String) -> XMLIndexer { - return parse((xml as NSString).dataUsingEncoding(NSUTF8StringEncoding)!) + public func parse(_ xml: String) -> XMLIndexer { + return parse(xml.data(using: String.Encoding.utf8)!) } /** Begins parsing the passed in XML string. - parameters: - - data: an `NSData` instance containing XML + - data: a `Data` instance containing XML - returns: an `XMLIndexer` instance that can be iterated over */ - public func parse(data: NSData) -> XMLIndexer { + public func parse(_ data: Data) -> XMLIndexer { let parser: SimpleXmlParser = options.shouldProcessLazily ? LazyXMLParser(options) - : XMLParser(options) + : FullXMLParser(options) return parser.parse(data) } @@ -97,17 +97,17 @@ public class SWXMLHash { - parameter xml: The XML to be parsed - returns: An XMLIndexer instance that is used to look up elements in the XML */ - class public func parse(xml: String) -> XMLIndexer { + class public func parse(_ xml: String) -> XMLIndexer { return SWXMLHash().parse(xml) } /** - Method to parse XML passed in as an NSData instance. + Method to parse XML passed in as a Data instance. - parameter data: The XML to be parsed - returns: An XMLIndexer instance that is used to look up elements in the XML */ - class public func parse(data: NSData) -> XMLIndexer { + class public func parse(_ data: Data) -> XMLIndexer { return SWXMLHash().parse(data) } @@ -117,31 +117,34 @@ public class SWXMLHash { - parameter xml: The XML to be parsed - returns: An XMLIndexer instance that is used to look up elements in the XML */ - class public func lazy(xml: String) -> XMLIndexer { + class public func lazy(_ xml: String) -> XMLIndexer { return config { conf in conf.shouldProcessLazily = true }.parse(xml) } /** - Method to lazily parse XML passed in as an NSData instance. + Method to lazily parse XML passed in as a Data instance. - parameter data: The XML to be parsed - returns: An XMLIndexer instance that is used to look up elements in the XML */ - class public func lazy(data: NSData) -> XMLIndexer { + class public func lazy(_ data: Data) -> XMLIndexer { return config { conf in conf.shouldProcessLazily = true }.parse(data) } } struct Stack { var items = [T]() - mutating func push(item: T) { + mutating func push(_ item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } + mutating func drop() { + let _ = pop() + } mutating func removeAll() { - items.removeAll(keepCapacity: false) + items.removeAll(keepingCapacity: false) } func top() -> T { return items[items.count - 1] @@ -150,11 +153,57 @@ struct Stack { protocol SimpleXmlParser { init(_ options: SWXMLHashOptions) - func parse(data: NSData) -> XMLIndexer + func parse(_ data: Data) -> XMLIndexer +} + +#if os(Linux) + +extension XMLParserDelegate { + + func parserDidStartDocument(_ parser: Foundation.XMLParser) { } + func parserDidEndDocument(_ parser: Foundation.XMLParser) { } + + func parser(_ parser: Foundation.XMLParser, foundNotationDeclarationWithName name: String, publicID: String?, systemID: String?) { } + + func parser(_ parser: Foundation.XMLParser, foundUnparsedEntityDeclarationWithName name: String, publicID: String?, systemID: String?, notationName: String?) { } + + func parser(_ parser: Foundation.XMLParser, foundAttributeDeclarationWithName attributeName: String, forElement elementName: String, type: String?, defaultValue: String?) { } + + func parser(_ parser: Foundation.XMLParser, foundElementDeclarationWithName elementName: String, model: String) { } + + func parser(_ parser: Foundation.XMLParser, foundInternalEntityDeclarationWithName name: String, value: String?) { } + + func parser(_ parser: Foundation.XMLParser, foundExternalEntityDeclarationWithName name: String, publicID: String?, systemID: String?) { } + + func parser(_ parser: Foundation.XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { } + + func parser(_ parser: Foundation.XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { } + + func parser(_ parser: Foundation.XMLParser, didStartMappingPrefix prefix: String, toURI namespaceURI: String) { } + + func parser(_ parser: Foundation.XMLParser, didEndMappingPrefix prefix: String) { } + + func parser(_ parser: Foundation.XMLParser, foundCharacters string: String) { } + + func parser(_ parser: Foundation.XMLParser, foundIgnorableWhitespace whitespaceString: String) { } + + func parser(_ parser: Foundation.XMLParser, foundProcessingInstructionWithTarget target: String, data: String?) { } + + func parser(_ parser: Foundation.XMLParser, foundComment comment: String) { } + + func parser(_ parser: Foundation.XMLParser, foundCDATA CDATABlock: Data) { } + + func parser(_ parser: Foundation.XMLParser, resolveExternalEntityName name: String, systemID: String?) -> Data? { return nil } + + func parser(_ parser: Foundation.XMLParser, parseErrorOccurred parseError: NSError) { } + + func parser(_ parser: Foundation.XMLParser, validationErrorOccurred validationError: NSError) { } } -/// The implementation of NSXMLParserDelegate and where the lazy parsing actually happens. -class LazyXMLParser: NSObject, SimpleXmlParser, NSXMLParserDelegate { +#endif + +/// The implementation of XMLParserDelegate and where the lazy parsing actually happens. +class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate { required init(_ options: SWXMLHashOptions) { self.options = options super.init() @@ -164,16 +213,16 @@ class LazyXMLParser: NSObject, SimpleXmlParser, NSXMLParserDelegate { var parentStack = Stack() var elementStack = Stack() - var data: NSData? + var data: Data? var ops: [IndexOp] = [] let options: SWXMLHashOptions - func parse(data: NSData) -> XMLIndexer { + func parse(_ data: Data) -> XMLIndexer { self.data = data return XMLIndexer(self) } - func startParsing(ops: [IndexOp]) { + func startParsing(_ ops: [IndexOp]) { // clear any prior runs of parse... expected that this won't be necessary, // but you never know parentStack.removeAll() @@ -181,13 +230,13 @@ class LazyXMLParser: NSObject, SimpleXmlParser, NSXMLParserDelegate { parentStack.push(root) self.ops = ops - let parser = NSXMLParser(data: data!) + let parser = Foundation.XMLParser(data: data!) parser.shouldProcessNamespaces = options.shouldProcessNamespaces parser.delegate = self parser.parse() } - func parser(parser: NSXMLParser, + func parser(_ parser: Foundation.XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, @@ -198,11 +247,16 @@ class LazyXMLParser: NSObject, SimpleXmlParser, NSXMLParserDelegate { if !onMatch() { return } - let currentNode = parentStack.top().addElement(elementName, withAttributes: attributeDict) +#if os(Linux) + let attributeNSDict = NSDictionary(objects: attributeDict.values.flatMap({ $0 as? AnyObject }), forKeys: attributeDict.keys.map({ NSString(string: $0) as NSObject })) + let currentNode = parentStack.top().addElement(elementName, withAttributes: attributeNSDict) +#else + let currentNode = parentStack.top().addElement(elementName, withAttributes: attributeDict as NSDictionary) +#endif parentStack.push(currentNode) } - func parser(parser: NSXMLParser, foundCharacters string: String) { + func parser(_ parser: Foundation.XMLParser, foundCharacters string: String) { if !onMatch() { return } @@ -212,17 +266,17 @@ class LazyXMLParser: NSObject, SimpleXmlParser, NSXMLParserDelegate { current.addText(string) } - func parser(parser: NSXMLParser, + func parser(_ parser: Foundation.XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { let match = onMatch() - elementStack.pop() + elementStack.drop() if match { - parentStack.pop() + parentStack.drop() } } @@ -230,15 +284,15 @@ class LazyXMLParser: NSObject, SimpleXmlParser, NSXMLParserDelegate { // we typically want to compare against the elementStack to see if it matches ops, *but* // if we're on the first element, we'll instead compare the other direction. if elementStack.items.count > ops.count { - return elementStack.items.startsWith(ops.map { $0.key }) + return elementStack.items.starts(with: ops.map { $0.key }) } else { - return ops.map { $0.key }.startsWith(elementStack.items) + return ops.map { $0.key }.starts(with: elementStack.items) } } } -/// The implementation of NSXMLParserDelegate and where the parsing actually happens. -class XMLParser: NSObject, SimpleXmlParser, NSXMLParserDelegate { +/// The implementation of XMLParserDelegate and where the parsing actually happens. +class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate { required init(_ options: SWXMLHashOptions) { self.options = options super.init() @@ -248,14 +302,14 @@ class XMLParser: NSObject, SimpleXmlParser, NSXMLParserDelegate { var parentStack = Stack() let options: SWXMLHashOptions - func parse(data: NSData) -> XMLIndexer { + func parse(_ data: Data) -> XMLIndexer { // clear any prior runs of parse... expected that this won't be necessary, // but you never know parentStack.removeAll() parentStack.push(root) - let parser = NSXMLParser(data: data) + let parser = Foundation.XMLParser(data: data) parser.shouldProcessNamespaces = options.shouldProcessNamespaces parser.delegate = self parser.parse() @@ -263,28 +317,32 @@ class XMLParser: NSObject, SimpleXmlParser, NSXMLParserDelegate { return XMLIndexer(root) } - func parser(parser: NSXMLParser, + func parser(_ parser: Foundation.XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { - - let currentNode = parentStack.top().addElement(elementName, withAttributes: attributeDict) +#if os(Linux) + let attributeNSDict = NSDictionary(objects: attributeDict.values.flatMap({ $0 as? AnyObject }), forKeys: attributeDict.keys.map({ NSString(string: $0) as NSObject })) + let currentNode = parentStack.top().addElement(elementName, withAttributes: attributeNSDict) +#else + let currentNode = parentStack.top().addElement(elementName, withAttributes: attributeDict as NSDictionary) +#endif parentStack.push(currentNode) } - func parser(parser: NSXMLParser, foundCharacters string: String) { + func parser(_ parser: Foundation.XMLParser, foundCharacters string: String) { let current = parentStack.top() current.addText(string) } - func parser(parser: NSXMLParser, + func parser(_ parser: Foundation.XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { - parentStack.pop() + parentStack.drop() } } @@ -328,7 +386,7 @@ public class IndexOps { childIndex = childIndex[op.index] } } - ops.removeAll(keepCapacity: false) + ops.removeAll(keepingCapacity: false) return childIndex } @@ -341,22 +399,23 @@ public class IndexOps { } } +/// Error type that is thrown when an indexing or parsing operation fails. +public enum IndexingError: Error { + case Attribute(attr: String) + case AttributeValue(attr: String, value: String) + case Key(key: String) + case Index(idx: Int) + case Init(instance: AnyObject) + case Error +} + /// Returned from SWXMLHash, allows easy element lookup into XML data. -public enum XMLIndexer: SequenceType { +public enum XMLIndexer: Sequence { case Element(XMLElement) case List([XMLElement]) case Stream(IndexOps) - case XMLError(Error) + case XMLError(IndexingError) - /// Error type that is thrown when an indexing or parsing operation fails. - public enum Error: ErrorType { - case Attribute(attr: String) - case AttributeValue(attr: String, value: String) - case Key(key: String) - case Index(idx: Int) - case Init(instance: AnyObject) - case Error - } /// The underlying XMLElement at the currently indexed level of XML. public var element: XMLElement? { @@ -410,7 +469,7 @@ public enum XMLIndexer: SequenceType { - throws: an XMLIndexer.XMLError if an element with the specified attribute isn't found - returns: instance of XMLIndexer */ - public func withAttr(attr: String, _ value: String) throws -> XMLIndexer { + public func withAttr(_ attr: String, _ value: String) throws -> XMLIndexer { switch self { case .Stream(let opStream): let match = opStream.findElements() @@ -419,14 +478,14 @@ public enum XMLIndexer: SequenceType { if let elem = list.filter({$0.attribute(by: attr)?.text == value}).first { return .Element(elem) } - throw Error.AttributeValue(attr: attr, value: value) + throw IndexingError.AttributeValue(attr: attr, value: value) case .Element(let elem): if elem.attribute(by: attr)?.text == value { return .Element(elem) } - throw Error.AttributeValue(attr: attr, value: value) + throw IndexingError.AttributeValue(attr: attr, value: value) default: - throw Error.Attribute(attr: attr) + throw IndexingError.Attribute(attr: attr) } } @@ -443,7 +502,7 @@ public enum XMLIndexer: SequenceType { case let value as LazyXMLParser: self = .Stream(IndexOps(parser: value)) default: - throw Error.Init(instance: rawObject) + throw IndexingError.Init(instance: rawObject) } } @@ -467,7 +526,7 @@ public enum XMLIndexer: SequenceType { - returns: instance of XMLIndexer to match the element (or elements) found by key - throws: Throws an XMLIndexerError.Key if no element was found */ - public func byKey(key: String) throws -> XMLIndexer { + public func byKey(_ key: String) throws -> XMLIndexer { switch self { case .Stream(let opStream): let op = IndexOp(key) @@ -484,7 +543,7 @@ public enum XMLIndexer: SequenceType { } fallthrough default: - throw Error.Key(key: key) + throw IndexingError.Key(key: key) } } @@ -497,10 +556,10 @@ public enum XMLIndexer: SequenceType { public subscript(key: String) -> XMLIndexer { do { return try self.byKey(key) - } catch let error as Error { + } catch let error as IndexingError { return .XMLError(error) } catch { - return .XMLError(.Key(key: key)) + return .XMLError(IndexingError.Key(key: key)) } } @@ -511,7 +570,7 @@ public enum XMLIndexer: SequenceType { - throws: XMLIndexer.XMLError if the index isn't found - returns: instance of XMLIndexer to match the element (or elements) found by index */ - public func byIndex(index: Int) throws -> XMLIndexer { + public func byIndex(_ index: Int) throws -> XMLIndexer { switch self { case .Stream(let opStream): opStream.ops[opStream.ops.count - 1].index = index @@ -520,14 +579,14 @@ public enum XMLIndexer: SequenceType { if index <= list.count { return .Element(list[index]) } - return .XMLError(.Index(idx: index)) + return .XMLError(IndexingError.Index(idx: index)) case .Element(let elem): if index == 0 { return .Element(elem) } fallthrough default: - return .XMLError(.Index(idx: index)) + return .XMLError(IndexingError.Index(idx: index)) } } @@ -540,10 +599,10 @@ public enum XMLIndexer: SequenceType { public subscript(index: Int) -> XMLIndexer { do { return try byIndex(index) - } catch let error as Error { + } catch let error as IndexingError { return .XMLError(error) } catch { - return .XMLError(.Index(idx: index)) + return .XMLError(IndexingError.Index(idx: index)) } } @@ -554,13 +613,14 @@ public enum XMLIndexer: SequenceType { - returns: an array of `XMLIndexer` instances */ - public func generate() -> IndexingGenerator<[XMLIndexer]> { - return all.generate() + public func makeIterator() -> IndexingIterator<[XMLIndexer]> { + return all.makeIterator() } } /// XMLIndexer extensions -extension XMLIndexer: BooleanType { +/* +extension XMLIndexer: Boolean { /// True if a valid XMLIndexer, false if an error type public var boolValue: Bool { switch self { @@ -571,16 +631,17 @@ extension XMLIndexer: BooleanType { } } } + */ extension XMLIndexer: CustomStringConvertible { /// The XML representation of the XMLIndexer at the current level public var description: String { switch self { case .List(let list): - return list.map { $0.description }.joinWithSeparator("") + return list.map { $0.description }.joined(separator: "") case .Element(let elem): if elem.name == rootElementName { - return elem.children.map { $0.description }.joinWithSeparator("") + return elem.children.map { $0.description }.joined(separator: "") } return elem.description @@ -590,7 +651,7 @@ extension XMLIndexer: CustomStringConvertible { } } -extension XMLIndexer.Error: CustomStringConvertible { +extension IndexingError: CustomStringConvertible { /// The description for the `XMLIndexer.Error`. public var description: String { switch self { @@ -637,7 +698,7 @@ public class XMLElement: XMLContent { public let name: String /// The attributes of the element - @available(*, deprecated, message="See `allAttributes` instead, which introduces the XMLAttribute type over a simple String type") + @available(*, deprecated, message: "See `allAttributes` instead, which introduces the XMLAttribute type over a simple String type") public var attributes: [String:String] { var attrMap = [String: String]() for (name, attr) in allAttributes { @@ -657,9 +718,10 @@ public class XMLElement: XMLContent { return children .map({ $0 as? TextElement }) .flatMap({ $0 }) - .reduce("", combine: { $0 + $1!.text }) + .reduce("", { $0 + $1!.text }) } + /// All child elements (text or XML) public var children = [XMLContent]() var count: Int = 0 var index: Int @@ -688,7 +750,7 @@ public class XMLElement: XMLContent { - withAttributes: The attributes dictionary for the element being added - returns: The XMLElement that has now been added */ - func addElement(name: String, withAttributes attributes: NSDictionary) -> XMLElement { + func addElement(_ name: String, withAttributes attributes: NSDictionary) -> XMLElement { let element = XMLElement(name: name, index: count) count += 1 @@ -696,7 +758,7 @@ public class XMLElement: XMLContent { for (keyAny, valueAny) in attributes { if let key = keyAny as? String, - value = valueAny as? String { + let value = valueAny as? String { element.allAttributes[key] = XMLAttribute(name: key, text: value) } } @@ -704,7 +766,7 @@ public class XMLElement: XMLContent { return element } - func addText(text: String) { + func addText(_ text: String) { let elem = TextElement(text: text) children.append(elem) @@ -728,7 +790,7 @@ extension XMLAttribute: CustomStringConvertible { extension XMLElement: CustomStringConvertible { /// The tag, attributes and content for a `XMLElement` instance (content) public var description: String { - var attributesString = allAttributes.map { $0.1.description }.joinWithSeparator(" ") + var attributesString = allAttributes.map { $0.1.description }.joined(separator: " ") if !attributesString.isEmpty { attributesString = " " + attributesString } @@ -740,7 +802,7 @@ extension XMLElement: CustomStringConvertible { xmlReturn.append(child.description) } xmlReturn.append("") - return xmlReturn.joinWithSeparator("") + return xmlReturn.joined(separator: "") } if text != nil { @@ -750,3 +812,14 @@ extension XMLElement: CustomStringConvertible { } } } + +// Workaround for "'XMLElement' is ambiguous for type lookup in this context" error on macOS. +// +// On macOS, `XMLElement` is defined in Foundation. +// So, the code referencing `XMLElement` generates above error. +// Following code allow to using `SWXMLhash.XMLElement` in client codes. +extension SWXMLHash { + public typealias XMLElement = SWXMLHashXMLElement +} + +public typealias SWXMLHashXMLElement = XMLElement diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift new file mode 100644 index 00000000..1305bc20 --- /dev/null +++ b/Tests/LinuxMain.swift @@ -0,0 +1,17 @@ +import XCTest + +@testable import SWXMLHashTests + +XCTMain([ + testCase(LazyTypesConversionTests.allTests), + testCase(LazyWhiteSpaceParsingTests.allTests), + testCase(LazyXMLParsingTests.allTests), + testCase(MixedTextWithXMLElementsTests.allTests), + testCase(SWXMLHashConfigTests.allTests), + testCase(TypeConversionArrayOfNonPrimitiveTypesTests.allTests), + testCase(TypeConversionBasicTypesTests.allTests), + testCase(TypeConversionComplexTypesTests.allTests), + testCase(TypeConversionPrimitypeTypesTests.allTests), + testCase(WhiteSpaceParsingTests.allTests), + testCase(XMLParsingTests.allTests), + ]) diff --git a/Tests/Info.plist b/Tests/SWXMLHashTests/Info.plist similarity index 100% rename from Tests/Info.plist rename to Tests/SWXMLHashTests/Info.plist diff --git a/Tests/LazyTypesConversionTests.swift b/Tests/SWXMLHashTests/LazyTypesConversionTests.swift similarity index 73% rename from Tests/LazyTypesConversionTests.swift rename to Tests/SWXMLHashTests/LazyTypesConversionTests.swift index 1525fa07..c12f6c55 100644 --- a/Tests/LazyTypesConversionTests.swift +++ b/Tests/SWXMLHashTests/LazyTypesConversionTests.swift @@ -48,12 +48,29 @@ class LazyTypesConversionTests: XCTestCase { } func testShouldConvertValueToNonOptional() { - let value: String = try! parser!["root"]["string"].value() - XCTAssertEqual(value, "the string value") + do { + let value: String = try parser!["root"]["string"].value() + XCTAssertEqual(value, "the string value") + } catch { + XCTFail("\(error)") + } } func testShouldConvertAttributeToNonOptional() { - let value: Int = try! parser!["root"]["attribute"].value(ofAttribute: "int") - XCTAssertEqual(value, 1) + do { + let value: Int = try parser!["root"]["attribute"].value(ofAttribute: "int") + XCTAssertEqual(value, 1) + } catch { + XCTFail("\(error)") + } + } +} + +extension LazyTypesConversionTests { + static var allTests: [(String, (LazyTypesConversionTests) -> () throws -> Void)] { + return [ + ("testShouldConvertValueToNonOptional", testShouldConvertValueToNonOptional), + ("testShouldConvertAttributeToNonOptional", testShouldConvertAttributeToNonOptional), + ] } } diff --git a/Tests/LazyWhiteSpaceParsingTests.swift b/Tests/SWXMLHashTests/LazyWhiteSpaceParsingTests.swift similarity index 68% rename from Tests/LazyWhiteSpaceParsingTests.swift rename to Tests/SWXMLHashTests/LazyWhiteSpaceParsingTests.swift index f87796ec..3887bbc1 100644 --- a/Tests/LazyWhiteSpaceParsingTests.swift +++ b/Tests/SWXMLHashTests/LazyWhiteSpaceParsingTests.swift @@ -21,10 +21,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +import Foundation import SWXMLHash import XCTest // swiftlint:disable line_length +// swiftlint:disable force_try class LazyWhiteSpaceParsingTests: XCTestCase { var xml: XMLIndexer? @@ -33,10 +35,14 @@ class LazyWhiteSpaceParsingTests: XCTestCase { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. - let bundle = NSBundle(forClass: WhiteSpaceParsingTests.self) - let path = bundle.pathForResource("test", ofType: "xml") - let data = NSData(contentsOfFile: path!) - xml = SWXMLHash.lazy(data!) +#if SWIFT_PACKAGE + let path = NSString.path(withComponents: NSString(string: #file).pathComponents.dropLast() + ["test.xml"]) +#else + let bundle = Bundle(for: WhiteSpaceParsingTests.self) + let path = bundle.path(forResource: "test", ofType: "xml")! +#endif + let data = try! Data(contentsOf: URL(fileURLWithPath: path)) + xml = SWXMLHash.lazy(data) } // issue #6 @@ -48,3 +54,12 @@ class LazyWhiteSpaceParsingTests: XCTestCase { XCTAssertEqual(xml!["niotemplate"]["other"].element?.text, "\n \n this\n has\n white\n space\n \n ") } } + +extension LazyWhiteSpaceParsingTests { + static var allTests: [(String, (LazyWhiteSpaceParsingTests) -> () throws -> Void)] { + return [ + ("testShouldBeAbleToPullTextBetweenElementsWithoutWhitespace", testShouldBeAbleToPullTextBetweenElementsWithoutWhitespace), + ("testShouldBeAbleToCorrectlyParseCDATASectionsWithWhitespace", testShouldBeAbleToCorrectlyParseCDATASectionsWithWhitespace), + ] + } +} diff --git a/Tests/LazyXMLParsingTests.swift b/Tests/SWXMLHashTests/LazyXMLParsingTests.swift similarity index 72% rename from Tests/LazyXMLParsingTests.swift rename to Tests/SWXMLHashTests/LazyXMLParsingTests.swift index 6278d459..a3b72a14 100644 --- a/Tests/LazyXMLParsingTests.swift +++ b/Tests/SWXMLHashTests/LazyXMLParsingTests.swift @@ -53,11 +53,16 @@ class LazyXMLParsingTests: XCTestCase { } func testShouldBeAbleToLookUpElementsByNameAndAttribute() { - XCTAssertEqual(try! xml!["root"]["catalog"]["book"].withAttr("id", "bk102")["author"].element?.text, "Ralls, Kim") + do { + let value = try xml!["root"]["catalog"]["book"].withAttr("id", "bk102")["author"].element?.text + XCTAssertEqual(value, "Ralls, Kim") + } catch { + XCTFail("\(error)") + } } func testShouldBeAbleToIterateElementGroups() { - let result = xml!["root"]["catalog"]["book"].all.map({ $0["genre"].element!.text! }).joinWithSeparator(", ") + let result = xml!["root"]["catalog"]["book"].all.map({ $0["genre"].element!.text! }).joined(separator: ", ") XCTAssertEqual(result, "Computer, Fantasy, Fantasy") } @@ -79,7 +84,7 @@ class LazyXMLParsingTests: XCTestCase { } func testShouldBeAbleToEnumerateChildren() { - let result = xml!["root"]["catalog"]["book"][0].children.map({ $0.element!.name }).joinWithSeparator(", ") + let result = xml!["root"]["catalog"]["book"][0].children.map({ $0.element!.name }).joined(separator: ", ") XCTAssertEqual(result, "author, title, genre, price, publish_date, description") } @@ -91,7 +96,7 @@ class LazyXMLParsingTests: XCTestCase { let interleavedXml = "

one

two

three

four
" let parsed = SWXMLHash.parse(interleavedXml) - let result = parsed["html"]["body"].children.map({ $0.element!.text! }).joinWithSeparator(", ") + let result = parsed["html"]["body"].children.map({ $0.element!.text! }).joined(separator: ", ") XCTAssertEqual(result, "one, two, three, four") } @@ -108,3 +113,23 @@ class LazyXMLParsingTests: XCTestCase { XCTAssertNil(xml!["root"]["what"]["header"]["foo"].element?.name) } } + +extension LazyXMLParsingTests { + static var allTests: [(String, (LazyXMLParsingTests) -> () throws -> Void)] { + return [ + ("testShouldBeAbleToParseIndividualElements", testShouldBeAbleToParseIndividualElements), + ("testShouldBeAbleToParseElementGroups", testShouldBeAbleToParseElementGroups), + ("testShouldBeAbleToParseAttributes", testShouldBeAbleToParseAttributes), + ("testShouldBeAbleToLookUpElementsByNameAndAttribute", testShouldBeAbleToLookUpElementsByNameAndAttribute), + ("testShouldBeAbleToIterateElementGroups", testShouldBeAbleToIterateElementGroups), + ("testShouldBeAbleToIterateElementGroupsEvenIfOnlyOneElementIsFound", testShouldBeAbleToIterateElementGroupsEvenIfOnlyOneElementIsFound), + ("testShouldBeAbleToIndexElementGroupsEvenIfOnlyOneElementIsFound", testShouldBeAbleToIndexElementGroupsEvenIfOnlyOneElementIsFound), + ("testShouldBeAbleToIterateUsingForIn", testShouldBeAbleToIterateUsingForIn), + ("testShouldBeAbleToEnumerateChildren", testShouldBeAbleToEnumerateChildren), + ("testShouldBeAbleToHandleMixedContent", testShouldBeAbleToHandleMixedContent), + ("testShouldHandleInterleavingXMLElements", testShouldHandleInterleavingXMLElements), + ("testShouldBeAbleToProvideADescriptionForTheDocument", testShouldBeAbleToProvideADescriptionForTheDocument), + ("testShouldReturnNilWhenKeysDontMatch", testShouldReturnNilWhenKeysDontMatch), + ] + } +} diff --git a/Tests/SWXMLHashTests/LinuxShims.swift b/Tests/SWXMLHashTests/LinuxShims.swift new file mode 100644 index 00000000..aacaa0b4 --- /dev/null +++ b/Tests/SWXMLHashTests/LinuxShims.swift @@ -0,0 +1,35 @@ +// +// LinuxShims.swift +// SWXMLHash +// +// Created by 野村 憲男 on 8/29/16. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +#if os(Linux) + + extension NSString { + class func path(withComponents components: [String]) -> String { + return pathWithComponents(components) + } + } + +#endif diff --git a/Tests/MixedTextWithXMLElementsTests.swift b/Tests/SWXMLHashTests/MixedTextWithXMLElementsTests.swift similarity index 86% rename from Tests/MixedTextWithXMLElementsTests.swift rename to Tests/SWXMLHashTests/MixedTextWithXMLElementsTests.swift index 97d9e83f..40f7250c 100644 --- a/Tests/MixedTextWithXMLElementsTests.swift +++ b/Tests/SWXMLHashTests/MixedTextWithXMLElementsTests.swift @@ -38,3 +38,11 @@ class MixedTextWithXMLElementsTests: XCTestCase { XCTAssertEqual(xml!["everything"]["news"]["content"].description, "Here is a cool thing A and second cool thing B") } } + +extension MixedTextWithXMLElementsTests { + static var allTests: [(String, (MixedTextWithXMLElementsTests) -> () throws -> Void)] { + return [ + ("testShouldBeAbleToGetAllContentsInsideOfAnElement", testShouldBeAbleToGetAllContentsInsideOfAnElement), + ] + } +} diff --git a/Tests/SWXMLHashConfigTests.swift b/Tests/SWXMLHashTests/SWXMLHashConfigTests.swift similarity index 88% rename from Tests/SWXMLHashConfigTests.swift rename to Tests/SWXMLHashTests/SWXMLHashConfigTests.swift index 1651336b..4daeebac 100644 --- a/Tests/SWXMLHashConfigTests.swift +++ b/Tests/SWXMLHashTests/SWXMLHashConfigTests.swift @@ -46,3 +46,11 @@ class SWXMLHashConfigTests: XCTestCase { XCTAssertEqual(parser!["root"]["table"]["tr"]["td"][0].element?.text, "Apples") } } + +extension SWXMLHashConfigTests { + static var allTests: [(String, (SWXMLHashConfigTests) -> () throws -> Void)] { + return [ + ("testShouldAllowProcessingNamespacesOrNot", testShouldAllowProcessingNamespacesOrNot), + ] + } +} diff --git a/Tests/TypeConversionArrayOfNonPrimitiveTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionArrayOfNonPrimitiveTypesTests.swift similarity index 63% rename from Tests/TypeConversionArrayOfNonPrimitiveTypesTests.swift rename to Tests/SWXMLHashTests/TypeConversionArrayOfNonPrimitiveTypesTests.swift index 8fd3d482..903a845d 100644 --- a/Tests/TypeConversionArrayOfNonPrimitiveTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionArrayOfNonPrimitiveTypesTests.swift @@ -87,18 +87,33 @@ class TypeConversionArrayOfNonPrimitiveTypesTests: XCTestCase { } func testShouldConvertArrayOfGoodBasicitemsItemsToNonOptional() { - let value: [BasicItem] = try! parser!["root"]["arrayOfGoodBasicItems"]["basicItem"].value() - XCTAssertEqual(value, correctBasicItems) + do { + let value: [BasicItem] = try parser!["root"]["arrayOfGoodBasicItems"]["basicItem"].value() + XCTAssertEqual(value, correctBasicItems) + } catch { + XCTFail("\(error)") + } } func testShouldConvertArrayOfGoodBasicitemsItemsToOptional() { - let value: [BasicItem]? = try! parser!["root"]["arrayOfGoodBasicItems"]["basicItem"].value() - XCTAssertEqual(value!, correctBasicItems) + do { + let value: [BasicItem]? = try parser!["root"]["arrayOfGoodBasicItems"]["basicItem"].value() + XCTAssertNotNil(value) + if let value = value { + XCTAssertEqual(value, correctBasicItems) + } + } catch { + XCTFail("\(error)") + } } func testShouldConvertArrayOfGoodBasicitemsItemsToArrayOfOptionals() { - let value: [BasicItem?] = try! parser!["root"]["arrayOfGoodBasicItems"]["basicItem"].value() - XCTAssertEqual(value.flatMap({ $0 }), correctBasicItems) + do { + let value: [BasicItem?] = try parser!["root"]["arrayOfGoodBasicItems"]["basicItem"].value() + XCTAssertEqual(value.flatMap({ $0 }), correctBasicItems) + } catch { + XCTFail("\(error)") + } } func testShouldThrowWhenConvertingArrayOfBadBasicitemsToNonOptional() { @@ -129,18 +144,33 @@ class TypeConversionArrayOfNonPrimitiveTypesTests: XCTestCase { } func testShouldConvertArrayOfGoodAttributeItemsToNonOptional() { - let value: [AttributeItem] = try! parser!["root"]["arrayOfGoodAttributeItems"]["attributeItem"].value() - XCTAssertEqual(value, correctAttributeItems) + do { + let value: [AttributeItem] = try parser!["root"]["arrayOfGoodAttributeItems"]["attributeItem"].value() + XCTAssertEqual(value, correctAttributeItems) + } catch { + XCTFail("\(error)") + } } func testShouldConvertArrayOfGoodAttributeItemsToOptional() { - let value: [AttributeItem]? = try! parser!["root"]["arrayOfGoodAttributeItems"]["attributeItem"].value() - XCTAssertEqual(value!, correctAttributeItems) + do { + let value: [AttributeItem]? = try parser!["root"]["arrayOfGoodAttributeItems"]["attributeItem"].value() + XCTAssertNotNil(value) + if let value = value { + XCTAssertEqual(value, correctAttributeItems) + } + } catch { + XCTFail("\(error)") + } } func testShouldConvertArrayOfGoodAttributeItemsToArrayOfOptionals() { - let value: [AttributeItem?] = try! parser!["root"]["arrayOfGoodAttributeItems"]["attributeItem"].value() - XCTAssertEqual(value.flatMap({ $0 }), correctAttributeItems) + do { + let value: [AttributeItem?] = try parser!["root"]["arrayOfGoodAttributeItems"]["attributeItem"].value() + XCTAssertEqual(value.flatMap({ $0 }), correctAttributeItems) + } catch { + XCTFail("\(error)") + } } func testShouldThrowWhenConvertingArrayOfBadAttributeItemsToNonOptional() { @@ -170,3 +200,22 @@ class TypeConversionArrayOfNonPrimitiveTypesTests: XCTestCase { } } } + +extension TypeConversionArrayOfNonPrimitiveTypesTests { + static var allTests: [(String, (TypeConversionArrayOfNonPrimitiveTypesTests) -> () throws -> Void)] { + return [ + ("testShouldConvertArrayOfGoodBasicitemsItemsToNonOptional", testShouldConvertArrayOfGoodBasicitemsItemsToNonOptional), + ("testShouldConvertArrayOfGoodBasicitemsItemsToOptional", testShouldConvertArrayOfGoodBasicitemsItemsToOptional), + ("testShouldConvertArrayOfGoodBasicitemsItemsToArrayOfOptionals", testShouldConvertArrayOfGoodBasicitemsItemsToArrayOfOptionals), + ("testShouldThrowWhenConvertingArrayOfBadBasicitemsToNonOptional", testShouldThrowWhenConvertingArrayOfBadBasicitemsToNonOptional), + ("testShouldThrowWhenConvertingArrayOfBadBasicitemsToOptional", testShouldThrowWhenConvertingArrayOfBadBasicitemsToOptional), + ("testShouldThrowWhenConvertingArrayOfBadBasicitemsToArrayOfOptionals", testShouldThrowWhenConvertingArrayOfBadBasicitemsToArrayOfOptionals), + ("testShouldConvertArrayOfGoodAttributeItemsToNonOptional", testShouldConvertArrayOfGoodAttributeItemsToNonOptional), + ("testShouldConvertArrayOfGoodAttributeItemsToOptional", testShouldConvertArrayOfGoodAttributeItemsToOptional), + ("testShouldConvertArrayOfGoodAttributeItemsToArrayOfOptionals", testShouldConvertArrayOfGoodAttributeItemsToArrayOfOptionals), + ("testShouldThrowWhenConvertingArrayOfBadAttributeItemsToNonOptional", testShouldThrowWhenConvertingArrayOfBadAttributeItemsToNonOptional), + ("testShouldThrowWhenConvertingArrayOfBadAttributeItemsToOptional", testShouldThrowWhenConvertingArrayOfBadAttributeItemsToOptional), + ("testShouldThrowWhenConvertingArrayOfBadAttributeItemsToArrayOfOptionals", testShouldThrowWhenConvertingArrayOfBadAttributeItemsToArrayOfOptionals), + ] + } +} diff --git a/Tests/TypeConversionBasicTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift similarity index 51% rename from Tests/TypeConversionBasicTypesTests.swift rename to Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift index 226e19d7..5144bbdd 100644 --- a/Tests/TypeConversionBasicTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift @@ -50,13 +50,21 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testShouldConvertValueToNonOptional() { - let value: String = try! parser!["root"]["string"].value() - XCTAssertEqual(value, "the string value") + do { + let value: String = try parser!["root"]["string"].value() + XCTAssertEqual(value, "the string value") + } catch { + XCTFail("\(error)") + } } func testShouldConvertEmptyToNonOptional() { - let value: String = try! parser!["root"]["empty"].value() - XCTAssertEqual(value, "") + do { + let value: String = try parser!["root"]["empty"].value() + XCTAssertEqual(value, "") + } catch { + XCTFail("\(error)") + } } func testShouldThrowWhenConvertingMissingToNonOptional() { @@ -69,23 +77,39 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testShouldConvertValueToOptional() { - let value: String? = try! parser!["root"]["string"].value() - XCTAssertEqual(value, "the string value") + do { + let value: String? = try parser!["root"]["string"].value() + XCTAssertEqual(value, "the string value") + } catch { + XCTFail("\(error)") + } } func testShouldConvertEmptyToOptional() { - let value: String? = try! parser!["root"]["empty"].value() - XCTAssertEqual(value, "") + do { + let value: String? = try parser!["root"]["empty"].value() + XCTAssertEqual(value, "") + } catch { + XCTFail("\(error)") + } } func testShouldConvertMissingToOptional() { - let value: String? = try! parser!["root"]["missing"].value() - XCTAssertNil(value) + do { + let value: String? = try parser!["root"]["missing"].value() + XCTAssertNil(value) + } catch { + XCTFail("\(error)") + } } func testShouldConvertAttributeToNonOptional() { - let value: String = try! parser!["root"]["attr"].value(ofAttribute: "string") - XCTAssertEqual(value, "stringValue") + do { + let value: String = try parser!["root"]["attr"].value(ofAttribute: "string") + XCTAssertEqual(value, "stringValue") + } catch { + XCTFail("\(error)") + } } func testShouldConvertAttributeToOptional() { @@ -108,8 +132,12 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testIntShouldConvertValueToNonOptional() { - let value: Int = try! parser!["root"]["int"].value() - XCTAssertEqual(value, 100) + do { + let value: Int = try parser!["root"]["int"].value() + XCTAssertEqual(value, 100) + } catch { + XCTFail("\(error)") + } } func testIntShouldThrowWhenConvertingEmptyToNonOptional() { @@ -131,8 +159,12 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testIntShouldConvertValueToOptional() { - let value: Int? = try! parser!["root"]["int"].value() - XCTAssertEqual(value, 100) + do { + let value: Int? = try parser!["root"]["int"].value() + XCTAssertEqual(value, 100) + } catch { + XCTFail("\(error)") + } } func testIntShouldConvertEmptyToOptional() { @@ -145,13 +177,21 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testIntShouldConvertMissingToOptional() { - let value: Int? = try! parser!["root"]["missing"].value() - XCTAssertNil(value) + do { + let value: Int? = try parser!["root"]["missing"].value() + XCTAssertNil(value) + } catch { + XCTFail("\(error)") + } } func testIntShouldConvertAttributeToNonOptional() { - let value: Int = try! parser!["root"]["attr"].value(ofAttribute: "int") - XCTAssertEqual(value, 200) + do { + let value: Int = try parser!["root"]["attr"].value(ofAttribute: "int") + XCTAssertEqual(value, 200) + } catch { + XCTFail("\(error)") + } } func testIntShouldConvertAttributeToOptional() { @@ -160,8 +200,12 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testDoubleShouldConvertValueToNonOptional() { - let value: Double = try! parser!["root"]["double"].value() - XCTAssertEqual(value, 100.45) + do { + let value: Double = try parser!["root"]["double"].value() + XCTAssertEqual(value, 100.45) + } catch { + XCTFail("\(error)") + } } func testDoubleShouldThrowWhenConvertingEmptyToNonOptional() { @@ -183,8 +227,12 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testDoubleShouldConvertValueToOptional() { - let value: Double? = try! parser!["root"]["double"].value() - XCTAssertEqual(value, 100.45) + do { + let value: Double? = try parser!["root"]["double"].value() + XCTAssertEqual(value, 100.45) + } catch { + XCTFail("\(error)") + } } func testDoubleShouldConvertEmptyToOptional() { @@ -197,13 +245,21 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testDoubleShouldConvertMissingToOptional() { - let value: Double? = try! parser!["root"]["missing"].value() - XCTAssertNil(value) + do { + let value: Double? = try parser!["root"]["missing"].value() + XCTAssertNil(value) + } catch { + XCTFail("\(error)") + } } func testDoubleShouldConvertAttributeToNonOptional() { - let value: Double = try! parser!["root"]["attr"].value(ofAttribute: "double") - XCTAssertEqual(value, 200.15) + do { + let value: Double = try parser!["root"]["attr"].value(ofAttribute: "double") + XCTAssertEqual(value, 200.15) + } catch { + XCTFail("\(error)") + } } func testDoubleShouldConvertAttributeToOptional() { @@ -212,8 +268,12 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testFloatShouldConvertValueToNonOptional() { - let value: Float = try! parser!["root"]["float"].value() - XCTAssertEqual(value, 44.12) + do { + let value: Float = try parser!["root"]["float"].value() + XCTAssertEqual(value, 44.12) + } catch { + XCTFail("\(error)") + } } func testFloatShouldThrowWhenConvertingEmptyToNonOptional() { @@ -235,8 +295,12 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testFloatShouldConvertValueToOptional() { - let value: Float? = try! parser!["root"]["float"].value() - XCTAssertEqual(value, 44.12) + do { + let value: Float? = try parser!["root"]["float"].value() + XCTAssertEqual(value, 44.12) + } catch { + XCTFail("\(error)") + } } func testFloatShouldConvertEmptyToOptional() { @@ -249,13 +313,21 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testFloatShouldConvertMissingToOptional() { - let value: Float? = try! parser!["root"]["missing"].value() - XCTAssertNil(value) + do { + let value: Float? = try parser!["root"]["missing"].value() + XCTAssertNil(value) + } catch { + XCTFail("\(error)") + } } func testFloatShouldConvertAttributeToNonOptional() { - let value: Float = try! parser!["root"]["attr"].value(ofAttribute: "float") - XCTAssertEqual(value, 205.42) + do { + let value: Float = try parser!["root"]["attr"].value(ofAttribute: "float") + XCTAssertEqual(value, 205.42) + } catch { + XCTFail("\(error)") + } } func testFloatShouldConvertAttributeToOptional() { @@ -264,10 +336,14 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testBoolShouldConvertValueToNonOptional() { - let value1: Bool = try! parser!["root"]["bool1"].value() - let value2: Bool = try! parser!["root"]["bool2"].value() - XCTAssertFalse(value1) - XCTAssertTrue(value2) + do { + let value1: Bool = try parser!["root"]["bool1"].value() + let value2: Bool = try parser!["root"]["bool2"].value() + XCTAssertFalse(value1) + XCTAssertTrue(value2) + } catch { + XCTFail("\(error)") + } } func testBoolShouldThrowWhenConvertingEmptyToNonOptional() { @@ -289,10 +365,14 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testBoolShouldConvertValueToOptional() { - let value1: Bool? = try! parser!["root"]["bool1"].value() - XCTAssertEqual(value1, false) - let value2: Bool? = try! parser!["root"]["bool2"].value() - XCTAssertEqual(value2, true) + do { + let value1: Bool? = try parser!["root"]["bool1"].value() + XCTAssertEqual(value1, false) + let value2: Bool? = try parser!["root"]["bool2"].value() + XCTAssertEqual(value2, true) + } catch { + XCTFail("\(error)") + } } func testBoolShouldConvertEmptyToOptional() { @@ -305,13 +385,21 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testBoolShouldConvertMissingToOptional() { - let value: Bool? = try! parser!["root"]["missing"].value() - XCTAssertNil(value) + do { + let value: Bool? = try parser!["root"]["missing"].value() + XCTAssertNil(value) + } catch { + XCTFail("\(error)") + } } func testBoolShouldConvertAttributeToNonOptional() { - let value: Bool = try! parser!["root"]["attr"].value(ofAttribute: "bool1") - XCTAssertEqual(value, false) + do { + let value: Bool = try parser!["root"]["attr"].value(ofAttribute: "bool1") + XCTAssertEqual(value, false) + } catch { + XCTFail("\(error)") + } } func testBoolShouldConvertAttributeToOptional() { @@ -322,8 +410,12 @@ class TypeConversionBasicTypesTests: XCTestCase { let correctBasicItem = BasicItem(name: "the name of basic item", price: 99.14) func testBasicItemShouldConvertBasicitemToNonOptional() { - let value: BasicItem = try! parser!["root"]["basicItem"].value() - XCTAssertEqual(value, correctBasicItem) + do { + let value: BasicItem = try parser!["root"]["basicItem"].value() + XCTAssertEqual(value, correctBasicItem) + } catch { + XCTFail("\(error)") + } } func testBasicItemShouldThrowWhenConvertingEmptyToNonOptional() { @@ -345,8 +437,12 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testBasicItemShouldConvertBasicitemToOptional() { - let value: BasicItem? = try! parser!["root"]["basicItem"].value() - XCTAssertEqual(value, correctBasicItem) + do { + let value: BasicItem? = try parser!["root"]["basicItem"].value() + XCTAssertEqual(value, correctBasicItem) + } catch { + XCTFail("\(error)") + } } func testBasicItemShouldConvertEmptyToOptional() { @@ -359,15 +455,23 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testBasicItemShouldConvertMissingToOptional() { - let value: BasicItem? = try! parser!["root"]["missing"].value() - XCTAssertNil(value) + do { + let value: BasicItem? = try parser!["root"]["missing"].value() + XCTAssertNil(value) + } catch { + XCTFail("\(error)") + } } let correctAttributeItem = AttributeItem(name: "the name of attribute item", price: 19.99) func testAttributeItemShouldConvertAttributeItemToNonOptional() { - let value: AttributeItem = try! parser!["root"]["attributeItem"].value() - XCTAssertEqual(value, correctAttributeItem) + do { + let value: AttributeItem = try parser!["root"]["attributeItem"].value() + XCTAssertEqual(value, correctAttributeItem) + } catch { + XCTFail("\(error)") + } } func testAttributeItemShouldThrowWhenConvertingEmptyToNonOptional() { @@ -389,8 +493,12 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testAttributeItemShouldConvertAttributeItemToOptional() { - let value: AttributeItem? = try! parser!["root"]["attributeItem"].value() - XCTAssertEqual(value, correctAttributeItem) + do { + let value: AttributeItem? = try parser!["root"]["attributeItem"].value() + XCTAssertEqual(value, correctAttributeItem) + } catch { + XCTFail("\(error)") + } } func testAttributeItemShouldConvertEmptyToOptional() { @@ -403,8 +511,12 @@ class TypeConversionBasicTypesTests: XCTestCase { } func testAttributeItemShouldConvertMissingToOptional() { - let value: AttributeItem? = try! parser!["root"]["missing"].value() - XCTAssertNil(value) + do { + let value: AttributeItem? = try parser!["root"]["missing"].value() + XCTAssertNil(value) + } catch { + XCTFail("\(error)") + } } } @@ -412,7 +524,7 @@ struct BasicItem: XMLIndexerDeserializable { let name: String let price: Double - static func deserialize(node: XMLIndexer) throws -> BasicItem { + static func deserialize(_ node: XMLIndexer) throws -> BasicItem { return try BasicItem( name: node["name"].value(), price: node["price"].value() @@ -430,7 +542,7 @@ struct AttributeItem: XMLElementDeserializable { let name: String let price: Double - static func deserialize(element: XMLElement) throws -> AttributeItem { + static func deserialize(_ element: SWXMLHash.XMLElement) throws -> AttributeItem { return try AttributeItem( name: element.value(ofAttribute: "name"), price: element.value(ofAttribute: "price") @@ -443,3 +555,64 @@ extension AttributeItem: Equatable {} func == (a: AttributeItem, b: AttributeItem) -> Bool { return a.name == b.name && a.price == b.price } + +extension TypeConversionBasicTypesTests { + static var allTests: [(String, (TypeConversionBasicTypesTests) -> () throws -> Void)] { + return [ + ("testShouldConvertValueToNonOptional", testShouldConvertValueToNonOptional), + ("testShouldConvertEmptyToNonOptional", testShouldConvertEmptyToNonOptional), + ("testShouldThrowWhenConvertingMissingToNonOptional", testShouldThrowWhenConvertingMissingToNonOptional), + ("testShouldConvertValueToOptional", testShouldConvertValueToOptional), + ("testShouldConvertEmptyToOptional", testShouldConvertEmptyToOptional), + ("testShouldConvertMissingToOptional", testShouldConvertMissingToOptional), + ("testShouldConvertAttributeToNonOptional", testShouldConvertAttributeToNonOptional), + ("testShouldConvertAttributeToOptional", testShouldConvertAttributeToOptional), + ("testShouldThrowWhenConvertingMissingAttributeToNonOptional", testShouldThrowWhenConvertingMissingAttributeToNonOptional), + ("testShouldConvertMissingAttributeToOptional", testShouldConvertMissingAttributeToOptional), + ("testIntShouldConvertValueToNonOptional", testIntShouldConvertValueToNonOptional), + ("testIntShouldThrowWhenConvertingEmptyToNonOptional", testIntShouldThrowWhenConvertingEmptyToNonOptional), + ("testIntShouldThrowWhenConvertingMissingToNonOptional", testIntShouldThrowWhenConvertingMissingToNonOptional), + ("testIntShouldConvertValueToOptional", testIntShouldConvertValueToOptional), + ("testIntShouldConvertEmptyToOptional", testIntShouldConvertEmptyToOptional), + ("testIntShouldConvertMissingToOptional", testIntShouldConvertMissingToOptional), + ("testIntShouldConvertAttributeToNonOptional", testIntShouldConvertAttributeToNonOptional), + ("testIntShouldConvertAttributeToOptional", testIntShouldConvertAttributeToOptional), + ("testDoubleShouldConvertValueToNonOptional", testDoubleShouldConvertValueToNonOptional), + ("testDoubleShouldThrowWhenConvertingEmptyToNonOptional", testDoubleShouldThrowWhenConvertingEmptyToNonOptional), + ("testDoubleShouldThrowWhenConvertingMissingToNonOptional", testDoubleShouldThrowWhenConvertingMissingToNonOptional), + ("testDoubleShouldConvertValueToOptional", testDoubleShouldConvertValueToOptional), + ("testDoubleShouldConvertEmptyToOptional", testDoubleShouldConvertEmptyToOptional), + ("testDoubleShouldConvertMissingToOptional", testDoubleShouldConvertMissingToOptional), + ("testDoubleShouldConvertAttributeToNonOptional", testDoubleShouldConvertAttributeToNonOptional), + ("testDoubleShouldConvertAttributeToOptional", testDoubleShouldConvertAttributeToOptional), + ("testFloatShouldConvertValueToNonOptional", testFloatShouldConvertValueToNonOptional), + ("testFloatShouldThrowWhenConvertingEmptyToNonOptional", testFloatShouldThrowWhenConvertingEmptyToNonOptional), + ("testFloatShouldThrowWhenConvertingMissingToNonOptional", testFloatShouldThrowWhenConvertingMissingToNonOptional), + ("testFloatShouldConvertValueToOptional", testFloatShouldConvertValueToOptional), + ("testFloatShouldConvertEmptyToOptional", testFloatShouldConvertEmptyToOptional), + ("testFloatShouldConvertMissingToOptional", testFloatShouldConvertMissingToOptional), + ("testFloatShouldConvertAttributeToNonOptional", testFloatShouldConvertAttributeToNonOptional), + ("testFloatShouldConvertAttributeToOptional", testFloatShouldConvertAttributeToOptional), + ("testBoolShouldConvertValueToNonOptional", testBoolShouldConvertValueToNonOptional), + ("testBoolShouldThrowWhenConvertingEmptyToNonOptional", testBoolShouldThrowWhenConvertingEmptyToNonOptional), + ("testBoolShouldThrowWhenConvertingMissingToNonOptional", testBoolShouldThrowWhenConvertingMissingToNonOptional), + ("testBoolShouldConvertValueToOptional", testBoolShouldConvertValueToOptional), + ("testBoolShouldConvertEmptyToOptional", testBoolShouldConvertEmptyToOptional), + ("testBoolShouldConvertMissingToOptional", testBoolShouldConvertMissingToOptional), + ("testBoolShouldConvertAttributeToNonOptional", testBoolShouldConvertAttributeToNonOptional), + ("testBoolShouldConvertAttributeToOptional", testBoolShouldConvertAttributeToOptional), + ("testBasicItemShouldConvertBasicitemToNonOptional", testBasicItemShouldConvertBasicitemToNonOptional), + ("testBasicItemShouldThrowWhenConvertingEmptyToNonOptional", testBasicItemShouldThrowWhenConvertingEmptyToNonOptional), + ("testBasicItemShouldThrowWhenConvertingMissingToNonOptional", testBasicItemShouldThrowWhenConvertingMissingToNonOptional), + ("testBasicItemShouldConvertBasicitemToOptional", testBasicItemShouldConvertBasicitemToOptional), + ("testBasicItemShouldConvertEmptyToOptional", testBasicItemShouldConvertEmptyToOptional), + ("testBasicItemShouldConvertMissingToOptional", testBasicItemShouldConvertMissingToOptional), + ("testAttributeItemShouldConvertAttributeItemToNonOptional", testAttributeItemShouldConvertAttributeItemToNonOptional), + ("testAttributeItemShouldThrowWhenConvertingEmptyToNonOptional", testAttributeItemShouldThrowWhenConvertingEmptyToNonOptional), + ("testAttributeItemShouldThrowWhenConvertingMissingToNonOptional", testAttributeItemShouldThrowWhenConvertingMissingToNonOptional), + ("testAttributeItemShouldConvertAttributeItemToOptional", testAttributeItemShouldConvertAttributeItemToOptional), + ("testAttributeItemShouldConvertEmptyToOptional", testAttributeItemShouldConvertEmptyToOptional), + ("testAttributeItemShouldConvertMissingToOptional", testAttributeItemShouldConvertMissingToOptional), + ] + } +} diff --git a/Tests/TypeConversionComplexTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionComplexTypesTests.swift similarity index 76% rename from Tests/TypeConversionComplexTypesTests.swift rename to Tests/SWXMLHashTests/TypeConversionComplexTypesTests.swift index 9a8c2f78..3983aded 100644 --- a/Tests/TypeConversionComplexTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionComplexTypesTests.swift @@ -76,8 +76,12 @@ class TypeConversionComplexTypesTests: XCTestCase { } func testShouldConvertComplexitemToNonOptional() { - let value: ComplexItem = try! parser!["root"]["complexItem"].value() - XCTAssertEqual(value, correctComplexItem) + do { + let value: ComplexItem = try parser!["root"]["complexItem"].value() + XCTAssertEqual(value, correctComplexItem) + } catch { + XCTFail("\(error)") + } } func testShouldThrowWhenConvertingEmptyToNonOptional() { @@ -99,8 +103,12 @@ class TypeConversionComplexTypesTests: XCTestCase { } func testShouldConvertComplexitemToOptional() { - let value: ComplexItem? = try! parser!["root"]["complexItem"].value() - XCTAssertEqual(value, correctComplexItem) + do { + let value: ComplexItem? = try parser!["root"]["complexItem"].value() + XCTAssertEqual(value, correctComplexItem) + } catch { + XCTFail("\(error)") + } } func testShouldConvertEmptyToOptional() { @@ -113,8 +121,12 @@ class TypeConversionComplexTypesTests: XCTestCase { } func testShouldConvertMissingToOptional() { - let value: ComplexItem? = try! parser!["root"]["missing"].value() - XCTAssertNil(value) + do { + let value: ComplexItem? = try parser!["root"]["missing"].value() + XCTAssertNil(value) + } catch { + XCTFail("\(error)") + } } } @@ -124,7 +136,7 @@ struct ComplexItem: XMLIndexerDeserializable { let basics: [BasicItem] let attrs: [AttributeItem] - static func deserialize(node: XMLIndexer) throws -> ComplexItem { + static func deserialize(_ node: XMLIndexer) throws -> ComplexItem { return try ComplexItem( name: node["name"].value(), priceOptional: node["price"].value(), @@ -139,3 +151,16 @@ extension ComplexItem: Equatable {} func == (a: ComplexItem, b: ComplexItem) -> Bool { return a.name == b.name && a.priceOptional == b.priceOptional && a.basics == b.basics && a.attrs == b.attrs } + +extension TypeConversionComplexTypesTests { + static var allTests: [(String, (TypeConversionComplexTypesTests) -> () throws -> Void)] { + return [ + ("testShouldConvertComplexitemToNonOptional", testShouldConvertComplexitemToNonOptional), + ("testShouldThrowWhenConvertingEmptyToNonOptional", testShouldThrowWhenConvertingEmptyToNonOptional), + ("testShouldThrowWhenConvertingMissingToNonOptional", testShouldThrowWhenConvertingMissingToNonOptional), + ("testShouldConvertComplexitemToOptional", testShouldConvertComplexitemToOptional), + ("testShouldConvertEmptyToOptional", testShouldConvertEmptyToOptional), + ("testShouldConvertMissingToOptional", testShouldConvertMissingToOptional), + ] + } +} diff --git a/Tests/TypeConversionPrimitypeTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionPrimitypeTypesTests.swift similarity index 53% rename from Tests/TypeConversionPrimitypeTypesTests.swift rename to Tests/SWXMLHashTests/TypeConversionPrimitypeTypesTests.swift index 566f38a8..868b5677 100644 --- a/Tests/TypeConversionPrimitypeTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionPrimitypeTypesTests.swift @@ -50,18 +50,33 @@ class TypeConversionPrimitypeTypesTests: XCTestCase { } func testShouldConvertArrayOfGoodIntsToNonOptional() { - let value: [Int] = try! parser!["root"]["arrayOfGoodInts"]["int"].value() - XCTAssertEqual(value, [0, 1, 2, 3]) + do { + let value: [Int] = try parser!["root"]["arrayOfGoodInts"]["int"].value() + XCTAssertEqual(value, [0, 1, 2, 3]) + } catch { + XCTFail("\(error)") + } } func testShouldConvertArrayOfGoodIntsToOptional() { - let value: [Int]? = try! parser!["root"]["arrayOfGoodInts"]["int"].value() - XCTAssertEqual(value!, [0, 1, 2, 3]) + do { + let value: [Int]? = try parser!["root"]["arrayOfGoodInts"]["int"].value() + XCTAssertNotNil(value) + if let value = value { + XCTAssertEqual(value, [0, 1, 2, 3]) + } + } catch { + XCTFail("\(error)") + } } func testShouldConvertArrayOfGoodIntsToArrayOfOptionals() { - let value: [Int?] = try! parser!["root"]["arrayOfGoodInts"]["int"].value() - XCTAssertEqual(value.flatMap({ $0 }), [0, 1, 2, 3]) + do { + let value: [Int?] = try parser!["root"]["arrayOfGoodInts"]["int"].value() + XCTAssertEqual(value.flatMap({ $0 }), [0, 1, 2, 3]) + } catch { + XCTFail("\(error)") + } } func testShouldThrowWhenConvertingArrayOfBadIntsToNonOptional() { @@ -119,32 +134,81 @@ class TypeConversionPrimitypeTypesTests: XCTestCase { } func testShouldConvertArrayOfAttributeIntsToNonOptional() { - let value: [Int] = try! parser!["root"]["arrayOfAttributeInts"]["int"].value(ofAttribute: "value") - XCTAssertEqual(value, [0, 1, 2, 3]) + do { + let value: [Int] = try parser!["root"]["arrayOfAttributeInts"]["int"].value(ofAttribute: "value") + XCTAssertEqual(value, [0, 1, 2, 3]) + } catch { + XCTFail("\(error)") + } } func testShouldConvertArrayOfAttributeIntsToOptional() { - let value: [Int]? = try! parser!["root"]["arrayOfAttributeInts"]["int"].value(ofAttribute: "value") - XCTAssertEqual(value!, [0, 1, 2, 3]) + do { + let value: [Int]? = try parser!["root"]["arrayOfAttributeInts"]["int"].value(ofAttribute: "value") + XCTAssertNotNil(value) + if let value = value { + XCTAssertEqual(value, [0, 1, 2, 3]) + } + } catch { + XCTFail("\(error)") + } } func testShouldConvertArrayOfAttributeIntsToArrayOfOptionals() { - let value: [Int?] = try! parser!["root"]["arrayOfAttributeInts"]["int"].value(ofAttribute: "value") - XCTAssertEqual(value.flatMap({ $0 }), [0, 1, 2, 3]) + do { + let value: [Int?] = try parser!["root"]["arrayOfAttributeInts"]["int"].value(ofAttribute: "value") + XCTAssertEqual(value.flatMap({ $0 }), [0, 1, 2, 3]) + } catch { + XCTFail("\(error)") + } } func testShouldConvertEmptyArrayOfIntsToNonOptional() { - let value: [Int] = try! parser!["root"]["empty"]["int"].value() - XCTAssertEqual(value, []) + do { + let value: [Int] = try parser!["root"]["empty"]["int"].value() + XCTAssertEqual(value, []) + } catch { + XCTFail("\(error)") + } } func testShouldConvertEmptyArrayOfIntsToOptional() { - let value: [Int]? = try! parser!["root"]["empty"]["int"].value() - XCTAssertNil(value) + do { + let value: [Int]? = try parser!["root"]["empty"]["int"].value() + XCTAssertNil(value) + } catch { + XCTFail("\(error)") + } } func testShouldConvertEmptyArrayOfIntsToArrayOfOptionals() { - let value: [Int?] = try! parser!["root"]["empty"]["int"].value() - XCTAssertEqual(value.count, 0) + do { + let value: [Int?] = try parser!["root"]["empty"]["int"].value() + XCTAssertEqual(value.count, 0) + } catch { + XCTFail("\(error)") + } + } +} + +extension TypeConversionPrimitypeTypesTests { + static var allTests: [(String, (TypeConversionPrimitypeTypesTests) -> () throws -> Void)] { + return [ + ("testShouldConvertArrayOfGoodIntsToNonOptional", testShouldConvertArrayOfGoodIntsToNonOptional), + ("testShouldConvertArrayOfGoodIntsToOptional", testShouldConvertArrayOfGoodIntsToOptional), + ("testShouldConvertArrayOfGoodIntsToArrayOfOptionals", testShouldConvertArrayOfGoodIntsToArrayOfOptionals), + ("testShouldThrowWhenConvertingArrayOfBadIntsToNonOptional", testShouldThrowWhenConvertingArrayOfBadIntsToNonOptional), + ("testShouldThrowWhenConvertingArrayOfBadIntsToOptional", testShouldThrowWhenConvertingArrayOfBadIntsToOptional), + ("testShouldThrowWhenConvertingArrayOfBadIntsToArrayOfOptionals", testShouldThrowWhenConvertingArrayOfBadIntsToArrayOfOptionals), + ("testShouldThrowWhenConvertingArrayOfMixedIntsToNonOptional", testShouldThrowWhenConvertingArrayOfMixedIntsToNonOptional), + ("testShouldThrowWhenConvertingArrayOfMixedIntsToOptional", testShouldThrowWhenConvertingArrayOfMixedIntsToOptional), + ("testShouldThrowWhenConvertingArrayOfMixedIntsToArrayOfOptionals", testShouldThrowWhenConvertingArrayOfMixedIntsToArrayOfOptionals), + ("testShouldConvertArrayOfAttributeIntsToNonOptional", testShouldConvertArrayOfAttributeIntsToNonOptional), + ("testShouldConvertArrayOfAttributeIntsToOptional", testShouldConvertArrayOfAttributeIntsToOptional), + ("testShouldConvertArrayOfAttributeIntsToArrayOfOptionals", testShouldConvertArrayOfAttributeIntsToArrayOfOptionals), + ("testShouldConvertEmptyArrayOfIntsToNonOptional", testShouldConvertEmptyArrayOfIntsToNonOptional), + ("testShouldConvertEmptyArrayOfIntsToOptional", testShouldConvertEmptyArrayOfIntsToOptional), + ("testShouldConvertEmptyArrayOfIntsToArrayOfOptionals", testShouldConvertEmptyArrayOfIntsToArrayOfOptionals), + ] } } diff --git a/Tests/WhiteSpaceParsingTests.swift b/Tests/SWXMLHashTests/WhiteSpaceParsingTests.swift similarity index 68% rename from Tests/WhiteSpaceParsingTests.swift rename to Tests/SWXMLHashTests/WhiteSpaceParsingTests.swift index 5cb1704e..c5ec2f33 100644 --- a/Tests/WhiteSpaceParsingTests.swift +++ b/Tests/SWXMLHashTests/WhiteSpaceParsingTests.swift @@ -21,10 +21,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +import Foundation import SWXMLHash import XCTest // swiftlint:disable line_length +// swiftlint:disable force_try class WhiteSpaceParsingTests: XCTestCase { var xml: XMLIndexer? @@ -32,11 +34,14 @@ class WhiteSpaceParsingTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. - - let bundle = NSBundle(forClass: WhiteSpaceParsingTests.self) - let path = bundle.pathForResource("test", ofType: "xml") - let data = NSData(contentsOfFile: path!) - xml = SWXMLHash.parse(data!) +#if SWIFT_PACKAGE + let path = NSString.path(withComponents: NSString(string: #file).pathComponents.dropLast() + ["test.xml"]) +#else + let bundle = Bundle(for: WhiteSpaceParsingTests.self) + let path = bundle.path(forResource: "test", ofType: "xml")! +#endif + let data = try! Data(contentsOf: URL(fileURLWithPath: path)) + xml = SWXMLHash.parse(data) } // issue #6 @@ -48,3 +53,12 @@ class WhiteSpaceParsingTests: XCTestCase { XCTAssertEqual(xml!["niotemplate"]["other"].element?.text, "\n \n this\n has\n white\n space\n \n ") } } + +extension WhiteSpaceParsingTests { + static var allTests: [(String, (WhiteSpaceParsingTests) -> () throws -> Void)] { + return [ + ("testShouldBeAbleToPullTextBetweenElementsWithoutWhitespace", testShouldBeAbleToPullTextBetweenElementsWithoutWhitespace), + ("testShouldBeAbleToCorrectlyParseCDATASectionsWithWhitespace", testShouldBeAbleToCorrectlyParseCDATASectionsWithWhitespace), + ] + } +} diff --git a/Tests/XMLParsingTests.swift b/Tests/SWXMLHashTests/XMLParsingTests.swift similarity index 63% rename from Tests/XMLParsingTests.swift rename to Tests/SWXMLHashTests/XMLParsingTests.swift index a7ce8ee2..13fdc862 100644 --- a/Tests/XMLParsingTests.swift +++ b/Tests/SWXMLHashTests/XMLParsingTests.swift @@ -53,11 +53,17 @@ class XMLParsingTests: XCTestCase { } func testShouldBeAbleToLookUpElementsByNameAndAttribute() { - XCTAssertEqual(try! xml!["root"]["catalog"]["book"].withAttr("id", "bk102")["author"].element?.text, "Ralls, Kim") + do { + let value = try xml!["root"]["catalog"]["book"].withAttr("id", "bk102")["author"].element?.text + XCTAssertEqual(value, "Ralls, Kim") + } catch { + XCTFail("\(error)") + } + } func testShouldBeAbleToIterateElementGroups() { - let result = xml!["root"]["catalog"]["book"].all.map({ $0["genre"].element!.text! }).joinWithSeparator(", ") + let result = xml!["root"]["catalog"]["book"].all.map({ $0["genre"].element!.text! }).joined(separator: ", ") XCTAssertEqual(result, "Computer, Fantasy, Fantasy") } @@ -79,7 +85,7 @@ class XMLParsingTests: XCTestCase { } func testShouldBeAbleToEnumerateChildren() { - let result = xml!["root"]["catalog"]["book"][0].children.map({ $0.element!.name }).joinWithSeparator(", ") + let result = xml!["root"]["catalog"]["book"][0].children.map({ $0.element!.name }).joined(separator: ", ") XCTAssertEqual(result, "author, title, genre, price, publish_date, description") } @@ -90,27 +96,31 @@ class XMLParsingTests: XCTestCase { func testShouldBeAbleToIterateOverMixedContent() { let mixedContentXml = "

mixed content iteration support" let parsed = SWXMLHash.parse(mixedContentXml) - let result = parsed["html"]["body"]["p"].element!.children.reduce("") { acc, child in - switch child { - case let elm as XMLElement: - guard let text = elm.text else { return acc } - return acc + text - case let elm as TextElement: - return acc + elm.text - default: - XCTAssert(false, "Unknown element type") - return acc + let element = parsed["html"]["body"]["p"].element + XCTAssertNotNil(element) + if let element = element { + let result = element.children.reduce("") { acc, child in + switch child { + case let elm as SWXMLHash.XMLElement: + guard let text = elm.text else { return acc } + return acc + text + case let elm as TextElement: + return acc + elm.text + default: + XCTAssert(false, "Unknown element type") + return acc + } } - } - XCTAssertEqual(result, "mixed content iteration support") + XCTAssertEqual(result, "mixed content iteration support") + } } func testShouldHandleInterleavingXMLElements() { let interleavedXml = "

one

two

three

four
" let parsed = SWXMLHash.parse(interleavedXml) - let result = parsed["html"]["body"].children.map({ $0.element!.text! }).joinWithSeparator(", ") + let result = parsed["html"]["body"].children.map({ $0.element!.text! }).joined(separator: ", ") XCTAssertEqual(result, "one, two, three, four") } @@ -128,31 +138,31 @@ class XMLParsingTests: XCTestCase { } func testShouldProvideAnErrorObjectWhenKeysDontMatch() { - var err: XMLIndexer.Error? + var err: IndexingError? defer { XCTAssertNotNil(err) } do { - try xml!.byKey("root").byKey("what").byKey("header").byKey("foo") - } catch let error as XMLIndexer.Error { + let _ = try xml!.byKey("root").byKey("what").byKey("header").byKey("foo") + } catch let error as IndexingError { err = error } catch { err = nil } } func testShouldProvideAnErrorElementWhenIndexersDontMatch() { - var err: XMLIndexer.Error? + var err: IndexingError? defer { XCTAssertNotNil(err) } do { - try xml!.byKey("what").byKey("subelement").byIndex(5).byKey("nomatch") - } catch let error as XMLIndexer.Error { + let _ = try xml!.byKey("what").byKey("subelement").byIndex(5).byKey("nomatch") + } catch let error as IndexingError { err = error } catch { err = nil } } func testShouldStillReturnErrorsWhenAccessingViaSubscripting() { - var err: XMLIndexer.Error? = nil + var err: IndexingError? = nil switch xml!["what"]["subelement"][5]["nomatch"] { case .XMLError(let error): err = error @@ -162,3 +172,27 @@ class XMLParsingTests: XCTestCase { XCTAssertNotNil(err) } } + +extension XMLParsingTests { + static var allTests: [(String, (XMLParsingTests) -> () throws -> Void)] { + return [ + ("testShouldBeAbleToParseIndividualElements", testShouldBeAbleToParseIndividualElements), + ("testShouldBeAbleToParseElementGroups", testShouldBeAbleToParseElementGroups), + ("testShouldBeAbleToParseAttributes", testShouldBeAbleToParseAttributes), + ("testShouldBeAbleToLookUpElementsByNameAndAttribute", testShouldBeAbleToLookUpElementsByNameAndAttribute), + ("testShouldBeAbleToIterateElementGroups", testShouldBeAbleToIterateElementGroups), + ("testShouldBeAbleToIterateElementGroupsEvenIfOnlyOneElementIsFound", testShouldBeAbleToIterateElementGroupsEvenIfOnlyOneElementIsFound), + ("testShouldBeAbleToIndexElementGroupsEvenIfOnlyOneElementIsFound", testShouldBeAbleToIndexElementGroupsEvenIfOnlyOneElementIsFound), + ("testShouldBeAbleToIterateUsingForIn", testShouldBeAbleToIterateUsingForIn), + ("testShouldBeAbleToEnumerateChildren", testShouldBeAbleToEnumerateChildren), + ("testShouldBeAbleToHandleMixedContent", testShouldBeAbleToHandleMixedContent), + ("testShouldBeAbleToIterateOverMixedContent", testShouldBeAbleToIterateOverMixedContent), + ("testShouldHandleInterleavingXMLElements", testShouldHandleInterleavingXMLElements), + ("testShouldBeAbleToProvideADescriptionForTheDocument", testShouldBeAbleToProvideADescriptionForTheDocument), + ("testShouldReturnNilWhenKeysDontMatch", testShouldReturnNilWhenKeysDontMatch), + ("testShouldProvideAnErrorObjectWhenKeysDontMatch", testShouldProvideAnErrorObjectWhenKeysDontMatch), + ("testShouldProvideAnErrorElementWhenIndexersDontMatch", testShouldProvideAnErrorElementWhenIndexersDontMatch), + ("testShouldStillReturnErrorsWhenAccessingViaSubscripting", testShouldStillReturnErrorsWhenAccessingViaSubscripting), + ] + } +} diff --git a/Tests/test.xml b/Tests/SWXMLHashTests/test.xml similarity index 100% rename from Tests/test.xml rename to Tests/SWXMLHashTests/test.xml