diff --git a/Package.swift b/Package.swift index 216810019bc..976f8d43788 100644 --- a/Package.swift +++ b/Package.swift @@ -788,7 +788,12 @@ let package = Package( .testTarget( name: "BasicsTests", - dependencies: ["Basics", "_InternalTestSupport", "tsan_utils"], + dependencies: [ + "Basics", + "_InternalTestSupport", + "tsan_utils", + .product(name: "Numerics", package: "swift-numerics"), + ], exclude: [ "Archiver/Inputs/archive.tar.gz", "Archiver/Inputs/archive.zip", @@ -1028,6 +1033,8 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { .package(url: "https://github.com/apple/swift-collections.git", "1.0.1" ..< "1.2.0"), .package(url: "https://github.com/apple/swift-certificates.git", "1.0.1" ..< "1.6.0"), .package(url: "https://github.com/swiftlang/swift-toolchain-sqlite.git", from: "1.0.0"), + // Test Dependencies + .package(url: "https://github.com/apple/swift-numerics", exact: "1.0.2") ] } else { package.dependencies += [ @@ -1040,6 +1047,8 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { .package(path: "../swift-collections"), .package(path: "../swift-certificates"), .package(path: "../swift-toolchain-sqlite"), + // Test Dependencies + .package(path: "../swift-numerics"), ] } diff --git a/Sources/_InternalTestSupport/Observability.swift b/Sources/_InternalTestSupport/Observability.swift index 36c260ede79..3c53bed86c0 100644 --- a/Sources/_InternalTestSupport/Observability.swift +++ b/Sources/_InternalTestSupport/Observability.swift @@ -14,10 +14,10 @@ import Basics import func XCTest.XCTAssertTrue import func XCTest.XCTAssertEqual import func XCTest.XCTFail - import struct TSCBasic.StringError import TSCTestSupport +import Testing extension ObservabilitySystem { public static func makeForTesting(verbose: Bool = true) -> TestingObservability { @@ -139,6 +139,39 @@ public func testDiagnostics( } } + +public func expectDiagnostics( + _ diagnostics: [Basics.Diagnostic], + problemsOnly: Bool = true, + sourceLocation: SourceLocation = #_sourceLocation, + handler: (DiagnosticsTestResult) throws -> Void +) throws { + try expectDiagnostics( + diagnostics, + minSeverity: problemsOnly ? .warning : .debug, + sourceLocation: sourceLocation, + handler: handler + ) +} + + +public func expectDiagnostics( + _ diagnostics: [Basics.Diagnostic], + minSeverity: Basics.Diagnostic.Severity, + sourceLocation: SourceLocation = #_sourceLocation, + handler: (DiagnosticsTestResult) throws -> Void +) throws { + let diagnostics = diagnostics.filter { $0.severity >= minSeverity } + let testResult = DiagnosticsTestResult(diagnostics) + + try handler(testResult) + + if !testResult.uncheckedDiagnostics.isEmpty { + Issue.record("unchecked diagnostics \(testResult.uncheckedDiagnostics)", sourceLocation: sourceLocation) + } +} + + public func testPartialDiagnostics( _ diagnostics: [Basics.Diagnostic], minSeverity: Basics.Diagnostic.Severity, diff --git a/Sources/_InternalTestSupport/XCTAssertHelpers.swift b/Sources/_InternalTestSupport/XCTAssertHelpers.swift index c99154e2f48..27b4464786d 100644 --- a/Sources/_InternalTestSupport/XCTAssertHelpers.swift +++ b/Sources/_InternalTestSupport/XCTAssertHelpers.swift @@ -44,8 +44,12 @@ public func XCTAssertEqual (_ lhs:(T,U), _ rhs:(T,U), TSCTestSupport.XCTAssertEqual(lhs, rhs, file: file, line: line) } +public func isRunninginCI(file: StaticString = #filePath, line: UInt = #line) -> Bool { + return (ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] != nil || ProcessInfo.processInfo.environment["CI"] != nil) +} + public func XCTSkipIfCI(file: StaticString = #filePath, line: UInt = #line) throws { - if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] != nil { + if isRunninginCI() { throw XCTSkip("Skipping because the test is being run on CI", file: file, line: line) } } diff --git a/Tests/BasicsTests/Archiver/ZipArchiverTests.swift b/Tests/BasicsTests/Archiver/ZipArchiverTests.swift index 7b036813376..05c40d5b2f6 100644 --- a/Tests/BasicsTests/Archiver/ZipArchiverTests.swift +++ b/Tests/BasicsTests/Archiver/ZipArchiverTests.swift @@ -105,15 +105,15 @@ final class ZipArchiverTests: XCTestCase { #endif try await testWithTemporaryDirectory { tmpdir in - let archiver = ZipArchiver(fileSystem: localFileSystem) + let archiver = ZipArchiver(fileSystem: localFileSystem) - let rootDir = tmpdir.appending(component: UUID().uuidString) - try localFileSystem.createDirectory(rootDir) - try localFileSystem.writeFileContents(rootDir.appending("file1.txt"), string: "Hello World!") + let rootDir = tmpdir.appending(component: UUID().uuidString) + try localFileSystem.createDirectory(rootDir) + try localFileSystem.writeFileContents(rootDir.appending("file1.txt"), string: "Hello World!") - let dir1 = rootDir.appending("dir1") - try localFileSystem.createDirectory(dir1) - try localFileSystem.writeFileContents(dir1.appending("file2.txt"), string: "Hello World 2!") + let dir1 = rootDir.appending("dir1") + try localFileSystem.createDirectory(dir1) + try localFileSystem.writeFileContents(dir1.appending("file2.txt"), string: "Hello World 2!") let dir2 = dir1.appending("dir2") try localFileSystem.createDirectory(dir2) @@ -121,28 +121,28 @@ final class ZipArchiverTests: XCTestCase { try localFileSystem.writeFileContents(dir2.appending("file4.txt"), string: "Hello World 4!") try localFileSystem.createSymbolicLink(dir2.appending("file5.txt"), pointingAt: dir1.appending("file2.txt"), relative: true) - let archivePath = tmpdir.appending(component: UUID().uuidString + ".zip") - try await archiver.compress(directory: rootDir, to: archivePath) - XCTAssertFileExists(archivePath) - - let extractRootDir = tmpdir.appending(component: UUID().uuidString) - try localFileSystem.createDirectory(extractRootDir) - try await archiver.extract(from: archivePath, to: extractRootDir) - try localFileSystem.stripFirstLevel(of: extractRootDir) - - XCTAssertFileExists(extractRootDir.appending("file1.txt")) - XCTAssertEqual( - try? localFileSystem.readFileContents(extractRootDir.appending("file1.txt")), - "Hello World!" - ) - - let extractedDir1 = extractRootDir.appending("dir1") - XCTAssertDirectoryExists(extractedDir1) - XCTAssertFileExists(extractedDir1.appending("file2.txt")) - XCTAssertEqual( - try? localFileSystem.readFileContents(extractedDir1.appending("file2.txt")), - "Hello World 2!" - ) + let archivePath = tmpdir.appending(component: UUID().uuidString + ".zip") + try await archiver.compress(directory: rootDir, to: archivePath) + XCTAssertFileExists(archivePath) + + let extractRootDir = tmpdir.appending(component: UUID().uuidString) + try localFileSystem.createDirectory(extractRootDir) + try await archiver.extract(from: archivePath, to: extractRootDir) + try localFileSystem.stripFirstLevel(of: extractRootDir) + + XCTAssertFileExists(extractRootDir.appending("file1.txt")) + XCTAssertEqual( + try? localFileSystem.readFileContents(extractRootDir.appending("file1.txt")), + "Hello World!" + ) + + let extractedDir1 = extractRootDir.appending("dir1") + XCTAssertDirectoryExists(extractedDir1) + XCTAssertFileExists(extractedDir1.appending("file2.txt")) + XCTAssertEqual( + try? localFileSystem.readFileContents(extractedDir1.appending("file2.txt")), + "Hello World 2!" + ) let extractedDir2 = extractedDir1.appending("dir2") XCTAssertDirectoryExists(extractedDir2) diff --git a/Tests/BasicsTests/AsyncProcessTests.swift b/Tests/BasicsTests/AsyncProcessTests.swift index cae5f2cd604..b4ad012cbdd 100644 --- a/Tests/BasicsTests/AsyncProcessTests.swift +++ b/Tests/BasicsTests/AsyncProcessTests.swift @@ -7,11 +7,12 @@ See http://swift.org/LICENSE.txt for license information See http://swift.org/CONTRIBUTORS.txt for Swift project authors */ +import Foundation import _InternalTestSupport import _Concurrency import Basics -import XCTest +import Testing import TSCclibc // for SPM_posix_spawn_file_actions_addchdir_np_supported @@ -22,33 +23,39 @@ import class TSCBasic.Thread import func TSCBasic.withTemporaryFile import func TSCTestSupport.withCustomEnv -final class AsyncProcessTests: XCTestCase { - func testBasics() throws { +@Suite( + // because suite is very flaky otherwise. Need to investigate whether the tests can run in parallel + .serialized +) +struct AsyncProcessTests { + @Test + func basics() throws { do { let process = AsyncProcess(args: "echo", "hello") try process.launch() let result = try process.waitUntilExit() - XCTAssertEqual(try result.utf8Output(), "hello\n") - XCTAssertEqual(result.exitStatus, .terminated(code: 0)) - XCTAssertEqual(result.arguments, process.arguments) + #expect(try result.utf8Output() == "hello\n") + #expect(result.exitStatus == .terminated(code: 0)) + #expect(result.arguments == process.arguments) } do { let process = AsyncProcess(scriptName: "exit4") try process.launch() let result = try process.waitUntilExit() - XCTAssertEqual(result.exitStatus, .terminated(code: 4)) + #expect(result.exitStatus == .terminated(code: 4)) } } - func testPopen() throws { - #if os(Windows) + @Test + func popen() throws { +#if os(Windows) let echo = "echo.exe" - #else +#else let echo = "echo" - #endif +#endif // Test basic echo. - XCTAssertEqual(try AsyncProcess.popen(arguments: [echo, "hello"]).utf8Output(), "hello\n") + #expect(try AsyncProcess.popen(arguments: [echo, "hello"]).utf8Output() == "hello\n") // Test buffer larger than that allocated. try withTemporaryFile { file in @@ -56,24 +63,25 @@ final class AsyncProcessTests: XCTestCase { let stream = BufferedOutputByteStream() stream.send(Format.asRepeating(string: "a", count: count)) try localFileSystem.writeFileContents(file.path, bytes: stream.bytes) - #if os(Windows) +#if os(Windows) let cat = "cat.exe" - #else +#else let cat = "cat" - #endif +#endif let outputCount = try AsyncProcess.popen(args: cat, file.path.pathString).utf8Output().count - XCTAssert(outputCount == count) + #expect(outputCount == count) } } - func testPopenLegacyAsync() throws { - #if os(Windows) + @Test + func popenLegacyAsync() throws { +#if os(Windows) let args = ["where.exe", "where"] let answer = "C:\\Windows\\System32\\where.exe" - #else +#else let args = ["whoami"] let answer = NSUserName() - #endif +#endif var popenResult: Result? let group = DispatchGroup() group.enter() @@ -83,72 +91,75 @@ final class AsyncProcessTests: XCTestCase { } group.wait() switch popenResult { - case .success(let processResult): - let output = try processResult.utf8Output() - XCTAssertTrue(output.hasPrefix(answer)) - case .failure(let error): - XCTFail("error = \(error)") - case nil: - XCTFail() + case .success(let processResult): + let output = try processResult.utf8Output() + #expect(output.hasPrefix(answer)) + case .failure(let error): + throw error + case nil: + Issue.record("AsyncProcess.popen did not yield a result!") } } + @Test @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) - func testPopenAsync() async throws { - #if os(Windows) + func popenAsync() async throws { +#if os(Windows) let args = ["where.exe", "where"] let answer = "C:\\Windows\\System32\\where.exe" - #else +#else let args = ["whoami"] let answer = NSUserName() - #endif +#endif let processResult: AsyncProcessResult do { processResult = try await AsyncProcess.popen(arguments: args) } catch { - XCTFail("error = \(error)") - return + throw error } let output = try processResult.utf8Output() - XCTAssertTrue(output.hasPrefix(answer)) + #expect(output.hasPrefix(answer)) } - func testCheckNonZeroExit() throws { + @Test + func checkNonZeroExit() throws { do { let output = try AsyncProcess.checkNonZeroExit(args: "echo", "hello") - XCTAssertEqual(output, "hello\n") + #expect(output == "hello\n") } do { let output = try AsyncProcess.checkNonZeroExit(scriptName: "exit4") - XCTFail("Unexpected success \(output)") + Issue.record("Unexpected success \(output)") } catch AsyncProcessResult.Error.nonZeroExit(let result) { - XCTAssertEqual(result.exitStatus, .terminated(code: 4)) + #expect(result.exitStatus == .terminated(code: 4)) } } + @Test @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) - func testCheckNonZeroExitAsync() async throws { + func checkNonZeroExitAsync() async throws { do { let output = try await AsyncProcess.checkNonZeroExit(args: "echo", "hello") - XCTAssertEqual(output, "hello\n") + #expect(output == "hello\n") } do { let output = try await AsyncProcess.checkNonZeroExit(scriptName: "exit4") - XCTFail("Unexpected success \(output)") + Issue.record("Unexpected success \(output)") } catch AsyncProcessResult.Error.nonZeroExit(let result) { - XCTAssertEqual(result.exitStatus, .terminated(code: 4)) + #expect(result.exitStatus == .terminated(code: 4)) } } - func testFindExecutable() throws { + @Test + func findExecutable() throws { try testWithTemporaryDirectory { tmpdir in // This process should always work. - XCTAssertTrue(AsyncProcess.findExecutable("ls") != nil) + #expect(AsyncProcess.findExecutable("ls") != nil) - XCTAssertEqual(AsyncProcess.findExecutable("nonExistantProgram"), nil) - XCTAssertEqual(AsyncProcess.findExecutable(""), nil) + #expect(AsyncProcess.findExecutable("nonExistantProgram") == nil) + #expect(AsyncProcess.findExecutable("") == nil) // Create a local nonexecutable file to test. let tempExecutable = tmpdir.appending(component: "nonExecutableProgram") @@ -159,12 +170,13 @@ final class AsyncProcessTests: XCTestCase { """) try withCustomEnv(["PATH": tmpdir.pathString]) { - XCTAssertEqual(AsyncProcess.findExecutable("nonExecutableProgram"), nil) + #expect(AsyncProcess.findExecutable("nonExecutableProgram") == nil) } } } - func testNonExecutableLaunch() throws { + @Test + func nonExecutableLaunch() throws { try testWithTemporaryDirectory { tmpdir in // Create a local nonexecutable file to test. let tempExecutable = tmpdir.appending(component: "nonExecutableProgram") @@ -178,15 +190,16 @@ final class AsyncProcessTests: XCTestCase { do { let process = AsyncProcess(args: "nonExecutableProgram") try process.launch() - XCTFail("Should have failed to validate nonExecutableProgram") + Issue.record("Should have failed to validate nonExecutableProgram") } catch AsyncProcess.Error.missingExecutableProgram(let program) { - XCTAssert(program == "nonExecutableProgram") + #expect(program == "nonExecutableProgram") } } } } - func testThreadSafetyOnWaitUntilExit() throws { + @Test + func threadSafetyOnWaitUntilExit() throws { let process = AsyncProcess(args: "echo", "hello") try process.launch() @@ -206,12 +219,13 @@ final class AsyncProcessTests: XCTestCase { t1.join() t2.join() - XCTAssertEqual(result1, "hello\n") - XCTAssertEqual(result2, "hello\n") + #expect(result1 == "hello\n") + #expect(result2 == "hello\n") } + @Test @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) - func testThreadSafetyOnWaitUntilExitAsync() async throws { + func threadSafetyOnWaitUntilExitAsync() async throws { let process = AsyncProcess(args: "echo", "hello") try process.launch() @@ -226,11 +240,12 @@ final class AsyncProcessTests: XCTestCase { let result1 = try await t1.value let result2 = try await t2.value - XCTAssertEqual(result1, "hello\n") - XCTAssertEqual(result2, "hello\n") + #expect(result1 == "hello\n") + #expect(result2 == "hello\n") } - func testStdin() throws { + @Test + func stdin() throws { var stdout = [UInt8]() let process = AsyncProcess(scriptName: "in-to-out", outputRedirection: .stream(stdout: { stdoutBytes in stdout += stdoutBytes @@ -244,62 +259,63 @@ final class AsyncProcessTests: XCTestCase { try process.waitUntilExit() - XCTAssertEqual(String(decoding: stdout, as: UTF8.self), "hello\n") + #expect(String(decoding: stdout, as: UTF8.self) == "hello\n") } - func testStdoutStdErr() throws { - // A simple script to check that stdout and stderr are captured separatly. + @Test + func stdoutStdErr() throws { do { let result = try AsyncProcess.popen(scriptName: "simple-stdout-stderr") - XCTAssertEqual(try result.utf8Output(), "simple output\n") - XCTAssertEqual(try result.utf8stderrOutput(), "simple error\n") + #expect(try result.utf8Output() == "simple output\n") + #expect(try result.utf8stderrOutput() == "simple error\n") } // A long stdout and stderr output. do { let result = try AsyncProcess.popen(scriptName: "long-stdout-stderr") let count = 16 * 1024 - XCTAssertEqual(try result.utf8Output(), String(repeating: "1", count: count)) - XCTAssertEqual(try result.utf8stderrOutput(), String(repeating: "2", count: count)) + #expect(try result.utf8Output() == String(repeating: "1", count: count)) + #expect(try result.utf8stderrOutput() == String(repeating: "2", count: count)) } // This script will block if the streams are not read. do { let result = try AsyncProcess.popen(scriptName: "deadlock-if-blocking-io") let count = 16 * 1024 - XCTAssertEqual(try result.utf8Output(), String(repeating: "1", count: count)) - XCTAssertEqual(try result.utf8stderrOutput(), String(repeating: "2", count: count)) + #expect(try result.utf8Output() == String(repeating: "1", count: count)) + #expect(try result.utf8stderrOutput() == String(repeating: "2", count: count)) } } + @Test @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) - func testStdoutStdErrAsync() async throws { - // A simple script to check that stdout and stderr are captured separatly. + func stdoutStdErrAsync() async throws { + // A simple script to check that stdout and stderr are captured separatly do { let result = try await AsyncProcess.popen(scriptName: "simple-stdout-stderr") - XCTAssertEqual(try result.utf8Output(), "simple output\n") - XCTAssertEqual(try result.utf8stderrOutput(), "simple error\n") + #expect(try result.utf8Output() == "simple output\n") + #expect(try result.utf8stderrOutput() == "simple error\n") } // A long stdout and stderr output. do { let result = try await AsyncProcess.popen(scriptName: "long-stdout-stderr") let count = 16 * 1024 - XCTAssertEqual(try result.utf8Output(), String(repeating: "1", count: count)) - XCTAssertEqual(try result.utf8stderrOutput(), String(repeating: "2", count: count)) + #expect(try result.utf8Output() == String(repeating: "1", count: count)) + #expect(try result.utf8stderrOutput() == String(repeating: "2", count: count)) } // This script will block if the streams are not read. do { let result = try await AsyncProcess.popen(scriptName: "deadlock-if-blocking-io") let count = 16 * 1024 - XCTAssertEqual(try result.utf8Output(), String(repeating: "1", count: count)) - XCTAssertEqual(try result.utf8stderrOutput(), String(repeating: "2", count: count)) + #expect(try result.utf8Output() == String(repeating: "1", count: count)) + #expect(try result.utf8stderrOutput() == String(repeating: "2", count: count)) } } - func testStdoutStdErrRedirected() throws { - // A simple script to check that stdout and stderr are captured in the same location. + @Test + func stdoutStdErrRedirected() throws { do { let process = AsyncProcess( scriptName: "simple-stdout-stderr", @@ -307,8 +323,8 @@ final class AsyncProcessTests: XCTestCase { ) try process.launch() let result = try process.waitUntilExit() - XCTAssertEqual(try result.utf8Output(), "simple error\nsimple output\n") - XCTAssertEqual(try result.utf8stderrOutput(), "") + #expect(try result.utf8Output() == "simple error\nsimple output\n") + #expect(try result.utf8stderrOutput() == "") } // A long stdout and stderr output. @@ -321,12 +337,13 @@ final class AsyncProcessTests: XCTestCase { let result = try process.waitUntilExit() let count = 16 * 1024 - XCTAssertEqual(try result.utf8Output(), String(repeating: "12", count: count)) - XCTAssertEqual(try result.utf8stderrOutput(), "") + #expect(try result.utf8Output() == String(repeating: "12", count: count)) + #expect(try result.utf8stderrOutput() == "") } } - func testStdoutStdErrStreaming() throws { + @Test + func stdoutStdErrStreaming() throws { var stdout = [UInt8]() var stderr = [UInt8]() let process = AsyncProcess(scriptName: "long-stdout-stderr", outputRedirection: .stream(stdout: { stdoutBytes in @@ -338,11 +355,12 @@ final class AsyncProcessTests: XCTestCase { try process.waitUntilExit() let count = 16 * 1024 - XCTAssertEqual(String(bytes: stdout, encoding: .utf8), String(repeating: "1", count: count)) - XCTAssertEqual(String(bytes: stderr, encoding: .utf8), String(repeating: "2", count: count)) + #expect(String(bytes: stdout, encoding: .utf8) == String(repeating: "1", count: count)) + #expect(String(bytes: stderr, encoding: .utf8) == String(repeating: "2", count: count)) } - func testStdoutStdErrStreamingRedirected() throws { + @Test + func stdoutStdErrStreamingRedirected() throws { var stdout = [UInt8]() var stderr = [UInt8]() let process = AsyncProcess(scriptName: "long-stdout-stderr", outputRedirection: .stream(stdout: { stdoutBytes in @@ -354,22 +372,20 @@ final class AsyncProcessTests: XCTestCase { try process.waitUntilExit() let count = 16 * 1024 - XCTAssertEqual(String(bytes: stdout, encoding: .utf8), String(repeating: "12", count: count)) - XCTAssertEqual(stderr, []) + #expect(String(bytes: stdout, encoding: .utf8) == String(repeating: "12", count: count)) + #expect(stderr == []) } - func testWorkingDirectory() throws { - guard #available(macOS 10.15, *) else { - // Skip this test since it's not supported in this OS. - return - } + @Test + @available(macOS 10.15, *) + func workingDirectory() throws { - #if os(Linux) || os(Android) +#if os(Linux) || os(Android) guard SPM_posix_spawn_file_actions_addchdir_np_supported() else { // Skip this test since it's not supported in this OS. return } - #endif +#endif try withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in let parentPath = tempDirPath.appending(component: "file") @@ -383,22 +399,23 @@ final class AsyncProcessTests: XCTestCase { let process = AsyncProcess(arguments: ["cat", "file"], workingDirectory: tempDirPath) try process.launch() let result = try process.waitUntilExit() - XCTAssertEqual(try result.utf8Output(), "parent") + #expect(try result.utf8Output() == "parent") } do { let process = AsyncProcess(arguments: ["cat", "file"], workingDirectory: childPath.parentDirectory) try process.launch() let result = try process.waitUntilExit() - XCTAssertEqual(try result.utf8Output(), "child") + #expect(try result.utf8Output() == "child") } } } - func testAsyncStream() async throws { - // rdar://133548796 - try XCTSkipIfCI() - + @Test( + .disabled(if: isRunninginCI(), "Disabled in CI"), + .bug("rdar://133548796") + ) + func asyncStream() async throws { let (stdoutStream, stdoutContinuation) = AsyncProcess.ReadableStream.makeStream() let (stderrStream, stderrContinuation) = AsyncProcess.ReadableStream.makeStream() @@ -420,14 +437,14 @@ final class AsyncProcessTests: XCTestCase { stdin.flush() for await output in stdoutStream { - XCTAssertEqual(output, .init("Hello \(counter)\n".utf8)) + #expect(output == .init("Hello \(counter)\n".utf8)) counter += 1 stdin.write(.init("Hello \(counter)\n".utf8)) stdin.flush() } - XCTAssertEqual(counter, 5) + #expect(counter == 5) try stdin.close() } @@ -438,7 +455,7 @@ final class AsyncProcessTests: XCTestCase { counter += 1 } - XCTAssertEqual(counter, 0) + #expect(counter == 0) } defer { @@ -449,13 +466,14 @@ final class AsyncProcessTests: XCTestCase { return try await process.waitUntilExit() } - XCTAssertEqual(result.exitStatus, .terminated(code: 0)) + #expect(result.exitStatus == .terminated(code: 0)) } - func testAsyncStreamHighLevelAPI() async throws { - // rdar://133548796 - try XCTSkipIfCI() - + @Test( + .disabled(if: isRunninginCI(), "Disabled in CI"), + .bug("rdar://133548796") + ) + func asyncStreamHighLevelAPI() async throws { let result = try await AsyncProcess.popen( scriptName: "echo", stdout: { stdin, stdout in @@ -464,14 +482,14 @@ final class AsyncProcessTests: XCTestCase { stdin.flush() for await output in stdout { - XCTAssertEqual(output, .init("Hello \(counter)\n".utf8)) + #expect(output == .init("Hello \(counter)\n".utf8)) counter += 1 stdin.write(.init("Hello \(counter)\n".utf8)) stdin.flush() } - XCTAssertEqual(counter, 5) + #expect(counter == 5) try stdin.close() }, @@ -481,11 +499,11 @@ final class AsyncProcessTests: XCTestCase { counter += 1 } - XCTAssertEqual(counter, 0) + #expect(counter == 0) } ) - XCTAssertEqual(result.exitStatus, .terminated(code: 0)) + #expect(result.exitStatus == .terminated(code: 0)) } } diff --git a/Tests/BasicsTests/ByteStringExtensionsTests.swift b/Tests/BasicsTests/ByteStringExtensionsTests.swift index 7659695c0e5..88bde18c450 100644 --- a/Tests/BasicsTests/ByteStringExtensionsTests.swift +++ b/Tests/BasicsTests/ByteStringExtensionsTests.swift @@ -9,18 +9,20 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation import Basics -import XCTest +import Testing import struct TSCBasic.ByteString -final class ByteStringExtensionsTests: XCTestCase { - func testSHA256Checksum() { +struct ByteStringExtensionsTests { + @Test + func sHA256Checksum() { let byteString = ByteString(encodingAsUTF8: "abc") - XCTAssertEqual(byteString.contents, [0x61, 0x62, 0x63]) + #expect(byteString.contents == [0x61, 0x62, 0x63]) // See https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - XCTAssertEqual(byteString.sha256Checksum, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") + #expect(byteString.sha256Checksum == "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") } } diff --git a/Tests/BasicsTests/ConcurrencyHelpersTests.swift b/Tests/BasicsTests/ConcurrencyHelpersTests.swift index 2efa891fded..fa1e970fd0a 100644 --- a/Tests/BasicsTests/ConcurrencyHelpersTests.swift +++ b/Tests/BasicsTests/ConcurrencyHelpersTests.swift @@ -9,15 +9,17 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation @testable import Basics import TSCTestSupport -import XCTest +import Testing -final class ConcurrencyHelpersTest: XCTestCase { +struct ConcurrencyHelpersTest { let queue = DispatchQueue(label: "ConcurrencyHelpersTest", attributes: .concurrent) - func testThreadSafeKeyValueStore() { + @Test + func threadSafeKeyValueStore() throws { for _ in 0 ..< 100 { let sync = DispatchGroup() @@ -41,18 +43,15 @@ final class ConcurrencyHelpersTest: XCTestCase { } } - switch sync.wait(timeout: .now() + .seconds(2)) { - case .timedOut: - XCTFail("timeout") - case .success: - expected.forEach { key, value in - XCTAssertEqual(cache[key], value) - } + try #require(sync.wait(timeout: .now() + .seconds(2)) == .success) + expected.forEach { key, value in + #expect(cache[key] == value) } } } - func testThreadSafeArrayStore() { + @Test + func threadSafeArrayStore() throws { for _ in 0 ..< 100 { let sync = DispatchGroup() @@ -71,18 +70,15 @@ final class ConcurrencyHelpersTest: XCTestCase { } } - switch sync.wait(timeout: .now() + .seconds(2)) { - case .timedOut: - XCTFail("timeout") - case .success: - let expectedSorted = expected.sorted() - let resultsSorted = cache.get().sorted() - XCTAssertEqual(expectedSorted, resultsSorted) - } + try #require(sync.wait(timeout: .now() + .seconds(2)) == .success) + let expectedSorted = expected.sorted() + let resultsSorted = cache.get().sorted() + #expect(expectedSorted == resultsSorted) } } - func testThreadSafeBox() { + @Test + func threadSafeBox() throws { for _ in 0 ..< 100 { let sync = DispatchGroup() @@ -108,12 +104,8 @@ final class ConcurrencyHelpersTest: XCTestCase { } } - switch sync.wait(timeout: .now() + .seconds(2)) { - case .timedOut: - XCTFail("timeout") - case .success: - XCTAssertEqual(cache.get(), winner) - } + try #require(sync.wait(timeout: .now() + .seconds(2)) == .success) + #expect(cache.get() == winner) } } } diff --git a/Tests/BasicsTests/DictionaryTest.swift b/Tests/BasicsTests/DictionaryTest.swift index 88e4cbbd913..b53ae06463a 100644 --- a/Tests/BasicsTests/DictionaryTest.swift +++ b/Tests/BasicsTests/DictionaryTest.swift @@ -11,26 +11,27 @@ //===----------------------------------------------------------------------===// @testable import Basics -import XCTest +import Testing -final class DictionaryTests: XCTestCase { - func testThrowingUniqueKeysWithValues() throws { +struct DictionaryTests { + @Test + func throwingUniqueKeysWithValues() throws { do { let keysWithValues = [("key1", "value1"), ("key2", "value2")] let dictionary = try Dictionary(throwingUniqueKeysWithValues: keysWithValues) - XCTAssertEqual(dictionary["key1"], "value1") - XCTAssertEqual(dictionary["key2"], "value2") + #expect(dictionary["key1"] == "value1") + #expect(dictionary["key2"] == "value2") } do { let keysWithValues = [("key1", "value"), ("key2", "value")] let dictionary = try Dictionary(throwingUniqueKeysWithValues: keysWithValues) - XCTAssertEqual(dictionary["key1"], "value") - XCTAssertEqual(dictionary["key2"], "value") + #expect(dictionary["key1"] == "value") + #expect(dictionary["key2"] == "value") } do { let keysWithValues = [("key", "value1"), ("key", "value2")] - XCTAssertThrowsError(try Dictionary(throwingUniqueKeysWithValues: keysWithValues)) { error in - XCTAssertEqual(error as? StringError, StringError("duplicate key found: 'key'")) + #expect(throws: StringError("duplicate key found: 'key'")) { + try Dictionary(throwingUniqueKeysWithValues: keysWithValues) } } } diff --git a/Tests/BasicsTests/DispatchTimeTests.swift b/Tests/BasicsTests/DispatchTimeTests.swift index 12286be62e4..50ff32f74da 100644 --- a/Tests/BasicsTests/DispatchTimeTests.swift +++ b/Tests/BasicsTests/DispatchTimeTests.swift @@ -9,30 +9,33 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation import Basics -import XCTest +import Testing -final class DispatchTimeTests: XCTestCase { - func testDifferencePositive() { +struct DispatchTimeTests { + @Test + func differencePositive() { let point: DispatchTime = .now() let future: DispatchTime = point + .seconds(10) let diff1: DispatchTimeInterval = point.distance(to: future) - XCTAssertEqual(diff1.seconds(), 10) + #expect(diff1.seconds() == 10) let diff2: DispatchTimeInterval = future.distance(to: point) - XCTAssertEqual(diff2.seconds(), -10) + #expect(diff2.seconds() == -10) } - func testDifferenceNegative() { + @Test + func differenceNegative() { let point: DispatchTime = .now() let past: DispatchTime = point - .seconds(10) let diff1: DispatchTimeInterval = point.distance(to: past) - XCTAssertEqual(diff1.seconds(), -10) + #expect(diff1.seconds() == -10) let diff2: DispatchTimeInterval = past.distance(to: point) - XCTAssertEqual(diff2.seconds(), 10) + #expect(diff2.seconds() == 10) } } diff --git a/Tests/BasicsTests/Environment/EnvironmentKeyTests.swift b/Tests/BasicsTests/Environment/EnvironmentKeyTests.swift index 60f6b0cb0ee..906f3ec0018 100644 --- a/Tests/BasicsTests/Environment/EnvironmentKeyTests.swift +++ b/Tests/BasicsTests/Environment/EnvironmentKeyTests.swift @@ -9,84 +9,93 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation @testable import Basics -import XCTest +import Testing -final class EnvironmentKeyTests: XCTestCase { - func test_comparable() { +struct EnvironmentKeyTests { + @Test + func comparable() { let key0 = EnvironmentKey("Test") let key1 = EnvironmentKey("Test1") - XCTAssertLessThan(key0, key1) + #expect(key0 < key1) let key2 = EnvironmentKey("test") - XCTAssertLessThan(key0, key2) + #expect(key0 < key2) } - func test_customStringConvertible() { + @Test + func customStringConvertible() { let key = EnvironmentKey("Test") - XCTAssertEqual(key.description, "Test") + #expect(key.description == "Test") } - func test_encodable() throws { + @Test + func encodable() throws { let key = EnvironmentKey("Test") let data = try JSONEncoder().encode(key) let string = String(data: data, encoding: .utf8) - XCTAssertEqual(string, #""Test""#) + #expect(string == #""Test""#) } - func test_equatable() { + @Test + func equatable() { let key0 = EnvironmentKey("Test") let key1 = EnvironmentKey("Test") - XCTAssertEqual(key0, key1) + #expect(key0 == key1) let key2 = EnvironmentKey("Test2") - XCTAssertNotEqual(key0, key2) + #expect(key0 != key2) - #if os(Windows) +#if os(Windows) // Test case insensitivity on windows let key3 = EnvironmentKey("teSt") - XCTAssertEqual(key0, key3) - #endif + #expect(key0 == key3) +#endif } - func test_expressibleByStringLiteral() { + @Test + func expressibleByStringLiteral() { let key0 = EnvironmentKey("Test") - XCTAssertEqual(key0, "Test") + #expect(key0 == "Test") } - func test_decodable() throws { + @Test + func decodable() throws { let jsonString = #""Test""# let data = jsonString.data(using: .utf8)! let key = try JSONDecoder().decode(EnvironmentKey.self, from: data) - XCTAssertEqual(key.rawValue, "Test") + #expect(key.rawValue == "Test") } - func test_hashable() { + @Test + func hashable() { var set = Set() let key0 = EnvironmentKey("Test") - XCTAssertTrue(set.insert(key0).inserted) + #expect(set.insert(key0).inserted) let key1 = EnvironmentKey("Test") - XCTAssertTrue(set.contains(key1)) - XCTAssertFalse(set.insert(key1).inserted) + #expect(set.contains(key1)) + #expect(!set.insert(key1).inserted) let key2 = EnvironmentKey("Test2") - XCTAssertFalse(set.contains(key2)) - XCTAssertTrue(set.insert(key2).inserted) + #expect(!set.contains(key2)) + #expect(set.insert(key2).inserted) - #if os(Windows) +#if os(Windows) // Test case insensitivity on windows let key3 = EnvironmentKey("teSt") - XCTAssertTrue(set.contains(key3)) - XCTAssertFalse(set.insert(key3).inserted) - #endif + #expect(set.contains(key3)) + #expect(!set.insert(key3).inserted) +#endif - XCTAssertEqual(set, ["Test", "Test2"]) + #expect(set == ["Test", "Test2"]) } - func test_rawRepresentable() { + @Test + func rawRepresentable() { let key = EnvironmentKey(rawValue: "Test") - XCTAssertEqual(key?.rawValue, "Test") + #expect(key?.rawValue == "Test") } } diff --git a/Tests/BasicsTests/Environment/EnvironmentTests.swift b/Tests/BasicsTests/Environment/EnvironmentTests.swift index b1476684808..fc53275c5cf 100644 --- a/Tests/BasicsTests/Environment/EnvironmentTests.swift +++ b/Tests/BasicsTests/Environment/EnvironmentTests.swift @@ -9,193 +9,213 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation @_spi(SwiftPMInternal) @testable import Basics -import XCTest +import Testing -final class EnvironmentTests: XCTestCase { - func test_init() { +struct EnvironmentTests { + @Test + func initialize() { let environment = Environment() - XCTAssertTrue(environment.isEmpty) + #expect(environment.isEmpty) } - func test_subscript() { + @Test + func setting_and_accessing_via_subscript() { var environment = Environment() let key = EnvironmentKey("TestKey") environment[key] = "TestValue" - XCTAssertEqual(environment[key], "TestValue") + #expect(environment[key] == "TestValue") } - func test_initDictionaryFromSelf() { + @Test + func initDictionaryFromSelf() { let dictionary = [ "TestKey": "TestValue", "testKey": "TestValue2", ] let environment = Environment(dictionary) - #if os(Windows) - XCTAssertEqual(environment["TestKey"], "TestValue2") // uppercase sorts before lowercase, so the second value overwrites the first - XCTAssertEqual(environment.count, 1) - #else - XCTAssertEqual(environment["TestKey"], "TestValue") - XCTAssertEqual(environment.count, 2) - #endif + let expectedValue: String + let expectedCount: Int +#if os(Windows) + expectedValue = "TestValue2" // uppercase sorts before lowercase, so the second value overwrites the first + expectedCount = 1 +#else + expectedValue = "TestValue" + expectedCount = 2 +#endif + #expect(environment["TestKey"] == expectedValue, "Actual value is not as expected") + #expect(environment.count == expectedCount, "Actual count is not as expected") } - func test_initSelfFromDictionary() { + @Test + func initSelfFromDictionary() { let dictionary = ["TestKey": "TestValue"] let environment = Environment(dictionary) - XCTAssertEqual(environment["TestKey"], "TestValue") + #expect(environment["TestKey"] == "TestValue") } func path(_ components: String...) -> String { components.joined(separator: Environment.pathEntryDelimiter) } - func test_prependPath() { + @Test + func prependPath() { var environment = Environment() let key = EnvironmentKey(UUID().uuidString) - XCTAssertNil(environment[key]) + #expect(environment[key] == nil) environment.prependPath(key: key, value: "/bin") - XCTAssertEqual(environment[key], path("/bin")) + #expect(environment[key] == path("/bin")) environment.prependPath(key: key, value: "/usr/bin") - XCTAssertEqual(environment[key], path("/usr/bin", "/bin")) + #expect(environment[key] == path("/usr/bin", "/bin")) environment.prependPath(key: key, value: "/usr/local/bin") - XCTAssertEqual(environment[key], path("/usr/local/bin", "/usr/bin", "/bin")) + #expect(environment[key] == path("/usr/local/bin", "/usr/bin", "/bin")) environment.prependPath(key: key, value: "") - XCTAssertEqual(environment[key], path("/usr/local/bin", "/usr/bin", "/bin")) + #expect(environment[key] == path("/usr/local/bin", "/usr/bin", "/bin")) } - func test_appendPath() { + @Test + func appendPath() { var environment = Environment() let key = EnvironmentKey(UUID().uuidString) - XCTAssertNil(environment[key]) + #expect(environment[key] == nil) environment.appendPath(key: key, value: "/bin") - XCTAssertEqual(environment[key], path("/bin")) + #expect(environment[key] == path("/bin")) environment.appendPath(key: key, value: "/usr/bin") - XCTAssertEqual(environment[key], path("/bin", "/usr/bin")) + #expect(environment[key] == path("/bin", "/usr/bin")) environment.appendPath(key: key, value: "/usr/local/bin") - XCTAssertEqual(environment[key], path("/bin", "/usr/bin", "/usr/local/bin")) + #expect(environment[key] == path("/bin", "/usr/bin", "/usr/local/bin")) environment.appendPath(key: key, value: "") - XCTAssertEqual(environment[key], path("/bin", "/usr/bin", "/usr/local/bin")) + #expect(environment[key] == path("/bin", "/usr/bin", "/usr/local/bin")) } - func test_pathEntryDelimiter() { - #if os(Windows) - XCTAssertEqual(Environment.pathEntryDelimiter, ";") - #else - XCTAssertEqual(Environment.pathEntryDelimiter, ":") - #endif + @Test + func pathEntryDelimiter() { +#if os(Windows) + #expect(Environment.pathEntryDelimiter == ";") +#else + #expect(Environment.pathEntryDelimiter == ":") +#endif } /// Important: This test is inherently race-prone, if it is proven to be /// flaky, it should run in a singled threaded environment/removed entirely. - func test_current() { - XCTAssertEqual( - Environment.current["PATH"], - ProcessInfo.processInfo.environment["PATH"]) + @Test + func current() { + #expect(Environment.current["PATH"] == ProcessInfo.processInfo.environment["PATH"]) } /// Important: This test is inherently race-prone, if it is proven to be /// flaky, it should run in a singled threaded environment/removed entirely. - func test_makeCustom() async throws { + @Test + func makeCustom() async throws { let key = EnvironmentKey(UUID().uuidString) let value = "TestValue" var customEnvironment = Environment() customEnvironment[key] = value - XCTAssertNil(Environment.current[key]) + #expect(Environment.current[key] == nil) try Environment.makeCustom(customEnvironment) { - XCTAssertEqual(Environment.current[key], value) + #expect(Environment.current[key] == value) } - XCTAssertNil(Environment.current[key]) + #expect(Environment.current[key] == nil) } /// Important: This test is inherently race-prone, if it is proven to be /// flaky, it should run in a singled threaded environment/removed entirely. - func testProcess() throws { + @Test + func process() throws { let key = EnvironmentKey(UUID().uuidString) let value = "TestValue" var environment = Environment.current - XCTAssertNil(environment[key]) + #expect(environment[key] == nil) try Environment.set(key: key, value: value) environment = Environment.current // reload - XCTAssertEqual(environment[key], value) + #expect(environment[key] == value) try Environment.set(key: key, value: nil) - XCTAssertEqual(environment[key], value) // this is a copy! + #expect(environment[key] == value) // this is a copy! environment = Environment.current // reload - XCTAssertNil(environment[key]) + #expect(environment[key] == nil) } - func test_cachable() { + @Test + func cachable() { let term = EnvironmentKey("TERM") var environment = Environment() environment[.path] = "/usr/bin" environment[term] = "xterm-256color" let cachableEnvironment = environment.cachable - XCTAssertNotNil(cachableEnvironment[.path]) - XCTAssertNil(cachableEnvironment[term]) + #expect(cachableEnvironment[.path] != nil) + #expect(cachableEnvironment[term] == nil) } - func test_collection() { + @Test + func collection() { let environment: Environment = ["TestKey": "TestValue"] - XCTAssertEqual(environment.count, 1) - XCTAssertEqual(environment.first?.key, EnvironmentKey("TestKey")) - XCTAssertEqual(environment.first?.value, "TestValue") + #expect(environment.count == 1) + #expect(environment.first?.key == EnvironmentKey("TestKey")) + #expect(environment.first?.value == "TestValue") } - func test_description() { + @Test + func description() { var environment = Environment() environment[EnvironmentKey("TestKey")] = "TestValue" - XCTAssertEqual(environment.description, #"["TestKey=TestValue"]"#) + #expect(environment.description == #"["TestKey=TestValue"]"#) } - func test_encodable() throws { + @Test + func encodable() throws { var environment = Environment() environment["TestKey"] = "TestValue" let data = try JSONEncoder().encode(environment) let jsonString = String(data: data, encoding: .utf8) - XCTAssertEqual(jsonString, #"{"TestKey":"TestValue"}"#) + #expect(jsonString == #"{"TestKey":"TestValue"}"#) } - func test_equatable() { + @Test + func equatable() { let environment0: Environment = ["TestKey": "TestValue"] let environment1: Environment = ["TestKey": "TestValue"] - XCTAssertEqual(environment0, environment1) + #expect(environment0 == environment1) #if os(Windows) // Test case insensitivity on windows let environment2: Environment = ["testKey": "TestValue"] - XCTAssertEqual(environment0, environment2) + #expect(environment0 == environment2) #endif } - func test_expressibleByDictionaryLiteral() { + @Test + func expressibleByDictionaryLiteral() { let environment: Environment = ["TestKey": "TestValue"] - XCTAssertEqual(environment["TestKey"], "TestValue") + #expect(environment["TestKey"] == "TestValue") } - func test_decodable() throws { + @Test + func decodable() throws { let jsonString = #"{"TestKey":"TestValue"}"# let data = jsonString.data(using: .utf8)! let environment = try JSONDecoder().decode(Environment.self, from: data) - XCTAssertEqual(environment[EnvironmentKey("TestKey")], "TestValue") + #expect(environment[EnvironmentKey("TestKey")] == "TestValue") } } diff --git a/Tests/BasicsTests/FileSystem/FileSystemTests.swift b/Tests/BasicsTests/FileSystem/FileSystemTests.swift index 32c6ddb121d..b48bf702167 100644 --- a/Tests/BasicsTests/FileSystem/FileSystemTests.swift +++ b/Tests/BasicsTests/FileSystem/FileSystemTests.swift @@ -9,13 +9,15 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation @testable import Basics import TSCTestSupport -import XCTest +import Testing -final class FileSystemTests: XCTestCase { - func testStripFirstLevelComponent() throws { +struct FileSystemTests { + @Test + func stripFirstLevelComponent() throws { let fileSystem = InMemoryFileSystem() let rootPath = AbsolutePath("/root") @@ -35,29 +37,31 @@ final class FileSystemTests: XCTestCase { do { let contents = try fileSystem.getDirectoryContents(.root) - XCTAssertEqual(contents.count, 1) + #expect(contents.count == 1) } try fileSystem.stripFirstLevel(of: .root) do { let contents = Set(try fileSystem.getDirectoryContents(.root)) - XCTAssertEqual(contents.count, totalDirectories + totalFiles) + #expect(contents.count == totalDirectories + totalFiles) for index in 0 ..< totalDirectories { - XCTAssertTrue(contents.contains("dir\(index)")) + #expect(contents.contains("dir\(index)")) } for index in 0 ..< totalFiles { - XCTAssertTrue(contents.contains("file\(index)")) + #expect(contents.contains("file\(index)")) } } } - func testStripFirstLevelComponentErrors() throws { + @Test + func stripFirstLevelComponentErrors() throws { + let functionUnderTest = "stripFirstLevel" do { let fileSystem = InMemoryFileSystem() - XCTAssertThrowsError(try fileSystem.stripFirstLevel(of: .root), "expected error") { error in - XCTAssertMatch((error as? StringError)?.description, .contains("requires single top level directory")) + #expect(throws: StringError("\(functionUnderTest) requires single top level directory")) { + try fileSystem.stripFirstLevel(of: .root) } } @@ -67,8 +71,8 @@ final class FileSystemTests: XCTestCase { let path = AbsolutePath.root.appending("dir\(index)") try fileSystem.createDirectory(path, recursive: false) } - XCTAssertThrowsError(try fileSystem.stripFirstLevel(of: .root), "expected error") { error in - XCTAssertMatch((error as? StringError)?.description, .contains("requires single top level directory")) + #expect(throws: StringError("\(functionUnderTest) requires single top level directory")) { + try fileSystem.stripFirstLevel(of: .root) } } @@ -78,8 +82,8 @@ final class FileSystemTests: XCTestCase { let path = AbsolutePath.root.appending("file\(index)") try fileSystem.writeFileContents(path, string: "\(index)") } - XCTAssertThrowsError(try fileSystem.stripFirstLevel(of: .root), "expected error") { error in - XCTAssertMatch((error as? StringError)?.description, .contains("requires single top level directory")) + #expect(throws: StringError("\(functionUnderTest) requires single top level directory")) { + try fileSystem.stripFirstLevel(of: .root) } } @@ -87,8 +91,8 @@ final class FileSystemTests: XCTestCase { let fileSystem = InMemoryFileSystem() let path = AbsolutePath.root.appending("file") try fileSystem.writeFileContents(path, string: "") - XCTAssertThrowsError(try fileSystem.stripFirstLevel(of: .root), "expected error") { error in - XCTAssertMatch((error as? StringError)?.description, .contains("requires single top level directory")) + #expect(throws: StringError("\(functionUnderTest) requires single top level directory")) { + try fileSystem.stripFirstLevel(of: .root) } } } diff --git a/Tests/BasicsTests/FileSystem/PathShimTests.swift b/Tests/BasicsTests/FileSystem/PathShimTests.swift index 6f0af62a76f..ec11b46098d 100644 --- a/Tests/BasicsTests/FileSystem/PathShimTests.swift +++ b/Tests/BasicsTests/FileSystem/PathShimTests.swift @@ -12,64 +12,65 @@ import Basics import Foundation -import XCTest +import Testing -class PathShimTests: XCTestCase { - func testRescursiveDirectoryCreation() { - // For the tests we'll need a temporary directory. - try! withTemporaryDirectory(removeTreeOnDeinit: true) { path in +struct PathShimTests { + @Test + func rescursiveDirectoryCreation() throws { + try withTemporaryDirectory(removeTreeOnDeinit: true) { path in // Create a directory under several ancestor directories. let dirPath = path.appending(components: "abc", "def", "ghi", "mno", "pqr") try! makeDirectories(dirPath) // Check that we were able to actually create the directory. - XCTAssertTrue(localFileSystem.isDirectory(dirPath)) + #expect(localFileSystem.isDirectory(dirPath)) // Check that there's no error if we try to create the directory again. - try! makeDirectories(dirPath) + #expect(throws: Never.self) { + try makeDirectories(dirPath) + } } } } -class WalkTests: XCTestCase { - func testNonRecursive() throws { - #if os(Android) +struct WalkTests { + @Test + func nonRecursive() throws { +#if os(Android) let root = "/system" var expected: [AbsolutePath] = [ "\(root)/usr", "\(root)/bin", "\(root)/etc", ] - #elseif os(Windows) + let expectedCount = 3 +#elseif os(Windows) let root = ProcessInfo.processInfo.environment["SystemRoot"]! var expected: [AbsolutePath] = [ "\(root)/System32", "\(root)/SysWOW64", ] - #else + let expectedCount = (root as NSString).pathComponents.count +#else let root = "" var expected: [AbsolutePath] = [ "/usr", "/bin", "/sbin", ] - #endif + let expectedCount = 2 +#endif for x in try walk(AbsolutePath(validating: "\(root)/"), recursively: false) { if let i = expected.firstIndex(of: x) { expected.remove(at: i) } - #if os(Android) - XCTAssertEqual(3, x.components.count) - #elseif os(Windows) - XCTAssertEqual((root as NSString).pathComponents.count + 2, x.components.count) - #else - XCTAssertEqual(2, x.components.count) - #endif + #expect(expectedCount == x.components.count) } - XCTAssertEqual(expected.count, 0) + #expect(expected.count == 0) } - func testRecursive() { + @Test + func recursive() { let root = AbsolutePath(#file).parentDirectory.parentDirectory.parentDirectory.parentDirectory .appending(component: "Sources") var expected = [ @@ -82,6 +83,6 @@ class WalkTests: XCTestCase { expected.remove(at: i) } } - XCTAssertEqual(expected, []) + #expect(expected == []) } } diff --git a/Tests/BasicsTests/FileSystem/PathTests.swift b/Tests/BasicsTests/FileSystem/PathTests.swift index 1f9e7b7b80b..f14aafa0cc1 100644 --- a/Tests/BasicsTests/FileSystem/PathTests.swift +++ b/Tests/BasicsTests/FileSystem/PathTests.swift @@ -6,11 +6,11 @@ See http://swift.org/LICENSE.txt for license information See http://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ + */ import Basics import Foundation -import XCTest +import Testing #if os(Windows) private var windows: Bool { true } @@ -18,388 +18,545 @@ private var windows: Bool { true } private var windows: Bool { false } #endif -class PathTests: XCTestCase { - func testBasics() { - XCTAssertEqual(AbsolutePath("/").pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/a").pathString, windows ? #"\a"# : "/a") - XCTAssertEqual(AbsolutePath("/a/b/c").pathString, windows ? #"\a\b\c"# : "/a/b/c") - XCTAssertEqual(RelativePath(".").pathString, ".") - XCTAssertEqual(RelativePath("a").pathString, "a") - XCTAssertEqual(RelativePath("a/b/c").pathString, windows ? #"a\b\c"# : "a/b/c") - XCTAssertEqual(RelativePath("~").pathString, "~") // `~` is not special - } +struct PathTests { + struct AbsolutePathTests { + @Test( + arguments: [ + // // basics + (path: "/", expected: (windows ? #"\"# : "/")), + (path: "/a", expected: (windows ? #"\a"# : "/a")), + (path: "/a/b/c", expected: (windows ? #"\a\b\c"# : "/a/b/c")), + // string literal initialization + (path: "/", expected: (windows ? #"\"# : "/")), + // repeated path seperators + (path: "/ab//cd//ef", expected: (windows ? #"\ab\cd\ef"# : "/ab/cd/ef")), + (path: "/ab///cd//ef", expected: (windows ? #"\ab\cd\ef"# : "/ab/cd/ef")), + // trailing path seperators + (path: "/ab/cd/ef/", expected: (windows ? #"\ab\cd\ef"# : "/ab/cd/ef")), + (path: "/ab/cd/ef//", expected: (windows ? #"\ab\cd\ef"# : "/ab/cd/ef")), + // dot path components + (path: "/ab/././cd//ef", expected: "/ab/cd/ef"), + (path: "/ab/./cd//ef/.", expected: "/ab/cd/ef"), + // dot dot path components + (path: "/..", expected: (windows ? #"\"# : "/")), + (path: "/../../../../..", expected: (windows ? #"\"# : "/")), + (path: "/abc/..", expected: (windows ? #"\"# : "/")), + (path: "/abc/../..", expected: (windows ? #"\"# : "/")), + (path: "/../abc", expected: (windows ? #"\abc"# : "/abc")), + (path: "/../abc/..", expected: (windows ? #"\"# : "/")), + (path: "/../abc/../def", expected: (windows ? #"\def"# : "/def")), + // combinations and edge cases + (path: "///", expected: (windows ? #"\"# : "/")), + (path: "/./", expected: (windows ? #"\"# : "/")) + ] + ) + func pathStringIsSetCorrectly(path: String, expected: String) { + let actual = AbsolutePath(path).pathString + + #expect(actual == expected, "Actual is not as expected") + } - func testStringInitialization() throws { - let abs1 = AbsolutePath("/") - let abs2 = AbsolutePath(abs1, ".") - XCTAssertEqual(abs1, abs2) - let rel3 = "." - let abs3 = try AbsolutePath(abs2, validating: rel3) - XCTAssertEqual(abs2, abs3) - let base = AbsolutePath("/base/path") - let abs4 = AbsolutePath("/a/b/c", relativeTo: base) - XCTAssertEqual(abs4, AbsolutePath("/a/b/c")) - let abs5 = AbsolutePath("./a/b/c", relativeTo: base) - XCTAssertEqual(abs5, AbsolutePath("/base/path/a/b/c")) - let abs6 = AbsolutePath("~/bla", relativeTo: base) // `~` isn't special - XCTAssertEqual(abs6, AbsolutePath("/base/path/~/bla")) - } + @Test( + arguments: [ + (path: "/", expected: (windows ? #"\"# : "/")), + (path: "/a", expected: (windows ? #"\"# : "/")), + (path: "/./a", expected: (windows ? #"\"# : "/")), + (path: "/../..", expected: (windows ? #"\"# : "/")), + (path: "/ab/c//d/", expected: (windows ? #"\ab\c"# : "/ab/c")) - func testStringLiteralInitialization() { - let abs = AbsolutePath("/") - XCTAssertEqual(abs.pathString, windows ? #"\"# : "/") - let rel1 = RelativePath(".") - XCTAssertEqual(rel1.pathString, ".") - let rel2 = RelativePath("~") - XCTAssertEqual(rel2.pathString, "~") // `~` is not special - } + ] + ) + func dirnameAttributeReturnsExpectedValue(path: String, expected: String) { + let actual = AbsolutePath(path).dirname - func testRepeatedPathSeparators() { - XCTAssertEqual(AbsolutePath("/ab//cd//ef").pathString, windows ? #"\ab\cd\ef"# : "/ab/cd/ef") - XCTAssertEqual(AbsolutePath("/ab///cd//ef").pathString, windows ? #"\ab\cd\ef"# : "/ab/cd/ef") - XCTAssertEqual(RelativePath("ab//cd//ef").pathString, windows ? #"ab\cd\ef"# : "ab/cd/ef") - XCTAssertEqual(RelativePath("ab//cd///ef").pathString, windows ? #"ab\cd\ef"# : "ab/cd/ef") - } + #expect(actual == expected, "Actual is not as expected") + } - func testTrailingPathSeparators() { - XCTAssertEqual(AbsolutePath("/ab/cd/ef/").pathString, windows ? #"\ab\cd\ef"# : "/ab/cd/ef") - XCTAssertEqual(AbsolutePath("/ab/cd/ef//").pathString, windows ? #"\ab\cd\ef"# : "/ab/cd/ef") - XCTAssertEqual(RelativePath("ab/cd/ef/").pathString, windows ? #"ab\cd\ef"# : "ab/cd/ef") - XCTAssertEqual(RelativePath("ab/cd/ef//").pathString, windows ? #"ab\cd\ef"# : "ab/cd/ef") - } + @Test( + arguments: [ + (path: "/", expected: (windows ? #"\"# : "/")), + (path: "/a", expected: "a"), + (path: "/./a", expected: "a"), + (path: "/../..", expected: "/") + ] + ) + func basenameAttributeReturnsExpectedValue(path: String, expected: String) { + let actual = AbsolutePath(path).basename + + #expect(actual == expected, "Actual is not as expected") + } - func testDotPathComponents() { - XCTAssertEqual(AbsolutePath("/ab/././cd//ef").pathString, "/ab/cd/ef") - XCTAssertEqual(AbsolutePath("/ab/./cd//ef/.").pathString, "/ab/cd/ef") - XCTAssertEqual(RelativePath("ab/./cd/././ef").pathString, "ab/cd/ef") - XCTAssertEqual(RelativePath("ab/./cd/ef/.").pathString, "ab/cd/ef") - } + @Test( + arguments: [ + // path without extension + (path: "/", expected: (windows ? #"\"# : "/")), + (path: "/a", expected: "a"), + (path: "/./a", expected: "a"), + (path: "/../..", expected: "/"), + // path with extension + (path: "/a.txt", expected: "a"), + (path: "/./a.txt", expected: "a") + + ] + ) + func basenameWithoutExtAttributeReturnsExpectedValue(path: String, expected: String) { + let actual = AbsolutePath(path).basenameWithoutExt + + #expect(actual == expected, "Actual is not as expected") + } - func testDotDotPathComponents() { - XCTAssertEqual(AbsolutePath("/..").pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/../../../../..").pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/abc/..").pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/abc/../..").pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/../abc").pathString, windows ? #"\abc"# : "/abc") - XCTAssertEqual(AbsolutePath("/../abc/..").pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/../abc/../def").pathString, windows ? #"\def"# : "/def") - XCTAssertEqual(RelativePath("..").pathString, "..") - XCTAssertEqual(RelativePath("../..").pathString, "../..") - XCTAssertEqual(RelativePath(".././..").pathString, "../..") - XCTAssertEqual(RelativePath("../abc/..").pathString, "..") - XCTAssertEqual(RelativePath("../abc/.././").pathString, "..") - XCTAssertEqual(RelativePath("abc/..").pathString, ".") - } + @Test( + arguments: [ + (path: "/", numParentDirectoryCalls: 1, expected: "/"), + (path: "/", numParentDirectoryCalls: 2, expected: "/"), + (path: "/bar", numParentDirectoryCalls: 1, expected: "/"), + (path: "/bar/../foo/..//", numParentDirectoryCalls: 2, expected: "/"), + (path: "/bar/../foo/..//yabba/a/b", numParentDirectoryCalls: 2, expected: "/yabba") + ] + ) + func parentDirectoryAttributeReturnsAsExpected(path: String, numParentDirectoryCalls: Int, expected: String) { + let pathUnderTest = AbsolutePath(path) + let expectedPath = AbsolutePath(expected) + + var actual = pathUnderTest + for _ in 0 ..< numParentDirectoryCalls { + actual = actual.parentDirectory + } + #expect(actual == expectedPath) + } + @Test( + arguments: [ + (path:"/", expected: ["/"]), + (path:"/.", expected: ["/"]), + (path:"/..", expected: ["/"]), + (path:"/bar", expected: ["/", "bar"]), + (path:"/foo/bar/..", expected: ["/", "foo"]), + (path:"/bar/../foo", expected: ["/", "foo"]), + (path:"/bar/../foo/..//", expected: ["/"]), + (path:"/bar/../foo/..//yabba/a/b/", expected: ["/", "yabba", "a", "b"]) + ] + ) + func componentsAttributeReturnsExpectedValue(path: String, expected: [String]) { + let actual = AbsolutePath(path).components + + #expect(actual == expected) + } - func testCombinationsAndEdgeCases() { - XCTAssertEqual(AbsolutePath("///").pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/./").pathString, windows ? #"\"# : "/") - XCTAssertEqual(RelativePath("").pathString, ".") - XCTAssertEqual(RelativePath(".").pathString, ".") - XCTAssertEqual(RelativePath("./abc").pathString, "abc") - XCTAssertEqual(RelativePath("./abc/").pathString, "abc") - XCTAssertEqual(RelativePath("./abc/../bar").pathString, "bar") - XCTAssertEqual(RelativePath("foo/../bar").pathString, "bar") - XCTAssertEqual(RelativePath("foo///..///bar///baz").pathString, "bar/baz") - XCTAssertEqual(RelativePath("foo/../bar/./").pathString, "bar") - XCTAssertEqual(RelativePath("../abc/def/").pathString, "../abc/def") - XCTAssertEqual(RelativePath("././././.").pathString, ".") - XCTAssertEqual(RelativePath("./././../.").pathString, "..") - XCTAssertEqual(RelativePath("./").pathString, ".") - XCTAssertEqual(RelativePath(".//").pathString, ".") - XCTAssertEqual(RelativePath("./.").pathString, ".") - XCTAssertEqual(RelativePath("././").pathString, ".") - XCTAssertEqual(RelativePath("../").pathString, "..") - XCTAssertEqual(RelativePath("../.").pathString, "..") - XCTAssertEqual(RelativePath("./..").pathString, "..") - XCTAssertEqual(RelativePath("./../.").pathString, "..") - XCTAssertEqual(RelativePath("./////../////./////").pathString, "..") - XCTAssertEqual(RelativePath("../a").pathString, windows ? #"..\a"# : "../a") - XCTAssertEqual(RelativePath("../a/..").pathString, "..") - XCTAssertEqual(RelativePath("a/..").pathString, ".") - XCTAssertEqual(RelativePath("a/../////../////./////").pathString, "..") - } + @Test( + arguments: [ + (path: "/", relativeTo: "/", expected: "."), + (path: "/a/b/c/d", relativeTo: "/", expected: "a/b/c/d"), + (path: "/", relativeTo: "/a/b/c", expected: "../../.."), + (path: "/a/b/c/d", relativeTo: "/a/b", expected: "c/d"), + (path: "/a/b/c/d", relativeTo: "/a/b/c", expected: "d"), + (path: "/a/b/c/d", relativeTo: "/a/c/d", expected: "../../b/c/d"), + (path: "/a/b/c/d", relativeTo: "/b/c/d", expected: "../../../a/b/c/d") + ] + ) + func relativePathFromAbsolutePaths(path: String, relativeTo: String, expected: String) { + let actual = AbsolutePath(path).relative(to: AbsolutePath(relativeTo)) + let expected = RelativePath(expected) + + #expect(actual == expected, "Actual is not as expected") + } - func testDirectoryNameExtraction() { - XCTAssertEqual(AbsolutePath("/").dirname, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/a").dirname, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/./a").dirname, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/../..").dirname, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/ab/c//d/").dirname, windows ? #"\ab\c"# : "/ab/c") - XCTAssertEqual(RelativePath("ab/c//d/").dirname, windows ? #"ab\c"# : "ab/c") - XCTAssertEqual(RelativePath("../a").dirname, "..") - XCTAssertEqual(RelativePath("../a/..").dirname, ".") - XCTAssertEqual(RelativePath("a/..").dirname, ".") - XCTAssertEqual(RelativePath("./..").dirname, ".") - XCTAssertEqual(RelativePath("a/../////../////./////").dirname, ".") - XCTAssertEqual(RelativePath("abc").dirname, ".") - XCTAssertEqual(RelativePath("").dirname, ".") - XCTAssertEqual(RelativePath(".").dirname, ".") - } + @Test + func comparison() { + #expect(AbsolutePath("/") <= AbsolutePath("/")); + #expect(AbsolutePath("/abc") < AbsolutePath("/def")); + #expect(AbsolutePath("/2") <= AbsolutePath("/2.1")); + #expect(AbsolutePath("/3.1") > AbsolutePath("/2")); + #expect(AbsolutePath("/2") >= AbsolutePath("/2")); + #expect(AbsolutePath("/2.1") >= AbsolutePath("/2")); + } - func testBaseNameExtraction() { - XCTAssertEqual(AbsolutePath("/").basename, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/a").basename, "a") - XCTAssertEqual(AbsolutePath("/./a").basename, "a") - XCTAssertEqual(AbsolutePath("/../..").basename, "/") - XCTAssertEqual(RelativePath("../..").basename, "..") - XCTAssertEqual(RelativePath("../a").basename, "a") - XCTAssertEqual(RelativePath("../a/..").basename, "..") - XCTAssertEqual(RelativePath("a/..").basename, ".") - XCTAssertEqual(RelativePath("./..").basename, "..") - XCTAssertEqual(RelativePath("a/../////../////./////").basename, "..") - XCTAssertEqual(RelativePath("abc").basename, "abc") - XCTAssertEqual(RelativePath("").basename, ".") - XCTAssertEqual(RelativePath(".").basename, ".") - } + struct ancestryTest{ + @Test( + arguments: [ + (path: "/a/b/c/d/e/f", descendentOfOrEqualTo: "/a/b/c/d", expected: true), + (path: "/a/b/c/d/e/f.swift", descendentOfOrEqualTo: "/a/b/c", expected: true), + (path: "/", descendentOfOrEqualTo: "/", expected: true), + (path: "/foo/bar", descendentOfOrEqualTo: "/", expected: true), + (path: "/foo/bar", descendentOfOrEqualTo: "/foo/bar/baz", expected: false), + (path: "/foo/bar", descendentOfOrEqualTo: "/bar", expected: false) + // (path: "", descendentOfOrEqualTo: "", expected: true), + ] + ) + func isDescendantOfOrEqual(path: String, descendentOfOrEqualTo: String, expected: Bool) { + let actual = AbsolutePath(path).isDescendantOfOrEqual(to: AbsolutePath(descendentOfOrEqualTo)) + + #expect(actual == expected, "Actual is not as expected") + } + + @Test( + arguments: [ + (path: "/foo/bar", descendentOf: "/foo/bar", expected: false), + (path: "/foo/bar", descendentOf: "/foo", expected: true) + ] + ) + func isDescendant(path: String, ancesterOf: String, expected: Bool) { + let actual = AbsolutePath(path).isDescendant(of: AbsolutePath(ancesterOf)) + + #expect(actual == expected, "Actual is not as expected") + } + + @Test( + arguments: [ + (path: "/a/b/c/d", ancestorOfOrEqualTo: "/a/b/c/d/e/f", expected: true), + (path: "/a/b/c", ancestorOfOrEqualTo: "/a/b/c/d/e/f.swift", expected: true), + (path: "/", ancestorOfOrEqualTo: "/", expected: true), + (path: "/", ancestorOfOrEqualTo: "/foo/bar", expected: true), + (path: "/foo/bar/baz", ancestorOfOrEqualTo: "/foo/bar", expected: false), + (path: "/bar", ancestorOfOrEqualTo: "/foo/bar", expected: false), + ] + ) + func isAncestorOfOrEqual(path: String, ancestorOfOrEqualTo: String, expected: Bool) { + let actual = AbsolutePath(path).isAncestorOfOrEqual(to: AbsolutePath(ancestorOfOrEqualTo)) + + #expect(actual == expected, "Actual is not as expected") + } + + @Test( + arguments: [ + (path: "/foo/bar", ancesterOf: "/foo/bar", expected: false), + (path: "/foo", ancesterOf: "/foo/bar", expected: true), + ] + ) + func isAncestor(path: String, ancesterOf: String, expected: Bool) { + let actual = AbsolutePath(path).isAncestor(of: AbsolutePath(ancesterOf)) + + #expect(actual == expected, "Actual is not as expected") + } + } - func testBaseNameWithoutExt() { - XCTAssertEqual(AbsolutePath("/").basenameWithoutExt, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/a").basenameWithoutExt, "a") - XCTAssertEqual(AbsolutePath("/./a").basenameWithoutExt, "a") - XCTAssertEqual(AbsolutePath("/../..").basenameWithoutExt, "/") - XCTAssertEqual(RelativePath("../..").basenameWithoutExt, "..") - XCTAssertEqual(RelativePath("../a").basenameWithoutExt, "a") - XCTAssertEqual(RelativePath("../a/..").basenameWithoutExt, "..") - XCTAssertEqual(RelativePath("a/..").basenameWithoutExt, ".") - XCTAssertEqual(RelativePath("./..").basenameWithoutExt, "..") - XCTAssertEqual(RelativePath("a/../////../////./////").basenameWithoutExt, "..") - XCTAssertEqual(RelativePath("abc").basenameWithoutExt, "abc") - XCTAssertEqual(RelativePath("").basenameWithoutExt, ".") - XCTAssertEqual(RelativePath(".").basenameWithoutExt, ".") - - XCTAssertEqual(AbsolutePath("/a.txt").basenameWithoutExt, "a") - XCTAssertEqual(AbsolutePath("/./a.txt").basenameWithoutExt, "a") - XCTAssertEqual(RelativePath("../a.bc").basenameWithoutExt, "a") - XCTAssertEqual(RelativePath("abc.swift").basenameWithoutExt, "abc") - XCTAssertEqual(RelativePath("../a.b.c").basenameWithoutExt, "a.b") - XCTAssertEqual(RelativePath("abc.xyz.123").basenameWithoutExt, "abc.xyz") - } + @Test + func absolutePathValidation() { + #expect(throws: Never.self) { + try AbsolutePath(validating: "/a/b/c/d") + } - func testSuffixExtraction() { - XCTAssertEqual(RelativePath("a").suffix, nil) - XCTAssertEqual(RelativePath("a").extension, nil) - XCTAssertEqual(RelativePath("a.").suffix, nil) - XCTAssertEqual(RelativePath("a.").extension, nil) - XCTAssertEqual(RelativePath(".a").suffix, nil) - XCTAssertEqual(RelativePath(".a").extension, nil) - XCTAssertEqual(RelativePath("").suffix, nil) - XCTAssertEqual(RelativePath("").extension, nil) - XCTAssertEqual(RelativePath(".").suffix, nil) - XCTAssertEqual(RelativePath(".").extension, nil) - XCTAssertEqual(RelativePath("..").suffix, nil) - XCTAssertEqual(RelativePath("..").extension, nil) - XCTAssertEqual(RelativePath("a.foo").suffix, ".foo") - XCTAssertEqual(RelativePath("a.foo").extension, "foo") - XCTAssertEqual(RelativePath(".a.foo").suffix, ".foo") - XCTAssertEqual(RelativePath(".a.foo").extension, "foo") - XCTAssertEqual(RelativePath(".a.foo.bar").suffix, ".bar") - XCTAssertEqual(RelativePath(".a.foo.bar").extension, "bar") - XCTAssertEqual(RelativePath("a.foo.bar").suffix, ".bar") - XCTAssertEqual(RelativePath("a.foo.bar").extension, "bar") - XCTAssertEqual(RelativePath(".a.foo.bar.baz").suffix, ".baz") - XCTAssertEqual(RelativePath(".a.foo.bar.baz").extension, "baz") - } + #expect {try AbsolutePath(validating: "~/a/b/d")} throws: { error in + ("\(error)" == "invalid absolute path '~/a/b/d'; absolute path must begin with '/'") + } - func testParentDirectory() { - XCTAssertEqual(AbsolutePath("/").parentDirectory, AbsolutePath("/")) - XCTAssertEqual(AbsolutePath("/").parentDirectory.parentDirectory, AbsolutePath("/")) - XCTAssertEqual(AbsolutePath("/bar").parentDirectory, AbsolutePath("/")) - XCTAssertEqual(AbsolutePath("/bar/../foo/..//").parentDirectory.parentDirectory, AbsolutePath("/")) - XCTAssertEqual(AbsolutePath("/bar/../foo/..//yabba/a/b").parentDirectory.parentDirectory, AbsolutePath("/yabba")) - } + #expect {try AbsolutePath(validating: "a/b/d") } throws: { error in + ("\(error)" == "invalid absolute path 'a/b/d'") + } + } - @available(*, deprecated) - func testConcatenation() { - XCTAssertEqual(AbsolutePath(AbsolutePath("/"), RelativePath("")).pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath(AbsolutePath("/"), RelativePath(".")).pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath(AbsolutePath("/"), RelativePath("..")).pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath(AbsolutePath("/"), RelativePath("bar")).pathString, windows ? #"\bar"# : "/bar") - XCTAssertEqual(AbsolutePath(AbsolutePath("/foo/bar"), RelativePath("..")).pathString, windows ? #"\foo"# : "/foo") - XCTAssertEqual(AbsolutePath(AbsolutePath("/bar"), RelativePath("../foo")).pathString, windows ? #"\foo"# : "/foo") - XCTAssertEqual(AbsolutePath(AbsolutePath("/bar"), RelativePath("../foo/..//")).pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath(AbsolutePath("/bar/../foo/..//yabba/"), RelativePath("a/b")).pathString, windows ? #"\yabba\a\b"# : "/yabba/a/b") - - XCTAssertEqual(AbsolutePath("/").appending(RelativePath("")).pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/").appending(RelativePath(".")).pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/").appending(RelativePath("..")).pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/").appending(RelativePath("bar")).pathString, windows ? #"\bar"# : "/bar") - XCTAssertEqual(AbsolutePath("/foo/bar").appending(RelativePath("..")).pathString, windows ? #"\foo"# : "/foo") - XCTAssertEqual(AbsolutePath("/bar").appending(RelativePath("../foo")).pathString, windows ? #"\foo"# : "/foo") - XCTAssertEqual(AbsolutePath("/bar").appending(RelativePath("../foo/..//")).pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/bar/../foo/..//yabba/").appending(RelativePath("a/b")).pathString, windows ? #"\yabba\a\b"# : "/yabba/a/b") - - XCTAssertEqual(AbsolutePath("/").appending(component: "a").pathString, windows ? #"\a"# : "/a") - XCTAssertEqual(AbsolutePath("/a").appending(component: "b").pathString, windows ? #"\a\b"# : "/a/b") - XCTAssertEqual(AbsolutePath("/").appending(components: "a", "b").pathString, windows ? #"\a\b"# : "/a/b") - XCTAssertEqual(AbsolutePath("/a").appending(components: "b", "c").pathString, windows ? #"\a\b\c"# : "/a/b/c") - - XCTAssertEqual(AbsolutePath("/a/b/c").appending(components: "", "c").pathString, windows ? #"\a\b\c\c"# : "/a/b/c/c") - XCTAssertEqual(AbsolutePath("/a/b/c").appending(components: "").pathString, windows ? #"\a\b\c"# : "/a/b/c") - XCTAssertEqual(AbsolutePath("/a/b/c").appending(components: ".").pathString, windows ? #"\a\b\c"# : "/a/b/c") - XCTAssertEqual(AbsolutePath("/a/b/c").appending(components: "..").pathString, windows ? #"\a\b"# : "/a/b") - XCTAssertEqual(AbsolutePath("/a/b/c").appending(components: "..", "d").pathString, windows ? #"\a\b\d"# : "/a/b/d") - XCTAssertEqual(AbsolutePath("/").appending(components: "..").pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/").appending(components: ".").pathString, windows ? #"\"# : "/") - XCTAssertEqual(AbsolutePath("/").appending(components: "..", "a").pathString, windows ? #"\a"# : "/a") - - XCTAssertEqual(RelativePath("hello").appending(components: "a", "b", "c", "..").pathString, windows ? #"hello\a\b"# : "hello/a/b") - XCTAssertEqual(RelativePath("hello").appending(RelativePath("a/b/../c/d")).pathString, windows ? #"hello\a\c\d"# : "hello/a/c/d") } - func testPathComponents() { - XCTAssertEqual(AbsolutePath("/").components, ["/"]) - XCTAssertEqual(AbsolutePath("/.").components, ["/"]) - XCTAssertEqual(AbsolutePath("/..").components, ["/"]) - XCTAssertEqual(AbsolutePath("/bar").components, ["/", "bar"]) - XCTAssertEqual(AbsolutePath("/foo/bar/..").components, ["/", "foo"]) - XCTAssertEqual(AbsolutePath("/bar/../foo").components, ["/", "foo"]) - XCTAssertEqual(AbsolutePath("/bar/../foo/..//").components, ["/"]) - XCTAssertEqual(AbsolutePath("/bar/../foo/..//yabba/a/b/").components, ["/", "yabba", "a", "b"]) - - XCTAssertEqual(RelativePath("").components, ["."]) - XCTAssertEqual(RelativePath(".").components, ["."]) - XCTAssertEqual(RelativePath("..").components, [".."]) - XCTAssertEqual(RelativePath("bar").components, ["bar"]) - XCTAssertEqual(RelativePath("foo/bar/..").components, ["foo"]) - XCTAssertEqual(RelativePath("bar/../foo").components, ["foo"]) - XCTAssertEqual(RelativePath("bar/../foo/..//").components, ["."]) - XCTAssertEqual(RelativePath("bar/../foo/..//yabba/a/b/").components, ["yabba", "a", "b"]) - XCTAssertEqual(RelativePath("../..").components, ["..", ".."]) - XCTAssertEqual(RelativePath(".././/..").components, ["..", ".."]) - XCTAssertEqual(RelativePath("../a").components, ["..", "a"]) - XCTAssertEqual(RelativePath("../a/..").components, [".."]) - XCTAssertEqual(RelativePath("a/..").components, ["."]) - XCTAssertEqual(RelativePath("./..").components, [".."]) - XCTAssertEqual(RelativePath("a/../////../////./////").components, [".."]) - XCTAssertEqual(RelativePath("abc").components, ["abc"]) - } + struct RelativePathTests { + @Test( + arguments: [ + // basics + (path: ".", expected: "."), + (path: "a", expected: "a"), + (path: "a/b/c", expected: (windows ? #"a\b\c"# : "a/b/c")), + (path: "~", expected: "~"), // `~` is not special + // string literal initialization + (path: ".", expected: "."), + (path: "~", expected: "~"), // `~` is not special + // repeated path seperators + (path: "ab//cd//ef", expected: (windows ? #"ab\cd\ef"# : "ab/cd/ef")), + (path: "ab///cd//ef", expected: (windows ? #"ab\cd\ef"# : "ab/cd/ef")), + // trailing path seperators + (path: "ab/cd/ef/", expected: (windows ? #"ab\cd\ef"# : "ab/cd/ef")), + (path: "ab/cd/ef//", expected: (windows ? #"ab\cd\ef"# : "ab/cd/ef")), + // dot path components + (path: "ab/./cd/././ef", expected: "ab/cd/ef"), + (path: "ab/./cd/ef/.", expected: "ab/cd/ef"), + // dot dot path components + (path: "..", expected: ".."), + (path: "../..", expected: "../.."), + (path: ".././..", expected: "../.."), + (path: "../abc/..", expected: ".."), + (path: "../abc/.././", expected: ".."), + (path: "abc/..", expected: "."), + // combinations and edge cases + (path: "", expected: "."), + (path: ".", expected: "."), + (path: "./abc", expected: "abc"), + (path: "./abc/", expected: "abc"), + (path: "./abc/../bar", expected: "bar"), + (path: "foo/../bar", expected: "bar"), + (path: "foo///..///bar///baz", expected: "bar/baz"), + (path: "foo/../bar/./", expected: "bar"), + (path: "../abc/def/", expected: "../abc/def"), + (path: "././././.", expected: "."), + (path: "./././../.", expected: ".."), + (path: "./", expected: "."), + (path: ".//", expected: "."), + (path: "./.", expected: "."), + (path: "././", expected: "."), + (path: "../", expected: ".."), + (path: "../.", expected: ".."), + (path: "./..", expected: ".."), + (path: "./../.", expected: ".."), + (path: "./////../////./////", expected: ".."), + (path: "../a", expected: (windows ? #"..\a"# : "../a")), + (path: "../a/..", expected: ".."), + (path: "a/..", expected: "."), + (path: "a/../////../////./////", expected: "..") + + ] + ) + func pathStringIsSetCorrectly(path: String, expected: String) { + let actual = RelativePath(path).pathString + + #expect(actual == expected, "Actual is not as expected") + } - func testRelativePathFromAbsolutePaths() { - XCTAssertEqual(AbsolutePath("/").relative(to: AbsolutePath("/")), RelativePath(".")); - XCTAssertEqual(AbsolutePath("/a/b/c/d").relative(to: AbsolutePath("/")), RelativePath("a/b/c/d")); - XCTAssertEqual(AbsolutePath("/").relative(to: AbsolutePath("/a/b/c")), RelativePath("../../..")); - XCTAssertEqual(AbsolutePath("/a/b/c/d").relative(to: AbsolutePath("/a/b")), RelativePath("c/d")); - XCTAssertEqual(AbsolutePath("/a/b/c/d").relative(to: AbsolutePath("/a/b/c")), RelativePath("d")); - XCTAssertEqual(AbsolutePath("/a/b/c/d").relative(to: AbsolutePath("/a/c/d")), RelativePath("../../b/c/d")); - XCTAssertEqual(AbsolutePath("/a/b/c/d").relative(to: AbsolutePath("/b/c/d")), RelativePath("../../../a/b/c/d")); - } + @Test( + arguments: [ + (path: "ab/c//d/", expected: (windows ? #"ab\c"# : "ab/c")), + (path: "../a", expected: ".."), + (path: "../a/..", expected: "."), + (path: "a/..", expected: "."), + (path: "./..", expected: "."), + (path: "a/../////../////./////", expected: "."), + (path: "abc", expected: "."), + (path: "", expected: "."), + (path: ".", expected: ".") + ] + ) + func dirnameAttributeReturnsExpectedValue(path: String, expected: String) { + let actual = RelativePath(path).dirname + + #expect(actual == expected, "Actual is not as expected") + } - func testComparison() { - XCTAssertTrue(AbsolutePath("/") <= AbsolutePath("/")); - XCTAssertTrue(AbsolutePath("/abc") < AbsolutePath("/def")); - XCTAssertTrue(AbsolutePath("/2") <= AbsolutePath("/2.1")); - XCTAssertTrue(AbsolutePath("/3.1") > AbsolutePath("/2")); - XCTAssertTrue(AbsolutePath("/2") >= AbsolutePath("/2")); - XCTAssertTrue(AbsolutePath("/2.1") >= AbsolutePath("/2")); - } + @Test( + arguments: [ + (path: "../..", expected:".."), + (path: "../a", expected:"a"), + (path: "../a/..", expected:".."), + (path: "a/..", expected:"."), + (path: "./..", expected:".."), + (path: "a/../////../////./////", expected:".."), + (path: "abc", expected:"abc"), + (path: "", expected:"."), + (path: ".", expected:".") + ] + ) + func basenameAttributeReturnsExpectedValue(path: String, expected: String) { + let actual = RelativePath(path).basename + + #expect(actual == expected, "Actual is not as expected") + } - func testAncestry() { - XCTAssertTrue(AbsolutePath("/a/b/c/d/e/f").isDescendantOfOrEqual(to: AbsolutePath("/a/b/c/d"))) - XCTAssertTrue(AbsolutePath("/a/b/c/d/e/f.swift").isDescendantOfOrEqual(to: AbsolutePath("/a/b/c"))) - XCTAssertTrue(AbsolutePath("/").isDescendantOfOrEqual(to: AbsolutePath("/"))) - XCTAssertTrue(AbsolutePath("/foo/bar").isDescendantOfOrEqual(to: AbsolutePath("/"))) - XCTAssertFalse(AbsolutePath("/foo/bar").isDescendantOfOrEqual(to: AbsolutePath("/foo/bar/baz"))) - XCTAssertFalse(AbsolutePath("/foo/bar").isDescendantOfOrEqual(to: AbsolutePath("/bar"))) - - XCTAssertFalse(AbsolutePath("/foo/bar").isDescendant(of: AbsolutePath("/foo/bar"))) - XCTAssertTrue(AbsolutePath("/foo/bar").isDescendant(of: AbsolutePath("/foo"))) - - XCTAssertTrue(AbsolutePath("/a/b/c/d").isAncestorOfOrEqual(to: AbsolutePath("/a/b/c/d/e/f"))) - XCTAssertTrue(AbsolutePath("/a/b/c").isAncestorOfOrEqual(to: AbsolutePath("/a/b/c/d/e/f.swift"))) - XCTAssertTrue(AbsolutePath("/").isAncestorOfOrEqual(to: AbsolutePath("/"))) - XCTAssertTrue(AbsolutePath("/").isAncestorOfOrEqual(to: AbsolutePath("/foo/bar"))) - XCTAssertFalse(AbsolutePath("/foo/bar/baz").isAncestorOfOrEqual(to: AbsolutePath("/foo/bar"))) - XCTAssertFalse(AbsolutePath("/bar").isAncestorOfOrEqual(to: AbsolutePath("/foo/bar"))) - - XCTAssertFalse(AbsolutePath("/foo/bar").isAncestor(of: AbsolutePath("/foo/bar"))) - XCTAssertTrue(AbsolutePath("/foo").isAncestor(of: AbsolutePath("/foo/bar"))) - } + @Test( + arguments: [ + // path without extension + (path: "../..", expected: ".."), + (path: "../a", expected: "a"), + (path: "../a/..", expected: ".."), + (path: "a/..", expected: "."), + (path: "./..", expected: ".."), + (path: "a/../////../////./////", expected: ".."), + (path: "abc", expected: "abc"), + (path: "", expected: "."), + (path: ".", expected: "."), + // path with extension + (path: "../a.bc", expected: "a"), + (path: "abc.swift", expected: "abc"), + (path: "../a.b.c", expected: "a.b"), + (path: "abc.xyz.123", expected: "abc.xyz") + ] + ) + func basenameWithoutExtAttributeReturnsExpectedValue(path: String, expected: String) { + let actual = RelativePath(path).basenameWithoutExt + + #expect(actual == expected, "Actual is not as expected") + } - func testAbsolutePathValidation() { - XCTAssertNoThrow(try AbsolutePath(validating: "/a/b/c/d")) + @Test( + arguments:[ + (path: "a", expectedSuffix: nil, expectedExtension: nil), + (path: "a.", expectedSuffix: nil, expectedExtension: nil), + (path: ".a", expectedSuffix: nil, expectedExtension: nil), + (path: "", expectedSuffix: nil, expectedExtension: nil), + (path: ".", expectedSuffix: nil, expectedExtension: nil), + (path: "..", expectedSuffix: nil, expectedExtension: nil), + (path: "a.foo", expectedSuffix: ".foo", expectedExtension: "foo"), + (path: ".a.foo", expectedSuffix: ".foo", expectedExtension: "foo"), + (path: "a.foo.bar", expectedSuffix: ".bar", expectedExtension: "bar"), + (path: ".a.foo.bar", expectedSuffix: ".bar", expectedExtension: "bar"), + (path: ".a.foo.bar.baz", expectedSuffix: ".baz", expectedExtension: "baz"), + ] + ) + func suffixAndExensionReturnExpectedValue(path: String, expectedSuffix: String?, expectedExtension: String?) { + let actual = RelativePath(path) + + #expect(actual.suffix == expectedSuffix, "Actual suffix not as expected") + #expect(actual.extension == expectedExtension, "Actual extension not as expected") + } - XCTAssertThrowsError(try AbsolutePath(validating: "~/a/b/d")) { error in - XCTAssertEqual("\(error)", "invalid absolute path '~/a/b/d'; absolute path must begin with '/'") + @Test( + arguments: [ + (path:"", expected: ["."]), + (path:".", expected: ["."]), + (path:"..", expected: [".."]), + (path:"bar", expected: ["bar"]), + (path:"foo/bar/..", expected: ["foo"]), + (path:"bar/../foo", expected: ["foo"]), + (path:"bar/../foo/..//", expected: ["."]), + (path:"bar/../foo/..//yabba/a/b/", expected: ["yabba", "a", "b"]), + (path:"../..", expected: ["..", ".."]), + (path:".././/..", expected: ["..", ".."]), + (path:"../a", expected: ["..", "a"]), + (path:"../a/..", expected: [".."]), + (path:"a/..", expected: ["."]), + (path:"./..", expected: [".."]), + (path:"a/../////../////./////", expected: [".."]), + (path:"abc", expected: ["abc"]) + ] + ) + func componentsAttributeReturnsExpectedValue(path: String, expected: [String]) { + let actual = RelativePath(path).components + + #expect(actual == expected) } - XCTAssertThrowsError(try AbsolutePath(validating: "a/b/d")) { error in - XCTAssertEqual("\(error)", "invalid absolute path 'a/b/d'") + @Test + func relativePathValidation() { + #expect(throws: Never.self) { + try RelativePath(validating: "a/b/c/d") + } + + #expect {try RelativePath(validating: "/a/b/d")} throws: { error in + ("\(error)" == "invalid relative path '/a/b/d'; relative path should not begin with '/'") + } + } } - func testRelativePathValidation() { - XCTAssertNoThrow(try RelativePath(validating: "a/b/c/d")) - XCTAssertThrowsError(try RelativePath(validating: "/a/b/d")) { error in - XCTAssertEqual("\(error)", "invalid relative path '/a/b/d'; relative path should not begin with '/'") - //XCTAssertEqual("\(error)", "invalid relative path '/a/b/d'; relative path should not begin with '/' or '~'") - } + @Test + func stringInitialization() throws { + let abs1 = AbsolutePath("/") + let abs2 = AbsolutePath(abs1, ".") + #expect(abs1 == abs2) + let rel3 = "." + let abs3 = try AbsolutePath(abs2, validating: rel3) + #expect(abs2 == abs3) + let base = AbsolutePath("/base/path") + let abs4 = AbsolutePath("/a/b/c", relativeTo: base) + #expect(abs4 == AbsolutePath("/a/b/c")) + let abs5 = AbsolutePath("./a/b/c", relativeTo: base) + #expect(abs5 == AbsolutePath("/base/path/a/b/c")) + let abs6 = AbsolutePath("~/bla", relativeTo: base) // `~` isn't special + #expect(abs6 == AbsolutePath("/base/path/~/bla")) + } + - /*XCTAssertThrowsError(try RelativePath(validating: "~/a/b/d")) { error in - XCTAssertEqual("\(error)", "invalid relative path '~/a/b/d'; relative path should not begin with '/' or '~'") - }*/ + @Test + @available(*, deprecated) + func concatenation() { + #expect(AbsolutePath(AbsolutePath("/"), RelativePath("")).pathString == (windows ? #"\"# : "/")) + #expect(AbsolutePath(AbsolutePath("/"), RelativePath(".")).pathString == (windows ? #"\"# : "/")) + #expect(AbsolutePath(AbsolutePath("/"), RelativePath("..")).pathString == (windows ? #"\"# : "/")) + #expect(AbsolutePath(AbsolutePath("/"), RelativePath("bar")).pathString == (windows ? #"\bar"# : "/bar")) + #expect(AbsolutePath(AbsolutePath("/foo/bar"), RelativePath("..")).pathString == (windows ? #"\foo"# : "/foo")) + #expect(AbsolutePath(AbsolutePath("/bar"), RelativePath("../foo")).pathString == (windows ? #"\foo"# : "/foo")) + #expect(AbsolutePath(AbsolutePath("/bar"), RelativePath("../foo/..//")).pathString == (windows ? #"\"# : "/")) + #expect(AbsolutePath(AbsolutePath("/bar/../foo/..//yabba/"), RelativePath("a/b")).pathString == (windows ? #"\yabba\a\b"# : "/yabba/a/b")) + + #expect(AbsolutePath("/").appending(RelativePath("")).pathString == (windows ? #"\"# : "/")) + #expect(AbsolutePath("/").appending(RelativePath(".")).pathString == (windows ? #"\"# : "/")) + #expect(AbsolutePath("/").appending(RelativePath("..")).pathString == (windows ? #"\"# : "/")) + #expect(AbsolutePath("/").appending(RelativePath("bar")).pathString == (windows ? #"\bar"# : "/bar")) + #expect(AbsolutePath("/foo/bar").appending(RelativePath("..")).pathString == (windows ? #"\foo"# : "/foo")) + #expect(AbsolutePath("/bar").appending(RelativePath("../foo")).pathString == (windows ? #"\foo"# : "/foo")) + #expect(AbsolutePath("/bar").appending(RelativePath("../foo/..//")).pathString == (windows ? #"\"# : "/")) + #expect(AbsolutePath("/bar/../foo/..//yabba/").appending(RelativePath("a/b")).pathString == (windows ? #"\yabba\a\b"# : "/yabba/a/b")) + + #expect(AbsolutePath("/").appending(component: "a").pathString == (windows ? #"\a"# : "/a")) + #expect(AbsolutePath("/a").appending(component: "b").pathString == (windows ? #"\a\b"# : "/a/b")) + #expect(AbsolutePath("/").appending(components: "a", "b").pathString == (windows ? #"\a\b"# : "/a/b")) + #expect(AbsolutePath("/a").appending(components: "b", "c").pathString == (windows ? #"\a\b\c"# : "/a/b/c")) + + #expect(AbsolutePath("/a/b/c").appending(components: "", "c").pathString == (windows ? #"\a\b\c\c"# : "/a/b/c/c")) + #expect(AbsolutePath("/a/b/c").appending(components: "").pathString == (windows ? #"\a\b\c"# : "/a/b/c")) + #expect(AbsolutePath("/a/b/c").appending(components: ".").pathString == (windows ? #"\a\b\c"# : "/a/b/c")) + #expect(AbsolutePath("/a/b/c").appending(components: "..").pathString == (windows ? #"\a\b"# : "/a/b")) + #expect(AbsolutePath("/a/b/c").appending(components: "..", "d").pathString == (windows ? #"\a\b\d"# : "/a/b/d")) + #expect(AbsolutePath("/").appending(components: "..").pathString == (windows ? #"\"# : "/")) + #expect(AbsolutePath("/").appending(components: ".").pathString == (windows ? #"\"# : "/")) + #expect(AbsolutePath("/").appending(components: "..", "a").pathString == (windows ? #"\a"# : "/a")) + + #expect(RelativePath("hello").appending(components: "a", "b", "c", "..").pathString == (windows ? #"hello\a\b"# : "hello/a/b")) + #expect(RelativePath("hello").appending(RelativePath("a/b/../c/d")).pathString == (windows ? #"hello\a\c\d"# : "hello/a/c/d")) } - func testCodable() throws { - struct Foo: Codable, Equatable { + @Test + func codable() throws { + struct AbsolutePathCodable: Codable, Equatable { var path: AbsolutePath } - struct Bar: Codable, Equatable { + struct RelativePathCodable: Codable, Equatable { var path: RelativePath } - struct Baz: Codable, Equatable { + struct StringCodable: Codable, Equatable { var path: String } do { - let foo = Foo(path: "/path/to/foo") + let foo = AbsolutePathCodable(path: "/path/to/foo") let data = try JSONEncoder().encode(foo) - let decodedFoo = try JSONDecoder().decode(Foo.self, from: data) - XCTAssertEqual(foo, decodedFoo) + let decodedFoo = try JSONDecoder().decode(AbsolutePathCodable.self, from: data) + #expect(foo == decodedFoo) } do { - let foo = Foo(path: "/path/to/../to/foo") + let foo = AbsolutePathCodable(path: "/path/to/../to/foo") let data = try JSONEncoder().encode(foo) - let decodedFoo = try JSONDecoder().decode(Foo.self, from: data) - XCTAssertEqual(foo, decodedFoo) - XCTAssertEqual(foo.path.pathString, windows ? #"\path\to\foo"# : "/path/to/foo") - XCTAssertEqual(decodedFoo.path.pathString, windows ? #"\path\to\foo"# : "/path/to/foo") + let decodedFoo = try JSONDecoder().decode(AbsolutePathCodable.self, from: data) + #expect(foo == decodedFoo) + #expect(foo.path.pathString == (windows ? #"\path\to\foo"# : "/path/to/foo")) + #expect(decodedFoo.path.pathString == (windows ? #"\path\to\foo"# : "/path/to/foo")) } do { - let bar = Bar(path: "path/to/bar") + let bar = RelativePathCodable(path: "path/to/bar") let data = try JSONEncoder().encode(bar) - let decodedBar = try JSONDecoder().decode(Bar.self, from: data) - XCTAssertEqual(bar, decodedBar) + let decodedBar = try JSONDecoder().decode(RelativePathCodable.self, from: data) + #expect(bar == decodedBar) } do { - let bar = Bar(path: "path/to/../to/bar") + let bar = RelativePathCodable(path: "path/to/../to/bar") let data = try JSONEncoder().encode(bar) - let decodedBar = try JSONDecoder().decode(Bar.self, from: data) - XCTAssertEqual(bar, decodedBar) - XCTAssertEqual(bar.path.pathString, "path/to/bar") - XCTAssertEqual(decodedBar.path.pathString, "path/to/bar") + let decodedBar = try JSONDecoder().decode(RelativePathCodable.self, from: data) + #expect(bar == decodedBar) + #expect(bar.path.pathString == "path/to/bar") + #expect(decodedBar.path.pathString == "path/to/bar") } do { - let data = try JSONEncoder().encode(Baz(path: "")) - XCTAssertThrowsError(try JSONDecoder().decode(Foo.self, from: data)) - XCTAssertNoThrow(try JSONDecoder().decode(Bar.self, from: data)) // empty string is a valid relative path + let data = try JSONEncoder().encode(StringCodable(path: "")) + #expect(throws: (any Error).self) { + try JSONDecoder().decode(AbsolutePathCodable.self, from: data) + } + #expect(throws: Never.self) { + try JSONDecoder().decode(RelativePathCodable.self, from: data) + } // empty string is a valid relative path } do { - let data = try JSONEncoder().encode(Baz(path: "foo")) - XCTAssertThrowsError(try JSONDecoder().decode(Foo.self, from: data)) + let data = try JSONEncoder().encode(StringCodable(path: "foo")) + #expect(throws: (any Error).self) { + try JSONDecoder().decode(AbsolutePathCodable.self, from: data) + } } do { - let data = try JSONEncoder().encode(Baz(path: "/foo")) - XCTAssertThrowsError(try JSONDecoder().decode(Bar.self, from: data)) + let data = try JSONEncoder().encode(StringCodable(path: "/foo")) + #expect(throws: (any Error).self) { + try JSONDecoder().decode(RelativePathCodable.self, from: data) + } } } diff --git a/Tests/BasicsTests/FileSystem/TemporaryFileTests.swift b/Tests/BasicsTests/FileSystem/TemporaryFileTests.swift index 5460faf901d..cb5b80206f8 100644 --- a/Tests/BasicsTests/FileSystem/TemporaryFileTests.swift +++ b/Tests/BasicsTests/FileSystem/TemporaryFileTests.swift @@ -9,83 +9,84 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation -import XCTest +import Testing import Basics -class TemporaryAsyncFileTests: XCTestCase { - func testBasicTemporaryDirectory() async throws { - // Test can create and remove temp directory. +struct TemporaryAsyncFileTests { + @Test + func basicTemporaryDirectory() async throws { let path1: AbsolutePath = try await withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in // Do some async task try await Task.sleep(nanoseconds: 1_000) - - XCTAssertTrue(localFileSystem.isDirectory(tempDirPath)) + + #expect(localFileSystem.isDirectory(tempDirPath)) return tempDirPath }.value - XCTAssertFalse(localFileSystem.isDirectory(path1)) - + #expect(!localFileSystem.isDirectory(path1)) + // Test temp directory is not removed when its not empty. let path2: AbsolutePath = try await withTemporaryDirectory { tempDirPath in - XCTAssertTrue(localFileSystem.isDirectory(tempDirPath)) + #expect(localFileSystem.isDirectory(tempDirPath)) // Create a file inside the temp directory. let filePath = tempDirPath.appending("somefile") // Do some async task try await Task.sleep(nanoseconds: 1_000) - + try localFileSystem.writeFileContents(filePath, bytes: []) return tempDirPath }.value - XCTAssertTrue(localFileSystem.isDirectory(path2)) + #expect(localFileSystem.isDirectory(path2)) // Cleanup. try localFileSystem.removeFileTree(path2) - XCTAssertFalse(localFileSystem.isDirectory(path2)) - + #expect(!localFileSystem.isDirectory(path2)) + // Test temp directory is removed when its not empty and removeTreeOnDeinit is enabled. let path3: AbsolutePath = try await withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in - XCTAssertTrue(localFileSystem.isDirectory(tempDirPath)) + #expect(localFileSystem.isDirectory(tempDirPath)) let filePath = tempDirPath.appending("somefile") // Do some async task try await Task.sleep(nanoseconds: 1_000) - + try localFileSystem.writeFileContents(filePath, bytes: []) return tempDirPath }.value - XCTAssertFalse(localFileSystem.isDirectory(path3)) + #expect(!localFileSystem.isDirectory(path3)) } - - func testCanCreateUniqueTempDirectories() async throws { + + @Test + func canCreateUniqueTempDirectories() async throws { let (pathOne, pathTwo): (AbsolutePath, AbsolutePath) = try await withTemporaryDirectory(removeTreeOnDeinit: true) { pathOne in let pathTwo: AbsolutePath = try await withTemporaryDirectory(removeTreeOnDeinit: true) { pathTwo in // Do some async task try await Task.sleep(nanoseconds: 1_000) - - XCTAssertTrue(localFileSystem.isDirectory(pathOne)) - XCTAssertTrue(localFileSystem.isDirectory(pathTwo)) + + #expect(localFileSystem.isDirectory(pathOne)) + #expect(localFileSystem.isDirectory(pathTwo)) // Their paths should be different. - XCTAssertTrue(pathOne != pathTwo) + #expect(pathOne != pathTwo) return pathTwo }.value return (pathOne, pathTwo) }.value - XCTAssertFalse(localFileSystem.isDirectory(pathOne)) - XCTAssertFalse(localFileSystem.isDirectory(pathTwo)) + #expect(!localFileSystem.isDirectory(pathOne)) + #expect(!localFileSystem.isDirectory(pathTwo)) } - - func testCancelOfTask() async throws { + + @Test + func cancelOfTask() async throws { let task: Task = try withTemporaryDirectory { path in - + try await Task.sleep(nanoseconds: 1_000_000_000) - XCTAssertTrue(Task.isCancelled) - XCTAssertFalse(localFileSystem.isDirectory(path)) + #expect(Task.isCancelled) + #expect(!localFileSystem.isDirectory(path)) return path } task.cancel() - do { - // The correct path is to throw an error here - let _ = try await task.value - XCTFail("The correct path here is to throw an error") - } catch {} + await #expect(throws: (any Error).self, "Error did not error when accessing `task.value`") { + try await task.value + } } } diff --git a/Tests/BasicsTests/FileSystem/VFSTests.swift b/Tests/BasicsTests/FileSystem/VFSTests.swift index 83b8513fa69..823a4ed76cd 100644 --- a/Tests/BasicsTests/FileSystem/VFSTests.swift +++ b/Tests/BasicsTests/FileSystem/VFSTests.swift @@ -9,10 +9,11 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation import Basics import func TSCBasic.withTemporaryFile -import XCTest +import Testing import struct TSCBasic.ByteString @@ -34,19 +35,19 @@ func testWithTemporaryDirectory( }.value } -class VFSTests: XCTestCase { - func testLocalBasics() throws { - // tiny PE binary from: https://archive.is/w01DO +struct VFSTests { + @Test + func localBasics() throws { let contents: [UInt8] = [ - 0x4d, 0x5a, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4c, 0x01, 0x01, 0x00, - 0x6a, 0x2a, 0x58, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x03, 0x01, 0x0b, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02 + 0x4d, 0x5a, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4c, 0x01, 0x01, 0x00, + 0x6a, 0x2a, 0x58, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x03, 0x01, 0x0b, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02 ] let fs = localFileSystem @@ -76,63 +77,65 @@ class VFSTests: XCTestCase { let vfs = try VirtualFileSystem(path: vfsPath.path, fs: fs) // exists() - XCTAssertTrue(vfs.exists(AbsolutePath("/"))) - XCTAssertFalse(vfs.exists(AbsolutePath("/does-not-exist"))) + #expect(vfs.exists(AbsolutePath("/"))) + #expect(!vfs.exists(AbsolutePath("/does-not-exist"))) // isFile() let filePath = AbsolutePath("/best") - XCTAssertTrue(vfs.exists(filePath)) - XCTAssertTrue(vfs.isFile(filePath)) - XCTAssertEqual(try vfs.getFileInfo(filePath).fileType, .typeRegular) - XCTAssertFalse(vfs.isDirectory(filePath)) - XCTAssertFalse(vfs.isFile(AbsolutePath("/does-not-exist"))) - XCTAssertFalse(vfs.isSymlink(AbsolutePath("/does-not-exist"))) - XCTAssertThrowsError(try vfs.getFileInfo(AbsolutePath("/does-not-exist"))) + #expect(vfs.exists(filePath)) + #expect(vfs.isFile(filePath)) + #expect(try vfs.getFileInfo(filePath).fileType == .typeRegular) + #expect(!vfs.isDirectory(filePath)) + #expect(!vfs.isFile(AbsolutePath("/does-not-exist"))) + #expect(!vfs.isSymlink(AbsolutePath("/does-not-exist"))) + #expect(throws: (any Error).self) { + try vfs.getFileInfo(AbsolutePath("/does-not-exist")) + } // isSymlink() let symPath = AbsolutePath("/hello") - XCTAssertTrue(vfs.isSymlink(symPath)) - XCTAssertTrue(vfs.isFile(symPath)) - XCTAssertEqual(try vfs.getFileInfo(symPath).fileType, .typeSymbolicLink) - XCTAssertFalse(vfs.isDirectory(symPath)) + #expect(vfs.isSymlink(symPath)) + #expect(vfs.isFile(symPath)) + #expect(try vfs.getFileInfo(symPath).fileType == .typeSymbolicLink) + #expect(!vfs.isDirectory(symPath)) // isExecutableFile let executablePath = AbsolutePath("/exec-foo") let executableSymPath = AbsolutePath("/exec-sym") - XCTAssertTrue(vfs.isExecutableFile(executablePath)) - XCTAssertTrue(vfs.isExecutableFile(executableSymPath)) - XCTAssertTrue(vfs.isSymlink(executableSymPath)) - XCTAssertFalse(vfs.isExecutableFile(symPath)) - XCTAssertFalse(vfs.isExecutableFile(filePath)) - XCTAssertFalse(vfs.isExecutableFile(AbsolutePath("/does-not-exist"))) - XCTAssertFalse(vfs.isExecutableFile(AbsolutePath("/"))) + #expect(vfs.isExecutableFile(executablePath)) + #expect(vfs.isExecutableFile(executableSymPath)) + #expect(vfs.isSymlink(executableSymPath)) + #expect(!vfs.isExecutableFile(symPath)) + #expect(!vfs.isExecutableFile(filePath)) + #expect(!vfs.isExecutableFile(AbsolutePath("/does-not-exist"))) + #expect(!vfs.isExecutableFile(AbsolutePath("/"))) // readFileContents let execFileContents = try vfs.readFileContents(executablePath) - XCTAssertEqual(execFileContents, ByteString(contents)) + #expect(execFileContents == ByteString(contents)) // isDirectory() - XCTAssertTrue(vfs.isDirectory(AbsolutePath("/"))) - XCTAssertFalse(vfs.isDirectory(AbsolutePath("/does-not-exist"))) + #expect(vfs.isDirectory(AbsolutePath("/"))) + #expect(!vfs.isDirectory(AbsolutePath("/does-not-exist"))) // getDirectoryContents() - do { - _ = try vfs.getDirectoryContents(AbsolutePath("/does-not-exist")) - XCTFail("Unexpected success") - } catch { - XCTAssertEqual(error.localizedDescription, "no such file or directory: \(AbsolutePath("/does-not-exist"))") + let dirContents = try vfs.getDirectoryContents(AbsolutePath("/")) + #expect(dirContents.sorted() == ["best", "dir", "exec-foo", "exec-sym", "hello"]) + #expect {try vfs.getDirectoryContents(AbsolutePath("/does-not-exist"))} throws: { error in + (error.localizedDescription == "no such file or directory: \(AbsolutePath("/does-not-exist"))") } + let thisDirectoryContents = try vfs.getDirectoryContents(AbsolutePath("/")) - XCTAssertFalse(thisDirectoryContents.contains(where: { $0 == "." })) - XCTAssertFalse(thisDirectoryContents.contains(where: { $0 == ".." })) - XCTAssertEqual(thisDirectoryContents.sorted(), ["best", "dir", "exec-foo", "exec-sym", "hello"]) + #expect(!thisDirectoryContents.contains(where: { $0 == "." })) + #expect(!thisDirectoryContents.contains(where: { $0 == ".." })) + #expect(thisDirectoryContents.sorted() == ["best", "dir", "exec-foo", "exec-sym", "hello"]) let contents = try vfs.getDirectoryContents(AbsolutePath("/dir")) - XCTAssertEqual(contents, ["file"]) + #expect(contents == ["file"]) let fileContents = try vfs.readFileContents(AbsolutePath("/dir/file")) - XCTAssertEqual(fileContents, "") + #expect(fileContents == "") } } } diff --git a/Tests/BasicsTests/Graph/AdjacencyMatrixTests.swift b/Tests/BasicsTests/Graph/AdjacencyMatrixTests.swift index c19178ef410..376bc09d3d9 100644 --- a/Tests/BasicsTests/Graph/AdjacencyMatrixTests.swift +++ b/Tests/BasicsTests/Graph/AdjacencyMatrixTests.swift @@ -9,30 +9,33 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation @testable import Basics -import XCTest +import Testing -final class AdjacencyMatrixTests: XCTestCase { - func testEmpty() { +struct AdjacencyMatrixTests { + @Test + func empty() { var matrix = AdjacencyMatrix(rows: 0, columns: 0) - XCTAssertEqual(matrix.bitCount, 0) + #expect(matrix.bitCount == 0) matrix = AdjacencyMatrix(rows: 0, columns: 42) - XCTAssertEqual(matrix.bitCount, 0) + #expect(matrix.bitCount == 0) matrix = AdjacencyMatrix(rows: 42, columns: 0) - XCTAssertEqual(matrix.bitCount, 0) + #expect(matrix.bitCount == 0) } - func testBits() { + @Test + func bits() { for count in 1..<10 { var matrix = AdjacencyMatrix(rows: count, columns: count) for row in 0..() + let maxAttempts = 5 + let errorCode = Int.random(in: 500 ..< 600) + let delay = SendableTimeInterval.milliseconds(100) + + let httpClient = HTTPClient { _, _ in + let count = await counter.value! + let expectedDelta = pow(2.0, Double(count - 1)) * delay.timeInterval()! + let delta = await lastCall.value.flatMap { Date().timeIntervalSince($0) } ?? 0 + XCTAssertEqual(delta, expectedDelta, accuracy: 0.1) + + await counter.increment() + await lastCall.resetDate() + return .init(statusCode: errorCode) + } + var request = HTTPClient.Request(method: .get, url: "http://test") + request.options.retryStrategy = .exponentialBackoff(maxAttempts: maxAttempts, baseDelay: delay) + + let response = try await httpClient.execute(request) + XCTAssertEqual(response.statusCode, errorCode) + let count = await counter.value + XCTAssertEqual(count, maxAttempts, "retries should match") + } +} + +struct HTTPClientTests { + @Test + func head() async throws { let url = URL("http://test") let requestHeaders = HTTPClientHeaders([HTTPClientHeaders.Item(name: UUID().uuidString, value: UUID().uuidString)]) let responseStatus = Int.random(in: 201 ..< 500) @@ -24,18 +59,19 @@ final class HTTPClientTests: XCTestCase { let responseBody: Data? = nil let httpClient = HTTPClient { request, _ in - XCTAssertEqual(request.url, url, "url should match") - XCTAssertEqual(request.method, .head, "method should match") + #expect(request.url == url, "url should match") + #expect(request.method == .head, "method should match") assertRequestHeaders(request.headers, expected: requestHeaders) return .init(statusCode: responseStatus, headers: responseHeaders, body: responseBody) } let response = try await httpClient.head(url, headers: requestHeaders) - XCTAssertEqual(response.statusCode, responseStatus, "statusCode should match") + #expect(response.statusCode == responseStatus, "statusCode should match") assertResponseHeaders(response.headers, expected: responseHeaders) - XCTAssertEqual(response.body, responseBody, "body should match") + #expect(response.body == responseBody, "body should match") } + @Test func testGet() async throws { let url = URL("http://test") let requestHeaders = HTTPClientHeaders([HTTPClientHeaders.Item(name: UUID().uuidString, value: UUID().uuidString)]) @@ -44,19 +80,20 @@ final class HTTPClientTests: XCTestCase { let responseBody = Data(UUID().uuidString.utf8) let httpClient = HTTPClient { request, _ in - XCTAssertEqual(request.url, url, "url should match") - XCTAssertEqual(request.method, .get, "method should match") + #expect(request.url == url, "url should match") + #expect(request.method == .get, "method should match") assertRequestHeaders(request.headers, expected: requestHeaders) return .init(statusCode: responseStatus, headers: responseHeaders, body: responseBody) } let response = try await httpClient.get(url, headers: requestHeaders) - XCTAssertEqual(response.statusCode, responseStatus, "statusCode should match") + #expect(response.statusCode == responseStatus, "statusCode should match") assertResponseHeaders(response.headers, expected: responseHeaders) - XCTAssertEqual(response.body, responseBody, "body should match") + #expect(response.body == responseBody, "body should match") } - func testPost() async throws { + @Test + func post() async throws { let url = URL("http://test") let requestHeaders = HTTPClientHeaders([HTTPClientHeaders.Item(name: UUID().uuidString, value: UUID().uuidString)]) let requestBody = Data(UUID().uuidString.utf8) @@ -65,20 +102,21 @@ final class HTTPClientTests: XCTestCase { let responseBody = Data(UUID().uuidString.utf8) let httpClient = HTTPClient { request, _ in - XCTAssertEqual(request.url, url, "url should match") - XCTAssertEqual(request.method, .post, "method should match") + #expect(request.url == url, "url should match") + #expect(request.method == .post, "method should match") assertRequestHeaders(request.headers, expected: requestHeaders) - XCTAssertEqual(request.body, requestBody, "body should match") + #expect(request.body == requestBody, "body should match") return .init(statusCode: responseStatus, headers: responseHeaders, body: responseBody) } let response = try await httpClient.post(url, body: requestBody, headers: requestHeaders) - XCTAssertEqual(response.statusCode, responseStatus, "statusCode should match") + #expect(response.statusCode == responseStatus, "statusCode should match") assertResponseHeaders(response.headers, expected: responseHeaders) - XCTAssertEqual(response.body, responseBody, "body should match") + #expect(response.body == responseBody, "body should match") } - func testPut() async throws { + @Test + func put() async throws { let url = URL("http://test") let requestHeaders = HTTPClientHeaders([HTTPClientHeaders.Item(name: UUID().uuidString, value: UUID().uuidString)]) let requestBody = Data(UUID().uuidString.utf8) @@ -87,20 +125,21 @@ final class HTTPClientTests: XCTestCase { let responseBody = Data(UUID().uuidString.utf8) let httpClient = HTTPClient { request, _ in - XCTAssertEqual(request.url, url, "url should match") - XCTAssertEqual(request.method, .put, "method should match") + #expect(request.url == url, "url should match") + #expect(request.method == .put, "method should match") assertRequestHeaders(request.headers, expected: requestHeaders) - XCTAssertEqual(request.body, requestBody, "body should match") + #expect(request.body == requestBody, "body should match") return .init(statusCode: responseStatus, headers: responseHeaders, body: responseBody) } let response = try await httpClient.put(url, body: requestBody, headers: requestHeaders) - XCTAssertEqual(response.statusCode, responseStatus, "statusCode should match") + #expect(response.statusCode == responseStatus, "statusCode should match") assertResponseHeaders(response.headers, expected: responseHeaders) - XCTAssertEqual(response.body, responseBody, "body should match") + #expect(response.body == responseBody, "body should match") } - func testDelete() async throws { + @Test + func delete() async throws { let url = URL("http://test") let requestHeaders = HTTPClientHeaders([HTTPClientHeaders.Item(name: UUID().uuidString, value: UUID().uuidString)]) let responseStatus = Int.random(in: 201 ..< 500) @@ -108,19 +147,20 @@ final class HTTPClientTests: XCTestCase { let responseBody = Data(UUID().uuidString.utf8) let httpClient = HTTPClient { request, _ in - XCTAssertEqual(request.url, url, "url should match") - XCTAssertEqual(request.method, .delete, "method should match") + #expect(request.url == url, "url should match") + #expect(request.method == .delete, "method should match") assertRequestHeaders(request.headers, expected: requestHeaders) return .init(statusCode: responseStatus, headers: responseHeaders, body: responseBody) } let response = try await httpClient.delete(url, headers: requestHeaders) - XCTAssertEqual(response.statusCode, responseStatus, "statusCode should match") + #expect(response.statusCode == responseStatus, "statusCode should match") assertResponseHeaders(response.headers, expected: responseHeaders) - XCTAssertEqual(response.body, responseBody, "body should match") + #expect(response.body == responseBody, "body should match") } - func testExtraHeaders() async throws { + @Test + func extraHeaders() async throws { let url = URL("http://test") let globalHeaders = HTTPClientHeaders([HTTPClientHeaders.Item(name: UUID().uuidString, value: UUID().uuidString)]) let requestHeaders = HTTPClientHeaders([HTTPClientHeaders.Item(name: UUID().uuidString, value: UUID().uuidString)]) @@ -136,15 +176,16 @@ final class HTTPClientTests: XCTestCase { request.options.addUserAgent = true let response = try await httpClient.execute(request) - XCTAssertEqual(response.statusCode, 200, "statusCode should match") + #expect(response.statusCode == 200, "statusCode should match") } - func testUserAgent() async throws { + @Test + func userAgent() async throws { let url = URL("http://test") let requestHeaders = HTTPClientHeaders([HTTPClientHeaders.Item(name: UUID().uuidString, value: UUID().uuidString)]) let httpClient = HTTPClient { request, _ in - XCTAssertTrue(request.headers.contains("User-Agent"), "expecting User-Agent") + #expect(request.headers.contains("User-Agent"), "expecting User-Agent") assertRequestHeaders(request.headers, expected: requestHeaders) return .init(statusCode: 200) } @@ -152,15 +193,16 @@ final class HTTPClientTests: XCTestCase { request.options.addUserAgent = true let response = try await httpClient.execute(request) - XCTAssertEqual(response.statusCode, 200, "statusCode should match") + #expect(response.statusCode == 200, "statusCode should match") } - func testNoUserAgent() async throws { + @Test + func noUserAgent() async throws { let url = URL("http://test") let requestHeaders = HTTPClientHeaders([HTTPClientHeaders.Item(name: UUID().uuidString, value: UUID().uuidString)]) let httpClient = HTTPClient { request, _ in - XCTAssertFalse(request.headers.contains("User-Agent"), "expecting User-Agent") + #expect(!request.headers.contains("User-Agent"), "expecting User-Agent") assertRequestHeaders(request.headers, expected: requestHeaders) return .init(statusCode: 200) } @@ -169,18 +211,19 @@ final class HTTPClientTests: XCTestCase { request.options.addUserAgent = false let response = try await httpClient.execute(request) - XCTAssertEqual(response.statusCode, 200, "statusCode should match") + #expect(response.statusCode == 200, "statusCode should match") } - func testAuthorization() async throws { + @Test + func authorization() async throws { let url = URL("http://test") do { let authorization = UUID().uuidString let httpClient = HTTPClient { request, _ in - XCTAssertTrue(request.headers.contains("Authorization"), "expecting Authorization") - XCTAssertEqual(request.headers.get("Authorization").first, authorization, "expecting Authorization to match") + #expect(request.headers.contains("Authorization"), "expecting Authorization") + #expect(request.headers.get("Authorization").first == authorization, "expecting Authorization to match") return .init(statusCode: 200) } @@ -190,12 +233,12 @@ final class HTTPClientTests: XCTestCase { } let response = try await httpClient.execute(request) - XCTAssertEqual(response.statusCode, 200, "statusCode should match") + #expect(response.statusCode == 200, "statusCode should match") } do { let httpClient = HTTPClient { request, _ in - XCTAssertFalse(request.headers.contains("Authorization"), "not expecting Authorization") + #expect(!request.headers.contains("Authorization"), "not expecting Authorization") return .init(statusCode: 200) } @@ -203,11 +246,12 @@ final class HTTPClientTests: XCTestCase { request.options.authorizationProvider = { _ in "" } let response = try await httpClient.execute(request) - XCTAssertEqual(response.statusCode, 200, "statusCode should match") + #expect(response.statusCode == 200, "statusCode should match") } } - func testValidResponseCodes() async throws { + @Test + func validResponseCodes() async throws { let statusCode = Int.random(in: 201 ..< 500) let httpClient = HTTPClient { _, _ in @@ -217,41 +261,13 @@ final class HTTPClientTests: XCTestCase { var request = HTTPClient.Request(method: .get, url: "http://test") request.options.validResponseCodes = [200] - do { - let response = try await httpClient.execute(request) - XCTFail("unexpected success \(response)") - } catch { - XCTAssertEqual(error as? HTTPClientError, .badResponseStatusCode(statusCode), "expected error to match") + await #expect(throws: HTTPClientError.badResponseStatusCode(statusCode)) { + let _ = try await httpClient.execute(request) } } - func testExponentialBackoff() async throws { - let counter = SendableBox(0) - let lastCall = SendableBox() - let maxAttempts = 5 - let errorCode = Int.random(in: 500 ..< 600) - let delay = SendableTimeInterval.milliseconds(100) - - let httpClient = HTTPClient { _, _ in - let count = await counter.value! - let expectedDelta = pow(2.0, Double(count - 1)) * delay.timeInterval()! - let delta = await lastCall.value.flatMap { Date().timeIntervalSince($0) } ?? 0 - XCTAssertEqual(delta, expectedDelta, accuracy: 0.1) - - await counter.increment() - await lastCall.resetDate() - return .init(statusCode: errorCode) - } - var request = HTTPClient.Request(method: .get, url: "http://test") - request.options.retryStrategy = .exponentialBackoff(maxAttempts: maxAttempts, baseDelay: delay) - - let response = try await httpClient.execute(request) - XCTAssertEqual(response.statusCode, errorCode) - let count = await counter.value - XCTAssertEqual(count, maxAttempts, "retries should match") - } - - func testHostCircuitBreaker() async throws { + @Test + func hostCircuitBreaker() async throws { let maxErrors = 5 let errorCode = Int.random(in: 500 ..< 600) let age = SendableTimeInterval.seconds(5) @@ -268,31 +284,29 @@ final class HTTPClientTests: XCTestCase { for index in (0 ..< maxErrors) { let response = try await httpClient.get(URL("\(host)/\(index)/foo")) await counter.increment() - XCTAssertEqual(response.statusCode, errorCode) + #expect(response.statusCode == errorCode) } let count = await counter.value - XCTAssertEqual(count, maxErrors, "expected results count to match") + #expect(count == maxErrors, "expected results count to match") } // these should all circuit break let counter = SendableBox(0) let total = Int.random(in: 10 ..< 20) for index in (0 ..< total) { - do { - let response = try await httpClient.get(URL("\(host)/\(index)/foo")) - XCTFail("unexpected success \(response)") - } catch { - XCTAssertEqual(error as? HTTPClientError, .circuitBreakerTriggered, "expected error to match") + await #expect(throws: HTTPClientError.circuitBreakerTriggered) { + let _ = try await httpClient.get(URL("\(host)/\(index)/foo")) } await counter.increment() } let count = await counter.value - XCTAssertEqual(count, total, "expected results count to match") + #expect(count == total, "expected results count to match") } - func testHostCircuitBreakerAging() async throws { + @Test + func hostCircuitBreakerAging() async throws { let maxErrors = 5 let errorCode = Int.random(in: 500 ..< 600) let ageInMilliseconds = 100 @@ -320,10 +334,10 @@ final class HTTPClientTests: XCTestCase { for index in (0 ..< maxErrors) { let response = try await httpClient.get(URL("\(host)/\(index)/error")) await counter.increment() - XCTAssertEqual(response.statusCode, errorCode) + #expect(response.statusCode == errorCode) } let count = await counter.value - XCTAssertEqual(count, maxErrors, "expected results count to match") + #expect(count == maxErrors, "expected results count to match") } // these should not circuit break since they are deliberately aged @@ -336,49 +350,51 @@ final class HTTPClientTests: XCTestCase { try await Task.sleep(nanoseconds: UInt64(sleepInterval.nanoseconds()!)) let response = try await httpClient.get("\(host)/\(index)/okay") count.increment() - XCTAssertEqual(response.statusCode, 200, "expected status code to match") + #expect(response.statusCode == 200, "expected status code to match") } - XCTAssertEqual(count.get(), total, "expected results count to match") + #expect(count.get() == total, "expected status code to match") } - func testHTTPClientHeaders() async throws { + @Test + func hTTPClientHeaders() async throws { var headers = HTTPClientHeaders() let items = (1 ... Int.random(in: 10 ... 20)).map { index in HTTPClientHeaders.Item(name: "header-\(index)", value: UUID().uuidString) } headers.add(items) - XCTAssertEqual(headers.count, items.count, "headers count should match") + #expect(headers.count == items.count, "headers count should match") items.forEach { item in - XCTAssertEqual(headers.get(item.name).first, item.value, "headers value should match") + #expect(headers.get(item.name).first == item.value, "headers value should match") } headers.add(items.first!) - XCTAssertEqual(headers.count, items.count, "headers count should match (no duplicates)") + #expect(headers.count == items.count, "headers count should match (no duplicates)") let name = UUID().uuidString let values = (1 ... Int.random(in: 10 ... 20)).map { "value-\($0)" } values.forEach { value in headers.add(name: name, value: value) } - XCTAssertEqual(headers.count, items.count + 1, "headers count should match (no duplicates)") - XCTAssertEqual(values, headers.get(name), "multiple headers value should match") + #expect(headers.count == items.count + 1, "headers count should match (no duplicates)") + #expect(values == headers.get(name), "multiple headers value should match") } - func testExceedsDownloadSizeLimitProgress() async throws { + @Test + func exceedsDownloadSizeLimitProgress() async throws { let maxSize: Int64 = 50 let httpClient = HTTPClient { request, progress in switch request.method { - case .head: - return .init( - statusCode: 200, - headers: .init([.init(name: "Content-Length", value: "0")]) - ) - case .get: - try progress?(Int64(maxSize * 2), 0) - default: - XCTFail("method should match") + case .head: + return .init( + statusCode: 200, + headers: .init([.init(name: "Content-Length", value: "0")]) + ) + case .get: + try progress?(Int64(maxSize * 2), 0) + default: + Issue.record("method should match. Received: \(request.method)") } fatalError("unreachable") @@ -387,15 +403,13 @@ final class HTTPClientTests: XCTestCase { var request = HTTPClient.Request(url: "http://test") request.options.maximumResponseSizeInBytes = 10 - do { - let response = try await httpClient.execute(request) - XCTFail("unexpected success \(response)") - } catch { - XCTAssertEqual(error as? HTTPClientError, .responseTooLarge(maxSize * 2), "expected error to match") + await #expect(throws: HTTPClientError.responseTooLarge(maxSize * 2)) { + let _ = try await httpClient.execute(request) } } - func testMaxConcurrency() async throws { + @Test + func maxConcurrency() async throws { let maxConcurrentRequests = 2 let concurrentRequests = SendableBox(0) @@ -404,8 +418,9 @@ final class HTTPClientTests: XCTestCase { let httpClient = HTTPClient(configuration: configuration) { request, _ in await concurrentRequests.increment() - if await concurrentRequests.value! > maxConcurrentRequests { - XCTFail("too many concurrent requests \(concurrentRequests), expected \(maxConcurrentRequests)") + let concurrentRequestsCounts = await concurrentRequests.value! + if concurrentRequestsCounts > maxConcurrentRequests { + Issue.record("too many concurrent requests \(concurrentRequestsCounts), expected \(maxConcurrentRequests)") } await concurrentRequests.decrement() @@ -426,10 +441,10 @@ final class HTTPClientTests: XCTestCase { results.append(result) } - XCTAssertEqual(results.count, total, "expected number of results to match") + #expect(results.count == total, "expected number of results to match") for result in results { - XCTAssertEqual(result.statusCode, 200, "expected '200 okay' response") + #expect(result.statusCode == 200, "expected '200 okay' response") } } } @@ -437,9 +452,9 @@ final class HTTPClientTests: XCTestCase { private func assertRequestHeaders(_ headers: HTTPClientHeaders, expected: HTTPClientHeaders) { let noAgent = HTTPClientHeaders(headers.filter { $0.name != "User-Agent" }) - XCTAssertEqual(noAgent, expected, "expected headers to match") + #expect(noAgent == expected, "expected headers to match") } private func assertResponseHeaders(_ headers: HTTPClientHeaders, expected: HTTPClientHeaders) { - XCTAssertEqual(headers, expected, "expected headers to match") + #expect(headers == expected, "expected headers to match") } diff --git a/Tests/BasicsTests/NetrcTests.swift b/Tests/BasicsTests/NetrcTests.swift index 72e69bcbd29..8af207ebbf1 100644 --- a/Tests/BasicsTests/NetrcTests.swift +++ b/Tests/BasicsTests/NetrcTests.swift @@ -11,30 +11,32 @@ //===----------------------------------------------------------------------===// import Basics -import XCTest +import Testing -class NetrcTests: XCTestCase { +struct NetrcTests { /// should load machines for a given inline format - func testLoadMachinesInline() throws { + @Test + func loadMachinesInline() throws { let content = "machine example.com login anonymous password qwerty" let netrc = try NetrcParser.parse(content) - XCTAssertEqual(netrc.machines.count, 1) + #expect(netrc.machines.count == 1) let machine = netrc.machines.first - XCTAssertEqual(machine?.name, "example.com") - XCTAssertEqual(machine?.login, "anonymous") - XCTAssertEqual(machine?.password, "qwerty") + #expect(machine?.name == "example.com") + #expect(machine?.login == "anonymous") + #expect(machine?.password == "qwerty") let authorization = netrc.authorization(for: "http://example.com/resource.zip") - XCTAssertEqual(authorization, Netrc.Authorization(login: "anonymous", password: "qwerty")) + #expect(authorization == Netrc.Authorization(login: "anonymous", password: "qwerty")) - XCTAssertNil(netrc.authorization(for: "http://example2.com/resource.zip")) - XCTAssertNil(netrc.authorization(for: "http://www.example2.com/resource.zip")) + #expect(netrc.authorization(for: "http://example2.com/resource.zip") == nil) + #expect(netrc.authorization(for: "http://www.example2.com/resource.zip") == nil) } /// should load machines for a given multi-line format - func testLoadMachinesMultiLine() throws { + @Test + func loadMachinesMultiLine() throws { let content = """ machine example.com login anonymous @@ -42,22 +44,23 @@ class NetrcTests: XCTestCase { """ let netrc = try NetrcParser.parse(content) - XCTAssertEqual(netrc.machines.count, 1) + #expect(netrc.machines.count == 1) let machine = netrc.machines.first - XCTAssertEqual(machine?.name, "example.com") - XCTAssertEqual(machine?.login, "anonymous") - XCTAssertEqual(machine?.password, "qwerty") + #expect(machine?.name == "example.com") + #expect(machine?.login == "anonymous") + #expect(machine?.password == "qwerty") let authorization = netrc.authorization(for: "http://example.com/resource.zip") - XCTAssertEqual(authorization, Netrc.Authorization(login: "anonymous", password: "qwerty")) + #expect(authorization == Netrc.Authorization(login: "anonymous", password: "qwerty")) - XCTAssertNil(netrc.authorization(for: "http://example2.com/resource.zip")) - XCTAssertNil(netrc.authorization(for: "http://www.example2.com/resource.zip")) + #expect(netrc.authorization(for: "http://example2.com/resource.zip") == nil) + #expect(netrc.authorization(for: "http://www.example2.com/resource.zip") == nil) } /// Should fall back to default machine when not matching host - func testLoadDefaultMachine() throws { + @Test + func loadDefaultMachine() throws { let content = """ machine example.com login anonymous @@ -69,23 +72,24 @@ class NetrcTests: XCTestCase { """ let netrc = try NetrcParser.parse(content) - XCTAssertEqual(netrc.machines.count, 2) + #expect(netrc.machines.count == 2) let machine = netrc.machines.first - XCTAssertEqual(machine?.name, "example.com") - XCTAssertEqual(machine?.login, "anonymous") - XCTAssertEqual(machine?.password, "qwerty") + #expect(machine?.name == "example.com") + #expect(machine?.login == "anonymous") + #expect(machine?.password == "qwerty") let machine2 = netrc.machines.last - XCTAssertEqual(machine2?.name, "default") - XCTAssertEqual(machine2?.login, "id") - XCTAssertEqual(machine2?.password, "secret") + #expect(machine2?.name == "default") + #expect(machine2?.login == "id") + #expect(machine2?.password == "secret") let authorization = netrc.authorization(for: "http://example2.com/resource.zip") - XCTAssertEqual(authorization, Netrc.Authorization(login: "id", password: "secret")) + #expect(authorization == Netrc.Authorization(login: "id", password: "secret")) } - func testRegexParsing() throws { + @Test + func regexParsing() throws { let content = """ machine machine login login @@ -109,25 +113,26 @@ class NetrcTests: XCTestCase { """ let netrc = try NetrcParser.parse(content) - XCTAssertEqual(netrc.machines.count, 3) + #expect(netrc.machines.count == 3) - XCTAssertEqual(netrc.machines[0].name, "machine") - XCTAssertEqual(netrc.machines[0].login, "login") - XCTAssertEqual(netrc.machines[0].password, "password") + #expect(netrc.machines[0].name == "machine") + #expect(netrc.machines[0].login == "login") + #expect(netrc.machines[0].password == "password") - XCTAssertEqual(netrc.machines[1].name, "login") - XCTAssertEqual(netrc.machines[1].login, "password") - XCTAssertEqual(netrc.machines[1].password, "machine") + #expect(netrc.machines[1].name == "login") + #expect(netrc.machines[1].login == "password") + #expect(netrc.machines[1].password == "machine") - XCTAssertEqual(netrc.machines[2].name, "default") - XCTAssertEqual(netrc.machines[2].login, "id") - XCTAssertEqual(netrc.machines[2].password, "secret") + #expect(netrc.machines[2].name == "default") + #expect(netrc.machines[2].login == "id") + #expect(netrc.machines[2].password == "secret") let authorization = netrc.authorization(for: "http://example2.com/resource.zip") - XCTAssertEqual(authorization, Netrc.Authorization(login: "id", password: "secret")) + #expect(authorization == Netrc.Authorization(login: "id", password: "secret")) } - func testOutOfOrderDefault() { + @Test + func outOfOrderDefault() { let content = """ machine machine login login @@ -146,12 +151,13 @@ class NetrcTests: XCTestCase { password secret """ - XCTAssertThrowsError(try NetrcParser.parse(content)) { error in - XCTAssertEqual(error as? NetrcError, .invalidDefaultMachinePosition) + #expect(throws: NetrcError.invalidDefaultMachinePosition) { + try NetrcParser.parse(content) } } - func testErrorOnMultipleDefault() { + @Test + func errorOnMultipleDefault() { let content = """ machine machine login login @@ -174,13 +180,14 @@ class NetrcTests: XCTestCase { password terces """ - XCTAssertThrowsError(try NetrcParser.parse(content)) { error in - XCTAssertEqual(error as? NetrcError, .invalidDefaultMachinePosition) + #expect(throws: NetrcError.invalidDefaultMachinePosition) { + try NetrcParser.parse(content) } } /// should load machines for a given multi-line format with comments - func testLoadMachinesMultilineComments() throws { + @Test + func loadMachinesMultilineComments() throws { let content = """ ## This is a comment # This is another comment @@ -190,48 +197,51 @@ class NetrcTests: XCTestCase { """ let machines = try NetrcParser.parse(content).machines - XCTAssertEqual(machines.count, 1) + #expect(machines.count == 1) let machine = machines.first - XCTAssertEqual(machine?.name, "example.com") - XCTAssertEqual(machine?.login, "anonymous") - XCTAssertEqual(machine?.password, "qwerty") + #expect(machine?.name == "example.com") + #expect(machine?.login == "anonymous") + #expect(machine?.password == "qwerty") } /// should load machines for a given multi-line + whitespaces format - func testLoadMachinesMultilineWhitespaces() throws { + @Test + func loadMachinesMultilineWhitespaces() throws { let content = """ machine example.com login anonymous password qwerty """ let machines = try NetrcParser.parse(content).machines - XCTAssertEqual(machines.count, 1) + #expect(machines.count == 1) let machine = machines.first - XCTAssertEqual(machine?.name, "example.com") - XCTAssertEqual(machine?.login, "anonymous") - XCTAssertEqual(machine?.password, "qwerty") + #expect(machine?.name == "example.com") + #expect(machine?.login == "anonymous") + #expect(machine?.password == "qwerty") } /// should load multiple machines for a given inline format - func testLoadMultipleMachinesInline() throws { + @Test + func loadMultipleMachinesInline() throws { let content = "machine example.com login anonymous password qwerty machine example2.com login anonymous2 password qwerty2" let netrc = try NetrcParser.parse(content) - XCTAssertEqual(netrc.machines.count, 2) + #expect(netrc.machines.count == 2) - XCTAssertEqual(netrc.machines[0].name, "example.com") - XCTAssertEqual(netrc.machines[0].login, "anonymous") - XCTAssertEqual(netrc.machines[0].password, "qwerty") + #expect(netrc.machines[0].name == "example.com") + #expect(netrc.machines[0].login == "anonymous") + #expect(netrc.machines[0].password == "qwerty") - XCTAssertEqual(netrc.machines[1].name, "example2.com") - XCTAssertEqual(netrc.machines[1].login, "anonymous2") - XCTAssertEqual(netrc.machines[1].password, "qwerty2") + #expect(netrc.machines[1].name == "example2.com") + #expect(netrc.machines[1].login == "anonymous2") + #expect(netrc.machines[1].password == "qwerty2") } /// should load multiple machines for a given multi-line format - func testLoadMultipleMachinesMultiline() throws { + @Test + func loadMultipleMachinesMultiline() throws { let content = """ machine example.com login anonymous password qwerty @@ -241,90 +251,98 @@ class NetrcTests: XCTestCase { """ let machines = try NetrcParser.parse(content).machines - XCTAssertEqual(machines.count, 2) + #expect(machines.count == 2) var machine = machines[0] - XCTAssertEqual(machine.name, "example.com") - XCTAssertEqual(machine.login, "anonymous") - XCTAssertEqual(machine.password, "qwerty") + #expect(machine.name == "example.com") + #expect(machine.login == "anonymous") + #expect(machine.password == "qwerty") machine = machines[1] - XCTAssertEqual(machine.name, "example2.com") - XCTAssertEqual(machine.login, "anonymous2") - XCTAssertEqual(machine.password, "qwerty2") + #expect(machine.name == "example2.com") + #expect(machine.login == "anonymous2") + #expect(machine.password == "qwerty2") } /// should throw error when machine parameter is missing - func testErrorMachineParameterMissing() throws { + @Test + func errorMachineParameterMissing() throws { let content = "login anonymous password qwerty" - XCTAssertThrowsError(try NetrcParser.parse(content)) { error in - XCTAssertEqual(error as? NetrcError, .machineNotFound) + #expect(throws: NetrcError.machineNotFound) { + try NetrcParser.parse(content) } } /// should throw error for an empty machine values - func testErrorEmptyMachineValue() throws { + @Test + func errorEmptyMachineValue() throws { let content = "machine" - XCTAssertThrowsError(try NetrcParser.parse(content)) { error in - XCTAssertEqual(error as? NetrcError, .machineNotFound) + #expect(throws: NetrcError.machineNotFound) { + try NetrcParser.parse(content) } } /// should throw error for an empty machine values - func testEmptyMachineValueFollowedByDefaultNoError() throws { + @Test + func emptyMachineValueFollowedByDefaultNoError() throws { let content = "machine default login id password secret" let netrc = try NetrcParser.parse(content) let authorization = netrc.authorization(for: "http://example.com/resource.zip") - XCTAssertEqual(authorization, Netrc.Authorization(login: "id", password: "secret")) + #expect(authorization == Netrc.Authorization(login: "id", password: "secret")) } /// should return authorization when config contains a given machine - func testReturnAuthorizationForMachineMatch() throws { + @Test + func returnAuthorizationForMachineMatch() throws { let content = "machine example.com login anonymous password qwerty" let netrc = try NetrcParser.parse(content) let authorization = netrc.authorization(for: "http://example.com/resource.zip") - XCTAssertEqual(authorization, Netrc.Authorization(login: "anonymous", password: "qwerty")) + #expect(authorization == Netrc.Authorization(login: "anonymous", password: "qwerty")) } - func testReturnNoAuthorizationForUnmatched() throws { + @Test + func returnNoAuthorizationForUnmatched() throws { let content = "machine example.com login anonymous password qwerty" let netrc = try NetrcParser.parse(content) - XCTAssertNil(netrc.authorization(for: "http://www.example.com/resource.zip")) - XCTAssertNil(netrc.authorization(for: "ftp.example.com/resource.zip")) - XCTAssertNil(netrc.authorization(for: "http://example2.com/resource.zip")) - XCTAssertNil(netrc.authorization(for: "http://www.example2.com/resource.zip")) + #expect(netrc.authorization(for: "http://www.example.com/resource.zip") == nil) + #expect(netrc.authorization(for: "ftp.example.com/resource.zip") == nil) + #expect(netrc.authorization(for: "http://example2.com/resource.zip") == nil) + #expect(netrc.authorization(for: "http://www.example2.com/resource.zip") == nil) } /// should not return authorization when config does not contain a given machine - func testNoReturnAuthorizationForNoMachineMatch() throws { + @Test + func noReturnAuthorizationForNoMachineMatch() throws { let content = "machine example.com login anonymous password qwerty" let netrc = try NetrcParser.parse(content) - XCTAssertNil(netrc.authorization(for: "https://example99.com")) - XCTAssertNil(netrc.authorization(for: "http://www.example.com/resource.zip")) - XCTAssertNil(netrc.authorization(for: "ftp.example.com/resource.zip")) - XCTAssertNil(netrc.authorization(for: "http://example2.com/resource.zip")) - XCTAssertNil(netrc.authorization(for: "http://www.example2.com/resource.zip")) + #expect(netrc.authorization(for: "https://example99.com") == nil) + #expect(netrc.authorization(for: "http://www.example.com/resource.zip") == nil) + #expect(netrc.authorization(for: "ftp.example.com/resource.zip") == nil) + #expect(netrc.authorization(for: "http://example2.com/resource.zip") == nil) + #expect(netrc.authorization(for: "http://www.example2.com/resource.zip") == nil) } /// Test case: https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/filesreference/netrc.html - func testIBMDocumentation() throws { + @Test + func iBMDocumentation() throws { let content = "machine host1.austin.century.com login fred password bluebonnet" let netrc = try NetrcParser.parse(content) let machine = netrc.machines.first - XCTAssertEqual(machine?.name, "host1.austin.century.com") - XCTAssertEqual(machine?.login, "fred") - XCTAssertEqual(machine?.password, "bluebonnet") + #expect(machine?.name == "host1.austin.century.com") + #expect(machine?.login == "fred") + #expect(machine?.password == "bluebonnet") } /// Should not fail on presence of `account`, `macdef`, `default` /// test case: https://gist.github.com/tpope/4247721 - func testNoErrorTrailingAccountMacdefDefault() throws { + @Test + func noErrorTrailingAccountMacdefDefault() throws { let content = """ machine api.heroku.com login my@email.com @@ -341,28 +359,29 @@ class NetrcTests: XCTestCase { let netrc = try NetrcParser.parse(content) - XCTAssertEqual(netrc.machines.count, 4) + #expect(netrc.machines.count == 4) - XCTAssertEqual(netrc.machines[0].name, "api.heroku.com") - XCTAssertEqual(netrc.machines[0].login, "my@email.com") - XCTAssertEqual(netrc.machines[0].password, "01230123012301230123012301230123") + #expect(netrc.machines[0].name == "api.heroku.com") + #expect(netrc.machines[0].login == "my@email.com") + #expect(netrc.machines[0].password == "01230123012301230123012301230123") - XCTAssertEqual(netrc.machines[1].name, "api.github.com") - XCTAssertEqual(netrc.machines[1].login, "somebody") - XCTAssertEqual(netrc.machines[1].password, "something") + #expect(netrc.machines[1].name == "api.github.com") + #expect(netrc.machines[1].login == "somebody") + #expect(netrc.machines[1].password == "something") - XCTAssertEqual(netrc.machines[2].name, "ftp.server") - XCTAssertEqual(netrc.machines[2].login, "abc") - XCTAssertEqual(netrc.machines[2].password, "def") + #expect(netrc.machines[2].name == "ftp.server") + #expect(netrc.machines[2].login == "abc") + #expect(netrc.machines[2].password == "def") - XCTAssertEqual(netrc.machines[3].name, "default") - XCTAssertEqual(netrc.machines[3].login, "anonymous") - XCTAssertEqual(netrc.machines[3].password, "my@email.com") + #expect(netrc.machines[3].name == "default") + #expect(netrc.machines[3].login == "anonymous") + #expect(netrc.machines[3].password == "my@email.com") } /// Should not fail on presence of `account`, `macdef`, `default` /// test case: https://gist.github.com/tpope/4247721 - func testNoErrorMixedAccount() throws { + @Test + func noErrorMixedAccount() throws { let content = """ machine api.heroku.com login my@email.com @@ -379,28 +398,29 @@ class NetrcTests: XCTestCase { let netrc = try NetrcParser.parse(content) - XCTAssertEqual(netrc.machines.count, 4) + #expect(netrc.machines.count == 4) - XCTAssertEqual(netrc.machines[0].name, "api.heroku.com") - XCTAssertEqual(netrc.machines[0].login, "my@email.com") - XCTAssertEqual(netrc.machines[0].password, "01230123012301230123012301230123") + #expect(netrc.machines[0].name == "api.heroku.com") + #expect(netrc.machines[0].login == "my@email.com") + #expect(netrc.machines[0].password == "01230123012301230123012301230123") - XCTAssertEqual(netrc.machines[1].name, "api.github.com") - XCTAssertEqual(netrc.machines[1].login, "somebody") - XCTAssertEqual(netrc.machines[1].password, "something") + #expect(netrc.machines[1].name == "api.github.com") + #expect(netrc.machines[1].login == "somebody") + #expect(netrc.machines[1].password == "something") - XCTAssertEqual(netrc.machines[2].name, "ftp.server") - XCTAssertEqual(netrc.machines[2].login, "abc") - XCTAssertEqual(netrc.machines[2].password, "def") + #expect(netrc.machines[2].name == "ftp.server") + #expect(netrc.machines[2].login == "abc") + #expect(netrc.machines[2].password == "def") - XCTAssertEqual(netrc.machines[3].name, "default") - XCTAssertEqual(netrc.machines[3].login, "anonymous") - XCTAssertEqual(netrc.machines[3].password, "my@email.com") + #expect(netrc.machines[3].name == "default") + #expect(netrc.machines[3].login == "anonymous") + #expect(netrc.machines[3].password == "my@email.com") } /// Should not fail on presence of `account`, `macdef`, `default` /// test case: https://renenyffenegger.ch/notes/Linux/fhs/home/username/_netrc - func testNoErrorMultipleMacdefAndComments() throws { + @Test + func noErrorMultipleMacdefAndComments() throws { let content = """ machine ftp.foobar.baz login john @@ -421,18 +441,19 @@ class NetrcTests: XCTestCase { let netrc = try NetrcParser.parse(content) - XCTAssertEqual(netrc.machines.count, 2) + #expect(netrc.machines.count == 2) - XCTAssertEqual(netrc.machines[0].name, "ftp.foobar.baz") - XCTAssertEqual(netrc.machines[0].login, "john") - XCTAssertEqual(netrc.machines[0].password, "5ecr3t") + #expect(netrc.machines[0].name == "ftp.foobar.baz") + #expect(netrc.machines[0].login == "john") + #expect(netrc.machines[0].password == "5ecr3t") - XCTAssertEqual(netrc.machines[1].name, "other.server.org") - XCTAssertEqual(netrc.machines[1].login, "fred") - XCTAssertEqual(netrc.machines[1].password, "sunshine4ever") + #expect(netrc.machines[1].name == "other.server.org") + #expect(netrc.machines[1].login == "fred") + #expect(netrc.machines[1].password == "sunshine4ever") } - func testComments() throws { + @Test + func comments() throws { let content = """ # A comment at the beginning of the line machine example.com # Another comment @@ -442,73 +463,55 @@ class NetrcTests: XCTestCase { let netrc = try NetrcParser.parse(content) - let machine = netrc.machines.first - XCTAssertEqual(machine?.name, "example.com") - XCTAssertEqual(machine?.login, "anonymous") - XCTAssertEqual(machine?.password, "qw#erty") + let machine = try #require(netrc.machines.first) + #expect(machine.name == "example.com") + #expect(machine.login == "anonymous") + #expect(machine.password == "qw#erty") } - // TODO: These permutation tests would be excellent swift-testing parameterized tests. - func testAllHashQuotingPermutations() throws { - let cases = [ - ("qwerty", "qwerty"), - ("qwe#rty", "qwe#rty"), - ("\"qwe#rty\"", "qwe#rty"), - ("\"qwe #rty\"", "qwe #rty"), - ("\"qwe# rty\"", "qwe# rty"), + @Test( + arguments: [ + (testCase: "qwerty", expected: "qwerty"), + (testCase: "qwe#rty", expected: "qwe#rty"), + (testCase: "\"qwe#rty\"", expected: "qwe#rty"), + (testCase: "\"qwe #rty\"", expected: "qwe #rty"), + (testCase: "\"qwe# rty\"", expected: "qwe# rty"), + // Comments permutations + (testCase: "qwerty # a comment", expected: "qwerty"), + (testCase: "qwe#rty # a comment", expected: "qwe#rty"), + (testCase: "\"qwe#rty\" # a comment", expected: "qwe#rty"), + (testCase: "\"qwe #rty\" # a comment", expected: "qwe #rty"), + (testCase: "\"qwe# rty\" # a comment", expected: "qwe# rty"), ] + ) + func allHashQuotingPermutations(testCase: String, expected: String) throws { - for (testCase, expected) in cases { - let content = """ - machine example.com - login \(testCase) - password \(testCase) - """ - let netrc = try NetrcParser.parse(content) - - let machine = netrc.machines.first - XCTAssertEqual(machine?.name, "example.com") - XCTAssertEqual(machine?.login, expected, "Expected login \(testCase) to parse as \(expected)") - XCTAssertEqual(machine?.password, expected, "Expected \(testCase) to parse as \(expected)") - } - } - - func testAllCommentPermutations() throws { - let cases = [ - ("qwerty # a comment", "qwerty"), - ("qwe#rty # a comment", "qwe#rty"), - ("\"qwe#rty\" # a comment", "qwe#rty"), - ("\"qwe #rty\" # a comment", "qwe #rty"), - ("\"qwe# rty\" # a comment", "qwe# rty"), - ] + let content = """ + machine example.com + login \(testCase) + password \(testCase) + """ + let netrc = try NetrcParser.parse(content) - for (testCase, expected) in cases { - let content = """ - machine example.com - login \(testCase) - password \(testCase) - """ - let netrc = try NetrcParser.parse(content) - - let machine = netrc.machines.first - XCTAssertEqual(machine?.name, "example.com") - XCTAssertEqual(machine?.login, expected, "Expected login \(testCase) to parse as \(expected)") - XCTAssertEqual(machine?.password, expected, "Expected password \(testCase) to parse as \(expected)") - } + let machine = try #require(netrc.machines.first) + #expect(machine.name == "example.com") + #expect(machine.login == expected, "Expected login \(testCase) to parse as \(expected)") + #expect(machine.password == expected, "Expected \(testCase) to parse as \(expected)") } - func testQuotedMachine() throws { + @Test + func quotedMachine() throws { let content = """ machine "example.com" login anonymous password qwerty """ - let netrc = try NetrcParser.parse(content) - let machine = netrc.machines.first - XCTAssertEqual(machine?.name, "example.com") - XCTAssertEqual(machine?.login, "anonymous") - XCTAssertEqual(machine?.password, "qwerty") + let machine = try #require(netrc.machines.first) + + #expect(machine.name == "example.com") + #expect(machine.login == "anonymous") + #expect(machine.password == "qwerty") } } diff --git a/Tests/BasicsTests/ObservabilitySystemTests.swift b/Tests/BasicsTests/ObservabilitySystemTests.swift index 300c988f8bb..14bc36f598b 100644 --- a/Tests/BasicsTests/ObservabilitySystemTests.swift +++ b/Tests/BasicsTests/ObservabilitySystemTests.swift @@ -9,16 +9,18 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation @testable import Basics import _InternalTestSupport -import XCTest +import Testing // TODO: remove when transition to new diagnostics system is complete typealias Diagnostic = Basics.Diagnostic -final class ObservabilitySystemTest: XCTestCase { - func testScopes() throws { +struct ObservabilitySystemTest { + @Test + func scopes() throws { let collector = Collector() let observabilitySystem = ObservabilitySystem(collector) @@ -33,16 +35,16 @@ final class ObservabilitySystemTest: XCTestCase { let emitter1 = childScope1.makeDiagnosticsEmitter() emitter1.emit(error: "error 1.5") - testDiagnostics(collector.diagnostics) { result in - let diagnostic1 = result.check(diagnostic: "error 1", severity: .error) - XCTAssertEqual(diagnostic1?.metadata?.testKey1, metadata1.testKey1) - XCTAssertEqual(diagnostic1?.metadata?.testKey2, metadata1.testKey2) - XCTAssertEqual(diagnostic1?.metadata?.testKey3, metadata1.testKey3) + try expectDiagnostics(collector.diagnostics) { result in + let diagnostic1 = try #require(result.check(diagnostic: "error 1", severity: .error)) + #expect(diagnostic1.metadata?.testKey1 == metadata1.testKey1) + #expect(diagnostic1.metadata?.testKey2 == metadata1.testKey2) + #expect(diagnostic1.metadata?.testKey3 == metadata1.testKey3) - let diagnostic1_5 = result.check(diagnostic: "error 1.5", severity: .error) - XCTAssertEqual(diagnostic1_5?.metadata?.testKey1, metadata1.testKey1) - XCTAssertEqual(diagnostic1_5?.metadata?.testKey2, metadata1.testKey2) - XCTAssertEqual(diagnostic1_5?.metadata?.testKey3, metadata1.testKey3) + let diagnostic1_5 = try #require(result.check(diagnostic: "error 1.5", severity: .error)) + #expect(diagnostic1_5.metadata?.testKey1 == metadata1.testKey1) + #expect(diagnostic1_5.metadata?.testKey2 == metadata1.testKey2) + #expect(diagnostic1_5.metadata?.testKey3 == metadata1.testKey3) } collector.clear() @@ -52,9 +54,9 @@ final class ObservabilitySystemTest: XCTestCase { metadata2.testKey2 = Int.random(in: Int.min..