31f5937a7f
Reduce extension memory pressure and delivery races while adding richer candidates, tactile feedback, and safer two-level creative polishing.
55 lines
2.0 KiB
Swift
55 lines
2.0 KiB
Swift
// FlowPhysicalAudioStressTests.swift
|
|
// OSGKeyboardTests
|
|
//
|
|
// Physical-device release gate for the PiP playback ↔ capture transition.
|
|
// Excluded from PR presets because Simulator cannot model Bluetooth HFP.
|
|
|
|
import AVFoundation
|
|
import XCTest
|
|
@testable import OSGKeyboardHostSupport
|
|
@testable import OSGKeyboardShared
|
|
|
|
final class FlowPhysicalAudioStressTests: XCTestCase {
|
|
@MainActor
|
|
func testFiftyCapturePlaybackCyclesRemainStable() async throws {
|
|
#if targetEnvironment(simulator)
|
|
throw XCTSkip("Bluetooth HFP stress requires a physical iPhone")
|
|
#else
|
|
guard AVAudioApplication.shared.recordPermission == .granted else {
|
|
throw XCTSkip("Grant microphone permission to OSGKeyboard first")
|
|
}
|
|
|
|
let capture = FlowContinuousCapture()
|
|
let initialRSS = OSGDiag.memoryMB()
|
|
|
|
for iteration in 1...50 {
|
|
try await capture.start()
|
|
let receivedFrame = await capture.awaitAudioFlowing(timeout: 2)
|
|
XCTAssertTrue(receivedFrame, "No microphone frame in iteration \(iteration)")
|
|
XCTAssertTrue(capture.engineIsLive, "Engine not live in iteration \(iteration)")
|
|
|
|
capture.stop(releaseSession: false)
|
|
let playbackReady = await FlowAudioSessionCoordinator.shared.activatePlayback()
|
|
XCTAssertTrue(playbackReady, "PiP playback restore failed in iteration \(iteration)")
|
|
try? await Task.sleep(nanoseconds: 50_000_000)
|
|
}
|
|
|
|
try? await Task.sleep(nanoseconds: 500_000_000)
|
|
XCTAssertEqual(capture.engineActivationCount, 50)
|
|
XCTAssertEqual(
|
|
capture.routeRecoveryCount,
|
|
0,
|
|
"Stable routeConfigurationChange notifications must not rebuild the engine"
|
|
)
|
|
|
|
let growth = OSGDiag.memoryMB() - initialRSS
|
|
XCTAssertLessThan(
|
|
growth,
|
|
80,
|
|
"Fifty audio cycles retained \(String(format: "%.1f", growth)) MB"
|
|
)
|
|
FlowAudioSessionCoordinator.shared.deactivate()
|
|
#endif
|
|
}
|
|
}
|