From 84e19f99b58125b77deda3ab23aa56ad5922423a Mon Sep 17 00:00:00 2001 From: "Bassam (Sam) Khouri" Date: Tue, 24 Jun 2025 09:18:21 -0400 Subject: [PATCH] Revert "Convert JSON/PropertyList tests to swift-testing (#1364)" This reverts commit 3ee8bc0d7eb3ac998466a49429882a181aff9926. --- .../JSONEncoderTests.swift | 1232 ++++++++--------- .../PropertyListEncoderTests.swift | 872 ++++++------ 2 files changed, 1021 insertions(+), 1083 deletions(-) diff --git a/Tests/FoundationEssentialsTests/JSONEncoderTests.swift b/Tests/FoundationEssentialsTests/JSONEncoderTests.swift index 6fc131ba6..6697df493 100644 --- a/Tests/FoundationEssentialsTests/JSONEncoderTests.swift +++ b/Tests/FoundationEssentialsTests/JSONEncoderTests.swift @@ -9,145 +9,138 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +// +// RUN: %target-run-simple-swift +// REQUIRES: executable_test +// REQUIRES: objc_interop +// REQUIRES: rdar49634697 +// REQUIRES: rdar55727144 -import Testing - -#if canImport(Darwin) -import Darwin -#elseif canImport(Bionic) -@preconcurrency import Bionic -#elseif canImport(Glibc) -@preconcurrency import Glibc -#elseif canImport(Musl) -@preconcurrency import Musl -#elseif canImport(CRT) -import CRT -#elseif os(WASI) -@preconcurrency import WASILibc -#endif +#if canImport(TestSupport) +import TestSupport +#endif // canImport(TestSupport) #if canImport(FoundationEssentials) @_spi(SwiftCorelibsFoundation) -import FoundationEssentials +@testable import FoundationEssentials #endif #if FOUNDATION_FRAMEWORK -import Foundation +@testable import Foundation #endif // MARK: - Test Suite -@Suite("JSONEncoder") -private struct JSONEncoderTests { +final class JSONEncoderTests : XCTestCase { // MARK: - Encoding Top-Level Empty Types - @Test func encodingTopLevelEmptyStruct() { + func testEncodingTopLevelEmptyStruct() { let empty = EmptyStruct() _testRoundTrip(of: empty, expectedJSON: _jsonEmptyDictionary) } - @Test func encodingTopLevelEmptyClass() { + func testEncodingTopLevelEmptyClass() { let empty = EmptyClass() _testRoundTrip(of: empty, expectedJSON: _jsonEmptyDictionary) } // MARK: - Encoding Top-Level Single-Value Types - @Test func encodingTopLevelSingleValueEnum() { + func testEncodingTopLevelSingleValueEnum() { _testRoundTrip(of: Switch.off) _testRoundTrip(of: Switch.on) } - @Test func encodingTopLevelSingleValueStruct() { + func testEncodingTopLevelSingleValueStruct() { _testRoundTrip(of: Timestamp(3141592653)) } - @Test func encodingTopLevelSingleValueClass() { + func testEncodingTopLevelSingleValueClass() { _testRoundTrip(of: Counter()) } // MARK: - Encoding Top-Level Structured Types - @Test func encodingTopLevelStructuredStruct() { + func testEncodingTopLevelStructuredStruct() { // Address is a struct type with multiple fields. let address = Address.testValue _testRoundTrip(of: address) } - @Test func encodingTopLevelStructuredSingleStruct() { + func testEncodingTopLevelStructuredSingleStruct() { // Numbers is a struct which encodes as an array through a single value container. let numbers = Numbers.testValue _testRoundTrip(of: numbers) } - @Test func encodingTopLevelStructuredSingleClass() { + func testEncodingTopLevelStructuredSingleClass() { // Mapping is a class which encodes as a dictionary through a single value container. let mapping = Mapping.testValue _testRoundTrip(of: mapping) } - @Test func encodingTopLevelDeepStructuredType() { + func testEncodingTopLevelDeepStructuredType() { // Company is a type with fields which are Codable themselves. let company = Company.testValue _testRoundTrip(of: company) } - @Test func encodingClassWhichSharesEncoderWithSuper() { + func testEncodingClassWhichSharesEncoderWithSuper() { // Employee is a type which shares its encoder & decoder with its superclass, Person. let employee = Employee.testValue _testRoundTrip(of: employee) } - @Test func encodingTopLevelNullableType() { + func testEncodingTopLevelNullableType() { // EnhancedBool is a type which encodes either as a Bool or as nil. - _testRoundTrip(of: EnhancedBool.true, expectedJSON: "true".data(using: .utf8)!) - _testRoundTrip(of: EnhancedBool.false, expectedJSON: "false".data(using: .utf8)!) - _testRoundTrip(of: EnhancedBool.fileNotFound, expectedJSON: "null".data(using: .utf8)!) + _testRoundTrip(of: EnhancedBool.true, expectedJSON: "true".data(using: String._Encoding.utf8)!) + _testRoundTrip(of: EnhancedBool.false, expectedJSON: "false".data(using: String._Encoding.utf8)!) + _testRoundTrip(of: EnhancedBool.fileNotFound, expectedJSON: "null".data(using: String._Encoding.utf8)!) } - @Test func encodingTopLevelArrayOfInt() throws { + func testEncodingTopLevelArrayOfInt() { let a = [1,2,3] - let result1 = String(data: try JSONEncoder().encode(a), encoding: .utf8) - #expect(result1 == "[1,2,3]") + let result1 = String(data: try! JSONEncoder().encode(a), encoding: String._Encoding.utf8) + XCTAssertEqual(result1, "[1,2,3]") let b : [Int] = [] - let result2 = String(data: try JSONEncoder().encode(b), encoding: .utf8) - #expect(result2 == "[]") + let result2 = String(data: try! JSONEncoder().encode(b), encoding: String._Encoding.utf8) + XCTAssertEqual(result2, "[]") } - @Test func encodingTopLevelWithConfiguration() throws { + func testEncodingTopLevelWithConfiguration() throws { // CodableTypeWithConfiguration is a struct that conforms to CodableWithConfiguration let value = CodableTypeWithConfiguration.testValue let encoder = JSONEncoder() let decoder = JSONDecoder() var decoded = try decoder.decode(CodableTypeWithConfiguration.self, from: try encoder.encode(value, configuration: .init(1)), configuration: .init(1)) - #expect(decoded == value) + XCTAssertEqual(decoded, value) decoded = try decoder.decode(CodableTypeWithConfiguration.self, from: try encoder.encode(value, configuration: CodableTypeWithConfiguration.ConfigProviding.self), configuration: CodableTypeWithConfiguration.ConfigProviding.self) - #expect(decoded == value) + XCTAssertEqual(decoded, value) } - #if FOUNDATION_EXIT_TESTS - @Test func encodingConflictedTypeNestedContainersWithTheSameTopLevelKey() async { +#if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 + func testEncodingConflictedTypeNestedContainersWithTheSameTopLevelKey() { struct Model : Encodable, Equatable { let first: String - + func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: TopLevelCodingKeys.self) - + var firstNestedContainer = container.nestedContainer(keyedBy: FirstNestedCodingKeys.self, forKey: .top) try firstNestedContainer.encode(self.first, forKey: .first) - + // The following line would fail as it attempts to re-encode into already encoded container is invalid. This will always fail var secondNestedContainer = container.nestedUnkeyedContainer(forKey: .top) try secondNestedContainer.encode("second") } - + init(first: String) { self.first = first } - + static var testValue: Model { return Model(first: "Johnny Appleseed") } - + enum TopLevelCodingKeys : String, CodingKey { case top } @@ -155,21 +148,20 @@ private struct JSONEncoderTests { case first } } - - await #expect(processExitsWith: .failure) { - let model = Model.testValue - // This following test would fail as it attempts to re-encode into already encoded container is invalid. This will always fail - _ = try JSONEncoder().encode(model) - } + + let model = Model.testValue + // This following test would fail as it attempts to re-encode into already encoded container is invalid. This will always fail + expectCrashLater() + _testEncodeFailure(of: model) } - #endif +#endif // MARK: - Date Strategy Tests - @Test func encodingDateSecondsSince1970() { + func testEncodingDateSecondsSince1970() { // Cannot encode an arbitrary number of seconds since we've lost precision since 1970. let seconds = 1000.0 - let expectedJSON = "1000".data(using: .utf8)! + let expectedJSON = "1000".data(using: String._Encoding.utf8)! _testRoundTrip(of: Date(timeIntervalSince1970: seconds), expectedJSON: expectedJSON, @@ -183,10 +175,10 @@ private struct JSONEncoderTests { dateDecodingStrategy: .secondsSince1970) } - @Test func encodingDateMillisecondsSince1970() { + func testEncodingDateMillisecondsSince1970() { // Cannot encode an arbitrary number of seconds since we've lost precision since 1970. let seconds = 1000.0 - let expectedJSON = "1000000".data(using: .utf8)! + let expectedJSON = "1000000".data(using: String._Encoding.utf8)! _testRoundTrip(of: Date(timeIntervalSince1970: seconds), expectedJSON: expectedJSON, @@ -223,7 +215,7 @@ private struct JSONEncoderTests { } } - @Test func encodingDateCustom() { + func test_encodingDateCustom() { let timestamp = Date() // We'll encode a number instead of a date. @@ -233,7 +225,7 @@ private struct JSONEncoderTests { } let decode = { @Sendable (_: Decoder) throws -> Date in return timestamp } - let expectedJSON = "42".data(using: .utf8)! + let expectedJSON = "42".data(using: String._Encoding.utf8)! _testRoundTrip(of: timestamp, expectedJSON: expectedJSON, dateEncodingStrategy: .custom(encode), @@ -246,21 +238,21 @@ private struct JSONEncoderTests { dateDecodingStrategy: .custom(decode)) // So should wrapped dates. - let expectedJSON_array = "[42]".data(using: .utf8)! + let expectedJSON_array = "[42]".data(using: String._Encoding.utf8)! _testRoundTrip(of: TopLevelArrayWrapper(timestamp), expectedJSON: expectedJSON_array, dateEncodingStrategy: .custom(encode), dateDecodingStrategy: .custom(decode)) } - @Test func encodingDateCustomEmpty() { + func testEncodingDateCustomEmpty() { let timestamp = Date() // Encoding nothing should encode an empty keyed container ({}). let encode = { @Sendable (_: Date, _: Encoder) throws -> Void in } let decode = { @Sendable (_: Decoder) throws -> Date in return timestamp } - let expectedJSON = "{}".data(using: .utf8)! + let expectedJSON = "{}".data(using: String._Encoding.utf8)! _testRoundTrip(of: timestamp, expectedJSON: expectedJSON, dateEncodingStrategy: .custom(encode), @@ -274,10 +266,10 @@ private struct JSONEncoderTests { } // MARK: - Data Strategy Tests - @Test func encodingData() { + func testEncodingData() { let data = Data([0xDE, 0xAD, 0xBE, 0xEF]) - let expectedJSON = "[222,173,190,239]".data(using: .utf8)! + let expectedJSON = "[222,173,190,239]".data(using: String._Encoding.utf8)! _testRoundTrip(of: data, expectedJSON: expectedJSON, dataEncodingStrategy: .deferredToData, @@ -290,7 +282,7 @@ private struct JSONEncoderTests { dataDecodingStrategy: .deferredToData) } - @Test func encodingDataCustom() { + func testEncodingDataCustom() { // We'll encode a number instead of data. let encode = { @Sendable (_ data: Data, _ encoder: Encoder) throws -> Void in var container = encoder.singleValueContainer() @@ -298,7 +290,7 @@ private struct JSONEncoderTests { } let decode = { @Sendable (_: Decoder) throws -> Data in return Data() } - let expectedJSON = "42".data(using: .utf8)! + let expectedJSON = "42".data(using: String._Encoding.utf8)! _testRoundTrip(of: Data(), expectedJSON: expectedJSON, dataEncodingStrategy: .custom(encode), @@ -311,12 +303,12 @@ private struct JSONEncoderTests { dataDecodingStrategy: .custom(decode)) } - @Test func encodingDataCustomEmpty() { + func testEncodingDataCustomEmpty() { // Encoding nothing should encode an empty keyed container ({}). let encode = { @Sendable (_: Data, _: Encoder) throws -> Void in } let decode = { @Sendable (_: Decoder) throws -> Data in return Data() } - let expectedJSON = "{}".data(using: .utf8)! + let expectedJSON = "{}".data(using: String._Encoding.utf8)! _testRoundTrip(of: Data(), expectedJSON: expectedJSON, dataEncodingStrategy: .custom(encode), @@ -330,7 +322,7 @@ private struct JSONEncoderTests { } // MARK: - Non-Conforming Floating Point Strategy Tests - @Test func encodingNonConformingFloats() { + func testEncodingNonConformingFloats() { _testEncodeFailure(of: Float.infinity) _testEncodeFailure(of: Float.infinity) _testEncodeFailure(of: -Float.infinity) @@ -350,62 +342,62 @@ private struct JSONEncoderTests { _testEncodeFailure(of: Double.nan) } - @Test func encodingNonConformingFloatStrings() { + func testEncodingNonConformingFloatStrings() { let encodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "INF", negativeInfinity: "-INF", nan: "NaN") let decodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "INF", negativeInfinity: "-INF", nan: "NaN") _testRoundTrip(of: Float.infinity, - expectedJSON: "\"INF\"".data(using: .utf8)!, + expectedJSON: "\"INF\"".data(using: String._Encoding.utf8)!, nonConformingFloatEncodingStrategy: encodingStrategy, nonConformingFloatDecodingStrategy: decodingStrategy) _testRoundTrip(of: -Float.infinity, - expectedJSON: "\"-INF\"".data(using: .utf8)!, + expectedJSON: "\"-INF\"".data(using: String._Encoding.utf8)!, nonConformingFloatEncodingStrategy: encodingStrategy, nonConformingFloatDecodingStrategy: decodingStrategy) // Since Float.nan != Float.nan, we have to use a placeholder that'll encode NaN but actually round-trip. _testRoundTrip(of: FloatNaNPlaceholder(), - expectedJSON: "\"NaN\"".data(using: .utf8)!, + expectedJSON: "\"NaN\"".data(using: String._Encoding.utf8)!, nonConformingFloatEncodingStrategy: encodingStrategy, nonConformingFloatDecodingStrategy: decodingStrategy) _testRoundTrip(of: Double.infinity, - expectedJSON: "\"INF\"".data(using: .utf8)!, + expectedJSON: "\"INF\"".data(using: String._Encoding.utf8)!, nonConformingFloatEncodingStrategy: encodingStrategy, nonConformingFloatDecodingStrategy: decodingStrategy) _testRoundTrip(of: -Double.infinity, - expectedJSON: "\"-INF\"".data(using: .utf8)!, + expectedJSON: "\"-INF\"".data(using: String._Encoding.utf8)!, nonConformingFloatEncodingStrategy: encodingStrategy, nonConformingFloatDecodingStrategy: decodingStrategy) // Since Double.nan != Double.nan, we have to use a placeholder that'll encode NaN but actually round-trip. _testRoundTrip(of: DoubleNaNPlaceholder(), - expectedJSON: "\"NaN\"".data(using: .utf8)!, + expectedJSON: "\"NaN\"".data(using: String._Encoding.utf8)!, nonConformingFloatEncodingStrategy: encodingStrategy, nonConformingFloatDecodingStrategy: decodingStrategy) // Optional Floats and Doubles should encode the same way. _testRoundTrip(of: Optional(Float.infinity), - expectedJSON: "\"INF\"".data(using: .utf8)!, + expectedJSON: "\"INF\"".data(using: String._Encoding.utf8)!, nonConformingFloatEncodingStrategy: encodingStrategy, nonConformingFloatDecodingStrategy: decodingStrategy) _testRoundTrip(of: Optional(-Float.infinity), - expectedJSON: "\"-INF\"".data(using: .utf8)!, + expectedJSON: "\"-INF\"".data(using: String._Encoding.utf8)!, nonConformingFloatEncodingStrategy: encodingStrategy, nonConformingFloatDecodingStrategy: decodingStrategy) _testRoundTrip(of: Optional(Double.infinity), - expectedJSON: "\"INF\"".data(using: .utf8)!, + expectedJSON: "\"INF\"".data(using: String._Encoding.utf8)!, nonConformingFloatEncodingStrategy: encodingStrategy, nonConformingFloatDecodingStrategy: decodingStrategy) _testRoundTrip(of: Optional(-Double.infinity), - expectedJSON: "\"-INF\"".data(using: .utf8)!, + expectedJSON: "\"-INF\"".data(using: String._Encoding.utf8)!, nonConformingFloatEncodingStrategy: encodingStrategy, nonConformingFloatDecodingStrategy: decodingStrategy) } // MARK: - Directly Encoded Array Tests - @Test func directlyEncodedArrays() { + func testDirectlyEncodedArrays() { let encodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "INF", negativeInfinity: "-INF", nan: "NaN") let decodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "INF", negativeInfinity: "-INF", nan: "NaN") @@ -438,7 +430,7 @@ private struct JSONEncoderTests { } } - @Test func encodingKeyStrategyCustom() { + func testEncodingKeyStrategyCustom() { let expected = "{\"QQQhello\":\"test\"}" let encoded = EncodeMe(keyName: "hello") @@ -449,9 +441,9 @@ private struct JSONEncoderTests { } encoder.keyEncodingStrategy = .custom(customKeyConversion) let resultData = try! encoder.encode(encoded) - let resultString = String(bytes: resultData, encoding: .utf8) + let resultString = String(bytes: resultData, encoding: String._Encoding.utf8) - #expect(expected == resultString) + XCTAssertEqual(expected, resultString) } private struct EncodeFailure : Encodable { @@ -470,7 +462,7 @@ private struct JSONEncoderTests { let outerValue: EncodeNested } - @Test func encodingKeyStrategyPath() throws { + func testEncodingKeyStrategyPath() { // Make sure a more complex path shows up the way we want // Make sure the path reflects keys in the Swift, not the resulting ones in the JSON let expected = "{\"QQQouterValue\":{\"QQQnestedValue\":{\"QQQhelloWorld\":\"test\"}}}" @@ -488,26 +480,26 @@ private struct JSONEncoderTests { callCount = callCount + 1 if path.count == 0 { - Issue.record("The path should always have at least one entry") + XCTFail("The path should always have at least one entry") } else if path.count == 1 { - #expect(["outerValue"] == path.map { $0.stringValue }) + XCTAssertEqual(["outerValue"], path.map { $0.stringValue }) } else if path.count == 2 { - #expect(["outerValue", "nestedValue"] == path.map { $0.stringValue }) + XCTAssertEqual(["outerValue", "nestedValue"], path.map { $0.stringValue }) } else if path.count == 3 { - #expect(["outerValue", "nestedValue", "helloWorld"] == path.map { $0.stringValue }) + XCTAssertEqual(["outerValue", "nestedValue", "helloWorld"], path.map { $0.stringValue }) } else { - Issue.record("The path mysteriously had more entries") + XCTFail("The path mysteriously had more entries") } let key = _TestKey(stringValue: "QQQ" + path.last!.stringValue)! return key } encoder.keyEncodingStrategy = .custom(customKeyConversion) - let resultData = try encoder.encode(encoded) - let resultString = String(bytes: resultData, encoding: .utf8) + let resultData = try! encoder.encode(encoded) + let resultString = String(bytes: resultData, encoding: String._Encoding.utf8) - #expect(expected == resultString) - #expect(3 == callCount) + XCTAssertEqual(expected, resultString) + XCTAssertEqual(3, callCount) } private struct DecodeMe : Decodable { @@ -524,8 +516,8 @@ private struct JSONEncoderTests { private struct DecodeMe2 : Decodable { var hello: String } - @Test func decodingKeyStrategyCustom() throws { - let input = "{\"----hello\":\"test\"}".data(using: .utf8)! + func testDecodingKeyStrategyCustom() { + let input = "{\"----hello\":\"test\"}".data(using: String._Encoding.utf8)! let decoder = JSONDecoder() let customKeyConversion = { @Sendable (_ path: [CodingKey]) -> CodingKey in // This converter removes the first 4 characters from the start of all string keys, if it has more than 4 characters @@ -535,31 +527,31 @@ private struct JSONEncoderTests { return _TestKey(stringValue: newString)! } decoder.keyDecodingStrategy = .custom(customKeyConversion) - let result = try decoder.decode(DecodeMe2.self, from: input) + let result = try! decoder.decode(DecodeMe2.self, from: input) - #expect("test" == result.hello) + XCTAssertEqual("test", result.hello) } - @Test func decodingDictionaryStringKeyConversionUntouched() throws { - let input = "{\"leave_me_alone\":\"test\"}".data(using: .utf8)! + func testDecodingDictionaryStringKeyConversionUntouched() { + let input = "{\"leave_me_alone\":\"test\"}".data(using: String._Encoding.utf8)! let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase - let result = try decoder.decode([String: String].self, from: input) + let result = try! decoder.decode([String: String].self, from: input) - #expect(["leave_me_alone": "test"] == result) + XCTAssertEqual(["leave_me_alone": "test"], result) } - @Test func decodingDictionaryFailureKeyPath() { - let input = "{\"leave_me_alone\":\"test\"}".data(using: .utf8)! + func testDecodingDictionaryFailureKeyPath() { + let input = "{\"leave_me_alone\":\"test\"}".data(using: String._Encoding.utf8)! let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase - #expect { - try decoder.decode([String: Int].self, from: input) - } throws: { - guard case DecodingError.typeMismatch(_, let context) = $0 else { - return false - } - return (1 == context.codingPath.count) && ("leave_me_alone" == context.codingPath[0].stringValue) + do { + _ = try decoder.decode([String: Int].self, from: input) + } catch DecodingError.typeMismatch(_, let context) { + XCTAssertEqual(1, context.codingPath.count) + XCTAssertEqual("leave_me_alone", context.codingPath[0].stringValue) + } catch { + XCTFail("Unexpected error: \(String(describing: error))") } } @@ -575,7 +567,7 @@ private struct JSONEncoderTests { var thisIsCamelCase : String } - @Test func keyStrategyDuplicateKeys() throws { + func testKeyStrategyDuplicateKeys() { // This test is mostly to make sure we don't assert on duplicate keys struct DecodeMe5 : Codable { var oneTwo : String @@ -611,44 +603,48 @@ private struct JSONEncoderTests { // Decoding // This input has a dictionary with two keys, but only one will end up in the container - let input = "{\"unused key 1\":\"test1\",\"unused key 2\":\"test2\"}".data(using: .utf8)! + let input = "{\"unused key 1\":\"test1\",\"unused key 2\":\"test2\"}".data(using: String._Encoding.utf8)! let decoder = JSONDecoder() decoder.keyDecodingStrategy = .custom(customKeyConversion) - let decodingResult = try decoder.decode(DecodeMe5.self, from: input) + let decodingResult = try! decoder.decode(DecodeMe5.self, from: input) // There will be only one result for oneTwo. - #expect(1 == decodingResult.numberOfKeys) + XCTAssertEqual(1, decodingResult.numberOfKeys) // While the order in which these values should be taken is NOT defined by the JSON spec in any way, the historical behavior has been to select the *first* value for a given key. - #expect(decodingResult.oneTwo == "test1") + XCTAssertEqual(decodingResult.oneTwo, "test1") // Encoding let encoded = DecodeMe5() let encoder = JSONEncoder() encoder.keyEncodingStrategy = .custom(customKeyConversion) - let decodingResultData = try encoder.encode(encoded) - let decodingResultString = String(bytes: decodingResultData, encoding: .utf8) + let decodingResultData = try! encoder.encode(encoded) + let decodingResultString = String(bytes: decodingResultData, encoding: String._Encoding.utf8) // There will be only one value in the result (the second one encoded) - #expect("{\"oneTwo\":\"test2\"}" == decodingResultString) + XCTAssertEqual("{\"oneTwo\":\"test2\"}", decodingResultString) } // MARK: - Encoder Features - @Test func nestedContainerCodingPaths() { + func testNestedContainerCodingPaths() { let encoder = JSONEncoder() - #expect(throws: Never.self) { - try encoder.encode(NestedContainersTestType()) + do { + let _ = try encoder.encode(NestedContainersTestType()) + } catch let error as NSError { + XCTFail("Caught error during encoding nested container types: \(error)") } } - @Test func superEncoderCodingPaths() { + func testSuperEncoderCodingPaths() { let encoder = JSONEncoder() - #expect(throws: Never.self) { - try encoder.encode(NestedContainersTestType(testSuperEncoder: true)) + do { + let _ = try encoder.encode(NestedContainersTestType(testSuperEncoder: true)) + } catch let error as NSError { + XCTFail("Caught error during encoding nested container types: \(error)") } } // MARK: - Type coercion - @Test func typeCoercion() { + func testTypeCoercion() { _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int].self) _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int8].self) _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int16].self) @@ -679,19 +675,25 @@ private struct JSONEncoderTests { _testRoundTripTypeCoercionFailure(of: [0.0, 1.0] as [Double], as: [Bool].self) } - @Test func decodingConcreteTypeParameter() throws { + func testDecodingConcreteTypeParameter() { let encoder = JSONEncoder() - let json = try encoder.encode(Employee.testValue) + guard let json = try? encoder.encode(Employee.testValue) else { + XCTFail("Unable to encode Employee.") + return + } let decoder = JSONDecoder() - let decoded = try decoder.decode(Employee.self as Person.Type, from: json) + guard let decoded = try? decoder.decode(Employee.self as Person.Type, from: json) else { + XCTFail("Failed to decode Employee as Person from JSON.") + return + } - #expect(type(of: decoded) == Employee.self, "Expected decoded value to be of type Employee; got \(type(of: decoded)) instead.") + expectEqual(type(of: decoded), Employee.self, "Expected decoded value to be of type Employee; got \(type(of: decoded)) instead.") } // MARK: - Encoder State // SR-6078 - @Test func encoderStateThrowOnEncode() { + func testEncoderStateThrowOnEncode() { struct ReferencingEncoderWrapper : Encodable { let value: T init(_ value: T) { self.value = value } @@ -720,7 +722,7 @@ private struct JSONEncoderTests { _ = try? JSONEncoder().encode(ReferencingEncoderWrapper([Float.infinity])) } - @Test func encoderStateThrowOnEncodeCustomDate() { + func testEncoderStateThrowOnEncodeCustomDate() { // This test is identical to testEncoderStateThrowOnEncode, except throwing via a custom Date closure. struct ReferencingEncoderWrapper : Encodable { let value: T @@ -744,7 +746,7 @@ private struct JSONEncoderTests { _ = try? encoder.encode(ReferencingEncoderWrapper(Date())) } - @Test func encoderStateThrowOnEncodeCustomData() { + func testEncoderStateThrowOnEncodeCustomData() { // This test is identical to testEncoderStateThrowOnEncode, except throwing via a custom Data closure. struct ReferencingEncoderWrapper : Encodable { let value: T @@ -768,7 +770,7 @@ private struct JSONEncoderTests { _ = try? encoder.encode(ReferencingEncoderWrapper(Data())) } - @Test func issue106506794() throws { + func test_106506794() throws { struct Level1: Codable, Equatable { let level2: Level2 @@ -800,21 +802,25 @@ private struct JSONEncoderTests { let value = Level1.init(level2: .init(name: "level2")) let data = try JSONEncoder().encode(value) - let decodedValue = try JSONDecoder().decode(Level1.self, from: data) - #expect(value == decodedValue) + do { + let decodedValue = try JSONDecoder().decode(Level1.self, from: data) + XCTAssertEqual(value, decodedValue) + } catch { + XCTFail("Decode should not have failed with error: \(error))") + } } // MARK: - Decoder State // SR-6048 - @Test func decoderStateThrowOnDecode() throws { + func testDecoderStateThrowOnDecode() { // The container stack here starts as [[1,2,3]]. Attempting to decode as [String] matches the outer layer (Array), and begins decoding the array. // Once Array decoding begins, 1 is pushed onto the container stack ([[1,2,3], 1]), and 1 is attempted to be decoded as String. This throws a .typeMismatch, but the container is not popped off the stack. // When attempting to decode [Int], the container stack is still ([[1,2,3], 1]), and 1 fails to decode as [Int]. - let json = "[1,2,3]".data(using: .utf8)! - let _ = try JSONDecoder().decode(EitherDecodable<[String], [Int]>.self, from: json) + let json = "[1,2,3]".data(using: String._Encoding.utf8)! + let _ = try! JSONDecoder().decode(EitherDecodable<[String], [Int]>.self, from: json) } - @Test func decoderStateThrowOnDecodeCustomDate() throws { + func testDecoderStateThrowOnDecodeCustomDate() { // This test is identical to testDecoderStateThrowOnDecode, except we're going to fail because our closure throws an error, not because we hit a type mismatch. let decoder = JSONDecoder() decoder.dateDecodingStrategy = .custom({ decoder in @@ -822,11 +828,11 @@ private struct JSONEncoderTests { throw CustomError.foo }) - let json = "1".data(using: .utf8)! - let _ = try decoder.decode(EitherDecodable.self, from: json) + let json = "1".data(using: String._Encoding.utf8)! + let _ = try! decoder.decode(EitherDecodable.self, from: json) } - @Test func decoderStateThrowOnDecodeCustomData() throws { + func testDecoderStateThrowOnDecodeCustomData() { // This test is identical to testDecoderStateThrowOnDecode, except we're going to fail because our closure throws an error, not because we hit a type mismatch. let decoder = JSONDecoder() decoder.dataDecodingStrategy = .custom({ decoder in @@ -834,20 +840,20 @@ private struct JSONEncoderTests { throw CustomError.foo }) - let json = "1".data(using: .utf8)! - let _ = try decoder.decode(EitherDecodable.self, from: json) + let json = "1".data(using: String._Encoding.utf8)! + let _ = try! decoder.decode(EitherDecodable.self, from: json) } - @Test func decodingFailure() { + func testDecodingFailure() { struct DecodeFailure : Decodable { var invalid: String } let toDecode = "{\"invalid\": json}"; - _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: .utf8)!) + _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: String._Encoding.utf8)!) } - @Test func decodingFailureThrowInInitKeyedContainer() { + func testDecodingFailureThrowInInitKeyedContainer() { struct DecodeFailure : Decodable { private enum CodingKeys: String, CodingKey { case checkedString @@ -869,10 +875,10 @@ private struct JSONEncoderTests { } let toDecode = "{ \"checkedString\" : \"baz\" }" - _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: .utf8)!) + _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: String._Encoding.utf8)!) } - @Test func decodingFailureThrowInInitSingleContainer() { + func testDecodingFailureThrowInInitSingleContainer() { struct DecodeFailure : Decodable { private enum Error: Swift.Error { case expectedError @@ -890,18 +896,18 @@ private struct JSONEncoderTests { } let toDecode = "{ \"checkedString\" : \"baz\" }" - _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: .utf8)!) + _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: String._Encoding.utf8)!) } - @Test func invalidFragment() { + func testInvalidFragment() { struct DecodeFailure: Decodable { var foo: String } let toDecode = "\"foo" - _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: .utf8)!) + _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: String._Encoding.utf8)!) } - @Test func repeatedFailedNilChecks() { + func testRepeatedFailedNilChecks() { struct RepeatNilCheckDecodable : Decodable { enum Failure : Error { case badNil @@ -945,13 +951,11 @@ private struct JSONEncoderTests { } } } - let json = "[1, 2, 3]".data(using: .utf8)! - #expect(throws: Never.self) { - try JSONDecoder().decode(RepeatNilCheckDecodable.self, from: json) - } + let json = "[1, 2, 3]".data(using: String._Encoding.utf8)! + XCTAssertNoThrow(try JSONDecoder().decode(RepeatNilCheckDecodable.self, from: json)) } - @Test func delayedDecoding() throws { + func testDelayedDecoding() throws { // One variation is deferring the use of a container. struct DelayedDecodable_ContainerVersion : Codable { @@ -985,9 +989,7 @@ private struct JSONEncoderTests { let data = try JSONEncoder().encode(before) let decoded = try JSONDecoder().decode(DelayedDecodable_ContainerVersion.self, from: data) - #expect(throws: Never.self) { - try decoded.i - } + XCTAssertNoThrow(try decoded.i) // The other variant is deferring the use of the *top-level* decoder. This does NOT work for non-top level decoders. struct DelayedDecodable_DecoderVersion : Codable { @@ -1018,25 +1020,29 @@ private struct JSONEncoderTests { } // Reuse the same data. let decoded2 = try JSONDecoder().decode(DelayedDecodable_DecoderVersion.self, from: data) - #expect(throws: Never.self) { - try decoded2.i - } + XCTAssertNoThrow(try decoded2.i) } // MARK: - Helper Functions private var _jsonEmptyDictionary: Data { - return "{}".data(using: .utf8)! + return "{}".data(using: String._Encoding.utf8)! } - private func _testEncodeFailure(of value: T, sourceLocation: SourceLocation = #_sourceLocation) { - #expect(throws: (any Error).self, "Encode of top-level \(T.self) was expected to fail.", sourceLocation: sourceLocation) { - try JSONEncoder().encode(value) + private func _testEncodeFailure(of value: T) { + do { + let _ = try JSONEncoder().encode(value) + XCTFail("Encode of top-level \(T.self) was expected to fail.") + } catch { + XCTAssertNotNil(error); } } - private func _testDecodeFailure(of value: T.Type, data: Data, sourceLocation: SourceLocation = #_sourceLocation) { - #expect(throws: (any Error).self, "Decode of top-level \(value) was expected to fail.", sourceLocation: sourceLocation) { - try JSONDecoder().decode(value, from: data) + private func _testDecodeFailure(of value: T.Type, data: Data) { + do { + let _ = try JSONDecoder().decode(value, from: data) + XCTFail("Decode of top-level \(value) was expected to fail.") + } catch { + XCTAssertNotNil(error); } } @@ -1050,9 +1056,8 @@ private struct JSONEncoderTests { keyEncodingStrategy: JSONEncoder.KeyEncodingStrategy = .useDefaultKeys, keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys, nonConformingFloatEncodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy = .throw, - nonConformingFloatDecodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy = .throw, - sourceLocation: SourceLocation = #_sourceLocation) where T : Codable, T : Equatable { - var payload: Data + nonConformingFloatDecodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy = .throw) where T : Codable, T : Equatable { + var payload: Data! = nil do { let encoder = JSONEncoder() encoder.outputFormatting = outputFormatting @@ -1062,14 +1067,13 @@ private struct JSONEncoderTests { encoder.keyEncodingStrategy = keyEncodingStrategy payload = try encoder.encode(value) } catch { - Issue.record("Failed to encode \(T.self) to JSON: \(error)", sourceLocation: sourceLocation) - return + XCTFail("Failed to encode \(T.self) to JSON: \(error)") } if let expectedJSON = json { let expected = String(data: expectedJSON, encoding: .utf8)! let actual = String(data: payload, encoding: .utf8)! - #expect(expected == actual, "Produced JSON not identical to expected JSON.", sourceLocation: sourceLocation) + XCTAssertEqual(expected, actual, "Produced JSON not identical to expected JSON.") } do { @@ -1079,21 +1083,27 @@ private struct JSONEncoderTests { decoder.nonConformingFloatDecodingStrategy = nonConformingFloatDecodingStrategy decoder.keyDecodingStrategy = keyDecodingStrategy let decoded = try decoder.decode(T.self, from: payload) - #expect(decoded == value, "\(T.self) did not round-trip to an equal value.", sourceLocation: sourceLocation) + XCTAssertEqual(decoded, value, "\(T.self) did not round-trip to an equal value.") } catch { - Issue.record("Failed to decode \(T.self) from JSON: \(error)", sourceLocation: sourceLocation) + XCTFail("Failed to decode \(T.self) from JSON: \(error)") } } - private func _testRoundTripTypeCoercionFailure(of value: T, as type: U.Type, sourceLocation: SourceLocation = #_sourceLocation) where T : Codable, U : Codable { - #expect(throws: (any Error).self, "Coercion from \(T.self) to \(U.self) was expected to fail.", sourceLocation: sourceLocation) { + private func _testRoundTripTypeCoercionFailure(of value: T, as type: U.Type) where T : Codable, U : Codable { + do { let data = try JSONEncoder().encode(value) let _ = try JSONDecoder().decode(U.self, from: data) - } + XCTFail("Coercion from \(T.self) to \(U.self) was expected to fail.") + } catch {} } - private func _test(JSONString: String, to object: T, sourceLocation: SourceLocation = #_sourceLocation) { - let encs : [String.Encoding] = [.utf8, .utf16BigEndian, .utf16LittleEndian, .utf32BigEndian, .utf32LittleEndian] + private func _test(JSONString: String, to object: T) { +#if FOUNDATION_FRAMEWORK + let encs : [String._Encoding] = [.utf8, .utf16BigEndian, .utf16LittleEndian, .utf32BigEndian, .utf32LittleEndian] +#else + // TODO: Reenable other encoding once string.data(using:) is fully implemented. + let encs : [String._Encoding] = [.utf8, .utf16BigEndian, .utf16LittleEndian] +#endif let decoder = JSONDecoder() for enc in encs { let data = JSONString.data(using: enc)! @@ -1101,26 +1111,26 @@ private struct JSONEncoderTests { do { parsed = try decoder.decode(T.self, from: data) } catch { - Issue.record("Failed to decode \(JSONString) with encoding \(enc): Error: \(error)", sourceLocation: sourceLocation) + XCTFail("Failed to decode \(JSONString) with encoding \(enc): Error: \(error)") continue } - #expect(object == parsed, sourceLocation: sourceLocation) + XCTAssertEqual(object, parsed) } } - @Test func jsonEscapedSlashes() { + func test_JSONEscapedSlashes() { _test(JSONString: "\"\\/test\\/path\"", to: "/test/path") _test(JSONString: "\"\\\\/test\\\\/path\"", to: "\\/test\\/path") } - @Test func jsonEscapedForwardSlashes() { + func test_JSONEscapedForwardSlashes() { _testRoundTrip(of: ["/":1], expectedJSON: """ {"\\/":1} -""".data(using: .utf8)!) +""".data(using: String._Encoding.utf8)!) } - @Test func jsonUnicodeCharacters() { + func test_JSONUnicodeCharacters() { // UTF8: // E9 96 86 E5 B4 AC EB B0 BA EB 80 AB E9 A2 92 // 閆崬밺뀫颒 @@ -1128,7 +1138,7 @@ private struct JSONEncoderTests { _test(JSONString: "[\"本日\"]", to: ["本日"]) } - @Test func jsonUnicodeEscapes() throws { + func test_JSONUnicodeEscapes() throws { let testCases = [ // e-acute and greater-than-or-equal-to "\"\\u00e9\\u2265\"" : "é≥", @@ -1147,7 +1157,7 @@ private struct JSONEncoderTests { } } - @Test func encodingJSONHexUnicodeEscapes() throws { + func test_encodingJSONHexUnicodeEscapes() throws { let testCases = [ "\u{0001}\u{0002}\u{0003}": "\"\\u0001\\u0002\\u0003\"", "\u{0010}\u{0018}\u{001f}": "\"\\u0010\\u0018\\u001f\"", @@ -1157,58 +1167,58 @@ private struct JSONEncoderTests { } } - @Test(arguments: [ - "\\uD834", "\\uD834hello", "hello\\uD834", "\\uD834\\u1221", "\\uD8", "\\uD834x\\uDD1E" - ]) - func jsonBadUnicodeEscapes(str: String) { - let data = str.data(using: .utf8)! - #expect(throws: (any Error).self) { - try JSONDecoder().decode(String.self, from: data) + func test_JSONBadUnicodeEscapes() { + let badCases = ["\\uD834", "\\uD834hello", "hello\\uD834", "\\uD834\\u1221", "\\uD8", "\\uD834x\\uDD1E"] + for str in badCases { + let data = str.data(using: String._Encoding.utf8)! + XCTAssertThrowsError(try JSONDecoder().decode(String.self, from: data)) } } - @Test func nullByte() throws { + func test_nullByte() throws { let string = "abc\u{0000}def" let encoder = JSONEncoder() let decoder = JSONDecoder() let data = try encoder.encode([string]) let decoded = try decoder.decode([String].self, from: data) - #expect([string] == decoded) + XCTAssertEqual([string], decoded) let data2 = try encoder.encode([string:string]) let decoded2 = try decoder.decode([String:String].self, from: data2) - #expect([string:string] == decoded2) + XCTAssertEqual([string:string], decoded2) struct Container: Codable { let s: String } let data3 = try encoder.encode(Container(s: string)) let decoded3 = try decoder.decode(Container.self, from: data3) - #expect(decoded3.s == string) + XCTAssertEqual(decoded3.s, string) } - @Test func superfluouslyEscapedCharacters() { + func test_superfluouslyEscapedCharacters() { let json = "[\"\\h\\e\\l\\l\\o\"]" - #expect(throws: (any Error).self) { - try JSONDecoder().decode([String].self, from: json.data(using: .utf8)!) - } + XCTAssertThrowsError(try JSONDecoder().decode([String].self, from: json.data(using: String._Encoding.utf8)!)) } - @Test func equivalentUTF8Sequences() throws { + func test_equivalentUTF8Sequences() { let json = """ { "caf\\u00e9" : true, "cafe\\u0301" : false } -""".data(using: .utf8)! +""".data(using: String._Encoding.utf8)! - let dict = try JSONDecoder().decode([String:Bool].self, from: json) - #expect(dict.count == 1) + do { + let dict = try JSONDecoder().decode([String:Bool].self, from: json) + XCTAssertEqual(dict.count, 1) + } catch { + XCTFail("Unexpected error: \(error)") + } } - @Test func jsonControlCharacters() { + func test_JSONControlCharacters() { let array = [ "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", "\\b", "\\t", @@ -1225,7 +1235,7 @@ private struct JSONEncoderTests { } } - @Test func jsonNumberFragments() { + func test_JSONNumberFragments() { let array = ["0 ", "1.0 ", "0.1 ", "1e3 ", "-2.01e-3 ", "0", "1.0", "1e3", "-2.01e-3", "0e-10"] let expected = [0, 1.0, 0.1, 1000, -0.00201, 0, 1.0, 1000, -0.00201, 0] for (json, expected) in zip(array, expected) { @@ -1233,59 +1243,55 @@ private struct JSONEncoderTests { } } - @Test func invalidJSONNumbersFailAsExpected() { + func test_invalidJSONNumbersFailAsExpected() { let array = ["0.", "1e ", "-2.01e- ", "+", "2.01e-1234", "+2.0q", "2s", "NaN", "nan", "Infinity", "inf", "-", "0x42", "1.e2"] for json in array { - let data = json.data(using: .utf8)! - #expect(throws: (any Error).self, "Expected error for input \"\(json)\"") { - _ = try JSONDecoder().decode(Float.self, from: data) - } + let data = json.data(using: String._Encoding.utf8)! + XCTAssertThrowsError(try JSONDecoder().decode(Float.self, from: data), "Expected error for input \"\(json)\"") } } - func _checkExpectedThrownDataCorruptionUnderlyingError(contains substring: String, sourceLocation: SourceLocation = #_sourceLocation, closure: () throws -> Void) { + func _checkExpectedThrownDataCorruptionUnderlyingError(contains substring: String, closure: () throws -> Void) { do { try closure() - Issue.record("Expected failure containing string: \"\(substring)\"", sourceLocation: sourceLocation) + XCTFail("Expected failure containing string: \"\(substring)\"") } catch let error as DecodingError { guard case let .dataCorrupted(context) = error else { - Issue.record("Unexpected DecodingError type: \(error)", sourceLocation: sourceLocation) + XCTFail("Unexpected DecodingError type: \(error)") return } #if FOUNDATION_FRAMEWORK let nsError = context.underlyingError! as NSError - #expect(nsError.debugDescription.contains(substring), "Description \"\(nsError.debugDescription)\" doesn't contain substring \"\(substring)\"", sourceLocation: sourceLocation) + XCTAssertTrue(nsError.debugDescription.contains(substring), "Description \"\(nsError.debugDescription)\" doesn't contain substring \"\(substring)\"") #endif } catch { - Issue.record("Unexpected error type: \(error)", sourceLocation: sourceLocation) + XCTFail("Unexpected error type: \(error)") } } - @Test func topLevelFragmentsWithGarbage() { + func test_topLevelFragmentsWithGarbage() { _checkExpectedThrownDataCorruptionUnderlyingError(contains: "Unexpected character") { - let _ = try JSONDecoder().decode(Bool.self, from: "tru_".data(using: .utf8)!) - let _ = try json5Decoder.decode(Bool.self, from: "tru_".data(using: .utf8)!) + let _ = try JSONDecoder().decode(Bool.self, from: "tru_".data(using: String._Encoding.utf8)!) + let _ = try json5Decoder.decode(Bool.self, from: "tru_".data(using: String._Encoding.utf8)!) } _checkExpectedThrownDataCorruptionUnderlyingError(contains: "Unexpected character") { - let _ = try JSONDecoder().decode(Bool.self, from: "fals_".data(using: .utf8)!) - let _ = try json5Decoder.decode(Bool.self, from: "fals_".data(using: .utf8)!) + let _ = try JSONDecoder().decode(Bool.self, from: "fals_".data(using: String._Encoding.utf8)!) + let _ = try json5Decoder.decode(Bool.self, from: "fals_".data(using: String._Encoding.utf8)!) } _checkExpectedThrownDataCorruptionUnderlyingError(contains: "Unexpected character") { - let _ = try JSONDecoder().decode(Bool?.self, from: "nul_".data(using: .utf8)!) - let _ = try json5Decoder.decode(Bool?.self, from: "nul_".data(using: .utf8)!) + let _ = try JSONDecoder().decode(Bool?.self, from: "nul_".data(using: String._Encoding.utf8)!) + let _ = try json5Decoder.decode(Bool?.self, from: "nul_".data(using: String._Encoding.utf8)!) } } - @Test func topLevelNumberFragmentsWithJunkDigitCharacters() throws { - let fullData = "3.141596".data(using: .utf8)! + func test_topLevelNumberFragmentsWithJunkDigitCharacters() { + let fullData = "3.141596".data(using: String._Encoding.utf8)! let partialData = fullData[0..<4] - #expect(try 3.14 == JSONDecoder().decode(Double.self, from: partialData)) + XCTAssertEqual(3.14, try JSONDecoder().decode(Double.self, from: partialData)) } - @Test - @MainActor // Deeply recursive tests which requires running on the main thread which has a higher stack size limit - func depthTraversal() { + func test_depthTraversal() { struct SuperNestedArray : Decodable { init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() @@ -1299,50 +1305,44 @@ private struct JSONEncoderTests { let jsonGood = String(repeating: "[", count: MAX_DEPTH / 2) + String(repeating: "]", count: MAX_DEPTH / 2) let jsonBad = String(repeating: "[", count: MAX_DEPTH + 1) + String(repeating: "]", count: MAX_DEPTH + 1) - #expect(throws: Never.self) { - try JSONDecoder().decode(SuperNestedArray.self, from: jsonGood.data(using: .utf8)!) - } - #expect(throws: (any Error).self) { - try JSONDecoder().decode(SuperNestedArray.self, from: jsonBad.data(using: .utf8)!) - } + XCTAssertNoThrow(try JSONDecoder().decode(SuperNestedArray.self, from: jsonGood.data(using: String._Encoding.utf8)!)) + XCTAssertThrowsError(try JSONDecoder().decode(SuperNestedArray.self, from: jsonBad.data(using: String._Encoding.utf8)!)) } - @Test func jsonPermitsTrailingCommas() throws { + func test_JSONPermitsTrailingCommas() { // Trailing commas aren't valid JSON and should never be emitted, but are syntactically unambiguous and are allowed by // most parsers for ease of use. let json = "{\"key\" : [ true, ],}" - let data = json.data(using: .utf8)! + let data = json.data(using: String._Encoding.utf8)! - let result = try JSONDecoder().decode([String:[Bool]].self, from: data) + let result = try! JSONDecoder().decode([String:[Bool]].self, from: data) let expected = ["key" : [true]] - #expect(result == expected) + XCTAssertEqual(result, expected) } - @Test func whitespaceOnlyData() { - let data = " ".data(using: .utf8)! - #expect(throws: (any Error).self) { - try JSONDecoder().decode(Int.self, from: data) - } + func test_whitespaceOnlyData() { + let data = " ".data(using: String._Encoding.utf8)! + XCTAssertThrowsError(try JSONDecoder().decode(Int.self, from: data)) } - @Test func smallFloatNumber() { + func test_smallFloatNumber() { _testRoundTrip(of: [["magic_number" : 7.45673334164903e-115]]) } - @Test func largeIntegerNumber() throws { + func test_largeIntegerNumber() { let num : UInt64 = 6032314514195021674 let json = "{\"a\":\(num)}" - let data = json.data(using: .utf8)! + let data = json.data(using: String._Encoding.utf8)! - let result = try JSONDecoder().decode([String:UInt64].self, from: data) - let number = try #require(result["a"]) - #expect(number == num) + let result = try! JSONDecoder().decode([String:UInt64].self, from: data) + let number = result["a"]! + XCTAssertEqual(number, num) } - @Test func largeIntegerNumberIsNotRoundedToNearestDoubleWhenDecodingAsAnInteger() { - #expect(Double(sign: .plus, exponent: 63, significand: 1).ulp == 2048) - #expect(Double(sign: .plus, exponent: 64, significand: 1).ulp == 4096) + func test_largeIntegerNumberIsNotRoundedToNearestDoubleWhenDecodingAsAnInteger() { + XCTAssertEqual(Double(sign: .plus, exponent: 63, significand: 1).ulp, 2048) + XCTAssertEqual(Double(sign: .plus, exponent: 64, significand: 1).ulp, 4096) let int64s: [(String, Int64?)] = [ ("-9223372036854776833", nil), // -2^63 - 1025 (Double: -2^63 - 2048) @@ -1373,18 +1373,18 @@ private struct JSONEncoderTests { decoder.allowsJSON5 = json5 for (json, value) in int64s { - let result = try? decoder.decode(Int64.self, from: json.data(using: .utf8)!) - #expect(result == value, "Unexpected \(decoder) result for input \"\(json)\"") + let result = try? decoder.decode(Int64.self, from: json.data(using: String._Encoding.utf8)!) + XCTAssertEqual(result, value, "Unexpected \(decoder) result for input \"\(json)\"") } for (json, value) in uint64s { - let result = try? decoder.decode(UInt64.self, from: json.data(using: .utf8)!) - #expect(result == value, "Unexpected \(decoder) result for input \"\(json)\"") + let result = try? decoder.decode(UInt64.self, from: json.data(using: String._Encoding.utf8)!) + XCTAssertEqual(result, value, "Unexpected \(decoder) result for input \"\(json)\"") } } } - @Test func roundTrippingExtremeValues() { + func test_roundTrippingExtremeValues() { struct Numbers : Codable, Equatable { let floats : [Float] let doubles : [Double] @@ -1393,30 +1393,32 @@ private struct JSONEncoderTests { _testRoundTrip(of: testValue) } - @Test(arguments: [ - Int128.min, - Int128.min + 1, - -0x1_0000_0000_0000_0000, - 0x0_8000_0000_0000_0000, - -1, - 0, - 0x7fff_ffff_ffff_ffff, - 0x8000_0000_0000_0000, - 0xffff_ffff_ffff_ffff, - 0x1_0000_0000_0000_0000, - .max - ]) - func roundTrippingInt128(i128: Int128) { - _testRoundTrip(of: i128) + func test_roundTrippingInt128() { + let values = [ + Int128.min, + Int128.min + 1, + -0x1_0000_0000_0000_0000, + 0x0_8000_0000_0000_0000, + -1, + 0, + 0x7fff_ffff_ffff_ffff, + 0x8000_0000_0000_0000, + 0xffff_ffff_ffff_ffff, + 0x1_0000_0000_0000_0000, + .max + ] + for i128 in values { + _testRoundTrip(of: i128) + } } - @Test func int128SlowPath() throws { + func test_Int128SlowPath() { let decoder = JSONDecoder() let work: [Int128] = [18446744073709551615, -18446744073709551615] for value in work { // force the slow-path by appending ".0" - let json = "\(value).0".data(using: .utf8)! - #expect(try value == decoder.decode(Int128.self, from: json)) + let json = "\(value).0".data(using: String._Encoding.utf8)! + XCTAssertEqual(value, try? decoder.decode(Int128.self, from: json)) } // These should work, but making them do so probably requires // rewriting the slow path to use a dedicated parser. For now, @@ -1427,35 +1429,35 @@ private struct JSONEncoderTests { ] for value in shouldWorkButDontYet { // force the slow-path by appending ".0" - let json = "\(value).0".data(using: .utf8)! - #expect(throws: (any Error).self) { - try decoder.decode(Int128.self, from: json) - } + let json = "\(value).0".data(using: String._Encoding.utf8)! + XCTAssertThrowsError(try decoder.decode(Int128.self, from: json)) } } - @Test(arguments: [ - UInt128.zero, - 1, - 0x0000_0000_0000_0000_7fff_ffff_ffff_ffff, - 0x0000_0000_0000_0000_8000_0000_0000_0000, - 0x0000_0000_0000_0000_ffff_ffff_ffff_ffff, - 0x0000_0000_0000_0001_0000_0000_0000_0000, - 0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff, - 0x8000_0000_0000_0000_0000_0000_0000_0000, - .max - ]) - func roundTrippingUInt128(u128: UInt128) { - _testRoundTrip(of: u128) + func test_roundTrippingUInt128() { + let values = [ + UInt128.zero, + 1, + 0x0000_0000_0000_0000_7fff_ffff_ffff_ffff, + 0x0000_0000_0000_0000_8000_0000_0000_0000, + 0x0000_0000_0000_0000_ffff_ffff_ffff_ffff, + 0x0000_0000_0000_0001_0000_0000_0000_0000, + 0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff, + 0x8000_0000_0000_0000_0000_0000_0000_0000, + .max + ] + for u128 in values { + _testRoundTrip(of: u128) + } } - @Test func uint128SlowPath() throws { + func test_UInt128SlowPath() { let decoder = JSONDecoder() let work: [UInt128] = [18446744073709551615] for value in work { // force the slow-path by appending ".0" - let json = "\(value).0".data(using: .utf8)! - #expect(try value == decoder.decode(UInt128.self, from: json)) + let json = "\(value).0".data(using: String._Encoding.utf8)! + XCTAssertEqual(value, try? decoder.decode(UInt128.self, from: json)) } // These should work, but making them do so probably requires // rewriting the slow path to use a dedicated parser. For now, @@ -1466,14 +1468,12 @@ private struct JSONEncoderTests { ] for value in shouldWorkButDontYet { // force the slow-path by appending ".0" - let json = "\(value).0".data(using: .utf8)! - #expect(throws: (any Error).self) { - try decoder.decode(UInt128.self, from: json) - } + let json = "\(value).0".data(using: String._Encoding.utf8)! + XCTAssertThrowsError(try decoder.decode(UInt128.self, from: json)) } } - @Test func roundTrippingDoubleValues() { + func test_roundTrippingDoubleValues() { struct Numbers : Codable, Equatable { let doubles : [String:Double] let decimals : [String:Decimal] @@ -1508,14 +1508,12 @@ private struct JSONEncoderTests { _testRoundTrip(of: testValue) } - @Test func decodeLargeDoubleAsInteger() throws { - let data = try JSONEncoder().encode(Double.greatestFiniteMagnitude) - #expect(throws: (any Error).self) { - try JSONDecoder().decode(UInt64.self, from: data) - } + func test_decodeLargeDoubleAsInteger() { + let data = try! JSONEncoder().encode(Double.greatestFiniteMagnitude) + XCTAssertThrowsError(try JSONDecoder().decode(UInt64.self, from: data)) } - @Test func localeDecimalPolicyIndependence() throws { + func test_localeDecimalPolicyIndependence() { var currentLocale: UnsafeMutablePointer? = nil if let localePtr = setlocale(LC_ALL, nil) { currentLocale = strdup(localePtr) @@ -1530,110 +1528,114 @@ private struct JSONEncoderTests { let orig = ["decimalValue" : 1.1] - setlocale(LC_ALL, "fr_FR") - let data = try JSONEncoder().encode(orig) + do { + setlocale(LC_ALL, "fr_FR") + let data = try JSONEncoder().encode(orig) #if os(Windows) - setlocale(LC_ALL, "en_US") + setlocale(LC_ALL, "en_US") #else - setlocale(LC_ALL, "en_US_POSIX") + setlocale(LC_ALL, "en_US_POSIX") #endif - let decoded = try JSONDecoder().decode(type(of: orig).self, from: data) + let decoded = try JSONDecoder().decode(type(of: orig).self, from: data) - #expect(orig == decoded) + XCTAssertEqual(orig, decoded) + } catch { + XCTFail("Error: \(error)") + } } - @Test func whitespace() { + func test_whitespace() { let tests : [(json: String, expected: [String:Bool])] = [ ("{\"v\"\n : true}", ["v":true]), ("{\"v\"\r\n : true}", ["v":true]), ("{\"v\"\r : true}", ["v":true]) ] for test in tests { - let data = test.json.data(using: .utf8)! + let data = test.json.data(using: String._Encoding.utf8)! let decoded = try! JSONDecoder().decode([String:Bool].self, from: data) - #expect(test.expected == decoded) + XCTAssertEqual(test.expected, decoded) } } - @Test func assumesTopLevelDictionary() throws { + func test_assumesTopLevelDictionary() { let decoder = JSONDecoder() decoder.assumesTopLevelDictionary = true let json = "\"x\" : 42" - var result = try decoder.decode([String:Int].self, from: json.data(using: .utf8)!) - #expect(result == ["x" : 42]) + do { + let result = try decoder.decode([String:Int].self, from: json.data(using: String._Encoding.utf8)!) + XCTAssertEqual(result, ["x" : 42]) + } catch { + XCTFail("Error thrown while decoding assumed top-level dictionary: \(error)") + } let jsonWithBraces = "{\"x\" : 42}" - result = try decoder.decode([String:Int].self, from: jsonWithBraces.data(using: .utf8)!) - #expect(result == ["x" : 42]) + do { + let result = try decoder.decode([String:Int].self, from: jsonWithBraces.data(using: String._Encoding.utf8)!) + XCTAssertEqual(result, ["x" : 42]) + } catch { + XCTFail("Error thrown while decoding assumed top-level dictionary: \(error)") + } - result = try decoder.decode([String:Int].self, from: Data()) - #expect(result == [:]) + do { + let result = try decoder.decode([String:Int].self, from: Data()) + XCTAssertEqual(result, [:]) + } catch { + XCTFail("Error thrown while decoding empty assumed top-level dictionary: \(error)") + } let jsonWithEndBraceOnly = "\"x\" : 42}" - #expect(throws: (any Error).self) { - try decoder.decode([String:Int].self, from: jsonWithEndBraceOnly.data(using: .utf8)!) - } + XCTAssertThrowsError(try decoder.decode([String:Int].self, from: jsonWithEndBraceOnly.data(using: String._Encoding.utf8)!)) let jsonWithStartBraceOnly = "{\"x\" : 42" - #expect(throws: (any Error).self) { - try decoder.decode([String:Int].self, from: jsonWithStartBraceOnly.data(using: .utf8)!) - } + XCTAssertThrowsError(try decoder.decode([String:Int].self, from: jsonWithStartBraceOnly.data(using: String._Encoding.utf8)!)) } - @Test func bomPrefixes() throws { + func test_BOMPrefixes() { let json = "\"👍🏻\"" let decoder = JSONDecoder() // UTF-8 BOM let utf8_BOM = Data([0xEF, 0xBB, 0xBF]) - #expect(try "👍🏻" == decoder.decode(String.self, from: utf8_BOM + json.data(using: .utf8)!)) + XCTAssertEqual("👍🏻", try decoder.decode(String.self, from: utf8_BOM + json.data(using: String._Encoding.utf8)!)) // UTF-16 BE let utf16_BE_BOM = Data([0xFE, 0xFF]) - #expect(try "👍🏻" == decoder.decode(String.self, from: utf16_BE_BOM + json.data(using: .utf16BigEndian)!)) + XCTAssertEqual("👍🏻", try decoder.decode(String.self, from: utf16_BE_BOM + json.data(using: String._Encoding.utf16BigEndian)!)) // UTF-16 LE let utf16_LE_BOM = Data([0xFF, 0xFE]) - #expect(try "👍🏻" == decoder.decode(String.self, from: utf16_LE_BOM + json.data(using: .utf16LittleEndian)!)) + XCTAssertEqual("👍🏻", try decoder.decode(String.self, from: utf16_LE_BOM + json.data(using: String._Encoding.utf16LittleEndian)!)) // UTF-32 BE let utf32_BE_BOM = Data([0x0, 0x0, 0xFE, 0xFF]) - #expect(try "👍🏻" == decoder.decode(String.self, from: utf32_BE_BOM + json.data(using: .utf32BigEndian)!)) + XCTAssertEqual("👍🏻", try decoder.decode(String.self, from: utf32_BE_BOM + json.data(using: String._Encoding.utf32BigEndian)!)) // UTF-32 LE let utf32_LE_BOM = Data([0xFE, 0xFF, 0, 0]) - #expect(try "👍🏻" == decoder.decode(String.self, from: utf32_LE_BOM + json.data(using: .utf32LittleEndian)!)) + XCTAssertEqual("👍🏻", try decoder.decode(String.self, from: utf32_LE_BOM + json.data(using: String._Encoding.utf32LittleEndian)!)) // Try some mismatched BOMs - #expect(throws: (any Error).self) { - try decoder.decode(String.self, from: utf32_LE_BOM + json.data(using: .utf32BigEndian)!) - } + XCTAssertThrowsError(try decoder.decode(String.self, from: utf32_LE_BOM + json.data(using: String._Encoding.utf32BigEndian)!)) - #expect(throws: (any Error).self) { - try decoder.decode(String.self, from: utf16_BE_BOM + json.data(using: .utf32LittleEndian)!) - } + XCTAssertThrowsError(try decoder.decode(String.self, from: utf16_BE_BOM + json.data(using: String._Encoding.utf32LittleEndian)!)) - #expect(throws: (any Error).self) { - try decoder.decode(String.self, from: utf8_BOM + json.data(using: .utf16BigEndian)!) - } + XCTAssertThrowsError(try decoder.decode(String.self, from: utf8_BOM + json.data(using: String._Encoding.utf16BigEndian)!)) } - @Test func invalidKeyUTF8() { + func test_invalidKeyUTF8() { // {"key[255]":"value"} // The invalid UTF-8 byte sequence in the key should trigger a thrown error, not a crash. let data = Data([123, 34, 107, 101, 121, 255, 34, 58, 34, 118, 97, 108, 117, 101, 34, 125]) struct Example: Decodable { let key: String } - #expect(throws: (any Error).self) { - try JSONDecoder().decode(Example.self, from: data) - } + XCTAssertThrowsError(try JSONDecoder().decode(Example.self, from: data)) } - @Test func valueNotFoundError() { + func test_valueNotFoundError() { struct ValueNotFound : Decodable { let a: Bool let nope: String? @@ -1655,36 +1657,28 @@ private struct JSONEncoderTests { } } } - let json = "{\"a\":true}".data(using: .utf8)! + let json = "{\"a\":true}".data(using: String._Encoding.utf8)! // The expected valueNotFound error is swalled by the init(from:) implementation. - #expect(throws: Never.self) { - try JSONDecoder().decode(ValueNotFound.self, from: json) - } + XCTAssertNoThrow(try JSONDecoder().decode(ValueNotFound.self, from: json)) } - @Test func infiniteDate() { + func test_infiniteDate() { let date = Date(timeIntervalSince1970: .infinity) let encoder = JSONEncoder() encoder.dateEncodingStrategy = .deferredToDate - #expect(throws: (any Error).self) { - try encoder.encode([date]) - } + XCTAssertThrowsError(try encoder.encode([date])) encoder.dateEncodingStrategy = .secondsSince1970 - #expect(throws: (any Error).self) { - try encoder.encode([date]) - } + XCTAssertThrowsError(try encoder.encode([date])) encoder.dateEncodingStrategy = .millisecondsSince1970 - #expect(throws: (any Error).self) { - try encoder.encode([date]) - } + XCTAssertThrowsError(try encoder.encode([date])) } - @Test func typeEncodesNothing() { + func test_typeEncodesNothing() { struct EncodesNothing : Encodable { func encode(to encoder: Encoder) throws { // Intentionally nothing. @@ -1692,20 +1686,18 @@ private struct JSONEncoderTests { } let enc = JSONEncoder() - #expect(throws: (any Error).self) { - try enc.encode(EncodesNothing()) - } + XCTAssertThrowsError(try enc.encode(EncodesNothing())) // Unknown if the following behavior is strictly correct, but it's what the prior implementation does, so this test exists to make sure we maintain compatibility. let arrayData = try! enc.encode([EncodesNothing()]) - #expect("[{}]" == String(data: arrayData, encoding: .utf8)) + XCTAssertEqual("[{}]", String(data: arrayData, encoding: .utf8)) let objectData = try! enc.encode(["test" : EncodesNothing()]) - #expect("{\"test\":{}}" == String(data: objectData, encoding: .utf8)) + XCTAssertEqual("{\"test\":{}}", String(data: objectData, encoding: .utf8)) } - @Test func superEncoders() throws { + func test_superEncoders() { struct SuperEncoding : Encodable { enum CodingKeys: String, CodingKey { case firstSuper @@ -1739,16 +1731,16 @@ private struct JSONEncoderTests { // NOTE!!! At present, the order in which the values in the unkeyed container's superEncoders above get inserted into the resulting array depends on the order in which the superEncoders are deinit'd!! This can result in some very unexpected results, and this pattern is not recommended. This test exists just to verify compatibility. } } - let data = try JSONEncoder().encode(SuperEncoding()) + let data = try! JSONEncoder().encode(SuperEncoding()) let string = String(data: data, encoding: .utf8)! - #expect(string.contains("\"firstSuper\":\"First\"")) - #expect(string.contains("\"secondSuper\":\"Second\"")) - #expect(string.contains("[0,\"First\",\"Second\",42]")) - #expect(string.contains("{\"direct\":\"super\"}")) + XCTAssertTrue(string.contains("\"firstSuper\":\"First\"")) + XCTAssertTrue(string.contains("\"secondSuper\":\"Second\"")) + XCTAssertTrue(string.contains("[0,\"First\",\"Second\",42]")) + XCTAssertTrue(string.contains("{\"direct\":\"super\"}")) } - @Test func redundantKeys() throws { + func testRedundantKeys() { // Last encoded key wins. struct RedundantEncoding : Encodable { @@ -1781,26 +1773,26 @@ private struct JSONEncoderTests { } } } - var data = try JSONEncoder().encode(RedundantEncoding(replacedType: .value, useSuperEncoder: false)) - #expect(String(data: data, encoding: .utf8) == ("{\"key\":42}")) + var data = try! JSONEncoder().encode(RedundantEncoding(replacedType: .value, useSuperEncoder: false)) + XCTAssertEqual(String(data: data, encoding: .utf8), ("{\"key\":42}")) - data = try JSONEncoder().encode(RedundantEncoding(replacedType: .value, useSuperEncoder: true)) - #expect(String(data: data, encoding: .utf8) == ("{\"key\":42}")) + data = try! JSONEncoder().encode(RedundantEncoding(replacedType: .value, useSuperEncoder: true)) + XCTAssertEqual(String(data: data, encoding: .utf8), ("{\"key\":42}")) - data = try JSONEncoder().encode(RedundantEncoding(replacedType: .keyedContainer, useSuperEncoder: false)) - #expect(String(data: data, encoding: .utf8) == ("{\"key\":42}")) + data = try! JSONEncoder().encode(RedundantEncoding(replacedType: .keyedContainer, useSuperEncoder: false)) + XCTAssertEqual(String(data: data, encoding: .utf8), ("{\"key\":42}")) - data = try JSONEncoder().encode(RedundantEncoding(replacedType: .keyedContainer, useSuperEncoder: true)) - #expect(String(data: data, encoding: .utf8) == ("{\"key\":42}")) + data = try! JSONEncoder().encode(RedundantEncoding(replacedType: .keyedContainer, useSuperEncoder: true)) + XCTAssertEqual(String(data: data, encoding: .utf8), ("{\"key\":42}")) - data = try JSONEncoder().encode(RedundantEncoding(replacedType: .unkeyedContainer, useSuperEncoder: false)) - #expect(String(data: data, encoding: .utf8) == ("{\"key\":42}")) + data = try! JSONEncoder().encode(RedundantEncoding(replacedType: .unkeyedContainer, useSuperEncoder: false)) + XCTAssertEqual(String(data: data, encoding: .utf8), ("{\"key\":42}")) - data = try JSONEncoder().encode(RedundantEncoding(replacedType: .unkeyedContainer, useSuperEncoder: true)) - #expect(String(data: data, encoding: .utf8) == ("{\"key\":42}")) + data = try! JSONEncoder().encode(RedundantEncoding(replacedType: .unkeyedContainer, useSuperEncoder: true)) + XCTAssertEqual(String(data: data, encoding: .utf8), ("{\"key\":42}")) } - @Test func SR17581_codingEmptyDictionaryWithNonstringKeyDoesRoundtrip() throws { + func test_SR17581_codingEmptyDictionaryWithNonstringKeyDoesRoundtrip() throws { struct Something: Codable { struct Key: Codable, Hashable { var x: String @@ -1830,11 +1822,11 @@ private struct JSONEncoderTests { let toEncode = Something(dict: [:]) let data = try JSONEncoder().encode(toEncode) let result = try JSONDecoder().decode(Something.self, from: data) - #expect(result.dict.count == 0) + XCTAssertEqual(result.dict.count, 0) } - #if FOUNDATION_EXIT_TESTS - @Test func preconditionFailuresForContainerReplacement() async { + // None of these tests can be run in our automatic test suites right now, because they are expected to hit a preconditionFailure. They can only be verified manually. + func disabled_testPreconditionFailuresForContainerReplacement() { struct RedundantEncoding : Encodable { enum Subcase { case replaceValueWithKeyedContainer @@ -1868,45 +1860,36 @@ private struct JSONEncoderTests { } } } - await #expect(processExitsWith: .failure) { - let _ = try JSONEncoder().encode(RedundantEncoding(subcase: .replaceValueWithKeyedContainer)) - } - await #expect(processExitsWith: .failure) { - let _ = try JSONEncoder().encode(RedundantEncoding(subcase: .replaceValueWithUnkeyedContainer)) - } - await #expect(processExitsWith: .failure) { - let _ = try JSONEncoder().encode(RedundantEncoding(subcase: .replaceKeyedContainerWithUnkeyed)) - } - await #expect(processExitsWith: .failure) { - let _ = try JSONEncoder().encode(RedundantEncoding(subcase: .replaceUnkeyedContainerWithKeyed)) - } + let _ = try! JSONEncoder().encode(RedundantEncoding(subcase: .replaceValueWithKeyedContainer)) +// let _ = try! JSONEncoder().encode(RedundantEncoding(subcase: .replaceValueWithUnkeyedContainer)) +// let _ = try! JSONEncoder().encode(RedundantEncoding(subcase: .replaceKeyedContainerWithUnkeyed)) +// let _ = try! JSONEncoder().encode(RedundantEncoding(subcase: .replaceUnkeyedContainerWithKeyed)) } - #endif - @Test func decodeIfPresent() throws { + func test_decodeIfPresent() throws { let emptyDictJSON = try JSONEncoder().encode(DecodeIfPresentAllTypes.allNils) let testEmptyDict = try JSONDecoder().decode(DecodeIfPresentAllTypes.self, from: emptyDictJSON) - #expect(testEmptyDict == .allNils) + XCTAssertEqual(testEmptyDict, .allNils) let allNullDictJSON = try JSONEncoder().encode(DecodeIfPresentAllTypes.allNils) let testAllNullDict = try JSONDecoder().decode(DecodeIfPresentAllTypes.self, from: allNullDictJSON) - #expect(testAllNullDict == .allNils) + XCTAssertEqual(testAllNullDict, .allNils) let allOnesDictJSON = try JSONEncoder().encode(DecodeIfPresentAllTypes.allOnes) let testAllOnesDict = try JSONDecoder().decode(DecodeIfPresentAllTypes.self, from: allOnesDictJSON) - #expect(testAllOnesDict == .allOnes) + XCTAssertEqual(testAllOnesDict, .allOnes) let emptyArrayJSON = try JSONEncoder().encode(DecodeIfPresentAllTypes.allNils) let testEmptyArray = try JSONDecoder().decode(DecodeIfPresentAllTypes.self, from: emptyArrayJSON) - #expect(testEmptyArray == .allNils) + XCTAssertEqual(testEmptyArray, .allNils) let allNullArrayJSON = try JSONEncoder().encode(DecodeIfPresentAllTypes.allNils) let testAllNullArray = try JSONDecoder().decode(DecodeIfPresentAllTypes.self, from: allNullArrayJSON) - #expect(testAllNullArray == .allNils) + XCTAssertEqual(testAllNullArray, .allNils) let allOnesArrayJSON = try JSONEncoder().encode(DecodeIfPresentAllTypes.allOnes) let testAllOnesArray = try JSONDecoder().decode(DecodeIfPresentAllTypes.self, from: allOnesArrayJSON) - #expect(testAllOnesArray == .allOnes) + XCTAssertEqual(testAllOnesArray, .allOnes) } } @@ -1918,7 +1901,7 @@ extension JSONEncoderTests { return decoder } - @Test func json5Numbers() { + func test_json5Numbers() { let decoder = json5Decoder let successfulIntegers: [(String,Int)] = [ @@ -1946,9 +1929,11 @@ extension JSONEncoderTests { ("1E+02", 100), ] for (json, expected) in successfulIntegers { - #expect(throws: Never.self, "Error when parsing input \"\(json)\"") { - let val = try decoder.decode(Int.self, from: json.data(using: .utf8)!) - #expect(val == expected, "Wrong value parsed from input \"\(json)\"") + do { + let val = try decoder.decode(Int.self, from: json.data(using: String._Encoding.utf8)!) + XCTAssertEqual(val, expected, "Wrong value parsed from input \"\(json)\"") + } catch { + XCTFail("Error when parsing input \"\(json)\": \(error)") } } @@ -1988,13 +1973,15 @@ extension JSONEncoderTests { ("+0X1f", Double(+0x1f)), ] for (json, expected) in successfulDoubles { - #expect(throws: Never.self, "Error when parsing input \"\(json)\"") { - let val = try decoder.decode(Double.self, from: json.data(using: .utf8)!) + do { + let val = try decoder.decode(Double.self, from: json.data(using: String._Encoding.utf8)!) if expected.isNaN { - #expect(val.isNaN, "Wrong value \(val) parsed from input \"\(json)\"") + XCTAssertTrue(val.isNaN, "Wrong value \(val) parsed from input \"\(json)\"") } else { - #expect(val == expected, "Wrong value parsed from input \"\(json)\"") + XCTAssertEqual(val, expected, "Wrong value parsed from input \"\(json)\"") } + } catch { + XCTFail("Error when parsing input \"\(json)\": \(error)") } } @@ -2020,9 +2007,10 @@ extension JSONEncoderTests { "-1E ", ] for json in unsuccessfulIntegers { - #expect(throws: (any Error).self, "Expected failure for input \"\(json)\"") { - try decoder.decode(Int.self, from: json.data(using: .utf8)!) - } + do { + let _ = try decoder.decode(Int.self, from: json.data(using: String._Encoding.utf8)!) + XCTFail("Expected failure for input \"\(json)\"") + } catch { } } let unsuccessfulDoubles = [ @@ -2050,13 +2038,14 @@ extension JSONEncoderTests { "0xFFFFFFFFFFFFFFFFFFFFFF", ]; for json in unsuccessfulDoubles { - #expect(throws: (any Error).self, "Expected failure for input \"\(json)\"") { - try decoder.decode(Double.self, from: json.data(using: .utf8)!) - } + do { + let _ = try decoder.decode(Double.self, from: json.data(using: String._Encoding.utf8)!) + XCTFail("Expected failure for input \"\(json)\"") + } catch { } } } - @Test func json5Null() { + func test_json5Null() { let validJSON = "null" let invalidJSON = [ "Null", @@ -2067,18 +2056,14 @@ extension JSONEncoderTests { "nu " ] - #expect(throws: Never.self) { - try json5Decoder.decode(NullReader.self, from: validJSON.data(using: .utf8)!) - } + XCTAssertNoThrow(try json5Decoder.decode(NullReader.self, from: validJSON.data(using: String._Encoding.utf8)!)) for json in invalidJSON { - #expect(throws: (any Error).self, "Expected failure while decoding input \"\(json)\"") { - try json5Decoder.decode(NullReader.self, from: json.data(using: .utf8)!) - } + XCTAssertThrowsError(try json5Decoder.decode(NullReader.self, from: json.data(using: String._Encoding.utf8)!), "Expected failure while decoding input \"\(json)\"") } } - @Test func json5EsotericErrors() { + func test_json5EsotericErrors() { // All of the following should fail let arrayStrings = [ "[", @@ -2107,23 +2092,17 @@ extension JSONEncoderTests { [.init(ascii: "{"), 0xf0, 0x80, 0x80], // Invalid UTF-8: Initial byte of 3-byte sequence with only one continuation ] for json in arrayStrings { - #expect(throws: (any Error).self, "Expected error for input \"\(json)\"") { - try json5Decoder.decode([String].self, from: json.data(using: .utf8)!) - } + XCTAssertThrowsError(try json5Decoder.decode([String].self, from: json.data(using: String._Encoding.utf8)!), "Expected error for input \"\(json)\"") } for json in objectStrings { - #expect(throws: (any Error).self, "Expected error for input \(json)") { - try json5Decoder.decode([String:Bool].self, from: json.data(using: .utf8)!) - } + XCTAssertThrowsError(try json5Decoder.decode([String:Bool].self, from: json.data(using: String._Encoding.utf8)!), "Expected error for input \(json)") } for json in objectCharacterArrays { - #expect(throws: (any Error).self, "Expected error for input \(json)") { - try json5Decoder.decode([String:Bool].self, from: Data(json)) - } + XCTAssertThrowsError(try json5Decoder.decode([String:Bool].self, from: Data(json)), "Expected error for input \(json)") } } - @Test func json5Strings() { + func test_json5Strings() { let stringsToTrues = [ "{v\n : true}", "{v \n : true}", @@ -2164,23 +2143,21 @@ extension JSONEncoderTests { ] for json in stringsToTrues { - #expect(throws: Never.self, "Failed to parse \"\(json)\"") { - try json5Decoder.decode([String:Bool].self, from: json.data(using: .utf8)!) - } + XCTAssertNoThrow(try json5Decoder.decode([String:Bool].self, from: json.data(using: String._Encoding.utf8)!), "Failed to parse \"\(json)\"") } for (json, expected) in stringsToStrings { do { - let decoded = try json5Decoder.decode([String:String].self, from: json.data(using: .utf8)!) - #expect(expected == decoded["v"]) + let decoded = try json5Decoder.decode([String:String].self, from: json.data(using: String._Encoding.utf8)!) + XCTAssertEqual(expected, decoded["v"]) } catch { if let expected { - Issue.record("Expected \(expected) for input \"\(json)\", but failed with \(error)") + XCTFail("Expected \(expected) for input \"\(json)\", but failed with \(error)") } } } } - @Test func json5AssumedDictionary() { + func test_json5AssumedDictionary() { let decoder = json5Decoder decoder.assumesTopLevelDictionary = true @@ -2209,11 +2186,11 @@ extension JSONEncoderTests { ] for (json, expected) in stringsToString { do { - let decoded = try decoder.decode([String:String].self, from: json.data(using: .utf8)!) - #expect(expected == decoded) + let decoded = try decoder.decode([String:String].self, from: json.data(using: String._Encoding.utf8)!) + XCTAssertEqual(expected, decoded) } catch { if let expected { - Issue.record("Expected \(expected) for input \"\(json)\", but failed with \(error)") + XCTFail("Expected \(expected) for input \"\(json)\", but failed with \(error)") } } } @@ -2231,26 +2208,22 @@ extension JSONEncoderTests { "hello: \"world\", goodbye: {\"hi\":\"there\",},", // more than one value, nested dictionary, trailing comma 2 ] for json in stringsToNestedDictionary { - #expect(throws: Never.self, "Unexpected error for input \"\(json)\"") { - let decoded = try decoder.decode(HelloGoodbye.self, from: json.data(using: .utf8)!) - #expect(helloGoodbyeExpectedValue == decoded) + do { + let decoded = try decoder.decode(HelloGoodbye.self, from: json.data(using: String._Encoding.utf8)!) + XCTAssertEqual(helloGoodbyeExpectedValue, decoded) + } catch { + XCTFail("Expected \(helloGoodbyeExpectedValue) for input \"\(json)\", but failed with \(error)") } } - let arrayJSON = "[1,2,3]".data(using: .utf8)! // Assumed dictionary can't be an array - #expect(throws: (any Error).self) { - try decoder.decode([Int].self, from: arrayJSON) - } + let arrayJSON = "[1,2,3]".data(using: String._Encoding.utf8)! // Assumed dictionary can't be an array + XCTAssertThrowsError(try decoder.decode([Int].self, from: arrayJSON)) - let strFragmentJSON = "fragment".data(using: .utf8)! // Assumed dictionary can't be a fragment - #expect(throws: (any Error).self) { - try decoder.decode(String.self, from: strFragmentJSON) - } + let strFragmentJSON = "fragment".data(using: String._Encoding.utf8)! // Assumed dictionary can't be a fragment + XCTAssertThrowsError(try decoder.decode(String.self, from: strFragmentJSON)) - let numFragmentJSON = "42".data(using: .utf8)! // Assumed dictionary can't be a fragment - #expect(throws: (any Error).self) { - try decoder.decode(Int.self, from: numFragmentJSON) - } + let numFragmentJSON = "42".data(using: String._Encoding.utf8)! // Assumed dictionary can't be a fragment + XCTAssertThrowsError(try decoder.decode(Int.self, from: numFragmentJSON)) } enum JSON5SpecTestType { @@ -2274,7 +2247,7 @@ extension JSONEncoderTests { // MARK: - SnakeCase Tests extension JSONEncoderTests { - @Test func decodingKeyStrategyCamel() throws { + func testDecodingKeyStrategyCamel() { let fromSnakeCaseTests = [ ("", ""), // don't die on empty string ("a", "a"), // single character @@ -2313,30 +2286,30 @@ extension JSONEncoderTests { for test in fromSnakeCaseTests { // This JSON contains the camel case key that the test object should decode with, then it uses the snake case key (test.0) as the actual key for the boolean value. - let input = "{\"camelCaseKey\":\"\(test.1)\",\"\(test.0)\":true}".data(using: .utf8)! + let input = "{\"camelCaseKey\":\"\(test.1)\",\"\(test.0)\":true}".data(using: String._Encoding.utf8)! let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase - let result = try decoder.decode(DecodeMe.self, from: input) + let result = try! decoder.decode(DecodeMe.self, from: input) - #expect(result.found) + XCTAssertTrue(result.found) } } - @Test func encodingDictionaryStringKeyConversionUntouched() throws { + func testEncodingDictionaryStringKeyConversionUntouched() { let expected = "{\"leaveMeAlone\":\"test\"}" let toEncode: [String: String] = ["leaveMeAlone": "test"] let encoder = JSONEncoder() encoder.keyEncodingStrategy = .convertToSnakeCase - let resultData = try encoder.encode(toEncode) - let resultString = String(bytes: resultData, encoding: .utf8) + let resultData = try! encoder.encode(toEncode) + let resultString = String(bytes: resultData, encoding: String._Encoding.utf8) - #expect(expected == resultString) + XCTAssertEqual(expected, resultString) } - @Test func keyStrategySnakeGeneratedAndCustom() throws { + func testKeyStrategySnakeGeneratedAndCustom() { // Test that this works with a struct that has automatically generated keys struct DecodeMe4 : Codable { var thisIsCamelCase : String @@ -2348,74 +2321,72 @@ extension JSONEncoderTests { } // Decoding - let input = "{\"foo_bar\":\"test\",\"this_is_camel_case_too\":\"test2\"}".data(using: .utf8)! + let input = "{\"foo_bar\":\"test\",\"this_is_camel_case_too\":\"test2\"}".data(using: String._Encoding.utf8)! let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase - let decodingResult = try decoder.decode(DecodeMe4.self, from: input) + let decodingResult = try! decoder.decode(DecodeMe4.self, from: input) - #expect("test" == decodingResult.thisIsCamelCase) - #expect("test2" == decodingResult.thisIsCamelCaseToo) + XCTAssertEqual("test", decodingResult.thisIsCamelCase) + XCTAssertEqual("test2", decodingResult.thisIsCamelCaseToo) // Encoding let encoded = DecodeMe4(thisIsCamelCase: "test", thisIsCamelCaseToo: "test2") let encoder = JSONEncoder() encoder.keyEncodingStrategy = .convertToSnakeCase - let encodingResultData = try encoder.encode(encoded) - let encodingResultString = try #require(String(bytes: encodingResultData, encoding: .utf8)) - #expect(encodingResultString.contains("foo_bar")) - #expect(encodingResultString.contains("this_is_camel_case_too")) + let encodingResultData = try! encoder.encode(encoded) + let encodingResultString = String(bytes: encodingResultData, encoding: String._Encoding.utf8) + XCTAssertTrue(encodingResultString!.contains("foo_bar")) + XCTAssertTrue(encodingResultString!.contains("this_is_camel_case_too")) } - @Test func decodingDictionaryFailureKeyPathNested() { - let input = "{\"top_level\": {\"sub_level\": {\"nested_value\": {\"int_value\": \"not_an_int\"}}}}".data(using: .utf8)! + func testDecodingDictionaryFailureKeyPathNested() { + let input = "{\"top_level\": {\"sub_level\": {\"nested_value\": {\"int_value\": \"not_an_int\"}}}}".data(using: String._Encoding.utf8)! let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase do { _ = try decoder.decode([String: [String : DecodeFailureNested]].self, from: input) } catch DecodingError.typeMismatch(_, let context) { - #expect(4 == context.codingPath.count) - #expect("top_level" == context.codingPath[0].stringValue) - #expect("sub_level" == context.codingPath[1].stringValue) - #expect("nestedValue" == context.codingPath[2].stringValue) - #expect("intValue" == context.codingPath[3].stringValue) + XCTAssertEqual(4, context.codingPath.count) + XCTAssertEqual("top_level", context.codingPath[0].stringValue) + XCTAssertEqual("sub_level", context.codingPath[1].stringValue) + XCTAssertEqual("nestedValue", context.codingPath[2].stringValue) + XCTAssertEqual("intValue", context.codingPath[3].stringValue) } catch { - Issue.record("Unexpected error: \(String(describing: error))") + XCTFail("Unexpected error: \(String(describing: error))") } } - @Test func decodingKeyStrategyCamelGenerated() throws { + func testDecodingKeyStrategyCamelGenerated() { let encoded = DecodeMe3(thisIsCamelCase: "test") let encoder = JSONEncoder() encoder.keyEncodingStrategy = .convertToSnakeCase - let resultData = try encoder.encode(encoded) - let resultString = String(bytes: resultData, encoding: .utf8) - #expect("{\"this_is_camel_case\":\"test\"}" == resultString) + let resultData = try! encoder.encode(encoded) + let resultString = String(bytes: resultData, encoding: String._Encoding.utf8) + XCTAssertEqual("{\"this_is_camel_case\":\"test\"}", resultString) } - @Test func decodingStringExpectedType() { - let input = #"{"thisIsCamelCase": null}"#.data(using: .utf8)! - #expect { + func testDecodingStringExpectedType() { + let input = #"{"thisIsCamelCase": null}"#.data(using: String._Encoding.utf8)! + do { _ = try JSONDecoder().decode(DecodeMe3.self, from: input) - } throws: { - guard let decodingError = $0 as? DecodingError, - case let DecodingError.valueNotFound(expected, _) = decodingError else { - return false - } - return expected == String.self + } catch DecodingError.valueNotFound(let expected, _) { + XCTAssertTrue(expected == String.self) + } catch { + XCTFail("Unexpected error: \(String(describing: error))") } } - @Test func encodingKeyStrategySnakeGenerated() throws { + func testEncodingKeyStrategySnakeGenerated() { // Test that this works with a struct that has automatically generated keys - let input = "{\"this_is_camel_case\":\"test\"}".data(using: .utf8)! + let input = "{\"this_is_camel_case\":\"test\"}".data(using: String._Encoding.utf8)! let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase - let result = try decoder.decode(DecodeMe3.self, from: input) + let result = try! decoder.decode(DecodeMe3.self, from: input) - #expect("test" == result.thisIsCamelCase) + XCTAssertEqual("test", result.thisIsCamelCase) } - @Test func encodingDictionaryFailureKeyPath() { + func testEncodingDictionaryFailureKeyPath() { let toEncode: [String: EncodeFailure] = ["key": EncodeFailure(someValue: Double.nan)] let encoder = JSONEncoder() @@ -2423,15 +2394,15 @@ extension JSONEncoderTests { do { _ = try encoder.encode(toEncode) } catch EncodingError.invalidValue(_, let context) { - #expect(2 == context.codingPath.count) - #expect("key" == context.codingPath[0].stringValue) - #expect("someValue" == context.codingPath[1].stringValue) + XCTAssertEqual(2, context.codingPath.count) + XCTAssertEqual("key", context.codingPath[0].stringValue) + XCTAssertEqual("someValue", context.codingPath[1].stringValue) } catch { - Issue.record("Unexpected error: \(String(describing: error))") + XCTFail("Unexpected error: \(String(describing: error))") } } - @Test func encodingDictionaryFailureKeyPathNested() { + func testEncodingDictionaryFailureKeyPathNested() { let toEncode: [String: [String: EncodeFailureNested]] = ["key": ["sub_key": EncodeFailureNested(nestedValue: EncodeFailure(someValue: Double.nan))]] let encoder = JSONEncoder() @@ -2439,17 +2410,17 @@ extension JSONEncoderTests { do { _ = try encoder.encode(toEncode) } catch EncodingError.invalidValue(_, let context) { - #expect(4 == context.codingPath.count) - #expect("key" == context.codingPath[0].stringValue) - #expect("sub_key" == context.codingPath[1].stringValue) - #expect("nestedValue" == context.codingPath[2].stringValue) - #expect("someValue" == context.codingPath[3].stringValue) + XCTAssertEqual(4, context.codingPath.count) + XCTAssertEqual("key", context.codingPath[0].stringValue) + XCTAssertEqual("sub_key", context.codingPath[1].stringValue) + XCTAssertEqual("nestedValue", context.codingPath[2].stringValue) + XCTAssertEqual("someValue", context.codingPath[3].stringValue) } catch { - Issue.record("Unexpected error: \(String(describing: error))") + XCTFail("Unexpected error: \(String(describing: error))") } } - @Test func encodingKeyStrategySnake() throws { + func testEncodingKeyStrategySnake() { let toSnakeCaseTests = [ ("simpleOneTwo", "simple_one_two"), ("myURL", "my_url"), @@ -2488,22 +2459,22 @@ extension JSONEncoderTests { let encoder = JSONEncoder() encoder.keyEncodingStrategy = .convertToSnakeCase - let resultData = try encoder.encode(encoded) - let resultString = String(bytes: resultData, encoding: .utf8) + let resultData = try! encoder.encode(encoded) + let resultString = String(bytes: resultData, encoding: String._Encoding.utf8) - #expect(expected == resultString) + XCTAssertEqual(expected, resultString) } } - @Test func twoByteUTF16Inputs() throws { + func test_twoByteUTF16Inputs() { let json = "7" let decoder = JSONDecoder() - #expect(try 7 == decoder.decode(Int.self, from: json.data(using: .utf16BigEndian)!)) - #expect(try 7 == decoder.decode(Int.self, from: json.data(using: .utf16LittleEndian)!)) + XCTAssertEqual(7, try decoder.decode(Int.self, from: json.data(using: .utf16BigEndian)!)) + XCTAssertEqual(7, try decoder.decode(Int.self, from: json.data(using: .utf16LittleEndian)!)) } - private func _run_passTest(name: String, json5: Bool = false, type: T.Type, sourceLocation: SourceLocation = #_sourceLocation) { + private func _run_passTest(name: String, json5: Bool = false, type: T.Type) { let jsonData = testData(forResource: name, withExtension: json5 ? "json5" : "json" , subdirectory: json5 ? "JSON5/pass" : "JSON/pass")! let plistData = testData(forResource: name, withExtension: "plist", subdirectory: "JSON/pass") @@ -2513,7 +2484,7 @@ extension JSONEncoderTests { do { decoded = try decoder.decode(T.self, from: jsonData) } catch { - Issue.record("Pass test \"\(name)\" failed with error: \(error)", sourceLocation: sourceLocation) + XCTFail("Pass test \"\(name)\" failed with error: \(error)") return } @@ -2521,22 +2492,18 @@ extension JSONEncoderTests { prettyPrintEncoder.outputFormatting = .prettyPrinted for encoder in [JSONEncoder(), prettyPrintEncoder] { - #expect(throws: Never.self, sourceLocation: sourceLocation) { - let reencodedData = try encoder.encode(decoded) - let redecodedObjects = try decoder.decode(T.self, from: reencodedData) - #expect(decoded == redecodedObjects) - } + let reencodedData = try! encoder.encode(decoded) + let redecodedObjects = try! decoder.decode(T.self, from: reencodedData) + XCTAssertEqual(decoded, redecodedObjects) if let plistData { - #expect(throws: Never.self, sourceLocation: sourceLocation) { - let decodedPlistObjects = try PropertyListDecoder().decode(T.self, from: plistData) - #expect(decoded == decodedPlistObjects) - } + let decodedPlistObjects = try! PropertyListDecoder().decode(T.self, from: plistData) + XCTAssertEqual(decoded, decodedPlistObjects) } } } - @Test func jsonPassTests() { + func test_JSONPassTests() { _run_passTest(name: "pass1-utf8", type: JSONPass.Test1.self) _run_passTest(name: "pass1-utf16be", type: JSONPass.Test1.self) _run_passTest(name: "pass1-utf16le", type: JSONPass.Test1.self) @@ -2558,7 +2525,7 @@ extension JSONEncoderTests { _run_passTest(name: "pass15", type: JSONPass.Test15.self) } - @Test func json5PassJSONFiles() { + func test_json5PassJSONFiles() { _run_passTest(name: "example", json5: true, type: JSON5Pass.Example.self) _run_passTest(name: "hex", json5: true, type: JSON5Pass.Hex.self) _run_passTest(name: "numbers", json5: true, type: JSON5Pass.Numbers.self) @@ -2566,17 +2533,20 @@ extension JSONEncoderTests { _run_passTest(name: "whitespace", json5: true, type: JSON5Pass.Whitespace.self) } - private func _run_failTest(name: String, type: T.Type, sourceLocation: SourceLocation = #_sourceLocation) { + private func _run_failTest(name: String, type: T.Type) { let jsonData = testData(forResource: name, withExtension: "json", subdirectory: "JSON/fail")! let decoder = JSONDecoder() decoder.assumesTopLevelDictionary = true - #expect(throws: (any Error).self, "Decoding should have failed for invalid JSON data (test name: \(name))", sourceLocation: sourceLocation) { - try decoder.decode(T.self, from: jsonData) + do { + let _ = try decoder.decode(T.self, from: jsonData) + XCTFail("Decoding should have failed for invalid JSON data (test name: \(name))") + } catch { + print(error as NSError) } } - @Test func jsonFailTests() { + func test_JSONFailTests() { _run_failTest(name: "fail1", type: JSONFail.Test1.self) _run_failTest(name: "fail2", type: JSONFail.Test2.self) _run_failTest(name: "fail3", type: JSONFail.Test3.self) @@ -2620,7 +2590,7 @@ extension JSONEncoderTests { } - func _run_json5SpecTest(_ category: String, _ name: String, testType: JSON5SpecTestType, type: T.Type, sourceLocation: SourceLocation = #_sourceLocation) { + func _run_json5SpecTest(_ category: String, _ name: String, testType: JSON5SpecTestType, type: T.Type) { let subdirectory = "/JSON5/spec/\(category)" let ext = testType.fileExtension let jsonData = testData(forResource: name, withExtension: ext, subdirectory: subdirectory)! @@ -2631,53 +2601,47 @@ extension JSONEncoderTests { switch testType { case .json, .json5_foundationPermissiveJSON: // Valid JSON should remain valid JSON5 - #expect(throws: Never.self, sourceLocation: sourceLocation) { - _ = try json5.decode(type, from: jsonData) - } + XCTAssertNoThrow(try json5.decode(type, from: jsonData)) // Repeat with non-JSON5-compliant decoder. - #expect(throws: Never.self, sourceLocation: sourceLocation) { - _ = try json.decode(type, from: jsonData) - } + XCTAssertNoThrow(try json.decode(type, from: jsonData)) case .json5: - #expect(throws: Never.self, sourceLocation: sourceLocation) { - _ = try json5.decode(type, from: jsonData) - } + XCTAssertNoThrow(try json5.decode(type, from: jsonData)) // Regular JSON decoder should throw. do { let val = try json.decode(type, from: jsonData) - Issue.record("Expected decode failure (original JSON)for test \(name).\(ext), but got: \(val)", sourceLocation: sourceLocation) + XCTFail("Expected decode failure (original JSON)for test \(name).\(ext), but got: \(val)") } catch { } case .js: // Valid ES5 that's explicitly disallowed by JSON5 is also invalid JSON. do { let val = try json5.decode(type, from: jsonData) - Issue.record("Expected decode failure (JSON5) for test \(name).\(ext), but got: \(val)", sourceLocation: sourceLocation) + XCTFail("Expected decode failure (JSON5) for test \(name).\(ext), but got: \(val)") } catch { } // Regular JSON decoder should also throw. do { let val = try json.decode(type, from: jsonData) - Issue.record("Expected decode failure (original JSON) for test \(name).\(ext), but got: \(val)", sourceLocation: sourceLocation) + XCTFail("Expected decode failure (original JSON) for test \(name).\(ext), but got: \(val)") } catch { } case .malformed: // Invalid ES5 should remain invalid JSON5 do { let val = try json5.decode(type, from: jsonData) - Issue.record("Expected decode failure (JSON5) for test \(name).\(ext), but got: \(val)", sourceLocation: sourceLocation) + XCTFail("Expected decode failure (JSON5) for test \(name).\(ext), but got: \(val)") } catch { } // Regular JSON decoder should also throw. do { let val = try json.decode(type, from: jsonData) - Issue.record("Expected decode failure (original JSON) for test \(name).\(ext), but got: \(val)", sourceLocation: sourceLocation) + XCTFail("Expected decode failure (original JSON) for test \(name).\(ext), but got: \(val)") } catch { } } } // Also tests non-JSON5 decoder against the non-JSON5 tests in this test suite. - @Test func json5Spec() { + func test_json5Spec() { // Expected successes: _run_json5SpecTest("arrays", "empty-array", testType: .json, type: [Bool].self) _run_json5SpecTest("arrays", "regular-array", testType: .json, type: [Bool?].self) @@ -2804,9 +2768,9 @@ extension JSONEncoderTests { } - @Test func encodingDateISO8601() { + func testEncodingDateISO8601() { let timestamp = Date(timeIntervalSince1970: 1000) - let expectedJSON = "\"\(timestamp.formatted(.iso8601))\"".data(using: .utf8)! + let expectedJSON = "\"\(timestamp.formatted(.iso8601))\"".data(using: String._Encoding.utf8)! _testRoundTrip(of: timestamp, expectedJSON: expectedJSON, @@ -2821,10 +2785,10 @@ extension JSONEncoderTests { dateDecodingStrategy: .iso8601) } - @Test func encodingDataBase64() { + func testEncodingDataBase64() { let data = Data([0xDE, 0xAD, 0xBE, 0xEF]) - let expectedJSON = "\"3q2+7w==\"".data(using: .utf8)! + let expectedJSON = "\"3q2+7w==\"".data(using: String._Encoding.utf8)! _testRoundTrip(of: data, expectedJSON: expectedJSON) // Optional data should encode the same way. @@ -2834,8 +2798,8 @@ extension JSONEncoderTests { // MARK: - Decimal Tests extension JSONEncoderTests { - @Test func interceptDecimal() { - let expectedJSON = "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".data(using: .utf8)! + func testInterceptDecimal() { + let expectedJSON = "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".data(using: String._Encoding.utf8)! // Want to make sure we write out a JSON number, not the keyed encoding here. // 1e127 is too big to fit natively in a Double, too, so want to make sure it's encoded as a Decimal. @@ -2846,16 +2810,16 @@ extension JSONEncoderTests { _testRoundTrip(of: Optional(decimal), expectedJSON: expectedJSON) } - @Test func hugeNumbers() throws { + func test_hugeNumbers() { let json = "23456789012000000000000000000000000000000000000000000000000000000000000000000 " - let data = json.data(using: .utf8)! + let data = json.data(using: String._Encoding.utf8)! - let decimal = try JSONDecoder().decode(Decimal.self, from: data) + let decimal = try! JSONDecoder().decode(Decimal.self, from: data) let expected = Decimal(string: json) - #expect(decimal == expected) + XCTAssertEqual(decimal, expected) } - @Test func interceptLargeDecimal() { + func testInterceptLargeDecimal() { struct TestBigDecimal: Codable, Equatable { var uint64Max: Decimal = Decimal(UInt64.max) var unit64MaxPlus1: Decimal = Decimal( @@ -2881,11 +2845,9 @@ extension JSONEncoderTests { _testRoundTrip(of: testBigDecimal) } - @Test func overlargeDecimal() { + func testOverlargeDecimal() { // Check value too large fails to decode. - #expect(throws: (any Error).self) { - try JSONDecoder().decode(Decimal.self, from: "100e200".data(using: .utf8)!) - } + XCTAssertThrowsError(try JSONDecoder().decode(Decimal.self, from: "100e200".data(using: .utf8)!)) } } @@ -2894,13 +2856,13 @@ extension JSONEncoderTests { #if FOUNDATION_FRAMEWORK extension JSONEncoderTests { // This will remain a framework-only test due to dependence on `DateFormatter`. - @Test func encodingDateFormatted() { + func testEncodingDateFormatted() { let formatter = DateFormatter() formatter.dateStyle = .full formatter.timeStyle = .full let timestamp = Date(timeIntervalSince1970: 1000) - let expectedJSON = "\"\(formatter.string(from: timestamp))\"".data(using: .utf8)! + let expectedJSON = "\"\(formatter.string(from: timestamp))\"".data(using: String._Encoding.utf8)! _testRoundTrip(of: timestamp, expectedJSON: expectedJSON, @@ -2914,7 +2876,7 @@ extension JSONEncoderTests { dateDecodingStrategy: .formatted(formatter)) // So should wrapped dates. - let expectedJSON_array = "[\"\(formatter.string(from: timestamp))\"]".data(using: .utf8)! + let expectedJSON_array = "[\"\(formatter.string(from: timestamp))\"]".data(using: String._Encoding.utf8)! _testRoundTrip(of: TopLevelArrayWrapper(timestamp), expectedJSON: expectedJSON_array, dateEncodingStrategy: .formatted(formatter), @@ -2925,26 +2887,26 @@ extension JSONEncoderTests { // MARK: - .sortedKeys Tests extension JSONEncoderTests { - @Test func encodingTopLevelStructuredClass() { + func testEncodingTopLevelStructuredClass() { // Person is a class with multiple fields. - let expectedJSON = "{\"email\":\"appleseed@apple.com\",\"name\":\"Johnny Appleseed\"}".data(using: .utf8)! + let expectedJSON = "{\"email\":\"appleseed@apple.com\",\"name\":\"Johnny Appleseed\"}".data(using: String._Encoding.utf8)! let person = Person.testValue _testRoundTrip(of: person, expectedJSON: expectedJSON, outputFormatting: [.sortedKeys]) } - @Test func encodingOutputFormattingSortedKeys() { - let expectedJSON = "{\"email\":\"appleseed@apple.com\",\"name\":\"Johnny Appleseed\"}".data(using: .utf8)! + func testEncodingOutputFormattingSortedKeys() { + let expectedJSON = "{\"email\":\"appleseed@apple.com\",\"name\":\"Johnny Appleseed\"}".data(using: String._Encoding.utf8)! let person = Person.testValue _testRoundTrip(of: person, expectedJSON: expectedJSON, outputFormatting: [.sortedKeys]) } - @Test func encodingOutputFormattingPrettyPrintedSortedKeys() { - let expectedJSON = "{\n \"email\" : \"appleseed@apple.com\",\n \"name\" : \"Johnny Appleseed\"\n}".data(using: .utf8)! + func testEncodingOutputFormattingPrettyPrintedSortedKeys() { + let expectedJSON = "{\n \"email\" : \"appleseed@apple.com\",\n \"name\" : \"Johnny Appleseed\"\n}".data(using: String._Encoding.utf8)! let person = Person.testValue _testRoundTrip(of: person, expectedJSON: expectedJSON, outputFormatting: [.prettyPrinted, .sortedKeys]) } - @Test func encodingSortedKeys() { + func testEncodingSortedKeys() { // When requesting sorted keys, dictionary keys are sorted prior to being written out. // This sort should be stable, numeric, and follow human-readable sorting rules as defined by the system locale. let dict = [ @@ -2966,14 +2928,14 @@ extension JSONEncoderTests { "bar" : 10 ] - _testRoundTrip(of: dict, expectedJSON: #"{"FOO":2,"Foo":1,"Foo11":8,"Foo2":5,"bar":10,"foo":3,"foo1":4,"foo12":7,"foo3":6,"føo":9}"#.data(using: .utf8)!, outputFormatting: [.sortedKeys]) + _testRoundTrip(of: dict, expectedJSON: #"{"FOO":2,"Foo":1,"Foo11":8,"Foo2":5,"bar":10,"foo":3,"foo1":4,"foo12":7,"foo3":6,"føo":9}"#.data(using: String._Encoding.utf8)!, outputFormatting: [.sortedKeys]) } - @Test func encodingSortedKeysStableOrdering() { + func testEncodingSortedKeysStableOrdering() { // We want to make sure that keys of different length (but with identical prefixes) always sort in a stable way, regardless of their hash ordering. var dict = ["AAA" : 1, "AAAAAAB" : 2] var expectedJSONString = "{\"AAA\":1,\"AAAAAAB\":2}" - _testRoundTrip(of: dict, expectedJSON: expectedJSONString.data(using: .utf8)!, outputFormatting: [.sortedKeys]) + _testRoundTrip(of: dict, expectedJSON: expectedJSONString.data(using: String._Encoding.utf8)!, outputFormatting: [.sortedKeys]) // We don't want this test to rely on the hashing of Strings or how Dictionary uses that hash. // We'll insert a large number of keys into this dictionary and guarantee that the ordering of the above keys has indeed not changed. @@ -2998,10 +2960,10 @@ extension JSONEncoderTests { expectedJSONString.insert(contentsOf: insertedKeyJSON, at: expectedJSONString.index(before: expectedJSONString.endIndex)) } - _testRoundTrip(of: dict, expectedJSON: expectedJSONString.data(using: .utf8)!, outputFormatting: [.sortedKeys]) + _testRoundTrip(of: dict, expectedJSON: expectedJSONString.data(using: String._Encoding.utf8)!, outputFormatting: [.sortedKeys]) } - @Test func encodingMultipleNestedContainersWithTheSameTopLevelKey() { + func testEncodingMultipleNestedContainersWithTheSameTopLevelKey() { struct Model : Codable, Equatable { let first: String let second: String @@ -3049,11 +3011,11 @@ extension JSONEncoderTests { } let model = Model.testValue - let expectedJSON = "{\"top\":{\"first\":\"Johnny Appleseed\",\"second\":\"appleseed@apple.com\"}}".data(using: .utf8)! + let expectedJSON = "{\"top\":{\"first\":\"Johnny Appleseed\",\"second\":\"appleseed@apple.com\"}}".data(using: String._Encoding.utf8)! _testRoundTrip(of: model, expectedJSON: expectedJSON, outputFormatting: [.sortedKeys]) } - @Test func redundantKeyedContainer() throws { + func test_redundantKeyedContainer() { struct EncodesTwice: Encodable { enum CodingKeys: String, CodingKey { case container @@ -3079,13 +3041,13 @@ extension JSONEncoderTests { let encoder = JSONEncoder() encoder.outputFormatting = .sortedKeys - let data = try encoder.encode(EncodesTwice()) + let data = try! encoder.encode(EncodesTwice()) let string = String(data: data, encoding: .utf8)! - #expect(string == "{\"container\":{\"foo\":\"Test\",\"somethingElse\":\"SecondAgain\"},\"somethingElse\":\"Foo\"}") + XCTAssertEqual(string, "{\"container\":{\"foo\":\"Test\",\"somethingElse\":\"SecondAgain\"},\"somethingElse\":\"Foo\"}") } - @Test func singleValueDictionaryAmendedByContainer() throws { + func test_singleValueDictionaryAmendedByContainer() { struct Test: Encodable { enum CodingKeys: String, CodingKey { case a @@ -3101,18 +3063,18 @@ extension JSONEncoderTests { } let encoder = JSONEncoder() encoder.outputFormatting = .sortedKeys - let data = try encoder.encode(Test()) + let data = try! encoder.encode(Test()) let string = String(data: data, encoding: .utf8)! - #expect(string == "{\"a\":\"c\",\"other\":\"foo\"}") + XCTAssertEqual(string, "{\"a\":\"c\",\"other\":\"foo\"}") } } // MARK: - URL Tests extension JSONEncoderTests { - @Test func interceptURL() { + func testInterceptURL() { // Want to make sure JSONEncoder writes out single-value URLs, not the keyed encoding. - let expectedJSON = "\"http:\\/\\/swift.org\"".data(using: .utf8)! + let expectedJSON = "\"http:\\/\\/swift.org\"".data(using: String._Encoding.utf8)! let url = URL(string: "http://swift.org")! _testRoundTrip(of: url, expectedJSON: expectedJSON) @@ -3120,9 +3082,9 @@ extension JSONEncoderTests { _testRoundTrip(of: Optional(url), expectedJSON: expectedJSON) } - @Test func interceptURLWithoutEscapingOption() { + func testInterceptURLWithoutEscapingOption() { // Want to make sure JSONEncoder writes out single-value URLs, not the keyed encoding. - let expectedJSON = "\"http://swift.org\"".data(using: .utf8)! + let expectedJSON = "\"http://swift.org\"".data(using: String._Encoding.utf8)! let url = URL(string: "http://swift.org")! _testRoundTrip(of: url, expectedJSON: expectedJSON, outputFormatting: [.withoutEscapingSlashes]) @@ -3132,30 +3094,30 @@ extension JSONEncoderTests { } // MARK: - Helper Global Functions -func expectEqualPaths(_ lhs: [CodingKey], _ rhs: [CodingKey], _ prefix: String, sourceLocation: SourceLocation = #_sourceLocation) { - if lhs.count != rhs.count { - Issue.record("\(prefix) [CodingKey].count mismatch: \(lhs.count) != \(rhs.count)", sourceLocation: sourceLocation) - return - } +func expectEqualPaths(_ lhs: [CodingKey], _ rhs: [CodingKey], _ prefix: String) { + if lhs.count != rhs.count { + XCTFail("\(prefix) [CodingKey].count mismatch: \(lhs.count) != \(rhs.count)") + return + } - for (key1, key2) in zip(lhs, rhs) { - switch (key1.intValue, key2.intValue) { - case (.none, .none): break - case (.some(let i1), .none): - Issue.record("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != nil", sourceLocation: sourceLocation) + for (key1, key2) in zip(lhs, rhs) { + switch (key1.intValue, key2.intValue) { + case (.none, .none): break + case (.some(let i1), .none): + XCTFail("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != nil") + return + case (.none, .some(let i2)): + XCTFail("\(prefix) CodingKey.intValue mismatch: nil != \(type(of: key2))(\(i2))") + return + case (.some(let i1), .some(let i2)): + guard i1 == i2 else { + XCTFail("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != \(type(of: key2))(\(i2))") return - case (.none, .some(let i2)): - Issue.record("\(prefix) CodingKey.intValue mismatch: nil != \(type(of: key2))(\(i2))", sourceLocation: sourceLocation) - return - case (.some(let i1), .some(let i2)): - guard i1 == i2 else { - Issue.record("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != \(type(of: key2))(\(i2))", sourceLocation: sourceLocation) - return - } + } } - #expect(key1.stringValue == key2.stringValue, "\(prefix) CodingKey.stringValue mismatch: \(type(of: key1))('\(key1.stringValue)') != \(type(of: key2))('\(key2.stringValue)')", sourceLocation: sourceLocation) - } + XCTAssertEqual(key1.stringValue, key2.stringValue, "\(prefix) CodingKey.stringValue mismatch: \(type(of: key1))('\(key1.stringValue)') != \(type(of: key2))('\(key2.stringValue)')") + } } // MARK: - Test Types diff --git a/Tests/FoundationEssentialsTests/PropertyListEncoderTests.swift b/Tests/FoundationEssentialsTests/PropertyListEncoderTests.swift index 9ac492da9..cd8336bd0 100644 --- a/Tests/FoundationEssentialsTests/PropertyListEncoderTests.swift +++ b/Tests/FoundationEssentialsTests/PropertyListEncoderTests.swift @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// // -import Testing +#if canImport(TestSupport) +import TestSupport +#endif #if FOUNDATION_FRAMEWORK @testable import Foundation @@ -17,23 +19,24 @@ import Testing // MARK: - Test Suite -@Suite("PropertyListEncoder") -private struct PropertyListEncoderTests { +class TestPropertyListEncoder : XCTestCase { // MARK: - Encoding Top-Level Empty Types - @Test func encodingTopLevelEmptyStruct() { +#if FIXED_64141381 + func testEncodingTopLevelEmptyStruct() { let empty = EmptyStruct() _testRoundTrip(of: empty, in: .binary, expectedPlist: _plistEmptyDictionaryBinary) _testRoundTrip(of: empty, in: .xml, expectedPlist: _plistEmptyDictionaryXML) } - @Test func encodingTopLevelEmptyClass() { + func testEncodingTopLevelEmptyClass() { let empty = EmptyClass() _testRoundTrip(of: empty, in: .binary, expectedPlist: _plistEmptyDictionaryBinary) _testRoundTrip(of: empty, in: .xml, expectedPlist: _plistEmptyDictionaryXML) } +#endif // MARK: - Encoding Top-Level Single-Value Types - @Test func encodingTopLevelSingleValueEnum() { + func testEncodingTopLevelSingleValueEnum() { let s1 = Switch.off _testEncodeFailure(of: s1, in: .binary) _testEncodeFailure(of: s1, in: .xml) @@ -47,7 +50,7 @@ private struct PropertyListEncoderTests { _testRoundTrip(of: TopLevelWrapper(s2), in: .xml) } - @Test func encodingTopLevelSingleValueStruct() { + func testEncodingTopLevelSingleValueStruct() { let t = Timestamp(3141592653) _testEncodeFailure(of: t, in: .binary) _testEncodeFailure(of: t, in: .xml) @@ -55,7 +58,7 @@ private struct PropertyListEncoderTests { _testRoundTrip(of: TopLevelWrapper(t), in: .xml) } - @Test func encodingTopLevelSingleValueClass() { + func testEncodingTopLevelSingleValueClass() { let c = Counter() _testEncodeFailure(of: c, in: .binary) _testEncodeFailure(of: c, in: .xml) @@ -64,49 +67,49 @@ private struct PropertyListEncoderTests { } // MARK: - Encoding Top-Level Structured Types - @Test func encodingTopLevelStructuredStruct() { + func testEncodingTopLevelStructuredStruct() { // Address is a struct type with multiple fields. let address = Address.testValue _testRoundTrip(of: address, in: .binary) _testRoundTrip(of: address, in: .xml) } - @Test func encodingTopLevelStructuredClass() { + func testEncodingTopLevelStructuredClass() { // Person is a class with multiple fields. let person = Person.testValue _testRoundTrip(of: person, in: .binary) _testRoundTrip(of: person, in: .xml) } - @Test func encodingTopLevelStructuredSingleStruct() { + func testEncodingTopLevelStructuredSingleStruct() { // Numbers is a struct which encodes as an array through a single value container. let numbers = Numbers.testValue _testRoundTrip(of: numbers, in: .binary) _testRoundTrip(of: numbers, in: .xml) } - @Test func encodingTopLevelStructuredSingleClass() { + func testEncodingTopLevelStructuredSingleClass() { // Mapping is a class which encodes as a dictionary through a single value container. let mapping = Mapping.testValue _testRoundTrip(of: mapping, in: .binary) _testRoundTrip(of: mapping, in: .xml) } - @Test func encodingTopLevelDeepStructuredType() { + func testEncodingTopLevelDeepStructuredType() { // Company is a type with fields which are Codable themselves. let company = Company.testValue _testRoundTrip(of: company, in: .binary) _testRoundTrip(of: company, in: .xml) } - @Test func encodingClassWhichSharesEncoderWithSuper() { + func testEncodingClassWhichSharesEncoderWithSuper() { // Employee is a type which shares its encoder & decoder with its superclass, Person. let employee = Employee.testValue _testRoundTrip(of: employee, in: .binary) _testRoundTrip(of: employee, in: .xml) } - @Test func encodingTopLevelNullableType() { + func testEncodingTopLevelNullableType() { // EnhancedBool is a type which encodes either as a Bool or as nil. _testEncodeFailure(of: EnhancedBool.true, in: .binary) _testEncodeFailure(of: EnhancedBool.true, in: .xml) @@ -123,19 +126,20 @@ private struct PropertyListEncoderTests { _testRoundTrip(of: TopLevelWrapper(EnhancedBool.fileNotFound), in: .xml) } - @Test func encodingTopLevelWithConfiguration() throws { + func testEncodingTopLevelWithConfiguration() throws { // CodableTypeWithConfiguration is a struct that conforms to CodableWithConfiguration let value = CodableTypeWithConfiguration.testValue let encoder = PropertyListEncoder() let decoder = PropertyListDecoder() var decoded = try decoder.decode(CodableTypeWithConfiguration.self, from: try encoder.encode(value, configuration: .init(1)), configuration: .init(1)) - #expect(decoded == value) + XCTAssertEqual(decoded, value) decoded = try decoder.decode(CodableTypeWithConfiguration.self, from: try encoder.encode(value, configuration: CodableTypeWithConfiguration.ConfigProviding.self), configuration: CodableTypeWithConfiguration.ConfigProviding.self) - #expect(decoded == value) + XCTAssertEqual(decoded, value) } - @Test func encodingMultipleNestedContainersWithTheSameTopLevelKey() { +#if FIXED_64141381 + func testEncodingMultipleNestedContainersWithTheSameTopLevelKey() { struct Model : Codable, Equatable { let first: String let second: String @@ -182,12 +186,13 @@ private struct PropertyListEncoderTests { } let model = Model.testValue - let expectedXML = "\n\n\n\n\ttop\n\t\n\t\tfirst\n\t\tJohnny Appleseed\n\t\tsecond\n\t\tappleseed@apple.com\n\t\n\n\n".data(using: .utf8)! + let expectedXML = "\n\n\n\n\ttop\n\t\n\t\tfirst\n\t\tJohnny Appleseed\n\t\tsecond\n\t\tappleseed@apple.com\n\t\n\n\n".data(using: String._Encoding.utf8)! _testRoundTrip(of: model, in: .xml, expectedPlist: expectedXML) } +#endif -#if FOUNDATION_EXIT_TESTS - @Test func encodingConflictedTypeNestedContainersWithTheSameTopLevelKey() async { +#if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 + func testEncodingConflictedTypeNestedContainersWithTheSameTopLevelKey() { struct Model : Encodable, Equatable { let first: String @@ -218,128 +223,125 @@ private struct PropertyListEncoderTests { } } - await #expect(processExitsWith: .failure) { - let model = Model.testValue - // This following test would fail as it attempts to re-encode into already encoded container is invalid. This will always fail - let encoder = PropertyListEncoder() - encoder.outputFormat = .xml - let _ = try encoder.encode(model) - } + let model = Model.testValue + // This following test would fail as it attempts to re-encode into already encoded container is invalid. This will always fail + expectCrashLater() + _testEncodeFailure(of: model, in: .xml) } #endif // MARK: - Encoder Features - @Test func nestedContainerCodingPaths() { + func testNestedContainerCodingPaths() { let encoder = PropertyListEncoder() - #expect(throws: Never.self) { - try encoder.encode(NestedContainersTestType()) + do { + let _ = try encoder.encode(NestedContainersTestType()) + } catch let error as NSError { + XCTFail("Caught error during encoding nested container types: \(error)") } } - @Test func superEncoderCodingPaths() { + func testSuperEncoderCodingPaths() { let encoder = PropertyListEncoder() - #expect(throws: Never.self) { - try encoder.encode(NestedContainersTestType(testSuperEncoder: true)) + do { + let _ = try encoder.encode(NestedContainersTestType(testSuperEncoder: true)) + } catch let error as NSError { + XCTFail("Caught error during encoding nested container types: \(error)") } } #if FOUNDATION_FRAMEWORK // requires PropertyListSerialization, JSONSerialization - @Test func encodingTopLevelData() throws { - let data = try JSONSerialization.data(withJSONObject: [String](), options: []) - _testRoundTrip(of: data, in: .binary, expectedPlist: try PropertyListSerialization.data(fromPropertyList: data, format: .binary, options: 0)) - _testRoundTrip(of: data, in: .xml, expectedPlist: try PropertyListSerialization.data(fromPropertyList: data, format: .xml, options: 0)) + func testEncodingTopLevelData() { + let data = try! JSONSerialization.data(withJSONObject: [String](), options: []) + _testRoundTrip(of: data, in: .binary, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: data, format: .binary, options: 0)) + _testRoundTrip(of: data, in: .xml, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: data, format: .xml, options: 0)) } - @Test func interceptData() throws { - let data = try JSONSerialization.data(withJSONObject: [String](), options: []) + func testInterceptData() { + let data = try! JSONSerialization.data(withJSONObject: [String](), options: []) let topLevel = TopLevelWrapper(data) let plist = ["value": data] - _testRoundTrip(of: topLevel, in: .binary, expectedPlist: try PropertyListSerialization.data(fromPropertyList: plist, format: .binary, options: 0)) - _testRoundTrip(of: topLevel, in: .xml, expectedPlist: try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)) + _testRoundTrip(of: topLevel, in: .binary, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: plist, format: .binary, options: 0)) + _testRoundTrip(of: topLevel, in: .xml, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)) } - @Test func interceptDate() throws { + func testInterceptDate() { let date = Date(timeIntervalSinceReferenceDate: 0) let topLevel = TopLevelWrapper(date) let plist = ["value": date] - _testRoundTrip(of: topLevel, in: .binary, expectedPlist: try PropertyListSerialization.data(fromPropertyList: plist, format: .binary, options: 0)) - _testRoundTrip(of: topLevel, in: .xml, expectedPlist: try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)) + _testRoundTrip(of: topLevel, in: .binary, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: plist, format: .binary, options: 0)) + _testRoundTrip(of: topLevel, in: .xml, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)) } -#endif // FOUNDATION_FRAMEWORK +#endif // FOUNDATION_FRaMEWORK // MARK: - Type coercion - @Test func typeCoercion() throws { - func _testRoundTripTypeCoercionFailure(of value: T, as type: U.Type, sourceLocation: SourceLocation = #_sourceLocation) throws where T : Codable, U : Codable { + func testTypeCoercion() { + func _testRoundTripTypeCoercionFailure(of value: T, as type: U.Type) where T : Codable, U : Codable { let encoder = PropertyListEncoder() encoder.outputFormat = .xml - let xmlData = try encoder.encode(value) - #expect(throws: (any Error).self, "Coercion from \(T.self) to \(U.self) was expected to fail.", sourceLocation: sourceLocation) { - try PropertyListDecoder().decode(U.self, from: xmlData) - } + let xmlData = try! encoder.encode(value) + XCTAssertThrowsError(try PropertyListDecoder().decode(U.self, from: xmlData), "Coercion from \(T.self) to \(U.self) was expected to fail.") encoder.outputFormat = .binary - let binaryData = try encoder.encode(value) - #expect(throws: (any Error).self, "Coercion from \(T.self) to \(U.self) was expected to fail.", sourceLocation: sourceLocation) { - try PropertyListDecoder().decode(U.self, from: binaryData) - } - } - - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int].self) - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int8].self) - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int16].self) - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int32].self) - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int64].self) - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt].self) - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt8].self) - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt16].self) - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt32].self) - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt64].self) - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [Float].self) - try _testRoundTripTypeCoercionFailure(of: [false, true], as: [Double].self) - try _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int], as: [Bool].self) - try _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int8], as: [Bool].self) - try _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int16], as: [Bool].self) - try _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int32], as: [Bool].self) - try _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int64], as: [Bool].self) - try _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt], as: [Bool].self) - try _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt8], as: [Bool].self) - try _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt16], as: [Bool].self) - try _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt32], as: [Bool].self) - try _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt64], as: [Bool].self) - try _testRoundTripTypeCoercionFailure(of: [0.0, 1.0] as [Float], as: [Bool].self) - try _testRoundTripTypeCoercionFailure(of: [0.0, 1.0] as [Double], as: [Bool].self) + let binaryData = try! encoder.encode(value) + XCTAssertThrowsError(try PropertyListDecoder().decode(U.self, from: binaryData), "Coercion from \(T.self) to \(U.self) was expected to fail.") + } + + _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int].self) + _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int8].self) + _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int16].self) + _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int32].self) + _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int64].self) + _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt].self) + _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt8].self) + _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt16].self) + _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt32].self) + _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt64].self) + _testRoundTripTypeCoercionFailure(of: [false, true], as: [Float].self) + _testRoundTripTypeCoercionFailure(of: [false, true], as: [Double].self) + _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int], as: [Bool].self) + _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int8], as: [Bool].self) + _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int16], as: [Bool].self) + _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int32], as: [Bool].self) + _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int64], as: [Bool].self) + _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt], as: [Bool].self) + _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt8], as: [Bool].self) + _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt16], as: [Bool].self) + _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt32], as: [Bool].self) + _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt64], as: [Bool].self) + _testRoundTripTypeCoercionFailure(of: [0.0, 1.0] as [Float], as: [Bool].self) + _testRoundTripTypeCoercionFailure(of: [0.0, 1.0] as [Double], as: [Bool].self) // Real -> Integer coercions that are impossible. - try _testRoundTripTypeCoercionFailure(of: [256] as [Double], as: [UInt8].self) - try _testRoundTripTypeCoercionFailure(of: [-129] as [Double], as: [Int8].self) - try _testRoundTripTypeCoercionFailure(of: [-1.0] as [Double], as: [UInt64].self) - try _testRoundTripTypeCoercionFailure(of: [3.14159] as [Double], as: [UInt64].self) - try _testRoundTripTypeCoercionFailure(of: [.infinity] as [Double], as: [UInt64].self) - try _testRoundTripTypeCoercionFailure(of: [.nan] as [Double], as: [UInt64].self) + _testRoundTripTypeCoercionFailure(of: [256] as [Double], as: [UInt8].self) + _testRoundTripTypeCoercionFailure(of: [-129] as [Double], as: [Int8].self) + _testRoundTripTypeCoercionFailure(of: [-1.0] as [Double], as: [UInt64].self) + _testRoundTripTypeCoercionFailure(of: [3.14159] as [Double], as: [UInt64].self) + _testRoundTripTypeCoercionFailure(of: [.infinity] as [Double], as: [UInt64].self) + _testRoundTripTypeCoercionFailure(of: [.nan] as [Double], as: [UInt64].self) // Especially for binary plist, ensure we maintain different encoded representations of special values like Int64(-1) and UInt64.max, which have the same 8 byte representation. - try _testRoundTripTypeCoercionFailure(of: [Int64(-1)], as: [UInt64].self) - try _testRoundTripTypeCoercionFailure(of: [UInt64.max], as: [Int64].self) + _testRoundTripTypeCoercionFailure(of: [Int64(-1)], as: [UInt64].self) + _testRoundTripTypeCoercionFailure(of: [UInt64.max], as: [Int64].self) } - @Test func integerRealCoercion() throws { - func _testRoundTripTypeCoercion(of value: T, expectedCoercedValue: U, sourceLocation: SourceLocation = #_sourceLocation) throws { + func testIntegerRealCoercion() throws { + func _testRoundTripTypeCoercion(of value: T, expectedCoercedValue: U) throws { let encoder = PropertyListEncoder() encoder.outputFormat = .xml let xmlData = try encoder.encode([value]) var decoded = try PropertyListDecoder().decode([U].self, from: xmlData) - #expect(decoded.first == expectedCoercedValue, sourceLocation: sourceLocation) + XCTAssertEqual(decoded.first!, expectedCoercedValue) encoder.outputFormat = .binary let binaryData = try encoder.encode([value]) decoded = try PropertyListDecoder().decode([U].self, from: binaryData) - #expect(decoded.first == expectedCoercedValue, sourceLocation: sourceLocation) + XCTAssertEqual(decoded.first!, expectedCoercedValue) } try _testRoundTripTypeCoercion(of: 1 as UInt64, expectedCoercedValue: 1.0 as Double) @@ -356,19 +358,25 @@ private struct PropertyListEncoderTests { try _testRoundTripTypeCoercion(of: 2.99792458e8 as Double, expectedCoercedValue: 299792458) } - @Test func decodingConcreteTypeParameter() throws { + func testDecodingConcreteTypeParameter() { let encoder = PropertyListEncoder() - let plist = try encoder.encode(Employee.testValue) + guard let plist = try? encoder.encode(Employee.testValue) else { + XCTFail("Unable to encode Employee.") + return + } let decoder = PropertyListDecoder() - let decoded = try decoder.decode(Employee.self as Person.Type, from: plist) + guard let decoded = try? decoder.decode(Employee.self as Person.Type, from: plist) else { + XCTFail("Failed to decode Employee as Person from plist.") + return + } - #expect(type(of: decoded) == Employee.self, "Expected decoded value to be of type Employee; got \(type(of: decoded)) instead.") + expectEqual(type(of: decoded), Employee.self, "Expected decoded value to be of type Employee; got \(type(of: decoded)) instead.") } // MARK: - Encoder State // SR-6078 - @Test func encoderStateThrowOnEncode() { + func testEncoderStateThrowOnEncode() { struct Wrapper : Encodable { let value: T init(_ value: T) { self.value = value } @@ -412,16 +420,14 @@ private struct PropertyListEncoderTests { // MARK: - Decoder State // SR-6048 - @Test func decoderStateThrowOnDecode() { - #expect(throws: Never.self) { - let plist = try PropertyListEncoder().encode([1,2,3]) - let _ = try PropertyListDecoder().decode(EitherDecodable<[String], [Int]>.self, from: plist) - } + func testDecoderStateThrowOnDecode() { + let plist = try! PropertyListEncoder().encode([1,2,3]) + let _ = try! PropertyListDecoder().decode(EitherDecodable<[String], [Int]>.self, from: plist) } #if FOUNDATION_FRAMEWORK // MARK: - NSKeyedArchiver / NSKeyedUnarchiver integration - @Test func archiving() throws { + func testArchiving() { struct CodableType: Codable, Equatable { let willBeNil: String? let arrayOfOptionals: [String?] @@ -436,14 +442,18 @@ private struct PropertyListEncoderTests { arrayOfOptionals: ["a", "b", nil, "c"], dictionaryOfArrays: [ "data" : [Data([0xfe, 0xed, 0xfa, 0xce]), Data([0xba, 0xaa, 0xaa, 0xad])]]) - try keyedArchiver.encodeEncodable(value, forKey: "strings") - keyedArchiver.finishEncoding() - let data = keyedArchiver.encodedData - - let keyedUnarchiver = try NSKeyedUnarchiver(forReadingFrom: data) - let unarchived = try keyedUnarchiver.decodeTopLevelDecodable(CodableType.self, forKey: "strings") - - #expect(unarchived == value) + do { + try keyedArchiver.encodeEncodable(value, forKey: "strings") + keyedArchiver.finishEncoding() + let data = keyedArchiver.encodedData + + let keyedUnarchiver = try NSKeyedUnarchiver(forReadingFrom: data) + let unarchived = try keyedUnarchiver.decodeTopLevelDecodable(CodableType.self, forKey: "strings") + + XCTAssertEqual(unarchived, value) + } catch { + XCTFail("Unexpected error: \(error)") + } } #endif @@ -453,40 +463,41 @@ private struct PropertyListEncoderTests { } private var _plistEmptyDictionaryXML: Data { - return "\n\n\n\n\n".data(using: .utf8)! + return "\n\n\n\n\n".data(using: String._Encoding.utf8)! } - private func _testEncodeFailure(of value: T, in format: PropertyListDecoder.PropertyListFormat, sourceLocation: SourceLocation = #_sourceLocation) { - #expect(throws: (any Error).self, "Encode of top-level \(T.self) was expected to fail.", sourceLocation: sourceLocation) { + private func _testEncodeFailure(of value: T, in format: PropertyListDecoder.PropertyListFormat) { + do { let encoder = PropertyListEncoder() encoder.outputFormat = format let _ = try encoder.encode(value) - } + XCTFail("Encode of top-level \(T.self) was expected to fail.") + } catch {} } @discardableResult - private func _testRoundTrip(of value: T, in format: PropertyListDecoder.PropertyListFormat, expectedPlist plist: Data? = nil, sourceLocation: SourceLocation = #_sourceLocation) -> T? where T : Codable, T : Equatable { + private func _testRoundTrip(of value: T, in format: PropertyListDecoder.PropertyListFormat, expectedPlist plist: Data? = nil) -> T? where T : Codable, T : Equatable { var payload: Data! = nil do { let encoder = PropertyListEncoder() encoder.outputFormat = format payload = try encoder.encode(value) } catch { - Issue.record("Failed to encode \(T.self) to plist: \(error)") + XCTFail("Failed to encode \(T.self) to plist: \(error)") } if let expectedPlist = plist { - #expect(expectedPlist == payload, "Produced plist not identical to expected plist.") + XCTAssertEqual(expectedPlist, payload, "Produced plist not identical to expected plist.") } do { var decodedFormat: PropertyListDecoder.PropertyListFormat = format let decoded = try PropertyListDecoder().decode(T.self, from: payload, format: &decodedFormat) - #expect(format == decodedFormat, "Encountered plist format differed from requested format.") - #expect(decoded == value, "\(T.self) did not round-trip to an equal value.") + XCTAssertEqual(format, decodedFormat, "Encountered plist format differed from requested format.") + XCTAssertEqual(decoded, value, "\(T.self) did not round-trip to an equal value.") return decoded } catch { - Issue.record("Failed to decode \(T.self) from plist: \(error)") + XCTFail("Failed to decode \(T.self) from plist: \(error)") return nil } } @@ -497,7 +508,7 @@ private struct PropertyListEncoderTests { } // MARK: - Other tests - @Test func unkeyedContainerContainingNulls() throws { + func testUnkeyedContainerContainingNulls() throws { struct UnkeyedContainerContainingNullTestType : Codable, Equatable { var array = [String?]() @@ -532,40 +543,32 @@ private struct PropertyListEncoderTests { _testRoundTrip(of: UnkeyedContainerContainingNullTestType(array: array), in: .binary) } - @Test func invalidNSDataKey_82142612() { + func test_invalidNSDataKey_82142612() { let data = testData(forResource: "Test_82142612", withExtension: "bad")! let decoder = PropertyListDecoder() - #expect(throws: (any Error).self) { - try decoder.decode([String:String].self, from: data) - } + XCTAssertThrowsError(try decoder.decode([String:String].self, from: data)) // Repeat something similar with XML. - let xmlData = "abcdxyz".data(using: .utf8)! - #expect(throws: (any Error).self) { - try decoder.decode([String:String].self, from: xmlData) - } + let xmlData = "abcdxyz".data(using: String._Encoding.utf8)! + XCTAssertThrowsError(try decoder.decode([String:String].self, from: xmlData)) } #if FOUNDATION_FRAMEWORK // TODO: Depends on data's range(of:) implementation - @Test func nonStringDictionaryKey() throws { + func test_nonStringDictionaryKey() { let decoder = PropertyListDecoder() let encoder = PropertyListEncoder() encoder.outputFormat = .binary - var data = try encoder.encode(["abcd":"xyz"]) + var data = try! encoder.encode(["abcd":"xyz"]) // Replace the tag for the ASCII string (0101) that is length 4 ("abcd" => length: 0100) with a boolean "true" tag (0000_1001) let range = data.range(of: Data([0b0101_0100]))! data.replaceSubrange(range, with: Data([0b000_1001])) - #expect(throws: (any Error).self) { - try decoder.decode([String:String].self, from: data) - } + XCTAssertThrowsError(try decoder.decode([String:String].self, from: data)) - let xmlData = "abcdxyz".data(using: .utf8)! - #expect(throws: (any Error).self) { - try decoder.decode([String:String].self, from: xmlData) - } + let xmlData = "abcdxyz".data(using: String._Encoding.utf8)! + XCTAssertThrowsError(try decoder.decode([String:String].self, from: xmlData)) } #endif @@ -602,46 +605,42 @@ private struct PropertyListEncoderTests { } } - @Test func issue5616259() throws { + func test_5616259() throws { let plistData = testData(forResource: "Test_5616259", withExtension: "bad")! - #expect(throws: (any Error).self) { - try PropertyListDecoder().decode([String].self, from: plistData) - } + XCTAssertThrowsError(try PropertyListDecoder().decode([String].self, from: plistData)) } - @Test func genericProperties_XML() throws { + func test_genericProperties_XML() throws { let data = testData(forResource: "Generic_XML_Properties", withExtension: "plist")! let props = try PropertyListDecoder().decode(GenericProperties.self, from: data) - #expect(props.assertionFailure == nil) + XCTAssertNil(props.assertionFailure) } - @Test func genericProperties_binary() throws { + func test_genericProperties_binary() throws { let data = testData(forResource: "Generic_XML_Properties_Binary", withExtension: "plist")! let props = try PropertyListDecoder().decode(GenericProperties.self, from: data) - #expect(props.assertionFailure == nil) + XCTAssertNil(props.assertionFailure) } // Binary plist parser should parse any version 'bplist0?' - @Test func issue5877417() throws { + func test_5877417() throws { var data = testData(forResource: "Generic_XML_Properties_Binary", withExtension: "plist")! // Modify the data so the header starts with bplist0x data[7] = UInt8(ascii: "x") let props = try PropertyListDecoder().decode(GenericProperties.self, from: data) - #expect(props.assertionFailure == nil) + XCTAssertNil(props.assertionFailure) } - @Test func xmlErrors() { + func test_xmlErrors() { let data = testData(forResource: "Generic_XML_Properties", withExtension: "plist")! let originalXML = String(data: data, encoding: .utf8)! // Try an empty plist - #expect(throws: (any Error).self) { - try PropertyListDecoder().decode(GenericProperties.self, from: Data()) - } + XCTAssertThrowsError(try PropertyListDecoder().decode(GenericProperties.self, from: Data())) // We'll modify this string in all kinds of nasty ways to introduce errors // --- /* @@ -670,46 +669,44 @@ private struct PropertyListEncoderTests { var errorPlists = [String : String]() errorPlists["Deleted leading <"] = String(originalXML[originalXML.index(after: originalXML.startIndex)...]) - errorPlists["Unterminated comment"] = originalXML.replacing("", with: "<-- unending comment\n") - errorPlists["Mess with DOCTYPE"] = originalXML.replacing("DOCTYPE", with: "foobar") + errorPlists["Unterminated comment"] = originalXML.replacingOccurrences(of: "", with: "<-- unending comment\n") + errorPlists["Mess with DOCTYPE"] = originalXML.replacingOccurrences(of: "DOCTYPE", with: "foobar") - let range = originalXML.firstRange(of: "//EN")! + let range = originalXML.range(of: "//EN")! errorPlists["Early EOF"] = String(originalXML[originalXML.startIndex ..< range.lowerBound]) - errorPlists["MalformedDTD"] = originalXML.replacing("", with: "") - errorPlists["Bad open tag"] = originalXML.replacing("", with: "") - errorPlists["Extra plist object"] = originalXML.replacing("", with: "hello\n") - errorPlists["Non-key inside dict"] = originalXML.replacing("array1", with: "hello\narray1") - errorPlists["Missing value for key"] = originalXML.replacing("value1", with: "") - errorPlists["Malformed real tag"] = originalXML.replacing("42", with: "abc123") - errorPlists["Empty int tag"] = originalXML.replacing("42", with: "") - errorPlists["Strange int tag"] = originalXML.replacing("42", with: "42q") - errorPlists["Hex digit in non-hex int"] = originalXML.replacing("42", with: "42A") - errorPlists["Enormous int"] = originalXML.replacing("42", with: "99999999999999999999999999999999999999999") + errorPlists["MalformedDTD"] = originalXML.replacingOccurrences(of: "", with: "") + errorPlists["Bad open tag"] = originalXML.replacingOccurrences(of: "", with: "") + errorPlists["Extra plist object"] = originalXML.replacingOccurrences(of: "", with: "hello\n") + errorPlists["Non-key inside dict"] = originalXML.replacingOccurrences(of: "array1", with: "hello\narray1") + errorPlists["Missing value for key"] = originalXML.replacingOccurrences(of: "value1", with: "") + errorPlists["Malformed real tag"] = originalXML.replacingOccurrences(of: "42", with: "abc123") + errorPlists["Empty int tag"] = originalXML.replacingOccurrences(of: "42", with: "") + errorPlists["Strange int tag"] = originalXML.replacingOccurrences(of: "42", with: "42q") + errorPlists["Hex digit in non-hex int"] = originalXML.replacingOccurrences(of: "42", with: "42A") + errorPlists["Enormous int"] = originalXML.replacingOccurrences(of: "42", with: "99999999999999999999999999999999999999999") errorPlists["Empty plist"] = "" - errorPlists["Empty date"] = originalXML.replacing("1976-04-01T12:00:00Z", with: "") - errorPlists["Empty real"] = originalXML.replacing("42", with: "") - errorPlists["Fake inline DTD"] = originalXML.replacing("PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"", with: "[]") + errorPlists["Empty date"] = originalXML.replacingOccurrences(of: "1976-04-01T12:00:00Z", with: "") + errorPlists["Empty real"] = originalXML.replacingOccurrences(of: "42", with: "") + errorPlists["Fake inline DTD"] = originalXML.replacingOccurrences(of: "PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"", with: "[]") for (name, badPlist) in errorPlists { - let data = badPlist.data(using: .utf8)! - #expect(throws: (any Error).self, "Case \(name) did not fail as expected") { - try PropertyListDecoder().decode(GenericProperties.self, from: data) - } + let data = badPlist.data(using: String._Encoding.utf8)! + XCTAssertThrowsError(try PropertyListDecoder().decode(GenericProperties.self, from: data), "Case \(name) did not fail as expected") } } - @Test func issue6164184() throws { + func test_6164184() throws { let xml = "0x721B0x1111-0xFFFF" - let array = try PropertyListDecoder().decode([Int].self, from: xml.data(using: .utf8)!) - #expect([0x721B, 0x1111, -0xFFFF] == array) + let array = try PropertyListDecoder().decode([Int].self, from: xml.data(using: String._Encoding.utf8)!) + XCTAssertEqual([0x721B, 0x1111, -0xFFFF], array) } - @Test func xmlIntegerEdgeCases() throws { - func checkValidEdgeCase(_ xml: String, type: T.Type, expected: T, sourceLocation: SourceLocation = #_sourceLocation) throws { - let value = try PropertyListDecoder().decode(type, from: xml.data(using: .utf8)!) - #expect(value == expected, sourceLocation: sourceLocation) + func test_xmlIntegerEdgeCases() throws { + func checkValidEdgeCase(_ xml: String, type: T.Type, expected: T) throws { + let value = try PropertyListDecoder().decode(type, from: xml.data(using: String._Encoding.utf8)!) + XCTAssertEqual(value, expected) } try checkValidEdgeCase("127", type: Int8.self, expected: .max) @@ -735,10 +732,8 @@ private struct PropertyListEncoderTests { try checkValidEdgeCase("4294967295", type: UInt32.self, expected: .max) try checkValidEdgeCase("18446744073709551615", type: UInt64.self, expected: .max) - func checkInvalidEdgeCase(_ xml: String, type: T.Type, sourceLocation: SourceLocation = #_sourceLocation) { - #expect(throws: (any Error).self, sourceLocation: sourceLocation) { - try PropertyListDecoder().decode(type, from: xml.data(using: .utf8)!) - } + func checkInvalidEdgeCase(_ xml: String, type: T.Type) { + XCTAssertThrowsError(try PropertyListDecoder().decode(type, from: xml.data(using: String._Encoding.utf8)!)) } checkInvalidEdgeCase("128", type: Int8.self) @@ -765,14 +760,14 @@ private struct PropertyListEncoderTests { checkInvalidEdgeCase("18446744073709551616", type: UInt64.self) } - @Test func xmlIntegerWhitespace() throws { + func test_xmlIntegerWhitespace() throws { let xml = " +\t42\t- 99 -\t0xFACE" - let value = try PropertyListDecoder().decode([Int].self, from: xml.data(using: .utf8)!) - #expect(value == [42, -99, -0xFACE]) + let value = try PropertyListDecoder().decode([Int].self, from: xml.data(using: String._Encoding.utf8)!) + XCTAssertEqual(value, [42, -99, -0xFACE]) } - @Test func binaryNumberEdgeCases() throws { + func test_binaryNumberEdgeCases() throws { _testRoundTrip(of: [Int8.max], in: .binary) _testRoundTrip(of: [Int8.min], in: .binary) _testRoundTrip(of: [Int16.max], in: .binary) @@ -800,8 +795,8 @@ private struct PropertyListEncoderTests { _testRoundTrip(of: [-Double.infinity], in: .binary) } - @Test func binaryReals() throws { - func encode(_: T.Type) throws -> (data: Data, expected: [T]) { + func test_binaryReals() throws { + func encode(_: T.Type) -> (data: Data, expected: [T]) { let expected: [T] = [ 1.5, 2, @@ -813,23 +808,27 @@ private struct PropertyListEncoderTests { ] let encoder = PropertyListEncoder() encoder.outputFormat = .binary - let data = try encoder.encode(expected) + let data = try! encoder.encode(expected) return (data, expected) } - func test(_ type: T.Type) throws { - let (data, expected) = try encode(type) - let result = try PropertyListDecoder().decode([T].self, from: data) - #expect(result == expected, "Type: \(type)") + func test(_ type: T.Type) { + let (data, expected) = encode(type) + do { + let result = try PropertyListDecoder().decode([T].self, from: data) + XCTAssertEqual(result, expected, "Type: \(type)") + } catch { + XCTFail("Expected error \(error) for type: \(type)") + } } - try test(Float.self) - try test(Double.self) + test(Float.self) + test(Double.self) } - @Test func xmlReals() throws { + func test_XMLReals() throws { let xml = "1.52 -3.141.00000000000000000000000131415.9e-4-iNfinfInItY" - let array = try PropertyListDecoder().decode([Float].self, from: xml.data(using: .utf8)!) + let array = try PropertyListDecoder().decode([Float].self, from: xml.data(using: String._Encoding.utf8)!) let expected: [Float] = [ 1.5, 2, @@ -839,78 +838,76 @@ private struct PropertyListEncoderTests { -.infinity, .infinity ] - #expect(array == expected) + XCTAssertEqual(array, expected) // nan doesn't work with equality. let xmlNAN = "nAnNANnan" - let arrayNAN = try PropertyListDecoder().decode([Float].self, from: xmlNAN.data(using: .utf8)!) + let arrayNAN = try PropertyListDecoder().decode([Float].self, from: xmlNAN.data(using: String._Encoding.utf8)!) for val in arrayNAN { - #expect(val.isNaN) + XCTAssertTrue(val.isNaN) } } - @Test(arguments: [ - "0x10", - "notanumber", - "infinite", - "1.2.3", - "1.e", - "1.5 ", // Trailing whitespace is rejected, unlike leading whitespace. - "", - ]) - func bad_XMLReals(xml: String) { - #expect(throws: (any Error).self) { - try PropertyListDecoder().decode(Float.self, from: xml.data(using: .utf8)!) + func test_bad_XMLReals() { + let badRealXMLs = [ + "0x10", + "notanumber", + "infinite", + "1.2.3", + "1.e", + "1.5 ", // Trailing whitespace is rejected, unlike leading whitespace. + "", + ] + for xml in badRealXMLs { + XCTAssertThrowsError(try PropertyListDecoder().decode(Float.self, from: xml.data(using: String._Encoding.utf8)!), "Input: \(xml)") } } - @Test func oldStylePlist_invalid() { - let data = "goodbye cruel world".data(using: .utf16)! - #expect(throws: (any Error).self) { - try PropertyListDecoder().decode(String.self, from: data) - } + func test_oldStylePlist_invalid() { + let data = "goodbye cruel world".data(using: String._Encoding.utf16)! + XCTAssertThrowsError(try PropertyListDecoder().decode(String.self, from: data)) } // Microsoft: Microsoft vso 1857102 : High Sierra regression that caused data loss : CFBundleCopyLocalizedString returns incorrect string // Escaped octal chars can be shorter than 3 chars long; i.e. \5 ≡ \05 ≡ \005. - @Test func oldStylePlist_getSlashedChars_octal() throws { + func test_oldStylePlist_getSlashedChars_octal() { // ('\0', '\00', '\000', '\1', '\01', '\001', ..., '\777') let data = testData(forResource: "test_oldStylePlist_getSlashedChars_octal", withExtension: "plist")! - let actualStrings = try PropertyListDecoder().decode([String].self, from: data) + let actualStrings = try! PropertyListDecoder().decode([String].self, from: data) let expectedData = testData(forResource: "test_oldStylePlist_getSlashedChars_octal_expected", withExtension: "plist")! - let expectedStrings = try PropertyListDecoder().decode([String].self, from: expectedData) + let expectedStrings = try! PropertyListDecoder().decode([String].self, from: expectedData) - #expect(actualStrings == expectedStrings) + XCTAssertEqual(actualStrings, expectedStrings) } // Old-style plists support Unicode literals via \U syntax. They can be 1–4 characters wide. - @Test func oldStylePlist_getSlashedChars_unicode() throws { + func test_oldStylePlist_getSlashedChars_unicode() { // ('\U0', '\U00', '\U000', '\U0000', '\U1', ..., '\UFFFF') let data = testData(forResource: "test_oldStylePlist_getSlashedChars_unicode", withExtension: "plist")! - let actualStrings = try PropertyListDecoder().decode([String].self, from: data) + let actualStrings = try! PropertyListDecoder().decode([String].self, from: data) let expectedData = testData(forResource: "test_oldStylePlist_getSlashedChars_unicode_expected", withExtension: "plist")! - let expectedStrings = try PropertyListDecoder().decode([String].self, from: expectedData) + let expectedStrings = try! PropertyListDecoder().decode([String].self, from: expectedData) - #expect(actualStrings == expectedStrings) + XCTAssertEqual(actualStrings, expectedStrings) } - @Test func oldStylePlist_getSlashedChars_literals() throws { + func test_oldStylePlist_getSlashedChars_literals() { let literals = ["\u{7}", "\u{8}", "\u{12}", "\n", "\r", "\t", "\u{11}", "\"", "\\n"] - let data = "('\\a', '\\b', '\\f', '\\n', '\\r', '\\t', '\\v', '\\\"', '\\\\n')".data(using: .utf8)! + let data = "('\\a', '\\b', '\\f', '\\n', '\\r', '\\t', '\\v', '\\\"', '\\\\n')".data(using: String._Encoding.utf8)! - let strings = try PropertyListDecoder().decode([String].self, from: data) - #expect(strings == literals) + let strings = try! PropertyListDecoder().decode([String].self, from: data) + XCTAssertEqual(strings, literals) } - @Test func oldStylePlist_dictionary() { + func test_oldStylePlist_dictionary() { let data = """ { "test key" = value; testData = ; "nested array" = (a, b, c); } -""".data(using: .utf16)! +""".data(using: String._Encoding.utf16)! struct Values: Decodable { let testKey: String @@ -925,20 +922,20 @@ private struct PropertyListEncoderTests { } do { let decoded = try PropertyListDecoder().decode(Values.self, from: data) - #expect(decoded.testKey == "value") - #expect(decoded.testData == Data([0xfe, 0xed, 0xfa, 0xce])) - #expect(decoded.nestedArray == ["a", "b", "c"]) + XCTAssertEqual(decoded.testKey, "value") + XCTAssertEqual(decoded.testData, Data([0xfe, 0xed, 0xfa, 0xce])) + XCTAssertEqual(decoded.nestedArray, ["a", "b", "c"]) } catch { - Issue.record("Unexpected error: \(error)") + XCTFail("Unexpected error: \(error)") } } - @Test func oldStylePlist_stringsFileFormat() { + func test_oldStylePlist_stringsFileFormat() { let data = """ string1 = "Good morning"; string2 = "Good afternoon"; string3 = "Good evening"; -""".data(using: .utf16)! +""".data(using: String._Encoding.utf16)! do { let decoded = try PropertyListDecoder().decode([String:String].self, from: data) @@ -947,19 +944,19 @@ string3 = "Good evening"; "string2": "Good afternoon", "string3": "Good evening" ] - #expect(decoded == expected) + XCTAssertEqual(decoded, expected) } catch { - Issue.record("Unexpected error: \(error)") + XCTFail("Unexpected error: \(error)") } } - @Test func oldStylePlist_comments() { + func test_oldStylePlist_comments() { let data = """ // Initial comment */ string1 = /*Test*/ "Good morning"; // Test string2 = "Good afternoon" /*Test// */; string3 = "Good evening"; // Test -""".data(using: .utf16)! +""".data(using: String._Encoding.utf16)! do { let decoded = try PropertyListDecoder().decode([String:String].self, from: data) @@ -968,30 +965,30 @@ string3 = "Good evening"; // Test "string2": "Good afternoon", "string3": "Good evening" ] - #expect(decoded == expected) + XCTAssertEqual(decoded, expected) } catch { - Issue.record("Unexpected error: \(error)") + XCTFail("Unexpected error: \(error)") } } #if FOUNDATION_FRAMEWORK // Requires __PlistDictionaryDecoder - @Test func oldStylePlist_data() { + func test_oldStylePlist_data() { let data = """ data1 = <7465 73 74 696E67 31 323334>; -""".data(using: .utf16)! +""".data(using: String._Encoding.utf16)! do { let decoded = try PropertyListDecoder().decode([String:Data].self, from: data) - let expected = ["data1" : "testing1234".data(using: .utf8)!] - #expect(decoded == expected) + let expected = ["data1" : "testing1234".data(using: String._Encoding.utf8)!] + XCTAssertEqual(decoded, expected) } catch { - Issue.record("Unexpected error: \(error)") + XCTFail("Unexpected error: \(error)") } } #endif @@ -999,38 +996,40 @@ data1 = <7465 #if FOUNDATION_FRAMEWORK // Requires PropertyListSerialization - @Test func bplistCollectionReferences() throws { + func test_BPlistCollectionReferences() { // Use NSArray/NSDictionary and PropertyListSerialization so that we get a bplist with internal references. let c: NSArray = [ "a", "a", "a" ] let b: NSArray = [ c, c, c ] let a: NSArray = [ b, b, b ] let d: NSDictionary = ["a" : a, "b" : b, "c" : c] - let data = try PropertyListSerialization.data(fromPropertyList: d, format: .binary, options: 0) + let data = try! PropertyListSerialization.data(fromPropertyList: d, format: .binary, options: 0) - struct DecodedReferences: Decodable { - let a: [[[String]]] - let b: [[String]] - let c: [String] + do { + struct DecodedReferences: Decodable { + let a: [[[String]]] + let b: [[String]] + let c: [String] + } + + let decoded = try PropertyListDecoder().decode(DecodedReferences.self, from: data) + XCTAssertEqual(decoded.a, a as! [[[String]]]) + XCTAssertEqual(decoded.b, b as! [[String]]) + XCTAssertEqual(decoded.c, c as! [String]) + } catch { + XCTFail("Unexpected error: \(error)") } - - let decoded = try PropertyListDecoder().decode(DecodedReferences.self, from: data) - #expect(decoded.a == a as? [[[String]]]) - #expect(decoded.b == b as? [[String]]) - #expect(decoded.c == c as? [String]) } #endif - @Test func reallyOldDates_5842198() throws { + func test_reallyOldDates_5842198() throws { let plist = "\n\n\n0009-09-15T23:16:13Z\n" - let data = plist.data(using: .utf8)! + let data = plist.data(using: String._Encoding.utf8)! - #expect(throws: Never.self) { - try PropertyListDecoder().decode(Date.self, from: data) - } + XCTAssertNoThrow(try PropertyListDecoder().decode(Date.self, from: data)) } - @Test func badDates() throws { + func test_badDates() throws { let timeInterval = TimeInterval(-63145612800) // This is the equivalent of an all-zero gregorian date. let date = Date(timeIntervalSinceReferenceDate: timeInterval) @@ -1038,26 +1037,26 @@ data1 = <7465 _testRoundTrip(of: [date], in: .binary) } - @Test func badDate_encode() throws { + func test_badDate_encode() throws { let date = Date(timeIntervalSinceReferenceDate: -63145612800) // 0000-01-02 AD let encoder = PropertyListEncoder() encoder.outputFormat = .xml let data = try encoder.encode([date]) let str = String(data: data, encoding: String.Encoding.utf8) - #expect(str == "\n\n\n\n\t0000-01-02T00:00:00Z\n\n\n") + XCTAssertEqual(str, "\n\n\n\n\t0000-01-02T00:00:00Z\n\n\n") } - @Test func badDate_decode() throws { + func test_badDate_decode() throws { // Test that we can correctly decode a distant date in the past let plist = "\n\n\n0000-01-02T00:00:00Z\n" - let data = plist.data(using: .utf8)! + let data = plist.data(using: String._Encoding.utf8)! let d = try PropertyListDecoder().decode(Date.self, from: data) - #expect(d.timeIntervalSinceReferenceDate == -63145612800) + XCTAssertEqual(d.timeIntervalSinceReferenceDate, -63145612800) } - @Test func realEncodeRemoveZeroSuffix() throws { + func test_realEncodeRemoveZeroSuffix() throws { // Tests that we encode "whole-value reals" (such as `2.0`, `-5.0`, etc) // **without** the `.0` for backwards compactability let encoder = PropertyListEncoder() @@ -1066,171 +1065,166 @@ data1 = <7465 let wholeFloat: Float = 2.0 var data = try encoder.encode([wholeFloat]) - var str = try #require(String(data: data, encoding: String.Encoding.utf8)) - var expected = template.replacing( - "<%EXPECTED%>", with: "2") - #expect(str == expected) + var str = try XCTUnwrap(String(data: data, encoding: String.Encoding.utf8)) + var expected = template.replacingOccurrences( + of: "<%EXPECTED%>", with: "2") + XCTAssertEqual(str, expected) let wholeDouble: Double = -5.0 data = try encoder.encode([wholeDouble]) - str = try #require(String(data: data, encoding: String.Encoding.utf8)) - expected = template.replacing( - "<%EXPECTED%>", with: "-5") - #expect(str == expected) + str = try XCTUnwrap(String(data: data, encoding: String.Encoding.utf8)) + expected = template.replacingOccurrences( + of: "<%EXPECTED%>", with: "-5") + XCTAssertEqual(str, expected) // Make sure other reals are not affacted let notWholeDouble = 0.5 data = try encoder.encode([notWholeDouble]) - str = try #require(String(data: data, encoding: String.Encoding.utf8)) - expected = template.replacing( - "<%EXPECTED%>", with: "0.5") - #expect(str == expected) + str = try XCTUnwrap(String(data: data, encoding: String.Encoding.utf8)) + expected = template.replacingOccurrences( + of: "<%EXPECTED%>", with: "0.5") + XCTAssertEqual(str, expected) } - @Test func farFutureDates() throws { + func test_farFutureDates() throws { let date = Date(timeIntervalSince1970: 999999999999.0) _testRoundTrip(of: [date], in: .xml) } - @Test func encode_122065123() throws { + func test_122065123_encode() throws { let date = Date(timeIntervalSinceReferenceDate: 728512994) // 2024-02-01 20:43:14 UTC let encoder = PropertyListEncoder() encoder.outputFormat = .xml let data = try encoder.encode([date]) let str = String(data: data, encoding: String.Encoding.utf8) - #expect(str == "\n\n\n\n\t2024-02-01T20:43:14Z\n\n\n") // Previously encoded as "2024-01-32T20:43:14Z" + XCTAssertEqual(str, "\n\n\n\n\t2024-02-01T20:43:14Z\n\n\n") // Previously encoded as "2024-01-32T20:43:14Z" } - @Test func decodingCompatibility_122065123() throws { + func test_122065123_decodingCompatibility() throws { // Test that we can correctly decode an invalid date let plist = "\n\n\n2024-01-32T20:43:14Z\n" - let data = plist.data(using: .utf8)! + let data = plist.data(using: String._Encoding.utf8)! let d = try PropertyListDecoder().decode(Date.self, from: data) - #expect(d.timeIntervalSinceReferenceDate == 728512994) // 2024-02-01T20:43:14Z + XCTAssertEqual(d.timeIntervalSinceReferenceDate, 728512994) // 2024-02-01T20:43:14Z } - @Test func multibyteCharacters_escaped_noencoding() throws { - let plistData = "These are copyright signs © © blah blah blah.".data(using: .utf8)! + func test_multibyteCharacters_escaped_noencoding() throws { + let plistData = "These are copyright signs © © blah blah blah.".data(using: String._Encoding.utf8)! let result = try PropertyListDecoder().decode(String.self, from: plistData) - #expect("These are copyright signs © © blah blah blah." == result) + XCTAssertEqual("These are copyright signs © © blah blah blah.", result) } - @Test func escapedCharacters() throws { - let plistData = "&'<>"".data(using: .utf8)! + func test_escapedCharacters() throws { + let plistData = "&'<>"".data(using: String._Encoding.utf8)! let result = try PropertyListDecoder().decode(String.self, from: plistData) - #expect("&'<>\"" == result) + XCTAssertEqual("&'<>\"", result) } - @Test func dataWithBOM_utf8() throws { + func test_dataWithBOM_utf8() throws { let bom = Data([0xef, 0xbb, 0xbf]) - let plist = bom + "\n\n\nhello\n".data(using: .utf8)! + let plist = bom + "\n\n\nhello\n".data(using: String._Encoding.utf8)! let result = try PropertyListDecoder().decode(String.self, from: plist) - #expect(result == "hello") + XCTAssertEqual(result, "hello") } + +#if FOUNDATION_FRAMEWORK + // TODO: Depends on UTF32 encoding on non-Darwin platforms - @Test func dataWithBOM_utf32be() throws { + func test_dataWithBOM_utf32be() throws { let bom = Data([0x00, 0x00, 0xfe, 0xff]) - let plist = bom + "\n\n\nhello\n".data(using: .utf32BigEndian)! + let plist = bom + "\n\n\nhello\n".data(using: String._Encoding.utf32BigEndian)! let result = try PropertyListDecoder().decode(String.self, from: plist) - #expect(result == "hello") + XCTAssertEqual(result, "hello") } - @Test func dataWithBOM_utf32le() throws { + func test_dataWithBOM_utf32le() throws { let bom = Data([0xff, 0xfe]) - let plist = bom + "\n\n\nhello\n".data(using: .utf16LittleEndian)! + let plist = bom + "\n\n\nhello\n".data(using: String._Encoding.utf16LittleEndian)! let result = try PropertyListDecoder().decode(String.self, from: plist) - #expect(result == "hello") + XCTAssertEqual(result, "hello") } +#endif - @Test func plistWithBadUTF8() throws { + func test_plistWithBadUTF8() throws { let data = testData(forResource: "bad_plist", withExtension: "bad")! - #expect(throws: (any Error).self) { - try PropertyListDecoder().decode([String].self, from: data) -} } + XCTAssertThrowsError(try PropertyListDecoder().decode([String].self, from: data)) + } - @Test func plistWithEscapedCharacters() throws { - let plist = "com.apple.security.temporary-exception.sbpl(allow mach-lookup (global-name-regex #"^[0-9]+$"))".data(using: .utf8)! + func test_plistWithEscapedCharacters() throws { + let plist = "com.apple.security.temporary-exception.sbpl(allow mach-lookup (global-name-regex #"^[0-9]+$"))".data(using: String._Encoding.utf8)! let result = try PropertyListDecoder().decode([String:String].self, from: plist) - #expect(result == ["com.apple.security.temporary-exception.sbpl" : "(allow mach-lookup (global-name-regex #\"^[0-9]+$\"))"]) + XCTAssertEqual(result, ["com.apple.security.temporary-exception.sbpl" : "(allow mach-lookup (global-name-regex #\"^[0-9]+$\"))"]) } #if FOUNDATION_FRAMEWORK // OpenStep format is not supported in Essentials - @Test func returnRightFormatFromParse() throws { - let plist = "{ CFBundleDevelopmentRegion = en; }".data(using: .utf8)! + func test_returnRightFormatFromParse() throws { + let plist = "{ CFBundleDevelopmentRegion = en; }".data(using: String._Encoding.utf8)! var format : PropertyListDecoder.PropertyListFormat = .binary let _ = try PropertyListDecoder().decode([String:String].self, from: plist, format: &format) - #expect(format == .openStep) + XCTAssertEqual(format, .openStep) } #endif - @Test func decodingEmoji() throws { - let plist = "emoji🚘".data(using: .utf8)! + func test_decodingEmoji() throws { + let plist = "emoji🚘".data(using: String._Encoding.utf8)! let result = try PropertyListDecoder().decode([String:String].self, from: plist) let expected = "\u{0001F698}" - #expect(expected == result["emoji"]) + XCTAssertEqual(expected, result["emoji"]) } - @Test func decodingTooManyCharactersError() throws { + func test_decodingTooManyCharactersError() throws { // Try a plist with too many characters to be a unicode escape sequence - let plist = "emoji".data(using: .utf8)! + let plist = "emoji".data(using: String._Encoding.utf8)! + + XCTAssertThrowsError(try PropertyListDecoder().decode([String:String].self, from: plist)) - #expect(throws: (any Error).self) { - try PropertyListDecoder().decode([String:String].self, from: plist) - } // Try a plist with an invalid unicode escape sequence - let plist2 = "emoji".data(using: .utf8)! + let plist2 = "emoji".data(using: String._Encoding.utf8)! - #expect(throws: (any Error).self) { - try PropertyListDecoder().decode([String:String].self, from: plist2) - } + XCTAssertThrowsError(try PropertyListDecoder().decode([String:String].self, from: plist2)) } - @Test func roundTripEmoji() throws { + func test_roundTripEmoji() throws { let strings = ["🚘", "👩🏻‍❤️‍👨🏿", "🏋🏽‍♂️🕺🏼🥌"] _testRoundTrip(of: strings, in: .xml) _testRoundTrip(of: strings, in: .binary) } - @Test func roundTripEscapedStrings() { + func test_roundTripEscapedStrings() { let strings = ["&", "<", ">"] _testRoundTrip(of: strings, in: .xml) } - @Test func unterminatedComment() { - let plist = "".data(using: .utf8)! - #expect(throws: (any Error).self) { - try PropertyListDecoder().decode([String].self, from: plist) -} } + func test_unterminatedComment() { + let plist = "".data(using: String._Encoding.utf8)! + XCTAssertThrowsError(try PropertyListDecoder().decode([String].self, from: plist)) + } - @Test func incompleteOpenTag() { - let plist = ".allNils) let testEmptyDict = try PropertyListDecoder().decode(DecodeIfPresentAllTypes.self, from: emptyDictEncoding) - #expect(testEmptyDict == .allNils) + XCTAssertEqual(testEmptyDict, .allNils) let allNullDictEncoding = try encoder.encode(DecodeIfPresentAllTypes.allNils) let testAllNullDict = try PropertyListDecoder().decode(DecodeIfPresentAllTypes.self, from: allNullDictEncoding) - #expect(testAllNullDict == .allNils) + XCTAssertEqual(testAllNullDict, .allNils) let allOnesDictEncoding = try encoder.encode(DecodeIfPresentAllTypes.allOnes) let testAllOnesDict = try PropertyListDecoder().decode(DecodeIfPresentAllTypes.self, from: allOnesDictEncoding) - #expect(testAllOnesDict == .allOnes) + XCTAssertEqual(testAllOnesDict, .allOnes) let emptyArrayEncoding = try encoder.encode(DecodeIfPresentAllTypes.allNils) let testEmptyArray = try PropertyListDecoder().decode(DecodeIfPresentAllTypes.self, from: emptyArrayEncoding) - #expect(testEmptyArray == .allNils) + XCTAssertEqual(testEmptyArray, .allNils) let allNullArrayEncoding = try encoder.encode(DecodeIfPresentAllTypes.allNils) let testAllNullArray = try PropertyListDecoder().decode(DecodeIfPresentAllTypes.self, from: allNullArrayEncoding) - #expect(testAllNullArray == .allNils) + XCTAssertEqual(testAllNullArray, .allNils) let allOnesArrayEncoding = try encoder.encode(DecodeIfPresentAllTypes.allOnes) let testAllOnesArray = try PropertyListDecoder().decode(DecodeIfPresentAllTypes.self, from: allOnesArrayEncoding) - #expect(testAllOnesArray == .allOnes) + XCTAssertEqual(testAllOnesArray, .allOnes) } } - @Test func garbageCharactersAfterXMLTagName() throws { - let garbage = "barfoo".data(using: .utf8)! + func test_garbageCharactersAfterXMLTagName() throws { + let garbage = "barfoo".data(using: String._Encoding.utf8)! + + XCTAssertThrowsError(try PropertyListDecoder().decode([String:String].self, from: garbage)) - #expect(throws: (any Error).self) { - try PropertyListDecoder().decode([String:String].self, from: garbage) - } // Historical behavior allows for whitespace to immediately follow tag names - let acceptable = "barfoo".data(using: .utf8)! + let acceptable = "barfoo".data(using: String._Encoding.utf8)! - #expect(try PropertyListDecoder().decode([String:String].self, from: acceptable) == ["bar":"foo"]) + XCTAssertEqual(try PropertyListDecoder().decode([String:String].self, from: acceptable), ["bar":"foo"]) } } // MARK: - Helper Global Functions -func AssertEqualPaths(_ lhs: [CodingKey], _ rhs: [CodingKey], _ prefix: String, sourceLocation: SourceLocation = #_sourceLocation) { +func XCTAssertEqualPaths(_ lhs: [CodingKey], _ rhs: [CodingKey], _ prefix: String) { if lhs.count != rhs.count { - Issue.record("\(prefix) [CodingKey].count mismatch: \(lhs.count) != \(rhs.count)", sourceLocation: sourceLocation) + XCTFail("\(prefix) [CodingKey].count mismatch: \(lhs.count) != \(rhs.count)") return } @@ -1594,21 +1570,21 @@ func AssertEqualPaths(_ lhs: [CodingKey], _ rhs: [CodingKey], _ prefix: String, switch (key1.intValue, key2.intValue) { case (.none, .none): break case (.some(let i1), .none): - Issue.record("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != nil", sourceLocation: sourceLocation) + XCTFail("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != nil") return case (.none, .some(let i2)): - Issue.record("\(prefix) CodingKey.intValue mismatch: nil != \(type(of: key2))(\(i2))", sourceLocation: sourceLocation) + XCTFail("\(prefix) CodingKey.intValue mismatch: nil != \(type(of: key2))(\(i2))") return case (.some(let i1), .some(let i2)): guard i1 == i2 else { - Issue.record("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != \(type(of: key2))(\(i2))", sourceLocation: sourceLocation) + XCTFail("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != \(type(of: key2))(\(i2))") return } break } - #expect(key1.stringValue == key2.stringValue, "\(prefix) CodingKey.stringValue mismatch: \(type(of: key1))('\(key1.stringValue)') != \(type(of: key2))('\(key2.stringValue)')", sourceLocation: sourceLocation) + XCTAssertEqual(key1.stringValue, key2.stringValue, "\(prefix) CodingKey.stringValue mismatch: \(type(of: key1))('\(key1.stringValue)') != \(type(of: key2))('\(key2.stringValue)')") } } @@ -1951,13 +1927,13 @@ private struct NestedContainersTestType : Encodable { func encode(to encoder: Encoder) throws { if self.testSuperEncoder { var topLevelContainer = encoder.container(keyedBy: TopLevelCodingKeys.self) - AssertEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") - AssertEqualPaths(topLevelContainer.codingPath, [], "New first-level keyed container has non-empty codingPath.") + XCTAssertEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") + XCTAssertEqualPaths(topLevelContainer.codingPath, [], "New first-level keyed container has non-empty codingPath.") let superEncoder = topLevelContainer.superEncoder(forKey: .a) - AssertEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") - AssertEqualPaths(topLevelContainer.codingPath, [], "First-level keyed container's codingPath changed.") - AssertEqualPaths(superEncoder.codingPath, [TopLevelCodingKeys.a], "New superEncoder had unexpected codingPath.") + XCTAssertEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") + XCTAssertEqualPaths(topLevelContainer.codingPath, [], "First-level keyed container's codingPath changed.") + XCTAssertEqualPaths(superEncoder.codingPath, [TopLevelCodingKeys.a], "New superEncoder had unexpected codingPath.") _testNestedContainers(in: superEncoder, baseCodingPath: [TopLevelCodingKeys.a]) } else { _testNestedContainers(in: encoder, baseCodingPath: []) @@ -1965,57 +1941,57 @@ private struct NestedContainersTestType : Encodable { } func _testNestedContainers(in encoder: Encoder, baseCodingPath: [CodingKey]) { - AssertEqualPaths(encoder.codingPath, baseCodingPath, "New encoder has non-empty codingPath.") + XCTAssertEqualPaths(encoder.codingPath, baseCodingPath, "New encoder has non-empty codingPath.") // codingPath should not change upon fetching a non-nested container. var firstLevelContainer = encoder.container(keyedBy: TopLevelCodingKeys.self) - AssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - AssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "New first-level keyed container has non-empty codingPath.") + XCTAssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + XCTAssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "New first-level keyed container has non-empty codingPath.") // Nested Keyed Container do { // Nested container for key should have a new key pushed on. var secondLevelContainer = firstLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self, forKey: .a) - AssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - AssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - AssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "New second-level keyed container had unexpected codingPath.") + XCTAssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + XCTAssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") + XCTAssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "New second-level keyed container had unexpected codingPath.") // Inserting a keyed container should not change existing coding paths. let thirdLevelContainerKeyed = secondLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self, forKey: .one) - AssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - AssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - AssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") - AssertEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, IntermediateCodingKeys.one], "New third-level keyed container had unexpected codingPath.") + XCTAssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + XCTAssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") + XCTAssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") + XCTAssertEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, IntermediateCodingKeys.one], "New third-level keyed container had unexpected codingPath.") // Inserting an unkeyed container should not change existing coding paths. let thirdLevelContainerUnkeyed = secondLevelContainer.nestedUnkeyedContainer(forKey: .two) - AssertEqualPaths(encoder.codingPath, baseCodingPath + [], "Top-level Encoder's codingPath changed.") - AssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath + [], "First-level keyed container's codingPath changed.") - AssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") - AssertEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, IntermediateCodingKeys.two], "New third-level unkeyed container had unexpected codingPath.") + XCTAssertEqualPaths(encoder.codingPath, baseCodingPath + [], "Top-level Encoder's codingPath changed.") + XCTAssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath + [], "First-level keyed container's codingPath changed.") + XCTAssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") + XCTAssertEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, IntermediateCodingKeys.two], "New third-level unkeyed container had unexpected codingPath.") } // Nested Unkeyed Container do { // Nested container for key should have a new key pushed on. var secondLevelContainer = firstLevelContainer.nestedUnkeyedContainer(forKey: .b) - AssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - AssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - AssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "New second-level keyed container had unexpected codingPath.") + XCTAssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + XCTAssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") + XCTAssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "New second-level keyed container had unexpected codingPath.") // Appending a keyed container should not change existing coding paths. let thirdLevelContainerKeyed = secondLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self) - AssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - AssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - AssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") - AssertEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 0)], "New third-level keyed container had unexpected codingPath.") + XCTAssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + XCTAssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") + XCTAssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") + XCTAssertEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 0)], "New third-level keyed container had unexpected codingPath.") // Appending an unkeyed container should not change existing coding paths. let thirdLevelContainerUnkeyed = secondLevelContainer.nestedUnkeyedContainer() - AssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - AssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - AssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") - AssertEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 1)], "New third-level unkeyed container had unexpected codingPath.") + XCTAssertEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + XCTAssertEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") + XCTAssertEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") + XCTAssertEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 1)], "New third-level unkeyed container had unexpected codingPath.") } } }