linphonetester.py 32.4 KB
Newer Older
1 2
from datetime import timedelta, datetime
from nose.tools import assert_equals
3
from copy import deepcopy
4 5 6
import linphone
import logging
import os
7
import sys
8
import time
9
import weakref
10 11


12
test_domain = "sipopen.example.org"
13
auth_domain = "sip.example.org"
14 15 16
test_username = "liblinphone_tester"
test_password = "secret"
test_route = "sip2.linphone.org"
17 18 19 20 21 22
if os.path.isdir(os.path.join(os.path.dirname(__file__), "rcfiles")):
    # Running unit tests from an installed package
    tester_resources_path = os.path.abspath(os.path.dirname(__file__))
else:
    # Running unit tests from the linphone sources
    tester_resources_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../tester/"))
23 24


25
def linphonetester_log_handler(level, msg):
26
    import logging
27 28 29 30 31 32 33 34 35 36 37 38 39 40
    method = getattr(logging.getLogger("linphonetester"), level)
    if not msg.strip().startswith('[PYLINPHONE]'):
        msg = '[CORE] ' + msg
    method(msg)

linphonetester_logger = logging.getLogger("linphonetester")
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s.%(msecs)03d %(levelname)s: %(message)s', '%H:%M:%S')
handler.setFormatter(formatter)
linphonetester_logger.addHandler(handler)
linphone.set_log_handler(linphonetester_log_handler)


41
def create_address(domain):
Ghislain MARY's avatar
Ghislain MARY committed
42
    addr = linphone.Factory.get().create_address(None)
43 44 45 46 47 48 49 50 51 52 53 54 55
    assert addr != None
    addr.username = test_username
    assert_equals(addr.username, test_username)
    if domain is None:
        domain = test_route
    addr.domain = domain
    assert_equals(addr.domain, domain)
    addr.display_name = None
    addr.display_name = "Mr Tester"
    assert_equals(addr.display_name, "Mr Tester")
    return addr


56 57 58 59
class Account:
    def __init__(self, id_addr, unique_id):
        self.created = False
        self.done = False
Ghislain MARY's avatar
Ghislain MARY committed
60
        self.registered = False
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
        self.identity = id_addr.clone()
        self.password = linphone.testing.get_random_token(8)
        self.modified_identity = id_addr.clone()
        modified_username = "{username}_{unique_id}".format(username=id_addr.username, unique_id=unique_id)
        self.modified_identity.username = modified_username


class AccountManager:
    def __init__(self):
        self.unique_id = linphone.testing.get_random_token(6)
        self.accounts = []

    @classmethod
    def wait_for_until(cls, lc1, lc2, func, timeout):
        lcs = []
        if lc1 is not None:
            lcs.append(lc1)
        if lc2 is not None:
            lcs.append(lc2)
        return cls.wait_for_list(lcs, func, timeout)

    @classmethod
    def wait_for_list(cls, lcs, func, timeout):
        start = datetime.now()
        end = start + timedelta(milliseconds = timeout)
        res = func(*lcs)
        while not res and datetime.now() < end:
            for lc in lcs:
                lc.iterate()
            time.sleep(0.02)
            res = func(*lcs)
        return res

    @classmethod
    def account_created_on_server_cb(cls, lc, cfg, state, message):
Ghislain MARY's avatar
Ghislain MARY committed
96
        if state == linphone.RegistrationState.Ok:
Ghislain MARY's avatar
Ghislain MARY committed
97 98 99 100
            if cfg.error_info.phrase == "Test account created":
                lc.user_data().created = True
            else:
                lc.user_data().registered = True
Ghislain MARY's avatar
Ghislain MARY committed
101
        elif state == linphone.RegistrationState.Cleared:
102
            lc.user_data().done = True
103 104 105

    @classmethod
    def account_created_auth_requested_cb(cls, lc, realm, username, domain):
Ghislain MARY's avatar
Ghislain MARY committed
106
        lc.user_data().created = True
107

108
    def check_account(self, cfg):
109 110
        create_account = False
        lc = cfg.core
111
        id_addr = cfg.identity_address
112
        account = self._get_account(id_addr)
Ghislain MARY's avatar
Ghislain MARY committed
113
        original_ai = lc.find_auth_info(None, id_addr.username, id_addr.domain)
114
        if account is None:
115
            linphonetester_logger.info("[TESTER] No account for {identity} exists, going to create one.".format(identity=id_addr.as_string()))
116 117 118
            account = Account(id_addr, self.unique_id)
            self.accounts.append(account)
            create_account = True
119
        cfg.identity_address = account.modified_identity
120
        if create_account:
121
            self._create_account_on_server(account, cfg)
Ghislain MARY's avatar
Ghislain MARY committed
122 123
        if original_ai is not None:
            lc.remove_auth_info(original_ai)
