From d2529334c42538895ec2e0b21fe3fdb0973b5714 Mon Sep 17 00:00:00 2001 From: Bassam Khouri Date: Tue, 8 Oct 2024 12:58:29 -0400 Subject: [PATCH] Convert some tests BasicsTests to Swift Testing Convert some BasicsTests from XCTest to Swift Testing to make use of parallelism and, in some cases, test parameterization. Not all Test Suites in BasicsTests have been converted as some use helpers in swift-tools-core-support, which don't have a matching swift testing helper. --- Package.swift | 11 +- .../_InternalTestSupport/Observability.swift | 35 +- .../XCTAssertHelpers.swift | 6 +- .../Archiver/ZipArchiverTests.swift | 100 +-- Tests/BasicsTests/AsyncProcessTests.swift | 240 +++--- .../ByteStringExtensionsTests.swift | 12 +- .../BasicsTests/ConcurrencyHelpersTests.swift | 44 +- Tests/BasicsTests/DictionaryTest.swift | 19 +- Tests/BasicsTests/DispatchTimeTests.swift | 19 +- .../Environment/EnvironmentKeyTests.swift | 73 +- .../Environment/EnvironmentTests.swift | 144 ++-- .../FileSystem/FileSystemTests.swift | 36 +- .../FileSystem/PathShimTests.swift | 47 +- Tests/BasicsTests/FileSystem/PathTests.swift | 803 +++++++++++------- .../FileSystem/TemporaryFileTests.swift | 69 +- Tests/BasicsTests/FileSystem/VFSTests.swift | 95 ++- .../Graph/AdjacencyMatrixTests.swift | 21 +- .../Graph/DirectedGraphTests.swift | 20 +- .../Graph/UndirectedGraphTests.swift | 28 +- Tests/BasicsTests/HTTPClientTests.swift | 245 +++--- Tests/BasicsTests/NetrcTests.swift | 377 ++++---- .../ObservabilitySystemTests.swift | 181 ++-- .../BasicsTests/ProgressAnimationTests.swift | 22 +- .../Serialization/SerializedJSONTests.swift | 23 +- Tests/BasicsTests/StringExtensionsTests.swift | 19 +- Tests/BasicsTests/TripleTests.swift | 451 +++++----- 26 files changed, 1734 insertions(+), 1406 deletions(-) diff --git a/Package.swift b/Package.swift index 7560820f2fc..5a73ca1a67a 100644 --- a/Package.swift +++ b/Package.swift @@ -743,7 +743,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", @@ -985,6 +990,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 += [ @@ -997,5 +1004,7 @@ 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 e7e3956f2bf..e2ff5c325f1 100644 --- a/Sources/_InternalTestSupport/XCTAssertHelpers.swift +++ b/Sources/_InternalTestSupport/XCTAssertHelpers.swift @@ -43,8 +43,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 c8a5e113f0b..9f28503ab1c 100644 --- a/Tests/BasicsTests/Archiver/ZipArchiverTests.swift +++ b/Tests/BasicsTests/Archiver/ZipArchiverTests.swift @@ -104,56 +104,56 @@ final class ZipArchiverTests: XCTestCase { #endif try await testWithTemporaryDirectory { tmpdir in - 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 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) - try localFileSystem.writeFileContents(dir2.appending("file3.txt"), string: "Hello World 3!") - try localFileSystem.writeFileContents(dir2.appending("file4.txt"), string: "Hello World 4!") - - 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) - XCTAssertFileExists(extractedDir2.appending("file3.txt")) - XCTAssertEqual( - try? localFileSystem.readFileContents(extractedDir2.appending("file3.txt")), - "Hello World 3!" - ) - XCTAssertFileExists(extractedDir2.appending("file4.txt")) - XCTAssertEqual( - try? localFileSystem.readFileContents(extractedDir2.appending("file4.txt")), - "Hello World 4!" - ) + 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 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) + try localFileSystem.writeFileContents(dir2.appending("file3.txt"), string: "Hello World 3!") + try localFileSystem.writeFileContents(dir2.appending("file4.txt"), string: "Hello World 4!") + + 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) + XCTAssertFileExists(extractedDir2.appending("file3.txt")) + XCTAssertEqual( + try? localFileSystem.readFileContents(extractedDir2.appending("file3.txt")), + "Hello World 3!" + ) + XCTAssertFileExists(extractedDir2.appending("file4.txt")) + XCTAssertEqual( + try? localFileSystem.readFileContents(extractedDir2.appending("file4.txt")), + "Hello World 4!" + ) } } } 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 837d93706c4..6ff10ef4112 100644 --- a/Tests/BasicsTests/Environment/EnvironmentTests.swift +++ b/Tests/BasicsTests/Environment/EnvironmentTests.swift @@ -9,192 +9,208 @@ // 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) - XCTAssertEqual(environment["TestKey"], "TestValue") - #if os(Windows) - XCTAssertEqual(environment.count, 1) - #else - XCTAssertEqual(environment.count, 2) - #endif + #expect(environment["TestKey"] == "TestValue") +#if os(Windows) + #expect(environment.count == 1) +#else + #expect(environment.count == 2) +#endif } - 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..