AmazonChimeSDK/AmazonChimeSDKTests/audiovideo/audio/ActiveSpeakerDetectorTests.swift (110 lines of code) (raw):
//
// ActiveSpeakerDetectorTests.swift
// AmazonChimeSDK
//
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
import Foundation
@testable import AmazonChimeSDK
import XCTest
class ActiveSpeakerDetectorTests: XCTestCase, ActiveSpeakerPolicy, ActiveSpeakerObserver {
let scores = [0.1, 0.2, 0.3, 0.4, 0.5]
let volumes: [VolumeLevel] = [.muted, .notSpeaking, .low, .medium, .high]
var attendees = [AttendeeInfo]()
var volumeUpdates = [VolumeUpdate]()
var attendeesReceived: [AttendeeInfo] = [AttendeeInfo(attendeeId: "", externalUserId: "")]
var scoreChangeAttendees: [AttendeeInfo: Double] = [AttendeeInfo(attendeeId: "", externalUserId: ""): 0.0]
var scoreIndex = 0
var activeSpeakerDetector = DefaultActiveSpeakerDetector(selfAttendeeId: "")
private let emptyAttendeesReceivedExpectation = XCTestExpectation(
description: "Is fulfilled when received no attendees in callback")
private let calculateScoreExpectation = XCTestExpectation(
description: "Is fulfilled when calculateScore is called")
private let prioritizeBandwidthExpectation = XCTestExpectation(
description: "Is fulfilled when prioritizeVideoSendBandwidthForActiveSpeaker is called")
private let activeSpeakerDidDetectExpectation = XCTestExpectation(
description: "Is fulfilled when activeSpeakerDidDetect is called")
private let activeSpeakerScoreDidChangeExpectation = XCTestExpectation(
description: "Is fulfilled when activeSpeakerScoreDidChange is called")
private let oneMilliSecondInSeconds = 0.001
private let twoHundredMilliSecondsInSeconds = 0.2
private let halfSeconds = 0.5
override func setUp() {
for index in 0 ... 4 {
attendees.append(AttendeeInfo(
attendeeId: "attendee" + String(index),
externalUserId: "attendee" + String(index)
))
volumeUpdates.append(VolumeUpdate(attendeeInfo: attendees[index], volumeLevel: volumes[index]))
}
calculateScoreExpectation.expectedFulfillmentCount = 5
prioritizeBandwidthExpectation.assertForOverFulfill = true
activeSpeakerDetector = DefaultActiveSpeakerDetector(selfAttendeeId: "attendee0")
activeSpeakerDetector.addActiveSpeakerObserver(policy: self, observer: self)
activeSpeakerDetector.attendeesDidJoin(attendeeInfo: attendees)
}
override func tearDown() {
activeSpeakerDetector.removeActiveSpeakerObserver(observer: self)
}
func testActiveSpeakerDetectorShouldOnAttendeesJoinMakeExpectedCallbacks() {
// received no attendees in callback because scores are not calculated until volumes are received
wait(for: [emptyAttendeesReceivedExpectation], timeout: oneMilliSecondInSeconds)
XCTAssertEqual(attendeesReceived.isEmpty, true)
calculateScoreExpectation.isInverted = true
wait(for: [calculateScoreExpectation], timeout: oneMilliSecondInSeconds)
prioritizeBandwidthExpectation.isInverted = true
wait(for: [prioritizeBandwidthExpectation], timeout: oneMilliSecondInSeconds)
wait(for: [activeSpeakerDidDetectExpectation], timeout: oneMilliSecondInSeconds)
}
func testActiveSpeakerDetectorShouldOnVolumeChangeMakeExpectedCallbacks() {
activeSpeakerDetector.volumeDidChange(volumeUpdates: volumeUpdates)
calculateScoreExpectation.expectedFulfillmentCount = 2
wait(for: [calculateScoreExpectation], timeout: twoHundredMilliSecondsInSeconds)
wait(for: [prioritizeBandwidthExpectation], timeout: oneMilliSecondInSeconds)
wait(for: [activeSpeakerDidDetectExpectation], timeout: oneMilliSecondInSeconds)
XCTAssertEqual(attendeesReceived, attendees.reversed())
}
func testActiveSpeakerDetectorMakesScoresCallback() {
activeSpeakerDetector.volumeDidChange(volumeUpdates: volumeUpdates)
wait(for: [activeSpeakerScoreDidChangeExpectation], timeout: TimeInterval(scoresCallbackIntervalMs))
XCTAssertEqual(scoreChangeAttendees.map { $0.key }.sorted(), attendees.sorted())
}
func testActiveSpeakerDetectorShouldOnAttendeesLeaveReceiveCorrectActiveSpeakers() {
activeSpeakerDetector.volumeDidChange(volumeUpdates: volumeUpdates)
wait(for: [activeSpeakerScoreDidChangeExpectation], timeout: twoHundredMilliSecondsInSeconds)
XCTAssertEqual(attendeesReceived, attendees.reversed())
activeSpeakerDetector.attendeesDidLeave(attendeeInfo: [attendees[0], attendees[1]])
let newVolumeUpdates = [volumeUpdates[2], volumeUpdates[3], volumeUpdates[4]]
scoreIndex = 0
activeSpeakerDetector.volumeDidChange(volumeUpdates: newVolumeUpdates)
let adhocWaitingExpectation = XCTestExpectation(description: "Is fulfilled after adhoc waiting")
Timer.scheduledTimer(withTimeInterval: twoHundredMilliSecondsInSeconds, repeats: false, block: { _ in
adhocWaitingExpectation.fulfill()
})
wait(for: [adhocWaitingExpectation], timeout: halfSeconds)
XCTAssertEqual(attendeesReceived, [attendees[4], attendees[3], attendees[2]])
}
func testActiveSpeakerDetectorShouldRemoveActiveSpeakerObserver() {
activeSpeakerDetector.removeActiveSpeakerObserver(observer: self)
activeSpeakerDetector.attendeesDidJoin(attendeeInfo: attendees)
activeSpeakerDetector.volumeDidChange(volumeUpdates: volumeUpdates)
wait(for: [activeSpeakerDidDetectExpectation], timeout: oneMilliSecondInSeconds)
XCTAssertEqual(attendeesReceived, [])
}
func calculateScore(attendeeInfo: AttendeeInfo, volume: VolumeLevel) -> Double {
calculateScoreExpectation.fulfill()
let returnScore = scoreIndex < 5 ? scores[scoreIndex] : 0.0
scoreIndex += 1
return returnScore
}
func prioritizeVideoSendBandwidthForActiveSpeaker() -> Bool {
prioritizeBandwidthExpectation.fulfill()
return true
}
var observerId: String = "fakeObserverId"
func activeSpeakerDidDetect(attendeeInfo: [AttendeeInfo]) {
activeSpeakerDidDetectExpectation.fulfill()
if attendeeInfo.isEmpty {
emptyAttendeesReceivedExpectation.fulfill()
}
attendeesReceived = attendeeInfo
}
var scoresCallbackIntervalMs: Int = 1
func activeSpeakerScoreDidChange(scores: [AttendeeInfo: Double]) {
activeSpeakerScoreDidChangeExpectation.fulfill()
scoreChangeAttendees = scores
}
}