forked from swiftlang/swift-corelibs-foundation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTestXMLParser.swift
203 lines (184 loc) · 8.41 KB
/
TestXMLParser.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// 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
//
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: NSObject, 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),
("test_sr9758_abortParsing", test_sr9758_abortParsing),
("test_sr10157_swappedElementNames", test_sr10157_swappedElementNames),
]
}
// 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: "(") {
let range: Range<String.Index> = open.upperBound..<encoding.endIndex
encoding = String(encoding[range])
}
if let close = encoding.range(of: ")") {
encoding = String(encoding[..<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: .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)
}
func test_sr9758_abortParsing() {
class Delegate: NSObject, XMLParserDelegate {
func parserDidStartDocument(_ parser: XMLParser) { parser.abortParsing() }
}
let xml = TestXMLParser.xmlUnderTest(encoding: .utf8)
let parser = XMLParser(data: xml.data(using: .utf8)!)
let delegate = Delegate()
parser.delegate = delegate
XCTAssertFalse(parser.parse())
XCTAssertNotNil(parser.parserError)
}
func test_sr10157_swappedElementNames() {
class ElementNameChecker: NSObject, XMLParserDelegate {
let name: String
init(_ name: String) { self.name = name }
func parser(_ parser: XMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String: String] = [:])
{
if parser.shouldProcessNamespaces {
XCTAssertEqual(self.name, qName)
} else {
XCTAssertEqual(self.name, elementName)
}
}
func parser(_ parser: XMLParser,
didEndElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?)
{
if parser.shouldProcessNamespaces {
XCTAssertEqual(self.name, qName)
} else {
XCTAssertEqual(self.name, elementName)
}
}
func check() {
let elementString = "<\(self.name) />"
var parser = XMLParser(data: elementString.data(using: .utf8)!)
parser.delegate = self
XCTAssertTrue(parser.parse())
// Confirm that the parts of QName is also not swapped.
parser = XMLParser(data: elementString.data(using: .utf8)!)
parser.delegate = self
parser.shouldProcessNamespaces = true
XCTAssertTrue(parser.parse())
}
}
ElementNameChecker("noPrefix").check()
ElementNameChecker("myPrefix:myLocalName").check()
}
}