Files
Yaojia Wang cbaa08daba feat(ios): W0 scaffold + day-1 spike + WireProtocol frozen contract + TestSupport doubles
T-iOS-1: ios/ XcodeGen project (iOS 17, Swift 6 strict concurrency, ATS per PLAN §5.2), 5 SPM package shells, CI skeleton
T-iOS-2: Origin spike vs real server — URLSessionWebSocketTask custom Origin CONFIRMED (no Starscream); 16MiB replay + EMSGSIZE(40) errno correction written back to plan
T-iOS-3: WireProtocol frozen contract, 59 tests, 100% line coverage, cross-impl vectors vs src/protocol.ts via tsx
T-iOS-4: FakeTransport/FakeClock/FakeHTTPTransport doubles
Verify: independent agent re-ran all acceptance — 6/6 PASS
2026-07-04 21:19:30 +02:00

72 lines
2.8 KiB
Swift

import Foundation
import WireProtocol
/// Deterministic RNG for the roundtrip property test (reproducible failures:
/// the seed is baked into the test; change it only with a note in the test).
struct SplitMix64: RandomNumberGenerator {
private var state: UInt64
init(seed: UInt64) {
state = seed
}
mutating func next() -> UInt64 {
state &+= 0x9E37_79B9_7F4A_7C15
var z = state
z = (z ^ (z >> 30)) &* 0xBF58_476D_1CE4_E5B9
z = (z ^ (z >> 27)) &* 0x94D0_49BB_1331_11EB
return z ^ (z >> 31)
}
}
enum MessageGen {
/// Character pool that stresses the JSON escaper: quotes, backslashes,
/// C0 control bytes (ESC / CR / TAB / ^C / NUL), CJK, emoji, plain ASCII.
private static let stressPool: [Character] = [
"a", "Z", "0", " ", "\"", "\\", "/", "{", "}",
"\u{1B}", "\r", "\n", "\t", "\u{03}", "\u{00}", "\u{7F}",
"", "", "", "", "🚀", "🧪", "é", "ß",
]
static func randomString(using rng: inout some RandomNumberGenerator, maxLength: Int = 48) -> String {
let length = Int.random(in: 0...maxLength, using: &rng)
return String((0..<length).map { _ in stressPool.randomElement(using: &rng)! })
}
static func randomAbsolutePath(using rng: inout some RandomNumberGenerator) -> String {
"/" + randomString(using: &rng, maxLength: 24)
}
/// Seeded UUID v4 (version nibble 4, variant 10xx) passes SESSION_ID_RE.
static func randomUUIDv4(using rng: inout some RandomNumberGenerator) -> UUID {
var bytes = (0..<16).map { _ in UInt8.random(in: 0...255, using: &rng) }
bytes[6] = (bytes[6] & 0x0F) | 0x40
bytes[8] = (bytes[8] & 0x3F) | 0x80
return UUID(uuid: (
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]
))
}
static func randomClientMessage(using rng: inout some RandomNumberGenerator) -> ClientMessage {
switch Int.random(in: 0...4, using: &rng) {
case 0:
let sessionId = Bool.random(using: &rng) ? randomUUIDv4(using: &rng) : nil
let cwd = Bool.random(using: &rng) ? randomAbsolutePath(using: &rng) : nil
return .attach(sessionId: sessionId, cwd: cwd)
case 1:
return .input(data: randomString(using: &rng))
case 2:
return .resize(
cols: Int.random(in: 1...1000, using: &rng),
rows: Int.random(in: 1...1000, using: &rng)
)
case 3:
let mode = Bool.random(using: &rng) ? ApproveMode.allCases.randomElement(using: &rng) : nil
return .approve(mode: mode)
default:
return .reject
}
}
}