Ghislain MARY's avatar
Ghislain MARY committed
124
        ai = linphone.Factory.get().create_auth_info(account.modified_identity.username, None, account.password, None, None, account.modified_identity.domain)
125 126 127
        lc.add_auth_info(ai)
        return account.modified_identity

128 129 130 131 132 133
    def _get_account(self, id_addr):
        for account in self.accounts:
            if account.identity.weak_equal(id_addr):
                return account
        return None

134
    def _create_account_on_server(self, account, refcfg):
135
        tmp_identity = account.modified_identity.clone()
Ghislain MARY's avatar
Ghislain MARY committed
136 137 138 139
        cbs = linphone.Factory.get().create_core_cbs()
        cbs.registration_state_changed = AccountManager.account_created_on_server_cb
        cbs.authentication_requested = AccountManager.account_created_auth_requested_cb
        lc = CoreManager.configure_lc_from(cbs, tester_resources_path, None, account)
140
        lc.sip_transports = linphone.SipTransports(-1, -1, -1, -1)
141
        cfg = lc.create_proxy_config()
Ghislain MARY's avatar
Ghislain MARY committed
142
        tmp_identity.secure = False
143 144
        tmp_identity.password = account.password
        tmp_identity.set_header("X-Create-Account", "yes")
145
        cfg.identity_address = tmp_identity
Ghislain MARY's avatar
Ghislain MARY committed
146
        server_addr = linphone.Factory.get().create_address(refcfg.server_addr)
Ghislain MARY's avatar
Ghislain MARY committed
147
        server_addr.secure = False
Ghislain MARY's avatar
Ghislain MARY committed
148
        server_addr.transport = linphone.TransportType.Tcp;
149 150
        server_addr.port = 0
        cfg.server_addr = server_addr.as_string()
Ghislain MARY's avatar
Ghislain MARY committed
151
        cfg.expires = 3 * 3600 # Accounts are valid 3 hours
152
        lc.add_proxy_config(cfg)
Ghislain MARY's avatar
Ghislain MARY committed
153
        if AccountManager.wait_for_until(lc, None, lambda lc: lc.user_data().created == True, 10000) != True:
154
            linphonetester_logger.critical("[TESTER] Account for {identity} could not be created on server.".format(identity=refcfg.identity_address.as_string()))
155 156
            sys.exit(-1)
        cfg.edit()
Ghislain MARY's avatar
Ghislain MARY committed
157 158
        cfg.identity_address = account.modified_identity.clone()
        cfg.identity_address.secure = False
159
        cfg.done()
Ghislain MARY's avatar
Ghislain MARY committed
160
        ai = linphone.Factory.get().create_auth_info(account.modified_identity.username, None, account.password, None, None, account.modified_identity.domain)
161
        lc.add_auth_info(ai)
Ghislain MARY's avatar
Ghislain MARY committed
162
        if AccountManager.wait_for_until(lc, None, lambda lc: lc.user_data().registered == True, 3000) != True:
163
            linphonetester_logger.critical("[TESTER] Account for {identity} is not working on server.".format(identity=refcfg.identity_address.as_string()))
164 165
            sys.exit(-1)
        lc.remove_proxy_config(cfg)
166
        if AccountManager.wait_for_until(lc, None, lambda lc: lc.user_data().done == True, 3000) != True:
167
            linphonetester_logger.critical("[TESTER] Account creation could not clean the registration context.")
168 169 170 171 172 173
            sys.exit(-1)


account_manager = AccountManager()


