// SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT package webrtc import ( "testing" "github.com/pion/ice/v4" "github.com/stretchr/testify/assert" ) func TestICECandidate_Convert(t *testing.T) { testCases := []struct { native ICECandidate expectedType ice.CandidateType expectedNetwork string expectedAddress string expectedPort int expectedComponent uint16 expectedRelatedAddress *ice.CandidateRelatedAddress }{ { ICECandidate{ Foundation: "foundation", Priority: 128, Address: "1.0.0.1", Protocol: ICEProtocolUDP, Port: 1234, Typ: ICECandidateTypeHost, Component: 1, }, ice.CandidateTypeHost, "udp", "1.0.0.1", 1234, 1, nil, }, { ICECandidate{ Foundation: "foundation", Priority: 128, Address: "::1", Protocol: ICEProtocolUDP, Port: 1234, Typ: ICECandidateTypeSrflx, Component: 1, RelatedAddress: "1.0.0.1", RelatedPort: 4321, }, ice.CandidateTypeServerReflexive, "udp", "::1", 1234, 1, &ice.CandidateRelatedAddress{ Address: "1.0.0.1", Port: 4321, }, }, { ICECandidate{ Foundation: "foundation", Priority: 128, Address: "::1", Protocol: ICEProtocolUDP, Port: 1234, Typ: ICECandidateTypePrflx, Component: 1, RelatedAddress: "1.0.0.1", RelatedPort: 4321, }, ice.CandidateTypePeerReflexive, "udp", "::1", 1234, 1, &ice.CandidateRelatedAddress{ Address: "1.0.0.1", Port: 4321, }, }, } for i, testCase := range testCases { var expectedICE ice.Candidate var err error switch testCase.expectedType { // nolint:exhaustive case ice.CandidateTypeHost: config := ice.CandidateHostConfig{ Network: testCase.expectedNetwork, Address: testCase.expectedAddress, Port: testCase.expectedPort, Component: testCase.expectedComponent, Foundation: "foundation", Priority: 128, } expectedICE, err = ice.NewCandidateHost(&config) case ice.CandidateTypeServerReflexive: config := ice.CandidateServerReflexiveConfig{ Network: testCase.expectedNetwork, Address: testCase.expectedAddress, Port: testCase.expectedPort, Component: testCase.expectedComponent, Foundation: "foundation", Priority: 128, RelAddr: testCase.expectedRelatedAddress.Address, RelPort: testCase.expectedRelatedAddress.Port, } expectedICE, err = ice.NewCandidateServerReflexive(&config) case ice.CandidateTypePeerReflexive: config := ice.CandidatePeerReflexiveConfig{ Network: testCase.expectedNetwork, Address: testCase.expectedAddress, Port: testCase.expectedPort, Component: testCase.expectedComponent, Foundation: "foundation", Priority: 128, RelAddr: testCase.expectedRelatedAddress.Address, RelPort: testCase.expectedRelatedAddress.Port, } expectedICE, err = ice.NewCandidatePeerReflexive(&config) } assert.NoError(t, err) // first copy the candidate ID so it matches the new one testCase.native.statsID = expectedICE.ID() actualICE, err := testCase.native.ToICE() assert.NoError(t, err) assert.Equal(t, expectedICE, actualICE, "testCase: %d ice not equal %v", i, actualICE) } } func TestConvertTypeFromICE(t *testing.T) { t.Run("host", func(t *testing.T) { ct, err := convertTypeFromICE(ice.CandidateTypeHost) assert.NoError(t, err, "failed coverting ice.CandidateTypeHost") assert.Equal(t, ICECandidateTypeHost, ct, "should be converted to ICECandidateTypeHost") }) t.Run("srflx", func(t *testing.T) { ct, err := convertTypeFromICE(ice.CandidateTypeServerReflexive) assert.NoError(t, err, "failed coverting ice.CandidateTypeServerReflexive") assert.Equal(t, ICECandidateTypeSrflx, ct, "should be converted to ICECandidateTypeSrflx") }) t.Run("prflx", func(t *testing.T) { ct, err := convertTypeFromICE(ice.CandidateTypePeerReflexive) assert.NoError(t, err, "failed coverting ice.CandidateTypePeerReflexive") assert.Equal(t, ICECandidateTypePrflx, ct, "should be converted to ICECandidateTypePrflx") }) } func TestNewIdentifiedICECandidateFromICE(t *testing.T) { config := ice.CandidateHostConfig{ Network: "udp", Address: "::1", Port: 1234, Component: 1, Foundation: "foundation", Priority: 128, } ice, err := ice.NewCandidateHost(&config) assert.NoError(t, err) ct, err := newICECandidateFromICE(ice, "1", 2) assert.NoError(t, err) assert.Equal(t, "1", ct.SDPMid) assert.Equal(t, uint16(2), ct.SDPMLineIndex) } func TestNewIdentifiedICECandidatesFromICE(t *testing.T) { ic, err := ice.NewCandidateHost(&ice.CandidateHostConfig{ Network: "udp", Address: "::1", Port: 1234, Component: 1, Foundation: "foundation", Priority: 128, }) assert.NoError(t, err) candidates := []ice.Candidate{ic, ic, ic} sdpMid := "1" sdpMLineIndex := uint16(2) results, err := newICECandidatesFromICE(candidates, sdpMid, sdpMLineIndex) assert.NoError(t, err) assert.Equal(t, 3, len(results)) for _, result := range results { assert.Equal(t, sdpMid, result.SDPMid) assert.Equal(t, sdpMLineIndex, result.SDPMLineIndex) } } func TestICECandidate_ToJSON(t *testing.T) { candidate := ICECandidate{ Foundation: "foundation", Priority: 128, Address: "1.0.0.1", Protocol: ICEProtocolUDP, Port: 1234, Typ: ICECandidateTypeHost, Component: 1, } candidateInit := candidate.ToJSON() assert.Equal(t, uint16(0), *candidateInit.SDPMLineIndex) assert.Equal(t, "candidate:foundation 1 udp 128 1.0.0.1 1234 typ host", candidateInit.Candidate) } func TestICECandidateZeroSDPid(t *testing.T) { candidate := ICECandidate{} assert.Equal(t, candidate.SDPMid, "") assert.Equal(t, candidate.SDPMLineIndex, uint16(0)) } func TestICECandidateString(t *testing.T) { candidate := ICECandidate{ Foundation: "foundation", Priority: 128, Address: "1.0.0.1", Protocol: ICEProtocolUDP, Port: 1234, Typ: ICECandidateTypeHost, Component: 1, } iceCandidateConfig := ice.CandidateHostConfig{ Network: "udp", Address: "1.0.0.1", Port: 1234, Component: 1, Foundation: "foundation", Priority: 128, } iceCandidate, err := ice.NewCandidateHost(&iceCandidateConfig) assert.NoError(t, err) assert.Equal(t, candidate.String(), iceCandidate.String()) } func TestICECandidateSDPMid_ToJSON(t *testing.T) { candidate := ICECandidate{} candidate.SDPMid = "0" candidate.SDPMLineIndex = 1 assert.Equal(t, candidate.SDPMid, "0") assert.Equal(t, candidate.SDPMLineIndex, uint16(1)) } func TestICECandidateExtensions_ToJSON(t *testing.T) { candidates := []struct { candidate string extensions []ice.CandidateExtension }{ { "2637185494 1 udp 2121932543 192.168.1.4 50723 typ host generation 1 ufrag Jzd0 network-id 1", []ice.CandidateExtension{ { Key: "generation", Value: "1", }, { Key: "ufrag", Value: "Jzd0", }, { Key: "network-id", Value: "1", }, }, }, { "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host tcptype active ufrag Jzd0 network-id 1", []ice.CandidateExtension{ { Key: "tcptype", Value: "active", }, { Key: "ufrag", Value: "Jzd0", }, { Key: "network-id", Value: "1", }, }, }, { "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host tcptype active ufrag Jzd0 network-id 1 empty-ext ", []ice.CandidateExtension{ { Key: "tcptype", Value: "active", }, { Key: "ufrag", Value: "Jzd0", }, { Key: "network-id", Value: "1", }, { Key: "empty-ext", Value: "", }, }, }, { "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host tcptype active ufrag Jzd0 empty-ext network-id 1", []ice.CandidateExtension{ { Key: "tcptype", Value: "active", }, { Key: "ufrag", Value: "Jzd0", }, { Key: "empty-ext", Value: "", }, { Key: "network-id", Value: "1", }, }, }, } for _, cand := range candidates { cand := cand candidate, err := ice.UnmarshalCandidate(cand.candidate) assert.NoError(t, err) sdpMid := "1" sdpMLineIndex := uint16(2) iceCandidate, err := newICECandidateFromICE(candidate, sdpMid, sdpMLineIndex) assert.NoError(t, err) candidateInit := iceCandidate.ToJSON() assert.Equal(t, sdpMLineIndex, *candidateInit.SDPMLineIndex) assert.Equal(t, "candidate:"+cand.candidate, candidateInit.Candidate) iceBack, err := iceCandidate.ToICE() assert.NoError(t, err) assert.Equal(t, cand.extensions, iceBack.Extensions()) } }