blob: 84e5782de11b7139bc2d1b496bb1cc1f6e7f0792 [file] [log] [blame]
// 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 TestXMLParser : XCTestCase {
static var allTests: [(String, (TestXMLParser) -> () 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(TestXMLParser.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, TestXMLParser.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 = TestXMLParser.xmlUnderTest(encoding: encoding)
let parser = XMLParser(data: xml.data(using: encoding)!)
let stream = XMLParserDelegateEventStream()
parser.delegate = stream
let res = parser.parse()
XCTAssertEqual(stream.events, TestXMLParser.xmlUnderTestExpectedEvents())
XCTAssertTrue(res)
}
}
func test_withDataOptions() {
let xml = TestXMLParser.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, TestXMLParser.xmlUnderTestExpectedEvents(namespaces: true) )
XCTAssertTrue(res)
}
}