174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
class CoreManagerStats:
    def __init__(self):
        self.reset()

    def reset(self):
        self.number_of_LinphoneRegistrationNone = 0
        self.number_of_LinphoneRegistrationProgress = 0
        self.number_of_LinphoneRegistrationOk = 0
        self.number_of_LinphoneRegistrationCleared = 0
        self.number_of_LinphoneRegistrationFailed = 0
        self.number_of_auth_info_requested = 0

        self.number_of_LinphoneCallIncomingReceived = 0
        self.number_of_LinphoneCallOutgoingInit = 0
        self.number_of_LinphoneCallOutgoingProgress = 0
        self.number_of_LinphoneCallOutgoingRinging = 0
        self.number_of_LinphoneCallOutgoingEarlyMedia = 0
        self.number_of_LinphoneCallConnected = 0
        self.number_of_LinphoneCallStreamsRunning = 0
        self.number_of_LinphoneCallPausing = 0
        self.number_of_LinphoneCallPaused = 0
        self.number_of_LinphoneCallResuming = 0
        self.number_of_LinphoneCallRefered = 0
        self.number_of_LinphoneCallError = 0
        self.number_of_LinphoneCallEnd = 0
        self.number_of_LinphoneCallPausedByRemote = 0
        self.number_of_LinphoneCallUpdatedByRemote = 0
        self.number_of_LinphoneCallIncomingEarlyMedia = 0
        self.number_of_LinphoneCallUpdating = 0
        self.number_of_LinphoneCallReleased = 0

        self.number_of_LinphoneTransferCallOutgoingInit = 0
        self.number_of_LinphoneTransferCallOutgoingProgress = 0
        self.number_of_LinphoneTransferCallOutgoingRinging = 0
        self.number_of_LinphoneTransferCallOutgoingEarlyMedia = 0
        self.number_of_LinphoneTransferCallConnected = 0
        self.number_of_LinphoneTransferCallStreamsRunning = 0
        self.number_of_LinphoneTransferCallError = 0

        self.number_of_LinphoneMessageReceived = 0
        self.number_of_LinphoneMessageReceivedWithFile = 0
        self.number_of_LinphoneMessageReceivedLegacy = 0
        self.number_of_LinphoneMessageExtBodyReceived = 0
        self.number_of_LinphoneMessageInProgress = 0
        self.number_of_LinphoneMessageDelivered = 0
        self.number_of_LinphoneMessageNotDelivered = 0
        self.number_of_LinphoneIsComposingActiveReceived = 0
        self.number_of_LinphoneIsComposingIdleReceived = 0
Ghislain MARY's avatar
Ghislain MARY committed
222
        self.number_of_LinphoneFileTransferDownloadSuccessful = 0
223 224 225 226
        self.progress_of_LinphoneFileTransfer = 0

        self.number_of_IframeDecoded = 0

Ghislain MARY's avatar
Ghislain MARY committed
227
        self.number_of_NewSubscriptionRequest = 0
228
        self.number_of_NotifyReceived = 0
Ghislain MARY's avatar
Ghislain MARY committed
229 230 231
        self.number_of_NotifyPresenceReceived = 0
        self.number_of_LinphonePresenceBasicStatusOpen = 0
        self.number_of_LinphonePresenceBasicStatusClosed = 0
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
        self.number_of_LinphonePresenceActivityOffline = 0
        self.number_of_LinphonePresenceActivityOnline = 0
        self.number_of_LinphonePresenceActivityAppointment = 0
        self.number_of_LinphonePresenceActivityAway = 0
        self.number_of_LinphonePresenceActivityBreakfast = 0
        self.number_of_LinphonePresenceActivityBusy = 0
        self.number_of_LinphonePresenceActivityDinner = 0
        self.number_of_LinphonePresenceActivityHoliday = 0
        self.number_of_LinphonePresenceActivityInTransit = 0
        self.number_of_LinphonePresenceActivityLookingForWork = 0
        self.number_of_LinphonePresenceActivityLunch = 0
        self.number_of_LinphonePresenceActivityMeal = 0
        self.number_of_LinphonePresenceActivityMeeting = 0
        self.number_of_LinphonePresenceActivityOnThePhone = 0
        self.number_of_LinphonePresenceActivityOther = 0
        self.number_of_LinphonePresenceActivityPerformance = 0
        self.number_of_LinphonePresenceActivityPermanentAbsence = 0
        self.number_of_LinphonePresenceActivityPlaying = 0
        self.number_of_LinphonePresenceActivityPresentation = 0
        self.number_of_LinphonePresenceActivityShopping = 0
        self.number_of_LinphonePresenceActivitySleeping = 0
        self.number_of_LinphonePresenceActivitySpectator = 0
        self.number_of_LinphonePresenceActivitySteering = 0
        self.number_of_LinphonePresenceActivityTravel = 0
        self.number_of_LinphonePresenceActivityTV = 0
        self.number_of_LinphonePresenceActivityUnknown = 0
        self.number_of_LinphonePresenceActivityVacation = 0
        self.number_of_LinphonePresenceActivityWorking = 0
        self.number_of_LinphonePresenceActivityWorship = 0
261
        self.last_received_presence = None
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285

        self.number_of_inforeceived = 0

        self.number_of_LinphoneSubscriptionIncomingReceived = 0
        self.number_of_LinphoneSubscriptionOutgoingInit = 0
        self.number_of_LinphoneSubscriptionPending = 0
        self.number_of_LinphoneSubscriptionActive = 0
        self.number_of_LinphoneSubscriptionTerminated = 0
        self.number_of_LinphoneSubscriptionError = 0
        self.number_of_LinphoneSubscriptionExpiring = 0

        self.number_of_LinphonePublishProgress = 0
        self.number_of_LinphonePublishOk = 0
        self.number_of_LinphonePublishExpiring = 0
        self.number_of_LinphonePublishError = 0
        self.number_of_LinphonePublishCleared = 0

        self.number_of_LinphoneConfiguringSkipped = 0
        self.number_of_LinphoneConfiguringFailed = 0
        self.number_of_LinphoneConfiguringSuccessful = 0

        self.number_of_LinphoneCallEncryptedOn = 0
        self.number_of_LinphoneCallEncryptedOff = 0

