view tests/unit/test_plugin_calls.py @ 509:f0ce49b360c8

calls: move webrtc code to core: WebRTC code which can be used in several frontends has been factorized and moved to common `frontends.tools`. Test have been updated consequently. rel 426
author Goffi <goffi@goffi.org>
date Wed, 01 Nov 2023 13:41:07 +0100
parents f6b8300e8234
children
line wrap: on
line source

#!/usr/bin/env python3

# Libervia: an XMPP client
# Copyright (C) 2009-2023 Jérôme Poisson (goffi@goffi.org)

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from unittest.mock import AsyncMock, MagicMock

from libervia.backend.tools.common import data_format
import pytest

from libervia.desktop_kivy import G
from libervia.desktop_kivy.plugins import plugin_wid_calls


@pytest.fixture
def host(monkeypatch):
    host = MagicMock()
    host.a_bridge = AsyncMock()
    host.app.expand = lambda s: s
    monkeypatch.setattr(G, "_host", host, raising=False)
    return host


@pytest.fixture
def calls(monkeypatch, host):
    """Fixture for Call UI instantiation."""
    for attr in ("header_box", "local_video", "remote_video", "screen_manager"):
        monkeypatch.setattr(plugin_wid_calls.Calls, attr, MagicMock())
    calls = plugin_wid_calls.Calls(host, "test_peer@example.org", ["test_profile"])
    calls.jid_selector = MagicMock()
    calls.header_input = MagicMock()
    calls.header_input.text = "fake_jid@domain"
    calls.webrtc = MagicMock()
    calls.webrtc.setup_call = AsyncMock()
    calls.webrtc.start_pipeline = MagicMock()
    calls.end_call = AsyncMock()

    return calls


class TestCalls:
    @pytest.mark.asyncio
    async def test_toggle_call_sid_none(self, monkeypatch, calls):
        """Call is started when there is not sid set."""
        monkeypatch.setattr(calls.webrtc, "sid", None)

        await calls.toggle_call()

        calls.webrtc.setup_call.assert_called_once_with("initiator")
        calls.webrtc.start_pipeline.assert_called_once()
        assert calls.in_call == True

    @pytest.mark.asyncio
    async def test_toggle_call_sid_set(self, monkeypatch, host, calls):
        """Call is ended when a sid is set"""
        monkeypatch.setattr(calls.webrtc, "sid", "test_sid")

        await calls.toggle_call()

        calls.end_call.assert_called_once_with({"reason": "terminated"}, calls.profile)
        host.a_bridge.call_end.assert_called_once_with("test_sid", "", calls.profile)
        assert calls.in_call == False

    @pytest.mark.asyncio
    async def test_on_incoming_call_sid_none(self, monkeypatch, host, calls):
        """Incoming call is accepted if no ongoing call."""
        monkeypatch.setattr(calls.webrtc, "sid", None)
        fake_action_id = "fake_action_id"
        fake_action_data = {"session_id": "test_sid"}
        fake_profile = "fake_profile"

        await calls.on_incoming_call(fake_action_data, fake_action_id, fake_profile)

        assert calls.in_call == True
        assert calls.webrtc.sid == "test_sid"
        host.a_bridge.action_launch.assert_called_once_with(
            fake_action_id, data_format.serialise({"cancelled": False}), fake_profile
        )

    @pytest.mark.asyncio
    async def test_on_incoming_call_sid_set(self, monkeypatch, host, calls):
        """Incoming call is ignored if there's an ongoing call."""
        monkeypatch.setattr(calls.webrtc, "sid", "fake_old_sid")
        fake_action_id = "fake_action_id"
        fake_action_data = {"session_id": "test_sid_new"}
        fake_profile = "fake_profile"

        await calls.on_incoming_call(fake_action_data, fake_action_id, fake_profile)

        # Ensuring the state hasn't been changed to True
        assert calls.in_call == False
        host.a_bridge.action_launch.assert_not_called()

    @pytest.mark.asyncio
    async def test_on_call_setup_initiator(self, calls):
        """Correct method called if role is 'initiator'."""
        setup_data = {"role": "initiator", "sdp": "fake_sdp"}
        profile = "fake_profile"

        await calls.on_call_setup(setup_data, profile)

        calls.webrtc.on_accepted_call.assert_called_once_with(setup_data["sdp"], profile)

    @pytest.mark.asyncio
    async def test_on_call_setup_responder(self, monkeypatch, calls):
        """Correct method called if role is 'responder'."""
        monkeypatch.setattr(calls.webrtc, "answer_call", AsyncMock())
        setup_data = {"role": "responder", "sdp": "fake_sdp"}
        profile = "fake_profile"

        await calls.on_call_setup(setup_data, profile)

        calls.webrtc.answer_call.assert_called_once_with(setup_data["sdp"], profile)
        calls.webrtc.on_accepted_call.assert_not_called()

    @pytest.mark.asyncio
    async def test_on_call_setup_invalid_role(self, calls):
        """Nothing is called if role is neither 'initiator' nor 'responder'."""
        setup_data = {"role": "invalid_role", "sdp": "fake_sdp"}
        profile = "fake_profile"

        await calls.on_call_setup(setup_data, profile)

        calls.webrtc.answer_call.assert_not_called()
        calls.webrtc.on_accepted_call.assert_not_called()