blob: 4ccc05eea78d3330353f99838199133ebc118c2c [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
public class NSUserClass : NSObject, NSSecureCoding {
var ivar : Int
public class var supportsSecureCoding: Bool {
return true
}
public func encode(with aCoder : NSCoder) {
aCoder.encode(ivar, forKey:"$ivar") // also test escaping
}
init(_ value: Int) {
self.ivar = value
}
public required init?(coder aDecoder: NSCoder) {
self.ivar = aDecoder.decodeInteger(forKey: "$ivar")
}
public override var description: String {
get {
return "NSUserClass \(ivar)"
}
}
public override func isEqual(_ object: Any?) -> Bool {
if let custom = object as? NSUserClass {
return self.ivar == custom.ivar
} else {
return false
}
}
}
public class UserClass : CustomStringConvertible, Equatable, Hashable, NSSecureCoding {
var ivar : Int
public class var supportsSecureCoding: Bool {
return true
}
public func encode(with aCoder : NSCoder) {
aCoder.encode(ivar, forKey:"$ivar") // also test escaping
}
init(_ value: Int) {
self.ivar = value
}
public required init?(coder aDecoder: NSCoder) {
self.ivar = aDecoder.decodeInteger(forKey: "$ivar")
}
public var description: String {
get {
return "UserClass \(ivar)"
}
}
public static func ==(lhs: UserClass, rhs: UserClass) -> Bool {
return lhs.ivar == rhs.ivar
}
public var hashValue: Int {
return ivar
}
}
class TestNSKeyedArchiver : XCTestCase {
static var allTests: [(String, (TestNSKeyedArchiver) -> () throws -> Void)] {
return [
("test_archive_array", test_archive_array),
("test_archive_charptr", test_archive_charptr),
("test_archive_concrete_value", test_archive_concrete_value),
("test_archive_dictionary", test_archive_dictionary),
("test_archive_generic_objc", test_archive_generic_objc),
("test_archive_locale", test_archive_locale),
("test_archive_string", test_archive_string),
("test_archive_mutable_array", test_archive_mutable_array),
("test_archive_mutable_dictionary", test_archive_mutable_dictionary),
("test_archive_ns_user_class", test_archive_ns_user_class),
("test_archive_nspoint", test_archive_nspoint),
("test_archive_nsrange", test_archive_nsrange),
("test_archive_nsrect", test_archive_nsrect),
("test_archive_null", test_archive_null),
("test_archive_set", test_archive_set),
("test_archive_url", test_archive_url),
("test_archive_user_class", test_archive_user_class),
("test_archive_uuid_bvref", test_archive_uuid_byref),
("test_archive_uuid_byvalue", test_archive_uuid_byvalue),
("test_archive_unhashable", test_archive_unhashable),
]
}
private func test_archive(_ encode: (NSKeyedArchiver) -> Bool,
decode: (NSKeyedUnarchiver) -> Bool) {
// Archiving using custom NSMutableData instance
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWith: data)
XCTAssertTrue(encode(archiver))
archiver.finishEncoding()
let unarchiver = NSKeyedUnarchiver(forReadingWith: Data._unconditionallyBridgeFromObjectiveC(data))
XCTAssertTrue(decode(unarchiver))
// Archiving using the default initializer
let archiver1 = NSKeyedArchiver()
XCTAssertTrue(encode(archiver1))
let archivedData = archiver1.encodedData
let unarchiver1 = NSKeyedUnarchiver(forReadingWith: archivedData)
XCTAssertTrue(decode(unarchiver1))
}
private func test_archive(_ object: Any, classes: [AnyClass], allowsSecureCoding: Bool = true, outputFormat: PropertyListSerialization.PropertyListFormat) {
test_archive({ archiver -> Bool in
archiver.requiresSecureCoding = allowsSecureCoding
archiver.outputFormat = outputFormat
archiver.encode(object, forKey: NSKeyedArchiveRootObjectKey)
archiver.finishEncoding()
return true
},
decode: { unarchiver -> Bool in
unarchiver.requiresSecureCoding = allowsSecureCoding
do {
guard let rootObj = try unarchiver.decodeTopLevelObject(of: classes, forKey: NSKeyedArchiveRootObjectKey) else {
XCTFail("Unable to decode data")
return false
}
XCTAssertEqual(object as? AnyHashable, rootObj as? AnyHashable, "unarchived object \(rootObj) does not match \(object)")
} catch {
XCTFail("Error thrown: \(error)")
}
return true
})
}
private func test_archive(_ object: Any, classes: [AnyClass], allowsSecureCoding: Bool = true) {
// test both XML and binary encodings
test_archive(object, classes: classes, allowsSecureCoding: allowsSecureCoding, outputFormat: PropertyListSerialization.PropertyListFormat.xml)
test_archive(object, classes: classes, allowsSecureCoding: allowsSecureCoding, outputFormat: PropertyListSerialization.PropertyListFormat.binary)
}
private func test_archive(_ object: AnyObject, allowsSecureCoding: Bool = true) {
return test_archive(object, classes: [type(of: object)], allowsSecureCoding: allowsSecureCoding)
}
func test_archive_array() {
let array = NSArray(array: ["one", "two", "three"])
test_archive(array)
}
func test_archive_concrete_value() {
let array: Array<UInt64> = [12341234123, 23452345234, 23475982345, 9893563243, 13469816598]
let objctype = "[5Q]"
array.withUnsafeBufferPointer { cArray in
let concrete = NSValue(bytes: cArray.baseAddress!, objCType: objctype)
test_archive(concrete)
}
}
func test_archive_dictionary() {
let dictionary = NSDictionary(dictionary: ["one" : 1, "two" : 2, "three" : 3])
test_archive(dictionary)
}
func test_archive_generic_objc() {
let array: Array<Int32> = [1234, 2345, 3456, 10000]
test_archive({ archiver -> Bool in
array.withUnsafeBufferPointer { cArray in
archiver.encodeValue(ofObjCType: "[4i]", at: cArray.baseAddress!)
}
return true
},
decode: {unarchiver -> Bool in
var expected: Array<Int32> = [0, 0, 0, 0]
expected.withUnsafeMutableBufferPointer {(p: inout UnsafeMutableBufferPointer<Int32>) in
unarchiver.decodeValue(ofObjCType: "[4i]", at: UnsafeMutableRawPointer(p.baseAddress!))
}
XCTAssertEqual(expected, array)
return true
})
}
func test_archive_locale() {
let locale = Locale.current
test_archive(locale._bridgeToObjectiveC())
}
func test_archive_string() {
let string = NSString(string: "hello")
test_archive(string)
}
func test_archive_mutable_array() {
let array = NSMutableArray(array: ["one", "two", "three"])
test_archive(array)
}
func test_archive_mutable_dictionary() {
let mdictionary = NSMutableDictionary(dictionary: [
"one": NSNumber(value: Int(1)),
"two": NSNumber(value: Int(2)),
"three": NSNumber(value: Int(3)),
])
test_archive(mdictionary)
}
func test_archive_nspoint() {
let point = NSValue(point: NSPoint(x: CGFloat(20.0), y: CGFloat(35.0)))
test_archive(point)
}
func test_archive_nsrange() {
let range = NSValue(range: NSMakeRange(1234, 5678))
test_archive(range)
}
func test_archive_nsrect() {
let point = NSPoint(x: CGFloat(20.0), y: CGFloat(35.4))
let size = NSSize(width: CGFloat(50.0), height: CGFloat(155.0))
let rect = NSValue(rect: NSRect(origin: point, size: size))
test_archive(rect)
}
func test_archive_null() {
let null = NSNull()
test_archive(null)
}
func test_archive_set() {
let set = NSSet(array: [NSNumber(value: Int(1234234)),
NSNumber(value: Int(2374853)),
NSString(string: "foobarbarbar"),
NSValue(point: NSPoint(x: CGFloat(5.0), y: CGFloat(Double(1.5))))])
test_archive(set, classes: [NSValue.self, NSSet.self])
}
func test_archive_url() {
let url = NSURL(string: "index.html", relativeTo: URL(string: "http://www.apple.com"))!
test_archive(url)
}
func test_archive_charptr() {
let charArray = [CChar]("Hello world, we are testing!\0".utf8CString)
var charPtr = UnsafeMutablePointer(mutating: charArray)
test_archive({ archiver -> Bool in
let value = NSValue(bytes: &charPtr, objCType: "*")
archiver.encode(value, forKey: "root")
return true
},
decode: {unarchiver -> Bool in
guard let value = unarchiver.decodeObject(of: NSValue.self, forKey: "root") else {
return false
}
var expectedCharPtr: UnsafeMutablePointer<CChar>? = nil
value.getValue(&expectedCharPtr)
let s1 = String(cString: charPtr)
let s2 = String(cString: expectedCharPtr!)
// On Darwin decoded strings would belong to the autorelease pool, but as we don't have
// one in SwiftFoundation let's explicitly deallocate it here.
expectedCharPtr!.deallocate(capacity: charArray.count)
return s1 == s2
})
}
func test_archive_user_class() {
let userClass = UserClass(1234)
test_archive(userClass)
}
func test_archive_ns_user_class() {
let nsUserClass = NSUserClass(5678)
test_archive(nsUserClass)
}
func test_archive_uuid_byref() {
let uuid = NSUUID()
test_archive(uuid)
}
func test_archive_uuid_byvalue() {
let uuid = UUID()
return test_archive(uuid, classes: [NSUUID.self])
}
func test_archive_unhashable() {
let data = """
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "deflate, gzip",
"Accept-Language": "en",
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "TestFoundation (unknown version) curl/7.54.0"
},
"origin": "0.0.0.0",
"url": "https://httpbin.org/get"
}
""".data(using: .utf8)!
do {
let json = try JSONSerialization.jsonObject(with: data)
_ = NSKeyedArchiver.archivedData(withRootObject: json)
XCTAssert(true, "NSKeyedArchiver.archivedData handles unhashable")
}
catch {
XCTFail("test_archive_unhashable, de-serialization error \(error)")
}
}
}