| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756 |
- // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
- // SPDX-License-Identifier: MIT
- package webrtc
- import (
- "reflect"
- "sync"
- "sync/atomic"
- "testing"
- "time"
- "github.com/pion/sdp/v3"
- "github.com/pion/transport/v2/test"
- "github.com/pion/webrtc/v3/pkg/rtcerr"
- "github.com/stretchr/testify/assert"
- )
- // newPair creates two new peer connections (an offerer and an answerer)
- // *without* using an api (i.e. using the default settings).
- func newPair() (pcOffer *PeerConnection, pcAnswer *PeerConnection, err error) {
- pca, err := NewPeerConnection(Configuration{})
- if err != nil {
- return nil, nil, err
- }
- pcb, err := NewPeerConnection(Configuration{})
- if err != nil {
- return nil, nil, err
- }
- return pca, pcb, nil
- }
- func signalPairWithModification(pcOffer *PeerConnection, pcAnswer *PeerConnection, modificationFunc func(string) string) error {
- // Note(albrow): We need to create a data channel in order to trigger ICE
- // candidate gathering in the background for the JavaScript/Wasm bindings. If
- // we don't do this, the complete offer including ICE candidates will never be
- // generated.
- if _, err := pcOffer.CreateDataChannel("initial_data_channel", nil); err != nil {
- return err
- }
- offer, err := pcOffer.CreateOffer(nil)
- if err != nil {
- return err
- }
- offerGatheringComplete := GatheringCompletePromise(pcOffer)
- if err = pcOffer.SetLocalDescription(offer); err != nil {
- return err
- }
- <-offerGatheringComplete
- offer.SDP = modificationFunc(pcOffer.LocalDescription().SDP)
- if err = pcAnswer.SetRemoteDescription(offer); err != nil {
- return err
- }
- answer, err := pcAnswer.CreateAnswer(nil)
- if err != nil {
- return err
- }
- answerGatheringComplete := GatheringCompletePromise(pcAnswer)
- if err = pcAnswer.SetLocalDescription(answer); err != nil {
- return err
- }
- <-answerGatheringComplete
- return pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription())
- }
- func signalPair(pcOffer *PeerConnection, pcAnswer *PeerConnection) error {
- return signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string { return sessionDescription })
- }
- func offerMediaHasDirection(offer SessionDescription, kind RTPCodecType, direction RTPTransceiverDirection) bool {
- parsed := &sdp.SessionDescription{}
- if err := parsed.Unmarshal([]byte(offer.SDP)); err != nil {
- return false
- }
- for _, media := range parsed.MediaDescriptions {
- if media.MediaName.Media == kind.String() {
- _, exists := media.Attribute(direction.String())
- return exists
- }
- }
- return false
- }
- func untilConnectionState(state PeerConnectionState, peers ...*PeerConnection) *sync.WaitGroup {
- var triggered sync.WaitGroup
- triggered.Add(len(peers))
- for _, p := range peers {
- var done atomic.Value
- done.Store(false)
- hdlr := func(p PeerConnectionState) {
- if val, ok := done.Load().(bool); ok && (!val && p == state) {
- done.Store(true)
- triggered.Done()
- }
- }
- p.OnConnectionStateChange(hdlr)
- }
- return &triggered
- }
- func TestNew(t *testing.T) {
- pc, err := NewPeerConnection(Configuration{
- ICEServers: []ICEServer{
- {
- URLs: []string{
- "stun:stun.l.google.com:19302",
- },
- Username: "unittest",
- },
- },
- ICETransportPolicy: ICETransportPolicyRelay,
- BundlePolicy: BundlePolicyMaxCompat,
- RTCPMuxPolicy: RTCPMuxPolicyNegotiate,
- PeerIdentity: "unittest",
- ICECandidatePoolSize: 5,
- })
- assert.NoError(t, err)
- assert.NotNil(t, pc)
- assert.NoError(t, pc.Close())
- }
- func TestPeerConnection_SetConfiguration(t *testing.T) {
- // Note: These tests don't include ICEServer.Credential,
- // ICEServer.CredentialType, or Certificates because those are not supported
- // in the WASM bindings.
- for _, test := range []struct {
- name string
- init func() (*PeerConnection, error)
- config Configuration
- wantErr error
- }{
- {
- name: "valid",
- init: func() (*PeerConnection, error) {
- pc, err := NewPeerConnection(Configuration{
- ICECandidatePoolSize: 5,
- })
- if err != nil {
- return pc, err
- }
- err = pc.SetConfiguration(Configuration{
- ICEServers: []ICEServer{
- {
- URLs: []string{
- "stun:stun.l.google.com:19302",
- },
- Username: "unittest",
- },
- },
- ICETransportPolicy: ICETransportPolicyAll,
- BundlePolicy: BundlePolicyBalanced,
- RTCPMuxPolicy: RTCPMuxPolicyRequire,
- ICECandidatePoolSize: 5,
- })
- if err != nil {
- return pc, err
- }
- return pc, nil
- },
- config: Configuration{},
- wantErr: nil,
- },
- {
- name: "closed connection",
- init: func() (*PeerConnection, error) {
- pc, err := NewPeerConnection(Configuration{})
- assert.Nil(t, err)
- err = pc.Close()
- assert.Nil(t, err)
- return pc, err
- },
- config: Configuration{},
- wantErr: &rtcerr.InvalidStateError{Err: ErrConnectionClosed},
- },
- {
- name: "update PeerIdentity",
- init: func() (*PeerConnection, error) {
- return NewPeerConnection(Configuration{})
- },
- config: Configuration{
- PeerIdentity: "unittest",
- },
- wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingPeerIdentity},
- },
- {
- name: "update BundlePolicy",
- init: func() (*PeerConnection, error) {
- return NewPeerConnection(Configuration{})
- },
- config: Configuration{
- BundlePolicy: BundlePolicyMaxCompat,
- },
- wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingBundlePolicy},
- },
- {
- name: "update RTCPMuxPolicy",
- init: func() (*PeerConnection, error) {
- return NewPeerConnection(Configuration{})
- },
- config: Configuration{
- RTCPMuxPolicy: RTCPMuxPolicyNegotiate,
- },
- wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingRTCPMuxPolicy},
- },
- {
- name: "update ICECandidatePoolSize",
- init: func() (*PeerConnection, error) {
- pc, err := NewPeerConnection(Configuration{
- ICECandidatePoolSize: 0,
- })
- if err != nil {
- return pc, err
- }
- offer, err := pc.CreateOffer(nil)
- if err != nil {
- return pc, err
- }
- err = pc.SetLocalDescription(offer)
- if err != nil {
- return pc, err
- }
- return pc, nil
- },
- config: Configuration{
- ICECandidatePoolSize: 1,
- },
- wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingICECandidatePoolSize},
- },
- } {
- pc, err := test.init()
- if err != nil {
- t.Errorf("SetConfiguration %q: init failed: %v", test.name, err)
- }
- err = pc.SetConfiguration(test.config)
- if got, want := err, test.wantErr; !reflect.DeepEqual(got, want) {
- t.Errorf("SetConfiguration %q: err = %v, want %v", test.name, got, want)
- }
- assert.NoError(t, pc.Close())
- }
- }
- func TestPeerConnection_GetConfiguration(t *testing.T) {
- pc, err := NewPeerConnection(Configuration{})
- assert.NoError(t, err)
- expected := Configuration{
- ICEServers: []ICEServer{},
- ICETransportPolicy: ICETransportPolicyAll,
- BundlePolicy: BundlePolicyBalanced,
- RTCPMuxPolicy: RTCPMuxPolicyRequire,
- ICECandidatePoolSize: 0,
- }
- actual := pc.GetConfiguration()
- assert.True(t, &expected != &actual)
- assert.Equal(t, expected.ICEServers, actual.ICEServers)
- assert.Equal(t, expected.ICETransportPolicy, actual.ICETransportPolicy)
- assert.Equal(t, expected.BundlePolicy, actual.BundlePolicy)
- assert.Equal(t, expected.RTCPMuxPolicy, actual.RTCPMuxPolicy)
- // nolint:godox
- // TODO(albrow): Uncomment this after #513 is fixed.
- // See: https://github.com/pion/webrtc/issues/513.
- // assert.Equal(t, len(expected.Certificates), len(actual.Certificates))
- assert.Equal(t, expected.ICECandidatePoolSize, actual.ICECandidatePoolSize)
- assert.NoError(t, pc.Close())
- }
- const minimalOffer = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- a=msid-semantic: WMS
- m=application 47299 DTLS/SCTP 5000
- c=IN IP4 192.168.20.129
- a=candidate:1966762134 1 udp 2122260223 192.168.20.129 47299 typ host generation 0
- a=candidate:1966762134 1 udp 2122262783 2001:db8::1 47199 typ host generation 0
- a=candidate:211962667 1 udp 2122194687 10.0.3.1 40864 typ host generation 0
- a=candidate:1002017894 1 tcp 1518280447 192.168.20.129 0 typ host tcptype active generation 0
- a=candidate:1109506011 1 tcp 1518214911 10.0.3.1 0 typ host tcptype active generation 0
- a=ice-ufrag:1/MvHwjAyVf27aLu
- a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
- a=ice-options:google-ice
- a=fingerprint:sha-256 75:74:5A:A6:A4:E5:52:F4:A7:67:4C:01:C7:EE:91:3F:21:3D:A2:E3:53:7B:6F:30:86:F2:30:AA:65:FB:04:24
- a=setup:actpass
- a=mid:data
- a=sctpmap:5000 webrtc-datachannel 1024
- `
- func TestSetRemoteDescription(t *testing.T) {
- testCases := []struct {
- desc SessionDescription
- expectError bool
- }{
- {SessionDescription{Type: SDPTypeOffer, SDP: minimalOffer}, false},
- {SessionDescription{Type: 0, SDP: ""}, true},
- }
- for i, testCase := range testCases {
- peerConn, err := NewPeerConnection(Configuration{})
- if err != nil {
- t.Errorf("Case %d: got error: %v", i, err)
- }
- if testCase.expectError {
- assert.Error(t, peerConn.SetRemoteDescription(testCase.desc))
- } else {
- assert.NoError(t, peerConn.SetRemoteDescription(testCase.desc))
- }
- assert.NoError(t, peerConn.Close())
- }
- }
- func TestCreateOfferAnswer(t *testing.T) {
- offerPeerConn, err := NewPeerConnection(Configuration{})
- assert.NoError(t, err)
- answerPeerConn, err := NewPeerConnection(Configuration{})
- assert.NoError(t, err)
- _, err = offerPeerConn.CreateDataChannel("test-channel", nil)
- assert.NoError(t, err)
- offer, err := offerPeerConn.CreateOffer(nil)
- assert.NoError(t, err)
- assert.NoError(t, offerPeerConn.SetLocalDescription(offer))
- assert.NoError(t, answerPeerConn.SetRemoteDescription(offer))
- answer, err := answerPeerConn.CreateAnswer(nil)
- assert.NoError(t, err)
- assert.NoError(t, answerPeerConn.SetLocalDescription(answer))
- assert.NoError(t, offerPeerConn.SetRemoteDescription(answer))
- // after setLocalDescription(answer), signaling state should be stable.
- // so CreateAnswer should return an InvalidStateError
- assert.Equal(t, answerPeerConn.SignalingState(), SignalingStateStable)
- _, err = answerPeerConn.CreateAnswer(nil)
- assert.Error(t, err)
- closePairNow(t, offerPeerConn, answerPeerConn)
- }
- func TestPeerConnection_EventHandlers(t *testing.T) {
- pcOffer, err := NewPeerConnection(Configuration{})
- assert.NoError(t, err)
- pcAnswer, err := NewPeerConnection(Configuration{})
- assert.NoError(t, err)
- // wasCalled is a list of event handlers that were called.
- wasCalled := []string{}
- wasCalledMut := &sync.Mutex{}
- // wg is used to wait for all event handlers to be called.
- wg := &sync.WaitGroup{}
- wg.Add(6)
- // Each sync.Once is used to ensure that we call wg.Done once for each event
- // handler and don't add multiple entries to wasCalled. The event handlers can
- // be called more than once in some cases.
- onceOffererOnICEConnectionStateChange := &sync.Once{}
- onceOffererOnConnectionStateChange := &sync.Once{}
- onceOffererOnSignalingStateChange := &sync.Once{}
- onceAnswererOnICEConnectionStateChange := &sync.Once{}
- onceAnswererOnConnectionStateChange := &sync.Once{}
- onceAnswererOnSignalingStateChange := &sync.Once{}
- // Register all the event handlers.
- pcOffer.OnICEConnectionStateChange(func(ICEConnectionState) {
- onceOffererOnICEConnectionStateChange.Do(func() {
- wasCalledMut.Lock()
- defer wasCalledMut.Unlock()
- wasCalled = append(wasCalled, "offerer OnICEConnectionStateChange")
- wg.Done()
- })
- })
- pcOffer.OnConnectionStateChange(func(callbackState PeerConnectionState) {
- if storedState := pcOffer.ConnectionState(); callbackState != storedState {
- t.Errorf("State in callback argument is different from ConnectionState(): callbackState=%s, storedState=%s", callbackState, storedState)
- }
- onceOffererOnConnectionStateChange.Do(func() {
- wasCalledMut.Lock()
- defer wasCalledMut.Unlock()
- wasCalled = append(wasCalled, "offerer OnConnectionStateChange")
- wg.Done()
- })
- })
- pcOffer.OnSignalingStateChange(func(SignalingState) {
- onceOffererOnSignalingStateChange.Do(func() {
- wasCalledMut.Lock()
- defer wasCalledMut.Unlock()
- wasCalled = append(wasCalled, "offerer OnSignalingStateChange")
- wg.Done()
- })
- })
- pcAnswer.OnICEConnectionStateChange(func(ICEConnectionState) {
- onceAnswererOnICEConnectionStateChange.Do(func() {
- wasCalledMut.Lock()
- defer wasCalledMut.Unlock()
- wasCalled = append(wasCalled, "answerer OnICEConnectionStateChange")
- wg.Done()
- })
- })
- pcAnswer.OnConnectionStateChange(func(PeerConnectionState) {
- onceAnswererOnConnectionStateChange.Do(func() {
- wasCalledMut.Lock()
- defer wasCalledMut.Unlock()
- wasCalled = append(wasCalled, "answerer OnConnectionStateChange")
- wg.Done()
- })
- })
- pcAnswer.OnSignalingStateChange(func(SignalingState) {
- onceAnswererOnSignalingStateChange.Do(func() {
- wasCalledMut.Lock()
- defer wasCalledMut.Unlock()
- wasCalled = append(wasCalled, "answerer OnSignalingStateChange")
- wg.Done()
- })
- })
- // Use signalPair to establish a connection between pcOffer and pcAnswer. This
- // process should trigger the above event handlers.
- assert.NoError(t, signalPair(pcOffer, pcAnswer))
- // Wait for all of the event handlers to be triggered.
- done := make(chan struct{})
- go func() {
- wg.Wait()
- done <- struct{}{}
- }()
- timeout := time.After(5 * time.Second)
- select {
- case <-done:
- break
- case <-timeout:
- t.Fatalf("timed out waiting for one or more events handlers to be called (these *were* called: %+v)", wasCalled)
- }
- closePairNow(t, pcOffer, pcAnswer)
- }
- func TestMultipleOfferAnswer(t *testing.T) {
- firstPeerConn, err := NewPeerConnection(Configuration{})
- if err != nil {
- t.Errorf("New PeerConnection: got error: %v", err)
- }
- if _, err = firstPeerConn.CreateOffer(nil); err != nil {
- t.Errorf("First Offer: got error: %v", err)
- }
- if _, err = firstPeerConn.CreateOffer(nil); err != nil {
- t.Errorf("Second Offer: got error: %v", err)
- }
- secondPeerConn, err := NewPeerConnection(Configuration{})
- if err != nil {
- t.Errorf("New PeerConnection: got error: %v", err)
- }
- secondPeerConn.OnICECandidate(func(i *ICECandidate) {
- })
- if _, err = secondPeerConn.CreateOffer(nil); err != nil {
- t.Errorf("First Offer: got error: %v", err)
- }
- if _, err = secondPeerConn.CreateOffer(nil); err != nil {
- t.Errorf("Second Offer: got error: %v", err)
- }
- closePairNow(t, firstPeerConn, secondPeerConn)
- }
- func TestNoFingerprintInFirstMediaIfSetRemoteDescription(t *testing.T) {
- const sdpNoFingerprintInFirstMedia = `v=0
- o=- 143087887 1561022767 IN IP4 192.168.84.254
- s=VideoRoom 404986692241682
- t=0 0
- a=group:BUNDLE audio
- a=msid-semantic: WMS 2867270241552712
- m=video 0 UDP/TLS/RTP/SAVPF 0
- a=mid:video
- c=IN IP4 192.168.84.254
- a=inactive
- m=audio 9 UDP/TLS/RTP/SAVPF 111
- c=IN IP4 192.168.84.254
- a=recvonly
- a=mid:audio
- a=rtcp-mux
- a=ice-ufrag:AS/w
- a=ice-pwd:9NOgoAOMALYu/LOpA1iqg/
- a=ice-options:trickle
- a=fingerprint:sha-256 D2:B9:31:8F:DF:24:D8:0E:ED:D2:EF:25:9E:AF:6F:B8:34:AE:53:9C:E6:F3:8F:F2:64:15:FA:E8:7F:53:2D:38
- a=setup:active
- a=rtpmap:111 opus/48000/2
- a=candidate:1 1 udp 2013266431 192.168.84.254 46492 typ host
- a=end-of-candidates
- `
- report := test.CheckRoutines(t)
- defer report()
- pc, err := NewPeerConnection(Configuration{})
- if err != nil {
- t.Error(err.Error())
- }
- desc := SessionDescription{
- Type: SDPTypeOffer,
- SDP: sdpNoFingerprintInFirstMedia,
- }
- if err = pc.SetRemoteDescription(desc); err != nil {
- t.Error(err.Error())
- }
- assert.NoError(t, pc.Close())
- }
- func TestNegotiationNeeded(t *testing.T) {
- lim := test.TimeOut(time.Second * 30)
- defer lim.Stop()
- report := test.CheckRoutines(t)
- defer report()
- pc, err := NewPeerConnection(Configuration{})
- if err != nil {
- t.Error(err.Error())
- }
- var wg sync.WaitGroup
- wg.Add(1)
- pc.OnNegotiationNeeded(wg.Done)
- _, err = pc.CreateDataChannel("initial_data_channel", nil)
- assert.NoError(t, err)
- wg.Wait()
- assert.NoError(t, pc.Close())
- }
- func TestMultipleCreateChannel(t *testing.T) {
- var wg sync.WaitGroup
- report := test.CheckRoutines(t)
- defer report()
- // Two OnDataChannel
- // One OnNegotiationNeeded
- wg.Add(3)
- pcOffer, _ := NewPeerConnection(Configuration{})
- pcAnswer, _ := NewPeerConnection(Configuration{})
- pcAnswer.OnDataChannel(func(d *DataChannel) {
- wg.Done()
- })
- pcOffer.OnNegotiationNeeded(func() {
- offer, err := pcOffer.CreateOffer(nil)
- assert.NoError(t, err)
- offerGatheringComplete := GatheringCompletePromise(pcOffer)
- if err = pcOffer.SetLocalDescription(offer); err != nil {
- t.Error(err)
- }
- <-offerGatheringComplete
- if err = pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()); err != nil {
- t.Error(err)
- }
- answer, err := pcAnswer.CreateAnswer(nil)
- assert.NoError(t, err)
- answerGatheringComplete := GatheringCompletePromise(pcAnswer)
- if err = pcAnswer.SetLocalDescription(answer); err != nil {
- t.Error(err)
- }
- <-answerGatheringComplete
- if err = pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()); err != nil {
- t.Error(err)
- }
- wg.Done()
- })
- if _, err := pcOffer.CreateDataChannel("initial_data_channel_0", nil); err != nil {
- t.Error(err)
- }
- if _, err := pcOffer.CreateDataChannel("initial_data_channel_1", nil); err != nil {
- t.Error(err)
- }
- wg.Wait()
- closePairNow(t, pcOffer, pcAnswer)
- }
- // Assert that candidates are gathered by calling SetLocalDescription, not SetRemoteDescription
- func TestGatherOnSetLocalDescription(t *testing.T) {
- lim := test.TimeOut(time.Second * 30)
- defer lim.Stop()
- report := test.CheckRoutines(t)
- defer report()
- pcOfferGathered := make(chan SessionDescription)
- pcAnswerGathered := make(chan SessionDescription)
- s := SettingEngine{}
- api := NewAPI(WithSettingEngine(s))
- pcOffer, err := api.NewPeerConnection(Configuration{})
- if err != nil {
- t.Error(err.Error())
- }
- // We need to create a data channel in order to trigger ICE
- if _, err = pcOffer.CreateDataChannel("initial_data_channel", nil); err != nil {
- t.Error(err.Error())
- }
- pcOffer.OnICECandidate(func(i *ICECandidate) {
- if i == nil {
- close(pcOfferGathered)
- }
- })
- offer, err := pcOffer.CreateOffer(nil)
- if err != nil {
- t.Error(err.Error())
- } else if err = pcOffer.SetLocalDescription(offer); err != nil {
- t.Error(err.Error())
- }
- <-pcOfferGathered
- pcAnswer, err := api.NewPeerConnection(Configuration{})
- if err != nil {
- t.Error(err.Error())
- }
- pcAnswer.OnICECandidate(func(i *ICECandidate) {
- if i == nil {
- close(pcAnswerGathered)
- }
- })
- if err = pcAnswer.SetRemoteDescription(offer); err != nil {
- t.Error(err.Error())
- }
- select {
- case <-pcAnswerGathered:
- t.Fatal("pcAnswer started gathering with no SetLocalDescription")
- // Gathering is async, not sure of a better way to catch this currently
- case <-time.After(3 * time.Second):
- }
- answer, err := pcAnswer.CreateAnswer(nil)
- if err != nil {
- t.Error(err.Error())
- } else if err = pcAnswer.SetLocalDescription(answer); err != nil {
- t.Error(err.Error())
- }
- <-pcAnswerGathered
- closePairNow(t, pcOffer, pcAnswer)
- }
- // Assert that SetRemoteDescription handles invalid states
- func TestSetRemoteDescriptionInvalid(t *testing.T) {
- t.Run("local-offer+SetRemoteDescription(Offer)", func(t *testing.T) {
- pc, err := NewPeerConnection(Configuration{})
- assert.NoError(t, err)
- offer, err := pc.CreateOffer(nil)
- assert.NoError(t, err)
- assert.NoError(t, pc.SetLocalDescription(offer))
- assert.Error(t, pc.SetRemoteDescription(offer))
- assert.NoError(t, pc.Close())
- })
- }
- func TestAddTransceiver(t *testing.T) {
- lim := test.TimeOut(time.Second * 30)
- defer lim.Stop()
- report := test.CheckRoutines(t)
- defer report()
- for _, testCase := range []struct {
- expectSender, expectReceiver bool
- direction RTPTransceiverDirection
- }{
- {true, true, RTPTransceiverDirectionSendrecv},
- // Go and WASM diverge
- // {true, false, RTPTransceiverDirectionSendonly},
- // {false, true, RTPTransceiverDirectionRecvonly},
- } {
- pc, err := NewPeerConnection(Configuration{})
- assert.NoError(t, err)
- transceiver, err := pc.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{
- Direction: testCase.direction,
- })
- assert.NoError(t, err)
- if testCase.expectReceiver {
- assert.NotNil(t, transceiver.Receiver())
- } else {
- assert.Nil(t, transceiver.Receiver())
- }
- if testCase.expectSender {
- assert.NotNil(t, transceiver.Sender())
- } else {
- assert.Nil(t, transceiver.Sender())
- }
- offer, err := pc.CreateOffer(nil)
- assert.NoError(t, err)
- assert.True(t, offerMediaHasDirection(offer, RTPCodecTypeVideo, testCase.direction))
- assert.NoError(t, pc.Close())
- }
- }
- // Assert that SCTPTransport -> DTLSTransport -> ICETransport works after connected
- func TestTransportChain(t *testing.T) {
- offer, answer, err := newPair()
- assert.NoError(t, err)
- peerConnectionsConnected := untilConnectionState(PeerConnectionStateConnected, offer, answer)
- assert.NoError(t, signalPair(offer, answer))
- peerConnectionsConnected.Wait()
- assert.NotNil(t, offer.SCTP().Transport().ICETransport())
- closePairNow(t, offer, answer)
- }
|