286 287
        self.last_received_chat_message = None

288 289 290

class CoreManager:

291
    @classmethod
Ghislain MARY's avatar
Ghislain MARY committed
292
    def configure_lc_from(cls, cbs, resources_path, rc_path, user_data=None):
293 294 295 296
        filepath = None
        if rc_path is not None:
            filepath = os.path.join(resources_path, rc_path)
            assert_equals(os.path.isfile(filepath), True)
Ghislain MARY's avatar
Ghislain MARY committed
297
        lc = linphone.Factory.get().create_core(cbs, None, filepath)
298 299 300 301 302
        linphone.testing.set_dns_user_hosts_file(lc, os.path.join(resources_path, 'tester_hosts'))
        lc.root_ca = os.path.join(resources_path, 'certificates', 'cn', 'cafile.pem')
        lc.ring = os.path.join(resources_path, 'sounds', 'oldphone.wav')
        lc.ringback = os.path.join(resources_path, 'sounds', 'ringback.wav')
        lc.static_picture = os.path.join(resources_path, 'images', 'nowebcamCIF.jpg')
303
        lc.user_data = weakref.ref(user_data)
304 305
        return lc

306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
    @classmethod
    def wait_for_until(cls, manager1, manager2, func, timeout):
        managers = []
        if manager1 is not None:
            managers.append(manager1)
        if manager2 is not None:
            managers.append(manager2)
        return cls.wait_for_list(managers, func, timeout)

    @classmethod
    def wait_for_list(cls, managers, func, timeout):
        start = datetime.now()
        end = start + timedelta(milliseconds = timeout)
        res = func(*managers)
        while not res and datetime.now() < end:
            for manager in managers:
                manager.lc.iterate()
            time.sleep(0.02)
            res = func(*managers)
        return res

327 328 329 330
    @classmethod
    def wait_for(cls, manager1, manager2, func):
        return cls.wait_for_until(manager1, manager2, func, 10000)

331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
    @classmethod
    def call(cls, caller_manager, callee_manager, caller_params = None, callee_params = None, build_callee_params = False):
        initial_caller_stats = deepcopy(caller_manager.stats)
        initial_callee_stats = deepcopy(callee_manager.stats)

        # Use playfile for callee to avoid locking on capture card
        callee_manager.lc.use_files = True
        callee_manager.lc.play_file = os.path.join(tester_resources_path, 'sounds', 'hello8000.wav')

        if caller_params is None:
            call = caller_manager.lc.invite_address(callee_manager.identity)
        else:
            call = caller_manager.lc.invite_address_with_params(callee_manager.identity, caller_params)
        assert call is not None

        assert_equals(CoreManager.wait_for(callee_manager, caller_manager,
            lambda callee_manager, caller_manager: callee_manager.stats.number_of_LinphoneCallIncomingReceived == initial_callee_stats.number_of_LinphoneCallIncomingReceived + 1), True)
        assert_equals(callee_manager.lc.incoming_invite_pending, True)
        assert_equals(caller_manager.stats.number_of_LinphoneCallOutgoingProgress, initial_caller_stats.number_of_LinphoneCallOutgoingProgress + 1)

        retry = 0
        while (caller_manager.stats.number_of_LinphoneCallOutgoingRinging != initial_caller_stats.number_of_LinphoneCallOutgoingRinging + 1) and \
            (caller_manager.stats.number_of_LinphoneCallOutgoingEarlyMedia != initial_caller_stats.number_of_LinphoneCallOutgoingEarlyMedia + 1) and \
            retry < 20:
            retry += 1
            caller_manager.lc.iterate()
            callee_manager.lc.iterate()
            time.sleep(0.1)
        assert ((caller_manager.stats.number_of_LinphoneCallOutgoingRinging == initial_caller_stats.number_of_LinphoneCallOutgoingRinging + 1) or \
            (caller_manager.stats.number_of_LinphoneCallOutgoingEarlyMedia == initial_caller_stats.number_of_LinphoneCallOutgoingEarlyMedia + 1)) == True

        assert callee_manager.lc.current_call_remote_address is not None
        if caller_manager.lc.current_call is None or callee_manager.lc.current_call is None or callee_manager.lc.current_call_remote_address is None:
            return False
        callee_from_address = caller_manager.identity.clone()
        callee_from_address.port = 0 # Remove port because port is never present in from header
        assert_equals(callee_from_address.weak_equal(callee_manager.lc.current_call_remote_address), True)

        if callee_params is not None:
            callee_manager.lc.accept_call_with_params(callee_manager.lc.current_call, callee_params)
        elif build_callee_params:
            default_params = callee_manager.lc.create_call_params(callee_manager.lc.current_call)
            callee_manager.lc.accept_call_with_params(callee_manager.lc.current_call, default_params)
        else:
            callee_manager.lc.accept_call(callee_manager.lc.current_call)
        assert_equals(CoreManager.wait_for(callee_manager, caller_manager,
            lambda callee_manager, caller_manager: (callee_manager.stats.number_of_LinphoneCallConnected == initial_callee_stats.number_of_LinphoneCallConnected + 1) and \
                (caller_manager.stats.number_of_LinphoneCallConnected == initial_caller_stats.number_of_LinphoneCallConnected + 1)), True)
        # Just to sleep
        result = CoreManager.wait_for(callee_manager, caller_manager,
            lambda callee_manager, caller_manager: (callee_manager.stats.number_of_LinphoneCallStreamsRunning == initial_callee_stats.number_of_LinphoneCallStreamsRunning + 1) and \
                (caller_manager.stats.number_of_LinphoneCallStreamsRunning == initial_caller_stats.number_of_LinphoneCallStreamsRunning + 1))

