From 647f0143e60b780c7c883012ad5ec7c42d401506 Mon Sep 17 00:00:00 2001 From: Charles Hu Date: Fri, 12 Apr 2024 10:05:04 -0700 Subject: [PATCH] Migrate FoundationEssentialsTests to Swift Testing resolves rdar://125855675 --- .../AttributedString/AttributedString.swift | 430 ++-- Benchmarks/Package.swift | 11 + Package.swift | 11 +- ...Utilities.swift => Utilities+XCTest.swift} | 48 +- .../AttributedStringCOWTests.swift | 36 +- ...butedStringConstrainingBehaviorTests.swift | 235 ++- .../AttributedStringTestSupport.swift | 6 +- .../AttributedStringTests.swift | 1353 +++++++------ .../BufferViewTests.swift | 188 +- .../BuiltInUnicodeScalarSetTest.swift | 16 +- .../DataIOTests.swift | 188 +- .../FoundationEssentialsTests/DataTests.swift | 906 +++++---- .../DateIntervalTests.swift | 111 +- .../FoundationEssentialsTests/DateTests.swift | 114 +- .../ErrorTests.swift | 14 +- .../FileManager/FileManagerPlayground.swift | 22 +- .../FileManager/FileManagerTests.swift | 491 +++-- .../BinaryInteger+FormatStyleTests.swift | 58 +- .../ISO8601FormatStyleFormattingTests.swift | 111 +- .../ISO8601FormatStyleParsingTests.swift | 194 +- .../GregorianCalendarTests.swift | 884 +++++---- .../IndexPathTests.swift | 818 ++++---- .../JSONEncoderTests.swift | 1480 +++++++------- .../LockedStateTests.swift | 38 +- .../PredicateCodableTests.swift | 276 ++- .../PredicateConversionTests.swift | 258 +-- .../PredicateTests.swift | 342 ++-- .../ProcessInfoTests.swift | 76 +- .../PropertyListEncoderTests.swift | 508 ++--- .../ResourceUtilities.swift | 37 +- .../SortComparatorTests.swift | 46 +- .../StringTests.swift | 1741 +++++++++-------- .../FoundationEssentialsTests/UUIDTests.swift | 91 +- .../Utilities+Testing.swift | 246 +++ .../CalendarTests.swift | 4 +- .../DateTests+Locale.swift | 2 +- 36 files changed, 6402 insertions(+), 4988 deletions(-) rename Tests/FoundationEssentialsTests/AttributedString/AttributedStringPerformanceTests.swift => Benchmarks/Benchmarks/AttributedString/AttributedString.swift (61%) rename Sources/TestSupport/{Utilities.swift => Utilities+XCTest.swift} (91%) create mode 100644 Tests/FoundationEssentialsTests/Utilities+Testing.swift diff --git a/Tests/FoundationEssentialsTests/AttributedString/AttributedStringPerformanceTests.swift b/Benchmarks/Benchmarks/AttributedString/AttributedString.swift similarity index 61% rename from Tests/FoundationEssentialsTests/AttributedString/AttributedStringPerformanceTests.swift rename to Benchmarks/Benchmarks/AttributedString/AttributedString.swift index efc8dbe6f..c42c26fc9 100644 --- a/Tests/FoundationEssentialsTests/AttributedString/AttributedStringPerformanceTests.swift +++ b/Benchmarks/Benchmarks/AttributedString/AttributedString.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2022-2023 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -11,110 +11,122 @@ //===----------------------------------------------------------------------===// #if FOUNDATION_FRAMEWORK -// TODO: Support AttributedString performance tests in FoundationPreview -// (see https://github.com/apple/swift-foundation/issues/254) - +import Benchmark import Foundation -import XCTest -/// Performance tests for `AttributedString` and its associated objects -final class TestAttributedStringPerformance: XCTestCase { - - /// Set to true to record a baseline for equivalent operations on `NSAttributedString` - static let runWithNSAttributedString = false - - func createLongString() -> AttributedString { +enum AttributedStringBenchmark { + fileprivate static func createLongString() -> AttributedString { var str = AttributedString(String(repeating: "a", count: 10000), attributes: AttributeContainer().testInt(1)) str += AttributedString(String(repeating: "b", count: 10000), attributes: AttributeContainer().testInt(2)) str += AttributedString(String(repeating: "c", count: 10000), attributes: AttributeContainer().testInt(3)) return str } - - func createManyAttributesString() -> AttributedString { + + fileprivate func createManyAttributesString() -> AttributedString { var str = AttributedString("a") for i in 0..<10000 { str += AttributedString("a", attributes: AttributeContainer().testInt(i)) } return str } - - func createLongNSString() -> NSMutableAttributedString { + + fileprivate func createLongNSString() -> NSMutableAttributedString { let str = NSMutableAttributedString(string: String(repeating: "a", count: 10000), attributes: [.testInt: NSNumber(1)]) str.append(NSMutableAttributedString(string: String(repeating: "b", count: 10000), attributes: [.testInt: NSNumber(2)])) str.append(NSMutableAttributedString(string: String(repeating: "c", count: 10000), attributes: [.testInt: NSNumber(3)])) return str } - - func createManyAttributesNSString() -> NSMutableAttributedString { + + fileprivate func createManyAttributesNSString() -> NSMutableAttributedString { let str = NSMutableAttributedString(string: "a") for i in 0..<10000 { str.append(NSAttributedString(string: "a", attributes: [.testInt: NSNumber(value: i)])) } return str } - +} + +let benchmarks = { + Benchmark.defaultConfiguration.maxIterations = 1_000 + Benchmark.defaultConfiguration.maxDuration = .seconds(3) + Benchmark.defaultConfiguration.scalingFactor = .kilo + Benchmark.defaultConfiguration.metrics = [.cpuTotal, .wallClock, .mallocCountTotal, .throughput] + + /// Set to true to record a baseline for equivalent operations on `NSAttributedString` + let runWithNSAttributedString = false + // MARK: - String Manipulation - - func testInsertIntoLongString() { + Benchmark( + "testInsertIntoLongString", + configuration: .init(scalingFactor: .mega) + ) { benchmark in var str = createLongString() let idx = str.characters.index(str.startIndex, offsetBy: str.characters.count / 2) let toInsert = AttributedString(String(repeating: "c", count: str.characters.count)) - + let strNS = createLongNSString() let idxNS = str.characters.count / 2 let toInsertNS = NSAttributedString(string: String(repeating: "c", count: str.characters.count)) - - self.measure(metrics: [XCTClockMetric()]) { - if TestAttributedStringPerformance.runWithNSAttributedString { + + for _ in benchmark.scaledIterations { + if runWithNSAttributedString { strNS.insert(toInsertNS, at: idxNS) } else { str.insert(toInsert, at: idx) } } } - - func testReplaceSubrangeOfLongString() { + + Benchmark( + "testReplaceSubrangeOfLongString", + configuration: .init(scalingFactor: .mega) + ) { benchmark in var str = createLongString() let start = str.characters.index(str.startIndex, offsetBy: str.characters.count / 2) let range = start ... str.characters.index(start, offsetBy: 10) let toInsert = AttributedString(String(repeating: "d", count: str.characters.count / 2), attributes: AttributeContainer().testDouble(2.5)) - + let strNS = createLongNSString() let startNS = strNS.string.count / 2 let rangeNS = NSRange(location: startNS, length: 10) let toInsertNS = NSAttributedString(string: String(repeating: "d", count: strNS.string.count / 2), attributes: [.testDouble: NSNumber(value: 2.5)]) - - - self.measure(metrics: [XCTClockMetric()]) { - if TestAttributedStringPerformance.runWithNSAttributedString { + + + for _ in benchmark.scaledIterations { + if runWithNSAttributedString { strNS.replaceCharacters(in: rangeNS, with: toInsertNS) } else { str.replaceSubrange(range, with: toInsert) } } } - + // MARK: - Attribute Manipulation - - func testSetAttribute() { + Benchmark( + "testSetAttribute", + configuration: .init(scalingFactor: .mega) + ) { benchmark in var str = createManyAttributesString() let strNS = createManyAttributesNSString() - - self.measure(metrics: [XCTClockMetric()]) { - if TestAttributedStringPerformance.runWithNSAttributedString { + + for _ in benchmark.scaledIterations { + if runWithNSAttributedString { strNS.addAttributes([.testDouble: NSNumber(value: 1.5)], range: NSRange(location: 0, length: strNS.string.count)) } else { str.testDouble = 1.5 } } } - - func testGetAttribute() { + + Benchmark( + "testSetAttribute", + configuration: .init(scalingFactor: .mega) + ) { benchmark in let str = createManyAttributesString() let strNS = createManyAttributesNSString() - - self.measure(metrics: [XCTClockMetric()]) { - if TestAttributedStringPerformance.runWithNSAttributedString { + + for _ in benchmark.scaledIterations { + if runWithNSAttributedString { strNS.enumerateAttribute(.testDouble, in: NSRange(location: 0, length: strNS.string.count), options: []) { (attr, range, pointer) in let _ = attr } @@ -123,15 +135,18 @@ final class TestAttributedStringPerformance: XCTestCase { } } } - - func testSetAttributeSubrange() { + + Benchmark( + "testSetAttribute", + configuration: .init(scalingFactor: .mega) + ) { benchmark in var str = createManyAttributesString() let range = str.characters.index(str.startIndex, offsetBy: str.characters.count / 2)... - + let strNS = createManyAttributesNSString() let rangeNS = NSRange(location: 0, length: str.characters.count / 2) - - self.measure(metrics: [XCTClockMetric()]) { + + for _ in benchmark.scaledIterations { if TestAttributedStringPerformance.runWithNSAttributedString { strNS.addAttributes([.testDouble: NSNumber(value: 1.5)], range: rangeNS) } else { @@ -139,15 +154,18 @@ final class TestAttributedStringPerformance: XCTestCase { } } } - - func testGetAttributeSubrange() { + + Benchmark( + "testGetAttributeSubrange", + configuration: .init(scalingFactor: .mega) + ) { benchmark in let str = createManyAttributesString() let range = str.characters.index(str.startIndex, offsetBy: str.characters.count / 2)... - + let strNS = createManyAttributesNSString() let rangeNS = NSRange(location: 0, length: str.characters.count / 2) - - self.measure(metrics: [XCTClockMetric()]) { + + for _ in benchmark.scaledIterations { if TestAttributedStringPerformance.runWithNSAttributedString { strNS.enumerateAttribute(.testDouble, in: rangeNS, options: []) { (attr, range, pointer) in let _ = attr @@ -157,13 +175,16 @@ final class TestAttributedStringPerformance: XCTestCase { } } } - - func testModifyAttributes() { + + Benchmark( + "testModifyAttributes", + configuration: .init(scalingFactor: .mega) + ) { benchmark in let str = createManyAttributesString() let strNS = createManyAttributesNSString() - - self.measure(metrics: [XCTClockMetric()]) { - if TestAttributedStringPerformance.runWithNSAttributedString { + + for _ in benchmark.scaledIterations { + if self.runWithNSAttributedString { strNS.enumerateAttribute(.testInt, in: NSRange(location: 0, length: strNS.string.count), options: []) { (val, range, pointer) in if let value = val as? NSNumber { strNS.addAttributes([.testInt: NSNumber(value: value.intValue + 2)], range: range) @@ -178,15 +199,18 @@ final class TestAttributedStringPerformance: XCTestCase { } } } - - func testReplaceAttributes() { + + Benchmark( + "testReplaceAttributes", + configuration: .init(scalingFactor: .mega) + ) { benchmark in var str = createManyAttributesString() let old = AttributeContainer().testInt(100) let new = AttributeContainer().testDouble(100.5) - + let strNS = createManyAttributesNSString() - - self.measure(metrics: [XCTClockMetric()]) { + + for _ in benchmark.scaledIterations { if TestAttributedStringPerformance.runWithNSAttributedString { strNS.enumerateAttribute(.testInt, in: NSRange(location: 0, length: strNS.string.count), options: []) { (val, range, pointer) in if let value = val as? NSNumber, value == 100 { @@ -199,15 +223,18 @@ final class TestAttributedStringPerformance: XCTestCase { } } } - - func testMergeMultipleAttributes() throws { + + Benchmark( + "testMergeMultipleAttributes", + configuration: .init(scalingFactor: .mega) + ) { benchmark in var str = createManyAttributesString() let new = AttributeContainer().testDouble(1.5).testString("test") - + let strNS = createManyAttributesNSString() let newNS: [NSAttributedString.Key: Any] = [.testDouble: NSNumber(value: 1.5), .testString: "test"] - - self.measure(metrics: [XCTClockMetric()]) { + + for _ in benchmark.scaledIterations { if TestAttributedStringPerformance.runWithNSAttributedString { strNS.addAttributes(newNS, range: NSRange(location: 0, length: strNS.string.count)) } else { @@ -215,16 +242,19 @@ final class TestAttributedStringPerformance: XCTestCase { } } } - - func testSetMultipleAttributes() throws { + + Benchmark( + "testSetMultipleAttributes", + configuration: .init(scalingFactor: .mega) + ) { benchmark in var str = createManyAttributesString() let new = AttributeContainer().testDouble(1.5).testString("test") - + let strNS = createManyAttributesNSString() let rangeNS = NSRange(location: 0, length: str.characters.count / 2) let newNS: [NSAttributedString.Key: Any] = [.testDouble: NSNumber(value: 1.5), .testString: "test"] - - self.measure(metrics: [XCTClockMetric()]) { + + for _ in benchmark.scaledIterations { if TestAttributedStringPerformance.runWithNSAttributedString { strNS.setAttributes(newNS, range: rangeNS) } else { @@ -232,121 +262,194 @@ final class TestAttributedStringPerformance: XCTestCase { } } } - + // MARK: - Attribute Enumeration - - func testEnumerateAttributes() { + + Benchmark( + "testEnumerateAttributes", + configuration: .init(scalingFactor: .mega) + ) { benchmark in let str = createManyAttributesString() let strNS = createManyAttributesNSString() - - self.measure(metrics: [XCTClockMetric()]) { - if TestAttributedStringPerformance.runWithNSAttributedString { + + for _ in benchmark.scaledIterations { + if self.runWithNSAttributedString { strNS.enumerateAttributes(in: NSRange(location: 0, length: strNS.string.count), options: []) { (attrs, range, pointer) in - + } } else { for _ in str.runs { - + } } } } - - func testEnumerateAttributesSlice() { + + Benchmark( + "testEnumerateAttributesSlice", + configuration: .init(scalingFactor: .mega) + ) { benchmark in let str = createManyAttributesString() let strNS = createManyAttributesNSString() - - self.measure(metrics: [XCTClockMetric()]) { + + for _ in benchmark.scaledIterations { if TestAttributedStringPerformance.runWithNSAttributedString { strNS.enumerateAttribute(.testInt, in: NSRange(location: 0, length: strNS.string.count), options: []) { (val, range, pointer) in - + } } else { for (_, _) in str.runs[\.testInt] { - + } } } } - + // MARK: - NSAS Conversion - - func testConvertToNSAS() throws { - guard !TestAttributedStringPerformance.runWithNSAttributedString else { - throw XCTSkip("Test disabled for NSAS") + if !runWithNSAttributedString { + Benchmark( + "testConvertToNSAS", + configuration: .init(scalingFactor: .mega) + ) { benchmark in + let str = createManyAttributesString() + + for _ in benchmark.scaledIterations { + let _ = try! NSAttributedString(str, including: AttributeScopes.TestAttributes.self) + } } - - let str = createManyAttributesString() - - self.measure(metrics: [XCTClockMetric()]) { - let _ = try! NSAttributedString(str, including: AttributeScopes.TestAttributes.self) + + Benchmark( + "testConvertToNSAS", + configuration: .init(scalingFactor: .mega) + ) { benchmark in + let str = createManyAttributesString() + let ns = try NSAttributedString(str, including: AttributeScopes.TestAttributes.self) + + for _ in benchmark.scaledIterations { + let _ = try! AttributedString(ns, including: AttributeScopes.TestAttributes.self) + } } - } - - func testConvertFromNSAS() throws { - guard !TestAttributedStringPerformance.runWithNSAttributedString else { - throw XCTSkip("Test disabled for NSAS") + + Benchmark( + "testEquality", + configuration: .init(scalingFactor: .mega) + ) { benchmark in + let str = createManyAttributesString() + let str2 = createManyAttributesString() + + for _ in benchmark.scaledIteration { + _ = str == str2 + } } - - let str = createManyAttributesString() - let ns = try NSAttributedString(str, including: AttributeScopes.TestAttributes.self) - - self.measure(metrics: [XCTClockMetric()]) { - let _ = try! AttributedString(ns, including: AttributeScopes.TestAttributes.self) + + Benchmark( + "testSubstringEquality", + configuration: .init(scalingFactor: .mega) + ) { benchmark in + let str = createManyAttributesString() + let str2 = createManyAttributesString() + let range = str.characters.index(str.startIndex, offsetBy: str.characters.count / 2)... + let substring = str[range] + let substring2 = str2[range] + + for _ in benchmark.scaledIteration { + _ = substring == substring2 + } + } + + Benchmark( + "testHashAttributedString", + configuration: .init(scalingFactor: .mega) + ) { benchmark in + let str = createManyAttributesString() + for _ in benchmark.scaledIteration { + var hasher = Hasher() + str.hash(into: &hasher) + _ = hasher.finalize() + } + } + + Benchmark( + "testHashAttributeContainer", + configuration: .init(scalingFactor: .mega) + ) { benchmark in + struct TestAttribute : AttributedStringKey { + static var name = "0" + typealias Value = Int + } + + var container = AttributeContainer() + for i in 0 ..< 100000 { + TestAttribute.name = "\(i)" + container[TestAttribute.self] = i + } + for _ in benchmark.scaledIteration { + var hasher = Hasher() + container.hash(into: &hasher) + _ = hasher.finalize() + } } } - + // MARK: - Encoding and Decoding - - func testEncode() throws { + + Benchmark( + "testEnumerateAttributesSlice", + configuration: .init(scalingFactor: .mega) + ) { benchmark in struct CodableType: Codable { @CodableConfiguration(from: AttributeScopes.TestAttributes.self) var str = AttributedString() } - + let str = createManyAttributesString() let codableType = CodableType(str: str) let encoder = JSONEncoder() - + let ns = createManyAttributesNSString() - - self.measure(metrics: [XCTClockMetric()]) { - if TestAttributedStringPerformance.runWithNSAttributedString { + + for _ in benchmark.scaledIterations { + if runWithNSAttributedString { let _ = try! NSKeyedArchiver.archivedData(withRootObject: ns, requiringSecureCoding: false) } else { let _ = try! encoder.encode(codableType) } } } - - func testDecode() throws { + + Benchmark( + "testDecode", + configuration: .init(scalingFactor: .mega) + ) { benchmark in struct CodableType: Codable { @CodableConfiguration(from: AttributeScopes.TestAttributes.self) var str = AttributedString() } - + let str = createManyAttributesString() let codableType = CodableType(str: str) let encoder = JSONEncoder() let data = try encoder.encode(codableType) let decoder = JSONDecoder() - + let ns = createManyAttributesNSString() let dataNS = try NSKeyedArchiver.archivedData(withRootObject: ns, requiringSecureCoding: false) - - self.measure(metrics: [XCTClockMetric()]) { - if TestAttributedStringPerformance.runWithNSAttributedString { + + for _ in benchmark.scaledIterations { + if runWithNSAttributedString { let _ = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(dataNS) } else { let _ = try! decoder.decode(CodableType.self, from: data) } } } - + // MARK: - Other - - func testCreateLongString() { - self.measure(metrics: [XCTClockMetric()]) { + Benchmark( + "testCreateLongString", + configuration: .init(scalingFactor: .mega) + ) { benchmark in + for _ in benchmark.scaledIteration { if TestAttributedStringPerformance.runWithNSAttributedString { let _ = createLongNSString() } else { @@ -354,9 +457,12 @@ final class TestAttributedStringPerformance: XCTestCase { } } } - - func testCreateManyAttributesString() { - self.measure(metrics: [XCTClockMetric()]) { + + Benchmark( + "testCreateManyAttributesString", + configuration: .init(scalingFactor: .mega) + ) { benchmark in + for _ in benchmark.scaledIteration { if TestAttributedStringPerformance.runWithNSAttributedString { let _ = createManyAttributesNSString() } else { @@ -364,70 +470,6 @@ final class TestAttributedStringPerformance: XCTestCase { } } } - - func testEquality() throws { - guard !TestAttributedStringPerformance.runWithNSAttributedString else { - throw XCTSkip("Test disabled for NSAS") - } - - let str = createManyAttributesString() - let str2 = createManyAttributesString() - - self.measure(metrics: [XCTClockMetric()]) { - _ = str == str2 - } - } - - func testSubstringEquality() throws { - guard !TestAttributedStringPerformance.runWithNSAttributedString else { - throw XCTSkip("Test disabled for NSAS") - } - - let str = createManyAttributesString() - let str2 = createManyAttributesString() - let range = str.characters.index(str.startIndex, offsetBy: str.characters.count / 2)... - let substring = str[range] - let substring2 = str2[range] - - self.measure(metrics: [XCTClockMetric()]) { - _ = substring == substring2 - } - } - - func testHashAttributedString() throws { - guard !TestAttributedStringPerformance.runWithNSAttributedString else { - throw XCTSkip("Test disabled for NSAS") - } - - let str = createManyAttributesString() - self.measure(metrics: [XCTClockMetric()]) { - var hasher = Hasher() - str.hash(into: &hasher) - _ = hasher.finalize() - } - } - - func testHashAttributeContainer() throws { - guard !TestAttributedStringPerformance.runWithNSAttributedString else { - throw XCTSkip("Test disabled for NSAS") - } - - struct TestAttribute : AttributedStringKey { - static var name = "0" - typealias Value = Int - } - - var container = AttributeContainer() - for i in 0 ..< 100000 { - TestAttribute.name = "\(i)" - container[TestAttribute.self] = i - } - self.measure(metrics: [XCTClockMetric()]) { - var hasher = Hasher() - container.hash(into: &hasher) - _ = hasher.finalize() - } - } } #endif // FOUNDATION_FRAMEWORK diff --git a/Benchmarks/Package.swift b/Benchmarks/Package.swift index 6463366ec..e8a6283cb 100644 --- a/Benchmarks/Package.swift +++ b/Benchmarks/Package.swift @@ -78,5 +78,16 @@ let package = Package( .plugin(name: "BenchmarkPlugin", package: "package-benchmark") ] ), + .executableTarget( + name: "AttributedStringBenchmarks", + dependencies: [ + .product(name: "FoundationEssentials", package: "swift-foundation-local"), + .product(name: "Benchmark", package: "package-benchmark"), + ], + path: "Benchmarks/AttributedString", + plugins: [ + .plugin(name: "BenchmarkPlugin", package: "package-benchmark") + ] + ), ] ) diff --git a/Package.swift b/Package.swift index b8a153efc..c89772929 100644 --- a/Package.swift +++ b/Package.swift @@ -54,7 +54,10 @@ let package = Package( exact: "0.0.6"), .package( url: "https://github.com/apple/swift-syntax.git", - from: "510.0.0") + from: "510.0.0"), + .package( + url: "https://github.com/apple/swift-testing.git", + branch: "main"), ], targets: [ // Foundation (umbrella) @@ -97,8 +100,8 @@ let package = Package( .testTarget( name: "FoundationEssentialsTests", dependencies: [ - "TestSupport", - "FoundationEssentials" + "FoundationEssentials", + .product(name: "Testing", package: "swift-testing"), ], resources: [ .copy("Resources") @@ -141,7 +144,7 @@ let package = Package( "TestSupport" ], swiftSettings: availabilityMacros - ) + ), ] ) diff --git a/Sources/TestSupport/Utilities.swift b/Sources/TestSupport/Utilities+XCTest.swift similarity index 91% rename from Sources/TestSupport/Utilities.swift rename to Sources/TestSupport/Utilities+XCTest.swift index aa161fe0e..da66a9e05 100644 --- a/Sources/TestSupport/Utilities.swift +++ b/Sources/TestSupport/Utilities+XCTest.swift @@ -43,14 +43,6 @@ func expectDoesNotThrow(_ test: () throws -> Void, _ message: @autoclosure () -> XCTAssertNoThrow(try test(), message(), file: file, line: line) } -func expectTrue(_ actual: Bool, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - XCTAssertTrue(actual, message(), file: file, line: line) -} - -func expectFalse(_ actual: Bool, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - XCTAssertFalse(actual, message(), file: file, line: line) -} - public func expectEqual(_ expected: T, _ actual: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(expected, actual, message(), file: file, line: line) } @@ -80,28 +72,6 @@ public func expectEqual( XCTAssertTrue(expected == actual, message(), file: file, line: line) } -public func expectEqualSequence< Expected: Sequence, Actual: Sequence>( - _ expected: Expected, _ actual: Actual, - _ message: @autoclosure () -> String = "", - file: String = #file, line: UInt = #line, - sameValue: (Expected.Element, Expected.Element) -> Bool -) where Expected.Element == Actual.Element { - if !expected.elementsEqual(actual, by: sameValue) { - XCTFail("expected elements: \"\(expected)\"\n" - + "actual: \"\(actual)\" (of type \(String(reflecting: type(of: actual)))), \(message())") - } -} - -public func expectEqualSequence< Expected: Sequence, Actual: Sequence>( - _ expected: Expected, _ actual: Actual, - _ message: @autoclosure () -> String = "", - file: String = #file, line: UInt = #line -) where Expected.Element == Actual.Element, Expected.Element: Equatable { - expectEqualSequence(expected, actual, message()) { - $0 == $1 - } -} - func expectEqual(_ actual: Date, _ expected: Date , within: Double = 0.001, file: StaticString = #file, line: UInt = #line) { let debugDescription = "\nactual: \(actual.formatted(.iso8601));\nexpected: \(expected.formatted(.iso8601))" XCTAssertEqual(actual.timeIntervalSinceReferenceDate, expected.timeIntervalSinceReferenceDate, accuracy: within, debugDescription, file: file, line: line) @@ -170,7 +140,7 @@ func expectNoChanges(_ check: @autoclosure () -> T, by differe /// /// - Note: `oracle` is also checked for conformance to the /// laws. -public func checkEquatable( +public func checkEquatableXCTest( _ instances: Instances, oracle: (Instances.Index, Instances.Index) -> Bool, allowBrokenTransitivity: Bool = false, @@ -196,7 +166,7 @@ private class Box { } } -internal func _checkEquatableImpl( +fileprivate func _checkEquatableImpl( _ instances: [Instance], oracle: (Int, Int) -> Bool, allowBrokenTransitivity: Bool = false, @@ -211,7 +181,7 @@ internal func _checkEquatableImpl( for i in instances.indices { let x = instances[i] - expectTrue(oracle(i, i), "bad oracle: broken reflexivity at index \(i)") + XCTAssertTrue(oracle(i, i), "bad oracle: broken reflexivity at index \(i)") for j in instances.indices { let y = instances[j] @@ -255,7 +225,7 @@ internal func _checkEquatableImpl( transitivityScoreboard[i].value.insert(i) } for k in transitivityScoreboard[i].value { - expectTrue( + XCTAssertTrue( oracle(j, k), "bad oracle: broken transitivity at indices \(i), \(j), \(k)", file: file, @@ -280,14 +250,14 @@ func hash(_ value: H, salt: Int? = nil) -> Int { return hasher.finalize() } -public func checkHashable( +public func checkHashableXCTest( _ instances: Instances, equalityOracle: (Instances.Index, Instances.Index) -> Bool, allowIncompleteHashing: Bool = false, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line ) where Instances.Element: Hashable { - checkHashable( + checkHashableXCTest( instances, equalityOracle: equalityOracle, hashEqualityOracle: equalityOracle, @@ -298,7 +268,7 @@ public func checkHashable( } -public func checkHashable( +public func checkHashableXCTest( _ instances: Instances, equalityOracle: (Instances.Index, Instances.Index) -> Bool, hashEqualityOracle: (Instances.Index, Instances.Index) -> Bool, @@ -307,7 +277,7 @@ public func checkHashable( file: StaticString = #file, line: UInt = #line ) where Instances.Element: Hashable { - checkEquatable( + checkEquatableXCTest( instances, oracle: equalityOracle, message(), @@ -405,7 +375,7 @@ public func checkHashableGroups( func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool { return groupIndices[lhs] == groupIndices[rhs] } - checkHashable( + checkHashableXCTest( instances, equalityOracle: equalityOracle, hashEqualityOracle: equalityOracle, diff --git a/Tests/FoundationEssentialsTests/AttributedString/AttributedStringCOWTests.swift b/Tests/FoundationEssentialsTests/AttributedString/AttributedStringCOWTests.swift index 32c2e65ff..f4e463fa4 100644 --- a/Tests/FoundationEssentialsTests/AttributedString/AttributedStringCOWTests.swift +++ b/Tests/FoundationEssentialsTests/AttributedString/AttributedStringCOWTests.swift @@ -10,9 +10,7 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing #if FOUNDATION_FRAMEWORK @testable import Foundation @@ -27,8 +25,8 @@ extension AttributedStringProtocol { } /// Tests for `AttributedString` to confirm expected CoW behavior -final class TestAttributedStringCOW: XCTestCase { - +final class AttributedStringCOWTests { + // MARK: - Utility Functions func createAttributedString() -> AttributedString { @@ -42,7 +40,14 @@ final class TestAttributedStringCOW: XCTestCase { let str = createAttributedString() var copy = str operation(©) - XCTAssertNotEqual(str, copy, "Mutation operation did not copy when multiple references exist", file: file, line: line) + #expect( + str != copy, + "Mutation operation did not copy when multiple references exist", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } func assertCOWNoCopy(file: StaticString = #file, line: UInt = #line, _ operation: (inout AttributedString) -> Void) { @@ -50,7 +55,14 @@ final class TestAttributedStringCOW: XCTestCase { let gutsPtr = Unmanaged.passUnretained(str._guts) operation(&str) let newGutsPtr = Unmanaged.passUnretained(str._guts) - XCTAssertEqual(gutsPtr.toOpaque(), newGutsPtr.toOpaque(), "Mutation operation copied when only one reference exists", file: file, line: line) + #expect( + gutsPtr.toOpaque() == newGutsPtr.toOpaque(), + "Mutation operation copied when only one reference exists", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } func assertCOWBehavior(file: StaticString = #file, line: UInt = #line, _ operation: (inout AttributedString) -> Void) { @@ -76,7 +88,7 @@ final class TestAttributedStringCOW: XCTestCase { // MARK: - Tests - func testTopLevelType() { + @Test func testTopLevelType() { assertCOWBehavior { (str) in str.setAttributes(container) } @@ -109,7 +121,7 @@ final class TestAttributedStringCOW: XCTestCase { } } - func testSubstring() { + @Test func testSubstring() { assertCOWBehavior { (str) in str[makeSubrange(str)].setAttributes(container) } @@ -130,7 +142,7 @@ final class TestAttributedStringCOW: XCTestCase { } } - func testCharacters() { + @Test func testCharacters() { let char: Character = "a" assertCOWBehavior { (str) in @@ -153,7 +165,7 @@ final class TestAttributedStringCOW: XCTestCase { } } - func testUnicodeScalars() { + @Test func testUnicodeScalars() { let scalar: UnicodeScalar = "a" assertCOWBehavior { (str) in @@ -161,7 +173,7 @@ final class TestAttributedStringCOW: XCTestCase { } } - func testGenericProtocol() { + @Test func testGenericProtocol() { assertCOWBehavior { $0.genericSetAttribute() } diff --git a/Tests/FoundationEssentialsTests/AttributedString/AttributedStringConstrainingBehaviorTests.swift b/Tests/FoundationEssentialsTests/AttributedString/AttributedStringConstrainingBehaviorTests.swift index 79dba6463..d94ad2136 100644 --- a/Tests/FoundationEssentialsTests/AttributedString/AttributedStringConstrainingBehaviorTests.swift +++ b/Tests/FoundationEssentialsTests/AttributedString/AttributedStringConstrainingBehaviorTests.swift @@ -10,12 +10,16 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport +import Testing + +#if FOUNDATION_FRAMEWORK +@testable import Foundation +#else +@testable import FoundationEssentials #endif -class TestAttributedStringConstrainingBehavior: XCTestCase { - +struct AttributedStringConstrainingBehaviorTests { + func verify( string: AttributedString, matches expected: [(String, K.Value?)], @@ -23,54 +27,201 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { file: StaticString = #file, line: UInt = #line ) { let runs = string.runs[key] - XCTAssertEqual(runs.count, expected.count, "Unexpected number of runs", file: file, line: line) + #expect( + runs.count == expected.count, + "Unexpected number of runs", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) for ((val, range), expectation) in zip(runs, expected) { let slice = String.UnicodeScalarView(string.unicodeScalars[range]) - XCTAssertTrue(slice.elementsEqual(expectation.0.unicodeScalars), "Unexpected range of run: \(slice.debugDescription) vs \(expectation.0.debugDescription)", file: file, line: line) - XCTAssertEqual(val, expectation.1, "Unexpected value of attribute \(K.self) for range \(expectation.0)", file: file, line: line) + #expect( + slice.elementsEqual(expectation.0.unicodeScalars), + "Unexpected range of run: \(slice.debugDescription) vs \(expectation.0.debugDescription)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val == expectation.1, + "Unexpected value of attribute \(K.self) for range \(expectation.0)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } for ((val, range), expectation) in zip(runs.reversed(), expected.reversed()) { let slice = String.UnicodeScalarView(string.unicodeScalars[range]) - XCTAssertTrue(slice.elementsEqual(expectation.0.unicodeScalars), "Unexpected range of run while reverse iterating: \(slice.debugDescription) vs \(expectation.0.debugDescription)", file: file, line: line) - XCTAssertEqual(val, expectation.1, "Unexpected value of attribute \(K.self) for range \(expectation.0) while reverse iterating", file: file, line: line) + #expect( + slice.elementsEqual(expectation.0.unicodeScalars), + "Unexpected range of run while reverse iterating: \(slice.debugDescription) vs \(expectation.0.debugDescription)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val == expectation.1, + "Unexpected value of attribute \(K.self) for range \(expectation.0) while reverse iterating", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } } func verify(string: AttributedString, matches expected: [(String, K.Value?, K2.Value?)], for key: KeyPath, _ key2: KeyPath, file: StaticString = #file, line: UInt = #line) { let runs = string.runs[key, key2] - XCTAssertEqual(runs.count, expected.count, "Unexpected number of runs", file: file, line: line) + #expect( + runs.count == expected.count, + "Unexpected number of runs", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) for ((val1, val2, range), expectation) in zip(runs, expected) { - XCTAssertEqual(String(string.characters[range]),expectation.0, "Unexpected range of run", file: file, line: line) - XCTAssertEqual(val1, expectation.1, "Unexpected value of attribute \(K.self) for range \(expectation.0)", file: file, line: line) - XCTAssertEqual(val2, expectation.2, "Unexpected value of attribute \(K2.self) for range \(expectation.0)", file: file, line: line) + #expect( + String(string.characters[range]) == expectation.0, + "Unexpected range of run", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val1 == expectation.1, + "Unexpected value of attribute \(K.self) for range \(expectation.0)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val2 == expectation.2, + "Unexpected value of attribute \(K2.self) for range \(expectation.0)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } for ((val1, val2, range), expectation) in zip(runs.reversed(), expected.reversed()) { - XCTAssertEqual(String(string.characters[range]), expectation.0, "Unexpected range of run while reverse iterating", file: file, line: line) - XCTAssertEqual(val1, expectation.1, "Unexpected value of attribute \(K.self) for range \(expectation.0) while reverse iterating", file: file, line: line) - XCTAssertEqual(val2, expectation.2, "Unexpected value of attribute \(K2.self) for range \(expectation.0) while reverse iterating", file: file, line: line) + #expect( + String(string.characters[range]) == expectation.0, + "Unexpected range of run while reverse iterating", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val1 == expectation.1, + "Unexpected value of attribute \(K.self) for range \(expectation.0) while reverse iterating", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val2 == expectation.2, + "Unexpected value of attribute \(K2.self) for range \(expectation.0) while reverse iterating", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } } func verify(string: AttributedString, matches expected: [(String, K.Value?, K2.Value?, K3.Value?)], for key: KeyPath, _ key2: KeyPath, _ key3: KeyPath, file: StaticString = #file, line: UInt = #line) { let runs = string.runs[key, key2, key3] - XCTAssertEqual(runs.count, expected.count, "Unexpected number of runs", file: file, line: line) + #expect( + runs.count == expected.count, + "Unexpected number of runs", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) for ((val1, val2, val3, range), expectation) in zip(runs, expected) { - XCTAssertEqual(String(string.characters[range]),expectation.0, "Unexpected range of run", file: file, line: line) - XCTAssertEqual(val1, expectation.1, "Unexpected value of attribute \(K.self) for range \(expectation.0)", file: file, line: line) - XCTAssertEqual(val2, expectation.2, "Unexpected value of attribute \(K2.self) for range \(expectation.0)", file: file, line: line) - XCTAssertEqual(val3, expectation.3, "Unexpected value of attribute \(K3.self) for range \(expectation.0)", file: file, line: line) + #expect( + String(string.characters[range]) == expectation.0, + "Unexpected range of run", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val1 == expectation.1, + "Unexpected value of attribute \(K.self) for range \(expectation.0)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val2 == expectation.2, + "Unexpected value of attribute \(K2.self) for range \(expectation.0)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val3 == expectation.3, + "Unexpected value of attribute \(K3.self) for range \(expectation.0)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } for ((val1, val2, val3, range), expectation) in zip(runs.reversed(), expected.reversed()) { - XCTAssertEqual(String(string.characters[range]), expectation.0, "Unexpected range of run while reverse iterating", file: file, line: line) - XCTAssertEqual(val1, expectation.1, "Unexpected value of attribute \(K.self) for range \(expectation.0) while reverse iterating", file: file, line: line) - XCTAssertEqual(val2, expectation.2, "Unexpected value of attribute \(K2.self) for range \(expectation.0) while reverse iterating", file: file, line: line) - XCTAssertEqual(val3, expectation.3, "Unexpected value of attribute \(K3.self) for range \(expectation.0) while reverse iterating", file: file, line: line) + #expect( + String(string.characters[range]) == expectation.0, + "Unexpected range of run while reverse iterating", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val1 == expectation.1, + "Unexpected value of attribute \(K.self) for range \(String(describing: expectation.1)) while reverse iterating", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val2 == expectation.2, + "Unexpected value of attribute \(K2.self) for range \(String(describing: expectation.2)) while reverse iterating", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + val3 == expectation.3, + "Unexpected value of attribute \(K3.self) for range \(String(describing: expectation.3)) while reverse iterating", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } } // MARK: Extending Run Tests - func testExtendingRunAddCharacters() { + @Test func testExtendingRunAddCharacters() { let str = AttributedString("Hello, world", attributes: .init().testInt(2).testNonExtended(1)) var result = str @@ -97,7 +248,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { verify(string: result, matches: [("He", 2, 1), ("Hi!", 2, nil), ("rld", 2, 1)], for: \.testInt, \.testNonExtended) } - func testExtendingRunAddUnicodeScalars() { + @Test func testExtendingRunAddUnicodeScalars() { let str = AttributedString("Hello, world", attributes: .init().testInt(2).testNonExtended(1)) let scalarsStr = "A\u{0301}B" @@ -121,7 +272,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { // MARK: - Paragraph Constrained Tests - func testParagraphAttributeExpanding() { + @Test func testParagraphAttributeExpanding() { var str = AttributedString("Hello, world\nNext Paragraph") var range = str.index(afterCharacter: str.startIndex) ..< str.index(str.startIndex, offsetByCharacters: 3) str[range].testParagraphConstrained = 2 @@ -142,7 +293,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { verify(string: str, matches: [("Hello, world\n", 4), ("Next Paragraph", 4)], for: \.testParagraphConstrained) } - func testParagraphAttributeRemoval() { + @Test func testParagraphAttributeRemoval() { var str = AttributedString("Hello, world\nNext Paragraph", attributes: .init().testParagraphConstrained(2)) var range = str.index(afterCharacter: str.startIndex) ..< str.index(str.startIndex, offsetByCharacters: 3) str[range].testParagraphConstrained = nil @@ -161,7 +312,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { verify(string: str, matches: [("Hello, world\n", nil), ("Next Paragraph", nil)], for: \.testParagraphConstrained) } - func testParagraphAttributeContainerApplying() { + @Test func testParagraphAttributeContainerApplying() { var container = AttributeContainer.testParagraphConstrained(2).testString("Hello") var str = AttributedString("Hello, world\nNext Paragraph") var range = str.index(afterCharacter: str.startIndex) ..< str.index(str.startIndex, offsetByCharacters: 3) @@ -189,7 +340,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { verify(string: str, matches: [("H", 4, nil, 1), ("el", 4, "Hello", 1), ("lo, w", 4, nil, 1), ("orld\n", 4, nil, 2), ("N", 4, nil, 2), ("ext Paragrap", 4, nil, 1), ("h", 4, "Hello", 2)], for: \.testParagraphConstrained, \.testString, \.testInt) } - func testParagraphAttributeContainerReplacing() { + @Test func testParagraphAttributeContainerReplacing() { var str = AttributedString("Hello, world\nNext Paragraph") let range = str.index(afterCharacter: str.startIndex) ..< str.index(str.startIndex, offsetByCharacters: 3) str[range].testInt = 2 @@ -210,7 +361,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { verify(string: result, matches: [("H", 3, 2, nil), ("el", 3, nil, true), ("lo, world\n", 3, 2, nil), ("Next Paragraph", nil, 2, nil)], for: \.testParagraphConstrained, \.testInt, \.testBool) } - func testParagraphTextMutation() { + @Test func testParagraphTextMutation() { let str = AttributedString("Hello, world\n", attributes: .init().testParagraphConstrained(1)) + AttributedString("Next Paragraph", attributes: .init().testParagraphConstrained(2)) var result = str @@ -254,7 +405,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { verify(string: result, matches: [("Hello, wTest\n", 1), ("Replacementxt Paragraph", 1)], for: \.testParagraphConstrained) } - func testParagraphAttributedTextMutation() { + @Test func testParagraphAttributedTextMutation() { let str = AttributedString("Hello, world\n", attributes: .init().testParagraphConstrained(1)) + AttributedString("Next Paragraph", attributes: .init().testParagraphConstrained(2)) let singleReplacement = AttributedString("Test", attributes: .init().testParagraphConstrained(5).testSecondParagraphConstrained(6).testBool(true)) let multiReplacement = AttributedString("Test\nInserted", attributes: .init().testParagraphConstrained(5).testSecondParagraphConstrained(6).testBool(true)) @@ -305,7 +456,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { } #if FOUNDATION_FRAMEWORK - func testParagraphFromUntrustedRuns() throws { + @Test func testParagraphFromUntrustedRuns() throws { let str = NSMutableAttributedString(string: "Hello ", attributes: [.testParagraphConstrained : NSNumber(2)]) str.append(NSAttributedString(string: "World", attributes: [.testParagraphConstrained : NSNumber(3), .testSecondParagraphConstrained : NSNumber(4)])) @@ -314,7 +465,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { } #endif // FOUNDATION_FRAMEWORK - func testParagraphFromReplacedSubrange() { + @Test func testParagraphFromReplacedSubrange() { let str = AttributedString("Before\nHello, world\nNext Paragraph\nAfter", attributes: .init().testParagraphConstrained(1)) // Range of "world\nNext" @@ -338,7 +489,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { // MARK: - Character Constrained Tests - func testCharacterAttributeApply() { + @Test func testCharacterAttributeApply() { let str = AttributedString("*__*__**__*") var result = str @@ -356,7 +507,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { verify(string: result, matches: [("*", nil, 1), ("__", nil, 1), ("*", nil, 1), ("__", nil, 1), ("*", nil, 1), ("*", nil, 1), ("__", nil, 1), ("*", 3, 1)], for: \.testCharacterConstrained, \.testInt) } - func testCharacterAttributeSubCharacterApply() { + @Test func testCharacterAttributeSubCharacterApply() { let str = AttributedString("ABC \u{FFFD} DEF") var result = str @@ -388,7 +539,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { } - func testCharacterAttributeContainerReplacing() { + @Test func testCharacterAttributeContainerReplacing() { var str = AttributedString("*__*__**__*") let range = str.index(afterCharacter: str.startIndex) ..< str.index(str.startIndex, offsetByCharacters: 4) str[range].testInt = 2 @@ -409,7 +560,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { verify(string: result, matches: [("*", nil, 2, nil), ("__", nil, nil, true), ("*", 3, nil, true), ("__", nil, 2, nil), ("*", nil, 2, nil), ("*", nil, 2, nil), ("__", nil, 2, nil), ("*", nil, 2, nil)], for: \.testCharacterConstrained, \.testInt, \.testBool) } - func testCharacterTextMutation() { + @Test func testCharacterTextMutation() { let str = AttributedString("*__*__**__*", attributes: .init().testCharacterConstrained(2)) var result = str @@ -438,7 +589,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { } #if FOUNDATION_FRAMEWORK - func testCharacterFromUntrustedRuns() throws { + @Test func testCharacterFromUntrustedRuns() throws { let str = NSMutableAttributedString(string: "*__*__**__*", attributes: [.testCharacterConstrained : NSNumber(2)]) str.append(NSAttributedString(string: "_*")) @@ -449,7 +600,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { // MARK: Invalidation Tests - func testInvalidationAttributeChange() { + @Test func testInvalidationAttributeChange() { let str = AttributedString("Hello, world", attributes: .init().testInt(1).testAttributeDependent(2)) var result = str @@ -483,7 +634,7 @@ class TestAttributedStringConstrainingBehavior: XCTestCase { verify(string: result, matches: [("Hello, world", 2, nil)], for: \.testInt, \.testAttributeDependent) } - func testInvalidationCharacterChange() { + @Test func testInvalidationCharacterChange() { let str = AttributedString("Hello, world", attributes: .init().testInt(1).testCharacterDependent(2)) var result = str diff --git a/Tests/FoundationEssentialsTests/AttributedString/AttributedStringTestSupport.swift b/Tests/FoundationEssentialsTests/AttributedString/AttributedStringTestSupport.swift index 78e78bee3..f9048908d 100644 --- a/Tests/FoundationEssentialsTests/AttributedString/AttributedStringTestSupport.swift +++ b/Tests/FoundationEssentialsTests/AttributedString/AttributedStringTestSupport.swift @@ -10,8 +10,10 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport +#if FOUNDATION_FRAMEWORK +@testable import Foundation +#else +@testable import FoundationEssentials #endif #if FOUNDATION_FRAMEWORK diff --git a/Tests/FoundationEssentialsTests/AttributedString/AttributedStringTests.swift b/Tests/FoundationEssentialsTests/AttributedString/AttributedStringTests.swift index 9ff935797..c8e68e7c3 100644 --- a/Tests/FoundationEssentialsTests/AttributedString/AttributedStringTests.swift +++ b/Tests/FoundationEssentialsTests/AttributedString/AttributedStringTests.swift @@ -10,9 +10,7 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing #if canImport(FoundationEssentials) @testable import FoundationEssentials @@ -23,25 +21,40 @@ import TestSupport // For testing default attribute scope conversion #if canImport(Accessibility) import Accessibility +private let canImportAccessibility = true +#else +private let canImportAccessibility = false #endif + #if canImport(SwiftUI) import SwiftUI +private let canImportSwiftUI = true +#else +private let canImportSwiftUI = false #endif + #if canImport(UIKit) import UIKit +private let canImportUIKit = true +#else +private let canImportUIKit = false #endif + #if canImport(AppKit) import AppKit +private let canImportAppKit = true +#else +private let canImportAppKit = false #endif #endif // FOUNDATION_FRAMEWORK /// Regression and coverage tests for `AttributedString` and its associated objects -final class TestAttributedString: XCTestCase { +struct AttributedStringTests { // MARK: - Enumeration Tests - func testEmptyEnumeration() { + @Test func testEmptyEnumeration() { for _ in AttributedString().runs { - XCTFail("Empty AttributedString should not enumerate any attributes") + Issue.record("Empty AttributedString should not enumerate any attributes") } } @@ -50,19 +63,31 @@ final class TestAttributedString: XCTestCase { var expectIterator = expectation.makeIterator() for (attribute, range) in runs { let expected = expectIterator.next()! - XCTAssertEqual(String(string[range].characters), expected.0, "Substring of AttributedString characters for range of run did not match expectation") - XCTAssertEqual(attribute, expected.1, "Attribute of run did not match expectation") + #expect( + String(string[range].characters) == expected.0, + "Substring of AttributedString characters for range of run did not match expectation" + ) + #expect( + attribute == expected.1, + "Attribute of run did not match expectation" + ) } - XCTAssertNil(expectIterator.next(), "Additional runs expected but not found") + #expect(expectIterator.next() == nil, "Additional runs expected but not found") // Test that the attribute is correct when iterating through reversed attribute runs expectIterator = expectation.reversed().makeIterator() for (attribute, range) in runs.reversed() { let expected = expectIterator.next()! - XCTAssertEqual(String(string[range].characters), expected.0, "Substring of AttributedString characters for range of run did not match expectation") - XCTAssertEqual(attribute, expected.1, "Attribute of run did not match expectation") + #expect( + String(string[range].characters) == expected.0, + "Substring of AttributedString characters for range of run did not match expectation" + ) + #expect( + attribute == expected.1, + "Attribute of run did not match expectation" + ) } - XCTAssertNil(expectIterator.next(), "Additional runs expected but not found") + #expect(expectIterator.next() == nil, "Additional runs expected but not found") } func verifyAttributes(_ runs: AttributedString.Runs.AttributesSlice2, string: AttributedString, expectation: [(String, T.Value?, U.Value?)]) { @@ -70,21 +95,39 @@ final class TestAttributedString: XCTestCase { var expectIterator = expectation.makeIterator() for (attribute, attribute2, range) in runs { let expected = expectIterator.next()! - XCTAssertEqual(String(string[range].characters), expected.0, "Substring of AttributedString characters for range of run did not match expectation") - XCTAssertEqual(attribute, expected.1, "Attribute of run did not match expectation") - XCTAssertEqual(attribute2, expected.2, "Attribute of run did not match expectation") - } - XCTAssertNil(expectIterator.next(), "Additional runs expected but not found") + #expect( + String(string[range].characters) == expected.0, + "Substring of AttributedString characters for range of run did not match expectation" + ) + #expect( + attribute == expected.1, + "Attribute of run did not match expectation" + ) + #expect( + attribute2 == expected.2, + "Attribute of run did not match expectation" + ) + } + #expect(expectIterator.next() == nil, "Additional runs expected but not found") // Test that the attributes are correct when iterating through reversed attribute runs expectIterator = expectation.reversed().makeIterator() for (attribute, attribute2, range) in runs.reversed() { let expected = expectIterator.next()! - XCTAssertEqual(String(string[range].characters), expected.0, "Substring of AttributedString characters for range of run did not match expectation") - XCTAssertEqual(attribute, expected.1, "Attribute of run did not match expectation") - XCTAssertEqual(attribute2, expected.2, "Attribute of run did not match expectation") - } - XCTAssertNil(expectIterator.next(), "Additional runs expected but not found") + #expect( + String(string[range].characters) == expected.0, + "Substring of AttributedString characters for range of run did not match expectation" + ) + #expect( + attribute == expected.1, + "Attribute of run did not match expectation" + ) + #expect( + attribute2 == expected.2, + "Attribute of run did not match expectation" + ) + } + #expect(expectIterator.next() == nil, "Additional runs expected but not found") } #if FOUNDATION_FRAMEWORK @@ -93,23 +136,65 @@ final class TestAttributedString: XCTestCase { var expectIterator = expectation.makeIterator() for (attribute, range) in runs { let expected = expectIterator.next()! - XCTAssertEqual(String(string[range].characters), expected.0, "Substring of AttributedString characters for range of run did not match expectation", file: file, line: line) - XCTAssertEqual(attribute, expected.1, "Attribute of run did not match expectation", file: file, line: line) - } - XCTAssertNil(expectIterator.next(), "Additional runs expected but not found", file: file, line: line) + #expect( + String(string[range].characters) == expected.0, + "Substring of AttributedString characters for range of run did not match expectation", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + attribute == expected.1, + "Attribute of run did not match expectation", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + } + #expect( + expectIterator.next() == nil, + "Additional runs expected but not found", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) // Test that the attribute is correct when iterating through reversed attribute runs expectIterator = expectation.reversed().makeIterator() for (attribute, range) in runs.reversed() { let expected = expectIterator.next()! - XCTAssertEqual(String(string[range].characters), expected.0, "Substring of AttributedString characters for range of run did not match expectation", file: file, line: line) - XCTAssertEqual(attribute, expected.1, "Attribute of run did not match expectation", file: file, line: line) - } - XCTAssertNil(expectIterator.next(), "Additional runs expected but not found", file: file, line: line) + #expect( + String(string[range].characters) == expected.0, + "Substring of AttributedString characters for range of run did not match expectation", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + attribute == expected.1, + "Attribute of run did not match expectation", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + } + #expect( + expectIterator.next() == nil, + "Additional runs expected but not found", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } #endif // FOUNDATION_FRAMEWORK - func testSimpleEnumeration() { + @Test func testSimpleEnumeration() { var attrStr = AttributedString("Hello", attributes: AttributeContainer().testInt(1)) attrStr += " " attrStr += AttributedString("World", attributes: AttributeContainer().testDouble(2.0)) @@ -118,22 +203,22 @@ final class TestAttributedString: XCTestCase { var expectationIterator = expectation.makeIterator() for run in attrStr.runs { let expected = expectationIterator.next()! - XCTAssertEqual(String(attrStr[run.range].characters), expected.0) - XCTAssertEqual(run.testInt, expected.1) - XCTAssertEqual(run.testDouble, expected.2) - XCTAssertNil(run.testString) + #expect(String(attrStr[run.range].characters) == expected.0) + #expect(run.testInt == expected.1) + #expect(run.testDouble == expected.2) + #expect(run.testString == nil) } - XCTAssertNil(expectationIterator.next()) + #expect(expectationIterator.next() == nil) expectationIterator = expectation.reversed().makeIterator() for run in attrStr.runs.reversed() { let expected = expectationIterator.next()! - XCTAssertEqual(String(attrStr[run.range].characters), expected.0) - XCTAssertEqual(run.testInt, expected.1) - XCTAssertEqual(run.testDouble, expected.2) - XCTAssertNil(run.testString) + #expect(String(attrStr[run.range].characters) == expected.0) + #expect(run.testInt == expected.1) + #expect(run.testDouble == expected.2) + #expect(run.testString == nil) } - XCTAssertNil(expectationIterator.next()) + #expect(expectationIterator.next() == nil) let attrView = attrStr.runs verifyAttributes(attrView[\.testInt], string: attrStr, expectation: [("Hello", 1), (" World", nil)]) @@ -142,7 +227,7 @@ final class TestAttributedString: XCTestCase { verifyAttributes(attrView[\.testInt, \.testDouble], string: attrStr, expectation: [("Hello", 1, nil), (" ", nil, nil), ("World", nil, 2.0)]) } - func testSliceEnumeration() { + @Test func testSliceEnumeration() { var attrStr = AttributedString("Hello", attributes: AttributeContainer().testInt(1)) attrStr += AttributedString(" ") attrStr += AttributedString("World", attributes: AttributeContainer().testDouble(2.0)) @@ -153,22 +238,22 @@ final class TestAttributedString: XCTestCase { var expectationIterator = expectation.makeIterator() for run in attrStrSlice.runs { let expected = expectationIterator.next()! - XCTAssertEqual(String(attrStr[run.range].characters), expected.0) - XCTAssertEqual(run.testInt, expected.1) - XCTAssertEqual(run.testDouble, expected.2) - XCTAssertNil(run.testString) + #expect(String(attrStr[run.range].characters) == expected.0) + #expect(run.testInt == expected.1) + #expect(run.testDouble == expected.2) + #expect(run.testString == nil) } - XCTAssertNil(expectationIterator.next()) + #expect(expectationIterator.next() == nil) expectationIterator = expectation.reversed().makeIterator() for run in attrStrSlice.runs.reversed() { let expected = expectationIterator.next()! - XCTAssertEqual(String(attrStr[run.range].characters), expected.0) - XCTAssertEqual(run.testInt, expected.1) - XCTAssertEqual(run.testDouble, expected.2) - XCTAssertNil(run.testString) + #expect(String(attrStr[run.range].characters) == expected.0) + #expect(run.testInt == expected.1) + #expect(run.testDouble == expected.2) + #expect(run.testString == nil) } - XCTAssertNil(expectationIterator.next()) + #expect(expectationIterator.next() == nil) let attrView = attrStrSlice.runs verifyAttributes(attrView[\.testInt], string: attrStr, expectation: [("lo", 1), (" Wo", nil)]) @@ -178,7 +263,7 @@ final class TestAttributedString: XCTestCase { } #if FOUNDATION_FRAMEWORK - func testNSSliceEnumeration() { + @Test func testNSSliceEnumeration() { var attrStr = AttributedString("Hello", attributes: AttributeContainer().testInt(1)) attrStr += AttributedString(" ") attrStr += AttributedString("World", attributes: AttributeContainer().testDouble(2.0)) @@ -206,23 +291,23 @@ final class TestAttributedString: XCTestCase { // MARK: - Attribute Tests - func testSimpleAttribute() { + @Test func testSimpleAttribute() { let attrStr = AttributedString("Foo", attributes: AttributeContainer().testInt(42)) let (value, range) = attrStr.runs[\.testInt][attrStr.startIndex] - XCTAssertEqual(value, 42) - XCTAssertEqual(range, attrStr.startIndex ..< attrStr.endIndex) + #expect(value == 42) + #expect(range == attrStr.startIndex ..< attrStr.endIndex) } - func testConstructorAttribute() { + @Test func testConstructorAttribute() { // TODO: Re-evaluate whether we want these. let attrStr = AttributedString("Hello", attributes: AttributeContainer().testString("Helvetica").testInt(2)) var expected = AttributedString("Hello") expected.testString = "Helvetica" expected.testInt = 2 - XCTAssertEqual(attrStr, expected) + #expect(attrStr == expected) } - func testAddAndRemoveAttribute() { + @Test func testAddAndRemoveAttribute() { let attr : Int = 42 let attr2 : Double = 1.0 var attrStr = AttributedString("Test") @@ -230,70 +315,86 @@ final class TestAttributedString: XCTestCase { attrStr.testDouble = attr2 let expected1 = AttributedString("Test", attributes: AttributeContainer().testInt(attr).testDouble(attr2)) - XCTAssertEqual(attrStr, expected1) + #expect(attrStr == expected1) attrStr.testDouble = nil let expected2 = AttributedString("Test", attributes: AttributeContainer().testInt(attr)) - XCTAssertEqual(attrStr, expected2) + #expect(attrStr == expected2) } - func testAddingAndRemovingAttribute() { + @Test func testAddingAndRemovingAttribute() { let container = AttributeContainer().testInt(1).testDouble(2.2) let attrStr = AttributedString("Test").mergingAttributes(container) let expected = AttributedString("Test", attributes: AttributeContainer().testInt(1).testDouble(2.2)) - XCTAssertEqual(attrStr, expected) + #expect(attrStr == expected) var doubleRemoved = attrStr doubleRemoved.testDouble = nil - XCTAssertEqual(doubleRemoved, AttributedString("Test", attributes: AttributeContainer().testInt(1))) + #expect( + doubleRemoved == AttributedString( + "Test", attributes: AttributeContainer().testInt(1)) + ) } - func testScopedAttributes() { + @Test func testScopedAttributes() { var str = AttributedString("Hello, world", attributes: AttributeContainer().testInt(2).testDouble(3.4)) - XCTAssertEqual(str.test.testInt, 2) - XCTAssertEqual(str.test.testDouble, 3.4) - XCTAssertEqual(str.runs[str.runs.startIndex].test.testInt, 2) - + #expect(str.test.testInt == 2) + #expect(str.test.testDouble == 3.4) + #expect(str.runs[str.runs.startIndex].test.testInt == 2) + str.test.testInt = 4 - XCTAssertEqual(str, AttributedString("Hello, world", attributes: AttributeContainer.testInt(4).testDouble(3.4))) - + #expect(str == AttributedString("Hello, world", attributes: AttributeContainer.testInt(4).testDouble(3.4))) + let range = str.startIndex ..< str.characters.index(after: str.startIndex) str[range].test.testBool = true - XCTAssertNil(str.test.testBool) - XCTAssertNotNil(str[range].test.testBool) - XCTAssertTrue(str[range].test.testBool!) + #expect(str.test.testBool == nil) + #expect(str[range].test.testBool != nil) + #expect(str[range].test.testBool!) } - func testRunAttributes() { + @Test func testRunAttributes() { var str = AttributedString("String", attributes: .init().testString("test1")) str += "None" str += AttributedString("String+Int", attributes: .init().testString("test2").testInt(42)) let attributes = str.runs.map { $0.attributes } - XCTAssertEqual(attributes.count, 3) - XCTAssertEqual(attributes[0], .init().testString("test1")) - XCTAssertEqual(attributes[1], .init()) - XCTAssertEqual(attributes[2], .init().testString("test2").testInt(42)) + #expect(attributes.count == 3) + #expect(attributes[0] == .init().testString("test1")) + #expect(attributes[1] == .init()) + #expect(attributes[2] == .init().testString("test2").testInt(42)) } // MARK: - Comparison Tests - func testAttributedStringEquality() { - XCTAssertEqual(AttributedString(), AttributedString()) - XCTAssertEqual(AttributedString("abc"), AttributedString("abc")) - XCTAssertEqual(AttributedString("abc", attributes: AttributeContainer().testInt(1)), AttributedString("abc", attributes: AttributeContainer().testInt(1))) - XCTAssertNotEqual(AttributedString("abc", attributes: AttributeContainer().testInt(1)), AttributedString("abc", attributes: AttributeContainer().testInt(2))) - XCTAssertNotEqual(AttributedString("abc", attributes: AttributeContainer().testInt(1)), AttributedString("def", attributes: AttributeContainer().testInt(1))) + @Test func testAttributedStringEquality() { + #expect( + AttributedString() == AttributedString() + ) + #expect( + AttributedString("abc") == AttributedString("abc") + ) + #expect( + AttributedString("abc", attributes: AttributeContainer().testInt(1)) == + AttributedString("abc", attributes: AttributeContainer().testInt(1)) + ) + #expect( + AttributedString("abc", attributes: AttributeContainer().testInt(1)) != + AttributedString("abc", attributes: AttributeContainer().testInt(2)) + ) + #expect( + AttributedString("abc", attributes: AttributeContainer().testInt(1)) != + AttributedString("def", attributes: AttributeContainer().testInt(1)) + ) var a = AttributedString("abc", attributes: AttributeContainer().testInt(1)) a += AttributedString("def", attributes: AttributeContainer().testInt(1)) - XCTAssertEqual(a, AttributedString("abcdef", attributes: AttributeContainer().testInt(1))) + #expect(a == AttributedString("abcdef", attributes: AttributeContainer().testInt(1))) a = AttributedString("ab", attributes: AttributeContainer().testInt(1)) a += AttributedString("cdef", attributes: AttributeContainer().testInt(2)) var b = AttributedString("abcd", attributes: AttributeContainer().testInt(1)) b += AttributedString("ef", attributes: AttributeContainer().testInt(2)) - XCTAssertNotEqual(a, b) + #expect(a != b) a = AttributedString("abc") a += AttributedString("defghi", attributes: AttributeContainer().testInt(2)) @@ -301,22 +402,22 @@ final class TestAttributedString: XCTestCase { b = AttributedString("abc") b += AttributedString("def", attributes: AttributeContainer().testInt(2)) b += "ghijkl" - XCTAssertNotEqual(a, b) + #expect(a != b) let a1 = AttributedString("Café", attributes: AttributeContainer().testInt(1)) let a2 = AttributedString("Cafe\u{301}", attributes: AttributeContainer().testInt(1)) - XCTAssertEqual(a1, a2) + #expect(a1 == a2) let a3 = (AttributedString("Cafe", attributes: AttributeContainer().testInt(1)) + AttributedString("\u{301}", attributes: AttributeContainer().testInt(2))) - XCTAssertNotEqual(a1, a3) - XCTAssertNotEqual(a2, a3) - XCTAssertTrue(a1.characters.elementsEqual(a3.characters)) - XCTAssertTrue(a2.characters.elementsEqual(a3.characters)) + #expect(a1 != a3) + #expect(a2 != a3) + #expect(a1.characters.elementsEqual(a3.characters)) + #expect(a2.characters.elementsEqual(a3.characters)) } - func testAttributedSubstringEquality() { + @Test func testAttributedSubstringEquality() { let emptyStr = AttributedString("01234567890123456789") let index0 = emptyStr.characters.startIndex @@ -331,22 +432,22 @@ final class TestAttributedString: XCTestCase { halfhalfStr[index0 ..< index10].testInt = 1 halfhalfStr[index10 ..< index20].testDouble = 2.0 - XCTAssertEqual(emptyStr[index0 ..< index0], emptyStr[index0 ..< index0]) - XCTAssertEqual(emptyStr[index0 ..< index5], emptyStr[index0 ..< index5]) - XCTAssertEqual(emptyStr[index0 ..< index20], emptyStr[index0 ..< index20]) - XCTAssertEqual(singleAttrStr[index0 ..< index20], singleAttrStr[index0 ..< index20]) - XCTAssertEqual(halfhalfStr[index0 ..< index20], halfhalfStr[index0 ..< index20]) + #expect(emptyStr[index0 ..< index0] == emptyStr[index0 ..< index0]) + #expect(emptyStr[index0 ..< index5] == emptyStr[index0 ..< index5]) + #expect(emptyStr[index0 ..< index20] == emptyStr[index0 ..< index20]) + #expect(singleAttrStr[index0 ..< index20] == singleAttrStr[index0 ..< index20]) + #expect(halfhalfStr[index0 ..< index20] == halfhalfStr[index0 ..< index20]) - XCTAssertEqual(emptyStr[index0 ..< index10], singleAttrStr[index10 ..< index20]) - XCTAssertEqual(halfhalfStr[index0 ..< index10], singleAttrStr[index0 ..< index10]) + #expect(emptyStr[index0 ..< index10] == singleAttrStr[index10 ..< index20]) + #expect(halfhalfStr[index0 ..< index10] == singleAttrStr[index0 ..< index10]) - XCTAssertNotEqual(emptyStr[index0 ..< index10], singleAttrStr[index0 ..< index10]) - XCTAssertNotEqual(emptyStr[index0 ..< index10], singleAttrStr[index0 ..< index20]) + #expect(emptyStr[index0 ..< index10] != singleAttrStr[index0 ..< index10]) + #expect(emptyStr[index0 ..< index10] != singleAttrStr[index0 ..< index20]) - XCTAssertTrue(emptyStr[index0 ..< index5] == AttributedString("01234")) + #expect(emptyStr[index0 ..< index5] == AttributedString("01234")) } - func testRunEquality() { + @Test func testRunEquality() { var attrStr = AttributedString("Hello", attributes: AttributeContainer().testInt(1)) attrStr += AttributedString(" ") attrStr += AttributedString("World", attributes: AttributeContainer().testInt(2)) @@ -365,25 +466,25 @@ final class TestAttributedString: XCTestCase { } // Same string, same range, different attributes - XCTAssertNotEqual(run(0, in: attrStr), run(0, in: attrStr2)) - + #expect(run(0, in: attrStr) != run(0, in: attrStr2)) + // Different strings, same range, same attributes - XCTAssertEqual(run(1, in: attrStr), run(1, in: attrStr2)) - + #expect(run(1, in: attrStr) == run(1, in: attrStr2)) + // Same string, same range, same attributes - XCTAssertEqual(run(2, in: attrStr), run(2, in: attrStr2)) - + #expect(run(2, in: attrStr) == run(2, in: attrStr2)) + // Different string, different range, same attributes - XCTAssertEqual(run(2, in: attrStr), run(0, in: attrStr2)) - + #expect(run(2, in: attrStr) == run(0, in: attrStr2)) + // Same string, different range, same attributes - XCTAssertEqual(run(0, in: attrStr), run(3, in: attrStr2)) - + #expect(run(0, in: attrStr) == run(3, in: attrStr2)) + // A runs collection of the same order but different run lengths - XCTAssertNotEqual(attrStr.runs, attrStr3.runs) + #expect(attrStr.runs != attrStr3.runs) } - func testSubstringRunEquality() { + @Test func testSubstringRunEquality() { var attrStr = AttributedString("Hello", attributes: AttributeContainer().testInt(1)) attrStr += AttributedString(" ") attrStr += AttributedString("World", attributes: AttributeContainer().testInt(2)) @@ -392,46 +493,52 @@ final class TestAttributedString: XCTestCase { attrStr2 += AttributedString("_") attrStr2 += AttributedString("World", attributes: AttributeContainer().testInt(2)) - XCTAssertEqual(attrStr[attrStr.runs.last!.range].runs, attrStr2[attrStr2.runs.first!.range].runs) - XCTAssertEqual(attrStr[attrStr.runs.last!.range].runs, attrStr2[attrStr2.runs.last!.range].runs) - + #expect( + attrStr[attrStr.runs.last!.range].runs == + attrStr2[attrStr2.runs.first!.range].runs + ) + #expect( + attrStr[attrStr.runs.last!.range].runs == + attrStr2[attrStr2.runs.last!.range].runs + ) + let rangeA = attrStr.runs.first!.range.upperBound ..< attrStr.endIndex let rangeB = attrStr2.runs.first!.range.upperBound ..< attrStr.endIndex let rangeC = attrStr.startIndex ..< attrStr.runs.last!.range.lowerBound let rangeD = attrStr.runs.first!.range - XCTAssertEqual(attrStr[rangeA].runs, attrStr2[rangeB].runs) - XCTAssertNotEqual(attrStr[rangeC].runs, attrStr2[rangeB].runs) - XCTAssertNotEqual(attrStr[rangeD].runs, attrStr2[rangeB].runs) - + #expect(attrStr[rangeA].runs == attrStr2[rangeB].runs) + #expect(attrStr[rangeC].runs != attrStr2[rangeB].runs) + #expect(attrStr[rangeD].runs != attrStr2[rangeB].runs) + // Test starting/ending runs that only differ outside of the range do not prevent equality attrStr2[attrStr.runs.first!.range].testInt = 1 attrStr2.characters.insert(contentsOf: "123", at: attrStr.startIndex) attrStr2.characters.append(contentsOf: "45") let rangeE = attrStr.startIndex ..< attrStr.endIndex let rangeF = attrStr2.characters.index(attrStr2.startIndex, offsetBy: 3) ..< attrStr2.characters.index(attrStr2.startIndex, offsetBy: 14) - XCTAssertEqual(attrStr[rangeE].runs, attrStr2[rangeF].runs) + #expect(attrStr[rangeE].runs == attrStr2[rangeF].runs) } // MARK: - Mutation Tests - func testDirectMutationCopyOnWrite() { + @Test func testDirectMutationCopyOnWrite() { var attrStr = AttributedString("ABC") let copy = attrStr attrStr += "D" - XCTAssertEqual(copy, AttributedString("ABC")) - XCTAssertNotEqual(attrStr, copy) + #expect(copy == AttributedString("ABC")) + #expect(attrStr != copy) } - func testAttributeMutationCopyOnWrite() { + @Test func testAttributeMutationCopyOnWrite() { var attrStr = AttributedString("ABC") let copy = attrStr attrStr.testInt = 1 - XCTAssertNotEqual(attrStr, copy) + #expect(attrStr != copy) } - func testSliceAttributeMutation() { + @Test func testSliceAttributeMutation() { let attr : Int = 42 let attr2 : Double = 1.0 @@ -445,12 +552,12 @@ final class TestAttributedString: XCTestCase { var expected = AttributedString("Hello", attributes: AttributeContainer().testInt(attr).testDouble(attr2)) expected += AttributedString(" World", attributes: AttributeContainer().testInt(attr)) - XCTAssertEqual(attrStr, expected) + #expect(attrStr == expected) - XCTAssertNotEqual(copy, attrStr) + #expect(copy != attrStr) } - func testEnumerationAttributeMutation() { + @Test func testEnumerationAttributeMutation() { var attrStr = AttributedString("A", attributes: AttributeContainer().testInt(1)) attrStr += AttributedString("B", attributes: AttributeContainer().testDouble(2.0)) attrStr += AttributedString("C", attributes: AttributeContainer().testInt(3)) @@ -464,10 +571,10 @@ final class TestAttributedString: XCTestCase { var expected = AttributedString("A") expected += AttributedString("B", attributes: AttributeContainer().testDouble(2.0)) expected += "C" - XCTAssertEqual(expected, attrStr) + #expect(expected == attrStr) } - func testMutateMultipleAttributes() { + @Test func testMutateMultipleAttributes() { var attrStr = AttributedString("A", attributes: AttributeContainer().testInt(1).testBool(true)) attrStr += AttributedString("B", attributes: AttributeContainer().testInt(1).testDouble(2)) attrStr += AttributedString("C", attributes: AttributeContainer().testDouble(2).testBool(false)) @@ -479,7 +586,7 @@ final class TestAttributedString: XCTestCase { $2.value = nil } let removal1expected = AttributedString("ABC") - XCTAssertEqual(removal1expected, removal1) + #expect(removal1expected == removal1) // Test change value, same attribute. let changeSame1 = attrStr.transformingAttributes(\.testInt, \.testDouble, \.testBool) { @@ -496,7 +603,7 @@ final class TestAttributedString: XCTestCase { var changeSame1expected = AttributedString("A", attributes: AttributeContainer().testInt(42).testBool(false)) changeSame1expected += AttributedString("B", attributes: AttributeContainer().testInt(42).testDouble(3)) changeSame1expected += AttributedString("C", attributes: AttributeContainer().testDouble(3).testBool(true)) - XCTAssertEqual(changeSame1expected, changeSame1) + #expect(changeSame1expected == changeSame1) // Test change value, different attribute let changeDifferent1 = attrStr.transformingAttributes(\.testInt, \.testDouble, \.testBool) { @@ -514,7 +621,7 @@ final class TestAttributedString: XCTestCase { var changeDifferent1expected = AttributedString("A", attributes: AttributeContainer().testDouble(2).testInt(42)) changeDifferent1expected += AttributedString("B", attributes: AttributeContainer().testDouble(2).testBool(false)) changeDifferent1expected += AttributedString("C", attributes: AttributeContainer().testBool(false).testInt(42)) - XCTAssertEqual(changeDifferent1expected, changeDifferent1) + #expect(changeDifferent1expected == changeDifferent1) // Test change range var changeRange1First = true @@ -531,10 +638,10 @@ final class TestAttributedString: XCTestCase { var changeRange1expected = AttributedString("A", attributes: AttributeContainer().testInt(1).testBool(true)) changeRange1expected += AttributedString("B", attributes: AttributeContainer().testInt(1).testBool(true)) changeRange1expected += AttributedString("C", attributes: AttributeContainer().testDouble(2).testBool(false)) - XCTAssertEqual(changeRange1expected, changeRange1) + #expect(changeRange1expected == changeRange1) } - func testMutateAttributes() { + @Test func testMutateAttributes() { var attrStr = AttributedString("A", attributes: AttributeContainer().testInt(1).testBool(true)) attrStr += AttributedString("B", attributes: AttributeContainer().testInt(1).testDouble(2)) attrStr += AttributedString("C", attributes: AttributeContainer().testDouble(2).testBool(false)) @@ -546,7 +653,7 @@ final class TestAttributedString: XCTestCase { var removal1expected = AttributedString("A", attributes: AttributeContainer().testBool(true)) removal1expected += AttributedString("B", attributes: AttributeContainer().testDouble(2)) removal1expected += AttributedString("C", attributes: AttributeContainer().testDouble(2).testBool(false)) - XCTAssertEqual(removal1expected, removal1) + #expect(removal1expected == removal1) // Test change value, same attribute. let changeSame1 = attrStr.transformingAttributes(\.testBool) { @@ -557,7 +664,7 @@ final class TestAttributedString: XCTestCase { var changeSame1expected = AttributedString("A", attributes: AttributeContainer().testInt(1).testBool(false)) changeSame1expected += AttributedString("B", attributes: AttributeContainer().testInt(1).testDouble(2)) changeSame1expected += AttributedString("C", attributes: AttributeContainer().testDouble(2).testBool(true)) - XCTAssertEqual(changeSame1expected, changeSame1) + #expect(changeSame1expected == changeSame1) // Test change value, different attribute let changeDifferent1 = attrStr.transformingAttributes(\.testBool) { @@ -568,7 +675,7 @@ final class TestAttributedString: XCTestCase { var changeDifferent1expected = AttributedString("A", attributes: AttributeContainer().testInt(1).testDouble(42)) changeDifferent1expected += AttributedString("B", attributes: AttributeContainer().testInt(1).testDouble(2)) changeDifferent1expected += AttributedString("C", attributes: AttributeContainer().testDouble(43)) - XCTAssertEqual(changeDifferent1expected, changeDifferent1) + #expect(changeDifferent1expected == changeDifferent1) // Test change range let changeRange1 = attrStr.transformingAttributes(\.testInt) { @@ -580,7 +687,7 @@ final class TestAttributedString: XCTestCase { var changeRange1expected = AttributedString("A", attributes: AttributeContainer().testInt(1).testBool(true)) changeRange1expected += AttributedString("B", attributes: AttributeContainer().testDouble(2)) changeRange1expected += AttributedString("C", attributes: AttributeContainer().testDouble(2).testBool(false)) - XCTAssertEqual(changeRange1expected, changeRange1) + #expect(changeRange1expected == changeRange1) // Now try extending it let changeRange2 = attrStr.transformingAttributes(\.testInt) { @@ -592,10 +699,10 @@ final class TestAttributedString: XCTestCase { var changeRange2expected = AttributedString("A", attributes: AttributeContainer().testInt(1).testBool(true)) changeRange2expected += AttributedString("B", attributes: AttributeContainer().testInt(1).testDouble(2)) changeRange2expected += AttributedString("C", attributes: AttributeContainer().testInt(1).testDouble(2).testBool(false)) - XCTAssertEqual(changeRange2expected, changeRange2) + #expect(changeRange2expected == changeRange2) } - func testReplaceAttributes() { + @Test func testReplaceAttributes() { var attrStr = AttributedString("A", attributes: AttributeContainer().testInt(1).testBool(true)) attrStr += AttributedString("B", attributes: AttributeContainer().testInt(1).testDouble(2)) attrStr += AttributedString("C", attributes: AttributeContainer().testDouble(2).testBool(false)) @@ -609,8 +716,8 @@ final class TestAttributedString: XCTestCase { var removal1expected = AttributedString("A", attributes: AttributeContainer().testBool(true)) removal1expected += AttributedString("B", attributes: AttributeContainer().testDouble(2)) removal1expected += AttributedString("C", attributes: AttributeContainer().testDouble(2).testBool(false)) - XCTAssertEqual(removal1expected, removal1) - + #expect(removal1expected == removal1) + // Test change value, same attribute. let changeSame1Find = AttributeContainer().testBool(false) let changeSame1Replace = AttributeContainer().testBool(true) @@ -620,8 +727,8 @@ final class TestAttributedString: XCTestCase { var changeSame1expected = AttributedString("A", attributes: AttributeContainer().testInt(1).testBool(true)) changeSame1expected += AttributedString("B", attributes: AttributeContainer().testInt(1).testDouble(2)) changeSame1expected += AttributedString("C", attributes: AttributeContainer().testDouble(2).testBool(true)) - XCTAssertEqual(changeSame1expected, changeSame1) - + #expect(changeSame1expected == changeSame1) + // Test change value, different attribute let changeDifferent1Find = AttributeContainer().testBool(false) let changeDifferent1Replace = AttributeContainer().testDouble(43) @@ -631,22 +738,22 @@ final class TestAttributedString: XCTestCase { var changeDifferent1expected = AttributedString("A", attributes: AttributeContainer().testInt(1).testBool(true)) changeDifferent1expected += AttributedString("B", attributes: AttributeContainer().testInt(1).testDouble(2)) changeDifferent1expected += AttributedString("C", attributes: AttributeContainer().testDouble(43)) - XCTAssertEqual(changeDifferent1expected, changeDifferent1) + #expect(changeDifferent1expected == changeDifferent1) } - func testSliceMutation() { + @Test func testSliceMutation() { var attrStr = AttributedString("Hello World", attributes: AttributeContainer().testInt(1)) let start = attrStr.characters.index(attrStr.startIndex, offsetBy: 6) attrStr.replaceSubrange(start ..< attrStr.characters.index(start, offsetBy:5), with: AttributedString("Goodbye", attributes: AttributeContainer().testInt(2))) var expected = AttributedString("Hello ", attributes: AttributeContainer().testInt(1)) expected += AttributedString("Goodbye", attributes: AttributeContainer().testInt(2)) - XCTAssertEqual(attrStr, expected) - XCTAssertNotEqual(attrStr, AttributedString("Hello Goodbye", attributes: AttributeContainer().testInt(1))) + #expect(attrStr == expected) + #expect(attrStr != AttributedString("Hello Goodbye", attributes: AttributeContainer().testInt(1))) } - func testOverlappingSliceMutation() { + @Test func testOverlappingSliceMutation() { var attrStr = AttributedString("Hello, world!") attrStr[attrStr.range(of: "Hello")!].testInt = 1 attrStr[attrStr.range(of: "world")!].testInt = 2 @@ -658,43 +765,43 @@ final class TestAttributedString: XCTestCase { expected += AttributedString("wo", attributes: AttributeContainer().testBool(true).testInt(2)) expected += AttributedString("rld", attributes: AttributeContainer().testInt(2)) expected += AttributedString("!") - XCTAssertEqual(attrStr, expected) + #expect(attrStr == expected) } - func testCharacters_replaceSubrange() { + @Test func testCharacters_replaceSubrange() { var attrStr = AttributedString("Hello World", attributes: AttributeContainer().testInt(1)) attrStr.characters.replaceSubrange(attrStr.range(of: " ")!, with: " Good ") let expected = AttributedString("Hello Good World", attributes: AttributeContainer().testInt(1)) - XCTAssertEqual(expected, attrStr) + #expect(expected == attrStr) } - func testCharactersMutation_append() { + @Test func testCharactersMutation_append() { var attrStr = AttributedString("Hello World", attributes: AttributeContainer().testInt(1)) attrStr.characters.append(contentsOf: " Goodbye") let expected = AttributedString("Hello World Goodbye", attributes: AttributeContainer().testInt(1)) - XCTAssertEqual(expected, attrStr) + #expect(expected == attrStr) } - func testUnicodeScalars_replaceSubrange() { + @Test func testUnicodeScalars_replaceSubrange() { var attrStr = AttributedString("La Cafe\u{301}", attributes: AttributeContainer().testInt(1)) let unicode = attrStr.unicodeScalars attrStr.unicodeScalars.replaceSubrange(unicode.index(unicode.startIndex, offsetBy: 3) ..< unicode.index(unicode.startIndex, offsetBy: 7), with: "Ole".unicodeScalars) let expected = AttributedString("La Ole\u{301}", attributes: AttributeContainer().testInt(1)) - XCTAssertEqual(expected, attrStr) + #expect(expected == attrStr) } - func testUnicodeScalarsMutation_append() { + @Test func testUnicodeScalarsMutation_append() { var attrStr = AttributedString("Cafe", attributes: AttributeContainer().testInt(1)) attrStr.unicodeScalars.append("\u{301}") let expected = AttributedString("Cafe\u{301}", attributes: AttributeContainer().testInt(1)) - XCTAssertEqual(expected, attrStr) + #expect(expected == attrStr) } - func testSubCharacterAttributeSetting() { + @Test func testSubCharacterAttributeSetting() { var attrStr = AttributedString("Cafe\u{301}", attributes: AttributeContainer().testInt(1)) let cafRange = attrStr.characters.startIndex ..< attrStr.characters.index(attrStr.characters.startIndex, offsetBy: 3) let eRange = cafRange.upperBound ..< attrStr.unicodeScalars.index(after: cafRange.upperBound) @@ -706,10 +813,10 @@ final class TestAttributedString: XCTestCase { var expected = AttributedString("Caf", attributes: AttributeContainer().testInt(1).testDouble(1.5)) expected += AttributedString("e", attributes: AttributeContainer().testInt(1).testDouble(2.5)) expected += AttributedString("\u{301}", attributes: AttributeContainer().testInt(1).testDouble(3.5)) - XCTAssertEqual(expected, attrStr) + #expect(expected == attrStr) } - func testReplaceSubrange_rangeExpression() { + @Test func testReplaceSubrange_rangeExpression() { var attrStr = AttributedString("Hello World", attributes: AttributeContainer().testInt(1)) // Test with PartialRange, which conforms to RangeExpression but is not a Range @@ -718,20 +825,20 @@ final class TestAttributedString: XCTestCase { var expected = AttributedString("Goodbye") expected += AttributedString(" World", attributes: AttributeContainer().testInt(1)) - XCTAssertEqual(attrStr, expected) + #expect(attrStr == expected) } - func testSettingAttributes() { + @Test func testSettingAttributes() { var attrStr = AttributedString("Hello World", attributes: .init().testInt(1)) attrStr += AttributedString(". My name is Foundation!", attributes: .init().testBool(true)) let result = attrStr.settingAttributes(.init().testBool(false)) let expected = AttributedString("Hello World. My name is Foundation!", attributes: .init().testBool(false)) - XCTAssertEqual(result, expected) + #expect(result == expected) } - func testAddAttributedString() { + @Test func testAddAttributedString() { let attrStr = AttributedString("Hello ", attributes: .init().testInt(1)) let attrStr2 = AttributedString("World", attributes: .init().testInt(2)) let original = attrStr @@ -740,22 +847,22 @@ final class TestAttributedString: XCTestCase { var concat = AttributedString("Hello ", attributes: .init().testInt(1)) concat += AttributedString("World", attributes: .init().testInt(2)) let combine = attrStr + attrStr2 - XCTAssertEqual(attrStr, original) - XCTAssertEqual(attrStr2, original2) - XCTAssertEqual(String(combine.characters), "Hello World") - XCTAssertEqual(String(concat.characters), "Hello World") - + #expect(attrStr == original) + #expect(attrStr2 == original2) + #expect(String(combine.characters) == "Hello World") + #expect(String(concat.characters) == "Hello World") + let testInts = [1, 2] for str in [concat, combine] { var i = 0 for run in str.runs { - XCTAssertEqual(run.testInt, testInts[i]) + #expect(run.testInt == testInts[i]) i += 1 } } } - func testReplaceSubrangeWithSubstrings() { + @Test func testReplaceSubrangeWithSubstrings() { let baseString = AttributedString("A", attributes: .init().testInt(1)) + AttributedString("B", attributes: .init().testInt(2)) + AttributedString("C", attributes: .init().testInt(3)) @@ -773,7 +880,7 @@ final class TestAttributedString: XCTestCase { + AttributedString("D", attributes: .init().testInt(4)) + AttributedString("Z", attributes: .init().testString("foo")) - XCTAssertEqual(targetString, expected) + #expect(targetString == expected) targetString = AttributedString("XYZ", attributes: .init().testString("foo")) targetString.append(substring) @@ -782,20 +889,20 @@ final class TestAttributedString: XCTestCase { + AttributedString("C", attributes: .init().testInt(3)) + AttributedString("D", attributes: .init().testInt(4)) - XCTAssertEqual(targetString, expected) + #expect(targetString == expected) } func assertStringIsCoalesced(_ str: AttributedString) { var prev: AttributedString.Runs.Run? for run in str.runs { if let prev = prev { - XCTAssertNotEqual(prev.attributes, run.attributes) + #expect(prev.attributes != run.attributes) } prev = run } } - func testCoalescing() { + @Test func testCoalescing() { let str = AttributedString("Hello", attributes: .init().testInt(1)) let appendSame = str + AttributedString("World", attributes: .init().testInt(1)) let appendDifferent = str + AttributedString("World", attributes: .init().testInt(2)) @@ -803,67 +910,67 @@ final class TestAttributedString: XCTestCase { assertStringIsCoalesced(str) assertStringIsCoalesced(appendSame) assertStringIsCoalesced(appendDifferent) - XCTAssertEqual(appendSame.runs.count, 1) - XCTAssertEqual(appendDifferent.runs.count, 2) - + #expect(appendSame.runs.count == 1) + #expect(appendDifferent.runs.count == 2) + // Ensure replacing whole string keeps coalesced var str2 = str str2.replaceSubrange(str2.startIndex ..< str2.endIndex, with: AttributedString("Hello", attributes: .init().testInt(2))) assertStringIsCoalesced(str2) - XCTAssertEqual(str2.runs.count, 1) - + #expect(str2.runs.count == 1) + // Ensure replacing subranges splits runs and doesn't coalesce when not equal var str3 = str str3.replaceSubrange(str3.characters.index(after: str3.startIndex) ..< str3.endIndex, with: AttributedString("ello", attributes: .init().testInt(2))) assertStringIsCoalesced(str3) - XCTAssertEqual(str3.runs.count, 2) - + #expect(str3.runs.count == 2) + var str4 = str str4.replaceSubrange(str4.startIndex ..< str4.characters.index(before: str4.endIndex), with: AttributedString("Hell", attributes: .init().testInt(2))) assertStringIsCoalesced(str4) - XCTAssertEqual(str4.runs.count, 2) - + #expect(str4.runs.count == 2) + var str5 = str str5.replaceSubrange(str5.characters.index(after: str5.startIndex) ..< str5.characters.index(before: str4.endIndex), with: AttributedString("ell", attributes: .init().testInt(2))) assertStringIsCoalesced(str5) - XCTAssertEqual(str5.runs.count, 3) - + #expect(str5.runs.count == 3) + // Ensure changing attributes back to match bordering runs coalesces with edge of subrange var str6 = str5 str6.replaceSubrange(str6.characters.index(after: str6.startIndex) ..< str6.endIndex, with: AttributedString("ello", attributes: .init().testInt(1))) assertStringIsCoalesced(str6) - XCTAssertEqual(str6.runs.count, 1) - + #expect(str6.runs.count == 1) + var str7 = str5 str7.replaceSubrange(str7.startIndex ..< str7.characters.index(before: str7.endIndex), with: AttributedString("Hell", attributes: .init().testInt(1))) assertStringIsCoalesced(str7) - XCTAssertEqual(str7.runs.count, 1) - + #expect(str7.runs.count == 1) + var str8 = str5 str8.replaceSubrange(str8.characters.index(after: str8.startIndex) ..< str8.characters.index(before: str8.endIndex), with: AttributedString("ell", attributes: .init().testInt(1))) assertStringIsCoalesced(str8) - XCTAssertEqual(str8.runs.count, 1) - + #expect(str8.runs.count == 1) + var str9 = str5 str9.testInt = 1 assertStringIsCoalesced(str9) - XCTAssertEqual(str9.runs.count, 1) - + #expect(str9.runs.count == 1) + var str10 = str5 str10[str10.characters.index(after: str10.startIndex) ..< str10.characters.index(before: str10.endIndex)].testInt = 1 assertStringIsCoalesced(str10) - XCTAssertEqual(str10.runs.count, 1) + #expect(str10.runs.count == 1) } - func testReplaceWithEmptyElements() { + @Test func testReplaceWithEmptyElements() { var str = AttributedString("Hello, world") let range = str.startIndex ..< str.characters.index(str.startIndex, offsetBy: 5) str.characters.replaceSubrange(range, with: []) - XCTAssertEqual(str, AttributedString(", world")) + #expect(str == AttributedString(", world")) } - func testDescription() { + @Test func testDescription() { let string = AttributedString("A", attributes: .init().testInt(1)) + AttributedString("B", attributes: .init().testInt(2)) + AttributedString("C", attributes: .init().testInt(3)) @@ -888,27 +995,27 @@ E { \tTestInt = 5 } """ - XCTAssertEqual(desc, expected) - + #expect(desc == expected) + let runsDesc = String(describing: string.runs) - XCTAssertEqual(runsDesc, expected) + #expect(runsDesc == expected) } - func testContainerDescription() { + @Test func testContainerDescription() { let cont = AttributeContainer().testBool(false).testInt(1).testDouble(2.0).testString("3") let desc = String(describing: cont) // Don't get bitten by any potential changes in the hashing algorithm. - XCTAssertTrue(desc.hasPrefix("{\n")) - XCTAssertTrue(desc.hasSuffix("\n}")) - XCTAssertTrue(desc.contains("\tTestDouble = 2.0\n")) - XCTAssertTrue(desc.contains("\tTestInt = 1\n")) - XCTAssertTrue(desc.contains("\tTestString = 3\n")) - XCTAssertTrue(desc.contains("\tTestBool = false\n")) + #expect(desc.hasPrefix("{\n")) + #expect(desc.hasSuffix("\n}")) + #expect(desc.contains("\tTestDouble = 2.0\n")) + #expect(desc.contains("\tTestInt = 1\n")) + #expect(desc.contains("\tTestString = 3\n")) + #expect(desc.contains("\tTestBool = false\n")) } - func testRunAndSubstringDescription() { + @Test func testRunAndSubstringDescription() { let string = AttributedString("A", attributes: .init().testInt(1)) + AttributedString("B", attributes: .init().testInt(2)) + AttributedString("C", attributes: .init().testInt(3)) @@ -937,134 +1044,158 @@ E { \tTestInt = 5 } """] - XCTAssertEqual(runsDescs, expected) - + #expect(runsDescs == expected) + let subDescs = string.runs.map() { String(describing: string[$0.range]) } - XCTAssertEqual(subDescs, expected) + #expect(subDescs == expected) } - - func testReplacingAttributes() { + + @Test func testReplacingAttributes() { var str = AttributedString("Hello", attributes: .init().testInt(2)) str += AttributedString("World", attributes: .init().testString("Test")) - + var result = str.replacingAttributes(.init().testInt(2).testString("NotTest"), with: .init().testBool(false)) - XCTAssertEqual(result, str) - + #expect(result == str) + result = str.replacingAttributes(.init().testInt(2), with: .init().testBool(false)) var expected = AttributedString("Hello", attributes: .init().testBool(false)) expected += AttributedString("World", attributes: .init().testString("Test")) - XCTAssertEqual(result, expected) + #expect(result == expected) } - func testScopedAttributeContainer() { + @Test func testScopedAttributeContainer() { var str = AttributedString("Hello, world") - XCTAssertNil(str.test.testInt) - XCTAssertNil(str.testInt) + #expect(str.test.testInt == nil) + #expect(str.testInt == nil) str.test.testInt = 2 - XCTAssertEqual(str.test.testInt, 2) - XCTAssertEqual(str.testInt, 2) + #expect(str.test.testInt == 2) + #expect(str.testInt == 2) str.test.testInt = nil - XCTAssertNil(str.test.testInt) - XCTAssertNil(str.testInt) - + #expect(str.test.testInt == nil) + #expect(str.testInt == nil) + let range = str.startIndex ..< str.index(str.startIndex, offsetByCharacters: 5) let otherRange = range.upperBound ..< str.endIndex str[range].test.testBool = true - XCTAssertEqual(str[range].test.testBool, true) - XCTAssertEqual(str[range].testBool, true) - XCTAssertNil(str.test.testBool) - XCTAssertNil(str.testBool) + #expect(str[range].test.testBool == true) + #expect(str[range].testBool == true) + #expect(str.test.testBool == nil) + #expect(str.testBool == nil) str[range].test.testBool = nil - XCTAssertNil(str[range].test.testBool) - XCTAssertNil(str[range].testBool) - XCTAssertNil(str.test.testBool) - XCTAssertNil(str.testBool) - + #expect(str[range].test.testBool == nil) + #expect(str[range].testBool == nil) + #expect(str.test.testBool == nil) + #expect(str.testBool == nil) + str.test.testBool = true str[range].test.testBool = nil - XCTAssertNil(str[range].test.testBool) - XCTAssertNil(str[range].testBool) - XCTAssertNil(str.test.testBool) - XCTAssertNil(str.testBool) - XCTAssertEqual(str[otherRange].test.testBool, true) - XCTAssertEqual(str[otherRange].testBool, true) + #expect(str[range].test.testBool == nil) + #expect(str[range].testBool == nil) + #expect(str.test.testBool == nil) + #expect(str.testBool == nil) + #expect(str[otherRange].test.testBool == true) + #expect(str[otherRange].testBool == true) } - func testMergeAttributes() { + @Test func testMergeAttributes() { let originalAttributes = AttributeContainer.testInt(2).testBool(true) let newAttributes = AttributeContainer.testString("foo") let overlappingAttributes = AttributeContainer.testInt(3).testDouble(4.3) let str = AttributedString("Hello, world", attributes: originalAttributes) - XCTAssertEqual(str.mergingAttributes(newAttributes, mergePolicy: .keepNew), AttributedString("Hello, world", attributes: newAttributes.testInt(2).testBool(true))) - XCTAssertEqual(str.mergingAttributes(newAttributes, mergePolicy: .keepCurrent), AttributedString("Hello, world", attributes: newAttributes.testInt(2).testBool(true))) - XCTAssertEqual(str.mergingAttributes(overlappingAttributes, mergePolicy: .keepNew), AttributedString("Hello, world", attributes: overlappingAttributes.testBool(true))) - XCTAssertEqual(str.mergingAttributes(overlappingAttributes, mergePolicy: .keepCurrent), AttributedString("Hello, world", attributes: originalAttributes.testDouble(4.3))) - } - - func testMergeAttributeContainers() { + #expect( + str.mergingAttributes(newAttributes, mergePolicy: .keepNew) == + AttributedString("Hello, world", attributes: newAttributes.testInt(2).testBool(true)) + ) + #expect( + str.mergingAttributes(newAttributes, mergePolicy: .keepCurrent) == + AttributedString("Hello, world", attributes: newAttributes.testInt(2).testBool(true)) + ) + #expect( + str.mergingAttributes(overlappingAttributes, mergePolicy: .keepNew) == + AttributedString("Hello, world", attributes: overlappingAttributes.testBool(true)) + ) + #expect( + str.mergingAttributes(overlappingAttributes, mergePolicy: .keepCurrent) == + AttributedString("Hello, world", attributes: originalAttributes.testDouble(4.3)) + ) + } + + @Test func testMergeAttributeContainers() { let originalAttributes = AttributeContainer.testInt(2).testBool(true) let newAttributes = AttributeContainer.testString("foo") let overlappingAttributes = AttributeContainer.testInt(3).testDouble(4.3) - XCTAssertEqual(originalAttributes.merging(newAttributes, mergePolicy: .keepNew), newAttributes.testInt(2).testBool(true)) - XCTAssertEqual(originalAttributes.merging(newAttributes, mergePolicy: .keepCurrent), newAttributes.testInt(2).testBool(true)) - XCTAssertEqual(originalAttributes.merging(overlappingAttributes, mergePolicy: .keepNew), overlappingAttributes.testBool(true)) - XCTAssertEqual(originalAttributes.merging(overlappingAttributes, mergePolicy: .keepCurrent), originalAttributes.testDouble(4.3)) - } - - func testChangingSingleCharacterUTF8Length() { + #expect( + originalAttributes.merging(newAttributes, mergePolicy: .keepNew) == + newAttributes.testInt(2).testBool(true) + ) + #expect( + originalAttributes.merging(newAttributes, mergePolicy: .keepCurrent) == + newAttributes.testInt(2).testBool(true) + ) + #expect( + originalAttributes.merging(overlappingAttributes, mergePolicy: .keepNew) == + overlappingAttributes.testBool(true) + ) + #expect( + originalAttributes.merging(overlappingAttributes, mergePolicy: .keepCurrent) == + originalAttributes.testDouble(4.3) + ) + } + + @Test func testChangingSingleCharacterUTF8Length() { var attrstr = AttributedString("\u{1F3BA}\u{1F3BA}") // UTF-8 Length of 8 attrstr.characters[attrstr.startIndex] = "A" // Changes UTF-8 Length to 5 - XCTAssertEqual(attrstr.runs.count, 1) + #expect(attrstr.runs.count == 1) let runRange = attrstr.runs.first!.range let substring = String(attrstr[runRange].characters) - XCTAssertEqual(substring, "A\u{1F3BA}") + #expect(substring == "A\u{1F3BA}") } // MARK: - Substring Tests - func testSubstringBase() { + @Test func testSubstringBase() { let str = AttributedString("Hello World", attributes: .init().testInt(1)) var substr = str[str.startIndex ..< str.characters.index(str.startIndex, offsetBy: 5)] - XCTAssertEqual(substr.base, str) + #expect(substr.base == str) substr.testInt = 3 - XCTAssertNotEqual(substr.base, str) - + #expect(substr.base != str) + var str2 = AttributedString("Hello World", attributes: .init().testInt(1)) let range = str2.startIndex ..< str2.characters.index(str2.startIndex, offsetBy: 5) - XCTAssertEqual(str2[range].base, str2) + #expect(str2[range].base == str2) str2[range].testInt = 3 - XCTAssertEqual(str2[range].base, str2) + #expect(str2[range].base == str2) } - func testSubstringGetAttribute() { + @Test func testSubstringGetAttribute() { let str = AttributedString("Hello World", attributes: .init().testInt(1)) let range = str.startIndex ..< str.characters.index(str.startIndex, offsetBy: 5) - XCTAssertEqual(str[range].testInt, 1) - XCTAssertNil(str[range].testString) - + #expect(str[range].testInt == 1) + #expect(str[range].testString == nil) + var str2 = AttributedString("Hel", attributes: .init().testInt(1)) str2 += AttributedString("lo World", attributes: .init().testInt(2).testBool(true)) let range2 = str2.startIndex ..< str2.characters.index(str2.startIndex, offsetBy: 5) - XCTAssertNil(str2[range2].testInt) - XCTAssertNil(str2[range2].testBool) + #expect(str2[range2].testInt == nil) + #expect(str2[range2].testBool == nil) } - func testSubstringDescription() { + @Test func testSubstringDescription() { var str = AttributedString("Hello", attributes: .init().testInt(2)) str += " " str += AttributedString("World", attributes: .init().testInt(3)) for run in str.runs { let desc = str[run.range].description - XCTAssertFalse(desc.isEmpty) + #expect(desc.isEmpty == false) } } - func testSubstringReplaceAttributes() { + @Test func testSubstringReplaceAttributes() { var str = AttributedString("Hello", attributes: .init().testInt(2).testString("Foundation")) str += " " str += AttributedString("World", attributes: .init().testInt(3)) @@ -1076,23 +1207,23 @@ E { expected += AttributedString("llo", attributes: .init().testBool(true)) expected += " " expected += AttributedString("World", attributes: .init().testInt(3)) - XCTAssertEqual(str, expected) + #expect(str == expected) } - func testSubstringEquality() { + @Test func testSubstringEquality() { let str = AttributedString("") let range = str.startIndex ..< str.endIndex - XCTAssertEqual(str[range], str[range]) - + #expect(str[range] == str[range]) + let str2 = "A" + AttributedString("A", attributes: .init().testInt(2)) let substringA = str2[str2.startIndex ..< str2.index(afterCharacter: str2.startIndex)] let substringB = str2[str2.index(afterCharacter: str2.startIndex) ..< str2.endIndex] - XCTAssertNotEqual(substringA, substringB) - XCTAssertEqual(substringA, substringA) - XCTAssertEqual(substringB, substringB) + #expect(substringA != substringB) + #expect(substringA == substringA) + #expect(substringB == substringB) } - func testInitializationFromSubstring() { + @Test func testInitializationFromSubstring() { var attrStr = AttributedString("yolo^+1 result<:s>^", attributes: AttributeContainer.testInt(2).testString("Hello")) attrStr.replaceSubrange(attrStr.range(of: "<:s>")!, with: AttributedString("")) attrStr[attrStr.range(of: "1 result")!].testInt = 3 @@ -1100,9 +1231,9 @@ E { let range = attrStr.range(of: "+1 result")! let subFinal = attrStr[range] let attrFinal = AttributedString(subFinal) - XCTAssertTrue(attrFinal.characters.elementsEqual(subFinal.characters)) - XCTAssertEqual(attrFinal.runs, subFinal.runs) - + #expect(attrFinal.characters.elementsEqual(subFinal.characters)) + #expect(attrFinal.runs == subFinal.runs) + var attrStr2 = AttributedString("xxxxxxxx", attributes: .init().testInt(1)) attrStr2 += AttributedString("y", attributes: .init().testInt(2)) attrStr2 += AttributedString("zzzzzzzz", attributes: .init().testInt(3)) @@ -1110,7 +1241,7 @@ E { let subrange = attrStr2.index(attrStr2.startIndex, offsetByCharacters: 5) ..< attrStr2.endIndex let substring2 = attrStr2[subrange] let recreated = AttributedString(substring2) - XCTAssertEqual(recreated.runs.count, 3) + #expect(recreated.runs.count == 3) } #if FOUNDATION_FRAMEWORK @@ -1122,7 +1253,7 @@ E { var attributedString = AttributedString() } - func testJSONEncoding() throws { + @Test func testJSONEncoding() throws { let encoder = JSONEncoder() var attrStr = AttributedString("Hello", attributes: AttributeContainer().testBool(true).testString("blue").testInt(1)) attrStr += AttributedString(" World", attributes: AttributeContainer().testInt(2).testDouble(3.0).testString("http://www.apple.com")) @@ -1132,10 +1263,10 @@ E { let decoder = JSONDecoder() let decoded = try decoder.decode(CodableType.self, from: json) - XCTAssertEqual(decoded.attributedString, attrStr) + #expect(decoded.attributedString == attrStr) } - - func testDecodingThenConvertingToNSAttributedString() throws { + + @Test func testDecodingThenConvertingToNSAttributedString() throws { let encoder = JSONEncoder() var attrStr = AttributedString("Hello", attributes: AttributeContainer().testBool(true)) attrStr += AttributedString(" World", attributes: AttributeContainer().testInt(2)) @@ -1146,10 +1277,10 @@ E { let decoded = try decoder.decode(CodableType.self, from: json) let decodedns = try NSAttributedString(decoded.attributedString, including: AttributeScopes.TestAttributes.self) let ns = try NSAttributedString(attrStr, including: AttributeScopes.TestAttributes.self) - XCTAssertEqual(ns, decodedns) + #expect(ns == decodedns) } - func testCustomAttributeCoding() throws { + @Test func testCustomAttributeCoding() throws { struct MyAttributes : AttributeScope { var customCodable : AttributeScopes.TestAttributes.CustomCodableAttribute } @@ -1168,10 +1299,10 @@ E { let decoder = JSONDecoder() let decoded = try decoder.decode(CodableType.self, from: json) - XCTAssertEqual(decoded.attributedString, attrStr) + #expect(decoded.attributedString == attrStr) } - func testCustomCodableTypeWithCodableAttributedString() throws { + @Test func testCustomCodableTypeWithCodableAttributedString() throws { struct MyType : Codable, Equatable { var other: NonCodableType var str: AttributedString @@ -1207,10 +1338,10 @@ E { let data = try encoder.encode(type) let decoder = JSONDecoder() let decoded = try decoder.decode(MyType.self, from: data) - XCTAssertEqual(type, decoded) + #expect(type == decoded) } - func testCodingErrorsPropagateUpToCallSite() { + @Test func testCodingErrorsPropagateUpToCallSite() { enum CustomAttribute : CodableAttributedStringKey { typealias Value = String static var name = "CustomAttribute" @@ -1235,12 +1366,12 @@ E { var str = AttributedString("Hello, world") str[CustomAttribute.self] = "test" let encoder = JSONEncoder() - XCTAssertThrowsError(try encoder.encode(Obj(str: str)), "Attribute encoding error did not throw at call site") { err in - XCTAssert(err is TestError, "Encoding did not throw the proper error") + #expect(throws: TestError.self, "Attribute encoding error did not throw at call site") { + try encoder.encode(Obj(str: str)) } } - func testEncodeWithPartiallyCodableScope() throws { + @Test func testEncodeWithPartiallyCodableScope() throws { enum NonCodableAttribute : AttributedStringKey { typealias Value = Int static var name = "NonCodableAttributes" @@ -1264,10 +1395,10 @@ E { var expected = str expected[NonCodableAttribute.self] = nil - XCTAssertEqual(decoded.str, expected) + #expect(decoded.str == expected) } - func testAutomaticCoding() throws { + @Test func testAutomaticCoding() throws { struct Obj : Codable, Equatable { @CodableConfiguration(from: AttributeScopes.TestAttributes.self) var attrStr = AttributedString() @CodableConfiguration(from: AttributeScopes.TestAttributes.self) var optAttrStr : AttributedString? = nil @@ -1294,11 +1425,10 @@ E { let val = Obj(testValueWithNils: true) let encoder = JSONEncoder() let data = try encoder.encode(val) - print(String(data: data, encoding: .utf8)!) let decoder = JSONDecoder() let decoded = try decoder.decode(Obj.self, from: data) - XCTAssertEqual(decoded, val) + #expect(decoded == val) } // non-nil @@ -1306,17 +1436,14 @@ E { let val = Obj(testValueWithNils: false) let encoder = JSONEncoder() let data = try encoder.encode(val) - print(String(data: data, encoding: .utf8)!) let decoder = JSONDecoder() let decoded = try decoder.decode(Obj.self, from: data) - XCTAssertEqual(decoded, val) + #expect(decoded == val) } - } - - func testManualCoding() throws { + @Test func testManualCoding() throws { struct Obj : Codable, Equatable { var attrStr : AttributedString var optAttrStr : AttributedString? @@ -1366,11 +1493,10 @@ E { let val = Obj(testValueWithNils: true) let encoder = JSONEncoder() let data = try encoder.encode(val) - print(String(data: data, encoding: .utf8)!) let decoder = JSONDecoder() let decoded = try decoder.decode(Obj.self, from: data) - XCTAssertEqual(decoded, val) + #expect(decoded == val) } // non-nil @@ -1378,16 +1504,15 @@ E { let val = Obj(testValueWithNils: false) let encoder = JSONEncoder() let data = try encoder.encode(val) - print(String(data: data, encoding: .utf8)!) let decoder = JSONDecoder() let decoded = try decoder.decode(Obj.self, from: data) - XCTAssertEqual(decoded, val) + #expect(decoded == val) } } - func testDecodingCorruptedData() throws { + @Test func testDecodingCorruptedData() throws { let jsonStrings = [ "{\"attributedString\": 2}", "{\"attributedString\": []}", @@ -1405,16 +1530,16 @@ E { "{\"attributedString\": {\"runs\": [\"\", {}, \"Test\", {}], \"attributeTable\": []}}", "{\"attributedString\": {\"runs\": \"Test\", {}, \"\", {}, \"attributeTable\": []}}", ] - + let decoder = JSONDecoder() for string in jsonStrings { - XCTAssertThrowsError(try decoder.decode(CodableType.self, from: string.data(using: .utf8)!), "Corrupt data did not throw error for json data: \(string)") { err in - XCTAssertTrue(err is DecodingError, "Decoding threw an error that was not a DecodingError") + #expect(throws: DecodingError.self, "Corrupt data did not throw error for json data: \(string)") { + try decoder.decode(CodableType.self, from: string.data(using: .utf8)!) } } } - - func testCodableRawRepresentableAttribute() throws { + + @Test func testCodableRawRepresentableAttribute() throws { struct Attribute : CodableAttributedStringKey { static let name = "MyAttribute" enum Value: String, Codable, Hashable { @@ -1423,40 +1548,39 @@ E { case three = "three" } } - + struct Scope : AttributeScope { let attribute: Attribute } - + struct Object : Codable { @CodableConfiguration(from: Scope.self) var str = AttributedString() } - + var str = AttributedString("Test") str[Attribute.self] = .two let encoder = JSONEncoder() let encoded = try encoder.encode(Object(str: str)) let decoder = JSONDecoder() let decoded = try decoder.decode(Object.self, from: encoded) - XCTAssertEqual(decoded.str[Attribute.self], .two) + #expect(decoded.str[Attribute.self] == .two) } - func testContainerEncoding() throws { + @Test func testContainerEncoding() throws { struct ContainerContainer : Codable { @CodableConfiguration(from: AttributeScopes.TestAttributes.self) var container = AttributeContainer() } let obj = ContainerContainer(container: AttributeContainer().testInt(1).testBool(true)) let encoder = JSONEncoder() let data = try encoder.encode(obj) - print(String(data: data, encoding: .utf8)!) let decoder = JSONDecoder() let decoded = try decoder.decode(ContainerContainer.self, from: data) - XCTAssertEqual(obj.container, decoded.container) + #expect(obj.container == decoded.container) } - func testDefaultAttributesCoding() throws { + @Test func testDefaultAttributesCoding() throws { struct DefaultContainer : Codable, Equatable { var str : AttributedString } @@ -1466,25 +1590,25 @@ E { let encoded = try encoder.encode(cont) let decoder = JSONDecoder() let decoded = try decoder.decode(DefaultContainer.self, from: encoded) - XCTAssertEqual(cont, decoded) + #expect(cont == decoded) } - func testDecodingMultibyteCharacters() throws { + @Test func testDecodingMultibyteCharacters() throws { let json = "{\"str\": [\"🎺ABC\", {\"TestInt\": 2}]}" struct Object : Codable { @CodableConfiguration(from: AttributeScopes.TestAttributes.self) var str: AttributedString = AttributedString() } let decoder = JSONDecoder() let str = try decoder.decode(Object.self, from: json.data(using: .utf8)!).str - XCTAssertEqual(str.runs.count, 1) - XCTAssertEqual(str.testInt, 2) + #expect(str.runs.count == 1) + #expect(str.testInt == 2) let idx = str.index(beforeCharacter: str.endIndex) - XCTAssertEqual(str.runs[idx].testInt, 2) + #expect(str.runs[idx].testInt == 2) } // MARK: - Conversion Tests - - func testConversionToObjC() throws { + + @Test func testConversionToObjC() throws { var ourString = AttributedString("Hello", attributes: AttributeContainer().testInt(2)) ourString += AttributedString(" ") ourString += AttributedString("World", attributes: AttributeContainer().testString("Courier")) @@ -1492,10 +1616,10 @@ E { let theirString = NSMutableAttributedString(string: "Hello World") theirString.addAttributes([.testInt: NSNumber(value: 2)], range: NSMakeRange(0, 5)) theirString.addAttributes([.testString: "Courier"], range: NSMakeRange(6, 5)) - XCTAssertEqual(theirString, ourObjCString) + #expect(theirString == ourObjCString) } - - func testConversionFromObjC() throws { + + @Test func testConversionFromObjC() throws { let nsString = NSMutableAttributedString(string: "Hello!") let rangeA = NSMakeRange(0, 3) let rangeB = NSMakeRange(3, 3) @@ -1505,34 +1629,34 @@ E { var string = AttributedString("Hel") string.testString = "Courier" string += AttributedString("lo!", attributes: AttributeContainer().testBool(true)) - XCTAssertEqual(string, convertedString) + #expect(string == convertedString) } - func testRoundTripConversion_boxed() throws { + @Test func testRoundTripConversion_boxed() throws { struct MyCustomType : Hashable { var num: Int var str: String } - + enum MyCustomAttribute : AttributedStringKey { typealias Value = MyCustomType static let name = "MyCustomAttribute" } - + struct MyCustomScope : AttributeScope { let attr : MyCustomAttribute } - + let customVal = MyCustomType(num: 2, str: "test") var attrString = AttributedString("Hello world") attrString[MyCustomAttribute.self] = customVal let nsString = try NSAttributedString(attrString, including: MyCustomScope.self) let converted = try AttributedString(nsString, including: MyCustomScope.self) - - XCTAssertEqual(converted[MyCustomAttribute.self], customVal) + + #expect(converted[MyCustomAttribute.self] == customVal) } - func testRoundTripConversion_customConversion() throws { + @Test func testRoundTripConversion_customConversion() throws { struct MyCustomType : Hashable { } enum MyCustomAttribute : ObjectiveCConvertibleAttributedStringKey { @@ -1552,13 +1676,13 @@ E { attrString[MyCustomAttribute.self] = customVal let nsString = try NSAttributedString(attrString, including: MyCustomScope.self) - XCTAssertTrue(nsString.attribute(.init(MyCustomAttribute.name), at: 0, effectiveRange: nil) is NSUUID) + #expect(nsString.attribute(.init(MyCustomAttribute.name), at: 0, effectiveRange: nil) is NSUUID) let converted = try AttributedString(nsString, including: MyCustomScope.self) - XCTAssertEqual(converted[MyCustomAttribute.self], customVal) + #expect(converted[MyCustomAttribute.self] == customVal) } - func testIncompleteConversionFromObjC() throws { + @Test func testIncompleteConversionFromObjC() throws { struct TestStringAttributeOnly : AttributeScope { var testString: AttributeScopes.TestAttributes.TestStringAttribute // Missing TestBoolAttribute } @@ -1572,10 +1696,10 @@ E { var expected = AttributedString("Hel", attributes: AttributeContainer().testString("Courier")) expected += AttributedString("lo!") - XCTAssertEqual(converted, expected) + #expect(converted == expected) } - func testIncompleteConversionToObjC() throws { + @Test func testIncompleteConversionToObjC() throws { struct TestStringAttributeOnly : AttributeScope { var testString: AttributeScopes.TestAttributes.TestStringAttribute // Missing TestBoolAttribute } @@ -1585,10 +1709,10 @@ E { let converted = try NSAttributedString(attrStr, including: TestStringAttributeOnly.self) let attrs = converted.attributes(at: 0, effectiveRange: nil) - XCTAssertFalse(attrs.keys.contains(.testBool)) + #expect(attrs.keys.contains(.testBool) == false) } - func testConversionNestedScope() throws { + @Test func testConversionNestedScope() throws { struct SuperScope : AttributeScope { var subscope : SubScope var testString: AttributeScopes.TestAttributes.TestStringAttribute @@ -1607,10 +1731,10 @@ E { var expected = AttributedString("Hel", attributes: AttributeContainer().testString("Courier")) expected += AttributedString("lo!", attributes: AttributeContainer().testBool(true)) - XCTAssertEqual(converted, expected) + #expect(converted == expected) } - func testConversionAttributeContainers() throws { + @Test func testConversionAttributeContainers() throws { let container = AttributeContainer.testInt(2).testDouble(3.1).testString("Hello") let dictionary = try Dictionary(container, including: \.test) @@ -1619,19 +1743,21 @@ E { .testDouble: 3.1, .testString: "Hello" ] - XCTAssertEqual(dictionary.keys, expected.keys) - XCTAssertEqual(dictionary[.testInt] as! Int, expected[.testInt] as! Int) - XCTAssertEqual(dictionary[.testDouble] as! Double, expected[.testDouble] as! Double) - XCTAssertEqual(dictionary[.testString] as! String, expected[.testString] as! String) - + #expect(dictionary.keys == expected.keys) + #expect(dictionary[.testInt] as! Int == expected[.testInt] as! Int) + #expect(dictionary[.testDouble] as! Double == expected[.testDouble] as! Double) + #expect(dictionary[.testString] as! String == expected[.testString] as! String) + let container2 = try AttributeContainer(dictionary, including: \.test) - XCTAssertEqual(container, container2) + #expect(container == container2) } - func testConversionFromInvalidObjectiveCValueTypes() throws { + @Test func testConversionFromInvalidObjectiveCValueTypes() throws { let nsStr = NSAttributedString(string: "Hello", attributes: [.testInt : "I am not an Int"]) - XCTAssertThrowsError(try AttributedString(nsStr, including: AttributeScopes.TestAttributes.self)) - + #expect(throws: (any Error).self) { + try AttributedString(nsStr, including: AttributeScopes.TestAttributes.self) + } + struct ConvertibleAttribute: ObjectiveCConvertibleAttributedStringKey { struct Value : Hashable { var subValue: String @@ -1652,10 +1778,12 @@ E { } let nsStr2 = NSAttributedString(string: "Hello", attributes: [NSAttributedString.Key(ConvertibleAttribute.name) : 12345]) - XCTAssertThrowsError(try AttributedString(nsStr2, including: Scope.self)) + #expect(throws: (any Error).self) { + try AttributedString(nsStr2, including: Scope.self) + } } - func testConversionToUTF16() throws { + @Test func testConversionToUTF16() throws { // Ensure that we're correctly using UTF16 offsets with NSAS and UTF8 offsets with AS without mixing the two let multiByteCharacters = ["\u{2029}", "\u{1D11E}", "\u{1D122}", "\u{1F91A}\u{1F3FB}"] @@ -1664,29 +1792,29 @@ E { let nsStr = NSAttributedString(string: str, attributes: [.testInt: 2]) let convertedAttrStr = try AttributedString(nsStr, including: AttributeScopes.TestAttributes.self) - XCTAssertEqual(str.utf8.count, convertedAttrStr._guts.runs.first!.length) - XCTAssertEqual(attrStr, convertedAttrStr) - + #expect(str.utf8.count == convertedAttrStr._guts.runs.first!.length) + #expect(attrStr == convertedAttrStr) + let convertedNSStr = try NSAttributedString(attrStr, including: AttributeScopes.TestAttributes.self) - XCTAssertEqual(nsStr, convertedNSStr) + #expect(nsStr == convertedNSStr) } } - func testConversionWithoutScope() throws { + @Test func testConversionWithoutScope() throws { // Ensure simple conversion works (no errors when loading AppKit/UIKit/SwiftUI) let attrStr = AttributedString() let nsStr = NSAttributedString(attrStr) - XCTAssertEqual(nsStr, NSAttributedString()) + #expect(nsStr == NSAttributedString()) let attrStrReverse = AttributedString(nsStr) - XCTAssertEqual(attrStrReverse, attrStr) - + #expect(attrStrReverse == attrStr) + // Ensure foundation attributes are converted let attrStr2 = AttributedString("Hello", attributes: .init().link(URL(string: "http://apple.com")!)) let nsStr2 = NSAttributedString(attrStr2) - XCTAssertEqual(nsStr2, NSAttributedString(string: "Hello", attributes: [.link : URL(string: "http://apple.com")! as NSURL])) + #expect(nsStr2 == NSAttributedString(string: "Hello", attributes: [.link : URL(string: "http://apple.com")! as NSURL])) let attrStr2Reverse = AttributedString(nsStr2) - XCTAssertEqual(attrStr2Reverse, attrStr2) - + #expect(attrStr2Reverse == attrStr2) + // Ensure attributes that throw are dropped enum Attribute : ObjectiveCConvertibleAttributedStringKey { static var name = "TestAttribute" @@ -1712,13 +1840,12 @@ E { container[Attribute.self] = 3 let str = AttributedString("Hello", attributes: container) let result = try? NSAttributedString(str, attributeTable: Scope.attributeKeyTypes(), options: .dropThrowingAttributes) // The same call that the no-scope initializer will make - XCTAssertEqual(result, NSAttributedString(string: "Hello", attributes: [NSAttributedString.Key("TestInt") : 2])) + #expect(result == NSAttributedString(string: "Hello", attributes: [NSAttributedString.Key("TestInt") : 2])) } - + + @Test(.disabled(if: !canImportAccessibility)) func testConversionWithoutScope_Accessibility() throws { -#if !canImport(Accessibility) - throw XCTSkip("Unable to import the Accessibility framework") -#else +#if canImport(Accessibility) let attributedString = AttributedString("Hello", attributes: .init().accessibilityTextCustom(["ABC"])) let nsAttributedString = NSAttributedString(attributedString) #if os(macOS) @@ -1726,72 +1853,69 @@ E { #else let attribute = NSAttributedString.Key.accessibilityTextCustom #endif - XCTAssertEqual(nsAttributedString, NSAttributedString(string: "Hello", attributes: [attribute : ["ABC"]])) + #expect(nsAttributedString == NSAttributedString(string: "Hello", attributes: [attribute : ["ABC"]])) let attributedStringReverse = AttributedString(nsAttributedString) - XCTAssertEqual(attributedStringReverse, attributedString) -#endif + #expect(attributedStringReverse == attributedString) +#endif // canImport(Accessibility) } - + + @Test(.disabled(if: !canImportAppKit)) func testConversionWithoutScope_AppKit() throws { -#if !canImport(AppKit) - throw XCTSkip("Unable to import the AppKit framework") -#else +#if canImport(AppKit) var container = AttributeContainer() container.appKit.kern = 2.3 let attributedString = AttributedString("Hello", attributes: container) let nsAttributedString = NSAttributedString(attributedString) - XCTAssertEqual(nsAttributedString, NSAttributedString(string: "Hello", attributes: [.kern : CGFloat(2.3)])) + #expect(nsAttributedString == NSAttributedString(string: "Hello", attributes: [.kern : CGFloat(2.3)])) let attributedStringReverse = AttributedString(nsAttributedString) - XCTAssertEqual(attributedStringReverse, attributedString) -#endif + #expect(attributedStringReverse == attributedString) +#endif // canImport(AppKit) } - + + @Test(.disabled(if: !canImportUIKit)) func testConversionWithoutScope_UIKit() throws { -#if !canImport(UIKit) - throw XCTSkip("Unable to import the UIKit framework") -#else +#if canImport(UIKit) var container = AttributeContainer() container.uiKit.kern = 2.3 let attributedString = AttributedString("Hello", attributes: container) let nsAttributedString = NSAttributedString(attributedString) - XCTAssertEqual(nsAttributedString, NSAttributedString(string: "Hello", attributes: [.kern : CGFloat(2.3)])) + #expect(nsAttributedString == NSAttributedString(string: "Hello", attributes: [.kern : CGFloat(2.3)])) let attributedStringReverse = AttributedString(nsAttributedString) - XCTAssertEqual(attributedStringReverse, attributedString) + #expect(attributedStringReverse == attributedString) #endif } - + + @Test(.disabled(if: !canImportSwiftUI)) func testConversionWithoutScope_SwiftUI() throws { -#if !canImport(SwiftUI) - throw XCTSkip("Unable to import the SwiftUI framework") -#else +#if canImport(SwiftUI) var container = AttributeContainer() container.swiftUI.kern = 2.3 let attributedString = AttributedString("Hello", attributes: container) let nsAttributedString = NSAttributedString(attributedString) - XCTAssertEqual(nsAttributedString, NSAttributedString(string: "Hello", attributes: [.init("SwiftUI.Kern") : CGFloat(2.3)])) + #expect(nsAttributedString == NSAttributedString(string: "Hello", attributes: [.init("SwiftUI.Kern") : CGFloat(2.3)])) let attributedStringReverse = AttributedString(nsAttributedString) - XCTAssertEqual(attributedStringReverse, attributedString) + #expect(attributedStringReverse == attributedString) #endif } - - func testConversionIncludingOnly() throws { + + @Test func testConversionIncludingOnly() throws { let str = AttributedString("Hello, world", attributes: .init().testInt(2).link(URL(string: "http://apple.com")!)) let nsStr = try NSAttributedString(str, includingOnly: \.test) - XCTAssertEqual(nsStr, NSAttributedString(string: "Hello, world", attributes: [.testInt: 2])) + #expect(nsStr == NSAttributedString(string: "Hello, world", attributes: [.testInt: 2])) } - func testConversionCoalescing() throws { + @Test func testConversionCoalescing() throws { let nsStr = NSMutableAttributedString("Hello, world") nsStr.setAttributes([.link : NSURL(string: "http://apple.com")!, .testInt : NSNumber(integerLiteral: 2)], range: NSRange(location: 0, length: 6)) nsStr.setAttributes([.testInt : NSNumber(integerLiteral: 2)], range: NSRange(location: 6, length: 6)) let attrStr = try AttributedString(nsStr, including: \.test) - XCTAssertEqual(attrStr.runs.count, 1) - XCTAssertEqual(attrStr.runs.first!.range, attrStr.startIndex ..< attrStr.endIndex) - XCTAssertEqual(attrStr.testInt, 2) - XCTAssertNil(attrStr.link) + #expect(attrStr.runs.count == 1) + #expect(attrStr.runs.first!.range == attrStr.startIndex ..< attrStr.endIndex) + #expect(attrStr.testInt == 2) + #expect(attrStr.link == nil) } - func testUnalignedConversion() throws { + @Test func testUnalignedConversion() throws { let tests: [(NSRange, Int)] = [ (NSRange(location: 0, length: 12), 1), (NSRange(location: 5, length: 2), 3), @@ -1807,7 +1931,10 @@ E { let nsAttributedString = NSMutableAttributedString("Test \u{1F3BA} Test") nsAttributedString.addAttribute(.testInt, value: NSNumber(1), range: test.0) let attrStr = try AttributedString(nsAttributedString, including: \.test) - XCTAssertEqual(attrStr.runs.count, test.1, "Replacement of range \(NSStringFromRange(test.0)) caused a run count of \(attrStr.runs.count)") + #expect( + attrStr.runs.count == test.1, + "Replacement of range \(NSStringFromRange(test.0)) caused a run count of \(attrStr.runs.count)" + ) } } @@ -1815,14 +1942,14 @@ E { // MARK: - View Tests - func testCharViewIndexing_backwardsFromEndIndex() { + @Test func testCharViewIndexing_backwardsFromEndIndex() { let testString = AttributedString("abcdefghi") let testChars = testString.characters let testIndex = testChars.index(testChars.endIndex, offsetBy: -1) - XCTAssertEqual(testChars[testIndex], "i") + #expect(testChars[testIndex] == "i") } - func testAttrViewIndexing() { + @Test func testAttrViewIndexing() { var attrStr = AttributedString("A") attrStr += "B" attrStr += "C" @@ -1836,49 +1963,49 @@ E { i += 1 curIdx = attrStrRuns.index(after: curIdx) } - XCTAssertEqual(i, 1) - XCTAssertEqual(attrStrRuns.count, 1) + #expect(i == 1) + #expect(attrStrRuns.count == 1) } - func testUnicodeScalarsViewIndexing() { + @Test func testUnicodeScalarsViewIndexing() { let attrStr = AttributedString("Cafe\u{301}", attributes: AttributeContainer().testInt(1)) let unicode = attrStr.unicodeScalars - XCTAssertEqual(unicode[unicode.index(before: unicode.endIndex)], "\u{301}") - XCTAssertEqual(unicode[unicode.index(unicode.endIndex, offsetBy: -2)], "e") + #expect(unicode[unicode.index(before: unicode.endIndex)] == "\u{301}") + #expect(unicode[unicode.index(unicode.endIndex, offsetBy: -2)] == "e") } - func testCharacterSlicing() { + @Test func testCharacterSlicing() { let a: AttributedString = "\u{1f1fa}\u{1f1f8}" // Regional indicators U & S let i = a.unicodeScalars.index(after: a.startIndex) let b = a.characters[..(nsRange, in: str) - XCTAssertNotNil(strRange) - XCTAssertEqual(strRange, str.unicodeScalars.index(str.startIndex, offsetBy: 8) ..< str.unicodeScalars.index(str.startIndex, offsetBy: 9)) - XCTAssertEqual(str[strRange!], "e") - + #expect(strRange != nil) + #expect(strRange == str.unicodeScalars.index(str.startIndex, offsetBy: 8) ..< str.unicodeScalars.index(str.startIndex, offsetBy: 9)) + #expect(str[strRange!] == "e") + var attrStrRange = Range(nsRange, in: attrStr) - XCTAssertNotNil(attrStrRange) - XCTAssertEqual(attrStrRange, attrStr.unicodeScalars.index(attrStr.startIndex, offsetBy: 8) ..< attrStr.unicodeScalars.index(attrStr.startIndex, offsetBy: 9)) - XCTAssertEqual(AttributedString(attrStr[attrStrRange!]), AttributedString("e")) - + #expect(attrStrRange != nil) + #expect(attrStrRange == attrStr.unicodeScalars.index(attrStr.startIndex, offsetBy: 8) ..< attrStr.unicodeScalars.index(attrStr.startIndex, offsetBy: 9)) + #expect(AttributedString(attrStr[attrStrRange!]) == AttributedString("e")) + attrStrRange = Range(strRange!, in: attrStr) - XCTAssertNotNil(attrStrRange) - XCTAssertEqual(attrStrRange, attrStr.unicodeScalars.index(attrStr.startIndex, offsetBy: 8) ..< attrStr.unicodeScalars.index(attrStr.startIndex, offsetBy: 9)) - XCTAssertEqual(AttributedString(attrStr[attrStrRange!]), AttributedString("e")) - - XCTAssertEqual(NSRange(strRange!, in: str), nsRange) - XCTAssertEqual(NSRange(attrStrRange!, in: attrStr), nsRange) - XCTAssertEqual(Range(attrStrRange!, in: str), strRange!) + #expect(attrStrRange != nil) + #expect(attrStrRange == attrStr.unicodeScalars.index(attrStr.startIndex, offsetBy: 8) ..< attrStr.unicodeScalars.index(attrStr.startIndex, offsetBy: 9)) + #expect(AttributedString(attrStr[attrStrRange!]) == AttributedString("e")) + + #expect(NSRange(strRange!, in: str) == nsRange) + #expect(NSRange(attrStrRange!, in: attrStr) == nsRange) + #expect(Range(attrStrRange!, in: str) == strRange!) } do { @@ -2289,35 +2416,35 @@ E { let nsRange = NSRange(location: 5, length: 3) // The whole first U+1F3BA and the leading surrogate character of the second U+1F3BA let strRange = Range(nsRange, in: str) - XCTAssertNotNil(strRange) - XCTAssertEqual(str[strRange!], "\u{1F3BA}") - + #expect(strRange != nil) + #expect(str[strRange!] == "\u{1F3BA}") + var attrStrRange = Range(nsRange, in: attrStr) - XCTAssertNotNil(attrStrRange) - XCTAssertEqual(AttributedString(attrStr[attrStrRange!]), AttributedString("\u{1F3BA}")) - + #expect(attrStrRange != nil) + #expect(AttributedString(attrStr[attrStrRange!]) == AttributedString("\u{1F3BA}")) + attrStrRange = Range(strRange!, in: attrStr) - XCTAssertNotNil(attrStrRange) - XCTAssertEqual(AttributedString(attrStr[attrStrRange!]), AttributedString("\u{1F3BA}")) - - XCTAssertEqual(NSRange(strRange!, in: str), nsRange) - XCTAssertEqual(NSRange(attrStrRange!, in: attrStr), nsRange) - XCTAssertEqual(Range(attrStrRange!, in: str), strRange!) + #expect(attrStrRange != nil) + #expect(AttributedString(attrStr[attrStrRange!]) == AttributedString("\u{1F3BA}")) + + #expect(NSRange(strRange!, in: str) == nsRange) + #expect(NSRange(attrStrRange!, in: attrStr) == nsRange) + #expect(Range(attrStrRange!, in: str) == strRange!) } } #endif // FOUNDATION_FRAMEWORK - func testOOBRangeConversion() { + @Test func testOOBRangeConversion() { let attrStr = AttributedString("") let str = "Hello" let range = str.index(before: str.endIndex) ..< str.endIndex - XCTAssertNil(Range(range, in: attrStr)) + #expect(Range(range, in: attrStr) == nil) } - + #if FOUNDATION_FRAMEWORK // TODO: Support scope-specific AttributedString initialization in FoundationPreview - func testScopedCopy() { + @Test func testScopedCopy() { var str = AttributedString("A") str += AttributedString("B", attributes: .init().testInt(2)) str += AttributedString("C", attributes: .init().link(URL(string: "http://apple.com")!)) @@ -2327,44 +2454,42 @@ E { let foundation: AttributeScopes.FoundationAttributes let test: AttributeScopes.TestAttributes } - XCTAssertEqual(AttributedString(str, including: FoundationAndTest.self), str) - - struct None : AttributeScope { - - } - XCTAssertEqual(AttributedString(str, including: None.self), AttributedString("ABCD")) - + #expect(AttributedString(str, including: FoundationAndTest.self) == str) + + struct None : AttributeScope { } + #expect(AttributedString(str, including: None.self) == AttributedString("ABCD")) + var expected = AttributedString("AB") expected += AttributedString("CD", attributes: .init().link(URL(string: "http://apple.com")!)) - XCTAssertEqual(AttributedString(str, including: \.foundation), expected) - + #expect(AttributedString(str, including: \.foundation) == expected) + expected = AttributedString("A") expected += AttributedString("B", attributes: .init().testInt(2)) expected += "C" expected += AttributedString("D", attributes: .init().testInt(3)) - XCTAssertEqual(AttributedString(str, including: \.test), expected) - + #expect(AttributedString(str, including: \.test) == expected) + let range = str.index(afterCharacter: str.startIndex) ..< str.index(beforeCharacter: str.endIndex) expected = AttributedString("B", attributes: .init().testInt(2)) + "C" - XCTAssertEqual(AttributedString(str[range], including: \.test), expected) - + #expect(AttributedString(str[range], including: \.test) == expected) + expected = "B" + AttributedString("C", attributes: .init().link(URL(string: "http://apple.com")!)) - XCTAssertEqual(AttributedString(str[range], including: \.foundation), expected) - - XCTAssertEqual(AttributedString(str[range], including: None.self), AttributedString("BC")) + #expect(AttributedString(str[range], including: \.foundation) == expected) + + #expect(AttributedString(str[range], including: None.self) == AttributedString("BC")) } #endif // FOUNDATION_FRAMEWORK - func testAssignDifferentSubstring() { + @Test func testAssignDifferentSubstring() { var attrStr1 = AttributedString("ABCDE") let attrStr2 = AttributedString("XYZ") attrStr1[ attrStr1.range(of: "BCD")! ] = attrStr2[ attrStr2.range(of: "X")! ] - XCTAssertEqual(attrStr1, "AXE") + #expect(attrStr1 == "AXE") } - func testCOWDuringSubstringMutation() { + @Test func testCOWDuringSubstringMutation() { func frobnicate(_ sub: inout AttributedSubstring) { var new = sub new.testInt = 2 @@ -2375,11 +2500,11 @@ E { frobnicate(&attrStr[ attrStr.range(of: "BCD")! ]) let expected = AttributedString("A") + AttributedString("BCD", attributes: .init().testInt(2).testString("Hello")) + AttributedString("E") - XCTAssertEqual(attrStr, expected) + #expect(attrStr == expected) } #if false // This causes an intentional fatalError(), which we can't test for yet, so unfortunately this test can't be enabled. - func testReassignmentDuringMutation() { + @Test func testReassignmentDuringMutation() { func frobnicate(_ sub: inout AttributedSubstring) { let other = AttributedString("XYZ") sub = other[ other.range(of: "X")! ] @@ -2387,19 +2512,19 @@ E { var attrStr = AttributedString("ABCDE") frobnicate(&attrStr[ attrStr.range(of: "BCD")! ]) - XCTAssertEqual(attrStr, "AXE") + #expect(attrStr == "AXE") } #endif - func testAssignDifferentCharacterView() { + @Test func testAssignDifferentCharacterView() { var attrStr1 = AttributedString("ABC", attributes: .init().testInt(1)) + AttributedString("DE", attributes: .init().testInt(3)) let attrStr2 = AttributedString("XYZ", attributes: .init().testInt(2)) attrStr1.characters = attrStr2.characters - XCTAssertEqual(attrStr1, AttributedString("XYZ", attributes: .init().testInt(1))) + #expect(attrStr1 == AttributedString("XYZ", attributes: .init().testInt(1))) } - func testCOWDuringCharactersMutation() { + @Test func testCOWDuringCharactersMutation() { func frobnicate(_ chars: inout AttributedString.CharacterView) { var new = chars new.replaceSubrange(chars.startIndex ..< chars.endIndex, with: "XYZ") @@ -2408,18 +2533,18 @@ E { var attrStr = AttributedString("ABCDE", attributes: .init().testInt(1)) frobnicate(&attrStr.characters) - XCTAssertEqual(attrStr, AttributedString("XYZ", attributes: .init().testInt(1))) + #expect(attrStr == AttributedString("XYZ", attributes: .init().testInt(1))) } - func testAssignDifferentUnicodeScalarView() { + @Test func testAssignDifferentUnicodeScalarView() { var attrStr1 = AttributedString("ABC", attributes: .init().testInt(1)) + AttributedString("DE", attributes: .init().testInt(3)) let attrStr2 = AttributedString("XYZ", attributes: .init().testInt(2)) attrStr1.unicodeScalars = attrStr2.unicodeScalars - XCTAssertEqual(attrStr1, AttributedString("XYZ", attributes: .init().testInt(1))) + #expect(attrStr1 == AttributedString("XYZ", attributes: .init().testInt(1))) } - func testCOWDuringUnicodeScalarsMutation() { + @Test func testCOWDuringUnicodeScalarsMutation() { func frobnicate(_ chars: inout AttributedString.CharacterView) { var new = chars new.replaceSubrange(chars.startIndex ..< chars.endIndex, with: "XYZ") @@ -2428,6 +2553,6 @@ E { var attrStr = AttributedString("ABCDE", attributes: .init().testInt(1)) frobnicate(&attrStr.characters) - XCTAssertEqual(attrStr, AttributedString("XYZ", attributes: .init().testInt(1))) + #expect(attrStr == AttributedString("XYZ", attributes: .init().testInt(1))) } } diff --git a/Tests/FoundationEssentialsTests/BufferViewTests.swift b/Tests/FoundationEssentialsTests/BufferViewTests.swift index 704ee470f..ef13d9f8a 100644 --- a/Tests/FoundationEssentialsTests/BufferViewTests.swift +++ b/Tests/FoundationEssentialsTests/BufferViewTests.swift @@ -10,9 +10,7 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing #if canImport(FoundationEssentials) @testable import FoundationEssentials @@ -22,21 +20,21 @@ import TestSupport @testable import Foundation #endif -final class BufferViewTests: XCTestCase { +struct BufferViewTests { - func testOptionalStorage() { - XCTAssertEqual( - MemoryLayout>.size, MemoryLayout?>.size + @Test func testOptionalStorage() { + #expect( + MemoryLayout>.size == MemoryLayout?>.size ) - XCTAssertEqual( - MemoryLayout>.stride, MemoryLayout?>.stride + #expect( + MemoryLayout>.stride == MemoryLayout?>.stride ) - XCTAssertEqual( - MemoryLayout>.alignment, MemoryLayout?>.alignment + #expect( + MemoryLayout>.alignment == MemoryLayout?>.alignment ) } - func testInitBufferViewOrdinaryElement() { + @Test func testInitBufferViewOrdinaryElement() { let capacity = 4 let s = (0..(start: nil, count: 0) @@ -58,17 +56,17 @@ final class BufferViewTests: XCTestCase { a.withUnsafeBytes { let b = BufferView(unsafeRawBufferPointer: $0)! - XCTAssertEqual(b.count, capacity) + #expect(b.count == capacity) let r = BufferView(unsafeRawBufferPointer: $0)! - XCTAssertEqual(r.count, capacity * MemoryLayout.stride) + #expect(r.count == capacity * MemoryLayout.stride) } let v = UnsafeRawBufferPointer(start: nil, count: 0) _ = BufferView(unsafeRawBufferPointer: v) } - func testIndex() { + @Test func testIndex() { let count = 4 let strings = (1...count).map({ "This String is not BitwiseCopyable (\($0))." }) strings.withUnsafeBufferPointer { @@ -76,12 +74,12 @@ final class BufferViewTests: XCTestCase { let first = buffer.startIndex let second = first.advanced(by: 1) - XCTAssertLessThan(first, second) - XCTAssertEqual(1, first.distance(to: second)) + #expect(first < second) + #expect(1 == first.distance(to: second)) } } - func testIteratorOrdinaryElement() { + @Test func testIteratorOrdinaryElement() { let capacity = 4 let s = (0...stride + offset var a = Array(repeating: UInt8.zero, count: bytes) - XCTAssertLessThan(offset, MemoryLayout.stride) + #expect(offset < MemoryLayout.stride) a.withUnsafeMutableBytes { for i in 0..<$0.count where i % 8 == offset { @@ -110,7 +108,7 @@ final class BufferViewTests: XCTestCase { } let orig = $0.loadUnaligned(as: Int64.self) - XCTAssertNotEqual(orig, 1) + #expect(orig != 1) // BufferView doesn't need to be aligned for accessing `BitwiseCopyable` types. let buffer = BufferView( @@ -121,14 +119,14 @@ final class BufferViewTests: XCTestCase { var iterator = buffer.makeIterator() var buffered = 0 while let value = iterator.next() { - XCTAssertEqual(value, 1) + #expect(value == 1) buffered += 1 } - XCTAssertEqual(buffered, count) + #expect(buffered == count) } } - func testBufferViewSequence() { + @Test func testBufferViewSequence() { let capacity = 4 let a = Array(0...stride let s0 = view.load(as: String.self) - XCTAssertEqual(s0.contains("0"), true) + #expect(s0.contains("0") == true) let i1 = view.startIndex.advanced(by: stride / 2) let s1 = view.load(from: i1, as: String.self) - XCTAssertEqual(s1.contains("1"), true) + #expect(s1.contains("1") == true) let s2 = view.load(fromByteOffset: 2 * stride, as: String.self) - XCTAssertEqual(s2.contains("2"), true) + #expect(s2.contains("2") == true) } } - func testLoadUnaligned() { + @Test func testLoadUnaligned() { let capacity = 64 let a = Array(0..(unsafeRawBufferPointer: $0)! let u0 = view.dropFirst(1).loadUnaligned(as: UInt64.self) - XCTAssertEqual(u0 & 0xff, 2) - XCTAssertEqual(u0.byteSwapped & 0xff, 9) + #expect(u0 & 0xff == 2) + #expect(u0.byteSwapped & 0xff == 9) let i1 = view.startIndex.advanced(by: 3) let u1 = view.loadUnaligned(from: i1, as: UInt64.self) - XCTAssertEqual(u1 & 0xff, 6) + #expect(u1 & 0xff == 6) let u3 = view.loadUnaligned(fromByteOffset: 7, as: UInt32.self) - XCTAssertEqual(u3 & 0xff, 7) + #expect(u3 & 0xff == 7) } } - func testOffsetSubscript() { + @Test func testOffsetSubscript() { let capacity = 4 let a = Array(0.. Data { // 16 MB file, big enough to trigger things like chunking let count = 16_777_216 - + let memory = malloc(count)! let ptr = memory.bindMemory(to: UInt8.self, capacity: count) @@ -55,18 +55,18 @@ class DataIOTests : XCTestCase { buf[j * 1024 + i] = UInt8.random(in: 1..<42) } } - + return Data(bytesNoCopy: ptr, count: count, deallocator: .free) } - + #if FOUNDATION_FRAMEWORK func writeAndVerifyTestData(to url: URL, writeOptions: Data.WritingOptions = [], readOptions: Data.ReadingOptions = []) throws { let data = generateTestData() try data.write(to: url, options: writeOptions) let readData = try Data(contentsOf: url, options: readOptions) - XCTAssertEqual(data, readData) + #expect(data == readData) } - + func cleanup(at url: URL) { do { try FileManager.default.removeItem(at: url) @@ -79,7 +79,7 @@ class DataIOTests : XCTestCase { let data = generateTestData() try data.write(to: path, options: writeOptions) let readData = try Data(contentsOf: path, options: readOptions) - XCTAssertEqual(data, readData) + #expect(data == readData) } func cleanup(at path: String) { @@ -87,17 +87,16 @@ class DataIOTests : XCTestCase { // Ignore any errors } #endif - - + // MARK: - Tests - - func test_basicReadWrite() throws { + + @Test func test_basicReadWrite() throws { let url = testURL() try writeAndVerifyTestData(to: url) cleanup(at: url) } - func test_slicedReadWrite() throws { + @Test func test_slicedReadWrite() throws { // Be sure to use progress reporting so we get tests of the chunking let url = testURL() let data = generateTestData() @@ -112,12 +111,12 @@ class DataIOTests : XCTestCase { p.resignCurrent() #endif let readData = try Data(contentsOf: url, options: []) - XCTAssertEqual(readData, slice) + #expect(readData == slice) cleanup(at: url) } // Atomic writing is a very different code path - func test_readWriteAtomic() throws { + @Test func test_readWriteAtomic() throws { let url = testURL() // Perform an atomic write to a file that does not exist try writeAndVerifyTestData(to: url, writeOptions: [.atomic]) @@ -128,27 +127,32 @@ class DataIOTests : XCTestCase { cleanup(at: url) } - func test_readWriteMapped() throws { + @Test func test_readWriteMapped() throws { let url = testURL() try writeAndVerifyTestData(to: url, readOptions: [.mappedIfSafe]) cleanup(at: url) } - func test_writeFailure() throws { + @Test func test_writeFailure() throws { let url = testURL() let data = Data() try data.write(to: url) #if FOUNDATION_FRAMEWORK - XCTAssertThrowsError(try data.write(to: url, options: [.withoutOverwriting])) { e in - XCTAssertEqual((e as NSError).code, NSFileWriteFileExistsError) + #expect { + try data.write(to: url, options: [.withoutOverwriting]) + } throws: { error in + #expect((error as NSError).code == NSFileWriteFileExistsError) + return true } #else - XCTAssertThrowsError(try data.write(to: url, options: [.withoutOverwriting])) + #expect(throws: (any Error).self) { + try data.write(to: url, options: [.withoutOverwriting]) + } #endif - + cleanup(at: url) // Make sure clearing the error condition allows the write to succeed @@ -156,10 +160,10 @@ class DataIOTests : XCTestCase { cleanup(at: url) } - + #if FOUNDATION_FRAMEWORK // Progress is curently stubbed out for FoundationPreview - func test_writeWithProgress() throws { + @Test func test_writeWithProgress() throws { let url = testURL() let p = Progress(totalUnitCount: 1) @@ -167,14 +171,14 @@ class DataIOTests : XCTestCase { try writeAndVerifyTestData(to: url) p.resignCurrent() - XCTAssertEqual(p.completedUnitCount, 1) - XCTAssertEqual(p.fractionCompleted, 1.0, accuracy: 0.1) + #expect(p.completedUnitCount == 1) + #expect(abs(p.fractionCompleted - 1.0) <= 0.1) cleanup(at: url) } #endif - + #if FOUNDATION_FRAMEWORK - func test_writeWithAttributes() throws { + @Test func test_writeWithAttributes() throws { let writeData = generateTestData() let url = testURL() @@ -187,45 +191,45 @@ class DataIOTests : XCTestCase { var readAttrs: [String : Data] = [:] let readData = try readDataFromFile(path: .url(url), reportProgress: false, options: [], attributesToRead: [FileAttributeKey.hfsCreatorCode.rawValue], attributes: &readAttrs) - XCTAssertEqual(writeData, readData) - XCTAssertEqual(writeAttrs, readAttrs) - + #expect(writeData == readData) + #expect(writeAttrs == readAttrs) + cleanup(at: url) } #endif - - func test_emptyFile() throws { + + @Test func test_emptyFile() throws { let data = Data() let url = testURL() try data.write(to: url) let read = try Data(contentsOf: url, options: []) - XCTAssertEqual(data, read) - + #expect(data == read) + cleanup(at: url) } - + #if FOUNDATION_FRAMEWORK // String(contentsOf:) is not available outside the framework yet - func test_emptyFileString() { + @Test func test_emptyFileString() { let data = Data() let url = testURL() do { try data.write(to: url) let readString = try String(contentsOf: url) - XCTAssertEqual(readString, "") - + #expect(readString == "") + let readStringWithEncoding = try String(contentsOf: url, encoding: String._Encoding.utf8) - XCTAssertEqual(readStringWithEncoding, "") - + #expect(readStringWithEncoding == "") + cleanup(at: url) } catch { - XCTFail("Could not read file: \(error)") + Issue.record("Could not read file: \(error)") } } #endif - - func test_largeFile() throws { + + @Test func test_largeFile() throws { #if !os(watchOS) // More than 2 GB let size = 0x80010000 @@ -239,63 +243,63 @@ class DataIOTests : XCTestCase { #endif } +#if os(Linux) || os(Windows) func test_writeToSpecialFile() throws { - #if !os(Linux) && !os(Windows) - throw XCTSkip("This test is only supported on Linux and Windows") - #else #if os(Windows) let path = "CON" #else let path = "/dev/stdout" #endif - XCTAssertNoThrow(try Data("Output to STDOUT\n".utf8).write(to: path)) - #endif + #expect(throws: Never.self) { + try Data("Output to STDOUT\n".utf8).write(to: path) + } } +#endif // os(Linux) || os(Windows) // MARK: - String Path Tests - func testStringDeletingLastPathComponent() { - XCTAssertEqual("/a/b/c".deletingLastPathComponent(), "/a/b") - XCTAssertEqual("".deletingLastPathComponent(), "") - XCTAssertEqual("/".deletingLastPathComponent(), "/") - XCTAssertEqual("q".deletingLastPathComponent(), "") - XCTAssertEqual("/aaa".deletingLastPathComponent(), "/") - XCTAssertEqual("/a/b/c/".deletingLastPathComponent(), "/a/b") - XCTAssertEqual("hello".deletingLastPathComponent(), "") - XCTAssertEqual("hello/".deletingLastPathComponent(), "") + @Test func testStringDeletingLastPathComponent() { + #expect("/a/b/c".deletingLastPathComponent() == "/a/b") + #expect("".deletingLastPathComponent() == "") + #expect("/".deletingLastPathComponent() == "/") + #expect("q".deletingLastPathComponent() == "") + #expect("/aaa".deletingLastPathComponent() == "/") + #expect("/a/b/c/".deletingLastPathComponent() == "/a/b") + #expect("hello".deletingLastPathComponent() == "") + #expect("hello/".deletingLastPathComponent() == "") } - - func testAppendingPathComponent() { + + @Test func testAppendingPathComponent() { let comp = "test" - XCTAssertEqual("/a/b/c".appendingPathComponent(comp), "/a/b/c/test") - XCTAssertEqual("".appendingPathComponent(comp), "test") - XCTAssertEqual("/".appendingPathComponent(comp), "/test") - XCTAssertEqual("q".appendingPathComponent(comp), "q/test") - XCTAssertEqual("/aaa".appendingPathComponent(comp), "/aaa/test") - XCTAssertEqual("/a/b/c/".appendingPathComponent(comp), "/a/b/c/test") - XCTAssertEqual("hello".appendingPathComponent(comp), "hello/test") - XCTAssertEqual("hello/".appendingPathComponent(comp), "hello/test") - - XCTAssertEqual("hello/".appendingPathComponent("/test"), "hello/test") - XCTAssertEqual("hello".appendingPathComponent("/test"), "hello/test") - XCTAssertEqual("hello///".appendingPathComponent("///test"), "hello/test") - XCTAssertEqual("hello".appendingPathComponent("test/"), "hello/test") - XCTAssertEqual("hello".appendingPathComponent("test/test2"), "hello/test/test2") - XCTAssertEqual("hello".appendingPathComponent("test/test2/"), "hello/test/test2") - XCTAssertEqual("hello".appendingPathComponent("test///test2/"), "hello/test/test2") - XCTAssertEqual("hello".appendingPathComponent("/"), "hello") - XCTAssertEqual("//".appendingPathComponent("/"), "/") - XCTAssertEqual("".appendingPathComponent(""), "") + #expect("/a/b/c".appendingPathComponent(comp) == "/a/b/c/test") + #expect("".appendingPathComponent(comp) == "test") + #expect("/".appendingPathComponent(comp) == "/test") + #expect("q".appendingPathComponent(comp) == "q/test") + #expect("/aaa".appendingPathComponent(comp) == "/aaa/test") + #expect("/a/b/c/".appendingPathComponent(comp) == "/a/b/c/test") + #expect("hello".appendingPathComponent(comp) == "hello/test") + #expect("hello/".appendingPathComponent(comp) == "hello/test") + + #expect("hello/".appendingPathComponent("/test") == "hello/test") + #expect("hello".appendingPathComponent("/test") == "hello/test") + #expect("hello///".appendingPathComponent("///test") == "hello/test") + #expect("hello".appendingPathComponent("test/") == "hello/test") + #expect("hello".appendingPathComponent("test/test2") == "hello/test/test2") + #expect("hello".appendingPathComponent("test/test2/") == "hello/test/test2") + #expect("hello".appendingPathComponent("test///test2/") == "hello/test/test2") + #expect("hello".appendingPathComponent("/") == "hello") + #expect("//".appendingPathComponent("/") == "/") + #expect("".appendingPathComponent("") == "") } - - func testStringLastPathComponent() { - XCTAssertEqual("/a/b/c".lastPathComponent, "c") - XCTAssertEqual("".lastPathComponent, "") - XCTAssertEqual("/".lastPathComponent, "/") - XCTAssertEqual("q".lastPathComponent, "q") - XCTAssertEqual("/aaa".lastPathComponent, "aaa") - XCTAssertEqual("/a/b/c/".lastPathComponent, "c") - XCTAssertEqual("hello".lastPathComponent, "hello") - XCTAssertEqual("hello/".lastPathComponent, "hello") + + @Test func testStringLastPathComponent() { + #expect("/a/b/c".lastPathComponent == "c") + #expect("".lastPathComponent == "") + #expect("/".lastPathComponent == "/") + #expect("q".lastPathComponent == "q") + #expect("/aaa".lastPathComponent == "aaa") + #expect("/a/b/c/".lastPathComponent == "c") + #expect("hello".lastPathComponent == "hello") + #expect("hello/".lastPathComponent == "hello") } } diff --git a/Tests/FoundationEssentialsTests/DataTests.swift b/Tests/FoundationEssentialsTests/DataTests.swift index 1863a4906..e13d3275a 100644 --- a/Tests/FoundationEssentialsTests/DataTests.swift +++ b/Tests/FoundationEssentialsTests/DataTests.swift @@ -10,11 +10,11 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing -#if canImport(Glibc) +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) import Glibc #endif @@ -42,7 +42,7 @@ extension Data { } } -class DataTests : XCTestCase { +final class DataTests { var heldData: Data? @@ -68,12 +68,12 @@ class DataTests : XCTestCase { // MARK: - - func testBasicConstruction() { + @Test func testBasicConstruction() { // Make sure that we were able to create some data let hello = dataFrom("hello") let helloLength = hello.count - XCTAssertEqual(hello[0], 0x68, "Unexpected first byte") + #expect(hello[0] == 0x68, "Unexpected first byte") let world = dataFrom(" world") var helloWorld = hello @@ -81,26 +81,26 @@ class DataTests : XCTestCase { helloWorld.append($0, count: world.count) } - XCTAssertEqual(hello[0], 0x68, "First byte should not have changed") - XCTAssertEqual(hello.count, helloLength, "Length of first data should not have changed") - XCTAssertEqual(helloWorld.count, hello.count + world.count, "The total length should include both buffers") + #expect(hello[0] == 0x68, "First byte should not have changed") + #expect(hello.count == helloLength, "Length of first data should not have changed") + #expect(helloWorld.count == hello.count + world.count, "The total length should include both buffers") } - func testInitializationWithArray() { + @Test func testInitializationWithArray() { let data = Data([1, 2, 3]) - XCTAssertEqual(3, data.count) + #expect(3 == data.count) let data2 = Data([1, 2, 3].filter { $0 >= 2 }) - XCTAssertEqual(2, data2.count) + #expect(2 == data2.count) let data3 = Data([1, 2, 3, 4, 5][1..<3]) - XCTAssertEqual(2, data3.count) + #expect(2 == data3.count) } - func testInitializationWithBufferPointer() { + @Test func testInitializationWithBufferPointer() { let nilBuffer = UnsafeBufferPointer(start: nil, count: 0) let data = Data(buffer: nilBuffer) - XCTAssertEqual(data, Data()) + #expect(data == Data()) let validPointer = UnsafeMutablePointer.allocate(capacity: 2) validPointer[0] = 0xCA @@ -109,29 +109,29 @@ class DataTests : XCTestCase { let emptyBuffer = UnsafeBufferPointer(start: validPointer, count: 0) let data2 = Data(buffer: emptyBuffer) - XCTAssertEqual(data2, Data()) + #expect(data2 == Data()) let shortBuffer = UnsafeBufferPointer(start: validPointer, count: 1) let data3 = Data(buffer: shortBuffer) - XCTAssertEqual(data3, Data([0xCA])) + #expect(data3 == Data([0xCA])) let fullBuffer = UnsafeBufferPointer(start: validPointer, count: 2) let data4 = Data(buffer: fullBuffer) - XCTAssertEqual(data4, Data([0xCA, 0xFE])) + #expect(data4 == Data([0xCA, 0xFE])) let tuple: (UInt16, UInt16, UInt16, UInt16) = (0xFF, 0xFE, 0xFD, 0xFC) withUnsafeBytes(of: tuple) { // If necessary, port this to big-endian. let tupleBuffer: UnsafeBufferPointer = $0.bindMemory(to: UInt8.self) let data5 = Data(buffer: tupleBuffer) - XCTAssertEqual(data5, Data([0xFF, 0x00, 0xFE, 0x00, 0xFD, 0x00, 0xFC, 0x00])) + #expect(data5 == Data([0xFF, 0x00, 0xFE, 0x00, 0xFD, 0x00, 0xFC, 0x00])) } } - func testInitializationWithMutableBufferPointer() { + @Test func testInitializationWithMutableBufferPointer() { let nilBuffer = UnsafeMutableBufferPointer(start: nil, count: 0) let data = Data(buffer: nilBuffer) - XCTAssertEqual(data, Data()) + #expect(data == Data()) let validPointer = UnsafeMutablePointer.allocate(capacity: 2) validPointer[0] = 0xCA @@ -140,59 +140,59 @@ class DataTests : XCTestCase { let emptyBuffer = UnsafeMutableBufferPointer(start: validPointer, count: 0) let data2 = Data(buffer: emptyBuffer) - XCTAssertEqual(data2, Data()) + #expect(data2 == Data()) let shortBuffer = UnsafeMutableBufferPointer(start: validPointer, count: 1) let data3 = Data(buffer: shortBuffer) - XCTAssertEqual(data3, Data([0xCA])) + #expect(data3 == Data([0xCA])) let fullBuffer = UnsafeMutableBufferPointer(start: validPointer, count: 2) let data4 = Data(buffer: fullBuffer) - XCTAssertEqual(data4, Data([0xCA, 0xFE])) + #expect(data4 == Data([0xCA, 0xFE])) var tuple: (UInt16, UInt16, UInt16, UInt16) = (0xFF, 0xFE, 0xFD, 0xFC) withUnsafeMutableBytes(of: &tuple) { // If necessary, port this to big-endian. let tupleBuffer: UnsafeMutableBufferPointer = $0.bindMemory(to: UInt8.self) let data5 = Data(buffer: tupleBuffer) - XCTAssertEqual(data5, Data([0xFF, 0x00, 0xFE, 0x00, 0xFD, 0x00, 0xFC, 0x00])) + #expect(data5 == Data([0xFF, 0x00, 0xFE, 0x00, 0xFD, 0x00, 0xFC, 0x00])) } } - func testMutableData() { + @Test func testMutableData() { let hello = dataFrom("hello") let helloLength = hello.count - XCTAssertEqual(hello[0], 0x68, "Unexpected first byte") + #expect(hello[0] == 0x68, "Unexpected first byte") // Double the length var mutatingHello = hello mutatingHello.count *= 2 - XCTAssertEqual(hello.count, helloLength, "The length of the initial data should not have changed") - XCTAssertEqual(mutatingHello.count, helloLength * 2, "The length should have changed") + #expect(hello.count == helloLength, "The length of the initial data should not have changed") + #expect(mutatingHello.count == helloLength * 2, "The length should have changed") // Get the underlying data for hello2 mutatingHello.withUnsafeMutableUInt8Bytes { (bytes : UnsafeMutablePointer) in - XCTAssertEqual(bytes.pointee, 0x68, "First byte should be 0x68") + #expect(bytes.pointee == 0x68, "First byte should be 0x68") // Mutate it bytes.pointee = 0x67 - XCTAssertEqual(bytes.pointee, 0x67, "First byte should be 0x67") + #expect(bytes.pointee == 0x67, "First byte should be 0x67") // Verify that the first data is still correct - XCTAssertEqual(hello[0], 0x68, "The first byte should still be 0x68") + #expect(hello[0] == 0x68, "The first byte should still be 0x68") } } - func testEquality() { + @Test func testEquality() { let d1 = dataFrom("hello") let d2 = dataFrom("hello") // Use == explicitly here to make sure we're calling the right methods - XCTAssertTrue(d1 == d2, "Data should be equal") + #expect(d1 == d2, "Data should be equal") } - func testDataInSet() { + @Test func testDataInSet() { let d1 = dataFrom("Hello") let d2 = dataFrom("Hello") let d3 = dataFrom("World") @@ -202,25 +202,25 @@ class DataTests : XCTestCase { s.insert(d2) s.insert(d3) - XCTAssertEqual(s.count, 2, "Expected only two entries in the Set") + #expect(s.count == 2, "Expected only two entries in the Set") } - func testReplaceSubrange() { + @Test func testReplaceSubrange() { var hello = dataFrom("Hello") let world = dataFrom("World") hello[0] = world[0] - XCTAssertEqual(hello[0], world[0]) + #expect(hello[0] == world[0]) var goodbyeWorld = dataFrom("Hello World") let goodbye = dataFrom("Goodbye") let expected = dataFrom("Goodbye World") goodbyeWorld.replaceSubrange(0..<5, with: goodbye) - XCTAssertEqual(goodbyeWorld, expected) + #expect(goodbyeWorld == expected) } - func testReplaceSubrange3() { + @Test func testReplaceSubrange3() { // The expected result let expectedBytes : [UInt8] = [1, 2, 9, 10, 11, 12, 13] let expected = expectedBytes.withUnsafeBufferPointer { @@ -238,10 +238,10 @@ class DataTests : XCTestCase { b.withUnsafeBufferPointer { a.replaceSubrange(2..<5, with: $0) } - XCTAssertEqual(expected, a) + #expect(expected == a) } - func testReplaceSubrange4() { + @Test func testReplaceSubrange4() { let expectedBytes : [UInt8] = [1, 2, 9, 10, 11, 12, 13] let expected = Data(expectedBytes) @@ -252,31 +252,31 @@ class DataTests : XCTestCase { // The bytes we'll insert let b : [UInt8] = [9, 10, 11, 12, 13] a.replaceSubrange(2..<5, with: b) - XCTAssertEqual(expected, a) + #expect(expected == a) } - func testReplaceSubrange5() { + @Test func testReplaceSubrange5() { var d = Data([1, 2, 3]) d.replaceSubrange(0..<0, with: [4]) - XCTAssertEqual(Data([4, 1, 2, 3]), d) + #expect(Data([4, 1, 2, 3]) == d) d.replaceSubrange(0..<4, with: [9]) - XCTAssertEqual(Data([9]), d) + #expect(Data([9]) == d) d.replaceSubrange(0..= 65 && byte <= 90 } let allCaps = hello.filter(isCapital) - XCTAssertEqual(allCaps.count, 2) + #expect(allCaps.count == 2) let capCount = hello.reduce(0) { isCapital($1) ? $0 + 1 : $0 } - XCTAssertEqual(capCount, 2) + #expect(capCount == 2) let allLower = hello.map { isCapital($0) ? $0 + 31 : $0 } - XCTAssertEqual(allLower.count, hello.count) + #expect(allLower.count == hello.count) } - func testCopyBytes() { + @Test func testCopyBytes() { let c = 10 let underlyingBuffer = malloc(c * MemoryLayout.stride)! let u16Ptr = underlyingBuffer.bindMemory(to: UInt16.self, capacity: c) @@ -326,50 +326,50 @@ class DataTests : XCTestCase { data[0] = 0xFF data[1] = 0xFF let copiedCount = data.copyBytes(to: buffer) - XCTAssertEqual(copiedCount, c * MemoryLayout.stride) + #expect(copiedCount == c * MemoryLayout.stride) - XCTAssertEqual(buffer[0], 0xFFFF) + #expect(buffer[0] == 0xFFFF) free(underlyingBuffer) } - func testCopyBytes_undersized() { + @Test func testCopyBytes_undersized() { let a : [UInt8] = [1, 2, 3, 4, 5] let data = a.withUnsafeBufferPointer { return Data(buffer: $0) } let expectedSize = MemoryLayout.stride * a.count - XCTAssertEqual(expectedSize, data.count) + #expect(expectedSize == data.count) let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: expectedSize - 1, alignment: MemoryLayout.size) // We should only copy in enough bytes that can fit in the buffer let copiedCount = data.copyBytes(to: buffer) - XCTAssertEqual(expectedSize - 1, copiedCount) + #expect(expectedSize - 1 == copiedCount) var index = 0 for v in a[0...stride * a.count - XCTAssertEqual(expectedSize, data.count) + #expect(expectedSize == data.count) let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: expectedSize, alignment: MemoryLayout.size) let copiedCount = data.copyBytes(to: buffer) - XCTAssertEqual(expectedSize, copiedCount) + #expect(expectedSize == copiedCount) buffer.deallocate() } - func testCopyBytes_ranges() { + @Test func testCopyBytes_ranges() { do { // Equal sized buffer, data @@ -383,17 +383,17 @@ class DataTests : XCTestCase { var copiedCount : Int copiedCount = data.copyBytes(to: buffer, from: 0..<0) - XCTAssertEqual(0, copiedCount) + #expect(0 == copiedCount) copiedCount = data.copyBytes(to: buffer, from: 1..<1) - XCTAssertEqual(0, copiedCount) + #expect(0 == copiedCount) copiedCount = data.copyBytes(to: buffer, from: 0..<3) - XCTAssertEqual((0..<3).count, copiedCount) + #expect((0..<3).count == copiedCount) var index = 0 for v in a[0..<3] { - XCTAssertEqual(v, buffer[index]) + #expect(v == buffer[index]) index += 1 } buffer.deallocate() @@ -410,11 +410,11 @@ class DataTests : XCTestCase { var copiedCount : Int copiedCount = data.copyBytes(to: buffer, from: 0..<3) - XCTAssertEqual((0..<3).count, copiedCount) + #expect((0..<3).count == copiedCount) var index = 0 for v in a[0..<3] { - XCTAssertEqual(v, buffer[index]) + #expect(v == buffer[index]) index += 1 } buffer.deallocate() @@ -432,11 +432,11 @@ class DataTests : XCTestCase { var copiedCount : Int copiedCount = data.copyBytes(to: buffer, from: 0...stride * a.count - XCTAssertEqual(expectedSize, data.count) + #expect(expectedSize == data.count) [false, true].withUnsafeBufferPointer { data.append($0) } expectedSize += MemoryLayout.stride * 2 - XCTAssertEqual(expectedSize, data.count) + #expect(expectedSize == data.count) let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: expectedSize, alignment: MemoryLayout.size) let copiedCount = data.copyBytes(to: buffer) - XCTAssertEqual(copiedCount, expectedSize) + #expect(copiedCount == expectedSize) buffer.deallocate() } @@ -480,7 +480,7 @@ class DataTests : XCTestCase { } } - func test_bufferSizeCalculation() { + @Test func test_bufferSizeCalculation() { // Make sure that Data is correctly using strideof instead of sizeof. // n.b. if sizeof(MyStruct) == strideof(MyStruct), this test is not as useful as it could be @@ -490,7 +490,7 @@ class DataTests : XCTestCase { return Data(buffer: $0) } - XCTAssertEqual(data.count, MemoryLayout.stride * 3) + #expect(data.count == MemoryLayout.stride * 3) // append @@ -498,7 +498,7 @@ class DataTests : XCTestCase { data.append($0) } - XCTAssertEqual(data.count, MemoryLayout.stride * 6) + #expect(data.count == MemoryLayout.stride * 6) // copyBytes do { @@ -510,7 +510,7 @@ class DataTests : XCTestCase { let buffer = UnsafeMutableBufferPointer(start: ptr, count: 6) let byteCount = data.copyBytes(to: buffer) - XCTAssertEqual(6 * MemoryLayout.stride, byteCount) + #expect(6 * MemoryLayout.stride == byteCount) } do { @@ -522,7 +522,7 @@ class DataTests : XCTestCase { let buffer = UnsafeMutableBufferPointer(start: ptr, count: 3) let byteCount = data.copyBytes(to: buffer) - XCTAssertEqual(3 * MemoryLayout.stride, byteCount) + #expect(3 * MemoryLayout.stride == byteCount) } do { @@ -534,47 +534,47 @@ class DataTests : XCTestCase { let buffer = UnsafeMutableBufferPointer(start: ptr, count: 6) let byteCount = data.copyBytes(to: buffer) - XCTAssertEqual(6 * MemoryLayout.stride, byteCount) + #expect(6 * MemoryLayout.stride == byteCount) } } // MARK: - - func test_repeatingValueInitialization() { + @Test func test_repeatingValueInitialization() { var d = Data(repeating: 0x01, count: 3) let elements = repeatElement(UInt8(0x02), count: 3) // ensure we fall into the sequence case d.append(contentsOf: elements) - XCTAssertEqual(d[0], 0x01) - XCTAssertEqual(d[1], 0x01) - XCTAssertEqual(d[2], 0x01) + #expect(d[0] == 0x01) + #expect(d[1] == 0x01) + #expect(d[2] == 0x01) - XCTAssertEqual(d[3], 0x02) - XCTAssertEqual(d[4], 0x02) - XCTAssertEqual(d[5], 0x02) + #expect(d[3] == 0x02) + #expect(d[4] == 0x02) + #expect(d[5] == 0x02) } - func test_rangeSlice() { + @Test func test_rangeSlice() { let a: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7] let d = Data(a) for i in 0.. String = "", file: StaticString = #file, line: UInt = #line) { + let sourceLocation: SourceLocation = .init( + filePath: String(describing: file), + line: Int(line) + ) if let index = expectedStartIndex { let expectedRange: Range = index..<(index + fragment.count) if let someRange = range { - XCTAssertEqual(data.firstRange(of: fragment, in: someRange), expectedRange, message(), file: file, line: line) + #expect( + data.firstRange(of: fragment, in: someRange) == expectedRange, + .init(rawValue: message()), + sourceLocation: sourceLocation + ) } else { - XCTAssertEqual(data.firstRange(of: fragment), expectedRange, message(), file: file, line: line) + #expect( + data.firstRange(of: fragment) == expectedRange, + .init(rawValue: message()), + sourceLocation: sourceLocation + ) } } else { if let someRange = range { - XCTAssertNil(data.firstRange(of: fragment, in: someRange), message(), file: file, line: line) + #expect( + data.firstRange(of: fragment, in: someRange) == nil, + .init(rawValue: message()), + sourceLocation: sourceLocation + ) } else { - XCTAssertNil(data.firstRange(of: fragment), message(), file: file, line: line) + #expect( + data.firstRange(of: fragment) == nil, + .init(rawValue: message()), + sourceLocation: sourceLocation + ) } } } @@ -646,18 +666,38 @@ class DataTests : XCTestCase { expectedStartIndex: Int?, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { + let sourceLocation: SourceLocation = .init( + filePath: String(describing: file), + line: Int(line) + ) if let index = expectedStartIndex { let expectedRange: Range = index..<(index + fragment.count) if let someRange = range { - XCTAssertEqual(data.lastRange(of: fragment, in: someRange), expectedRange, file: file, line: line) + #expect( + data.lastRange(of: fragment, in: someRange) == expectedRange, + .init(rawValue: message()), + sourceLocation: sourceLocation + ) } else { - XCTAssertEqual(data.lastRange(of: fragment), expectedRange, message(), file: file, line: line) + #expect( + data.lastRange(of: fragment) == expectedRange, + .init(rawValue: message()), + sourceLocation: sourceLocation + ) } } else { if let someRange = range { - XCTAssertNil(data.lastRange(of: fragment, in: someRange), message(), file: file, line: line) + #expect( + data.lastRange(of: fragment, in: someRange) == nil, + .init(rawValue: message()), + sourceLocation: sourceLocation + ) } else { - XCTAssertNil(data.lastRange(of: fragment), message(), file: file, line: line) + #expect( + data.lastRange(of: fragment) == nil, + .init(rawValue: message()), + sourceLocation: sourceLocation + ) } } } @@ -684,98 +724,98 @@ class DataTests : XCTestCase { } } - func test_sliceAppending() { + @Test func test_sliceAppending() { // https://bugs.swift.org/browse/SR-4473 var fooData = Data() let barData = Data([0, 1, 2, 3, 4, 5]) let slice = barData.suffix(from: 3) fooData.append(slice) - XCTAssertEqual(fooData[0], 0x03) - XCTAssertEqual(fooData[1], 0x04) - XCTAssertEqual(fooData[2], 0x05) + #expect(fooData[0] == 0x03) + #expect(fooData[1] == 0x04) + #expect(fooData[2] == 0x05) } - func test_sliceWithUnsafeBytes() { + @Test func test_sliceWithUnsafeBytes() { let base = Data([0, 1, 2, 3, 4, 5]) let slice = base[2..<4] let segment = slice.withUnsafeUInt8Bytes { (ptr: UnsafePointer) -> [UInt8] in return [ptr.pointee, ptr.advanced(by: 1).pointee] } - XCTAssertEqual(segment, [UInt8(2), UInt8(3)]) + #expect(segment == [UInt8(2), UInt8(3)]) } - func test_sliceIteration() { + @Test func test_sliceIteration() { let base = Data([0, 1, 2, 3, 4, 5]) let slice = base[2..<4] var found = [UInt8]() for byte in slice { found.append(byte) } - XCTAssertEqual(found[0], 2) - XCTAssertEqual(found[1], 3) + #expect(found[0] == 2) + #expect(found[1] == 3) } - func test_sliceIndexing() { + @Test func test_sliceIndexing() { let d = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) let slice = d[5..<10] - XCTAssertEqual(slice[5], d[5]) + #expect(slice[5] == d[5]) } - func test_sliceEquality() { + @Test func test_sliceEquality() { let d = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) let slice = d[5..<7] let expected = Data([5, 6]) - XCTAssertEqual(expected, slice) + #expect(expected == slice) } - func test_sliceEquality2() { + @Test func test_sliceEquality2() { let d = Data([5, 6, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) let slice1 = d[0..<2] let slice2 = d[5..<7] - XCTAssertEqual(slice1, slice2) + #expect(slice1 == slice2) } - func test_map() { + @Test func test_map() { let d1 = Data([81, 0, 0, 0, 14]) let d2 = d1[1...4] - XCTAssertEqual(4, d2.count) + #expect(4 == d2.count) let expected: [UInt8] = [0, 0, 0, 14] let actual = d2.map { $0 } - XCTAssertEqual(expected, actual) + #expect(expected == actual) } - func test_dropFirst() { + @Test func test_dropFirst() { let data = Data([0, 1, 2, 3, 4, 5]) let sliced = data.dropFirst() - XCTAssertEqual(data.count - 1, sliced.count) - XCTAssertEqual(UInt8(1), sliced[1]) - XCTAssertEqual(UInt8(2), sliced[2]) - XCTAssertEqual(UInt8(3), sliced[3]) - XCTAssertEqual(UInt8(4), sliced[4]) - XCTAssertEqual(UInt8(5), sliced[5]) + #expect(data.count - 1 == sliced.count) + #expect(UInt8(1) == sliced[1]) + #expect(UInt8(2) == sliced[2]) + #expect(UInt8(3) == sliced[3]) + #expect(UInt8(4) == sliced[4]) + #expect(UInt8(5) == sliced[5]) } - func test_dropFirst2() { + @Test func test_dropFirst2() { let data = Data([0, 1, 2, 3, 4, 5]) let sliced = data.dropFirst(2) - XCTAssertEqual(data.count - 2, sliced.count) - XCTAssertEqual(UInt8(2), sliced[2]) - XCTAssertEqual(UInt8(3), sliced[3]) - XCTAssertEqual(UInt8(4), sliced[4]) - XCTAssertEqual(UInt8(5), sliced[5]) + #expect(data.count - 2 == sliced.count) + #expect(UInt8(2) == sliced[2]) + #expect(UInt8(3) == sliced[3]) + #expect(UInt8(4) == sliced[4]) + #expect(UInt8(5) == sliced[5]) } - func test_copyBytes1() { + @Test func test_copyBytes1() { var array: [UInt8] = [0, 1, 2, 3] let data = Data(array) array.withUnsafeMutableBufferPointer { data[1..<3].copyBytes(to: $0.baseAddress!, from: 1..<3) } - XCTAssertEqual([UInt8(1), UInt8(2), UInt8(2), UInt8(3)], array) + #expect([UInt8(1), UInt8(2), UInt8(2), UInt8(3)] == array) } - func test_copyBytes2() { + @Test func test_copyBytes2() { let array: [UInt8] = [0, 1, 2, 3] let data = Data(array) @@ -785,10 +825,10 @@ class DataTests : XCTestCase { let end = data.index(before: data.endIndex) let slice = data[start..(_:)` -- a discontiguous sequence of unknown length. - func test_appendingNonContiguousSequence_underestimatedCount() { + @Test func test_appendingNonContiguousSequence_underestimatedCount() { var d = Data() // d should go from .empty representation to .inline. // Appending a small enough sequence to fit in .inline should actually be copied. d.append(contentsOf: (0x00...0x01).makeIterator()) // `.makeIterator()` produces a sequence whose `.underestimatedCount` is 0. - XCTAssertEqual(Data([0x00, 0x01]), d) + #expect(Data([0x00, 0x01]) == d) // Appending another small sequence should similarly still work. d.append(contentsOf: (0x02...0x02).makeIterator()) // `.makeIterator()` produces a sequence whose `.underestimatedCount` is 0. - XCTAssertEqual(Data([0x00, 0x01, 0x02]), d) + #expect(Data([0x00, 0x01, 0x02]) == d) // If we append a sequence of elements larger than a single InlineData, the internal append here should buffer. // We want to make sure that buffering in this way does not accidentally drop trailing elements on the floor. d.append(contentsOf: (0x03...0x2F).makeIterator()) // `.makeIterator()` produces a sequence whose `.underestimatedCount` is 0. - XCTAssertEqual(Data([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F]), d) + #expect(Data([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F]) == d) } - func test_sequenceInitializers() { + @Test func test_sequenceInitializers() { let seq = repeatElement(UInt8(0x02), count: 3) // ensure we fall into the sequence case let dataFromSeq = Data(seq) - XCTAssertEqual(3, dataFromSeq.count) - XCTAssertEqual(UInt8(0x02), dataFromSeq[0]) - XCTAssertEqual(UInt8(0x02), dataFromSeq[1]) - XCTAssertEqual(UInt8(0x02), dataFromSeq[2]) + #expect(3 == dataFromSeq.count) + #expect(UInt8(0x02) == dataFromSeq[0]) + #expect(UInt8(0x02) == dataFromSeq[1]) + #expect(UInt8(0x02) == dataFromSeq[2]) let array: [UInt8] = [0, 1, 2, 3, 4, 5, 6] let dataFromArray = Data(array) - XCTAssertEqual(array.count, dataFromArray.count) - XCTAssertEqual(array[0], dataFromArray[0]) - XCTAssertEqual(array[1], dataFromArray[1]) - XCTAssertEqual(array[2], dataFromArray[2]) - XCTAssertEqual(array[3], dataFromArray[3]) + #expect(array.count == dataFromArray.count) + #expect(array[0] == dataFromArray[0]) + #expect(array[1] == dataFromArray[1]) + #expect(array[2] == dataFromArray[2]) + #expect(array[3] == dataFromArray[3]) let slice = array[1..<4] let dataFromSlice = Data(slice) - XCTAssertEqual(slice.count, dataFromSlice.count) - XCTAssertEqual(slice.first, dataFromSlice.first) - XCTAssertEqual(slice.last, dataFromSlice.last) + #expect(slice.count == dataFromSlice.count) + #expect(slice.first == dataFromSlice.first) + #expect(slice.last == dataFromSlice.last) let data = Data([1, 2, 3, 4, 5, 6, 7, 8, 9]) let dataFromData = Data(data) - XCTAssertEqual(data, dataFromData) + #expect(data == dataFromData) let sliceOfData = data[1..<3] let dataFromSliceOfData = Data(sliceOfData) - XCTAssertEqual(sliceOfData, dataFromSliceOfData) + #expect(sliceOfData == dataFromSliceOfData) } - func test_reversedDataInit() { + @Test func test_reversedDataInit() { let data = Data([1, 2, 3, 4, 5, 6, 7, 8, 9]) let reversedData = Data(data.reversed()) let expected = Data([9, 8, 7, 6, 5, 4, 3, 2, 1]) - XCTAssertEqual(expected, reversedData) + #expect(expected == reversedData) } - func test_validateMutation_withUnsafeMutableBytes() { + @Test func test_validateMutation_withUnsafeMutableBytes() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) data.withUnsafeMutableUInt8Bytes { (ptr: UnsafeMutablePointer) in ptr.advanced(by: 5).pointee = 0xFF } - XCTAssertEqual(data, Data([0, 1, 2, 3, 4, 0xFF, 6, 7, 8, 9])) + #expect(data == Data([0, 1, 2, 3, 4, 0xFF, 6, 7, 8, 9])) } - func test_validateMutation_appendBytes() { + @Test func test_validateMutation_appendBytes() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) data.append("hello", count: 5) - XCTAssertEqual(data[data.startIndex.advanced(by: 5)], 0x5) + #expect(data[data.startIndex.advanced(by: 5)] == 0x5) } - func test_validateMutation_appendData() { + @Test func test_validateMutation_appendData() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) let other = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) data.append(other) - XCTAssertEqual(data[data.startIndex.advanced(by: 9)], 9) - XCTAssertEqual(data[data.startIndex.advanced(by: 10)], 0) + #expect(data[data.startIndex.advanced(by: 9)] == 9) + #expect(data[data.startIndex.advanced(by: 10)] == 0) } - func test_validateMutation_appendBuffer() { + @Test func test_validateMutation_appendBuffer() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] bytes.withUnsafeBufferPointer { data.append($0) } - XCTAssertEqual(data[data.startIndex.advanced(by: 9)], 9) - XCTAssertEqual(data[data.startIndex.advanced(by: 10)], 0) + #expect(data[data.startIndex.advanced(by: 9)] == 9) + #expect(data[data.startIndex.advanced(by: 10)] == 0) } - func test_validateMutation_appendSequence() { + @Test func test_validateMutation_appendSequence() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) let seq = repeatElement(UInt8(1), count: 10) data.append(contentsOf: seq) - XCTAssertEqual(data[data.startIndex.advanced(by: 9)], 9) - XCTAssertEqual(data[data.startIndex.advanced(by: 10)], 1) + #expect(data[data.startIndex.advanced(by: 9)] == 9) + #expect(data[data.startIndex.advanced(by: 10)] == 1) } - func test_validateMutation_appendContentsOf() { + @Test func test_validateMutation_appendContentsOf() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] data.append(contentsOf: bytes) - XCTAssertEqual(data[data.startIndex.advanced(by: 9)], 9) - XCTAssertEqual(data[data.startIndex.advanced(by: 10)], 0) + #expect(data[data.startIndex.advanced(by: 9)] == 9) + #expect(data[data.startIndex.advanced(by: 10)] == 0) } - func test_validateMutation_resetBytes() { + @Test func test_validateMutation_resetBytes() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) data.resetBytes(in: 5..<8) - XCTAssertEqual(data, Data([0, 1, 2, 3, 4, 0, 0, 0, 8, 9])) + #expect(data == Data([0, 1, 2, 3, 4, 0, 0, 0, 8, 9])) } - func test_validateMutation_replaceSubrange() { + @Test func test_validateMutation_replaceSubrange() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) let range: Range = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4)..) in ptr.advanced(by: 1).pointee = 0xFF } - XCTAssertEqual(data, Data([4, 0xFF, 6, 7, 8])) + #expect(data == Data([4, 0xFF, 6, 7, 8])) } - func test_validateMutation_slice_appendBytes() { + @Test func test_validateMutation_slice_appendBytes() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] let bytes: [UInt8] = [0xFF, 0xFF] bytes.withUnsafeBufferPointer { data.append($0.baseAddress!, count: $0.count) } - XCTAssertEqual(data, Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) + #expect(data == Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) } - func test_validateMutation_slice_appendData() { + @Test func test_validateMutation_slice_appendData() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] let other = Data([0xFF, 0xFF]) data.append(other) - XCTAssertEqual(data, Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) + #expect(data == Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) } - func test_validateMutation_slice_appendBuffer() { + @Test func test_validateMutation_slice_appendBuffer() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] let bytes: [UInt8] = [0xFF, 0xFF] bytes.withUnsafeBufferPointer { data.append($0) } - XCTAssertEqual(data, Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) + #expect(data == Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) } - func test_validateMutation_slice_appendSequence() { + @Test func test_validateMutation_slice_appendSequence() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] let seq = repeatElement(UInt8(0xFF), count: 2) data.append(contentsOf: seq) - XCTAssertEqual(data, Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) + #expect(data == Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) } - func test_validateMutation_slice_appendContentsOf() { + @Test func test_validateMutation_slice_appendContentsOf() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] let bytes: [UInt8] = [0xFF, 0xFF] data.append(contentsOf: bytes) - XCTAssertEqual(data, Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) + #expect(data == Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) } - func test_validateMutation_slice_resetBytes() { + @Test func test_validateMutation_slice_resetBytes() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] data.resetBytes(in: 5..<8) - XCTAssertEqual(data, Data([4, 0, 0, 0, 8])) + #expect(data == Data([4, 0, 0, 0, 8])) } - func test_validateMutation_slice_replaceSubrange() { + @Test func test_validateMutation_slice_replaceSubrange() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in ptr.advanced(by: 5).pointee = 0xFF } - XCTAssertEqual(data, Data([0, 1, 2, 3, 4, 0xFF, 6, 7, 8, 9])) + #expect(data == Data([0, 1, 2, 3, 4, 0xFF, 6, 7, 8, 9])) } } - func test_validateMutation_cow_appendBytes() { + @Test func test_validateMutation_cow_appendBytes() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) holdReference(data) { data.append("hello", count: 5) - XCTAssertEqual(data[data.startIndex.advanced(by: 9)], 0x9) - XCTAssertEqual(data[data.startIndex.advanced(by: 10)], 0x68) + #expect(data[data.startIndex.advanced(by: 9)] == 0x9) + #expect(data[data.startIndex.advanced(by: 10)] == 0x68) } } - func test_validateMutation_cow_appendData() { + @Test func test_validateMutation_cow_appendData() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) holdReference(data) { let other = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) data.append(other) - XCTAssertEqual(data[data.startIndex.advanced(by: 9)], 9) - XCTAssertEqual(data[data.startIndex.advanced(by: 10)], 0) + #expect(data[data.startIndex.advanced(by: 9)] == 9) + #expect(data[data.startIndex.advanced(by: 10)] == 0) } } - func test_validateMutation_cow_appendBuffer() { + @Test func test_validateMutation_cow_appendBuffer() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) holdReference(data) { let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] bytes.withUnsafeBufferPointer { data.append($0) } - XCTAssertEqual(data[data.startIndex.advanced(by: 9)], 9) - XCTAssertEqual(data[data.startIndex.advanced(by: 10)], 0) + #expect(data[data.startIndex.advanced(by: 9)] == 9) + #expect(data[data.startIndex.advanced(by: 10)] == 0) } } - func test_validateMutation_cow_appendSequence() { + @Test func test_validateMutation_cow_appendSequence() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) holdReference(data) { let seq = repeatElement(UInt8(1), count: 10) data.append(contentsOf: seq) - XCTAssertEqual(data[data.startIndex.advanced(by: 9)], 9) - XCTAssertEqual(data[data.startIndex.advanced(by: 10)], 1) + #expect(data[data.startIndex.advanced(by: 9)] == 9) + #expect(data[data.startIndex.advanced(by: 10)] == 1) } } - func test_validateMutation_cow_appendContentsOf() { + @Test func test_validateMutation_cow_appendContentsOf() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) holdReference(data) { let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] data.append(contentsOf: bytes) - XCTAssertEqual(data[data.startIndex.advanced(by: 9)], 9) - XCTAssertEqual(data[data.startIndex.advanced(by: 10)], 0) + #expect(data[data.startIndex.advanced(by: 9)] == 9) + #expect(data[data.startIndex.advanced(by: 10)] == 0) } } - func test_validateMutation_cow_resetBytes() { + @Test func test_validateMutation_cow_resetBytes() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) holdReference(data) { data.resetBytes(in: 5..<8) - XCTAssertEqual(data, Data([0, 1, 2, 3, 4, 0, 0, 0, 8, 9])) + #expect(data == Data([0, 1, 2, 3, 4, 0, 0, 0, 8, 9])) } } - func test_validateMutation_cow_replaceSubrange() { + @Test func test_validateMutation_cow_replaceSubrange() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) holdReference(data) { let range: Range = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4)..) in ptr.advanced(by: 1).pointee = 0xFF } - XCTAssertEqual(data, Data([4, 0xFF, 6, 7, 8])) + #expect(data == Data([4, 0xFF, 6, 7, 8])) } } - func test_validateMutation_slice_cow_appendBytes() { + @Test func test_validateMutation_slice_cow_appendBytes() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] holdReference(data) { data.append("hello", count: 5) - XCTAssertEqual(data[data.startIndex.advanced(by: 4)], 0x8) - XCTAssertEqual(data[data.startIndex.advanced(by: 5)], 0x68) + #expect(data[data.startIndex.advanced(by: 4)] == 0x8) + #expect(data[data.startIndex.advanced(by: 5)] == 0x68) } } - func test_validateMutation_slice_cow_appendData() { + @Test func test_validateMutation_slice_cow_appendData() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] holdReference(data) { let other = Data([0xFF, 0xFF]) data.append(other) - XCTAssertEqual(data, Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) + #expect(data == Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) } } - func test_validateMutation_slice_cow_appendBuffer() { + @Test func test_validateMutation_slice_cow_appendBuffer() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] holdReference(data) { let bytes: [UInt8] = [0xFF, 0xFF] bytes.withUnsafeBufferPointer { data.append($0) } - XCTAssertEqual(data, Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) + #expect(data == Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) } } - func test_validateMutation_slice_cow_appendSequence() { + @Test func test_validateMutation_slice_cow_appendSequence() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] holdReference(data) { let seq = repeatElement(UInt8(0xFF), count: 2) data.append(contentsOf: seq) - XCTAssertEqual(data, Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) + #expect(data == Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) } } - func test_validateMutation_slice_cow_appendContentsOf() { + @Test func test_validateMutation_slice_cow_appendContentsOf() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] holdReference(data) { let bytes: [UInt8] = [0xFF, 0xFF] data.append(contentsOf: bytes) - XCTAssertEqual(data, Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) + #expect(data == Data([4, 5, 6, 7, 8, 0xFF, 0xFF])) } } - func test_validateMutation_slice_cow_resetBytes() { + @Test func test_validateMutation_slice_cow_resetBytes() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] holdReference(data) { data.resetBytes(in: 5..<8) - XCTAssertEqual(data, Data([4, 0, 0, 0, 8])) + #expect(data == Data([4, 0, 0, 0, 8])) } } - func test_validateMutation_slice_cow_replaceSubrange() { + @Test func test_validateMutation_slice_cow_replaceSubrange() { var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] holdReference(data) { let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in ptr.advanced(by: 1).pointee = 0xFF } - XCTAssertEqual(data, Data([4, 0xFF])) + #expect(data == Data([4, 0xFF])) } - func test_increaseCount() { + @Test func test_increaseCount() { let initials: [Range] = [ 0..<0, 0..<2, @@ -1466,14 +1506,14 @@ class DataTests : XCTestCase { for diff in diffs { var data = Data(initial) data.count += diff - XCTAssertEqual( - Data(Array(initial) + Array(repeating: 0, count: diff)), - data) + #expect( + Data(Array(initial) + Array(repeating: 0, count: diff)) == data + ) } } } - func test_decreaseCount() { + @Test func test_decreaseCount() { let initials: [Range] = [ 0..<0, 0..<2, @@ -1489,25 +1529,25 @@ class DataTests : XCTestCase { guard initial.count >= diff else { continue } var data = Data(initial) data.count -= diff - XCTAssertEqual( - Data(initial.dropLast(diff)), - data) + #expect( + Data(initial.dropLast(diff)) == data + ) } } } - func test_decrease_increase_count() { + @Test func test_decrease_increase_count() { var data = Data(Array(repeating: 0, count: 8) + [42]) data.count -= 1 - XCTAssertEqual(Data(Array(repeating: 0, count: 8)), data) + #expect(Data(Array(repeating: 0, count: 8)) == data) data.count += 1 - XCTAssertEqual(Data(Array(repeating: 0, count: 9)), data) + #expect(Data(Array(repeating: 0, count: 9)) == data) data = Data(Array(repeating: 0, count: 64) + [42]) data.count -= 1 - XCTAssertEqual(Data(Array(repeating: 0, count: 64)), data) + #expect(Data(Array(repeating: 0, count: 64)) == data) data.count += 1 - XCTAssertEqual(Data(Array(repeating: 0, count: 65)), data) + #expect(Data(Array(repeating: 0, count: 65)) == data) } // This is a (potentially invalid) sequence that produces a configurable number of 42s and has a freely customizable `underestimatedCount`. @@ -1532,26 +1572,26 @@ class DataTests : XCTestCase { } } - func test_init_TestSequence() { + @Test func test_init_TestSequence() { // Underestimated count do { let d = Data(TestSequence(underestimatedCount: 0, count: 10)) - XCTAssertEqual(10, d.count) - XCTAssertEqual(Array(repeating: 42 as UInt8, count: 10), Array(d)) + #expect(10 == d.count) + #expect(Array(repeating: 42 as UInt8, count: 10) == Array(d)) } // Very underestimated count (to exercise realloc path) do { let d = Data(TestSequence(underestimatedCount: 0, count: 1000)) - XCTAssertEqual(1000, d.count) - XCTAssertEqual(Array(repeating: 42 as UInt8, count: 1000), Array(d)) + #expect(1000 == d.count) + #expect(Array(repeating: 42 as UInt8, count: 1000) == Array(d)) } // Exact count do { let d = Data(TestSequence(underestimatedCount: 10, count: 10)) - XCTAssertEqual(10, d.count) - XCTAssertEqual(Array(repeating: 42 as UInt8, count: 10), Array(d)) + #expect(10 == d.count) + #expect(Array(repeating: 42 as UInt8, count: 10) == Array(d)) } // Overestimated count. This is an illegal case, so trapping would be fine. @@ -1559,37 +1599,40 @@ class DataTests : XCTestCase { // handles this case by simply truncating itself to the actual size. do { let d = Data(TestSequence(underestimatedCount: 20, count: 10)) - XCTAssertEqual(10, d.count) - XCTAssertEqual(Array(repeating: 42 as UInt8, count: 10), Array(d)) + #expect(10 == d.count) + #expect(Array(repeating: 42 as UInt8, count: 10) == Array(d)) } } - func test_append_TestSequence() { + @Test func test_append_TestSequence() { let base = Data(Array(repeating: 23 as UInt8, count: 10)) // Underestimated count do { var d = base d.append(contentsOf: TestSequence(underestimatedCount: 0, count: 10)) - XCTAssertEqual(20, d.count) - XCTAssertEqual(Array(base) + Array(repeating: 42 as UInt8, count: 10), - Array(d)) + #expect(20 == d.count) + #expect( + Array(base) + Array(repeating: 42 as UInt8, count: 10) == Array(d) + ) } // Very underestimated count (to exercise realloc path) do { var d = base d.append(contentsOf: TestSequence(underestimatedCount: 0, count: 1000)) - XCTAssertEqual(1010, d.count) - XCTAssertEqual(Array(base) + Array(repeating: 42 as UInt8, count: 1000), Array(d)) + #expect(1010 == d.count) + #expect( + Array(base) + Array(repeating: 42 as UInt8, count: 1000) == Array(d) + ) } // Exact count do { var d = base d.append(contentsOf: TestSequence(underestimatedCount: 10, count: 10)) - XCTAssertEqual(20, d.count) - XCTAssertEqual(Array(base) + Array(repeating: 42 as UInt8, count: 10), Array(d)) + #expect(20 == d.count) + #expect(Array(base) + Array(repeating: 42 as UInt8, count: 10) == Array(d)) } // Overestimated count. This is an illegal case, so trapping would be fine. @@ -1598,49 +1641,49 @@ class DataTests : XCTestCase { do { var d = base d.append(contentsOf: TestSequence(underestimatedCount: 20, count: 10)) - XCTAssertEqual(20, d.count) - XCTAssertEqual(Array(base) + Array(repeating: 42 as UInt8, count: 10), Array(d)) + #expect(20 == d.count) + #expect(Array(base) + Array(repeating: 42 as UInt8, count: 10) == Array(d)) } } - func testAdvancedBy() { + @Test func testAdvancedBy() { let source: Data = Data([1, 42, 64, 8]) - XCTAssertEqual(source.advanced(by: 0), Data([1, 42, 64, 8])) - XCTAssertEqual(source.advanced(by: 2), Data([64, 8])) - XCTAssertEqual(source.advanced(by: 4), Data()) + #expect(source.advanced(by: 0) == Data([1, 42, 64, 8])) + #expect(source.advanced(by: 2) == Data([64, 8])) + #expect(source.advanced(by: 4) == Data()) // Make sure .advanced creates a new data - XCTAssert(source.advanced(by: 3).startIndex == 0) + #expect(source.advanced(by: 3).startIndex == 0) // Make sure .advanced works on Data whose `startIndex` isn't 0 let offsetData: Data = Data([1, 42, 64, 8, 90, 80])[1..<5] - XCTAssertEqual(offsetData.advanced(by: 0), Data([42, 64, 8, 90])) - XCTAssertEqual(offsetData.advanced(by: 2), Data([8, 90])) - XCTAssertEqual(offsetData.advanced(by: 4), Data()) - XCTAssert(offsetData.advanced(by: 3).startIndex == 0) + #expect(offsetData.advanced(by: 0) == Data([42, 64, 8, 90])) + #expect(offsetData.advanced(by: 2) == Data([8, 90])) + #expect(offsetData.advanced(by: 4) == Data()) + #expect(offsetData.advanced(by: 3).startIndex == 0) - // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 + // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 // source.advanced(by: -1) // source.advanced(by: 5) } - #if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 - func test_bounding_failure_subdata() { + #if false // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 + @Test func test_bounding_failure_subdata() { let data = "Hello World".data(using: .utf8)! expectCrashLater() let c = data.subdata(in: 5..<200) } #endif - #if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 - func test_bounding_failure_replace() { + #if false // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 + @Test func test_bounding_failure_replace() { var data = "Hello World".data(using: .utf8)! expectCrashLater() data.replaceSubrange(5..<200, with: Data()) } #endif - #if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 - func test_bounding_failure_replace2() { + #if false // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 + @Test func test_bounding_failure_replace2() { var data = "a".data(using: .utf8)! var bytes : [UInt8] = [1, 2, 3] expectCrashLater() @@ -1651,8 +1694,8 @@ class DataTests : XCTestCase { } #endif - #if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 - func test_bounding_failure_replace3() { + #if false // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 + @Test func test_bounding_failure_replace3() { var data = "a".data(using: .utf8)! var bytes : [UInt8] = [1, 2, 3] expectCrashLater() @@ -1663,8 +1706,8 @@ class DataTests : XCTestCase { } #endif - #if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 - func test_bounding_failure_replace4() { + #if false // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 + @Test func test_bounding_failure_replace4() { var data = "a".data(using: .utf8)! var bytes : [UInt8] = [1, 2, 3] expectCrashLater() @@ -1673,32 +1716,32 @@ class DataTests : XCTestCase { } #endif - #if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 - func test_bounding_failure_reset_range() { + #if false // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 + @Test func test_bounding_failure_reset_range() { var data = "Hello World".data(using: .utf8)! expectCrashLater() data.resetBytes(in: 100..<200) } #endif - #if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 - func test_bounding_failure_append_bad_length() { + #if false // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 + @Test func test_bounding_failure_append_bad_length() { var data = "Hello World".data(using: .utf8)! expectCrashLater() data.append("hello", count: -2) } #endif - #if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 - func test_bounding_failure_append_absurd_length() { + #if false // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 + @Test func test_bounding_failure_append_absurd_length() { var data = "Hello World".data(using: .utf8)! expectCrashLater() data.append("hello", count: Int.min) } #endif - #if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 - func test_bounding_failure_subscript() { + #if false // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 + @Test func test_bounding_failure_subscript() { var data = "Hello World".data(using: .utf8)! expectCrashLater() data[100] = 4 @@ -1708,7 +1751,7 @@ class DataTests : XCTestCase { #if FOUNDATION_FRAMEWORK // FIXME: Re-enable test after String.data(using:) is implemented extension DataTests { - func test_splittingHttp() { + @Test func test_splittingHttp() { func split(_ data: Data, on delimiter: String) -> [Data] { let dataDelimiter = delimiter.data(using: .utf8)! var found = [Data]() @@ -1733,23 +1776,23 @@ extension DataTests { let data = "GET /index.html HTTP/1.1\r\nHost: www.example.com\r\n\r\n".data(using: .ascii)! let fields = split(data, on: "\r\n") let splitFields = fields.map { String(data:$0, encoding: .utf8)! } - XCTAssertEqual([ + #expect([ "GET /index.html HTTP/1.1", "Host: www.example.com", "" - ], splitFields) + ] == splitFields) } - func test_doubleDeallocation() { + @Test func test_doubleDeallocation() { let data = "12345679".data(using: .utf8)! let len = data.withUnsafeUInt8Bytes { (bytes: UnsafePointer) -> Int in let slice = Data(bytesNoCopy: UnsafeMutablePointer(mutating: bytes), count: 1, deallocator: .none) return slice.count } - XCTAssertEqual(len, 1) + #expect(len == 1) } - func test_discontiguousEnumerateBytes() { + @Test func test_discontiguousEnumerateBytes() { let dataToEncode = "Hello World".data(using: .utf8)! let subdata1 = dataToEncode.withUnsafeBytes { bytes in @@ -1768,17 +1811,17 @@ extension DataTests { offsets.append(offset) } - XCTAssertEqual(2, numChunks, "composing two dispatch_data should enumerate as structural data as 2 chunks") - XCTAssertEqual(0, offsets[0], "composing two dispatch_data should enumerate as structural data with the first offset as the location of the region") - XCTAssertEqual(dataToEncode.count, offsets[1], "composing two dispatch_data should enumerate as structural data with the first offset as the location of the region") + #expect(2 == numChunks, "composing two dispatch_data should enumerate as structural data as 2 chunks") + #expect(0 == offsets[0], "composing two dispatch_data should enumerate as structural data with the first offset as the location of the region") + #expect(dataToEncode.count == offsets[1], "composing two dispatch_data should enumerate as structural data with the first offset as the location of the region") } - func test_rangeOfSlice() { + @Test func test_rangeOfSlice() { let data = "FooBar".data(using: .ascii)! let slice = data[3...] // Bar let range = slice.range(of: "a".data(using: .ascii)!) - XCTAssertEqual(range, 4..<5 as Range) + #expect(range == (4..<5 as Range)) } } #endif @@ -1786,65 +1829,68 @@ extension DataTests { // MARK: - Base64 Encode/Decode Tests extension DataTests { - func test_base64Data_small() { + @Test func test_base64Data_small() { let data = Data("Hello World".utf8) let base64 = data.base64EncodedString() - XCTAssertEqual("SGVsbG8gV29ybGQ=", base64, "trivial base64 conversion should work") + #expect("SGVsbG8gV29ybGQ=" == base64, "trivial base64 conversion should work") } - func test_base64Data_medium() { + @Test func test_base64Data_medium() { let data = Data("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut at tincidunt arcu. Suspendisse nec sodales erat, sit amet imperdiet ipsum. Etiam sed ornare felis. Nunc mauris turpis, bibendum non lectus quis, malesuada placerat turpis. Nam adipiscing non massa et semper. Nulla convallis semper bibendum. Aliquam dictum nulla cursus mi ultricies, at tincidunt mi sagittis. Nulla faucibus at dui quis sodales. Morbi rutrum, dui id ultrices venenatis, arcu urna egestas felis, vel suscipit mauris arcu quis risus. Nunc venenatis ligula at orci tristique, et mattis purus pulvinar. Etiam ultricies est odio. Nunc eleifend malesuada justo, nec euismod sem ultrices quis. Etiam nec nibh sit amet lorem faucibus dapibus quis nec leo. Praesent sit amet mauris vel lacus hendrerit porta mollis consectetur mi. Donec eget tortor dui. Morbi imperdiet, arcu sit amet elementum interdum, quam nisl tempor quam, vitae feugiat augue purus sed lacus. In ac urna adipiscing purus venenatis volutpat vel et metus. Nullam nec auctor quam. Phasellus porttitor felis ac nibh gravida suscipit tempus at ante. Nunc pellentesque iaculis sapien a mattis. Aenean eleifend dolor non nunc laoreet, non dictum massa aliquam. Aenean quis turpis augue. Praesent augue lectus, mollis nec elementum eu, dignissim at velit. Ut congue neque id ullamcorper pellentesque. Maecenas euismod in elit eu vehicula. Nullam tristique dui nulla, nec convallis metus suscipit eget. Cras semper augue nec cursus blandit. Nulla rhoncus et odio quis blandit. Praesent lobortis dignissim velit ut pulvinar. Duis interdum quam adipiscing dolor semper semper. Nunc bibendum convallis dui, eget mollis magna hendrerit et. Morbi facilisis, augue eu fringilla convallis, mauris est cursus dolor, eu posuere odio nunc quis orci. Ut eu justo sem. Phasellus ut erat rhoncus, faucibus arcu vitae, vulputate erat. Aliquam nec magna viverra, interdum est vitae, rhoncus sapien. Duis tincidunt tempor ipsum ut dapibus. Nullam commodo varius metus, sed sollicitudin eros. Etiam nec odio et dui tempor blandit posuere.".utf8) let base64 = data.base64EncodedString() - XCTAssertEqual("TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gVXQgYXQgdGluY2lkdW50IGFyY3UuIFN1c3BlbmRpc3NlIG5lYyBzb2RhbGVzIGVyYXQsIHNpdCBhbWV0IGltcGVyZGlldCBpcHN1bS4gRXRpYW0gc2VkIG9ybmFyZSBmZWxpcy4gTnVuYyBtYXVyaXMgdHVycGlzLCBiaWJlbmR1bSBub24gbGVjdHVzIHF1aXMsIG1hbGVzdWFkYSBwbGFjZXJhdCB0dXJwaXMuIE5hbSBhZGlwaXNjaW5nIG5vbiBtYXNzYSBldCBzZW1wZXIuIE51bGxhIGNvbnZhbGxpcyBzZW1wZXIgYmliZW5kdW0uIEFsaXF1YW0gZGljdHVtIG51bGxhIGN1cnN1cyBtaSB1bHRyaWNpZXMsIGF0IHRpbmNpZHVudCBtaSBzYWdpdHRpcy4gTnVsbGEgZmF1Y2lidXMgYXQgZHVpIHF1aXMgc29kYWxlcy4gTW9yYmkgcnV0cnVtLCBkdWkgaWQgdWx0cmljZXMgdmVuZW5hdGlzLCBhcmN1IHVybmEgZWdlc3RhcyBmZWxpcywgdmVsIHN1c2NpcGl0IG1hdXJpcyBhcmN1IHF1aXMgcmlzdXMuIE51bmMgdmVuZW5hdGlzIGxpZ3VsYSBhdCBvcmNpIHRyaXN0aXF1ZSwgZXQgbWF0dGlzIHB1cnVzIHB1bHZpbmFyLiBFdGlhbSB1bHRyaWNpZXMgZXN0IG9kaW8uIE51bmMgZWxlaWZlbmQgbWFsZXN1YWRhIGp1c3RvLCBuZWMgZXVpc21vZCBzZW0gdWx0cmljZXMgcXVpcy4gRXRpYW0gbmVjIG5pYmggc2l0IGFtZXQgbG9yZW0gZmF1Y2lidXMgZGFwaWJ1cyBxdWlzIG5lYyBsZW8uIFByYWVzZW50IHNpdCBhbWV0IG1hdXJpcyB2ZWwgbGFjdXMgaGVuZHJlcml0IHBvcnRhIG1vbGxpcyBjb25zZWN0ZXR1ciBtaS4gRG9uZWMgZWdldCB0b3J0b3IgZHVpLiBNb3JiaSBpbXBlcmRpZXQsIGFyY3Ugc2l0IGFtZXQgZWxlbWVudHVtIGludGVyZHVtLCBxdWFtIG5pc2wgdGVtcG9yIHF1YW0sIHZpdGFlIGZldWdpYXQgYXVndWUgcHVydXMgc2VkIGxhY3VzLiBJbiBhYyB1cm5hIGFkaXBpc2NpbmcgcHVydXMgdmVuZW5hdGlzIHZvbHV0cGF0IHZlbCBldCBtZXR1cy4gTnVsbGFtIG5lYyBhdWN0b3IgcXVhbS4gUGhhc2VsbHVzIHBvcnR0aXRvciBmZWxpcyBhYyBuaWJoIGdyYXZpZGEgc3VzY2lwaXQgdGVtcHVzIGF0IGFudGUuIE51bmMgcGVsbGVudGVzcXVlIGlhY3VsaXMgc2FwaWVuIGEgbWF0dGlzLiBBZW5lYW4gZWxlaWZlbmQgZG9sb3Igbm9uIG51bmMgbGFvcmVldCwgbm9uIGRpY3R1bSBtYXNzYSBhbGlxdWFtLiBBZW5lYW4gcXVpcyB0dXJwaXMgYXVndWUuIFByYWVzZW50IGF1Z3VlIGxlY3R1cywgbW9sbGlzIG5lYyBlbGVtZW50dW0gZXUsIGRpZ25pc3NpbSBhdCB2ZWxpdC4gVXQgY29uZ3VlIG5lcXVlIGlkIHVsbGFtY29ycGVyIHBlbGxlbnRlc3F1ZS4gTWFlY2VuYXMgZXVpc21vZCBpbiBlbGl0IGV1IHZlaGljdWxhLiBOdWxsYW0gdHJpc3RpcXVlIGR1aSBudWxsYSwgbmVjIGNvbnZhbGxpcyBtZXR1cyBzdXNjaXBpdCBlZ2V0LiBDcmFzIHNlbXBlciBhdWd1ZSBuZWMgY3Vyc3VzIGJsYW5kaXQuIE51bGxhIHJob25jdXMgZXQgb2RpbyBxdWlzIGJsYW5kaXQuIFByYWVzZW50IGxvYm9ydGlzIGRpZ25pc3NpbSB2ZWxpdCB1dCBwdWx2aW5hci4gRHVpcyBpbnRlcmR1bSBxdWFtIGFkaXBpc2NpbmcgZG9sb3Igc2VtcGVyIHNlbXBlci4gTnVuYyBiaWJlbmR1bSBjb252YWxsaXMgZHVpLCBlZ2V0IG1vbGxpcyBtYWduYSBoZW5kcmVyaXQgZXQuIE1vcmJpIGZhY2lsaXNpcywgYXVndWUgZXUgZnJpbmdpbGxhIGNvbnZhbGxpcywgbWF1cmlzIGVzdCBjdXJzdXMgZG9sb3IsIGV1IHBvc3VlcmUgb2RpbyBudW5jIHF1aXMgb3JjaS4gVXQgZXUganVzdG8gc2VtLiBQaGFzZWxsdXMgdXQgZXJhdCByaG9uY3VzLCBmYXVjaWJ1cyBhcmN1IHZpdGFlLCB2dWxwdXRhdGUgZXJhdC4gQWxpcXVhbSBuZWMgbWFnbmEgdml2ZXJyYSwgaW50ZXJkdW0gZXN0IHZpdGFlLCByaG9uY3VzIHNhcGllbi4gRHVpcyB0aW5jaWR1bnQgdGVtcG9yIGlwc3VtIHV0IGRhcGlidXMuIE51bGxhbSBjb21tb2RvIHZhcml1cyBtZXR1cywgc2VkIHNvbGxpY2l0dWRpbiBlcm9zLiBFdGlhbSBuZWMgb2RpbyBldCBkdWkgdGVtcG9yIGJsYW5kaXQgcG9zdWVyZS4=", base64, "medium base64 conversion should work") + #expect( + "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gVXQgYXQgdGluY2lkdW50IGFyY3UuIFN1c3BlbmRpc3NlIG5lYyBzb2RhbGVzIGVyYXQsIHNpdCBhbWV0IGltcGVyZGlldCBpcHN1bS4gRXRpYW0gc2VkIG9ybmFyZSBmZWxpcy4gTnVuYyBtYXVyaXMgdHVycGlzLCBiaWJlbmR1bSBub24gbGVjdHVzIHF1aXMsIG1hbGVzdWFkYSBwbGFjZXJhdCB0dXJwaXMuIE5hbSBhZGlwaXNjaW5nIG5vbiBtYXNzYSBldCBzZW1wZXIuIE51bGxhIGNvbnZhbGxpcyBzZW1wZXIgYmliZW5kdW0uIEFsaXF1YW0gZGljdHVtIG51bGxhIGN1cnN1cyBtaSB1bHRyaWNpZXMsIGF0IHRpbmNpZHVudCBtaSBzYWdpdHRpcy4gTnVsbGEgZmF1Y2lidXMgYXQgZHVpIHF1aXMgc29kYWxlcy4gTW9yYmkgcnV0cnVtLCBkdWkgaWQgdWx0cmljZXMgdmVuZW5hdGlzLCBhcmN1IHVybmEgZWdlc3RhcyBmZWxpcywgdmVsIHN1c2NpcGl0IG1hdXJpcyBhcmN1IHF1aXMgcmlzdXMuIE51bmMgdmVuZW5hdGlzIGxpZ3VsYSBhdCBvcmNpIHRyaXN0aXF1ZSwgZXQgbWF0dGlzIHB1cnVzIHB1bHZpbmFyLiBFdGlhbSB1bHRyaWNpZXMgZXN0IG9kaW8uIE51bmMgZWxlaWZlbmQgbWFsZXN1YWRhIGp1c3RvLCBuZWMgZXVpc21vZCBzZW0gdWx0cmljZXMgcXVpcy4gRXRpYW0gbmVjIG5pYmggc2l0IGFtZXQgbG9yZW0gZmF1Y2lidXMgZGFwaWJ1cyBxdWlzIG5lYyBsZW8uIFByYWVzZW50IHNpdCBhbWV0IG1hdXJpcyB2ZWwgbGFjdXMgaGVuZHJlcml0IHBvcnRhIG1vbGxpcyBjb25zZWN0ZXR1ciBtaS4gRG9uZWMgZWdldCB0b3J0b3IgZHVpLiBNb3JiaSBpbXBlcmRpZXQsIGFyY3Ugc2l0IGFtZXQgZWxlbWVudHVtIGludGVyZHVtLCBxdWFtIG5pc2wgdGVtcG9yIHF1YW0sIHZpdGFlIGZldWdpYXQgYXVndWUgcHVydXMgc2VkIGxhY3VzLiBJbiBhYyB1cm5hIGFkaXBpc2NpbmcgcHVydXMgdmVuZW5hdGlzIHZvbHV0cGF0IHZlbCBldCBtZXR1cy4gTnVsbGFtIG5lYyBhdWN0b3IgcXVhbS4gUGhhc2VsbHVzIHBvcnR0aXRvciBmZWxpcyBhYyBuaWJoIGdyYXZpZGEgc3VzY2lwaXQgdGVtcHVzIGF0IGFudGUuIE51bmMgcGVsbGVudGVzcXVlIGlhY3VsaXMgc2FwaWVuIGEgbWF0dGlzLiBBZW5lYW4gZWxlaWZlbmQgZG9sb3Igbm9uIG51bmMgbGFvcmVldCwgbm9uIGRpY3R1bSBtYXNzYSBhbGlxdWFtLiBBZW5lYW4gcXVpcyB0dXJwaXMgYXVndWUuIFByYWVzZW50IGF1Z3VlIGxlY3R1cywgbW9sbGlzIG5lYyBlbGVtZW50dW0gZXUsIGRpZ25pc3NpbSBhdCB2ZWxpdC4gVXQgY29uZ3VlIG5lcXVlIGlkIHVsbGFtY29ycGVyIHBlbGxlbnRlc3F1ZS4gTWFlY2VuYXMgZXVpc21vZCBpbiBlbGl0IGV1IHZlaGljdWxhLiBOdWxsYW0gdHJpc3RpcXVlIGR1aSBudWxsYSwgbmVjIGNvbnZhbGxpcyBtZXR1cyBzdXNjaXBpdCBlZ2V0LiBDcmFzIHNlbXBlciBhdWd1ZSBuZWMgY3Vyc3VzIGJsYW5kaXQuIE51bGxhIHJob25jdXMgZXQgb2RpbyBxdWlzIGJsYW5kaXQuIFByYWVzZW50IGxvYm9ydGlzIGRpZ25pc3NpbSB2ZWxpdCB1dCBwdWx2aW5hci4gRHVpcyBpbnRlcmR1bSBxdWFtIGFkaXBpc2NpbmcgZG9sb3Igc2VtcGVyIHNlbXBlci4gTnVuYyBiaWJlbmR1bSBjb252YWxsaXMgZHVpLCBlZ2V0IG1vbGxpcyBtYWduYSBoZW5kcmVyaXQgZXQuIE1vcmJpIGZhY2lsaXNpcywgYXVndWUgZXUgZnJpbmdpbGxhIGNvbnZhbGxpcywgbWF1cmlzIGVzdCBjdXJzdXMgZG9sb3IsIGV1IHBvc3VlcmUgb2RpbyBudW5jIHF1aXMgb3JjaS4gVXQgZXUganVzdG8gc2VtLiBQaGFzZWxsdXMgdXQgZXJhdCByaG9uY3VzLCBmYXVjaWJ1cyBhcmN1IHZpdGFlLCB2dWxwdXRhdGUgZXJhdC4gQWxpcXVhbSBuZWMgbWFnbmEgdml2ZXJyYSwgaW50ZXJkdW0gZXN0IHZpdGFlLCByaG9uY3VzIHNhcGllbi4gRHVpcyB0aW5jaWR1bnQgdGVtcG9yIGlwc3VtIHV0IGRhcGlidXMuIE51bGxhbSBjb21tb2RvIHZhcml1cyBtZXR1cywgc2VkIHNvbGxpY2l0dWRpbiBlcm9zLiBFdGlhbSBuZWMgb2RpbyBldCBkdWkgdGVtcG9yIGJsYW5kaXQgcG9zdWVyZS4=" == base64, + "medium base64 conversion should work" + ) } - func test_AnyHashableContainingData() { + @Test func test_AnyHashableContainingData() { let values: [Data] = [ Data(base64Encoded: "AAAA")!, Data(base64Encoded: "AAAB")!, Data(base64Encoded: "AAAB")!, ] let anyHashables = values.map(AnyHashable.init) - expectEqual(Data.self, type(of: anyHashables[0].base)) - expectEqual(Data.self, type(of: anyHashables[1].base)) - expectEqual(Data.self, type(of: anyHashables[2].base)) - XCTAssertNotEqual(anyHashables[0], anyHashables[1]) - XCTAssertEqual(anyHashables[1], anyHashables[2]) + #expect(Data.self == type(of: anyHashables[0].base)) + #expect(Data.self == type(of: anyHashables[1].base)) + #expect(Data.self == type(of: anyHashables[2].base)) + #expect(anyHashables[0] != anyHashables[1]) + #expect(anyHashables[1] == anyHashables[2]) } - func test_replaceSubrange() { + @Test func test_replaceSubrange() { // https://bugs.swift.org/browse/SR-4462 let data = Data([0x01, 0x02]) var dataII = Data(base64Encoded: data.base64EncodedString())! dataII.replaceSubrange(0..<1, with: Data()) - XCTAssertEqual(dataII[0], 0x02) + #expect(dataII[0] == 0x02) } } #if FOUNDATION_FRAMEWORK // FIXME: Re-enable tests once range(of:) is implemented extension DataTests { - func testRange() { + @Test func testRange() { let helloWorld = dataFrom("Hello World") let goodbye = dataFrom("Goodbye") let hello = dataFrom("Hello") do { let found = helloWorld.range(of: goodbye) - XCTAssertNil(found) + #expect(found == nil) } do { let found = helloWorld.range(of: goodbye, options: .anchored) - XCTAssertNil(found) + #expect(found == nil) } do { let found = helloWorld.range(of: hello, in: 7.. testInterval1) + #expect(testInterval1 < testInterval3) + #expect(testInterval3 > testInterval1) // dateWithString("2009-05-17 14:49:47 -0700") let earlierStart = Date(timeIntervalSinceReferenceDate: 264289787.0) let testInterval4 = DateInterval(start: earlierStart, duration: duration) - - XCTAssertTrue(testInterval4 < testInterval1) - XCTAssertTrue(testInterval1 > testInterval4) + #expect(testInterval4 < testInterval1) + #expect(testInterval1 > testInterval4) } - func test_isEqualToDateInterval() { + @Test func testIsEqualToDateInterval() { // dateWithString("2010-05-17 14:49:47 -0700") let start = Date(timeIntervalSinceReferenceDate: 295825787.0) let duration = 10000000.0 let testInterval1 = DateInterval(start: start, duration: duration) let testInterval2 = DateInterval(start: start, duration: duration) - - XCTAssertEqual(testInterval1, testInterval2) + #expect(testInterval1 == testInterval2) let testInterval3 = DateInterval(start: start, duration: 100.0) - XCTAssertNotEqual(testInterval1, testInterval3) - } - - func test_hashing() { - // dateWithString("2019-04-04 17:09:23 -0700") - let start1a = Date(timeIntervalSinceReferenceDate: 576115763.0) - let start1b = Date(timeIntervalSinceReferenceDate: 576115763.0) - let start2a = Date(timeIntervalSinceReferenceDate: start1a.timeIntervalSinceReferenceDate.nextUp) - let start2b = Date(timeIntervalSinceReferenceDate: start1a.timeIntervalSinceReferenceDate.nextUp) - let duration1 = 1800.0 - let duration2 = duration1.nextUp - let intervals: [[DateInterval]] = [ - [ - DateInterval(start: start1a, duration: duration1), - DateInterval(start: start1b, duration: duration1), - ], - [ - DateInterval(start: start1a, duration: duration2), - DateInterval(start: start1b, duration: duration2), - ], - [ - DateInterval(start: start2a, duration: duration1), - DateInterval(start: start2b, duration: duration1), - ], - [ - DateInterval(start: start2a, duration: duration2), - DateInterval(start: start2b, duration: duration2), - ], - ] - checkHashableGroups(intervals) + #expect(testInterval1 != testInterval3) } - func test_checkIntersection() { + @Test func testCheckIntersection() { // dateWithString("2010-05-17 14:49:47 -0700") let start1 = Date(timeIntervalSinceReferenceDate: 295825787.0) // dateWithString("2010-08-17 14:49:47 -0700") @@ -95,7 +66,7 @@ final class DateIntervalTests : XCTestCase { let testInterval2 = DateInterval(start: start2, end: end2) - XCTAssertTrue(testInterval1.intersects(testInterval2)) + #expect(testInterval1.intersects(testInterval2)) // dateWithString("2010-10-17 14:49:47 -0700") let start3 = Date(timeIntervalSinceReferenceDate: 309044987.0) @@ -104,10 +75,10 @@ final class DateIntervalTests : XCTestCase { let testInterval3 = DateInterval(start: start3, end: end3) - XCTAssertFalse(testInterval1.intersects(testInterval3)) + #expect(!testInterval1.intersects(testInterval3)) } - func test_validIntersections() { + @Test func testValidIntersections() { // dateWithString("2010-05-17 14:49:47 -0700") let start1 = Date(timeIntervalSinceReferenceDate: 295825787.0) // dateWithString("2010-08-17 14:49:47 -0700") @@ -130,15 +101,15 @@ final class DateIntervalTests : XCTestCase { let testInterval3 = DateInterval(start: start3, end: end3) let intersection1 = testInterval2.intersection(with: testInterval1) - XCTAssertNotNil(intersection1) - XCTAssertEqual(testInterval3, intersection1) + #expect(intersection1 != nil) + #expect(testInterval3 == intersection1) let intersection2 = testInterval1.intersection(with: testInterval2) - XCTAssertNotNil(intersection2) - XCTAssertEqual(intersection1, intersection2) + #expect(intersection2 != nil) + #expect(intersection1 == intersection2) } - func test_containsDate() { + @Test func testContainsDate() { // dateWithString("2010-05-17 14:49:47 -0700") let start = Date(timeIntervalSinceReferenceDate: 295825787.0) let duration = 10000000.0 @@ -147,14 +118,14 @@ final class DateIntervalTests : XCTestCase { // dateWithString("2010-05-17 20:49:47 -0700") let containedDate = Date(timeIntervalSinceReferenceDate: 295847387.0) - XCTAssertTrue(testInterval.contains(containedDate)) + #expect(testInterval.contains(containedDate)) // dateWithString("2009-05-17 14:49:47 -0700") let earlierStart = Date(timeIntervalSinceReferenceDate: 264289787.0) - XCTAssertFalse(testInterval.contains(earlierStart)) + #expect(!testInterval.contains(earlierStart)) } - func test_AnyHashableContainingDateInterval() { + @Test func testAnyHashableContainingDateInterval() { // dateWithString("2010-05-17 14:49:47 -0700") let start = Date(timeIntervalSinceReferenceDate: 295825787.0) let duration = 10000000.0 @@ -164,18 +135,18 @@ final class DateIntervalTests : XCTestCase { DateInterval(start: start, duration: duration / 2), ] let anyHashables = values.map(AnyHashable.init) - expectEqual(DateInterval.self, type(of: anyHashables[0].base)) - expectEqual(DateInterval.self, type(of: anyHashables[1].base)) - expectEqual(DateInterval.self, type(of: anyHashables[2].base)) - XCTAssertNotEqual(anyHashables[0], anyHashables[1]) - XCTAssertEqual(anyHashables[1], anyHashables[2]) + #expect(DateInterval.self == type(of: anyHashables[0].base)) + #expect(DateInterval.self == type(of: anyHashables[1].base)) + #expect(DateInterval.self == type(of: anyHashables[2].base)) + #expect(anyHashables[0] != anyHashables[1]) + #expect(anyHashables[1] == anyHashables[2]) } } // MARK: - Bridging Tests #if FOUNDATION_FRAMEWORK extension DateIntervalTests { - func test_AnyHashableCreatedFromNSDateInterval() { + @Test func testAnyHashableCreatedFromNSDateInterval() { // dateWithString("2010-05-17 14:49:47 -0700") let start = Date(timeIntervalSinceReferenceDate: 295825787.0) let duration = 10000000.0 @@ -185,11 +156,11 @@ extension DateIntervalTests { NSDateInterval(start: start, duration: duration / 2), ] let anyHashables = values.map(AnyHashable.init) - expectEqual(DateInterval.self, type(of: anyHashables[0].base)) - expectEqual(DateInterval.self, type(of: anyHashables[1].base)) - expectEqual(DateInterval.self, type(of: anyHashables[2].base)) - XCTAssertNotEqual(anyHashables[0], anyHashables[1]) - XCTAssertEqual(anyHashables[1], anyHashables[2]) + #expect(DateInterval.self == type(of: anyHashables[0].base)) + #expect(DateInterval.self == type(of: anyHashables[1].base)) + #expect(DateInterval.self == type(of: anyHashables[2].base)) + #expect(anyHashables[0] != anyHashables[1]) + #expect(anyHashables[1] == anyHashables[2]) } } #endif diff --git a/Tests/FoundationEssentialsTests/DateTests.swift b/Tests/FoundationEssentialsTests/DateTests.swift index 343a1f1c8..7dfd01ec1 100644 --- a/Tests/FoundationEssentialsTests/DateTests.swift +++ b/Tests/FoundationEssentialsTests/DateTests.swift @@ -10,141 +10,139 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing #if canImport(FoundationEssentials) @testable import FoundationEssentials #endif -final class DateTests : XCTestCase { +struct DateTests { - func testDateComparison() { + @Test func testDateComparison() { let d1 = Date() let d2 = d1 + 1 - XCTAssertGreaterThan(d2, d1) - XCTAssertLessThan(d1, d2) + #expect(d2 > d1) + #expect(d1 < d2) let d3 = Date(timeIntervalSince1970: 12345) let d4 = Date(timeIntervalSince1970: 12345) - XCTAssertEqual(d3, d4) - XCTAssertLessThanOrEqual(d3, d4) - XCTAssertGreaterThanOrEqual(d4, d3) + #expect(d3 == d4) + #expect(d3 <= d4) + #expect(d4 >= d3) } - func testDateMutation() { + @Test func testDateMutation() { let d0 = Date() var d1 = Date() d1 = d1 + 1.0 let d2 = Date(timeIntervalSinceNow: 10) - XCTAssertGreaterThan(d2, d1) - XCTAssertNotEqual(d1, d0) + #expect(d2 > d1) + #expect(d1 != d0) let d3 = d1 d1 += 10 - XCTAssertGreaterThan(d1, d3) + #expect(d1 > d3) } - func testDistantPast() { + @Test func testDistantPast() { let distantPast = Date.distantPast let currentDate = Date() - XCTAssertLessThan(distantPast, currentDate) - XCTAssertGreaterThan(currentDate, distantPast) - XCTAssertLessThan(distantPast.timeIntervalSince(currentDate), - 3600.0 * 24 * 365 * 100) /* ~1 century in seconds */ + #expect(distantPast < currentDate) + #expect(currentDate > distantPast) + #expect(distantPast.timeIntervalSince(currentDate) < + 3600.0 * 24 * 365 * 100) /* ~1 century in seconds */ } - func testDistantFuture() { + @Test func testDistantFuture() { let distantFuture = Date.distantFuture let currentDate = Date() - XCTAssertLessThan(currentDate, distantFuture) - XCTAssertGreaterThan(distantFuture, currentDate) - XCTAssertGreaterThan(distantFuture.timeIntervalSince(currentDate), - 3600.0 * 24 * 365 * 100) /* ~1 century in seconds */ + #expect(currentDate < distantFuture) + #expect(distantFuture > currentDate) + #expect(distantFuture.timeIntervalSince(currentDate) > + 3600.0 * 24 * 365 * 100) /* ~1 century in seconds */ } @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) - func test_now() { + @Test func test_now() { let date1 : Date = .now let date2 : Date = .now - XCTAssertLessThanOrEqual(date1, date2) + #expect(date1 <= date2) } - func testDescriptionReferenceDate() { + @Test func testDescriptionReferenceDate() { let date = Date(timeIntervalSinceReferenceDate: TimeInterval(0)) - XCTAssertEqual("2001-01-01 00:00:00 +0000", date.description) + #expect("2001-01-01 00:00:00 +0000" == date.description) } - func testDescription1970() { + @Test func testDescription1970() { let date = Date(timeIntervalSince1970: TimeInterval(0)) - XCTAssertEqual("1970-01-01 00:00:00 +0000", date.description) + #expect("1970-01-01 00:00:00 +0000" == date.description) } - func testDescriptionDistantPast() { + @Test func testDescriptionDistantPast() { #if FOUNDATION_FRAMEWORK - XCTAssertEqual("0001-01-01 00:00:00 +0000", Date.distantPast.description) + #expect("0001-01-01 00:00:00 +0000" == Date.distantPast.description) #else - XCTAssertEqual("0000-12-30 00:00:00 +0000", Date.distantPast.description) + #expect("0000-12-30 00:00:00 +0000" == Date.distantPast.description) #endif } - func testDescriptionDistantFuture() { - XCTAssertEqual("4001-01-01 00:00:00 +0000", Date.distantFuture.description) + @Test func testDescriptionDistantFuture() { + #expect("4001-01-01 00:00:00 +0000" == Date.distantFuture.description) } - func testDescriptionBeyondDistantPast() { + @Test func testDescriptionBeyondDistantPast() { let date = Date.distantPast.addingTimeInterval(TimeInterval(-1)) #if FOUNDATION_FRAMEWORK - XCTAssertEqual("0000-12-31 23:59:59 +0000", date.description) + #expect("0000-12-31 23:59:59 +0000" == date.description) #else - XCTAssertEqual("", date.description) + #expect("" == date.description) #endif } - func testDescriptionBeyondDistantFuture() { + @Test func testDescriptionBeyondDistantFuture() { let date = Date.distantFuture.addingTimeInterval(TimeInterval(1)) #if FOUNDATION_FRAMEWORK - XCTAssertEqual("4001-01-01 00:00:01 +0000", date.description) + #expect("4001-01-01 00:00:01 +0000" == date.description) #else - XCTAssertEqual("", date.description) + #expect("" == date.description) #endif } } // MARK: - Bridging #if FOUNDATION_FRAMEWORK -final class DateBridgingTests : XCTestCase { - func testCast() { +struct DateBridgingTests { + @Test func testCast() { let d0 = NSDate() let d1 = d0 as Date - XCTAssertEqual(d0.timeIntervalSinceReferenceDate, d1.timeIntervalSinceReferenceDate) + #expect(d0.timeIntervalSinceReferenceDate == d1.timeIntervalSinceReferenceDate) } - func test_AnyHashableCreatedFromNSDate() { + @Test func test_AnyHashableCreatedFromNSDate() { let values: [NSDate] = [ NSDate(timeIntervalSince1970: 1000000000), NSDate(timeIntervalSince1970: 1000000001), NSDate(timeIntervalSince1970: 1000000001), ] let anyHashables = values.map(AnyHashable.init) - expectEqual(Date.self, type(of: anyHashables[0].base)) - expectEqual(Date.self, type(of: anyHashables[1].base)) - expectEqual(Date.self, type(of: anyHashables[2].base)) - XCTAssertNotEqual(anyHashables[0], anyHashables[1]) - XCTAssertEqual(anyHashables[1], anyHashables[2]) + #expect(Date.self == type(of: anyHashables[0].base)) + #expect(Date.self == type(of: anyHashables[1].base)) + #expect(Date.self == type(of: anyHashables[2].base)) + #expect(anyHashables[0] != anyHashables[1]) + #expect(anyHashables[1] == anyHashables[2]) } - func test_AnyHashableCreatedFromNSDateComponents() { + @Test func test_AnyHashableCreatedFromNSDateComponents() { func makeNSDateComponents(year: Int) -> NSDateComponents { let result = NSDateComponents() result.year = year @@ -156,15 +154,15 @@ final class DateBridgingTests : XCTestCase { makeNSDateComponents(year: 1995), ] let anyHashables = values.map(AnyHashable.init) - expectEqual(DateComponents.self, type(of: anyHashables[0].base)) - expectEqual(DateComponents.self, type(of: anyHashables[1].base)) - expectEqual(DateComponents.self, type(of: anyHashables[2].base)) - XCTAssertNotEqual(anyHashables[0], anyHashables[1]) - XCTAssertEqual(anyHashables[1], anyHashables[2]) + #expect(DateComponents.self == type(of: anyHashables[0].base)) + #expect(DateComponents.self == type(of: anyHashables[1].base)) + #expect(DateComponents.self == type(of: anyHashables[2].base)) + #expect(anyHashables[0] != anyHashables[1]) + #expect(anyHashables[1] == anyHashables[2]) } - func test_dateComponents_unconditionallyBridgeFromObjectiveC() { - XCTAssertEqual(DateComponents(), DateComponents._unconditionallyBridgeFromObjectiveC(nil)) + @Test func test_dateComponents_unconditionallyBridgeFromObjectiveC() { + #expect(DateComponents() == DateComponents._unconditionallyBridgeFromObjectiveC(nil)) } } #endif // FOUNDATION_FRAMEWORK diff --git a/Tests/FoundationEssentialsTests/ErrorTests.swift b/Tests/FoundationEssentialsTests/ErrorTests.swift index f3cc6b3ce..6c70b32ba 100644 --- a/Tests/FoundationEssentialsTests/ErrorTests.swift +++ b/Tests/FoundationEssentialsTests/ErrorTests.swift @@ -10,20 +10,20 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing -#if canImport(FoundationEssentials) +#if FOUNDATION_FRAMEWORK +@testable import Foundation +#else @testable import FoundationEssentials #endif -final class ErrorTests : XCTestCase { +struct ErrorTests { func thisThrows() throws { throw CocoaError(CocoaError.Code(rawValue: 42), userInfo: ["hi" : "there"]) } - func test_throwCocoaError() { + @Test func test_throwCocoaError() { let code: CocoaError.Code do { try thisThrows() @@ -36,6 +36,6 @@ final class ErrorTests : XCTestCase { } } - XCTAssertEqual(code.rawValue, 42) + #expect(code.rawValue == 42) } } diff --git a/Tests/FoundationEssentialsTests/FileManager/FileManagerPlayground.swift b/Tests/FoundationEssentialsTests/FileManager/FileManagerPlayground.swift index 52ce3cc5b..873da38ca 100644 --- a/Tests/FoundationEssentialsTests/FileManager/FileManagerPlayground.swift +++ b/Tests/FoundationEssentialsTests/FileManager/FileManagerPlayground.swift @@ -10,9 +10,7 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing #if FOUNDATION_FRAMEWORK @testable import Foundation @@ -112,9 +110,23 @@ struct FileManagerPlayground { fileManager.delegate = capturingDelegate } let createdDir = tempDir.appendingPathComponent(directory.name) - XCTAssertTrue(fileManager.changeCurrentDirectoryPath(createdDir), "Failed to change CWD to the newly created playground directory", file: file, line: line) + #expect( + fileManager.changeCurrentDirectoryPath(createdDir), + "Failed to change CWD to the newly created playground directory", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) try tester(fileManager) - XCTAssertTrue(fileManager.changeCurrentDirectoryPath(previousCWD), "Failed to change CWD back to the original directory", file: file, line: line) + #expect( + fileManager.changeCurrentDirectoryPath(previousCWD), + "Failed to change CWD back to the original directory", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) try fileManager.removeItem(atPath: createdDir) } } diff --git a/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift b/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift index ec4fab120..bd96192be 100644 --- a/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift +++ b/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift @@ -10,10 +10,13 @@ // //===----------------------------------------------------------------------===// +@_spi(Experimental) import Testing -#if canImport(TestSupport) -import TestSupport -#endif // canImport(TestSupport) +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#endif #if canImport(FoundationEssentials) @testable import FoundationEssentials @@ -169,21 +172,24 @@ class CapturingFileManagerDelegate : FileManagerDelegate { } #endif -final class FileManagerTests : XCTestCase { +// `FileManagerTests` has global states (directories created etc) +// therefore they must run in serial. +@Suite(.serial) +struct FileManagerTests { private func randomData(count: Int = 10000) -> Data { Data((0 ..< count).map { _ in UInt8.random(in: .min ..< .max) }) } - func testContentsAtPath() throws { + @Test func testContentsAtPath() throws { let data = randomData() try FileManagerPlayground { File("test", contents: data) }.test { - XCTAssertEqual($0.contents(atPath: "test"), data) + #expect($0.contents(atPath: "test") == data) } } - func testContentsEqualAtPaths() throws { + @Test func testContentsEqualAtPaths() throws { try FileManagerPlayground { Directory("dir1") { Directory("dir2") { @@ -213,13 +219,13 @@ final class FileManagerTests : XCTestCase { } } }.test { - XCTAssertTrue($0.contentsEqual(atPath: "dir1", andPath: "dir1_copy")) - XCTAssertFalse($0.contentsEqual(atPath: "dir1/dir2", andPath: "dir1/dir3")) - XCTAssertFalse($0.contentsEqual(atPath: "dir1", andPath: "dir1_diffdata")) + #expect($0.contentsEqual(atPath: "dir1", andPath: "dir1_copy")) + #expect($0.contentsEqual(atPath: "dir1/dir2", andPath: "dir1/dir3") == false) + #expect($0.contentsEqual(atPath: "dir1", andPath: "dir1_diffdata") == false) } } - func testDirectoryContentsAtPath() throws { + @Test func testDirectoryContentsAtPath() throws { try FileManagerPlayground { Directory("dir1") { Directory("dir2") { @@ -230,17 +236,26 @@ final class FileManagerTests : XCTestCase { "Baz" } } - }.test { - XCTAssertEqual(try $0.contentsOfDirectory(atPath: "dir1").sorted(), ["dir2", "dir3"]) - XCTAssertEqual(try $0.contentsOfDirectory(atPath: "dir1/dir2").sorted(), ["Bar", "Foo"]) - XCTAssertEqual(try $0.contentsOfDirectory(atPath: "dir1/dir3").sorted(), ["Baz"]) - XCTAssertThrowsError(try $0.contentsOfDirectory(atPath: "does_not_exist")) { - XCTAssertEqual(($0 as? CocoaError)?.code, .fileReadNoSuchFile) + }.test { (fileManager) throws in + var results = try fileManager.contentsOfDirectory(atPath: "dir1").sorted() + #expect(results == ["dir2", "dir3"]) + results = try fileManager.contentsOfDirectory(atPath: "dir1/dir2").sorted() + #expect(results == ["Bar", "Foo"]) + results = try fileManager.contentsOfDirectory(atPath: "dir1/dir3").sorted() + #expect(results == ["Baz"]) + #expect { + try fileManager.contentsOfDirectory(atPath: "does_not_exist") + } throws: { error in + guard let cocoaError = error as? CocoaError else { + return false + } + #expect(cocoaError.code == .fileReadNoSuchFile) + return true } } } - func testSubpathsOfDirectoryAtPath() throws { + @Test func testSubpathsOfDirectoryAtPath() throws { try FileManagerPlayground { Directory("dir1") { Directory("dir2") { @@ -251,114 +266,171 @@ final class FileManagerTests : XCTestCase { "Baz" } } - }.test { - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: "dir1").sorted(), ["dir2", "dir2/Bar", "dir2/Foo", "dir3", "dir3/Baz"]) - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: "dir1/dir2").sorted(), ["Bar", "Foo"]) - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: "dir1/dir3").sorted(), ["Baz"]) - XCTAssertThrowsError(try $0.subpathsOfDirectory(atPath: "does_not_exist")) { - XCTAssertEqual(($0 as? CocoaError)?.code, .fileReadNoSuchFile) + }.test { (fileManager) throws in + var results = try fileManager.subpathsOfDirectory(atPath: "dir1").sorted() + #expect(results == ["dir2", "dir2/Bar", "dir2/Foo", "dir3", "dir3/Baz"]) + results = try fileManager.subpathsOfDirectory(atPath: "dir1/dir2").sorted() + #expect(results == ["Bar", "Foo"]) + results = try fileManager.subpathsOfDirectory(atPath: "dir1/dir3").sorted() + #expect(results == ["Baz"]) + #expect { + try fileManager.subpathsOfDirectory(atPath: "does_not_exist") + } throws: { error in + guard let cocoaError = error as? CocoaError else { + return false + } + #expect(cocoaError.code == .fileReadNoSuchFile) + return true } let fullContents = ["dir1", "dir1/dir2", "dir1/dir2/Bar", "dir1/dir2/Foo", "dir1/dir3", "dir1/dir3/Baz"] - let cwd = $0.currentDirectoryPath - XCTAssertNotEqual(cwd.last, "/") + let cwd = fileManager.currentDirectoryPath + #expect(cwd.last != "/") let paths = [cwd, "\(cwd)/", "\(cwd)//", ".", "./", ".//"] for path in paths { - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: path).sorted(), fullContents) + let results = try fileManager.subpathsOfDirectory(atPath: path).sorted() + #expect(results == fullContents) } } } - func testCreateDirectoryAtPath() throws { + @Test func testCreateDirectoryAtPath() throws { try FileManagerPlayground { "preexisting_file" - }.test { - try $0.createDirectory(atPath: "create_dir_test", withIntermediateDirectories: false) - XCTAssertEqual(try $0.contentsOfDirectory(atPath: ".").sorted(), ["create_dir_test", "preexisting_file"]) - try $0.createDirectory(atPath: "create_dir_test2/nested", withIntermediateDirectories: true) - XCTAssertEqual(try $0.contentsOfDirectory(atPath: "create_dir_test2"), ["nested"]) - try $0.createDirectory(atPath: "create_dir_test2/nested2", withIntermediateDirectories: true) - XCTAssertEqual(try $0.contentsOfDirectory(atPath: "create_dir_test2").sorted(), ["nested", "nested2"]) - XCTAssertNoThrow(try $0.createDirectory(atPath: "create_dir_test2/nested2", withIntermediateDirectories: true)) - XCTAssertThrowsError(try $0.createDirectory(atPath: "create_dir_test", withIntermediateDirectories: false)) { - XCTAssertEqual(($0 as? CocoaError)?.code, .fileWriteFileExists) + }.test { fileManager in + try fileManager.createDirectory(atPath: "create_dir_test", withIntermediateDirectories: false) + var result = try fileManager.contentsOfDirectory(atPath: ".").sorted() + #expect(result == ["create_dir_test", "preexisting_file"]) + try fileManager.createDirectory(atPath: "create_dir_test2/nested", withIntermediateDirectories: true) + result = try fileManager.contentsOfDirectory(atPath: "create_dir_test2") + #expect(result == ["nested"]) + try fileManager.createDirectory(atPath: "create_dir_test2/nested2", withIntermediateDirectories: true) + result = try fileManager.contentsOfDirectory(atPath: "create_dir_test2").sorted() + #expect(result == ["nested", "nested2"]) + #expect(throws: Never.self) { + try fileManager.createDirectory(atPath: "create_dir_test2/nested2", withIntermediateDirectories: true) } - XCTAssertThrowsError(try $0.createDirectory(atPath: "create_dir_test3/nested", withIntermediateDirectories: false)) { - XCTAssertEqual(($0 as? CocoaError)?.code, .fileNoSuchFile) + #expect { + try fileManager.createDirectory(atPath: "create_dir_test", withIntermediateDirectories: false) + } throws: { error in + guard let cocoaError = error as? CocoaError else { + return false + } + #expect(cocoaError.code == .fileWriteFileExists) + return true } - XCTAssertThrowsError(try $0.createDirectory(atPath: "preexisting_file", withIntermediateDirectories: false)) { - XCTAssertEqual(($0 as? CocoaError)?.code, .fileWriteFileExists) + #expect { + try fileManager.createDirectory(atPath: "create_dir_test3/nested", withIntermediateDirectories: false) + } throws: { error in + guard let cocoaError = error as? CocoaError else { + return false + } + #expect(cocoaError.code == .fileNoSuchFile) + return true + } + #expect { + try fileManager.createDirectory(atPath: "preexisting_file", withIntermediateDirectories: false) + } throws: { error in + guard let cocoaError = error as? CocoaError else { + return false + } + #expect(cocoaError.code == .fileWriteFileExists) + return true } - XCTAssertThrowsError(try $0.createDirectory(atPath: "preexisting_file", withIntermediateDirectories: true)) { - XCTAssertEqual(($0 as? CocoaError)?.code, .fileWriteFileExists) + #expect { + try fileManager.createDirectory(atPath: "preexisting_file", withIntermediateDirectories: true) + } throws: { error in + guard let cocoaError = error as? CocoaError else { + return false + } + #expect(cocoaError.code == .fileWriteFileExists) + return true } } } - func testLinkFileAtPathToPath() throws { + @Test func testLinkFileAtPathToPath() throws { try FileManagerPlayground { "foo" }.test(captureDelegateCalls: true) { - XCTAssertTrue($0.delegateCaptures.isEmpty) + #expect($0.delegateCaptures.isEmpty) try $0.linkItem(atPath: "foo", toPath: "bar") - XCTAssertEqual($0.delegateCaptures.shouldLink, [.init("foo", "bar")]) - XCTAssertEqual($0.delegateCaptures.shouldProceedAfterLinkError, []) - XCTAssertTrue($0.fileExists(atPath: "bar")) + #expect($0.delegateCaptures.shouldLink == [.init("foo", "bar")]) + #expect($0.delegateCaptures.shouldProceedAfterLinkError == []) + #expect($0.fileExists(atPath: "bar")) } try FileManagerPlayground { "foo" "bar" }.test(captureDelegateCalls: true) { - XCTAssertTrue($0.delegateCaptures.isEmpty) + #expect($0.delegateCaptures.isEmpty) try $0.linkItem(atPath: "foo", toPath: "bar") - XCTAssertEqual($0.delegateCaptures.shouldLink, [.init("foo", "bar")]) - XCTAssertEqual($0.delegateCaptures.shouldProceedAfterLinkError, [.init("foo", "bar", code: .fileWriteFileExists)]) + #expect($0.delegateCaptures.shouldLink == [.init("foo", "bar")]) + #expect($0.delegateCaptures.shouldProceedAfterLinkError == [.init("foo", "bar", code: .fileWriteFileExists)]) } } - func testCopyFileAtPathToPath() throws { + @Test func testCopyFileAtPathToPath() throws { try FileManagerPlayground { "foo" }.test(captureDelegateCalls: true) { - XCTAssertTrue($0.delegateCaptures.isEmpty) + #expect($0.delegateCaptures.isEmpty) try $0.copyItem(atPath: "foo", toPath: "bar") - XCTAssertEqual($0.delegateCaptures.shouldCopy, [.init("foo", "bar")]) - XCTAssertEqual($0.delegateCaptures.shouldProceedAfterCopyError, []) - XCTAssertTrue($0.fileExists(atPath: "bar")) + #expect($0.delegateCaptures.shouldCopy == [.init("foo", "bar")]) + #expect($0.delegateCaptures.shouldProceedAfterCopyError == []) + #expect($0.fileExists(atPath: "bar")) } try FileManagerPlayground { "foo" "bar" }.test(captureDelegateCalls: true) { - XCTAssertTrue($0.delegateCaptures.isEmpty) + #expect($0.delegateCaptures.isEmpty) try $0.copyItem(atPath: "foo", toPath: "bar") - XCTAssertEqual($0.delegateCaptures.shouldCopy, [.init("foo", "bar")]) - XCTAssertEqual($0.delegateCaptures.shouldProceedAfterCopyError, [.init("foo", "bar", code: .fileWriteFileExists)]) + #expect($0.delegateCaptures.shouldCopy == [.init("foo", "bar")]) + #expect($0.delegateCaptures.shouldProceedAfterCopyError == [.init("foo", "bar", code: .fileWriteFileExists)]) } } - func testCreateSymbolicLinkAtPath() throws { + @Test func testCreateSymbolicLinkAtPath() throws { try FileManagerPlayground { "foo" - }.test { - try $0.createSymbolicLink(atPath: "bar", withDestinationPath: "foo") - XCTAssertEqual(try $0.destinationOfSymbolicLink(atPath: "bar"), "foo") - - XCTAssertThrowsError(try $0.createSymbolicLink(atPath: "bar", withDestinationPath: "foo")) { - XCTAssertEqual(($0 as? CocoaError)?.code, .fileWriteFileExists) + }.test { fileManager in + try fileManager.createSymbolicLink(atPath: "bar", withDestinationPath: "foo") + let results = try fileManager.destinationOfSymbolicLink(atPath: "bar") + #expect(results == "foo") + #expect { + try fileManager.createSymbolicLink(atPath: "bar", withDestinationPath: "foo") + } throws: { error in + guard let cocoaError = error as? CocoaError else { + return false + } + #expect(cocoaError.code == .fileWriteFileExists) + return true } - XCTAssertThrowsError(try $0.createSymbolicLink(atPath: "foo", withDestinationPath: "baz")) { - XCTAssertEqual(($0 as? CocoaError)?.code, .fileWriteFileExists) + #expect { + try fileManager.createSymbolicLink(atPath: "foo", withDestinationPath: "baz") + } throws: { error in + guard let cocoaError = error as? CocoaError else { + return false + } + #expect(cocoaError.code == .fileWriteFileExists) + return true } - XCTAssertThrowsError(try $0.destinationOfSymbolicLink(atPath: "foo")) { - XCTAssertEqual(($0 as? CocoaError)?.code, .fileReadUnknown) + #expect { + try fileManager.destinationOfSymbolicLink(atPath: "foo") + } throws: { error in + guard let cocoaError = error as? CocoaError else { + return false + } + #expect(cocoaError.code == .fileReadUnknown) + return true } } } - func testMoveItemAtPathToPath() throws { + @Test func testMoveItemAtPathToPath() throws { let data = randomData() try FileManagerPlayground { Directory("dir") { @@ -367,31 +439,32 @@ final class FileManagerTests : XCTestCase { } "other_file" }.test(captureDelegateCalls: true) { - XCTAssertTrue($0.delegateCaptures.isEmpty) + #expect($0.delegateCaptures.isEmpty) try $0.moveItem(atPath: "dir", toPath: "dir2") - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: ".").sorted(), ["dir2", "dir2/bar", "dir2/foo", "other_file"]) - XCTAssertEqual($0.contents(atPath: "dir2/foo"), data) + let results = try $0.subpathsOfDirectory(atPath: ".").sorted() + #expect(results == ["dir2", "dir2/bar", "dir2/foo", "other_file"]) + #expect($0.contents(atPath: "dir2/foo") == data) #if FOUNDATION_FRAMEWORK // Behavior differs here due to usage of URL(filePath:) let rootDir = $0.currentDirectoryPath - XCTAssertEqual($0.delegateCaptures.shouldMove, [.init("\(rootDir)/dir", "\(rootDir)/dir2")]) + #expect($0.delegateCaptures.shouldMove == [.init("\(rootDir)/dir", "\(rootDir)/dir2")]) #else - XCTAssertEqual($0.delegateCaptures.shouldMove, [.init("dir", "dir2")]) + #expect($0.delegateCaptures.shouldMove == [.init("dir", "dir2")]) #endif try $0.moveItem(atPath: "does_not_exist", toPath: "dir3") - XCTAssertEqual($0.delegateCaptures.shouldProceedAfterCopyError, []) + #expect($0.delegateCaptures.shouldProceedAfterCopyError == []) try $0.moveItem(atPath: "dir2", toPath: "other_file") #if FOUNDATION_FRAMEWORK - XCTAssertTrue($0.delegateCaptures.shouldProceedAfterMoveError.contains(.init("\(rootDir)/dir2", "\(rootDir)/other_file", code: .fileWriteFileExists))) + #expect($0.delegateCaptures.shouldProceedAfterMoveError.contains(.init("\(rootDir)/dir2", "\(rootDir)/other_file", code: .fileWriteFileExists))) #else - XCTAssertTrue($0.delegateCaptures.shouldProceedAfterMoveError.contains(.init("dir2", "other_file", code: .fileWriteFileExists))) + #expect($0.delegateCaptures.shouldProceedAfterMoveError.contains(.init("dir2", "other_file", code: .fileWriteFileExists))) #endif } } - func testCopyItemAtPathToPath() throws { + @Test func testCopyItemAtPathToPath() throws { let data = randomData() try FileManagerPlayground { Directory("dir") { @@ -400,25 +473,26 @@ final class FileManagerTests : XCTestCase { } "other_file" }.test(captureDelegateCalls: true) { - XCTAssertTrue($0.delegateCaptures.isEmpty) + #expect($0.delegateCaptures.isEmpty) try $0.copyItem(atPath: "dir", toPath: "dir2") - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: ".").sorted(), ["dir", "dir/bar", "dir/foo", "dir2", "dir2/bar", "dir2/foo", "other_file"]) - XCTAssertEqual($0.contents(atPath: "dir/foo"), data) - XCTAssertEqual($0.contents(atPath: "dir2/foo"), data) - XCTAssertEqual($0.delegateCaptures.shouldCopy, [.init("dir", "dir2"), .init("dir/foo", "dir2/foo"), .init("dir/bar", "dir2/bar")]) - + let results = try $0.subpathsOfDirectory(atPath: ".").sorted() + #expect(results == ["dir", "dir/bar", "dir/foo", "dir2", "dir2/bar", "dir2/foo", "other_file"]) + #expect($0.contents(atPath: "dir/foo") == data) + #expect($0.contents(atPath: "dir2/foo") == data) + #expect($0.delegateCaptures.shouldCopy == [.init("dir", "dir2"), .init("dir/foo", "dir2/foo"), .init("dir/bar", "dir2/bar")]) + try $0.copyItem(atPath: "does_not_exist", toPath: "dir3") - XCTAssertEqual($0.delegateCaptures.shouldProceedAfterCopyError.last, .init("does_not_exist", "dir3", code: .fileNoSuchFile)) - + #expect($0.delegateCaptures.shouldProceedAfterCopyError.last == .init("does_not_exist", "dir3", code: .fileNoSuchFile)) + #if canImport(Darwin) // Not supported on linux because it ends up trying to set attributes that are currently unimplemented try $0.copyItem(atPath: "dir", toPath: "other_file") - XCTAssertTrue($0.delegateCaptures.shouldProceedAfterCopyError.contains(.init("dir", "other_file", code: .fileWriteFileExists))) + #expect($0.delegateCaptures.shouldProceedAfterCopyError.contains(.init("dir", "other_file", code: .fileWriteFileExists))) #endif } } - func testRemoveItemAtPath() throws { + @Test func testRemoveItemAtPath() throws { try FileManagerPlayground { Directory("dir") { "foo" @@ -426,26 +500,29 @@ final class FileManagerTests : XCTestCase { } "other" }.test(captureDelegateCalls: true) { - XCTAssertTrue($0.delegateCaptures.isEmpty) + #expect($0.delegateCaptures.isEmpty) try $0.removeItem(atPath: "dir/bar") - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: ".").sorted(), ["dir", "dir/foo", "other"]) - XCTAssertEqual($0.delegateCaptures.shouldRemove, [.init("dir/bar")]) - XCTAssertEqual($0.delegateCaptures.shouldProceedAfterRemoveError, []) - + var results = try $0.subpathsOfDirectory(atPath: ".").sorted() + #expect(results == ["dir", "dir/foo", "other"]) + #expect($0.delegateCaptures.shouldRemove == [.init("dir/bar")]) + #expect($0.delegateCaptures.shouldProceedAfterRemoveError == []) + let rootDir = $0.currentDirectoryPath try $0.removeItem(atPath: "dir") - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: ".").sorted(), ["other"]) - XCTAssertEqual($0.delegateCaptures.shouldRemove, [.init("dir/bar"), .init("\(rootDir)/dir"), .init("\(rootDir)/dir/foo")]) - XCTAssertEqual($0.delegateCaptures.shouldProceedAfterRemoveError, []) - + results = try $0.subpathsOfDirectory(atPath: ".").sorted() + #expect(results == ["other"]) + #expect($0.delegateCaptures.shouldRemove == [.init("dir/bar"), .init("\(rootDir)/dir"), .init("\(rootDir)/dir/foo")]) + #expect($0.delegateCaptures.shouldProceedAfterRemoveError == []) + try $0.removeItem(atPath: "other") - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: ".").sorted(), []) - XCTAssertEqual($0.delegateCaptures.shouldRemove, [.init("dir/bar"), .init("\(rootDir)/dir"), .init("\(rootDir)/dir/foo"), .init("other")]) - XCTAssertEqual($0.delegateCaptures.shouldProceedAfterRemoveError, []) - + results = try $0.subpathsOfDirectory(atPath: ".").sorted() + #expect(results == []) + #expect($0.delegateCaptures.shouldRemove == [.init("dir/bar"), .init("\(rootDir)/dir"), .init("\(rootDir)/dir/foo"), .init("other")]) + #expect($0.delegateCaptures.shouldProceedAfterRemoveError == []) + try $0.removeItem(atPath: "does_not_exist") - XCTAssertEqual($0.delegateCaptures.shouldRemove, [.init("dir/bar"), .init("\(rootDir)/dir"), .init("\(rootDir)/dir/foo"), .init("other"), .init("does_not_exist")]) - XCTAssertEqual($0.delegateCaptures.shouldProceedAfterRemoveError, [.init("does_not_exist", code: .fileNoSuchFile)]) + #expect($0.delegateCaptures.shouldRemove == [.init("dir/bar"), .init("\(rootDir)/dir"), .init("\(rootDir)/dir/foo"), .init("other"), .init("does_not_exist")]) + #expect($0.delegateCaptures.shouldProceedAfterRemoveError == [.init("does_not_exist", code: .fileNoSuchFile)]) } #if canImport(Darwin) @@ -453,9 +530,9 @@ final class FileManagerTests : XCTestCase { // not supported on older versions of Darwin where removefile would return ENOENT instead of ENAMETOOLONG if #available(macOS 14.4, iOS 17.0, watchOS 10.0, tvOS 17.0, *) { try FileManagerPlayground { - }.test { + }.test { fileManager in // Create hierarchy in which the leaf is a long path (length > PATH_MAX) - let rootDir = $0.currentDirectoryPath + let rootDir = fileManager.currentDirectoryPath let aas = Array(repeating: "a", count: Int(NAME_MAX) - 3).joined() let bbs = Array(repeating: "b", count: Int(NAME_MAX) - 3).joined() let ccs = Array(repeating: "c", count: Int(NAME_MAX) - 3).joined() @@ -463,45 +540,51 @@ final class FileManagerTests : XCTestCase { let ees = Array(repeating: "e", count: Int(NAME_MAX) - 3).joined() let leaf = "longpath" - try $0.createDirectory(atPath: aas, withIntermediateDirectories: true) - XCTAssertTrue($0.changeCurrentDirectoryPath(aas)) - try $0.createDirectory(atPath: bbs, withIntermediateDirectories: true) - XCTAssertTrue($0.changeCurrentDirectoryPath(bbs)) - try $0.createDirectory(atPath: ccs, withIntermediateDirectories: true) - XCTAssertTrue($0.changeCurrentDirectoryPath(ccs)) - try $0.createDirectory(atPath: dds, withIntermediateDirectories: true) - XCTAssertTrue($0.changeCurrentDirectoryPath(dds)) - try $0.createDirectory(atPath: ees, withIntermediateDirectories: true) - XCTAssertTrue($0.changeCurrentDirectoryPath(ees)) - try $0.createDirectory(atPath: leaf, withIntermediateDirectories: true) - - XCTAssertTrue($0.changeCurrentDirectoryPath(rootDir)) + try fileManager.createDirectory(atPath: aas, withIntermediateDirectories: true) + #expect(fileManager.changeCurrentDirectoryPath(aas)) + try fileManager.createDirectory(atPath: bbs, withIntermediateDirectories: true) + #expect(fileManager.changeCurrentDirectoryPath(bbs)) + try fileManager.createDirectory(atPath: ccs, withIntermediateDirectories: true) + #expect(fileManager.changeCurrentDirectoryPath(ccs)) + try fileManager.createDirectory(atPath: dds, withIntermediateDirectories: true) + #expect(fileManager.changeCurrentDirectoryPath(dds)) + try fileManager.createDirectory(atPath: ees, withIntermediateDirectories: true) + #expect(fileManager.changeCurrentDirectoryPath(ees)) + try fileManager.createDirectory(atPath: leaf, withIntermediateDirectories: true) + + #expect(fileManager.changeCurrentDirectoryPath(rootDir)) let fullPath = "\(aas)/\(bbs)/\(ccs)/\(dds)/\(ees)/\(leaf)" - XCTAssertThrowsError(try $0.removeItem(atPath: fullPath)) { - let underlyingPosixError = ($0 as? CocoaError)?.underlying as? POSIXError - XCTAssertEqual(underlyingPosixError?.code, .ENAMETOOLONG, "removeItem didn't fail with ENAMETOOLONG; produced error: \($0)") + #expect { + try fileManager.removeItem(atPath: fullPath) + } throws: { error in + guard let cocoaError = error as? CocoaError, + let posixError = cocoaError.underlying as? POSIXError else { + return false + } + #expect(posixError.code == .ENAMETOOLONG, "removeItem didn't fail with ENAMETOOLONG; produced error: \(error)") + return true } // Clean up - XCTAssertTrue($0.changeCurrentDirectoryPath(aas)) - XCTAssertTrue($0.changeCurrentDirectoryPath(bbs)) - XCTAssertTrue($0.changeCurrentDirectoryPath(ccs)) - XCTAssertTrue($0.changeCurrentDirectoryPath(dds)) - try $0.removeItem(atPath: ees) - XCTAssertTrue($0.changeCurrentDirectoryPath("..")) - try $0.removeItem(atPath: dds) - XCTAssertTrue($0.changeCurrentDirectoryPath("..")) - try $0.removeItem(atPath: ccs) - XCTAssertTrue($0.changeCurrentDirectoryPath("..")) - try $0.removeItem(atPath: bbs) - XCTAssertTrue($0.changeCurrentDirectoryPath("..")) - try $0.removeItem(atPath: aas) + #expect(fileManager.changeCurrentDirectoryPath(aas)) + #expect(fileManager.changeCurrentDirectoryPath(bbs)) + #expect(fileManager.changeCurrentDirectoryPath(ccs)) + #expect(fileManager.changeCurrentDirectoryPath(dds)) + try fileManager.removeItem(atPath: ees) + #expect(fileManager.changeCurrentDirectoryPath("..")) + try fileManager.removeItem(atPath: dds) + #expect(fileManager.changeCurrentDirectoryPath("..")) + try fileManager.removeItem(atPath: ccs) + #expect(fileManager.changeCurrentDirectoryPath("..")) + try fileManager.removeItem(atPath: bbs) + #expect(fileManager.changeCurrentDirectoryPath("..")) + try fileManager.removeItem(atPath: aas) } } #endif } - func testFileExistsAtPath() throws { + @Test func testFileExistsAtPath() throws { try FileManagerPlayground { Directory("dir") { "foo" @@ -520,24 +603,23 @@ final class FileManagerTests : XCTestCase { isDir } #endif - XCTAssertTrue($0.fileExists(atPath: "dir/foo", isDirectory: &isDir)) - XCTAssertFalse(isDirBool()) - XCTAssertTrue($0.fileExists(atPath: "dir/bar", isDirectory: &isDir)) - XCTAssertFalse(isDirBool()) - XCTAssertTrue($0.fileExists(atPath: "dir", isDirectory: &isDir)) - XCTAssertTrue(isDirBool()) - XCTAssertTrue($0.fileExists(atPath: "other", isDirectory: &isDir)) - XCTAssertFalse(isDirBool()) - XCTAssertFalse($0.fileExists(atPath: "does_not_exist")) + #expect($0.fileExists(atPath: "dir/foo", isDirectory: &isDir)) + #expect(isDirBool() == false) + #expect($0.fileExists(atPath: "dir/bar", isDirectory: &isDir)) + #expect(isDirBool() == false) + #expect($0.fileExists(atPath: "dir", isDirectory: &isDir)) + #expect(isDirBool()) + #expect($0.fileExists(atPath: "other", isDirectory: &isDir)) + #expect(isDirBool() == false) + #expect($0.fileExists(atPath: "does_not_exist") == false) } } - + + @Test(.enabled( + if: getuid() != 0, + "Root users can always access anything, so this test will not function when run as root") + ) func testFileAccessAtPath() throws { - guard getuid() != 0 else { - // Root users can always access anything, so this test will not function when run as root - throw XCTSkip("This test is not available when running as the root user") - } - try FileManagerPlayground { File("000", attributes: [.posixPermissions: 0o000]) File("111", attributes: [.posixPermissions: 0o111]) @@ -553,45 +635,54 @@ final class FileManagerTests : XCTestCase { let executable = ["111", "333", "555", "777"] for number in 0...7 { let file = "\(number)\(number)\(number)" - XCTAssertEqual($0.isReadableFile(atPath: file), readable.contains(file), "'\(file)' failed readable check") - XCTAssertEqual($0.isWritableFile(atPath: file), writable.contains(file), "'\(file)' failed writable check") - XCTAssertEqual($0.isExecutableFile(atPath: file), executable.contains(file), "'\(file)' failed executable check") - XCTAssertTrue($0.isDeletableFile(atPath: file), "'\(file)' failed deletable check") + #expect($0.isReadableFile(atPath: file) == readable.contains(file), "'\(file)' failed readable check") + #expect($0.isWritableFile(atPath: file) == writable.contains(file), "'\(file)' failed writable check") + #expect($0.isExecutableFile(atPath: file) == executable.contains(file), "'\(file)' failed executable check") + #expect($0.isDeletableFile(atPath: file), "'\(file)' failed deletable check") } } } - func testFileSystemAttributesAtPath() throws { + @Test func testFileSystemAttributesAtPath() throws { try FileManagerPlayground { "Foo" - }.test { - let dict = try $0.attributesOfFileSystem(forPath: "Foo") - XCTAssertNotNil(dict[.systemSize]) - XCTAssertThrowsError(try $0.attributesOfFileSystem(forPath: "does_not_exist")) { - XCTAssertEqual(($0 as? CocoaError)?.code, .fileReadNoSuchFile) + }.test { fileManager in + let dict = try fileManager.attributesOfFileSystem(forPath: "Foo") + #expect(dict[.systemSize] != nil) + #expect { + try fileManager.attributesOfFileSystem(forPath: "does_not_exist") + } throws: { error in + guard let cocoaError = error as? CocoaError else { + return false + } + #expect(cocoaError.code == .fileReadNoSuchFile) + return true } } } - func testCurrentWorkingDirectory() throws { + @Test func testCurrentWorkingDirectory() throws { try FileManagerPlayground { Directory("dir") { "foo" } "bar" - }.test { - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: ".").sorted(), ["bar", "dir", "dir/foo"]) - XCTAssertTrue($0.changeCurrentDirectoryPath("dir")) - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: "."), ["foo"]) - XCTAssertFalse($0.changeCurrentDirectoryPath("foo")) - XCTAssertTrue($0.changeCurrentDirectoryPath("..")) - XCTAssertEqual(try $0.subpathsOfDirectory(atPath: ".").sorted(), ["bar", "dir", "dir/foo"]) - XCTAssertFalse($0.changeCurrentDirectoryPath("does_not_exist")) + }.test { (fileManager) throws in + var results = try fileManager.subpathsOfDirectory(atPath: ".").sorted() + #expect(results == ["bar", "dir", "dir/foo"]) + #expect(fileManager.changeCurrentDirectoryPath("dir")) + results = try fileManager.subpathsOfDirectory(atPath: ".") + #expect(results == ["foo"]) + #expect(fileManager.changeCurrentDirectoryPath("foo") == false) + #expect(fileManager.changeCurrentDirectoryPath("..")) + results = try fileManager.subpathsOfDirectory(atPath: ".").sorted() + #expect(results == ["bar", "dir", "dir/foo"]) + #expect(fileManager.changeCurrentDirectoryPath("does_not_exist") == false) } } - func testBooleanFileAttributes() throws { - #if canImport(Darwin) +#if canImport(Darwin) + @Test func testBooleanFileAttributes() throws { try FileManagerPlayground { "none" File("immutable", attributes: [.immutable: true]) @@ -607,82 +698,80 @@ final class FileManagerTests : XCTestCase { for test in tests { let result = try $0.attributesOfItem(atPath: test.path) - XCTAssertEqual(result[.immutable] as? Bool, test.immutable, "Item at path '\(test.path)' did not provide expected result for immutable key") - XCTAssertEqual(result[.appendOnly] as? Bool, test.appendOnly, "Item at path '\(test.path)' did not provide expected result for appendOnly key") - + #expect(result[.immutable] as? Bool == test.immutable, "Item at path '\(test.path)' did not provide expected result for immutable key") + #expect(result[.appendOnly] as? Bool == test.appendOnly, "Item at path '\(test.path)' did not provide expected result for appendOnly key") + // Manually clean up attributes so removal does not fail try $0.setAttributes([.immutable: false, .appendOnly: false], ofItemAtPath: test.path) } } - #else - throw XCTSkip("This test is not applicable on this platform") - #endif } - - func testMalformedModificationDateAttribute() throws { +#endif + + @Test func testMalformedModificationDateAttribute() throws { let sentinelDate = Date(timeIntervalSince1970: 100) try FileManagerPlayground { File("foo", attributes: [.modificationDate: sentinelDate]) }.test { - XCTAssertEqual(try $0.attributesOfItem(atPath: "foo")[.modificationDate] as? Date, sentinelDate) + var results = try $0.attributesOfItem(atPath: "foo")[.modificationDate] as? Date + #expect(results == sentinelDate) for value in [Double.infinity, -Double.infinity, Double.nan] { // Malformed modification dates should be dropped instead of throwing or crashing try $0.setAttributes([.modificationDate : Date(timeIntervalSince1970: value)], ofItemAtPath: "foo") } - XCTAssertEqual(try $0.attributesOfItem(atPath: "foo")[.modificationDate] as? Date, sentinelDate) + results = try $0.attributesOfItem(atPath: "foo")[.modificationDate] as? Date + #expect(results == sentinelDate) } } - func testImplicitlyConvertibleFileAttributes() throws { + @Test func testImplicitlyConvertibleFileAttributes() throws { try FileManagerPlayground { File("foo", attributes: [.posixPermissions : UInt16(0o644)]) }.test { let attributes = try $0.attributesOfItem(atPath: "foo") // Ensure the unconventional UInt16 was accepted as input - XCTAssertEqual(attributes[.posixPermissions] as? UInt, 0o644) + #expect(attributes[.posixPermissions] as? UInt == 0o644) #if FOUNDATION_FRAMEWORK // Where we have NSNumber, ensure that we can get the value back as an unconventional Double value - XCTAssertEqual(attributes[.posixPermissions] as? Double, Double(0o644)) + #expect(attributes[.posixPermissions] as? Double == Double(0o644)) // Ensure that the file type can be converted to a String when it is an ObjC enum - XCTAssertEqual(attributes[.type] as? String, FileAttributeType.typeRegular.rawValue) + #expect(attributes[.type] as? String == FileAttributeType.typeRegular.rawValue) #endif // Ensure that the file type can be converted to a FileAttributeType when it is an ObjC enum and in swift-foundation - XCTAssertEqual(attributes[.type] as? FileAttributeType, .typeRegular) - + #expect(attributes[.type] as? FileAttributeType == .typeRegular) + } } - func testStandardizingPathAutomount() throws { - #if canImport(Darwin) +#if canImport(Darwin) + @Test func testStandardizingPathAutomount() throws { let tests = [ "/private/System" : "/private/System", "/private/tmp" : "/tmp", "/private/System/foo" : "/private/System/foo" ] for (input, expected) in tests { - XCTAssertEqual(input.standardizingPath, expected, "Standardizing the path '\(input)' did not produce the expected result") + #expect(input.standardizingPath == expected, "Standardizing the path '\(input)' did not produce the expected result") } - #else - throw XCTSkip("This test is not applicable to this platform") - #endif } - - func testResolveSymlinksViaGetAttrList() throws { +#endif + + @Test func testResolveSymlinksViaGetAttrList() throws { try FileManagerPlayground { "destination" }.test { try $0.createSymbolicLink(atPath: "link", withDestinationPath: "destination") let absolutePath = $0.currentDirectoryPath.appendingPathComponent("link") let resolved = absolutePath._resolvingSymlinksInPath() // Call internal function to avoid path standardization - XCTAssertEqual(resolved, $0.currentDirectoryPath.appendingPathComponent("destination")) + #expect(resolved == $0.currentDirectoryPath.appendingPathComponent("destination")) } } #if os(macOS) && FOUNDATION_FRAMEWORK - func testSpecialTrashDirectoryTruncation() throws { + @Test func testSpecialTrashDirectoryTruncation() throws { try FileManagerPlayground {}.test { if let trashURL = try? $0.url(for: .trashDirectory, in: .allDomainsMask, appropriateFor: nil, create: false) { - XCTAssertEqual(trashURL.pathComponents.last, ".Trash") + #expect(trashURL.pathComponents.last == ".Trash") } } } diff --git a/Tests/FoundationEssentialsTests/Formatting/BinaryInteger+FormatStyleTests.swift b/Tests/FoundationEssentialsTests/Formatting/BinaryInteger+FormatStyleTests.swift index 6cd180132..73739fcbd 100644 --- a/Tests/FoundationEssentialsTests/Formatting/BinaryInteger+FormatStyleTests.swift +++ b/Tests/FoundationEssentialsTests/Formatting/BinaryInteger+FormatStyleTests.swift @@ -9,7 +9,7 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test -import XCTest +import Testing #if canImport(FoundationEssentials) @testable import FoundationEssentials @@ -27,13 +27,16 @@ import Numberick import BigInt #endif -final class BinaryIntegerFormatStyleTests: XCTestCase { +struct BinaryIntegerFormatStyleTests { // NSR == numericStringRepresentation func checkNSR(value: some BinaryInteger, expected: String) { - XCTAssertEqual(String(decoding: value.numericStringRepresentation.utf8, as: Unicode.ASCII.self), expected) + #expect( + String(decoding: value.numericStringRepresentation.utf8, as: Unicode.ASCII.self) == + expected + ) } - func testNumericStringRepresentation_builtinIntegersLimits() throws { + @Test func testNumericStringRepresentation_builtinIntegersLimits() throws { func check(type: I.Type = I.self, min: String, max: String) { checkNSR(value: I.min, expected: min) checkNSR(value: I.max, expected: max) @@ -50,7 +53,7 @@ final class BinaryIntegerFormatStyleTests: XCTestCase { check(type: UInt64.self, min: "0", max: "18446744073709551615") } - func testNumericStringRepresentation_builtinIntegersAroundDecimalMagnitude() throws { + @Test func testNumericStringRepresentation_builtinIntegersAroundDecimalMagnitude() throws { func check(type: I.Type = I.self, magnitude: String, oneLess: String, oneMore: String) { var mag = I(1); while !mag.multipliedReportingOverflow(by: 10).overflow { mag *= 10 } @@ -103,14 +106,14 @@ final class BinaryIntegerFormatStyleTests: XCTestCase { String(repeating: "1234567890", count: 10), String(repeating: "1234567890", count: 100)] { if let value = initialiser(valueAsString) { // The test cases cover a wide range of values, that don't all fit into every type tested (i.e. the fixed-width types from Numberick). - XCTAssertEqual(value.description, valueAsString) // Sanity check that it initialised from the string correctly. + #expect(value.description == valueAsString) // Sanity check that it initialised from the string correctly. checkNSR(value: value, expected: valueAsString) if I.isSigned { let negativeValueAsString = "-" + valueAsString let negativeValue = initialiser(negativeValueAsString)! - XCTAssertEqual(negativeValue.description, negativeValueAsString) // Sanity check that it initialised from the string correctly. + #expect(negativeValue.description == negativeValueAsString) // Sanity check that it initialised from the string correctly. checkNSR(value: negativeValue, expected: negativeValueAsString) } } @@ -118,7 +121,7 @@ final class BinaryIntegerFormatStyleTests: XCTestCase { } #if canImport(Numberick) - func testNumericStringRepresentation_largeIntegers() throws { + @Test func testNumericStringRepresentation_largeIntegers() throws { check(type: Int128.self, initialiser: { Int128($0) }) check(type: UInt128.self, initialiser: { UInt128($0) }) @@ -128,7 +131,7 @@ final class BinaryIntegerFormatStyleTests: XCTestCase { #endif #if canImport(BigInt) - func testNumericStringRepresentation_arbitraryPrecisionIntegers() throws { + @Test func testNumericStringRepresentation_arbitraryPrecisionIntegers() throws { check(type: BigInt.self, initialiser: { BigInt($0)! }) check(type: BigUInt.self, initialiser: { BigUInt($0)! }) } @@ -136,11 +139,11 @@ final class BinaryIntegerFormatStyleTests: XCTestCase { #endif // canImport(Numberick) || canImport(BigInt) } -final class BinaryIntegerFormatStyleTestsUsingBinaryIntegerWords: XCTestCase { - +struct BinaryIntegerFormatStyleTestsUsingBinaryIntegerWords { + // MARK: Tests - func testInt32() { + @Test func testInt32() { check( Int32(truncatingIfNeeded: 0x00000000 as UInt32), expectation: "0") check( Int32(truncatingIfNeeded: 0x03020100 as UInt32), expectation: "50462976") check( Int32(truncatingIfNeeded: 0x7fffffff as UInt32), expectation: "2147483647") // Int32.max @@ -150,7 +153,7 @@ final class BinaryIntegerFormatStyleTestsUsingBinaryIntegerWords: XCTestCase { check( Int32(truncatingIfNeeded: 0xffffffff as UInt32), expectation: "-1") } - func testUInt32() { + @Test func testUInt32() { check(UInt32(truncatingIfNeeded: 0x00000000 as UInt32), expectation: "0") // UInt32.min check(UInt32(truncatingIfNeeded: 0x03020100 as UInt32), expectation: "50462976") check(UInt32(truncatingIfNeeded: 0x7fffffff as UInt32), expectation: "2147483647") @@ -160,7 +163,7 @@ final class BinaryIntegerFormatStyleTestsUsingBinaryIntegerWords: XCTestCase { check(UInt32(truncatingIfNeeded: 0xffffffff as UInt32), expectation: "4294967295") // UInt32.max } - func testInt64() { + @Test func testInt64() { check( Int64(truncatingIfNeeded: 0x0000000000000000 as UInt64), expectation: "0") check( Int64(truncatingIfNeeded: 0x0706050403020100 as UInt64), expectation: "506097522914230528") check( Int64(truncatingIfNeeded: 0x7fffffffffffffff as UInt64), expectation: "9223372036854775807") // Int64.max @@ -170,7 +173,7 @@ final class BinaryIntegerFormatStyleTestsUsingBinaryIntegerWords: XCTestCase { check( Int64(truncatingIfNeeded: 0xffffffffffffffff as UInt64), expectation: "-1") } - func testUInt64() { + @Test func testUInt64() { check(UInt64(truncatingIfNeeded: 0x0000000000000000 as UInt64), expectation: "0") // UInt64.min check(UInt64(truncatingIfNeeded: 0x0706050403020100 as UInt64), expectation: "506097522914230528") check(UInt64(truncatingIfNeeded: 0x7fffffffffffffff as UInt64), expectation: "9223372036854775807") @@ -182,7 +185,7 @@ final class BinaryIntegerFormatStyleTestsUsingBinaryIntegerWords: XCTestCase { // MARK: Tests + Big Integer - func testInt128() { + @Test func testInt128() { check(x64:[0x0000000000000000, 0x0000000000000000] as [UInt64], isSigned: true, expectation: "0") check(x64:[0x0706050403020100, 0x0f0e0d0c0b0a0908] as [UInt64], isSigned: true, expectation: "20011376718272490338853433276725592320") check(x64:[0xffffffffffffffff, 0x7fffffffffffffff] as [UInt64], isSigned: true, expectation: "170141183460469231731687303715884105727") // Int128.max @@ -191,7 +194,7 @@ final class BinaryIntegerFormatStyleTestsUsingBinaryIntegerWords: XCTestCase { check(x64:[0xffffffffffffffff, 0xffffffffffffffff] as [UInt64], isSigned: true, expectation: "-1") } - func testUInt128() { + @Test func testUInt128() { check(x64:[0x0000000000000000, 0x0000000000000000] as [UInt64], isSigned: false, expectation: "0") // UInt128.min check(x64:[0x0706050403020100, 0x0f0e0d0c0b0a0908] as [UInt64], isSigned: false, expectation: "20011376718272490338853433276725592320") check(x64:[0x0000000000000000, 0x8000000000000000] as [UInt64], isSigned: false, expectation: "170141183460469231731687303715884105728") @@ -202,12 +205,12 @@ final class BinaryIntegerFormatStyleTestsUsingBinaryIntegerWords: XCTestCase { // MARK: Tests + Big Integer + Miscellaneous - func testWordsIsEmptyResultsInZero() { + @Test func testWordsIsEmptyResultsInZero() { check(words:[ ] as [UInt], isSigned: true, expectation: "0") check(words:[ ] as [UInt], isSigned: false, expectation: "0") } - func testSignExtendingDoesNotChangeTheResult() { + @Test func testSignExtendingDoesNotChangeTheResult() { check(words:[ 0 ] as [UInt], isSigned: true, expectation: "0") check(words:[ 0, 0 ] as [UInt], isSigned: true, expectation: "0") check(words:[ 0, 0, 0 ] as [UInt], isSigned: true, expectation: "0") @@ -227,7 +230,14 @@ final class BinaryIntegerFormatStyleTestsUsingBinaryIntegerWords: XCTestCase { // MARK: Assertions func check(_ integer: some BinaryInteger, expectation: String, file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(integer.description, expectation, "integer description does not match expectation", file: file, line: line) + #expect( + integer.description == expectation, + "integer description does not match expectation", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) check(ascii: integer.numericStringRepresentation.utf8, expectation: expectation, file: file, line: line) check(words: Array(integer.words), isSigned: type(of: integer).isSigned, expectation: expectation, file: file, line: line) } @@ -242,6 +252,12 @@ final class BinaryIntegerFormatStyleTestsUsingBinaryIntegerWords: XCTestCase { } func check(ascii: some Collection, expectation: String, file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(String(decoding: ascii, as: Unicode.ASCII.self), expectation, file: file, line: line) + #expect( + String(decoding: ascii, as: Unicode.ASCII.self) == expectation, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } } diff --git a/Tests/FoundationEssentialsTests/Formatting/ISO8601FormatStyleFormattingTests.swift b/Tests/FoundationEssentialsTests/Formatting/ISO8601FormatStyleFormattingTests.swift index 1f2a2d4a6..0fb2eb806 100644 --- a/Tests/FoundationEssentialsTests/Formatting/ISO8601FormatStyleFormattingTests.swift +++ b/Tests/FoundationEssentialsTests/Formatting/ISO8601FormatStyleFormattingTests.swift @@ -10,9 +10,7 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing #if canImport(FoundationEssentials) @testable import FoundationEssentials @@ -22,42 +20,42 @@ import TestSupport @testable import Foundation #endif -final class ISO8601FormatStyleFormattingTests: XCTestCase { +struct ISO8601FormatStyleFormattingTests { - func test_ISO8601Format() throws { + @Test func test_ISO8601Format() throws { let date = Date(timeIntervalSinceReferenceDate: 665076946.0) let fractionalSecondsDate = Date(timeIntervalSinceReferenceDate: 665076946.011) let iso8601 = Date.ISO8601FormatStyle() // Date is: "2022-01-28 15:35:46" - XCTAssertEqual(iso8601.format(date), "2022-01-28T15:35:46Z") + #expect(iso8601.format(date) == "2022-01-28T15:35:46Z") - XCTAssertEqual(iso8601.time(includingFractionalSeconds: true).format(fractionalSecondsDate), "15:35:46.011") + #expect(iso8601.time(includingFractionalSeconds: true).format(fractionalSecondsDate) == "15:35:46.011") - XCTAssertEqual(iso8601.year().month().day().time(includingFractionalSeconds: true).format(fractionalSecondsDate), "2022-01-28T15:35:46.011") + #expect(iso8601.year().month().day().time(includingFractionalSeconds: true).format(fractionalSecondsDate) == "2022-01-28T15:35:46.011") // Day-only results: the default time is midnight for parsed date when the time piece is missing // Date is: "2022-01-28 00:00:00" - XCTAssertEqual(iso8601.year().month().day().dateSeparator(.dash).format(date), "2022-01-28") - + #expect(iso8601.year().month().day().dateSeparator(.dash).format(date) == "2022-01-28") + // Date is: "2022-01-28 00:00:00" - XCTAssertEqual(iso8601.year().month().day().dateSeparator(.omitted).format(date), "20220128") + #expect(iso8601.year().month().day().dateSeparator(.omitted).format(date) == "20220128") // Time-only results: we use the default date of the format style, 1970-01-01, to supplement the parsed date without year, month or day // Date is: "1970-01-23 00:00:00" - XCTAssertEqual(iso8601.weekOfYear().day().dateSeparator(.dash).format(date), "W04-05") + #expect(iso8601.weekOfYear().day().dateSeparator(.dash).format(date) == "W04-05") // Date is: "1970-01-28 15:35:46" - XCTAssertEqual(iso8601.day().time(includingFractionalSeconds: false).timeSeparator(.colon).format(date), "028T15:35:46") + #expect(iso8601.day().time(includingFractionalSeconds: false).timeSeparator(.colon).format(date) == "028T15:35:46") // Date is: "1970-01-01 15:35:46" - XCTAssertEqual(iso8601.time(includingFractionalSeconds: false).timeSeparator(.colon).format(date), "15:35:46") + #expect(iso8601.time(includingFractionalSeconds: false).timeSeparator(.colon).format(date) == "15:35:46") // Date is: "1970-01-01 15:35:46" - XCTAssertEqual(iso8601.time(includingFractionalSeconds: false).timeZone(separator: .omitted).format(date), "15:35:46Z") + #expect(iso8601.time(includingFractionalSeconds: false).timeZone(separator: .omitted).format(date) == "15:35:46Z") // Date is: "1970-01-01 15:35:46" - XCTAssertEqual(iso8601.time(includingFractionalSeconds: false).timeZone(separator: .colon).format(date), "15:35:46Z") + #expect(iso8601.time(includingFractionalSeconds: false).timeZone(separator: .colon).format(date) == "15:35:46Z") // Date is: "1970-01-01 15:35:46" - XCTAssertEqual(iso8601.timeZone(separator: .colon).time(includingFractionalSeconds: false).timeSeparator(.colon).format(date), "15:35:46Z") - + #expect(iso8601.timeZone(separator: .colon).time(includingFractionalSeconds: false).timeSeparator(.colon).format(date) == "15:35:46Z") + // Time zones var iso8601Pacific = iso8601 @@ -67,73 +65,72 @@ final class ISO8601FormatStyleFormattingTests: XCTestCase { var iso8601PacificIsh = iso8601 iso8601PacificIsh.timeZone = TimeZone(secondsFromGMT: -3600 * 8 - 30)! - XCTAssertEqual(iso8601Pacific.timeSeparator(.omitted).format(date), "2022-01-28T073546-0800") - XCTAssertEqual(iso8601Pacific.timeSeparator(.omitted).timeZoneSeparator(.colon).format(date), "2022-01-28T073546-08:00") + #expect(iso8601Pacific.timeSeparator(.omitted).format(date) == "2022-01-28T073546-0800") + #expect(iso8601Pacific.timeSeparator(.omitted).timeZoneSeparator(.colon).format(date) == "2022-01-28T073546-08:00") + + #expect(iso8601PacificIsh.timeSeparator(.omitted).format(date) == "2022-01-28T073516-080030") + #expect(iso8601PacificIsh.timeSeparator(.omitted).timeZoneSeparator(.colon).format(date) == "2022-01-28T073516-08:00:30") - XCTAssertEqual(iso8601PacificIsh.timeSeparator(.omitted).format(date), "2022-01-28T073516-080030") - XCTAssertEqual(iso8601PacificIsh.timeSeparator(.omitted).timeZoneSeparator(.colon).format(date), "2022-01-28T073516-08:00:30") - var iso8601gmtP1 = iso8601 iso8601gmtP1.timeZone = TimeZone(secondsFromGMT: 3600)! - XCTAssertEqual(iso8601gmtP1.timeSeparator(.omitted).format(date), "2022-01-28T163546+0100") - XCTAssertEqual(iso8601gmtP1.timeSeparator(.omitted).timeZoneSeparator(.colon).format(date), "2022-01-28T163546+01:00") - + #expect(iso8601gmtP1.timeSeparator(.omitted).format(date) == "2022-01-28T163546+0100") + #expect(iso8601gmtP1.timeSeparator(.omitted).timeZoneSeparator(.colon).format(date) == "2022-01-28T163546+01:00") } - func test_codable() { + @Test func test_codable() { let iso8601Style = Date.ISO8601FormatStyle().year().month().day() let encoder = JSONEncoder() let encodedStyle = try! encoder.encode(iso8601Style) let decoder = JSONDecoder() let decodedStyle = try? decoder.decode(Date.ISO8601FormatStyle.self, from: encodedStyle) - XCTAssertNotNil(decodedStyle) + #expect(decodedStyle != nil) } - func testLeadingDotSyntax() { + @Test func testLeadingDotSyntax() { let _ = Date().formatted(.iso8601) } - func test_ISO8601FormatWithDate() throws { + @Test func test_ISO8601FormatWithDate() throws { // dateFormatter.date(from: "2021-07-01 15:56:32")! let date = Date(timeIntervalSinceReferenceDate: 646847792.0) // Thursday - XCTAssertEqual(date.formatted(.iso8601), "2021-07-01T15:56:32Z") - XCTAssertEqual(date.formatted(.iso8601.dateSeparator(.omitted)), "20210701T15:56:32Z") - XCTAssertEqual(date.formatted(.iso8601.dateTimeSeparator(.space)), "2021-07-01 15:56:32Z") - XCTAssertEqual(date.formatted(.iso8601.timeSeparator(.omitted)), "2021-07-01T155632Z") - XCTAssertEqual(date.formatted(.iso8601.dateSeparator(.omitted).timeSeparator(.omitted)), "20210701T155632Z") - XCTAssertEqual(date.formatted(.iso8601.year().month().day().time(includingFractionalSeconds: false).timeZone(separator: .omitted)), "2021-07-01T15:56:32Z") - XCTAssertEqual(date.formatted(.iso8601.year().month().day().time(includingFractionalSeconds: true).timeZone(separator: .omitted).dateSeparator(.dash).dateTimeSeparator(.standard).timeSeparator(.colon)), "2021-07-01T15:56:32.000Z") - - XCTAssertEqual(date.formatted(.iso8601.year()), "2021") - XCTAssertEqual(date.formatted(.iso8601.year().month()), "2021-07") - XCTAssertEqual(date.formatted(.iso8601.year().month().day()), "2021-07-01") - XCTAssertEqual(date.formatted(.iso8601.year().month().day().dateSeparator(.omitted)), "20210701") - - XCTAssertEqual(date.formatted(.iso8601.year().weekOfYear()), "2021-W26") - XCTAssertEqual(date.formatted(.iso8601.year().weekOfYear().day()), "2021-W26-04") // day() is the weekday number - XCTAssertEqual(date.formatted(.iso8601.year().day()), "2021-182") // day() is the ordinal day - - XCTAssertEqual(date.formatted(.iso8601.time(includingFractionalSeconds: false)), "15:56:32") - XCTAssertEqual(date.formatted(.iso8601.time(includingFractionalSeconds: true)), "15:56:32.000") - XCTAssertEqual(date.formatted(.iso8601.time(includingFractionalSeconds: false).timeZone(separator: .omitted)), "15:56:32Z") + #expect(date.formatted(.iso8601) == "2021-07-01T15:56:32Z") + #expect(date.formatted(.iso8601.dateSeparator(.omitted)) == "20210701T15:56:32Z") + #expect(date.formatted(.iso8601.dateTimeSeparator(.space)) == "2021-07-01 15:56:32Z") + #expect(date.formatted(.iso8601.timeSeparator(.omitted)) == "2021-07-01T155632Z") + #expect(date.formatted(.iso8601.dateSeparator(.omitted).timeSeparator(.omitted)) == "20210701T155632Z") + #expect(date.formatted(.iso8601.year().month().day().time(includingFractionalSeconds: false).timeZone(separator: .omitted)) == "2021-07-01T15:56:32Z") + #expect(date.formatted(.iso8601.year().month().day().time(includingFractionalSeconds: true).timeZone(separator: .omitted).dateSeparator(.dash).dateTimeSeparator(.standard).timeSeparator(.colon)) == "2021-07-01T15:56:32.000Z") + + #expect(date.formatted(.iso8601.year()) == "2021") + #expect(date.formatted(.iso8601.year().month()) == "2021-07") + #expect(date.formatted(.iso8601.year().month().day()) == "2021-07-01") + #expect(date.formatted(.iso8601.year().month().day().dateSeparator(.omitted)) == "20210701") + + #expect(date.formatted(.iso8601.year().weekOfYear()) == "2021-W26") + #expect(date.formatted(.iso8601.year().weekOfYear().day()) == "2021-W26-04") // day() is the weekday number + #expect(date.formatted(.iso8601.year().day()) == "2021-182") // day() is the ordinal day + + #expect(date.formatted(.iso8601.time(includingFractionalSeconds: false)) == "15:56:32") + #expect(date.formatted(.iso8601.time(includingFractionalSeconds: true)) == "15:56:32.000") + #expect(date.formatted(.iso8601.time(includingFractionalSeconds: false).timeZone(separator: .omitted)) == "15:56:32Z") } - func test_remoteDate() throws { + @Test func test_remoteDate() throws { let date = Date(timeIntervalSince1970: 999999999999.0) // Remote date - XCTAssertEqual(date.formatted(.iso8601), "33658-09-27T01:46:39Z") - XCTAssertEqual(date.formatted(.iso8601.year().weekOfYear().day()), "33658-W39-05") // day() is the weekday number + #expect(date.formatted(.iso8601) == "33658-09-27T01:46:39Z") + #expect(date.formatted(.iso8601.year().weekOfYear().day()) == "33658-W39-05") // day() is the weekday number } - func test_internal_formatDateComponents() throws { + @Test func test_internal_formatDateComponents() throws { let dc = DateComponents(year: -2025, month: 1, day: 20, hour: 0, minute: 0, second: 0) let str = Date.ISO8601FormatStyle().format(dc, appendingTimeZoneOffset: 0) - XCTAssertEqual(str, "-2025-01-20T00:00:00Z") + #expect(str == "-2025-01-20T00:00:00Z") } - func test_rounding() { + @Test func test_rounding() { // Date is: "1970-01-01 15:35:45.9999" let date = Date(timeIntervalSinceReferenceDate: -978251054.0 - 0.0001) let str = Date.ISO8601FormatStyle().timeZone(separator: .colon).time(includingFractionalSeconds: true).timeSeparator(.colon).format(date) - XCTAssertEqual(str, "15:35:45.999Z") + #expect(str == "15:35:45.999Z") } } diff --git a/Tests/FoundationEssentialsTests/Formatting/ISO8601FormatStyleParsingTests.swift b/Tests/FoundationEssentialsTests/Formatting/ISO8601FormatStyleParsingTests.swift index e805d7ccc..732a5331b 100644 --- a/Tests/FoundationEssentialsTests/Formatting/ISO8601FormatStyleParsingTests.swift +++ b/Tests/FoundationEssentialsTests/Formatting/ISO8601FormatStyleParsingTests.swift @@ -6,9 +6,7 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing #if canImport(FoundationEssentials) @testable import FoundationEssentials @@ -18,41 +16,66 @@ import TestSupport @testable import Foundation #endif -final class ISO8601FormatStyleParsingTests: XCTestCase { +struct ISO8601FormatStyleParsingTests { /// See also the format-only tests in DateISO8601ForamtStyleEssentialsTests - func test_ISO8601Parse() throws { + @Test func test_ISO8601Parse() throws { let iso8601 = Date.ISO8601FormatStyle() // Date is: "2022-01-28 15:35:46" - XCTAssertEqual(try? iso8601.parse("2022-01-28T15:35:46Z"), Date(timeIntervalSinceReferenceDate: 665076946.0)) + var results = try iso8601.parse("2022-01-28T15:35:46Z") + #expect(results == Date(timeIntervalSinceReferenceDate: 665076946.0)) var iso8601Pacific = iso8601 iso8601Pacific.timeZone = TimeZone(secondsFromGMT: -3600 * 8)! - XCTAssertEqual(try? iso8601Pacific.timeSeparator(.omitted).parse("2022-01-28T073546-0800"), Date(timeIntervalSinceReferenceDate: 665076946.0)) + results = try iso8601Pacific.timeSeparator(.omitted).parse("2022-01-28T073546-0800") + #expect(results == Date(timeIntervalSinceReferenceDate: 665076946.0)) // Day-only results: the default time is midnight for parsed date when the time piece is missing // Date is: "2022-01-28 00:00:00" - XCTAssertEqual(try? iso8601.year().month().day().dateSeparator(.dash).parse("2022-01-28"), Date(timeIntervalSinceReferenceDate: 665020800.0)) + results = try iso8601.year().month().day().dateSeparator(.dash).parse("2022-01-28") + #expect(results == Date(timeIntervalSinceReferenceDate: 665020800.0)) // Date is: "2022-01-28 00:00:00" - XCTAssertEqual(try? iso8601.year().month().day().dateSeparator(.omitted).parse("20220128"), Date(timeIntervalSinceReferenceDate: 665020800.0)) + results = try iso8601.year().month().day().dateSeparator(.omitted).parse("20220128") + #expect(results == Date(timeIntervalSinceReferenceDate: 665020800.0)) // Time-only results: we use the default date of the format style, 1970-01-01, to supplement the parsed date without year, month or day // Date is: "1970-01-23 00:00:00" - XCTAssertEqual(try? iso8601.weekOfYear().day().dateSeparator(.dash).parse("W04-05"), Date(timeIntervalSinceReferenceDate: -976406400.0)) + results = try iso8601.weekOfYear().day().dateSeparator(.dash).parse("W04-05") + #expect(results == Date(timeIntervalSinceReferenceDate: -976406400.0)) // Date is: "1970-01-28 15:35:46" - XCTAssertEqual(try? iso8601.day().time(includingFractionalSeconds: false).timeSeparator(.colon).parse("028T15:35:46"), Date(timeIntervalSinceReferenceDate: -975918254.0)) + results = try iso8601.day() + .time(includingFractionalSeconds: false) + .timeSeparator(.colon) + .parse("028T15:35:46") + #expect(results == Date(timeIntervalSinceReferenceDate: -975918254.0)) // Date is: "1970-01-01 15:35:46" - XCTAssertEqual(try? iso8601.time(includingFractionalSeconds: false).timeSeparator(.colon).parse("15:35:46"), Date(timeIntervalSinceReferenceDate: -978251054.0)) + results = try iso8601 + .time(includingFractionalSeconds: false) + .timeSeparator(.colon) + .parse("15:35:46") + #expect(results == Date(timeIntervalSinceReferenceDate: -978251054.0)) // Date is: "1970-01-01 15:35:46" - XCTAssertEqual(try? iso8601.time(includingFractionalSeconds: false).timeZone(separator: .omitted).parse("15:35:46Z"), Date(timeIntervalSinceReferenceDate: -978251054.0)) + results = try iso8601 + .time(includingFractionalSeconds: false) + .timeZone(separator: .omitted) + .parse("15:35:46Z") + #expect(results == Date(timeIntervalSinceReferenceDate: -978251054.0)) // Date is: "1970-01-01 15:35:46" - XCTAssertEqual(try? iso8601.time(includingFractionalSeconds: false).timeZone(separator: .colon).parse("15:35:46Z"), Date(timeIntervalSinceReferenceDate: -978251054.0)) + results = try iso8601 + .time(includingFractionalSeconds: false) + .timeZone(separator: .colon) + .parse("15:35:46Z") + #expect(results == Date(timeIntervalSinceReferenceDate: -978251054.0)) // Date is: "1970-01-01 15:35:46" - XCTAssertEqual(try? iso8601.timeZone(separator: .colon).time(includingFractionalSeconds: false).timeSeparator(.colon).parse("15:35:46Z"), Date(timeIntervalSinceReferenceDate: -978251054.0)) + results = try iso8601.timeZone(separator: .colon) + .time(includingFractionalSeconds: false) + .timeSeparator(.colon) + .parse("15:35:46Z") + #expect(results == Date(timeIntervalSinceReferenceDate: -978251054.0)) } - func test_weekOfYear() throws { + @Test func test_weekOfYear() throws { let iso8601 = Date.ISO8601FormatStyle() // Test some dates around the 2019 - 2020 end of year, and 2026 which has W53 @@ -75,64 +98,100 @@ final class ISO8601FormatStyleParsingTests: XCTestCase { for d in dates { let parsedWoY = try iso8601.year().weekOfYear().day().parse(d.0) let parsedY = try iso8601.year().month().day().parse(d.1) - XCTAssertEqual(parsedWoY, parsedY) + #expect(parsedWoY == parsedY) } } - - func test_zeroLeadingDigits() { + + @Test func test_zeroLeadingDigits() throws { // The parser allows for an arbitrary number of 0 pads in digits, including none. let iso8601 = Date.ISO8601FormatStyle() // Date is: "2022-01-28 15:35:46" - XCTAssertEqual(try? iso8601.parse("2022-01-28T15:35:46Z"), Date(timeIntervalSinceReferenceDate: 665076946.0)) - XCTAssertEqual(try? iso8601.parse("002022-01-28T15:35:46Z"), Date(timeIntervalSinceReferenceDate: 665076946.0)) - XCTAssertEqual(try? iso8601.parse("2022-0001-28T15:35:46Z"), Date(timeIntervalSinceReferenceDate: 665076946.0)) - XCTAssertEqual(try? iso8601.parse("2022-01-0028T15:35:46Z"), Date(timeIntervalSinceReferenceDate: 665076946.0)) - XCTAssertEqual(try? iso8601.parse("2022-1-28T15:35:46Z"), Date(timeIntervalSinceReferenceDate: 665076946.0)) - XCTAssertEqual(try? iso8601.parse("2022-01-28T15:35:06Z"), Date(timeIntervalSinceReferenceDate: 665076906.0)) - XCTAssertEqual(try? iso8601.parse("2022-01-28T15:35:6Z"), Date(timeIntervalSinceReferenceDate: 665076906.0)) - XCTAssertEqual(try? iso8601.parse("2022-01-28T15:05:46Z"), Date(timeIntervalSinceReferenceDate: 665075146.0)) - XCTAssertEqual(try? iso8601.parse("2022-01-28T15:5:46Z"), Date(timeIntervalSinceReferenceDate: 665075146.0)) + var results = try iso8601.parse("2022-01-28T15:35:46Z") + #expect(results == Date(timeIntervalSinceReferenceDate: 665076946.0)) + results = try iso8601.parse("002022-01-28T15:35:46Z") + #expect(results == Date(timeIntervalSinceReferenceDate: 665076946.0)) + results = try iso8601.parse("2022-0001-28T15:35:46Z") + #expect(results == Date(timeIntervalSinceReferenceDate: 665076946.0)) + results = try iso8601.parse("2022-01-0028T15:35:46Z") + #expect(results == Date(timeIntervalSinceReferenceDate: 665076946.0)) + results = try iso8601.parse("2022-1-28T15:35:46Z") + #expect(results == Date(timeIntervalSinceReferenceDate: 665076946.0)) + results = try iso8601.parse("2022-01-28T15:35:06Z") + #expect(results == Date(timeIntervalSinceReferenceDate: 665076906.0)) + results = try iso8601.parse("2022-01-28T15:35:6Z") + #expect(results == Date(timeIntervalSinceReferenceDate: 665076906.0)) + results = try iso8601.parse("2022-01-28T15:05:46Z") + #expect(results == Date(timeIntervalSinceReferenceDate: 665075146.0)) + results = try iso8601.parse("2022-01-28T15:5:46Z") + #expect(results == Date(timeIntervalSinceReferenceDate: 665075146.0)) } - - func test_timeZones() { + + @Test func test_timeZones() throws { let iso8601 = Date.ISO8601FormatStyle() let date = Date(timeIntervalSinceReferenceDate: 665076946.0) - + var iso8601Pacific = iso8601 iso8601Pacific.timeZone = TimeZone(secondsFromGMT: -3600 * 8)! - + // Has a seconds component (-28830) var iso8601PacificIsh = iso8601 iso8601PacificIsh.timeZone = TimeZone(secondsFromGMT: -3600 * 8 - 30)! - - XCTAssertEqual(try? iso8601Pacific.timeSeparator(.omitted).parse("2022-01-28T073546-0800"), date) - XCTAssertEqual(try? iso8601Pacific.timeSeparator(.omitted).timeZoneSeparator(.colon).parse("2022-01-28T073546-08:00"), date) - XCTAssertEqual(try? iso8601PacificIsh.timeSeparator(.omitted).parse("2022-01-28T073516-080030"), date) - XCTAssertEqual(try? iso8601PacificIsh.timeSeparator(.omitted).timeZoneSeparator(.colon).parse("2022-01-28T073516-08:00:30"), date) - + var results = try iso8601Pacific.timeSeparator(.omitted).parse("2022-01-28T073546-0800") + #expect(results == date) + results = try iso8601Pacific + .timeSeparator(.omitted) + .timeZoneSeparator(.colon) + .parse("2022-01-28T073546-08:00") + #expect(results == date) + + results = try iso8601PacificIsh.timeSeparator(.omitted).parse("2022-01-28T073516-080030") + #expect(results == date) + results = try iso8601PacificIsh + .timeSeparator(.omitted) + .timeZoneSeparator(.colon) + .parse("2022-01-28T073516-08:00:30") + #expect(results == date) + var iso8601gmtP1 = iso8601 iso8601gmtP1.timeZone = TimeZone(secondsFromGMT: 3600)! - XCTAssertEqual(try? iso8601gmtP1.timeSeparator(.omitted).parse("2022-01-28T163546+0100"), date) - XCTAssertEqual(try? iso8601gmtP1.timeSeparator(.omitted).parse("2022-01-28T163546+010000"), date) - XCTAssertEqual(try? iso8601gmtP1.timeSeparator(.omitted).timeZoneSeparator(.colon).parse("2022-01-28T163546+01:00"), date) - XCTAssertEqual(try? iso8601gmtP1.timeSeparator(.omitted).timeZoneSeparator(.colon).parse("2022-01-28T163546+01:00:00"), date) + results = try iso8601gmtP1.timeSeparator(.omitted).parse("2022-01-28T163546+0100") + #expect(results == date) + results = try iso8601gmtP1.timeSeparator(.omitted).parse("2022-01-28T163546+010000") + #expect(results == date) + results = try iso8601gmtP1 + .timeSeparator(.omitted) + .timeZoneSeparator(.colon) + .parse("2022-01-28T163546+01:00") + #expect(results == date) + results = try iso8601gmtP1 + .timeSeparator(.omitted) + .timeZoneSeparator(.colon) + .parse("2022-01-28T163546+01:00:00") + #expect(results == date) // Due to a quirk of the original implementation, colons are allowed to be present in the time zone even if the time zone separator is omitted - XCTAssertEqual(try? iso8601gmtP1.timeSeparator(.omitted).parse("2022-01-28T163546+01:00"), date) - XCTAssertEqual(try? iso8601gmtP1.timeSeparator(.omitted).parse("2022-01-28T163546+01:00:00"), date) + results = try iso8601gmtP1 + .timeSeparator(.omitted) + .parse("2022-01-28T163546+01:00") + #expect(results == date) + results = try iso8601gmtP1 + .timeSeparator(.omitted) + .parse("2022-01-28T163546+01:00:00") + #expect(results == date) } - - func test_fractionalSeconds() { + + @Test func test_fractionalSeconds() throws { let expectedDate = Date(timeIntervalSinceReferenceDate: 646876592.34567) var iso8601 = Date.ISO8601FormatStyle().year().month().day().time(includingFractionalSeconds: true) iso8601.timeZone = .gmt - XCTAssertEqual(try? iso8601.parse("2021-07-01T23:56:32.34567"), expectedDate) + let results = try iso8601.parse("2021-07-01T23:56:32.34567") + #expect(results == expectedDate) } - - func test_specialTimeZonesAndSpaces() { + + @Test func test_specialTimeZonesAndSpaces() { let reference = try! Date("2020-03-05T12:00:00+00:00", strategy: .iso8601) let tests : [(String, Date.ISO8601FormatStyle)] = [ @@ -165,7 +224,7 @@ final class ISO8601FormatStyleParsingTests: XCTestCase { for (parseMe, style) in tests { let parsed = try? style.parse(parseMe) - XCTAssertEqual(parsed, reference, """ + #expect(parsed == reference, """ parsing : \(parseMe) expected: \(reference) \(reference.timeIntervalSinceReferenceDate) @@ -175,18 +234,18 @@ result : \(parsed != nil ? parsed!.debugDescription : "nil") \(parsed != nil ? } #if canImport(FoundationInternationalization) || FOUNDATION_FRAMEWORK - func test_chileTimeZone() { + @Test func test_chileTimeZone() { var iso8601Chile = Date.ISO8601FormatStyle().year().month().day() iso8601Chile.timeZone = TimeZone(name: "America/Santiago")! let date = try? iso8601Chile.parse("2023-09-03") - XCTAssertNotNil(date) + #expect(date != nil) } #endif } @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) -final class DateISO8601FormatStylePatternMatchingTests : XCTestCase { +struct DateISO8601FormatStylePatternMatchingTests { func _matchFullRange(_ str: String, formatStyle: Date.ISO8601FormatStyle, expectedUpperBound: String.Index?, expectedDate: Date?, file: StaticString = #file, line: UInt = #line) { _matchRange(str, formatStyle: formatStyle, range: nil, expectedUpperBound: expectedUpperBound, expectedDate: expectedDate, file: file, line: line) @@ -200,19 +259,28 @@ final class DateISO8601FormatStylePatternMatchingTests : XCTestCase { let upperBoundDescription = upperBound?.utf16Offset(in: str) let expectedUpperBoundDescription = expectedUpperBound?.utf16Offset(in: str) - XCTAssertEqual(upperBound, expectedUpperBound, "found upperBound: \(String(describing: upperBoundDescription)); expected: \(String(describing: expectedUpperBoundDescription))", file: file, line: line) + #expect( + upperBound == expectedUpperBound, + "found upperBound: \(String(describing: upperBoundDescription)); expected: \(String(describing: expectedUpperBoundDescription))", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) if let match, let expectedDate { - XCTAssertEqual( - match.timeIntervalSinceReferenceDate, - expectedDate.timeIntervalSinceReferenceDate, - accuracy: 0.001, - file: file, - line: line + let delta = abs(match.timeIntervalSinceReferenceDate - + expectedDate.timeIntervalSinceReferenceDate) + #expect( + delta <= 0.001, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) ) } } - func testMatchDefaultISO8601Style() throws { + @Test func testMatchDefaultISO8601Style() throws { let iso8601FormatStyle = Date.ISO8601FormatStyle() func verify(_ str: String, expectedUpperBound: String.Index?, expectedDate: Date?, file: StaticString = #file, line: UInt = #line) { @@ -230,7 +298,7 @@ final class DateISO8601FormatStylePatternMatchingTests : XCTestCase { verify("9999-37-40T35:70:99Z", expectedUpperBound: nil, expectedDate: nil) // This is not a valid date } - func testPartialMatchISO8601() throws { + @Test func testPartialMatchISO8601() throws { var expectedDate: Date? var expectedLength: Int? func verify(_ str: String, _ style: Date.ISO8601FormatStyle, file: StaticString = #file, line: UInt = #line) { @@ -302,7 +370,7 @@ final class DateISO8601FormatStylePatternMatchingTests : XCTestCase { } } - func testFullMatch() { + @Test func testFullMatch() { var expectedDate: Date func verify(_ str: String, _ style: Date.ISO8601FormatStyle, file: StaticString = #file, line: UInt = #line) { diff --git a/Tests/FoundationEssentialsTests/GregorianCalendarTests.swift b/Tests/FoundationEssentialsTests/GregorianCalendarTests.swift index f9a6bafa4..c0ecc5640 100644 --- a/Tests/FoundationEssentialsTests/GregorianCalendarTests.swift +++ b/Tests/FoundationEssentialsTests/GregorianCalendarTests.swift @@ -10,9 +10,7 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing #if canImport(FoundationEssentials) @testable import FoundationEssentials @@ -23,61 +21,67 @@ import TestSupport #endif // Tests for _GregorianCalendar -final class GregorianCalendarTests : XCTestCase { +struct GregorianCalendarTests { - func testCopy() { + @Test func testCopy() { let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: nil, locale: nil, firstWeekday: 5, minimumDaysInFirstWeek: 3, gregorianStartDate: nil) let newLocale = Locale(identifier: "new locale") let tz = TimeZone(identifier: "America/Los_Angeles")! let copied = gregorianCalendar.copy(changingLocale: newLocale, changingTimeZone: tz, changingFirstWeekday: nil, changingMinimumDaysInFirstWeek: nil) // newly set values - XCTAssertEqual(copied.locale, newLocale) - XCTAssertEqual(copied.timeZone, tz) + #expect(copied.locale == newLocale) + #expect(copied.timeZone == tz) // unset values stay the same - XCTAssertEqual(copied.firstWeekday, 5) - XCTAssertEqual(copied.minimumDaysInFirstWeek, 3) + #expect(copied.firstWeekday == 5) + #expect(copied.minimumDaysInFirstWeek == 3) let copied2 = gregorianCalendar.copy(changingLocale: nil, changingTimeZone: nil, changingFirstWeekday: 1, changingMinimumDaysInFirstWeek: 1) // unset values stay the same - XCTAssertEqual(copied2.locale, gregorianCalendar.locale) - XCTAssertEqual(copied2.timeZone, gregorianCalendar.timeZone) + #expect(copied2.locale == gregorianCalendar.locale) + #expect(copied2.timeZone == gregorianCalendar.timeZone) // overriding existing values - XCTAssertEqual(copied2.firstWeekday, 1) - XCTAssertEqual(copied2.minimumDaysInFirstWeek, 1) + #expect(copied2.firstWeekday == 1) + #expect(copied2.minimumDaysInFirstWeek == 1) } // MARK: Basic - func testNumberOfDaysInMonth() { + @Test func testNumberOfDaysInMonth() { let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: nil, locale: nil, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil) - XCTAssertEqual(gregorianCalendar.numberOfDaysInMonth(2, year: 2023), 28) // not leap year - XCTAssertEqual(gregorianCalendar.numberOfDaysInMonth(0, year: 2023), 31) // equivalent to month: 12, year: 2022 - XCTAssertEqual(gregorianCalendar.numberOfDaysInMonth(14, year: 2023), 29) // equivalent to month: 2, year: 2024 + #expect(gregorianCalendar.numberOfDaysInMonth(2, year: 2023) == 28) // not leap year + #expect(gregorianCalendar.numberOfDaysInMonth(0, year: 2023) == 31) // equivalent to month: 12, year: 2022 + #expect(gregorianCalendar.numberOfDaysInMonth(14, year: 2023) == 29) // equivalent to month: 2, year: 2024 - XCTAssertEqual(gregorianCalendar.numberOfDaysInMonth(2, year: 2024), 29) // leap year - XCTAssertEqual(gregorianCalendar.numberOfDaysInMonth(-10, year: 2024), 28) // equivalent to month: 2, year: 2023, not leap - XCTAssertEqual(gregorianCalendar.numberOfDaysInMonth(14, year: 2024), 28) // equivalent to month: 2, year: 2025, not leap + #expect(gregorianCalendar.numberOfDaysInMonth(2, year: 2024) == 29) // leap year + #expect(gregorianCalendar.numberOfDaysInMonth(-10, year: 2024) == 28) // equivalent to month: 2, year: 2023, not leap + #expect(gregorianCalendar.numberOfDaysInMonth(14, year: 2024) == 28) // equivalent to month: 2, year: 2025, not leap - XCTAssertEqual(gregorianCalendar.numberOfDaysInMonth(50, year: 2024), 29) // equivalent to month: 2, year: 2028, leap + #expect(gregorianCalendar.numberOfDaysInMonth(50, year: 2024) == 29) // equivalent to month: 2, year: 2028, leap } - func testRemoteJulianDayCrash() { + @Test func testRemoteJulianDayCrash() { // Accessing the integer julianDay of a remote date should not crash let d = Date(julianDate: 9223372036854775808) // Int64.max + 1 _ = d.julianDay } // MARK: Date from components - func testDateFromComponents_DST() { + @Test func testDateFromComponents_DST() { // The expected dates were generated using ICU Calendar let tz = TimeZone(identifier: "America/Los_Angeles")! let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: tz, locale: nil, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil) func test(_ dateComponents: DateComponents, expected: Date, file: StaticString = #file, line: UInt = #line) { let date = gregorianCalendar.date(from: dateComponents)! - XCTAssertEqual(date, expected, "DateComponents: \(dateComponents)", file: file, line: line) + #expect( + date == expected, + "DateComponents: \(dateComponents)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } test(.init(year: 2023, month: 10, day: 16), expected: Date(timeIntervalSince1970: 1697439600.0)) @@ -94,13 +98,19 @@ final class GregorianCalendarTests : XCTestCase { test(.init(year: 2023, month: 11, day: 5, hour: 3, minute: 34, second: 52), expected: Date(timeIntervalSince1970: 1699184092.0)) } - func testDateFromComponents() { + @Test func testDateFromComponents() { // The expected dates were generated using ICU Calendar let tz = TimeZone.gmt let cal = _CalendarGregorian(identifier: .gregorian, timeZone: tz, locale: nil, firstWeekday: 1, minimumDaysInFirstWeek: 4, gregorianStartDate: nil) func test(_ dateComponents: DateComponents, expected: Date, file: StaticString = #file, line: UInt = #line) { let date = cal.date(from: dateComponents) - XCTAssertEqual(date, expected, "date components: \(dateComponents)", file: file, line: line) + #expect( + date == expected, + "DateComponents: \(dateComponents)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } test(.init(year: 1582, month: -7, weekday: 5, weekdayOrdinal: 0), expected: Date(timeIntervalSince1970: -12264739200.0)) @@ -168,7 +178,7 @@ final class GregorianCalendarTests : XCTestCase { test(.init(weekOfYear: 43, yearForWeekOfYear: 2935), expected: Date(timeIntervalSince1970: 30477945600.0)) } - func testDateFromComponents_componentsTimeZone() { + @Test func testDateFromComponents_componentsTimeZone() { let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: .gmt, locale: nil, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil) let dcCalendar = Calendar(identifier: .japanese, locale: Locale(identifier: ""), timeZone: .init(secondsFromGMT: -25200), firstWeekday: 1, minimumDaysInFirstWeek: 1, gregorianStartDate: nil) @@ -179,21 +189,30 @@ final class GregorianCalendarTests : XCTestCase { dc_customCalendarAndTimeZone.timeZone = .init(secondsFromGMT: 28800) // calendar.timeZone = UTC+0, dc.calendar.timeZone = UTC-7, dc.timeZone = UTC+8 // expect local time in dc.timeZone (UTC+8) - XCTAssertEqual(gregorianCalendar.date(from: dc_customCalendarAndTimeZone)!, Date(timeIntervalSinceReferenceDate: 679024975.891)) // 2022-07-09T02:02:55Z + #expect( + gregorianCalendar.date(from: dc_customCalendarAndTimeZone)! == + Date(timeIntervalSinceReferenceDate: 679024975.891) + ) // 2022-07-09T02:02:55Z var dc_customCalendar = dc dc_customCalendar.calendar = dcCalendar dc_customCalendar.timeZone = nil // calendar.timeZone = UTC+0, dc.calendar.timeZone = UTC-7, dc.timeZone = nil // expect local time in calendar.timeZone (UTC+0) - XCTAssertEqual(gregorianCalendar.date(from: dc_customCalendar)!, Date(timeIntervalSinceReferenceDate: 679053775.891)) // 2022-07-09T10:02:55Z + #expect( + gregorianCalendar.date(from: dc_customCalendar)! == + Date(timeIntervalSinceReferenceDate: 679053775.891) + ) // 2022-07-09T10:02:55Z var dc_customTimeZone = dc_customCalendarAndTimeZone dc_customTimeZone.calendar = nil dc_customTimeZone.timeZone = .init(secondsFromGMT: 28800) // calendar.timeZone = UTC+0, dc.calendar = nil, dc.timeZone = UTC+8 // expect local time in dc.timeZone (UTC+8) - XCTAssertEqual(gregorianCalendar.date(from: dc_customTimeZone)!, Date(timeIntervalSinceReferenceDate: 679024975.891)) // 2022-07-09T02:02:55Z + #expect( + gregorianCalendar.date(from: dc_customTimeZone)! == + Date(timeIntervalSinceReferenceDate: 679024975.891) + ) // 2022-07-09T02:02:55Z let dcCalendar_noTimeZone = Calendar(identifier: .japanese, locale: Locale(identifier: ""), timeZone: nil, firstWeekday: 1, minimumDaysInFirstWeek: 1, gregorianStartDate: nil) var dc_customCalendarNoTimeZone_customTimeZone = dc @@ -201,10 +220,13 @@ final class GregorianCalendarTests : XCTestCase { dc_customCalendarNoTimeZone_customTimeZone.timeZone = .init(secondsFromGMT: 28800) // calendar.timeZone = UTC+0, dc.calendar.timeZone = nil, dc.timeZone = UTC+8 // expect local time in dc.timeZone (UTC+8) - XCTAssertEqual(gregorianCalendar.date(from: dc_customCalendarNoTimeZone_customTimeZone)!, Date(timeIntervalSinceReferenceDate: 679024975.891)) // 2022-07-09T02:02:55Z + #expect( + gregorianCalendar.date(from: dc_customCalendarNoTimeZone_customTimeZone)! == + Date(timeIntervalSinceReferenceDate: 679024975.891) + ) // 2022-07-09T02:02:55Z } - func testDateFromComponents_componentsTimeZoneConversion() { + @Test func testDateFromComponents_componentsTimeZoneConversion() { let timeZone = TimeZone.gmt let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: timeZone, locale: nil, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil) @@ -217,30 +239,31 @@ final class GregorianCalendarTests : XCTestCase { let startOfYearEST_greg = gregorianCalendar.date(from: components) let expected = startOfYearGMT + 3600*5 // January 1, 2020 12:00:00 AM (GMT) - XCTAssertEqual(startOfYearEST_greg, expected) + #expect(startOfYearEST_greg == expected) } // MARK: - DateComponents from date - func testDateComponentsFromDate() { + @Test func testDateComponentsFromDate() { let calendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(secondsFromGMT: 0)!, locale: nil, firstWeekday: 1, minimumDaysInFirstWeek: 5, gregorianStartDate: nil) func test(_ date: Date, _ timeZone: TimeZone, expectedEra era: Int, year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int, nanosecond: Int, weekday: Int, weekdayOrdinal: Int, quarter: Int, weekOfMonth: Int, weekOfYear: Int, yearForWeekOfYear: Int, isLeapMonth: Bool, file: StaticString = #file, line: UInt = #line) { let dc = calendar.dateComponents([.era, .year, .month, .day, .hour, .minute, .second, .nanosecond, .weekday, .weekdayOrdinal, .quarter, .weekOfMonth, .weekOfYear, .yearForWeekOfYear, .calendar, .timeZone], from: date) - XCTAssertEqual(dc.era, era, file: file, line: line) - XCTAssertEqual(dc.year, year, file: file, line: line) - XCTAssertEqual(dc.month, month, file: file, line: line) - XCTAssertEqual(dc.day, day, file: file, line: line) - XCTAssertEqual(dc.hour, hour, file: file, line: line) - XCTAssertEqual(dc.minute, minute, file: file, line: line) - XCTAssertEqual(dc.second, second, file: file, line: line) - XCTAssertEqual(dc.weekday, weekday, file: file, line: line) - XCTAssertEqual(dc.weekdayOrdinal, weekdayOrdinal, file: file, line: line) - XCTAssertEqual(dc.weekOfMonth, weekOfMonth, file: file, line: line) - XCTAssertEqual(dc.weekOfYear, weekOfYear, file: file, line: line) - XCTAssertEqual(dc.yearForWeekOfYear, yearForWeekOfYear, file: file, line: line) - XCTAssertEqual(dc.quarter, quarter, file: file, line: line) - XCTAssertEqual(dc.nanosecond, nanosecond, file: file, line: line) - XCTAssertEqual(dc.isLeapMonth, isLeapMonth, file: file, line: line) - XCTAssertEqual(dc.timeZone, timeZone, file: file, line: line) + let location = SourceLocation(filePath: String(describing: file), line: Int(line)) + #expect(dc.era == era, sourceLocation: location) + #expect(dc.year == year, sourceLocation: location) + #expect(dc.month == month, sourceLocation: location) + #expect(dc.day == day, sourceLocation: location) + #expect(dc.hour == hour, sourceLocation: location) + #expect(dc.minute == minute, sourceLocation: location) + #expect(dc.second == second, sourceLocation: location) + #expect(dc.weekday == weekday, sourceLocation: location) + #expect(dc.weekdayOrdinal == weekdayOrdinal, sourceLocation: location) + #expect(dc.weekOfMonth == weekOfMonth, sourceLocation: location) + #expect(dc.weekOfYear == weekOfYear, sourceLocation: location) + #expect(dc.yearForWeekOfYear == yearForWeekOfYear, sourceLocation: location) + #expect(dc.quarter == quarter, sourceLocation: location) + #expect(dc.nanosecond == nanosecond, sourceLocation: location) + #expect(dc.isLeapMonth == isLeapMonth, sourceLocation: location) + #expect(dc.timeZone == timeZone, sourceLocation: location) } test(Date(timeIntervalSince1970: 852045787.0), .gmt, expectedEra: 1, year: 1996, month: 12, day: 31, hour: 15, minute: 23, second: 7, nanosecond: 0, weekday: 3, weekdayOrdinal: 5, quarter: 4, weekOfMonth: 5, weekOfYear: 53, yearForWeekOfYear: 1996, isLeapMonth: false) // 1996-12-31T15:23:07Z test(Date(timeIntervalSince1970: 825607387.0), .gmt, expectedEra: 1, year: 1996, month: 2, day: 29, hour: 15, minute: 23, second: 7, nanosecond: 0, weekday: 5, weekdayOrdinal: 5, quarter: 1, weekOfMonth: 4, weekOfYear: 9, yearForWeekOfYear: 1996, isLeapMonth: false) // 1996-02-29T15:23:07Z @@ -249,26 +272,27 @@ final class GregorianCalendarTests : XCTestCase { test(Date(timeIntervalSince1970: -62135852213.0), .gmt, expectedEra: 0, year: 1, month: 12, day: 31, hour: 1, minute: 3, second: 7, nanosecond: 0, weekday: 6, weekdayOrdinal: 5, quarter: 4, weekOfMonth: 4, weekOfYear: 52, yearForWeekOfYear: 0, isLeapMonth: false) // 0000-12-31T01:03:07Z } - func testDateComponentsFromDate_DST() { + @Test func testDateComponentsFromDate_DST() { func test(_ date: Date, expectedEra era: Int, year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int, nanosecond: Int, weekday: Int, weekdayOrdinal: Int, quarter: Int, weekOfMonth: Int, weekOfYear: Int, yearForWeekOfYear: Int, isLeapMonth: Bool, file: StaticString = #file, line: UInt = #line) { let dc = calendar.dateComponents([.era, .year, .month, .day, .hour, .minute, .second, .nanosecond, .weekday, .weekdayOrdinal, .quarter, .weekOfMonth, .weekOfYear, .yearForWeekOfYear, .calendar, .timeZone], from: date) - XCTAssertEqual(dc.era, era, "era should be equal", file: file, line: line) - XCTAssertEqual(dc.year, year, "era should be equal", file: file, line: line) - XCTAssertEqual(dc.month, month, "month should be equal", file: file, line: line) - XCTAssertEqual(dc.day, day, "day should be equal", file: file, line: line) - XCTAssertEqual(dc.hour, hour, "hour should be equal", file: file, line: line) - XCTAssertEqual(dc.minute, minute, "minute should be equal", file: file, line: line) - XCTAssertEqual(dc.second, second, "second should be equal", file: file, line: line) - XCTAssertEqual(dc.weekday, weekday, "weekday should be equal", file: file, line: line) - XCTAssertEqual(dc.weekdayOrdinal, weekdayOrdinal, "weekdayOrdinal should be equal", file: file, line: line) - XCTAssertEqual(dc.weekOfMonth, weekOfMonth, "weekOfMonth should be equal", file: file, line: line) - XCTAssertEqual(dc.weekOfYear, weekOfYear, "weekOfYear should be equal", file: file, line: line) - XCTAssertEqual(dc.yearForWeekOfYear, yearForWeekOfYear, "yearForWeekOfYear should be equal", file: file, line: line) - XCTAssertEqual(dc.quarter, quarter, "quarter should be equal", file: file, line: line) - XCTAssertEqual(dc.nanosecond, nanosecond, "nanosecond should be equal", file: file, line: line) - XCTAssertEqual(dc.isLeapMonth, isLeapMonth, "isLeapMonth should be equal", file: file, line: line) - XCTAssertEqual(dc.timeZone, calendar.timeZone, "timeZone should be equal", file: file, line: line) + let location = SourceLocation(filePath: String(describing: file), line: Int(line)) + #expect(dc.era == era, "era should be equal", sourceLocation: location) + #expect(dc.year == year, "era should be equal", sourceLocation: location) + #expect(dc.month == month, "month should be equal", sourceLocation: location) + #expect(dc.day == day, "day should be equal", sourceLocation: location) + #expect(dc.hour == hour, "hour should be equal", sourceLocation: location) + #expect(dc.minute == minute, "minute should be equal", sourceLocation: location) + #expect(dc.second == second, "second should be equal", sourceLocation: location) + #expect(dc.weekday == weekday, "weekday should be equal", sourceLocation: location) + #expect(dc.weekdayOrdinal == weekdayOrdinal, "weekdayOrdinal should be equal", sourceLocation: location) + #expect(dc.weekOfMonth == weekOfMonth, "weekOfMonth should be equal", sourceLocation: location) + #expect(dc.weekOfYear == weekOfYear, "weekOfYear should be equal", sourceLocation: location) + #expect(dc.yearForWeekOfYear == yearForWeekOfYear, "yearForWeekOfYear should be equal", sourceLocation: location) + #expect(dc.quarter == quarter, "quarter should be equal", sourceLocation: location) + #expect(dc.nanosecond == nanosecond, "nanosecond should be equal", sourceLocation: location) + #expect(dc.isLeapMonth == isLeapMonth, "isLeapMonth should be equal", sourceLocation: location) + #expect(dc.timeZone == calendar.timeZone, "timeZone should be equal", sourceLocation: location) } var calendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(identifier: "America/Los_Angeles")!, locale: nil, firstWeekday: 1, minimumDaysInFirstWeek: 1, gregorianStartDate: nil) @@ -286,13 +310,18 @@ final class GregorianCalendarTests : XCTestCase { // MARK: - Add - func testAdd() { + @Test func testAdd() { let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(secondsFromGMT: 3600)!, locale: nil, firstWeekday: 3, minimumDaysInFirstWeek: 4, gregorianStartDate: nil) var date: Date func test(addField field: Calendar.Component, value: Int, to addingToDate: Date, wrap: Bool, expectedDate: Date, _ file: StaticString = #file, _ line: UInt = #line) { let components = DateComponents(component: field, value: value)! let result = gregorianCalendar.date(byAdding: components, to: addingToDate, wrappingComponents: wrap)! - XCTAssertEqual(result, expectedDate, file: file, line: line) + #expect( + result == expectedDate, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } date = Date(timeIntervalSince1970: 825723300.0) @@ -408,13 +437,18 @@ final class GregorianCalendarTests : XCTestCase { test(addField: .second, value: -1, to: date, wrap: false, expectedDate: Date(timeIntervalSince1970: -210866774823)) } - func testAdd_boundaries() { + @Test func testAdd_boundaries() { let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(secondsFromGMT: 3600)!, locale: nil, firstWeekday: 3, minimumDaysInFirstWeek: 4, gregorianStartDate: nil) var date: Date func test(addField field: Calendar.Component, value: Int, to addingToDate: Date, wrap: Bool, expectedDate: Date, _ file: StaticString = #file, _ line: UInt = #line) { let components = DateComponents(component: field, value: value)! let result = gregorianCalendar.date(byAdding: components, to: addingToDate, wrappingComponents: wrap)! - XCTAssertEqual(result, expectedDate, file: file, line: line) + #expect( + result == expectedDate, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } date = Date(timeIntervalSince1970: 62135596800.0) // 3939-01-01 @@ -528,11 +562,16 @@ final class GregorianCalendarTests : XCTestCase { test(addField: .nanosecond, value: -1, to: date, wrap: false, expectedDate: Date(timeIntervalSince1970: -62135769600.0)) } - func testAddDateComponents() { + @Test func testAddDateComponents() { func testAdding(_ comp: DateComponents, to date: Date, wrap: Bool, expected: Date, _ file: StaticString = #file, _ line: UInt = #line) { let result = gregorianCalendar.date(byAdding: comp, to: date, wrappingComponents: wrap)! - XCTAssertEqual(result, expected, file: file, line: line) + #expect( + result == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var gregorianCalendar: _CalendarGregorian @@ -616,12 +655,17 @@ final class GregorianCalendarTests : XCTestCase { } } - func testAddDateComponents_DST() { + @Test func testAddDateComponents_DST() { let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(identifier: "America/Los_Angeles")!, locale: nil, firstWeekday: 2, minimumDaysInFirstWeek: 4, gregorianStartDate: nil) func testAdding(_ comp: DateComponents, to date: Date, wrap: Bool, expected: Date, _ file: StaticString = #file, _ line: UInt = #line) { let result = gregorianCalendar.date(byAdding: comp, to: date, wrappingComponents: wrap)! - XCTAssertEqual(result, expected, file: file, line: line) + #expect( + result == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } let march1_1996 = Date(timeIntervalSince1970: 825723300) @@ -646,13 +690,18 @@ final class GregorianCalendarTests : XCTestCase { testAdding(.init(day: -7, weekOfMonth: 1, weekOfYear: 1), to: march1_1996, wrap: true, expected: Date(timeIntervalSince1970: 829002900.0)) } - func testAddDateComponents_DSTBoundaries() { + @Test func testAddDateComponents_DSTBoundaries() { let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(identifier: "America/Los_Angeles")!, locale: nil, firstWeekday: 3, minimumDaysInFirstWeek: 5, gregorianStartDate: nil) let fmt = Date.ISO8601FormatStyle(timeZone: gregorianCalendar.timeZone) func testAdding(_ comp: DateComponents, to date: Date, expected: Date, _ file: StaticString = #file, _ line: UInt = #line) { let result = gregorianCalendar.date(byAdding: comp, to: date, wrappingComponents: false)! - XCTAssertEqual(result, expected, "result: \(fmt.format(result)); expected: \(fmt.format(expected))", file: file, line: line) + #expect( + result == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var date: Date @@ -1337,13 +1386,19 @@ final class GregorianCalendarTests : XCTestCase { testAdding(.init(weekOfYear: 1), to: date, expected: Date(timeIntervalSince1970: 814968187.0)) } - func testAddDateComponents_Wrapping_DSTBoundaries() { + @Test func testAddDateComponents_Wrapping_DSTBoundaries() { let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(identifier: "America/Los_Angeles")!, locale: nil, firstWeekday: 3, minimumDaysInFirstWeek: 5, gregorianStartDate: nil) let fmt = Date.ISO8601FormatStyle(timeZone: gregorianCalendar.timeZone) func testAdding(_ comp: DateComponents, to date: Date, expected: Date, _ file: StaticString = #file, _ line: UInt = #line) { let result = gregorianCalendar.date(byAdding: comp, to: date, wrappingComponents: true)! - XCTAssertEqual(result, expected, "result: \(fmt.format(result)); expected: \(fmt.format(expected))", file: file, line: line) + #expect( + result == expected, + "result: \(fmt.format(result)); expected: \(fmt.format(expected))", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var date: Date @@ -1901,7 +1956,7 @@ final class GregorianCalendarTests : XCTestCase { testAdding(.init(weekOfYear: 1), to: date, expected: Date(timeIntervalSince1970: 814968187.0)) } - func testAdd_DST() { + @Test func testAdd_DST() { let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(identifier: "America/Los_Angeles")!, locale: nil, firstWeekday: 3, minimumDaysInFirstWeek: 5, gregorianStartDate: nil) let fmt = Date.ISO8601FormatStyle(timeZone: gregorianCalendar.timeZone) @@ -1912,7 +1967,13 @@ final class GregorianCalendarTests : XCTestCase { let expectedDiff = expectedDate.timeIntervalSince(addingToDate) let msg = "actual diff: \(actualDiff), expected: \(expectedDiff), actual ti = \(result.timeIntervalSince1970), expected ti = \(expectedDate.timeIntervalSince1970), actual = \(fmt.format(result)), expected = \(fmt.format(expectedDate))" - XCTAssertEqual(result, expectedDate, msg, file: file, line: line) + #expect( + result == expectedDate, + .init(rawValue: msg), + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var date: Date @@ -2233,7 +2294,7 @@ final class GregorianCalendarTests : XCTestCase { test(addField: .nanosecond, value: -1, to: date, expectedDate: Date(timeIntervalSince1970: 814964587.0)) } - func testAdd_Wrap_DST() { + @Test func testAdd_Wrap_DST() { let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(identifier: "America/Los_Angeles")!, locale: nil, firstWeekday: 3, minimumDaysInFirstWeek: 5, gregorianStartDate: nil) let fmt = Date.ISO8601FormatStyle(timeZone: gregorianCalendar.timeZone) @@ -2241,7 +2302,13 @@ final class GregorianCalendarTests : XCTestCase { let components = DateComponents(component: field, value: value)! let result = gregorianCalendar.date(byAdding: components, to: addingToDate, wrappingComponents: true)! let msg = "actual = \(fmt.format(result)), expected = \(fmt.format(expectedDate))" - XCTAssertEqual(result, expectedDate, msg, file: file, line: line) + #expect( + result == expectedDate, + .init(rawValue: msg), + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var date: Date @@ -2418,12 +2485,18 @@ final class GregorianCalendarTests : XCTestCase { // This test requires 64-bit integers #if arch(x86_64) || arch(arm64) - func testOrdinality() { + @Test func testOrdinality() { let cal = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(secondsFromGMT: 3600)!, locale: nil, firstWeekday: 5, minimumDaysInFirstWeek: 4, gregorianStartDate: nil) func test(_ small: Calendar.Component, in large: Calendar.Component, for date: Date, expected: Int?, file: StaticString = #file, line: UInt = #line) { let result = cal.ordinality(of: small, in: large, for: date) - XCTAssertEqual(result, expected, "small: \(small), large: \(large)", file: file, line: line) + #expect( + result == expected, + "small: \(small), large: \(large)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var date: Date @@ -2534,12 +2607,18 @@ final class GregorianCalendarTests : XCTestCase { } #endif - func testOrdinality_DST() { + @Test func testOrdinality_DST() { let cal = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(identifier: "America/Los_Angeles")!, locale: nil, firstWeekday: 5, minimumDaysInFirstWeek: 4, gregorianStartDate: nil) func test(_ small: Calendar.Component, in large: Calendar.Component, for date: Date, expected: Int?, file: StaticString = #file, line: UInt = #line) { let result = cal.ordinality(of: small, in: large, for: date) - XCTAssertEqual(result, expected, "small: \(small), large: \(large)", file: file, line: line) + #expect( + result == expected, + "small: \(small), large: \(large)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var date: Date @@ -2597,233 +2676,238 @@ final class GregorianCalendarTests : XCTestCase { // This test requires 64-bit integers #if arch(x86_64) || arch(arm64) - func testOrdinality_DST2() { + @Test func testOrdinality_DST2() { let calendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(identifier: "America/Los_Angeles")!, locale: nil, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil) let date = Date(timeIntervalSinceReferenceDate: 682898558.712307) - XCTAssertEqual(calendar.ordinality(of: .era, in: .era, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .year, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .month, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .day, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .hour, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .weekday, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .weekdayOrdinal, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .quarter, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .weekOfMonth, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .weekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .yearForWeekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .era, in: .nanosecond, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .year, in: .era, for: date), 2022) - XCTAssertEqual(calendar.ordinality(of: .year, in: .year, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .month, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .day, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .hour, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .weekday, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .weekdayOrdinal, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .quarter, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .weekOfMonth, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .weekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .yearForWeekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .year, in: .nanosecond, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .month, in: .era, for: date), 24260) - XCTAssertEqual(calendar.ordinality(of: .month, in: .year, for: date), 8) - XCTAssertEqual(calendar.ordinality(of: .month, in: .month, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .month, in: .day, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .month, in: .hour, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .month, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .month, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .month, in: .weekday, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .month, in: .weekdayOrdinal, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .month, in: .quarter, for: date), 2) - XCTAssertEqual(calendar.ordinality(of: .month, in: .weekOfMonth, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .month, in: .weekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .month, in: .yearForWeekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .month, in: .nanosecond, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .day, in: .era, for: date), 738389) - XCTAssertEqual(calendar.ordinality(of: .day, in: .year, for: date), 234) - XCTAssertEqual(calendar.ordinality(of: .day, in: .month, for: date), 22) - XCTAssertEqual(calendar.ordinality(of: .day, in: .day, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .day, in: .hour, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .day, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .day, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .day, in: .weekday, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .day, in: .weekdayOrdinal, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .day, in: .quarter, for: date), 53) - XCTAssertEqual(calendar.ordinality(of: .day, in: .weekOfMonth, for: date), 2) - XCTAssertEqual(calendar.ordinality(of: .day, in: .weekOfYear, for: date), 2) - XCTAssertEqual(calendar.ordinality(of: .day, in: .yearForWeekOfYear, for: date), 240) - XCTAssertEqual(calendar.ordinality(of: .day, in: .nanosecond, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .hour, in: .era, for: date), 17721328) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .year, for: date), 5608) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .month, for: date), 520) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .day, for: date), 16) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .hour, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .weekday, for: date), 16) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .weekdayOrdinal, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .quarter, for: date), 1264) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .weekOfMonth, for: date), 40) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .weekOfYear, for: date), 40) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .yearForWeekOfYear, for: date), 5737) - XCTAssertEqual(calendar.ordinality(of: .hour, in: .nanosecond, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .minute, in: .era, for: date), 1063279623) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .year, for: date), 336423) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .month, for: date), 31143) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .day, for: date), 903) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .hour, for: date), 3) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .weekday, for: date), 903) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .weekdayOrdinal, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .quarter, for: date), 75783) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .weekOfMonth, for: date), 2343) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .weekOfYear, for: date), 2343) - - XCTAssertEqual(calendar.ordinality(of: .minute, in: .yearForWeekOfYear, for: date), 344161) - XCTAssertEqual(calendar.ordinality(of: .minute, in: .nanosecond, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .second, in: .era, for: date), 63796777359) - XCTAssertEqual(calendar.ordinality(of: .second, in: .year, for: date), 20185359) - XCTAssertEqual(calendar.ordinality(of: .second, in: .month, for: date), 1868559) - XCTAssertEqual(calendar.ordinality(of: .second, in: .day, for: date), 54159) - XCTAssertEqual(calendar.ordinality(of: .second, in: .hour, for: date), 159) - XCTAssertEqual(calendar.ordinality(of: .second, in: .minute, for: date), 39) - XCTAssertEqual(calendar.ordinality(of: .second, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .second, in: .weekday, for: date), 54159) - XCTAssertEqual(calendar.ordinality(of: .second, in: .weekdayOrdinal, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .second, in: .quarter, for: date), 4546959) - XCTAssertEqual(calendar.ordinality(of: .second, in: .weekOfMonth, for: date), 140559) - XCTAssertEqual(calendar.ordinality(of: .second, in: .weekOfYear, for: date), 140559) - XCTAssertEqual(calendar.ordinality(of: .second, in: .yearForWeekOfYear, for: date), 20649601) - XCTAssertEqual(calendar.ordinality(of: .second, in: .nanosecond, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .era, for: date), 105484) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .year, for: date), 34) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .month, for: date), 4) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .day, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .hour, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .weekday, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .weekdayOrdinal, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .quarter, for: date), 8) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .weekOfMonth, for: date), 2) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .weekOfYear, for: date), 2) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .yearForWeekOfYear, for: date), 35) - XCTAssertEqual(calendar.ordinality(of: .weekday, in: .nanosecond, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .era, for: date), 105484) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .year, for: date), 34) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .month, for: date), 4) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .day, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .hour, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .weekday, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .weekdayOrdinal, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .quarter, for: date), 8) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .weekOfMonth, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .weekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .yearForWeekOfYear, for: date), 35) - XCTAssertEqual(calendar.ordinality(of: .weekdayOrdinal, in: .nanosecond, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .era, for: date), 8087) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .year, for: date), 3) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .month, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .day, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .hour, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .weekday, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .weekdayOrdinal, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .quarter, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .weekOfMonth, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .weekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .yearForWeekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .quarter, in: .nanosecond, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .era, for: date), 105485) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .year, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .month, for: date), 4) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .day, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .hour, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .weekday, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .weekdayOrdinal, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .quarter, for: date), 9) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .weekOfMonth, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .weekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .yearForWeekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfMonth, in: .nanosecond, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .era, for: date), 105485) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .year, for: date), 35) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .month, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .day, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .hour, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .weekday, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .weekdayOrdinal, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .quarter, for: date), 9) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .weekOfMonth, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .weekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .yearForWeekOfYear, for: date), 35) - XCTAssertEqual(calendar.ordinality(of: .weekOfYear, in: .nanosecond, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .era, for: date), 2022) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .year, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .month, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .day, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .hour, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .minute, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .second, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .weekday, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .weekdayOrdinal, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .quarter, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .weekOfMonth, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .weekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .yearForWeekOfYear, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .yearForWeekOfYear, in: .nanosecond, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .era, for: date), nil) - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .year, for: date), 20185358712306977) - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .month, for: date), 1868558712306977) - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .day, for: date), 54158712306977) - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .hour, for: date), 158712306977) - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .minute, for: date), 38712306977) - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .second, for: date), 712306977) - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .weekday, for: date), 54158712306977) - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .weekdayOrdinal, for: date), nil) - - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .quarter, for: date), 4546958712306977) - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .weekOfMonth, for: date), 140558712306977) - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .weekOfYear, for: date), 140558712306977) + #expect(calendar.ordinality(of: .era, in: .era, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .year, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .month, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .day, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .hour, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .weekday, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .weekdayOrdinal, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .quarter, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .weekOfMonth, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .weekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .yearForWeekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .era, in: .nanosecond, for: date) == nil) + + #expect(calendar.ordinality(of: .year, in: .era, for: date) == 2022) + #expect(calendar.ordinality(of: .year, in: .year, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .month, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .day, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .hour, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .weekday, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .weekdayOrdinal, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .quarter, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .weekOfMonth, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .weekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .yearForWeekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .year, in: .nanosecond, for: date) == nil) + + #expect(calendar.ordinality(of: .month, in: .era, for: date) == 24260) + #expect(calendar.ordinality(of: .month, in: .year, for: date) == 8) + #expect(calendar.ordinality(of: .month, in: .month, for: date) == nil) + #expect(calendar.ordinality(of: .month, in: .day, for: date) == nil) + #expect(calendar.ordinality(of: .month, in: .hour, for: date) == nil) + #expect(calendar.ordinality(of: .month, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .month, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .month, in: .weekday, for: date) == nil) + #expect(calendar.ordinality(of: .month, in: .weekdayOrdinal, for: date) == nil) + #expect(calendar.ordinality(of: .month, in: .quarter, for: date) == 2) + #expect(calendar.ordinality(of: .month, in: .weekOfMonth, for: date) == nil) + #expect(calendar.ordinality(of: .month, in: .weekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .month, in: .yearForWeekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .month, in: .nanosecond, for: date) == nil) + + #expect(calendar.ordinality(of: .day, in: .era, for: date) == 738389) + #expect(calendar.ordinality(of: .day, in: .year, for: date) == 234) + #expect(calendar.ordinality(of: .day, in: .month, for: date) == 22) + #expect(calendar.ordinality(of: .day, in: .day, for: date) == nil) + #expect(calendar.ordinality(of: .day, in: .hour, for: date) == nil) + #expect(calendar.ordinality(of: .day, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .day, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .day, in: .weekday, for: date) == nil) + #expect(calendar.ordinality(of: .day, in: .weekdayOrdinal, for: date) == nil) + #expect(calendar.ordinality(of: .day, in: .quarter, for: date) == 53) + #expect(calendar.ordinality(of: .day, in: .weekOfMonth, for: date) == 2) + #expect(calendar.ordinality(of: .day, in: .weekOfYear, for: date) == 2) + #expect(calendar.ordinality(of: .day, in: .yearForWeekOfYear, for: date) == 240) + #expect(calendar.ordinality(of: .day, in: .nanosecond, for: date) == nil) + + #expect(calendar.ordinality(of: .hour, in: .era, for: date) == 17721328) + #expect(calendar.ordinality(of: .hour, in: .year, for: date) == 5608) + #expect(calendar.ordinality(of: .hour, in: .month, for: date) == 520) + #expect(calendar.ordinality(of: .hour, in: .day, for: date) == 16) + #expect(calendar.ordinality(of: .hour, in: .hour, for: date) == nil) + #expect(calendar.ordinality(of: .hour, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .hour, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .hour, in: .weekday, for: date) == 16) + #expect(calendar.ordinality(of: .hour, in: .weekdayOrdinal, for: date) == nil) + #expect(calendar.ordinality(of: .hour, in: .quarter, for: date) == 1264) + #expect(calendar.ordinality(of: .hour, in: .weekOfMonth, for: date) == 40) + #expect(calendar.ordinality(of: .hour, in: .weekOfYear, for: date) == 40) + #expect(calendar.ordinality(of: .hour, in: .yearForWeekOfYear, for: date) == 5737) + #expect(calendar.ordinality(of: .hour, in: .nanosecond, for: date) == nil) + + #expect(calendar.ordinality(of: .minute, in: .era, for: date) == 1063279623) + #expect(calendar.ordinality(of: .minute, in: .year, for: date) == 336423) + #expect(calendar.ordinality(of: .minute, in: .month, for: date) == 31143) + #expect(calendar.ordinality(of: .minute, in: .day, for: date) == 903) + #expect(calendar.ordinality(of: .minute, in: .hour, for: date) == 3) + #expect(calendar.ordinality(of: .minute, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .minute, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .minute, in: .weekday, for: date) == 903) + #expect(calendar.ordinality(of: .minute, in: .weekdayOrdinal, for: date) == nil) + #expect(calendar.ordinality(of: .minute, in: .quarter, for: date) == 75783) + #expect(calendar.ordinality(of: .minute, in: .weekOfMonth, for: date) == 2343) + #expect(calendar.ordinality(of: .minute, in: .weekOfYear, for: date) == 2343) + + #expect(calendar.ordinality(of: .minute, in: .yearForWeekOfYear, for: date) == 344161) + #expect(calendar.ordinality(of: .minute, in: .nanosecond, for: date) == nil) + #expect(calendar.ordinality(of: .second, in: .era, for: date) == 63796777359) + #expect(calendar.ordinality(of: .second, in: .year, for: date) == 20185359) + #expect(calendar.ordinality(of: .second, in: .month, for: date) == 1868559) + #expect(calendar.ordinality(of: .second, in: .day, for: date) == 54159) + #expect(calendar.ordinality(of: .second, in: .hour, for: date) == 159) + #expect(calendar.ordinality(of: .second, in: .minute, for: date) == 39) + #expect(calendar.ordinality(of: .second, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .second, in: .weekday, for: date) == 54159) + #expect(calendar.ordinality(of: .second, in: .weekdayOrdinal, for: date) == nil) + + #expect(calendar.ordinality(of: .second, in: .quarter, for: date) == 4546959) + #expect(calendar.ordinality(of: .second, in: .weekOfMonth, for: date) == 140559) + #expect(calendar.ordinality(of: .second, in: .weekOfYear, for: date) == 140559) + #expect(calendar.ordinality(of: .second, in: .yearForWeekOfYear, for: date) == 20649601) + #expect(calendar.ordinality(of: .second, in: .nanosecond, for: date) == nil) + + #expect(calendar.ordinality(of: .weekday, in: .era, for: date) == 105484) + #expect(calendar.ordinality(of: .weekday, in: .year, for: date) == 34) + #expect(calendar.ordinality(of: .weekday, in: .month, for: date) == 4) + #expect(calendar.ordinality(of: .weekday, in: .day, for: date) == nil) + #expect(calendar.ordinality(of: .weekday, in: .hour, for: date) == nil) + #expect(calendar.ordinality(of: .weekday, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .weekday, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .weekday, in: .weekday, for: date) == nil) + #expect(calendar.ordinality(of: .weekday, in: .weekdayOrdinal, for: date) == nil) + #expect(calendar.ordinality(of: .weekday, in: .quarter, for: date) == 8) + #expect(calendar.ordinality(of: .weekday, in: .weekOfMonth, for: date) == 2) + #expect(calendar.ordinality(of: .weekday, in: .weekOfYear, for: date) == 2) + #expect(calendar.ordinality(of: .weekday, in: .yearForWeekOfYear, for: date) == 35) + #expect(calendar.ordinality(of: .weekday, in: .nanosecond, for: date) == nil) + + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .era, for: date) == 105484) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .year, for: date) == 34) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .month, for: date) == 4) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .day, for: date) == nil) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .hour, for: date) == nil) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .weekday, for: date) == nil) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .weekdayOrdinal, for: date) == nil) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .quarter, for: date) == 8) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .weekOfMonth, for: date) == nil) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .weekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .yearForWeekOfYear, for: date) == 35) + #expect(calendar.ordinality(of: .weekdayOrdinal, in: .nanosecond, for: date) == nil) + + #expect(calendar.ordinality(of: .quarter, in: .era, for: date) == 8087) + #expect(calendar.ordinality(of: .quarter, in: .year, for: date) == 3) + #expect(calendar.ordinality(of: .quarter, in: .month, for: date) == nil) + #expect(calendar.ordinality(of: .quarter, in: .day, for: date) == nil) + #expect(calendar.ordinality(of: .quarter, in: .hour, for: date) == nil) + #expect(calendar.ordinality(of: .quarter, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .quarter, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .quarter, in: .weekday, for: date) == nil) + #expect(calendar.ordinality(of: .quarter, in: .weekdayOrdinal, for: date) == nil) + #expect(calendar.ordinality(of: .quarter, in: .quarter, for: date) == nil) + #expect(calendar.ordinality(of: .quarter, in: .weekOfMonth, for: date) == nil) + #expect(calendar.ordinality(of: .quarter, in: .weekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .quarter, in: .yearForWeekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .quarter, in: .nanosecond, for: date) == nil) + + #expect(calendar.ordinality(of: .weekOfMonth, in: .era, for: date) == 105485) + #expect(calendar.ordinality(of: .weekOfMonth, in: .year, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfMonth, in: .month, for: date) == 4) + #expect(calendar.ordinality(of: .weekOfMonth, in: .day, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfMonth, in: .hour, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfMonth, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfMonth, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfMonth, in: .weekday, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfMonth, in: .weekdayOrdinal, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfMonth, in: .quarter, for: date) == 9) + #expect(calendar.ordinality(of: .weekOfMonth, in: .weekOfMonth, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfMonth, in: .weekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfMonth, in: .yearForWeekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfMonth, in: .nanosecond, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfYear, in: .era, for: date) == 105485) + #expect(calendar.ordinality(of: .weekOfYear, in: .year, for: date) == 35) + #expect(calendar.ordinality(of: .weekOfYear, in: .month, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfYear, in: .day, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfYear, in: .hour, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfYear, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfYear, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfYear, in: .weekday, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfYear, in: .weekdayOrdinal, for: date) == nil) + + #expect(calendar.ordinality(of: .weekOfYear, in: .quarter, for: date) == 9) + #expect(calendar.ordinality(of: .weekOfYear, in: .weekOfMonth, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfYear, in: .weekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .weekOfYear, in: .yearForWeekOfYear, for: date) == 35) + #expect(calendar.ordinality(of: .weekOfYear, in: .nanosecond, for: date) == nil) + + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .era, for: date) == 2022) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .year, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .month, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .day, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .hour, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .minute, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .second, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .weekday, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .weekdayOrdinal, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .quarter, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .weekOfMonth, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .weekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .yearForWeekOfYear, for: date) == nil) + #expect(calendar.ordinality(of: .yearForWeekOfYear, in: .nanosecond, for: date) == nil) + + #expect(calendar.ordinality(of: .nanosecond, in: .era, for: date) == nil) + #expect(calendar.ordinality(of: .nanosecond, in: .year, for: date) == 20185358712306977) + #expect(calendar.ordinality(of: .nanosecond, in: .month, for: date) == 1868558712306977) + #expect(calendar.ordinality(of: .nanosecond, in: .day, for: date) == 54158712306977) + #expect(calendar.ordinality(of: .nanosecond, in: .hour, for: date) == 158712306977) + #expect(calendar.ordinality(of: .nanosecond, in: .minute, for: date) == 38712306977) + #expect(calendar.ordinality(of: .nanosecond, in: .second, for: date) == 712306977) + #expect(calendar.ordinality(of: .nanosecond, in: .weekday, for: date) == 54158712306977) + #expect(calendar.ordinality(of: .nanosecond, in: .weekdayOrdinal, for: date) == nil) + + #expect(calendar.ordinality(of: .nanosecond, in: .quarter, for: date) == 4546958712306977) + #expect(calendar.ordinality(of: .nanosecond, in: .weekOfMonth, for: date) == 140558712306977) + #expect(calendar.ordinality(of: .nanosecond, in: .weekOfYear, for: date) == 140558712306977) let actual = calendar.ordinality(of: .nanosecond, in: .yearForWeekOfYear, for: date) - XCTAssertEqual(actual, 20649600712306977) - XCTAssertEqual(calendar.ordinality(of: .nanosecond, in: .nanosecond, for: date), nil) + #expect(actual == 20649600712306977) + #expect(calendar.ordinality(of: .nanosecond, in: .nanosecond, for: date) == nil) } #endif - func testStartOf() { + @Test func testStartOf() { let firstWeekday = 2 let minimumDaysInFirstWeek = 4 let timeZone = TimeZone(secondsFromGMT: -3600 * 8)! let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: timeZone, locale: nil, firstWeekday: firstWeekday, minimumDaysInFirstWeek: minimumDaysInFirstWeek, gregorianStartDate: nil) func test(_ unit: Calendar.Component, at date: Date, expected: Date, file: StaticString = #file, line: UInt = #line) { let new = gregorianCalendar.start(of: unit, at: date)! - XCTAssertEqual(new, expected, file: file, line: line) + #expect( + new == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var date: Date @@ -2849,14 +2933,19 @@ final class GregorianCalendarTests : XCTestCase { test(.quarter, at: date, expected: Date(timeIntervalSince1970: 844156800.0)) // expect: 1996-10-01 08:00:00 +0000 } - func testStartOf_DST() { + @Test func testStartOf_DST() { let firstWeekday = 2 let minimumDaysInFirstWeek = 4 let timeZone = TimeZone(identifier: "America/Los_Angeles")! let gregorianCalendar = _CalendarGregorian(identifier: .gregorian, timeZone: timeZone, locale: nil, firstWeekday: firstWeekday, minimumDaysInFirstWeek: minimumDaysInFirstWeek, gregorianStartDate: nil) func test(_ unit: Calendar.Component, at date: Date, expected: Date, file: StaticString = #file, line: UInt = #line) { let new = gregorianCalendar.start(of: unit, at: date)! - XCTAssertEqual(new, expected, file: file, line: line) + #expect( + new == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var date: Date @@ -2949,7 +3038,7 @@ final class GregorianCalendarTests : XCTestCase { // MARK: - Weekend - func testIsDateInWeekend() { + @Test func testIsDateInWeekend() { let c = _CalendarGregorian(identifier: .gregorian, timeZone: .gmt, locale: nil, firstWeekday: 1, minimumDaysInFirstWeek: 1, gregorianStartDate: nil) let sat0000_mon0000 = WeekendRange(onsetTime: 0, ceaseTime: 0, start: 7, end: 2) // Sat 00:00:00 ..< Mon 00:00:00 @@ -2960,36 +3049,36 @@ final class GregorianCalendarTests : XCTestCase { let mon_tue = WeekendRange(onsetTime: 0, ceaseTime: 86400, start: 2, end: 3) // Mon 00:00:00 ... Tue 23:59:59 var date = Date(timeIntervalSince1970: 846320587) // 1996-10-26, Sat 09:03:07 - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat0000_mon0000)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: sat1200_sun1200)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat_sun)) + #expect(c.isDateInWeekend(date, weekendRange: sat0000_mon0000)) + #expect(c.isDateInWeekend(date, weekendRange: sat1200_sun1200) == false) + #expect(c.isDateInWeekend(date, weekendRange: sat_sun)) date = Date(timeIntervalSince1970: 846406987.0) // 1996-10-27, Sun 09:03:07 - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat0000_mon0000)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat1200_sun1200)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat_sun)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: sunPM)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: mon)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: mon_tue)) + #expect(c.isDateInWeekend(date, weekendRange: sat0000_mon0000)) + #expect(c.isDateInWeekend(date, weekendRange: sat1200_sun1200)) + #expect(c.isDateInWeekend(date, weekendRange: sat_sun)) + #expect(c.isDateInWeekend(date, weekendRange: sunPM) == false) + #expect(c.isDateInWeekend(date, weekendRange: mon) == false) + #expect(c.isDateInWeekend(date, weekendRange: mon_tue) == false) date = Date(timeIntervalSince1970: 846450187) // 1996-10-27, Sun 19:03:07 - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat0000_mon0000)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: sat1200_sun1200)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat_sun)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sunPM)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: mon)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: mon_tue)) + #expect(c.isDateInWeekend(date, weekendRange: sat0000_mon0000)) + #expect(c.isDateInWeekend(date, weekendRange: sat1200_sun1200) == false) + #expect(c.isDateInWeekend(date, weekendRange: sat_sun)) + #expect(c.isDateInWeekend(date, weekendRange: sunPM)) + #expect(c.isDateInWeekend(date, weekendRange: mon) == false) + #expect(c.isDateInWeekend(date, weekendRange: mon_tue) == false) date = Date(timeIntervalSince1970: 846536587) // 1996-10-28, Mon 19:03:07 - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: sat0000_mon0000)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: sat1200_sun1200)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: sat_sun)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: sunPM)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: mon)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: mon_tue)) + #expect(c.isDateInWeekend(date, weekendRange: sat0000_mon0000) == false) + #expect(c.isDateInWeekend(date, weekendRange: sat1200_sun1200) == false) + #expect(c.isDateInWeekend(date, weekendRange: sat_sun) == false) + #expect(c.isDateInWeekend(date, weekendRange: sunPM) == false) + #expect(c.isDateInWeekend(date, weekendRange: mon)) + #expect(c.isDateInWeekend(date, weekendRange: mon_tue)) } - func testIsDateInWeekend_wholeDays() { + @Test func testIsDateInWeekend_wholeDays() { let c = _CalendarGregorian(identifier: .gregorian, timeZone: .gmt, locale: nil, firstWeekday: 1, minimumDaysInFirstWeek: 1, gregorianStartDate: nil) let sat_mon = WeekendRange(start: 7, end: 2) @@ -2999,35 +3088,35 @@ final class GregorianCalendarTests : XCTestCase { let mon_tue = WeekendRange(start: 2, end: 3) var date = Date(timeIntervalSince1970: 846320587) // 1996-10-26, Sat 09:03:07 - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat_mon)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat_sun)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: sun)) + #expect(c.isDateInWeekend(date, weekendRange: sat_mon)) + #expect(c.isDateInWeekend(date, weekendRange: sat_sun)) + #expect(c.isDateInWeekend(date, weekendRange: sun) == false) date = Date(timeIntervalSince1970: 846406987.0) // 1996-10-27, Sun 09:03:07 - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat_mon)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat_sun)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sun)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: mon)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: mon_tue)) + #expect(c.isDateInWeekend(date, weekendRange: sat_mon)) + #expect(c.isDateInWeekend(date, weekendRange: sat_sun)) + #expect(c.isDateInWeekend(date, weekendRange: sun)) + #expect(c.isDateInWeekend(date, weekendRange: mon) == false) + #expect(c.isDateInWeekend(date, weekendRange: mon_tue) == false) date = Date(timeIntervalSince1970: 846450187) // 1996-10-27, Sun 19:03:07 - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat_mon)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat_sun)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sun)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: mon)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: mon_tue)) + #expect(c.isDateInWeekend(date, weekendRange: sat_mon)) + #expect(c.isDateInWeekend(date, weekendRange: sat_sun)) + #expect(c.isDateInWeekend(date, weekendRange: sun)) + #expect(c.isDateInWeekend(date, weekendRange: mon) == false) + #expect(c.isDateInWeekend(date, weekendRange: mon_tue) == false) date = Date(timeIntervalSince1970: 846536587) // 1996-10-28, Mon 19:03:07 - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: sat_mon)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: sat_sun)) - XCTAssertFalse(c.isDateInWeekend(date, weekendRange: sun)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: mon)) - XCTAssertTrue(c.isDateInWeekend(date, weekendRange: mon_tue)) + #expect(c.isDateInWeekend(date, weekendRange: sat_mon)) + #expect(c.isDateInWeekend(date, weekendRange: sat_sun) == false) + #expect(c.isDateInWeekend(date, weekendRange: sun) == false) + #expect(c.isDateInWeekend(date, weekendRange: mon)) + #expect(c.isDateInWeekend(date, weekendRange: mon_tue)) } // MARK: - DateInterval - func testDateInterval() { + @Test func testDateInterval() { let calendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(secondsFromGMT: -28800)!, locale: nil, firstWeekday: 3, minimumDaysInFirstWeek: 5, gregorianStartDate: nil) func test(_ c: Calendar.Component, _ date: Date, expectedStart start: Date?, end: Date?, file: StaticString = #file, line: UInt = #line) { @@ -3035,8 +3124,20 @@ final class GregorianCalendarTests : XCTestCase { let new_start = new?.start let new_end = new?.end - XCTAssertEqual(new_start, start, "interval start did not match", file: file, line: line) - XCTAssertEqual(new_end, end, "interval end did not match", file: file, line: line) + #expect( + new_start == start, + "interval start did not match", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) + #expect( + new_end == end, + "interval end did not match", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var date: Date @@ -3088,15 +3189,26 @@ final class GregorianCalendarTests : XCTestCase { test(.yearForWeekOfYear, date, expectedStart: nil, end: nil) } - func testDateInterval_DST() { + @Test func testDateInterval_DST() { let calendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(identifier: "America/Los_Angeles")!, locale: nil, firstWeekday: 3, minimumDaysInFirstWeek: 5, gregorianStartDate: nil) func test(_ c: Calendar.Component, _ date: Date, expectedStart start: Date, end: Date, file: StaticString = #file, line: UInt = #line) { let new = calendar.dateInterval(of: c, for: date)! let new_start = new.start let new_end = new.end let delta = 0.005 - XCTAssertEqual(Double(new_start.timeIntervalSinceReferenceDate), Double(start.timeIntervalSinceReferenceDate), accuracy: delta, file: file, line: line) - XCTAssertEqual(Double(new_end.timeIntervalSinceReferenceDate), Double(end.timeIntervalSinceReferenceDate), accuracy: delta, file: file, line: line) + #expect( + abs(Double(new_start.timeIntervalSinceReferenceDate) - Double(start.timeIntervalSinceReferenceDate)) <= delta, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) + #expect( + abs(Double(new_end.timeIntervalSinceReferenceDate) - + Double(end.timeIntervalSinceReferenceDate)) <= delta, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var date: Date date = Date(timeIntervalSince1970: 828867787.0) // 1996-04-07T01:03:07-0800 (1996-04-07T09:03:07Z) @@ -3213,72 +3325,77 @@ final class GregorianCalendarTests : XCTestCase { } // MARK: - Day Of Year - func test_dayOfYear() { + @Test func test_dayOfYear() { // An arbitrary date, for which we know the answers let date = Date(timeIntervalSinceReferenceDate: 682898558) // 2022-08-22 22:02:38 UTC, day 234 let leapYearDate = Date(timeIntervalSinceReferenceDate: 745891200) // 2024-08-21 00:00:00 UTC, day 234 let cal = _CalendarGregorian(identifier: .gregorian, timeZone: .gmt, locale: nil, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil) // Ordinality - XCTAssertEqual(cal.ordinality(of: .dayOfYear, in: .year, for: date), 234) - XCTAssertEqual(cal.ordinality(of: .hour, in: .dayOfYear, for: date), 23) - XCTAssertEqual(cal.ordinality(of: .minute, in: .dayOfYear, for: date), 1323) - XCTAssertEqual(cal.ordinality(of: .second, in: .dayOfYear, for: date), 79359) + #expect(cal.ordinality(of: .dayOfYear, in: .year, for: date) == 234) + #expect(cal.ordinality(of: .hour, in: .dayOfYear, for: date) == 23) + #expect(cal.ordinality(of: .minute, in: .dayOfYear, for: date) == 1323) + #expect(cal.ordinality(of: .second, in: .dayOfYear, for: date) == 79359) // Nonsense ordinalities. Since day of year is already relative, we don't count the Nth day of year in an era. - XCTAssertEqual(cal.ordinality(of: .dayOfYear, in: .era, for: date), nil) - XCTAssertEqual(cal.ordinality(of: .year, in: .dayOfYear, for: date), nil) + #expect(cal.ordinality(of: .dayOfYear, in: .era, for: date) == nil) + #expect(cal.ordinality(of: .year, in: .dayOfYear, for: date) == nil) // Interval let interval = cal.dateInterval(of: .dayOfYear, for: date) - XCTAssertEqual(interval, DateInterval(start: Date(timeIntervalSinceReferenceDate: 682819200), duration: 86400)) + #expect(interval == DateInterval(start: Date(timeIntervalSinceReferenceDate: 682819200), duration: 86400)) // Specific component values - XCTAssertEqual(cal.dateComponent(.dayOfYear, from: date), 234) + #expect(cal.dateComponent(.dayOfYear, from: date) == 234) // Ranges let min = cal.minimumRange(of: .dayOfYear) let max = cal.maximumRange(of: .dayOfYear) - XCTAssertEqual(min, 1..<366) // hard coded for gregorian - XCTAssertEqual(max, 1..<367) + #expect(min == 1..<366) // hard coded for gregorian + #expect(max == 1..<367) - XCTAssertEqual(cal.range(of: .dayOfYear, in: .year, for: date), 1..<366) - XCTAssertEqual(cal.range(of: .dayOfYear, in: .year, for: leapYearDate), 1..<367) + #expect(cal.range(of: .dayOfYear, in: .year, for: date) == 1..<366) + #expect(cal.range(of: .dayOfYear, in: .year, for: leapYearDate) == 1..<367) // Addition let d1 = cal.add(.dayOfYear, to: date, amount: 1, inTimeZone: .gmt) - XCTAssertEqual(d1, date + 86400) + #expect(d1 == date + 86400) let d2 = cal.addAndWrap(.dayOfYear, to: date, amount: 365, inTimeZone: .gmt) - XCTAssertEqual(d2, date) + #expect(d2 == date) // Conversion from DateComponents var dc = DateComponents(year: 2022, hour: 22, minute: 2, second: 38) dc.dayOfYear = 234 let d = cal.date(from: dc) - XCTAssertEqual(d, date) + #expect(d == date) var subtractMe = DateComponents() subtractMe.dayOfYear = -1 let firstDay = Date(timeIntervalSinceReferenceDate: 662688000) let previousDay = cal.date(byAdding: subtractMe, to:firstDay, wrappingComponents: false) - XCTAssertNotNil(previousDay) + #expect(previousDay != nil) let previousDayComps = cal.dateComponents([.year, .dayOfYear], from: previousDay!) var previousDayExpectationComps = DateComponents() previousDayExpectationComps.year = 2021 previousDayExpectationComps.dayOfYear = 365 - XCTAssertEqual(previousDayComps, previousDayExpectationComps) + #expect(previousDayComps == previousDayExpectationComps) } // MARK: - Range of - func testRangeOf() { + @Test func testRangeOf() { let calendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(secondsFromGMT: -28800)!, locale: nil, firstWeekday: 1, minimumDaysInFirstWeek: 4, gregorianStartDate: nil) func test(_ small: Calendar.Component, in large: Calendar.Component, for date: Date, expected: Range?, file: StaticString = #file, line: UInt = #line) { let new = calendar.range(of: small, in: large, for: date) - XCTAssertEqual(new, expected, file: file, line: line) + #expect( + new == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } var date: Date @@ -3441,13 +3558,18 @@ final class GregorianCalendarTests : XCTestCase { // MARK: - Difference - func testDateComponentsFromStartToEnd() { + @Test func testDateComponentsFromStartToEnd() { var calendar = _CalendarGregorian(identifier: .gregorian, timeZone: .gmt, locale: nil, firstWeekday: 1, minimumDaysInFirstWeek: 4, gregorianStartDate: nil) var start: Date! var end: Date! func test(_ components: Calendar.ComponentSet, expected: DateComponents, file: StaticString = #file, line: UInt = #line) { let actual = calendar.dateComponents(components, from: start, to: end) - XCTAssertEqual(actual, expected, file: file, line: line) + #expect( + actual == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } // non leap to leap @@ -3562,13 +3684,18 @@ final class GregorianCalendarTests : XCTestCase { test([.era, .year, .month, .day, .hour, .minute, .second, .nanosecond, .weekday, .weekdayOrdinal, .quarter, .weekOfMonth, .weekOfYear, .yearForWeekOfYear, .dayOfYear, .calendar, .timeZone], expected: expected) } - func testDifference() { + @Test func testDifference() { var calendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(secondsFromGMT: -28800)!, locale: nil, firstWeekday: 1, minimumDaysInFirstWeek: 4, gregorianStartDate: nil) var start: Date! var end: Date! func test(_ component: Calendar.Component, expected: Int, file: StaticString = #file, line: UInt = #line) { let (actualDiff, _) = try! calendar.difference(inComponent: component, from: start, to: end) - XCTAssertEqual(actualDiff, expected, file: file, line: line) + #expect( + actualDiff == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } // non leap to leap @@ -3806,14 +3933,19 @@ final class GregorianCalendarTests : XCTestCase { test(.dayOfYear, expected: 63) } - func testDifference_DST() { + @Test func testDifference_DST() { let calendar = _CalendarGregorian(identifier: .gregorian, timeZone: TimeZone(identifier: "America/Los_Angeles")!, locale: nil, firstWeekday: 1, minimumDaysInFirstWeek: 4, gregorianStartDate: nil) var start: Date! var end: Date! func test(_ component: Calendar.Component, expected: Int, file: StaticString = #file, line: UInt = #line) { let (actualDiff, _) = try! calendar.difference(inComponent: component, from: start, to: end) - XCTAssertEqual(actualDiff, expected, file: file, line: line) + #expect( + actualDiff == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line)) + ) } start = Date(timeIntervalSince1970: 828867787.0) // 1996-04-07T01:03:07-0800 diff --git a/Tests/FoundationEssentialsTests/IndexPathTests.swift b/Tests/FoundationEssentialsTests/IndexPathTests.swift index 86e9e825a..0d14e6a19 100644 --- a/Tests/FoundationEssentialsTests/IndexPathTests.swift +++ b/Tests/FoundationEssentialsTests/IndexPathTests.swift @@ -7,224 +7,222 @@ //===----------------------------------------------------------------------===// // -#if canImport(TestSupport) -import TestSupport -#endif +import Testing #if canImport(FoundationEssentials) @testable import FoundationEssentials #endif -class TestIndexPath: XCTestCase { - func testEmpty() { +struct IndexPathTests { + @Test func testEmpty() { let ip = IndexPath() - XCTAssertEqual(ip.count, 0) - + #expect(ip.count == 0) + #if FOUNDATION_FRAMEWORK // Darwin allows nil if length is 0 let nsip = NSIndexPath(indexes: nil, length: 0) - XCTAssertEqual(nsip.length, 0) + #expect(nsip.length == 0) let newIp = nsip.adding(1) - XCTAssertEqual(newIp.count, 1) + #expect(newIp.count == 1) #endif } - func testSingleIndex() { + @Test func testSingleIndex() { let ip = IndexPath(index: 1) - XCTAssertEqual(ip.count, 1) - XCTAssertEqual(ip[0], 1) - + #expect(ip.count == 1) + #expect(ip[0] == 1) + let highValueIp = IndexPath(index: .max) - XCTAssertEqual(highValueIp.count, 1) - XCTAssertEqual(highValueIp[0], .max) - + #expect(highValueIp.count == 1) + #expect(highValueIp[0] == .max) + let lowValueIp = IndexPath(index: .min) - XCTAssertEqual(lowValueIp.count, 1) - XCTAssertEqual(lowValueIp[0], .min) + #expect(lowValueIp.count == 1) + #expect(lowValueIp[0] == .min) } - func testTwoIndexes() { + @Test func testTwoIndexes() { let ip = IndexPath(indexes: [0, 1]) - XCTAssertEqual(ip.count, 2) - XCTAssertEqual(ip[0], 0) - XCTAssertEqual(ip[1], 1) + #expect(ip.count == 2) + #expect(ip[0] == 0) + #expect(ip[1] == 1) } - func testManyIndexes() { + @Test func testManyIndexes() { let ip = IndexPath(indexes: [0, 1, 2, 3, 4]) - XCTAssertEqual(ip.count, 5) - XCTAssertEqual(ip[0], 0) - XCTAssertEqual(ip[1], 1) - XCTAssertEqual(ip[2], 2) - XCTAssertEqual(ip[3], 3) - XCTAssertEqual(ip[4], 4) + #expect(ip.count == 5) + #expect(ip[0] == 0) + #expect(ip[1] == 1) + #expect(ip[2] == 2) + #expect(ip[3] == 3) + #expect(ip[4] == 4) } - func testCreateFromSequence() { + @Test func testCreateFromSequence() { let seq = repeatElement(5, count: 3) let ip = IndexPath(indexes: seq) - XCTAssertEqual(ip.count, 3) - XCTAssertEqual(ip[0], 5) - XCTAssertEqual(ip[1], 5) - XCTAssertEqual(ip[2], 5) + #expect(ip.count == 3) + #expect(ip[0] == 5) + #expect(ip[1] == 5) + #expect(ip[2] == 5) } - func testCreateFromLiteral() { + @Test func testCreateFromLiteral() { let ip: IndexPath = [1, 2, 3, 4] - XCTAssertEqual(ip.count, 4) - XCTAssertEqual(ip[0], 1) - XCTAssertEqual(ip[1], 2) - XCTAssertEqual(ip[2], 3) - XCTAssertEqual(ip[3], 4) + #expect(ip.count == 4) + #expect(ip[0] == 1) + #expect(ip[1] == 2) + #expect(ip[2] == 3) + #expect(ip[3] == 4) } - func testDropLast() { + @Test func testDropLast() { let ip: IndexPath = [1, 2, 3, 4] let ip2 = ip.dropLast() - XCTAssertEqual(ip2.count, 3) - XCTAssertEqual(ip2[0], 1) - XCTAssertEqual(ip2[1], 2) - XCTAssertEqual(ip2[2], 3) + #expect(ip2.count == 3) + #expect(ip2[0] == 1) + #expect(ip2[1] == 2) + #expect(ip2[2] == 3) } - func testDropLastFromEmpty() { + @Test func testDropLastFromEmpty() { let ip: IndexPath = [] let ip2 = ip.dropLast() - XCTAssertEqual(ip2.count, 0) + #expect(ip2.count == 0) } - func testDropLastFromSingle() { + @Test func testDropLastFromSingle() { let ip: IndexPath = [1] let ip2 = ip.dropLast() - XCTAssertEqual(ip2.count, 0) + #expect(ip2.count == 0) } - func testDropLastFromPair() { + @Test func testDropLastFromPair() { let ip: IndexPath = [1, 2] let ip2 = ip.dropLast() - XCTAssertEqual(ip2.count, 1) - XCTAssertEqual(ip2[0], 1) + #expect(ip2.count == 1) + #expect(ip2[0] == 1) } - func testDropLastFromTriple() { + @Test func testDropLastFromTriple() { let ip: IndexPath = [1, 2, 3] let ip2 = ip.dropLast() - XCTAssertEqual(ip2.count, 2) - XCTAssertEqual(ip2[0], 1) - XCTAssertEqual(ip2[1], 2) + #expect(ip2.count == 2) + #expect(ip2[0] == 1) + #expect(ip2[1] == 2) } - func testStartEndIndex() { + @Test func testStartEndIndex() { let ip: IndexPath = [1, 2, 3, 4] - XCTAssertEqual(ip.startIndex, 0) - XCTAssertEqual(ip.endIndex, ip.count) + #expect(ip.startIndex == 0) + #expect(ip.endIndex == ip.count) } - func testIterator() { + @Test func testIterator() { let ip: IndexPath = [1, 2, 3, 4] var iter = ip.makeIterator() var sum = 0 while let index = iter.next() { sum += index } - XCTAssertEqual(sum, 1 + 2 + 3 + 4) + #expect(sum == 1 + 2 + 3 + 4) } - func testIndexing() { + @Test func testIndexing() { let ip: IndexPath = [1, 2, 3, 4] - XCTAssertEqual(ip.index(before: 1), 0) - XCTAssertEqual(ip.index(before: 0), -1) // beyond range! - XCTAssertEqual(ip.index(after: 1), 2) - XCTAssertEqual(ip.index(after: 4), 5) // beyond range! + #expect(ip.index(before: 1) == 0) + #expect(ip.index(before: 0) == -1) // beyond range! + #expect(ip.index(after: 1) == 2) + #expect(ip.index(after: 4) == 5) // beyond range! } - func testCompare() { + @Test func testCompare() { let ip1: IndexPath = [1, 2] let ip2: IndexPath = [3, 4] let ip3: IndexPath = [5, 1] let ip4: IndexPath = [1, 1, 1] let ip5: IndexPath = [1, 1, 9] - XCTAssertEqual(ip1.compare(ip1), ComparisonResult.orderedSame) - XCTAssertEqual(ip1 < ip1, false) - XCTAssertEqual(ip1 <= ip1, true) - XCTAssertEqual(ip1 == ip1, true) - XCTAssertEqual(ip1 >= ip1, true) - XCTAssertEqual(ip1 > ip1, false) - - XCTAssertEqual(ip1.compare(ip2), ComparisonResult.orderedAscending) - XCTAssertEqual(ip1 < ip2, true) - XCTAssertEqual(ip1 <= ip2, true) - XCTAssertEqual(ip1 == ip2, false) - XCTAssertEqual(ip1 >= ip2, false) - XCTAssertEqual(ip1 > ip2, false) - - XCTAssertEqual(ip1.compare(ip3), ComparisonResult.orderedAscending) - XCTAssertEqual(ip1 < ip3, true) - XCTAssertEqual(ip1 <= ip3, true) - XCTAssertEqual(ip1 == ip3, false) - XCTAssertEqual(ip1 >= ip3, false) - XCTAssertEqual(ip1 > ip3, false) - - XCTAssertEqual(ip1.compare(ip4), ComparisonResult.orderedDescending) - XCTAssertEqual(ip1 < ip4, false) - XCTAssertEqual(ip1 <= ip4, false) - XCTAssertEqual(ip1 == ip4, false) - XCTAssertEqual(ip1 >= ip4, true) - XCTAssertEqual(ip1 > ip4, true) - - XCTAssertEqual(ip1.compare(ip5), ComparisonResult.orderedDescending) - XCTAssertEqual(ip1 < ip5, false) - XCTAssertEqual(ip1 <= ip5, false) - XCTAssertEqual(ip1 == ip5, false) - XCTAssertEqual(ip1 >= ip5, true) - XCTAssertEqual(ip1 > ip5, true) - - XCTAssertEqual(ip2.compare(ip1), ComparisonResult.orderedDescending) - XCTAssertEqual(ip2 < ip1, false) - XCTAssertEqual(ip2 <= ip1, false) - XCTAssertEqual(ip2 == ip1, false) - XCTAssertEqual(ip2 >= ip1, true) - XCTAssertEqual(ip2 > ip1, true) - - XCTAssertEqual(ip2.compare(ip2), ComparisonResult.orderedSame) - XCTAssertEqual(ip2 < ip2, false) - XCTAssertEqual(ip2 <= ip2, true) - XCTAssertEqual(ip2 == ip2, true) - XCTAssertEqual(ip2 >= ip2, true) - XCTAssertEqual(ip2 > ip2, false) - - XCTAssertEqual(ip2.compare(ip3), ComparisonResult.orderedAscending) - XCTAssertEqual(ip2 < ip3, true) - XCTAssertEqual(ip2 <= ip3, true) - XCTAssertEqual(ip2 == ip3, false) - XCTAssertEqual(ip2 >= ip3, false) - XCTAssertEqual(ip2 > ip3, false) - - XCTAssertEqual(ip2.compare(ip4), ComparisonResult.orderedDescending) - XCTAssertEqual(ip2.compare(ip5), ComparisonResult.orderedDescending) - XCTAssertEqual(ip3.compare(ip1), ComparisonResult.orderedDescending) - XCTAssertEqual(ip3.compare(ip2), ComparisonResult.orderedDescending) - XCTAssertEqual(ip3.compare(ip3), ComparisonResult.orderedSame) - XCTAssertEqual(ip3.compare(ip4), ComparisonResult.orderedDescending) - XCTAssertEqual(ip3.compare(ip5), ComparisonResult.orderedDescending) - XCTAssertEqual(ip4.compare(ip1), ComparisonResult.orderedAscending) - XCTAssertEqual(ip4.compare(ip2), ComparisonResult.orderedAscending) - XCTAssertEqual(ip4.compare(ip3), ComparisonResult.orderedAscending) - XCTAssertEqual(ip4.compare(ip4), ComparisonResult.orderedSame) - XCTAssertEqual(ip4.compare(ip5), ComparisonResult.orderedAscending) - XCTAssertEqual(ip5.compare(ip1), ComparisonResult.orderedAscending) - XCTAssertEqual(ip5.compare(ip2), ComparisonResult.orderedAscending) - XCTAssertEqual(ip5.compare(ip3), ComparisonResult.orderedAscending) - XCTAssertEqual(ip5.compare(ip4), ComparisonResult.orderedDescending) - XCTAssertEqual(ip5.compare(ip5), ComparisonResult.orderedSame) - + #expect(ip1.compare(ip1) == ComparisonResult.orderedSame) + #expect((ip1 < ip1) == false) + #expect((ip1 <= ip1) == true) + #expect((ip1 == ip1) == true) + #expect((ip1 >= ip1) == true) + #expect((ip1 > ip1) == false) + + #expect(ip1.compare(ip2) == ComparisonResult.orderedAscending) + #expect((ip1 < ip2) == true) + #expect((ip1 <= ip2) == true) + #expect((ip1 == ip2) == false) + #expect((ip1 >= ip2) == false) + #expect((ip1 > ip2) == false) + + #expect(ip1.compare(ip3) == ComparisonResult.orderedAscending) + #expect((ip1 < ip3) == true) + #expect((ip1 <= ip3) == true) + #expect((ip1 == ip3) == false) + #expect((ip1 >= ip3) == false) + #expect((ip1 > ip3) == false) + + #expect(ip1.compare(ip4) == ComparisonResult.orderedDescending) + #expect((ip1 < ip4) == false) + #expect((ip1 <= ip4) == false) + #expect((ip1 == ip4) == false) + #expect((ip1 >= ip4) == true) + #expect((ip1 > ip4) == true) + + #expect(ip1.compare(ip5) == ComparisonResult.orderedDescending) + #expect((ip1 < ip5) == false) + #expect((ip1 <= ip5) == false) + #expect((ip1 == ip5) == false) + #expect((ip1 >= ip5) == true) + #expect((ip1 > ip5) == true) + + #expect(ip2.compare(ip1) == ComparisonResult.orderedDescending) + #expect((ip2 < ip1) == false) + #expect((ip2 <= ip1) == false) + #expect((ip2 == ip1) == false) + #expect((ip2 >= ip1) == true) + #expect((ip2 > ip1) == true) + + #expect(ip2.compare(ip2) == ComparisonResult.orderedSame) + #expect((ip2 < ip2) == false) + #expect((ip2 <= ip2) == true) + #expect((ip2 == ip2) == true) + #expect((ip2 >= ip2) == true) + #expect((ip2 > ip2) == false) + + #expect(ip2.compare(ip3) == ComparisonResult.orderedAscending) + #expect((ip2 < ip3) == true) + #expect((ip2 <= ip3) == true) + #expect((ip2 == ip3) == false) + #expect((ip2 >= ip3) == false) + #expect((ip2 > ip3) == false) + + #expect(ip2.compare(ip4) == ComparisonResult.orderedDescending) + #expect(ip2.compare(ip5) == ComparisonResult.orderedDescending) + #expect(ip3.compare(ip1) == ComparisonResult.orderedDescending) + #expect(ip3.compare(ip2) == ComparisonResult.orderedDescending) + #expect(ip3.compare(ip3) == ComparisonResult.orderedSame) + #expect(ip3.compare(ip4) == ComparisonResult.orderedDescending) + #expect(ip3.compare(ip5) == ComparisonResult.orderedDescending) + #expect(ip4.compare(ip1) == ComparisonResult.orderedAscending) + #expect(ip4.compare(ip2) == ComparisonResult.orderedAscending) + #expect(ip4.compare(ip3) == ComparisonResult.orderedAscending) + #expect(ip4.compare(ip4) == ComparisonResult.orderedSame) + #expect(ip4.compare(ip5) == ComparisonResult.orderedAscending) + #expect(ip5.compare(ip1) == ComparisonResult.orderedAscending) + #expect(ip5.compare(ip2) == ComparisonResult.orderedAscending) + #expect(ip5.compare(ip3) == ComparisonResult.orderedAscending) + #expect(ip5.compare(ip4) == ComparisonResult.orderedDescending) + #expect(ip5.compare(ip5) == ComparisonResult.orderedSame) + let ip6: IndexPath = [1, 1] - XCTAssertEqual(ip6.compare(ip5), ComparisonResult.orderedAscending) - XCTAssertEqual(ip5.compare(ip6), ComparisonResult.orderedDescending) + #expect(ip6.compare(ip5) == ComparisonResult.orderedAscending) + #expect(ip5.compare(ip6) == ComparisonResult.orderedDescending) } - func testHashing() { + @Test func testHashing() { let samples: [IndexPath] = [ [], [1], @@ -245,314 +243,314 @@ class TestIndexPath: XCTestCase { _ = IndexPath(indexes: [Int.max >> 8, 2, Int.max >> 36]).hashValue } - func testEquality() { + @Test func testEquality() { let ip1: IndexPath = [1, 1] let ip2: IndexPath = [1, 1] let ip3: IndexPath = [1, 1, 1] let ip4: IndexPath = [] let ip5: IndexPath = [1] - XCTAssertTrue(ip1 == ip2) - XCTAssertFalse(ip1 == ip3) - XCTAssertFalse(ip1 == ip4) - XCTAssertFalse(ip4 == ip1) - XCTAssertFalse(ip5 == ip1) - XCTAssertFalse(ip5 == ip4) - XCTAssertTrue(ip4 == ip4) - XCTAssertTrue(ip5 == ip5) + #expect(ip1 == ip2) + #expect(ip1 != ip3) + #expect(ip1 != ip4) + #expect(ip4 != ip1) + #expect(ip5 != ip1) + #expect(ip5 != ip4) + #expect(ip4 == ip4) + #expect(ip5 == ip5) } - func testSubscripting() { + @Test func testSubscripting() { var ip1: IndexPath = [1] var ip2: IndexPath = [1, 2] var ip3: IndexPath = [1, 2, 3] - - XCTAssertEqual(ip1[0], 1) - - XCTAssertEqual(ip2[0], 1) - XCTAssertEqual(ip2[1], 2) - - XCTAssertEqual(ip3[0], 1) - XCTAssertEqual(ip3[1], 2) - XCTAssertEqual(ip3[2], 3) - + + #expect(ip1[0] == 1) + + #expect(ip2[0] == 1) + #expect(ip2[1] == 2) + + #expect(ip3[0] == 1) + #expect(ip3[1] == 2) + #expect(ip3[2] == 3) + ip1[0] = 2 - XCTAssertEqual(ip1[0], 2) - + #expect(ip1[0] == 2) + ip2[0] = 2 ip2[1] = 3 - XCTAssertEqual(ip2[0], 2) - XCTAssertEqual(ip2[1], 3) - + #expect(ip2[0] == 2) + #expect(ip2[1] == 3) + ip3[0] = 2 ip3[1] = 3 ip3[2] = 4 - XCTAssertEqual(ip3[0], 2) - XCTAssertEqual(ip3[1], 3) - XCTAssertEqual(ip3[2], 4) - + #expect(ip3[0] == 2) + #expect(ip3[1] == 3) + #expect(ip3[2] == 4) + let ip4 = ip3[0..<2] - XCTAssertEqual(ip4.count, 2) - XCTAssertEqual(ip4[0], 2) - XCTAssertEqual(ip4[1], 3) - + #expect(ip4.count == 2) + #expect(ip4[0] == 2) + #expect(ip4[1] == 3) + let ip5 = ip3[1...] - XCTAssertEqual(ip5.count, 2) - XCTAssertEqual(ip5[0], 3) - XCTAssertEqual(ip5[1], 4) + #expect(ip5.count == 2) + #expect(ip5[0] == 3) + #expect(ip5[1] == 4) let ip6 = ip3[2...] - XCTAssertEqual(ip6.count, 1) - XCTAssertEqual(ip6[0], 4) + #expect(ip6.count == 1) + #expect(ip6[0] == 4) } - func testAppending() { + @Test func testAppending() { var ip : IndexPath = [1, 2, 3, 4] let ip2 = IndexPath(indexes: [5, 6, 7]) - + ip.append(ip2) - - XCTAssertEqual(ip.count, 7) - XCTAssertEqual(ip[0], 1) - XCTAssertEqual(ip[6], 7) - + + #expect(ip.count == 7) + #expect(ip[0] == 1) + #expect(ip[6] == 7) + let ip3 = ip.appending(IndexPath(indexes: [8, 9])) - XCTAssertEqual(ip3.count, 9) - XCTAssertEqual(ip3[7], 8) - XCTAssertEqual(ip3[8], 9) - + #expect(ip3.count == 9) + #expect(ip3[7] == 8) + #expect(ip3[8] == 9) + let ip4 = ip3.appending([10, 11]) - XCTAssertEqual(ip4.count, 11) - XCTAssertEqual(ip4[9], 10) - XCTAssertEqual(ip4[10], 11) - + #expect(ip4.count == 11) + #expect(ip4[9] == 10) + #expect(ip4[10] == 11) + let ip5 = ip.appending(8) - XCTAssertEqual(ip5.count, 8) - XCTAssertEqual(ip5[7], 8) + #expect(ip5.count == 8) + #expect(ip5[7] == 8) } - func testAppendEmpty() { + @Test func testAppendEmpty() { var ip: IndexPath = [] ip.append(1) - - XCTAssertEqual(ip.count, 1) - XCTAssertEqual(ip[0], 1) - + + #expect(ip.count == 1) + #expect(ip[0] == 1) + ip.append(2) - XCTAssertEqual(ip.count, 2) - XCTAssertEqual(ip[0], 1) - XCTAssertEqual(ip[1], 2) - + #expect(ip.count == 2) + #expect(ip[0] == 1) + #expect(ip[1] == 2) + ip.append(3) - XCTAssertEqual(ip.count, 3) - XCTAssertEqual(ip[0], 1) - XCTAssertEqual(ip[1], 2) - XCTAssertEqual(ip[2], 3) - + #expect(ip.count == 3) + #expect(ip[0] == 1) + #expect(ip[1] == 2) + #expect(ip[2] == 3) + ip.append(4) - XCTAssertEqual(ip.count, 4) - XCTAssertEqual(ip[0], 1) - XCTAssertEqual(ip[1], 2) - XCTAssertEqual(ip[2], 3) - XCTAssertEqual(ip[3], 4) + #expect(ip.count == 4) + #expect(ip[0] == 1) + #expect(ip[1] == 2) + #expect(ip[2] == 3) + #expect(ip[3] == 4) } - - func testAppendEmptyIndexPath() { + + @Test func testAppendEmptyIndexPath() { var ip: IndexPath = [] ip.append(IndexPath(indexes: [])) - - XCTAssertEqual(ip.count, 0) + + #expect(ip.count == 0) } - - func testAppendManyIndexPath() { + + @Test func testAppendManyIndexPath() { var ip: IndexPath = [] ip.append(IndexPath(indexes: [1, 2, 3])) - - XCTAssertEqual(ip.count, 3) - XCTAssertEqual(ip[0], 1) - XCTAssertEqual(ip[1], 2) - XCTAssertEqual(ip[2], 3) + + #expect(ip.count == 3) + #expect(ip[0] == 1) + #expect(ip[1] == 2) + #expect(ip[2] == 3) } - - func testAppendEmptyIndexPathToSingle() { + + @Test func testAppendEmptyIndexPathToSingle() { var ip: IndexPath = [1] ip.append(IndexPath(indexes: [])) - - XCTAssertEqual(ip.count, 1) - XCTAssertEqual(ip[0], 1) + + #expect(ip.count == 1) + #expect(ip[0] == 1) } - func testAppendSingleIndexPath() { + @Test func testAppendSingleIndexPath() { var ip: IndexPath = [] ip.append(IndexPath(indexes: [1])) - - XCTAssertEqual(ip.count, 1) - XCTAssertEqual(ip[0], 1) + + #expect(ip.count == 1) + #expect(ip[0] == 1) } - func testAppendSingleIndexPathToSingle() { + @Test func testAppendSingleIndexPathToSingle() { var ip: IndexPath = [1] ip.append(IndexPath(indexes: [1])) - XCTAssertEqual(ip.count, 2) - XCTAssertEqual(ip[0], 1) - XCTAssertEqual(ip[1], 1) + #expect(ip.count == 2) + #expect(ip[0] == 1) + #expect(ip[1] == 1) } - func testAppendPairIndexPath() { + @Test func testAppendPairIndexPath() { var ip: IndexPath = [] ip.append(IndexPath(indexes: [1, 2])) - XCTAssertEqual(ip.count, 2) - XCTAssertEqual(ip[0], 1) - XCTAssertEqual(ip[1], 2) + #expect(ip.count == 2) + #expect(ip[0] == 1) + #expect(ip[1] == 2) } - func testAppendManyIndexPathToEmpty() { + @Test func testAppendManyIndexPathToEmpty() { var ip: IndexPath = [] ip.append(IndexPath(indexes: [1, 2, 3])) - XCTAssertEqual(ip.count, 3) - XCTAssertEqual(ip[0], 1) - XCTAssertEqual(ip[1], 2) - XCTAssertEqual(ip[2], 3) + #expect(ip.count == 3) + #expect(ip[0] == 1) + #expect(ip[1] == 2) + #expect(ip[2] == 3) } - func testAppendByOperator() { + @Test func testAppendByOperator() { let ip1: IndexPath = [] let ip2: IndexPath = [] let ip3 = ip1 + ip2 - XCTAssertEqual(ip3.count, 0) - + #expect(ip3.count == 0) + let ip4: IndexPath = [1] let ip5: IndexPath = [2] let ip6 = ip4 + ip5 - XCTAssertEqual(ip6.count, 2) - XCTAssertEqual(ip6[0], 1) - XCTAssertEqual(ip6[1], 2) - + #expect(ip6.count == 2) + #expect(ip6[0] == 1) + #expect(ip6[1] == 2) + var ip7: IndexPath = [] ip7 += ip6 - XCTAssertEqual(ip7.count, 2) - XCTAssertEqual(ip7[0], 1) - XCTAssertEqual(ip7[1], 2) + #expect(ip7.count == 2) + #expect(ip7[0] == 1) + #expect(ip7[1] == 2) } - func testAppendArray() { + @Test func testAppendArray() { var ip: IndexPath = [1, 2, 3, 4] let indexes = [5, 6, 7] - + ip.append(indexes) - - XCTAssertEqual(ip.count, 7) - XCTAssertEqual(ip[0], 1) - XCTAssertEqual(ip[6], 7) + + #expect(ip.count == 7) + #expect(ip[0] == 1) + #expect(ip[6] == 7) } - func testRanges() { + @Test func testRanges() { let ip1 = IndexPath(indexes: [1, 2, 3]) let ip2 = IndexPath(indexes: [6, 7, 8]) // Replace the whole range var mutateMe = ip1 mutateMe[0..<3] = ip2 - XCTAssertEqual(mutateMe, ip2) - + #expect(mutateMe == ip2) + // Insert at the beginning mutateMe = ip1 mutateMe[0..<0] = ip2 - XCTAssertEqual(mutateMe, IndexPath(indexes: [6, 7, 8, 1, 2, 3])) - + #expect(mutateMe == IndexPath(indexes: [6, 7, 8, 1, 2, 3])) + // Insert at the end mutateMe = ip1 mutateMe[3..<3] = ip2 - XCTAssertEqual(mutateMe, IndexPath(indexes: [1, 2, 3, 6, 7, 8])) - + #expect(mutateMe == IndexPath(indexes: [1, 2, 3, 6, 7, 8])) + // Insert in middle mutateMe = ip1 mutateMe[2..<2] = ip2 - XCTAssertEqual(mutateMe, IndexPath(indexes: [1, 2, 6, 7, 8, 3])) + #expect(mutateMe == IndexPath(indexes: [1, 2, 6, 7, 8, 3])) } - - func testRangeFromEmpty() { + + @Test func testRangeFromEmpty() { let ip1 = IndexPath() let ip2 = ip1[0..<0] - XCTAssertEqual(ip2.count, 0) + #expect(ip2.count == 0) } - - func testRangeFromSingle() { + + @Test func testRangeFromSingle() { let ip1 = IndexPath(indexes: [1]) let ip2 = ip1[0..<0] - XCTAssertEqual(ip2.count, 0) + #expect(ip2.count == 0) let ip3 = ip1[0..<1] - XCTAssertEqual(ip3.count, 1) - XCTAssertEqual(ip3[0], 1) + #expect(ip3.count == 1) + #expect(ip3[0] == 1) } - - func testRangeFromPair() { + + @Test func testRangeFromPair() { let ip1 = IndexPath(indexes: [1, 2]) let ip2 = ip1[0..<0] - XCTAssertEqual(ip2.count, 0) + #expect(ip2.count == 0) let ip3 = ip1[0..<1] - XCTAssertEqual(ip3.count, 1) - XCTAssertEqual(ip3[0], 1) + #expect(ip3.count == 1) + #expect(ip3[0] == 1) let ip4 = ip1[1..<1] - XCTAssertEqual(ip4.count, 0) + #expect(ip4.count == 0) let ip5 = ip1[0..<2] - XCTAssertEqual(ip5.count, 2) - XCTAssertEqual(ip5[0], 1) - XCTAssertEqual(ip5[1], 2) + #expect(ip5.count == 2) + #expect(ip5[0] == 1) + #expect(ip5[1] == 2) let ip6 = ip1[1..<2] - XCTAssertEqual(ip6.count, 1) - XCTAssertEqual(ip6[0], 2) + #expect(ip6.count == 1) + #expect(ip6[0] == 2) let ip7 = ip1[2..<2] - XCTAssertEqual(ip7.count, 0) + #expect(ip7.count == 0) } - - func testRangeFromMany() { + + @Test func testRangeFromMany() { let ip1 = IndexPath(indexes: [1, 2, 3]) let ip2 = ip1[0..<0] - XCTAssertEqual(ip2.count, 0) + #expect(ip2.count == 0) let ip3 = ip1[0..<1] - XCTAssertEqual(ip3.count, 1) + #expect(ip3.count == 1) let ip4 = ip1[0..<2] - XCTAssertEqual(ip4.count, 2) + #expect(ip4.count == 2) let ip5 = ip1[0..<3] - XCTAssertEqual(ip5.count, 3) + #expect(ip5.count == 3) } - - func testRangeReplacementSingle() { + + @Test func testRangeReplacementSingle() { var ip1 = IndexPath(indexes: [1]) ip1[0..<1] = IndexPath(indexes: [2]) - XCTAssertEqual(ip1[0], 2) - + #expect(ip1[0] == 2) + ip1[0..<1] = IndexPath(indexes: []) - XCTAssertEqual(ip1.count, 0) + #expect(ip1.count == 0) } - func testRangeReplacementPair() { + @Test func testRangeReplacementPair() { var ip1 = IndexPath(indexes: [1, 2]) ip1[0..<1] = IndexPath(indexes: [2, 3]) - XCTAssertEqual(ip1.count, 3) - XCTAssertEqual(ip1[0], 2) - XCTAssertEqual(ip1[1], 3) - XCTAssertEqual(ip1[2], 2) - + #expect(ip1.count == 3) + #expect(ip1[0] == 2) + #expect(ip1[1] == 3) + #expect(ip1[2] == 2) + ip1[0..<1] = IndexPath(indexes: []) - XCTAssertEqual(ip1.count, 2) + #expect(ip1.count == 2) } - func testMoreRanges() { + @Test func testMoreRanges() { var ip = IndexPath(indexes: [1, 2, 3]) let ip2 = IndexPath(indexes: [5, 6, 7, 8, 9, 10]) ip[1..<2] = ip2 - XCTAssertEqual(ip, IndexPath(indexes: [1, 5, 6, 7, 8, 9, 10, 3])) + #expect(ip == IndexPath(indexes: [1, 5, 6, 7, 8, 9, 10, 3])) } - func testIteration() { + @Test func testIteration() { let ip = IndexPath(indexes: [1, 2, 3]) var count = 0 @@ -560,50 +558,50 @@ class TestIndexPath: XCTestCase { count += 1 } - XCTAssertEqual(3, count) + #expect(3 == count) } - - func testDescription() { + + @Test func testDescription() { let ip1: IndexPath = [] let ip2: IndexPath = [1] let ip3: IndexPath = [1, 2] let ip4: IndexPath = [1, 2, 3] - XCTAssertEqual(ip1.description, "[]") - XCTAssertEqual(ip2.description, "[1]") - XCTAssertEqual(ip3.description, "[1, 2]") - XCTAssertEqual(ip4.description, "[1, 2, 3]") - - XCTAssertEqual(ip1.debugDescription, ip1.description) - XCTAssertEqual(ip2.debugDescription, ip2.description) - XCTAssertEqual(ip3.debugDescription, ip3.description) - XCTAssertEqual(ip4.debugDescription, ip4.description) + #expect(ip1.description == "[]") + #expect(ip2.description == "[1]") + #expect(ip3.description == "[1, 2]") + #expect(ip4.description == "[1, 2, 3]") + + #expect(ip1.debugDescription == ip1.description) + #expect(ip2.debugDescription == ip2.description) + #expect(ip3.debugDescription == ip3.description) + #expect(ip4.debugDescription == ip4.description) } - func test_AnyHashableContainingIndexPath() { + @Test func test_AnyHashableContainingIndexPath() { let values: [IndexPath] = [ IndexPath(indexes: [1, 2]), IndexPath(indexes: [1, 2, 3]), IndexPath(indexes: [1, 2, 3]), ] let anyHashables = values.map(AnyHashable.init) - expectEqual(IndexPath.self, type(of: anyHashables[0].base)) - expectEqual(IndexPath.self, type(of: anyHashables[1].base)) - expectEqual(IndexPath.self, type(of: anyHashables[2].base)) - XCTAssertNotEqual(anyHashables[0], anyHashables[1]) - XCTAssertEqual(anyHashables[1], anyHashables[2]) + #expect(IndexPath.self == type(of: anyHashables[0].base)) + #expect(IndexPath.self == type(of: anyHashables[1].base)) + #expect(IndexPath.self == type(of: anyHashables[2].base)) + #expect(anyHashables[0] != anyHashables[1]) + #expect(anyHashables[1] == anyHashables[2]) } - func test_slice_1ary() { + @Test func test_slice_1ary() { let indexPath: IndexPath = [0] let res = indexPath.dropFirst() - XCTAssertEqual(0, res.count) + #expect(0 == res.count) let slice = indexPath[1..<1] - XCTAssertEqual(0, slice.count) + #expect(0 == slice.count) } - func test_dropFirst() { + @Test func test_dropFirst() { var pth = IndexPath(indexes:[1,2,3,4]) while !pth.isEmpty { // this should not crash @@ -614,8 +612,8 @@ class TestIndexPath: XCTestCase { #if FOUNDATION_FRAMEWORK -class TestIndexPathBridging: XCTestCase { - func testBridgeToObjC() { +struct IndexPathBridgingTests { + @Test func testBridgeToObjC() { let ip1: IndexPath = [] let ip2: IndexPath = [1] let ip3: IndexPath = [1, 2] @@ -626,13 +624,13 @@ class TestIndexPathBridging: XCTestCase { let nsip3 = ip3 as NSIndexPath let nsip4 = ip4 as NSIndexPath - XCTAssertEqual(nsip1.length, 0) - XCTAssertEqual(nsip2.length, 1) - XCTAssertEqual(nsip3.length, 2) - XCTAssertEqual(nsip4.length, 3) + #expect(nsip1.length == 0) + #expect(nsip2.length == 1) + #expect(nsip3.length == 2) + #expect(nsip4.length == 3) } - - func testForceBridgeFromObjC() { + + @Test func testForceBridgeFromObjC() { let nsip1 = NSIndexPath() let nsip2 = NSIndexPath(index: 1) let nsip3 = [1, 2].withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> NSIndexPath in @@ -644,32 +642,32 @@ class TestIndexPathBridging: XCTestCase { var ip1: IndexPath? = IndexPath() IndexPath._forceBridgeFromObjectiveC(nsip1, result: &ip1) - XCTAssertNotNil(ip1) - XCTAssertEqual(ip1!.count, 0) - + #expect(ip1 != nil) + #expect(ip1!.count == 0) + var ip2: IndexPath? = IndexPath() IndexPath._forceBridgeFromObjectiveC(nsip2, result: &ip2) - XCTAssertNotNil(ip2) - XCTAssertEqual(ip2!.count, 1) - XCTAssertEqual(ip2![0], 1) - + #expect(ip2 != nil) + #expect(ip2!.count == 1) + #expect(ip2![0] == 1) + var ip3: IndexPath? = IndexPath() IndexPath._forceBridgeFromObjectiveC(nsip3, result: &ip3) - XCTAssertNotNil(ip3) - XCTAssertEqual(ip3!.count, 2) - XCTAssertEqual(ip3![0], 1) - XCTAssertEqual(ip3![1], 2) - + #expect(ip3 != nil) + #expect(ip3!.count == 2) + #expect(ip3![0] == 1) + #expect(ip3![1] == 2) + var ip4: IndexPath? = IndexPath() IndexPath._forceBridgeFromObjectiveC(nsip4, result: &ip4) - XCTAssertNotNil(ip4) - XCTAssertEqual(ip4!.count, 3) - XCTAssertEqual(ip4![0], 1) - XCTAssertEqual(ip4![1], 2) - XCTAssertEqual(ip4![2], 3) + #expect(ip4 != nil) + #expect(ip4!.count == 3) + #expect(ip4![0] == 1) + #expect(ip4![1] == 2) + #expect(ip4![2] == 3) } - - func testConditionalBridgeFromObjC() { + + @Test func testConditionalBridgeFromObjC() { let nsip1 = NSIndexPath() let nsip2 = NSIndexPath(index: 1) let nsip3 = [1, 2].withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> NSIndexPath in @@ -680,33 +678,33 @@ class TestIndexPathBridging: XCTestCase { } var ip1: IndexPath? = IndexPath() - XCTAssertTrue(IndexPath._conditionallyBridgeFromObjectiveC(nsip1, result: &ip1)) - XCTAssertNotNil(ip1) - XCTAssertEqual(ip1!.count, 0) - + #expect(IndexPath._conditionallyBridgeFromObjectiveC(nsip1, result: &ip1)) + #expect(ip1 != nil) + #expect(ip1!.count == 0) + var ip2: IndexPath? = IndexPath() - XCTAssertTrue(IndexPath._conditionallyBridgeFromObjectiveC(nsip2, result: &ip2)) - XCTAssertNotNil(ip2) - XCTAssertEqual(ip2!.count, 1) - XCTAssertEqual(ip2![0], 1) - + #expect(IndexPath._conditionallyBridgeFromObjectiveC(nsip2, result: &ip2)) + #expect(ip2 != nil) + #expect(ip2!.count == 1) + #expect(ip2![0] == 1) + var ip3: IndexPath? = IndexPath() - XCTAssertTrue(IndexPath._conditionallyBridgeFromObjectiveC(nsip3, result: &ip3)) - XCTAssertNotNil(ip3) - XCTAssertEqual(ip3!.count, 2) - XCTAssertEqual(ip3![0], 1) - XCTAssertEqual(ip3![1], 2) - + #expect(IndexPath._conditionallyBridgeFromObjectiveC(nsip3, result: &ip3)) + #expect(ip3 != nil) + #expect(ip3!.count == 2) + #expect(ip3![0] == 1) + #expect(ip3![1] == 2) + var ip4: IndexPath? = IndexPath() - XCTAssertTrue(IndexPath._conditionallyBridgeFromObjectiveC(nsip4, result: &ip4)) - XCTAssertNotNil(ip4) - XCTAssertEqual(ip4!.count, 3) - XCTAssertEqual(ip4![0], 1) - XCTAssertEqual(ip4![1], 2) - XCTAssertEqual(ip4![2], 3) + #expect(IndexPath._conditionallyBridgeFromObjectiveC(nsip4, result: &ip4)) + #expect(ip4 != nil) + #expect(ip4!.count == 3) + #expect(ip4![0] == 1) + #expect(ip4![1] == 2) + #expect(ip4![2] == 3) } - func testUnconditionalBridgeFromObjC() { + @Test func testUnconditionalBridgeFromObjC() { let nsip1 = NSIndexPath() let nsip2 = NSIndexPath(index: 1) let nsip3 = [1, 2].withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> NSIndexPath in @@ -717,44 +715,44 @@ class TestIndexPathBridging: XCTestCase { } let ip1: IndexPath = IndexPath._unconditionallyBridgeFromObjectiveC(nsip1) - XCTAssertEqual(ip1.count, 0) - + #expect(ip1.count == 0) + let ip2: IndexPath = IndexPath._unconditionallyBridgeFromObjectiveC(nsip2) - XCTAssertEqual(ip2.count, 1) - XCTAssertEqual(ip2[0], 1) - + #expect(ip2.count == 1) + #expect(ip2[0] == 1) + let ip3: IndexPath = IndexPath._unconditionallyBridgeFromObjectiveC(nsip3) - XCTAssertEqual(ip3.count, 2) - XCTAssertEqual(ip3[0], 1) - XCTAssertEqual(ip3[1], 2) - + #expect(ip3.count == 2) + #expect(ip3[0] == 1) + #expect(ip3[1] == 2) + let ip4: IndexPath = IndexPath._unconditionallyBridgeFromObjectiveC(nsip4) - XCTAssertEqual(ip4.count, 3) - XCTAssertEqual(ip4[0], 1) - XCTAssertEqual(ip4[1], 2) - XCTAssertEqual(ip4[2], 3) + #expect(ip4.count == 3) + #expect(ip4[0] == 1) + #expect(ip4[1] == 2) + #expect(ip4[2] == 3) } - func testObjcBridgeType() { - XCTAssertTrue(IndexPath._getObjectiveCType() == NSIndexPath.self) + @Test func testObjcBridgeType() { + #expect(IndexPath._getObjectiveCType() == NSIndexPath.self) } - func test_AnyHashableCreatedFromNSIndexPath() { + @Test func test_AnyHashableCreatedFromNSIndexPath() { let values: [NSIndexPath] = [ NSIndexPath(index: 1), NSIndexPath(index: 2), NSIndexPath(index: 2), ] let anyHashables = values.map(AnyHashable.init) - expectEqual(IndexPath.self, type(of: anyHashables[0].base)) - expectEqual(IndexPath.self, type(of: anyHashables[1].base)) - expectEqual(IndexPath.self, type(of: anyHashables[2].base)) - XCTAssertNotEqual(anyHashables[0], anyHashables[1]) - XCTAssertEqual(anyHashables[1], anyHashables[2]) + #expect(IndexPath.self == type(of: anyHashables[0].base)) + #expect(IndexPath.self == type(of: anyHashables[1].base)) + #expect(IndexPath.self == type(of: anyHashables[2].base)) + #expect(anyHashables[0] != anyHashables[1]) + #expect(anyHashables[1] == anyHashables[2]) } - func test_unconditionallyBridgeFromObjectiveC() { - XCTAssertEqual(IndexPath(), IndexPath._unconditionallyBridgeFromObjectiveC(nil)) + @Test func test_unconditionallyBridgeFromObjectiveC() { + #expect(IndexPath() == IndexPath._unconditionallyBridgeFromObjectiveC(nil)) } } diff --git a/Tests/FoundationEssentialsTests/JSONEncoderTests.swift b/Tests/FoundationEssentialsTests/JSONEncoderTests.swift index 506c49bba..1fd280fcc 100644 --- a/Tests/FoundationEssentialsTests/JSONEncoderTests.swift +++ b/Tests/FoundationEssentialsTests/JSONEncoderTests.swift @@ -16,109 +16,111 @@ // REQUIRES: rdar49634697 // REQUIRES: rdar55727144 -#if canImport(TestSupport) -import TestSupport -#endif // canImport(TestSupport) +import Testing -#if canImport(FoundationEssentials) -@testable import FoundationEssentials +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc #endif #if FOUNDATION_FRAMEWORK @testable import Foundation +#else +@testable import FoundationEssentials #endif // MARK: - Test Suite -final class JSONEncoderTests : XCTestCase { +struct JSONEncoderTests { // MARK: - Encoding Top-Level Empty Types - func testEncodingTopLevelEmptyStruct() { + @Test func testEncodingTopLevelEmptyStruct() { let empty = EmptyStruct() _testRoundTrip(of: empty, expectedJSON: _jsonEmptyDictionary) } - func testEncodingTopLevelEmptyClass() { + @Test func testEncodingTopLevelEmptyClass() { let empty = EmptyClass() _testRoundTrip(of: empty, expectedJSON: _jsonEmptyDictionary) } // MARK: - Encoding Top-Level Single-Value Types - func testEncodingTopLevelSingleValueEnum() { + @Test func testEncodingTopLevelSingleValueEnum() { _testRoundTrip(of: Switch.off) _testRoundTrip(of: Switch.on) } - func testEncodingTopLevelSingleValueStruct() { + @Test func testEncodingTopLevelSingleValueStruct() { _testRoundTrip(of: Timestamp(3141592653)) } - func testEncodingTopLevelSingleValueClass() { + @Test func testEncodingTopLevelSingleValueClass() { _testRoundTrip(of: Counter()) } // MARK: - Encoding Top-Level Structured Types - func testEncodingTopLevelStructuredStruct() { + @Test func testEncodingTopLevelStructuredStruct() { // Address is a struct type with multiple fields. let address = Address.testValue _testRoundTrip(of: address) } - func testEncodingTopLevelStructuredSingleStruct() { + @Test func testEncodingTopLevelStructuredSingleStruct() { // Numbers is a struct which encodes as an array through a single value container. let numbers = Numbers.testValue _testRoundTrip(of: numbers) } - func testEncodingTopLevelStructuredSingleClass() { + @Test func testEncodingTopLevelStructuredSingleClass() { // Mapping is a class which encodes as a dictionary through a single value container. let mapping = Mapping.testValue _testRoundTrip(of: mapping) } - func testEncodingTopLevelDeepStructuredType() { + @Test func testEncodingTopLevelDeepStructuredType() { // Company is a type with fields which are Codable themselves. let company = Company.testValue _testRoundTrip(of: company) } - func testEncodingClassWhichSharesEncoderWithSuper() { + @Test func testEncodingClassWhichSharesEncoderWithSuper() { // Employee is a type which shares its encoder & decoder with its superclass, Person. let employee = Employee.testValue _testRoundTrip(of: employee) } - func testEncodingTopLevelNullableType() { + @Test func testEncodingTopLevelNullableType() { // EnhancedBool is a type which encodes either as a Bool or as nil. _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)!) } - func testEncodingTopLevelArrayOfInt() { + @Test func testEncodingTopLevelArrayOfInt() { let a = [1,2,3] let result1 = String(data: try! JSONEncoder().encode(a), encoding: String._Encoding.utf8) - XCTAssertEqual(result1, "[1,2,3]") - + #expect(result1 == "[1,2,3]") + let b : [Int] = [] let result2 = String(data: try! JSONEncoder().encode(b), encoding: String._Encoding.utf8) - XCTAssertEqual(result2, "[]") + #expect(result2 == "[]") } @available(FoundationPreview 0.1, *) - func testEncodingTopLevelWithConfiguration() throws { + @Test 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)) - XCTAssertEqual(decoded, value) + #expect(decoded == value) decoded = try decoder.decode(CodableTypeWithConfiguration.self, from: try encoder.encode(value, configuration: CodableTypeWithConfiguration.ConfigProviding.self), configuration: CodableTypeWithConfiguration.ConfigProviding.self) - XCTAssertEqual(decoded, value) + #expect(decoded == value) } -#if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 - func testEncodingConflictedTypeNestedContainersWithTheSameTopLevelKey() { +#if false // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 + @Test func testEncodingConflictedTypeNestedContainersWithTheSameTopLevelKey() { struct Model : Encodable, Equatable { let first: String @@ -158,7 +160,7 @@ final class JSONEncoderTests : XCTestCase { // MARK: - Date Strategy Tests - func testEncodingDateSecondsSince1970() { + @Test 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: String._Encoding.utf8)! @@ -175,7 +177,7 @@ final class JSONEncoderTests : XCTestCase { dateDecodingStrategy: .secondsSince1970) } - func testEncodingDateMillisecondsSince1970() { + @Test 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: String._Encoding.utf8)! @@ -192,7 +194,7 @@ final class JSONEncoderTests : XCTestCase { dateDecodingStrategy: .millisecondsSince1970) } - func testEncodingDateCustom() { + @Test func testEncodingDateCustom() { let timestamp = Date() // We'll encode a number instead of a date. @@ -215,7 +217,7 @@ final class JSONEncoderTests : XCTestCase { dateDecodingStrategy: .custom(decode)) } - func testEncodingDateCustomEmpty() { + @Test func testEncodingDateCustomEmpty() { let timestamp = Date() // Encoding nothing should encode an empty keyed container ({}). @@ -236,7 +238,7 @@ final class JSONEncoderTests : XCTestCase { } // MARK: - Data Strategy Tests - func testEncodingData() { + @Test func testEncodingData() { let data = Data([0xDE, 0xAD, 0xBE, 0xEF]) let expectedJSON = "[222,173,190,239]".data(using: String._Encoding.utf8)! @@ -252,7 +254,7 @@ final class JSONEncoderTests : XCTestCase { dataDecodingStrategy: .deferredToData) } - func testEncodingDataCustom() { + @Test func testEncodingDataCustom() { // We'll encode a number instead of data. let encode = { (_ data: Data, _ encoder: Encoder) throws -> Void in var container = encoder.singleValueContainer() @@ -273,7 +275,7 @@ final class JSONEncoderTests : XCTestCase { dataDecodingStrategy: .custom(decode)) } - func testEncodingDataCustomEmpty() { + @Test func testEncodingDataCustomEmpty() { // Encoding nothing should encode an empty keyed container ({}). let encode = { (_: Data, _: Encoder) throws -> Void in } let decode = { (_: Decoder) throws -> Data in return Data() } @@ -292,7 +294,7 @@ final class JSONEncoderTests : XCTestCase { } // MARK: - Non-Conforming Floating Point Strategy Tests - func testEncodingNonConformingFloats() { + @Test func testEncodingNonConformingFloats() { _testEncodeFailure(of: Float.infinity) _testEncodeFailure(of: Float.infinity) _testEncodeFailure(of: -Float.infinity) @@ -312,7 +314,7 @@ final class JSONEncoderTests : XCTestCase { _testEncodeFailure(of: Double.nan) } - func testEncodingNonConformingFloatStrings() { + @Test func testEncodingNonConformingFloatStrings() { let encodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "INF", negativeInfinity: "-INF", nan: "NaN") let decodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "INF", negativeInfinity: "-INF", nan: "NaN") @@ -374,7 +376,7 @@ final class JSONEncoderTests : XCTestCase { } } - func testEncodingKeyStrategyCustom() { + @Test func testEncodingKeyStrategyCustom() { let expected = "{\"QQQhello\":\"test\"}" let encoded = EncodeMe(keyName: "hello") @@ -387,7 +389,7 @@ final class JSONEncoderTests : XCTestCase { let resultData = try! encoder.encode(encoded) let resultString = String(bytes: resultData, encoding: String._Encoding.utf8) - XCTAssertEqual(expected, resultString) + #expect(expected == resultString) } private struct EncodeFailure : Encodable { @@ -406,7 +408,7 @@ final class JSONEncoderTests : XCTestCase { let outerValue: EncodeNested } - func testEncodingKeyStrategyPath() { + @Test 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\"}}}" @@ -423,15 +425,15 @@ final class JSONEncoderTests : XCTestCase { callCount = callCount + 1 if path.count == 0 { - XCTFail("The path should always have at least one entry") + Issue.record("The path should always have at least one entry") } else if path.count == 1 { - XCTAssertEqual(["outerValue"], path.map { $0.stringValue }) + #expect(["outerValue"] == path.map { $0.stringValue }) } else if path.count == 2 { - XCTAssertEqual(["outerValue", "nestedValue"], path.map { $0.stringValue }) + #expect(["outerValue", "nestedValue"] == path.map { $0.stringValue }) } else if path.count == 3 { - XCTAssertEqual(["outerValue", "nestedValue", "helloWorld"], path.map { $0.stringValue }) + #expect(["outerValue", "nestedValue", "helloWorld"] == path.map { $0.stringValue }) } else { - XCTFail("The path mysteriously had more entries") + Issue.record("The path mysteriously had more entries") } let key = _TestKey(stringValue: "QQQ" + path.last!.stringValue)! @@ -441,8 +443,8 @@ final class JSONEncoderTests : XCTestCase { let resultData = try! encoder.encode(encoded) let resultString = String(bytes: resultData, encoding: String._Encoding.utf8) - XCTAssertEqual(expected, resultString) - XCTAssertEqual(3, callCount) + #expect(expected == resultString) + #expect(3 == callCount) } private struct DecodeMe : Decodable { @@ -459,7 +461,7 @@ final class JSONEncoderTests : XCTestCase { private struct DecodeMe2 : Decodable { var hello: String } - func testDecodingKeyStrategyCustom() { + @Test func testDecodingKeyStrategyCustom() { let input = "{\"----hello\":\"test\"}".data(using: String._Encoding.utf8)! let decoder = JSONDecoder() let customKeyConversion = { (_ path: [CodingKey]) -> CodingKey in @@ -472,29 +474,29 @@ final class JSONEncoderTests : XCTestCase { decoder.keyDecodingStrategy = .custom(customKeyConversion) let result = try! decoder.decode(DecodeMe2.self, from: input) - XCTAssertEqual("test", result.hello) + #expect("test" == result.hello) } - func testDecodingDictionaryStringKeyConversionUntouched() { + @Test 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) - XCTAssertEqual(["leave_me_alone": "test"], result) + #expect(["leave_me_alone": "test"] == result) } - func testDecodingDictionaryFailureKeyPath() { + @Test func testDecodingDictionaryFailureKeyPath() { let input = "{\"leave_me_alone\":\"test\"}".data(using: String._Encoding.utf8)! let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase 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) + #expect(1 == context.codingPath.count) + #expect("leave_me_alone" == context.codingPath[0].stringValue) } catch { - XCTFail("Unexpected error: \(String(describing: error))") + Issue.record(error, "Unexpected error: \(String(describing: error))") } } @@ -510,7 +512,7 @@ final class JSONEncoderTests : XCTestCase { var thisIsCamelCase : String } - func testKeyStrategyDuplicateKeys() { + @Test func testKeyStrategyDuplicateKeys() { // This test is mostly to make sure we don't assert on duplicate keys struct DecodeMe5 : Codable { var oneTwo : String @@ -552,9 +554,9 @@ final class JSONEncoderTests : XCTestCase { let decodingResult = try! decoder.decode(DecodeMe5.self, from: input) // There will be only one result for oneTwo. - XCTAssertEqual(1, decodingResult.numberOfKeys) + #expect(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. - XCTAssertEqual(decodingResult.oneTwo, "test1") + #expect(decodingResult.oneTwo == "test1") // Encoding let encoded = DecodeMe5() @@ -564,30 +566,30 @@ final class JSONEncoderTests : XCTestCase { let decodingResultString = String(bytes: decodingResultData, encoding: String._Encoding.utf8) // There will be only one value in the result (the second one encoded) - XCTAssertEqual("{\"oneTwo\":\"test2\"}", decodingResultString) + #expect("{\"oneTwo\":\"test2\"}" == decodingResultString) } // MARK: - Encoder Features - func testNestedContainerCodingPaths() { + @Test func testNestedContainerCodingPaths() { let encoder = JSONEncoder() do { let _ = try encoder.encode(NestedContainersTestType()) - } catch let error as NSError { - XCTFail("Caught error during encoding nested container types: \(error)") + } catch { + Issue.record(error, "Caught error during encoding nested container types: \(error)") } } - func testSuperEncoderCodingPaths() { + @Test func testSuperEncoderCodingPaths() { let encoder = JSONEncoder() do { let _ = try encoder.encode(NestedContainersTestType(testSuperEncoder: true)) - } catch let error as NSError { - XCTFail("Caught error during encoding nested container types: \(error)") + } catch { + Issue.record(error, "Caught error during encoding nested container types: \(error)") } } // MARK: - Type coercion - func testTypeCoercion() { + @Test func testTypeCoercion() { _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int].self) _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int8].self) _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int16].self) @@ -614,25 +616,28 @@ final class JSONEncoderTests : XCTestCase { _testRoundTripTypeCoercionFailure(of: [0.0, 1.0] as [Double], as: [Bool].self) } - func testDecodingConcreteTypeParameter() { + @Test func testDecodingConcreteTypeParameter() { let encoder = JSONEncoder() guard let json = try? encoder.encode(Employee.testValue) else { - XCTFail("Unable to encode Employee.") + Issue.record("Unable to encode Employee.") return } let decoder = JSONDecoder() guard let decoded = try? decoder.decode(Employee.self as Person.Type, from: json) else { - XCTFail("Failed to decode Employee as Person from JSON.") + Issue.record("Failed to decode Employee as Person from JSON.") return } - expectEqual(type(of: decoded), Employee.self, "Expected decoded value to be of type Employee; got \(type(of: decoded)) instead.") + #expect( + type(of: decoded) == Employee.self, + "Expected decoded value to be of type Employee; got \(type(of: decoded)) instead." + ) } // MARK: - Encoder State // SR-6078 - func testEncoderStateThrowOnEncode() { + @Test func testEncoderStateThrowOnEncode() { struct ReferencingEncoderWrapper : Encodable { let value: T init(_ value: T) { self.value = value } @@ -661,7 +666,7 @@ final class JSONEncoderTests : XCTestCase { _ = try? JSONEncoder().encode(ReferencingEncoderWrapper([Float.infinity])) } - func testEncoderStateThrowOnEncodeCustomDate() { + @Test func testEncoderStateThrowOnEncodeCustomDate() { // This test is identical to testEncoderStateThrowOnEncode, except throwing via a custom Date closure. struct ReferencingEncoderWrapper : Encodable { let value: T @@ -685,7 +690,7 @@ final class JSONEncoderTests : XCTestCase { _ = try? encoder.encode(ReferencingEncoderWrapper(Date())) } - func testEncoderStateThrowOnEncodeCustomData() { + @Test func testEncoderStateThrowOnEncodeCustomData() { // This test is identical to testEncoderStateThrowOnEncode, except throwing via a custom Data closure. struct ReferencingEncoderWrapper : Encodable { let value: T @@ -709,7 +714,7 @@ final class JSONEncoderTests : XCTestCase { _ = try? encoder.encode(ReferencingEncoderWrapper(Data())) } - func test_106506794() throws { + @Test func test_106506794() throws { struct Level1: Codable, Equatable { let level2: Level2 @@ -743,15 +748,15 @@ final class JSONEncoderTests : XCTestCase { do { let decodedValue = try JSONDecoder().decode(Level1.self, from: data) - XCTAssertEqual(value, decodedValue) + #expect(value == decodedValue) } catch { - XCTFail("Decode should not have failed with error: \(error))") + Issue.record(error, "Decode should not have failed with error: \(error))") } } // MARK: - Decoder State // SR-6048 - func testDecoderStateThrowOnDecode() { + @Test 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]. @@ -759,7 +764,7 @@ final class JSONEncoderTests : XCTestCase { let _ = try! JSONDecoder().decode(EitherDecodable<[String], [Int]>.self, from: json) } - func testDecoderStateThrowOnDecodeCustomDate() { + @Test 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 @@ -771,7 +776,7 @@ final class JSONEncoderTests : XCTestCase { let _ = try! decoder.decode(EitherDecodable.self, from: json) } - func testDecoderStateThrowOnDecodeCustomData() { + @Test 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 @@ -784,7 +789,7 @@ final class JSONEncoderTests : XCTestCase { } - func testDecodingFailure() { + @Test func testDecodingFailure() { struct DecodeFailure : Decodable { var invalid: String } @@ -792,7 +797,7 @@ final class JSONEncoderTests : XCTestCase { _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: String._Encoding.utf8)!) } - func testDecodingFailureThrowInInitKeyedContainer() { + @Test func testDecodingFailureThrowInInitKeyedContainer() { struct DecodeFailure : Decodable { private enum CodingKeys: String, CodingKey { case checkedString @@ -817,7 +822,7 @@ final class JSONEncoderTests : XCTestCase { _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: String._Encoding.utf8)!) } - func testDecodingFailureThrowInInitSingleContainer() { + @Test func testDecodingFailureThrowInInitSingleContainer() { struct DecodeFailure : Decodable { private enum Error: Swift.Error { case expectedError @@ -838,7 +843,7 @@ final class JSONEncoderTests : XCTestCase { _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: String._Encoding.utf8)!) } - func testInvalidFragment() { + @Test func testInvalidFragment() { struct DecodeFailure: Decodable { var foo: String } @@ -846,7 +851,7 @@ final class JSONEncoderTests : XCTestCase { _testDecodeFailure(of: DecodeFailure.self, data: toDecode.data(using: String._Encoding.utf8)!) } - func testRepeatedFailedNilChecks() { + @Test func testRepeatedFailedNilChecks() { struct RepeatNilCheckDecodable : Decodable { enum Failure : Error { case badNil @@ -891,10 +896,12 @@ final class JSONEncoderTests : XCTestCase { } } let json = "[1, 2, 3]".data(using: String._Encoding.utf8)! - XCTAssertNoThrow(try JSONDecoder().decode(RepeatNilCheckDecodable.self, from: json)) + #expect(throws: Never.self) { + try JSONDecoder().decode(RepeatNilCheckDecodable.self, from: json) + } } - func testDelayedDecoding() throws { + @Test func testDelayedDecoding() throws { // One variation is deferring the use of a container. struct DelayedDecodable_ContainerVersion : Codable { @@ -928,7 +935,9 @@ final class JSONEncoderTests : XCTestCase { let data = try JSONEncoder().encode(before) let decoded = try JSONDecoder().decode(DelayedDecodable_ContainerVersion.self, from: data) - XCTAssertNoThrow(try decoded.i) + #expect(throws: Never.self) { + 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 { @@ -959,7 +968,9 @@ final class JSONEncoderTests : XCTestCase { } // Reuse the same data. let decoded2 = try JSONDecoder().decode(DelayedDecodable_DecoderVersion.self, from: data) - XCTAssertNoThrow(try decoded2.i) + #expect(throws: Never.self) { + try decoded2.i + } } // MARK: - Helper Functions @@ -970,18 +981,18 @@ final class JSONEncoderTests : XCTestCase { private func _testEncodeFailure(of value: T) { do { let _ = try JSONEncoder().encode(value) - XCTFail("Encode of top-level \(T.self) was expected to fail.") + Issue.record("Encode of top-level \(T.self) was expected to fail.") } catch { - XCTAssertNotNil(error); + #expect(error != nil) } } 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.") + Issue.record("Decode of top-level \(value) was expected to fail.") } catch { - XCTAssertNotNil(error); + #expect(error != nil) } } @@ -1006,13 +1017,13 @@ final class JSONEncoderTests : XCTestCase { encoder.keyEncodingStrategy = keyEncodingStrategy payload = try encoder.encode(value) } catch { - XCTFail("Failed to encode \(T.self) to JSON: \(error)") + Issue.record(error, "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)! - XCTAssertEqual(expected, actual, "Produced JSON not identical to expected JSON.") + #expect(expected == actual, "Produced JSON not identical to expected JSON.") } do { @@ -1022,9 +1033,9 @@ final class JSONEncoderTests : XCTestCase { decoder.nonConformingFloatDecodingStrategy = nonConformingFloatDecodingStrategy decoder.keyDecodingStrategy = keyDecodingStrategy let decoded = try decoder.decode(T.self, from: payload) - XCTAssertEqual(decoded, value, "\(T.self) did not round-trip to an equal value.") + #expect(decoded == value, "\(T.self) did not round-trip to an equal value.") } catch { - XCTFail("Failed to decode \(T.self) from JSON: \(error)") + Issue.record(error, "Failed to decode \(T.self) from JSON: \(error)") } } @@ -1032,7 +1043,7 @@ final class JSONEncoderTests : XCTestCase { 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.") + Issue.record("Coercion from \(T.self) to \(U.self) was expected to fail.") } catch {} } @@ -1050,26 +1061,29 @@ final class JSONEncoderTests : XCTestCase { do { parsed = try decoder.decode(T.self, from: data) } catch { - XCTFail("Failed to decode \(JSONString) with encoding \(enc): Error: \(error)") + Issue.record( + error, + "Failed to decode \(JSONString) with encoding \(enc): Error: \(error)" + ) continue } - XCTAssertEqual(object, parsed) + #expect(object == parsed) } } - func test_JSONEscapedSlashes() { + @Test func test_JSONEscapedSlashes() { _test(JSONString: "\"\\/test\\/path\"", to: "/test/path") _test(JSONString: "\"\\\\/test\\\\/path\"", to: "\\/test\\/path") } - func test_JSONEscapedForwardSlashes() { + @Test func test_JSONEscapedForwardSlashes() { _testRoundTrip(of: ["/":1], expectedJSON: """ {"\\/":1} """.data(using: String._Encoding.utf8)!) } - func test_JSONUnicodeCharacters() { + @Test func test_JSONUnicodeCharacters() { // UTF8: // E9 96 86 E5 B4 AC EB B0 BA EB 80 AB E9 A2 92 // 閆崬밺뀫颒 @@ -1077,7 +1091,7 @@ final class JSONEncoderTests : XCTestCase { _test(JSONString: "[\"本日\"]", to: ["本日"]) } - func test_JSONUnicodeEscapes() throws { + @Test func test_JSONUnicodeEscapes() throws { let testCases = [ // e-acute and greater-than-or-equal-to "\"\\u00e9\\u2265\"" : "é≥", @@ -1096,41 +1110,48 @@ final class JSONEncoderTests : XCTestCase { } } - func test_JSONBadUnicodeEscapes() { + @Test 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)) + #expect(throws: (any Error).self) { + try JSONDecoder().decode(String.self, from: data) + } } } - func test_nullByte() throws { + @Test 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) - XCTAssertEqual([string], decoded) - + #expect([string] == decoded) + let data2 = try encoder.encode([string:string]) let decoded2 = try decoder.decode([String:String].self, from: data2) - XCTAssertEqual([string:string], decoded2) - + #expect([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) - XCTAssertEqual(decoded3.s, string) + #expect(decoded3.s == string) } - func test_superfluouslyEscapedCharacters() { + @Test func test_superfluouslyEscapedCharacters() { let json = "[\"\\h\\e\\l\\l\\o\"]" - XCTAssertThrowsError(try JSONDecoder().decode([String].self, from: json.data(using: String._Encoding.utf8)!)) + #expect(throws: (any Error).self) { + try JSONDecoder().decode( + [String].self, + from: json.data(using: String._Encoding.utf8)! + ) + } } - func test_equivalentUTF8Sequences() { + @Test func test_equivalentUTF8Sequences() { let json = """ { @@ -1141,13 +1162,13 @@ final class JSONEncoderTests : XCTestCase { do { let dict = try JSONDecoder().decode([String:Bool].self, from: json) - XCTAssertEqual(dict.count, 1) + #expect(dict.count == 1) } catch { - XCTFail("Unexpected error: \(error)") + Issue.record(error, "Unexpected error: \(error)") } } - func test_JSONControlCharacters() { + @Test func test_JSONControlCharacters() { let array = [ "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", "\\b", "\\t", @@ -1164,7 +1185,7 @@ final class JSONEncoderTests : XCTestCase { } } - func test_JSONNumberFragments() { + @Test 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) { @@ -1172,33 +1193,35 @@ final class JSONEncoderTests : XCTestCase { } } - func test_invalidJSONNumbersFailAsExpected() { + @Test 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: String._Encoding.utf8)! - XCTAssertThrowsError(try JSONDecoder().decode(Float.self, from: data), "Expected error for input \"\(json)\"") + #expect(throws: (any Error).self, "Expected error for input \"\(json)\"") { + try JSONDecoder().decode(Float.self, from: data) + } } } func _checkExpectedThrownDataCorruptionUnderlyingError(contains substring: String, closure: () throws -> Void) { do { try closure() - XCTFail("Expected failure containing string: \"\(substring)\"") + Issue.record("Expected failure containing string: \"\(substring)\"") } catch let error as DecodingError { guard case let .dataCorrupted(context) = error else { - XCTFail("Unexpected DecodingError type: \(error)") + Issue.record(error, "Unexpected DecodingError type: \(error)") return } #if FOUNDATION_FRAMEWORK let nsError = context.underlyingError! as NSError - XCTAssertTrue(nsError.debugDescription.contains(substring), "Description \"\(nsError.debugDescription)\" doesn't contain substring \"\(substring)\"") + #expect(nsError.debugDescription.contains(substring), "Description \"\(nsError.debugDescription)\" doesn't contain substring \"\(substring)\"") #endif } catch { - XCTFail("Unexpected error type: \(error)") + Issue.record(error, "Unexpected error type: \(error)") } } - func test_topLevelFragmentsWithGarbage() { + @Test func test_topLevelFragmentsWithGarbage() { _checkExpectedThrownDataCorruptionUnderlyingError(contains: "Unexpected character") { 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)!) @@ -1213,14 +1236,19 @@ final class JSONEncoderTests : XCTestCase { } } - func test_topLevelNumberFragmentsWithJunkDigitCharacters() { + @Test func test_topLevelNumberFragmentsWithJunkDigitCharacters() throws { let fullData = "3.141596".data(using: String._Encoding.utf8)! let partialData = fullData[0..<4] - - XCTAssertEqual(3.14, try JSONDecoder().decode(Double.self, from: partialData)) + let result = try JSONDecoder().decode(Double.self, from: partialData) + #expect(3.14 == result) } - func test_depthTraversal() { +#if !FOUNDATION_FRAMEWORK + // rdar://126294929 (JSONEncoderTests.test_depthTraversal + // causes EXC_BAD_ACCESS when running under ASAN and Swift Testing) + // This test causes EXC_BAD_ACCESS when running under ASAN and Swift Testing. + // Disable it for now when we investigate. + @Test func test_depthTraversal() throws { struct SuperNestedArray : Decodable { init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() @@ -1234,12 +1262,22 @@ final class JSONEncoderTests : XCTestCase { 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) - 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)!)) - + #expect(throws: Never.self) { + try JSONDecoder().decode( + SuperNestedArray.self, + from: jsonGood.data(using: String._Encoding.utf8)! + ) + } + #expect(throws: (any Error).self) { + try JSONDecoder().decode( + SuperNestedArray.self, + from: jsonBad.data(using: String._Encoding.utf8)! + ) + } } +#endif - func test_JSONPermitsTrailingCommas() { + @Test 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, ],}" @@ -1247,29 +1285,31 @@ final class JSONEncoderTests : XCTestCase { let result = try! JSONDecoder().decode([String:[Bool]].self, from: data) let expected = ["key" : [true]] - XCTAssertEqual(result, expected) + #expect(result == expected) } - func test_whitespaceOnlyData() { + @Test func test_whitespaceOnlyData() { let data = " ".data(using: String._Encoding.utf8)! - XCTAssertThrowsError(try JSONDecoder().decode(Int.self, from: data)) + #expect(throws: (any Error).self) { + try JSONDecoder().decode(Int.self, from: data) + } } - func test_smallFloatNumber() { + @Test func test_smallFloatNumber() { _testRoundTrip(of: [["magic_number" : 7.45673334164903e-115]]) } - func test_largeIntegerNumber() { + @Test func test_largeIntegerNumber() { let num : UInt64 = 6032314514195021674 let json = "{\"a\":\(num)}" let data = json.data(using: String._Encoding.utf8)! let result = try! JSONDecoder().decode([String:UInt64].self, from: data) let number = result["a"]! - XCTAssertEqual(number, num) + #expect(number == num) } - func test_roundTrippingExtremeValues() { + @Test func test_roundTrippingExtremeValues() { struct Numbers : Codable, Equatable { let floats : [Float] let doubles : [Double] @@ -1278,7 +1318,7 @@ final class JSONEncoderTests : XCTestCase { _testRoundTrip(of: testValue) } - func test_roundTrippingDoubleValues() { + @Test func test_roundTrippingDoubleValues() { struct Numbers : Codable, Equatable { let doubles : [String:Double] let decimals : [String:Decimal] @@ -1313,7 +1353,7 @@ final class JSONEncoderTests : XCTestCase { _testRoundTrip(of: testValue) } - func test_localeDecimalPolicyIndependence() { + @Test func test_localeDecimalPolicyIndependence() { var currentLocale: UnsafeMutablePointer? = nil if let localePtr = setlocale(LC_ALL, nil) { currentLocale = strdup(localePtr) @@ -1339,13 +1379,13 @@ final class JSONEncoderTests : XCTestCase { #endif let decoded = try JSONDecoder().decode(type(of: orig).self, from: data) - XCTAssertEqual(orig, decoded) + #expect(orig == decoded) } catch { - XCTFail("Error: \(error)") + Issue.record(error, "Error: \(error)") } } - func test_whitespace() { + @Test func test_whitespace() { let tests : [(json: String, expected: [String:Bool])] = [ ("{\"v\"\n : true}", ["v":true]), ("{\"v\"\r\n : true}", ["v":true]), @@ -1354,78 +1394,130 @@ final class JSONEncoderTests : XCTestCase { for test in tests { let data = test.json.data(using: String._Encoding.utf8)! let decoded = try! JSONDecoder().decode([String:Bool].self, from: data) - XCTAssertEqual(test.expected, decoded) + #expect(test.expected == decoded) } } - func test_assumesTopLevelDictionary() { + @Test func test_assumesTopLevelDictionary() { let decoder = JSONDecoder() decoder.assumesTopLevelDictionary = true let json = "\"x\" : 42" do { let result = try decoder.decode([String:Int].self, from: json.data(using: String._Encoding.utf8)!) - XCTAssertEqual(result, ["x" : 42]) + #expect(result == ["x" : 42]) } catch { - XCTFail("Error thrown while decoding assumed top-level dictionary: \(error)") + Issue.record(error, "Error thrown while decoding assumed top-level dictionary: \(error)") } let jsonWithBraces = "{\"x\" : 42}" do { let result = try decoder.decode([String:Int].self, from: jsonWithBraces.data(using: String._Encoding.utf8)!) - XCTAssertEqual(result, ["x" : 42]) + #expect(result == ["x" : 42]) } catch { - XCTFail("Error thrown while decoding assumed top-level dictionary: \(error)") + Issue.record(error, "Error thrown while decoding assumed top-level dictionary: \(error)") } do { let result = try decoder.decode([String:Int].self, from: Data()) - XCTAssertEqual(result, [:]) + #expect(result == [:]) } catch { - XCTFail("Error thrown while decoding empty assumed top-level dictionary: \(error)") + Issue.record( + error, + "Error thrown while decoding empty assumed top-level dictionary: \(error)" + ) } let jsonWithEndBraceOnly = "\"x\" : 42}" - XCTAssertThrowsError(try decoder.decode([String:Int].self, from: jsonWithEndBraceOnly.data(using: String._Encoding.utf8)!)) + #expect(throws: (any Error).self) { + try decoder.decode( + [String:Int].self, + from: jsonWithEndBraceOnly.data(using: String._Encoding.utf8)! + ) + } let jsonWithStartBraceOnly = "{\"x\" : 42" - XCTAssertThrowsError(try decoder.decode([String:Int].self, from: jsonWithStartBraceOnly.data(using: String._Encoding.utf8)!)) + #expect(throws: (any Error).self) { + try decoder.decode( + [String:Int].self, + from: jsonWithStartBraceOnly.data(using: String._Encoding.utf8)! + ) + } } - func test_BOMPrefixes() { + @Test func test_BOMPrefixes() throws { let json = "\"👍🏻\"" let decoder = JSONDecoder() // UTF-8 BOM let utf8_BOM = Data([0xEF, 0xBB, 0xBF]) - XCTAssertEqual("👍🏻", try decoder.decode(String.self, from: utf8_BOM + json.data(using: String._Encoding.utf8)!)) + var result = try decoder.decode( + String.self, + from: utf8_BOM + json.data(using: String._Encoding.utf8)! + ) + #expect("👍🏻" == result) + result = "" // UTF-16 BE let utf16_BE_BOM = Data([0xFE, 0xFF]) - XCTAssertEqual("👍🏻", try decoder.decode(String.self, from: utf16_BE_BOM + json.data(using: String._Encoding.utf16BigEndian)!)) + result = try decoder.decode( + String.self, + from: utf16_BE_BOM + json.data(using: String._Encoding.utf16BigEndian)! + ) + #expect("👍🏻" == result) + result = "" // UTF-16 LE let utf16_LE_BOM = Data([0xFF, 0xFE]) - XCTAssertEqual("👍🏻", try decoder.decode(String.self, from: utf16_LE_BOM + json.data(using: String._Encoding.utf16LittleEndian)!)) + result = try decoder.decode( + String.self, + from: utf16_LE_BOM + json.data(using: String._Encoding.utf16LittleEndian)! + ) + #expect("👍🏻" == result) + result = "" // UTF-32 BE let utf32_BE_BOM = Data([0x0, 0x0, 0xFE, 0xFF]) - XCTAssertEqual("👍🏻", try decoder.decode(String.self, from: utf32_BE_BOM + json.data(using: String._Encoding.utf32BigEndian)!)) + result = try decoder.decode( + String.self, + from: utf32_BE_BOM + json.data(using: String._Encoding.utf32BigEndian)! + ) + #expect("👍🏻" == result) + result = "" // UTF-32 LE let utf32_LE_BOM = Data([0xFE, 0xFF, 0, 0]) - XCTAssertEqual("👍🏻", try decoder.decode(String.self, from: utf32_LE_BOM + json.data(using: String._Encoding.utf32LittleEndian)!)) + result = try decoder.decode( + String.self, + from: utf32_LE_BOM + json.data(using: String._Encoding.utf32LittleEndian)! + ) + #expect("👍🏻" == result) // Try some mismatched BOMs - XCTAssertThrowsError(try decoder.decode(String.self, from: utf32_LE_BOM + json.data(using: String._Encoding.utf32BigEndian)!)) - - XCTAssertThrowsError(try decoder.decode(String.self, from: utf16_BE_BOM + json.data(using: String._Encoding.utf32LittleEndian)!)) - - XCTAssertThrowsError(try decoder.decode(String.self, from: utf8_BOM + json.data(using: String._Encoding.utf16BigEndian)!)) + #expect(throws: (any Error).self) { + 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: String._Encoding.utf32LittleEndian)! + ) + } + + #expect(throws: (any Error).self) { + try decoder.decode( + String.self, + from: utf8_BOM + json.data(using: String._Encoding.utf16BigEndian)! + ) + } } - func test_valueNotFoundError() { + @Test func test_valueNotFoundError() { struct ValueNotFound : Decodable { let a: Bool let nope: String? @@ -1450,25 +1542,33 @@ final class JSONEncoderTests : XCTestCase { let json = "{\"a\":true}".data(using: String._Encoding.utf8)! // The expected valueNotFound error is swalled by the init(from:) implementation. - XCTAssertNoThrow(try JSONDecoder().decode(ValueNotFound.self, from: json)) + #expect(throws: Never.self) { + try JSONDecoder().decode(ValueNotFound.self, from: json) + } } - func test_infiniteDate() { + @Test func test_infiniteDate() { let date = Date(timeIntervalSince1970: .infinity) let encoder = JSONEncoder() encoder.dateEncodingStrategy = .deferredToDate - XCTAssertThrowsError(try encoder.encode([date])) + #expect(throws: (any Error).self) { + try encoder.encode([date]) + } encoder.dateEncodingStrategy = .secondsSince1970 - XCTAssertThrowsError(try encoder.encode([date])) + #expect(throws: (any Error).self) { + try encoder.encode([date]) + } encoder.dateEncodingStrategy = .millisecondsSince1970 - XCTAssertThrowsError(try encoder.encode([date])) + #expect(throws: (any Error).self) { + try encoder.encode([date]) + } } - func test_typeEncodesNothing() { + @Test func test_typeEncodesNothing() { struct EncodesNothing : Encodable { func encode(to encoder: Encoder) throws { // Intentionally nothing. @@ -1476,18 +1576,20 @@ final class JSONEncoderTests : XCTestCase { } let enc = JSONEncoder() - XCTAssertThrowsError(try enc.encode(EncodesNothing())) + #expect(throws: (any Error).self) { + 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()]) - XCTAssertEqual("[{}]", String(data: arrayData, encoding: .utf8)) + #expect("[{}]" == String(data: arrayData, encoding: .utf8)) let objectData = try! enc.encode(["test" : EncodesNothing()]) - XCTAssertEqual("{\"test\":{}}", String(data: objectData, encoding: .utf8)) + #expect("{\"test\":{}}" == String(data: objectData, encoding: .utf8)) } - func test_superEncoders() { + @Test func test_superEncoders() { struct SuperEncoding : Encodable { enum CodingKeys: String, CodingKey { case firstSuper @@ -1520,12 +1622,12 @@ final class JSONEncoderTests : XCTestCase { let data = try! JSONEncoder().encode(SuperEncoding()) let string = String(data: data, encoding: .utf8)! - XCTAssertTrue(string.contains("\"firstSuper\":\"First\"")) - XCTAssertTrue(string.contains("\"secondSuper\":\"Second\"")) - XCTAssertTrue(string.contains("[0,\"First\",\"Second\",42]")) + #expect(string.contains("\"firstSuper\":\"First\"")) + #expect(string.contains("\"secondSuper\":\"Second\"")) + #expect(string.contains("[0,\"First\",\"Second\",42]")) } - func testRedundantKeys() { + @Test func testRedundantKeys() { // Last encoded key wins. struct RedundantEncoding : Encodable { @@ -1559,22 +1661,22 @@ final class JSONEncoderTests : XCTestCase { } } var data = try! JSONEncoder().encode(RedundantEncoding(replacedType: .value, useSuperEncoder: false)) - XCTAssertEqual(String(data: data, encoding: .utf8), ("{\"key\":42}")) + #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}")) + #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}")) + #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}")) + #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}")) + #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}")) + #expect(String(data: data, encoding: .utf8) == ("{\"key\":42}")) } // 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. @@ -1624,7 +1726,7 @@ final class JSONEncoderTests : XCTestCase { return decoder } - func test_json5Numbers() { + @Test func test_json5Numbers() { let decoder = json5Decoder let successfulIntegers: [(String,Int)] = [ @@ -1654,9 +1756,9 @@ final class JSONEncoderTests : XCTestCase { for (json, expected) in successfulIntegers { do { let val = try decoder.decode(Int.self, from: json.data(using: String._Encoding.utf8)!) - XCTAssertEqual(val, expected, "Wrong value parsed from input \"\(json)\"") + #expect(val == expected, "Wrong value parsed from input \"\(json)\"") } catch { - XCTFail("Error when parsing input \"\(json)\": \(error)") + Issue.record(error, "Error when parsing input \"\(json)\": \(error)") } } @@ -1699,12 +1801,12 @@ final class JSONEncoderTests : XCTestCase { do { let val = try decoder.decode(Double.self, from: json.data(using: String._Encoding.utf8)!) if expected.isNaN { - XCTAssertTrue(val.isNaN, "Wrong value \(val) parsed from input \"\(json)\"") + #expect(val.isNaN, "Wrong value \(val) parsed from input \"\(json)\"") } else { - XCTAssertEqual(val, expected, "Wrong value parsed from input \"\(json)\"") + #expect(val == expected, "Wrong value parsed from input \"\(json)\"") } } catch { - XCTFail("Error when parsing input \"\(json)\": \(error)") + Issue.record(error, "Error when parsing input \"\(json)\": \(error)") } } @@ -1732,7 +1834,7 @@ final class JSONEncoderTests : XCTestCase { for json in unsuccessfulIntegers { do { let _ = try decoder.decode(Int.self, from: json.data(using: String._Encoding.utf8)!) - XCTFail("Expected failure for input \"\(json)\"") + Issue.record("Expected failure for input \"\(json)\"") } catch { } } @@ -1763,12 +1865,12 @@ final class JSONEncoderTests : XCTestCase { for json in unsuccessfulDoubles { do { let _ = try decoder.decode(Double.self, from: json.data(using: String._Encoding.utf8)!) - XCTFail("Expected failure for input \"\(json)\"") + Issue.record("Expected failure for input \"\(json)\"") } catch { } } } - func test_json5Null() { + @Test func test_json5Null() { let validJSON = "null" let invalidJSON = [ "Null", @@ -1779,14 +1881,24 @@ final class JSONEncoderTests : XCTestCase { "nu " ] - XCTAssertNoThrow(try json5Decoder.decode(NullReader.self, from: validJSON.data(using: String._Encoding.utf8)!)) + #expect(throws: Never.self) { + try json5Decoder.decode( + NullReader.self, + from: validJSON.data(using: String._Encoding.utf8)! + ) + } for json in invalidJSON { - XCTAssertThrowsError(try json5Decoder.decode(NullReader.self, from: json.data(using: String._Encoding.utf8)!), "Expected failure while decoding input \"\(json)\"") + #expect(throws: (any Error).self, "Expected failure while decoding input \"\(json)\"") { + try json5Decoder.decode( + NullReader.self, + from: json.data(using: String._Encoding.utf8)! + ) + } } } - func test_json5EsotericErrors() { + @Test func test_json5EsotericErrors() { // All of the following should fail let arrayStrings = [ "[", @@ -1815,17 +1927,29 @@ final class JSONEncoderTests : XCTestCase { [.init(ascii: "{"), 0xf0, 0x80, 0x80], // Invalid UTF-8: Initial byte of 3-byte sequence with only one continuation ] for json in arrayStrings { - XCTAssertThrowsError(try json5Decoder.decode([String].self, from: json.data(using: String._Encoding.utf8)!), "Expected error for input \"\(json)\"") + #expect(throws: (any Error).self, "Expected error for input \"\(json)\"") { + try json5Decoder.decode( + [String].self, + from: json.data(using: String._Encoding.utf8)! + ) + } } for json in objectStrings { - XCTAssertThrowsError(try json5Decoder.decode([String:Bool].self, from: json.data(using: String._Encoding.utf8)!), "Expected error for input \(json)") + #expect(throws: (any Error).self, "Expected error for input \(json)") { + try json5Decoder.decode( + [String:Bool].self, + from: json.data(using: String._Encoding.utf8)! + ) + } } for json in objectCharacterArrays { - XCTAssertThrowsError(try json5Decoder.decode([String:Bool].self, from: Data(json)), "Expected error for input \(json)") + #expect(throws: (any Error).self, "Expected error for input \(json)") { + try json5Decoder.decode([String:Bool].self, from: Data(json)) + } } } - func test_json5Strings() { + @Test func test_json5Strings() { let stringsToTrues = [ "{v\n : true}", "{v \n : true}", @@ -1866,21 +1990,29 @@ final class JSONEncoderTests : XCTestCase { ] for json in stringsToTrues { - XCTAssertNoThrow(try json5Decoder.decode([String:Bool].self, from: json.data(using: String._Encoding.utf8)!), "Failed to parse \"\(json)\"") + #expect(throws: Never.self, "Failed to parse \"\(json)\"") { + try json5Decoder.decode( + [String:Bool].self, + from: json.data(using: String._Encoding.utf8)! + ) + } } for (json, expected) in stringsToStrings { do { let decoded = try json5Decoder.decode([String:String].self, from: json.data(using: String._Encoding.utf8)!) - XCTAssertEqual(expected, decoded["v"]) + #expect(expected == decoded["v"]) } catch { if let expected { - XCTFail("Expected \(expected) for input \"\(json)\", but failed with \(error)") + Issue.record( + error, + "Expected \(expected) for input \"\(json)\", but failed with \(error)" + ) } } } } - func test_json5AssumedDictionary() { + @Test func test_json5AssumedDictionary() { let decoder = json5Decoder decoder.assumesTopLevelDictionary = true @@ -1910,10 +2042,12 @@ final class JSONEncoderTests : XCTestCase { for (json, expected) in stringsToString { do { let decoded = try decoder.decode([String:String].self, from: json.data(using: String._Encoding.utf8)!) - XCTAssertEqual(expected, decoded) + #expect(expected == decoded) } catch { if let expected { - XCTFail("Expected \(expected) for input \"\(json)\", but failed with \(error)") + Issue.record( + error, + "Expected \(expected) for input \"\(json)\", but failed with \(error)") } } } @@ -1933,20 +2067,29 @@ final class JSONEncoderTests : XCTestCase { for json in stringsToNestedDictionary { do { let decoded = try decoder.decode(HelloGoodbye.self, from: json.data(using: String._Encoding.utf8)!) - XCTAssertEqual(helloGoodbyeExpectedValue, decoded) + #expect(helloGoodbyeExpectedValue == decoded) } catch { - XCTFail("Expected \(helloGoodbyeExpectedValue) for input \"\(json)\", but failed with \(error)") + Issue.record( + error, + "Expected \(helloGoodbyeExpectedValue) for input \"\(json)\", but failed with \(error)" + ) } } 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)) + #expect(throws: (any Error).self) { + try decoder.decode([Int].self, from: arrayJSON) + } let strFragmentJSON = "fragment".data(using: String._Encoding.utf8)! // Assumed dictionary can't be a fragment - XCTAssertThrowsError(try decoder.decode(String.self, from: strFragmentJSON)) + #expect(throws: (any Error).self) { + try decoder.decode(String.self, from: strFragmentJSON) + } let numFragmentJSON = "42".data(using: String._Encoding.utf8)! // Assumed dictionary can't be a fragment - XCTAssertThrowsError(try decoder.decode(Int.self, from: numFragmentJSON)) + #expect(throws: (any Error).self) { + try decoder.decode(Int.self, from: numFragmentJSON) + } } enum JSON5SpecTestType { @@ -1970,7 +2113,7 @@ final class JSONEncoderTests : XCTestCase { // MARK: - SnakeCase Tests extension JSONEncoderTests { - func testDecodingKeyStrategyCamel() { + @Test func testDecodingKeyStrategyCamel() { let fromSnakeCaseTests = [ ("", ""), // don't die on empty string ("a", "a"), // single character @@ -2016,11 +2159,11 @@ extension JSONEncoderTests { let result = try! decoder.decode(DecodeMe.self, from: input) - XCTAssertTrue(result.found) + #expect(result.found) } } - func testEncodingDictionaryStringKeyConversionUntouched() { + @Test func testEncodingDictionaryStringKeyConversionUntouched() { let expected = "{\"leaveMeAlone\":\"test\"}" let toEncode: [String: String] = ["leaveMeAlone": "test"] @@ -2029,10 +2172,10 @@ extension JSONEncoderTests { let resultData = try! encoder.encode(toEncode) let resultString = String(bytes: resultData, encoding: String._Encoding.utf8) - XCTAssertEqual(expected, resultString) + #expect(expected == resultString) } - func testKeyStrategySnakeGeneratedAndCustom() { + @Test func testKeyStrategySnakeGeneratedAndCustom() { // Test that this works with a struct that has automatically generated keys struct DecodeMe4 : Codable { var thisIsCamelCase : String @@ -2049,8 +2192,8 @@ extension JSONEncoderTests { decoder.keyDecodingStrategy = .convertFromSnakeCase let decodingResult = try! decoder.decode(DecodeMe4.self, from: input) - XCTAssertEqual("test", decodingResult.thisIsCamelCase) - XCTAssertEqual("test2", decodingResult.thisIsCamelCaseToo) + #expect("test" == decodingResult.thisIsCamelCase) + #expect("test2" == decodingResult.thisIsCamelCaseToo) // Encoding let encoded = DecodeMe4(thisIsCamelCase: "test", thisIsCamelCaseToo: "test2") @@ -2058,58 +2201,58 @@ extension JSONEncoderTests { encoder.keyEncodingStrategy = .convertToSnakeCase 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")) + #expect(encodingResultString!.contains("foo_bar")) + #expect(encodingResultString!.contains("this_is_camel_case_too")) } - func testDecodingDictionaryFailureKeyPathNested() { + @Test 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) { - 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) + #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) } catch { - XCTFail("Unexpected error: \(String(describing: error))") + Issue.record(error, "Unexpected error: \(String(describing: error))") } } - func testDecodingKeyStrategyCamelGenerated() { + @Test 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: String._Encoding.utf8) - XCTAssertEqual("{\"this_is_camel_case\":\"test\"}", resultString) + #expect("{\"this_is_camel_case\":\"test\"}" == resultString) } - func testDecodingStringExpectedType() { + @Test func testDecodingStringExpectedType() { let input = #"{"thisIsCamelCase": null}"#.data(using: String._Encoding.utf8)! do { _ = try JSONDecoder().decode(DecodeMe3.self, from: input) } catch DecodingError.valueNotFound(let expected, _) { - XCTAssertTrue(expected == String.self) + #expect(expected == String.self) } catch { - XCTFail("Unexpected error: \(String(describing: error))") + Issue.record(error, "Unexpected error: \(String(describing: error))") } } - func testEncodingKeyStrategySnakeGenerated() { + @Test func testEncodingKeyStrategySnakeGenerated() { // Test that this works with a struct that has automatically generated keys 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) - XCTAssertEqual("test", result.thisIsCamelCase) + #expect("test" == result.thisIsCamelCase) } - func testEncodingDictionaryFailureKeyPath() { + @Test func testEncodingDictionaryFailureKeyPath() { let toEncode: [String: EncodeFailure] = ["key": EncodeFailure(someValue: Double.nan)] let encoder = JSONEncoder() @@ -2117,15 +2260,15 @@ extension JSONEncoderTests { do { _ = try encoder.encode(toEncode) } catch EncodingError.invalidValue(_, let context) { - XCTAssertEqual(2, context.codingPath.count) - XCTAssertEqual("key", context.codingPath[0].stringValue) - XCTAssertEqual("someValue", context.codingPath[1].stringValue) + #expect(2 == context.codingPath.count) + #expect("key" == context.codingPath[0].stringValue) + #expect("someValue" == context.codingPath[1].stringValue) } catch { - XCTFail("Unexpected error: \(String(describing: error))") + Issue.record(error, "Unexpected error: \(String(describing: error))") } } - func testEncodingDictionaryFailureKeyPathNested() { + @Test func testEncodingDictionaryFailureKeyPathNested() { let toEncode: [String: [String: EncodeFailureNested]] = ["key": ["sub_key": EncodeFailureNested(nestedValue: EncodeFailure(someValue: Double.nan))]] let encoder = JSONEncoder() @@ -2133,17 +2276,17 @@ extension JSONEncoderTests { do { _ = try encoder.encode(toEncode) } catch EncodingError.invalidValue(_, let context) { - 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) + #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) } catch { - XCTFail("Unexpected error: \(String(describing: error))") + Issue.record(error, "Unexpected error: \(String(describing: error))") } } - func testEncodingKeyStrategySnake() { + @Test func testEncodingKeyStrategySnake() { let toSnakeCaseTests = [ ("simpleOneTwo", "simple_one_two"), ("myURL", "my_url"), @@ -2185,16 +2328,25 @@ extension JSONEncoderTests { let resultData = try! encoder.encode(encoded) let resultString = String(bytes: resultData, encoding: String._Encoding.utf8) - XCTAssertEqual(expected, resultString) + #expect(expected == resultString) } } - func test_twoByteUTF16Inputs() { + @Test func test_twoByteUTF16Inputs() throws { let json = "7" let decoder = JSONDecoder() - XCTAssertEqual(7, try decoder.decode(Int.self, from: json.data(using: .utf16BigEndian)!)) - XCTAssertEqual(7, try decoder.decode(Int.self, from: json.data(using: .utf16LittleEndian)!)) + var result = try decoder.decode( + Int.self, + from: json.data(using: .utf16BigEndian)! + ) + #expect(7 == result) + result = 0 + result = try decoder.decode( + Int.self, + from: json.data(using: .utf16LittleEndian)! + ) + #expect(7 == result) } private func _run_passTest(name: String, json5: Bool = false, type: T.Type) { @@ -2207,7 +2359,7 @@ extension JSONEncoderTests { do { decoded = try decoder.decode(T.self, from: jsonData) } catch { - XCTFail("Pass test \"\(name)\" failed with error: \(error)") + Issue.record(error, "Pass test \"\(name)\" failed with error: \(error)") return } @@ -2217,16 +2369,16 @@ extension JSONEncoderTests { for encoder in [JSONEncoder(), prettyPrintEncoder] { let reencodedData = try! encoder.encode(decoded) let redecodedObjects = try! decoder.decode(T.self, from: reencodedData) - XCTAssertEqual(decoded, redecodedObjects) + #expect(decoded == redecodedObjects) if let plistData { let decodedPlistObjects = try! PropertyListDecoder().decode(T.self, from: plistData) - XCTAssertEqual(decoded, decodedPlistObjects) + #expect(decoded == decodedPlistObjects) } } } - func test_JSONPassTests() { + @Test 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) @@ -2248,7 +2400,7 @@ extension JSONEncoderTests { _run_passTest(name: "pass15", type: JSONPass.Test15.self) } - func test_json5PassJSONFiles() { + @Test 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) @@ -2263,13 +2415,11 @@ extension JSONEncoderTests { decoder.assumesTopLevelDictionary = true 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) - } + Issue.record("Decoding should have failed for invalid JSON data (test name: \(name))") + } catch { } } - func test_JSONFailTests() { + @Test 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) @@ -2324,47 +2474,53 @@ extension JSONEncoderTests { switch testType { case .json, .json5_foundationPermissiveJSON: // Valid JSON should remain valid JSON5 - XCTAssertNoThrow(try json5.decode(type, from: jsonData)) + #expect(throws: Never.self) { + try json5.decode(type, from: jsonData) + } // Repeat with non-JSON5-compliant decoder. - XCTAssertNoThrow(try json.decode(type, from: jsonData)) + #expect(throws: Never.self) { + try json.decode(type, from: jsonData) + } case .json5: - XCTAssertNoThrow(try json5.decode(type, from: jsonData)) + #expect(throws: Never.self) { + try json5.decode(type, from: jsonData) + } // Regular JSON decoder should throw. do { let val = try json.decode(type, from: jsonData) - XCTFail("Expected decode failure (original JSON)for test \(name).\(ext), but got: \(val)") + Issue.record("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) - XCTFail("Expected decode failure (JSON5) for test \(name).\(ext), but got: \(val)") + Issue.record("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) - XCTFail("Expected decode failure (original JSON) for test \(name).\(ext), but got: \(val)") + Issue.record("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) - XCTFail("Expected decode failure (JSON5) for test \(name).\(ext), but got: \(val)") + Issue.record("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) - XCTFail("Expected decode failure (original JSON) for test \(name).\(ext), but got: \(val)") + Issue.record("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. - func test_json5Spec() { + @Test 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) @@ -2491,7 +2647,7 @@ extension JSONEncoderTests { } - func testEncodingDateISO8601() { + @Test func testEncodingDateISO8601() { let timestamp = Date(timeIntervalSince1970: 1000) let expectedJSON = "\"\(timestamp.formatted(.iso8601))\"".data(using: String._Encoding.utf8)! @@ -2508,7 +2664,7 @@ extension JSONEncoderTests { dateDecodingStrategy: .iso8601) } - func testEncodingDataBase64() { + @Test func testEncodingDataBase64() { let data = Data([0xDE, 0xAD, 0xBE, 0xEF]) let expectedJSON = "\"3q2+7w==\"".data(using: String._Encoding.utf8)! @@ -2524,7 +2680,7 @@ extension JSONEncoderTests { #if FOUNDATION_FRAMEWORK extension JSONEncoderTests { // This will remain a framework-only test due to dependence on `DateFormatter`. - func testEncodingDateFormatted() { + @Test func testEncodingDateFormatted() { let formatter = DateFormatter() formatter.dateStyle = .full formatter.timeStyle = .full @@ -2548,26 +2704,26 @@ extension JSONEncoderTests { // MARK: - .sortedKeys Tests // TODO: Reenable these tests once .sortedKeys is implemented extension JSONEncoderTests { - func testEncodingTopLevelStructuredClass() { + @Test func testEncodingTopLevelStructuredClass() { // Person is a class with multiple fields. 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]) } - func testEncodingOutputFormattingSortedKeys() { + @Test 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]) } - func testEncodingOutputFormattingPrettyPrintedSortedKeys() { + @Test 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]) } - func testEncodingSortedKeys() { + @Test 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 = [ @@ -2592,7 +2748,7 @@ extension JSONEncoderTests { _testRoundTrip(of: dict, expectedJSON: "{\"bar\":10,\"foo\":3,\"Foo\":1,\"FOO\":2,\"føo\":9,\"foo1\":4,\"Foo2\":5,\"foo3\":6,\"Foo11\":8,\"foo12\":7}".data(using: String._Encoding.utf8)!, outputFormatting: [.sortedKeys]) } - func testEncodingSortedKeysStableOrdering() { + @Test 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}" @@ -2624,7 +2780,7 @@ extension JSONEncoderTests { } // TODO: Reenable once .sortedKeys is implemented - func testEncodingMultipleNestedContainersWithTheSameTopLevelKey() { + @Test func testEncodingMultipleNestedContainersWithTheSameTopLevelKey() { struct Model : Codable, Equatable { let first: String let second: String @@ -2676,7 +2832,7 @@ extension JSONEncoderTests { _testRoundTrip(of: model, expectedJSON: expectedJSON, outputFormatting: [.sortedKeys]) } - func test_redundantKeyedContainer() { + @Test func test_redundantKeyedContainer() { struct EncodesTwice: Encodable { enum CodingKeys: String, CodingKey { case container @@ -2705,10 +2861,11 @@ extension JSONEncoderTests { let data = try! encoder.encode(EncodesTwice()) let string = String(data: data, encoding: .utf8)! - XCTAssertEqual(string, "{\"container\":{\"foo\":\"Test\",\"somethingElse\":\"SecondAgain\"},\"somethingElse\":\"Foo\"}") + #expect(string == "{\"container\":{\"foo\":\"Test\",\"somethingElse\":\"SecondAgain\"},\"somethingElse\":\"Foo\"}" + ) } - func test_singleValueDictionaryAmendedByContainer() { + @Test func test_singleValueDictionaryAmendedByContainer() { struct Test: Encodable { enum CodingKeys: String, CodingKey { case a @@ -2727,14 +2884,14 @@ extension JSONEncoderTests { let data = try! encoder.encode(Test()) let string = String(data: data, encoding: .utf8)! - XCTAssertEqual(string, "{\"a\":\"c\",\"other\":\"foo\"}") + #expect(string == "{\"a\":\"c\",\"other\":\"foo\"}") } } // MARK: - Decimal Tests // TODO: Reenable these tests once Decimal is moved extension JSONEncoderTests { - func testInterceptDecimal() { + @Test 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. @@ -2746,16 +2903,16 @@ extension JSONEncoderTests { _testRoundTrip(of: Optional(decimal), expectedJSON: expectedJSON) } - func test_hugeNumbers() { + @Test func test_hugeNumbers() { let json = "23456789012000000000000000000000000000000000000000000000000000000000000000000 " let data = json.data(using: String._Encoding.utf8)! let decimal = try! JSONDecoder().decode(Decimal.self, from: data) let expected = Decimal(string: json) - XCTAssertEqual(decimal, expected) + #expect(decimal == expected) } - func testInterceptLargeDecimal() { + @Test func testInterceptLargeDecimal() { struct TestBigDecimal: Codable, Equatable { var uint64Max: Decimal = Decimal(UInt64.max) var unit64MaxPlus1: Decimal = Decimal(UInt64.max) + Decimal(1) @@ -2771,7 +2928,7 @@ extension JSONEncoderTests { // MARK: - URL Tests // TODO: Reenable these tests once URL is moved extension JSONEncoderTests { - func testInterceptURL() { + @Test func testInterceptURL() { // Want to make sure JSONEncoder writes out single-value URLs, not the keyed encoding. let expectedJSON = "\"http:\\/\\/swift.org\"".data(using: String._Encoding.utf8)! let url = URL(string: "http://swift.org")! @@ -2781,7 +2938,7 @@ extension JSONEncoderTests { _testRoundTrip(of: Optional(url), expectedJSON: expectedJSON) } - func testInterceptURLWithoutEscapingOption() { + @Test func testInterceptURLWithoutEscapingOption() { // Want to make sure JSONEncoder writes out single-value URLs, not the keyed encoding. let expectedJSON = "\"http://swift.org\"".data(using: String._Encoding.utf8)! let url = URL(string: "http://swift.org")! @@ -2795,29 +2952,32 @@ extension JSONEncoderTests { // MARK: - Helper Global Functions 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): - 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))") + if lhs.count != rhs.count { + Issue.record("\(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") return - } + case (.none, .some(let i2)): + Issue.record("\(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))") + return + } } - XCTAssertEqual(key1.stringValue, key2.stringValue, "\(prefix) CodingKey.stringValue mismatch: \(type(of: key1))('\(key1.stringValue)') != \(type(of: key2))('\(key2.stringValue)')") - } + #expect( + key1.stringValue == key2.stringValue, + "\(prefix) CodingKey.stringValue mismatch: \(type(of: key1))('\(key1.stringValue)') != \(type(of: key2))('\(key2.stringValue)')" + ) + } } // MARK: - Test Types @@ -2825,392 +2985,392 @@ func expectEqualPaths(_ lhs: [CodingKey], _ rhs: [CodingKey], _ prefix: String) // MARK: - Empty Types fileprivate struct EmptyStruct : Codable, Equatable { - static func ==(_ lhs: EmptyStruct, _ rhs: EmptyStruct) -> Bool { - return true - } + static func ==(_ lhs: EmptyStruct, _ rhs: EmptyStruct) -> Bool { + return true + } } fileprivate class EmptyClass : Codable, Equatable { - static func ==(_ lhs: EmptyClass, _ rhs: EmptyClass) -> Bool { - return true - } + static func ==(_ lhs: EmptyClass, _ rhs: EmptyClass) -> Bool { + return true + } } // MARK: - Single-Value Types /// A simple on-off switch type that encodes as a single Bool value. fileprivate enum Switch : Codable { - case off - case on + case off + case on - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - switch try container.decode(Bool.self) { - case false: self = .off - case true: self = .on + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + switch try container.decode(Bool.self) { + case false: self = .off + case true: self = .on + } } - } - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - switch self { - case .off: try container.encode(false) - case .on: try container.encode(true) + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case .off: try container.encode(false) + case .on: try container.encode(true) + } } - } } /// A simple timestamp type that encodes as a single Double value. fileprivate struct Timestamp : Codable, Equatable { - let value: Double + let value: Double - init(_ value: Double) { - self.value = value - } + init(_ value: Double) { + self.value = value + } - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - value = try container.decode(Double.self) - } + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + value = try container.decode(Double.self) + } - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.value) - } + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.value) + } - static func ==(_ lhs: Timestamp, _ rhs: Timestamp) -> Bool { - return lhs.value == rhs.value - } + static func ==(_ lhs: Timestamp, _ rhs: Timestamp) -> Bool { + return lhs.value == rhs.value + } } /// A simple referential counter type that encodes as a single Int value. fileprivate final class Counter : Codable, Equatable { - var count: Int = 0 + var count: Int = 0 - init() {} + init() {} - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - count = try container.decode(Int.self) - } + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + count = try container.decode(Int.self) + } - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.count) - } + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.count) + } - static func ==(_ lhs: Counter, _ rhs: Counter) -> Bool { - return lhs === rhs || lhs.count == rhs.count - } + static func ==(_ lhs: Counter, _ rhs: Counter) -> Bool { + return lhs === rhs || lhs.count == rhs.count + } } // MARK: - Structured Types /// A simple address type that encodes as a dictionary of values. fileprivate struct Address : Codable, Equatable { - let street: String - let city: String - let state: String - let zipCode: Int - let country: String - - init(street: String, city: String, state: String, zipCode: Int, country: String) { - self.street = street - self.city = city - self.state = state - self.zipCode = zipCode - self.country = country - } - - static func ==(_ lhs: Address, _ rhs: Address) -> Bool { - return lhs.street == rhs.street && - lhs.city == rhs.city && - lhs.state == rhs.state && - lhs.zipCode == rhs.zipCode && - lhs.country == rhs.country - } - - static var testValue: Address { - return Address(street: "1 Infinite Loop", - city: "Cupertino", - state: "CA", - zipCode: 95014, - country: "United States") - } + let street: String + let city: String + let state: String + let zipCode: Int + let country: String + + init(street: String, city: String, state: String, zipCode: Int, country: String) { + self.street = street + self.city = city + self.state = state + self.zipCode = zipCode + self.country = country + } + + static func ==(_ lhs: Address, _ rhs: Address) -> Bool { + return lhs.street == rhs.street && + lhs.city == rhs.city && + lhs.state == rhs.state && + lhs.zipCode == rhs.zipCode && + lhs.country == rhs.country + } + + static var testValue: Address { + return Address(street: "1 Infinite Loop", + city: "Cupertino", + state: "CA", + zipCode: 95014, + country: "United States") + } } /// A simple person class that encodes as a dictionary of values. fileprivate class Person : Codable, Equatable { - let name: String - let email: String + let name: String + let email: String #if FOUNDATION_FRAMEWORK - let website: URL? + let website: URL? - init(name: String, email: String, website: URL? = nil) { - self.name = name - self.email = email - self.website = website - } + init(name: String, email: String, website: URL? = nil) { + self.name = name + self.email = email + self.website = website + } #else - init(name: String, email: String) { - self.name = name - self.email = email - } + init(name: String, email: String) { + self.name = name + self.email = email + } #endif - func isEqual(_ other: Person) -> Bool { + func isEqual(_ other: Person) -> Bool { #if FOUNDATION_FRAMEWORK - return self.name == other.name && - self.email == other.email && - self.website == other.website + return self.name == other.name && + self.email == other.email && + self.website == other.website #else - return self.name == other.name && - self.email == other.email + return self.name == other.name && + self.email == other.email #endif - } + } - static func ==(_ lhs: Person, _ rhs: Person) -> Bool { - return lhs.isEqual(rhs) - } + static func ==(_ lhs: Person, _ rhs: Person) -> Bool { + return lhs.isEqual(rhs) + } - class var testValue: Person { - return Person(name: "Johnny Appleseed", email: "appleseed@apple.com") - } + class var testValue: Person { + return Person(name: "Johnny Appleseed", email: "appleseed@apple.com") + } } /// A class which shares its encoder and decoder with its superclass. fileprivate class Employee : Person { - let id: Int + let id: Int #if FOUNDATION_FRAMEWORK - init(name: String, email: String, website: URL? = nil, id: Int) { - self.id = id - super.init(name: name, email: email, website: website) - } + init(name: String, email: String, website: URL? = nil, id: Int) { + self.id = id + super.init(name: name, email: email, website: website) + } #else - init(name: String, email: String, id: Int) { - self.id = id - super.init(name: name, email: email) - } + init(name: String, email: String, id: Int) { + self.id = id + super.init(name: name, email: email) + } #endif - enum CodingKeys : String, CodingKey { - case id - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - id = try container.decode(Int.self, forKey: .id) - try super.init(from: decoder) - } + enum CodingKeys : String, CodingKey { + case id + } - override func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(id, forKey: .id) - try super.encode(to: encoder) - } + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + id = try container.decode(Int.self, forKey: .id) + try super.init(from: decoder) + } - override func isEqual(_ other: Person) -> Bool { - if let employee = other as? Employee { - guard self.id == employee.id else { return false } + override func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(id, forKey: .id) + try super.encode(to: encoder) } - return super.isEqual(other) - } + override func isEqual(_ other: Person) -> Bool { + if let employee = other as? Employee { + guard self.id == employee.id else { return false } + } + + return super.isEqual(other) + } - override class var testValue: Employee { - return Employee(name: "Johnny Appleseed", email: "appleseed@apple.com", id: 42) - } + override class var testValue: Employee { + return Employee(name: "Johnny Appleseed", email: "appleseed@apple.com", id: 42) + } } /// A simple company struct which encodes as a dictionary of nested values. fileprivate struct Company : Codable, Equatable { - let address: Address - var employees: [Employee] + let address: Address + var employees: [Employee] - init(address: Address, employees: [Employee]) { - self.address = address - self.employees = employees - } + init(address: Address, employees: [Employee]) { + self.address = address + self.employees = employees + } - static func ==(_ lhs: Company, _ rhs: Company) -> Bool { - return lhs.address == rhs.address && lhs.employees == rhs.employees - } + static func ==(_ lhs: Company, _ rhs: Company) -> Bool { + return lhs.address == rhs.address && lhs.employees == rhs.employees + } - static var testValue: Company { - return Company(address: Address.testValue, employees: [Employee.testValue]) - } + static var testValue: Company { + return Company(address: Address.testValue, employees: [Employee.testValue]) + } } /// An enum type which decodes from Bool?. fileprivate enum EnhancedBool : Codable { - case `true` - case `false` - case fileNotFound - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - if container.decodeNil() { - self = .fileNotFound - } else { - let value = try container.decode(Bool.self) - self = value ? .true : .false - } - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - switch self { - case .true: try container.encode(true) - case .false: try container.encode(false) - case .fileNotFound: try container.encodeNil() - } - } + case `true` + case `false` + case fileNotFound + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if container.decodeNil() { + self = .fileNotFound + } else { + let value = try container.decode(Bool.self) + self = value ? .true : .false + } + } + + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case .true: try container.encode(true) + case .false: try container.encode(false) + case .fileNotFound: try container.encodeNil() + } + } } /// A type which encodes as an array directly through a single value container. private struct Numbers : Codable, Equatable { - let values = [4, 8, 15, 16, 23, 42] + let values = [4, 8, 15, 16, 23, 42] - init() {} + init() {} - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let decodedValues = try container.decode([Int].self) - guard decodedValues == values else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "The Numbers are wrong!")) + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let decodedValues = try container.decode([Int].self) + guard decodedValues == values else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "The Numbers are wrong!")) + } } - } - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(values) - } + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(values) + } - static func ==(_ lhs: Numbers, _ rhs: Numbers) -> Bool { - return lhs.values == rhs.values - } + static func ==(_ lhs: Numbers, _ rhs: Numbers) -> Bool { + return lhs.values == rhs.values + } - static var testValue: Numbers { - return Numbers() - } + static var testValue: Numbers { + return Numbers() + } } /// A type which encodes as a dictionary directly through a single value container. fileprivate final class Mapping : Codable, Equatable { - let values: [String : Int] - - init(values: [String : Int]) { - self.values = values - } - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - values = try container.decode([String : Int].self) - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(values) - } - - static func ==(_ lhs: Mapping, _ rhs: Mapping) -> Bool { - return lhs === rhs || lhs.values == rhs.values - } - - static var testValue: Mapping { - return Mapping(values: ["Apple": 42, - "localhost": 127]) - } + let values: [String : Int] + + init(values: [String : Int]) { + self.values = values + } + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + values = try container.decode([String : Int].self) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(values) + } + + static func ==(_ lhs: Mapping, _ rhs: Mapping) -> Bool { + return lhs === rhs || lhs.values == rhs.values + } + + static var testValue: Mapping { + return Mapping(values: ["Apple": 42, + "localhost": 127]) + } } private struct NestedContainersTestType : Encodable { - let testSuperEncoder: Bool - - init(testSuperEncoder: Bool = false) { - self.testSuperEncoder = testSuperEncoder - } - - enum TopLevelCodingKeys : Int, CodingKey { - case a - case b - case c - } - - enum IntermediateCodingKeys : Int, CodingKey { - case one - case two - } - - func encode(to encoder: Encoder) throws { - if self.testSuperEncoder { - var topLevelContainer = encoder.container(keyedBy: TopLevelCodingKeys.self) - expectEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") - expectEqualPaths(topLevelContainer.codingPath, [], "New first-level keyed container has non-empty codingPath.") - - let superEncoder = topLevelContainer.superEncoder(forKey: .a) - expectEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") - expectEqualPaths(topLevelContainer.codingPath, [], "First-level keyed container's codingPath changed.") - expectEqualPaths(superEncoder.codingPath, [TopLevelCodingKeys.a], "New superEncoder had unexpected codingPath.") - _testNestedContainers(in: superEncoder, baseCodingPath: [TopLevelCodingKeys.a]) - } else { - _testNestedContainers(in: encoder, baseCodingPath: []) - } - } - - func _testNestedContainers(in encoder: Encoder, baseCodingPath: [CodingKey]) { - expectEqualPaths(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) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(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) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(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) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") - expectEqualPaths(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) - expectEqualPaths(encoder.codingPath, baseCodingPath + [], "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath + [], "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") - expectEqualPaths(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) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(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) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") - expectEqualPaths(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() - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") - expectEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 1)], "New third-level unkeyed container had unexpected codingPath.") - } - } + let testSuperEncoder: Bool + + init(testSuperEncoder: Bool = false) { + self.testSuperEncoder = testSuperEncoder + } + + enum TopLevelCodingKeys : Int, CodingKey { + case a + case b + case c + } + + enum IntermediateCodingKeys : Int, CodingKey { + case one + case two + } + + func encode(to encoder: Encoder) throws { + if self.testSuperEncoder { + var topLevelContainer = encoder.container(keyedBy: TopLevelCodingKeys.self) + expectEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") + expectEqualPaths(topLevelContainer.codingPath, [], "New first-level keyed container has non-empty codingPath.") + + let superEncoder = topLevelContainer.superEncoder(forKey: .a) + expectEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") + expectEqualPaths(topLevelContainer.codingPath, [], "First-level keyed container's codingPath changed.") + expectEqualPaths(superEncoder.codingPath, [TopLevelCodingKeys.a], "New superEncoder had unexpected codingPath.") + _testNestedContainers(in: superEncoder, baseCodingPath: [TopLevelCodingKeys.a]) + } else { + _testNestedContainers(in: encoder, baseCodingPath: []) + } + } + + func _testNestedContainers(in encoder: Encoder, baseCodingPath: [CodingKey]) { + expectEqualPaths(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) + expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + expectEqualPaths(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) + expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") + expectEqualPaths(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) + expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") + expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") + expectEqualPaths(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) + expectEqualPaths(encoder.codingPath, baseCodingPath + [], "Top-level Encoder's codingPath changed.") + expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath + [], "First-level keyed container's codingPath changed.") + expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") + expectEqualPaths(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) + expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") + expectEqualPaths(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) + expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") + expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") + expectEqualPaths(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() + expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") + expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") + expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") + expectEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 1)], "New third-level unkeyed container had unexpected codingPath.") + } + } } private struct CodableTypeWithConfiguration : CodableWithConfiguration, Equatable { @@ -3254,79 +3414,79 @@ private struct CodableTypeWithConfiguration : CodableWithConfiguration, Equatabl /// A key type which can take on any string or integer value. /// This needs to mirror _CodingKey. fileprivate struct _TestKey : CodingKey { - var stringValue: String - var intValue: Int? - - init?(stringValue: String) { - self.stringValue = stringValue - self.intValue = nil - } - - init?(intValue: Int) { - self.stringValue = "\(intValue)" - self.intValue = intValue - } - - init(index: Int) { - self.stringValue = "Index \(index)" - self.intValue = index - } + var stringValue: String + var intValue: Int? + + init?(stringValue: String) { + self.stringValue = stringValue + self.intValue = nil + } + + init?(intValue: Int) { + self.stringValue = "\(intValue)" + self.intValue = intValue + } + + init(index: Int) { + self.stringValue = "Index \(index)" + self.intValue = index + } } fileprivate struct FloatNaNPlaceholder : Codable, Equatable { - init() {} + init() {} - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(Float.nan) - } + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(Float.nan) + } - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let float = try container.decode(Float.self) - if !float.isNaN { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Couldn't decode NaN.")) + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let float = try container.decode(Float.self) + if !float.isNaN { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Couldn't decode NaN.")) + } } - } - static func ==(_ lhs: FloatNaNPlaceholder, _ rhs: FloatNaNPlaceholder) -> Bool { - return true - } + static func ==(_ lhs: FloatNaNPlaceholder, _ rhs: FloatNaNPlaceholder) -> Bool { + return true + } } fileprivate struct DoubleNaNPlaceholder : Codable, Equatable { - init() {} + init() {} - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(Double.nan) - } + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(Double.nan) + } - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let double = try container.decode(Double.self) - if !double.isNaN { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Couldn't decode NaN.")) + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let double = try container.decode(Double.self) + if !double.isNaN { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Couldn't decode NaN.")) + } } - } - static func ==(_ lhs: DoubleNaNPlaceholder, _ rhs: DoubleNaNPlaceholder) -> Bool { - return true - } + static func ==(_ lhs: DoubleNaNPlaceholder, _ rhs: DoubleNaNPlaceholder) -> Bool { + return true + } } fileprivate enum EitherDecodable : Decodable { - case t(T) - case u(U) + case t(T) + case u(U) - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - do { - self = .t(try container.decode(T.self)) - } catch { - self = .u(try container.decode(U.self)) + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + do { + self = .t(try container.decode(T.self)) + } catch { + self = .u(try container.decode(U.self)) + } } - } } struct NullReader : Decodable, Equatable { diff --git a/Tests/FoundationEssentialsTests/LockedStateTests.swift b/Tests/FoundationEssentialsTests/LockedStateTests.swift index 0a6f714aa..7d3d736f4 100644 --- a/Tests/FoundationEssentialsTests/LockedStateTests.swift +++ b/Tests/FoundationEssentialsTests/LockedStateTests.swift @@ -10,15 +10,15 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing -#if canImport(FoundationEssentials) +#if FOUNDATION_FRAMEWORK +@testable import Foundation +#else @testable import FoundationEssentials #endif -final class LockedStateTests : XCTestCase { +struct LockedStateTests { final class TestObject { var deinitBlock: () -> Void = {} @@ -29,7 +29,7 @@ final class LockedStateTests : XCTestCase { struct TestError: Error {} - func testWithLockDoesNotExtendLifetimeOfState() { + @Test func testWithLockDoesNotExtendLifetimeOfState() { weak var state: TestObject? let lockedState: LockedState @@ -41,23 +41,26 @@ final class LockedStateTests : XCTestCase { lockedState.withLock { state in weak var oldState = state state = TestObject() - XCTAssertNil(oldState, "State object lifetime was extended after reassignment within body") + #expect(oldState == nil, "State object lifetime was extended after reassignment within body") } - XCTAssertNil(state, "State object lifetime was extended beyond end of call") + #expect(state == nil, "State object lifetime was extended beyond end of call") } - func testWithLockExtendingLifetimeExtendsLifetimeOfStatePastReassignment() { + @Test func testWithLockExtendingLifetimeExtendsLifetimeOfStatePastReassignment() { let lockedState = LockedState(initialState: TestObject()) lockedState.withLockExtendingLifetimeOfState { state in weak var oldState = state state = TestObject() - XCTAssertNotNil(oldState, "State object lifetime was not extended after reassignment within body") + #expect( + oldState != nil, + "State object lifetime was not extended after reassignment within body" + ) } } - func testWithLockExtendingLifetimeExtendsLifetimeOfStatePastEndOfLockedScope() { + @Test func testWithLockExtendingLifetimeExtendsLifetimeOfStatePastEndOfLockedScope() { let lockedState: LockedState = { let state = TestObject() let lockedState = LockedState(initialState: state) @@ -77,7 +80,7 @@ final class LockedStateTests : XCTestCase { } } - func testWithLockExtendingLifetimeDoesNotExtendLifetimeOfStatePastEndOfCall() { + @Test func testWithLockExtendingLifetimeDoesNotExtendLifetimeOfStatePastEndOfCall() { weak var state: TestObject? let lockedState: LockedState @@ -90,18 +93,17 @@ final class LockedStateTests : XCTestCase { state = TestObject() } - XCTAssertNil(state, "State object lifetime was extended beyond end of call") + #expect(state == nil, "State object lifetime was extended beyond end of call") } - func testWithLockExtendingLifetimeReleasesLockWhenBodyThrows() { + @Test func testWithLockExtendingLifetimeReleasesLockWhenBodyThrows() { let lockedState = LockedState(initialState: TestObject()) - XCTAssertThrowsError( + #expect(throws: (any Error).self, "The body was expected to throw an error, but it did not.") { try lockedState.withLockExtendingLifetimeOfState { _ in throw TestError() - }, - "The body was expected to throw an error, but it did not." - ) + } + } assertLockNotHeld(lockedState, "Lock was not properly released by withLockExtendingLifetimeOfState()") } diff --git a/Tests/FoundationEssentialsTests/PredicateCodableTests.swift b/Tests/FoundationEssentialsTests/PredicateCodableTests.swift index 7092a6590..2b8f0e701 100644 --- a/Tests/FoundationEssentialsTests/PredicateCodableTests.swift +++ b/Tests/FoundationEssentialsTests/PredicateCodableTests.swift @@ -10,13 +10,10 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -@_spi(Expression) import TestSupport -#endif - #if FOUNDATION_FRAMEWORK @_spi(Expression) import Foundation +import Testing fileprivate protocol PredicateCodingConfigurationProviding : EncodingConfigurationProviding, DecodingConfigurationProviding where EncodingConfiguration == PredicateCodableConfiguration, DecodingConfiguration == PredicateCodableConfiguration { static var config: PredicateCodableConfiguration { get } @@ -60,8 +57,8 @@ extension PredicateExpressions { } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) -final class PredicateCodableTests: XCTestCase { - +struct PredicateCodableTests { + struct Object : Equatable, PredicateCodableKeyPathProviding { var a: Int var b: String @@ -195,163 +192,235 @@ final class PredicateCodableTests: XCTestCase { return try decoder.decode(T.self, from: data) } - func testBasicEncodeDecode() throws { + @Test func testBasicEncodeDecode() throws { let predicate = #Predicate { $0.a == 2 } let decoded = try _encodeDecode(predicate, for: StandardConfig.self) var object = Object.example - XCTAssertEqual(try predicate.evaluate(object), try decoded.evaluate(object)) + var predicateResult = try predicate.evaluate(object) + var decodedResult = try decoded.evaluate(object) + #expect(predicateResult == decodedResult) object.a = 2 - XCTAssertEqual(try predicate.evaluate(object), try decoded.evaluate(object)) + predicateResult = try predicate.evaluate(object) + decodedResult = try decoded.evaluate(object) + #expect(predicateResult == decodedResult) object.a = 3 - XCTAssertEqual(try predicate.evaluate(object), try decoded.evaluate(object)) - - XCTAssertThrowsError(try _encodeDecode(predicate, for: EmptyConfig.self)) - XCTAssertThrowsError(try _encodeDecode(predicate)) + predicateResult = try predicate.evaluate(object) + decodedResult = try decoded.evaluate(object) + #expect(predicateResult == decodedResult) + + #expect(throws: (any Error).self) { + try _encodeDecode(predicate, for: EmptyConfig.self) + } + #expect(throws: (any Error).self) { + try _encodeDecode(predicate) + } } - func testDisallowedKeyPath() throws { + @Test func testDisallowedKeyPath() throws { var predicate = #Predicate { $0.f } - - XCTAssertThrowsError(try _encodeDecode(predicate)) - XCTAssertThrowsError(try _encodeDecode(predicate, for: StandardConfig.self)) - + + #expect(throws: (any Error).self) { + try _encodeDecode(predicate) + } + #expect(throws: (any Error).self) { + try _encodeDecode(predicate, for: StandardConfig.self) + } + predicate = #Predicate { $0.a == 1 } - XCTAssertThrowsError(try _encodeDecode(predicate, encoding: StandardConfig.self, decoding: MinimalConfig.self)) { - guard let decodingError = $0 as? DecodingError else { - XCTFail("Incorrect error thrown: \($0)") - return + #expect { + try _encodeDecode(predicate, encoding: StandardConfig.self, decoding: MinimalConfig.self) + } throws: { error in + guard let decodingError = error as? DecodingError else { + Issue.record("Incorrect error thrown: \(error)") + return false } - XCTAssertEqual(decodingError.debugDescription, "A keypath for the 'Object.a' identifier is not in the provided allowlist") + #expect( + decodingError.debugDescription == + "A keypath for the 'Object.a' identifier is not in the provided allowlist" + ) + return true } } - func testKeyPathTypeMismatch() throws { + @Test func testKeyPathTypeMismatch() throws { let predicate = #Predicate { $0.a == 2 } try _encodeDecode(predicate, for: StandardConfig.self) - XCTAssertThrowsError(try _encodeDecode(predicate, encoding: StandardConfig.self, decoding: MismatchedKeyPathConfig.self)) { - guard let decodingError = $0 as? DecodingError else { - XCTFail("Incorrect error thrown: \($0)") - return + #expect { + try _encodeDecode( + predicate, + encoding: StandardConfig.self, + decoding: MismatchedKeyPathConfig.self) + } throws: { error in + guard let decodingError = error as? DecodingError else { + Issue.record("Incorrect error thrown: \(error)") + return false } - XCTAssertEqual(decodingError.debugDescription, "Key path '\\Object.b' (KeyPath<\(_typeName(Object.self)), Swift.String>) for identifier 'Object.a' did not match the expression's requirement for KeyPath<\(_typeName(Object.self)), Swift.Int>") + #expect(decodingError.debugDescription == "Key path '\\Object.b' (KeyPath<\(_typeName(Object.self)), Swift.String>) for identifier 'Object.a' did not match the expression's requirement for KeyPath<\(_typeName(Object.self)), Swift.Int>") + return true } } - func testDisallowedType() throws { + @Test func testDisallowedType() throws { let uuid = UUID() let predicate = #Predicate { obj in uuid == uuid } - - XCTAssertThrowsError(try _encodeDecode(predicate)) - XCTAssertThrowsError(try _encodeDecode(predicate, for: StandardConfig.self)) - XCTAssertThrowsError(try _encodeDecode(predicate, encoding: UUIDConfig.self, decoding: MinimalConfig.self)) { - XCTAssertEqual(String(describing: $0), "The 'Foundation.UUID' identifier is not in the provided allowlist (required by /PredicateExpressions.Equal/PredicateExpressions.Value)") + + #expect(throws: (any Error).self) { + try _encodeDecode(predicate) + } + #expect(throws: (any Error).self) { + try _encodeDecode(predicate, for: StandardConfig.self) + } + #expect { + try _encodeDecode(predicate, encoding: UUIDConfig.self, decoding: MinimalConfig.self) + } throws: { error in + #expect( + String(describing: error) == + "The 'Foundation.UUID' identifier is not in the provided allowlist (required by /PredicateExpressions.Equal/PredicateExpressions.Value)" + ) + return true } - - let decoded = try _encodeDecode(predicate, for: UUIDConfig.self) - XCTAssertEqual(try decoded.evaluate(.example), try predicate.evaluate(.example)) + + let decoded = try _encodeDecode(predicate, for: UUIDConfig.self).evaluate(.example) + let predicateResult = try predicate.evaluate(.example) + #expect(decoded == predicateResult) } - func testProvidedProperties() throws { + @Test func testProvidedProperties() throws { var predicate = #Predicate { $0.a == 2 } - XCTAssertThrowsError(try _encodeDecode(predicate, for: ProvidedKeyPathConfig.self)) - XCTAssertThrowsError(try _encodeDecode(predicate, for: RecursiveProvidedKeyPathConfig.self)) - + #expect(throws: (any Error).self) { + try _encodeDecode(predicate, for: ProvidedKeyPathConfig.self) + } + #expect(throws: (any Error).self) { + try _encodeDecode(predicate, for: RecursiveProvidedKeyPathConfig.self) + } + predicate = #Predicate { $0.f == false } var decoded = try _encodeDecode(predicate, for: ProvidedKeyPathConfig.self) - XCTAssertEqual(try decoded.evaluate(.example), try predicate.evaluate(.example)) + var decodedResult = try decoded.evaluate(.example) + var predicateResult = try predicate.evaluate(.example) + #expect(decodedResult == predicateResult) decoded = try _encodeDecode(predicate, for: RecursiveProvidedKeyPathConfig.self) - XCTAssertEqual(try decoded.evaluate(.example), try predicate.evaluate(.example)) - + decodedResult = try decoded.evaluate(.example) + predicateResult = try predicate.evaluate(.example) + #expect(decodedResult == predicateResult) + predicate = #Predicate { $0.h.a == 1 } - - XCTAssertThrowsError(try _encodeDecode(predicate, for: ProvidedKeyPathConfig.self)) + + #expect(throws: (any Error).self) { + try _encodeDecode(predicate, for: ProvidedKeyPathConfig.self) + } decoded = try _encodeDecode(predicate, for: RecursiveProvidedKeyPathConfig.self) - XCTAssertEqual(try decoded.evaluate(.example), try predicate.evaluate(.example)) + decodedResult = try decoded.evaluate(.example) + predicateResult = try predicate.evaluate(.example) + #expect(decodedResult == predicateResult) } - func testDefaultAllowlist() throws { + @Test func testDefaultAllowlist() throws { var predicate = #Predicate { $0.isEmpty } var decoded = try _encodeDecode(predicate) - XCTAssertEqual(try decoded.evaluate("Hello world"), try predicate.evaluate("Hello world")) - + var decodedResult = try decoded.evaluate("Hello world") + var predicateResult = try predicate.evaluate("Hello world") + #expect(decodedResult == predicateResult) + predicate = #Predicate { $0.count > 2 } decoded = try _encodeDecode(predicate) - XCTAssertEqual(try decoded.evaluate("Hello world"), try predicate.evaluate("Hello world")) - + decodedResult = try decoded.evaluate("Hello world") + predicateResult = try predicate.evaluate("Hello world") + #expect(decodedResult == predicateResult) + predicate = #Predicate { $0.contains(/[a-z]/) } decoded = try _encodeDecode(predicate) - XCTAssertEqual(try decoded.evaluate("Hello world"), try predicate.evaluate("Hello world")) - + decodedResult = try decoded.evaluate("Hello world") + predicateResult = try predicate.evaluate("Hello world") + #expect(decodedResult == predicateResult) + let predicate2 = #Predicate { $0 == $0 } let decoded2 = try _encodeDecode(predicate2) - XCTAssertEqual(try decoded2.evaluate(.example), try predicate2.evaluate(.example)) - - + decodedResult = try decoded2.evaluate(.example) + predicateResult = try predicate2.evaluate(.example) + #expect(decodedResult == predicateResult) + var predicate3 = #Predicate> { $0.isEmpty } var decoded3 = try _encodeDecode(predicate3) - XCTAssertEqual(try decoded3.evaluate(["A", "B", "C"]), try predicate3.evaluate(["A", "B", "C"])) - + decodedResult = try decoded3.evaluate(["A", "B", "C"]) + predicateResult = try predicate3.evaluate(["A", "B", "C"]) + #expect(decodedResult == predicateResult) + predicate3 = #Predicate> { $0.count == 2 } decoded3 = try _encodeDecode(predicate3) - XCTAssertEqual(try decoded3.evaluate(["A", "B", "C"]), try predicate3.evaluate(["A", "B", "C"])) - + decodedResult = try decoded3.evaluate(["A", "B", "C"]) + predicateResult = try predicate3.evaluate(["A", "B", "C"]) + #expect(decodedResult == predicateResult) + var predicate4 = #Predicate> { $0.isEmpty } var decoded4 = try _encodeDecode(predicate4) - XCTAssertEqual(try decoded4.evaluate(["A": 1, "B": 2, "C": 3]), try predicate4.evaluate(["A": 1, "B": 2, "C": 3])) - + decodedResult = try decoded4.evaluate(["A": 1, "B": 2, "C": 3]) + predicateResult = try predicate4.evaluate(["A": 1, "B": 2, "C": 3]) + #expect(decodedResult == predicateResult) + predicate4 = #Predicate> { $0.count == 2 } decoded4 = try _encodeDecode(predicate4) - XCTAssertEqual(try decoded4.evaluate(["A": 1, "B": 2, "C": 3]), try predicate4.evaluate(["A": 1, "B": 2, "C": 3])) - + decodedResult = try decoded4.evaluate(["A": 1, "B": 2, "C": 3]) + predicateResult = try predicate4.evaluate(["A": 1, "B": 2, "C": 3]) + #expect(decodedResult == predicateResult) + let predicate5 = #Predicate { (0 ..< 4).contains($0) } let decoded5 = try _encodeDecode(predicate5) - XCTAssertEqual(try decoded5.evaluate(2), try predicate5.evaluate(2)) + decodedResult = try decoded5.evaluate(2) + predicateResult = try predicate5.evaluate(2) + #expect(decodedResult == predicateResult) } - func testMalformedData() { + @Test func testMalformedData() { func _malformedDecode(_ json: String, config: T.Type = StandardConfig.self, reason: String, file: StaticString = #file, line: UInt = #line) { let data = Data(json.utf8) let decoder = JSONDecoder() - XCTAssertThrowsError(try decoder.decode(CodableConfiguration, T>.self, from: data), file: file, line: line) { - XCTAssertTrue(String(describing: $0).contains(reason), "Error '\($0)' did not contain reason '\(reason)'", file: file, line: line) + #expect { + try decoder.decode(CodableConfiguration, T>.self, from: data) + } throws: { error in + #expect( + String(describing: error).contains(reason), + .init(rawValue: "Error '\(error)' did not contain reason '\(reason)'") + ) + return true } } @@ -427,7 +496,7 @@ final class PredicateCodableTests: XCTestCase { ) } - func testBasicVariadic() throws { + @Test func testBasicVariadic() throws { let predicate = #Predicate { $0.a == 2 && $1.a == 3 } @@ -435,17 +504,27 @@ final class PredicateCodableTests: XCTestCase { let decoded = try _encodeDecode(predicate, for: StandardConfig.self) var object = Object.example let object2 = Object.example - XCTAssertEqual(try predicate.evaluate(object, object2), try decoded.evaluate(object, object2)) + var predicateResult = try predicate.evaluate(object, object2) + var decodedResult = try decoded.evaluate(object, object2) + #expect(predicateResult == decodedResult) object.a = 2 - XCTAssertEqual(try predicate.evaluate(object, object2), try decoded.evaluate(object, object2)) + predicateResult = try predicate.evaluate(object, object2) + decodedResult = try decoded.evaluate(object, object2) + #expect(predicateResult == decodedResult) object.a = 3 - XCTAssertEqual(try predicate.evaluate(object, object2), try decoded.evaluate(object, object2)) - - XCTAssertThrowsError(try _encodeDecode(predicate, for: EmptyConfig.self)) - XCTAssertThrowsError(try _encodeDecode(predicate)) + predicateResult = try predicate.evaluate(object, object2) + decodedResult = try decoded.evaluate(object, object2) + #expect(predicateResult == decodedResult) + + #expect(throws: (any Error).self) { + try _encodeDecode(predicate, for: EmptyConfig.self) + } + #expect(throws: (any Error).self) { + try _encodeDecode(predicate) + } } - func testCapturedVariadicTypes() throws { + @Test func testCapturedVariadicTypes() throws { struct A : Equatable, Codable { init(_: repeat (each T).Type) {} @@ -478,10 +557,12 @@ final class PredicateCodableTests: XCTestCase { } let decoded = try _encodeDecode(predicate, for: CustomConfig.self) - XCTAssertEqual(try decoded.evaluate(2), try predicate.evaluate(2)) + var predicateResult = try predicate.evaluate(2) + var decodedResult = try decoded.evaluate(2) + #expect(predicateResult == decodedResult) } - func testNestedPredicates() throws { + @Test func testNestedPredicates() throws { let predicateA = #Predicate { $0.a == 3 } @@ -501,11 +582,14 @@ final class PredicateCodableTests: XCTestCase { ] for object in objects { - XCTAssertEqual(try decoded.evaluate(object), try predicateB.evaluate(object), "Evaluation failed to produce equal results for \(object)") + #expect( + try decoded.evaluate(object) == (try predicateB.evaluate(object)), + "Evaluation failed to produce equal results for \(object)" + ) } } - func testNestedPredicateRestrictedConfiguration() throws { + @Test func testNestedPredicateRestrictedConfiguration() throws { struct RestrictedBox : Codable { let predicate: Predicate @@ -544,10 +628,12 @@ final class PredicateCodableTests: XCTestCase { } // Throws an error because the sub-predicate's configuration won't contain anything in the allowlist - XCTAssertThrowsError(try _encodeDecode(predicateB, for: CustomConfig.self)) + #expect(throws: (any Error).self) { + try _encodeDecode(predicateB, for: CustomConfig.self) + } } - func testExpression() throws { + @Test func testExpression() throws { let expression = Expression() { PredicateExpressions.build_KeyPath( root: PredicateExpressions.build_Arg($0), @@ -556,14 +642,24 @@ final class PredicateCodableTests: XCTestCase { } let decoded = try _encodeDecode(expression, for: StandardConfig.self) var object = Object.example - XCTAssertEqual(try expression.evaluate(object), try decoded.evaluate(object)) + var expressionResult = try expression.evaluate(object) + var decodedResult = try decoded.evaluate(object) + #expect(expressionResult == decodedResult) object.a = 2 - XCTAssertEqual(try expression.evaluate(object), try decoded.evaluate(object)) + expressionResult = try expression.evaluate(object) + decodedResult = try decoded.evaluate(object) + #expect(expressionResult == decodedResult) object.a = 3 - XCTAssertEqual(try expression.evaluate(object), try decoded.evaluate(object)) - - XCTAssertThrowsError(try _encodeDecode(expression, for: EmptyConfig.self)) - XCTAssertThrowsError(try _encodeDecode(expression)) + expressionResult = try expression.evaluate(object) + decodedResult = try decoded.evaluate(object) + #expect(expressionResult == decodedResult) + + #expect(throws: (any Error).self) { + try _encodeDecode(expression, for: EmptyConfig.self) + } + #expect(throws: (any Error).self) { + try _encodeDecode(expression) + } } } diff --git a/Tests/FoundationEssentialsTests/PredicateConversionTests.swift b/Tests/FoundationEssentialsTests/PredicateConversionTests.swift index fe29fc7dc..f56a2a8a6 100644 --- a/Tests/FoundationEssentialsTests/PredicateConversionTests.swift +++ b/Tests/FoundationEssentialsTests/PredicateConversionTests.swift @@ -12,9 +12,10 @@ #if FOUNDATION_FRAMEWORK +import Testing @_spi(Expression) import Foundation -final class NSPredicateConversionTests: XCTestCase { +struct NSPredicateConversionTests { private func convert(_ predicate: Predicate) -> NSPredicate? { NSPredicate(predicate) } @@ -61,64 +62,64 @@ final class NSPredicateConversionTests: XCTestCase { var b: [Int] } - func testBasics() { + @Test func testBasics() { let obj = ObjCObject() let compareTo = 2 var predicate = #Predicate { $0.a == compareTo } var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "a == 2")) - XCTAssertFalse(converted!.evaluate(with: obj)) - + #expect(converted == NSPredicate(format: "a == 2")) + #expect(converted!.evaluate(with: obj) == false) + predicate = #Predicate { $0.a + 2 == 4 } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "a + 2 == 4")) - XCTAssertFalse(converted!.evaluate(with: obj)) - + #expect(converted == NSPredicate(format: "a + 2 == 4")) + #expect(converted!.evaluate(with: obj) == false) + predicate = #Predicate { $0.b.count == 5 } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "b.length == 5")) - XCTAssertTrue(converted!.evaluate(with: obj)) - + #expect(converted == NSPredicate(format: "b.length == 5")) + #expect(converted!.evaluate(with: obj)) + predicate = #Predicate { $0.g.count == 5 } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "g.@count == 5")) - XCTAssertTrue(converted!.evaluate(with: obj)) - + #expect(converted == NSPredicate(format: "g.@count == 5")) + #expect(converted!.evaluate(with: obj)) + predicate = #Predicate { object in object.g.filter { $0 == object.d }.count > 0 } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "SUBQUERY(g, $_local_1, $_local_1 == d).@count > 0")) - XCTAssertFalse(converted!.evaluate(with: obj)) + #expect(converted == NSPredicate(format: "SUBQUERY(g, $_local_1, $_local_1 == d).@count > 0")) + #expect(converted!.evaluate(with: obj) == false) } - func testEquality() { + @Test func testEquality() { var predicate = #Predicate { $0.a == 0 } var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "a == 0")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) - + #expect(converted == NSPredicate(format: "a == 0")) + #expect(converted!.evaluate(with: ObjCObject()) == false) + predicate = #Predicate { $0.a != 0 } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "a != 0")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "a != 0")) + #expect(converted!.evaluate(with: ObjCObject())) } - func testRanges() { + @Test func testRanges() { let now = Date.now let range = now ..< now let closedRange = now ... now @@ -131,181 +132,181 @@ final class NSPredicateConversionTests: XCTestCase { ($0.i ... $0.i).contains($0.i) } var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "i BETWEEN {i, i}")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) - + #expect(converted == NSPredicate(format: "i BETWEEN {i, i}")) + #expect(converted!.evaluate(with: ObjCObject())) + // Non-closed Range Operator predicate = #Predicate { ($0.i ..< $0.i).contains($0.i) } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "i >= i AND i < i")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) - + #expect(converted == NSPredicate(format: "i >= i AND i < i")) + #expect(converted!.evaluate(with: ObjCObject()) == false) + // Various values predicate = #Predicate { range.contains($0.i) } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "i >= %@ AND i < %@", now as NSDate, now as NSDate)) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "i >= %@ AND i < %@", now as NSDate, now as NSDate)) + #expect(converted!.evaluate(with: ObjCObject()) == false) predicate = #Predicate { closedRange.contains($0.i) } converted = convert(predicate) let other = NSPredicate(format: "i BETWEEN %@", [now, now]) - XCTAssertEqual(converted, other) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + #expect(converted == other) + #expect(converted!.evaluate(with: ObjCObject()) == false) predicate = #Predicate { from.contains($0.i) } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "i >= %@", now as NSDate)) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "i >= %@", now as NSDate)) + #expect(converted!.evaluate(with: ObjCObject())) predicate = #Predicate { through.contains($0.i) } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "i <= %@", now as NSDate)) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "i <= %@", now as NSDate)) + #expect(converted!.evaluate(with: ObjCObject()) == false) predicate = #Predicate { upTo.contains($0.i) } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "i < %@", now as NSDate)) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "i < %@", now as NSDate)) + #expect(converted!.evaluate(with: ObjCObject()) == false) } - func testNonObjC() { + @Test func testNonObjC() { let predicate = #Predicate { $0.nonObjCKeypath == 2 } - XCTAssertNil(convert(predicate)) + #expect(convert(predicate) == nil) } - func testNonObjCConstantKeyPath() { + @Test func testNonObjCConstantKeyPath() { let nonObjC = NonObjCStruct(a: 1, b: [1, 2, 3]) var predicate = #Predicate { $0.a == nonObjC.a } var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "a == 1")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) - + #expect(converted == NSPredicate(format: "a == 1")) + #expect(converted!.evaluate(with: ObjCObject())) + predicate = #Predicate { $0.f == nonObjC.b.contains([1, 2]) } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "f == YES")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "f == YES")) + #expect(converted!.evaluate(with: ObjCObject())) } - func testSubscripts() { + @Test func testSubscripts() { let obj = ObjCObject() var predicate = #Predicate { $0.g[0] == 2 } var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "(SELF.g)[0] == 2")) - XCTAssertFalse(converted!.evaluate(with: obj)) - + #expect(converted == NSPredicate(format: "(SELF.g)[0] == 2")) + #expect(converted!.evaluate(with: obj) == false) + predicate = #Predicate { $0.h["A"] == 1 } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "(SELF.h)['A'] == 1")) - XCTAssertTrue(converted!.evaluate(with: obj)) + #expect(converted == NSPredicate(format: "(SELF.h)['A'] == 1")) + #expect(converted!.evaluate(with: obj)) } - func testStringSearching() { + @Test func testStringSearching() { let obj = ObjCObject() var predicate = #Predicate { $0.b.contains("foo") } var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "b CONTAINS 'foo'")) - XCTAssertFalse(converted!.evaluate(with: obj)) - + #expect(converted == NSPredicate(format: "b CONTAINS 'foo'")) + #expect(converted!.evaluate(with: obj) == false) + predicate = #Predicate { $0.b.starts(with: "foo") } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "b BEGINSWITH 'foo'")) - XCTAssertFalse(converted!.evaluate(with: obj)) + #expect(converted == NSPredicate(format: "b BEGINSWITH 'foo'")) + #expect(converted!.evaluate(with: obj) == false) } - func testExpressionEnforcement() { + @Test func testExpressionEnforcement() { var predicate = #Predicate { _ in true } var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "YES == YES")) - XCTAssertTrue(converted!.evaluate(with: "Hello")) - + #expect(converted == NSPredicate(format: "YES == YES")) + #expect(converted!.evaluate(with: "Hello")) + predicate = #Predicate { _ in false } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "NO == YES")) - XCTAssertFalse(converted!.evaluate(with: "Hello")) - + #expect(converted == NSPredicate(format: "NO == YES")) + #expect(converted!.evaluate(with: "Hello") == false) + predicate = #Predicate { _ in true && false } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "(YES == YES) && (NO == YES)")) - XCTAssertFalse(converted!.evaluate(with: "Hello")) - + #expect(converted == NSPredicate(format: "(YES == YES) && (NO == YES)")) + #expect(converted!.evaluate(with: "Hello") == false) + predicate = #Predicate { $0.f } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "f == YES")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) - + #expect(converted == NSPredicate(format: "f == YES")) + #expect(converted!.evaluate(with: ObjCObject())) + predicate = #Predicate { ($0.f && true) == false } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "TERNARY(f == YES AND YES == YES, YES, NO) == NO")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "TERNARY(f == YES AND YES == YES, YES, NO) == NO")) + #expect(converted!.evaluate(with: ObjCObject()) == false) } - func testConditional() { + @Test func testConditional() { let predicate = #Predicate { $0.f ? true : false } let converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "TERNARY(f == YES, YES, NO) == YES")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "TERNARY(f == YES, YES, NO) == YES")) + #expect(converted!.evaluate(with: ObjCObject())) } - func testOptionals() { + @Test func testOptionals() { var predicate = #Predicate { ($0.j ?? "").isEmpty } var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "TERNARY(j != NULL, j, '').length == 0")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) - + #expect(converted == NSPredicate(format: "TERNARY(j != NULL, j, '').length == 0")) + #expect(converted!.evaluate(with: ObjCObject())) + predicate = #Predicate { ($0.j?.count ?? -1) > 1 } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "TERNARY(TERNARY(j != nil, j.length, nil) != nil, TERNARY(j != nil, j.length, nil), 1 * -1) > 1")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) - + #expect(converted == NSPredicate(format: "TERNARY(TERNARY(j != nil, j.length, nil) != nil, TERNARY(j != nil, j.length, nil), 1 * -1) > 1")) + #expect(converted!.evaluate(with: ObjCObject()) == false) + predicate = #Predicate { $0.j == nil } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "j == nil")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "j == nil")) + #expect(converted!.evaluate(with: ObjCObject())) } - func testUUID() { + @Test func testUUID() { let obj = ObjCObject() let uuid = obj.k let predicate = #Predicate { @@ -313,100 +314,100 @@ final class NSPredicateConversionTests: XCTestCase { } let converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "k == %@", uuid as NSUUID)) - XCTAssertTrue(converted!.evaluate(with: obj)) + #expect(converted == NSPredicate(format: "k == %@", uuid as NSUUID)) + #expect(converted!.evaluate(with: obj)) let obj2 = ObjCObject() - XCTAssertNotEqual(obj2.k, uuid) - XCTAssertFalse(converted!.evaluate(with: obj2)) + #expect(obj2.k != uuid) + #expect(converted!.evaluate(with: obj2) == false) } - func testDate() { + @Test func testDate() { let now = Date.now let predicate = #Predicate { $0.i > now } let converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "i > %@", now as NSDate)) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "i > %@", now as NSDate)) + #expect(converted!.evaluate(with: ObjCObject())) } - func testData() { + @Test func testData() { let data = Data([1, 2, 3]) let predicate = #Predicate { $0.l == data } let converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "l == %@", data as NSData)) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "l == %@", data as NSData)) + #expect(converted!.evaluate(with: ObjCObject())) } - func testURL() { + @Test func testURL() { let url = URL(string: "http://apple.com")! let predicate = #Predicate { $0.m == url } let converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "m == %@", url as NSURL)) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "m == %@", url as NSURL)) + #expect(converted!.evaluate(with: ObjCObject())) } - func testSequenceContainsWhere() { + @Test func testSequenceContainsWhere() { let predicate = #Predicate { $0.g.contains { $0 == 2 } } let converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "SUBQUERY(g, $_local_1, $_local_1 == 2).@count != 0")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "SUBQUERY(g, $_local_1, $_local_1 == 2).@count != 0")) + #expect(converted!.evaluate(with: ObjCObject()) == false) } - func testSequenceAllSatisfy() { + @Test func testSequenceAllSatisfy() { let predicate = #Predicate { $0.g.allSatisfy { $0 == 2 } } let converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "SUBQUERY(g, $_local_1, NOT ($_local_1 == 2)).@count == 0")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "SUBQUERY(g, $_local_1, NOT ($_local_1 == 2)).@count == 0")) + #expect(converted!.evaluate(with: ObjCObject()) == false) } - func testMaxMin() { + @Test func testMaxMin() { let predicate = #Predicate { $0.g.max() == $0.g.min() } let converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "g.@max.#self == g.@min.#self")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "g.@max.#self == g.@min.#self")) + #expect(converted!.evaluate(with: ObjCObject()) == false) } - func testStringComparison() { + @Test func testStringComparison() { let equal = ComparisonResult.orderedSame var predicate = #Predicate { $0.b.caseInsensitiveCompare("ABC") == equal } var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "TERNARY(b ==[c] 'ABC', 0, TERNARY(b <[c] 'ABC', -1, 1)) == 0")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) - + #expect(converted == NSPredicate(format: "TERNARY(b ==[c] 'ABC', 0, TERNARY(b <[c] 'ABC', -1, 1)) == 0")) + #expect(converted!.evaluate(with: ObjCObject()) == false) + predicate = #Predicate { $0.b.localizedCompare("ABC") == equal } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "TERNARY(b ==[l] 'ABC', 0, TERNARY(b <[l] 'ABC', -1, 1)) == 0")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) - + #expect(converted == NSPredicate(format: "TERNARY(b ==[l] 'ABC', 0, TERNARY(b <[l] 'ABC', -1, 1)) == 0")) + #expect(converted!.evaluate(with: ObjCObject()) == false) + predicate = #Predicate { $0.b.localizedStandardContains("ABC") } converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "b CONTAINS[cdl] 'ABC'")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "b CONTAINS[cdl] 'ABC'")) + #expect(converted!.evaluate(with: ObjCObject()) == false) } - func testNested() { + @Test func testNested() { let predicateA = Predicate { PredicateExpressions.build_Equal( lhs: PredicateExpressions.build_KeyPath( @@ -435,21 +436,21 @@ final class NSPredicateConversionTests: XCTestCase { } let converted = convert(predicateB) - XCTAssertEqual(converted, NSPredicate(format: "a == 3 AND a > 2")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "a == 3 AND a > 2")) + #expect(converted!.evaluate(with: ObjCObject()) == false) } - func testRegex() { + @Test func testRegex() { let regex = #/[e-f][l-m]/# let predicate = #Predicate { $0.b.contains(regex) } let converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "b MATCHES '.*[e-f][l-m].*'")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + #expect(converted == NSPredicate(format: "b MATCHES '.*[e-f][l-m].*'")) + #expect(converted!.evaluate(with: ObjCObject())) } - func testExpression() { + @Test func testExpression() { let expression = Expression() { PredicateExpressions.build_KeyPath( root: PredicateExpressions.build_Arg($0), @@ -457,10 +458,13 @@ final class NSPredicateConversionTests: XCTestCase { ) } let converted = convert(expression) - XCTAssertEqual(converted, NSExpression(format: "a")) + #expect(converted == NSExpression(format: "a")) let obj = ObjCObject() let value = converted!.expressionValue(with: obj, context: nil) - XCTAssertEqual(value as? Int, obj.a, "Expression produced \(String(describing: value)) instead of \(obj.a)") + #expect( + value as? Int == obj.a, + "Expression produced \(String(describing: value)) instead of \(obj.a)" + ) } } diff --git a/Tests/FoundationEssentialsTests/PredicateTests.swift b/Tests/FoundationEssentialsTests/PredicateTests.swift index b984fe1b5..c92b0dad9 100644 --- a/Tests/FoundationEssentialsTests/PredicateTests.swift +++ b/Tests/FoundationEssentialsTests/PredicateTests.swift @@ -10,8 +10,12 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -@_spi(Expression) import TestSupport +import Testing + +#if FOUNDATION_FRAMEWORK +@testable import Foundation +#else +@testable import FoundationEssentials #endif #if canImport(RegexBuilder) @@ -29,14 +33,16 @@ import RegexBuilder macro Predicate(_ body: (repeat each Input) -> Bool) -> Predicate = #externalMacro(module: "FoundationMacros", type: "PredicateMacro") #endif -final class PredicateTests: XCTestCase { - - override func setUp() async throws { - guard #available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) else { - throw XCTSkip("This test is not available on this OS version") - } +func predicateTestsAvailable() -> Bool { + if #available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) { + return true } - + return false +} + +@Suite(.enabled(if: predicateTestsAvailable(), "PredicateTests is not available on this OS version")) +struct PredicateTests { + struct Object { var a: Int var b: String @@ -54,107 +60,126 @@ final class PredicateTests: XCTestCase { } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testBasic() throws { + @Test func testBasic() throws { let compareTo = 2 let predicate = #Predicate { $0.a == compareTo } - try XCTAssertFalse(predicate.evaluate(Object(a: 1, b: "", c: 0, d: 0, e: "c", f: true, g: []))) - try XCTAssertTrue(predicate.evaluate(Object(a: 2, b: "", c: 0, d: 0, e: "c", f: true, g: []))) + var result = try predicate.evaluate(Object(a: 1, b: "", c: 0, d: 0, e: "c", f: true, g: [])) + #expect(result == false) + result = try predicate.evaluate(Object(a: 2, b: "", c: 0, d: 0, e: "c", f: true, g: [])) + #expect(result == true) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testVariadic() throws { + @Test func testVariadic() throws { let predicate = #Predicate { $0.a == $1 + 1 } - XCTAssert(try predicate.evaluate(Object(a: 3, b: "", c: 0, d: 0, e: "c", f: true, g: []), 2)) + let result = try predicate.evaluate(Object(a: 3, b: "", c: 0, d: 0, e: "c", f: true, g: []), 2) + #expect(result == true) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testArithmetic() throws { + @Test func testArithmetic() throws { let predicate = #Predicate { $0.a + 2 == 4 } - XCTAssert(try predicate.evaluate(Object(a: 2, b: "", c: 0, d: 0, e: "c", f: true, g: []))) + let results = try predicate.evaluate(Object(a: 2, b: "", c: 0, d: 0, e: "c", f: true, g: [])) + #expect(results) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testDivision() throws { + @Test func testDivision() throws { let predicate = #Predicate { $0.a / 2 == 3 } let predicate2 = #Predicate { $0.c / 2.1 <= 3.0 } - XCTAssert(try predicate.evaluate(Object(a: 6, b: "", c: 0, d: 0, e: "c", f: true, g: []))) - XCTAssert(try predicate2.evaluate(Object(a: 2, b: "", c: 6.0, d: 0, e: "c", f: true, g: []))) + var results = try predicate.evaluate(Object(a: 6, b: "", c: 0, d: 0, e: "c", f: true, g: [])) + #expect(results) + results = try predicate2.evaluate(Object(a: 2, b: "", c: 6.0, d: 0, e: "c", f: true, g: [])) + #expect(results) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testBuildDivision() throws { + @Test func testBuildDivision() throws { let predicate = #Predicate { $0.a / 2 == 3 } - XCTAssert(try predicate.evaluate(Object(a: 6, b: "", c: 0, d: 0, e: "c", f: true, g: []))) + let results = try predicate.evaluate(Object(a: 6, b: "", c: 0, d: 0, e: "c", f: true, g: [])) + #expect(results) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testUnaryMinus() throws { + @Test func testUnaryMinus() throws { let predicate = #Predicate { -$0.a == 17 } - XCTAssert(try predicate.evaluate(Object(a: -17, b: "", c: 0, d: 0, e: "c", f: true, g: []))) + let results = try predicate + .evaluate(Object(a: -17, b: "", c: 0, d: 0, e: "c", f: true, g: [])) + #expect(results) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testCount() throws { + @Test func testCount() throws { let predicate = #Predicate { $0.g.count == 5 } - XCTAssert(try predicate.evaluate(Object(a: 0, b: "", c: 0, d: 0, e: "c", f: true, g: [2, 3, 5, 7, 11]))) + let results = try predicate + .evaluate(Object(a: 0, b: "", c: 0, d: 0, e: "c", f: true, g: [2, 3, 5, 7, 11])) + #expect(results) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testFilter() throws { + @Test func testFilter() throws { let predicate = #Predicate { object in !object.g.filter { $0 == object.d }.isEmpty } - XCTAssert(try predicate.evaluate(Object(a: 0, b: "", c: 0.0, d: 17, e: "c", f: true, g: [3, 5, 7, 11, 13, 17, 19]))) + let results = try predicate + .evaluate(Object(a: 0, b: "", c: 0.0, d: 17, e: "c", f: true, g: [3, 5, 7, 11, 13, 17, 19])) + #expect(results) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testContains() throws { + @Test func testContains() throws { let predicate = #Predicate { $0.g.contains($0.a) } - XCTAssert(try predicate.evaluate(Object(a: 13, b: "", c: 0.0, d: 0, e: "c", f: true, g: [2, 3, 5, 11, 13, 17]))) + let results = try predicate + .evaluate(Object(a: 13, b: "", c: 0.0, d: 0, e: "c", f: true, g: [2, 3, 5, 11, 13, 17])) + #expect(results) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testContainsWhere() throws { + @Test func testContainsWhere() throws { let predicate = #Predicate { object in object.g.contains { $0 % object.a == 0 } } - XCTAssert(try predicate.evaluate(Object(a: 2, b: "", c: 0.0, d: 0, e: "c", f: true, g: [3, 5, 7, 2, 11, 13]))) + let results = try predicate + .evaluate(Object(a: 2, b: "", c: 0.0, d: 0, e: "c", f: true, g: [3, 5, 7, 2, 11, 13])) + #expect(results) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testAllSatisfy() throws { + @Test func testAllSatisfy() throws { let predicate = #Predicate { object in object.g.allSatisfy { $0 % object.d != 0 } } - XCTAssert(try predicate.evaluate(Object(a: 0, b: "", c: 0.0, d: 2, e: "c", f: true, g: [3, 5, 7, 11, 13, 17, 19]))) + let results = try predicate + .evaluate(Object(a: 0, b: "", c: 0.0, d: 2, e: "c", f: true, g: [3, 5, 7, 11, 13, 17, 19])) + #expect(results) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testOptional() throws { + @Test func testOptional() throws { struct Wrapper { let wrapped: T? } @@ -164,41 +189,52 @@ final class PredicateTests: XCTestCase { let predicate2 = #Predicate> { $0.wrapped! == 19 } - XCTAssert(try predicate.evaluate(Wrapper(wrapped: 4))) - XCTAssert(try predicate.evaluate(Wrapper(wrapped: nil))) - XCTAssert(try predicate2.evaluate(Wrapper(wrapped: 19))) - XCTAssertThrowsError(try predicate2.evaluate(Wrapper(wrapped: nil))) - + var results = try predicate.evaluate(Wrapper(wrapped: 4)) + #expect(results) + results = try predicate.evaluate(Wrapper(wrapped: nil)) + #expect(results) + results = try predicate2.evaluate(Wrapper(wrapped: 19)) + #expect(results) + #expect(throws: (any Error).self) { + try predicate2.evaluate(Wrapper(wrapped: nil)) + } + struct _NonCodableType : Equatable {} let predicate3 = #Predicate> { $0.wrapped == nil } - XCTAssertFalse(try predicate3.evaluate(Wrapper(wrapped: _NonCodableType()))) - XCTAssertTrue(try predicate3.evaluate(Wrapper(wrapped: nil))) + results = try predicate3.evaluate(Wrapper(wrapped: _NonCodableType())) + #expect(results == false) + results = try predicate3.evaluate(Wrapper(wrapped: nil)) + #expect(results) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testConditional() throws { + @Test func testConditional() throws { let predicate = #Predicate { ($0 ? $1 : $2) == "if branch" } - XCTAssert(try predicate.evaluate(true, "if branch", "else branch")) + let results = try predicate.evaluate(true, "if branch", "else branch") + #expect(results) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testClosedRange() throws { + @Test func testClosedRange() throws { let predicate = #Predicate { (3...5).contains($0.a) } let predicate2 = #Predicate { ($0.a ... $0.d).contains(4) } - XCTAssert(try predicate.evaluate(Object(a: 4, b: "", c: 0.0, d: 0, e: "c", f: true, g: []))) - XCTAssert(try predicate2.evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: []))) + var results = try predicate + .evaluate(Object(a: 4, b: "", c: 0.0, d: 0, e: "c", f: true, g: [])) + #expect(results == true) + results = try predicate2.evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: [])) + #expect(results == true) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testRange() throws { + @Test func testRange() throws { let predicate = #Predicate { (3 ..< 5).contains($0.a) } @@ -206,54 +242,75 @@ final class PredicateTests: XCTestCase { let predicate2 = #Predicate { ($0.a ..< $0.d).contains(toMatch) } - XCTAssert(try predicate.evaluate(Object(a: 4, b: "", c: 0.0, d: 0, e: "c", f: true, g: []))) - XCTAssert(try predicate2.evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: []))) + var results = try predicate + .evaluate(Object(a: 4, b: "", c: 0.0, d: 0, e: "c", f: true, g: [])) + #expect(results == true) + results = try predicate2 + .evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: [])) + #expect(results == true) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testRangeContains() throws { + @Test func testRangeContains() throws { let date = Date.distantPast let predicate = #Predicate { (date ..< date).contains($0.h) } - - XCTAssertFalse(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: []))) + let results = try predicate + .evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: [])) + #expect(results == false) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testTypes() throws { + @Test func testTypes() throws { let predicate = #Predicate { ($0.i as? Int).flatMap { $0 == 3 } ?? false } let predicate2 = #Predicate { $0.i is Int } - XCTAssert(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: []))) - XCTAssert(try predicate2.evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: []))) + var results = try predicate + .evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [])) + #expect(results == true) + results = try predicate2.evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: [])) + #expect(results == true) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testSubscripts() throws { + @Test func testSubscripts() throws { var predicate = #Predicate { $0.g[0] == 0 } - - XCTAssertTrue(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [0]))) - XCTAssertFalse(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1]))) - XCTAssertThrowsError(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: []))) - + var results = try predicate + .evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [0])) + #expect(results == true) + results = try predicate + .evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1])) + #expect(results == false) + #expect(throws: (any Error).self) { + try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [])) + } + predicate = #Predicate { $0.g[0 ..< 2].isEmpty } - - XCTAssertFalse(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [0, 1, 2]))) - XCTAssertFalse(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [0, 1]))) - XCTAssertThrowsError(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [0]))) - XCTAssertThrowsError(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: []))) + + results = try predicate + .evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [0, 1, 2])) + #expect(results == false) + results = try predicate + .evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [0, 1])) + #expect(results == false) + #expect(throws: (any Error).self) { + try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [0])) + } + #expect(throws: (any Error).self) { + try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [])) + } } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testLazyDefaultValueSubscript() throws { + @Test func testLazyDefaultValueSubscript() throws { struct Foo : Codable, Sendable { static var num = 1 @@ -267,14 +324,16 @@ final class PredicateTests: XCTestCase { let predicate = #Predicate<[String : Int]> { $0["key", default: foo.property] == 1 } - XCTAssertFalse(try predicate.evaluate(["key" : 2])) - XCTAssertEqual(Foo.num, 1) + let results = try predicate.evaluate(["key" : 2]) + #expect(results == false) + #expect(Foo.num == 1) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testStaticValues() throws { + @Test func testStaticValues() throws { func assertPredicate(_ pred: Predicate, value: T, expected: Bool) throws { - XCTAssertEqual(try pred.evaluate(value), expected) + let results = try pred.evaluate(value) + #expect(results == expected) } try assertPredicate(.true, value: "Hello", expected: true) @@ -282,36 +341,45 @@ final class PredicateTests: XCTestCase { } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testMaxMin() throws { + @Test func testMaxMin() throws { var predicate = #Predicate { $0.g.max() == 2 } - XCTAssertFalse(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) - XCTAssertTrue(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1, 2]))) - + var results = try predicate + .evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1, 3])) + #expect(results == false) + results = try predicate + .evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1, 2])) + #expect(results == true) + predicate = #Predicate { $0.g.min() == 2 } - XCTAssertFalse(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) - XCTAssertTrue(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [2, 3]))) + results = try predicate + .evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1, 3])) + #expect(results == false) + results = try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [2, 3])) + #expect(results == true) } #if FOUNDATION_FRAMEWORK @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testCaseInsensitiveCompare() throws { + @Test func testCaseInsensitiveCompare() throws { let equal = ComparisonResult.orderedSame let predicate = #Predicate { $0.b.caseInsensitiveCompare("ABC") == equal } - XCTAssertTrue(try predicate.evaluate(Object(a: 3, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) - XCTAssertFalse(try predicate.evaluate(Object(a: 3, b: "def", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) + var results = try predicate.evaluate(Object(a: 3, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3])) + #expect(results == true) + results = try predicate.evaluate(Object(a: 3, b: "def", c: 0.0, d: 0, e: "c", f: true, g: [1, 3])) + #expect(results == false) } #endif @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testBuildDynamically() throws { + @Test func testBuildDynamically() throws { func _build(_ equal: Bool) -> Predicate { Predicate { if equal { @@ -327,13 +395,14 @@ final class PredicateTests: XCTestCase { } } } - - XCTAssertTrue(try _build(true).evaluate(1)) - XCTAssertFalse(try _build(false).evaluate(1)) + var results = try _build(true).evaluate(1) + #expect(results == true) + results = try _build(false).evaluate(1) + #expect(results == false) } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - func testResilientKeyPaths() { + @Test func testResilientKeyPaths() { // Local, non-resilient type struct Foo { let a: String // Non-resilient @@ -347,35 +416,30 @@ final class PredicateTests: XCTestCase { } } - #if compiler(>=5.11) - func testRegex() throws { - guard #available(FoundationPredicateRegex 0.4, *) else { - throw XCTSkip("This test is not available on this OS version") - } - +#if compiler(>=5.11) + @available(FoundationPredicateRegex 0.4, *) + @Test func testRegex() throws { let literalRegex = #/[AB0-9]\/?[^\n]+/# var predicate = #Predicate { $0.b.contains(literalRegex) } - XCTAssertTrue(try predicate.evaluate(Object(a: 0, b: "_0/bc", c: 0, d: 0, e: " ", f: true, g: []))) - XCTAssertFalse(try predicate.evaluate(Object(a: 0, b: "_C/bc", c: 0, d: 0, e: " ", f: true, g: []))) + var result = try predicate + .evaluate(Object(a: 0, b: "_0/bc", c: 0, d: 0, e: " ", f: true, g: [])) + #expect(result) + result = try predicate.evaluate(Object(a: 0, b: "_C/bc", c: 0, d: 0, e: " ", f: true, g: [])) == false + #expect(result) predicate = #Predicate { $0.b.contains(#/[AB0-9]\/?[^\n]+/#) } - XCTAssertTrue(try predicate.evaluate(Object(a: 0, b: "_0/bc", c: 0, d: 0, e: " ", f: true, g: []))) - XCTAssertFalse(try predicate.evaluate(Object(a: 0, b: "_C/bc", c: 0, d: 0, e: " ", f: true, g: []))) + result = try predicate.evaluate(Object(a: 0, b: "_0/bc", c: 0, d: 0, e: " ", f: true, g: [])) + #expect(result) + result = try predicate.evaluate(Object(a: 0, b: "_C/bc", c: 0, d: 0, e: " ", f: true, g: [])) == false + #expect(result) } - func testRegex_RegexBuilder() throws { - #if !canImport(RegexBuilder) - throw XCTSkip("RegexBuilder is unavavailable on this platform") - #elseif !os(Linux) && !FOUNDATION_FRAMEWORK - // Disable this test in swift-foundation macOS CI because of incorrect availability annotations in the StringProcessing module - throw XCTSkip("This test is currently disabled on this platform") - #else - guard #available(FoundationPredicateRegex 0.4, *) else { - throw XCTSkip("This test is not available on this OS version") - } +#if canImport(RegexBuilder) && (os(Linux) || FOUNDATION_FRAMEWORK) + @available(FoundationPredicateRegex 0.4, *) + @Test func testRegex_RegexBuilder() throws { let builtRegex = Regex { ChoiceOf { @@ -389,17 +453,18 @@ final class PredicateTests: XCTestCase { let predicate = #Predicate { $0.b.contains(builtRegex) } - XCTAssertTrue(try predicate.evaluate(Object(a: 0, b: "_0/bc", c: 0, d: 0, e: " ", f: true, g: []))) - XCTAssertFalse(try predicate.evaluate(Object(a: 0, b: "_C/bc", c: 0, d: 0, e: " ", f: true, g: []))) - #endif + var results = try predicate + .evaluate(Object(a: 0, b: "_0/bc", c: 0, d: 0, e: " ", f: true, g: [])) + #expect(results == true) + results = try predicate + .evaluate(Object(a: 0, b: "_C/bc", c: 0, d: 0, e: " ", f: true, g: [])) + #expect(results == false) } - #endif - - func testDebugDescription() throws { - guard #available(FoundationPredicate 0.3, *) else { - throw XCTSkip("This test is not available on this OS version") - } - +#endif // canImport(RegexBuilder) && (os(Linux) || FOUNDATION_FRAMEWORK) +#endif // compiler(>=5.11) + + @available(FoundationPredicate 0.3, *) + @Test func testDebugDescription() throws { let date = Date.now let predicate = #Predicate { if let num = $0.i as? Int { @@ -415,8 +480,8 @@ final class PredicateTests: XCTestCase { let moduleName = "FoundationEssentials" let testModuleName = "FoundationEssentialsTests" #endif - XCTAssertEqual( - predicate.description, + #expect( + predicate.description == """ capture1 (Swift.Int): 3 capture2 (\(moduleName).Date): @@ -429,18 +494,15 @@ final class PredicateTests: XCTestCase { ) let debugDescription = predicate.debugDescription.replacing(#/Variable\([0-9]+\)/#, with: "Variable(#)") - XCTAssertEqual( - debugDescription, + #expect( + debugDescription == "\(moduleName).Predicate(variable: (Variable(#)), expression: NilCoalesce(lhs: OptionalFlatMap(wrapped: ConditionalCast(input: KeyPath(root: Variable(#), keyPath: \\Object.i), desiredType: Swift.Int), variable: Variable(#), transform: Equal(lhs: Variable(#), rhs: Value(3))), rhs: Equal(lhs: KeyPath(root: Variable(#), keyPath: \\Object.h), rhs: Value<\(moduleName).Date>(\(date.debugDescription)))))" ) } #if FOUNDATION_FRAMEWORK - func testNested() throws { - guard #available(FoundationPredicate 0.3, *) else { - throw XCTSkip("This test is not available on this OS version") - } - + @available(FoundationPredicate 0.3, *) + @Test func testNested() throws { let predicateA = #Predicate { $0.a == 3 } @@ -448,18 +510,23 @@ final class PredicateTests: XCTestCase { let predicateB = #Predicate { predicateA.evaluate($0) && $0.a > 2 } - - XCTAssertTrue(try predicateA.evaluate(Object(a: 3, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) - XCTAssertFalse(try predicateA.evaluate(Object(a: 2, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) - XCTAssertTrue(try predicateB.evaluate(Object(a: 3, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) - XCTAssertFalse(try predicateB.evaluate(Object(a: 2, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) - XCTAssertFalse(try predicateB.evaluate(Object(a: 4, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) - } - - func testExpression() throws { - guard #available(FoundationPredicate 0.4, *) else { - throw XCTSkip("This test is not available on this OS version") - } + var results = try predicateA + .evaluate(Object(a: 3, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3])) + #expect(results == true) + results = try predicateA + .evaluate(Object(a: 2, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3])) + #expect(results == false) + results = try predicateB + .evaluate(Object(a: 3, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3])) + #expect(results == true) + results = try predicateB.evaluate(Object(a: 2, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3])) + #expect(results == false) + results = try predicateB.evaluate(Object(a: 4, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3])) + #expect(results == false) + } + + @available(FoundationPredicate 0.4, *) + @Test func testExpression() throws { let expression = Expression() { PredicateExpressions.build_Arithmetic( @@ -469,7 +536,8 @@ final class PredicateTests: XCTestCase { ) } for i in 0 ..< 10 { - XCTAssertEqual(try expression.evaluate(i), i + 1) + let result = try expression.evaluate(i) + #expect(result == i + 1) } } #endif diff --git a/Tests/FoundationEssentialsTests/ProcessInfoTests.swift b/Tests/FoundationEssentialsTests/ProcessInfoTests.swift index b21605b50..5121e30ac 100644 --- a/Tests/FoundationEssentialsTests/ProcessInfoTests.swift +++ b/Tests/FoundationEssentialsTests/ProcessInfoTests.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -import XCTest +import Testing #if canImport(FoundationEssentials) @testable import FoundationEssentials @@ -26,119 +26,113 @@ import Glibc /// Since we can't really mock system settings like OS name, /// these tests simply check that the values returned are not empty -final class ProcessInfoTests : XCTestCase { - func testArguments() { +struct ProcessInfoTests { + @Test func testArguments() { let args = _ProcessInfo.processInfo.arguments - XCTAssertTrue( - !args.isEmpty,"arguments should not have been empty") + #expect(!args.isEmpty, "arguments should not have been empty") } - func testEnvironment() { + @Test func testEnvironment() { let env = _ProcessInfo.processInfo.environment - XCTAssertTrue( - !env.isEmpty, "environment should not have been empty") + #expect(!env.isEmpty, "environment should not have been empty") } - func testProcessIdentifier() { + @Test func testProcessIdentifier() { let pid = _ProcessInfo.processInfo.processIdentifier - XCTAssertEqual( - pid, getpid(), "ProcessInfo disagrees with getpid()") + #expect(pid == getpid(), "ProcessInfo disagrees with getpid()") } - func testGlobalUniqueString() { + @Test func testGlobalUniqueString() { let unique = _ProcessInfo.processInfo.globallyUniqueString - XCTAssertNotEqual( - unique, - _ProcessInfo.processInfo.globallyUniqueString, + #expect(unique != _ProcessInfo.processInfo.globallyUniqueString, "globallyUniqueString should never return the same string twice") } - func testOperatingSystemVersionString() { + @Test func testOperatingSystemVersionString() { let version = _ProcessInfo.processInfo.operatingSystemVersionString - XCTAssertTrue( - !version.isEmpty, "ProcessInfo returned empty string for operation system version") + #expect(!version.isEmpty, + "ProcessInfo returned empty string for operation system version") } - func testProcessorCount() { + @Test func testProcessorCount() { let count = _ProcessInfo.processInfo.processorCount - XCTAssertTrue(count > 0, "ProcessInfo doesn't think we have any processors") + #expect(count > 0, "ProcessInfo doesn't think we have any processors") } - func testActiveProcessorCount() { + @Test func testActiveProcessorCount() { let count = _ProcessInfo.processInfo.activeProcessorCount - XCTAssertTrue(count > 0, "ProcessInfo doesn't think we have any active processors") + #expect(count > 0, "ProcessInfo doesn't think we have any active processors") } - func testPhysicalMemory() { + @Test func testPhysicalMemory() { let memory = _ProcessInfo.processInfo.physicalMemory - XCTAssertTrue(memory > 0, "ProcessInfo doesn't think we have any memory") + #expect(memory > 0, "ProcessInfo doesn't think we have any memory") } - func testSystemUpTime() { + @Test func testSystemUpTime() { let now = _ProcessInfo.processInfo.systemUptime - XCTAssertTrue( - now > 1, "ProcessInfo returned an unrealistically low system uptime") + #expect(now > 1, "ProcessInfo returned an unrealistically low system uptime") // Sleep for 0.1s var ts: timespec = timespec(tv_sec: 0, tv_nsec: 100000000) nanosleep(&ts, nil) - XCTAssertTrue( + #expect( _ProcessInfo.processInfo.systemUptime > now, "ProcessInfo returned the same system uptime with 400") } #if canImport(Darwin) // Only test on Apple's OSs - func testOperatingSystemVersion() { + @Test func testOperatingSystemVersion() { let version = _ProcessInfo.processInfo.operatingSystemVersion #if os(visionOS) let expectedMinMajorVersion = 1 #else let expectedMinMajorVersion = 2 #endif - XCTAssertTrue( + #expect( version.major >= expectedMinMajorVersion, "Unrealistic major system version") } - func testOperatingSystemIsAtLeastVersion() { + @Test func testOperatingSystemIsAtLeastVersion() { #if os(watchOS) - XCTAssertTrue(_ProcessInfo.processInfo + #expect(_ProcessInfo.processInfo .isOperatingSystemAtLeast( (major: 1, minor: 12, patch: 0) ), "ProcessInfo thinks 1.12 is > than 2.something") - XCTAssertTrue(_ProcessInfo.processInfo + #expect(_ProcessInfo.processInfo .isOperatingSystemAtLeast( (major: 1, minor: 0, patch: 0) ), "ProcessInfo thinks we are on watchOS 1") #elseif os(macOS) || os(iOS) - XCTAssertTrue(_ProcessInfo.processInfo + #expect(_ProcessInfo.processInfo .isOperatingSystemAtLeast( (major: 6, minor: 12, patch: 0) ), "ProcessInfo thinks 6.12 is > than 10.something") - XCTAssertTrue(_ProcessInfo.processInfo + #expect(_ProcessInfo.processInfo .isOperatingSystemAtLeast( (major: 6, minor: 0, patch: 0) ), "ProcessInfo thinks we are on System 5") #endif - XCTAssertFalse(_ProcessInfo.processInfo + #expect(_ProcessInfo.processInfo .isOperatingSystemAtLeast( (major: 70, minor: 0, patch: 0) - ), + ) == false, "ProcessInfo thinks we are on System 70") } #endif #if os(macOS) - func testUserName() { - XCTAssertFalse(_ProcessInfo.processInfo.userName.isEmpty) + @Test func testUserName() { + #expect(!_ProcessInfo.processInfo.userName.isEmpty) } - func testFullUserName() { - XCTAssertFalse(_ProcessInfo.processInfo.fullUserName.isEmpty) + @Test func testFullUserName() { + #expect(!_ProcessInfo.processInfo.fullUserName.isEmpty) } #endif } diff --git a/Tests/FoundationEssentialsTests/PropertyListEncoderTests.swift b/Tests/FoundationEssentialsTests/PropertyListEncoderTests.swift index 5680c7101..077f8e3b2 100644 --- a/Tests/FoundationEssentialsTests/PropertyListEncoderTests.swift +++ b/Tests/FoundationEssentialsTests/PropertyListEncoderTests.swift @@ -7,9 +7,7 @@ //===----------------------------------------------------------------------===// // -#if canImport(TestSupport) -import TestSupport -#endif +import Testing #if FOUNDATION_FRAMEWORK @testable import Foundation @@ -19,16 +17,16 @@ import TestSupport // MARK: - Test Suite -class TestPropertyListEncoder : XCTestCase { +struct PropertyListEncoderTests { // MARK: - Encoding Top-Level Empty Types #if FIXED_64141381 - func testEncodingTopLevelEmptyStruct() { + @Test func testEncodingTopLevelEmptyStruct() { let empty = EmptyStruct() _testRoundTrip(of: empty, in: .binary, expectedPlist: _plistEmptyDictionaryBinary) _testRoundTrip(of: empty, in: .xml, expectedPlist: _plistEmptyDictionaryXML) } - func testEncodingTopLevelEmptyClass() { + @Test func testEncodingTopLevelEmptyClass() { let empty = EmptyClass() _testRoundTrip(of: empty, in: .binary, expectedPlist: _plistEmptyDictionaryBinary) _testRoundTrip(of: empty, in: .xml, expectedPlist: _plistEmptyDictionaryXML) @@ -36,7 +34,7 @@ class TestPropertyListEncoder : XCTestCase { #endif // MARK: - Encoding Top-Level Single-Value Types - func testEncodingTopLevelSingleValueEnum() { + @Test func testEncodingTopLevelSingleValueEnum() { let s1 = Switch.off _testEncodeFailure(of: s1, in: .binary) _testEncodeFailure(of: s1, in: .xml) @@ -50,7 +48,7 @@ class TestPropertyListEncoder : XCTestCase { _testRoundTrip(of: TopLevelWrapper(s2), in: .xml) } - func testEncodingTopLevelSingleValueStruct() { + @Test func testEncodingTopLevelSingleValueStruct() { let t = Timestamp(3141592653) _testEncodeFailure(of: t, in: .binary) _testEncodeFailure(of: t, in: .xml) @@ -58,7 +56,7 @@ class TestPropertyListEncoder : XCTestCase { _testRoundTrip(of: TopLevelWrapper(t), in: .xml) } - func testEncodingTopLevelSingleValueClass() { + @Test func testEncodingTopLevelSingleValueClass() { let c = Counter() _testEncodeFailure(of: c, in: .binary) _testEncodeFailure(of: c, in: .xml) @@ -67,49 +65,49 @@ class TestPropertyListEncoder : XCTestCase { } // MARK: - Encoding Top-Level Structured Types - func testEncodingTopLevelStructuredStruct() { + @Test func testEncodingTopLevelStructuredStruct() { // Address is a struct type with multiple fields. let address = Address.testValue _testRoundTrip(of: address, in: .binary) _testRoundTrip(of: address, in: .xml) } - func testEncodingTopLevelStructuredClass() { + @Test func testEncodingTopLevelStructuredClass() { // Person is a class with multiple fields. let person = Person.testValue _testRoundTrip(of: person, in: .binary) _testRoundTrip(of: person, in: .xml) } - func testEncodingTopLevelStructuredSingleStruct() { + @Test 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) } - func testEncodingTopLevelStructuredSingleClass() { + @Test 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) } - func testEncodingTopLevelDeepStructuredType() { + @Test 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) } - func testEncodingClassWhichSharesEncoderWithSuper() { + @Test 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) } - func testEncodingTopLevelNullableType() { + @Test 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) @@ -126,20 +124,20 @@ class TestPropertyListEncoder : XCTestCase { _testRoundTrip(of: TopLevelWrapper(EnhancedBool.fileNotFound), in: .xml) } - func testEncodingTopLevelWithConfiguration() throws { + @Test 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)) - XCTAssertEqual(decoded, value) + #expect(decoded == value) decoded = try decoder.decode(CodableTypeWithConfiguration.self, from: try encoder.encode(value, configuration: CodableTypeWithConfiguration.ConfigProviding.self), configuration: CodableTypeWithConfiguration.ConfigProviding.self) - XCTAssertEqual(decoded, value) + #expect(decoded == value) } #if FIXED_64141381 - func testEncodingMultipleNestedContainersWithTheSameTopLevelKey() { + @Test func testEncodingMultipleNestedContainersWithTheSameTopLevelKey() { struct Model : Codable, Equatable { let first: String let second: String @@ -191,8 +189,8 @@ class TestPropertyListEncoder : XCTestCase { } #endif -#if false // FIXME: XCTest doesn't support crash tests yet rdar://20195010&22387653 - func testEncodingConflictedTypeNestedContainersWithTheSameTopLevelKey() { +#if false // FIXME: Swift Testing doesn't support crash tests yet rdar://20195010&22387653 + @Test func testEncodingConflictedTypeNestedContainersWithTheSameTopLevelKey() { struct Model : Encodable, Equatable { let first: String @@ -231,34 +229,34 @@ class TestPropertyListEncoder : XCTestCase { #endif // MARK: - Encoder Features - func testNestedContainerCodingPaths() { + @Test func testNestedContainerCodingPaths() throws { let encoder = PropertyListEncoder() do { let _ = try encoder.encode(NestedContainersTestType()) - } catch let error as NSError { - XCTFail("Caught error during encoding nested container types: \(error)") + } catch { + Issue.record(error, "Caught error during encoding nested container types: \(error)") } } - func testSuperEncoderCodingPaths() { + @Test func testSuperEncoderCodingPaths() throws { let encoder = PropertyListEncoder() do { let _ = try encoder.encode(NestedContainersTestType(testSuperEncoder: true)) - } catch let error as NSError { - XCTFail("Caught error during encoding nested container types: \(error)") + } catch { + Issue.record(error, "Caught error during encoding nested container types: \(error)") } } #if FOUNDATION_FRAMEWORK // requires PropertyListSerialization, JSONSerialization - func testEncodingTopLevelData() { + @Test 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)) } - func testInterceptData() { + @Test func testInterceptData() { let data = try! JSONSerialization.data(withJSONObject: [String](), options: []) let topLevel = TopLevelWrapper(data) let plist = ["value": data] @@ -266,7 +264,7 @@ class TestPropertyListEncoder : XCTestCase { _testRoundTrip(of: topLevel, in: .xml, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)) } - func testInterceptDate() { + @Test func testInterceptDate() { let date = Date(timeIntervalSinceReferenceDate: 0) let topLevel = TopLevelWrapper(date) let plist = ["value": date] @@ -276,17 +274,21 @@ class TestPropertyListEncoder : XCTestCase { #endif // FOUNDATION_FRaMEWORK // MARK: - Type coercion - func testTypeCoercion() { + @Test 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) - XCTAssertThrowsError(try PropertyListDecoder().decode(U.self, from: xmlData), "Coercion from \(T.self) to \(U.self) was expected to fail.") + #expect(throws: (any Error).self, "Coercion from \(T.self) to \(U.self) was expected to fail.") { + try PropertyListDecoder().decode(U.self, from: xmlData) + } encoder.outputFormat = .binary 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.") + #expect(throws: (any Error).self, "Coercion from \(T.self) to \(U.self) was expected to fail.") { + try PropertyListDecoder().decode(U.self, from: binaryData) + } } _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int].self) @@ -327,7 +329,7 @@ class TestPropertyListEncoder : XCTestCase { _testRoundTripTypeCoercionFailure(of: [UInt64.max], as: [Int64].self) } - func testIntegerRealCoercion() throws { + @Test func testIntegerRealCoercion() throws { func _testRoundTripTypeCoercion(of value: T, expectedCoercedValue: U) throws { let encoder = PropertyListEncoder() @@ -335,13 +337,13 @@ class TestPropertyListEncoder : XCTestCase { let xmlData = try encoder.encode([value]) var decoded = try PropertyListDecoder().decode([U].self, from: xmlData) - XCTAssertEqual(decoded.first!, expectedCoercedValue) + #expect(decoded.first! == expectedCoercedValue) encoder.outputFormat = .binary let binaryData = try encoder.encode([value]) decoded = try PropertyListDecoder().decode([U].self, from: binaryData) - XCTAssertEqual(decoded.first!, expectedCoercedValue) + #expect(decoded.first! == expectedCoercedValue) } try _testRoundTripTypeCoercion(of: 1 as UInt64, expectedCoercedValue: 1.0 as Double) @@ -358,25 +360,25 @@ class TestPropertyListEncoder : XCTestCase { try _testRoundTripTypeCoercion(of: 2.99792458e8 as Double, expectedCoercedValue: 299792458) } - func testDecodingConcreteTypeParameter() { + @Test func testDecodingConcreteTypeParameter() { let encoder = PropertyListEncoder() guard let plist = try? encoder.encode(Employee.testValue) else { - XCTFail("Unable to encode Employee.") + Issue.record("Unable to encode Employee.") return } let decoder = PropertyListDecoder() guard let decoded = try? decoder.decode(Employee.self as Person.Type, from: plist) else { - XCTFail("Failed to decode Employee as Person from plist.") + Issue.record("Failed to decode Employee as Person from plist.") return } - expectEqual(type(of: decoded), Employee.self, "Expected decoded value to be of type Employee; got \(type(of: decoded)) instead.") + #expect(type(of: decoded) == Employee.self, "Expected decoded value to be of type Employee; got \(type(of: decoded)) instead.") } // MARK: - Encoder State // SR-6078 - func testEncoderStateThrowOnEncode() { + @Test func testEncoderStateThrowOnEncode() { struct Wrapper : Encodable { let value: T init(_ value: T) { self.value = value } @@ -420,14 +422,14 @@ class TestPropertyListEncoder : XCTestCase { // MARK: - Decoder State // SR-6048 - func testDecoderStateThrowOnDecode() { + @Test 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 - func testArchiving() { + @Test func testArchiving() { struct CodableType: Codable, Equatable { let willBeNil: String? let arrayOfOptionals: [String?] @@ -450,9 +452,9 @@ class TestPropertyListEncoder : XCTestCase { let keyedUnarchiver = try NSKeyedUnarchiver(forReadingFrom: data) let unarchived = try keyedUnarchiver.decodeTopLevelDecodable(CodableType.self, forKey: "strings") - XCTAssertEqual(unarchived, value) + #expect(unarchived == value) } catch { - XCTFail("Unexpected error: \(error)") + Issue.record(error, "Unexpected error: \(error)") } } #endif @@ -471,7 +473,7 @@ class TestPropertyListEncoder : XCTestCase { let encoder = PropertyListEncoder() encoder.outputFormat = format let _ = try encoder.encode(value) - XCTFail("Encode of top-level \(T.self) was expected to fail.") + Issue.record("Encode of top-level \(T.self) was expected to fail.") } catch {} } @@ -482,25 +484,25 @@ class TestPropertyListEncoder : XCTestCase { encoder.outputFormat = format payload = try encoder.encode(value) } catch { - XCTFail("Failed to encode \(T.self) to plist: \(error)") + Issue.record(error, "Failed to encode \(T.self) to plist: \(error)") } if let expectedPlist = plist { - XCTAssertEqual(expectedPlist, payload, "Produced plist not identical to expected plist.") + #expect(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) - XCTAssertEqual(format, decodedFormat, "Encountered plist format differed from requested format.") - XCTAssertEqual(decoded, value, "\(T.self) did not round-trip to an equal value.") + #expect(format == decodedFormat, "Encountered plist format differed from requested format.") + #expect(decoded == value, "\(T.self) did not round-trip to an equal value.") } catch { - XCTFail("Failed to decode \(T.self) from plist: \(error)") + Issue.record(error, "Failed to decode \(T.self) from plist: \(error)") } } // MARK: - Other tests - func testUnkeyedContainerContainingNulls() throws { + @Test func testUnkeyedContainerContainingNulls() throws { struct UnkeyedContainerContainingNullTestType : Codable, Equatable { var array = [String?]() @@ -535,20 +537,24 @@ class TestPropertyListEncoder : XCTestCase { _testRoundTrip(of: UnkeyedContainerContainingNullTestType(array: array), in: .binary) } - func test_invalidNSDataKey_82142612() { + @Test func test_invalidNSDataKey_82142612() { let data = testData(forResource: "Test_82142612", withExtension: "bad")! let decoder = PropertyListDecoder() - XCTAssertThrowsError(try decoder.decode([String:String].self, from: data)) + #expect(throws: (any Error).self) { + try decoder.decode([String:String].self, from: data) + } // Repeat something similar with XML. let xmlData = "abcdxyz".data(using: String._Encoding.utf8)! - XCTAssertThrowsError(try decoder.decode([String:String].self, from: xmlData)) + #expect(throws: (any Error).self) { + try decoder.decode([String:String].self, from: xmlData) + } } #if FOUNDATION_FRAMEWORK // TODO: Depends on data's range(of:) implementation - func test_nonStringDictionaryKey() { + @Test func test_nonStringDictionaryKey() { let decoder = PropertyListDecoder() let encoder = PropertyListEncoder() encoder.outputFormat = .binary @@ -557,10 +563,14 @@ class TestPropertyListEncoder : XCTestCase { // 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])) - XCTAssertThrowsError(try decoder.decode([String:String].self, from: data)) + #expect(throws: (any Error).self) { + try decoder.decode([String:String].self, from: data) + } let xmlData = "abcdxyz".data(using: String._Encoding.utf8)! - XCTAssertThrowsError(try decoder.decode([String:String].self, from: xmlData)) + #expect(throws: (any Error).self) { + try decoder.decode([String:String].self, from: xmlData) + } } #endif @@ -597,31 +607,37 @@ class TestPropertyListEncoder : XCTestCase { } } - func test_5616259() throws { + @Test func test_5616259() throws { let plistData = testData(forResource: "Test_5616259", withExtension: "bad")! - XCTAssertThrowsError(try PropertyListDecoder().decode([String].self, from: plistData)) + #expect(throws: (any Error).self) { + try PropertyListDecoder().decode([String].self, from: plistData) + } } - func test_genericProperties_XML() throws { + @Test func test_genericProperties_XML() throws { defer { GenericProperties.assertionFailure = nil } let data = testData(forResource: "Generic_XML_Properties", withExtension: "plist")! - XCTAssertNoThrow(try PropertyListDecoder().decode(GenericProperties.self, from: data)) - XCTAssertNil(GenericProperties.assertionFailure) + #expect(throws: Never.self) { + try PropertyListDecoder().decode(GenericProperties.self, from: data) + } + #expect(GenericProperties.assertionFailure == nil) } - func test_genericProperties_binary() throws { + @Test func test_genericProperties_binary() throws { let data = testData(forResource: "Generic_XML_Properties_Binary", withExtension: "plist")! defer { GenericProperties.assertionFailure = nil } - XCTAssertNoThrow(try PropertyListDecoder().decode(GenericProperties.self, from: data)) - XCTAssertNil(GenericProperties.assertionFailure) + #expect(throws: Never.self) { + try PropertyListDecoder().decode(GenericProperties.self, from: data) + } + #expect(GenericProperties.assertionFailure == nil) } // Binary plist parser should parse any version 'bplist0?' - func test_5877417() { + @Test func test_5877417() { var data = testData(forResource: "Generic_XML_Properties_Binary", withExtension: "plist")! // Modify the data so the header starts with bplist0x @@ -629,19 +645,23 @@ class TestPropertyListEncoder : XCTestCase { defer { GenericProperties.assertionFailure = nil } - XCTAssertNoThrow(try PropertyListDecoder().decode(GenericProperties.self, from: data)) - XCTAssertNil(GenericProperties.assertionFailure) + #expect(throws: Never.self) { + try PropertyListDecoder().decode(GenericProperties.self, from: data) + } + #expect(GenericProperties.assertionFailure == nil) } - func test_xmlErrors() { + @Test func test_xmlErrors() { let data = testData(forResource: "Generic_XML_Properties", withExtension: "plist")! let originalXML = String(data: data, encoding: .utf8)! defer { GenericProperties.assertionFailure = nil } // Try an empty plist - XCTAssertThrowsError(try PropertyListDecoder().decode(GenericProperties.self, from: Data())) - XCTAssertNil(GenericProperties.assertionFailure) + #expect(throws: (any Error).self) { + try PropertyListDecoder().decode(GenericProperties.self, from: Data()) + } + #expect(GenericProperties.assertionFailure == nil) // We'll modify this string in all kinds of nasty ways to introduce errors // --- /* @@ -693,21 +713,23 @@ class TestPropertyListEncoder : XCTestCase { 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: String._Encoding.utf8)! - XCTAssertThrowsError(try PropertyListDecoder().decode(GenericProperties.self, from: data), "Case \(name) did not fail as expected") + #expect(throws: (any Error).self, "Case \(name) did not fail as expected") { + try PropertyListDecoder().decode(GenericProperties.self, from: data) + } } } - func test_6164184() throws { + @Test func test_6164184() throws { let xml = "0x721B0x1111-0xFFFF" let array = try PropertyListDecoder().decode([Int].self, from: xml.data(using: String._Encoding.utf8)!) - XCTAssertEqual([0x721B, 0x1111, -0xFFFF], array) + #expect([0x721B, 0x1111, -0xFFFF] == array) } - func test_xmlIntegerEdgeCases() throws { + @Test 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) + #expect(value == expected) } try checkValidEdgeCase("127", type: Int8.self, expected: .max) @@ -734,7 +756,9 @@ class TestPropertyListEncoder : XCTestCase { try checkValidEdgeCase("18446744073709551615", type: UInt64.self, expected: .max) func checkInvalidEdgeCase(_ xml: String, type: T.Type) { - XCTAssertThrowsError(try PropertyListDecoder().decode(type, from: xml.data(using: String._Encoding.utf8)!)) + #expect(throws: (any Error).self) { + try PropertyListDecoder().decode(type, from: xml.data(using: String._Encoding.utf8)!) + } } checkInvalidEdgeCase("128", type: Int8.self) @@ -761,14 +785,14 @@ class TestPropertyListEncoder : XCTestCase { checkInvalidEdgeCase("18446744073709551616", type: UInt64.self) } - func test_xmlIntegerWhitespace() throws { + @Test func test_xmlIntegerWhitespace() throws { let xml = " +\t42\t- 99 -\t0xFACE" let value = try PropertyListDecoder().decode([Int].self, from: xml.data(using: String._Encoding.utf8)!) - XCTAssertEqual(value, [42, -99, -0xFACE]) + #expect(value == [42, -99, -0xFACE]) } - func test_binaryNumberEdgeCases() throws { + @Test func test_binaryNumberEdgeCases() throws { _testRoundTrip(of: [Int8.max], in: .binary) _testRoundTrip(of: [Int8.min], in: .binary) _testRoundTrip(of: [Int16.max], in: .binary) @@ -796,7 +820,7 @@ class TestPropertyListEncoder : XCTestCase { _testRoundTrip(of: [-Double.infinity], in: .binary) } - func test_binaryReals() throws { + @Test func test_binaryReals() throws { func encode(_: T.Type) -> (data: Data, expected: [T]) { let expected: [T] = [ 1.5, @@ -817,9 +841,9 @@ class TestPropertyListEncoder : XCTestCase { let (data, expected) = encode(type) do { let result = try PropertyListDecoder().decode([T].self, from: data) - XCTAssertEqual(result, expected, "Type: \(type)") + #expect(result == expected, "Type: \(type)") } catch { - XCTFail("Expected error \(error) for type: \(type)") + Issue.record(error, "Expected error \(error) for type: \(type)") } } @@ -827,7 +851,7 @@ class TestPropertyListEncoder : XCTestCase { test(Double.self) } - func test_XMLReals() throws { + @Test 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: String._Encoding.utf8)!) let expected: [Float] = [ @@ -839,17 +863,17 @@ class TestPropertyListEncoder : XCTestCase { -.infinity, .infinity ] - XCTAssertEqual(array, expected) + #expect(array == expected) // nan doesn't work with equality. let xmlNAN = "nAnNANnan" let arrayNAN = try PropertyListDecoder().decode([Float].self, from: xmlNAN.data(using: String._Encoding.utf8)!) for val in arrayNAN { - XCTAssertTrue(val.isNaN) + #expect(val.isNaN) } } - func test_bad_XMLReals() { + @Test func test_bad_XMLReals() { let badRealXMLs = [ "0x10", "notanumber", @@ -860,7 +884,9 @@ class TestPropertyListEncoder : XCTestCase { "", ] for xml in badRealXMLs { - XCTAssertThrowsError(try PropertyListDecoder().decode(Float.self, from: xml.data(using: String._Encoding.utf8)!), "Input: \(xml)") + #expect(throws: (any Error).self, "Input: \(xml)") { + try PropertyListDecoder().decode(Float.self, from: xml.data(using: String._Encoding.utf8)!) + } } } @@ -868,14 +894,16 @@ class TestPropertyListEncoder : XCTestCase { // Requires old style plist support // Requires "NEXTStep" decoding in String(bytes:encoding:) for decoding the octal characters - func test_oldStylePlist_invalid() { + @Test func test_oldStylePlist_invalid() { let data = "goodbye cruel world".data(using: String._Encoding.utf16)! - XCTAssertThrowsError(try PropertyListDecoder().decode(String.self, from: data)) + #expect(throws: (any Error).self) { + 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. - func test_oldStylePlist_getSlashedChars_octal() { + @Test 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) @@ -883,11 +911,11 @@ class TestPropertyListEncoder : XCTestCase { let expectedData = testData(forResource: "test_oldStylePlist_getSlashedChars_octal_expected", withExtension: "plist")! let expectedStrings = try! PropertyListDecoder().decode([String].self, from: expectedData) - XCTAssertEqual(actualStrings, expectedStrings) + #expect(actualStrings == expectedStrings) } // Old-style plists support Unicode literals via \U syntax. They can be 1–4 characters wide. - func test_oldStylePlist_getSlashedChars_unicode() { + @Test 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) @@ -895,18 +923,18 @@ class TestPropertyListEncoder : XCTestCase { let expectedData = testData(forResource: "test_oldStylePlist_getSlashedChars_unicode_expected", withExtension: "plist")! let expectedStrings = try! PropertyListDecoder().decode([String].self, from: expectedData) - XCTAssertEqual(actualStrings, expectedStrings) + #expect(actualStrings == expectedStrings) } - func test_oldStylePlist_getSlashedChars_literals() { + @Test 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: String._Encoding.utf8)! let strings = try! PropertyListDecoder().decode([String].self, from: data) - XCTAssertEqual(strings, literals) + #expect(strings == literals) } - func test_oldStylePlist_dictionary() { + @Test func test_oldStylePlist_dictionary() { let data = """ { "test key" = value; testData = ; @@ -926,15 +954,15 @@ class TestPropertyListEncoder : XCTestCase { } do { let decoded = try PropertyListDecoder().decode(Values.self, from: data) - XCTAssertEqual(decoded.testKey, "value") - XCTAssertEqual(decoded.testData, Data([0xfe, 0xed, 0xfa, 0xce])) - XCTAssertEqual(decoded.nestedArray, ["a", "b", "c"]) + #expect(decoded.testKey == "value") + #expect(decoded.testData == Data([0xfe, 0xed, 0xfa, 0xce])) + #expect(decoded.nestedArray == ["a", "b", "c"]) } catch { - XCTFail("Unexpected error: \(error)") + Issue.record(error, "Unexpected error: \(error)") } } - func test_oldStylePlist_stringsFileFormat() { + @Test func test_oldStylePlist_stringsFileFormat() { let data = """ string1 = "Good morning"; string2 = "Good afternoon"; @@ -948,13 +976,13 @@ string3 = "Good evening"; "string2": "Good afternoon", "string3": "Good evening" ] - XCTAssertEqual(decoded, expected) + #expect(decoded == expected) } catch { - XCTFail("Unexpected error: \(error)") + Issue.record(error, "Unexpected error: \(error)") } } - func test_oldStylePlist_comments() { + @Test func test_oldStylePlist_comments() { let data = """ // Initial comment */ string1 = /*Test*/ "Good morning"; // Test @@ -969,9 +997,9 @@ string3 = "Good evening"; // Test "string2": "Good afternoon", "string3": "Good evening" ] - XCTAssertEqual(decoded, expected) + #expect(decoded == expected) } catch { - XCTFail("Unexpected error: \(error)") + Issue.record(error, "Unexpected error: \(error)") } } #endif @@ -979,7 +1007,7 @@ string3 = "Good evening"; // Test #if FOUNDATION_FRAMEWORK // Requires __PlistDictionaryDecoder - func test_oldStylePlist_data() { + @Test func test_oldStylePlist_data() { let data = """ data1 = <7465 73 74 @@ -991,9 +1019,9 @@ data1 = <7465 do { let decoded = try PropertyListDecoder().decode([String:Data].self, from: data) let expected = ["data1" : "testing1234".data(using: String._Encoding.utf8)!] - XCTAssertEqual(decoded, expected) + #expect(decoded == expected) } catch { - XCTFail("Unexpected error: \(error)") + Issue.record(error, "Unexpected error: \(error)") } } #endif @@ -1001,7 +1029,7 @@ data1 = <7465 #if FOUNDATION_FRAMEWORK // Requires PropertyListSerialization - func test_BPlistCollectionReferences() { + @Test 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 ] @@ -1017,24 +1045,26 @@ data1 = <7465 } 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]) + #expect(decoded.a == a as! [[[String]]]) + #expect(decoded.b == b as! [[String]]) + #expect(decoded.c == c as! [String]) } catch { - XCTFail("Unexpected error: \(error)") + Issue.record(error, "Unexpected error: \(error)") } } #endif - func test_reallyOldDates_5842198() throws { + @Test func test_reallyOldDates_5842198() throws { let plist = "\n\n\n0009-09-15T23:16:13Z\n" let data = plist.data(using: String._Encoding.utf8)! - XCTAssertNoThrow(try PropertyListDecoder().decode(Date.self, from: data)) + #expect(throws: Never.self) { + try PropertyListDecoder().decode(Date.self, from: data) + } } - func test_badDates() throws { + @Test func test_badDates() throws { let timeInterval = TimeInterval(-63145612800) // This is the equivalent of an all-zero gregorian date. let date = Date(timeIntervalSinceReferenceDate: timeInterval) @@ -1042,164 +1072,176 @@ data1 = <7465 _testRoundTrip(of: [date], in: .binary) } - func test_badDate_encode() throws { + @Test 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) - XCTAssertEqual(str, "\n\n\n\n\t0000-01-02T00:00:00Z\n\n\n") + #expect(str == "\n\n\n\n\t0000-01-02T00:00:00Z\n\n\n") } - func test_badDate_decode() throws { + @Test 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: String._Encoding.utf8)! let d = try PropertyListDecoder().decode(Date.self, from: data) - XCTAssertEqual(d.timeIntervalSinceReferenceDate, -63145612800) + #expect(d.timeIntervalSinceReferenceDate == -63145612800) } - func test_farFutureDates() throws { + @Test func test_farFutureDates() throws { let date = Date(timeIntervalSince1970: 999999999999.0) _testRoundTrip(of: [date], in: .xml) } - func test_122065123_encode() throws { + @Test 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) - XCTAssertEqual(str, "\n\n\n\n\t2024-02-01T20:43:14Z\n\n\n") // Previously encoded as "2024-01-32T20:43:14Z" + #expect(str == "\n\n\n\n\t2024-02-01T20:43:14Z\n\n\n") // Previously encoded as "2024-01-32T20:43:14Z" } - func test_122065123_decodingCompatibility() throws { + @Test 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: String._Encoding.utf8)! let d = try PropertyListDecoder().decode(Date.self, from: data) - XCTAssertEqual(d.timeIntervalSinceReferenceDate, 728512994) // 2024-02-01T20:43:14Z + #expect(d.timeIntervalSinceReferenceDate == 728512994) // 2024-02-01T20:43:14Z } - func test_multibyteCharacters_escaped_noencoding() throws { + @Test 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) - XCTAssertEqual("These are copyright signs © © blah blah blah.", result) + #expect("These are copyright signs © © blah blah blah." == result) } - func test_escapedCharacters() throws { + @Test func test_escapedCharacters() throws { let plistData = "&'<>"".data(using: String._Encoding.utf8)! let result = try PropertyListDecoder().decode(String.self, from: plistData) - XCTAssertEqual("&'<>\"", result) + #expect("&'<>\"" == result) } - func test_dataWithBOM_utf8() throws { + @Test func test_dataWithBOM_utf8() throws { let bom = Data([0xef, 0xbb, 0xbf]) let plist = bom + "\n\n\nhello\n".data(using: String._Encoding.utf8)! let result = try PropertyListDecoder().decode(String.self, from: plist) - XCTAssertEqual(result, "hello") + #expect(result == "hello") } #if FOUNDATION_FRAMEWORK // TODO: Depends on UTF32 encoding on non-Darwin platforms - func test_dataWithBOM_utf32be() throws { + @Test func test_dataWithBOM_utf32be() throws { let bom = Data([0x00, 0x00, 0xfe, 0xff]) let plist = bom + "\n\n\nhello\n".data(using: String._Encoding.utf32BigEndian)! let result = try PropertyListDecoder().decode(String.self, from: plist) - XCTAssertEqual(result, "hello") + #expect(result == "hello") } - func test_dataWithBOM_utf32le() throws { + @Test func test_dataWithBOM_utf32le() throws { let bom = Data([0xff, 0xfe]) let plist = bom + "\n\n\nhello\n".data(using: String._Encoding.utf16LittleEndian)! let result = try PropertyListDecoder().decode(String.self, from: plist) - XCTAssertEqual(result, "hello") + #expect(result == "hello") } #endif - func test_plistWithBadUTF8() throws { + @Test func test_plistWithBadUTF8() throws { let data = testData(forResource: "bad_plist", withExtension: "bad")! - XCTAssertThrowsError(try PropertyListDecoder().decode([String].self, from: data)) + #expect(throws: (any Error).self) { + try PropertyListDecoder().decode([String].self, from: data) + } } - func test_plistWithEscapedCharacters() throws { + @Test 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) - XCTAssertEqual(result, ["com.apple.security.temporary-exception.sbpl" : "(allow mach-lookup (global-name-regex #\"^[0-9]+$\"))"]) + #expect(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 - func test_returnRightFormatFromParse() throws { + @Test 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) - XCTAssertEqual(format, .openStep) + #expect(format == .openStep) } #endif - func test_decodingEmoji() throws { + @Test 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}" - XCTAssertEqual(expected, result["emoji"]) + #expect(expected == result["emoji"]) } - func test_decodingTooManyCharactersError() throws { + @Test func test_decodingTooManyCharactersError() throws { // Try a plist with too many characters to be a unicode escape sequence 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: String._Encoding.utf8)! - XCTAssertThrowsError(try PropertyListDecoder().decode([String:String].self, from: plist2)) + #expect(throws: (any Error).self) { + try PropertyListDecoder().decode([String:String].self, from: plist2) + } } - func test_roundTripEmoji() throws { + @Test func test_roundTripEmoji() throws { let strings = ["🚘", "👩🏻‍❤️‍👨🏿", "🏋🏽‍♂️🕺🏼🥌"] _testRoundTrip(of: strings, in: .xml) _testRoundTrip(of: strings, in: .binary) } - func test_roundTripEscapedStrings() { + @Test func test_roundTripEscapedStrings() { let strings = ["&", "<", ">"] _testRoundTrip(of: strings, in: .xml) } - func test_unterminatedComment() { + @Test func test_unterminatedComment() { let plist = "".data(using: String._Encoding.utf8)! - XCTAssertThrowsError(try PropertyListDecoder().decode([String].self, from: plist)) + #expect(throws: (any Error).self) { + try PropertyListDecoder().decode([String].self, from: plist) + } } - func test_incompleteOpenTag() { + @Test func test_incompleteOpenTag() { let plist = " = ComparableComparator() - XCTAssertEqual(intDesc.compare(0, 1), .orderedAscending) + #expect(intDesc.compare(0, 1) == .orderedAscending) let result = intDesc.compare(1000, -10) - XCTAssertEqual(result, .orderedDescending) + #expect(result == .orderedDescending) } - func test_order() { + @Test func test_order() { var intDesc: ComparableComparator = ComparableComparator(order: .reverse) - XCTAssertEqual(intDesc.compare(0, 1), .orderedDescending) - XCTAssertEqual(intDesc.compare(1000, -10), .orderedAscending) - XCTAssertEqual(intDesc.compare(100, 100), .orderedSame) - + #expect(intDesc.compare(0, 1) == .orderedDescending) + #expect(intDesc.compare(1000, -10) == .orderedAscending) + #expect(intDesc.compare(100, 100) == .orderedSame) + intDesc.order = .forward - XCTAssertEqual(intDesc.compare(0, 1), .orderedAscending) - XCTAssertEqual(intDesc.compare(1000, -10), .orderedDescending) - XCTAssertEqual(intDesc.compare(100, 100), .orderedSame) + #expect(intDesc.compare(0, 1) == .orderedAscending) + #expect(intDesc.compare(1000, -10) == .orderedDescending) + #expect(intDesc.compare(100, 100) == .orderedSame) } - func test_compare_options_descriptor() { + @Test func test_compare_options_descriptor() { let compareOptions = String.Comparator(options: [.numeric]) - XCTAssertEqual( - compareOptions.compare("ttestest005", "test2"), - "test005".compare("test2", options: [.numeric])) - XCTAssertEqual( - compareOptions.compare("test2", "test005"), - "test2".compare("test005", options: [.numeric])) - } + #expect( + compareOptions.compare("ttestest005", "test2") == + "test005".compare("test2", options: [.numeric]) + ) + #expect( + compareOptions.compare("test2", "test005") == + "test2".compare("test005", options: [.numeric]) + ) + } } diff --git a/Tests/FoundationEssentialsTests/StringTests.swift b/Tests/FoundationEssentialsTests/StringTests.swift index 7eb804460..fb01ab0eb 100644 --- a/Tests/FoundationEssentialsTests/StringTests.swift +++ b/Tests/FoundationEssentialsTests/StringTests.swift @@ -10,22 +10,33 @@ // //===----------------------------------------------------------------------===// +import Testing + #if FOUNDATION_FRAMEWORK @testable import Foundation #else @testable import FoundationEssentials #endif // FOUNDATION_FRAMEWORK -#if canImport(TestSupport) -import TestSupport +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc #endif -final class StringTests : XCTestCase { +struct StringTests { // MARK: - Case mapping - func testCapitalize() { + @Test func testCapitalize() { func test(_ string: String, _ expected: String, file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(string._capitalized(), expected, file: file, line: line) + #expect(string._capitalized() == expected, + sourceLocation: .init( + fileID: #fileID, + filePath: String(describing: file), + line: Int(line), + column: #column + ) + ) } test("iı", "Iı") @@ -63,9 +74,15 @@ final class StringTests : XCTestCase { test("ぁぃぅぇぉ ど ゕゖくけこ", "ぁぃぅぇぉ ど ゕゖくけこ") } - func testTrimmingWhitespace() { + @Test func testTrimmingWhitespace() { func test(_ str: String, _ expected: String, file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(str._trimmingWhitespace(), expected, file: file, line: line) + #expect( + str._trimmingWhitespace() == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } test(" \tABCDEFGAbc \t \t ", "ABCDEFGAbc") test("ABCDEFGAbc \t \t ", "ABCDEFGAbc") @@ -79,9 +96,15 @@ final class StringTests : XCTestCase { test(" \u{202F}\u{00A0} X \u{202F}\u{00A0}", "X") // NBSP and narrow NBSP } - func testTrimmingCharactersWithPredicate() { + @Test func testTrimmingCharactersWithPredicate() { func test(_ str: String, while predicate: (Character) -> Bool, _ expected: Substring, file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(str._trimmingCharacters(while: predicate), expected, file: file, line: line) + #expect( + str._trimmingCharacters(while: predicate) == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } typealias TrimmingPredicate = (Character) -> Bool @@ -152,10 +175,16 @@ final class StringTests : XCTestCase { } else { message = "Actual: nil" } - XCTAssertEqual(result, exp, message, file: file, line: line) + #expect(result == exp, + Comment(rawValue: message), + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } - func testRangeOfString() { + @Test func testRangeOfString() { var tested: String func testASCII(_ string: String, anchored: Bool, backwards: Bool, _ expectation: Range?, file: StaticString = #file, line: UInt = #line) { return _testRangeOfString(tested, string: string, anchored: anchored, backwards: backwards, expectation, file: file, line: line) @@ -205,7 +234,7 @@ final class StringTests : XCTestCase { testASCII("ABCDER", anchored: false, backwards: false, nil) } - func testRangeOfString_graphemeCluster() { + @Test func testRangeOfString_graphemeCluster() { var tested: String func test(_ string: String, anchored: Bool, backwards: Bool, _ expectation: Range?, file: StaticString = #file, line: UInt = #line) { return _testRangeOfString(tested, string: string, anchored: anchored, backwards: backwards, expectation, file: file, line: line) @@ -240,7 +269,7 @@ final class StringTests : XCTestCase { } } - func testRangeOfString_lineSeparator() { + @Test func testRangeOfString_lineSeparator() { func test(_ tested: String, _ string: String, anchored: Bool, backwards: Bool, _ expectation: Range?, file: StaticString = #file, line: UInt = #line) { return _testRangeOfString(tested, string: string, anchored: anchored, backwards: backwards, expectation, file: file, line: line) } @@ -255,13 +284,19 @@ final class StringTests : XCTestCase { test("\r \r\n \r", "\r", anchored: true, backwards: true, 4..<5) } - func testTryFromUTF16() { + @Test func testTryFromUTF16() { func test(_ utf16Buffer: [UInt16], expected: String?, file: StaticString = #file, line: UInt = #line) { let result = utf16Buffer.withUnsafeBufferPointer { String(_utf16: $0) } - XCTAssertEqual(result, expected, file: file, line: line) + #expect( + result == expected, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } test([], expected: "") @@ -283,15 +318,27 @@ final class StringTests : XCTestCase { test([ 0xD800, 0x42 ], expected: nil) } - func testTryFromUTF16_roundtrip() { + @Test func testTryFromUTF16_roundtrip() { func test(_ string: String, file: StaticString = #file, line: UInt = #line) { let utf16Array = Array(string.utf16) let res = utf16Array.withUnsafeBufferPointer { String(_utf16: $0) } - XCTAssertNotNil(res, file: file, line: line) - XCTAssertEqual(res, string, file: file, line: line) + #expect( + res != nil, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + res == string, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } // BMP: consists code points up to U+FFFF @@ -308,105 +355,111 @@ final class StringTests : XCTestCase { test("🏳️‍🌈AB👩‍👩‍👧‍👦ab🕵️‍♀️") } - func testParagraphLineRangeOfSeparator() { + @Test func testParagraphLineRangeOfSeparator() { for separator in ["\n", "\r", "\r\n", "\u{2029}", "\u{2028}", "\u{85}"] { let range = separator.startIndex ..< separator.endIndex let paragraphResult = separator._paragraphBounds(around: range) let lineResult = separator._lineBounds(around: range) - XCTAssertEqual(paragraphResult.start ..< paragraphResult.end, range) - XCTAssertEqual(lineResult.start ..< lineResult.end, range) + #expect(paragraphResult.start ..< paragraphResult.end == range) + #expect(lineResult.start ..< lineResult.end == range) } } - func testAlmostMatchingSeparator() { + @Test func testAlmostMatchingSeparator() { let string = "A\u{200D}B" // U+200D Zero Width Joiner (ZWJ) matches U+2028 Line Separator except for the final UTF-8 scalar let lineResult = string._lineBounds(around: string.startIndex ..< string.startIndex) - XCTAssertEqual(lineResult.start, string.startIndex) - XCTAssertEqual(lineResult.end, string.endIndex) - XCTAssertEqual(lineResult.contentsEnd, string.endIndex) + #expect(lineResult.start == string.startIndex) + #expect(lineResult.end == string.endIndex) + #expect(lineResult.contentsEnd == string.endIndex) } - func testFileSystemRepresentation() { + @Test func testFileSystemRepresentation() { func assertCString(_ ptr: UnsafePointer, equals other: String, file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(String(cString: ptr), other, file: file, line: line) + #expect( + String(cString: ptr) == other, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) } let original = "/Path1/Path Two/Path Three/Some Really Long File Name Section.txt" original.withFileSystemRepresentation { - XCTAssertNotNil($0) + #expect($0 != nil) assertCString($0!, equals: original) } let withWhitespace = original + "\u{2000}\u{2001}" withWhitespace.withFileSystemRepresentation { - XCTAssertNotNil($0) + #expect($0 != nil) assertCString($0!, equals: withWhitespace) } let withHangul = original + "\u{AC00}\u{AC01}" withHangul.withFileSystemRepresentation { buf1 in - XCTAssertNotNil(buf1) + #expect(buf1 != nil) buf1!.withMemoryRebound(to: UInt8.self, capacity: strlen(buf1!)) { buf1Rebound in let fsr = String(decodingCString: buf1Rebound, as: UTF8.self) fsr.withFileSystemRepresentation { buf2 in - XCTAssertNotNil(buf2) - XCTAssertEqual(strcmp(buf1!, buf2!), 0) + #expect(buf2 != nil) + #expect(strcmp(buf1!, buf2!) == 0) } } } let withNullSuffix = original + "\u{0000}\u{0000}" withNullSuffix.withFileSystemRepresentation { - XCTAssertNotNil($0) + #expect($0 != nil) assertCString($0!, equals: original) } #if canImport(Darwin) || FOUNDATION_FRAMEWORK // The buffer should dynamically grow and not be limited to a size of PATH_MAX Array(repeating: "A", count: Int(PATH_MAX) - 1).joined().withFileSystemRepresentation { ptr in - XCTAssertNotNil(ptr) + #expect(ptr != nil) } Array(repeating: "A", count: Int(PATH_MAX)).joined().withFileSystemRepresentation { ptr in - XCTAssertNotNil(ptr) + #expect(ptr != nil) } // The buffer should fit the scalars that expand the most during decomposition for string in ["\u{1D160}", "\u{0CCB}", "\u{0390}"] { string.withFileSystemRepresentation { ptr in - XCTAssertNotNil(ptr, "Could not create file system representation for \(string.debugDescription)") + #expect(ptr != nil, "Could not create file system representation for \(string.debugDescription)") } } #endif } - func testLastPathComponent() { - XCTAssertEqual("".lastPathComponent, "") - XCTAssertEqual("a".lastPathComponent, "a") - XCTAssertEqual("/a".lastPathComponent, "a") - XCTAssertEqual("a/".lastPathComponent, "a") - XCTAssertEqual("/a/".lastPathComponent, "a") + @Test func testLastPathComponent() { + #expect("".lastPathComponent == "") + #expect("a".lastPathComponent == "a") + #expect("/a".lastPathComponent == "a") + #expect("a/".lastPathComponent == "a") + #expect("/a/".lastPathComponent == "a") - XCTAssertEqual("a/b".lastPathComponent, "b") - XCTAssertEqual("/a/b".lastPathComponent, "b") - XCTAssertEqual("a/b/".lastPathComponent, "b") - XCTAssertEqual("/a/b/".lastPathComponent, "b") + #expect("a/b".lastPathComponent == "b") + #expect("/a/b".lastPathComponent == "b") + #expect("a/b/".lastPathComponent == "b") + #expect("/a/b/".lastPathComponent == "b") - XCTAssertEqual("a//".lastPathComponent, "a") - XCTAssertEqual("a////".lastPathComponent, "a") - XCTAssertEqual("/a//".lastPathComponent, "a") - XCTAssertEqual("/a////".lastPathComponent, "a") - XCTAssertEqual("//a//".lastPathComponent, "a") - XCTAssertEqual("/a/b//".lastPathComponent, "b") - XCTAssertEqual("//a//b////".lastPathComponent, "b") + #expect("a//".lastPathComponent == "a") + #expect("a////".lastPathComponent == "a") + #expect("/a//".lastPathComponent == "a") + #expect("/a////".lastPathComponent == "a") + #expect("//a//".lastPathComponent == "a") + #expect("/a/b//".lastPathComponent == "b") + #expect("//a//b////".lastPathComponent == "b") - XCTAssertEqual("/".lastPathComponent, "/") - XCTAssertEqual("//".lastPathComponent, "/") - XCTAssertEqual("/////".lastPathComponent, "/") - XCTAssertEqual("/./..//./..//".lastPathComponent, "..") + #expect("/".lastPathComponent == "/") + #expect("//".lastPathComponent == "/") + #expect("/////".lastPathComponent == "/") + #expect("/./..//./..//".lastPathComponent == "..") } - - func test_dataUsingEncoding() { + + @Test func test_dataUsingEncoding() { let s = "hello 🧮" // Verify things work on substrings too @@ -417,28 +470,28 @@ final class StringTests : XCTestCase { let utf16BEExpected = Data([0, 104, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 216, 62, 221, 238]) let utf16BEOutput = s.data(using: String._Encoding.utf16BigEndian) - XCTAssertEqual(utf16BEOutput, utf16BEExpected) - + #expect(utf16BEOutput == utf16BEExpected) + let utf16BEOutputSubstring = subString.data(using: String._Encoding.utf16BigEndian) - XCTAssertEqual(utf16BEOutputSubstring, utf16BEExpected) - + #expect(utf16BEOutputSubstring == utf16BEExpected) + let utf16LEExpected = Data([104, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 62, 216, 238, 221]) let utf16LEOutput = s.data(using: String._Encoding.utf16LittleEndian) - XCTAssertEqual(utf16LEOutput, utf16LEExpected) + #expect(utf16LEOutput == utf16LEExpected) let utf16LEOutputSubstring = subString.data(using: String._Encoding.utf16LittleEndian) - XCTAssertEqual(utf16LEOutputSubstring, utf16LEExpected) + #expect(utf16LEOutputSubstring == utf16LEExpected) // UTF32 - specific endianness let utf32BEExpected = Data([0, 0, 0, 104, 0, 0, 0, 101, 0, 0, 0, 108, 0, 0, 0, 108, 0, 0, 0, 111, 0, 0, 0, 32, 0, 1, 249, 238]) let utf32BEOutput = s.data(using: String._Encoding.utf32BigEndian) - XCTAssertEqual(utf32BEOutput, utf32BEExpected) + #expect(utf32BEOutput == utf32BEExpected) let utf32LEExpected = Data([104, 0, 0, 0, 101, 0, 0, 0, 108, 0, 0, 0, 108, 0, 0, 0, 111, 0, 0, 0, 32, 0, 0, 0, 238, 249, 1, 0]) let utf32LEOutput = s.data(using: String._Encoding.utf32LittleEndian) - XCTAssertEqual(utf32LEOutput, utf32LEExpected) - + #expect(utf32LEOutput == utf32LEExpected) + // UTF16 and 32, platform endianness let utf16LEWithBOM = Data([0xFF, 0xFE]) + utf16LEExpected @@ -453,12 +506,12 @@ final class StringTests : XCTestCase { if bom.littleEndian == bom { // We are on a little endian system. Expect a LE BOM - XCTAssertEqual(utf16Output, utf16LEWithBOM) - XCTAssertEqual(utf32Output, utf32LEWithBOM) + #expect(utf16Output == utf16LEWithBOM) + #expect(utf32Output == utf32LEWithBOM) } else if bom.bigEndian == bom { // We are on a big endian system. Expect a BE BOM - XCTAssertEqual(utf16Output, utf16BEWithBOM) - XCTAssertEqual(utf32Output, utf32BEWithBOM) + #expect(utf16Output == utf16BEWithBOM) + #expect(utf32Output == utf32BEWithBOM) } else { fatalError("Unknown endianness") } @@ -466,63 +519,63 @@ final class StringTests : XCTestCase { // UTF16 let utf16BEString = String(bytes: utf16BEExpected, encoding: String._Encoding.utf16BigEndian) - XCTAssertEqual(s, utf16BEString) - + #expect(s == utf16BEString) + let utf16LEString = String(bytes: utf16LEExpected, encoding: String._Encoding.utf16LittleEndian) - XCTAssertEqual(s, utf16LEString) - + #expect(s == utf16LEString) + let utf16LEBOMString = String(bytes: utf16LEWithBOM, encoding: String._Encoding.utf16) - XCTAssertEqual(s, utf16LEBOMString) - + #expect(s == utf16LEBOMString) + let utf16BEBOMString = String(bytes: utf16BEWithBOM, encoding: String._Encoding.utf16) - XCTAssertEqual(s, utf16BEBOMString) - + #expect(s == utf16BEBOMString) + // No BOM, no encoding specified. We assume the data is big endian, which leads to garbage (but not nil). let utf16LENoBOMString = String(bytes: utf16LEExpected, encoding: String._Encoding.utf16) - XCTAssertNotNil(utf16LENoBOMString) + #expect(utf16LENoBOMString != nil) // No BOM, no encoding specified. We assume the data is big endian, which leads to an expected value. let utf16BENoBOMString = String(bytes: utf16BEExpected, encoding: String._Encoding.utf16) - XCTAssertEqual(s, utf16BENoBOMString) + #expect(s == utf16BENoBOMString) // UTF32 let utf32BEString = String(bytes: utf32BEExpected, encoding: String._Encoding.utf32BigEndian) - XCTAssertEqual(s, utf32BEString) - + #expect(s == utf32BEString) + let utf32LEString = String(bytes: utf32LEExpected, encoding: String._Encoding.utf32LittleEndian) - XCTAssertEqual(s, utf32LEString) - + #expect(s == utf32LEString) + let utf32BEBOMString = String(bytes: utf32BEWithBOM, encoding: String._Encoding.utf32) - XCTAssertEqual(s, utf32BEBOMString) - + #expect(s == utf32BEBOMString) + let utf32LEBOMString = String(bytes: utf32LEWithBOM, encoding: String._Encoding.utf32) - XCTAssertEqual(s, utf32LEBOMString) - + #expect(s == utf32LEBOMString) + // No BOM, no encoding specified. We assume the data is big endian, which leads to a nil. let utf32LENoBOMString = String(bytes: utf32LEExpected, encoding: String._Encoding.utf32) - XCTAssertNil(utf32LENoBOMString) - + #expect(utf32LENoBOMString == nil) + // No BOM, no encoding specified. We assume the data is big endian, which leads to an expected value. let utf32BENoBOMString = String(bytes: utf32BEExpected, encoding: String._Encoding.utf32) - XCTAssertEqual(s, utf32BENoBOMString) + #expect(s == utf32BENoBOMString) // Check what happens when we mismatch a string with a BOM and the encoding. The bytes are interpreted according to the specified encoding regardless of the BOM, the BOM is preserved, and the String will look garbled. However the bytes are preserved as-is. This is the expected behavior for UTF16. let utf16LEBOMStringMismatch = String(bytes: utf16LEWithBOM, encoding: String._Encoding.utf16BigEndian) let utf16LEBOMStringMismatchBytes = utf16LEBOMStringMismatch?.data(using: String._Encoding.utf16BigEndian) - XCTAssertEqual(utf16LEWithBOM, utf16LEBOMStringMismatchBytes) - + #expect(utf16LEWithBOM == utf16LEBOMStringMismatchBytes) + let utf16BEBOMStringMismatch = String(bytes: utf16BEWithBOM, encoding: String._Encoding.utf16LittleEndian) let utf16BEBomStringMismatchBytes = utf16BEBOMStringMismatch?.data(using: String._Encoding.utf16LittleEndian) - XCTAssertEqual(utf16BEWithBOM, utf16BEBomStringMismatchBytes) + #expect(utf16BEWithBOM == utf16BEBomStringMismatchBytes) // For a UTF32 mismatch, the string creation simply returns nil. let utf32LEBOMStringMismatch = String(bytes: utf32LEWithBOM, encoding: String._Encoding.utf32BigEndian) - XCTAssertNil(utf32LEBOMStringMismatch) - + #expect(utf32LEBOMStringMismatch == nil) + let utf32BEBOMStringMismatch = String(bytes: utf32BEWithBOM, encoding: String._Encoding.utf32LittleEndian) - XCTAssertNil(utf32BEBOMStringMismatch) + #expect(utf32BEBOMStringMismatch == nil) } } @@ -536,7 +589,7 @@ extension String { } } -final class StringTestsStdlib: XCTestCase { +struct StringTestsStdlib { // The most simple subclass of NSString that CoreFoundation does not know // about. @@ -595,7 +648,7 @@ final class StringTestsStdlib: XCTestCase { do { try FileManager.default.removeItem(at: rootURL) } catch { - XCTFail() + Issue.record(error) } } @@ -605,17 +658,17 @@ final class StringTestsStdlib: XCTestCase { block(fileURL, nonExisting) } - func test_Encodings() { + @Test func test_Encodings() { let availableEncodings: [String.Encoding] = String.availableStringEncodings - expectNotEqual(0, availableEncodings.count) + #expect(0 != availableEncodings.count) let defaultCStringEncoding = String.defaultCStringEncoding - expectTrue(availableEncodings.contains(defaultCStringEncoding)) + #expect(availableEncodings.contains(defaultCStringEncoding)) - expectNotEqual("", String.localizedName(of: .utf8)) + #expect("" != String.localizedName(of: .utf8)) } - func test_NSStringEncoding() { + @Test func test_NSStringEncoding() { // Make sure NSStringEncoding and its values are type-compatible. var enc: String.Encoding enc = .windowsCP1250 @@ -623,10 +676,10 @@ final class StringTestsStdlib: XCTestCase { enc = .utf32BigEndian enc = .ascii enc = .utf8 - expectEqual(.utf8, enc) + #expect(.utf8 == enc) } - func test_NSStringEncoding_Hashable() { + @Test func test_NSStringEncoding_Hashable() { let instances: [String.Encoding] = [ .windowsCP1250, .utf32LittleEndian, @@ -637,117 +690,123 @@ final class StringTestsStdlib: XCTestCase { checkHashable(instances, equalityOracle: { $0 == $1 }) } - func test_localizedStringWithFormat() { + @Test func test_localizedStringWithFormat() { let world: NSString = "world" - expectEqual("Hello, world!%42", String.localizedStringWithFormat( + #expect("Hello, world!%42" == String.localizedStringWithFormat( "Hello, %@!%%%ld", world, 42)) - expectEqual("0.5", String.init(format: "%g", locale: Locale(identifier: "en_US"), 0.5)) - expectEqual("0,5", String.init(format: "%g", locale: Locale(identifier: "uk"), 0.5)) + #expect("0.5" == String.init(format: "%g", locale: Locale(identifier: "en_US"), 0.5)) + #expect("0,5" == String.init(format: "%g", locale: Locale(identifier: "uk"), 0.5)) } - func test_init_contentsOfFile_encoding() { + @Test func test_init_contentsOfFile_encoding() { withTemporaryStringFile { existingURL, nonExistentURL in do { let content = try String( contentsOfFile: existingURL.path, encoding: .ascii) - expectEqual( - "Lorem ipsum dolor sit amet, consectetur adipisicing elit,", - content.lines[0]) + #expect( + "Lorem ipsum dolor sit amet, consectetur adipisicing elit," == + content.lines[0] + ) } catch { - XCTFail(error.localizedDescription) + Issue.record(error) } do { let _ = try String( contentsOfFile: nonExistentURL.path, encoding: .ascii) - XCTFail() + Issue.record() } catch { } } } - func test_init_contentsOfFile_usedEncoding() { + @Test func test_init_contentsOfFile_usedEncoding() { withTemporaryStringFile { existingURL, nonExistentURL in do { var usedEncoding: String.Encoding = String.Encoding(rawValue: 0) let content = try String( contentsOfFile: existingURL.path(), usedEncoding: &usedEncoding) - expectNotEqual(0, usedEncoding.rawValue) - expectEqual( - "Lorem ipsum dolor sit amet, consectetur adipisicing elit,", - content.lines[0]) + #expect(0 != usedEncoding.rawValue) + #expect( + "Lorem ipsum dolor sit amet, consectetur adipisicing elit," == + content.lines[0] + ) } catch { - XCTFail(error.localizedDescription) + Issue.record(error) } let usedEncoding: String.Encoding = String.Encoding(rawValue: 0) do { _ = try String(contentsOfFile: nonExistentURL.path()) - XCTFail() + Issue.record() } catch { - expectEqual(0, usedEncoding.rawValue) + #expect(0 == usedEncoding.rawValue) } } } - func test_init_contentsOf_encoding() { + @Test func test_init_contentsOf_encoding() { withTemporaryStringFile { existingURL, nonExistentURL in do { let content = try String( contentsOf: existingURL, encoding: .ascii) - expectEqual( - "Lorem ipsum dolor sit amet, consectetur adipisicing elit,", - content.lines[0]) + #expect( + "Lorem ipsum dolor sit amet, consectetur adipisicing elit," == + content.lines[0] + ) } catch { - XCTFail(error.localizedDescription) + Issue.record(error) } do { _ = try String(contentsOf: nonExistentURL, encoding: .ascii) - XCTFail() + Issue.record() } catch { } } } - func test_init_contentsOf_usedEncoding() { + @Test func test_init_contentsOf_usedEncoding() { withTemporaryStringFile { existingURL, nonExistentURL in do { var usedEncoding: String.Encoding = String.Encoding(rawValue: 0) let content = try String( contentsOf: existingURL, usedEncoding: &usedEncoding) - expectNotEqual(0, usedEncoding.rawValue) - expectEqual( - "Lorem ipsum dolor sit amet, consectetur adipisicing elit,", - content.lines[0]) + #expect(0 != usedEncoding.rawValue) + #expect( + "Lorem ipsum dolor sit amet, consectetur adipisicing elit," == + content.lines[0] + ) } catch { - XCTFail(error.localizedDescription) + Issue.record(error) } var usedEncoding: String.Encoding = String.Encoding(rawValue: 0) do { _ = try String(contentsOf: nonExistentURL, usedEncoding: &usedEncoding) - XCTFail() + Issue.record() } catch { - expectEqual(0, usedEncoding.rawValue) + #expect(0 == usedEncoding.rawValue) } } } - func test_init_cString_encoding() { + @Test func test_init_cString_encoding() { "foo, a basmati bar!".withCString { - expectEqual("foo, a basmati bar!", - String(cString: $0, encoding: String.defaultCStringEncoding)) + #expect( + "foo, a basmati bar!" == + String(cString: $0, encoding: String.defaultCStringEncoding) + ) } } - func test_init_utf8String() { + @Test func test_init_utf8String() { let s = "foo あいう" let up = UnsafeMutablePointer.allocate(capacity: 100) var i = 0 @@ -758,25 +817,26 @@ final class StringTestsStdlib: XCTestCase { up[i] = 0 let cstr = UnsafeMutableRawPointer(up) .bindMemory(to: CChar.self, capacity: 100) - expectEqual(s, String(utf8String: cstr)) + #expect(s == String(utf8String: cstr)) up.deallocate() } - func test_canBeConvertedToEncoding() { - expectTrue("foo".canBeConverted(to: .ascii)) - expectFalse("あいう".canBeConverted(to: .ascii)) + @Test func test_canBeConvertedToEncoding() { + #expect("foo".canBeConverted(to: .ascii)) + #expect("あいう".canBeConverted(to: .ascii) == false) } - func test_capitalized() { - expectEqual("Foo Foo Foo Foo", "foo Foo fOO FOO".capitalized) - expectEqual("Жжж", "жжж".capitalized) + @Test func test_capitalized() { + #expect("Foo Foo Foo Foo" == "foo Foo fOO FOO".capitalized) + #expect("Жжж" == "жжж".capitalized) } - func test_localizedCapitalized() { - expectEqual( - "Foo Foo Foo Foo", - "foo Foo fOO FOO".capitalized(with: Locale(identifier: "en"))) - expectEqual("Жжж", "жжж".capitalized(with: Locale(identifier: "en"))) + @Test func test_localizedCapitalized() { + #expect( + "Foo Foo Foo Foo" == + "foo Foo fOO FOO".capitalized(with: Locale(identifier: "en")) + ) + #expect("Жжж" == "жжж".capitalized(with: Locale(identifier: "en"))) // // Special casing. @@ -785,12 +845,12 @@ final class StringTestsStdlib: XCTestCase { // U+0069 LATIN SMALL LETTER I // to upper case: // U+0049 LATIN CAPITAL LETTER I - expectEqual("Iii Iii", "iii III".capitalized(with: Locale(identifier: "en"))) + #expect("Iii Iii" == "iii III".capitalized(with: Locale(identifier: "en"))) // U+0069 LATIN SMALL LETTER I // to upper case in Turkish locale: // U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE - expectEqual("\u{0130}ii Iıı", "iii III".capitalized(with: Locale(identifier: "tr"))) + #expect("\u{0130}ii Iıı" == "iii III".capitalized(with: Locale(identifier: "tr"))) } /// Checks that executing the operation in the locale with the given @@ -814,22 +874,24 @@ final class StringTestsStdlib: XCTestCase { Locale(identifier: $0) } ?? nil - expectEqual( - expected, op(locale), - message()) + #expect( + expected == op(locale), + Comment(rawValue: message()) + ) } - func test_capitalizedString() { + @Test func test_capitalizedString() { expectLocalizedEquality( "Foo Foo Foo Foo", { loc in "foo Foo fOO FOO".capitalized(with: loc) }) expectLocalizedEquality("Жжж", { loc in "жжж".capitalized(with: loc) }) - expectEqual( - "Foo Foo Foo Foo", - "foo Foo fOO FOO".capitalized(with: nil)) - expectEqual("Жжж", "жжж".capitalized(with: nil)) + #expect( + "Foo Foo Foo Foo" == + "foo Foo fOO FOO".capitalized(with: nil) + ) + #expect("Жжж" == "жжж".capitalized(with: nil)) // // Special casing. @@ -850,67 +912,63 @@ final class StringTestsStdlib: XCTestCase { { loc in "iii III".capitalized(with: loc) }, "tr") } - func test_caseInsensitiveCompare() { - expectEqual(ComparisonResult.orderedSame, - "abCD".caseInsensitiveCompare("AbCd")) - expectEqual(ComparisonResult.orderedAscending, - "abCD".caseInsensitiveCompare("AbCdE")) + @Test func test_caseInsensitiveCompare() { + #expect( + ComparisonResult.orderedSame == + "abCD".caseInsensitiveCompare("AbCd") + ) + #expect( + ComparisonResult.orderedAscending == + "abCD".caseInsensitiveCompare("AbCdE") + ) - expectEqual(ComparisonResult.orderedSame, - "абвг".caseInsensitiveCompare("АбВг")) - expectEqual(ComparisonResult.orderedAscending, - "абВГ".caseInsensitiveCompare("АбВгД")) + #expect( + ComparisonResult.orderedSame == + "абвг".caseInsensitiveCompare("АбВг") + ) + #expect( + ComparisonResult.orderedAscending == + "абВГ".caseInsensitiveCompare("АбВгД") + ) } - func test_commonPrefix() { - expectEqual("ab", - "abcd".commonPrefix(with: "abdc", options: [])) - expectEqual("abC", - "abCd".commonPrefix(with: "abce", options: .caseInsensitive)) + @Test func test_commonPrefix() { + #expect("ab" == "abcd".commonPrefix(with: "abdc", options: [])) + #expect("abC" == "abCd".commonPrefix(with: "abce", options: .caseInsensitive)) - expectEqual("аб", - "абвг".commonPrefix(with: "абгв", options: [])) - expectEqual("абВ", - "абВг".commonPrefix(with: "абвд", options: .caseInsensitive)) + #expect("аб" == "абвг".commonPrefix(with: "абгв", options: [])) + #expect("абВ" == "абВг".commonPrefix(with: "абвд", options: .caseInsensitive)) } - func test_compare() { - expectEqual(ComparisonResult.orderedSame, - "abc".compare("abc")) - expectEqual(ComparisonResult.orderedAscending, - "абв".compare("где")) + @Test func test_compare() { + #expect(.orderedSame == "abc".compare("abc")) + #expect(.orderedAscending == "абв".compare("где")) - expectEqual(ComparisonResult.orderedSame, - "abc".compare("abC", options: .caseInsensitive)) - expectEqual(ComparisonResult.orderedSame, - "абв".compare("абВ", options: .caseInsensitive)) + #expect(.orderedSame == "abc".compare("abC", options: .caseInsensitive)) + #expect(.orderedSame == "абв".compare("абВ", options: .caseInsensitive)) do { let s = "abcd" let r = s.index(after: s.startIndex).., stop: inout Bool) in substrings.append(substring!) - expectEqual(substring, String(s[substringRange])) - expectEqual(substring, String(s[enclosingRange])) + #expect(substring == String(s[substringRange])) + #expect(substring == String(s[enclosingRange])) } - expectEqual(["\u{304b}\u{3099}", "お", "☺️", "😀"], substrings) + #expect(["\u{304b}\u{3099}", "お", "☺️", "😀"] == substrings) } do { var substrings: [String] = [] @@ -1120,21 +1188,21 @@ final class StringTestsStdlib: XCTestCase { (substring_: String?, substringRange: Range, enclosingRange: Range, stop: inout Bool) in - XCTAssertNil(substring_) + #expect(substring_ == nil) let substring = s[substringRange] substrings.append(String(substring)) expectEqual(substring, s[enclosingRange]) } - expectEqual(["\u{304b}\u{3099}", "お", "☺️", "😀"], substrings) + #expect(["\u{304b}\u{3099}", "お", "☺️", "😀"] == substrings) } } - func test_fastestEncoding() { + @Test func test_fastestEncoding() { let availableEncodings: [String.Encoding] = String.availableStringEncodings - expectTrue(availableEncodings.contains("abc".fastestEncoding)) + #expect(availableEncodings.contains("abc".fastestEncoding)) } - func test_getBytes() { + @Test func test_getBytes() { let s = "abc абв def где gh жз zzz" let startIndex = s.index(s.startIndex, offsetBy: 8) let endIndex = s.index(s.startIndex, offsetBy: 22) @@ -1152,11 +1220,11 @@ final class StringTestsStdlib: XCTestCase { encoding: .utf8, options: [], range: startIndex.. NFKD normalization as implemented by @@ -1605,55 +1677,56 @@ final class StringTestsStdlib: XCTestCase { expectEqual("\u{30c0}クテン", "\u{ff80}\u{ff9e}クテン".precomposedStringWithCompatibilityMapping) */ - expectEqual("ffi", "\u{fb03}".precomposedStringWithCompatibilityMapping) + #expect("ffi" == "\u{fb03}".precomposedStringWithCompatibilityMapping) } - func test_propertyList() { - expectEqual(["foo", "bar"], - "(\"foo\", \"bar\")".propertyList() as! [String]) + @Test func test_propertyList() { + #expect(["foo", "bar"] == "(\"foo\", \"bar\")".propertyList() as! [String]) } - func test_propertyListFromStringsFileFormat() { - expectEqual(["foo": "bar", "baz": "baz"], - "/* comment */\n\"foo\" = \"bar\";\n\"baz\";" - .propertyListFromStringsFileFormat() as Dictionary) + @Test func test_propertyListFromStringsFileFormat() { + #expect( + ["foo": "bar", "baz": "baz"] == + "/* comment */\n\"foo\" = \"bar\";\n\"baz\";" + .propertyListFromStringsFileFormat() as Dictionary + ) } - func test_rangeOfCharacterFrom() { + @Test func test_rangeOfCharacterFrom() { do { let charset = CharacterSet(charactersIn: "абв") do { let s = "Глокая куздра" let r = s.rangeOfCharacter(from: charset)! - expectEqual(s.index(s.startIndex, offsetBy: 4), r.lowerBound) - expectEqual(s.index(s.startIndex, offsetBy: 5), r.upperBound) + #expect(s.index(s.startIndex, offsetBy: 4) == r.lowerBound) + #expect(s.index(s.startIndex, offsetBy: 5) == r.upperBound) } do { - XCTAssertNil("клмн".rangeOfCharacter(from: charset)) + #expect("клмн".rangeOfCharacter(from: charset) == nil) } do { let s = "абвклмнабвклмн" let r = s.rangeOfCharacter(from: charset, options: .backwards)! - expectEqual(s.index(s.startIndex, offsetBy: 9), r.lowerBound) - expectEqual(s.index(s.startIndex, offsetBy: 10), r.upperBound) + #expect(s.index(s.startIndex, offsetBy: 9) == r.lowerBound) + #expect(s.index(s.startIndex, offsetBy: 10) == r.upperBound) } do { let s = "абвклмнабв" let r = s.rangeOfCharacter(from: charset, range: s.index(s.startIndex, offsetBy: 3)..( @@ -1698,110 +1787,110 @@ final class StringTestsStdlib: XCTestCase { return string.distance(from: string.startIndex, to: range.lowerBound) ..< string.distance(from: string.startIndex, to: range.upperBound) } - func test_range() { + @Test func test_range() { do { let s = "" - XCTAssertNil(s.range(of: "")) - XCTAssertNil(s.range(of: "abc")) + #expect(s.range(of: "") == nil) + #expect(s.range(of: "abc") == nil) } do { let s = "abc" - XCTAssertNil(s.range(of: "")) - XCTAssertNil(s.range(of: "def")) - expectEqual(0..<3, toIntRange(s, s.range(of: "abc"))) + #expect(s.range(of: "") == nil) + #expect(s.range(of: "def") == nil) + #expect(0..<3 == toIntRange(s, s.range(of: "abc"))) } do { let s = "さ\u{3099}し\u{3099}す\u{3099}せ\u{3099}そ\u{3099}" - expectEqual(2..<3, toIntRange(s, s.range(of: "す\u{3099}"))) - expectEqual(2..<3, toIntRange(s, s.range(of: "\u{305a}"))) + #expect(2..<3 == toIntRange(s, s.range(of: "す\u{3099}"))) + #expect(2..<3 == toIntRange(s, s.range(of: "\u{305a}"))) - XCTAssertNil(s.range(of: "\u{3099}す")) - XCTAssertNil(s.range(of: "す")) + #expect(s.range(of: "\u{3099}す") == nil) + #expect(s.range(of: "す") == nil) - XCTAssertNil(s.range(of: "\u{3099}")) - expectEqual("\u{3099}", s[s.range(of: "\u{3099}", options: .literal)!]) + #expect(s.range(of: "\u{3099}") == nil) + #expect("\u{3099}" == s[s.range(of: "\u{3099}", options: .literal)!]) } do { let s = "а\u{0301}б\u{0301}в\u{0301}г\u{0301}" - expectEqual(0..<1, toIntRange(s, s.range(of: "а\u{0301}"))) - expectEqual(1..<2, toIntRange(s, s.range(of: "б\u{0301}"))) + #expect(0..<1 == toIntRange(s, s.range(of: "а\u{0301}"))) + #expect(1..<2 == toIntRange(s, s.range(of: "б\u{0301}"))) - XCTAssertNil(s.range(of: "б")) - XCTAssertNil(s.range(of: "\u{0301}б")) + #expect(s.range(of: "б") == nil) + #expect(s.range(of: "\u{0301}б") == nil) - XCTAssertNil(s.range(of: "\u{0301}")) - expectEqual("\u{0301}", s[s.range(of: "\u{0301}", options: .literal)!]) + #expect(s.range(of: "\u{0301}") == nil) + #expect("\u{0301}" == s[s.range(of: "\u{0301}", options: .literal)!]) } } - func test_contains() { - expectFalse("".contains("")) - expectFalse("".contains("a")) - expectFalse("a".contains("")) - expectFalse("a".contains("b")) - expectTrue("a".contains("a")) - expectFalse("a".contains("A")) - expectFalse("A".contains("a")) - expectFalse("a".contains("a\u{0301}")) - expectTrue("a\u{0301}".contains("a\u{0301}")) - expectFalse("a\u{0301}".contains("a")) - expectFalse("a\u{0301}".contains("\u{0301}")) // Update to match stdlib's `firstRange` and `contains` result - expectFalse("a".contains("\u{0301}")) + @Test func test_contains() { + #expect("".contains("") == false) + #expect("".contains("a") == false) + #expect("a".contains("") == false) + #expect("a".contains("b") == false) + #expect("a".contains("a")) + #expect("a".contains("A") == false) + #expect("A".contains("a") == false) + #expect("a".contains("a\u{0301}") == false) + #expect("a\u{0301}".contains("a\u{0301}")) + #expect("a\u{0301}".contains("a") == false) + #expect("a\u{0301}".contains("\u{0301}") == false) // Update to match stdlib's `firstRange` an == falsed `contains` result + #expect("a".contains("\u{0301}") == false) - expectFalse("i".contains("I")) - expectFalse("I".contains("i")) - expectFalse("\u{0130}".contains("i")) - expectFalse("i".contains("\u{0130}")) - expectFalse("\u{0130}".contains("ı")) + #expect("i".contains("I") == false) + #expect("I".contains("i") == false) + #expect("\u{0130}".contains("i") == false) + #expect("i".contains("\u{0130}") == false) + #expect("\u{0130}".contains("ı") == false) } - func test_localizedCaseInsensitiveContains() { + @Test func test_localizedCaseInsensitiveContains() { let en = Locale(identifier: "en") - expectFalse("".localizedCaseInsensitiveContains("", locale: en)) - expectFalse("".localizedCaseInsensitiveContains("a", locale: en)) - expectFalse("a".localizedCaseInsensitiveContains("", locale: en)) - expectFalse("a".localizedCaseInsensitiveContains("b", locale: en)) - expectTrue("a".localizedCaseInsensitiveContains("a", locale: en)) - expectTrue("a".localizedCaseInsensitiveContains("A", locale: en)) - expectTrue("A".localizedCaseInsensitiveContains("a", locale: en)) - expectFalse("a".localizedCaseInsensitiveContains("a\u{0301}", locale: en)) - expectTrue("a\u{0301}".localizedCaseInsensitiveContains("a\u{0301}", locale: en)) - expectFalse("a\u{0301}".localizedCaseInsensitiveContains("a", locale: en)) - expectTrue("a\u{0301}".localizedCaseInsensitiveContains("\u{0301}", locale: en)) - expectFalse("a".localizedCaseInsensitiveContains("\u{0301}", locale: en)) - - expectTrue("i".localizedCaseInsensitiveContains("I", locale: en)) - expectTrue("I".localizedCaseInsensitiveContains("i", locale: en)) - expectFalse("\u{0130}".localizedCaseInsensitiveContains("i", locale: en)) - expectFalse("i".localizedCaseInsensitiveContains("\u{0130}", locale: en)) - - expectFalse("\u{0130}".localizedCaseInsensitiveContains("ı", locale: Locale(identifier: "tr"))) - } - - func test_localizedStandardContains() { + #expect("".localizedCaseInsensitiveContains("", locale: en) == false) + #expect("".localizedCaseInsensitiveContains("a", locale: en) == false) + #expect("a".localizedCaseInsensitiveContains("", locale: en) == false) + #expect("a".localizedCaseInsensitiveContains("b", locale: en) == false) + #expect("a".localizedCaseInsensitiveContains("a", locale: en)) + #expect("a".localizedCaseInsensitiveContains("A", locale: en)) + #expect("A".localizedCaseInsensitiveContains("a", locale: en)) + #expect("a".localizedCaseInsensitiveContains("a\u{0301}", locale: en) == false) + #expect("a\u{0301}".localizedCaseInsensitiveContains("a\u{0301}", locale: en)) + #expect("a\u{0301}".localizedCaseInsensitiveContains("a", locale: en) == false) + #expect("a\u{0301}".localizedCaseInsensitiveContains("\u{0301}", locale: en)) + #expect("a".localizedCaseInsensitiveContains("\u{0301}", locale: en) == false) + + #expect("i".localizedCaseInsensitiveContains("I", locale: en)) + #expect("I".localizedCaseInsensitiveContains("i", locale: en)) + #expect("\u{0130}".localizedCaseInsensitiveContains("i", locale: en) == false) + #expect("i".localizedCaseInsensitiveContains("\u{0130}", locale: en) == false) + + #expect("\u{0130}".localizedCaseInsensitiveContains("ı", locale: Locale(identifier: "tr")) == false) + } + + @Test func test_localizedStandardContains() { let en = Locale(identifier: "en") - expectFalse("".localizedStandardContains("", locale: en)) - expectFalse("".localizedStandardContains("a", locale: en)) - expectFalse("a".localizedStandardContains("", locale: en)) - expectFalse("a".localizedStandardContains("b", locale: en)) - expectTrue("a".localizedStandardContains("a", locale: en)) - expectTrue("a".localizedStandardContains("A", locale: en)) - expectTrue("A".localizedStandardContains("a", locale: en)) - expectTrue("a".localizedStandardContains("a\u{0301}", locale: en)) - expectTrue("a\u{0301}".localizedStandardContains("a\u{0301}", locale: en)) - expectTrue("a\u{0301}".localizedStandardContains("a", locale: en)) - expectTrue("a\u{0301}".localizedStandardContains("\u{0301}", locale: en)) - expectFalse("a".localizedStandardContains("\u{0301}", locale: en)) - - expectTrue("i".localizedStandardContains("I", locale: en)) - expectTrue("I".localizedStandardContains("i", locale: en)) - expectTrue("\u{0130}".localizedStandardContains("i", locale: en)) - expectTrue("i".localizedStandardContains("\u{0130}", locale: en)) - - expectTrue("\u{0130}".localizedStandardContains("ı", locale: Locale(identifier: "tr"))) - } - - func test_localizedStandardRange() { + #expect("".localizedStandardContains("", locale: en) == false) + #expect("".localizedStandardContains("a", locale: en) == false) + #expect("a".localizedStandardContains("", locale: en) == false) + #expect("a".localizedStandardContains("b", locale: en) == false) + #expect("a".localizedStandardContains("a", locale: en)) + #expect("a".localizedStandardContains("A", locale: en)) + #expect("A".localizedStandardContains("a", locale: en)) + #expect("a".localizedStandardContains("a\u{0301}", locale: en)) + #expect("a\u{0301}".localizedStandardContains("a\u{0301}", locale: en)) + #expect("a\u{0301}".localizedStandardContains("a", locale: en)) + #expect("a\u{0301}".localizedStandardContains("\u{0301}", locale: en)) + #expect("a".localizedStandardContains("\u{0301}", locale: en) == false) + + #expect("i".localizedStandardContains("I", locale: en)) + #expect("I".localizedStandardContains("i", locale: en)) + #expect("\u{0130}".localizedStandardContains("i", locale: en)) + #expect("i".localizedStandardContains("\u{0130}", locale: en)) + + #expect("\u{0130}".localizedStandardContains("ı", locale: Locale(identifier: "tr"))) + } + + @Test func test_localizedStandardRange() { func rangeOf(_ string: String, _ substring: String, locale: Locale) -> Range? { return toIntRange( string, string.localizedStandardRange(of: substring, locale: locale)) @@ -1809,37 +1898,38 @@ final class StringTestsStdlib: XCTestCase { let en = Locale(identifier: "en") - XCTAssertNil(rangeOf("", "", locale: en)) - XCTAssertNil(rangeOf("", "a", locale: en)) - XCTAssertNil(rangeOf("a", "", locale: en)) - XCTAssertNil(rangeOf("a", "b", locale: en)) - expectEqual(0..<1, rangeOf("a", "a", locale: en)) - expectEqual(0..<1, rangeOf("a", "A", locale: en)) - expectEqual(0..<1, rangeOf("A", "a", locale: en)) - expectEqual(0..<1, rangeOf("a", "a\u{0301}", locale: en)) - expectEqual(0..<1, rangeOf("a\u{0301}", "a\u{0301}", locale: en)) - expectEqual(0..<1, rangeOf("a\u{0301}", "a", locale: en)) + #expect(rangeOf("", "", locale: en) == nil) + #expect(rangeOf("", "a", locale: en) == nil) + #expect(rangeOf("a", "", locale: en) == nil) + #expect(rangeOf("a", "b", locale: en) == nil) + #expect(0..<1 == rangeOf("a", "a", locale: en)) + #expect(0..<1 == rangeOf("a", "A", locale: en)) + #expect(0..<1 == rangeOf("A", "a", locale: en)) + #expect(0..<1 == rangeOf("a", "a\u{0301}", locale: en)) + #expect(0..<1 == rangeOf("a\u{0301}", "a\u{0301}", locale: en)) + #expect(0..<1 == rangeOf("a\u{0301}", "a", locale: en)) do { // FIXME: Indices that don't correspond to grapheme cluster boundaries. let s = "a\u{0301}" - expectEqual( - "\u{0301}", s[s.localizedStandardRange(of: "\u{0301}", locale: en)!]) + #expect( + "\u{0301}" == s[s.localizedStandardRange(of: "\u{0301}", locale: en)!] + ) } - XCTAssertNil(rangeOf("a", "\u{0301}", locale: en)) + #expect(rangeOf("a", "\u{0301}", locale: en) == nil) - expectEqual(0..<1, rangeOf("i", "I", locale: en)) - expectEqual(0..<1, rangeOf("I", "i", locale: en)) - expectEqual(0..<1, rangeOf("\u{0130}", "i", locale: en)) - expectEqual(0..<1, rangeOf("i", "\u{0130}", locale: en)) + #expect(0..<1 == rangeOf("i", "I", locale: en)) + #expect(0..<1 == rangeOf("I", "i", locale: en)) + #expect(0..<1 == rangeOf("\u{0130}", "i", locale: en)) + #expect(0..<1 == rangeOf("i", "\u{0130}", locale: en)) let tr = Locale(identifier: "tr") - expectEqual(0..<1, rangeOf("\u{0130}", "ı", locale: tr)) + #expect(0..<1 == rangeOf("\u{0130}", "ı", locale: tr)) } - func test_smallestEncoding() { + @Test func test_smallestEncoding() { let availableEncodings: [String.Encoding] = String.availableStringEncodings - expectTrue(availableEncodings.contains("abc".smallestEncoding)) + #expect(availableEncodings.contains("abc".smallestEncoding)) } func getHomeDir() -> String { @@ -1853,37 +1943,41 @@ final class StringTestsStdlib: XCTestCase { #endif } - func test_addingPercentEncoding() { - expectEqual( - "abcd1234", - "abcd1234".addingPercentEncoding(withAllowedCharacters: .alphanumerics)) - expectEqual( - "abcd%20%D0%B0%D0%B1%D0%B2%D0%B3", - "abcd абвг".addingPercentEncoding(withAllowedCharacters: .alphanumerics)) + @Test func test_addingPercentEncoding() { + #expect( + "abcd1234" == + "abcd1234".addingPercentEncoding(withAllowedCharacters: .alphanumerics) + ) + #expect( + "abcd%20%D0%B0%D0%B1%D0%B2%D0%B3" == + "abcd абвг".addingPercentEncoding(withAllowedCharacters: .alphanumerics) + ) } - func test_appendingFormat() { - expectEqual("", "".appendingFormat("")) - expectEqual("a", "a".appendingFormat("")) - expectEqual( - "abc абв \u{0001F60A}", - "abc абв \u{0001F60A}".appendingFormat("")) + @Test func test_appendingFormat() { + #expect("" == "".appendingFormat("")) + #expect("a" == "a".appendingFormat("")) + #expect( + "abc абв \u{0001F60A}" == + "abc абв \u{0001F60A}".appendingFormat("") + ) let formatArg: NSString = "привет мир \u{0001F60A}" - expectEqual( - "abc абв \u{0001F60A}def привет мир \u{0001F60A} 42", + #expect( + "abc абв \u{0001F60A}def привет мир \u{0001F60A} 42" == "abc абв \u{0001F60A}" - .appendingFormat("def %@ %ld", formatArg, 42)) + .appendingFormat("def %@ %ld", formatArg, 42) + ) } - func test_appending() { - expectEqual("", "".appending("")) - expectEqual("a", "a".appending("")) - expectEqual("a", "".appending("a")) - expectEqual("さ\u{3099}", "さ".appending("\u{3099}")) + @Test func test_appending() { + #expect("" == "".appending("")) + #expect("a" == "a".appending("")) + #expect("a" == "".appending("a")) + #expect("さ\u{3099}" == "さ".appending("\u{3099}")) } - func test_folding() { + @Test func test_folding() { func fwo( _ s: String, _ options: String.CompareOptions @@ -1910,22 +2004,25 @@ final class StringTestsStdlib: XCTestCase { "example123", fwo("example123", .widthInsensitive), "en") } - func test_padding() { - expectEqual( - "abc абв \u{0001F60A}", + @Test func test_padding() { + #expect( + "abc абв \u{0001F60A}" == "abc абв \u{0001F60A}".padding( - toLength: 10, withPad: "XYZ", startingAt: 0)) - expectEqual( - "abc абв \u{0001F60A}XYZXY", + toLength: 10, withPad: "XYZ", startingAt: 0) + ) + #expect( + "abc абв \u{0001F60A}XYZXY" == "abc абв \u{0001F60A}".padding( - toLength: 15, withPad: "XYZ", startingAt: 0)) - expectEqual( - "abc абв \u{0001F60A}YZXYZ", + toLength: 15, withPad: "XYZ", startingAt: 0) + ) + #expect( + "abc абв \u{0001F60A}YZXYZ" == "abc абв \u{0001F60A}".padding( - toLength: 15, withPad: "XYZ", startingAt: 1)) + toLength: 15, withPad: "XYZ", startingAt: 1) + ) } - func test_replacingCharacters() { + @Test func test_replacingCharacters() { do { let empty = "" expectEqual("", empty.replacingCharacters( @@ -1934,223 +2031,280 @@ final class StringTestsStdlib: XCTestCase { let s = "\u{1F601}abc さ\u{3099}し\u{3099}す\u{3099}せ\u{3099}そ\u{3099}" - expectEqual(s, s.replacingCharacters( + #expect(s == s.replacingCharacters( in: s.startIndex.. equivalent representation.") - XCTAssertNotEqual(uuidC, uuidD, "Two different UUIDs must not be equal.") + #expect(uuidA == uuidB, "String case must not matter.") + #expect(uuidA == uuidC,"A UUID initialized with a string must be equal to the same UUID initialized with its UnsafePointer equivalent representation.") + #expect(uuidC != uuidD, "Two different UUIDs must not be equal.") } - func test_UUIDInvalid() { + @Test func test_UUIDInvalid() { let invalid = UUID(uuidString: "Invalid UUID") - XCTAssertNil(invalid, "The convenience initializer `init?(uuidString string:)` must return nil for an invalid UUID string.") + #expect(invalid == nil, "The convenience initializer `init?(uuidString string:)` must return nil for an invalid UUID string.") } // `uuidString` should return an uppercase string // See: https://bugs.swift.org/browse/SR-865 - func test_UUIDuuidString() { + @Test func test_UUIDuuidString() { let uuid = UUID(uuid: (0xe6,0x21,0xe1,0xf8,0xc3,0x6c,0x49,0x5a,0x93,0xfc,0x0c,0x24,0x7a,0x3e,0x6e,0x5f)) - XCTAssertEqual(uuid.uuidString, "E621E1F8-C36C-495A-93FC-0C247A3E6E5F", "The uuidString representation must be uppercase.") + #expect(uuid.uuidString == "E621E1F8-C36C-495A-93FC-0C247A3E6E5F", + "The uuidString representation must be uppercase.") } - func test_UUIDdescription() { + @Test func test_UUIDdescription() { let uuid = UUID() let description: String = uuid.description let uuidString: String = uuid.uuidString - XCTAssertEqual(description, uuidString, "The description must be the same as the uuidString.") + #expect(description == uuidString, "The description must be the same as the uuidString.") } - func test_hash() { + @Test func test_hash() { let values: [UUID] = [ // This list takes a UUID and tweaks every byte while // leaving the version/variant intact. @@ -70,50 +75,50 @@ final class UUIDTests : XCTestCase { checkHashable(values, equalityOracle: { $0 == $1 }) } - func test_AnyHashableContainingUUID() { + @Test func test_AnyHashableContainingUUID() { let values: [UUID] = [ UUID(uuidString: "e621e1f8-c36c-495a-93fc-0c247a3e6e5f")!, UUID(uuidString: "f81d4fae-7dec-11d0-a765-00a0c91e6bf6")!, UUID(uuidString: "f81d4fae-7dec-11d0-a765-00a0c91e6bf6")!, ] let anyHashables = values.map(AnyHashable.init) - expectEqual(UUID.self, type(of: anyHashables[0].base)) - expectEqual(UUID.self, type(of: anyHashables[1].base)) - expectEqual(UUID.self, type(of: anyHashables[2].base)) - XCTAssertNotEqual(anyHashables[0], anyHashables[1]) - XCTAssertEqual(anyHashables[1], anyHashables[2]) + #expect(UUID.self == type(of: anyHashables[0].base)) + #expect(UUID.self == type(of: anyHashables[1].base)) + #expect(UUID.self == type(of: anyHashables[2].base)) + #expect(anyHashables[0] != anyHashables[1]) + #expect(anyHashables[1] == anyHashables[2]) } // rdar://71190003 (UUID has no customMirror) - func test_UUID_custom_mirror() { + @Test func test_UUID_custom_mirror() { let uuid = UUID(uuidString: "89E90DC6-5EBA-41A8-A64D-81D3576EE46E")! - XCTAssertEqual(String(reflecting: uuid), "89E90DC6-5EBA-41A8-A64D-81D3576EE46E") + #expect(String(reflecting: uuid) == "89E90DC6-5EBA-41A8-A64D-81D3576EE46E") } @available(FoundationPreview 0.1, *) - func test_UUID_Comparable() throws { - var uuid1 = try XCTUnwrap(UUID(uuidString: "00000000-0000-0000-0000-000000000001")) - var uuid2 = try XCTUnwrap(UUID(uuidString: "00000000-0000-0000-0000-000000000002")) - XCTAssertTrue(uuid1 < uuid2) - XCTAssertFalse(uuid2 < uuid1) - XCTAssertFalse(uuid2 == uuid1) + @Test func test_UUID_Comparable() throws { + var uuid1 = try #require(UUID(uuidString: "00000000-0000-0000-0000-000000000001")) + var uuid2 = try #require(UUID(uuidString: "00000000-0000-0000-0000-000000000002")) + #expect(uuid1 < uuid2) + #expect(uuid2 > uuid1) + #expect(uuid2 != uuid1) - uuid1 = try XCTUnwrap(UUID(uuidString: "9707CE8D-251F-4858-8BF9-C9EC3D690FCE")) - uuid2 = try XCTUnwrap(UUID(uuidString: "9807CE8D-251F-4858-8BF9-C9EC3D690FCE")) - XCTAssertTrue(uuid1 < uuid2) - XCTAssertFalse(uuid2 < uuid1) - XCTAssertFalse(uuid2 == uuid1) + uuid1 = try #require(UUID(uuidString: "9707CE8D-251F-4858-8BF9-C9EC3D690FCE")) + uuid2 = try #require(UUID(uuidString: "9807CE8D-251F-4858-8BF9-C9EC3D690FCE")) + #expect(uuid1 < uuid2) + #expect(uuid2 > uuid1) + #expect(uuid2 != uuid1) - uuid1 = try XCTUnwrap(UUID(uuidString: "9707CE8D-261F-4858-8BF9-C9EC3D690FCE")) - uuid2 = try XCTUnwrap(UUID(uuidString: "9707CE8D-251F-4858-8BF9-C9EC3D690FCE")) - XCTAssertTrue(uuid1 > uuid2) - XCTAssertFalse(uuid2 > uuid1) - XCTAssertFalse(uuid2 == uuid1) + uuid1 = try #require(UUID(uuidString: "9707CE8D-261F-4858-8BF9-C9EC3D690FCE")) + uuid2 = try #require(UUID(uuidString: "9707CE8D-251F-4858-8BF9-C9EC3D690FCE")) + #expect(uuid1 > uuid2) + #expect(uuid2 < uuid1) + #expect(uuid2 != uuid1) - uuid1 = try XCTUnwrap(UUID(uuidString: "9707CE8D-251F-4858-8BF9-C9EC3D690FCE")) - uuid2 = try XCTUnwrap(UUID(uuidString: "9707CE8D-251F-4858-8BF9-C9EC3D690FCE")) - XCTAssertFalse(uuid1 > uuid2) - XCTAssertFalse(uuid2 > uuid1) - XCTAssertTrue(uuid2 == uuid1) + uuid1 = try #require(UUID(uuidString: "9707CE8D-251F-4858-8BF9-C9EC3D690FCE")) + uuid2 = try #require(UUID(uuidString: "9707CE8D-251F-4858-8BF9-C9EC3D690FCE")) + #expect(!(uuid1 > uuid2)) + #expect(!(uuid2 > uuid1)) + #expect(uuid2 == uuid1) } } diff --git a/Tests/FoundationEssentialsTests/Utilities+Testing.swift b/Tests/FoundationEssentialsTests/Utilities+Testing.swift new file mode 100644 index 000000000..6fdad0c1d --- /dev/null +++ b/Tests/FoundationEssentialsTests/Utilities+Testing.swift @@ -0,0 +1,246 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Testing + +/// Test that the elements of `instances` satisfy the semantic +/// requirements of `Equatable`, using `oracle` to generate equality +/// expectations from pairs of positions in `instances`. +/// +/// - Note: `oracle` is also checked for conformance to the +/// laws. +func checkEquatable( + _ _instances: Instances, + oracle _oracle: @escaping (Instances.Index, Instances.Index) -> Bool, + allowBrokenTransitivity: Bool = false, + _ message: @autoclosure () -> String = "", + file: StaticString = #file, + line: UInt = #line +) where Instances.Element: Equatable { + let instances = Array(_instances) + let indices = Array(_instances.indices) + let oracle: (Int, Int) -> Bool = { + _oracle(indices[$0], indices[$1]) + } + + // For each index (which corresponds to an instance being tested) track the + // set of equal instances. + var transitivityScoreboard: [Box>] = + instances.indices.map { _ in Box([]) } + + for i in instances.indices { + let x = instances[i] + #expect(oracle(i, i), "bad oracle: broken reflexivity at index \(i)") + + for j in instances.indices { + let y = instances[j] + + let predictedXY = oracle(i, j) + #expect( + predictedXY == oracle(j, i), + "bad oracle: broken symmetry between indices \(i), \(j)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + + let isEqualXY = x == y + #expect( + predictedXY == isEqualXY, + """ + \((predictedXY + ? "expected equal, found not equal" + : "expected not equal, found equal")) + lhs (at index \(i)): \(String(reflecting: x)) + rhs (at index \(j)): \(String(reflecting: y)) + """, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + + // Not-equal is an inverse of equal. + #expect( + isEqualXY != (x != y), + """ + lhs (at index \(i)): \(String(reflecting: x)) + rhs (at index \(j)): \(String(reflecting: y)) + """, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + + if !allowBrokenTransitivity { + // Check transitivity of the predicate represented by the oracle. + // If we are adding the instance `j` into an equivalence set, check that + // it is equal to every other instance in the set. + if predictedXY && i < j && transitivityScoreboard[i].value.insert(j).inserted { + if transitivityScoreboard[i].value.count == 1 { + transitivityScoreboard[i].value.insert(i) + } + for k in transitivityScoreboard[i].value { + #expect( + oracle(j, k), + "bad oracle: broken transitivity at indices \(i), \(j), \(k)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + // No need to check equality between actual values, we will check + // them with the checks above. + } + precondition(transitivityScoreboard[j].value.isEmpty) + transitivityScoreboard[j] = transitivityScoreboard[i] + } + } + } + } +} + +func checkHashable( + _ instances: Instances, + equalityOracle: @escaping (Instances.Index, Instances.Index) -> Bool, + allowIncompleteHashing: Bool = false, + _ message: @autoclosure () -> String = "", + file: StaticString = #file, line: UInt = #line +) where Instances.Element: Hashable { + checkEquatable( + instances, + oracle: equalityOracle, + message(), + file: file, line: line + ) + + for i in instances.indices { + let x = instances[i] + for j in instances.indices { + let y = instances[j] + let predicted = equalityOracle(i, j) + #expect( + predicted == equalityOracle(j, i), + "bad hash oracle: broken symmetry between indices \(i), \(j)", + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + if x == y { + #expect( + predicted, + """ + bad hash oracle: equality must imply hash equality + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + } + if predicted { + #expect( + hash(x) == hash(y), + """ + hash(into:) expected to match, found to differ + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + x.hashValue == y.hashValue, + """ + hashValue expected to match, found to differ + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + x._rawHashValue(seed: 0) == y._rawHashValue(seed: 0), + """ + _rawHashValue(seed:) expected to match, found to differ + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + } else if !allowIncompleteHashing { + // Try a few different seeds; at least one of them should discriminate + // between the hashes. It is extremely unlikely this check will fail + // all ten attempts, unless the type's hash encoding is not unique, + // or unless the hash equality oracle is wrong. + #expect( + (0..<10).contains { hash(x, salt: $0) != hash(y, salt: $0) }, + """ + hash(into:) expected to differ, found to match + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + #expect( + (0..<10).contains { i in + x._rawHashValue(seed: i) != y._rawHashValue(seed: i) + }, + """ + _rawHashValue(seed:) expected to differ, found to match + lhs (at index \(i)): \(x) + rhs (at index \(j)): \(y) + """, + sourceLocation: .init( + filePath: String(describing: file), + line: Int(line) + ) + ) + } + } + } +} + +// MARK: - Private Types +private class Box { + var value: T + + init(_ value: T) { + self.value = value + } +} + +#if !FOUNDATION_FRAMEWORK +private func hash(_ value: H, salt: Int? = nil) -> Int { + var hasher = Hasher() + if let salt = salt { + hasher.combine(salt) + } + hasher.combine(value) + return hasher.finalize() +} +#endif diff --git a/Tests/FoundationInternationalizationTests/CalendarTests.swift b/Tests/FoundationInternationalizationTests/CalendarTests.swift index 9489f74cb..e6a125a79 100644 --- a/Tests/FoundationInternationalizationTests/CalendarTests.swift +++ b/Tests/FoundationInternationalizationTests/CalendarTests.swift @@ -161,7 +161,7 @@ final class CalendarTests : XCTestCase { Calendar(identifier: .islamic), Calendar(identifier: .iso8601), ] - checkHashable(calendars, equalityOracle: { $0 == $1 }) + checkHashableXCTest(calendars, equalityOracle: { $0 == $1 }) // autoupdating calendar isn't equal to the current, even though it's // likely to be the same. @@ -169,7 +169,7 @@ final class CalendarTests : XCTestCase { Calendar.autoupdatingCurrent, Calendar.current, ] - checkHashable(calendars2, equalityOracle: { $0 == $1 }) + checkHashableXCTest(calendars2, equalityOracle: { $0 == $1 }) } func test_AnyHashableContainingCalendar() { diff --git a/Tests/FoundationInternationalizationTests/DateTests+Locale.swift b/Tests/FoundationInternationalizationTests/DateTests+Locale.swift index 609e47476..e29c9b033 100644 --- a/Tests/FoundationInternationalizationTests/DateTests+Locale.swift +++ b/Tests/FoundationInternationalizationTests/DateTests+Locale.swift @@ -71,7 +71,7 @@ final class DateLocaleTests : XCTestCase { dateWithString("2010-05-17 14:50:47 -0700"), dateWithString("2010-05-17 14:49:48 -0700"), ] - checkHashable(values, equalityOracle: { $0 == $1 }) + checkHashableXCTest(values, equalityOracle: { $0 == $1 }) } func test_AnyHashableContainingDate() {