498f407585
Introduce optional Apple account-backed credits with scoped gateway access while preserving local and BYOK paths. Refresh assistant behavior, tests, privacy disclosures, docs, and the website for the 2.0 experience.
104 lines
4.2 KiB
Swift
104 lines
4.2 KiB
Swift
// UtteranceStreamChunkerTests.swift
|
|
// OSGKeyboardTests
|
|
|
|
@testable import OSGKeyboardShared
|
|
import XCTest
|
|
|
|
final class UtteranceStreamChunkerTests: XCTestCase {
|
|
|
|
private let config = FlowUtteranceChunkConfig(
|
|
maxChunkDurationSeconds: 1,
|
|
overlapDurationSeconds: 0.1,
|
|
pauseExtensionMaxSeconds: 0.2,
|
|
pauseRMSThreshold: 0.02,
|
|
sampleRate: 1_000
|
|
)
|
|
|
|
func testPauseAwareSplitPrefersSilenceNearWindowEnd() {
|
|
var buffer = [Float](repeating: 0.2, count: 900)
|
|
buffer.append(contentsOf: [Float](repeating: 0.001, count: 50))
|
|
buffer.append(contentsOf: [Float](repeating: 0.2, count: 100))
|
|
|
|
let split = UtteranceStreamChunker.pauseAwareSplitIndex(in: buffer, config: config)
|
|
XCTAssertGreaterThanOrEqual(split, config.maxChunkSamples)
|
|
XCTAssertLessThanOrEqual(split, config.maxChunkSamples + config.pauseExtensionSamples)
|
|
}
|
|
|
|
func testPauseAwareSplitReportsPauseDuration() {
|
|
var buffer = [Float](repeating: 0.2, count: config.maxChunkSamples)
|
|
buffer.append(contentsOf: [Float](repeating: 0.001, count: 200))
|
|
let result = UtteranceStreamChunker.pauseAwareSplit(in: buffer, config: config)
|
|
XCTAssertGreaterThan(result.pauseSamples, 0)
|
|
XCTAssertGreaterThan(result.index, config.maxChunkSamples)
|
|
}
|
|
|
|
func testFirstChunkUsesShorterWindow() async {
|
|
let config = FlowUtteranceChunkConfig(
|
|
firstChunkDurationSeconds: 0.5,
|
|
subsequentChunkDurationSeconds: 1.0,
|
|
overlapDurationSeconds: 0,
|
|
pauseExtensionMaxSeconds: 0,
|
|
pauseRMSThreshold: 0.02,
|
|
sampleRate: 1_000
|
|
)
|
|
let firstChunkSamples = config.maxChunkSamples(forChunkIndex: 0) + 50
|
|
let samples = [Float](repeating: 0.05, count: firstChunkSamples)
|
|
let (stream, continuation) = AsyncStream<AudioBufferSnapshot>.makeStream()
|
|
continuation.yield(AudioBufferSnapshot(samples: samples, sampleRate: Double(config.sampleRate)))
|
|
continuation.finish()
|
|
|
|
var received: [UtteranceAudioChunk] = []
|
|
for await chunk in UtteranceStreamChunker.chunks(from: stream, config: config) {
|
|
received.append(chunk)
|
|
}
|
|
|
|
XCTAssertGreaterThanOrEqual(received.count, 2)
|
|
XCTAssertLessThanOrEqual(received[0].samples.count, config.maxChunkSamples(forChunkIndex: 0) + 50)
|
|
}
|
|
|
|
func testChunksEmitMultipleSegmentsForLongStream() async {
|
|
let sampleCount = config.maxChunkSamples * 2 + 100
|
|
let samples = [Float](repeating: 0.05, count: sampleCount)
|
|
let (stream, continuation) = AsyncStream<AudioBufferSnapshot>.makeStream()
|
|
continuation.yield(AudioBufferSnapshot(samples: samples, sampleRate: Double(config.sampleRate)))
|
|
continuation.finish()
|
|
|
|
var received: [UtteranceAudioChunk] = []
|
|
for await chunk in UtteranceStreamChunker.chunks(from: stream, config: config) {
|
|
received.append(chunk)
|
|
}
|
|
|
|
XCTAssertGreaterThanOrEqual(received.count, 2)
|
|
XCTAssertTrue(received.last?.isLast == true)
|
|
}
|
|
|
|
func testFinalChunkIncludesLateArrivingTailSamples() async {
|
|
let config = FlowUtteranceChunkConfig(
|
|
firstChunkDurationSeconds: 0.5,
|
|
subsequentChunkDurationSeconds: 1.0,
|
|
overlapDurationSeconds: 0,
|
|
pauseExtensionMaxSeconds: 0,
|
|
pauseRMSThreshold: 0.02,
|
|
sampleRate: 1_000
|
|
)
|
|
let head = [Float](repeating: 0.05, count: 600)
|
|
let tail = [Float](repeating: 0.08, count: 250)
|
|
|
|
let (stream, continuation) = AsyncStream<AudioBufferSnapshot>.makeStream()
|
|
continuation.yield(AudioBufferSnapshot(samples: head, sampleRate: Double(config.sampleRate)))
|
|
continuation.yield(AudioBufferSnapshot(samples: tail, sampleRate: Double(config.sampleRate)))
|
|
continuation.finish()
|
|
|
|
var received: [UtteranceAudioChunk] = []
|
|
for await chunk in UtteranceStreamChunker.chunks(from: stream, config: config) {
|
|
received.append(chunk)
|
|
}
|
|
|
|
guard let last = received.last else {
|
|
return XCTFail("expected at least one chunk")
|
|
}
|
|
XCTAssertTrue(last.isLast)
|
|
XCTAssertGreaterThanOrEqual(last.samples.count, tail.count)
|
|
}
|
|
}
|