384
        if caller_manager.lc.media_encryption != linphone.MediaEncryption.MediaEncryptionNone and callee_manager.lc.media_encryption != linphone.MediaEncryption._None:
385
            # Wait for encryption to be on, in case of zrtp, it can take a few seconds
Ghislain MARY's avatar
Ghislain MARY committed
386
            if caller_manager.lc.media_encryption == linphone.MediaEncryption.ZRTP:
387 388
                CoreManager.wait_for(callee_manager, caller_manager,
                    lambda callee_manager, caller_manager: caller_manager.stats.number_of_LinphoneCallEncryptedOn == initial_caller_stats.number_of_LinphoneCallEncryptedOn + 1)
Ghislain MARY's avatar
Ghislain MARY committed
389
            if callee_manager.lc.media_encryption == linphone.MediaEncryption.ZRTP:
390 391 392 393 394 395 396 397 398 399 400 401 402
                CoreManager.wait_for(callee_manager, caller_manager,
                    lambda callee_manager, caller_manager: callee_manager.stats.number_of_LinphoneCallEncryptedOn == initial_callee_stats.number_of_LinphoneCallEncryptedOn + 1)
            assert_equals(callee_manager.lc.current_call.current_params.media_encryption, caller_manager.lc.media_encryption)
            assert_equals(caller_manager.lc.current_call.current_params.media_encryption, callee_manager.lc.media_encryption)

        return result

    @classmethod
    def end_call(cls, caller_manager, callee_manager):
        caller_manager.lc.terminate_all_calls()
        assert_equals(CoreManager.wait_for(caller_manager, callee_manager,
            lambda caller_manager, callee_manager: caller_manager.stats.number_of_LinphoneCallEnd == 1 and callee_manager.stats.number_of_LinphoneCallEnd == 1), True)

403 404
    @classmethod
    def registration_state_changed(cls, lc, cfg, state, message):
405
        manager = lc.user_data()
406
        linphonetester_logger.info("[TESTER] New registration state {state} for user id [{identity}] at proxy [{addr}]".format(
407
            state=linphone.RegistrationState.string(state), identity=cfg.identity_address.as_string(), addr=cfg.server_addr))
408
        if state == linphone.RegistrationState._None:
409
            manager.stats.number_of_LinphoneRegistrationNone += 1
Ghislain MARY's avatar
Ghislain MARY committed
410
        elif state == linphone.RegistrationState.Progress:
411
            manager.stats.number_of_LinphoneRegistrationProgress += 1
Ghislain MARY's avatar
Ghislain MARY committed
412
        elif state == linphone.RegistrationState.Ok:
413
            manager.stats.number_of_LinphoneRegistrationOk += 1
Ghislain MARY's avatar
Ghislain MARY committed
414
        elif state == linphone.RegistrationState.Cleared:
415
            manager.stats.number_of_LinphoneRegistrationCleared += 1
Ghislain MARY's avatar
Ghislain MARY committed
416
        elif state == linphone.RegistrationState.Failed:
417 418 419 420 421
            manager.stats.number_of_LinphoneRegistrationFailed += 1
        else:
            raise Exception("Unexpected registration state")

    @classmethod
Ghislain MARY's avatar
Ghislain MARY committed
422
    def authentication_requested(cls, lc, auth_info, method):
423
        manager = lc.user_data()
424
        linphonetester_logger.info("[TESTER] Auth info requested  for user id [{username}] at realm [{realm}]".format(
Ghislain MARY's avatar
Ghislain MARY committed
425
            username=auth_info.username, realm=auth_info.realm))
426 427
        manager.stats.number_of_auth_info_requested +=1

