blob: f0ff81317dc06af6bcebf93481b427a969a28747 [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 TestNSJSONSerialization : XCTestCase {
let supportedEncodings: [String.Encoding] = [
.utf8,
.utf16, .utf16BigEndian,
.utf32LittleEndian, .utf32BigEndian
]
static var allTests: [(String, (TestNSJSONSerialization) -> () throws -> Void)] {
return JSONObjectWithDataTests
+ deserializationTests
+ isValidJSONObjectTests
+ serializationTests
}
}
//MARK: - JSONObjectWithData
extension TestNSJSONSerialization {
class var JSONObjectWithDataTests: [(String, (TestNSJSONSerialization) -> () throws -> Void)] {
return [
("test_JSONObjectWithData_emptyObject", test_JSONObjectWithData_emptyObject),
("test_JSONObjectWithData_encodingDetection", test_JSONObjectWithData_encodingDetection),
]
}
func test_JSONObjectWithData_emptyObject() {
let subject = Data(bytes: UnsafeRawPointer([UInt8]([0x7B, 0x7D])), count: 2)
let object = try! JSONSerialization.jsonObject(with: subject, options: []) as? [String:Any]
XCTAssertEqual(object?.count, 0)
}
//MARK: - Encoding Detection
func test_JSONObjectWithData_encodingDetection() {
let subjects: [(String, [UInt8])] = [
// BOM Detection
("{} UTF-8 w/BOM", [0xEF, 0xBB, 0xBF, 0x7B, 0x7D]),
("{} UTF-16BE w/BOM", [0xFE, 0xFF, 0x0, 0x7B, 0x0, 0x7D]),
("{} UTF-16LE w/BOM", [0xFF, 0xFE, 0x7B, 0x0, 0x7D, 0x0]),
("{} UTF-32BE w/BOM", [0x00, 0x00, 0xFE, 0xFF, 0x0, 0x0, 0x0, 0x7B, 0x0, 0x0, 0x0, 0x7D]),
("{} UTF-32LE w/BOM", [0xFF, 0xFE, 0x00, 0x00, 0x7B, 0x0, 0x0, 0x0, 0x7D, 0x0, 0x0, 0x0]),
// RFC4627 Detection
("{} UTF-8", [0x7B, 0x7D]),
("{} UTF-16BE", [0x0, 0x7B, 0x0, 0x7D]),
("{} UTF-16LE", [0x7B, 0x0, 0x7D, 0x0]),
("{} UTF-32BE", [0x0, 0x0, 0x0, 0x7B, 0x0, 0x0, 0x0, 0x7D]),
("{} UTF-32LE", [0x7B, 0x0, 0x0, 0x0, 0x7D, 0x0, 0x0, 0x0]),
// // Single Characters
// ("'3' UTF-8", [0x33]),
// ("'3' UTF-16BE", [0x0, 0x33]),
// ("'3' UTF-16LE", [0x33, 0x0]),
]
for (description, encoded) in subjects {
let result = try? JSONSerialization.jsonObject(with: Data(bytes:UnsafeRawPointer(encoded), count: encoded.count), options: [])
XCTAssertNotNil(result, description)
}
}
}
//MARK: - JSONDeserialization
extension TestNSJSONSerialization {
class var deserializationTests: [(String, (TestNSJSONSerialization) -> () throws -> Void)] {
return [
("test_deserialize_emptyObject", test_deserialize_emptyObject),
("test_deserialize_multiStringObject", test_deserialize_multiStringObject),
("test_deserialize_emptyArray", test_deserialize_emptyArray),
("test_deserialize_multiStringArray", test_deserialize_multiStringArray),
("test_deserialize_unicodeString", test_deserialize_unicodeString),
("test_deserialize_stringWithSpacesAtStart", test_deserialize_stringWithSpacesAtStart),
("test_deserialize_values", test_deserialize_values),
("test_deserialize_numbers", test_deserialize_numbers),
("test_deserialize_simpleEscapeSequences", test_deserialize_simpleEscapeSequences),
("test_deserialize_unicodeEscapeSequence", test_deserialize_unicodeEscapeSequence),
("test_deserialize_unicodeSurrogatePairEscapeSequence", test_deserialize_unicodeSurrogatePairEscapeSequence),
// Disabled due to uninitialized memory SR-606
// ("test_deserialize_allowFragments", test_deserialize_allowFragments),
("test_deserialize_unterminatedObjectString", test_deserialize_unterminatedObjectString),
("test_deserialize_missingObjectKey", test_deserialize_missingObjectKey),
("test_deserialize_unexpectedEndOfFile", test_deserialize_unexpectedEndOfFile),
("test_deserialize_invalidValueInObject", test_deserialize_invalidValueInObject),
("test_deserialize_invalidValueIncorrectSeparatorInObject", test_deserialize_invalidValueIncorrectSeparatorInObject),
("test_deserialize_invalidValueInArray", test_deserialize_invalidValueInArray),
("test_deserialize_badlyFormedArray", test_deserialize_badlyFormedArray),
("test_deserialize_invalidEscapeSequence", test_deserialize_invalidEscapeSequence),
("test_deserialize_unicodeMissingTrailingSurrogate", test_deserialize_unicodeMissingTrailingSurrogate),
]
}
//MARK: - Object Deserialization
func test_deserialize_emptyObject() {
let subject = "{}"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let t = try JSONSerialization.jsonObject(with: data, options: [])
let result = t as? [String: Any]
XCTAssertEqual(result?.count, 0)
} catch {
XCTFail("Error thrown: \(error)")
}
}
func test_deserialize_multiStringObject() {
let subject = "{ \"hello\": \"world\", \"swift\": \"rocks\" }"
do {
for encoding in [String.Encoding.utf8, String.Encoding.utf16BigEndian] {
guard let data = subject.data(using: encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
XCTAssertEqual(result?["hello"] as? String, "world")
XCTAssertEqual(result?["swift"] as? String, "rocks")
}
} catch {
XCTFail("Error thrown: \(error)")
}
}
func test_deserialize_stringWithSpacesAtStart(){
let subject = "{\"title\" : \" hello world!!\" }"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let result = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
XCTAssertEqual(result?["title"] as? String, " hello world!!")
} catch{
XCTFail("Error thrown: \(error)")
}
}
//MARK: - Array Deserialization
func test_deserialize_emptyArray() {
let subject = "[]"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let result = try JSONSerialization.jsonObject(with: data, options: []) as? [Any]
XCTAssertEqual(result?.count, 0)
} catch {
XCTFail("Unexpected error: \(error)")
}
}
func test_deserialize_multiStringArray() {
let subject = "[\"hello\", \"swift⚡️\"]"
do {
for encoding in [String.Encoding.utf8, String.Encoding.utf16BigEndian] {
guard let data = subject.data(using: encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try JSONSerialization.jsonObject(with: data, options: []) as? [Any]
XCTAssertEqual(result?[0] as? String, "hello")
XCTAssertEqual(result?[1] as? String, "swift⚡️")
}
} catch {
XCTFail("Unexpected error: \(error)")
}
}
func test_deserialize_unicodeString() {
/// Ģ has the same LSB as quotation mark " (U+0022) so test guarding against this case
let subject = "[\"unicode\", \"Ģ\", \"😢\"]"
do {
for encoding in [String.Encoding.utf16LittleEndian, String.Encoding.utf16BigEndian, String.Encoding.utf32LittleEndian, String.Encoding.utf32BigEndian] {
guard let data = subject.data(using: encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try JSONSerialization.jsonObject(with: data, options: []) as? [Any]
XCTAssertEqual(result?[0] as? String, "unicode")
XCTAssertEqual(result?[1] as? String, "Ģ")
XCTAssertEqual(result?[2] as? String, "😢")
}
} catch {
XCTFail("Unexpected error: \(error)")
}
}
//MARK: - Value parsing
func test_deserialize_values() {
let subject = "[true, false, \"hello\", null, {}, []]"
do {
for encoding in supportedEncodings {
guard let data = subject.data(using: encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try JSONSerialization.jsonObject(with: data, options: []) as? [Any]
XCTAssertEqual(result?[0] as? Bool, true)
XCTAssertEqual(result?[1] as? Bool, false)
XCTAssertEqual(result?[2] as? String, "hello")
XCTAssertNotNil(result?[3] as? NSNull)
XCTAssertNotNil(result?[4] as? [String:Any])
XCTAssertNotNil(result?[5] as? [Any])
}
} catch {
XCTFail("Unexpected error: \(error)")
}
}
//MARK: - Number parsing
func test_deserialize_numbers() {
let subject = "[1, -1, 1.3, -1.3, 1e3, 1E-3]"
do {
for encoding in supportedEncodings {
guard let data = subject.data(using: encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try JSONSerialization.jsonObject(with: data, options: []) as? [Any]
XCTAssertEqual(result?[0] as? Int, 1)
XCTAssertEqual(result?[1] as? Int, -1)
XCTAssertEqual(result?[2] as? Double, 1.3)
XCTAssertEqual(result?[3] as? Double, -1.3)
XCTAssertEqual(result?[4] as? Double, 1000)
XCTAssertEqual(result?[5] as? Double, 0.001)
}
} catch {
XCTFail("Unexpected error: \(error)")
}
}
//MARK: - Escape Sequences
func test_deserialize_simpleEscapeSequences() {
let subject = "[\"\\\"\", \"\\\\\", \"\\/\", \"\\b\", \"\\f\", \"\\n\", \"\\r\", \"\\t\"]"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let res = try JSONSerialization.jsonObject(with: data, options: []) as? [Any]
let result = res?.flatMap { $0 as? String }
XCTAssertEqual(result?[0], "\"")
XCTAssertEqual(result?[1], "\\")
XCTAssertEqual(result?[2], "/")
XCTAssertEqual(result?[3], "\u{08}")
XCTAssertEqual(result?[4], "\u{0C}")
XCTAssertEqual(result?[5], "\u{0A}")
XCTAssertEqual(result?[6], "\u{0D}")
XCTAssertEqual(result?[7], "\u{09}")
} catch {
XCTFail("Unexpected error: \(error)")
}
}
func test_deserialize_unicodeEscapeSequence() {
let subject = "[\"\\u2728\"]"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let result = try JSONSerialization.jsonObject(with: data, options: []) as? [Any]
// result?[0] as? String returns an Optional<String> and RHS is promoted
// to Optional<String>
XCTAssertEqual(result?[0] as? String, "✨")
} catch {
XCTFail("Unexpected error: \(error)")
}
}
func test_deserialize_unicodeSurrogatePairEscapeSequence() {
let subject = "[\"\\uD834\\udd1E\"]"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let result = try JSONSerialization.jsonObject(with: data, options: []) as? [Any]
// result?[0] as? String returns an Optional<String> and RHS is promoted
// to Optional<String>
XCTAssertEqual(result?[0] as? String, "\u{1D11E}")
} catch {
XCTFail("Unexpected error: \(error)")
}
}
func test_deserialize_allowFragments() {
let subject = "3"
do {
for encoding in supportedEncodings {
guard let data = subject.data(using: encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? Int
XCTAssertEqual(result, 3)
}
} catch {
XCTFail("Unexpected error: \(error)")
}
}
//MARK: - Parsing Errors
func test_deserialize_unterminatedObjectString() {
let subject = "{\"}"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let _ = try JSONSerialization.jsonObject(with: data, options: [])
XCTFail("Expected error: UnterminatedString")
} catch {
// Passing case; the object as unterminated
}
}
func test_deserialize_missingObjectKey() {
let subject = "{3}"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let _ = try JSONSerialization.jsonObject(with: data, options: [])
XCTFail("Expected error: Missing key for value")
} catch {
// Passing case; the key was missing for a value
}
}
func test_deserialize_unexpectedEndOfFile() {
let subject = "{"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let _ = try JSONSerialization.jsonObject(with: data, options: [])
XCTFail("Expected error: Unexpected end of file")
} catch {
// Success
}
}
func test_deserialize_invalidValueInObject() {
let subject = "{\"error\":}"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let _ = try JSONSerialization.jsonObject(with: data, options: [])
XCTFail("Expected error: Invalid value")
} catch {
// Passing case; the value is invalid
}
}
func test_deserialize_invalidValueIncorrectSeparatorInObject() {
let subject = "{\"missing\";}"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let _ = try JSONSerialization.jsonObject(with: data, options: [])
XCTFail("Expected error: Invalid value")
} catch {
// passing case the value is invalid
}
}
func test_deserialize_invalidValueInArray() {
let subject = "[,"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let _ = try JSONSerialization.jsonObject(with: data, options: [])
XCTFail("Expected error: Invalid value")
} catch {
// Passing case; the element in the array is missing
}
}
func test_deserialize_badlyFormedArray() {
let subject = "[2b4]"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let _ = try JSONSerialization.jsonObject(with: data, options: [])
XCTFail("Expected error: Badly formed array")
} catch {
// Passing case; the array is malformed
}
}
func test_deserialize_invalidEscapeSequence() {
let subject = "[\"\\e\"]"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let _ = try JSONSerialization.jsonObject(with: data, options: [])
XCTFail("Expected error: Invalid escape sequence")
} catch {
// Passing case; the escape sequence is invalid
}
}
func test_deserialize_unicodeMissingTrailingSurrogate() {
let subject = "[\"\\uD834\"]"
do {
guard let data = subject.data(using: .utf8) else {
XCTFail("Unable to convert string to data")
return
}
let _ = try JSONSerialization.jsonObject(with: data, options: []) as? [String]
XCTFail("Expected error: Missing Trailing Surrogate")
} catch {
// Passing case; the unicode character is malformed
}
}
}
// MARK: - isValidJSONObjectTests
extension TestNSJSONSerialization {
class var isValidJSONObjectTests: [(String, (TestNSJSONSerialization) -> () throws -> Void)] {
return [
("test_isValidJSONObjectTrue", test_isValidJSONObjectTrue),
("test_isValidJSONObjectFalse", test_isValidJSONObjectFalse),
]
}
func test_isValidJSONObjectTrue() {
let trueJSON: [Any] = [
// []
Array<Any>(),
// [1, ["string", [[]]]]
Array<Any>(arrayLiteral:
NSNumber(value: Int(1)),
Array<Any>(arrayLiteral:
"string",
Array<Any>(arrayLiteral:
Array<Any>()
)
)
),
// [NSNull(), ["1" : ["string", 1], "2" : NSNull()]]
Array<Any>(arrayLiteral:
NSNull(),
Dictionary<String, Any>(dictionaryLiteral:
(
"1",
Array<Any>(arrayLiteral:
"string",
NSNumber(value: Int(1))
)
),
(
"2",
NSNull()
)
)
),
// ["0" : 0]
Dictionary<String, Any>(dictionaryLiteral:
(
"0",
NSNumber(value: Int(0))
)
)
]
for testCase in trueJSON {
XCTAssertTrue(JSONSerialization.isValidJSONObject(testCase))
}
}
func test_isValidJSONObjectFalse() {
var falseJSON = [Any]()
falseJSON.append(NSNumber(value: Int(0)))
falseJSON.append(NSNull())
falseJSON.append("string")
falseJSON.append(Array<Any>(arrayLiteral:
NSNumber(value: Int(1)),
NSNumber(value: Int(2)),
NSNumber(value: Int(3)),
Dictionary<NSNumber, Any>(dictionaryLiteral:
(
NSNumber(value: Int(4)),
NSNumber(value: Int(5))
)
)
))
let one = NSNumber(value: Int(1))
let two = NSNumber(value: Int(2))
let divo = NSNumber(value: Double(1) / Double(0))
falseJSON.append([one, two, divo])
falseJSON.append([NSNull() : NSNumber(value: Int(1))])
falseJSON.append(Array<Any>(arrayLiteral:
Array<Any>(arrayLiteral:
Array<Any>(arrayLiteral:
Dictionary<NSNumber, Any>(dictionaryLiteral:
(
NSNumber(value: Int(1)),
NSNumber(value: Int(2))
)
)
)
)
))
for testCase in falseJSON {
XCTAssertFalse(JSONSerialization.isValidJSONObject(testCase))
}
}
}
// MARK: - serializationTests
extension TestNSJSONSerialization {
class var serializationTests: [(String, (TestNSJSONSerialization) -> () throws -> Void)] {
return [
("test_serialize_emptyObject", test_serialize_emptyObject),
("test_serialize_null", test_serialize_null),
("test_serialize_complexObject", test_serialize_complexObject),
("test_nested_array", test_nested_array),
("test_nested_dictionary", test_nested_dictionary),
("test_serialize_number", test_serialize_number),
("test_serialize_stringEscaping", test_serialize_stringEscaping),
("test_serialize_invalid_json", test_serialize_invalid_json),
("test_jsonReadingOffTheEndOfBuffers", test_jsonReadingOffTheEndOfBuffers),
("test_jsonObjectToOutputStreamBuffer", test_jsonObjectToOutputStreamBuffer),
("test_jsonObjectToOutputStreamFile", test_jsonObjectToOutputStreamFile),
("test_invalidJsonObjectToStreamBuffer", test_invalidJsonObjectToStreamBuffer),
("test_jsonObjectToOutputStreamInsufficeintBuffer", test_jsonObjectToOutputStreamInsufficeintBuffer),
]
}
func trySerialize(_ obj: AnyObject) throws -> String {
let data = try JSONSerialization.data(withJSONObject: obj, options: [])
guard let string = String(data: data, encoding: .utf8) else {
XCTFail("Unable to create string")
return ""
}
return string
}
func test_serialize_emptyObject() {
let dict1 = [String: Any]().bridge()
XCTAssertEqual(try trySerialize(dict1), "{}")
let dict2 = [String: NSNumber]().bridge()
XCTAssertEqual(try trySerialize(dict2), "{}")
let dict3 = [String: String]().bridge()
XCTAssertEqual(try trySerialize(dict3), "{}")
let array1 = [String]().bridge()
XCTAssertEqual(try trySerialize(array1), "[]")
let array2 = [NSNumber]().bridge()
XCTAssertEqual(try trySerialize(array2), "[]")
}
func test_serialize_null() {
let arr = [NSNull()].bridge()
XCTAssertEqual(try trySerialize(arr), "[null]")
let dict = ["a":NSNull()].bridge()
XCTAssertEqual(try trySerialize(dict), "{\"a\":null}")
let arr2 = [NSNull(), NSNull(), NSNull()].bridge()
XCTAssertEqual(try trySerialize(arr2), "[null,null,null]")
let dict2 = [["a":NSNull()], ["b":NSNull()], ["c":NSNull()]].bridge()
XCTAssertEqual(try trySerialize(dict2), "[{\"a\":null},{\"b\":null},{\"c\":null}]")
}
func test_serialize_complexObject() {
let jsonDict = ["a": 4].bridge()
XCTAssertEqual(try trySerialize(jsonDict), "{\"a\":4}")
let jsonArr = [1, 2, 3, 4].bridge()
XCTAssertEqual(try trySerialize(jsonArr), "[1,2,3,4]")
let jsonDict2 = ["a": [1,2]].bridge()
XCTAssertEqual(try trySerialize(jsonDict2), "{\"a\":[1,2]}")
let jsonArr2 = ["a", "b", "c"].bridge()
XCTAssertEqual(try trySerialize(jsonArr2), "[\"a\",\"b\",\"c\"]")
let jsonArr3 = [["a":1],["b":2]].bridge()
XCTAssertEqual(try trySerialize(jsonArr3), "[{\"a\":1},{\"b\":2}]")
let jsonArr4 = [["a":NSNull()],["b":NSNull()]].bridge()
XCTAssertEqual(try trySerialize(jsonArr4), "[{\"a\":null},{\"b\":null}]")
}
func test_nested_array() {
var arr = ["a"].bridge()
XCTAssertEqual(try trySerialize(arr), "[\"a\"]")
arr = [["b"]].bridge()
XCTAssertEqual(try trySerialize(arr), "[[\"b\"]]")
arr = [[["c"]]].bridge()
XCTAssertEqual(try trySerialize(arr), "[[[\"c\"]]]")
arr = [[[["d"]]]].bridge()
XCTAssertEqual(try trySerialize(arr), "[[[[\"d\"]]]]")
}
func test_nested_dictionary() {
var dict = ["a":1].bridge()
XCTAssertEqual(try trySerialize(dict), "{\"a\":1}")
dict = ["a":["b":1]].bridge()
XCTAssertEqual(try trySerialize(dict), "{\"a\":{\"b\":1}}")
dict = ["a":["b":["c":1]]].bridge()
XCTAssertEqual(try trySerialize(dict), "{\"a\":{\"b\":{\"c\":1}}}")
dict = ["a":["b":["c":["d":1]]]].bridge()
XCTAssertEqual(try trySerialize(dict), "{\"a\":{\"b\":{\"c\":{\"d\":1}}}}")
}
func test_serialize_number() {
var json = [1, 1.1, 0, -2].bridge()
XCTAssertEqual(try trySerialize(json), "[1,1.1,0,-2]")
// Cannot generate "true"/"false" currently
json = [NSNumber(value:false),NSNumber(value:true)].bridge()
XCTAssertEqual(try trySerialize(json), "[0,1]")
}
func test_serialize_stringEscaping() {
var json = ["foo"].bridge()
XCTAssertEqual(try trySerialize(json), "[\"foo\"]")
json = ["a\0"].bridge()
XCTAssertEqual(try trySerialize(json), "[\"a\\u0000\"]")
json = ["b\\"].bridge()
XCTAssertEqual(try trySerialize(json), "[\"b\\\\\"]")
json = ["c\t"].bridge()
XCTAssertEqual(try trySerialize(json), "[\"c\\t\"]")
json = ["d\n"].bridge()
XCTAssertEqual(try trySerialize(json), "[\"d\\n\"]")
json = ["e\r"].bridge()
XCTAssertEqual(try trySerialize(json), "[\"e\\r\"]")
json = ["f\""].bridge()
XCTAssertEqual(try trySerialize(json), "[\"f\\\"\"]")
json = ["g\'"].bridge()
XCTAssertEqual(try trySerialize(json), "[\"g\'\"]")
json = ["h\u{7}"].bridge()
XCTAssertEqual(try trySerialize(json), "[\"h\\u0007\"]")
json = ["i\u{1f}"].bridge()
XCTAssertEqual(try trySerialize(json), "[\"i\\u001f\"]")
}
func test_serialize_invalid_json() {
let str = "Invalid JSON".bridge()
do {
let _ = try trySerialize(str)
XCTFail("Top-level JSON object cannot be string")
} catch {
// should get here
}
let double = NSNumber(value: Double(1.2))
do {
let _ = try trySerialize(double)
XCTFail("Top-level JSON object cannot be double")
} catch {
// should get here
}
let dict = [NSNumber(value: Double(1.2)):"a"].bridge()
do {
let _ = try trySerialize(dict)
XCTFail("Dictionary keys must be strings")
} catch {
// should get here
}
}
func test_jsonReadingOffTheEndOfBuffers() {
let data = "12345679".data(using: .utf8)!
do {
let res = try data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Any in
let slice = Data(bytesNoCopy: UnsafeMutablePointer(mutating: bytes), count: 1, deallocator: .none)
return try JSONSerialization.jsonObject(with: slice, options: .allowFragments)
}
if let num = res as? Int {
XCTAssertEqual(1, num) // the slice truncation should only parse 1 byte!
} else {
XCTFail("expected an integer but got a \(res)")
}
} catch {
XCTFail("Unknow json decoding failure")
}
}
func test_jsonObjectToOutputStreamBuffer(){
let dict = ["a":["b":1]]
do {
let buffer = Array<UInt8>(repeating: 0, count: 20)
let outputStream = NSOutputStream(toBuffer: UnsafeMutablePointer(mutating: buffer), capacity: 20)
outputStream.open()
let result = try JSONSerialization.writeJSONObject(dict.bridge(), toStream: outputStream, options: [])
outputStream.close()
if(result > -1) {
XCTAssertEqual(NSString(bytes: buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue), "{\"a\":{\"b\":1}}")
}
} catch {
XCTFail("Error thrown: \(error)")
}
}
func test_jsonObjectToOutputStreamFile() {
let dict = ["a":["b":1]]
do {
let filePath = createTestFile("TestFileOut.txt",_contents: Data(capacity: 128)!)
if filePath != nil {
let outputStream = NSOutputStream(toFileAtPath: filePath!, append: true)
outputStream?.open()
let result = try JSONSerialization.writeJSONObject(dict.bridge(), toStream: outputStream!, options: [])
outputStream?.close()
if(result > -1) {
let fileStream: InputStream = InputStream(fileAtPath: filePath!)!
var buffer = [UInt8](repeating: 0, count: 20)
fileStream.open()
if fileStream.hasBytesAvailable {
let resultRead: Int = fileStream.read(&buffer, maxLength: buffer.count)
fileStream.close()
if(resultRead > -1){
XCTAssertEqual(NSString(bytes: buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue), "{\"a\":{\"b\":1}}")
}
}
removeTestFile(filePath!)
} else {
XCTFail("Unable to create temp file")
}
}
} catch {
XCTFail("Error thrown: \(error)")
}
}
func test_jsonObjectToOutputStreamInsufficeintBuffer() {
let dict = ["a":["b":1]]
let buffer = Array<UInt8>(repeating: 0, count: 10)
let outputStream = NSOutputStream(toBuffer: UnsafeMutablePointer(mutating: buffer), capacity: 20)
outputStream.open()
do {
let result = try JSONSerialization.writeJSONObject(dict.bridge(), toStream: outputStream, options: [])
outputStream.close()
if(result > -1) {
XCTAssertNotEqual(NSString(bytes: buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue), "{\"a\":{\"b\":1}}")
}
} catch {
XCTFail("Error occurred while writing to stream")
}
}
func test_invalidJsonObjectToStreamBuffer() {
let str = "Invalid JSON"
let buffer = Array<UInt8>(repeating: 0, count: 10)
let outputStream = NSOutputStream(toBuffer: UnsafeMutablePointer(mutating: buffer), capacity: 20)
outputStream.open()
XCTAssertThrowsError(try JSONSerialization.writeJSONObject(str.bridge(), toStream: outputStream, options: []))
}
private func createTestFile(_ path: String,_contents: Data) -> String? {
let tempDir = "/tmp/TestFoundation_Playground_" + NSUUID().uuidString + "/"
do {
try FileManager.default.createDirectory(atPath: tempDir, withIntermediateDirectories: false, attributes: nil)
if FileManager.default.createFile(atPath: tempDir + "/" + path, contents: _contents,
attributes: nil) {
return tempDir + path
} else {
return nil
}
} catch _ {
return nil
}
}
private func removeTestFile(_ location: String) {
do {
try FileManager.default.removeItem(atPath: location)
} catch _ {
}
}
}