diff --git a/Sources/ZIPFoundation/FileManager+ZIP.swift b/Sources/ZIPFoundation/FileManager+ZIP.swift index 770366b6..2ce73d72 100644 --- a/Sources/ZIPFoundation/FileManager+ZIP.swift +++ b/Sources/ZIPFoundation/FileManager+ZIP.swift @@ -29,10 +29,15 @@ extension FileManager { /// By default, `zipItem` will create uncompressed archives. /// - progress: A progress object that can be used to track or cancel the zip operation. /// - Throws: Throws an error if the source item does not exist or the destination URL is not writable. - public func zipItem(at sourceURL: URL, to destinationURL: URL, - shouldKeepParent: Bool = true, compressionMethod: CompressionMethod = .none, - progress: Progress? = nil) throws { + public func zipItem( + at sourceURL: URL, + to destinationURL: URL, + shouldKeepParent: Bool = true, + compressionMethod: CompressionMethod = .none, + progress: Progress? = nil + ) throws { let fileManager = FileManager() + // FIXME: Somehow testZipItemErrorConditions() fails, if the method doesn't check for source's existance here. guard fileManager.itemExists(at: sourceURL) else { throw CocoaError(.fileReadNoSuchFile, userInfo: [NSFilePathErrorKey: sourceURL.path]) } @@ -42,6 +47,42 @@ extension FileManager { guard let archive = Archive(url: destinationURL, accessMode: .create) else { throw Archive.ArchiveError.unwritableArchive } + try zipItem( + at: sourceURL, + to: archive, + shouldKeepParent: shouldKeepParent, + compressionMethod: compressionMethod, + progress: progress + ) + } + + /// Zips the file or direcory contents at the specified source URL to the given archive. + /// + /// If the item at the source URL is a directory, the directory itself will be + /// represented within the ZIP `Archive`. Calling this method with a directory URL + /// `file:///path/directory/` will create an archive with a `directory/` entry at the root level. + /// You can override this behavior by passing `false` for `shouldKeepParent`. In that case, the contents + /// of the source directory will be placed at the root of the archive. + /// - Parameters: + /// - sourceURL: The file URL pointing to an existing file or directory. + /// - archive: The file URL that identifies the destination of the zip operation. + /// - shouldKeepParent: Indicates that the directory name of a source item should be used as root element + /// within the archive. Default is `true`. + /// - compressionMethod: Indicates the `CompressionMethod` that should be applied. + /// By default, `zipItem` will create uncompressed archives. + /// - progress: A progress object that can be used to track or cancel the zip operation. + /// - Throws: Throws an error if the source item does not exist. + public func zipItem( + at sourceURL: URL, + to archive: Archive, + shouldKeepParent: Bool = true, + compressionMethod: CompressionMethod = .none, + progress: Progress? = nil + ) throws { + let fileManager = FileManager() + guard fileManager.itemExists(at: sourceURL) else { + throw CocoaError(.fileReadNoSuchFile, userInfo: [NSFilePathErrorKey: sourceURL.path]) + } let isDirectory = try FileManager.typeForItem(at: sourceURL) == .directory if isDirectory { let subPaths = try self.subpathsOfDirectory(atPath: sourceURL.path) @@ -54,9 +95,9 @@ extension FileManager { }) progress.totalUnitCount = totalUnitCount } - - // If the caller wants to keep the parent directory, we use the lastPathComponent of the source URL - // as common base for all entries (similar to macOS' Archive Utility.app) + // If the caller wants to keep the parent directory, + // we use the lastPathComponent of the source URL as common base for all entries + // (similar to macOS' Archive Utility.app) let directoryPrefix = sourceURL.lastPathComponent for entryPath in subPaths { let finalEntryPath = shouldKeepParent ? directoryPrefix + "/" + entryPath : entryPath @@ -80,6 +121,41 @@ extension FileManager { } } + #if swift(>=5) + /// Zips the file or direcory contents at the specified source URL and returns an archive containing them. + /// + /// If the item at the source URL is a directory, the directory itself will be represented within the ZIP `Archive`. + /// Calling this method with a directory URL `file:///path/directory/` will create an archive with a `directory/` entry at the root level. + /// You can override this behavior by passing `false` for `shouldKeepParent`. + /// In that case, the contents of the source directory will be placed at the root of the archive. + /// - Parameters: + /// - sourceURL: The file URL pointing to an existing file or directory. + /// - shouldKeepParent: Indicates that the directory name of a source item should be used as root element within the archive. + /// Default is `true`. + /// - compressionMethod: Indicates the `CompressionMethod` that should be applied. + /// By default, `zipItem` will create uncompressed archives. + /// - progress: A progress object that can be used to track or cancel the zip operation. + /// - Returns: If successful at creating an in-memory Archive, an archive containing the zipped file or direcory contents at the specified source URL; + /// if not, `nil`. + /// - Throws: Throws an error if the source item does not exist. + public func itemZipped( + from sourceURL: URL, + shouldKeepParent: Bool = true, + compressionMethod: CompressionMethod = .none, + progress: Progress? = nil + ) throws -> Archive? { + guard let archive = Archive(accessMode: .create) else { return nil } + try zipItem( + at: sourceURL, + to: archive, + shouldKeepParent: shouldKeepParent, + compressionMethod: compressionMethod, + progress: progress + ) + return archive + } + #endif + /// Unzips the contents at the specified source URL to the destination URL. /// /// - Parameters: @@ -89,8 +165,13 @@ extension FileManager { /// - progress: A progress object that can be used to track or cancel the unzip operation. /// - preferredEncoding: Encoding for entry paths. Overrides the encoding specified in the archive. /// - Throws: Throws an error if the source item does not exist or the destination URL is not writable. - public func unzipItem(at sourceURL: URL, to destinationURL: URL, skipCRC32: Bool = false, - progress: Progress? = nil, preferredEncoding: String.Encoding? = nil) throws { + public func unzipItem( + at sourceURL: URL, + to destinationURL: URL, + skipCRC32: Bool = false, + progress: Progress? = nil, + preferredEncoding: String.Encoding? = nil + ) throws { let fileManager = FileManager() guard fileManager.itemExists(at: sourceURL) else { throw CocoaError(.fileReadNoSuchFile, userInfo: [NSFilePathErrorKey: sourceURL.path]) @@ -98,6 +179,30 @@ extension FileManager { guard let archive = Archive(url: sourceURL, accessMode: .read, preferredEncoding: preferredEncoding) else { throw Archive.ArchiveError.unreadableArchive } + try unzip( + archive, + to: destinationURL, + skipCRC32: skipCRC32, + progress: progress, + preferredEncoding: preferredEncoding + ) + } + + /// Unzips the contents of the specified archive to the destination URL. + /// + /// - Parameters: + /// - archive: The archive to be unzipped. + /// - destinationURL: The file URL that identifies the destination directory of the unzip operation. + /// - skipCRC32: Optional flag to skip calculation of the CRC32 checksum to improve performance. + /// - progress: A progress object that can be used to track or cancel the unzip operation. + /// - preferredEncoding: Encoding for entry paths. Overrides the encoding specified in the archive. + /// - Throws: Throws an error if destination URL is not writable. + public func unzip(_ archive: Archive, to destinationURL: URL, skipCRC32: Bool = false, + progress: Progress? = nil, preferredEncoding: String.Encoding? = nil) throws { + let fileManager = FileManager() + guard fileManager.isWritableFile(atPath: destinationURL.path) else { + throw CocoaError(.fileWriteNoPermission, userInfo: [NSFilePathErrorKey: destinationURL.path]) + } // Defer extraction of symlinks until all files & directories have been created. // This is necessary because we can't create links to files that haven't been created yet. let sortedEntries = archive.sorted { (left, right) -> Bool in @@ -113,7 +218,6 @@ extension FileManager { totalUnitCount = sortedEntries.reduce(0, { $0 + archive.totalUnitCountForReading($1) }) progress.totalUnitCount = totalUnitCount } - for entry in sortedEntries { let path = preferredEncoding == nil ? entry.path : entry.path(using: preferredEncoding!) let destinationEntryURL = destinationURL.appendingPathComponent(path) @@ -136,10 +240,10 @@ extension FileManager { func itemExists(at url: URL) -> Bool { // Use `URL.checkResourceIsReachable()` instead of `FileManager.fileExists()` here // because we don't want implicit symlink resolution. - // As per documentation, `FileManager.fileExists()` traverses symlinks and therefore a broken symlink - // would throw a `.fileReadNoSuchFile` false positive error. - // For ZIP files it may be intended to archive "broken" symlinks because they might be - // resolvable again when extracting the archive to a different destination. + // As per documentation, `FileManager.fileExists()` traverses symlinks + // and therefore a broken symlink would throw a `.fileReadNoSuchFile` false positive error. + // For ZIP files it may be intended to archive "broken" symlinks + // because they might be resolvable again when extracting the archive to a different destination. return (try? url.checkResourceIsReachable()) == true } diff --git a/Tests/ZIPFoundationTests/Resources/testUnzipArchive.zip b/Tests/ZIPFoundationTests/Resources/testUnzipArchive.zip new file mode 100644 index 00000000..69e3e2c6 Binary files /dev/null and b/Tests/ZIPFoundationTests/Resources/testUnzipArchive.zip differ diff --git a/Tests/ZIPFoundationTests/Resources/testUnzipArchiveErrorConditions.png b/Tests/ZIPFoundationTests/Resources/testUnzipArchiveErrorConditions.png new file mode 100644 index 00000000..41002265 Binary files /dev/null and b/Tests/ZIPFoundationTests/Resources/testUnzipArchiveErrorConditions.png differ diff --git a/Tests/ZIPFoundationTests/Resources/testUnzipArchiveErrorConditions.zip b/Tests/ZIPFoundationTests/Resources/testUnzipArchiveErrorConditions.zip new file mode 100644 index 00000000..205bb9f3 Binary files /dev/null and b/Tests/ZIPFoundationTests/Resources/testUnzipArchiveErrorConditions.zip differ diff --git a/Tests/ZIPFoundationTests/Resources/testUnzipArchiveProgress.zip b/Tests/ZIPFoundationTests/Resources/testUnzipArchiveProgress.zip new file mode 100644 index 00000000..69e3e2c6 Binary files /dev/null and b/Tests/ZIPFoundationTests/Resources/testUnzipArchiveProgress.zip differ diff --git a/Tests/ZIPFoundationTests/Resources/testUnzipArchiveWithPreferredEncoding.zip b/Tests/ZIPFoundationTests/Resources/testUnzipArchiveWithPreferredEncoding.zip new file mode 100644 index 00000000..884cd81c Binary files /dev/null and b/Tests/ZIPFoundationTests/Resources/testUnzipArchiveWithPreferredEncoding.zip differ diff --git a/Tests/ZIPFoundationTests/Resources/testZipItemToArchive.png b/Tests/ZIPFoundationTests/Resources/testZipItemToArchive.png new file mode 100644 index 00000000..41002265 Binary files /dev/null and b/Tests/ZIPFoundationTests/Resources/testZipItemToArchive.png differ diff --git a/Tests/ZIPFoundationTests/ZIPFoundationFileManagerTests.swift b/Tests/ZIPFoundationTests/ZIPFoundationFileManagerTests.swift old mode 100755 new mode 100644 index b148de01..e2a6d53a --- a/Tests/ZIPFoundationTests/ZIPFoundationFileManagerTests.swift +++ b/Tests/ZIPFoundationTests/ZIPFoundationFileManagerTests.swift @@ -100,6 +100,153 @@ extension ZIPFoundationTests { } } + func testZipItemToArchive() { + let fileManager = FileManager() + let assetURL = self.resourceURL(for: #function, pathExtension: "png") + let archive = self.archive(for: #function, mode: .create) + do { + try fileManager.zipItem(at: assetURL, to: archive) + } catch { XCTFail("Failed to zip item at URL:\(assetURL)") } + XCTAssertNotNil(archive[assetURL.lastPathComponent]) + XCTAssert(archive.checkIntegrity()) + var directoryURL = ZIPFoundationTests.tempZipDirectoryURL + directoryURL.appendPathComponent(ProcessInfo.processInfo.globallyUniqueString) + let newAssetURL = directoryURL.appendingPathComponent(assetURL.lastPathComponent) + do { + try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) + try fileManager.createDirectory(at: directoryURL.appendingPathComponent("nested"), + withIntermediateDirectories: true, attributes: nil) + try fileManager.copyItem(at: assetURL, to: newAssetURL) + try fileManager.createSymbolicLink(at: directoryURL.appendingPathComponent("link"), + withDestinationURL: newAssetURL) + try fileManager.zipItem(at: directoryURL, to: archive) + } catch { XCTFail("Unexpected error while trying to zip via fileManager.") } + #if swift(>=5.0) + guard let inMemoryArchive = Archive(accessMode: .create) else { + XCTFail("Failed to create an in-memory archive.") + return + } + do { + try fileManager.zipItem(at: assetURL, to: inMemoryArchive) + } catch { XCTFail("Failed to zip item at URL:\(assetURL)") } + XCTAssertNotNil(inMemoryArchive[assetURL.lastPathComponent]) + XCTAssert(inMemoryArchive.checkIntegrity()) + do { + try fileManager.zipItem(at: directoryURL, to: inMemoryArchive) + } catch { XCTFail("Unexpected error while trying to zip via fileManager.") } + #endif + } + + func testZipItemToArchiveErrorConditions() { + let fileManager = FileManager() + let archive = self.archive(for: #function, mode: .create) + do { + try fileManager.zipItem(at: URL(fileURLWithPath: "/nothing"), to: archive) + XCTFail("Error when zipping non-existant archive not raised") + } catch let error as CocoaError { XCTAssert(error.code == CocoaError.fileReadNoSuchFile) + } catch { + XCTFail("Unexpected error while trying to zip via fileManager.") + } + var unreadableFileURL = ZIPFoundationTests.tempZipDirectoryURL + let pathComponent = self.pathComponent(for: #function) + "Directory" + do { + unreadableFileURL.appendPathComponent(pathComponent) + unreadableFileURL.appendPathComponent(ProcessInfo.processInfo.globallyUniqueString) + try fileManager.createParentDirectoryStructure(for: unreadableFileURL) + let noPermissionAttributes = [FileAttributeKey.posixPermissions: Int16(0o000)] + let result = fileManager.createFile(atPath: unreadableFileURL.path, contents: nil, + attributes: noPermissionAttributes) + XCTAssert(result == true) + try fileManager.zipItem(at: unreadableFileURL.deletingLastPathComponent(), to: archive) + } catch let error as CocoaError { + XCTAssert(error.code == CocoaError.fileReadNoPermission) + } catch { + XCTFail("Unexpected error while trying to zip via fileManager.") + } + #if swift(>=5.0) + guard let inMemoryArchive = Archive(accessMode: .create) else { + XCTFail("Failed to create an in-memory archive.") + return + } + do { + try fileManager.zipItem(at: URL(fileURLWithPath: "/nothing"), to: inMemoryArchive) + XCTFail("Error when zipping non-existant archive not raised") + } catch let error as CocoaError { XCTAssert(error.code == CocoaError.fileReadNoSuchFile) + } catch { + XCTFail("Unexpected error while trying to zip via fileManager.") + } + do { + unreadableFileURL.appendPathComponent(pathComponent) + unreadableFileURL.appendPathComponent(ProcessInfo.processInfo.globallyUniqueString) + try fileManager.createParentDirectoryStructure(for: unreadableFileURL) + let noPermissionAttributes = [FileAttributeKey.posixPermissions: Int16(0o000)] + let result = fileManager.createFile(atPath: unreadableFileURL.path, contents: nil, + attributes: noPermissionAttributes) + XCTAssert(result == true) + try fileManager.zipItem(at: unreadableFileURL.deletingLastPathComponent(), to: inMemoryArchive) + } catch let error as CocoaError { + XCTAssert(error.code == CocoaError.fileReadNoPermission) + } catch { + XCTFail("Unexpected error while trying to zip via fileManager.") + } + #endif + } + + #if swift(>=5.0) + func testZipItemAndReturnArchive() { + let fileManager = FileManager() + let assetURL = self.resourceURL(for: #function, pathExtension: "png") + var archive: Archive + do { + archive = try fileManager.itemZipped(from: assetURL)! + } catch { XCTFail("Failed to zip item at URL:\(assetURL)") } + XCTAssertNotNil(archive[assetURL.lastPathComponent]) + XCTAssert(archive.checkIntegrity()) + var directoryURL = ZIPFoundationTests.tempZipDirectoryURL + directoryURL.appendPathComponent(ProcessInfo.processInfo.globallyUniqueString) + let newAssetURL = directoryURL.appendingPathComponent(assetURL.lastPathComponent) + do { + try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) + try fileManager.createDirectory(at: directoryURL.appendingPathComponent("nested"), + withIntermediateDirectories: true, attributes: nil) + try fileManager.copyItem(at: assetURL, to: newAssetURL) + try fileManager.createSymbolicLink(at: directoryURL.appendingPathComponent("link"), + withDestinationURL: newAssetURL) + archive = try fileManager.itemZipped(from: directoryURL)! + } catch { XCTFail("Unexpected error while trying to zip via fileManager.") } + } + #endif + + #if swift(>=5.0) + func testZipItemAndReturnArchiveErrorConditions() { + let fileManager = FileManager() + var archive: Archive + do { + archive = try fileManager.itemZipped(from: URL(fileURLWithPath: "/nothing"))! + XCTFail("Error when zipping non-existant archive not raised") + } catch let error as CocoaError { XCTAssert(error.code == CocoaError.fileReadNoSuchFile) + } catch { + XCTFail("Unexpected error while trying to zip via fileManager.") + } + var unreadableFileURL = ZIPFoundationTests.tempZipDirectoryURL + let pathComponent = self.pathComponent(for: #function) + "Directory" + do { + unreadableFileURL.appendPathComponent(pathComponent) + unreadableFileURL.appendPathComponent(ProcessInfo.processInfo.globallyUniqueString) + try fileManager.createParentDirectoryStructure(for: unreadableFileURL) + let noPermissionAttributes = [FileAttributeKey.posixPermissions: Int16(0o000)] + let result = fileManager.createFile(atPath: unreadableFileURL.path, contents: nil, + attributes: noPermissionAttributes) + XCTAssert(result == true) + archive = try fileManager.itemZipped(from: unreadableFileURL.deletingLastPathComponent())! + } catch let error as CocoaError { + XCTAssert(error.code == CocoaError.fileReadNoPermission) + } catch { + XCTFail("Unexpected error while trying to zip via fileManager.") + } + } + #endif + func testUnzipItem() { let fileManager = FileManager() let archive = self.archive(for: #function, mode: .read) @@ -171,6 +318,126 @@ extension ZIPFoundationTests { } catch { XCTFail("Unexpected error while trying to unzip via fileManager."); return } } + func testUnzipArchive() { + let fileManager = FileManager() + let archive = self.archive(for: #function, mode: .read) + let destinationURL = self.createDirectory(for: #function) + do { + try fileManager.unzip(archive, to: destinationURL) + } catch { + XCTFail("Failed to extract item."); return + } + var itemsExist = false + for entry in archive { + let directoryURL = destinationURL.appendingPathComponent(entry.path) + itemsExist = fileManager.itemExists(at: directoryURL) + if !itemsExist { break } + } + XCTAssert(itemsExist) + #if swift(>=5.0) + guard let inMemoryArchive = Archive(accessMode: .create) else { + XCTFail("Failed to create an in-memory archive.") + return + } + do { + try fileManager.unzip(inMemoryArchive, to: destinationURL) + } catch { + XCTFail("Failed to extract item."); return + } + itemsExist = false + for entry in inMemoryArchive { + let directoryURL = destinationURL.appendingPathComponent(entry.path) + itemsExist = fileManager.itemExists(at: directoryURL) + if !itemsExist { break } + } + XCTAssert(itemsExist) + #endif + } + + func testUnzipArchiveWithPreferredEncoding() { + let fileManager = FileManager() + let encoding = String.Encoding.utf8 + let archive = self.archive(for: #function, mode: .read, preferredEncoding: encoding) + let destinationURL = self.createDirectory(for: #function) + do { + try fileManager.unzip(archive, to: destinationURL, preferredEncoding: encoding) + } catch { + XCTFail("Failed to extract item."); return + } + var itemsExist = false + for entry in archive { + let directoryURL = destinationURL.appendingPathComponent(entry.path(using: encoding)) + itemsExist = fileManager.itemExists(at: directoryURL) + if !itemsExist { break } + } + XCTAssert(itemsExist) + #if swift(>=5.0) + guard let inMemoryArchive = Archive(accessMode: .create) else { + XCTFail("Failed to create an in-memory archive.") + return + } + do { + try fileManager.unzip(inMemoryArchive, to: destinationURL, preferredEncoding: encoding) + } catch { + XCTFail("Failed to extract item."); return + } + itemsExist = false + for entry in inMemoryArchive { + let directoryURL = destinationURL.appendingPathComponent(entry.path(using: encoding)) + itemsExist = fileManager.itemExists(at: directoryURL) + if !itemsExist { break } + } + XCTAssert(itemsExist) + #endif + } + + func testUnzipArchiveErrorConditions() { + let archive = self.archive(for: #function, mode: .read) + let destinationURL = ZIPFoundationTests.tempZipDirectoryURL + var existingURL = destinationURL + existingURL.appendPathComponent("test") + existingURL.appendPathComponent("faust.txt") + let fileManager = FileManager() + do { + try fileManager.createParentDirectoryStructure(for: existingURL) + fileManager.createFile(atPath: existingURL.path, contents: Data(), attributes: nil) + try fileManager.unzip(archive, to: destinationURL) + XCTFail("Error when unzipping archive to existing destination not raised.") + } catch let error as CocoaError { + XCTAssertTrue(error.code == CocoaError.fileWriteFileExists) + } catch { + XCTFail("Unexpected error while trying to unzip via fileManager."); return + } + do { + let unwritableURL = URL(fileURLWithPath: "/test.zip") + try fileManager.unzip(archive, to: unwritableURL) + XCTFail("Error when unzipping to unwritable destination not raised.") + } catch let error as Archive.ArchiveError { XCTAssert(error == .unwritableArchive) + } catch CocoaError.fileWriteNoPermission { + } catch { XCTFail("Unexpected error while trying to unzip via fileManager.") } + #if swift(>=5.0) + guard let inMemoryArchive = Archive(accessMode: .create) else { + XCTFail("Failed to create an in-memory archive.") + return + } + do { + try fileManager.unzip(inMemoryArchive, to: destinationURL) + XCTFail("Error when unzipping archive to existing destination not raised.") + } catch let error as CocoaError { + XCTAssertTrue(error.code == CocoaError.fileWriteFileExists) + } catch { + XCTFail("Unexpected error while trying to unzip via fileManager."); return + } + do { + let unwritableURL = URL(fileURLWithPath: "/test.zip") + try fileManager.unzip(inMemoryArchive, to: unwritableURL) + XCTFail("Error when unzipping to unwritable destination not raised.") + } catch let error as Archive.ArchiveError { XCTAssert(error == .unwritableArchive) + } catch CocoaError.fileWriteNoPermission { + } catch { XCTFail("Unexpected error while trying to unzip via fileManager.") } + #endif + } + func testDirectoryCreationHelperMethods() { let processInfo = ProcessInfo.processInfo var nestedURL = ZIPFoundationTests.tempZipDirectoryURL diff --git a/Tests/ZIPFoundationTests/ZIPFoundationProgressTests.swift b/Tests/ZIPFoundationTests/ZIPFoundationProgressTests.swift index 7d99c539..76e4f340 100644 --- a/Tests/ZIPFoundationTests/ZIPFoundationProgressTests.swift +++ b/Tests/ZIPFoundationTests/ZIPFoundationProgressTests.swift @@ -179,5 +179,30 @@ extension ZIPFoundationTests { } self.wait(for: [expectation], timeout: 10.0) } + + func testUnzipArchiveProgress() { + let fileManager = FileManager() + let archive = self.archive(for: #function, mode: .read) + let destinationURL = self.createDirectory(for: #function) + let progress = Progress() + let expectation = self.keyValueObservingExpectation(for: progress, + keyPath: #keyPath(Progress.fractionCompleted), + expectedValue: 1.0) + DispatchQueue.global().async { + do { + try fileManager.unzip(archive, to: destinationURL, progress: progress) + } catch { + XCTFail("Failed to extract item."); return + } + var itemsExist = false + for entry in archive { + let directoryURL = destinationURL.appendingPathComponent(entry.path) + itemsExist = fileManager.itemExists(at: directoryURL) + if !itemsExist { break } + } + XCTAssert(itemsExist) + } + self.wait(for: [expectation], timeout: 10.0) + } } #endif diff --git a/Tests/ZIPFoundationTests/ZIPFoundationTests.swift b/Tests/ZIPFoundationTests/ZIPFoundationTests.swift index 89b5b4b7..d67ec719 100644 --- a/Tests/ZIPFoundationTests/ZIPFoundationTests.swift +++ b/Tests/ZIPFoundationTests/ZIPFoundationTests.swift @@ -53,6 +53,8 @@ class ZIPFoundationTests: XCTestCase { } catch { XCTFail("Unexpected error while trying to set up test resources.") } + + // TODO: Setup empty in-memory archive for testing here. } override class func tearDown() { @@ -303,10 +305,14 @@ extension ZIPFoundationTests { ("testRemoveUncompressedEntry", testRemoveUncompressedEntry), ("testUniqueTemporaryDirectoryURL", testUniqueTemporaryDirectoryURL), ("testTraversalAttack", testTraversalAttack), + ("testUnzipArchive", testUnzipArchive), + ("testUnzipArchiveWithPreferredEncoding", testUnzipArchiveWithPreferredEncoding), + ("testUnzipArchiveErrorConditions", testUnzipArchiveErrorConditions), ("testUnzipItem", testUnzipItem), ("testUnzipItemWithPreferredEncoding", testUnzipItemWithPreferredEncoding), ("testUnzipItemErrorConditions", testUnzipItemErrorConditions), ("testZipItem", testZipItem), + ("testZipItemToArchive", testZipItemToArchive), ("testLinuxTestSuiteIncludesAllTests", testLinuxTestSuiteIncludesAllTests) ] + darwinOnlyTests + swift5OnlyTests } @@ -318,6 +324,7 @@ extension ZIPFoundationTests { ("testFileModificationDateHelperMethods", testFileModificationDateHelperMethods), ("testZipItemProgress", testZipItemProgress), ("testUnzipItemProgress", testUnzipItemProgress), + ("testUnzipArchiveProgress", testUnzipArchiveProgress), ("testRemoveEntryProgress", testRemoveEntryProgress), ("testReplaceCurrentArchiveWithArchiveCrossLink", testReplaceCurrentArchiveWithArchiveCrossLink), ("testArchiveAddUncompressedEntryProgress", testArchiveAddUncompressedEntryProgress), @@ -328,7 +335,8 @@ extension ZIPFoundationTests { ("testReadChunkErrorConditions", testReadChunkErrorConditions), ("testWriteChunkErrorConditions", testWriteChunkErrorConditions), // Fails for Swift < 4.2 on Linux. We can re-enable that when we drop Swift 4.x support - ("testZipItemErrorConditions", testZipItemErrorConditions) + ("testZipItemErrorConditions", testZipItemErrorConditions), + ("testZipItemToArchiveErrorConditions", testZipItemToArchiveErrorConditions) ] #else return [] @@ -347,7 +355,9 @@ extension ZIPFoundationTests { ("testWriteOnlyFile", testWriteOnlyFile), ("testReadOnlyFile", testReadOnlyFile), ("testReadOnlySlicedFile", testReadOnlySlicedFile), - ("testReadWriteFile", testReadWriteFile) + ("testReadWriteFile", testReadWriteFile), + ("testZipItemAndReturnArchive", testZipItemAndReturnArchive), + ("testZipItemAndReturnArchiveErrorConditions", testZipItemAndReturnArchiveErrorConditions) ] #else return []