diff 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 diff
--- a/tests/unit/test_plugin_calls.py	Wed Oct 25 15:29:33 2023 +0200
+++ b/tests/unit/test_plugin_calls.py	Wed Nov 01 13:41:07 2023 +0100
@@ -18,8 +18,6 @@
 
 from unittest.mock import AsyncMock, MagicMock
 
-from gi.repository import Gst
-from libervia.backend.core import exceptions
 from libervia.backend.tools.common import data_format
 import pytest
 
@@ -36,39 +34,12 @@
     return host
 
 
-@pytest.fixture(scope="function")
-def webrtc():
-    """Fixture for WebRTC instantiation."""
-    host_mock = MagicMock()
-    profile = "test_profile"
-    instance = plugin_wid_calls.WebRTC(host_mock, profile)
-
-    instance._set_media_types = MagicMock()
-    instance.start_pipeline = MagicMock()
-    instance.webrtc = MagicMock()
-    instance.webrtc.emit = MagicMock()
-
-    instance.GstSdp_SDPMessage_new_from_text = MagicMock()
-    instance.GstWebRTC_WebRTCSessionDescription_new = MagicMock()
-    instance.Gst_Promise_new_with_change_func = MagicMock()
-
-    return instance
-
-
 @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"]
-    )
+        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"
@@ -80,197 +51,7 @@
     return calls
 
 
