mirror of
https://github.com/StikDebug/StikDebug.git
synced 2026-10-15 20:10:30 +00:00
The store now prefers the canonical Documents file whenever it exists and falls back to the legacy file only when it is absent. Directory resolution consistently uses the supplied FileManager, allowing isolated regression tests for precedence, imports, migration, permissions, and cleanup. Temporary-file cleanup is registered before copying so it also runs if the copy fails. Fixes #471
182 lines
8 KiB
Swift
182 lines
8 KiB
Swift
//
|
|
// StikDebugTests.swift
|
|
// StikDebugTests
|
|
//
|
|
// Created by Stephen on 3/26/25.
|
|
//
|
|
|
|
import Foundation
|
|
import Testing
|
|
@testable import StikDebug
|
|
|
|
struct StikDebugTests {
|
|
|
|
@Test func pairingImportSurvivesNewerLegacyFile() throws {
|
|
let fileManager = try PairingFileManager()
|
|
defer { try? fileManager.removeItem(at: fileManager.root) }
|
|
let canonical = fileManager.documents.appendingPathComponent(PairingFileStore.fileName)
|
|
let legacy = fileManager.documents.appendingPathComponent("rp_pairing_file.plist")
|
|
let source = fileManager.root.appendingPathComponent("import.plist")
|
|
let imported = Data("imported pairing".utf8)
|
|
try Data("stale legacy pairing".utf8).write(to: legacy)
|
|
try fileManager.setAttributes([.modificationDate: Date(timeIntervalSince1970: 200)], ofItemAtPath: legacy.path)
|
|
try imported.write(to: source)
|
|
try fileManager.setAttributes([.modificationDate: Date(timeIntervalSince1970: 100)], ofItemAtPath: source.path)
|
|
|
|
try PairingFileStore.importFromPicker(source, fileManager: fileManager)
|
|
for _ in 0..<2 {
|
|
let active = PairingFileStore.prepareURL(fileManager: fileManager)
|
|
#expect(try Data(contentsOf: active) == imported)
|
|
#expect(try fileManager.attributesOfItem(atPath: active.path)[.posixPermissions] as? Int == 0o600)
|
|
}
|
|
try PairingFileStore.importFromPicker(canonical, fileManager: fileManager)
|
|
#expect(throws: (any Error).self) {
|
|
try PairingFileStore.importFromPicker(fileManager.root.appendingPathComponent("missing.plist"), fileManager: fileManager)
|
|
}
|
|
#expect(throws: (any Error).self) {
|
|
try PairingFileStore.replace(with: fileManager.root.appendingPathComponent("missing.plist"), fileManager: fileManager)
|
|
}
|
|
#expect(try Data(contentsOf: canonical) == imported)
|
|
let active = fileManager.support.appendingPathComponent("Pairing").appendingPathComponent(PairingFileStore.fileName)
|
|
#expect(try Data(contentsOf: active) == imported)
|
|
for directory in [fileManager.documents, active.deletingLastPathComponent()] {
|
|
#expect(try fileManager.contentsOfDirectory(atPath: directory.path).allSatisfy { !$0.hasSuffix(".tmp") })
|
|
}
|
|
try PairingFileStore.remove(fileManager: fileManager)
|
|
#expect(!fileManager.fileExists(atPath: active.path))
|
|
#expect(!fileManager.fileExists(atPath: canonical.path))
|
|
#expect(!fileManager.fileExists(atPath: legacy.path))
|
|
}
|
|
|
|
@Test(arguments: [100.0, 200.0, 300.0])
|
|
func canonicalPairingTakesPrecedence(modificationTime: TimeInterval) throws {
|
|
let fileManager = try PairingFileManager()
|
|
defer { try? fileManager.removeItem(at: fileManager.root) }
|
|
let canonical = fileManager.documents.appendingPathComponent(PairingFileStore.fileName)
|
|
let legacy = fileManager.documents.appendingPathComponent("rp_pairing_file.plist")
|
|
let active = PairingFileStore.prepareURL(fileManager: fileManager)
|
|
try Data("stale active pairing".utf8).write(to: active)
|
|
try Data("legacy pairing".utf8).write(to: legacy)
|
|
try fileManager.setAttributes([.modificationDate: Date(timeIntervalSince1970: 200)], ofItemAtPath: legacy.path)
|
|
let canonicalData = Data("canonical pairing".utf8)
|
|
try canonicalData.write(to: canonical)
|
|
try fileManager.setAttributes([.modificationDate: Date(timeIntervalSince1970: modificationTime)], ofItemAtPath: canonical.path)
|
|
#expect(try Data(contentsOf: PairingFileStore.prepareURL(fileManager: fileManager)) == canonicalData)
|
|
|
|
let edited = Data("edited through Files".utf8)
|
|
try edited.write(to: canonical)
|
|
try fileManager.setAttributes([.modificationDate: Date(timeIntervalSince1970: 50)], ofItemAtPath: canonical.path)
|
|
#expect(try Data(contentsOf: PairingFileStore.prepareURL(fileManager: fileManager)) == edited)
|
|
}
|
|
|
|
@Test(arguments: ["documents", "support", "none"])
|
|
func pairingLegacyMigration(source: String) throws {
|
|
let fileManager = try PairingFileManager()
|
|
defer { try? fileManager.removeItem(at: fileManager.root) }
|
|
let pairingDirectory = fileManager.support.appendingPathComponent("Pairing")
|
|
let expected = pairingDirectory.appendingPathComponent(PairingFileStore.fileName)
|
|
let data = Data("legacy pairing".utf8)
|
|
if source != "none" {
|
|
let directory = source == "documents" ? fileManager.documents : pairingDirectory
|
|
try fileManager.createDirectory(at: directory, withIntermediateDirectories: true)
|
|
try data.write(to: directory.appendingPathComponent("rp_pairing_file.plist"))
|
|
}
|
|
let active = PairingFileStore.prepareURL(fileManager: fileManager)
|
|
#expect(active == expected)
|
|
if source == "none" {
|
|
#expect(!fileManager.fileExists(atPath: active.path))
|
|
} else {
|
|
#expect(try Data(contentsOf: active) == data)
|
|
#expect(try fileManager.attributesOfItem(atPath: active.path)[.posixPermissions] as? Int == 0o600)
|
|
}
|
|
}
|
|
|
|
@Test func txmDetectionUsesClassicTXMBeforeIOS266() async throws {
|
|
#expect(
|
|
ProcessInfo.hasTXMSupport(
|
|
isIOS266OrNewer: false,
|
|
hasTXMClassic: false,
|
|
hardwareIdentifier: "iPhone15,2"
|
|
) == false
|
|
)
|
|
#expect(
|
|
ProcessInfo.hasTXMSupport(
|
|
isIOS266OrNewer: false,
|
|
hasTXMClassic: true,
|
|
hardwareIdentifier: "iPhone1,1"
|
|
) == true
|
|
)
|
|
}
|
|
|
|
@Test func txmDetectionUsesClassicTXMWhenAvailableOnIOS266() async throws {
|
|
#expect(
|
|
ProcessInfo.hasTXMSupport(
|
|
isIOS266OrNewer: true,
|
|
hasTXMClassic: true,
|
|
hardwareIdentifier: "iPhone1,1"
|
|
) == true
|
|
)
|
|
}
|
|
|
|
@Test func txmDetectionFallsBackToIPhoneThresholdOnIOS266() async throws {
|
|
#expect(
|
|
ProcessInfo.hasTXMSupport(
|
|
isIOS266OrNewer: true,
|
|
hasTXMClassic: false,
|
|
hardwareIdentifier: "iPhone14,1"
|
|
) == false
|
|
)
|
|
#expect(
|
|
ProcessInfo.hasTXMSupport(
|
|
isIOS266OrNewer: true,
|
|
hasTXMClassic: false,
|
|
hardwareIdentifier: "iPhone14,2"
|
|
) == true
|
|
)
|
|
}
|
|
|
|
@Test func txmDetectionFallsBackToIPadThresholdOnIOS266() async throws {
|
|
#expect(
|
|
ProcessInfo.hasTXMSupport(
|
|
isIOS266OrNewer: true,
|
|
hasTXMClassic: false,
|
|
hardwareIdentifier: "iPad14,4"
|
|
) == false
|
|
)
|
|
#expect(
|
|
ProcessInfo.hasTXMSupport(
|
|
isIOS266OrNewer: true,
|
|
hasTXMClassic: false,
|
|
hardwareIdentifier: "iPad14,5"
|
|
) == true
|
|
)
|
|
}
|
|
|
|
@Test func deviceVersionParsesSupportedIdentifiers() async throws {
|
|
#expect(ProcessInfo.processInfo.deviceVersion(from: "iPhone14,2") == 14.2)
|
|
#expect(ProcessInfo.processInfo.deviceVersion(from: "iPad14,5") == 14.5)
|
|
#expect(ProcessInfo.processInfo.deviceVersion(from: "Mac14,2") == nil)
|
|
}
|
|
|
|
}
|
|
|
|
private final class PairingFileManager: FileManager, @unchecked Sendable {
|
|
let root: URL
|
|
var documents: URL { root.appendingPathComponent("Documents") }
|
|
var support: URL { root.appendingPathComponent("Application Support") }
|
|
|
|
init(root: URL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)) throws {
|
|
self.root = root
|
|
super.init()
|
|
try createDirectory(at: documents, withIntermediateDirectories: true)
|
|
try createDirectory(at: support, withIntermediateDirectories: true)
|
|
}
|
|
|
|
override func urls(for directory: FileManager.SearchPathDirectory, in domainMask: FileManager.SearchPathDomainMask) -> [URL] {
|
|
switch directory {
|
|
case .documentDirectory: return [documents]
|
|
case .applicationSupportDirectory: return [support]
|
|
default: return super.urls(for: directory, in: domainMask)
|
|
}
|
|
}
|
|
}
|