mirror of
https://github.com/tsolomko/SWCompression.git
synced 2026-06-23 14:56:41 +00:00
368 lines
16 KiB
Swift
368 lines
16 KiB
Swift
// Copyright (c) 2026 Timofey Solomko
|
|
// Licensed under MIT License
|
|
//
|
|
// See LICENSE for license information
|
|
|
|
import XCTest
|
|
import SWCompression
|
|
|
|
class TarWriterTests: XCTestCase {
|
|
|
|
private static let tempDir = URL(fileURLWithPath: NSTemporaryDirectory())
|
|
.appendingPathComponent("TestSWCompression-" + UUID().uuidString, isDirectory: true)
|
|
|
|
class override func setUp() {
|
|
do {
|
|
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true, attributes: nil)
|
|
} catch let error {
|
|
fatalError("TarWriterTests.setUp(): unable to create temporary directory: \(error)")
|
|
}
|
|
}
|
|
|
|
class override func tearDown() {
|
|
do {
|
|
try FileManager.default.removeItem(at: tempDir)
|
|
} catch let error {
|
|
fatalError("TarWriterTests.tearDown(): unable to remove temporary directory: \(error)")
|
|
}
|
|
}
|
|
|
|
private static func generateContainerData(_ entries: [TarEntry], format: TarContainer.Format = .pax) throws -> Data {
|
|
let tempFileUrl = tempDir.appendingPathComponent(UUID().uuidString, isDirectory: false)
|
|
try "".write(to: tempFileUrl, atomically: true, encoding: .utf8)
|
|
let handle = try FileHandle(forWritingTo: tempFileUrl)
|
|
var writer = TarWriter(fileHandle: handle, force: format)
|
|
for entry in entries {
|
|
try writer.append(entry)
|
|
}
|
|
try writer.finalize()
|
|
try handle.close()
|
|
return try Data(contentsOf: tempFileUrl)
|
|
}
|
|
|
|
func test1() throws {
|
|
var info = TarEntryInfo(name: "file.txt", type: .regular)
|
|
info.ownerUserName = "timofeysolomko"
|
|
info.ownerGroupName = "staff"
|
|
info.ownerID = 501
|
|
info.groupID = 20
|
|
info.permissions = Permissions(rawValue: 420)
|
|
// We have to convert time interval to int, since tar can't store fractional timestamps, so we lose in accuracy.
|
|
let intTimeInterval = Int(Date().timeIntervalSince1970)
|
|
let date = Date(timeIntervalSince1970: Double(intTimeInterval))
|
|
info.modificationTime = date
|
|
info.creationTime = date
|
|
info.accessTime = date
|
|
info.comment = "comment"
|
|
let data = Data("Hello, World!\n".utf8)
|
|
let entry = TarEntry(info: info, data: data)
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry])
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .pax)
|
|
let newEntries = try TarContainer.open(container: containerData)
|
|
|
|
XCTAssertEqual(newEntries.count, 1)
|
|
XCTAssertEqual(newEntries[0].info.name, "file.txt")
|
|
XCTAssertEqual(newEntries[0].info.type, .regular)
|
|
XCTAssertEqual(newEntries[0].info.size, 14)
|
|
XCTAssertEqual(newEntries[0].info.ownerUserName, "timofeysolomko")
|
|
XCTAssertEqual(newEntries[0].info.ownerGroupName, "staff")
|
|
XCTAssertEqual(newEntries[0].info.ownerID, 501)
|
|
XCTAssertEqual(newEntries[0].info.groupID, 20)
|
|
XCTAssertEqual(newEntries[0].info.permissions, Permissions(rawValue: 420))
|
|
XCTAssertEqual(newEntries[0].info.modificationTime, date)
|
|
XCTAssertEqual(newEntries[0].info.creationTime, date)
|
|
XCTAssertEqual(newEntries[0].info.accessTime, date)
|
|
XCTAssertEqual(newEntries[0].info.comment, "comment")
|
|
XCTAssertEqual(newEntries[0].data, data)
|
|
}
|
|
|
|
func test2() throws {
|
|
let dict = [
|
|
"SWCompression/Tests/TAR": "value",
|
|
"key": "valuevaluevaluevaluevaluevaluevaluevaluevaluevaluevaluevaluevaluevaluevaluevaluevaluevalue22"
|
|
]
|
|
|
|
var info = TarEntryInfo(name: "symbolic-link", type: .symbolicLink)
|
|
info.accessTime = Date(timeIntervalSince1970: 1)
|
|
info.creationTime = Date(timeIntervalSince1970: 2)
|
|
info.modificationTime = Date(timeIntervalSince1970: 0)
|
|
info.permissions = Permissions(rawValue: 420)
|
|
info.permissions?.insert(.executeOwner)
|
|
info.ownerID = 250
|
|
info.groupID = 250
|
|
info.ownerUserName = "testUserName"
|
|
info.ownerGroupName = "testGroupName"
|
|
info.deviceMajorNumber = 1
|
|
info.deviceMinorNumber = 2
|
|
info.charset = "UTF-8"
|
|
info.comment = "some comment..."
|
|
info.linkName = "file"
|
|
info.unknownExtendedHeaderRecords = dict
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry])
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .pax)
|
|
let newInfo = try TarContainer.open(container: containerData)[0].info
|
|
|
|
XCTAssertEqual(newInfo.name, "symbolic-link")
|
|
XCTAssertEqual(newInfo.type, .symbolicLink)
|
|
XCTAssertEqual(newInfo.permissions?.rawValue, 484)
|
|
XCTAssertEqual(newInfo.ownerID, 250)
|
|
XCTAssertEqual(newInfo.groupID, 250)
|
|
XCTAssertEqual(newInfo.size, 0)
|
|
XCTAssertEqual(newInfo.modificationTime?.timeIntervalSince1970, 0)
|
|
XCTAssertEqual(newInfo.linkName, "file")
|
|
XCTAssertEqual(newInfo.ownerUserName, "testUserName")
|
|
XCTAssertEqual(newInfo.ownerGroupName, "testGroupName")
|
|
XCTAssertEqual(newInfo.deviceMajorNumber, 1)
|
|
XCTAssertEqual(newInfo.deviceMinorNumber, 2)
|
|
XCTAssertEqual(newInfo.accessTime?.timeIntervalSince1970, 1)
|
|
XCTAssertEqual(newInfo.creationTime?.timeIntervalSince1970, 2)
|
|
XCTAssertEqual(newInfo.charset, "UTF-8")
|
|
XCTAssertEqual(newInfo.comment, "some comment...")
|
|
XCTAssertEqual(newInfo.unknownExtendedHeaderRecords, dict)
|
|
}
|
|
|
|
func testLongName() throws {
|
|
var info = TarEntryInfo(name: "", type: .regular)
|
|
info.name = "path/to/"
|
|
info.name.append(String(repeating: "readme/", count: 15))
|
|
info.name.append("readme.txt")
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry])
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .pax)
|
|
let newInfo = try TarContainer.open(container: containerData)[0].info
|
|
|
|
// This name should fit into ustar format using "prefix" field
|
|
XCTAssertEqual(newInfo.name, info.name)
|
|
}
|
|
|
|
func testVeryLongName() throws {
|
|
var info = TarEntryInfo(name: "", type: .regular)
|
|
info.name = "path/to/"
|
|
info.name.append(String(repeating: "readme/", count: 25))
|
|
info.name.append("readme.txt")
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry])
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .pax)
|
|
let newInfo = try TarContainer.open(container: containerData)[0].info
|
|
|
|
XCTAssertEqual(newInfo.name, info.name)
|
|
}
|
|
|
|
func testLongDirectoryName() throws {
|
|
// Tests what happens to the filename's trailing slash when "prefix" field is used.
|
|
var info = TarEntryInfo(name: "", type: .regular)
|
|
info.name = "path/to/"
|
|
info.name.append(String(repeating: "readme/", count: 15))
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry])
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .pax)
|
|
let newInfo = try TarContainer.open(container: containerData)[0].info
|
|
|
|
XCTAssertEqual(newInfo.name, info.name)
|
|
}
|
|
|
|
func testUnicode() throws {
|
|
let date = Date(timeIntervalSince1970: 1300000)
|
|
var info = TarEntryInfo(name: "ссылка", type: .symbolicLink)
|
|
info.accessTime = date
|
|
info.creationTime = date
|
|
info.modificationTime = date
|
|
info.permissions = Permissions(rawValue: 420)
|
|
info.ownerID = 501
|
|
info.groupID = 20
|
|
info.ownerUserName = "timofeysolomko"
|
|
info.ownerGroupName = "staff"
|
|
info.deviceMajorNumber = 1
|
|
info.deviceMinorNumber = 2
|
|
info.comment = "комментарий"
|
|
info.linkName = "путь/к/файлу"
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry])
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .pax)
|
|
let newInfo = try TarContainer.open(container: containerData)[0].info
|
|
|
|
XCTAssertEqual(newInfo.name, "ссылка")
|
|
XCTAssertEqual(newInfo.type, .symbolicLink)
|
|
XCTAssertEqual(newInfo.permissions?.rawValue, 420)
|
|
XCTAssertEqual(newInfo.ownerID, 501)
|
|
XCTAssertEqual(newInfo.groupID, 20)
|
|
XCTAssertEqual(newInfo.size, 0)
|
|
XCTAssertEqual(newInfo.modificationTime?.timeIntervalSince1970, 1300000)
|
|
XCTAssertEqual(newInfo.linkName, "путь/к/файлу")
|
|
XCTAssertEqual(newInfo.ownerUserName, "timofeysolomko")
|
|
XCTAssertEqual(newInfo.ownerGroupName, "staff")
|
|
XCTAssertEqual(newInfo.accessTime?.timeIntervalSince1970, 1300000)
|
|
XCTAssertEqual(newInfo.creationTime?.timeIntervalSince1970, 1300000)
|
|
XCTAssertEqual(newInfo.comment, "комментарий")
|
|
}
|
|
|
|
func testUstar() throws {
|
|
// This set of settings should result in the container which uses only ustar TAR format features.
|
|
let date = Date(timeIntervalSince1970: 1300000)
|
|
var info = TarEntryInfo(name: "file.txt", type: .regular)
|
|
info.permissions = Permissions(rawValue: 420)
|
|
info.ownerID = 501
|
|
info.groupID = 20
|
|
info.modificationTime = date
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry], format: .ustar)
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .ustar)
|
|
let newInfo = try TarContainer.open(container: containerData)[0].info
|
|
|
|
XCTAssertEqual(newInfo.name, "file.txt")
|
|
XCTAssertEqual(newInfo.type, .regular)
|
|
XCTAssertEqual(newInfo.permissions?.rawValue, 420)
|
|
XCTAssertEqual(newInfo.ownerID, 501)
|
|
XCTAssertEqual(newInfo.groupID, 20)
|
|
XCTAssertEqual(newInfo.size, 0)
|
|
XCTAssertEqual(newInfo.modificationTime?.timeIntervalSince1970, 1300000)
|
|
XCTAssertEqual(newInfo.linkName, "")
|
|
XCTAssertEqual(newInfo.ownerUserName, "")
|
|
XCTAssertEqual(newInfo.ownerGroupName, "")
|
|
XCTAssertNil(newInfo.accessTime)
|
|
XCTAssertNil(newInfo.creationTime)
|
|
XCTAssertNil(newInfo.comment)
|
|
}
|
|
|
|
func testNegativeMtime() throws {
|
|
let date = Date(timeIntervalSince1970: -1300000)
|
|
var info = TarEntryInfo(name: "file.txt", type: .regular)
|
|
info.modificationTime = date
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry])
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .pax)
|
|
let newInfo = try TarContainer.open(container: containerData)[0].info
|
|
|
|
XCTAssertEqual(newInfo.name, "file.txt")
|
|
XCTAssertEqual(newInfo.type, .regular)
|
|
XCTAssertEqual(newInfo.size, 0)
|
|
XCTAssertEqual(newInfo.modificationTime?.timeIntervalSince1970, -1300000)
|
|
XCTAssertEqual(newInfo.linkName, "")
|
|
XCTAssertEqual(newInfo.ownerUserName, "")
|
|
XCTAssertEqual(newInfo.ownerGroupName, "")
|
|
XCTAssertNil(newInfo.permissions)
|
|
XCTAssertNil(newInfo.ownerID)
|
|
XCTAssertNil(newInfo.groupID)
|
|
XCTAssertNil(newInfo.accessTime)
|
|
XCTAssertNil(newInfo.creationTime)
|
|
XCTAssertNil(newInfo.comment)
|
|
}
|
|
|
|
func testBigUid() throws {
|
|
// Int.max tests that base-256 encoding of integer fields works in the edge case.
|
|
for uid in [(1 << 32) - 1, Int.max] {
|
|
var info = TarEntryInfo(name: "file.txt", type: .regular)
|
|
info.ownerID = uid
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry])
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .pax)
|
|
let newInfo = try TarContainer.open(container: containerData)[0].info
|
|
|
|
XCTAssertEqual(newInfo.name, "file.txt")
|
|
XCTAssertEqual(newInfo.type, .regular)
|
|
XCTAssertEqual(newInfo.size, 0)
|
|
XCTAssertEqual(newInfo.ownerID, uid)
|
|
XCTAssertEqual(newInfo.linkName, "")
|
|
XCTAssertEqual(newInfo.ownerUserName, "")
|
|
XCTAssertEqual(newInfo.ownerGroupName, "")
|
|
XCTAssertNil(newInfo.permissions)
|
|
XCTAssertNil(newInfo.groupID)
|
|
XCTAssertNil(newInfo.accessTime)
|
|
XCTAssertNil(newInfo.creationTime)
|
|
XCTAssertNil(newInfo.modificationTime)
|
|
XCTAssertNil(newInfo.comment)
|
|
}
|
|
}
|
|
|
|
func testGnuLongName() throws {
|
|
var info = TarEntryInfo(name: "", type: .regular)
|
|
info.name = "path/to/"
|
|
info.name.append(String(repeating: "name/", count: 25))
|
|
info.name.append("name.txt")
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry], format: .gnu)
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .gnu)
|
|
let newInfo = try TarContainer.open(container: containerData)[0].info
|
|
|
|
XCTAssertEqual(newInfo.name, info.name)
|
|
}
|
|
|
|
func testGnuLongLinkName() throws {
|
|
var info = TarEntryInfo(name: "", type: .symbolicLink)
|
|
info.name = "link"
|
|
info.linkName = "path/to/"
|
|
info.linkName.append(String(repeating: "name/", count: 25))
|
|
info.linkName.append("name.txt")
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry], format: .gnu)
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .gnu)
|
|
let newInfo = try TarContainer.open(container: containerData)[0].info
|
|
|
|
XCTAssertEqual(newInfo.name, info.name)
|
|
}
|
|
|
|
func testGnuBothLongNames() throws {
|
|
var info = TarEntryInfo(name: "", type: .symbolicLink)
|
|
info.name = "path/to/"
|
|
info.name.append(String(repeating: "name/", count: 25))
|
|
info.name.append("name.txt")
|
|
info.linkName = "path/to/"
|
|
info.linkName.append(String(repeating: "link/", count: 25))
|
|
info.linkName.append("link.txt")
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry], format: .gnu)
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .gnu)
|
|
let newInfo = try TarContainer.open(container: containerData)[0].info
|
|
|
|
XCTAssertEqual(newInfo.name, info.name)
|
|
}
|
|
|
|
func testGnuTimes() throws {
|
|
var info = TarEntryInfo(name: "dir", type: .directory)
|
|
info.ownerUserName = "tsolomko"
|
|
info.ownerGroupName = "staff"
|
|
info.ownerID = 501
|
|
info.groupID = 20
|
|
info.permissions = Permissions(rawValue: 420)
|
|
// We have to convert time interval to int, since tar can't store fractional timestamps, so we lose in accuracy.
|
|
let intTimeInterval = Int(Date().timeIntervalSince1970)
|
|
let date = Date(timeIntervalSince1970: Double(intTimeInterval))
|
|
info.modificationTime = date
|
|
info.creationTime = date
|
|
info.accessTime = date
|
|
let entry = TarEntry(info: info, data: Data())
|
|
|
|
let containerData = try TarWriterTests.generateContainerData([entry], format: .gnu)
|
|
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .gnu)
|
|
let newEntries = try TarContainer.open(container: containerData)
|
|
|
|
XCTAssertEqual(newEntries.count, 1)
|
|
XCTAssertEqual(newEntries[0].info.name, "dir")
|
|
XCTAssertEqual(newEntries[0].info.type, .directory)
|
|
XCTAssertEqual(newEntries[0].info.size, 0)
|
|
XCTAssertEqual(newEntries[0].info.ownerUserName, "tsolomko")
|
|
XCTAssertEqual(newEntries[0].info.ownerGroupName, "staff")
|
|
XCTAssertEqual(newEntries[0].info.ownerID, 501)
|
|
XCTAssertEqual(newEntries[0].info.groupID, 20)
|
|
XCTAssertEqual(newEntries[0].info.permissions, Permissions(rawValue: 420))
|
|
XCTAssertEqual(newEntries[0].info.modificationTime, date)
|
|
XCTAssertEqual(newEntries[0].info.creationTime, date)
|
|
XCTAssertEqual(newEntries[0].info.accessTime, date)
|
|
XCTAssertNil(newEntries[0].info.comment)
|
|
}
|
|
|
|
}
|