Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions AudioStreaming/Streaming/Audio Entry/AudioEntry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,12 @@ class AudioEntry {
if let byteCount = audioStreamState.dataByteCount {
return UInt(byteCount)
}
guard source.length > 0 else { return 0 }
return UInt(source.length) - UInt(audioStreamState.dataOffset)
let length = UInt64(max(source.length, 0))
let dataOffset = audioStreamState.dataOffset
// A live stream can report a length smaller than the parsed header offset;
// an unsigned subtraction would trap there.
guard length > dataOffset else { return 0 }
return UInt(length - dataOffset)
}
}

Expand Down
3 changes: 3 additions & 0 deletions AudioStreamingTests/Core/DispatchTimerSourceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ class DispatchTimerSourceTests: XCTestCase {

func test_HandlerIsExecuted_On_The_Specified_Queue() {
let expectaction = expectation(description: "fired")
// The source repeats every 100 ms; on a slow runner it can fire again before
// `wait` returns, and a second `fulfill()` raises an API violation.
expectaction.assertForOverFulfill = false

timerSource?.add {
XCTAssertEqual(DispatchQueue.getSpecific(key: self.dispatchKey), 1)
Expand Down
70 changes: 70 additions & 0 deletions AudioStreamingTests/Streaming/Audio Entry/AudioEntryTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// AudioEntryTests.swift
// AudioStreamingTests
//

import AVFoundation
import XCTest
@testable import AudioStreaming

class AudioEntryTests: XCTestCase {
func test_AudioDataLengthBytes_Is_Zero_When_DataOffset_Exceeds_Length() {
// a live stream can report a length smaller than the parsed header offset
let entry = audioEntry(length: 100)
entry.audioStreamState.dataOffset = 200

XCTAssertEqual(entry.audioDataLengthBytes(), 0)
}

func test_AudioDataLengthBytes_Is_Zero_When_DataOffset_Equals_Length() {
let entry = audioEntry(length: 100)
entry.audioStreamState.dataOffset = 100

XCTAssertEqual(entry.audioDataLengthBytes(), 0)
}

func test_AudioDataLengthBytes_Subtracts_DataOffset_From_Length() {
let entry = audioEntry(length: 1000)
entry.audioStreamState.dataOffset = 200

XCTAssertEqual(entry.audioDataLengthBytes(), 800)
}

func test_AudioDataLengthBytes_Prefers_Explicit_ByteCount() {
let entry = audioEntry(length: 100)
entry.audioStreamState.dataOffset = 200
entry.audioStreamState.dataByteCount = 5000

XCTAssertEqual(entry.audioDataLengthBytes(), 5000)
}

func test_Duration_Is_Zero_When_DataOffset_Exceeds_Length() {
let entry = audioEntry(length: 100)
entry.sampleRate = 44100
entry.audioStreamState.bitRate = 128_000
entry.audioStreamState.dataOffset = 200

XCTAssertEqual(entry.duration(), 0)
}
}

private final class FixedLengthSource: CoreAudioStreamSource {
let position = 0
let length: Int
weak var delegate: AudioStreamSourceDelegate?
let audioFileHint: AudioFileTypeID = kAudioFileMP3Type
let underlyingQueue = DispatchQueue(label: "audio-entry-tests")

init(length: Int) { self.length = length }

func close() {}
func suspend() {}
func resume() {}
func seek(at _: Int) {}
}

private func audioEntry(length: Int) -> AudioEntry {
AudioEntry(source: FixedLengthSource(length: length),
entryId: AudioEntryId(id: "entry"),
outputAudioFormat: AVAudioFormat())
}
Loading