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.
 
 
Music/MusicTests/HLSSegmenterTests.swift

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
}
}