mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-11-03 06:14:14 +01:00
Ghostgram for iOS, rebuilt from scratch on Telegram iOS 13.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
117 lines
4.4 KiB
Swift
117 lines
4.4 KiB
Swift
import Foundation
|
|
import XCTest
|
|
import SwiftSignalKit
|
|
import RangeSet
|
|
@testable import Postbox
|
|
|
|
/// The file map (`<id>_partial.meta`) is the context's record of which bytes of the
|
|
/// partial file are present. It must never claim more than the partial file holds, and a
|
|
/// write that cannot be performed must fail the fetch rather than leave requests pending.
|
|
final class MediaBoxFileContextV2ReopenTests: 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" }
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
self.queue = Queue(name: "MediaBoxFileContextV2ReopenTests")
|
|
self.basePath = NSTemporaryDirectory() + "MediaBoxFileContextV2ReopenTests-" + UUID().uuidString
|
|
try? FileManager.default.createDirectory(atPath: self.basePath, withIntermediateDirectories: true)
|
|
self.manager = MediaBoxFileManager(queue: self.queue)
|
|
}
|
|
|
|
override func tearDown() {
|
|
self.manager = nil
|
|
let _ = try? FileManager.default.removeItem(atPath: self.basePath)
|
|
super.tearDown()
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
private func makeContext() -> MediaBoxFileContextV2Impl {
|
|
var context: MediaBoxFileContextV2Impl?
|
|
self.queue.sync {
|
|
context = MediaBoxFileContextV2Impl(
|
|
queue: self.queue,
|
|
manager: self.manager,
|
|
storageBox: nil,
|
|
resourceId: "resource".data(using: .utf8)!,
|
|
path: self.fullPath,
|
|
partialPath: self.partialPath,
|
|
metaPath: self.metaPath
|
|
)
|
|
}
|
|
return context!
|
|
}
|
|
|
|
private func writeMeta(claiming range: Range<Int64>) {
|
|
self.queue.sync {
|
|
let map = MediaBoxFileMap()
|
|
map.fill(range)
|
|
map.serialize(manager: self.manager, to: self.metaPath)
|
|
}
|
|
}
|
|
|
|
private func reportedRanges(of context: MediaBoxFileContextV2Impl) -> RangeSet<Int64>? {
|
|
var reported: RangeSet<Int64>?
|
|
self.queue.sync {
|
|
let _ = context.rangeStatus(next: { ranges in
|
|
reported = ranges
|
|
}, completed: {})
|
|
}
|
|
return reported
|
|
}
|
|
|
|
// MARK: - Tests
|
|
|
|
func testMapClaimingMoreThanThePartialFileHoldsIsDiscardedOnReopen() {
|
|
XCTAssertTrue(FileManager.default.createFile(atPath: self.partialPath, contents: Data(repeating: 0x7A, count: 10)))
|
|
self.writeMeta(claiming: 0 ..< 100)
|
|
|
|
let context = self.makeContext()
|
|
|
|
XCTAssertEqual(self.reportedRanges(of: context), RangeSet<Int64>(), "a map that claims bytes the partial file does not hold must be reset")
|
|
}
|
|
|
|
func testMapConsistentWithThePartialFileIsKeptOnReopen() {
|
|
XCTAssertTrue(FileManager.default.createFile(atPath: self.partialPath, contents: Data(repeating: 0x7A, count: 100)))
|
|
self.writeMeta(claiming: 0 ..< 40)
|
|
|
|
let context = self.makeContext()
|
|
|
|
XCTAssertEqual(self.reportedRanges(of: context), RangeSet<Int64>(0 ..< 40))
|
|
}
|
|
|
|
func testUnwritablePartialFileFailsTheFetchInsteadOfHanging() {
|
|
// A read-only partial file cannot be opened read-write, so every write fails.
|
|
XCTAssertTrue(FileManager.default.createFile(atPath: self.partialPath, contents: Data(), attributes: [.posixPermissions: 0o444]))
|
|
let context = self.makeContext()
|
|
|
|
let finished = self.expectation(description: "fetch finished")
|
|
var failed = false
|
|
self.queue.sync {
|
|
let _ = context.fetched(range: 0 ..< 8, priority: .default, fetch: { _ in
|
|
return Signal { subscriber in
|
|
subscriber.putNext(.dataPart(resourceOffset: 0, data: Data(repeating: 0x01, count: 8), range: 0 ..< 8, complete: true))
|
|
subscriber.putCompletion()
|
|
return EmptyDisposable
|
|
}
|
|
}, error: { _ in
|
|
failed = true
|
|
finished.fulfill()
|
|
}, completed: {
|
|
XCTFail("a fetch whose bytes could not be written must not complete")
|
|
finished.fulfill()
|
|
})
|
|
}
|
|
|
|
self.wait(for: [finished], timeout: 5.0)
|
|
XCTAssertTrue(failed)
|
|
XCTAssertFalse(FileManager.default.fileExists(atPath: self.fullPath))
|
|
}
|
|
}
|