blob: e513052f4c4b5b1c63f2d3adb0174e913eb8c6fd [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
//
class TestXMLDocument : LoopbackServerTest {
func test_basicCreation() {
let doc = XMLDocument(rootElement: nil)
XCTAssert(doc.version == "1.0", "expected 1.0, got \(String(describing: doc.version))")
doc.version = "1.1"
XCTAssert(doc.version == "1.1", "expected 1.1, got \(String(describing: doc.version))")
let node = XMLElement(name: "Hello", uri: "http://www.example.com")
doc.setRootElement(node)
let element = doc.rootElement()!
XCTAssert(element === node)
}
func test_createElement() throws {
let element = try XMLElement(xmlString: "<D:propfind xmlns:D=\"DAV:\"><D:prop></D:prop></D:propfind>")
XCTAssert(element.name! == "D:propfind")
XCTAssert(element.rootDocument == nil)
if let namespace = element.namespaces?.first {
XCTAssert(namespace.prefix == "D")
XCTAssert(namespace.stringValue == "DAV:")
} else {
XCTFail("Namespace was not parsed correctly")
}
if let child = element.elements(forName: "D:prop").first {
XCTAssert(child.localName == "prop")
XCTAssert(child.prefix == "D")
XCTAssert(child.name == "D:prop")
} else {
XCTFail("Child element was not parsed correctly!")
}
}
func test_nextPreviousNode() {
let doc = XMLDocument(rootElement: nil)
let node = XMLElement(name: "Hello", uri: "http://www.example.com")
let fooNode = XMLElement(name: "Foo")
let barNode = XMLElement(name: "Bar")
let bazNode = XMLElement(name: "Baz")
doc.setRootElement(node)
node.addChild(fooNode)
fooNode.addChild(bazNode)
node.addChild(barNode)
XCTAssert(doc.next === node)
XCTAssert(doc.next?.next === fooNode)
XCTAssert(doc.next?.next?.next === bazNode)
XCTAssert(doc.next?.next?.next?.next === barNode)
XCTAssert(barNode.previous === bazNode)
XCTAssert(barNode.previous?.previous === fooNode)
XCTAssert(barNode.previous?.previous?.previous === node)
XCTAssert(barNode.previous?.previous?.previous?.previous === doc)
}
func test_xpath() throws {
let doc = XMLDocument(rootElement: nil)
let foo = XMLElement(name: "foo")
let bar1 = XMLElement(name: "bar")
let bar2 = XMLElement(name: "bar")
let bar3 = XMLElement(name: "bar")
let baz = XMLElement(name: "baz")
doc.setRootElement(foo)
foo.addChild(bar1)
foo.addChild(bar2)
foo.addChild(bar3)
bar2.addChild(baz)
XCTAssertEqual(baz.xPath, "/foo/bar[2]/baz")
let baz2 = XMLElement(name: "/baz")
bar2.addChild(baz2)
XCTAssertEqual(baz.xPath, "/foo/bar[2]/baz")
XCTAssertEqual(try! doc.nodes(forXPath:baz.xPath!).first, baz)
let nodes = try! doc.nodes(forXPath:"/foo/bar")
XCTAssertEqual(nodes.count, 3)
XCTAssertEqual(nodes[0], bar1)
XCTAssertEqual(nodes[1], bar2)
XCTAssertEqual(nodes[2], bar3)
let emptyResults = try! doc.nodes(forXPath: "/items/item/name[@type='alternate']/@value")
XCTAssertEqual(emptyResults.count, 0)
let xmlString = """
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<D:propfind xmlns:D="DAV:">
<D:prop>
<D:getlastmodified></D:getlastmodified>
<D:getcontentlength></D:getcontentlength>
<D:creationdate></D:creationdate>
<D:resourcetype></D:resourcetype>
</D:prop>
</D:propfind>
"""
let namespaceDoc = try XMLDocument(xmlString: xmlString, options: [])
let propNodes = try namespaceDoc.nodes(forXPath: "//D:prop")
if let propNode = propNodes.first {
XCTAssert(propNode.name == "D:prop")
} else {
XCTAssert(false, "propNode should have existed, but was nil")
}
}
func test_elementCreation() {
let element = XMLElement(name: "test", stringValue: "This is my value")
XCTAssertEqual(element.xmlString, "<test>This is my value</test>")
XCTAssertEqual(element.children?.count, 1)
}
func test_elementChildren() {
let element = XMLElement(name: "root")
let foo = XMLElement(name: "foo")
let bar = XMLElement(name: "bar")
let bar2 = bar.copy() as! XMLElement
element.addChild(foo)
element.addChild(bar)
element.addChild(bar2)
XCTAssertEqual(element.elements(forName:"bar"), [bar, bar2])
XCTAssertFalse(element.elements(forName:"foo").contains(bar))
XCTAssertFalse(element.elements(forName:"foo").contains(bar2))
let baz = XMLElement(name: "baz")
element.insertChild(baz, at: 2)
XCTAssertEqual(element.children?[2], baz)
foo.detach()
bar.detach()
element.insertChildren([foo, bar], at: 1)
XCTAssertEqual(element.children?[1], foo)
XCTAssertEqual(element.children?[2], bar)
XCTAssertEqual(element.children?[0], baz)
let faz = XMLElement(name: "faz")
element.replaceChild(at: 2, with: faz)
XCTAssertEqual(element.children?[2], faz)
for node in [foo, bar, baz, bar2, faz] {
node.detach()
}
XCTAssert(element.children?.count == 0)
element.setChildren([foo, bar, baz, bar2, faz])
XCTAssert(element.children?.count == 5)
}
func test_stringValue() {
let element = XMLElement(name: "root")
let foo = XMLElement(name: "foo")
let text = XMLNode.text(withStringValue:"<text>") as! XMLNode
let comment = XMLNode.comment(withStringValue:"<comment>") as! XMLNode
foo.addChild(text)
foo.addChild(comment)
element.addChild(foo)
XCTAssertEqual(text.stringValue, "<text>")
XCTAssertEqual(comment.stringValue, "<comment>")
XCTAssertEqual(foo.stringValue, "<text><comment>") // Same with Darwin
XCTAssertEqual(element.stringValue, "<text><comment>") // Same with Darwin
// Confirm that SR-10759 is resolved.
// https://bugs.swift.org/browse/SR-10759
text.stringValue = "<modified text>"
comment.stringValue = "<modified comment>"
XCTAssertEqual(text.stringValue, "<modified text>")
XCTAssertEqual(comment.stringValue, "<modified comment>")
XCTAssertEqual(element.stringValue, "<modified text><modified comment>")
XCTAssertEqual(element.xmlString, "<root><foo>&lt;modified text&gt;<!--<modified comment>--></foo></root>")
element.stringValue = "Hello!<evil/>"
XCTAssertEqual(element.xmlString, "<root>Hello!&lt;evil/&gt;</root>")
XCTAssertEqual(element.stringValue, "Hello!<evil/>", element.stringValue ?? "stringValue unexpectedly nil")
element.stringValue = nil
// let doc = XMLDocument(rootElement: element)
// xmlCreateIntSubset(xmlDocPtr(doc._xmlNode), "test.dtd", nil, nil)
// xmlAddDocEntity(xmlDocPtr(doc._xmlNode), "author", Int32(XML_INTERNAL_GENERAL_ENTITY.rawValue), nil, nil, "Robert Thompson")
// let author = XMLElement(name: "author")
// doc.rootElement()?.addChild(author)
// author.setStringValue("&author;", resolvingEntities: true)
// XCTAssertEqual(author.stringValue, "Robert Thompson", author.stringValue ?? "")
}
func test_objectValue() {
let element = XMLElement(name: "root")
let dict: [String: String] = ["hello": "world"]
element.objectValue = dict
/// - Todo: verify this behavior
// id to Any conversion changed descriptions so this now is "<root>[\"hello\": \"world\"]</root>"
// XCTAssertEqual(element.xmlString, "<root>{\n hello = world;\n}</root>", element.xmlString)
}
func test_attributes() {
let element = XMLElement(name: "root")
let attribute = XMLNode.attribute(withName: "color", stringValue: "#ff00ff") as! XMLNode
element.addAttribute(attribute)
XCTAssertEqual(element.xmlString, "<root color=\"#ff00ff\"></root>", element.xmlString)
element.removeAttribute(forName:"color")
XCTAssertEqual(element.xmlString, "<root></root>", element.xmlString)
element.addAttribute(attribute)
let attribute2 = XMLNode.attribute(withName: "color", stringValue: "#00ff00") as! XMLNode
element.addAttribute(attribute2)
XCTAssertEqual(element.attribute(forName: "color")?.stringValue, "#00ff00")
let otherAttribute = XMLNode.attribute(withName: "foo", stringValue: "bar") as! XMLNode
element.addAttribute(otherAttribute)
guard let attributes = element.attributes else {
XCTFail()
return
}
XCTAssertEqual(attributes.count, 2)
XCTAssertEqual(attributes.first, attribute2)
XCTAssertEqual(attributes.last, otherAttribute)
let barAttribute = XMLNode.attribute(withName: "bar", stringValue: "buz") as! XMLNode
let bazAttribute = XMLNode.attribute(withName: "baz", stringValue: "fiz") as! XMLNode
element.attributes = [barAttribute, bazAttribute]
XCTAssertEqual(element.attributes?.count, 2)
XCTAssertEqual(element.attributes?.first, barAttribute)
XCTAssertEqual(element.attributes?.last, bazAttribute)
element.setAttributesWith(["hello": "world", "foobar": "buzbaz"])
XCTAssertEqual(element.attribute(forName:"hello")?.stringValue, "world", "\(element.attribute(forName:"hello")?.stringValue as Optional)")
XCTAssertEqual(element.attribute(forName:"foobar")?.stringValue, "buzbaz", "\(element.attributes ?? [])")
}
func test_attributesWithNamespace() {
let uriNs1 = "http://example.com/ns1"
let uriNs2 = "http://example.com/ns2"
let root = XMLNode.element(withName: "root") as! XMLElement
root.addNamespace(XMLNode.namespace(withName: "ns1", stringValue: uriNs1) as! XMLNode)
let element = XMLNode.element(withName: "element") as! XMLElement
element.addNamespace(XMLNode.namespace(withName: "ns2", stringValue: uriNs2) as! XMLNode)
root.addChild(element)
// Add attributes without URI
element.addAttribute(XMLNode.attribute(withName: "name", stringValue: "John") as! XMLNode)
element.addAttribute(XMLNode.attribute(withName: "ns1:name", stringValue: "Tom") as! XMLNode)
// Add attributes with URI
element.addAttribute(XMLNode.attribute(withName: "ns1:age", uri: uriNs1, stringValue: "44") as! XMLNode)
element.addAttribute(XMLNode.attribute(withName: "ns2:address", uri: uriNs2, stringValue: "Foobar City") as! XMLNode)
// Retrieve attributes without URI
XCTAssertEqual(element.attribute(forName: "name")?.stringValue, "John", "name==John")
XCTAssertEqual(element.attribute(forName: "ns1:name")?.stringValue, "Tom", "ns1:name==Tom")
XCTAssertEqual(element.attribute(forName: "ns1:age")?.stringValue, "44", "ns1:age==44")
XCTAssertEqual(element.attribute(forName: "ns2:address")?.stringValue, "Foobar City", "ns2:addresss==Foobar City")
// Retrieve attributes with URI
XCTAssertEqual(element.attribute(forLocalName: "name", uri: nil)?.stringValue, "John", "name==John")
XCTAssertEqual(element.attribute(forLocalName: "name", uri: uriNs1)?.stringValue, "Tom", "name==Tom")
XCTAssertEqual(element.attribute(forLocalName: "age", uri: uriNs1)?.stringValue, "44", "age==44")
XCTAssertNil(element.attribute(forLocalName: "address", uri: uriNs1), "address==nil")
XCTAssertEqual(element.attribute(forLocalName: "address", uri: uriNs2)?.stringValue, "Foobar City", "addresss==Foobar City")
// Overwrite attributes
element.addAttribute(XMLNode.attribute(withName: "ns1:age", stringValue: "33") as! XMLNode)
XCTAssertEqual(element.attribute(forName: "ns1:age")?.stringValue, "33", "ns1:age==33")
element.addAttribute(XMLNode.attribute(withName: "ns1:name", uri: uriNs1, stringValue: "Tommy") as! XMLNode)
XCTAssertEqual(element.attribute(forLocalName: "name", uri: uriNs1)?.stringValue, "Tommy", "ns1:name==Tommy")
// Remove attributes
element.removeAttribute(forName: "name")
XCTAssertNil(element.attribute(forLocalName: "name", uri: nil), "name removed")
XCTAssertNotNil(element.attribute(forLocalName: "name", uri: uriNs1), "ns1:name not removed")
element.removeAttribute(forName: "ns1:name")
XCTAssertNil(element.attribute(forLocalName: "name", uri: uriNs1), "ns1:name removed")
}
func test_comments() {
let element = XMLElement(name: "root")
let comment = XMLNode.comment(withStringValue:"Here is a comment") as! XMLNode
element.addChild(comment)
XCTAssertEqual(element.xmlString, "<root><!--Here is a comment--></root>")
}
func test_processingInstruction() {
let document = XMLDocument(rootElement: XMLElement(name: "root"))
let pi = XMLNode.processingInstruction(withName:"xml-stylesheet", stringValue: "type=\"text/css\" href=\"style.css\"") as! XMLNode
document.addChild(pi)
XCTAssertEqual(pi.xmlString, "<?xml-stylesheet type=\"text/css\" href=\"style.css\"?>")
}
func test_parseXMLString() throws {
let string = "<?xml version=\"1.0\" encoding=\"utf-8\"?><!DOCTYPE test.dtd [\n <!ENTITY author \"Robert Thompson\">\n ]><root><author>&author;</author></root>"
let doc = try XMLDocument(xmlString: string, options: [.nodeLoadExternalEntitiesNever])
XCTAssert(doc.childCount == 1)
XCTAssertEqual(doc.rootElement()?.children?[0].stringValue, "Robert Thompson")
guard let testDataURL = testBundle().url(forResource: "NSXMLDocumentTestData", withExtension: "xml") else {
XCTFail("Could not find XML test data")
return
}
let newDoc = try XMLDocument(contentsOf: testDataURL, options: [])
XCTAssertEqual(newDoc.rootElement()?.name, "root")
let root = newDoc.rootElement()!
let children = root.children!
XCTAssertEqual(children[0].stringValue, "Hello world", children[0].stringValue!)
XCTAssertEqual(children[1].children?[0].stringValue, "I'm here", (children[1].children?[0].stringValue)!)
doc.insertChild(XMLElement(name: "body"), at: 1)
XCTAssertEqual(doc.children?[1].name, "body")
XCTAssertEqual(doc.children?[2].name, "root", (doc.children?[2].name)!)
}
func test_prefixes() {
let element = XMLElement(name: "xml:root")
XCTAssertEqual(element.prefix, "xml")
XCTAssertEqual(element.localName, "root")
}
func test_addNamespace() {
let element = XMLElement(name: "foo")
let xmlnsURI = "http://example.com/fakexmlns"
let xmlns = XMLNode.namespace(withName: "", stringValue: xmlnsURI) as! XMLNode
element.addNamespace(xmlns)
XCTAssert((element.namespaces ?? []).compactMap({ $0.stringValue }).contains(xmlnsURI), "namespaces didn't include the added namespace!")
XCTAssertEqual(element.uri, xmlnsURI, "uri was \(element.uri ?? "null") instead of \(xmlnsURI)")
XCTAssertEqual(element.xmlString(options:.nodeCompactEmptyElement), #"<foo xmlns="\#(xmlnsURI)"/>"#, "invalid namespace declaration.")
let otherURI = "http://example.com/fakenamespace"
let otherNS = XMLNode.namespace(withName: "other", stringValue: otherURI) as! XMLNode
element.addNamespace(otherNS)
XCTAssert((element.namespaces ?? []).compactMap({ $0.stringValue }).contains(xmlnsURI), "lost original namespace")
XCTAssert((element.namespaces ?? []).compactMap({ $0.stringValue }).contains(otherURI), "Lost new namespace")
let otherNS2 = XMLNode.namespace(withName: "other", stringValue: otherURI) as! XMLNode
element.addNamespace(otherNS2)
XCTAssertEqual(element.namespaces?.count, 2, "incorrectly added a namespace with duplicate name!")
let xmlString = element.xmlString(options:.nodeCompactEmptyElement)
XCTAssert(xmlString == #"<foo xmlns="\#(xmlnsURI)" xmlns:other="\#(otherURI)"/>"# || xmlString == #"<foo xmlns:other="\#(otherURI)" xmlns="\#(xmlnsURI)"/>"#, "unexpected namespace declaration: \(xmlString)")
let otherDoc = XMLDocument(rootElement: XMLElement(name: "Bar"))
otherDoc.rootElement()?.namespaces = [XMLNode.namespace(withName: "R", stringValue: "http://example.com/rnamespace") as! XMLNode, XMLNode.namespace(withName: "F", stringValue: "http://example.com/fakenamespace") as! XMLNode]
XCTAssert(otherDoc.rootElement()?.namespaces?.count == 2)
let namespaces: [XMLNode]? = otherDoc.rootElement()?.namespaces
let names: [String]? = namespaces?.compactMap { $0.name }
XCTAssertNotNil(names)
XCTAssert(names![0] == "R" && names![1] == "F")
otherDoc.rootElement()?.namespaces = nil
XCTAssert((otherDoc.rootElement()?.namespaces?.count ?? 0) == 0)
}
func test_removeNamespace() {
let doc = XMLDocument(rootElement: XMLElement(name: "Foo"))
let ns = XMLNode.namespace(withName: "F", stringValue: "http://example.com/fakenamespace") as! XMLNode
let otherNS = XMLNode.namespace(withName: "R", stringValue: "http://example.com/rnamespace") as! XMLNode
doc.rootElement()?.addNamespace(ns)
doc.rootElement()?.addNamespace(otherNS)
XCTAssert(doc.rootElement()?.namespaces?.count == 2)
doc.rootElement()?.removeNamespace(forPrefix: "F")
XCTAssert(doc.rootElement()?.namespaces?.count == 1)
XCTAssert(doc.rootElement()?.namespaces?.first?.name == "R")
}
func test_validation_success() throws {
let validString = "<?xml version=\"1.0\" standalone=\"yes\"?><!DOCTYPE foo [ <!ELEMENT foo (#PCDATA)> ]><foo>Hello world</foo>"
do {
let doc = try XMLDocument(xmlString: validString, options: [])
try doc.validate()
} catch {
XCTFail("\(error)")
}
let dtdUrl = "http://127.0.0.1:\(TestURLSession.serverPort)/DTDs/PropertyList-1.0.dtd"
let plistDocString = "<?xml version='1.0' encoding='utf-8'?><!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"\(dtdUrl)\"> <plist version='1.0'><dict><key>MyKey</key><string>Hello!</string></dict></plist>"
let plistDoc = try XMLDocument(xmlString: plistDocString, options: [])
do {
try plistDoc.validate()
XCTAssert(plistDoc.rootElement()?.name == "plist")
let plist = try PropertyListSerialization.propertyList(from: plistDoc.xmlData, options: [], format: nil) as! [String: Any]
XCTAssert((plist["MyKey"] as? String) == "Hello!")
} catch let nsError as NSError {
XCTFail("\(nsError.userInfo)")
}
}
func test_validation_failure() throws {
let xmlString = "<?xml version=\"1.0\" standalone=\"yes\"?><!DOCTYPE foo [ <!ELEMENT img EMPTY> ]><foo><img>not empty</img></foo>"
do {
let doc = try XMLDocument(xmlString: xmlString, options: [])
try doc.validate()
XCTFail("Should have thrown")
} catch let nsError as NSError {
XCTAssert(nsError.code == XMLParser.ErrorCode.internalError.rawValue)
XCTAssert(nsError.domain == XMLParser.errorDomain)
XCTAssert((nsError.userInfo[NSLocalizedDescriptionKey] as! String).contains("Element img was declared EMPTY this one has content"))
}
let dtdUrl = "http://127.0.0.1:\(TestURLSession.serverPort)/DTDs/PropertyList-1.0.dtd"
let plistDocString = "<?xml version='1.0' encoding='utf-8'?><!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"\(dtdUrl)\"> <plist version='1.0'><dict><key>MyKey</key><string>Hello!</string><key>MyBooleanThing</key><true>foobar</true></dict></plist>"
let plistDoc = try XMLDocument(xmlString: plistDocString, options: [])
do {
try plistDoc.validate()
XCTFail("Should have thrown!")
} catch let error as NSError {
XCTAssert((error.userInfo[NSLocalizedDescriptionKey] as! String).contains("Element true was declared EMPTY this one has content"))
}
}
func test_dtd() throws {
let node = XMLNode.dtdNode(withXMLString:"<!ELEMENT foo (#PCDATA)>") as! XMLDTDNode
XCTAssert(node.name == "foo")
let dtd = try XMLDTD(contentsOf: testBundle().url(forResource: "PropertyList-1.0", withExtension: "dtd")!, options: [])
// dtd.systemID = testBundle().URLForResource("PropertyList-1.0", withExtension: "dtd")?.absoluteString
dtd.name = "plist"
// dtd.publicID = "-//Apple//DTD PLIST 1.0//EN"
let plistNode = dtd.elementDeclaration(forName:"plist")
XCTAssert(plistNode?.name == "plist")
let plistObjectNode = dtd.entityDeclaration(forName:"plistObject")
XCTAssert(plistObjectNode?.name == "plistObject")
XCTAssert(plistObjectNode?.stringValue == "(array | data | date | dict | real | integer | string | true | false )")
let plistAttribute = dtd.attributeDeclaration(forName:"version", elementName: "plist")
XCTAssert(plistAttribute?.name == "version")
let doc = try XMLDocument(xmlString: "<?xml version='1.0' encoding='utf-8'?><plist version='1.0'><dict><key>hello</key><string>world</string></dict></plist>", options: [])
doc.dtd = dtd
do {
try doc.validate()
} catch let error as NSError {
XCTFail("\(error.userInfo)")
}
let amp = XMLDTD.predefinedEntityDeclaration(forName:"amp")
XCTAssert(amp?.name == "amp", amp?.name ?? "")
XCTAssert(amp?.stringValue == "&", amp?.stringValue ?? "")
if let entityNode = XMLNode.dtdNode(withXMLString:"<!ENTITY author 'Robert Thompson'>") as? XMLDTDNode {
XCTAssert(entityNode.name == "author")
XCTAssert(entityNode.stringValue == "Robert Thompson")
}
let elementDecl = XMLDTDNode(kind: .elementDeclaration)
elementDecl.name = "MyElement"
elementDecl.stringValue = "(#PCDATA | array)*"
XCTAssert(elementDecl.stringValue == "(#PCDATA | array)*", elementDecl.stringValue ?? "nil string value")
XCTAssert(elementDecl.name == "MyElement")
}
func test_documentWithDTD() throws {
let doc = try XMLDocument(contentsOf: testBundle().url(forResource: "NSXMLDTDTestData", withExtension: "xml")!, options: [])
let dtd = doc.dtd
XCTAssert(dtd?.name == "root")
let notation = dtd?.notationDeclaration(forName:"myNotation")
notation?.detach()
XCTAssert(notation?.name == "myNotation")
XCTAssert(notation?.systemID == "http://www.example.com", notation?.systemID ?? "nil system id!")
do {
try doc.validate()
} catch {
XCTFail("\(error)")
}
let root = dtd?.elementDeclaration(forName:"root")
root?.stringValue = "(#PCDATA)"
do {
try doc.validate()
XCTFail("should have thrown")
} catch let error as NSError {
XCTAssert(error.code == XMLParser.ErrorCode.internalError.rawValue)
} catch {
XCTFail("\(error)")
}
}
func test_dtd_attributes() throws {
let doc = try XMLDocument(contentsOf: testBundle().url(forResource: "NSXMLDTDTestData", withExtension: "xml")!, options: [])
let dtd = doc.dtd!
let attrDecl = dtd.attributeDeclaration(forName: "print", elementName: "foo")!
XCTAssert(attrDecl.dtdKind == .enumerationAttribute)
}
func test_documentWithEncodingSetDoesntCrash() throws {
weak var weakDoc: XMLDocument? = nil
func makeSureDocumentIsAllocatedAndFreed() {
let doc = XMLDocument(rootElement: XMLElement(name: "test"))
doc.characterEncoding = "UTF-8"
weakDoc = doc
}
makeSureDocumentIsAllocatedAndFreed()
XCTAssertNil(weakDoc, "document not freed even through it should have")
}
func test_nodeFindingWithNamespaces() throws {
let xmlString = """
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<D:propfind xmlns:D="DAV:">
<D:prop>
<D:getlastmodified></D:getlastmodified>
<D:getcontentlength></D:getcontentlength>
<D:creationdate></D:creationdate>
<D:resourcetype></D:resourcetype>
</D:prop>
</D:propfind>
"""
let doc = try XMLDocument(xmlString: xmlString, options: [])
let namespace = (doc.rootElement()?.namespaces?.first)!
XCTAssert(namespace.kind == .namespace, "The node was not a namespace but was a \(namespace.kind)")
XCTAssert(namespace.stringValue == "DAV:", "expected a string value of DAV: got \(namespace.stringValue as Any)")
XCTAssert(namespace.name == "D", "expected a name of D, got \(namespace.name as Any)")
let newNS = XMLNode.namespace(withName: "R", stringValue: "http://apple.com") as! XMLNode
XCTAssert(newNS.name == "R", "expected name R, got name \(newNS.name as Any)")
XCTAssert(newNS.stringValue == "http://apple.com", "expected stringValue http://apple.com, got stringValue \(newNS.stringValue as Any)")
newNS.stringValue = "FOO:"
XCTAssert(newNS.stringValue == "FOO:")
newNS.name = "F"
XCTAssert(newNS.name == "F")
let root = doc.rootElement()!
XCTAssert(root.localName == "propfind")
XCTAssert(root.name == "D:propfind")
XCTAssert(root.prefix == "D")
let node = doc.findFirstChild(named: "prop")
XCTAssert(node != nil, "failed to find existing node")
XCTAssert(node?.localName == "prop")
XCTAssert(doc.rootElement()?.elements(forLocalName: "prop", uri: "DAV:").first?.name == "D:prop", "failed to get elements, got \(doc.rootElement()?.elements(forLocalName: "prop", uri: "DAV:").first as Any)")
}
func test_optionPreserveAll() {
let xmlString = """
<?xml version="1.0" encoding="UTF-8"?>
<document>
</document>
"""
let data = xmlString.data(using: .utf8)!
guard let document = try? XMLDocument(data: data, options: .nodePreserveAll) else {
XCTFail("XMLDocument with options .nodePreserveAll")
return
}
let expected = xmlString.lowercased() + "\n"
XCTAssertEqual(expected, String(describing: document))
}
func test_rootElementRetainsDocument() {
let str = """
<?xml version="1.0" encoding="UTF-8"?>
<plans></plans>
"""
let data = str.data(using: .utf8)!
func test() throws -> String? {
let doc = try XMLDocument(data: data, options: []).rootElement()
return doc?.name
}
XCTAssertEqual(try? test(), "plans")
}
func test_nodeKinds() {
XCTAssertEqual(XMLDocument(rootElement: nil).kind, .document)
XCTAssertEqual(XMLElement(name: "prefix:localName").kind, .element)
XCTAssertEqual((XMLNode.attribute(withName: "name", stringValue: "value") as? XMLNode)?.kind, .attribute)
XCTAssertEqual((XMLNode.namespace(withName: "namespace", stringValue: "http://example.com/") as? XMLNode)?.kind, .namespace)
XCTAssertEqual((XMLNode.processingInstruction(withName: "name", stringValue: "value") as? XMLNode)?.kind, .processingInstruction)
XCTAssertEqual((XMLNode.comment(withStringValue: "comment") as? XMLNode)?.kind, .comment)
XCTAssertEqual((XMLNode.text(withStringValue: "text") as? XMLNode)?.kind, .text)
XCTAssertEqual((try? XMLDTD(data:#"<!ENTITY a "A">"#.data(using: .utf8)!))?.kind, .DTDKind)
XCTAssertEqual(XMLDTDNode(xmlString: #"<!ENTITY b "B">"#)?.kind, .entityDeclaration)
XCTAssertEqual(XMLDTDNode(xmlString: "<!ATTLIST A B CDATA #IMPLIED>")?.kind, .attributeDeclaration)
XCTAssertEqual(XMLDTDNode(xmlString: "<!ELEMENT E EMPTY>")?.kind, .elementDeclaration)
XCTAssertEqual(XMLDTDNode(xmlString: #"<!NOTATION f SYSTEM "F">"#)?.kind, .notationDeclaration)
}
func test_nodeNames() throws {
let doc = XMLDocument(rootElement: nil)
XCTAssertNil(doc.name)
doc.name = "name"
XCTAssertNil(doc.name) // `name` of XMLDocument is always nil.
let element = try XMLElement(xmlString: #"<element xmlns="http://example.com/defaultNS" />"#)
XCTAssertEqual(element.name, "element")
element.name = "otherElement"
XCTAssertEqual(element.name, "otherElement")
let attribute = try XCTUnwrap(XMLNode.attribute(withName: "name", stringValue: "value") as? XMLNode)
XCTAssertEqual(attribute.name, "name")
attribute.name = "otherName"
XCTAssertEqual(attribute.name, "otherName")
let namespace = try XCTUnwrap(element.namespaces?.first)
XCTAssertEqual(namespace.name, "")
namespace.name = "namespacePrefix"
XCTAssertEqual(namespace.name, "namespacePrefix")
let pi = try XCTUnwrap(XMLNode.processingInstruction(withName: "name", stringValue: "value") as? XMLNode)
XCTAssertEqual(pi.name, "name")
pi.name = "otherName"
XCTAssertEqual(pi.name, "otherName")
let comment = try XCTUnwrap(XMLNode.comment(withStringValue: "comment") as? XMLNode)
XCTAssertNil(comment.name)
comment.name = "name"
XCTAssertNil(comment.name) // always nil
let text = try XCTUnwrap(XMLNode.text(withStringValue: "text") as? XMLNode)
XCTAssertNil(text.name)
text.name = "name"
XCTAssertNil(text.name) // always nil
let dtd = try XMLDTD(data: #"<!ENTITY a "A">"#.data(using: .utf8)!)
XCTAssertNil(dtd.name)
dtd.name = "root"
XCTAssertEqual(dtd.name, "root")
let entityDecl = try XCTUnwrap(XMLDTDNode(xmlString: #"<!ENTITY b "B">"#))
XCTAssertEqual(entityDecl.name, "b")
entityDecl.name = "otherEntity"
XCTAssertEqual(entityDecl.name, "otherEntity")
let attrDecl = try XCTUnwrap(XMLDTDNode(xmlString: "<!ATTLIST A B CDATA #IMPLIED>"))
XCTAssertEqual(attrDecl.name, "B")
attrDecl.name = "otherAttr"
XCTAssertEqual(attrDecl.name, "otherAttr")
let elementDecl = try XCTUnwrap(XMLDTDNode(xmlString: "<!ELEMENT E EMPTY>"))
XCTAssertEqual(elementDecl.name, "E")
elementDecl.name = "otherElement"
XCTAssertEqual(elementDecl.name, "otherElement")
let notationDecl = try XCTUnwrap(XMLDTDNode(xmlString: #"<!NOTATION f SYSTEM "F">"#))
XCTAssertEqual(notationDecl.name, "f")
notationDecl.name = "otherNotation"
XCTAssertEqual(notationDecl.name, "otherNotation")
}
func test_creatingAnEmptyDocumentAndNode() {
_ = XMLDocument()
_ = XMLNode()
}
func test_creatingAnEmptyDTD() {
let dtd = XMLDTD()
XCTAssertEqual(dtd.publicID, "")
XCTAssertEqual(dtd.systemID, "")
XCTAssertEqual(dtd.children ?? [], [])
let plistDTDUrl = "https://www.apple.com/DTDs/PropertyList-1.0.dtd"
dtd.systemID = plistDTDUrl
XCTAssertEqual(dtd.systemID, plistDTDUrl)
}
func test_parsingCDataSections() throws {
let xmlString = """
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<content>some text <![CDATA[Some verbatim content! <br> Yep, it's HTML, what are you going to do]]> some more text</content>
"""
do {
let doc = try XMLDocument(xmlString: xmlString, options: [])
let root = try XCTUnwrap(doc.rootElement())
XCTAssertEqual(root.childCount, 3)
root.normalizeAdjacentTextNodesPreservingCDATA(false)
XCTAssertEqual(root.childCount, 1)
XCTAssertEqual(root.children?.first?.stringValue, "some text Some verbatim content! <br> Yep, it's HTML, what are you going to do some more text")
}
do {
let doc = try XMLDocument(xmlString: xmlString, options: [])
let root = try XCTUnwrap(doc.rootElement())
XCTAssertEqual(root.childCount, 3)
let prefix = XMLNode.text(withStringValue: "prefix! ") as! XMLNode
root.insertChild(prefix, at: 0)
print(root.children!.map { (name: $0.name, kind: $0.kind, stringValue: $0.stringValue ?? "") }.map { String(describing: $0) }.joined(separator: "\n"))
root.normalizeAdjacentTextNodesPreservingCDATA(true)
XCTAssertEqual(root.childCount, 3)
let children = try XCTUnwrap(root.children)
XCTAssertEqual(children[0].stringValue, "prefix! some text ")
XCTAssertEqual(children[1].stringValue, "Some verbatim content! <br> Yep, it's HTML, what are you going to do")
XCTAssertEqual(children[2].stringValue, " some more text")
}
}
static var allTests: [(String, (TestXMLDocument) -> () throws -> Void)] {
return [
("test_basicCreation", test_basicCreation),
("test_nextPreviousNode", test_nextPreviousNode),
// Disabled because of https://bugs.swift.org/browse/SR-10098
// ("test_xpath", test_xpath),
("test_elementCreation", test_elementCreation),
("test_elementChildren", test_elementChildren),
("test_stringValue", test_stringValue),
("test_objectValue", test_objectValue),
("test_attributes", test_attributes),
("test_attributesWithNamespace", test_attributesWithNamespace),
("test_comments", test_comments),
("test_processingInstruction", test_processingInstruction),
("test_parseXMLString", test_parseXMLString),
("test_prefixes", test_prefixes),
/* ⚠️ */ ("test_validation_success", testExpectedToFail(test_validation_success,
/* ⚠️ */ #"<https://bugs.swift.org/browse/SR-10643> Could not build URI for external subset "http://127.0.0.1:-2/DTDs/PropertyList-1.0.dtd""#)),
/* ⚠️ */ ("test_validation_failure", testExpectedToFail(test_validation_failure,
/* ⚠️ */ "<https://bugs.swift.org/browse/SR-10643> XCTAssert in last catch block fails")),
("test_dtd", test_dtd),
("test_documentWithDTD", test_documentWithDTD),
("test_dtd_attributes", test_dtd_attributes),
("test_documentWithEncodingSetDoesntCrash", test_documentWithEncodingSetDoesntCrash),
("test_nodeFindingWithNamespaces", test_nodeFindingWithNamespaces),
("test_createElement", test_createElement),
("test_addNamespace", test_addNamespace),
("test_removeNamespace", test_removeNamespace),
("test_optionPreserveAll", test_optionPreserveAll),
("test_rootElementRetainsDocument", test_rootElementRetainsDocument),
("test_nodeKinds", test_nodeKinds),
("test_nodeNames", test_nodeNames),
("test_creatingAnEmptyDocumentAndNode", test_creatingAnEmptyDocumentAndNode),
("test_creatingAnEmptyDTD", test_creatingAnEmptyDTD),
("test_parsingCDataSections", test_parsingCDataSections),
]
}
}
fileprivate extension XMLNode {
func findFirstChild(named name: String) -> XMLNode? {
guard let children = self.children else {
return nil
}
for child in children {
if let childName = child.localName {
if childName == name {
return child
}
}
if let result = child.findFirstChild(named: name) {
return result
}
}
return nil
}
}