forked from mirrors/linphone-iphone
380 lines
13 KiB
Swift
380 lines
13 KiB
Swift
import XCTest
|
|
import linphonesw
|
|
|
|
|
|
class UITestsCoreManager {
|
|
|
|
private var mCore: Core!
|
|
private var coreVersion: String = Core.getVersion
|
|
|
|
private var mAccountCreator: AccountCreator!
|
|
|
|
var appAccountAuthInfo: AuthInfo!
|
|
var ghostAccounts: UITestsGhostAccounts!
|
|
let dnsServer = "51.255.123.121"
|
|
|
|
static let instance = UITestsCoreManager()
|
|
|
|
|
|
init() {
|
|
LoggingService.Instance.logLevel = LogLevel.Debug
|
|
Core.enableLogCollection(state: .Enabled)
|
|
|
|
//Config account creator for flexiapi
|
|
let config: Config! = try! Factory.Instance.createConfig(path: "\(Factory.Instance.getConfigDir(context: nil))/linphonerc")
|
|
config.setInt(section: "account_creator", key: "backend", value: AccountCreatorBackend.FlexiAPI.rawValue)
|
|
config.setString(section: "account_creator", key: "url", value: "http://subscribe.example.org/flexiapi/api/")
|
|
try! mCore = Factory.Instance.createCoreWithConfig(config: config, systemContext: nil)
|
|
mCore.dnsServersApp = [dnsServer]
|
|
mAccountCreator = try! mCore.createAccountCreator(xmlrpcUrl: nil)
|
|
|
|
try? mCore.start()
|
|
|
|
ghostAccounts = UITestsGhostAccounts(coreCreationFunction: newRegisteredLinphoneCore)
|
|
|
|
appAccountAuthInfo = mCore.authInfoList.isEmpty ? createAccount() : mCore.authInfoList[0]
|
|
}
|
|
|
|
deinit {
|
|
mCore.stop()
|
|
mCore = nil
|
|
}
|
|
|
|
func newRegisteredLinphoneCore() -> UITestsRegisteredLinphoneCore {
|
|
let authInfo = mCore.authInfoList.indices.contains(ghostAccounts.count+1) ? mCore.authInfoList[ghostAccounts.count+1] : createAccount()
|
|
return UITestsRegisteredLinphoneCore(authInfo: authInfo)
|
|
}
|
|
|
|
func createAccount() -> AuthInfo {
|
|
XCTContext.runActivity(named: "Create new account") { _ in
|
|
mAccountCreator.username = "uitester_\(String(Int(Date().timeIntervalSince1970*1000)).suffix(5))"
|
|
mAccountCreator.password = String((0..<15).map{ _ in mAccountCreator!.username.randomElement()! })
|
|
mAccountCreator.domain = "sip.example.org"
|
|
mAccountCreator.email = "\(mAccountCreator!.username)@\(mAccountCreator!.domain)"
|
|
mAccountCreator.transport = TransportType.Tcp
|
|
_ = try! mAccountCreator.createAccount()
|
|
waitForAccountCreationStatus(status: .RequestOk, timeout: 5)
|
|
|
|
let authInfo = try! Factory.Instance.createAuthInfo(username: mAccountCreator.username, userid: "", passwd: mAccountCreator.password, ha1: "", realm: "", domain: mAccountCreator.domain)
|
|
mCore.addAuthInfo(info: authInfo)
|
|
XCTContext.runActivity(named: "username : \(mAccountCreator.username)\npassword : \(mAccountCreator.password)\ndomain : \(mAccountCreator.domain)") { _ in}
|
|
return authInfo
|
|
}
|
|
}
|
|
|
|
func accountsReset() {
|
|
XCTContext.runActivity(named: "Clear all accounts") { _ in
|
|
mCore.clearAllAuthInfo()
|
|
ghostAccounts.reset()
|
|
appAccountAuthInfo = createAccount()
|
|
}
|
|
}
|
|
|
|
func createAdress(authInfo: AuthInfo) -> Address {
|
|
return try! Factory.Instance.createAddress(addr: "sip:\(authInfo.username)@\(authInfo.domain)")
|
|
}
|
|
|
|
func waitForAccountCreationStatus(status: AccountCreator.Status, timeout: Double) {
|
|
let expectation = XCTestExpectation(description: "account status is successfully : \(status)")
|
|
XCTContext.runActivity(named: "Waiting for account status : \(status)") { _ in
|
|
let accountCreatorDelegate = AccountCreatorDelegateStub(onCreateAccount: { (creator: AccountCreator, status: AccountCreator.Status, response: String) in
|
|
if (status == status) {
|
|
expectation.fulfill()
|
|
}
|
|
})
|
|
self.mAccountCreator?.addDelegate(delegate: accountCreatorDelegate)
|
|
let result = XCTWaiter().wait(for: [expectation], timeout: timeout)
|
|
self.mAccountCreator?.removeDelegate(delegate: accountCreatorDelegate)
|
|
XCTAssert(result == .completed, "\"\(status)\" account status still not verified after \(timeout) seconds")
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class UITestsGhostAccounts {
|
|
|
|
private var mCores = [UITestsRegisteredLinphoneCore]() {
|
|
didSet {
|
|
count = mCores.count
|
|
}
|
|
}
|
|
private(set) var count: Int!
|
|
|
|
private let newCore: (()->UITestsRegisteredLinphoneCore)!
|
|
|
|
init(coreCreationFunction: @escaping ()->UITestsRegisteredLinphoneCore) {
|
|
count = mCores.count
|
|
newCore = coreCreationFunction
|
|
}
|
|
|
|
func reset() {
|
|
mCores = []
|
|
}
|
|
|
|
subscript (index: Int) -> UITestsRegisteredLinphoneCore {
|
|
while (index >= mCores.count) {
|
|
mCores.append(newCore())
|
|
}
|
|
return mCores[index]
|
|
}
|
|
|
|
}
|
|
|
|
|
|
class UITestsRegisteredLinphoneCore {
|
|
|
|
var mCore: Core!
|
|
var coreVersion: String = Core.getVersion
|
|
var description: String
|
|
|
|
private let manager = UITestsCoreManager.instance
|
|
|
|
private(set) var mCoreDelegate : CoreDelegate!
|
|
private(set) var mAccount: Account!
|
|
private(set) var mAuthInfo: AuthInfo!
|
|
|
|
private(set) var callState : Call.State = .Released
|
|
private(set) var registrationState : RegistrationState = .Cleared
|
|
|
|
init(authInfo: AuthInfo) {
|
|
|
|
description = "Ghost Account (\(authInfo.username))"
|
|
LoggingService.Instance.logLevel = LogLevel.Debug
|
|
Core.enableLogCollection(state: .Enabled)
|
|
|
|
try! mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
|
|
mCore.dnsServers = [manager.dnsServer]
|
|
|
|
mCore.videoCaptureEnabled = true
|
|
mCore.videoDisplayEnabled = true
|
|
mCore.recordAwareEnabled = true
|
|
mCore.videoActivationPolicy!.automaticallyAccept = true
|
|
|
|
mCoreDelegate = CoreDelegateStub(onCallStateChanged: { (core: Core, call: Call, state: Call.State, message: String) in
|
|
self.callState = state
|
|
NSLog("\(call.params?.account?.params?.identityAddress) current call state is \(self.callState)\n")
|
|
|
|
}, onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in
|
|
self.registrationState = state
|
|
NSLog("New registration state is \(state) for user id \(account.params?.identityAddress)\n")
|
|
})
|
|
mCore.addDelegate(delegate: mCoreDelegate)
|
|
|
|
mCore.playFile = "sounds/hello8000.wav"
|
|
mCore.useFiles = true
|
|
|
|
try? mCore.start()
|
|
|
|
mAuthInfo = authInfo
|
|
login(transport: TransportType.Tcp)
|
|
}
|
|
|
|
deinit {
|
|
mCore.stop()
|
|
mCore = nil
|
|
mCoreDelegate = nil
|
|
}
|
|
|
|
func login(transport: TransportType) {
|
|
XCTContext.runActivity(named: "\(description) : Login") { _ in
|
|
do {
|
|
let accountParams = try mCore.createAccountParams()
|
|
let identity = manager.createAdress(authInfo: mAuthInfo)
|
|
try accountParams.setIdentityaddress(newValue: identity)
|
|
let address = try Factory.Instance.createAddress(addr: String("sip:" + mAuthInfo.domain))
|
|
try address.setTransport(newValue: transport)
|
|
try accountParams.setServeraddress(newValue: address)
|
|
accountParams.registerEnabled = true
|
|
let account = try mCore.createAccount(params: accountParams)
|
|
mCore.addAuthInfo(info: mAuthInfo)
|
|
try mCore.addAccount(account: account)
|
|
mAccount = account
|
|
mCore.defaultAccount = mAccount
|
|
waitForRegistrationState(registrationState: .Ok, timeout: 5)
|
|
|
|
} catch { NSLog(error.localizedDescription) }
|
|
}
|
|
}
|
|
|
|
private func makeRecordFilePath() -> String{
|
|
var filePath = "recording_"
|
|
let now = Date()
|
|
let dateFormat = DateFormatter()
|
|
dateFormat.dateFormat = "E-d-MMM-yyyy-HH-mm-ss"
|
|
let date = dateFormat.string(from: now)
|
|
filePath = filePath.appending("\(date).mkv")
|
|
|
|
let paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
|
|
let writablePath = paths[0]
|
|
return writablePath.appending("/\(filePath)")
|
|
}
|
|
|
|
func startCall(adress: Address) {
|
|
XCTContext.runActivity(named: "\(description) : Start calling \(adress.username)") { _ in
|
|
do {
|
|
let params = try mCore.createCallParams(call: nil)
|
|
params.mediaEncryption = MediaEncryption.None
|
|
params.recordFile = makeRecordFilePath()
|
|
let _ = mCore.inviteAddressWithParams(addr: adress, params: params)
|
|
} catch { NSLog(error.localizedDescription) }
|
|
}
|
|
|
|
}
|
|
|
|
func terminateCall() {
|
|
XCTContext.runActivity(named: "\(description) : End call") { _ in
|
|
do {
|
|
if (mCore.callsNb == 0) { return }
|
|
let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0]
|
|
if let call = coreCall {
|
|
try call.terminate()
|
|
}
|
|
} catch { NSLog(error.localizedDescription) }
|
|
}
|
|
}
|
|
|
|
func acceptCall() {
|
|
XCTContext.runActivity(named: "\(description) : Accept call from \(mAuthInfo.username)") { _ in
|
|
// IMPORTANT : Make sure you allowed the use of the microphone (see key "Privacy - Microphone usage description" in Info.plist) !
|
|
do {
|
|
// if we wanted, we could create a CallParams object
|
|
// and answer using this object to make changes to the call configuration
|
|
// (see OutgoingCall tutorial)
|
|
try mCore.currentCall?.accept()
|
|
} catch { NSLog(error.localizedDescription) }
|
|
}
|
|
}
|
|
|
|
func toggleMicrophone() {
|
|
// The following toggles the microphone, disabling completely / enabling the sound capture
|
|
// from the device microphone
|
|
mCore.micEnabled = !mCore.micEnabled
|
|
}
|
|
|
|
func toggleSpeaker() {
|
|
// Get the currently used audio device
|
|
let currentAudioDevice = mCore.currentCall?.outputAudioDevice
|
|
let speakerEnabled = currentAudioDevice?.type == AudioDeviceType.Speaker
|
|
|
|
let test = currentAudioDevice?.deviceName
|
|
// We can get a list of all available audio devices using
|
|
// Note that on tablets for example, there may be no Earpiece device
|
|
for audioDevice in mCore.audioDevices {
|
|
|
|
// For IOS, the Speaker is an exception, Linphone cannot differentiate Input and Output.
|
|
// This means that the default output device, the earpiece, is paired with the default phone microphone.
|
|
// Setting the output audio device to the microphone will redirect the sound to the earpiece.
|
|
if (speakerEnabled && audioDevice.type == AudioDeviceType.Microphone) {
|
|
mCore.currentCall?.outputAudioDevice = audioDevice
|
|
return
|
|
} else if (!speakerEnabled && audioDevice.type == AudioDeviceType.Speaker) {
|
|
mCore.currentCall?.outputAudioDevice = audioDevice
|
|
return
|
|
}
|
|
/* If we wanted to route the audio to a bluetooth headset
|
|
else if (audioDevice.type == AudioDevice.Type.Bluetooth) {
|
|
core.currentCall?.outputAudioDevice = audioDevice
|
|
}*/
|
|
}
|
|
}
|
|
|
|
func toggleVideo() {
|
|
do {
|
|
if (mCore.callsNb == 0) { return }
|
|
let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0]
|
|
if let call = coreCall {
|
|
let params = try mCore.createCallParams(call: call)
|
|
params.videoEnabled = !(call.currentParams!.videoEnabled)
|
|
try call.update(params: params)
|
|
}
|
|
} catch { NSLog(error.localizedDescription) }
|
|
}
|
|
|
|
func toggleCamera() {
|
|
do {
|
|
let currentDevice = mCore.videoDevice
|
|
for camera in mCore.videoDevicesList {
|
|
if (camera != currentDevice && camera != "StaticImage: Static picture") {
|
|
try mCore.setVideodevice(newValue: camera)
|
|
break
|
|
}
|
|
}
|
|
} catch { NSLog(error.localizedDescription) }
|
|
}
|
|
|
|
func pauseCall() {
|
|
do {
|
|
if (mCore.callsNb == 0) { return }
|
|
let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0]
|
|
try coreCall!.pause()
|
|
} catch { NSLog(error.localizedDescription) }
|
|
}
|
|
|
|
func resumeCall() {
|
|
do {
|
|
if (mCore.callsNb == 0) { return }
|
|
let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0]
|
|
try coreCall!.resume()
|
|
} catch { NSLog(error.localizedDescription) }
|
|
}
|
|
|
|
func startRecording() {
|
|
mCore.currentCall?.startRecording()
|
|
}
|
|
|
|
func stopRecording() {
|
|
mCore.currentCall?.stopRecording()
|
|
}
|
|
|
|
func waitForRegistrationState(registrationState: RegistrationState, timeout: TimeInterval) {
|
|
let expectation = XCTestExpectation(description: "registration state is successfully : \(registrationState)")
|
|
XCTContext.runActivity(named: "Waiting for registration state : \(registrationState)") { _ in
|
|
if (registrationState == self.registrationState) { return}
|
|
let registeredDelegate = AccountDelegateStub(onRegistrationStateChanged: { (account: Account, state: RegistrationState, message: String) in
|
|
if (registrationState == state) {
|
|
expectation.fulfill()
|
|
}
|
|
})
|
|
self.mCore.defaultAccount!.addDelegate(delegate: registeredDelegate)
|
|
let result = XCTWaiter().wait(for: [expectation], timeout: timeout)
|
|
self.mCore.defaultAccount!.removeDelegate(delegate: registeredDelegate)
|
|
XCTAssert(result == .completed, "\"\(registrationState)\" registration state still not verified after \(timeout) seconds")
|
|
}
|
|
}
|
|
|
|
func waitForCallState(callState: Call.State, timeout: TimeInterval) {
|
|
let expectation = XCTestExpectation(description: "call state is successfully : \(callState)")
|
|
XCTContext.runActivity(named: "Waiting for call state : \(callState)") { _ in
|
|
if (callState == self.callState) { return}
|
|
let callStateDelegate = CoreDelegateStub(onCallStateChanged: { (lc: Core, call: Call, state: Call.State, message: String) in
|
|
if (callState == state) {
|
|
expectation.fulfill()
|
|
}
|
|
})
|
|
self.mCore.addDelegate(delegate: callStateDelegate)
|
|
let result = XCTWaiter().wait(for: [expectation], timeout: timeout)
|
|
self.mCore.removeDelegate(delegate: callStateDelegate)
|
|
XCTAssert(result == .completed, "\"\(callState)\" call state still not verified after \(timeout) seconds")
|
|
}
|
|
}
|
|
|
|
func waitForRecordingState(recording: Bool, onRemote: Bool = false, timeout: TimeInterval) {
|
|
XCTContext.runActivity(named: "Waiting for call recording state : \(recording)") { _ in
|
|
var result = XCTWaiter.Result.timedOut
|
|
for _ in 0...Int(timeout) {
|
|
if (!onRemote && recording == mCore.currentCall?.params?.isRecording) {
|
|
result = .completed
|
|
break
|
|
}
|
|
if (onRemote && recording == mCore.currentCall?.remoteParams?.isRecording) {
|
|
result = .completed
|
|
break
|
|
}
|
|
_ = XCTWaiter().wait(for: [XCTestExpectation()], timeout: 1)
|
|
}
|
|
let remoteText = onRemote ? "remote" : ""
|
|
XCTAssert(result == .completed, "\(remoteText) call recording is still not \(recording) after \(timeout) seconds")
|
|
}
|
|
}
|
|
|
|
}
|