Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Init doesn't fail for invalid XML #128

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .swift-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.0
5.8
5 changes: 3 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.0
// swift-tools-version:5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand All @@ -15,7 +15,8 @@ let package = Package(
),
.testTarget(name: "FuziTests",
dependencies: ["Fuzi"],
path: "Tests"
path: "Tests",
resources: [.process("Resources")]
)
]
)
26 changes: 16 additions & 10 deletions Sources/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,15 @@ open class XMLDocument {

- returns: An `XMLDocument` with the contents of the specified XML string.
*/
public convenience init(string: String, encoding: String.Encoding = String.Encoding.utf8) throws {
public convenience init(
string: String,
encoding: String.Encoding = String.Encoding.utf8,
parseOptions: XmlParseOptions = .defaultOptions
) throws {
guard let cChars = string.cString(using: encoding) else {
throw XMLError.invalidData
}
try self.init(cChars: cChars)
try self.init(cChars: cChars, parseOptions: parseOptions)
}

/**
Expand All @@ -90,9 +94,9 @@ open class XMLDocument {

- returns: An `XMLDocument` with the contents of the specified XML string.
*/
public convenience init(data: Data) throws {
public convenience init(data: Data, parseOptions: XmlParseOptions = .defaultOptions) throws {
let buffer = data.withUnsafeBytes { $0.bindMemory(to: Int8.self) }
try self.init(buffer: buffer)
try self.init(buffer: buffer, parseOptions: parseOptions)
}

/**
Expand All @@ -104,11 +108,11 @@ open class XMLDocument {

- returns: An `XMLDocument` with the contents of the specified XML string.
*/
public convenience init(cChars: [CChar]) throws {
public convenience init(cChars: [CChar], parseOptions: XmlParseOptions = .defaultOptions) throws {
let buffer = cChars.withUnsafeBufferPointer { buffer in
UnsafeBufferPointer(rebasing: buffer[0..<buffer.count])
}
try self.init(buffer: buffer)
try self.init(buffer: buffer, parseOptions: parseOptions)
}

/**
Expand All @@ -121,10 +125,12 @@ open class XMLDocument {
- returns: An `XMLDocument` with the contents of the specified XML string.
*/

public convenience init(buffer: UnsafeBufferPointer<Int8>) throws {
let options = Int32(XML_PARSE_NOWARNING.rawValue | XML_PARSE_NOERROR.rawValue | XML_PARSE_RECOVER.rawValue)
try self.init(buffer: buffer, options: options)
}
public convenience init(
buffer: UnsafeBufferPointer<Int8>,
parseOptions: XmlParseOptions = .defaultOptions
) throws {
try self.init(buffer: buffer, options: Int32(parseOptions.rawValue))
}

fileprivate convenience init(buffer: UnsafeBufferPointer<Int8>, options: Int32) throws {
guard let document = type(of: self).parse(buffer: buffer, options: options) else {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import libxml2
/**
* XMLError enumeration.
*/
public enum XMLError: Error {
public enum XMLError: Error, Equatable {
/// No error
case noError
/// Contains a libxml2 error with error code and message
Expand Down
36 changes: 36 additions & 0 deletions Sources/XmlOptionsSet.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// XmlParseOptions.swift
// Copyright (c) 2024 BPerlakiH
//
// 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
import libxml2


public struct XmlParseOptions: OptionSet {
public let rawValue: UInt32
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
public static let recover = XmlParseOptions(rawValue: 1 << 0)
public static let noError = XmlParseOptions(rawValue: 1 << 5)
public static let noWarning = XmlParseOptions(rawValue: 1 << 6)

public static let defaultOptions: XmlParseOptions = [.recover, .noError, .noWarning]
}
2 changes: 1 addition & 1 deletion Tests/AtomTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class AtomTests: XCTestCase {
var document: Fuzi.XMLDocument!
override func setUp() {
super.setUp()
let filePath = Bundle(for: AtomTests.self).url(forResource: "atom", withExtension: "xml")!
let filePath = Bundle.testResources.url(forResource: "atom", withExtension: "xml")!
do {
document = try XMLDocument(data: Data(contentsOf: filePath))
} catch {
Expand Down
37 changes: 37 additions & 0 deletions Tests/Bundle+Ext.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// XmlOptionsTests.swift
// Copyright (c) 2024 BPerlakiH
//
// 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

extension Bundle {
static let testResources: Bundle = {
let bundle = Bundle(for: BundleFinder.self)
if let moduleName = bundle.bundleIdentifier,
let baseURL = bundle.resourceURL,
let testBundlePath = ProcessInfo.processInfo.environment["XCTestBundlePath"] {
if let resourceBundle = Bundle(path: baseURL.appending(path: "/\(moduleName)_\(moduleName).bundle").absoluteString) {
return resourceBundle
}
}
return Bundle.module
}()

private final class BundleFinder {}
}
2 changes: 1 addition & 1 deletion Tests/DefaultNamespaceXPathTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class DefaultNamespaceXPathTests: XCTestCase {
var document: Fuzi.XMLDocument!
override func setUp() {
super.setUp()
let filePath = Bundle(for: DefaultNamespaceXPathTests.self).url(forResource: "ocf", withExtension: "xml")!
let filePath = Bundle.testResources.url(forResource: "ocf", withExtension: "xml")!
do {
document = try XMLDocument(data: Data(contentsOf: filePath))
} catch {
Expand Down
2 changes: 1 addition & 1 deletion Tests/HTMLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class HTMLTests: XCTestCase {
var document: HTMLDocument!
override func setUp() {
super.setUp()
let filePath = Bundle(for: HTMLTests.self).url(forResource: "web", withExtension: "html")!
let filePath = Bundle.testResources.url(forResource: "web", withExtension: "html")!
do {
document = try HTMLDocument(data: Data(contentsOf: filePath))
} catch {
Expand Down
40 changes: 40 additions & 0 deletions Tests/OptionsTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// XmlOptionsTests.swift
// Copyright (c) 2024 BPerlakiH
//
// 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 XCTest
import Fuzi
import libxml2

final class XmlOptionsTests: XCTestCase {

func testXmlParseOptionValues() {
XCTAssertEqual(XML_PARSE_NOWARNING.rawValue, XmlParseOptions.noWarning.rawValue)
XCTAssertEqual(XML_PARSE_NOERROR.rawValue, XmlParseOptions.noError.rawValue)
XCTAssertEqual(XML_PARSE_RECOVER.rawValue, XmlParseOptions.recover.rawValue)
}

func testXmlParseOptionSet() {
let defaultOptions = UInt32(XML_PARSE_NOWARNING.rawValue | XML_PARSE_NOERROR.rawValue | XML_PARSE_RECOVER.rawValue)
let options: XmlParseOptions = [.noWarning, .noError, .recover]
XCTAssertEqual(options.rawValue, defaultOptions)
XCTAssertEqual(XmlParseOptions.defaultOptions.rawValue, defaultOptions)
}
}
2 changes: 1 addition & 1 deletion Tests/VMAPTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class VMAPTests: XCTestCase {
var document: Fuzi.XMLDocument!
override func setUp() {
super.setUp()
let filePath = Bundle(for: VMAPTests.self).url(forResource: "vmap", withExtension: "xml")!
let filePath = Bundle.testResources.url(forResource: "vmap", withExtension: "xml")!
do {
document = try XMLDocument(data: Data(contentsOf: filePath))
} catch {
Expand Down
8 changes: 4 additions & 4 deletions Tests/XMLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class XMLTests: XCTestCase {
var document: Fuzi.XMLDocument!
override func setUp() {
super.setUp()
let filePath = Bundle(for: XMLTests.self).url(forResource: "xml", withExtension: "xml")!
let filePath = Bundle.testResources.url(forResource: "xml", withExtension: "xml")!
do {
document = try XMLDocument(data: Data(contentsOf: filePath))
} catch {
Expand Down Expand Up @@ -81,8 +81,8 @@ class XMLTests: XCTestCase {
do {
_ = try document.tryXPath("//*[unknown()]")
XCTAssertFalse(true, "error should have been thrown")
} catch XMLError.libXMLError(code: 1223, message: "Stack usage error") {
} catch XMLError.libXMLError(code: 1209, message: "Unregistered function") {

} catch {
XCTAssertFalse(true, "error type should be libXMLError \(error)")
}
Expand All @@ -91,7 +91,7 @@ class XMLTests: XCTestCase {
func testLineNumber() {
let headerElement = document.root!.firstChild(tag: "header")
XCTAssertNotNil(headerElement, "header element should not be nil")
XCTAssertEqual(headerElement?.lineNumber, 123, "header line number should be correct")
XCTAssertEqual(headerElement?.lineNumber, 120, "header line number should be correct")
}

func testThrowsError() {
Expand Down
2 changes: 1 addition & 1 deletion Tests/XPathFunctionResultTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class XPathFunctionResultTests: XCTestCase {
var document: Fuzi.XMLDocument!
override func setUp() {
super.setUp()
let filePath = Bundle(for: AtomTests.self).url(forResource: "atom", withExtension: "xml")!
let filePath = Bundle.testResources.url(forResource: "atom", withExtension: "xml")!
do {
document = try XMLDocument(data: Data(contentsOf: filePath))
} catch {
Expand Down