import Quick import Nimble import AVFoundation import XCTest @testable import SwiftAudio class AudioPlayerTests: XCTestCase { var audioPlayer: AudioPlayer! var listener: AudioPlayerEventListener! override func setUp() { super.setUp() audioPlayer = AudioPlayer() audioPlayer.volume = 0.0 audioPlayer.bufferDuration = 0.001 audioPlayer.automaticallyWaitsToMinimizeStalling = false listener = AudioPlayerEventListener(audioPlayer: audioPlayer) } override func tearDown() { audioPlayer = nil listener = nil super.tearDown() } func test_AudioPlayer__state__should_be_idle() { XCTAssert(audioPlayer.playerState == AudioPlayerState.idle) } func test_AudioPlayer__state__load_source__should_be_ready() { let expectation = XCTestExpectation() listener.stateUpdate = { state in switch state { case .ready: expectation.fulfill() default: break } } try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false) wait(for: [expectation], timeout: 20.0) } func test_AudioPlayer__state__load_source_playWhenReady__should_be_playing() { let expectation = XCTestExpectation() listener.stateUpdate = { state in switch state { case .playing: expectation.fulfill() default: break } } try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true) wait(for: [expectation], timeout: 20.0) } func test_AudioPlayer__state__play_source__should_be_playing() { let expectation = XCTestExpectation() listener.stateUpdate = { state in switch state { case .ready: self.audioPlayer.play() case .playing: expectation.fulfill() default: break } } try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false) wait(for: [expectation], timeout: 20.0) } func test_AudioPlayer__state__pausing_source__should_be_paused() { let expectation = XCTestExpectation() listener.stateUpdate = { [weak audioPlayer] state in switch state { case .playing: audioPlayer?.pause() case .paused: expectation.fulfill() default: break } } try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true) wait(for: [expectation], timeout: 20.0) } func test_AudioPlayer__state__stopping_source__should_be_idle() { let expectation = XCTestExpectation() var hasBeenPlaying: Bool = false listener.stateUpdate = { [weak audioPlayer] state in switch state { case .playing: hasBeenPlaying = true audioPlayer?.stop() case .idle: if hasBeenPlaying { expectation.fulfill() } default: break } } try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true) wait(for: [expectation], timeout: 20.0) } // MARK: - Current time func test_AudioPlayer__currentTime__should_be_0() { XCTAssert(audioPlayer.currentTime == 0.0) } // Commented out -- Keeps failing in CI at Bitrise, but succeeds locally, even with Bitrise CLI. // func test_AudioPlayer__currentTime__playing_source__shold_be_greater_than_0() { // let expectation = XCTestExpectation() // audioPlayer.timeEventFrequency = .everyQuarterSecond // listener.secondsElapse = { _ in // if self.audioPlayer.currentTime > 0.0 { // expectation.fulfill() // } // } // try? audioPlayer.load(item: LongSource.getAudioItem(), playWhenReady: true) // wait(for: [expectation], timeout: 20.0) // } // MARK: - Rate func test_AudioPlayer__rate__should_be_0() { XCTAssert(audioPlayer.rate == 0.0) } func test_AudioPlayer__rate__playing_source__should_be_1() { let expectation = XCTestExpectation() listener.stateUpdate = { [weak audioPlayer] state in guard let audioPlayer = audioPlayer else { return } switch state { case .playing: if audioPlayer.rate == 1.0 { expectation.fulfill() } default: break } } try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true) wait(for: [expectation], timeout: 20.0) } // MARK: - Current item func test_AudioPlayer__currentItem__should_be_nil() { XCTAssertNil(audioPlayer.currentItem) } func test_AudioPlayer__currentItem__loading_source__should_not_be_nil() { let expectation = XCTestExpectation() listener.stateUpdate = { [weak audioPlayer] state in guard let audioPlayer = audioPlayer else { return } switch state { case .ready: if audioPlayer.currentItem != nil { expectation.fulfill() } default: break } } try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false) wait(for: [expectation], timeout: 20.0) } } class AudioPlayerEventListener { var state: AudioPlayerState? { didSet { if let state = state { stateUpdate?(state) } } } var stateUpdate: ((_ state: AudioPlayerState) -> Void)? var secondsElapse: ((_ seconds: TimeInterval) -> Void)? var seekCompletion: (() -> Void)? weak var audioPlayer: AudioPlayer? init(audioPlayer: AudioPlayer) { audioPlayer.event.stateChange.addListener(self, handleDidUpdateState) audioPlayer.event.seek.addListener(self, handleSeek) audioPlayer.event.secondElapse.addListener(self, handleSecondsElapse) } deinit { audioPlayer?.event.stateChange.removeListener(self) audioPlayer?.event.seek.removeListener(self) audioPlayer?.event.secondElapse.removeListener(self) } func handleDidUpdateState(state: AudioPlayerState) { self.state = state } func handleSeek(data: AudioPlayer.SeekEventData) { seekCompletion?() } func handleSecondsElapse(data: AudioPlayer.SecondElapseEventData) { self.secondsElapse?(data) } }