You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
3.4 KiB
89 lines
3.4 KiB
import Testing
|
|
import Foundation
|
|
@testable import Music
|
|
|
|
@MainActor
|
|
struct HLSSegmenterTests {
|
|
// Creates a segmenter for a test MP3 file and verifies it reports the correct duration.
|
|
@Test func readsDurationFromFile() async throws {
|
|
let url = try TestFixtures.shortMP3URL()
|
|
let segmenter = try HLSSegmenter(fileURL: url)
|
|
|
|
// The test fixture is ~3 seconds long
|
|
#expect(segmenter.duration > 2.0)
|
|
#expect(segmenter.duration < 5.0)
|
|
}
|
|
|
|
// Extracts the first segment and verifies it returns non-empty data.
|
|
@Test func extractsFirstSegment() async throws {
|
|
let url = try TestFixtures.shortMP3URL()
|
|
let segmenter = try HLSSegmenter(fileURL: url)
|
|
|
|
let data = try #require(await segmenter.segment(at: 0, segmentDuration: 6.0))
|
|
#expect(!data.isEmpty)
|
|
}
|
|
|
|
// Requesting a segment index beyond the track duration returns nil.
|
|
@Test func outOfRangeSegmentReturnsNil() async throws {
|
|
let url = try TestFixtures.shortMP3URL()
|
|
let segmenter = try HLSSegmenter(fileURL: url)
|
|
|
|
let data = try await segmenter.segment(at: 999, segmentDuration: 6.0)
|
|
#expect(data == nil)
|
|
}
|
|
}
|
|
|
|
enum TestFixtures {
|
|
// Returns the URL of a short test audio file (M4A/AAC).
|
|
// macOS cannot encode MP3, so we use AAC which AVAssetReader handles identically.
|
|
static func shortMP3URL() throws -> URL {
|
|
let tempDir = FileManager.default.temporaryDirectory
|
|
let url = tempDir.appendingPathComponent("test_fixture.m4a")
|
|
|
|
if !FileManager.default.fileExists(atPath: url.path) {
|
|
let wavURL = tempDir.appendingPathComponent("test_fixture.wav")
|
|
let sampleRate = 44100
|
|
let channels = 1
|
|
let durationSamples = sampleRate * 3
|
|
let bytesPerSample = 2
|
|
let dataSize = durationSamples * channels * bytesPerSample
|
|
|
|
var wavData = Data()
|
|
func appendString(_ s: String) { wavData.append(contentsOf: s.utf8) }
|
|
func appendUInt32(_ v: UInt32) { withUnsafeBytes(of: v.littleEndian) { wavData.append(contentsOf: $0) } }
|
|
func appendUInt16(_ v: UInt16) { withUnsafeBytes(of: v.littleEndian) { wavData.append(contentsOf: $0) } }
|
|
|
|
appendString("RIFF")
|
|
appendUInt32(UInt32(36 + dataSize))
|
|
appendString("WAVE")
|
|
appendString("fmt ")
|
|
appendUInt32(16)
|
|
appendUInt16(1)
|
|
appendUInt16(UInt16(channels))
|
|
appendUInt32(UInt32(sampleRate))
|
|
appendUInt32(UInt32(sampleRate * channels * bytesPerSample))
|
|
appendUInt16(UInt16(channels * bytesPerSample))
|
|
appendUInt16(UInt16(bytesPerSample * 8))
|
|
appendString("data")
|
|
appendUInt32(UInt32(dataSize))
|
|
wavData.append(Data(count: dataSize))
|
|
|
|
try wavData.write(to: wavURL)
|
|
|
|
let process = Process()
|
|
process.executableURL = URL(fileURLWithPath: "/usr/bin/afconvert")
|
|
process.arguments = [wavURL.path, url.path, "-f", "m4af", "-d", "aac"]
|
|
try process.run()
|
|
process.waitUntilExit()
|
|
|
|
try? FileManager.default.removeItem(at: wavURL)
|
|
|
|
guard FileManager.default.fileExists(atPath: url.path) else {
|
|
throw NSError(domain: "TestFixtures", code: 1,
|
|
userInfo: [NSLocalizedDescriptionKey: "Failed to create test audio fixture"])
|
|
}
|
|
}
|
|
|
|
return url
|
|
}
|
|
}
|
|
|