-class TestWebRtc:
-    def test_get_payload_types(self, webrtc):
-        """The method can identify the correct payload types for video and audio."""
-        fake_sdpmsg = MagicMock()
-        fake_media = MagicMock()
-        fake_caps = MagicMock()
-        fake_structure = MagicMock()
-
-        # This side effect will return 'fake_video_encoding' first, then
-        # 'fake_audio_encoding'.
-        fake_structure.__getitem__.side_effect = [
-            "fake_video_encoding",
-            "fake_audio_encoding",
-        ]
-        fake_caps.get_structure.return_value = fake_structure
-        fake_media.get_format.side_effect = ["webrtc-datachannel", "10", "20"]
-        fake_media.get_caps_from_media.return_value = fake_caps
-        fake_sdpmsg.get_media.return_value = fake_media
-        fake_sdpmsg.medias_len.return_value = 1
-        fake_media.formats_len.return_value = 3
-
-        result = webrtc.get_payload_types(
-            fake_sdpmsg, "fake_video_encoding", "fake_audio_encoding"
-        )
-        expected_result = {"fake_video_encoding": 10, "fake_audio_encoding": 20}
-
-        assert result == expected_result
-
-    def test_on_accepted_call(self, webrtc):
-        """The method correctly sets the remote SDP upon acceptance of an outgoing call."""
-        sdp_str = "mock_sdp_string"
-        profile_str = "test_profile"
-
-        webrtc.on_accepted_call(sdp_str, profile_str)
-
-        # remote description must be set
-        assert webrtc.webrtc.emit.call_count == 1
-        assert webrtc.webrtc.emit.call_args[0][0] == "set-remote-description"
-
-    @pytest.mark.asyncio
-    async def test_answer_call(self, webrtc, monkeypatch):
-        """The method correctly answers an incoming call."""
-        mock_setup_call = AsyncMock()
-
-        def mock_get_payload_types(sdpmsg, video_encoding, audio_encoding):
-            return {"VP8": 96, "OPUS": 97}
-
-        monkeypatch.setattr(webrtc, "setup_call", mock_setup_call)
-        monkeypatch.setattr(webrtc, "get_payload_types", mock_get_payload_types)
-
-        sdp_str = "mock_sdp_string"
-        profile_str = "mock_profile"
-
-        await webrtc.answer_call(sdp_str, profile_str)
-
-        mock_setup_call.assert_called_once_with("responder", audio_pt=97, video_pt=96)
-
-        # remote description must be set
-        assert webrtc.webrtc.emit.call_count == 1
-        assert webrtc.webrtc.emit.call_args[0][0] == "set-remote-description"
-
-    def test_on_remote_decodebin_stream_video(self, webrtc, monkeypatch):
-        """The method correctly handles video streams from the remote decodebin."""
-        mock_pipeline = MagicMock()
-        monkeypatch.setattr(webrtc, "pipeline", mock_pipeline)
-
-        mock_pad = MagicMock()
-        mock_caps = MagicMock()
-        mock_structure = MagicMock()
-
-        mock_pad.has_current_caps.return_value = True
-        mock_pad.get_current_caps.return_value = mock_caps
-        mock_caps.__len__.return_value = 1
-        mock_caps.__getitem__.return_value = mock_structure
-        mock_structure.get_name.return_value = "video/x-h264"
-        # We use non-standard resolution as example to trigger the workaround
-        mock_structure.get_int.side_effect = lambda x: MagicMock(
-            value=990 if x == "width" else 557
-        )
-
-        webrtc.on_remote_decodebin_stream(None, mock_pad)
-
-        assert webrtc._remote_video_pad == mock_pad
-        mock_pipeline.add.assert_called()
-        mock_pipeline.set_state.assert_called()
-        mock_pad.link.assert_called()
-
-    def test_on_remote_decodebin_stream_audio(self, webrtc, monkeypatch):
-        """The method correctly handles audio streams from the remote decodebin."""
-        mock_pipeline = MagicMock()
-        monkeypatch.setattr(webrtc, "pipeline", mock_pipeline)
-
-        mock_pad = MagicMock()
-        mock_caps = MagicMock()
-        mock_structure = MagicMock()
-
-        mock_pad.has_current_caps.return_value = True
-        mock_pad.get_current_caps.return_value = mock_caps
-        mock_caps.__len__.return_value = 1
-        mock_caps.__getitem__.return_value = mock_structure
-        mock_structure.get_name.return_value = "audio/x-raw"
-
-        webrtc.on_remote_decodebin_stream(None, mock_pad)
-
-        mock_pipeline.add.assert_called()
-        mock_pipeline.set_state.assert_called()
-        mock_pad.link.assert_called()
-
-    @pytest.mark.asyncio
-    async def test_setup_call_correct_role(self, host, webrtc, monkeypatch):
-        """Roles are set in setup_call."""
-        monkeypatch.setattr(Gst, "parse_launch", MagicMock())
-        monkeypatch.setattr(data_format, "deserialise", MagicMock(return_value=[]))
-
-        await webrtc.setup_call("initiator")
-        assert webrtc.role == "initiator"
-
-        await webrtc.setup_call("responder")
-        assert webrtc.role == "responder"
-
-        with pytest.raises(AssertionError):
-            await webrtc.setup_call("invalid_role")
-
-    @pytest.mark.asyncio
-    async def test_setup_call_test_mode(self, host, webrtc, monkeypatch):
-        """Test mode use fake video and audio in setup_call."""
-        monkeypatch.setattr(data_format, "deserialise", MagicMock(return_value=[]))
-        monkeypatch.setattr(webrtc, "test_mode", True)
-        await webrtc.setup_call("initiator")
-        assert "videotestsrc" in webrtc.gst_pipe_desc
-        assert "audiotestsrc" in webrtc.gst_pipe_desc
-
-    @pytest.mark.asyncio
-    async def test_setup_call_normal_mode(self, host, webrtc, monkeypatch):
-        """Normal mode use real video and audio in setup_call."""
-        monkeypatch.setattr(data_format, "deserialise", MagicMock(return_value=[]))
-        monkeypatch.setattr(webrtc, "test_mode", False)
-        await webrtc.setup_call("initiator")
-        assert "v4l2src" in webrtc.gst_pipe_desc
-        assert "pulsesrc" in webrtc.gst_pipe_desc
-
-    @pytest.mark.asyncio
-    async def test_setup_call_with_stun_and_turn(self, host, webrtc, monkeypatch):
-        """STUN and TURN server configurations are done in setup_call."""
-        mock_pipeline = MagicMock()
-        mock_parse_launch = MagicMock()
-        mock_parse_launch.return_value = mock_pipeline
-        monkeypatch.setattr(Gst, "parse_launch", mock_parse_launch)
-
-        mock_pipeline.get_by_name.return_value = webrtc.webrtc
-
-        mock_external_disco = [
-            {"type": "stun", "transport": "udp", "host": "stun.host", "port": "3478"},
-            {
-                "type": "turn",
-                "transport": "udp",
-                "host": "turn.host",
-                "port": "3478",
-                "username": "user",
-                "password": "pass",
-            },
-        ]
-
-        monkeypatch.setattr(
-            data_format, "deserialise", MagicMock(return_value=mock_external_disco)
-        )
-
-        mock_emit = AsyncMock()
-        monkeypatch.setattr(webrtc.webrtc, "emit", mock_emit)
-
-        mock_set_property = AsyncMock()
-        monkeypatch.setattr(webrtc.webrtc, "set_property", mock_set_property)
-
-        await webrtc.setup_call("initiator")
-
-        G.host.a_bridge.external_disco_get.assert_called_once_with("", webrtc.profile)
-        mock_set_property.assert_any_call("stun-server", "stun://stun.host:3478")
-        mock_emit.assert_called_once_with(
-            "add-turn-server", "turn://user:pass@turn.host:3478"
-        )
-
-    @pytest.mark.asyncio
-    async def test_setup_call_gstreamer_pipeline_failure(self, webrtc, monkeypatch):
-        """Test setup_call method handling Gstreamer pipeline failure."""
-        monkeypatch.setattr(Gst, "parse_launch", lambda _: None)
-        with pytest.raises(exceptions.InternalError):
-            await webrtc.setup_call("initiator")
-
-
 class TestCalls:
-
     @pytest.mark.asyncio
     async def test_toggle_call_sid_none(self, monkeypatch, calls):
         """Call is started when there is not sid set."""
@@ -293,7 +74,6 @@
         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."""
@@ -307,9 +87,7 @@
         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
+            fake_action_id, data_format.serialise({"cancelled": False}), fake_profile
         )
 
     @pytest.mark.asyncio
@@ -329,10 +107,7 @@
     @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"
-        }
+        setup_data = {"role": "initiator", "sdp": "fake_sdp"}
         profile = "fake_profile"
 
         await calls.on_call_setup(setup_data, profile)
@@ -342,15 +117,8 @@
     @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"
-        }
+        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)
@@ -361,10 +129,7 @@
     @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"
-        }
+        setup_data = {"role": "invalid_role", "sdp": "fake_sdp"}
         profile = "fake_profile"
 
         await calls.on_call_setup(setup_data, profile)