From 9bf0f2bbfeb80a42adc1a5137ebbc829f8522554 Mon Sep 17 00:00:00 2001 From: Sacha DSO Date: Mon, 19 Aug 2024 21:43:55 -1000 Subject: [PATCH 1/3] Migrates to Swift 6 --- Package.swift | 3 ++- Sources/Arrow/Arrow.swift | 23 ++++++++++++++++------- Sources/Arrow/JSON.swift | 3 +-- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Package.swift b/Package.swift index 5dfe63f..3c05780 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,5 @@ -// swift-tools-version:5.9 +// swift-tools-version:6.0 + import PackageDescription let package = Package( diff --git a/Sources/Arrow/Arrow.swift b/Sources/Arrow/Arrow.swift index 829cc9a..40a935c 100644 --- a/Sources/Arrow/Arrow.swift +++ b/Sources/Arrow/Arrow.swift @@ -45,8 +45,6 @@ public extension ArrowParsable { } } -private var dateFormatter: DateFormatter? = DateFormatter() -private var useReferenceDate = false /** This is used to configure NSDate parsing on a global scale. @@ -61,14 +59,23 @@ For more fine grained control, use `dateFormat` on a per field basis : createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") */ public class Arrow { + + internal static let arrowSerialQueue = DispatchQueue(label: "com.arrow.arrow") + nonisolated(unsafe) internal static var dateFormatter: DateFormatter? = DateFormatter() + nonisolated(unsafe) internal static var useReferenceDate = false + /// Sets the defaut dateFormat for parsing NSDates. public class func setDateFormat(_ format: String) { - dateFormatter?.dateFormat = format + arrowSerialQueue.sync { + dateFormatter?.dateFormat = format + } } /// Sets the defaut dateFormatter for parsing NSDates. public class func setDateFormatter(_ formatter: DateFormatter?) { - dateFormatter = formatter + arrowSerialQueue.sync { + dateFormatter = formatter + } } /** @@ -80,7 +87,9 @@ public class Arrow { documentation */ public class func setUseTimeIntervalSinceReferenceDate(_ ref: Bool) { - useReferenceDate = ref + arrowSerialQueue.sync { + useReferenceDate = ref + } } } @@ -202,7 +211,7 @@ public func <-- (left: inout Date?, right: JSON?) { df.dateFormat = customFormat left = df.date(from: s) } else if let s = right?.data as? String { - if let date = dateFormatter?.date(from: s) { + if let date = Arrow.arrowSerialQueue.sync(execute: { Arrow.dateFormatter?.date(from: s) }) { left = date } else if let t = TimeInterval(s) { left = timeIntervalToDate(t) @@ -292,7 +301,7 @@ func parseString(_ left: inout T?, string: String) { } func timeIntervalToDate(_ timeInterval: TimeInterval) -> Date { - return useReferenceDate + return Arrow.arrowSerialQueue.sync(execute: { Arrow.useReferenceDate }) ? Date(timeIntervalSinceReferenceDate: timeInterval) : Date(timeIntervalSince1970: timeInterval) } diff --git a/Sources/Arrow/JSON.swift b/Sources/Arrow/JSON.swift index b963aae..2e8e3cb 100644 --- a/Sources/Arrow/JSON.swift +++ b/Sources/Arrow/JSON.swift @@ -125,8 +125,7 @@ open class JSON { extension JSON: CustomDebugStringConvertible { /// This is just for supporting default console logs. - open var debugDescription: String { + public var debugDescription: String { return data.debugDescription } - } From ffda8d4b74c0a0618b0ab9a4b0c4f48020415c50 Mon Sep 17 00:00:00 2001 From: Sacha DSO Date: Tue, 24 Sep 2024 07:56:58 -1000 Subject: [PATCH 2/3] Migrates XCTests to Testing --- Sources/Arrow/Arrow.swift | 2 +- Sources/Arrow/Extensions.swift | 2 +- Tests/ArrowTests/ArrayTests.swift | 119 ++++++++++-------- Tests/ArrowTests/CustomModelTests.swift | 25 ++-- .../CustomRawRepresentableTests.swift | 14 +-- Tests/ArrowTests/DateTests.swift | 30 +++-- Tests/ArrowTests/EnumTests.swift | 28 +++-- Tests/ArrowTests/Models/Profile.swift | 1 - Tests/ArrowTests/NativeTypesTests.swift | 27 ++-- Tests/ArrowTests/StringCoercionTests.swift | 18 +-- Tests/ArrowTests/StringTests.swift | 18 +-- Tests/ArrowTests/TypeConversionTests.swift | 40 +++--- Tests/ArrowTests/URLTests.swift | 32 ++--- Tests/ArrowTests/XCTestManifests.swift | 18 --- Tests/LinuxMain.swift | 16 --- 15 files changed, 199 insertions(+), 191 deletions(-) delete mode 100644 Tests/ArrowTests/XCTestManifests.swift delete mode 100644 Tests/LinuxMain.swift diff --git a/Sources/Arrow/Arrow.swift b/Sources/Arrow/Arrow.swift index 40a935c..9fe0593 100644 --- a/Sources/Arrow/Arrow.swift +++ b/Sources/Arrow/Arrow.swift @@ -7,7 +7,7 @@ // import Foundation -import CoreGraphics + /** This is the protocol that makes your swift Models JSON parsable. diff --git a/Sources/Arrow/Extensions.swift b/Sources/Arrow/Extensions.swift index 56cb9b6..6064f4a 100644 --- a/Sources/Arrow/Extensions.swift +++ b/Sources/Arrow/Extensions.swift @@ -7,7 +7,7 @@ // import Foundation -import CoreGraphics + public protocol ArrowInitializable { init?(_ json: JSON?) diff --git a/Tests/ArrowTests/ArrayTests.swift b/Tests/ArrowTests/ArrayTests.swift index 75fcdf9..753ed09 100644 --- a/Tests/ArrowTests/ArrayTests.swift +++ b/Tests/ArrowTests/ArrayTests.swift @@ -6,7 +6,7 @@ // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // -import XCTest +import Testing import Arrow struct ArrayContainer { @@ -36,104 +36,113 @@ extension ArrayContainer: ArrowParsable { } } -class ArrayTests: XCTestCase { +@Suite +struct ArrayTests { var arrayContainer = ArrayContainer() - override func setUp() { - super.setUp() + init() { if let json: JSON = mockJSON() { arrayContainer <-- json } } - func testParsingArrayOfCustomModels() { - XCTAssertEqual(arrayContainer.phoneNumbers.count, 3) + @Test + func parsingArrayOfCustomModels() { + #expect(arrayContainer.phoneNumbers.count == 3) if arrayContainer.phoneNumbers.count >= 3 { - XCTAssertEqual(arrayContainer.phoneNumbers[0].label, "house") - XCTAssertEqual(arrayContainer.phoneNumbers[1].label, "cell") - XCTAssertEqual(arrayContainer.phoneNumbers[2].label, "work") + #expect(arrayContainer.phoneNumbers[0].label == "house") + #expect(arrayContainer.phoneNumbers[1].label == "cell") + #expect(arrayContainer.phoneNumbers[2].label == "work") - XCTAssertEqual(arrayContainer.phoneNumbers[0].number, "9809876545") - XCTAssertEqual(arrayContainer.phoneNumbers[1].number, "0908070656") - XCTAssertEqual(arrayContainer.phoneNumbers[2].number, "0916570656") + #expect(arrayContainer.phoneNumbers[0].number == "9809876545") + #expect(arrayContainer.phoneNumbers[1].number == "0908070656") + #expect(arrayContainer.phoneNumbers[2].number == "0916570656") } else { - XCTFail("Parsing ArrayOf Custom Models Fails") + Issue.record("Parsing ArrayOf Custom Models Fails") } } - func testParsingOptionalArrayOfCustomModels() { - XCTAssertEqual(arrayContainer.optionalPhoneNumbers?.count, 3) + @Test + func parsingOptionalArrayOfCustomModels() { + #expect(arrayContainer.optionalPhoneNumbers?.count == 3) - XCTAssertEqual(arrayContainer.optionalPhoneNumbers?[0].label, "house") - XCTAssertEqual(arrayContainer.optionalPhoneNumbers?[1].label, "cell") - XCTAssertEqual(arrayContainer.optionalPhoneNumbers?[2].label, "work") + #expect(arrayContainer.optionalPhoneNumbers?[0].label == "house") + #expect(arrayContainer.optionalPhoneNumbers?[1].label == "cell") + #expect(arrayContainer.optionalPhoneNumbers?[2].label == "work") - XCTAssertEqual(arrayContainer.optionalPhoneNumbers?[0].number, "9809876545") - XCTAssertEqual(arrayContainer.optionalPhoneNumbers?[1].number, "0908070656") - XCTAssertEqual(arrayContainer.optionalPhoneNumbers?[2].number, "0916570656") + #expect(arrayContainer.optionalPhoneNumbers?[0].number == "9809876545") + #expect(arrayContainer.optionalPhoneNumbers?[1].number == "0908070656") + #expect(arrayContainer.optionalPhoneNumbers?[2].number == "0916570656") } - func testParsingArrayOfStrings() { - XCTAssertEqual(arrayContainer.strings.count, 3) + @Test + func parsingArrayOfStrings() { + #expect(arrayContainer.strings.count == 3) if arrayContainer.strings.count >= 3 { - XCTAssertEqual(arrayContainer.strings[0], "one") - XCTAssertEqual(arrayContainer.strings[1], "two") - XCTAssertEqual(arrayContainer.strings[2], "three") + #expect(arrayContainer.strings[0] == "one") + #expect(arrayContainer.strings[1] == "two") + #expect(arrayContainer.strings[2] == "three") } else { - XCTFail("Parsing an array of strings fails") + Issue.record("Parsing an array of strings fails") } } - func testParsingArrayOfInts() { - XCTAssertEqual(arrayContainer.ints.count, 3) + @Test + func parsingArrayOfInts() { + #expect(arrayContainer.ints.count == 3) if arrayContainer.ints.count >= 3 { - XCTAssertEqual(arrayContainer.ints[0], 1) - XCTAssertEqual(arrayContainer.ints[1], 2) - XCTAssertEqual(arrayContainer.ints[2], 3) + #expect(arrayContainer.ints[0] == 1) + #expect(arrayContainer.ints[1] == 2) + #expect(arrayContainer.ints[2] == 3) } else { - XCTFail("Parsing an array of ints fails") + Issue.record("Parsing an array of ints fails") } } - func testParsingArrayOfBools() { - XCTAssertEqual(arrayContainer.bools.count, 3) + @Test + func parsingArrayOfBools() { + #expect(arrayContainer.bools.count == 3) if arrayContainer.bools.count >= 3 { - XCTAssertEqual(arrayContainer.bools[0], true) - XCTAssertEqual(arrayContainer.bools[1], false) - XCTAssertEqual(arrayContainer.bools[2], true) + #expect(arrayContainer.bools[0] == true) + #expect(arrayContainer.bools[1] == false) + #expect(arrayContainer.bools[2] == true) } else { - XCTFail("Parsing an array of bools fails") + Issue.record("Parsing an array of bools fails") } } - func testNestedParsing() { - XCTAssertEqual(arrayContainer.meaningOfLife, 42) + @Test + func nestedParsing() { + #expect(arrayContainer.meaningOfLife == 42) } - func testNestedArrayParsing() { - XCTAssertEqual(arrayContainer.nestedArrayParsing, "Cool") + @Test + func nestedArrayParsing() { + #expect(arrayContainer.nestedArrayParsing == "Cool") } - func testParsingArrayOfEnums() { - XCTAssertEqual(arrayContainer.weekdays.count, 3) + @Test + func parsingArrayOfEnums() { + #expect(arrayContainer.weekdays.count == 3) if arrayContainer.weekdays.count >= 3 { - XCTAssertEqual(arrayContainer.weekdays[0], WeekDay.monday) - XCTAssertEqual(arrayContainer.weekdays[1], WeekDay.wednesday) - XCTAssertEqual(arrayContainer.weekdays[2], WeekDay.friday) + #expect(arrayContainer.weekdays[0] == WeekDay.monday) + #expect(arrayContainer.weekdays[1] == WeekDay.wednesday) + #expect(arrayContainer.weekdays[2] == WeekDay.friday) } else { - XCTFail("Parsing an array of enums fails") + Issue.record("Parsing an array of enums fails") } } - func testParsingOptionalArrayOfEnums() { - XCTAssertEqual(arrayContainer.optionalWeekdays?.count, 3) + @Test + func parsingOptionalArrayOfEnums() { + #expect(arrayContainer.optionalWeekdays?.count == 3) if let w = arrayContainer.optionalWeekdays, w.count >= 3 { - XCTAssertEqual(arrayContainer.optionalWeekdays?[0], WeekDay.monday) - XCTAssertEqual(arrayContainer.optionalWeekdays?[1], WeekDay.wednesday) - XCTAssertEqual(arrayContainer.optionalWeekdays?[2], WeekDay.friday) + #expect(arrayContainer.optionalWeekdays?[0] == WeekDay.monday) + #expect(arrayContainer.optionalWeekdays?[1] == WeekDay.wednesday) + #expect(arrayContainer.optionalWeekdays?[2] == WeekDay.friday) } else { - XCTFail("Parsing an array of optional enums fails") + Issue.record("Parsing an array of optional enums fails") } } } diff --git a/Tests/ArrowTests/CustomModelTests.swift b/Tests/ArrowTests/CustomModelTests.swift index 75d72df..371e7d3 100644 --- a/Tests/ArrowTests/CustomModelTests.swift +++ b/Tests/ArrowTests/CustomModelTests.swift @@ -6,30 +6,34 @@ // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // -import XCTest +import Testing +import Foundation import Arrow -class CustomModelTests: XCTestCase { +@Suite +struct CustomModelTests { var customModelContainer = CustomModelContainer() - override func setUp() { - super.setUp() + init() { if let json: JSON = mockJSON() { customModelContainer <-- json } } + @Test func testParsingCustomModel() { - XCTAssertEqual(customModelContainer.stats.numberOfFriends, 163) - XCTAssertEqual(customModelContainer.stats.numberOfFans, 10987) + #expect(customModelContainer.stats.numberOfFriends == 163) + #expect(customModelContainer.stats.numberOfFans == 10987) } + @Test func testParsingOptionalCustomModel() { - XCTAssertEqual(customModelContainer.optionalStats?.numberOfFriends, 163) - XCTAssertEqual(customModelContainer.optionalStats?.numberOfFans, 10987) + #expect(customModelContainer.optionalStats?.numberOfFriends == 163) + #expect(customModelContainer.optionalStats?.numberOfFans == 10987) } + @Test func testParsingIssue() { let myJson = "{ \"homer\": \"simpson\"}" guard let jsonData = myJson.data(using: .utf8), @@ -39,9 +43,10 @@ class CustomModelTests: XCTestCase { } var aSimpson = Doh() aSimpson.deserialize(json) - XCTAssertEqual(aSimpson.homer, "simpson") + #expect(aSimpson.homer == "simpson") } + @Test func testParsingIssueWorksWithString() { let myJson = "{ \"homer\": \"simpson\"}" guard let json = JSON(myJson) else { @@ -49,7 +54,7 @@ class CustomModelTests: XCTestCase { } var aSimpson = Doh() aSimpson.deserialize(json) - XCTAssertEqual(aSimpson.homer, "simpson") + #expect(aSimpson.homer == "simpson") } } diff --git a/Tests/ArrowTests/CustomRawRepresentableTests.swift b/Tests/ArrowTests/CustomRawRepresentableTests.swift index a6411cc..0eead0f 100644 --- a/Tests/ArrowTests/CustomRawRepresentableTests.swift +++ b/Tests/ArrowTests/CustomRawRepresentableTests.swift @@ -6,23 +6,23 @@ // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // -import XCTest +import Testing import Arrow -class CustomRawRepresentableTests: XCTestCase { +@Suite +struct CustomRawRepresentableTests { var customRawRepresentableContainer = CustomRawRepresentableContainer() - override func setUp() { - super.setUp() + init() { if let json: JSON = mockJSON() { customRawRepresentableContainer <-- json } } + @Test func testParsingCustomModel() { - XCTAssertEqual(customRawRepresentableContainer.identifier, 15678) - XCTAssertEqual(customRawRepresentableContainer.rawValue, "15678") + #expect(customRawRepresentableContainer.identifier == 15678) + #expect(customRawRepresentableContainer.rawValue == "15678") } - } diff --git a/Tests/ArrowTests/DateTests.swift b/Tests/ArrowTests/DateTests.swift index a534b57..e59aa34 100644 --- a/Tests/ArrowTests/DateTests.swift +++ b/Tests/ArrowTests/DateTests.swift @@ -6,41 +6,44 @@ // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // -import XCTest +import Testing +import Foundation import Arrow -class DateTests: XCTestCase { +@Suite +struct DateTests { var dateContainer = DateContainer() - override func setUp() { - super.setUp() + init() { Arrow.setUseTimeIntervalSinceReferenceDate(true) if let json: JSON = mockJSON() { dateContainer <-- json } } + @Test func testParsingDate() { let df = DateFormatter() df.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" if let date = df.date(from: "2013-06-07T16:38:40+02:00") { - XCTAssertEqual(date.timeIntervalSinceReferenceDate, dateContainer.createdAt - .timeIntervalSinceReferenceDate, accuracy: 0.1) + #expect(date.timeIntervalSinceReferenceDate == dateContainer.createdAt.timeIntervalSinceReferenceDate) } else { - XCTFail("Parsing a date fails") + Issue.record("Parsing a date fails") } } + @Test func testParsingOptionalDate() { let timestamp: TimeInterval = 392308720 if let d = dateContainer.optionalDate?.timeIntervalSinceReferenceDate { - XCTAssertEqual(timestamp, d, accuracy: 0.1) + #expect(timestamp == d) } else { - XCTFail("Parsing an Optional Date fails") + Issue.record("Parsing an Optional Date fails") } } + @Test func testCustomDateFormatterDate() { if let json: JSON = mockJSON() { var aDate = Date() @@ -48,12 +51,13 @@ class DateTests: XCTestCase { df.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" df.timeZone = TimeZone(secondsFromGMT: 60*60*5) aDate <-- json["created_at"]?.dateFormatter(df) - XCTAssertEqual(aDate, df.date(from: "2013-06-07T16:38:40+02:00")) + #expect(aDate == df.date(from: "2013-06-07T16:38:40+02:00")) } else { - XCTFail("Using a custom date Parser fails") + Issue.record("Using a custom date Parser fails") } } + @Test func testGlobalDateFormatterDate() { let df = DateFormatter() df.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" @@ -61,9 +65,9 @@ class DateTests: XCTestCase { if let json: JSON = mockJSON() { var aDate = Date() aDate <-- json["created_at"] - XCTAssertEqual(aDate, df.date(from: "2013-06-07T16:38:40+02:00")) + #expect(aDate == df.date(from: "2013-06-07T16:38:40+02:00")) } else { - XCTFail("Using a global date parser fails") + Issue.record("Using a global date parser fails") } Arrow.setDateFormatter(nil) diff --git a/Tests/ArrowTests/EnumTests.swift b/Tests/ArrowTests/EnumTests.swift index b6a2136..1be5ab5 100644 --- a/Tests/ArrowTests/EnumTests.swift +++ b/Tests/ArrowTests/EnumTests.swift @@ -6,33 +6,37 @@ // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // -import XCTest +import Testing import Arrow -class EnumTests: XCTestCase { +@Suite +struct EnumTests { var enumContainer = EnumContainer() - override func setUp() { - super.setUp() + init() { if let json: JSON = mockJSON() { enumContainer <-- json } } - func testParsingEnumInt() { - XCTAssertEqual(enumContainer.weekday, WeekDay.wednesday) + @Test + func parsingEnumInt() { + #expect(enumContainer.weekday == WeekDay.wednesday) } - func testParsingEnumString() { - XCTAssertEqual(enumContainer.difficulty, Difficulty.high) + @Test + func parsingEnumString() { + #expect(enumContainer.difficulty == Difficulty.high) } - func testParsingOptionalEnumInt() { - XCTAssertEqual(enumContainer.optionalWeekday, WeekDay.wednesday) + @Test + func parsingOptionalEnumInt() { + #expect(enumContainer.optionalWeekday == WeekDay.wednesday) } - func testParsingOptionalEnumString() { - XCTAssertEqual(enumContainer.optionalDifficulty, Difficulty.high) + @Test + func parsingOptionalEnumString() { + #expect(enumContainer.optionalDifficulty == Difficulty.high) } } diff --git a/Tests/ArrowTests/Models/Profile.swift b/Tests/ArrowTests/Models/Profile.swift index 44d6cc5..0b4df8e 100644 --- a/Tests/ArrowTests/Models/Profile.swift +++ b/Tests/ArrowTests/Models/Profile.swift @@ -7,7 +7,6 @@ // import Foundation -import CoreGraphics struct Profile { var identifier = 0 diff --git a/Tests/ArrowTests/NativeTypesTests.swift b/Tests/ArrowTests/NativeTypesTests.swift index af04bfc..8bd8015 100644 --- a/Tests/ArrowTests/NativeTypesTests.swift +++ b/Tests/ArrowTests/NativeTypesTests.swift @@ -6,35 +6,38 @@ // Copyright (c) 2015 Sacha Durand Saint Omer. All rights reserved. // -import XCTest +import Testing import Arrow -class NativeTypesTests: XCTestCase { +struct NativeTypesTests { var profile = Profile() - override func setUp() { - super.setUp() + init() { Arrow.setUseTimeIntervalSinceReferenceDate(true) if let json: JSON = mockJSON() { profile <-- json } } - func testParsingInt() { - XCTAssertEqual(profile.identifier, 15678) + @Test + func pParsingInt() { + #expect(profile.identifier == 15678) } - func testParsingFloat() { - XCTAssertEqual(profile.float, 0.12) + @Test + func parsingFloat() { + #expect(profile.float == 0.12) } - func testParsingCGFloat() { - XCTAssertEqual(profile.cgfloat, 0.12) + @Test + func parsingCGFloat() { + #expect(profile.cgfloat == 0.12) } - func testParsingDouble() { - XCTAssertEqual(profile.double, 0.123456789) + @Test + func parsingDouble() { + #expect(profile.double == 0.123456789) } } diff --git a/Tests/ArrowTests/StringCoercionTests.swift b/Tests/ArrowTests/StringCoercionTests.swift index a485e55..3837659 100644 --- a/Tests/ArrowTests/StringCoercionTests.swift +++ b/Tests/ArrowTests/StringCoercionTests.swift @@ -6,7 +6,8 @@ // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // -import XCTest +import Testing +import Foundation import Arrow struct CoercionContainer { @@ -24,26 +25,29 @@ extension CoercionContainer: ArrowParsable { } } -class StringCoercionTests: XCTestCase { +@Suite +struct StringCoercionTests { var coercionContainer = CoercionContainer() - override func setUp() { - super.setUp() + init() { if let json: JSON = mockJSON() { coercionContainer <-- json } } + @Test func testParsingFloatString() { - XCTAssertEqual(coercionContainer.floatString, 0.12) + #expect(coercionContainer.floatString == 0.12) } + @Test func testParsingCGFloatString() { - XCTAssertEqual(coercionContainer.cgfloatString, 0.12) + #expect(coercionContainer.cgfloatString == 0.12) } + @Test func testParsingDoubleString() { - XCTAssertEqual(coercionContainer.doubleString, 0.123456789) + #expect(coercionContainer.doubleString == 0.123456789) } } diff --git a/Tests/ArrowTests/StringTests.swift b/Tests/ArrowTests/StringTests.swift index 4ae19e4..5f71423 100644 --- a/Tests/ArrowTests/StringTests.swift +++ b/Tests/ArrowTests/StringTests.swift @@ -6,25 +6,27 @@ // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // -import XCTest +import Testing import Arrow -class StringTests: XCTestCase { +@Suite +struct StringTests { var stringContainer = StringContainer() - override func setUp() { - super.setUp() + init() { if let json: JSON = mockJSON() { stringContainer <-- json } } - func testParsingString() { - XCTAssertEqual(stringContainer.name, "Francky") + @Test + func parsingString() { + #expect(stringContainer.name == "Francky") } - func testParsingOptionalString() { - XCTAssertEqual(stringContainer.optionalName, "Francky") + @Test + func parsingOptionalString() { + #expect(stringContainer.optionalName == "Francky") } } diff --git a/Tests/ArrowTests/TypeConversionTests.swift b/Tests/ArrowTests/TypeConversionTests.swift index 800c751..73fee0a 100644 --- a/Tests/ArrowTests/TypeConversionTests.swift +++ b/Tests/ArrowTests/TypeConversionTests.swift @@ -6,55 +6,67 @@ // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // -import XCTest +import Testing +import Foundation import Arrow -class TypeConversionTests: XCTestCase { +@Suite +struct TypeConversionTests { let json: JSON? = mockJSON() + @Test func testStringConversion() { - XCTAssertEqual(String(json?["name"]), "Francky") + #expect(String(json?["name"]) == "Francky") } + @Test func testIntConversion() { - XCTAssertEqual(Int(json?["id"]), 15678) + #expect(Int(json?["id"]) == 15678) } + @Test func testUIntConversion() { - XCTAssertEqual(UInt(json?["id"]), 15678) + #expect(UInt(json?["id"]) == 15678) } + @Test func testDoubleConversion() { - XCTAssertEqual(Double(json?["double"]), 0.123456789) + #expect(Double(json?["double"]) == 0.123456789) } + @Test func testFloatConversion() { - XCTAssertEqual(Float(json?["float"]), 0.12) + #expect(Float(json?["float"]) == 0.12) } + @Test func testCGFloatConversion() { - XCTAssertEqual(CGFloat(json?["float"]), 0.12) + #expect(CGFloat(json?["float"]) == 0.12) } + @Test func testBoolConversion() { - XCTAssertEqual(Bool(json?["bool"]), true) + #expect(Bool(json?["bool"]) == true) } + @Test func testEnumConversion() { - XCTAssertEqual(WeekDay(json?["weekdayInt"]), WeekDay.wednesday) + #expect(WeekDay(json?["weekdayInt"]) == WeekDay.wednesday) } + @Test func testArrayConversions() { - XCTAssertEqual([String](json?["strings"]) ?? [], ["one", "two", "three"]) + #expect([String](json?["strings"]) ?? [] == ["one", "two", "three"]) } + @Test func testArrayEnumConversions() { - XCTAssertEqual([WeekDay](json?["weekdays"]) ?? [], [.monday, .wednesday, .friday]) + #expect([WeekDay](json?["weekdays"]) ?? [] == [.monday, .wednesday, .friday]) } + @Test func testDictionaryConversions() { - XCTAssertEqual([String: String](json?["dict"]) ?? [:], ["one": "1", "two": "2"]) + #expect([String: String](json?["dict"]) ?? [:] == ["one": "1", "two": "2"]) } - } diff --git a/Tests/ArrowTests/URLTests.swift b/Tests/ArrowTests/URLTests.swift index 4907496..ed2c815 100644 --- a/Tests/ArrowTests/URLTests.swift +++ b/Tests/ArrowTests/URLTests.swift @@ -6,37 +6,37 @@ // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // -import XCTest +import Testing import Arrow -class URLTests: XCTestCase { +@Suite +struct URLTests { var urlContainer = URLContainer() - override func setUp() { - super.setUp() + init() { if let json: JSON = mockJSON() { urlContainer <-- json } } - func testParsingURL() { - XCTAssertEqual(urlContainer.link.absoluteString.removingPercentEncoding, - "https://apple.com/steve") + @Test + func parsingURL() { + #expect(urlContainer.link.absoluteString.removingPercentEncoding == "https://apple.com/steve") } - func testParsingEmojiURL() { - XCTAssertEqual(urlContainer.emojiLink.absoluteString.removingPercentEncoding, - "http://🆒🔗.ws") + @Test + func parsingEmojiURL() { + #expect(urlContainer.emojiLink.absoluteString.removingPercentEncoding == "http://🆒🔗.ws") } - func testParsingAccentURL() { - XCTAssertEqual(urlContainer.accentLink.absoluteString.removingPercentEncoding, - "http://gégé.com") + @Test + func parsingAccentURL() { + #expect(urlContainer.accentLink.absoluteString.removingPercentEncoding == "http://gégé.com") } - func testParsingOptionalURL() { - XCTAssertEqual(urlContainer.optionalLink?.absoluteString.removingPercentEncoding, - "https://apple.com/steve") + @Test + func parsingOptionalURL() { + #expect(urlContainer.optionalLink?.absoluteString.removingPercentEncoding == "https://apple.com/steve") } } diff --git a/Tests/ArrowTests/XCTestManifests.swift b/Tests/ArrowTests/XCTestManifests.swift deleted file mode 100644 index 91274ea..0000000 --- a/Tests/ArrowTests/XCTestManifests.swift +++ /dev/null @@ -1,18 +0,0 @@ -import XCTest - -#if !canImport(ObjectiveC) -public func allTests() -> [XCTestCaseEntry] { - return [ - testCase(ArrayTests.allTests), - testCase(CustomModelTests.allTests), - testCase(CustomRawRepresentableTests.allTests), - testCase(DateTests.allTests), - testCase(EnumTests.allTests), - testCase(NativeTypesTests.allTests), - testCase(StringCoercionTests.allTests), - testCase(StringTests.allTests), - testCase(TypeConversionTests.allTests), - testCase(URLTests.allTests), - ] -} -#endif diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index db7a8d5..0000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,16 +0,0 @@ -import XCTest - -import MyPackageTests - -var tests = [XCTestCaseEntry]() -tests += ArrayTests.allTests() -tests += CustomModelTests.allTests() -tests += CustomRawRepresentableTests.allTests() -tests += DateTests.allTests() -tests += EnumTests.allTests() -tests += NativeTypesTests.allTests() -tests += StringCoercionTests.allTests() -tests += StringTests.allTests() -tests += TypeConversionTests.allTests() -tests += URLTests.allTests() -XCTMain(tests) From 4228ceb54bc59613996a4400838ba8703080584d Mon Sep 17 00:00:00 2001 From: Sacha DSO Date: Tue, 24 Sep 2024 12:51:37 -1000 Subject: [PATCH 3/3] Replace serialQueue with actor to avoid concurrent access --- Sources/Arrow/Arrow.swift | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/Sources/Arrow/Arrow.swift b/Sources/Arrow/Arrow.swift index 9fe0593..b20315a 100644 --- a/Sources/Arrow/Arrow.swift +++ b/Sources/Arrow/Arrow.swift @@ -58,24 +58,19 @@ For more fine grained control, use `dateFormat` on a per field basis : createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") */ -public class Arrow { +public actor Arrow { - internal static let arrowSerialQueue = DispatchQueue(label: "com.arrow.arrow") - nonisolated(unsafe) internal static var dateFormatter: DateFormatter? = DateFormatter() - nonisolated(unsafe) internal static var useReferenceDate = false + internal static var dateFormatter: DateFormatter? = DateFormatter() + internal static var useReferenceDate = false /// Sets the defaut dateFormat for parsing NSDates. - public class func setDateFormat(_ format: String) { - arrowSerialQueue.sync { - dateFormatter?.dateFormat = format - } + public static func setDateFormat(_ format: String) { + dateFormatter?.dateFormat = format } /// Sets the defaut dateFormatter for parsing NSDates. - public class func setDateFormatter(_ formatter: DateFormatter?) { - arrowSerialQueue.sync { - dateFormatter = formatter - } + public static func setDateFormatter(_ formatter: DateFormatter?) { + dateFormatter = formatter } /** @@ -86,10 +81,8 @@ public class Arrow { For more information see `NSDate(timeIntervalSinceReferenceDate` documentation */ - public class func setUseTimeIntervalSinceReferenceDate(_ ref: Bool) { - arrowSerialQueue.sync { - useReferenceDate = ref - } + public static func setUseTimeIntervalSinceReferenceDate(_ ref: Bool) { + useReferenceDate = ref } } @@ -211,7 +204,7 @@ public func <-- (left: inout Date?, right: JSON?) { df.dateFormat = customFormat left = df.date(from: s) } else if let s = right?.data as? String { - if let date = Arrow.arrowSerialQueue.sync(execute: { Arrow.dateFormatter?.date(from: s) }) { + if let date = Arrow.dateFormatter?.date(from: s) { left = date } else if let t = TimeInterval(s) { left = timeIntervalToDate(t) @@ -301,7 +294,7 @@ func parseString(_ left: inout T?, string: String) { } func timeIntervalToDate(_ timeInterval: TimeInterval) -> Date { - return Arrow.arrowSerialQueue.sync(execute: { Arrow.useReferenceDate }) + return Arrow.useReferenceDate ? Date(timeIntervalSinceReferenceDate: timeInterval) : Date(timeIntervalSince1970: timeInterval) }