Skip to content

Commit

Permalink
Add RTCIceCandidateInit and allow setting sdpMid and sdpMLineIndex (#2)
Browse files Browse the repository at this point in the history
Co-authored-by: Robert Resch <[email protected]>
  • Loading branch information
sdb9696 and edenhaus authored Nov 18, 2024
1 parent a403f31 commit 065e8e8
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# vscode
.vscode
20 changes: 18 additions & 2 deletions tests/__snapshots__/test_init.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,20 @@
]),
})
# ---
# name: test_decoding_and_encoding[RTCIceCandidate-RTCIceCandidate_candidate.json][dataclass]
# name: test_decoding_and_encoding[RTCIceCandidateInit-RTCIceCandidateInit_candidate.json][dataclass]
dict({
'candidate': '3932168448 1 udp 1694498815 1.2.3.4 10676 typ srflx raddr 0.0.0.0 rport 37566',
'sdp_m_line_index': 0,
'sdp_mid': None,
'user_fragment': None,
})
# ---
# name: test_decoding_and_encoding[RTCIceCandidate-RTCIceCandidate_end.json][dataclass]
# name: test_decoding_and_encoding[RTCIceCandidateInit-RTCIceCandidateInit_end.json][dataclass]
dict({
'candidate': '',
'sdp_m_line_index': None,
'sdp_mid': None,
'user_fragment': None,
})
# ---
# name: test_decoding_and_encoding[RTCIceServer-RTCIceServer_only_urls_list.json][dataclass]
Expand Down Expand Up @@ -79,3 +85,13 @@
'username': 'username',
})
# ---
# name: test_decoding_and_encoding_deprecated[RTCIceCandidate-RTCIceCandidate_candidate.json][dataclass]
dict({
'candidate': '3932168448 1 udp 1694498815 1.2.3.4 10676 typ srflx raddr 0.0.0.0 rport 37566',
})
# ---
# name: test_decoding_and_encoding_deprecated[RTCIceCandidate-RTCIceCandidate_end.json][dataclass]
dict({
'candidate': '',
})
# ---
4 changes: 4 additions & 0 deletions tests/fixtures/RTCIceCandidateInit_candidate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"candidate": "3932168448 1 udp 1694498815 1.2.3.4 10676 typ srflx raddr 0.0.0.0 rport 37566",
"sdpMLineIndex": 0
}
3 changes: 3 additions & 0 deletions tests/fixtures/RTCIceCandidateInit_end.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"candidate": ""
}
4 changes: 4 additions & 0 deletions tests/fixtures/RTCIceCandidateInit_invalid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"candidate": "3932168448 1 udp 1694498815 1.2.3.4 10676 typ srflx raddr 0.0.0.0 rport 37566",
"sdpMLineIndex": -1
}
60 changes: 56 additions & 4 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
import pytest

from tests import load_fixture
from webrtc_models import RTCConfiguration, RTCIceCandidate, RTCIceServer
from webrtc_models import (
RTCConfiguration,
RTCIceCandidate,
RTCIceCandidateInit,
RTCIceServer,
)

if TYPE_CHECKING:
from mashumaro.mixins.orjson import DataClassORJSONMixin
Expand All @@ -27,9 +32,9 @@
(RTCConfiguration, "RTCConfiguration_empty.json"),
(RTCConfiguration, "RTCConfiguration_one_iceServer.json"),
(RTCConfiguration, "RTCConfiguration_multiple_iceServers.json"),
# RTCIceCandidate
(RTCIceCandidate, "RTCIceCandidate_end.json"),
(RTCIceCandidate, "RTCIceCandidate_candidate.json"),
# RTCIceCandidateInit
(RTCIceCandidateInit, "RTCIceCandidateInit_end.json"),
(RTCIceCandidateInit, "RTCIceCandidateInit_candidate.json"),
],
)
def test_decoding_and_encoding(
Expand All @@ -51,3 +56,50 @@ def test_decoding_and_encoding(
# Verify dict
assert instance_dict == file_content_dict
assert instance == clazz.from_dict(instance_dict)


@pytest.mark.parametrize(
("clazz", "filename"),
[
# RTCIceCandidate
(RTCIceCandidate, "RTCIceCandidate_end.json"),
(RTCIceCandidate, "RTCIceCandidate_candidate.json"),
],
)
def test_decoding_and_encoding_deprecated(
snapshot: SnapshotAssertion,
clazz: type[DataClassORJSONMixin],
filename: str,
) -> None:
"""Test decoding/encoding."""
file_content = load_fixture(filename)
with pytest.deprecated_call():
instance = clazz.from_json(file_content)
assert instance == snapshot(name="dataclass")

file_content_dict = orjson.loads(file_content)
instance_dict = instance.to_dict()

# Verify json
assert instance.to_json() == orjson.dumps(file_content_dict).decode()

# Verify dict
assert instance_dict == file_content_dict
with pytest.deprecated_call():
assert instance == clazz.from_dict(instance_dict)


def test_no_mid_and_mlineindex() -> None:
"""Test spd_mid and sdp_multilineindex raises TypeError."""
file_content = load_fixture("RTCIceCandidate_candidate.json")
cand = RTCIceCandidateInit.from_json(file_content)
assert cand.sdp_m_line_index == 0
assert cand.sdp_mid is None


def test_invalid_mlineindex() -> None:
"""Test spd_mid and sdp_multilineindex raises TypeError."""
file_content = load_fixture("RTCIceCandidateInit_invalid.json")
msg = "sdpMLineIndex must be greater than or equal to 0"
with pytest.raises(ValueError, match=msg):
RTCIceCandidateInit.from_json(file_content)
40 changes: 40 additions & 0 deletions webrtc_models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""WebRTC models."""

from dataclasses import dataclass, field
from warnings import warn

from mashumaro import field_options
from mashumaro.config import BaseConfig
Expand Down Expand Up @@ -56,3 +57,42 @@ class RTCIceCandidate(_RTCBaseModel):
"""

candidate: str

def __post_init__(self) -> None:
"""Initialize class."""
msg = "Using RTCIceCandidate is deprecated. Use RTCIceCandidateInit instead"
warn(msg, DeprecationWarning, stacklevel=2)


@dataclass(frozen=True)
class RTCIceCandidateInit(RTCIceCandidate):
"""RTC Ice Candidate Init.
If neither sdp_mid nor sdp_m_line_index are provided and candidate is not an empty
string, sdp_m_line_index is set to 0.
See https://www.w3.org/TR/webrtc/#dom-rtcicecandidateinit
"""

candidate: str
sdp_mid: str | None = field(
metadata=field_options(alias="sdpMid"), default=None, kw_only=True
)
sdp_m_line_index: int | None = field(
metadata=field_options(alias="sdpMLineIndex"), default=None, kw_only=True
)
user_fragment: str | None = field(
metadata=field_options(alias="userFragment"), default=None, kw_only=True
)

def __post_init__(self) -> None:
"""Initialize class."""
if not self.candidate:
# An empty string represents an end-of-candidates indication
# or a peer reflexive remote candidate
return

if self.sdp_mid is None and self.sdp_m_line_index is None:
object.__setattr__(self, "sdp_m_line_index", 0)
elif (sdp := self.sdp_m_line_index) is not None and sdp < 0:
msg = "sdpMLineIndex must be greater than or equal to 0"
raise ValueError(msg)

0 comments on commit 065e8e8

Please sign in to comment.