Merge nucleic/mellow-dewy-falcon-rjhr into main
This commit is contained in:
@@ -140,6 +140,106 @@ struct PathResolutionTests {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - IPSW pre-validation
|
||||
|
||||
/// Writes `bytes` at the front of a sparse file of `size` bytes.
|
||||
///
|
||||
/// Sparse because a valid-size fixture is a gigabyte and nobody should wait
|
||||
/// for a gigabyte of zeroes to be written to test a two-byte check.
|
||||
private func withIPSW(bytes: [UInt8], size: UInt64, _ body: (String) throws -> Void) throws {
|
||||
let dir = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent("gmr-ipsw-\(UUID().uuidString)", isDirectory: true)
|
||||
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
|
||||
defer { try? FileManager.default.removeItem(at: dir) }
|
||||
|
||||
let path = dir.appendingPathComponent("image.ipsw").path
|
||||
#expect(FileManager.default.createFile(atPath: path, contents: Data(bytes)))
|
||||
let handle = try FileHandle(forWritingTo: URL(fileURLWithPath: path))
|
||||
try handle.truncate(atOffset: size)
|
||||
try handle.close()
|
||||
|
||||
try body(path)
|
||||
}
|
||||
|
||||
private let pk: [UInt8] = [0x50, 0x4B]
|
||||
private let validSize: UInt64 = 2_000_000_000
|
||||
|
||||
@Test("a plausible restore image passes")
|
||||
func healthyIPSWValidates() throws {
|
||||
try withIPSW(bytes: pk, size: validSize) { path in
|
||||
try IPSWFile.validate(path: path)
|
||||
}
|
||||
}
|
||||
|
||||
@Test("a missing file is notFound, not a hang")
|
||||
func missingIPSWThrows() throws {
|
||||
#expect(throws: CoreError.self) {
|
||||
try IPSWFile.validate(path: "/tmp/gmr-definitely-absent.ipsw")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("a directory is rejected before the framework sees it")
|
||||
func directoryIsRejected() throws {
|
||||
try withFiles([]) { dir in
|
||||
do {
|
||||
try IPSWFile.validate(path: dir)
|
||||
Issue.record("expected a throw")
|
||||
} catch let error as CoreError {
|
||||
#expect("\(error)".contains("directory"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test("a truncated download names its own size and says what happened")
|
||||
func truncatedIPSWIsRejected() throws {
|
||||
// 4 MB: the shape of a download that stopped early — right magic bytes,
|
||||
// nowhere near the right length.
|
||||
try withIPSW(bytes: pk, size: 4_000_000) { path in
|
||||
do {
|
||||
try IPSWFile.validate(path: path)
|
||||
Issue.record("expected a throw")
|
||||
} catch let error as CoreError {
|
||||
guard case .configInvalid(let message) = error else {
|
||||
Issue.record("expected .configInvalid, got \(error)")
|
||||
return
|
||||
}
|
||||
#expect(message.contains("3.8 MB"))
|
||||
#expect(message.contains("incomplete"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test("an empty file is rejected on size, before anything reads it")
|
||||
func emptyIPSWIsRejected() throws {
|
||||
try withIPSW(bytes: [], size: 0) { path in
|
||||
#expect(throws: CoreError.self) { try IPSWFile.validate(path: path) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test("a big file that is not a zip is rejected on its magic bytes")
|
||||
func wrongMagicIsRejected() throws {
|
||||
try withIPSW(bytes: [0x00, 0x01], size: validSize) { path in
|
||||
do {
|
||||
try IPSWFile.validate(path: path)
|
||||
Issue.record("expected a throw")
|
||||
} catch let error as CoreError {
|
||||
guard case .configInvalid(let message) = error else {
|
||||
Issue.record("expected .configInvalid, got \(error)")
|
||||
return
|
||||
}
|
||||
#expect(message.contains("PK"))
|
||||
#expect(message.contains("0001"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test("sizes are rendered the way the operator will compare them")
|
||||
func sizeDescriptions() {
|
||||
#expect(IPSWFile.describeSize(0) == "0 B")
|
||||
#expect(IPSWFile.describeSize(512) == "512 B")
|
||||
#expect(IPSWFile.describeSize(22_567_352_533) == "21.0 GB")
|
||||
}
|
||||
|
||||
@Test("the label names the offending flag so the operator knows what to fix")
|
||||
func labelAppearsInErrors() throws {
|
||||
try withFiles([]) { dir in
|
||||
|
||||
Reference in New Issue
Block a user