428 429
    @classmethod
    def call_state_changed(cls, lc, call, state, msg):
430
        manager = lc.user_data()
431 432
        to_address = call.call_log.to_address.as_string()
        from_address = call.call_log.from_address.as_string()
433
        direction = "Outgoing"
Ghislain MARY's avatar
Ghislain MARY committed
434
        if call.call_log.dir == linphone.CallDir.Incoming:
435
            direction = "Incoming"
436 437
        linphonetester_logger.info("[TESTER] {direction} call from [{from_address}] to [{to_address}], new state is [{state}]".format(
            direction=direction, from_address=from_address, to_address=to_address, state=linphone.CallState.string(state)))
Ghislain MARY's avatar
Ghislain MARY committed
438
        if state == linphone.CallState.IncomingReceived:
439
            manager.stats.number_of_LinphoneCallIncomingReceived += 1
Ghislain MARY's avatar
Ghislain MARY committed
440
        elif state == linphone.CallState.OutgoingInit:
441
            manager.stats.number_of_LinphoneCallOutgoingInit += 1
Ghislain MARY's avatar
Ghislain MARY committed
442
        elif state == linphone.CallState.OutgoingProgress:
443
            manager.stats.number_of_LinphoneCallOutgoingProgress += 1
Ghislain MARY's avatar
Ghislain MARY committed
444
        elif state == linphone.CallState.OutgoingRinging:
445
            manager.stats.number_of_LinphoneCallOutgoingRinging += 1
Ghislain MARY's avatar
Ghislain MARY committed
446
        elif state == linphone.CallState.OutgoingEarlyMedia:
447
            manager.stats.number_of_LinphoneCallOutgoingEarlyMedia += 1
Ghislain MARY's avatar
Ghislain MARY committed
448
        elif state == linphone.CallState.Connected:
449
            manager.stats.number_of_LinphoneCallConnected += 1
Ghislain MARY's avatar
Ghislain MARY committed
450
        elif state == linphone.CallState.StreamsRunning:
451
            manager.stats.number_of_LinphoneCallStreamsRunning += 1
Ghislain MARY's avatar
Ghislain MARY committed
452
        elif state == linphone.CallState.Pausing:
453
            manager.stats.number_of_LinphoneCallPausing += 1
Ghislain MARY's avatar
Ghislain MARY committed
454
        elif state == linphone.CallState.Paused:
455
            manager.stats.number_of_LinphoneCallPaused += 1
Ghislain MARY's avatar
Ghislain MARY committed
456
        elif state == linphone.CallState.Resuming:
457
            manager.stats.number_of_LinphoneCallResuming += 1
Ghislain MARY's avatar
Ghislain MARY committed
458
        elif state == linphone.CallState.Refered:
459
            manager.stats.number_of_LinphoneCallRefered += 1
Ghislain MARY's avatar
Ghislain MARY committed
460
        elif state == linphone.CallState.Error:
461
            manager.stats.number_of_LinphoneCallError += 1
Ghislain MARY's avatar
Ghislain MARY committed
462
        elif state == linphone.CallState.End:
463
            manager.stats.number_of_LinphoneCallEnd += 1
Ghislain MARY's avatar
Ghislain MARY committed
464
        elif state == linphone.CallState.PausedByRemote:
465
            manager.stats.number_of_LinphoneCallPausedByRemote += 1
Ghislain MARY's avatar
Ghislain MARY committed
466
        elif state == linphone.CallState.UpdatedByRemote:
467
            manager.stats.number_of_LinphoneCallUpdatedByRemote += 1
Ghislain MARY's avatar
Ghislain MARY committed
468
        elif state == linphone.CallState.IncomingEarlyMedia:
469
            manager.stats.number_of_LinphoneCallIncomingEarlyMedia += 1
Ghislain MARY's avatar
Ghislain MARY committed
470
        elif state == linphone.CallState.Updating:
471
            manager.stats.number_of_LinphoneCallUpdating += 1
Ghislain MARY's avatar
Ghislain MARY committed
472
        elif state == linphone.CallState.Released:
473 474 475 476
            manager.stats.number_of_LinphoneCallReleased += 1
        else:
            raise Exception("Unexpected call state")

477 478
    @classmethod
    def message_received(cls, lc, room, message):
479
        manager = lc.user_data()
480 481 482
        from_str = message.from_address.as_string()
        text_str = message.text
        external_body_url = message.external_body_url
483 484
        linphonetester_logger.info("[TESTER] Message from [{from_str}] is [{text_str}], external URL [{external_body_url}]".format(
            from_str=from_str, text_str=text_str, external_body_url=external_body_url))
485
        manager.stats.number_of_LinphoneMessageReceived += 1
