import CallsKit import LiveKitWebRTC import XCTest final class CallsPhoneTests: XCTestCase { @MainActor func testNativeVideoAndRenewal() async throws { guard let text = ProcessInfo.processInfo.environment["CALLS_TEST_MEDIA"], let data = text.data(using: .utf8) else { throw XCTSkip("Run the Go iPhone media harness for temporary sessions and a meeting.") } struct Fixture: Decodable { let origin: String let room: String let cookiesA: String let cookiesB: String } let input = try JSONDecoder().decode(Fixture.self, from: data) // No hardware capture or playback in this simulator video test. let a = Call(audioDevice: SilentAudioDevice()) let b = Call(audioDevice: SilentAudioDevice()) defer { a.close() b.close() } // Synthetic cameras behind the app's own camera management, as the Mac // gate has them; each call its own defaults, never the simulator's. func synthetic() -> ManagedCamera { ManagedCamera( backend: SyntheticCameras(), defaults: UserDefaults(suiteName: "calls-phone-test-\(UUID().uuidString)")!) } try await a.join( origin: input.origin, room: input.room, cookies: input.cookiesA, capture: synthetic()) a.mute() try await b.join( origin: input.origin, room: input.room, cookies: input.cookiesB, capture: synthetic()) b.mute() var decodedA: UInt64 = 0 var decodedB: UInt64 = 0 for _ in 0..<80 { decodedA = await a.sampleStats()["framesDecoded", default: 0] decodedB = await b.sampleStats()["framesDecoded", default: 0] if decodedA > 15 && decodedB > 15 { break } try await Task.sleep(for: .milliseconds(500)) } XCTAssertGreaterThan(decodedA, 15, "First iPhone peer must decode the other peer's video") XCTAssertGreaterThan(decodedB, 15, "Second iPhone peer must decode the other peer's video") XCTAssertEqual(a.state?.phase, "admitted") XCTAssertEqual(b.state?.phase, "admitted") let peerA = a.state?.peer let peerB = b.state?.peer try await Task.sleep(for: .seconds(8)) let renewedA = await a.sampleStats() let renewedB = await b.sampleStats() XCTAssertGreaterThan(renewedA["framesDecoded", default: 0], decodedA) XCTAssertGreaterThan(renewedB["framesDecoded", default: 0], decodedB) XCTAssertEqual(a.state?.peer, peerA) XCTAssertEqual(b.state?.peer, peerB) await b.leave() for _ in 0..<20 { if a.remoteTracks.isEmpty { break } try await Task.sleep(for: .milliseconds(250)) } XCTAssertTrue(a.remoteTracks.isEmpty, "Departure must remove the remote track") await a.leave() } }