diff --git a/Sources/TSCBasic/FileSystem.swift b/Sources/TSCBasic/FileSystem.swift index 91cbdb80..77170da7 100644 --- a/Sources/TSCBasic/FileSystem.swift +++ b/Sources/TSCBasic/FileSystem.swift @@ -12,74 +12,87 @@ import TSCLibc import Foundation import Dispatch -public enum FileSystemError: Swift.Error { - /// Access to the path is denied. - /// - /// This is used when an operation cannot be completed because a component of - /// the path cannot be accessed. - /// - /// Used in situations that correspond to the POSIX EACCES error code. - case invalidAccess - - /// IO Error encoding - /// - /// This is used when an operation cannot be completed due to an otherwise - /// unspecified IO error. - case ioError - - /// Is a directory - /// - /// This is used when an operation cannot be completed because a component - /// of the path which was expected to be a file was not. - /// - /// Used in situations that correspond to the POSIX EISDIR error code. - case isDirectory - - /// No such path exists. - /// - /// This is used when a path specified does not exist, but it was expected - /// to. - /// - /// Used in situations that correspond to the POSIX ENOENT error code. - case noEntry - - /// Not a directory - /// - /// This is used when an operation cannot be completed because a component - /// of the path which was expected to be a directory was not. - /// - /// Used in situations that correspond to the POSIX ENOTDIR error code. - case notDirectory - - /// Unsupported operation - /// - /// This is used when an operation is not supported by the concrete file - /// system implementation. - case unsupported - - /// An unspecific operating system error. - case unknownOSError - - /// File or folder already exists at destination. - /// - /// This is thrown when copying or moving a file or directory but the destination - /// path already contains a file or folder. - case alreadyExistsAtDestination +public struct FileSystemError: Swift.Error, Equatable { + public enum Kind: Equatable { + /// Access to the path is denied. + /// + /// This is used when an operation cannot be completed because a component of + /// the path cannot be accessed. + /// + /// Used in situations that correspond to the POSIX EACCES error code. + case invalidAccess + + /// IO Error encoding + /// + /// This is used when an operation cannot be completed due to an otherwise + /// unspecified IO error. + case ioError(code: Int32) + + /// Is a directory + /// + /// This is used when an operation cannot be completed because a component + /// of the path which was expected to be a file was not. + /// + /// Used in situations that correspond to the POSIX EISDIR error code. + case isDirectory + + /// No such path exists. + /// + /// This is used when a path specified does not exist, but it was expected + /// to. + /// + /// Used in situations that correspond to the POSIX ENOENT error code. + case noEntry + + /// Not a directory + /// + /// This is used when an operation cannot be completed because a component + /// of the path which was expected to be a directory was not. + /// + /// Used in situations that correspond to the POSIX ENOTDIR error code. + case notDirectory + + /// Unsupported operation + /// + /// This is used when an operation is not supported by the concrete file + /// system implementation. + case unsupported + + /// An unspecific operating system error at a given path. + case unknownOSError + + /// File or folder already exists at destination. + /// + /// This is thrown when copying or moving a file or directory but the destination + /// path already contains a file or folder. + case alreadyExistsAtDestination + } + + /// The kind of the error being raised. + public let kind: Kind + + /// The absolute path to the file associated with the error, if available. + public let path: AbsolutePath? + + public init(_ kind: Kind, _ path: AbsolutePath? = nil) { + self.kind = kind + self.path = path + } } public extension FileSystemError { - init(errno: Int32) { + init(errno: Int32, _ path: AbsolutePath) { switch errno { case TSCLibc.EACCES: - self = .invalidAccess + self.init(.invalidAccess, path) case TSCLibc.EISDIR: - self = .isDirectory + self.init(.isDirectory, path) case TSCLibc.ENOENT: - self = .noEntry + self.init(.noEntry, path) case TSCLibc.ENOTDIR: - self = .notDirectory + self.init(.notDirectory, path) default: - self = .unknownOSError + self.init(.unknownOSError, path) } } } @@ -238,7 +251,7 @@ public extension FileSystem { // if `atomically` is `true`, otherwise fall back to whatever implementation already exists. func writeFileContents(_ path: AbsolutePath, bytes: ByteString, atomically: Bool) throws { guard !atomically else { - throw FileSystemError.unsupported + throw FileSystemError(.unsupported, path) } try writeFileContents(path, bytes: bytes) } @@ -252,11 +265,11 @@ public extension FileSystem { } func getFileInfo(_ path: AbsolutePath) throws -> FileInfo { - throw FileSystemError.unsupported + throw FileSystemError(.unsupported, path) } func withLock(on path: AbsolutePath, type: FileLock.LockType, _ body: () throws -> T) throws -> T { - throw FileSystemError.unsupported + throw FileSystemError(.unsupported, path) } } @@ -316,11 +329,11 @@ private class LocalFileSystem: FileSystem { func changeCurrentWorkingDirectory(to path: AbsolutePath) throws { guard isDirectory(path) else { - throw FileSystemError.notDirectory + throw FileSystemError(.notDirectory, path) } guard FileManager.default.changeCurrentDirectoryPath(path.pathString) else { - throw FileSystemError.unknownOSError + throw FileSystemError(.unknownOSError, path) } } @@ -366,7 +379,7 @@ private class LocalFileSystem: FileSystem { // Open the file. let fp = fopen(path.pathString, "rb") if fp == nil { - throw FileSystemError(errno: errno) + throw FileSystemError(errno: errno, path) } defer { fclose(fp) } @@ -377,11 +390,12 @@ private class LocalFileSystem: FileSystem { let n = fread(&tmpBuffer, 1, tmpBuffer.count, fp) if n < 0 { if errno == EINTR { continue } - throw FileSystemError.ioError + throw FileSystemError(.ioError(code: errno), path) } if n == 0 { - if ferror(fp) != 0 { - throw FileSystemError.ioError + let errno = ferror(fp) + if errno != 0 { + throw FileSystemError(.ioError(code: errno), path) } break } @@ -395,7 +409,7 @@ private class LocalFileSystem: FileSystem { // Open the file. let fp = fopen(path.pathString, "wb") if fp == nil { - throw FileSystemError(errno: errno) + throw FileSystemError(errno: errno, path) } defer { fclose(fp) } @@ -405,10 +419,10 @@ private class LocalFileSystem: FileSystem { let n = fwrite(&contents, 1, contents.count, fp) if n < 0 { if errno == EINTR { continue } - throw FileSystemError.ioError + throw FileSystemError(.ioError(code: errno), path) } if n != contents.count { - throw FileSystemError.ioError + throw FileSystemError(.unknownOSError, path) } break } @@ -454,7 +468,7 @@ private class LocalFileSystem: FileSystem { guard let traverse = FileManager.default.enumerator( at: URL(fileURLWithPath: path.pathString), includingPropertiesForKeys: nil) else { - throw FileSystemError.noEntry + throw FileSystemError(.noEntry, path) } if !options.contains(.recursive) { @@ -467,14 +481,16 @@ private class LocalFileSystem: FileSystem { } func copy(from sourcePath: AbsolutePath, to destinationPath: AbsolutePath) throws { - guard exists(sourcePath) else { throw FileSystemError.noEntry } - guard !exists(destinationPath) else { throw FileSystemError.alreadyExistsAtDestination } + guard exists(sourcePath) else { throw FileSystemError(.noEntry, sourcePath) } + guard !exists(destinationPath) + else { throw FileSystemError(.alreadyExistsAtDestination, destinationPath) } try FileManager.default.copyItem(at: sourcePath.asURL, to: destinationPath.asURL) } func move(from sourcePath: AbsolutePath, to destinationPath: AbsolutePath) throws { - guard exists(sourcePath) else { throw FileSystemError.noEntry } - guard !exists(destinationPath) else { throw FileSystemError.alreadyExistsAtDestination } + guard exists(sourcePath) else { throw FileSystemError(.noEntry, sourcePath) } + guard !exists(destinationPath) + else { throw FileSystemError(.alreadyExistsAtDestination, destinationPath) } try FileManager.default.moveItem(at: sourcePath.asURL, to: destinationPath.asURL) } @@ -596,7 +612,7 @@ public class InMemoryFileSystem: FileSystem { // If we didn't find a directory, this is an error. guard case .directory(let contents) = parent.contents else { - throw FileSystemError.notDirectory + throw FileSystemError(.notDirectory, path.parentDirectory) } // Return the directory entry. @@ -683,7 +699,7 @@ public class InMemoryFileSystem: FileSystem { } public func changeCurrentWorkingDirectory(to path: AbsolutePath) throws { - throw FileSystemError.unsupported + throw FileSystemError(.unsupported, path) } public var homeDirectory: AbsolutePath { @@ -698,10 +714,10 @@ public class InMemoryFileSystem: FileSystem { public func getDirectoryContents(_ path: AbsolutePath) throws -> [String] { return try lock.withLock { guard let node = try getNode(path) else { - throw FileSystemError.noEntry + throw FileSystemError(.noEntry, path) } guard case .directory(let contents) = node.contents else { - throw FileSystemError.notDirectory + throw FileSystemError(.notDirectory, path) } // FIXME: Perhaps we should change the protocol to allow lazy behavior. @@ -728,14 +744,14 @@ public class InMemoryFileSystem: FileSystem { return try _createDirectory(path, recursive: false) } else { // Otherwise, we failed. - throw FileSystemError.noEntry + throw FileSystemError(.noEntry, parentPath) } } // Check that the parent is a directory. guard case .directory(let contents) = parent.contents else { // The parent isn't a directory, this is an error. - throw FileSystemError.notDirectory + throw FileSystemError(.notDirectory, parentPath) } // Check if the node already exists. @@ -743,7 +759,7 @@ public class InMemoryFileSystem: FileSystem { // Verify it is a directory. guard case .directory = node.contents else { // The path itself isn't a directory, this is an error. - throw FileSystemError.notDirectory + throw FileSystemError(.notDirectory, path) } // We are done. @@ -764,16 +780,16 @@ public class InMemoryFileSystem: FileSystem { return try lock.withLock { // Create directory to destination parent. guard let destinationParent = try getNode(path.parentDirectory) else { - throw FileSystemError.noEntry + throw FileSystemError(.noEntry, path.parentDirectory) } // Check that the parent is a directory. guard case .directory(let contents) = destinationParent.contents else { - throw FileSystemError.notDirectory + throw FileSystemError(.notDirectory, path.parentDirectory) } guard contents.entries[path.basename] == nil else { - throw FileSystemError.alreadyExistsAtDestination + throw FileSystemError(.alreadyExistsAtDestination, path) } let destination = relative ? destination.relative(to: path.parentDirectory).pathString : destination.pathString @@ -786,13 +802,13 @@ public class InMemoryFileSystem: FileSystem { return try lock.withLock { // Get the node. guard let node = try getNode(path) else { - throw FileSystemError.noEntry + throw FileSystemError(.noEntry, path) } // Check that the node is a file. guard case .file(let contents) = node.contents else { // The path is a directory, this is an error. - throw FileSystemError.isDirectory + throw FileSystemError(.isDirectory, path) } // Return the file contents. @@ -805,18 +821,18 @@ public class InMemoryFileSystem: FileSystem { // It is an error if this is the root node. let parentPath = path.parentDirectory guard path != parentPath else { - throw FileSystemError.isDirectory + throw FileSystemError(.isDirectory, path) } // Get the parent node. guard let parent = try getNode(parentPath) else { - throw FileSystemError.noEntry + throw FileSystemError(.noEntry, parentPath) } // Check that the parent is a directory. guard case .directory(let contents) = parent.contents else { // The parent isn't a directory, this is an error. - throw FileSystemError.notDirectory + throw FileSystemError(.notDirectory, parentPath) } // Check if the node exists. @@ -824,7 +840,7 @@ public class InMemoryFileSystem: FileSystem { // Verify it is a file. guard case .file = node.contents else { // The path is a directory, this is an error. - throw FileSystemError.isDirectory + throw FileSystemError(.isDirectory, path) } } @@ -861,21 +877,21 @@ public class InMemoryFileSystem: FileSystem { private func _copy(from sourcePath: AbsolutePath, to destinationPath: AbsolutePath) throws { // Get the source node. guard let source = try getNode(sourcePath) else { - throw FileSystemError.noEntry + throw FileSystemError(.noEntry, sourcePath) } // Create directory to destination parent. guard let destinationParent = try getNode(destinationPath.parentDirectory) else { - throw FileSystemError.noEntry + throw FileSystemError(.noEntry, destinationPath.parentDirectory) } // Check that the parent is a directory. guard case .directory(let contents) = destinationParent.contents else { - throw FileSystemError.notDirectory + throw FileSystemError(.notDirectory, destinationPath.parentDirectory) } guard contents.entries[destinationPath.basename] == nil else { - throw FileSystemError.alreadyExistsAtDestination + throw FileSystemError(.alreadyExistsAtDestination, destinationPath) } contents.entries[destinationPath.basename] = source @@ -891,12 +907,12 @@ public class InMemoryFileSystem: FileSystem { return try lock.withLock { // Get the source parent node. guard let sourceParent = try getNode(sourcePath.parentDirectory) else { - throw FileSystemError.noEntry + throw FileSystemError(.noEntry, sourcePath.parentDirectory) } // Check that the parent is a directory. guard case .directory(let contents) = sourceParent.contents else { - throw FileSystemError.notDirectory + throw FileSystemError(.notDirectory, sourcePath.parentDirectory) } try _copy(from: sourcePath, to: destinationPath) @@ -990,7 +1006,7 @@ public class RerootedFileSystemView: FileSystem { } public func changeCurrentWorkingDirectory(to path: AbsolutePath) throws { - throw FileSystemError.unsupported + throw FileSystemError(.unsupported, path) } public var homeDirectory: AbsolutePath { diff --git a/Sources/TSCBasic/Lock.swift b/Sources/TSCBasic/Lock.swift index d974d266..bbfadea4 100644 --- a/Sources/TSCBasic/Lock.swift +++ b/Sources/TSCBasic/Lock.swift @@ -110,7 +110,7 @@ public final class FileLock { if fileDescriptor == nil { let fd = TSCLibc.open(lockFile.pathString, O_WRONLY | O_CREAT | O_CLOEXEC, 0o666) if fd == -1 { - throw FileSystemError(errno: errno) + throw FileSystemError(errno: errno, lockFile) } self.fileDescriptor = fd } diff --git a/Sources/TSCBasic/WritableByteStream.swift b/Sources/TSCBasic/WritableByteStream.swift index 67528c16..3321e4a8 100644 --- a/Sources/TSCBasic/WritableByteStream.swift +++ b/Sources/TSCBasic/WritableByteStream.swift @@ -672,16 +672,20 @@ public final class LocalFileOutputByteStream: FileOutputByteStream { /// The pointer to the file. let filePointer: UnsafeMutablePointer - /// True if there were any IO error during writing. - private var error: Bool = false + /// Set to an error value if there were any IO error during writing. + private var error: FileSystemError? /// Closes the file on deinit if true. private var closeOnDeinit: Bool + /// Path to the file this stream should operate on. + private let path: AbsolutePath? + /// Instantiate using the file pointer. public init(filePointer: UnsafeMutablePointer, closeOnDeinit: Bool = true, buffered: Bool = true) throws { self.filePointer = filePointer self.closeOnDeinit = closeOnDeinit + self.path = nil super.init(buffered: buffered) } @@ -700,8 +704,9 @@ public final class LocalFileOutputByteStream: FileOutputByteStream { /// - Throws: FileSystemError public init(_ path: AbsolutePath, closeOnDeinit: Bool = true, buffered: Bool = true) throws { guard let filePointer = fopen(path.pathString, "wb") else { - throw FileSystemError(errno: errno) + throw FileSystemError(errno: errno, path) } + self.path = path self.filePointer = filePointer self.closeOnDeinit = closeOnDeinit super.init(buffered: buffered) @@ -713,8 +718,12 @@ public final class LocalFileOutputByteStream: FileOutputByteStream { } } - func errorDetected() { - error = true + func errorDetected(code: Int32?) { + if let code = code { + error = .init(.ioError(code: code), path) + } else { + error = .init(.unknownOSError, path) + } } override final func writeImpl(_ bytes: C) where C.Iterator.Element == UInt8 { @@ -724,9 +733,9 @@ public final class LocalFileOutputByteStream: FileOutputByteStream { let n = fwrite(&contents, 1, contents.count, filePointer) if n < 0 { if errno == EINTR { continue } - errorDetected() + errorDetected(code: errno) } else if n != contents.count { - errorDetected() + errorDetected(code: nil) } break } @@ -738,9 +747,9 @@ public final class LocalFileOutputByteStream: FileOutputByteStream { let n = fwrite(bytesPtr.baseAddress, 1, bytesPtr.count, filePointer) if n < 0 { if errno == EINTR { continue } - errorDetected() + errorDetected(code: errno) } else if n != bytesPtr.count { - errorDetected() + errorDetected(code: nil) } break } @@ -758,8 +767,8 @@ public final class LocalFileOutputByteStream: FileOutputByteStream { closeOnDeinit = false } // Throw if errors were found during writing. - if error { - throw FileSystemError.ioError + if let error = error { + throw error } } } diff --git a/Sources/TSCUtility/Archiver.swift b/Sources/TSCUtility/Archiver.swift index 5f5c034d..5d0f7db0 100644 --- a/Sources/TSCUtility/Archiver.swift +++ b/Sources/TSCUtility/Archiver.swift @@ -51,12 +51,12 @@ public struct ZipArchiver: Archiver { completion: @escaping (Result) -> Void ) { guard fileSystem.exists(archivePath) else { - completion(.failure(FileSystemError.noEntry)) + completion(.failure(FileSystemError(.noEntry, archivePath))) return } guard fileSystem.isDirectory(destinationPath) else { - completion(.failure(FileSystemError.notDirectory)) + completion(.failure(FileSystemError(.notDirectory, destinationPath))) return } diff --git a/Tests/TSCBasicTests/FileSystemTests.swift b/Tests/TSCBasicTests/FileSystemTests.swift index 5c724354..0495653f 100644 --- a/Tests/TSCBasicTests/FileSystemTests.swift +++ b/Tests/TSCBasicTests/FileSystemTests.swift @@ -245,42 +245,45 @@ class FileSystemTests: XCTestCase { XCTAssertEqual(try! fs.readFileContents(filePath), "Hello, new world!") // Check read/write of a directory. - XCTAssertThrows(FileSystemError.ioError) { + XCTAssertThrows(FileSystemError(.ioError(code: TSCLibc.EPERM), filePath.parentDirectory)) { _ = try fs.readFileContents(filePath.parentDirectory) } - XCTAssertThrows(FileSystemError.isDirectory) { + XCTAssertThrows(FileSystemError(.isDirectory, filePath.parentDirectory)) { try fs.writeFileContents(filePath.parentDirectory, bytes: []) } XCTAssertEqual(try! fs.readFileContents(filePath), "Hello, new world!") // Check read/write against root. - XCTAssertThrows(FileSystemError.ioError) { - #if os(Android) - _ = try fs.readFileContents(AbsolutePath("/system/")) - #else - _ = try fs.readFileContents(AbsolutePath("/")) - #endif + #if os(Android) + let root = AbsolutePath("/system/") + #else + let root = AbsolutePath("/") + #endif + XCTAssertThrows(FileSystemError(.ioError(code: TSCLibc.EPERM), root)) { + _ = try fs.readFileContents(root) + } - XCTAssertThrows(FileSystemError.isDirectory) { - try fs.writeFileContents(AbsolutePath("/"), bytes: []) + XCTAssertThrows(FileSystemError(.isDirectory, root)) { + try fs.writeFileContents(root, bytes: []) } XCTAssert(fs.exists(filePath)) // Check read/write into a non-directory. - XCTAssertThrows(FileSystemError.notDirectory) { - _ = try fs.readFileContents(filePath.appending(component: "not-possible")) + let notDirectoryPath = filePath.appending(component: "not-possible") + XCTAssertThrows(FileSystemError(.notDirectory, notDirectoryPath)) { + _ = try fs.readFileContents(notDirectoryPath) } - XCTAssertThrows(FileSystemError.notDirectory) { + XCTAssertThrows(FileSystemError(.notDirectory, notDirectoryPath)) { try fs.writeFileContents(filePath.appending(component: "not-possible"), bytes: []) } XCTAssert(fs.exists(filePath)) // Check read/write into a missing directory. let missingDir = tmpDirPath.appending(components: "does", "not", "exist") - XCTAssertThrows(FileSystemError.noEntry) { + XCTAssertThrows(FileSystemError(.noEntry, missingDir)) { _ = try fs.readFileContents(missingDir) } - XCTAssertThrows(FileSystemError.noEntry) { + XCTAssertThrows(FileSystemError(.noEntry, missingDir)) { try fs.writeFileContents(missingDir, bytes: []) } XCTAssert(!fs.exists(missingDir)) @@ -302,10 +305,10 @@ class FileSystemTests: XCTestCase { // Copy with no source - XCTAssertThrows(FileSystemError.noEntry) { + XCTAssertThrows(FileSystemError(.noEntry, source)) { try fs.copy(from: source, to: destination) } - XCTAssertThrows(FileSystemError.noEntry) { + XCTAssertThrows(FileSystemError(.noEntry, source)) { try fs.move(from: source, to: destination) } @@ -314,10 +317,10 @@ class FileSystemTests: XCTestCase { try fs.writeFileContents(source, bytes: "source1") try fs.writeFileContents(destination, bytes: "destination") - XCTAssertThrows(FileSystemError.alreadyExistsAtDestination) { + XCTAssertThrows(FileSystemError(.alreadyExistsAtDestination, destination)) { try fs.copy(from: source, to: destination) } - XCTAssertThrows(FileSystemError.alreadyExistsAtDestination) { + XCTAssertThrows(FileSystemError(.alreadyExistsAtDestination, destination)) { try fs.move(from: source, to: destination) } @@ -373,22 +376,23 @@ class FileSystemTests: XCTestCase { func testInMemoryBasics() throws { let fs = InMemoryFileSystem() + let doesNotExist = AbsolutePath("/does-not-exist") // exists() - XCTAssert(!fs.exists(AbsolutePath("/does-not-exist"))) + XCTAssert(!fs.exists(doesNotExist)) // isDirectory() - XCTAssert(!fs.isDirectory(AbsolutePath("/does-not-exist"))) + XCTAssert(!fs.isDirectory(doesNotExist)) // isFile() - XCTAssert(!fs.isFile(AbsolutePath("/does-not-exist"))) + XCTAssert(!fs.isFile(doesNotExist)) // isSymlink() - XCTAssert(!fs.isSymlink(AbsolutePath("/does-not-exist"))) + XCTAssert(!fs.isSymlink(doesNotExist)) // getDirectoryContents() - XCTAssertThrows(FileSystemError.noEntry) { - _ = try fs.getDirectoryContents(AbsolutePath("/does-not-exist")) + XCTAssertThrows(FileSystemError(.noEntry, doesNotExist)) { + _ = try fs.getDirectoryContents(doesNotExist) } // createDirectory() @@ -422,9 +426,10 @@ class FileSystemTests: XCTestCase { XCTAssert(fs.isDirectory(subsubdir)) // Check non-recursive failing subdir case. - let newsubdir = AbsolutePath("/very-new-dir/subdir") + let veryNewDir = AbsolutePath("/very-new-dir") + let newsubdir = veryNewDir.appending(component: "subdir") XCTAssert(!fs.isDirectory(newsubdir)) - XCTAssertThrows(FileSystemError.noEntry) { + XCTAssertThrows(FileSystemError(.noEntry, veryNewDir)) { try fs.createDirectory(newsubdir, recursive: false) } XCTAssert(!fs.isDirectory(newsubdir)) @@ -433,10 +438,10 @@ class FileSystemTests: XCTestCase { let filePath = AbsolutePath("/mach_kernel") try! fs.writeFileContents(filePath, bytes: [0xCD, 0x0D]) XCTAssert(fs.exists(filePath) && !fs.isDirectory(filePath)) - XCTAssertThrows(FileSystemError.notDirectory) { + XCTAssertThrows(FileSystemError(.notDirectory, filePath)) { try fs.createDirectory(filePath, recursive: true) } - XCTAssertThrows(FileSystemError.notDirectory) { + XCTAssertThrows(FileSystemError(.notDirectory, filePath)) { try fs.createDirectory(filePath.appending(component: "not-possible"), recursive: true) } XCTAssert(fs.exists(filePath) && !fs.isDirectory(filePath)) @@ -493,42 +498,45 @@ class FileSystemTests: XCTestCase { XCTAssertEqual(try! fs.readFileContents(filePath), "Hello, new world!") // Check read/write of a directory. - XCTAssertThrows(FileSystemError.isDirectory) { + XCTAssertThrows(FileSystemError(.isDirectory, filePath.parentDirectory)) { _ = try fs.readFileContents(filePath.parentDirectory) } - XCTAssertThrows(FileSystemError.isDirectory) { + XCTAssertThrows(FileSystemError(.isDirectory, filePath.parentDirectory)) { try fs.writeFileContents(filePath.parentDirectory, bytes: []) } XCTAssertEqual(try! fs.readFileContents(filePath), "Hello, new world!") // Check read/write against root. - XCTAssertThrows(FileSystemError.isDirectory) { - _ = try fs.readFileContents(AbsolutePath("/")) + let root = AbsolutePath("/") + XCTAssertThrows(FileSystemError(.isDirectory, root)) { + _ = try fs.readFileContents(root) } - XCTAssertThrows(FileSystemError.isDirectory) { - try fs.writeFileContents(AbsolutePath("/"), bytes: []) + XCTAssertThrows(FileSystemError(.isDirectory, root)) { + try fs.writeFileContents(root, bytes: []) } XCTAssert(fs.exists(filePath)) XCTAssertTrue(fs.isFile(filePath)) // Check read/write into a non-directory. - XCTAssertThrows(FileSystemError.notDirectory) { - _ = try fs.readFileContents(filePath.appending(component: "not-possible")) + let notDirectory = filePath.appending(component: "not-possible") + XCTAssertThrows(FileSystemError(.notDirectory, filePath)) { + _ = try fs.readFileContents(notDirectory) } - XCTAssertThrows(FileSystemError.notDirectory) { - try fs.writeFileContents(filePath.appending(component: "not-possible"), bytes: []) + XCTAssertThrows(FileSystemError(.notDirectory, filePath)) { + try fs.writeFileContents(notDirectory, bytes: []) } XCTAssert(fs.exists(filePath)) // Check read/write into a missing directory. - let missingDir = AbsolutePath("/does/not/exist") - XCTAssertThrows(FileSystemError.noEntry) { - _ = try fs.readFileContents(missingDir) + let missingParent = AbsolutePath("/does/not") + let missingFile = missingParent.appending(component: "exist") + XCTAssertThrows(FileSystemError(.noEntry, missingFile)) { + _ = try fs.readFileContents(missingFile) } - XCTAssertThrows(FileSystemError.noEntry) { - try fs.writeFileContents(missingDir, bytes: []) + XCTAssertThrows(FileSystemError(.noEntry, missingParent)) { + try fs.writeFileContents(missingFile, bytes: []) } - XCTAssert(!fs.exists(missingDir)) + XCTAssert(!fs.exists(missingFile)) } func testInMemoryFsCopy() throws { @@ -560,10 +568,10 @@ class FileSystemTests: XCTestCase { // Copy with no source - XCTAssertThrows(FileSystemError.noEntry) { + XCTAssertThrows(FileSystemError(.noEntry, source)) { try fs.copy(from: source, to: destination) } - XCTAssertThrows(FileSystemError.noEntry) { + XCTAssertThrows(FileSystemError(.noEntry, source)) { try fs.move(from: source, to: destination) } @@ -572,10 +580,10 @@ class FileSystemTests: XCTestCase { try fs.writeFileContents(source, bytes: "source1") try fs.writeFileContents(destination, bytes: "destination") - XCTAssertThrows(FileSystemError.alreadyExistsAtDestination) { + XCTAssertThrows(FileSystemError(.alreadyExistsAtDestination, destination)) { try fs.copy(from: source, to: destination) } - XCTAssertThrows(FileSystemError.alreadyExistsAtDestination) { + XCTAssertThrows(FileSystemError(.alreadyExistsAtDestination, destination)) { try fs.move(from: source, to: destination) } @@ -703,13 +711,13 @@ class FileSystemTests: XCTestCase { // Set foo to unwritable. try fs.chmod(.userUnWritable, path: foo) - XCTAssertThrows(FileSystemError.invalidAccess) { + XCTAssertThrows(FileSystemError(.invalidAccess, foo)) { try fs.writeFileContents(foo, bytes: "test") } // Set the directory as unwritable. try fs.chmod(.userUnWritable, path: dir, options: [.recursive, .onlyFiles]) - XCTAssertThrows(FileSystemError.invalidAccess) { + XCTAssertThrows(FileSystemError(.invalidAccess, bar)) { try fs.writeFileContents(bar, bytes: "test") } @@ -721,8 +729,9 @@ class FileSystemTests: XCTestCase { // But not anymore. try fs.chmod(.userUnWritable, path: dir, options: [.recursive]) - XCTAssertThrows(FileSystemError.invalidAccess) { - try fs.writeFileContents(dir.appending(component: "new2"), bytes: "") + let newFile = dir.appending(component: "new2") + XCTAssertThrows(FileSystemError(.invalidAccess, newFile)) { + try fs.writeFileContents(newFile, bytes: "") } try? fs.removeFileTree(bar) diff --git a/Tests/TSCUtilityTests/ArchiverTests.swift b/Tests/TSCUtilityTests/ArchiverTests.swift index bf98e396..d93d28e2 100644 --- a/Tests/TSCUtilityTests/ArchiverTests.swift +++ b/Tests/TSCUtilityTests/ArchiverTests.swift @@ -40,8 +40,9 @@ class ArchiverTests: XCTestCase { let fileSystem = InMemoryFileSystem() let archiver = ZipArchiver(fileSystem: fileSystem) - archiver.extract(from: AbsolutePath("/archive.zip"), to: AbsolutePath("/"), completion: { result in - XCTAssertResultFailure(result, equals: FileSystemError.noEntry) + let archive = AbsolutePath("/archive.zip") + archiver.extract(from: archive, to: AbsolutePath("/"), completion: { result in + XCTAssertResultFailure(result, equals: FileSystemError(.noEntry, archive)) expectation.fulfill() }) @@ -53,8 +54,9 @@ class ArchiverTests: XCTestCase { let fileSystem = InMemoryFileSystem(emptyFiles: "/archive.zip") let archiver = ZipArchiver(fileSystem: fileSystem) - archiver.extract(from: AbsolutePath("/archive.zip"), to: AbsolutePath("/destination"), completion: { result in - XCTAssertResultFailure(result, equals: FileSystemError.notDirectory) + let destination = AbsolutePath("/destination") + archiver.extract(from: AbsolutePath("/archive.zip"), to: destination, completion: { result in + XCTAssertResultFailure(result, equals: FileSystemError(.notDirectory, destination)) expectation.fulfill() }) @@ -66,8 +68,9 @@ class ArchiverTests: XCTestCase { let fileSystem = InMemoryFileSystem(emptyFiles: "/archive.zip", "/destination") let archiver = ZipArchiver(fileSystem: fileSystem) - archiver.extract(from: AbsolutePath("/archive.zip"), to: AbsolutePath("/destination"), completion: { result in - XCTAssertResultFailure(result, equals: FileSystemError.notDirectory) + let destination = AbsolutePath("/destination") + archiver.extract(from: AbsolutePath("/archive.zip"), to: destination, completion: { result in + XCTAssertResultFailure(result, equals: FileSystemError(.notDirectory, destination)) expectation.fulfill() })