486 487 488 489
        manager.stats.last_received_chat_message = message
        if message.file_transfer_information is not None:
            manager.stats.number_of_LinphoneMessageReceivedWithFile += 1
        elif message.external_body_url is not None:
490 491
            manager.stats.number_of_LinphoneMessageExtBodyReceived += 1

492 493
    @classmethod
    def new_subscription_requested(cls, lc, lf, url):
494
        manager = lc.user_data()
495 496
        linphonetester_logger.info("[TESTER] New subscription request: from [{from_str}], url [{url}]".format(
            from_str=lf.address.as_string(), url=url))
497
        manager.stats.number_of_NewSubscriptionRequest += 1
Ghislain MARY's avatar
Ghislain MARY committed
498
        lc.default_friend_list.add_friend(lf) # Accept subscription
499 500 501

    @classmethod
    def notify_presence_received(cls, lc, lf):
502
        manager = lc.user_data()
503 504
        linphonetester_logger.info("[TESTER] New notify request: from [{from_str}]".format(
            from_str=lf.address.as_string()))
Ghislain MARY's avatar
Ghislain MARY committed
505
        manager.stats.number_of_NotifyPresenceReceived += 1
506
        manager.stats.last_received_presence = lf.presence_model
Ghislain MARY's avatar
Ghislain MARY committed
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
        if manager.stats.last_received_presence.basic_status == linphone.PresenceBasicStatus.Open:
            manager.stats.number_of_LinphonePresenceBasicStatusOpen += 1
        elif manager.stats.last_received_presence.basic_status == linphone.PresenceBasicStatus.Closed:
            manager.stats.number_of_LinphonePresenceBasicStatusClosed += 1
        else:
            linphonetester_logger.error("[TESTER] Unexpected basic status {status}".format(status=manager.status.last_received_presence.basic_status))
        for i in range(0, manager.stats.last_received_presence.nb_activities):
            acttype = manager.stats.last_received_presence.get_nth_activity(i).type
            if acttype == linphone.PresenceActivityType.Offline:
                manager.stats.number_of_LinphonePresenceActivityOffline += 1
            elif acttype == linphone.PresenceActivityType.Online:
                manager.stats.number_of_LinphonePresenceActivityOnline += 1
            elif acttype == linphone.PresenceActivityType.Appointment:
                manager.stats.number_of_LinphonePresenceActivityAppointment += 1
            elif acttype == linphone.PresenceActivityType.Away:
                manager.stats.number_of_LinphonePresenceActivityAway += 1
            elif acttype == linphone.PresenceActivityType.Breakfast:
                manager.stats.number_of_LinphonePresenceActivityBreakfast += 1
            elif acttype == linphone.PresenceActivityType.Busy:
                manager.stats.number_of_LinphonePresenceActivityBusy += 1
            elif acttype == linphone.PresenceActivityType.Dinner:
                manager.stats.number_of_LinphonePresenceActivityDinner += 1
            elif acttype == linphone.PresenceActivityType.Holiday:
                manager.stats.number_of_LinphonePresenceActivityHoliday += 1
            elif acttype == linphone.PresenceActivityType.InTransit:
                manager.stats.number_of_LinphonePresenceActivityInTransit += 1
            elif acttype == linphone.PresenceActivityType.LookingForWork:
                manager.stats.number_of_LinphonePresenceActivityLookingForWork += 1
            elif acttype == linphone.PresenceActivityType.Lunch:
                manager.stats.number_of_LinphonePresenceActivityLunch += 1
            elif acttype == linphone.PresenceActivityType.Meal:
                manager.stats.number_of_LinphonePresenceActivityMeal += 1
            elif acttype == linphone.PresenceActivityType.Meeting:
                manager.stats.number_of_LinphonePresenceActivityMeeting += 1
            elif acttype == linphone.PresenceActivityType.OnThePhone:
                manager.stats.number_of_LinphonePresenceActivityOnThePhone += 1
            elif acttype == linphone.PresenceActivityType.Other:
                manager.stats.number_of_LinphonePresenceActivityOther += 1
            elif acttype == linphone.PresenceActivityType.Performance:
                manager.stats.number_of_LinphonePresenceActivityPerformance += 1
            elif acttype == linphone.PresenceActivityType.PermanentAbsence:
                manager.stats.number_of_LinphonePresenceActivityPermanentAbsence += 1
            elif acttype == linphone.PresenceActivityType.Playing:
                manager.stats.number_of_LinphonePresenceActivityPlaying += 1
            elif acttype == linphone.PresenceActivityType.Presentation:
                manager.stats.number_of_LinphonePresenceActivityPresentation += 1
            elif acttype == linphone.PresenceActivityType.Shopping:
                manager.stats.number_of_LinphonePresenceActivityShopping += 1
            elif acttype == linphone.PresenceActivityType.Sleeping:
                manager.stats.number_of_LinphonePresenceActivitySleeping += 1
            elif acttype == linphone.PresenceActivityType.Spectator:
                manager.stats.number_of_LinphonePresenceActivitySpectator += 1
            elif acttype == linphone.PresenceActivityType.Steering:
                manager.stats.number_of_LinphonePresenceActivitySteering += 1
            elif acttype == linphone.PresenceActivityType.Travel:
                manager.stats.number_of_LinphonePresenceActivityTravel += 1
            elif acttype == linphone.PresenceActivityType.TV:
                manager.stats.number_of_LinphonePresenceActivityTV += 1
            elif acttype == linphone.PresenceActivityType.Unknown:
                manager.stats.number_of_LinphonePresenceActivityUnknown += 1
            elif acttype == linphone.PresenceActivityType.Vacation:
                manager.stats.number_of_LinphonePresenceActivityVacation += 1
            elif acttype == linphone.PresenceActivityType.Working:
                manager.stats.number_of_LinphonePresenceActivityWorking += 1
            elif acttype == linphone.PresenceActivityType.Worship:
                manager.stats.number_of_LinphonePresenceActivityWorship += 1
