The CLI and iOS app now point at share.tzgyn.dev:9009 instead of the upstream croc relay. Both were verified against the deployed relay; local relays remain available through --relay and the app's Relay setting.
182 lines
6.2 KiB
Swift
182 lines
6.2 KiB
Swift
import Foundation
|
|
import Share
|
|
import SwiftUI
|
|
|
|
/// A headless-ish smoke test driven by launch arguments:
|
|
///
|
|
/// xcrun simctl launch <udid> dev.tzgyn.share \
|
|
/// -shareSelfTest -shareSelfTestRelay 127.0.0.1:9009 \
|
|
/// -shareSelfTestCode 1234-selftest-share-apple
|
|
///
|
|
/// The test starts a receive room, waits for one complete file, and writes
|
|
/// `Documents/share-selftest.txt`. Console output is unreliable from a
|
|
/// simulator and impossible to read from a device, so the report on disk is
|
|
/// the source of truth.
|
|
@MainActor
|
|
enum SelfTest {
|
|
private static var started = false
|
|
|
|
static func runIfRequested(model: AppModel) {
|
|
let args = ProcessInfo.processInfo.arguments
|
|
guard !started else { return }
|
|
let relay = value(after: "-shareSelfTestRelay", in: args) ?? ShareRelayDefault()
|
|
let code = value(after: "-shareSelfTestCode", in: args)
|
|
?? (args.contains("-shareSelfTestSend")
|
|
? "1234-selftest-send-apple"
|
|
: "1234-selftest-share-apple")
|
|
|
|
if args.contains("-shareSelfTestSend") {
|
|
started = true
|
|
runSend(model: model, relay: relay, code: code)
|
|
} else if args.contains("-shareSelfTest") {
|
|
started = true
|
|
runReceive(model: model, relay: relay, code: code)
|
|
}
|
|
}
|
|
|
|
private static func runReceive(model: AppModel, relay: String, code: String) {
|
|
model.mode = .receive
|
|
model.relay = relay
|
|
model.code = code
|
|
model.startReceive()
|
|
|
|
Task { @MainActor in
|
|
let deadline = Date().addingTimeInterval(120)
|
|
var lastStatus: RoomStatus?
|
|
|
|
while Date() < deadline {
|
|
try? await Task.sleep(nanoseconds: 500_000_000)
|
|
if let status = model.status {
|
|
lastStatus = status
|
|
if status.allComplete {
|
|
writeReport(result: "ok", model: model, status: status, error: nil)
|
|
return
|
|
}
|
|
if !status.running, let error = status.error, !error.isEmpty {
|
|
writeReport(result: "room-error", model: model, status: status, error: error)
|
|
return
|
|
}
|
|
}
|
|
if model.status == nil, let error = model.errorMessage, !error.isEmpty {
|
|
writeReport(result: "start-error", model: model, status: nil, error: error)
|
|
return
|
|
}
|
|
}
|
|
|
|
writeReport(
|
|
result: "timeout",
|
|
model: model,
|
|
status: lastStatus,
|
|
error: "no file completed before the 120s deadline"
|
|
)
|
|
}
|
|
}
|
|
|
|
private static func runSend(model: AppModel, relay: String, code: String) {
|
|
let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
|
let seedDir = documents.appendingPathComponent("Seeds/selftest-send", isDirectory: true)
|
|
let seedURL = seedDir.appendingPathComponent("share-send-selftest.bin")
|
|
do {
|
|
try FileManager.default.createDirectory(at: seedDir, withIntermediateDirectories: true)
|
|
let data = Data(repeating: 0x5A, count: 2 * 1024 * 1024)
|
|
try data.write(to: seedURL)
|
|
} catch {
|
|
writeReport(result: "seed-error", model: model, status: nil, error: error.localizedDescription)
|
|
return
|
|
}
|
|
|
|
model.mode = .send
|
|
model.relay = relay
|
|
model.code = code
|
|
model.pickedFiles = [
|
|
PickedFile(url: seedURL, name: seedURL.lastPathComponent, size: 2 * 1024 * 1024)
|
|
]
|
|
model.startSend()
|
|
|
|
Task { @MainActor in
|
|
let deadline = Date().addingTimeInterval(120)
|
|
var lastStatus: RoomStatus?
|
|
var sawPeer = false
|
|
|
|
while Date() < deadline {
|
|
try? await Task.sleep(nanoseconds: 500_000_000)
|
|
if let status = model.status {
|
|
lastStatus = status
|
|
if status.peers > 0 { sawPeer = true }
|
|
if sawPeer, status.allComplete {
|
|
// Give the receiver a moment to finish the last chunks and
|
|
// disconnect before recording the result.
|
|
try? await Task.sleep(nanoseconds: 1_000_000_000)
|
|
writeReport(result: "ok-seeded", model: model, status: status, error: nil)
|
|
return
|
|
}
|
|
if !status.running, let error = status.error, !error.isEmpty {
|
|
writeReport(result: "room-error", model: model, status: status, error: error)
|
|
return
|
|
}
|
|
}
|
|
if model.status == nil, let error = model.errorMessage, !error.isEmpty {
|
|
writeReport(result: "start-error", model: model, status: nil, error: error)
|
|
return
|
|
}
|
|
}
|
|
|
|
writeReport(
|
|
result: "timeout",
|
|
model: model,
|
|
status: lastStatus,
|
|
error: "no peer completed the seed before the 120s deadline"
|
|
)
|
|
}
|
|
}
|
|
|
|
private static func value(after flag: String, in args: [String]) -> String? {
|
|
guard let index = args.firstIndex(of: flag), index + 1 < args.count else { return nil }
|
|
return args[index + 1]
|
|
}
|
|
|
|
private static func writeReport(
|
|
result: String,
|
|
model: AppModel,
|
|
status: RoomStatus?,
|
|
error: String?
|
|
) {
|
|
let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
|
let url = documents.appendingPathComponent("share-selftest.txt")
|
|
|
|
var lines = [
|
|
"result=\(result)",
|
|
"code=\(status?.code ?? model.code)",
|
|
"relay=\(status?.relay ?? model.relay)",
|
|
"running=\(status?.running ?? false)",
|
|
"peers=\(status?.peers ?? 0)",
|
|
"files=\(status?.files.count ?? 0)",
|
|
]
|
|
if let error, !error.isEmpty {
|
|
lines.append("error=\(error.replacingOccurrences(of: "\n", with: " "))")
|
|
}
|
|
for file in status?.files ?? [] {
|
|
let localURL: URL?
|
|
if model.mode == .send {
|
|
localURL = model.pickedFiles.first(where: { $0.name == file.name })?.url
|
|
} else {
|
|
localURL = model.receivedURL(for: file)
|
|
}
|
|
let exists = localURL.map { FileManager.default.fileExists(atPath: $0.path) } ?? false
|
|
let size = localURL.flatMap {
|
|
try? $0.resourceValues(forKeys: [.fileSizeKey]).fileSize
|
|
} ?? 0
|
|
lines.append(
|
|
"file=\(file.name) size=\(file.size) chunks=\(file.haveChunks)/\(file.totalChunks) complete=\(file.complete) exists=\(exists) diskSize=\(size)"
|
|
)
|
|
}
|
|
|
|
try? lines.joined(separator: "\n").appending("\n").write(
|
|
to: url,
|
|
atomically: true,
|
|
encoding: .utf8
|
|
)
|
|
print("Share self-test \(result): \(url.path)")
|
|
}
|
|
}
|