// First light, headless: the full client pipeline against a REAL remote host — QUIC // handshake over the LAN, NVENC HEVC AUs through FEC + AES-GCM, AnnexB conversion, and a // real VTDecompressionSession turning them into pixels. Everything the GUI does except // putting the layer on glass. // // Run (host side, on the Linux box): // PUNKTFUNK_COMPOSITOR=gamescope PUNKTFUNK_GAMESCOPE_APP=vkcube PUNKTFUNK_ZEROCOPY=1 \ // punktfunk-host punktfunk1-host --source virtual --seconds 120 // Then here: // PUNKTFUNK_REMOTE_HOST=192.168.1.70 swift test --filter RemoteFirstLightTests import AVFoundation import CoreMedia import VideoToolbox import XCTest @testable import PunktfunkKit final class RemoteFirstLightTests: XCTestCase { /// The pairing ceremony over the real LAN, exactly as the app runs it: fresh identity, /// SPAKE2 with the host's arming PIN, then a pinned + identified session. Needs the /// host armed (--allow-pairing) and its logged PIN in PUNKTFUNK_REMOTE_PIN. Heads-up: /// every run durably adds one throwaway "remote-test" identity to the host's /// ~/.config/punktfunk/punktfunk1-paired.json — prune those entries at will. func testRemotePairingThenPinnedStream() throws { let env = ProcessInfo.processInfo.environment guard let host = env["PUNKTFUNK_REMOTE_HOST"], let pin = env["PUNKTFUNK_REMOTE_PIN"] else { throw XCTSkip("set PUNKTFUNK_REMOTE_HOST + PUNKTFUNK_REMOTE_PIN " + "(host armed with --allow-pairing)") } let port = env["PUNKTFUNK_REMOTE_PORT"].flatMap(UInt16.init) ?? 9777 let identity = try generateIdentity() let fingerprint = try pair( host: host, port: port, identity: identity, pin: pin, name: "remote-test") XCTAssertEqual(fingerprint.count, 32) let conn = try PunktfunkConnection( host: host, port: port, width: 1280, height: 720, refreshHz: 60, pinSHA256: fingerprint, identity: identity) defer { conn.close() } XCTAssertEqual(conn.hostFingerprint, fingerprint) var got = 0 let deadline = Date().addingTimeInterval(20) while got < 10, Date() < deadline { if try conn.nextAU(timeoutMs: 2000) != nil { got += 1 } } XCTAssertGreaterThanOrEqual(got, 10, "paired + pinned session must stream") } /// Audio both ways against the real host: drain the Opus plane and decode it to PCM /// (host → speaker path minus the speaker), and uplink an encoded tone (mic path /// minus the mic) — the host logs "punktfunk/1 virtual mic ready" on first frame. func testRemoteAudioBothDirections() throws { let env = ProcessInfo.processInfo.environment guard let host = env["PUNKTFUNK_REMOTE_HOST"] else { throw XCTSkip("set PUNKTFUNK_REMOTE_HOST (and start punktfunk1-host --source virtual there)") } let port = env["PUNKTFUNK_REMOTE_PORT"].flatMap(UInt16.init) ?? 9777 let conn = try PunktfunkConnection( host: host, port: port, width: 1280, height: 720, refreshHz: 60) defer { conn.close() } // Mic uplink: 2 s of 440 Hz tone (the host's mic service opens its virtual // source on the first frame — check its log). let encoder = try OpusEncoder() let chunk = AVAudioPCMBuffer( pcmFormat: encoder.pcmFormat, frameCapacity: OpusEncoder.framesPerPacket)! var phase: Float = 0 let step = 2 * Float.pi * 440 / 48_000 var seq: UInt32 = 0 for _ in 0..<100 { chunk.frameLength = OpusEncoder.framesPerPacket let p = chunk.floatChannelData![0] for f in 0..