blob: 54f346ac6bc2c5252b077f47a6e333077eacb4dc [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 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 = [
NSUTF8StringEncoding,
NSUTF16LittleEndianStringEncoding, NSUTF16BigEndianStringEncoding,
NSUTF32LittleEndianStringEncoding, NSUTF32BigEndianStringEncoding
]
static var allTests: [(String, TestNSJSONSerialization -> () throws -> Void)] {
return JSONObjectWithDataTests
+ deserializationTests
+ isValidJSONObjectTests
}
}
//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 = NSData(bytes: UnsafePointer<Void>([UInt8]([0x7B, 0x7D])), length: 2)
let object = try! NSJSONSerialization.JSONObjectWithData(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? NSJSONSerialization.JSONObjectWithData(NSData(bytes:UnsafePointer<Void>(encoded), length: 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_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.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
let t = try NSJSONSerialization.JSONObjectWithData(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 [NSUTF8StringEncoding, NSUTF16BigEndianStringEncoding] {
guard let data = subject.bridge().dataUsingEncoding(encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: Any]
XCTAssertEqual(result?["hello"] as? String, "world")
XCTAssertEqual(result?["swift"] as? String, "rocks")
}
} catch {
XCTFail("Error thrown: \(error)")
}
}
//MARK: - Array Deserialization
func test_deserialize_emptyArray() {
let subject = "[]"
do {
guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try NSJSONSerialization.JSONObjectWithData(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 [NSUTF8StringEncoding, NSUTF16BigEndianStringEncoding] {
guard let data = subject.bridge().dataUsingEncoding(encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try NSJSONSerialization.JSONObjectWithData(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 [NSUTF16LittleEndianStringEncoding, NSUTF16BigEndianStringEncoding, NSUTF32LittleEndianStringEncoding, NSUTF32BigEndianStringEncoding] {
guard let data = subject.bridge().dataUsingEncoding(encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try NSJSONSerialization.JSONObjectWithData(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.bridge().dataUsingEncoding(encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try NSJSONSerialization.JSONObjectWithData(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.bridge().dataUsingEncoding(encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [Any]
XCTAssertEqual(result?[0] as? Double, 1)
XCTAssertEqual(result?[1] as? Double, -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.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
let res = try NSJSONSerialization.JSONObjectWithData(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.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [Any]
XCTAssertEqual(result?[0] as? String, "✨")
} catch {
XCTFail("Unexpected error: \(error)")
}
}
func test_deserialize_unicodeSurrogatePairEscapeSequence() {
let subject = "[\"\\uD834\\udd1E\"]"
do {
guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [Any]
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.bridge().dataUsingEncoding(encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? Double
XCTAssertEqual(result, 3)
}
} catch {
XCTFail("Unexpected error: \(error)")
}
}
//MARK: - Parsing Errors
func test_deserialize_unterminatedObjectString() {
let subject = "{\"}"
do {
guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
try NSJSONSerialization.JSONObjectWithData(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.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
try NSJSONSerialization.JSONObjectWithData(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.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
try NSJSONSerialization.JSONObjectWithData(data, options: [])
XCTFail("Expected error: Unexpected end of file")
} catch {
// Success
}
}
func test_deserialize_invalidValueInObject() {
let subject = "{\"error\":}"
do {
guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
try NSJSONSerialization.JSONObjectWithData(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.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
try NSJSONSerialization.JSONObjectWithData(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.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
try NSJSONSerialization.JSONObjectWithData(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.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
try NSJSONSerialization.JSONObjectWithData(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.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
try NSJSONSerialization.JSONObjectWithData(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.bridge().dataUsingEncoding(NSUTF8StringEncoding) else {
XCTFail("Unable to convert string to data")
return
}
try NSJSONSerialization.JSONObjectWithData(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(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(int: 1)
)
),
(
"2",
NSNull()
)
)
),
// ["0" : 0]
Dictionary<String, Any>(dictionaryLiteral:
(
"0",
NSNumber(int: 0)
)
)
]
for testCase in trueJSON {
XCTAssertTrue(NSJSONSerialization.isValidJSONObject(testCase))
}
}
func test_isValidJSONObjectFalse() {
let falseJSON: [Any] = [
// 0
NSNumber(int: 0),
// NSNull()
NSNull(),
// "string"
"string",
// [1, 2, 3, [4 : 5]]
Array<Any>(arrayLiteral:
NSNumber(int: 1),
NSNumber(int: 2),
NSNumber(int: 3),
Dictionary<NSNumber, Any>(dictionaryLiteral:
(
NSNumber(int: 4),
NSNumber(int: 5)
)
)
),
// [1, 2, Infinity]
[NSNumber(int: 1), NSNumber(int: 2), NSNumber(double: 1 / 0)],
// [NSNull() : 1]
[NSNull() : NSNumber(int: 1)],
// [[[[1 : 2]]]]
Array<Any>(arrayLiteral:
Array<Any>(arrayLiteral:
Array<Any>(arrayLiteral:
Dictionary<NSNumber, Any>(dictionaryLiteral:
(
NSNumber(int: 1),
NSNumber(int: 2)
)
)
)
)
)
]
for testCase in falseJSON {
XCTAssertFalse(NSJSONSerialization.isValidJSONObject(testCase))
}
}
}