| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See http://swift.org/LICENSE.txt for license information |
| // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| |
| |
| #if DEPLOYMENT_RUNTIME_OBJC || os(Linux) |
| import Foundation |
| import XCTest |
| #else |
| import SwiftFoundation |
| import SwiftXCTest |
| #endif |
| |
| class OptionalParserConformance: XMLParserDelegate {} |
| |
| enum XMLParserDelegateEvent { |
| case startDocument |
| case endDocument |
| case didStartElement(String, String?, String?, [String: String]) |
| case didEndElement(String, String?, String?) |
| case foundCharacters(String) |
| } |
| |
| extension XMLParserDelegateEvent: Equatable { |
| |
| public static func ==(lhs: XMLParserDelegateEvent, rhs: XMLParserDelegateEvent) -> Bool { |
| switch (lhs, rhs) { |
| case (.startDocument, startDocument): |
| return true |
| case (.endDocument, endDocument): |
| return true |
| case let (.didStartElement(lhsElement, lhsNamespace, lhsQname, lhsAttr), |
| didStartElement(rhsElement, rhsNamespace, rhsQname, rhsAttr)): |
| return lhsElement == rhsElement && lhsNamespace == rhsNamespace && lhsQname == rhsQname && lhsAttr == rhsAttr |
| case let (.didEndElement(lhsElement, lhsNamespace, lhsQname), |
| .didEndElement(rhsElement, rhsNamespace, rhsQname)): |
| return lhsElement == rhsElement && lhsNamespace == rhsNamespace && lhsQname == rhsQname |
| case let (.foundCharacters(lhsChar), .foundCharacters(rhsChar)): |
| return lhsChar == rhsChar |
| default: |
| return false |
| } |
| } |
| |
| } |
| |
| class XMLParserDelegateEventStream: XMLParserDelegate { |
| var events: [XMLParserDelegateEvent] = [] |
| |
| func parserDidStartDocument(_ parser: XMLParser) { |
| events.append(.startDocument) |
| } |
| func parserDidEndDocument(_ parser: XMLParser) { |
| events.append(.endDocument) |
| } |
| func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { |
| events.append(.didStartElement(elementName, namespaceURI, qName, attributeDict)) |
| } |
| func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { |
| events.append(.didEndElement(elementName, namespaceURI, qName)) |
| } |
| func parser(_ parser: XMLParser, foundCharacters string: String) { |
| events.append(.foundCharacters(string)) |
| } |
| } |
| |
| class TestNSXMLParser : XCTestCase { |
| |
| static var allTests: [(String, (TestNSXMLParser) -> () throws -> Void)] { |
| return [ |
| ("test_withData", test_withData), |
| ("test_withDataEncodings", test_withDataEncodings), |
| ("test_withDataOptions", test_withDataOptions), |
| ] |
| } |
| |
| // Helper method to embed the correct encoding in the XML header |
| static func xmlUnderTest(encoding: String.Encoding? = nil) -> String { |
| let xmlUnderTest = "<test attribute='value'><foo>bar</foo></test>" |
| guard var encoding = encoding?.description else { |
| return xmlUnderTest |
| } |
| if let open = encoding.range(of: "(") { |
| encoding = encoding.substring(from: open.upperBound) |
| } |
| if let close = encoding.range(of: ")") { |
| encoding = encoding.substring(to: close.lowerBound) |
| } |
| return "<?xml version='1.0' encoding='\(encoding.uppercased())' standalone='no'?>\n\(xmlUnderTest)\n" |
| } |
| |
| static func xmlUnderTestExpectedEvents(namespaces: Bool = false) -> [XMLParserDelegateEvent] { |
| let uri: String? = namespaces ? "" : nil |
| return [ |
| .startDocument, |
| .didStartElement("test", uri, namespaces ? "test" : nil, ["attribute": "value"]), |
| .didStartElement("foo", uri, namespaces ? "foo" : nil, [:]), |
| .foundCharacters("bar"), |
| .didEndElement("foo", uri, namespaces ? "foo" : nil), |
| .didEndElement("test", uri, namespaces ? "test" : nil), |
| ] |
| } |
| |
| |
| func test_withData() { |
| let xml = Array(TestNSXMLParser.xmlUnderTest().utf8CString) |
| let data = xml.withUnsafeBufferPointer { (buffer: UnsafeBufferPointer<CChar>) -> Data in |
| return buffer.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: buffer.count * MemoryLayout<CChar>.stride) { |
| return Data(bytes: $0, count: buffer.count) |
| } |
| } |
| let parser = XMLParser(data: data) |
| let stream = XMLParserDelegateEventStream() |
| parser.delegate = stream |
| let res = parser.parse() |
| XCTAssertEqual(stream.events, TestNSXMLParser.xmlUnderTestExpectedEvents()) |
| XCTAssertTrue(res) |
| } |
| |
| func test_withDataEncodings() { |
| // If th <?xml header isn't present, any non-UTF8 encodings fail. This appears to be libxml2 behavior. |
| // These don't work, it may just be an issue with the `encoding=xxx`. |
| // - .nextstep, .utf32LittleEndian |
| let encodings: [String.Encoding] = [.utf16LittleEndian, .utf16BigEndian, .utf32BigEndian, .ascii] |
| for encoding in encodings { |
| let xml = TestNSXMLParser.xmlUnderTest(encoding: encoding) |
| let parser = XMLParser(data: xml.data(using: encoding)!) |
| let stream = XMLParserDelegateEventStream() |
| parser.delegate = stream |
| let res = parser.parse() |
| XCTAssertEqual(stream.events, TestNSXMLParser.xmlUnderTestExpectedEvents()) |
| XCTAssertTrue(res) |
| } |
| } |
| |
| func test_withDataOptions() { |
| let xml = TestNSXMLParser.xmlUnderTest() |
| let parser = XMLParser(data: xml.data(using: String.Encoding.utf8)!) |
| parser.shouldProcessNamespaces = true |
| parser.shouldReportNamespacePrefixes = true |
| parser.shouldResolveExternalEntities = true |
| let stream = XMLParserDelegateEventStream() |
| parser.delegate = stream |
| let res = parser.parse() |
| XCTAssertEqual(stream.events, TestNSXMLParser.xmlUnderTestExpectedEvents(namespaces: true) ) |
| XCTAssertTrue(res) |
| } |
| |
| } |