mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-11-03 14:17:19 +01:00
Ghostgram for iOS, rebuilt from scratch on Telegram iOS 13.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
108 lines
4.4 KiB
Swift
108 lines
4.4 KiB
Swift
import Foundation
|
|
import XCTest
|
|
import SwiftSignalKit
|
|
@testable import Postbox
|
|
|
|
/// `write(2)` may write fewer bytes than asked (disk full, file-size limit). The context
|
|
/// must record only what actually landed on disk, must not link the partial file as the
|
|
/// complete file, and must fail the fetch so the caller can retry.
|
|
final class MediaBoxFileContextV2ShortWriteTests: XCTestCase {
|
|
private var queue: Queue!
|
|
private var basePath: String!
|
|
private var manager: MediaBoxFileManager!
|
|
|
|
private var fullPath: String { return self.basePath + "/resource" }
|
|
private var partialPath: String { return self.basePath + "/resource_partial" }
|
|
private var metaPath: String { return self.basePath + "/resource_partial.meta" }
|
|
|
|
private var context: MediaBoxFileContextV2Impl!
|
|
|
|
private var previousLimit = rlimit()
|
|
private var previousSignalHandler: sig_t?
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
self.queue = Queue(name: "MediaBoxFileContextV2ShortWriteTests")
|
|
self.basePath = NSTemporaryDirectory() + "MediaBoxFileContextV2ShortWriteTests-" + UUID().uuidString
|
|
try? FileManager.default.createDirectory(atPath: self.basePath, withIntermediateDirectories: true)
|
|
self.manager = MediaBoxFileManager(queue: self.queue)
|
|
self.queue.sync {
|
|
self.context = MediaBoxFileContextV2Impl(
|
|
queue: self.queue,
|
|
manager: self.manager,
|
|
storageBox: nil,
|
|
resourceId: "resource".data(using: .utf8)!,
|
|
path: self.fullPath,
|
|
partialPath: self.partialPath,
|
|
metaPath: self.metaPath
|
|
)
|
|
}
|
|
XCTAssertNotNil(self.context)
|
|
getrlimit(RLIMIT_FSIZE, &self.previousLimit)
|
|
}
|
|
|
|
override func tearDown() {
|
|
setrlimit(RLIMIT_FSIZE, &self.previousLimit)
|
|
if let previousSignalHandler = self.previousSignalHandler {
|
|
signal(SIGXFSZ, previousSignalHandler)
|
|
}
|
|
// The context's file handles are released on its queue, as the file manager requires.
|
|
self.queue.sync {
|
|
self.context = nil
|
|
}
|
|
self.manager = nil
|
|
let _ = try? FileManager.default.removeItem(atPath: self.basePath)
|
|
super.tearDown()
|
|
}
|
|
|
|
/// Caps the size any file this process writes may reach, so a large write returns
|
|
/// short. SIGXFSZ, which the kernel raises alongside EFBIG, is ignored for the test.
|
|
private func limitFileSize(to bytes: rlim_t) {
|
|
self.previousSignalHandler = signal(SIGXFSZ, SIG_IGN)
|
|
var limit = self.previousLimit
|
|
limit.rlim_cur = bytes
|
|
XCTAssertEqual(setrlimit(RLIMIT_FSIZE, &limit), 0, "setrlimit failed: \(String(cString: strerror(errno)))")
|
|
}
|
|
|
|
func testShortWriteIsNotRecordedAndFailsTheFetch() {
|
|
// Large enough that the meta file and test-runner logs stay well under the cap,
|
|
// while the payload cannot fit.
|
|
let cap: Int = 4 * 1024 * 1024
|
|
let payload = Data(repeating: 0x5C, count: 2 * cap)
|
|
self.limitFileSize(to: rlim_t(cap))
|
|
defer {
|
|
setrlimit(RLIMIT_FSIZE, &self.previousLimit)
|
|
}
|
|
|
|
let finished = self.expectation(description: "fetch finished")
|
|
var failed = false
|
|
self.queue.sync {
|
|
let _ = self.context.fetched(range: 0 ..< Int64(payload.count), priority: .default, fetch: { _ in
|
|
return Signal { subscriber in
|
|
subscriber.putNext(.dataPart(resourceOffset: 0, data: payload, range: 0 ..< Int64(payload.count), complete: true))
|
|
subscriber.putCompletion()
|
|
return EmptyDisposable
|
|
}
|
|
}, error: { _ in
|
|
failed = true
|
|
finished.fulfill()
|
|
}, completed: {
|
|
XCTFail("a short write must not complete the fetch")
|
|
finished.fulfill()
|
|
})
|
|
}
|
|
self.wait(for: [finished], timeout: 5.0)
|
|
self.queue.sync {}
|
|
setrlimit(RLIMIT_FSIZE, &self.previousLimit)
|
|
|
|
XCTAssertTrue(failed)
|
|
XCTAssertFalse(FileManager.default.fileExists(atPath: self.fullPath), "a file with missing bytes must not be linked as complete")
|
|
|
|
var recorded: MediaBoxFileMap?
|
|
self.queue.sync {
|
|
recorded = try? MediaBoxFileMap.read(manager: self.manager, path: self.metaPath)
|
|
}
|
|
XCTAssertEqual(recorded?.sum ?? 0, 0, "the map must not claim bytes that were not written")
|
|
}
|
|
}
|