diff --git a/Tests/FoundationEssentialsTests/FileManager/FilePlayground.swift b/Tests/FoundationEssentialsTests/FileManager/FilePlayground.swift index 38fb4355e..9aa1e3582 100644 --- a/Tests/FoundationEssentialsTests/FileManager/FilePlayground.swift +++ b/Tests/FoundationEssentialsTests/FileManager/FilePlayground.swift @@ -144,18 +144,18 @@ struct FilePlayground { func test(captureDelegateCalls: Bool = false, sourceLocation: SourceLocation = #_sourceLocation, _ tester: sending (FileManager) throws -> Void) async throws { let capturingDelegate = CapturingFileManagerDelegate() - let fileManager = FileManager() let tempDir = String.temporaryDirectoryPath - try directory.build(in: tempDir, using: fileManager) - if captureDelegateCalls { - // Add the delegate after the call to `build` to ensure that the builder doesn't mutate the delegate - fileManager.delegate = capturingDelegate - } + try directory.build(in: tempDir, using: FileManager.default) let createdDir = tempDir.appendingPathComponent(directory.name) - try await CurrentWorkingDirectoryActor.withCurrentWorkingDirectory(createdDir, fileManager: fileManager, sourceLocation: sourceLocation) { + try await CurrentWorkingDirectoryActor.withCurrentWorkingDirectory(createdDir, sourceLocation: sourceLocation) { + let fileManager = FileManager() + if captureDelegateCalls { + // Add the delegate after the call to `build` to ensure that the builder doesn't mutate the delegate + fileManager.delegate = capturingDelegate + } try tester(fileManager) } - try fileManager.removeItem(atPath: createdDir) + try FileManager.default.removeItem(atPath: createdDir) extendLifetime(capturingDelegate) // Ensure capturingDelegate lives beyond the tester body } } diff --git a/Tests/FoundationEssentialsTests/GregorianCalendarRecurrenceRuleTests.swift b/Tests/FoundationEssentialsTests/GregorianCalendarRecurrenceRuleTests.swift index 8b6e8e69d..cd38e1d37 100644 --- a/Tests/FoundationEssentialsTests/GregorianCalendarRecurrenceRuleTests.swift +++ b/Tests/FoundationEssentialsTests/GregorianCalendarRecurrenceRuleTests.swift @@ -10,17 +10,16 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing -#if FOUNDATION_FRAMEWORK -@testable import Foundation -#else +#if canImport(FoundationEssentials) @testable import FoundationEssentials -#endif // FOUNDATION_FRAMEWORK +#else +@testable import Foundation +#endif -final class GregorianCalendarRecurrenceRuleTests: XCTestCase { +@Suite("GregorianCalendar RecurrenceRule") +private struct GregorianCalendarRecurrenceRuleTests { /// A Gregorian calendar in GMT with no time zone changes var gregorian: Calendar = { var gregorian = Calendar(identifier: .gregorian) @@ -28,17 +27,21 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { return gregorian }() - func testRoundtripEncoding() throws { + @Test func roundtripEncoding() throws { // These are not necessarily valid recurrence rule, they are constructed // in a way to test all encoding paths - var recurrenceRule1 = Calendar.RecurrenceRule(calendar: .current, frequency: .daily) + var calendar = Calendar(identifier: .gregorian) + calendar.locale = .init(identifier: "en_001") + calendar.timeZone = try #require(TimeZone(secondsFromGMT: 0)) + + var recurrenceRule1 = Calendar.RecurrenceRule(calendar: calendar, frequency: .daily) recurrenceRule1.interval = 2 recurrenceRule1.months = [1, 2, Calendar.RecurrenceRule.Month(4, isLeap: true)] recurrenceRule1.weeks = [2, 3] recurrenceRule1.weekdays = [.every(.monday), .nth(1, .wednesday)] recurrenceRule1.end = .afterOccurrences(5) - var recurrenceRule2 = Calendar.RecurrenceRule(calendar: .init(identifier: .gregorian), frequency: .daily) + var recurrenceRule2 = Calendar.RecurrenceRule(calendar: calendar, frequency: .daily) recurrenceRule2.months = [2, 10] recurrenceRule2.weeks = [1, -1] recurrenceRule2.setPositions = [1] @@ -55,12 +58,12 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { let decoded1 = try JSONDecoder().decode(Calendar.RecurrenceRule.self, from: recurrenceRule1JSON) let decoded2 = try JSONDecoder().decode(Calendar.RecurrenceRule.self, from: recurrenceRule2JSON) - XCTAssertEqual(recurrenceRule1, decoded1) - XCTAssertEqual(recurrenceRule2, decoded2) - XCTAssertNotEqual(recurrenceRule1, recurrenceRule2) + #expect(recurrenceRule1 == decoded1) + #expect(recurrenceRule2 == decoded2) + #expect(recurrenceRule1 != recurrenceRule2) } - func testSimpleDailyRecurrence() { + @Test func simpleDailyRecurrence() { let start = Date(timeIntervalSince1970: 1285027200.0) // 2010-09-21T00:00:00-0000 let end = Date(timeIntervalSince1970: 1287619200.0) // 2010-10-21T00:00:00-0000 @@ -102,10 +105,10 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1287583200.0), // 2010-10-20T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testSimpleDailyRecurrenceWithCount() { + @Test func simpleDailyRecurrenceWithCount() { let start = Date(timeIntervalSince1970: 1285027200.0) // 2010-09-21T00:00:00-0000 let end = Date(timeIntervalSince1970: 1287619200.0) // 2010-10-21T00:00:00-0000 @@ -121,10 +124,10 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1285336800.0), // 2010-09-24T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testDailyRecurrenceWithDaysOfTheWeek() { + @Test func dailyRecurrenceWithDaysOfTheWeek() { let start = Date(timeIntervalSince1970: 1285027200.0) // 2010-09-21T00:00:00-0000 let end = Date(timeIntervalSince1970: 1287619200.0) // 2010-10-21T00:00:00-0000 @@ -145,10 +148,10 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1287410400.0), // 2010-10-18T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testDailyRecurrenceWithDaysOfTheWeekAndMonth() { + @Test func dailyRecurrenceWithDaysOfTheWeekAndMonth() { let start = Date(timeIntervalSince1970: 1285027200.0) // 2010-09-21T00:00:00-0000 let end = Date(timeIntervalSince1970: 1287619200.0) // 2010-10-21T00:00:00-0000 @@ -164,10 +167,10 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1285596000.0), // 2010-09-27T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testDailyRecurrenceWithMonth() { + @Test func dailyRecurrenceWithMonth() { let start = Date(timeIntervalSince1970: 1285027200.0) // 2010-09-21T00:00:00-0000 let end = Date(timeIntervalSince1970: 1287619200.0) // 2010-10-21T00:00:00-0000 @@ -190,10 +193,10 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1285855200.0), // 2010-09-30T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testDailyRecurrenceEveryThreeDays() { + @Test func dailyRecurrenceEveryThreeDays() { let start = Date(timeIntervalSince1970: 1285027200.0) // 2010-09-21T00:00:00-0000 let end = Date(timeIntervalSince1970: 1287619200.0) // 2010-10-21T00:00:00-0000 @@ -216,11 +219,11 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1287410400.0), // 2010-10-18T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testSimpleWeeklyRecurrence() { + @Test func simpleWeeklyRecurrence() { let start = Date(timeIntervalSince1970: 1285027200.0) // 2010-09-21T00:00:00-0000 let end = Date(timeIntervalSince1970: 1287619200.0) // 2010-10-21T00:00:00-0000 @@ -237,10 +240,10 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1287496800.0), // 2010-10-19T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testWeeklyRecurrenceEveryOtherWeek() { + @Test func weeklyRecurrenceEveryOtherWeek() { let start = Date(timeIntervalSince1970: 1285027200.0) // 2010-09-21T00:00:00-0000 let end = Date(timeIntervalSince1970: 1287619200.0) // 2010-10-21T00:00:00-0000 @@ -256,10 +259,10 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1287496800.0), // 2010-10-19T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testWeeklyRecurrenceWithDaysOfWeek() { + @Test func weeklyRecurrenceWithDaysOfWeek() { let start = Date(timeIntervalSince1970: 1285027200.0) // 2010-09-21T00:00:00-0000 let end = Date(timeIntervalSince1970: 1287619200.0) // 2010-10-21T00:00:00-0000 @@ -280,10 +283,10 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1287410400.0), // 2010-10-18T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testWeeklyRecurrenceWithDaysOfWeekAndMonth() { + @Test func weeklyRecurrenceWithDaysOfWeekAndMonth() { let start = Date(timeIntervalSince1970: 1285027200.0) // 2010-09-21T00:00:00-0000 let end = Date(timeIntervalSince1970: 1287619200.0) // 2010-10-21T00:00:00-0000 @@ -299,9 +302,10 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1285596000.0), // 2010-09-27T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testWeeklyRecurrenceWithDaysOfWeekAndSetPositions() { + + @Test func weeklyRecurrenceWithDaysOfWeekAndSetPositions() { let start = Date(timeIntervalSince1970: 1285027200.0) // 2010-09-21T00:00:00-0000 let end = Date(timeIntervalSince1970: 1287619200.0) // 2010-10-21T00:00:00-0000 @@ -319,10 +323,10 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1287151200.0), // 2010-10-15T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testMonthlyRecurrenceWithWeekdays() { + @Test func monthlyRecurrenceWithWeekdays() { // Find the first monday and last friday of each month for a given range let start = Date(timeIntervalSince1970: 1641045600.0) // 2022-01-01T14:00:00-0000 let end = Date(timeIntervalSince1970: 1677679200.0) // 2023-03-01T14:00:00-0000 @@ -364,10 +368,10 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1677247200.0), // 2023-02-24T14:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) } - func testYearlyRecurrenceOnLeapDay() { + @Test func yearlyRecurrenceOnLeapDay() { let start = Date(timeIntervalSince1970: 1704067200.0) // 2024-01-01T00:00:00-0000 let end = Date(timeIntervalSince1970: 1956528000.0) // 2032-01-01T00:00:00-0000 let leapDay = Date(timeIntervalSince1970: 1709200800.0) // 2024-02-29T10:00:00-0000 @@ -387,7 +391,7 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase { Date(timeIntervalSince1970: 1898589600.0), // 2030-03-01T10:00:00-0000 Date(timeIntervalSince1970: 1930125600.0), // 2031-03-01T10:00:00-0000 ] - XCTAssertEqual(results, expectedResults) + #expect(results == expectedResults) rule.matchingPolicy = .nextTime results = Array(rule.recurrences(of: leapDay, in: start.. { $0.a == 2 } let decoded = try _encodeDecode(predicate, for: StandardConfig.self) var object = Object.example - XCTAssertEqual(try predicate.evaluate(object), try decoded.evaluate(object)) + #expect(try predicate.evaluate(object) == decoded.evaluate(object)) object.a = 2 - XCTAssertEqual(try predicate.evaluate(object), try decoded.evaluate(object)) + #expect(try predicate.evaluate(object) == decoded.evaluate(object)) object.a = 3 - XCTAssertEqual(try predicate.evaluate(object), try decoded.evaluate(object)) + #expect(try predicate.evaluate(object) == decoded.evaluate(object)) - XCTAssertThrowsError(try _encodeDecode(predicate, for: EmptyConfig.self)) - XCTAssertThrowsError(try _encodeDecode(predicate)) + #expect(throws: (any Error).self) { + try _encodeDecode(predicate, for: EmptyConfig.self) + } + #expect(throws: (any Error).self) { + try _encodeDecode(predicate) + } } - func testDisallowedKeyPath() throws { + @Test func disallowedKeyPath() 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 - } - XCTAssertEqual(decodingError.debugDescription, "A keypath for the 'Object.a' identifier is not in the provided allowlist") + #expect { + try _encodeDecode(predicate, encoding: StandardConfig.self, decoding: MinimalConfig.self) + } throws: { + let decodingError = try #require($0 as? DecodingError) + return decodingError.debugDescription == "A keypath for the 'Object.a' identifier is not in the provided allowlist" } } - func testKeyPathTypeMismatch() throws { + @Test func keyPathTypeMismatch() 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 - } - 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 { + try _encodeDecode(predicate, encoding: StandardConfig.self, decoding: MismatchedKeyPathConfig.self) + } throws: { + let decodingError = try #require($0 as? DecodingError) + return 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>" } } - func testDisallowedType() throws { + @Test func disallowedType() 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: { + String(describing: $0) == "The 'Foundation.UUID' identifier is not in the provided allowlist (required by /PredicateExpressions.Equal/PredicateExpressions.Value)" } let decoded = try _encodeDecode(predicate, for: UUIDConfig.self) - XCTAssertEqual(try decoded.evaluate(.example), try predicate.evaluate(.example)) + #expect(try decoded.evaluate(.example) == predicate.evaluate(.example)) } - func testProvidedProperties() throws { + @Test func providedProperties() 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)) + #expect(try decoded.evaluate(.example) == predicate.evaluate(.example)) decoded = try _encodeDecode(predicate, for: RecursiveProvidedKeyPathConfig.self) - XCTAssertEqual(try decoded.evaluate(.example), try predicate.evaluate(.example)) + #expect(try decoded.evaluate(.example) == predicate.evaluate(.example)) 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)) + #expect(try decoded.evaluate(.example) == predicate.evaluate(.example)) } - func testDefaultAllowlist() throws { + @Test func defaultAllowlist() throws { var predicate = #Predicate { $0.isEmpty } var decoded = try _encodeDecode(predicate) - XCTAssertEqual(try decoded.evaluate("Hello world"), try predicate.evaluate("Hello world")) + #expect(try decoded.evaluate("Hello world") == predicate.evaluate("Hello world")) predicate = #Predicate { $0.count > 2 } decoded = try _encodeDecode(predicate) - XCTAssertEqual(try decoded.evaluate("Hello world"), try predicate.evaluate("Hello world")) + #expect(try decoded.evaluate("Hello world") == predicate.evaluate("Hello world")) predicate = #Predicate { $0.contains(/[a-z]/) } decoded = try _encodeDecode(predicate) - XCTAssertEqual(try decoded.evaluate("Hello world"), try predicate.evaluate("Hello world")) + #expect(try decoded.evaluate("Hello world") == predicate.evaluate("Hello world")) let predicate2 = #Predicate { $0 == $0 } let decoded2 = try _encodeDecode(predicate2) - XCTAssertEqual(try decoded2.evaluate(.example), try predicate2.evaluate(.example)) + #expect(try decoded2.evaluate(.example) == predicate2.evaluate(.example)) var predicate3 = #Predicate> { $0.isEmpty } var decoded3 = try _encodeDecode(predicate3) - XCTAssertEqual(try decoded3.evaluate(["A", "B", "C"]), try predicate3.evaluate(["A", "B", "C"])) + #expect(try decoded3.evaluate(["A", "B", "C"]) == predicate3.evaluate(["A", "B", "C"])) predicate3 = #Predicate> { $0.count == 2 } decoded3 = try _encodeDecode(predicate3) - XCTAssertEqual(try decoded3.evaluate(["A", "B", "C"]), try predicate3.evaluate(["A", "B", "C"])) + #expect(try decoded3.evaluate(["A", "B", "C"]) == predicate3.evaluate(["A", "B", "C"])) 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])) + #expect(try decoded4.evaluate(["A": 1, "B": 2, "C": 3]) == predicate4.evaluate(["A": 1, "B": 2, "C": 3])) 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])) + #expect(try decoded4.evaluate(["A": 1, "B": 2, "C": 3]) == predicate4.evaluate(["A": 1, "B": 2, "C": 3])) let predicate5 = #Predicate { (0 ..< 4).contains($0) } let decoded5 = try _encodeDecode(predicate5) - XCTAssertEqual(try decoded5.evaluate(2), try predicate5.evaluate(2)) + #expect(try decoded5.evaluate(2) == predicate5.evaluate(2)) } - func testMalformedData() { - func _malformedDecode(_ json: String, config: T.Type = StandardConfig.self, reason: String, file: StaticString = #filePath, line: UInt = #line) { + @Test func malformedData() { + func _malformedDecode(_ json: String, config: T.Type = StandardConfig.self, reason: String, sourceLocation: SourceLocation = #_sourceLocation) { 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(sourceLocation: sourceLocation) { + try decoder.decode(CodableConfiguration, T>.self, from: data) + } throws: { + String(describing: $0).contains(reason) } } @@ -424,7 +444,7 @@ final class PredicateCodableTests: XCTestCase { ) } - func testBasicVariadic() throws { + @Test func basicVariadic() throws { let predicate = #Predicate { $0.a == 2 && $1.a == 3 } @@ -432,17 +452,21 @@ 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)) + #expect(try predicate.evaluate(object, object2) == decoded.evaluate(object, object2)) object.a = 2 - XCTAssertEqual(try predicate.evaluate(object, object2), try decoded.evaluate(object, object2)) + #expect(try predicate.evaluate(object, object2) == decoded.evaluate(object, object2)) object.a = 3 - XCTAssertEqual(try predicate.evaluate(object, object2), try decoded.evaluate(object, object2)) + #expect(try predicate.evaluate(object, object2) == decoded.evaluate(object, object2)) - XCTAssertThrowsError(try _encodeDecode(predicate, for: EmptyConfig.self)) - XCTAssertThrowsError(try _encodeDecode(predicate)) + #expect(throws: (any Error).self) { + try _encodeDecode(predicate, for: EmptyConfig.self) + } + #expect(throws: (any Error).self) { + try _encodeDecode(predicate) + } } - func testCapturedVariadicTypes() throws { + @Test func capturedVariadicTypes() throws { struct A : Equatable, Codable { init(_: repeat (each T).Type) {} @@ -475,10 +499,10 @@ final class PredicateCodableTests: XCTestCase { } let decoded = try _encodeDecode(predicate, for: CustomConfig.self) - XCTAssertEqual(try decoded.evaluate(2), try predicate.evaluate(2)) + #expect(try decoded.evaluate(2) == predicate.evaluate(2)) } - func testNestedPredicates() throws { + @Test func nestedPredicates() throws { let predicateA = #Predicate { $0.a == 3 } @@ -498,11 +522,11 @@ 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) == predicateB.evaluate(object), "Evaluation failed to produce equal results for \(object)") } } - func testNestedPredicateRestrictedConfiguration() throws { + @Test func nestedPredicateRestrictedConfiguration() throws { struct RestrictedBox : Codable { let predicate: Predicate @@ -541,24 +565,30 @@ 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 expression() throws { let expression = #Expression { $0.a } let decoded = try _encodeDecode(expression, for: StandardConfig.self) var object = Object.example - XCTAssertEqual(try expression.evaluate(object), try decoded.evaluate(object)) + #expect(try expression.evaluate(object) == decoded.evaluate(object)) object.a = 2 - XCTAssertEqual(try expression.evaluate(object), try decoded.evaluate(object)) + #expect(try expression.evaluate(object) == decoded.evaluate(object)) object.a = 3 - XCTAssertEqual(try expression.evaluate(object), try decoded.evaluate(object)) + #expect(try expression.evaluate(object) == decoded.evaluate(object)) - XCTAssertThrowsError(try _encodeDecode(expression, for: EmptyConfig.self)) - XCTAssertThrowsError(try _encodeDecode(expression)) + #expect(throws: (any Error).self) { + try _encodeDecode(expression, for: EmptyConfig.self) + } + #expect(throws: (any Error).self) { + try _encodeDecode(expression) + } } } -#endif // FOUNDATION_FRAMEWORK +#endif diff --git a/Tests/FoundationEssentialsTests/PredicateConversionTests.swift b/Tests/FoundationEssentialsTests/PredicateConversionTests.swift index c41040edc..77fa06ac8 100644 --- a/Tests/FoundationEssentialsTests/PredicateConversionTests.swift +++ b/Tests/FoundationEssentialsTests/PredicateConversionTests.swift @@ -11,8 +11,11 @@ //===----------------------------------------------------------------------===// #if FOUNDATION_FRAMEWORK +import Testing +import Foundation -final class NSPredicateConversionTests: XCTestCase { +@Suite("NSPredicate Conversion") +private struct NSPredicateConversionTests { private func convert(_ predicate: Predicate) -> NSPredicate? { NSPredicate(predicate) } @@ -59,64 +62,64 @@ final class NSPredicateConversionTests: XCTestCase { var b: [Int] } - func testBasics() { + @Test func basics() throws { 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)) + var converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "a == 2")) + #expect(!converted.evaluate(with: obj)) predicate = #Predicate { $0.a + 2 == 4 } - converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "a + 2 == 4")) - XCTAssertFalse(converted!.evaluate(with: obj)) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "a + 2 == 4")) + #expect(!converted.evaluate(with: obj)) predicate = #Predicate { $0.b.count == 5 } - converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "b.length == 5")) - XCTAssertTrue(converted!.evaluate(with: obj)) + converted = try #require(convert(predicate)) + #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)) + converted = try #require(convert(predicate)) + #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)) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "SUBQUERY(g, $_local_1, $_local_1 == d).@count > 0")) + #expect(!converted.evaluate(with: obj)) } - func testEquality() { + @Test func equality() throws { var predicate = #Predicate { $0.a == 0 } - var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "a == 0")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + var converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "a == 0")) + #expect(!converted.evaluate(with: ObjCObject())) predicate = #Predicate { $0.a != 0 } - converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "a != 0")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "a != 0")) + #expect(converted.evaluate(with: ObjCObject())) } - func testRanges() { + @Test func ranges() throws { let now = Date.now let range = now ..< now let closedRange = now ... now @@ -128,283 +131,283 @@ final class NSPredicateConversionTests: XCTestCase { var predicate = #Predicate { ($0.i ... $0.i).contains($0.i) } - var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "i BETWEEN {i, i}")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + var converted = try #require(convert(predicate)) + #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())) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "i >= i AND i < i")) + #expect(!converted.evaluate(with: ObjCObject())) // 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())) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "i >= %@ AND i < %@", now as NSDate, now as NSDate)) + #expect(!converted.evaluate(with: ObjCObject())) predicate = #Predicate { closedRange.contains($0.i) } - converted = convert(predicate) + converted = try #require(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())) predicate = #Predicate { from.contains($0.i) } - converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "i >= %@", now as NSDate)) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + converted = try #require(convert(predicate)) + #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())) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "i <= %@", now as NSDate)) + #expect(!converted.evaluate(with: ObjCObject())) predicate = #Predicate { upTo.contains($0.i) } - converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "i < %@", now as NSDate)) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "i < %@", now as NSDate)) + #expect(!converted.evaluate(with: ObjCObject())) } - func testNonObjC() { + @Test func nonObjC() throws { let predicate = #Predicate { $0.nonObjCKeypath == 2 } - XCTAssertNil(convert(predicate)) + #expect(convert(predicate) == nil) } - func testNonObjCConstantKeyPath() { + @Test func nonObjCConstantKeyPath() throws { 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())) + var converted = try #require(convert(predicate)) + #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())) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "f == YES")) + #expect(converted.evaluate(with: ObjCObject())) } - func testSubscripts() { + @Test func subscripts() throws { 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)) + var converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "(SELF.g)[0] == 2")) + #expect(!converted.evaluate(with: obj)) predicate = #Predicate { $0.h["A"] == 1 } - converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "(SELF.h)['A'] == 1")) - XCTAssertTrue(converted!.evaluate(with: obj)) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "(SELF.h)['A'] == 1")) + #expect(converted.evaluate(with: obj)) } - func testStringSearching() { + @Test func stringSearching() throws { 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)) + var converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "b CONTAINS 'foo'")) + #expect(!converted.evaluate(with: obj)) predicate = #Predicate { $0.b.starts(with: "foo") } - converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "b BEGINSWITH 'foo'")) - XCTAssertFalse(converted!.evaluate(with: obj)) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "b BEGINSWITH 'foo'")) + #expect(!converted.evaluate(with: obj)) } - func testExpressionEnforcement() { + @Test func expressionEnforcement() throws { var predicate = #Predicate { _ in true } - var converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "YES == YES")) - XCTAssertTrue(converted!.evaluate(with: "Hello")) + var converted = try #require(convert(predicate)) + #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")) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "NO == YES")) + #expect(!converted.evaluate(with: "Hello")) predicate = #Predicate { _ in true && false } - converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "(YES == YES) && (NO == YES)")) - XCTAssertFalse(converted!.evaluate(with: "Hello")) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "(YES == YES) && (NO == YES)")) + #expect(!converted.evaluate(with: "Hello")) predicate = #Predicate { $0.f } - converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "f == YES")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + converted = try #require(convert(predicate)) + #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())) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "TERNARY(f == YES AND YES == YES, YES, NO) == NO")) + #expect(!converted.evaluate(with: ObjCObject())) } - func testConditional() { + @Test func conditional() throws { 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())) + let converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "TERNARY(f == YES, YES, NO) == YES")) + #expect(converted.evaluate(with: ObjCObject())) } - func testOptionals() { + @Test func optionals() throws { 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())) + var converted = try #require(convert(predicate)) + #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())) + converted = try #require(convert(predicate)) + #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())) predicate = #Predicate { $0.j == nil } - converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "j == nil")) - XCTAssertTrue(converted!.evaluate(with: ObjCObject())) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "j == nil")) + #expect(converted.evaluate(with: ObjCObject())) } - func testUUID() { + @Test func uuid() throws { let obj = ObjCObject() let uuid = obj.k let predicate = #Predicate { $0.k == uuid } - let converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "k == %@", uuid as NSUUID)) - XCTAssertTrue(converted!.evaluate(with: obj)) + let converted = try #require(convert(predicate)) + #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)) } - func testDate() { + @Test func date() throws { 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())) + let converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "i > %@", now as NSDate)) + #expect(converted.evaluate(with: ObjCObject())) } - func testData() { + @Test func data() throws { 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())) + let converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "l == %@", data as NSData)) + #expect(converted.evaluate(with: ObjCObject())) } - func testURL() { + @Test func url() throws { 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())) + let converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "m == %@", url as NSURL)) + #expect(converted.evaluate(with: ObjCObject())) } - func testSequenceContainsWhere() { + @Test func sequenceContainsWhere() throws { 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())) + let converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "SUBQUERY(g, $_local_1, $_local_1 == 2).@count != 0")) + #expect(!converted.evaluate(with: ObjCObject())) } - func testSequenceAllSatisfy() { + @Test func sequenceAllSatisfy() throws { 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())) + let converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "SUBQUERY(g, $_local_1, NOT ($_local_1 == 2)).@count == 0")) + #expect(!converted.evaluate(with: ObjCObject())) } - func testMaxMin() { + @Test func maxMin() throws { 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())) + let converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "g.@max.#self == g.@min.#self")) + #expect(!converted.evaluate(with: ObjCObject())) } - func testStringComparison() { + @Test func stringComparison() throws { 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())) + var converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "TERNARY(b ==[c] 'ABC', 0, TERNARY(b <[c] 'ABC', -1, 1)) == 0")) + #expect(!converted.evaluate(with: ObjCObject())) 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())) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "TERNARY(b ==[l] 'ABC', 0, TERNARY(b <[l] 'ABC', -1, 1)) == 0")) + #expect(!converted.evaluate(with: ObjCObject())) predicate = #Predicate { $0.b.localizedStandardContains("ABC") } - converted = convert(predicate) - XCTAssertEqual(converted, NSPredicate(format: "b CONTAINS[cdl] 'ABC'")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "b CONTAINS[cdl] 'ABC'")) + #expect(!converted.evaluate(with: ObjCObject())) } - func testNested() { + @Test func nested() throws { let predicateA = Predicate { PredicateExpressions.build_Equal( lhs: PredicateExpressions.build_KeyPath( @@ -432,30 +435,30 @@ final class NSPredicateConversionTests: XCTestCase { ) } - let converted = convert(predicateB) - XCTAssertEqual(converted, NSPredicate(format: "a == 3 AND a > 2")) - XCTAssertFalse(converted!.evaluate(with: ObjCObject())) + let converted = try #require(convert(predicateB)) + #expect(converted == NSPredicate(format: "a == 3 AND a > 2")) + #expect(!converted.evaluate(with: ObjCObject())) } - func testRegex() { + @Test func regex() throws { 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())) + let converted = try #require(convert(predicate)) + #expect(converted == NSPredicate(format: "b MATCHES '.*[e-f][l-m].*'")) + #expect(converted.evaluate(with: ObjCObject())) } - func testExpression() { + @Test func expression() throws { let expression = #Expression { $0.a } - let converted = convert(expression) - XCTAssertEqual(converted, NSExpression(format: "a")) + let converted = try #require(convert(expression)) + #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)") + let value = converted.expressionValue(with: obj, context: nil) + #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 04f963508..147d4265b 100644 --- a/Tests/FoundationEssentialsTests/PredicateTests.swift +++ b/Tests/FoundationEssentialsTests/PredicateTests.swift @@ -10,35 +10,20 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport +import Testing + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation #endif #if canImport(RegexBuilder) import RegexBuilder #endif -#if !FOUNDATION_FRAMEWORK -// Resolve ambiguity between Foundation.#Predicate and FoundationEssentials.#Predicate -@freestanding(expression) -@available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) -macro Predicate(_ body: (repeat each Input) -> Bool) -> Predicate = #externalMacro(module: "FoundationMacros", type: "PredicateMacro") -@freestanding(expression) -@available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) -macro Expression(_ body: (repeat each Input) -> Output) -> Expression = #externalMacro(module: "FoundationMacros", type: "ExpressionMacro") -#endif - -// Work around an issue issue on older Swift compilers -#if compiler(>=6.0) - -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") - } - } - +@Suite("Predicate") +private struct PredicateTests { struct Object { var a: Int var b: String @@ -55,96 +40,89 @@ final class PredicateTests: XCTestCase { var a: Bool } - func testBasic() throws { + @Test func basic() 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: []))) + #expect(try !predicate.evaluate(Object(a: 1, b: "", c: 0, d: 0, e: "c", f: true, g: []))) + #expect(try predicate.evaluate(Object(a: 2, b: "", c: 0, d: 0, e: "c", f: true, g: []))) } - func testVariadic() throws { + @Test func variadic() 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)) + #expect(try predicate.evaluate(Object(a: 3, b: "", c: 0, d: 0, e: "c", f: true, g: []), 2)) } - func testArithmetic() throws { + @Test func arithmetic() 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: []))) + #expect(try predicate.evaluate(Object(a: 2, b: "", c: 0, d: 0, e: "c", f: true, g: []))) } - func testDivision() throws { + @Test func division() 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: []))) - } - - 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: []))) + #expect(try predicate.evaluate(Object(a: 6, b: "", c: 0, d: 0, e: "c", f: true, g: []))) + #expect(try predicate2.evaluate(Object(a: 2, b: "", c: 6.0, d: 0, e: "c", f: true, g: []))) } - func testUnaryMinus() throws { + @Test func unaryMinus() throws { let predicate = #Predicate { -$0.a == 17 } - XCTAssert(try predicate.evaluate(Object(a: -17, b: "", c: 0, d: 0, e: "c", f: true, g: []))) + #expect(try predicate.evaluate(Object(a: -17, b: "", c: 0, d: 0, e: "c", f: true, g: []))) } - func testCount() throws { + @Test func count() 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]))) + #expect(try predicate.evaluate(Object(a: 0, b: "", c: 0, d: 0, e: "c", f: true, g: [2, 3, 5, 7, 11]))) } - func testFilter() throws { + @Test func filter() 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]))) + #expect(try predicate.evaluate(Object(a: 0, b: "", c: 0.0, d: 17, e: "c", f: true, g: [3, 5, 7, 11, 13, 17, 19]))) } - func testContains() throws { + @Test func contains() 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]))) + #expect(try predicate.evaluate(Object(a: 13, b: "", c: 0.0, d: 0, e: "c", f: true, g: [2, 3, 5, 11, 13, 17]))) } - func testContainsWhere() throws { + @Test func containsWhere() 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]))) + #expect(try predicate.evaluate(Object(a: 2, b: "", c: 0.0, d: 0, e: "c", f: true, g: [3, 5, 7, 2, 11, 13]))) } - func testAllSatisfy() throws { + @Test func allSatisfy() 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]))) + #expect(try predicate.evaluate(Object(a: 0, b: "", c: 0.0, d: 2, e: "c", f: true, g: [3, 5, 7, 11, 13, 17, 19]))) } - func testOptional() throws { + @Test func optional() throws { struct Wrapper { let wrapped: T? } @@ -154,38 +132,40 @@ 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))) + #expect(try predicate.evaluate(Wrapper(wrapped: 4))) + #expect(try predicate.evaluate(Wrapper(wrapped: nil))) + #expect(try predicate2.evaluate(Wrapper(wrapped: 19))) + #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))) + #expect(try !predicate3.evaluate(Wrapper(wrapped: _NonCodableType()))) + #expect(try predicate3.evaluate(Wrapper(wrapped: nil))) } - func testConditional() throws { + @Test func conditional() throws { let predicate = #Predicate { ($0 ? $1 : $2) == "if branch" } - XCTAssert(try predicate.evaluate(true, "if branch", "else branch")) + #expect(try predicate.evaluate(true, "if branch", "else branch")) } - func testClosedRange() throws { + @Test func closedRange() 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: []))) + #expect(try predicate.evaluate(Object(a: 4, b: "", c: 0.0, d: 0, e: "c", f: true, g: []))) + #expect(try predicate2.evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: []))) } - func testRange() throws { + @Test func range() throws { let predicate = #Predicate { (3 ..< 5).contains($0.a) } @@ -193,50 +173,56 @@ 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: []))) + #expect(try predicate.evaluate(Object(a: 4, b: "", c: 0.0, d: 0, e: "c", f: true, g: []))) + #expect(try predicate2.evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: []))) } - func testRangeContains() throws { + @Test func rangeContains() 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: []))) + #expect(try !predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: []))) } - func testTypes() throws { + @Test func types() 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: []))) + #expect(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: []))) + #expect(try predicate2.evaluate(Object(a: 3, b: "", c: 0.0, d: 5, e: "c", f: true, g: []))) } - func testSubscripts() throws { + @Test func subscripts() 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: []))) + #expect(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [0]))) + #expect(try !predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1]))) + #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: []))) + #expect(try !predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [0, 1, 2]))) + #expect(try !predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [0, 1]))) + #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: [])) + } } - func testLazyDefaultValueSubscript() throws { + @Test func lazyDefaultValueSubscript() throws { struct Foo : Codable, Sendable { var property: Int { fatalError("This property should not have been accessed") @@ -247,46 +233,46 @@ final class PredicateTests: XCTestCase { let predicate = #Predicate<[String : Int]> { $0["key", default: foo.property] == 1 } - XCTAssertFalse(try predicate.evaluate(["key" : 2])) + #expect(try !predicate.evaluate(["key" : 2])) } - func testStaticValues() throws { - func assertPredicate(_ pred: Predicate, value: T, expected: Bool) throws { - XCTAssertEqual(try pred.evaluate(value), expected) + @Test func staticValues() throws { + func assertPredicate(_ pred: Predicate, value: T, expected: Bool, sourceLocation: SourceLocation = #_sourceLocation) throws { + #expect(try pred.evaluate(value) == expected, sourceLocation: sourceLocation) } try assertPredicate(.true, value: "Hello", expected: true) try assertPredicate(.false, value: "Hello", expected: false) } - func testMaxMin() throws { + @Test func maxMin() 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]))) + #expect(try !predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) + #expect(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1, 2]))) 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]))) + #expect(try !predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) + #expect(try predicate.evaluate(Object(a: 3, b: "", c: 0.0, d: 0, e: "c", f: true, g: [2, 3]))) } #if FOUNDATION_FRAMEWORK - func testCaseInsensitiveCompare() throws { + @Test func caseInsensitiveCompare() 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]))) + #expect(try predicate.evaluate(Object(a: 3, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) + #expect(try !predicate.evaluate(Object(a: 3, b: "def", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) } #endif - func testBuildDynamically() throws { + @Test func buildDynamically() throws { func _build(_ equal: Bool) -> Predicate { Predicate { if equal { @@ -303,11 +289,11 @@ final class PredicateTests: XCTestCase { } } - XCTAssertTrue(try _build(true).evaluate(1)) - XCTAssertFalse(try _build(false).evaluate(1)) + #expect(try _build(true).evaluate(1)) + #expect(try !_build(false).evaluate(1)) } - func testResilientKeyPaths() { + @Test func resilientKeyPaths() { // Local, non-resilient type struct Foo { let a: String // Non-resilient @@ -321,36 +307,26 @@ 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") - } - + @Test + @available(FoundationPredicateRegex 0.4, *) + func regex() 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: []))) + #expect(try predicate.evaluate(Object(a: 0, b: "_0/bc", c: 0, d: 0, e: " ", f: true, g: []))) + #expect(try !predicate.evaluate(Object(a: 0, b: "_C/bc", c: 0, d: 0, e: " ", f: true, g: []))) 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: []))) + #expect(try predicate.evaluate(Object(a: 0, b: "_0/bc", c: 0, d: 0, e: " ", f: true, g: []))) + #expect(try !predicate.evaluate(Object(a: 0, b: "_C/bc", c: 0, d: 0, e: " ", f: true, g: []))) } - func testRegex_RegexBuilder() throws { - #if !canImport(RegexBuilder) - throw XCTSkip("RegexBuilder is unavavailable on this platform") - #elseif !os(Linux) && !os(Android) && !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) + @Test + @available(FoundationPredicateRegex 0.4, *) + func regex_RegexBuilder() throws { let builtRegex = Regex { ChoiceOf { "A" @@ -363,17 +339,14 @@ 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 + #expect(try predicate.evaluate(Object(a: 0, b: "_0/bc", c: 0, d: 0, e: " ", f: true, g: []))) + #expect(try !predicate.evaluate(Object(a: 0, b: "_C/bc", c: 0, d: 0, e: " ", f: true, g: []))) } #endif - func testDebugDescription() throws { - guard #available(FoundationPredicate 0.3, *) else { - throw XCTSkip("This test is not available on this OS version") - } - + @Test + @available(FoundationPredicate 0.3, *) + func debugDescription() throws { let date = Date.now let predicate = #Predicate { if let num = $0.i as? Int { @@ -382,19 +355,15 @@ final class PredicateTests: XCTestCase { $0.h == date } } -#if FOUNDATION_FRAMEWORK - let moduleName = "Foundation" - let testModuleName = "Unit" -#else - let moduleName = "FoundationEssentials" - let testModuleName = "FoundationEssentialsTests" -#endif - XCTAssertEqual( - predicate.description, + + let dateName = _typeName(Date.self) + let objectName = _typeName(Object.self) + #expect( + predicate.description == """ capture1 (Swift.Int): 3 - capture2 (\(moduleName).Date): - Predicate<\(testModuleName).PredicateTests.Object> { input1 in + capture2 (\(dateName)): + Predicate<\(objectName)> { input1 in (input1.i as? Swift.Int).flatMap({ variable1 in variable1 == capture1 }) ?? (input1.h == capture2) @@ -403,18 +372,17 @@ final class PredicateTests: XCTestCase { ) let debugDescription = predicate.debugDescription.replacing(#/Variable\([0-9]+\)/#, with: "Variable(#)") - XCTAssertEqual( - 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)))))" + let predicateName = _typeName(Predicate.self) + #expect( + debugDescription == + "\(predicateName)(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<\(dateName)>(\(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") - } - + @Test + @available(FoundationPredicate 0.3, *) + func nested() throws { let predicateA = #Predicate { $0.a == 3 } @@ -423,26 +391,22 @@ final class PredicateTests: XCTestCase { 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]))) + #expect(try predicateA.evaluate(Object(a: 3, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) + #expect(try !predicateA.evaluate(Object(a: 2, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) + #expect(try predicateB.evaluate(Object(a: 3, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) + #expect(try !predicateB.evaluate(Object(a: 2, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) + #expect(try !predicateB.evaluate(Object(a: 4, b: "abc", c: 0.0, d: 0, e: "c", f: true, g: [1, 3]))) } #endif - func testExpression() throws { - guard #available(FoundationPredicate 0.4, *) else { - throw XCTSkip("This test is not available on this OS version") - } - + @Test + @available(FoundationPredicate 0.4, *) + func expression() throws { let expression = #Expression { $0 + 1 } for i in 0 ..< 10 { - XCTAssertEqual(try expression.evaluate(i), i + 1) + #expect(try expression.evaluate(i) == i + 1) } } } - -#endif // compiler(>=6.0) diff --git a/Tests/FoundationEssentialsTests/ProcessInfoTests.swift b/Tests/FoundationEssentialsTests/ProcessInfoTests.swift index 628c78773..cad8f18dd 100644 --- a/Tests/FoundationEssentialsTests/ProcessInfoTests.swift +++ b/Tests/FoundationEssentialsTests/ProcessInfoTests.swift @@ -10,12 +10,10 @@ // //===----------------------------------------------------------------------===// -#if canImport(TestSupport) -import TestSupport -#endif +import Testing #if canImport(FoundationEssentials) -@testable import FoundationEssentials +import FoundationEssentials #else @testable import Foundation #endif @@ -24,18 +22,20 @@ import TestSupport import Darwin #elseif canImport(Glibc) @preconcurrency import Glibc +#elseif canImport(CRT) +import CRT #endif -/// 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() { +// Since we can't really mock system settings like OS name, +// these tests simply check that the values returned are not empty +@Suite("ProcessInfo") +private struct ProcessInfoTests { + @Test func arguments() { 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 environment() { #if os(Windows) func setenv(_ key: String, _ value: String, _ overwrite: Int) -> Int32 { assert(overwrite == 1) @@ -47,66 +47,56 @@ final class ProcessInfoTests : XCTestCase { } #endif let env = ProcessInfo.processInfo.environment - XCTAssertTrue( - !env.isEmpty, "environment should not have been empty") + #expect(!env.isEmpty, "environment should not have been empty") - let preset = ProcessInfo.processInfo.environment["test"] + #expect(ProcessInfo.processInfo.environment["test"] == nil) setenv("test", "worked", 1) - let postset = ProcessInfo.processInfo.environment["test"] - XCTAssertNil(preset) - XCTAssertEqual(postset, "worked") + #expect(ProcessInfo.processInfo.environment["test"] == "worked") } - func testProcessIdentifier() { + @Test func processIdentifier() { let pid = ProcessInfo.processInfo.processIdentifier - XCTAssertEqual( - pid, getpid(), "ProcessInfo disagrees with getpid()") + #expect(pid == getpid(), "ProcessInfo disagrees with getpid()") } - func testGlobalUniqueString() { - let unique = ProcessInfo.processInfo.globallyUniqueString - XCTAssertNotEqual( - unique, - ProcessInfo.processInfo.globallyUniqueString, - "globallyUniqueString should never return the same string twice") + @Test func globalUniqueString() { + let a = ProcessInfo.processInfo.globallyUniqueString + let b = ProcessInfo.processInfo.globallyUniqueString + #expect(a != b, "globallyUniqueString should never return the same string twice") } - func testOperatingSystemVersionString() { + @Test func operatingSystemVersionString() { let version = ProcessInfo.processInfo.operatingSystemVersionString - XCTAssertFalse(version.isEmpty, "ProcessInfo returned empty string for operation system version") + #expect(!version.isEmpty, "ProcessInfo returned empty string for operation system version") #if os(Windows) - XCTAssertTrue(version.starts(with: "Windows"), "'\(version)' did not start with 'Windows'") + #expect(version.starts(with: "Windows"), "'\(version)' did not start with 'Windows'") #endif } - func testProcessorCount() { + @Test func processorCount() { 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 activeProcessorCount() { 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 physicalMemory() { 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() async throws { + @Test func systemUpTime() async throws { 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 try await Task.sleep(for: .milliseconds(100)) - XCTAssertTrue( - ProcessInfo.processInfo.systemUptime > now, - "ProcessInfo returned the same system uptime with 400") - + #expect(ProcessInfo.processInfo.systemUptime > now, "ProcessInfo returned the same system uptime with 400") } - func testOperatingSystemVersion() throws { + @Test func operatingSystemVersion() throws { #if canImport(Darwin) let version = ProcessInfo.processInfo.operatingSystemVersion #if os(visionOS) @@ -114,97 +104,96 @@ final class ProcessInfoTests : XCTestCase { #else let expectedMinMajorVersion = 2 #endif - XCTAssertGreaterThanOrEqual(version.majorVersion, expectedMinMajorVersion, "Unrealistic major system version") + #expect(version.majorVersion >= expectedMinMajorVersion, "Unrealistic major system version") #elseif os(Windows) || os(Linux) || os(Android) let minVersion = OperatingSystemVersion(majorVersion: 1, minorVersion: 0, patchVersion: 0) - XCTAssertTrue(ProcessInfo.processInfo.isOperatingSystemAtLeast(minVersion)) - #else - throw XCTSkip("This test is not supported on this platform") + #expect(ProcessInfo.processInfo.isOperatingSystemAtLeast(minVersion)) #endif } - func testOperatingSystemIsAtLeastVersion() throws { - #if !canImport(Darwin) - throw XCTSkip("This test is not supported on this platform") - #else + #if canImport(Darwin) + @Test + #else + @Test(.disabled("This test is not supported on this platform")) + #endif + func operatingSystemIsAtLeastVersion() throws { #if os(watchOS) - XCTAssertTrue(ProcessInfo.processInfo + #expect(ProcessInfo.processInfo .isOperatingSystemAtLeast( OperatingSystemVersion(majorVersion: 1, minorVersion: 12, patchVersion: 0) ), "ProcessInfo thinks 1.12 is > than 2.something") - XCTAssertTrue(ProcessInfo.processInfo + #expect(ProcessInfo.processInfo .isOperatingSystemAtLeast( OperatingSystemVersion(majorVersion: 1, minorVersion: 0, patchVersion: 0) ), "ProcessInfo thinks we are on watchOS 1") #elseif os(macOS) || (os(iOS) && !os(visionOS)) - XCTAssertTrue(ProcessInfo.processInfo + #expect(ProcessInfo.processInfo .isOperatingSystemAtLeast( OperatingSystemVersion(majorVersion: 6, minorVersion: 12, patchVersion: 0) ), "ProcessInfo thinks 6.12 is > than 10.something") - XCTAssertTrue(ProcessInfo.processInfo + #expect(ProcessInfo.processInfo .isOperatingSystemAtLeast( OperatingSystemVersion(majorVersion: 6, minorVersion: 0, patchVersion: 0) ), "ProcessInfo thinks we are on System 5") #endif - XCTAssertFalse(ProcessInfo.processInfo + #expect(!ProcessInfo.processInfo .isOperatingSystemAtLeast( OperatingSystemVersion(majorVersion: 70, minorVersion: 0, patchVersion: 0) ), "ProcessInfo thinks we are on System 70") - #endif } #if os(macOS) - func testUserName() { - XCTAssertFalse(ProcessInfo.processInfo.userName.isEmpty) + @Test func userName() { + #expect(!ProcessInfo.processInfo.userName.isEmpty) } - func testFullUserName() { - XCTAssertFalse(ProcessInfo.processInfo.fullUserName.isEmpty) + @Test func fullUserName() { + #expect(!ProcessInfo.processInfo.fullUserName.isEmpty) } #endif - func testProcessName() { + @Test func processName() { #if FOUNDATION_FRAMEWORK let targetName = "TestHost" #elseif os(Linux) || os(Windows) || os(Android) || os(FreeBSD) let targetName = "swift-foundationPackageTests.xctest" #else - let targetName = "xctest" + let targetName = "swiftpm-testing-helper" #endif let processInfo = ProcessInfo.processInfo let originalProcessName = processInfo.processName - XCTAssertEqual(originalProcessName, targetName) + #expect(originalProcessName == targetName) // Try assigning a new process name. let newProcessName = "TestProcessName" processInfo.processName = newProcessName - XCTAssertEqual(processInfo.processName, newProcessName) + #expect(processInfo.processName == newProcessName) // Assign back to the original process name. processInfo.processName = originalProcessName - XCTAssertEqual(processInfo.processName, originalProcessName) + #expect(processInfo.processName == originalProcessName) } - func testWindowsEnvironmentDoesNotContainMagicValues() { + @Test func windowsEnvironmentDoesNotContainMagicValues() { // Windows GetEnvironmentStringsW API can return // magic environment variables set by the cmd shell // that starts with `=` // This test makes sure we don't include these // magic variables let env = ProcessInfo.processInfo.environment - XCTAssertNil(env[""]) + #expect(env[""] == nil) } } // MARK: - ThermalState and PowerState tests #if FOUNDATION_FRAMEWORK extension ProcessInfoTests { - func testThermalPowerState() { + @Test func thermalPowerState() { // This test simply makes sure we can deliver the correct // thermal and power state for all platforms. // Fake a new value diff --git a/Tests/FoundationEssentialsTests/StringTests.swift b/Tests/FoundationEssentialsTests/StringTests.swift index 50a81618d..06ecd170c 100644 --- a/Tests/FoundationEssentialsTests/StringTests.swift +++ b/Tests/FoundationEssentialsTests/StringTests.swift @@ -10,22 +10,33 @@ // //===----------------------------------------------------------------------===// +import Testing + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(Musl) +import Musl +#elseif os(WASI) +import WASILibc +#elseif os(Windows) +import CRT +#endif + #if FOUNDATION_FRAMEWORK @testable import Foundation #else @testable import FoundationEssentials -#endif // FOUNDATION_FRAMEWORK - -#if canImport(TestSupport) -import TestSupport #endif -final class StringTests : XCTestCase { +@Suite("String") +private struct StringTests { // MARK: - Case mapping - func testCapitalize() { - func test(_ string: String, _ expected: String, file: StaticString = #filePath, line: UInt = #line) { - XCTAssertEqual(string._capitalized(), expected, file: file, line: line) + @Test func testCapitalize() { + func test(_ string: String, _ expected: String, sourceLocation: SourceLocation = #_sourceLocation) { + #expect(string.capitalized == expected, sourceLocation: sourceLocation) } test("iı", "Iı") @@ -63,9 +74,9 @@ final class StringTests : XCTestCase { test("ぁぃぅぇぉ ど ゕゖくけこ", "ぁぃぅぇぉ ど ゕゖくけこ") } - func testTrimmingWhitespace() { - func test(_ str: String, _ expected: String, file: StaticString = #filePath, line: UInt = #line) { - XCTAssertEqual(str._trimmingWhitespace(), expected, file: file, line: line) + @Test func testTrimmingWhitespace() { + func test(_ string: String, _ expected: String, sourceLocation: SourceLocation = #_sourceLocation) { + #expect(string._trimmingWhitespace() == expected, sourceLocation: sourceLocation) } test(" \tABCDEFGAbc \t \t ", "ABCDEFGAbc") test("ABCDEFGAbc \t \t ", "ABCDEFGAbc") @@ -79,12 +90,12 @@ final class StringTests : XCTestCase { test(" \u{202F}\u{00A0} X \u{202F}\u{00A0}", "X") // NBSP and narrow NBSP } - func testTrimmingCharactersWithPredicate() { - func test(_ str: String, while predicate: (Character) -> Bool, _ expected: Substring, file: StaticString = #filePath, line: UInt = #line) { - XCTAssertEqual(str._trimmingCharacters(while: predicate), expected, file: file, line: line) - } - + @Test func testTrimmingCharactersWithPredicate() { typealias TrimmingPredicate = (Character) -> Bool + + func test(_ str: String, while predicate: TrimmingPredicate, _ expected: Substring, sourceLocation: SourceLocation = #_sourceLocation) { + #expect(str._trimmingCharacters(while: predicate) == expected, sourceLocation: sourceLocation) + } let isNewline: TrimmingPredicate = { $0.isNewline } @@ -136,7 +147,7 @@ final class StringTests : XCTestCase { test("11 B\u{0662}\u{0661}", while: alwaysTrim, "") } - func _testRangeOfString(_ tested: String, string: String, anchored: Bool, backwards: Bool, _ expectation: Range?, file: StaticString = #filePath, line: UInt = #line) { + func _testRangeOfString(_ tested: String, string: String, anchored: Bool, backwards: Bool, _ expectation: Range?, sourceLocation: SourceLocation = #_sourceLocation) { let result = tested._range(of: string, anchored: anchored, backwards: backwards) var exp: Range? if let expectation { @@ -145,20 +156,20 @@ final class StringTests : XCTestCase { exp = nil } - var message: String + var message: Comment if let result { let readableRange = tested.distance(from: tested.startIndex, to: result.lowerBound)..?, file: StaticString = #filePath, line: UInt = #line) { - return _testRangeOfString(tested, string: string, anchored: anchored, backwards: backwards, expectation, file: file, line: line) + func testASCII(_ string: String, anchored: Bool, backwards: Bool, _ expectation: Range?, sourceLocation: SourceLocation = #_sourceLocation) { + return _testRangeOfString(tested, string: string, anchored: anchored, backwards: backwards, expectation, sourceLocation: sourceLocation) } tested = "ABCDEFGAbcABCDE" @@ -205,10 +216,10 @@ 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 = #filePath, line: UInt = #line) { - return _testRangeOfString(tested, string: string, anchored: anchored, backwards: backwards, expectation, file: file, line: line) + func test(_ string: String, anchored: Bool, backwards: Bool, _ expectation: Range?, sourceLocation: SourceLocation = #_sourceLocation) { + return _testRangeOfString(tested, string: string, anchored: anchored, backwards: backwards, expectation, sourceLocation: sourceLocation) } do { @@ -240,9 +251,9 @@ final class StringTests : XCTestCase { } } - func testRangeOfString_lineSeparator() { - func test(_ tested: String, _ string: String, anchored: Bool, backwards: Bool, _ expectation: Range?, file: StaticString = #filePath, line: UInt = #line) { - return _testRangeOfString(tested, string: string, anchored: anchored, backwards: backwards, expectation, file: file, line: line) + @Test func testRangeOfString_lineSeparator() { + func test(_ tested: String, _ string: String, anchored: Bool, backwards: Bool, _ expectation: Range?, sourceLocation: SourceLocation = #_sourceLocation) { + return _testRangeOfString(tested, string: string, anchored: anchored, backwards: backwards, expectation, sourceLocation: sourceLocation) } test("\r\n \r", "\r", anchored: false, backwards: false, 2..<3) test("\r\n \r", "\r", anchored: true, backwards: false, nil) @@ -255,13 +266,13 @@ final class StringTests : XCTestCase { test("\r \r\n \r", "\r", anchored: true, backwards: true, 4..<5) } - func testTryFromUTF16() { - func test(_ utf16Buffer: [UInt16], expected: String?, file: StaticString = #filePath, line: UInt = #line) { + @Test func testTryFromUTF16() { + func test(_ utf16Buffer: [UInt16], expected: String?, sourceLocation: SourceLocation = #_sourceLocation) { let result = utf16Buffer.withUnsafeBufferPointer { String(_utf16: $0) } - XCTAssertEqual(result, expected, file: file, line: line) + #expect(result == expected, sourceLocation: sourceLocation) } test([], expected: "") @@ -283,15 +294,14 @@ final class StringTests : XCTestCase { test([ 0xD800, 0x42 ], expected: nil) } - func testTryFromUTF16_roundtrip() { + @Test func testTryFromUTF16_roundtrip() { - func test(_ string: String, file: StaticString = #filePath, line: UInt = #line) { + func test(_ string: String, sourceLocation: SourceLocation = #_sourceLocation) { 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 == string, sourceLocation: sourceLocation) } // BMP: consists code points up to U+FFFF @@ -308,35 +318,35 @@ final class StringTests : XCTestCase { test("🏳️‍🌈AB👩‍👩‍👧‍👦ab🕵️‍♀️") } - func testRangeRegexB() throws { + @Test func testRangeRegexB() throws { let str = "self.name" let range = try str[...]._range(of: "\\bname"[...], options: .regularExpression) let start = str.index(str.startIndex, offsetBy: 5) let end = str.index(str.startIndex, offsetBy: 9) - XCTAssertEqual(range, start ..< end) + #expect(range == start ..< end) } - 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() { - func assertCString(_ ptr: UnsafePointer, equals other: String, file: StaticString = #filePath, line: UInt = #line) { - XCTAssertEqual(String(cString: ptr), other, file: file, line: line) + @Test func testFileSystemRepresentation() throws { + func assertCString(_ ptr: UnsafePointer, equals other: String, sourceLocation: SourceLocation = #_sourceLocation) { + #expect(String(cString: ptr) == other, sourceLocation: sourceLocation) } #if os(Windows) @@ -344,448 +354,445 @@ final class StringTests : XCTestCase { #else let original = "/Path1/Path Two/Path Three/Some Really Long File Name Section.txt" #endif - original.withFileSystemRepresentation { - XCTAssertNotNil($0) - assertCString($0!, equals: original) + try original.withFileSystemRepresentation { + assertCString(try #require($0), equals: original) } let withWhitespace = original + "\u{2000}\u{2001}" - withWhitespace.withFileSystemRepresentation { - XCTAssertNotNil($0) - assertCString($0!, equals: withWhitespace) + try withWhitespace.withFileSystemRepresentation { + assertCString(try #require($0), equals: withWhitespace) } let withHangul = original + "\u{AC00}\u{AC01}" - withHangul.withFileSystemRepresentation { buf1 in - XCTAssertNotNil(buf1) - buf1!.withMemoryRebound(to: UInt8.self, capacity: strlen(buf1!)) { buf1Rebound in + try withHangul.withFileSystemRepresentation { buf1 in + let buf1 = try #require(buf1) + try 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) + try fsr.withFileSystemRepresentation { buf2 in + let buf2 = try #require(buf2) + #expect(strcmp(buf1, buf2) == 0) } } } let withNullSuffix = original + "\u{0000}\u{0000}" - withNullSuffix.withFileSystemRepresentation { - XCTAssertNotNil($0) - assertCString($0!, equals: original) + try withNullSuffix.withFileSystemRepresentation { + assertCString(try #require($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") - - XCTAssertEqual("a/b".lastPathComponent, "b") - XCTAssertEqual("/a/b".lastPathComponent, "b") - XCTAssertEqual("a/b/".lastPathComponent, "b") - XCTAssertEqual("/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") - - XCTAssertEqual("/".lastPathComponent, "/") - XCTAssertEqual("//".lastPathComponent, "/") - XCTAssertEqual("/////".lastPathComponent, "/") - XCTAssertEqual("/./..//./..//".lastPathComponent, "..") - XCTAssertEqual("/😎/😂/❤️/".lastPathComponent, "❤️") - } - - func testRemovingDotSegments() { - XCTAssertEqual(".".removingDotSegments, "") - XCTAssertEqual("..".removingDotSegments, "") - XCTAssertEqual("../".removingDotSegments, "") - XCTAssertEqual("../.".removingDotSegments, "") - XCTAssertEqual("../..".removingDotSegments, "") - XCTAssertEqual("../../".removingDotSegments, "") - XCTAssertEqual("../../.".removingDotSegments, "") - XCTAssertEqual("../../..".removingDotSegments, "") - XCTAssertEqual("../../../".removingDotSegments, "") - XCTAssertEqual("../.././".removingDotSegments, "") - XCTAssertEqual("../../a".removingDotSegments, "a") - XCTAssertEqual("../../a/".removingDotSegments, "a/") - XCTAssertEqual(".././".removingDotSegments, "") - XCTAssertEqual(".././.".removingDotSegments, "") - XCTAssertEqual(".././..".removingDotSegments, "") - XCTAssertEqual(".././../".removingDotSegments, "") - XCTAssertEqual("../././".removingDotSegments, "") - XCTAssertEqual(".././a".removingDotSegments, "a") - XCTAssertEqual(".././a/".removingDotSegments, "a/") - XCTAssertEqual("../a".removingDotSegments, "a") - XCTAssertEqual("../a/".removingDotSegments, "a/") - XCTAssertEqual("../a/.".removingDotSegments, "a/") - XCTAssertEqual("../a/..".removingDotSegments, "/") - XCTAssertEqual("../a/../".removingDotSegments, "/") - XCTAssertEqual("../a/./".removingDotSegments, "a/") - XCTAssertEqual("../a/b".removingDotSegments, "a/b") - XCTAssertEqual("../a/b/".removingDotSegments, "a/b/") - XCTAssertEqual("./".removingDotSegments, "") - XCTAssertEqual("./.".removingDotSegments, "") - XCTAssertEqual("./..".removingDotSegments, "") - XCTAssertEqual("./../".removingDotSegments, "") - XCTAssertEqual("./../.".removingDotSegments, "") - XCTAssertEqual("./../..".removingDotSegments, "") - XCTAssertEqual("./../../".removingDotSegments, "") - XCTAssertEqual("./.././".removingDotSegments, "") - XCTAssertEqual("./../a".removingDotSegments, "a") - XCTAssertEqual("./../a/".removingDotSegments, "a/") - XCTAssertEqual("././".removingDotSegments, "") - XCTAssertEqual("././.".removingDotSegments, "") - XCTAssertEqual("././..".removingDotSegments, "") - XCTAssertEqual("././../".removingDotSegments, "") - XCTAssertEqual("./././".removingDotSegments, "") - XCTAssertEqual("././a".removingDotSegments, "a") - XCTAssertEqual("././a/".removingDotSegments, "a/") - XCTAssertEqual("./a".removingDotSegments, "a") - XCTAssertEqual("./a/".removingDotSegments, "a/") - XCTAssertEqual("./a/.".removingDotSegments, "a/") - XCTAssertEqual("./a/..".removingDotSegments, "/") - XCTAssertEqual("./a/../".removingDotSegments, "/") - XCTAssertEqual("./a/./".removingDotSegments, "a/") - XCTAssertEqual("./a/b".removingDotSegments, "a/b") - XCTAssertEqual("./a/b/".removingDotSegments, "a/b/") - XCTAssertEqual("/".removingDotSegments, "/") - XCTAssertEqual("/.".removingDotSegments, "/") - XCTAssertEqual("/..".removingDotSegments, "/") - XCTAssertEqual("/../".removingDotSegments, "/") - XCTAssertEqual("/../.".removingDotSegments, "/") - XCTAssertEqual("/../..".removingDotSegments, "/") - XCTAssertEqual("/../../".removingDotSegments, "/") - XCTAssertEqual("/../../.".removingDotSegments, "/") - XCTAssertEqual("/../../..".removingDotSegments, "/") - XCTAssertEqual("/../../../".removingDotSegments, "/") - XCTAssertEqual("/../.././".removingDotSegments, "/") - XCTAssertEqual("/../../a".removingDotSegments, "/a") - XCTAssertEqual("/../../a/".removingDotSegments, "/a/") - XCTAssertEqual("/.././".removingDotSegments, "/") - XCTAssertEqual("/.././.".removingDotSegments, "/") - XCTAssertEqual("/.././..".removingDotSegments, "/") - XCTAssertEqual("/.././../".removingDotSegments, "/") - XCTAssertEqual("/../././".removingDotSegments, "/") - XCTAssertEqual("/.././a".removingDotSegments, "/a") - XCTAssertEqual("/.././a/".removingDotSegments, "/a/") - XCTAssertEqual("/../a".removingDotSegments, "/a") - XCTAssertEqual("/../a/".removingDotSegments, "/a/") - XCTAssertEqual("/../a/.".removingDotSegments, "/a/") - XCTAssertEqual("/../a/..".removingDotSegments, "/") - XCTAssertEqual("/../a/../".removingDotSegments, "/") - XCTAssertEqual("/../a/./".removingDotSegments, "/a/") - XCTAssertEqual("/../a/b".removingDotSegments, "/a/b") - XCTAssertEqual("/../a/b/".removingDotSegments, "/a/b/") - XCTAssertEqual("/./".removingDotSegments, "/") - XCTAssertEqual("/./.".removingDotSegments, "/") - XCTAssertEqual("/./..".removingDotSegments, "/") - XCTAssertEqual("/./../".removingDotSegments, "/") - XCTAssertEqual("/./../.".removingDotSegments, "/") - XCTAssertEqual("/./../..".removingDotSegments, "/") - XCTAssertEqual("/./../../".removingDotSegments, "/") - XCTAssertEqual("/./.././".removingDotSegments, "/") - XCTAssertEqual("/./../a".removingDotSegments, "/a") - XCTAssertEqual("/./../a/".removingDotSegments, "/a/") - XCTAssertEqual("/././".removingDotSegments, "/") - XCTAssertEqual("/././.".removingDotSegments, "/") - XCTAssertEqual("/././..".removingDotSegments, "/") - XCTAssertEqual("/././../".removingDotSegments, "/") - XCTAssertEqual("/./././".removingDotSegments, "/") - XCTAssertEqual("/././a".removingDotSegments, "/a") - XCTAssertEqual("/././a/".removingDotSegments, "/a/") - XCTAssertEqual("/./a".removingDotSegments, "/a") - XCTAssertEqual("/./a/".removingDotSegments, "/a/") - XCTAssertEqual("/./a/.".removingDotSegments, "/a/") - XCTAssertEqual("/./a/..".removingDotSegments, "/") - XCTAssertEqual("/./a/../".removingDotSegments, "/") - XCTAssertEqual("/./a/./".removingDotSegments, "/a/") - XCTAssertEqual("/./a/b".removingDotSegments, "/a/b") - XCTAssertEqual("/./a/b/".removingDotSegments, "/a/b/") - XCTAssertEqual("/a".removingDotSegments, "/a") - XCTAssertEqual("/a/".removingDotSegments, "/a/") - XCTAssertEqual("/a/.".removingDotSegments, "/a/") - XCTAssertEqual("/a/..".removingDotSegments, "/") - XCTAssertEqual("/a/../".removingDotSegments, "/") - XCTAssertEqual("/a/../.".removingDotSegments, "/") - XCTAssertEqual("/a/../..".removingDotSegments, "/") - XCTAssertEqual("/a/../../".removingDotSegments, "/") - XCTAssertEqual("/a/.././".removingDotSegments, "/") - XCTAssertEqual("/a/../b".removingDotSegments, "/b") - XCTAssertEqual("/a/../b/".removingDotSegments, "/b/") - XCTAssertEqual("/a/./".removingDotSegments, "/a/") - XCTAssertEqual("/a/./.".removingDotSegments, "/a/") - XCTAssertEqual("/a/./..".removingDotSegments, "/") - XCTAssertEqual("/a/./../".removingDotSegments, "/") - XCTAssertEqual("/a/././".removingDotSegments, "/a/") - XCTAssertEqual("/a/./b".removingDotSegments, "/a/b") - XCTAssertEqual("/a/./b/".removingDotSegments, "/a/b/") - XCTAssertEqual("/a/b".removingDotSegments, "/a/b") - XCTAssertEqual("/a/b/".removingDotSegments, "/a/b/") - XCTAssertEqual("/a/b/.".removingDotSegments, "/a/b/") - XCTAssertEqual("/a/b/..".removingDotSegments, "/a/") - XCTAssertEqual("/a/b/../".removingDotSegments, "/a/") - XCTAssertEqual("/a/b/../.".removingDotSegments, "/a/") - XCTAssertEqual("/a/b/../..".removingDotSegments, "/") - XCTAssertEqual("/a/b/../../".removingDotSegments, "/") - XCTAssertEqual("/a/b/.././".removingDotSegments, "/a/") - XCTAssertEqual("/a/b/../c".removingDotSegments, "/a/c") - XCTAssertEqual("/a/b/../c/".removingDotSegments, "/a/c/") - XCTAssertEqual("/a/b/./".removingDotSegments, "/a/b/") - XCTAssertEqual("/a/b/./.".removingDotSegments, "/a/b/") - XCTAssertEqual("/a/b/./..".removingDotSegments, "/a/") - XCTAssertEqual("/a/b/./../".removingDotSegments, "/a/") - XCTAssertEqual("/a/b/././".removingDotSegments, "/a/b/") - XCTAssertEqual("/a/b/./c".removingDotSegments, "/a/b/c") - XCTAssertEqual("/a/b/./c/".removingDotSegments, "/a/b/c/") - XCTAssertEqual("/a/b/c".removingDotSegments, "/a/b/c") - XCTAssertEqual("/a/b/c/".removingDotSegments, "/a/b/c/") - XCTAssertEqual("/a/b/c/.".removingDotSegments, "/a/b/c/") - XCTAssertEqual("/a/b/c/..".removingDotSegments, "/a/b/") - XCTAssertEqual("/a/b/c/../".removingDotSegments, "/a/b/") - XCTAssertEqual("/a/b/c/./".removingDotSegments, "/a/b/c/") - XCTAssertEqual("a".removingDotSegments, "a") - XCTAssertEqual("a/".removingDotSegments, "a/") - XCTAssertEqual("a/.".removingDotSegments, "a/") - XCTAssertEqual("a/..".removingDotSegments, "/") - XCTAssertEqual("a/../".removingDotSegments, "/") - XCTAssertEqual("a/../.".removingDotSegments, "/") - XCTAssertEqual("a/../..".removingDotSegments, "/") - XCTAssertEqual("a/../../".removingDotSegments, "/") - XCTAssertEqual("a/.././".removingDotSegments, "/") - XCTAssertEqual("a/../b".removingDotSegments, "/b") - XCTAssertEqual("a/../b/".removingDotSegments, "/b/") - XCTAssertEqual("a/./".removingDotSegments, "a/") - XCTAssertEqual("a/./.".removingDotSegments, "a/") - XCTAssertEqual("a/./..".removingDotSegments, "/") - XCTAssertEqual("a/./../".removingDotSegments, "/") - XCTAssertEqual("a/././".removingDotSegments, "a/") - XCTAssertEqual("a/./b".removingDotSegments, "a/b") - XCTAssertEqual("a/./b/".removingDotSegments, "a/b/") - XCTAssertEqual("a/b".removingDotSegments, "a/b") - XCTAssertEqual("a/b/".removingDotSegments, "a/b/") - XCTAssertEqual("a/b/.".removingDotSegments, "a/b/") - XCTAssertEqual("a/b/..".removingDotSegments, "a/") - XCTAssertEqual("a/b/../".removingDotSegments, "a/") - XCTAssertEqual("a/b/../.".removingDotSegments, "a/") - XCTAssertEqual("a/b/../..".removingDotSegments, "/") - XCTAssertEqual("a/b/../../".removingDotSegments, "/") - XCTAssertEqual("a/b/.././".removingDotSegments, "a/") - XCTAssertEqual("a/b/../c".removingDotSegments, "a/c") - XCTAssertEqual("a/b/../c/".removingDotSegments, "a/c/") - XCTAssertEqual("a/b/./".removingDotSegments, "a/b/") - XCTAssertEqual("a/b/./.".removingDotSegments, "a/b/") - XCTAssertEqual("a/b/./..".removingDotSegments, "a/") - XCTAssertEqual("a/b/./../".removingDotSegments, "a/") - XCTAssertEqual("a/b/././".removingDotSegments, "a/b/") - XCTAssertEqual("a/b/./c".removingDotSegments, "a/b/c") - XCTAssertEqual("a/b/./c/".removingDotSegments, "a/b/c/") - XCTAssertEqual("a/b/c".removingDotSegments, "a/b/c") - XCTAssertEqual("a/b/c/".removingDotSegments, "a/b/c/") - XCTAssertEqual("a/b/c/.".removingDotSegments, "a/b/c/") - XCTAssertEqual("a/b/c/..".removingDotSegments, "a/b/") - XCTAssertEqual("a/b/c/../".removingDotSegments, "a/b/") - XCTAssertEqual("a/b/c/./".removingDotSegments, "a/b/c/") + @Test func testLastPathComponent() { + #expect("".lastPathComponent == "") + #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") + #expect("a/b/".lastPathComponent == "b") + #expect("/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") + + #expect("/".lastPathComponent == "/") + #expect("//".lastPathComponent == "/") + #expect("/////".lastPathComponent == "/") + #expect("/./..//./..//".lastPathComponent == "..") + #expect("/😎/😂/❤️/".lastPathComponent == "❤️") + } + + @Test func testRemovingDotSegments() { + #expect(".".removingDotSegments == "") + #expect("..".removingDotSegments == "") + #expect("../".removingDotSegments == "") + #expect("../.".removingDotSegments == "") + #expect("../..".removingDotSegments == "") + #expect("../../".removingDotSegments == "") + #expect("../../.".removingDotSegments == "") + #expect("../../..".removingDotSegments == "") + #expect("../../../".removingDotSegments == "") + #expect("../.././".removingDotSegments == "") + #expect("../../a".removingDotSegments == "a") + #expect("../../a/".removingDotSegments == "a/") + #expect(".././".removingDotSegments == "") + #expect(".././.".removingDotSegments == "") + #expect(".././..".removingDotSegments == "") + #expect(".././../".removingDotSegments == "") + #expect("../././".removingDotSegments == "") + #expect(".././a".removingDotSegments == "a") + #expect(".././a/".removingDotSegments == "a/") + #expect("../a".removingDotSegments == "a") + #expect("../a/".removingDotSegments == "a/") + #expect("../a/.".removingDotSegments == "a/") + #expect("../a/..".removingDotSegments == "/") + #expect("../a/../".removingDotSegments == "/") + #expect("../a/./".removingDotSegments == "a/") + #expect("../a/b".removingDotSegments == "a/b") + #expect("../a/b/".removingDotSegments == "a/b/") + #expect("./".removingDotSegments == "") + #expect("./.".removingDotSegments == "") + #expect("./..".removingDotSegments == "") + #expect("./../".removingDotSegments == "") + #expect("./../.".removingDotSegments == "") + #expect("./../..".removingDotSegments == "") + #expect("./../../".removingDotSegments == "") + #expect("./.././".removingDotSegments == "") + #expect("./../a".removingDotSegments == "a") + #expect("./../a/".removingDotSegments == "a/") + #expect("././".removingDotSegments == "") + #expect("././.".removingDotSegments == "") + #expect("././..".removingDotSegments == "") + #expect("././../".removingDotSegments == "") + #expect("./././".removingDotSegments == "") + #expect("././a".removingDotSegments == "a") + #expect("././a/".removingDotSegments == "a/") + #expect("./a".removingDotSegments == "a") + #expect("./a/".removingDotSegments == "a/") + #expect("./a/.".removingDotSegments == "a/") + #expect("./a/..".removingDotSegments == "/") + #expect("./a/../".removingDotSegments == "/") + #expect("./a/./".removingDotSegments == "a/") + #expect("./a/b".removingDotSegments == "a/b") + #expect("./a/b/".removingDotSegments == "a/b/") + #expect("/".removingDotSegments == "/") + #expect("/.".removingDotSegments == "/") + #expect("/..".removingDotSegments == "/") + #expect("/../".removingDotSegments == "/") + #expect("/../.".removingDotSegments == "/") + #expect("/../..".removingDotSegments == "/") + #expect("/../../".removingDotSegments == "/") + #expect("/../../.".removingDotSegments == "/") + #expect("/../../..".removingDotSegments == "/") + #expect("/../../../".removingDotSegments == "/") + #expect("/../.././".removingDotSegments == "/") + #expect("/../../a".removingDotSegments == "/a") + #expect("/../../a/".removingDotSegments == "/a/") + #expect("/.././".removingDotSegments == "/") + #expect("/.././.".removingDotSegments == "/") + #expect("/.././..".removingDotSegments == "/") + #expect("/.././../".removingDotSegments == "/") + #expect("/../././".removingDotSegments == "/") + #expect("/.././a".removingDotSegments == "/a") + #expect("/.././a/".removingDotSegments == "/a/") + #expect("/../a".removingDotSegments == "/a") + #expect("/../a/".removingDotSegments == "/a/") + #expect("/../a/.".removingDotSegments == "/a/") + #expect("/../a/..".removingDotSegments == "/") + #expect("/../a/../".removingDotSegments == "/") + #expect("/../a/./".removingDotSegments == "/a/") + #expect("/../a/b".removingDotSegments == "/a/b") + #expect("/../a/b/".removingDotSegments == "/a/b/") + #expect("/./".removingDotSegments == "/") + #expect("/./.".removingDotSegments == "/") + #expect("/./..".removingDotSegments == "/") + #expect("/./../".removingDotSegments == "/") + #expect("/./../.".removingDotSegments == "/") + #expect("/./../..".removingDotSegments == "/") + #expect("/./../../".removingDotSegments == "/") + #expect("/./.././".removingDotSegments == "/") + #expect("/./../a".removingDotSegments == "/a") + #expect("/./../a/".removingDotSegments == "/a/") + #expect("/././".removingDotSegments == "/") + #expect("/././.".removingDotSegments == "/") + #expect("/././..".removingDotSegments == "/") + #expect("/././../".removingDotSegments == "/") + #expect("/./././".removingDotSegments == "/") + #expect("/././a".removingDotSegments == "/a") + #expect("/././a/".removingDotSegments == "/a/") + #expect("/./a".removingDotSegments == "/a") + #expect("/./a/".removingDotSegments == "/a/") + #expect("/./a/.".removingDotSegments == "/a/") + #expect("/./a/..".removingDotSegments == "/") + #expect("/./a/../".removingDotSegments == "/") + #expect("/./a/./".removingDotSegments == "/a/") + #expect("/./a/b".removingDotSegments == "/a/b") + #expect("/./a/b/".removingDotSegments == "/a/b/") + #expect("/a".removingDotSegments == "/a") + #expect("/a/".removingDotSegments == "/a/") + #expect("/a/.".removingDotSegments == "/a/") + #expect("/a/..".removingDotSegments == "/") + #expect("/a/../".removingDotSegments == "/") + #expect("/a/../.".removingDotSegments == "/") + #expect("/a/../..".removingDotSegments == "/") + #expect("/a/../../".removingDotSegments == "/") + #expect("/a/.././".removingDotSegments == "/") + #expect("/a/../b".removingDotSegments == "/b") + #expect("/a/../b/".removingDotSegments == "/b/") + #expect("/a/./".removingDotSegments == "/a/") + #expect("/a/./.".removingDotSegments == "/a/") + #expect("/a/./..".removingDotSegments == "/") + #expect("/a/./../".removingDotSegments == "/") + #expect("/a/././".removingDotSegments == "/a/") + #expect("/a/./b".removingDotSegments == "/a/b") + #expect("/a/./b/".removingDotSegments == "/a/b/") + #expect("/a/b".removingDotSegments == "/a/b") + #expect("/a/b/".removingDotSegments == "/a/b/") + #expect("/a/b/.".removingDotSegments == "/a/b/") + #expect("/a/b/..".removingDotSegments == "/a/") + #expect("/a/b/../".removingDotSegments == "/a/") + #expect("/a/b/../.".removingDotSegments == "/a/") + #expect("/a/b/../..".removingDotSegments == "/") + #expect("/a/b/../../".removingDotSegments == "/") + #expect("/a/b/.././".removingDotSegments == "/a/") + #expect("/a/b/../c".removingDotSegments == "/a/c") + #expect("/a/b/../c/".removingDotSegments == "/a/c/") + #expect("/a/b/./".removingDotSegments == "/a/b/") + #expect("/a/b/./.".removingDotSegments == "/a/b/") + #expect("/a/b/./..".removingDotSegments == "/a/") + #expect("/a/b/./../".removingDotSegments == "/a/") + #expect("/a/b/././".removingDotSegments == "/a/b/") + #expect("/a/b/./c".removingDotSegments == "/a/b/c") + #expect("/a/b/./c/".removingDotSegments == "/a/b/c/") + #expect("/a/b/c".removingDotSegments == "/a/b/c") + #expect("/a/b/c/".removingDotSegments == "/a/b/c/") + #expect("/a/b/c/.".removingDotSegments == "/a/b/c/") + #expect("/a/b/c/..".removingDotSegments == "/a/b/") + #expect("/a/b/c/../".removingDotSegments == "/a/b/") + #expect("/a/b/c/./".removingDotSegments == "/a/b/c/") + #expect("a".removingDotSegments == "a") + #expect("a/".removingDotSegments == "a/") + #expect("a/.".removingDotSegments == "a/") + #expect("a/..".removingDotSegments == "/") + #expect("a/../".removingDotSegments == "/") + #expect("a/../.".removingDotSegments == "/") + #expect("a/../..".removingDotSegments == "/") + #expect("a/../../".removingDotSegments == "/") + #expect("a/.././".removingDotSegments == "/") + #expect("a/../b".removingDotSegments == "/b") + #expect("a/../b/".removingDotSegments == "/b/") + #expect("a/./".removingDotSegments == "a/") + #expect("a/./.".removingDotSegments == "a/") + #expect("a/./..".removingDotSegments == "/") + #expect("a/./../".removingDotSegments == "/") + #expect("a/././".removingDotSegments == "a/") + #expect("a/./b".removingDotSegments == "a/b") + #expect("a/./b/".removingDotSegments == "a/b/") + #expect("a/b".removingDotSegments == "a/b") + #expect("a/b/".removingDotSegments == "a/b/") + #expect("a/b/.".removingDotSegments == "a/b/") + #expect("a/b/..".removingDotSegments == "a/") + #expect("a/b/../".removingDotSegments == "a/") + #expect("a/b/../.".removingDotSegments == "a/") + #expect("a/b/../..".removingDotSegments == "/") + #expect("a/b/../../".removingDotSegments == "/") + #expect("a/b/.././".removingDotSegments == "a/") + #expect("a/b/../c".removingDotSegments == "a/c") + #expect("a/b/../c/".removingDotSegments == "a/c/") + #expect("a/b/./".removingDotSegments == "a/b/") + #expect("a/b/./.".removingDotSegments == "a/b/") + #expect("a/b/./..".removingDotSegments == "a/") + #expect("a/b/./../".removingDotSegments == "a/") + #expect("a/b/././".removingDotSegments == "a/b/") + #expect("a/b/./c".removingDotSegments == "a/b/c") + #expect("a/b/./c/".removingDotSegments == "a/b/c/") + #expect("a/b/c".removingDotSegments == "a/b/c") + #expect("a/b/c/".removingDotSegments == "a/b/c/") + #expect("a/b/c/.".removingDotSegments == "a/b/c/") + #expect("a/b/c/..".removingDotSegments == "a/b/") + #expect("a/b/c/../".removingDotSegments == "a/b/") + #expect("a/b/c/./".removingDotSegments == "a/b/c/") // None of the inputs below contain "." or ".." and should therefore be treated as regular path components - XCTAssertEqual("...".removingDotSegments, "...") - XCTAssertEqual(".../".removingDotSegments, ".../") - XCTAssertEqual(".../...".removingDotSegments, ".../...") - XCTAssertEqual(".../.../".removingDotSegments, ".../.../") - XCTAssertEqual(".../..a".removingDotSegments, ".../..a") - XCTAssertEqual(".../..a/".removingDotSegments, ".../..a/") - XCTAssertEqual(".../.a".removingDotSegments, ".../.a") - XCTAssertEqual(".../.a/".removingDotSegments, ".../.a/") - XCTAssertEqual(".../a.".removingDotSegments, ".../a.") - XCTAssertEqual(".../a..".removingDotSegments, ".../a..") - XCTAssertEqual(".../a../".removingDotSegments, ".../a../") - XCTAssertEqual(".../a./".removingDotSegments, ".../a./") - XCTAssertEqual("..a".removingDotSegments, "..a") - XCTAssertEqual("..a/".removingDotSegments, "..a/") - XCTAssertEqual("..a/...".removingDotSegments, "..a/...") - XCTAssertEqual("..a/.../".removingDotSegments, "..a/.../") - XCTAssertEqual("..a/..b".removingDotSegments, "..a/..b") - XCTAssertEqual("..a/..b/".removingDotSegments, "..a/..b/") - XCTAssertEqual("..a/.b".removingDotSegments, "..a/.b") - XCTAssertEqual("..a/.b/".removingDotSegments, "..a/.b/") - XCTAssertEqual("..a/b.".removingDotSegments, "..a/b.") - XCTAssertEqual("..a/b..".removingDotSegments, "..a/b..") - XCTAssertEqual("..a/b../".removingDotSegments, "..a/b../") - XCTAssertEqual("..a/b./".removingDotSegments, "..a/b./") - XCTAssertEqual(".a".removingDotSegments, ".a") - XCTAssertEqual(".a/".removingDotSegments, ".a/") - XCTAssertEqual(".a/...".removingDotSegments, ".a/...") - XCTAssertEqual(".a/.../".removingDotSegments, ".a/.../") - XCTAssertEqual(".a/..b".removingDotSegments, ".a/..b") - XCTAssertEqual(".a/..b/".removingDotSegments, ".a/..b/") - XCTAssertEqual(".a/.b".removingDotSegments, ".a/.b") - XCTAssertEqual(".a/.b/".removingDotSegments, ".a/.b/") - XCTAssertEqual(".a/b.".removingDotSegments, ".a/b.") - XCTAssertEqual(".a/b..".removingDotSegments, ".a/b..") - XCTAssertEqual(".a/b../".removingDotSegments, ".a/b../") - XCTAssertEqual(".a/b./".removingDotSegments, ".a/b./") - XCTAssertEqual("/".removingDotSegments, "/") - XCTAssertEqual("/...".removingDotSegments, "/...") - XCTAssertEqual("/.../".removingDotSegments, "/.../") - XCTAssertEqual("/..a".removingDotSegments, "/..a") - XCTAssertEqual("/..a/".removingDotSegments, "/..a/") - XCTAssertEqual("/.a".removingDotSegments, "/.a") - XCTAssertEqual("/.a/".removingDotSegments, "/.a/") - XCTAssertEqual("/a.".removingDotSegments, "/a.") - XCTAssertEqual("/a..".removingDotSegments, "/a..") - XCTAssertEqual("/a../".removingDotSegments, "/a../") - XCTAssertEqual("/a./".removingDotSegments, "/a./") - XCTAssertEqual("a.".removingDotSegments, "a.") - XCTAssertEqual("a..".removingDotSegments, "a..") - XCTAssertEqual("a../".removingDotSegments, "a../") - XCTAssertEqual("a../...".removingDotSegments, "a../...") - XCTAssertEqual("a../.../".removingDotSegments, "a../.../") - XCTAssertEqual("a../..b".removingDotSegments, "a../..b") - XCTAssertEqual("a../..b/".removingDotSegments, "a../..b/") - XCTAssertEqual("a../.b".removingDotSegments, "a../.b") - XCTAssertEqual("a../.b/".removingDotSegments, "a../.b/") - XCTAssertEqual("a../b.".removingDotSegments, "a../b.") - XCTAssertEqual("a../b..".removingDotSegments, "a../b..") - XCTAssertEqual("a../b../".removingDotSegments, "a../b../") - XCTAssertEqual("a../b./".removingDotSegments, "a../b./") - XCTAssertEqual("a./".removingDotSegments, "a./") - XCTAssertEqual("a./...".removingDotSegments, "a./...") - XCTAssertEqual("a./.../".removingDotSegments, "a./.../") - XCTAssertEqual("a./..b".removingDotSegments, "a./..b") - XCTAssertEqual("a./..b/".removingDotSegments, "a./..b/") - XCTAssertEqual("a./.b".removingDotSegments, "a./.b") - XCTAssertEqual("a./.b/".removingDotSegments, "a./.b/") - XCTAssertEqual("a./b.".removingDotSegments, "a./b.") - XCTAssertEqual("a./b..".removingDotSegments, "a./b..") - XCTAssertEqual("a./b../".removingDotSegments, "a./b../") - XCTAssertEqual("a./b./".removingDotSegments, "a./b./") + #expect("...".removingDotSegments == "...") + #expect(".../".removingDotSegments == ".../") + #expect(".../...".removingDotSegments == ".../...") + #expect(".../.../".removingDotSegments == ".../.../") + #expect(".../..a".removingDotSegments == ".../..a") + #expect(".../..a/".removingDotSegments == ".../..a/") + #expect(".../.a".removingDotSegments == ".../.a") + #expect(".../.a/".removingDotSegments == ".../.a/") + #expect(".../a.".removingDotSegments == ".../a.") + #expect(".../a..".removingDotSegments == ".../a..") + #expect(".../a../".removingDotSegments == ".../a../") + #expect(".../a./".removingDotSegments == ".../a./") + #expect("..a".removingDotSegments == "..a") + #expect("..a/".removingDotSegments == "..a/") + #expect("..a/...".removingDotSegments == "..a/...") + #expect("..a/.../".removingDotSegments == "..a/.../") + #expect("..a/..b".removingDotSegments == "..a/..b") + #expect("..a/..b/".removingDotSegments == "..a/..b/") + #expect("..a/.b".removingDotSegments == "..a/.b") + #expect("..a/.b/".removingDotSegments == "..a/.b/") + #expect("..a/b.".removingDotSegments == "..a/b.") + #expect("..a/b..".removingDotSegments == "..a/b..") + #expect("..a/b../".removingDotSegments == "..a/b../") + #expect("..a/b./".removingDotSegments == "..a/b./") + #expect(".a".removingDotSegments == ".a") + #expect(".a/".removingDotSegments == ".a/") + #expect(".a/...".removingDotSegments == ".a/...") + #expect(".a/.../".removingDotSegments == ".a/.../") + #expect(".a/..b".removingDotSegments == ".a/..b") + #expect(".a/..b/".removingDotSegments == ".a/..b/") + #expect(".a/.b".removingDotSegments == ".a/.b") + #expect(".a/.b/".removingDotSegments == ".a/.b/") + #expect(".a/b.".removingDotSegments == ".a/b.") + #expect(".a/b..".removingDotSegments == ".a/b..") + #expect(".a/b../".removingDotSegments == ".a/b../") + #expect(".a/b./".removingDotSegments == ".a/b./") + #expect("/".removingDotSegments == "/") + #expect("/...".removingDotSegments == "/...") + #expect("/.../".removingDotSegments == "/.../") + #expect("/..a".removingDotSegments == "/..a") + #expect("/..a/".removingDotSegments == "/..a/") + #expect("/.a".removingDotSegments == "/.a") + #expect("/.a/".removingDotSegments == "/.a/") + #expect("/a.".removingDotSegments == "/a.") + #expect("/a..".removingDotSegments == "/a..") + #expect("/a../".removingDotSegments == "/a../") + #expect("/a./".removingDotSegments == "/a./") + #expect("a.".removingDotSegments == "a.") + #expect("a..".removingDotSegments == "a..") + #expect("a../".removingDotSegments == "a../") + #expect("a../...".removingDotSegments == "a../...") + #expect("a../.../".removingDotSegments == "a../.../") + #expect("a../..b".removingDotSegments == "a../..b") + #expect("a../..b/".removingDotSegments == "a../..b/") + #expect("a../.b".removingDotSegments == "a../.b") + #expect("a../.b/".removingDotSegments == "a../.b/") + #expect("a../b.".removingDotSegments == "a../b.") + #expect("a../b..".removingDotSegments == "a../b..") + #expect("a../b../".removingDotSegments == "a../b../") + #expect("a../b./".removingDotSegments == "a../b./") + #expect("a./".removingDotSegments == "a./") + #expect("a./...".removingDotSegments == "a./...") + #expect("a./.../".removingDotSegments == "a./.../") + #expect("a./..b".removingDotSegments == "a./..b") + #expect("a./..b/".removingDotSegments == "a./..b/") + #expect("a./.b".removingDotSegments == "a./.b") + #expect("a./.b/".removingDotSegments == "a./.b/") + #expect("a./b.".removingDotSegments == "a./b.") + #expect("a./b..".removingDotSegments == "a./b..") + #expect("a./b../".removingDotSegments == "a./b../") + #expect("a./b./".removingDotSegments == "a./b./") // Repeated slashes should not be resolved when only removing dot segments - XCTAssertEqual("../..//".removingDotSegments, "/") - XCTAssertEqual(".././/".removingDotSegments, "/") - XCTAssertEqual("..//".removingDotSegments, "/") - XCTAssertEqual("..//.".removingDotSegments, "/") - XCTAssertEqual("..//..".removingDotSegments, "/") - XCTAssertEqual("..//../".removingDotSegments, "/") - XCTAssertEqual("..//./".removingDotSegments, "/") - XCTAssertEqual("..///".removingDotSegments, "//") - XCTAssertEqual("..//a".removingDotSegments, "/a") - XCTAssertEqual("..//a/".removingDotSegments, "/a/") - XCTAssertEqual("../a//".removingDotSegments, "a//") - XCTAssertEqual("./..//".removingDotSegments, "/") - XCTAssertEqual("././/".removingDotSegments, "/") - XCTAssertEqual(".//".removingDotSegments, "/") - XCTAssertEqual(".//.".removingDotSegments, "/") - XCTAssertEqual(".//..".removingDotSegments, "/") - XCTAssertEqual(".//../".removingDotSegments, "/") - XCTAssertEqual(".//./".removingDotSegments, "/") - XCTAssertEqual(".///".removingDotSegments, "//") - XCTAssertEqual(".//a".removingDotSegments, "/a") - XCTAssertEqual(".//a/".removingDotSegments, "/a/") - XCTAssertEqual("./a//".removingDotSegments, "a//") - XCTAssertEqual("/../..//".removingDotSegments, "//") - XCTAssertEqual("/.././/".removingDotSegments, "//") - XCTAssertEqual("/..//".removingDotSegments, "//") - XCTAssertEqual("/..//.".removingDotSegments, "//") - XCTAssertEqual("/..//..".removingDotSegments, "/") - XCTAssertEqual("/..//../".removingDotSegments, "/") - XCTAssertEqual("/..//./".removingDotSegments, "//") - XCTAssertEqual("/..///".removingDotSegments, "///") - XCTAssertEqual("/..//a".removingDotSegments, "//a") - XCTAssertEqual("/..//a/".removingDotSegments, "//a/") - XCTAssertEqual("/../a//".removingDotSegments, "/a//") - XCTAssertEqual("/./..//".removingDotSegments, "//") - XCTAssertEqual("/././/".removingDotSegments, "//") - XCTAssertEqual("/.//".removingDotSegments, "//") - XCTAssertEqual("/.//.".removingDotSegments, "//") - XCTAssertEqual("/.//..".removingDotSegments, "/") - XCTAssertEqual("/.//../".removingDotSegments, "/") - XCTAssertEqual("/.//./".removingDotSegments, "//") - XCTAssertEqual("/.///".removingDotSegments, "///") - XCTAssertEqual("/.//a".removingDotSegments, "//a") - XCTAssertEqual("/.//a/".removingDotSegments, "//a/") - XCTAssertEqual("/./a//".removingDotSegments, "/a//") - XCTAssertEqual("//".removingDotSegments, "//") - XCTAssertEqual("//.".removingDotSegments, "//") - XCTAssertEqual("//..".removingDotSegments, "/") - XCTAssertEqual("//../".removingDotSegments, "/") - XCTAssertEqual("//./".removingDotSegments, "//") - XCTAssertEqual("///".removingDotSegments, "///") - XCTAssertEqual("//a".removingDotSegments, "//a") - XCTAssertEqual("//a/".removingDotSegments, "//a/") - XCTAssertEqual("/a/..//".removingDotSegments, "//") - XCTAssertEqual("/a/.//".removingDotSegments, "/a//") - XCTAssertEqual("/a//".removingDotSegments, "/a//") - XCTAssertEqual("/a//.".removingDotSegments, "/a//") - XCTAssertEqual("/a//..".removingDotSegments, "/a/") - XCTAssertEqual("/a//../".removingDotSegments, "/a/") - XCTAssertEqual("/a//./".removingDotSegments, "/a//") - XCTAssertEqual("/a///".removingDotSegments, "/a///") - XCTAssertEqual("/a//b".removingDotSegments, "/a//b") - XCTAssertEqual("/a//b/".removingDotSegments, "/a//b/") - XCTAssertEqual("/a/b/..//".removingDotSegments, "/a//") - XCTAssertEqual("/a/b/.//".removingDotSegments, "/a/b//") - XCTAssertEqual("/a/b//".removingDotSegments, "/a/b//") - XCTAssertEqual("/a/b//.".removingDotSegments, "/a/b//") - XCTAssertEqual("/a/b//..".removingDotSegments, "/a/b/") - XCTAssertEqual("/a/b//../".removingDotSegments, "/a/b/") - XCTAssertEqual("/a/b//./".removingDotSegments, "/a/b//") - XCTAssertEqual("/a/b///".removingDotSegments, "/a/b///") - XCTAssertEqual("/a/b//c".removingDotSegments, "/a/b//c") - XCTAssertEqual("/a/b//c/".removingDotSegments, "/a/b//c/") - XCTAssertEqual("/a/b/c//".removingDotSegments, "/a/b/c//") - XCTAssertEqual("a/..//".removingDotSegments, "//") - XCTAssertEqual("a/.//".removingDotSegments, "a//") - XCTAssertEqual("a//".removingDotSegments, "a//") - XCTAssertEqual("a//.".removingDotSegments, "a//") - XCTAssertEqual("a//..".removingDotSegments, "a/") - XCTAssertEqual("a//../".removingDotSegments, "a/") - XCTAssertEqual("a//./".removingDotSegments, "a//") - XCTAssertEqual("a///".removingDotSegments, "a///") - XCTAssertEqual("a//b".removingDotSegments, "a//b") - XCTAssertEqual("a//b/".removingDotSegments, "a//b/") - XCTAssertEqual("a/b/..//".removingDotSegments, "a//") - XCTAssertEqual("a/b/.//".removingDotSegments, "a/b//") - XCTAssertEqual("a/b//".removingDotSegments, "a/b//") - XCTAssertEqual("a/b//.".removingDotSegments, "a/b//") - XCTAssertEqual("a/b//..".removingDotSegments, "a/b/") - XCTAssertEqual("a/b//../".removingDotSegments, "a/b/") - XCTAssertEqual("a/b//./".removingDotSegments, "a/b//") - XCTAssertEqual("a/b///".removingDotSegments, "a/b///") - XCTAssertEqual("a/b//c".removingDotSegments, "a/b//c") - XCTAssertEqual("a/b//c/".removingDotSegments, "a/b//c/") - XCTAssertEqual("a/b/c//".removingDotSegments, "a/b/c//") - } - - func testPathExtension() { + #expect("../..//".removingDotSegments == "/") + #expect(".././/".removingDotSegments == "/") + #expect("..//".removingDotSegments == "/") + #expect("..//.".removingDotSegments == "/") + #expect("..//..".removingDotSegments == "/") + #expect("..//../".removingDotSegments == "/") + #expect("..//./".removingDotSegments == "/") + #expect("..///".removingDotSegments == "//") + #expect("..//a".removingDotSegments == "/a") + #expect("..//a/".removingDotSegments == "/a/") + #expect("../a//".removingDotSegments == "a//") + #expect("./..//".removingDotSegments == "/") + #expect("././/".removingDotSegments == "/") + #expect(".//".removingDotSegments == "/") + #expect(".//.".removingDotSegments == "/") + #expect(".//..".removingDotSegments == "/") + #expect(".//../".removingDotSegments == "/") + #expect(".//./".removingDotSegments == "/") + #expect(".///".removingDotSegments == "//") + #expect(".//a".removingDotSegments == "/a") + #expect(".//a/".removingDotSegments == "/a/") + #expect("./a//".removingDotSegments == "a//") + #expect("/../..//".removingDotSegments == "//") + #expect("/.././/".removingDotSegments == "//") + #expect("/..//".removingDotSegments == "//") + #expect("/..//.".removingDotSegments == "//") + #expect("/..//..".removingDotSegments == "/") + #expect("/..//../".removingDotSegments == "/") + #expect("/..//./".removingDotSegments == "//") + #expect("/..///".removingDotSegments == "///") + #expect("/..//a".removingDotSegments == "//a") + #expect("/..//a/".removingDotSegments == "//a/") + #expect("/../a//".removingDotSegments == "/a//") + #expect("/./..//".removingDotSegments == "//") + #expect("/././/".removingDotSegments == "//") + #expect("/.//".removingDotSegments == "//") + #expect("/.//.".removingDotSegments == "//") + #expect("/.//..".removingDotSegments == "/") + #expect("/.//../".removingDotSegments == "/") + #expect("/.//./".removingDotSegments == "//") + #expect("/.///".removingDotSegments == "///") + #expect("/.//a".removingDotSegments == "//a") + #expect("/.//a/".removingDotSegments == "//a/") + #expect("/./a//".removingDotSegments == "/a//") + #expect("//".removingDotSegments == "//") + #expect("//.".removingDotSegments == "//") + #expect("//..".removingDotSegments == "/") + #expect("//../".removingDotSegments == "/") + #expect("//./".removingDotSegments == "//") + #expect("///".removingDotSegments == "///") + #expect("//a".removingDotSegments == "//a") + #expect("//a/".removingDotSegments == "//a/") + #expect("/a/..//".removingDotSegments == "//") + #expect("/a/.//".removingDotSegments == "/a//") + #expect("/a//".removingDotSegments == "/a//") + #expect("/a//.".removingDotSegments == "/a//") + #expect("/a//..".removingDotSegments == "/a/") + #expect("/a//../".removingDotSegments == "/a/") + #expect("/a//./".removingDotSegments == "/a//") + #expect("/a///".removingDotSegments == "/a///") + #expect("/a//b".removingDotSegments == "/a//b") + #expect("/a//b/".removingDotSegments == "/a//b/") + #expect("/a/b/..//".removingDotSegments == "/a//") + #expect("/a/b/.//".removingDotSegments == "/a/b//") + #expect("/a/b//".removingDotSegments == "/a/b//") + #expect("/a/b//.".removingDotSegments == "/a/b//") + #expect("/a/b//..".removingDotSegments == "/a/b/") + #expect("/a/b//../".removingDotSegments == "/a/b/") + #expect("/a/b//./".removingDotSegments == "/a/b//") + #expect("/a/b///".removingDotSegments == "/a/b///") + #expect("/a/b//c".removingDotSegments == "/a/b//c") + #expect("/a/b//c/".removingDotSegments == "/a/b//c/") + #expect("/a/b/c//".removingDotSegments == "/a/b/c//") + #expect("a/..//".removingDotSegments == "//") + #expect("a/.//".removingDotSegments == "a//") + #expect("a//".removingDotSegments == "a//") + #expect("a//.".removingDotSegments == "a//") + #expect("a//..".removingDotSegments == "a/") + #expect("a//../".removingDotSegments == "a/") + #expect("a//./".removingDotSegments == "a//") + #expect("a///".removingDotSegments == "a///") + #expect("a//b".removingDotSegments == "a//b") + #expect("a//b/".removingDotSegments == "a//b/") + #expect("a/b/..//".removingDotSegments == "a//") + #expect("a/b/.//".removingDotSegments == "a/b//") + #expect("a/b//".removingDotSegments == "a/b//") + #expect("a/b//.".removingDotSegments == "a/b//") + #expect("a/b//..".removingDotSegments == "a/b/") + #expect("a/b//../".removingDotSegments == "a/b/") + #expect("a/b//./".removingDotSegments == "a/b//") + #expect("a/b///".removingDotSegments == "a/b///") + #expect("a/b//c".removingDotSegments == "a/b//c") + #expect("a/b//c/".removingDotSegments == "a/b//c/") + #expect("a/b/c//".removingDotSegments == "a/b/c//") + } + + @Test func testPathExtension() { let stringNoExtension = "0123456789" let stringWithExtension = "\(stringNoExtension).foo" - XCTAssertEqual(stringNoExtension.appendingPathExtension("foo"), stringWithExtension) + #expect(stringNoExtension.appendingPathExtension("foo") == stringWithExtension) var invalidExtensions = [String]() for scalar in String.invalidExtensionScalars { @@ -795,71 +802,71 @@ final class StringTests : XCTestCase { } let invalidExtensionStrings = invalidExtensions.map { "\(stringNoExtension).\($0)" } - XCTAssertEqual(stringNoExtension.pathExtension, "") - XCTAssertEqual(stringWithExtension.pathExtension, "foo") - XCTAssertEqual(stringNoExtension.deletingPathExtension(), stringNoExtension) - XCTAssertEqual(stringWithExtension.deletingPathExtension(), stringNoExtension) + #expect(stringNoExtension.pathExtension == "") + #expect(stringWithExtension.pathExtension == "foo") + #expect(stringNoExtension.deletingPathExtension() == stringNoExtension) + #expect(stringWithExtension.deletingPathExtension() == stringNoExtension) for invalidExtensionString in invalidExtensionStrings { if invalidExtensionString.last == "/" { continue } - XCTAssertEqual(invalidExtensionString.pathExtension, "") - XCTAssertEqual(invalidExtensionString.deletingPathExtension(), invalidExtensionString) + #expect(invalidExtensionString.pathExtension == "") + #expect(invalidExtensionString.deletingPathExtension() == invalidExtensionString) } for invalidExtension in invalidExtensions { - XCTAssertEqual(stringNoExtension.appendingPathExtension(invalidExtension), stringNoExtension) - } - } - - func testAppendingPathExtension() { - XCTAssertEqual("".appendingPathExtension("foo"), ".foo") - XCTAssertEqual("/".appendingPathExtension("foo"), "/.foo") - XCTAssertEqual("//".appendingPathExtension("foo"), "/.foo/") - XCTAssertEqual("/path".appendingPathExtension("foo"), "/path.foo") - XCTAssertEqual("/path.zip".appendingPathExtension("foo"), "/path.zip.foo") - XCTAssertEqual("/path/".appendingPathExtension("foo"), "/path.foo/") - XCTAssertEqual("/path//".appendingPathExtension("foo"), "/path.foo/") - XCTAssertEqual("path".appendingPathExtension("foo"), "path.foo") - XCTAssertEqual("path/".appendingPathExtension("foo"), "path.foo/") - XCTAssertEqual("path//".appendingPathExtension("foo"), "path.foo/") - } - - func testDeletingPathExtenstion() { - XCTAssertEqual("".deletingPathExtension(), "") - XCTAssertEqual("/".deletingPathExtension(), "/") - XCTAssertEqual("/foo/bar".deletingPathExtension(), "/foo/bar") - XCTAssertEqual("/foo/bar.zip".deletingPathExtension(), "/foo/bar") - XCTAssertEqual("/foo/bar.baz.zip".deletingPathExtension(), "/foo/bar.baz") - XCTAssertEqual(".".deletingPathExtension(), ".") - XCTAssertEqual(".zip".deletingPathExtension(), ".zip") - XCTAssertEqual("zip.".deletingPathExtension(), "zip.") - XCTAssertEqual(".zip.".deletingPathExtension(), ".zip.") - XCTAssertEqual("/foo/bar/.zip".deletingPathExtension(), "/foo/bar/.zip") - XCTAssertEqual("..".deletingPathExtension(), "..") - XCTAssertEqual("..zip".deletingPathExtension(), "..zip") - XCTAssertEqual("/foo/bar/..zip".deletingPathExtension(), "/foo/bar/..zip") - XCTAssertEqual("/foo/bar/baz..zip".deletingPathExtension(), "/foo/bar/baz.") - XCTAssertEqual("...".deletingPathExtension(), "...") - XCTAssertEqual("...zip".deletingPathExtension(), "...zip") - XCTAssertEqual("/foo/bar/...zip".deletingPathExtension(), "/foo/bar/...zip") - XCTAssertEqual("/foo/bar/baz...zip".deletingPathExtension(), "/foo/bar/baz..") - XCTAssertEqual("/foo.bar/bar.baz/baz.zip".deletingPathExtension(), "/foo.bar/bar.baz/baz") - XCTAssertEqual("/.././.././a.zip".deletingPathExtension(), "/.././.././a") - XCTAssertEqual("/.././.././.".deletingPathExtension(), "/.././.././.") - - XCTAssertEqual("path.foo".deletingPathExtension(), "path") - XCTAssertEqual("path.foo.zip".deletingPathExtension(), "path.foo") - XCTAssertEqual("/path.foo".deletingPathExtension(), "/path") - XCTAssertEqual("/path.foo.zip".deletingPathExtension(), "/path.foo") - XCTAssertEqual("path.foo/".deletingPathExtension(), "path/") - XCTAssertEqual("path.foo//".deletingPathExtension(), "path/") - XCTAssertEqual("/path.foo/".deletingPathExtension(), "/path/") - XCTAssertEqual("/path.foo//".deletingPathExtension(), "/path/") - } - - func testPathComponents() { + #expect(stringNoExtension.appendingPathExtension(invalidExtension) == stringNoExtension) + } + } + + @Test func testAppendingPathExtension() { + #expect("".appendingPathExtension("foo") == ".foo") + #expect("/".appendingPathExtension("foo") == "/.foo") + #expect("//".appendingPathExtension("foo") == "/.foo/") + #expect("/path".appendingPathExtension("foo") == "/path.foo") + #expect("/path.zip".appendingPathExtension("foo") == "/path.zip.foo") + #expect("/path/".appendingPathExtension("foo") == "/path.foo/") + #expect("/path//".appendingPathExtension("foo") == "/path.foo/") + #expect("path".appendingPathExtension("foo") == "path.foo") + #expect("path/".appendingPathExtension("foo") == "path.foo/") + #expect("path//".appendingPathExtension("foo") == "path.foo/") + } + + @Test func testDeletingPathExtenstion() { + #expect("".deletingPathExtension() == "") + #expect("/".deletingPathExtension() == "/") + #expect("/foo/bar".deletingPathExtension() == "/foo/bar") + #expect("/foo/bar.zip".deletingPathExtension() == "/foo/bar") + #expect("/foo/bar.baz.zip".deletingPathExtension() == "/foo/bar.baz") + #expect(".".deletingPathExtension() == ".") + #expect(".zip".deletingPathExtension() == ".zip") + #expect("zip.".deletingPathExtension() == "zip.") + #expect(".zip.".deletingPathExtension() == ".zip.") + #expect("/foo/bar/.zip".deletingPathExtension() == "/foo/bar/.zip") + #expect("..".deletingPathExtension() == "..") + #expect("..zip".deletingPathExtension() == "..zip") + #expect("/foo/bar/..zip".deletingPathExtension() == "/foo/bar/..zip") + #expect("/foo/bar/baz..zip".deletingPathExtension() == "/foo/bar/baz.") + #expect("...".deletingPathExtension() == "...") + #expect("...zip".deletingPathExtension() == "...zip") + #expect("/foo/bar/...zip".deletingPathExtension() == "/foo/bar/...zip") + #expect("/foo/bar/baz...zip".deletingPathExtension() == "/foo/bar/baz..") + #expect("/foo.bar/bar.baz/baz.zip".deletingPathExtension() == "/foo.bar/bar.baz/baz") + #expect("/.././.././a.zip".deletingPathExtension() == "/.././.././a") + #expect("/.././.././.".deletingPathExtension() == "/.././.././.") + + #expect("path.foo".deletingPathExtension() == "path") + #expect("path.foo.zip".deletingPathExtension() == "path.foo") + #expect("/path.foo".deletingPathExtension() == "/path") + #expect("/path.foo.zip".deletingPathExtension() == "/path.foo") + #expect("path.foo/".deletingPathExtension() == "path/") + #expect("path.foo//".deletingPathExtension() == "path/") + #expect("/path.foo/".deletingPathExtension() == "/path/") + #expect("/path.foo//".deletingPathExtension() == "/path/") + } + + @Test func testPathComponents() { let tests: [(String, [String])] = [ ("", []), ("/", ["/"]), @@ -883,11 +890,11 @@ final class StringTests : XCTestCase { ] for (input, expected) in tests { let result = input.pathComponents - XCTAssertEqual(result, expected) + #expect(result == expected) } } - func test_dataUsingEncoding() { + @Test func dataUsingEncoding() { let s = "hello 🧮" // Verify things work on substrings too @@ -898,27 +905,27 @@ 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 @@ -934,12 +941,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") } @@ -947,73 +954,73 @@ 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) // UTF-8 With BOM let utf8BOM = Data([0xEF, 0xBB, 0xBF]) let helloWorld = Data("Hello, world".utf8) - XCTAssertEqual(String(bytes: utf8BOM + helloWorld, encoding: String._Encoding.utf8), "Hello, world") - XCTAssertEqual(String(bytes: helloWorld + utf8BOM, encoding: String._Encoding.utf8), "Hello, world\u{FEFF}") + #expect(String(bytes: utf8BOM + helloWorld, encoding: String._Encoding.utf8) == "Hello, world") + #expect(String(bytes: helloWorld + utf8BOM, encoding: String._Encoding.utf8) == "Hello, world\u{FEFF}") } - func test_dataUsingEncoding_preservingBOM() { + @Test func dataUsingEncoding_preservingBOM() { func roundTrip(_ data: Data) -> Bool { let str = String(data: data, encoding: .utf8)! let strAsUTF16BE = str.data(using: String._Encoding.utf16BigEndian)! @@ -1024,32 +1031,32 @@ final class StringTests : XCTestCase { // Verify that the BOM is preserved through a UTF8/16 transformation. // ASCII '2' followed by UTF8 BOM - XCTAssertTrue(roundTrip(Data([ 0x32, 0xef, 0xbb, 0xbf ]))) + #expect(roundTrip(Data([ 0x32, 0xef, 0xbb, 0xbf ]))) // UTF8 BOM followed by ASCII '4' - XCTAssertTrue(roundTrip(Data([ 0xef, 0xbb, 0xbf, 0x34 ]))) + #expect(roundTrip(Data([ 0xef, 0xbb, 0xbf, 0x34 ]))) } - func test_dataUsingEncoding_ascii() { - XCTAssertEqual("abc".data(using: .ascii), Data([UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c")])) - XCTAssertEqual("abc".data(using: .nonLossyASCII), Data([UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c")])) - XCTAssertEqual("e\u{301}\u{301}f".data(using: String._Encoding.ascii), nil) - XCTAssertEqual("e\u{301}\u{301}f".data(using: String._Encoding.nonLossyASCII), nil) + @Test func dataUsingEncoding_ascii() { + #expect("abc".data(using: .ascii) == Data([UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c")])) + #expect("abc".data(using: .nonLossyASCII) == Data([UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c")])) + #expect("e\u{301}\u{301}f".data(using: String._Encoding.ascii) == nil) + #expect("e\u{301}\u{301}f".data(using: String._Encoding.nonLossyASCII) == nil) - XCTAssertEqual("abc".data(using: .ascii, allowLossyConversion: true), Data([UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c")])) - XCTAssertEqual("abc".data(using: .nonLossyASCII, allowLossyConversion: true), Data([UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c")])) - XCTAssertEqual("e\u{301}\u{301}f".data(using: .ascii, allowLossyConversion: true), Data([UInt8(ascii: "e"), 0xFF, 0xFF, UInt8(ascii: "f")])) - XCTAssertEqual("e\u{301}\u{301}f".data(using: .nonLossyASCII, allowLossyConversion: true), Data([UInt8(ascii: "e"), UInt8(ascii: "?"), UInt8(ascii: "?"), UInt8(ascii: "f")])) + #expect("abc".data(using: .ascii, allowLossyConversion: true) == Data([UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c")])) + #expect("abc".data(using: .nonLossyASCII, allowLossyConversion: true) == Data([UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c")])) + #expect("e\u{301}\u{301}f".data(using: .ascii, allowLossyConversion: true) == Data([UInt8(ascii: "e"), 0xFF, 0xFF, UInt8(ascii: "f")])) + #expect("e\u{301}\u{301}f".data(using: .nonLossyASCII, allowLossyConversion: true) == Data([UInt8(ascii: "e"), UInt8(ascii: "?"), UInt8(ascii: "?"), UInt8(ascii: "f")])) } - func test_initWithBytes_ascii() { - XCTAssertEqual(String(bytes: "abc".utf8, encoding: String._Encoding.ascii), "abc") - XCTAssertEqual(String(bytes: "abc".utf8, encoding: String._Encoding.nonLossyASCII), "abc") - XCTAssertEqual(String(bytes: "e\u{301}\u{301}f".utf8, encoding: String._Encoding.ascii), nil) - XCTAssertEqual(String(bytes: "e\u{301}\u{301}f".utf8, encoding: String._Encoding.nonLossyASCII), nil) + @Test func initWithBytes_ascii() { + #expect(String(bytes: "abc".utf8, encoding: String._Encoding.ascii) == "abc") + #expect(String(bytes: "abc".utf8, encoding: String._Encoding.nonLossyASCII) == "abc") + #expect(String(bytes: "e\u{301}\u{301}f".utf8, encoding: String._Encoding.ascii) == nil) + #expect(String(bytes: "e\u{301}\u{301}f".utf8, encoding: String._Encoding.nonLossyASCII) == nil) } - func test_compressingSlashes() { + @Test func compressingSlashes() { let testCases: [(String, String)] = [ ("", ""), // Empty string ("/", "/"), // Single slash @@ -1067,11 +1074,11 @@ final class StringTests : XCTestCase { for (testString, expectedResult) in testCases { let result = testString ._compressingSlashes() - XCTAssertEqual(result, expectedResult) + #expect(result == expectedResult) } } - func test_pathHasDotDotComponent() { + @Test func pathHasDotDotComponent() { let testCases: [(String, Bool)] = [ ("../AB", true), // Begins with .. ("/ABC/..", true), // Ends with .. @@ -1094,61 +1101,45 @@ final class StringTests : XCTestCase { for (testString, expectedResult) in testCases { let result = testString ._hasDotDotComponent() - XCTAssertEqual(result, expectedResult) + #expect(result == expectedResult) } } - func test_init_contentsOfFile_encoding() { - withTemporaryStringFile { existingURL, nonExistentURL in - do { - let content = try String(contentsOfFile: existingURL.path, encoding: String._Encoding.ascii) - expectEqual(temporaryFileContents, content) - } catch { - XCTFail(error.localizedDescription) - } + @Test func init_contentsOfFile_encoding() throws { + try withTemporaryStringFile { existingURL, nonExistentURL in + let content = try String(contentsOfFile: existingURL.path, encoding: String._Encoding.ascii) + #expect(temporaryFileContents == content) - do { - let _ = try String(contentsOfFile: nonExistentURL.path, encoding: String._Encoding.ascii) - XCTFail() - } catch { + #expect(throws: (any Error).self) { + _ = try String(contentsOfFile: nonExistentURL.path, encoding: String._Encoding.ascii) } } } - 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(temporaryFileContents, content) - } catch { - XCTFail(error.localizedDescription) - } + @Test func init_contentsOfFile_usedEncoding() throws { + try withTemporaryStringFile { existingURL, nonExistentURL in + var usedEncoding: String._Encoding = String._Encoding(rawValue: 0) + let content = try String(contentsOfFile: existingURL.path(), usedEncoding: &usedEncoding) + #expect(0 != usedEncoding.rawValue) + #expect(temporaryFileContents == content) } } - func test_init_contentsOf_encoding() { - withTemporaryStringFile { existingURL, nonExistentURL in - do { - let content = try String(contentsOf: existingURL, encoding: String._Encoding.ascii) - expectEqual(temporaryFileContents, content) - } catch { - XCTFail(error.localizedDescription) - } + @Test func init_contentsOf_encoding() throws { + try withTemporaryStringFile { existingURL, nonExistentURL in + let content = try String(contentsOf: existingURL, encoding: String._Encoding.ascii) + #expect(temporaryFileContents == content) - do { + #expect(throws: (any Error).self) { _ = try String(contentsOf: nonExistentURL, encoding: String._Encoding.ascii) - XCTFail() - } catch { } } } - func test_init_contentsOf_usedEncoding() { + @Test func init_contentsOf_usedEncoding() throws { #if FOUNDATION_FRAMEWORK let encs : [String._Encoding] = [ .ascii, @@ -1197,34 +1188,28 @@ final class StringTests : XCTestCase { #endif for encoding in encs { - withTemporaryStringFile(encoding: encoding) { existingURL, _ in - do { - var usedEncoding = String._Encoding(rawValue: 0) - let content = try String(contentsOf: existingURL, usedEncoding: &usedEncoding) - - expectEqual(encoding, usedEncoding) - expectEqual(temporaryFileContents, content) - } catch { - XCTFail("\(error) - encoding \(encoding)") - } + try withTemporaryStringFile(encoding: encoding) { existingURL, _ in + var usedEncoding = String._Encoding(rawValue: 0) + let content = try String(contentsOf: existingURL, usedEncoding: &usedEncoding) + + #expect(encoding == usedEncoding) + #expect(temporaryFileContents == content) } } // Test non-existent file - withTemporaryStringFile { _, nonExistentURL in + try withTemporaryStringFile { _, nonExistentURL in var usedEncoding: String._Encoding = String._Encoding(rawValue: 0) - do { + #expect(throws: (any Error).self) { _ = try String(contentsOf: nonExistentURL, usedEncoding: &usedEncoding) - XCTFail() - } catch { - expectEqual(0, usedEncoding.rawValue) } + #expect(0 == usedEncoding.rawValue) } } - func test_extendedAttributeData() { - // XAttr is supported on some platforms, but not all. For now we just test this code on Darwin. #if FOUNDATION_FRAMEWORK + @Test func extendedAttributeEncodings() throws { + // XAttr is supported on some platforms, but not all. For now we just test this code on Darwin. let encs : [String._Encoding] = [ .ascii, .nextstep, @@ -1252,84 +1237,74 @@ final class StringTests : XCTestCase { for encoding in encs { // Round trip the - let packageData = extendedAttributeData(for: encoding) - XCTAssertNotNil(packageData) + let packageData = try #require(extendedAttributeData(for: encoding)) - let back = encodingFromDataForExtendedAttribute(packageData!)! - XCTAssertEqual(back, encoding) + let back = encodingFromDataForExtendedAttribute(packageData) + #expect(back == encoding) } - XCTAssertEqual(encodingFromDataForExtendedAttribute("us-ascii;1536".data(using: .utf8)!)!.rawValue, String._Encoding.ascii.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("x-nextstep;2817".data(using: .utf8)!)!.rawValue, String._Encoding.nextstep.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("euc-jp;2336".data(using: .utf8)!)!.rawValue, String._Encoding.japaneseEUC.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("utf-8;134217984".data(using: .utf8)!)!.rawValue, String._Encoding.utf8.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("iso-8859-1;513".data(using: .utf8)!)!.rawValue, String._Encoding.isoLatin1.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute(";3071".data(using: .utf8)!)!.rawValue, String._Encoding.nonLossyASCII.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("cp932;1056".data(using: .utf8)!)!.rawValue, String._Encoding.shiftJIS.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("iso-8859-2;514".data(using: .utf8)!)!.rawValue, String._Encoding.isoLatin2.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("utf-16;256".data(using: .utf8)!)!.rawValue, String._Encoding.unicode.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("windows-1251;1282".data(using: .utf8)!)!.rawValue, String._Encoding.windowsCP1251.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("windows-1252;1280".data(using: .utf8)!)!.rawValue, String._Encoding.windowsCP1252.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("windows-1253;1283".data(using: .utf8)!)!.rawValue, String._Encoding.windowsCP1253.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("windows-1254;1284".data(using: .utf8)!)!.rawValue, String._Encoding.windowsCP1254.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("windows-1250;1281".data(using: .utf8)!)!.rawValue, String._Encoding.windowsCP1250.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("iso-2022-jp;2080".data(using: .utf8)!)!.rawValue, String._Encoding.iso2022JP.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("macintosh;0".data(using: .utf8)!)!.rawValue, String._Encoding.macOSRoman.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("utf-16;256".data(using: .utf8)!)!.rawValue, String._Encoding.utf16.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("utf-16be;268435712".data(using: .utf8)!)!.rawValue, String._Encoding.utf16BigEndian.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("utf-16le;335544576".data(using: .utf8)!)!.rawValue, String._Encoding.utf16LittleEndian.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("utf-32;201326848".data(using: .utf8)!)!.rawValue, String._Encoding.utf32.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("utf-32be;402653440".data(using: .utf8)!)!.rawValue, String._Encoding.utf32BigEndian.rawValue) - XCTAssertEqual(encodingFromDataForExtendedAttribute("utf-32le;469762304".data(using: .utf8)!)!.rawValue, String._Encoding.utf32LittleEndian.rawValue) -#endif + #expect(encodingFromDataForExtendedAttribute("us-ascii;1536".data(using: .utf8)!)!.rawValue == String._Encoding.ascii.rawValue) + #expect(encodingFromDataForExtendedAttribute("x-nextstep;2817".data(using: .utf8)!)!.rawValue == String._Encoding.nextstep.rawValue) + #expect(encodingFromDataForExtendedAttribute("euc-jp;2336".data(using: .utf8)!)!.rawValue == String._Encoding.japaneseEUC.rawValue) + #expect(encodingFromDataForExtendedAttribute("utf-8;134217984".data(using: .utf8)!)!.rawValue == String._Encoding.utf8.rawValue) + #expect(encodingFromDataForExtendedAttribute("iso-8859-1;513".data(using: .utf8)!)!.rawValue == String._Encoding.isoLatin1.rawValue) + #expect(encodingFromDataForExtendedAttribute(";3071".data(using: .utf8)!)!.rawValue == String._Encoding.nonLossyASCII.rawValue) + #expect(encodingFromDataForExtendedAttribute("cp932;1056".data(using: .utf8)!)!.rawValue == String._Encoding.shiftJIS.rawValue) + #expect(encodingFromDataForExtendedAttribute("iso-8859-2;514".data(using: .utf8)!)!.rawValue == String._Encoding.isoLatin2.rawValue) + #expect(encodingFromDataForExtendedAttribute("utf-16;256".data(using: .utf8)!)!.rawValue == String._Encoding.unicode.rawValue) + #expect(encodingFromDataForExtendedAttribute("windows-1251;1282".data(using: .utf8)!)!.rawValue == String._Encoding.windowsCP1251.rawValue) + #expect(encodingFromDataForExtendedAttribute("windows-1252;1280".data(using: .utf8)!)!.rawValue == String._Encoding.windowsCP1252.rawValue) + #expect(encodingFromDataForExtendedAttribute("windows-1253;1283".data(using: .utf8)!)!.rawValue == String._Encoding.windowsCP1253.rawValue) + #expect(encodingFromDataForExtendedAttribute("windows-1254;1284".data(using: .utf8)!)!.rawValue == String._Encoding.windowsCP1254.rawValue) + #expect(encodingFromDataForExtendedAttribute("windows-1250;1281".data(using: .utf8)!)!.rawValue == String._Encoding.windowsCP1250.rawValue) + #expect(encodingFromDataForExtendedAttribute("iso-2022-jp;2080".data(using: .utf8)!)!.rawValue == String._Encoding.iso2022JP.rawValue) + #expect(encodingFromDataForExtendedAttribute("macintosh;0".data(using: .utf8)!)!.rawValue == String._Encoding.macOSRoman.rawValue) + #expect(encodingFromDataForExtendedAttribute("utf-16;256".data(using: .utf8)!)!.rawValue == String._Encoding.utf16.rawValue) + #expect(encodingFromDataForExtendedAttribute("utf-16be;268435712".data(using: .utf8)!)!.rawValue == String._Encoding.utf16BigEndian.rawValue) + #expect(encodingFromDataForExtendedAttribute("utf-16le;335544576".data(using: .utf8)!)!.rawValue == String._Encoding.utf16LittleEndian.rawValue) + #expect(encodingFromDataForExtendedAttribute("utf-32;201326848".data(using: .utf8)!)!.rawValue == String._Encoding.utf32.rawValue) + #expect(encodingFromDataForExtendedAttribute("utf-32be;402653440".data(using: .utf8)!)!.rawValue == String._Encoding.utf32BigEndian.rawValue) + #expect(encodingFromDataForExtendedAttribute("utf-32le;469762304".data(using: .utf8)!)!.rawValue == String._Encoding.utf32LittleEndian.rawValue) } +#endif - func test_write_toFile() { - withTemporaryStringFile { existingURL, nonExistentURL in + @Test func write_toFile() throws { + try withTemporaryStringFile { existingURL, nonExistentURL in let nonExistentPath = nonExistentURL.path() - do { - let s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit" - try s.write(toFile: nonExistentPath, atomically: false, encoding: String._Encoding.ascii) + let s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit" + try s.write(toFile: nonExistentPath, atomically: false, encoding: String._Encoding.ascii) - let content = try String(contentsOfFile: nonExistentPath, encoding: String._Encoding.ascii) + let content = try String(contentsOfFile: nonExistentPath, encoding: String._Encoding.ascii) - expectEqual(s, content) - } catch { - - XCTFail(error.localizedDescription) - } + #expect(s == content) } } - func test_write_to() { - withTemporaryStringFile { existingURL, nonExistentURL in + @Test func write_to() throws { + try withTemporaryStringFile { existingURL, nonExistentURL in let nonExistentPath = nonExistentURL.path() - do { - let s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit" - try s.write(to: nonExistentURL, atomically: false, encoding: String._Encoding.ascii) - - let content = try String(contentsOfFile: nonExistentPath, encoding: String._Encoding.ascii) - - expectEqual(s, content) - } catch { - XCTFail(error.localizedDescription) - } + let s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit" + try s.write(to: nonExistentURL, atomically: false, encoding: String._Encoding.ascii) + + let content = try String(contentsOfFile: nonExistentPath, encoding: String._Encoding.ascii) + + #expect(s == content) } } - func verifyEncoding(_ encoding: String._Encoding, valid: [String], invalid: [String], file: StaticString = #filePath, line: UInt = #line) throws { + func verifyEncoding(_ encoding: String._Encoding, valid: [String], invalid: [String], sourceLocation: SourceLocation = #_sourceLocation) throws { for string in valid { - let data = try XCTUnwrap(string.data(using: encoding), "Failed to encode \(string.debugDescription)", file: file, line: line) - XCTAssertNotNil(String(data: data, encoding: encoding), "Failed to decode \(data) (\(string.debugDescription))", file: file, line: line) + let data = try #require(string.data(using: encoding), "Failed to encode \(string.debugDescription)", sourceLocation: sourceLocation) + #expect(String(data: data, encoding: encoding) != nil, "Failed to decode \(data) (\(string.debugDescription))", sourceLocation: sourceLocation) } for string in invalid { - XCTAssertNil(string.data(using: String._Encoding.macOSRoman), "Incorrectly successfully encoded \(string.debugDescription)", file: file, line: line) + #expect(string.data(using: String._Encoding.macOSRoman) == nil, "Incorrectly successfully encoded \(string.debugDescription)", sourceLocation: sourceLocation) } } - func testISOLatin1Encoding() throws { + @Test func testISOLatin1Encoding() throws { try verifyEncoding(.isoLatin1, valid: [ "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", @@ -1346,7 +1321,7 @@ final class StringTests : XCTestCase { ]) } - func testMacRomanEncoding() throws { + @Test func testMacRomanEncoding() throws { try verifyEncoding(.macOSRoman, valid: [ "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", @@ -1366,30 +1341,24 @@ final class StringTests : XCTestCase { let temporaryFileContents = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." -func withTemporaryStringFile(encoding: String._Encoding = .utf8, _ block: (_ existingURL: URL, _ nonExistentURL: URL) -> ()) { +func withTemporaryStringFile(encoding: String._Encoding = .utf8, _ block: (_ existingURL: URL, _ nonExistentURL: URL) throws -> ()) throws { let rootURL = URL.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true) let fileURL = rootURL.appending(path: "NSStringTest.txt", directoryHint: .notDirectory) - try! FileManager.default.createDirectory(at: rootURL, withIntermediateDirectories: true) - defer { - do { - try FileManager.default.removeItem(at: rootURL) - } catch { - XCTFail() - } - } + try FileManager.default.createDirectory(at: rootURL, withIntermediateDirectories: true) - try! temporaryFileContents.write(to: fileURL, atomically: true, encoding: encoding) + try temporaryFileContents.write(to: fileURL, atomically: true, encoding: encoding) let nonExisting = rootURL.appending(path: "-NonExist", directoryHint: .notDirectory) - block(fileURL, nonExisting) + try block(fileURL, nonExisting) + try FileManager.default.removeItem(at: rootURL) } // MARK: - #if FOUNDATION_FRAMEWORK -final class StringTestsStdlib: XCTestCase { +struct StringTestsStdlib { // The most simple subclass of NSString that CoreFoundation does not know // about. @@ -1434,17 +1403,17 @@ final class StringTestsStdlib: XCTestCase { var _value: [UInt16] } - func test_Encodings() { + @Test func 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 NSStringEncoding() { // Make sure NSStringEncoding and its values are type-compatible. var enc: String.Encoding enc = .windowsCP1250 @@ -1452,10 +1421,10 @@ final class StringTestsStdlib: XCTestCase { enc = .utf32BigEndian enc = .ascii enc = .utf8 - expectEqual(.utf8, enc) + #expect(.utf8 == enc) } - func test_NSStringEncoding_Hashable() { + @Test func NSStringEncoding_Hashable() { let instances: [String.Encoding] = [ .windowsCP1250, .utf32LittleEndian, @@ -1463,26 +1432,26 @@ final class StringTestsStdlib: XCTestCase { .ascii, .utf8, ] - XCTCheckHashable(instances, equalityOracle: { $0 == $1 }) + checkHashable(instances, equalityOracle: { $0 == $1 }) } - func test_localizedStringWithFormat() { + @Test func 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_cString_encoding() { + @Test func init_cString_encoding() { "foo, a basmati bar!".withCString { - expectEqual("foo, a basmati bar!", + #expect("foo, a basmati bar!" == String(cString: $0, encoding: String.defaultCStringEncoding)) } } - func test_init_utf8String() { + @Test func init_utf8String() { let s = "foo あいう" let up = UnsafeMutablePointer.allocate(capacity: 100) var i = 0 @@ -1493,25 +1462,25 @@ 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 canBeConvertedToEncoding() { + #expect("foo".canBeConverted(to: .ascii)) + #expect(!"あいう".canBeConverted(to: .ascii)) } - func test_capitalized() { - expectEqual("Foo Foo Foo Foo", "foo Foo fOO FOO".capitalized) - expectEqual("Жжж", "жжж".capitalized) + @Test func capitalized() { + #expect("Foo Foo Foo Foo" == "foo Foo fOO FOO".capitalized) + #expect("Жжж" == "жжж".capitalized) } - func test_localizedCapitalized() { - expectEqual( - "Foo Foo Foo Foo", + @Test func localizedCapitalized() { + #expect( + "Foo Foo Foo Foo" == "foo Foo fOO FOO".capitalized(with: Locale(identifier: "en"))) - expectEqual("Жжж", "жжж".capitalized(with: Locale(identifier: "en"))) + #expect("Жжж" == "жжж".capitalized(with: Locale(identifier: "en"))) // // Special casing. @@ -1520,12 +1489,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 @@ -1540,31 +1509,29 @@ final class StringTestsStdlib: XCTestCase { _ expected: String, _ op: (_: Locale?) -> String, _ localeID: String? = nil, - _ message: @autoclosure () -> String = "", + _ message: @autoclosure () -> Comment? = nil, showFrame: Bool = true, - file: String = #file, line: UInt = #line + sourceLocation: SourceLocation = #_sourceLocation ) { let locale = localeID.map { Locale(identifier: $0) } ?? nil - expectEqual( - expected, op(locale), - message()) + #expect(expected == op(locale), message(), sourceLocation: sourceLocation) } - func test_capitalizedString() { + @Test func 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", + #expect( + "Foo Foo Foo Foo" == "foo Foo fOO FOO".capitalized(with: nil)) - expectEqual("Жжж", "жжж".capitalized(with: nil)) + #expect("Жжж" == "жжж".capitalized(with: nil)) // // Special casing. @@ -1585,67 +1552,67 @@ final class StringTestsStdlib: XCTestCase { { loc in "iii III".capitalized(with: loc) }, "tr") } - func test_caseInsensitiveCompare() { - expectEqual(ComparisonResult.orderedSame, + @Test func caseInsensitiveCompare() { + #expect(ComparisonResult.orderedSame == "abCD".caseInsensitiveCompare("AbCd")) - expectEqual(ComparisonResult.orderedAscending, + #expect(ComparisonResult.orderedAscending == "abCD".caseInsensitiveCompare("AbCdE")) - expectEqual(ComparisonResult.orderedSame, + #expect(ComparisonResult.orderedSame == "абвг".caseInsensitiveCompare("АбВг")) - expectEqual(ComparisonResult.orderedAscending, + #expect(ComparisonResult.orderedAscending == "абВГ".caseInsensitiveCompare("АбВгД")) } - func test_commonPrefix() { - expectEqual("ab", + @Test func commonPrefix() { + #expect("ab" == "abcd".commonPrefix(with: "abdc", options: [])) - expectEqual("abC", + #expect("abC" == "abCd".commonPrefix(with: "abce", options: .caseInsensitive)) - expectEqual("аб", + #expect("аб" == "абвг".commonPrefix(with: "абгв", options: [])) - expectEqual("абВ", + #expect("абВ" == "абВг".commonPrefix(with: "абвд", options: .caseInsensitive)) } - func test_compare() { - expectEqual(ComparisonResult.orderedSame, + @Test func compare() { + #expect(ComparisonResult.orderedSame == "abc".compare("abc")) - expectEqual(ComparisonResult.orderedAscending, + #expect(ComparisonResult.orderedAscending == "абв".compare("где")) - expectEqual(ComparisonResult.orderedSame, + #expect(ComparisonResult.orderedSame == "abc".compare("abC", options: .caseInsensitive)) - expectEqual(ComparisonResult.orderedSame, + #expect(ComparisonResult.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] = [] @@ -1855,21 +1820,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]) + #expect(substring == s[enclosingRange]) } - expectEqual(["\u{304b}\u{3099}", "お", "☺️", "😀"], substrings) + #expect(["\u{304b}\u{3099}", "お", "☺️", "😀"] == substrings) } } - func test_fastestEncoding() { + @Test func fastestEncoding() { let availableEncodings: [String.Encoding] = String.availableStringEncodings - expectTrue(availableEncodings.contains("abc".fastestEncoding)) + #expect(availableEncodings.contains("abc".fastestEncoding)) } - func test_getBytes() { + @Test func getBytes() { let s = "abc абв def где gh жз zzz" let startIndex = s.index(s.startIndex, offsetBy: 8) let endIndex = s.index(s.startIndex, offsetBy: 22) @@ -1887,11 +1852,11 @@ final class StringTestsStdlib: XCTestCase { encoding: .utf8, options: [], range: startIndex.. NFKD normalization as implemented by 'precomposedStringWithCompatibilityMapping:' is not idempotent - expectEqual("\u{30c0}クテン", + #expect("\u{30c0}クテン" == "\u{ff80}\u{ff9e}クテン".precomposedStringWithCompatibilityMapping) */ - expectEqual("ffi", "\u{fb03}".precomposedStringWithCompatibilityMapping) + #expect("ffi" == "\u{fb03}".precomposedStringWithCompatibilityMapping) } - func test_propertyList() { - expectEqual(["foo", "bar"], + @Test func propertyList() { + #expect(["foo", "bar"] == "(\"foo\", \"bar\")".propertyList() as! [String]) } - func test_propertyListFromStringsFileFormat() { - expectEqual(["foo": "bar", "baz": "baz"], + @Test func propertyListFromStringsFileFormat() { + #expect(["foo": "bar", "baz": "baz"] == "/* comment */\n\"foo\" = \"bar\";\n\"baz\";" .propertyListFromStringsFileFormat() as Dictionary) } - func test_rangeOfCharacterFrom() { + @Test func 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).. Range? { return toIntRange( string, string.localizedStandardRange(of: substring, locale: locale)) @@ -2544,70 +2505,70 @@ 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)!]) + let s = "a\u{0301}" + #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 smallestEncoding() { let availableEncodings: [String.Encoding] = String.availableStringEncodings - expectTrue(availableEncodings.contains("abc".smallestEncoding)) + #expect(availableEncodings.contains("abc".smallestEncoding)) } - func test_addingPercentEncoding() { - expectEqual( - "abcd1234", + @Test func addingPercentEncoding() { + #expect( + "abcd1234" == "abcd1234".addingPercentEncoding(withAllowedCharacters: .alphanumerics)) - expectEqual( - "abcd%20%D0%B0%D0%B1%D0%B2%D0%B3", + #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}", + @Test func 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)) } - func test_appending() { - expectEqual("", "".appending("")) - expectEqual("a", "a".appending("")) - expectEqual("a", "".appending("a")) - expectEqual("さ\u{3099}", "さ".appending("\u{3099}")) + @Test func appending() { + #expect("" == "".appending("")) + #expect("a" == "a".appending("")) + #expect("a" == "".appending("a")) + #expect("さ\u{3099}" == "さ".appending("\u{3099}")) } - func test_folding() { + @Test func folding() { func fwo( _ s: String, _ options: String.CompareOptions @@ -2634,113 +2595,113 @@ final class StringTestsStdlib: XCTestCase { "example123", fwo("example123", .widthInsensitive), "en") } - func test_padding() { - expectEqual( - "abc абв \u{0001F60A}", + @Test func padding() { + #expect( + "abc абв \u{0001F60A}" == "abc абв \u{0001F60A}".padding( toLength: 10, withPad: "XYZ", startingAt: 0)) - expectEqual( - "abc абв \u{0001F60A}XYZXY", + #expect( + "abc абв \u{0001F60A}XYZXY" == "abc абв \u{0001F60A}".padding( toLength: 15, withPad: "XYZ", startingAt: 0)) - expectEqual( - "abc абв \u{0001F60A}YZXYZ", + #expect( + "abc абв \u{0001F60A}YZXYZ" == "abc абв \u{0001F60A}".padding( toLength: 15, withPad: "XYZ", startingAt: 1)) } - func test_replacingCharacters() { + @Test func replacingCharacters() { do { let empty = "" - expectEqual("", empty.replacingCharacters( + #expect("" == empty.replacingCharacters( in: empty.startIndex..