Ghislain MARY's avatar
Ghislain MARY committed
573 574 575 576 577
        if manager.stats.last_received_presence.nb_activities == 0:
            if manager.stats.last_received_presence.basic_status == linphone.PresenceBasicStatus.Open:
                manager.stats.number_of_LinphonePresenceActivityOnline += 1
            else:
                manager.stats.number_of_LinphonePresenceActivityOffline += 1
578

Ghislain MARY's avatar
Ghislain MARY committed
579 580 581 582 583 584 585 586
    def __init__(self, rc_file = None, check_for_proxies = True, additional_cbs = None):
        cbs = linphone.Factory.get().create_core_cbs()
        cbs.registration_state_changed = CoreManager.registration_state_changed
        cbs.authentication_requested = CoreManager.authentication_requested
        cbs.call_state_changed = CoreManager.call_state_changed
        cbs.message_received = CoreManager.message_received
        cbs.new_subscription_requested = CoreManager.new_subscription_requested
        cbs.notify_presence_received = CoreManager.notify_presence_received
587 588 589 590 591
        self.identity = None
        self.stats = CoreManagerStats()
        rc_path = None
        if rc_file is not None:
            rc_path = os.path.join('rcfiles', rc_file)
Ghislain MARY's avatar
Ghislain MARY committed
592 593 594
        self.lc = CoreManager.configure_lc_from(cbs, tester_resources_path, rc_path, self)
        if additional_cbs:
            self.lc.add_callbacks(additional_cbs)
595
        self.check_accounts()
Ghislain MARY's avatar
Ghislain MARY committed
596 597 598 599 600

        self.lc.play_file = os.path.join(tester_resources_path, 'sounds', 'hello8000.wav')
        self.lc.user_certificates_path = os.getcwd()

        if check_for_proxies:
601 602 603
            proxy_count = len(self.lc.proxy_config_list)
        else:
            proxy_count = 0
Ghislain MARY's avatar
Ghislain MARY committed
604
            self.lc.network_reachable = False
605
        if proxy_count:
Ghislain MARY's avatar
Ghislain MARY committed
606 607 608 609
            nb_seconds = 20
            success = CoreManager.wait_for_until(self, None, lambda manager: manager.stats.number_of_LinphoneRegistrationOk == proxy_count, nb_seconds * 1000 * proxy_count)
            if not success:
                linphonetester_logger.info("[TESTER] Did not register after {nb_seconds} for {proxy_count} proxies".format(nb_seconds=nb_seconds, proxy_count=proxy_count))
610 611 612
        assert_equals(self.stats.number_of_LinphoneRegistrationOk, proxy_count)
        self.enable_audio_codec("PCMU", 8000)

613
        if self.lc.default_proxy_config is not None:
614
            self.lc.default_proxy_config.identity_address.clean()
Ghislain MARY's avatar
Ghislain MARY committed
615 616
        if not check_for_proxies:
            self.lc.network_reachable = True
617 618 619 620 621 622

    def enable_audio_codec(self, mime, rate):
        codecs = self.lc.audio_codecs
        for codec in codecs:
            self.lc.enable_payload_type(codec, False)
        codec = self.lc.find_payload_type(mime, rate, 1)
623
        assert codec is not None
624 625
        if codec is not None:
            self.lc.enable_payload_type(codec, True)
626 627 628

    def disable_all_audio_codecs_except_one(self, mime):
        self.enable_audio_codec(mime, -1)
629 630 631 632

    def check_accounts(self):
        pcl = self.lc.proxy_config_list
        for cfg in pcl:
633
            self.identity = account_manager.check_account(cfg)