1// +build !js
2
3package webrtc
4
5import (
6	"bufio"
7	"context"
8	"crypto/ecdsa"
9	"crypto/elliptic"
10	"crypto/rand"
11	"crypto/x509"
12	"fmt"
13	"math/big"
14	"reflect"
15	"regexp"
16	"strings"
17	"sync"
18	"testing"
19	"time"
20
21	"github.com/pion/ice/v2"
22	"github.com/pion/rtp"
23	"github.com/pion/transport/test"
24	"github.com/pion/transport/vnet"
25	"github.com/pion/webrtc/v3/internal/util"
26	"github.com/pion/webrtc/v3/pkg/rtcerr"
27	"github.com/stretchr/testify/assert"
28)
29
30// newPair creates two new peer connections (an offerer and an answerer) using
31// the api.
32func (api *API) newPair(cfg Configuration) (pcOffer *PeerConnection, pcAnswer *PeerConnection, err error) {
33	pca, err := api.NewPeerConnection(cfg)
34	if err != nil {
35		return nil, nil, err
36	}
37
38	pcb, err := api.NewPeerConnection(cfg)
39	if err != nil {
40		return nil, nil, err
41	}
42
43	return pca, pcb, nil
44}
45
46func TestNew_Go(t *testing.T) {
47	report := test.CheckRoutines(t)
48	defer report()
49
50	api := NewAPI()
51	t.Run("Success", func(t *testing.T) {
52		secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
53		assert.Nil(t, err)
54
55		certificate, err := GenerateCertificate(secretKey)
56		assert.Nil(t, err)
57
58		pc, err := api.NewPeerConnection(Configuration{
59			ICEServers: []ICEServer{
60				{
61					URLs: []string{
62						"stun:stun.l.google.com:19302",
63						"turns:google.de?transport=tcp",
64					},
65					Username: "unittest",
66					Credential: OAuthCredential{
67						MACKey:      "WmtzanB3ZW9peFhtdm42NzUzNG0=",
68						AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ==",
69					},
70					CredentialType: ICECredentialTypeOauth,
71				},
72			},
73			ICETransportPolicy:   ICETransportPolicyRelay,
74			BundlePolicy:         BundlePolicyMaxCompat,
75			RTCPMuxPolicy:        RTCPMuxPolicyNegotiate,
76			PeerIdentity:         "unittest",
77			Certificates:         []Certificate{*certificate},
78			ICECandidatePoolSize: 5,
79		})
80		assert.Nil(t, err)
81		assert.NotNil(t, pc)
82		assert.NoError(t, pc.Close())
83	})
84	t.Run("Failure", func(t *testing.T) {
85		testCases := []struct {
86			initialize  func() (*PeerConnection, error)
87			expectedErr error
88		}{
89			{func() (*PeerConnection, error) {
90				secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
91				assert.Nil(t, err)
92
93				certificate, err := NewCertificate(secretKey, x509.Certificate{
94					Version:      2,
95					SerialNumber: big.NewInt(1653),
96					NotBefore:    time.Now().AddDate(0, -2, 0),
97					NotAfter:     time.Now().AddDate(0, -1, 0),
98				})
99				assert.Nil(t, err)
100
101				return api.NewPeerConnection(Configuration{
102					Certificates: []Certificate{*certificate},
103				})
104			}, &rtcerr.InvalidAccessError{Err: ErrCertificateExpired}},
105			{func() (*PeerConnection, error) {
106				return api.NewPeerConnection(Configuration{
107					ICEServers: []ICEServer{
108						{
109							URLs: []string{
110								"stun:stun.l.google.com:19302",
111								"turns:google.de?transport=tcp",
112							},
113							Username: "unittest",
114						},
115					},
116				})
117			}, &rtcerr.InvalidAccessError{Err: ErrNoTurnCredentials}},
118		}
119
120		for i, testCase := range testCases {
121			pc, err := testCase.initialize()
122			assert.EqualError(t, err, testCase.expectedErr.Error(),
123				"testCase: %d %v", i, testCase,
124			)
125			if pc != nil {
126				assert.NoError(t, pc.Close())
127			}
128		}
129	})
130	t.Run("ICEServers_Copy", func(t *testing.T) {
131		const expectedURL = "stun:stun.l.google.com:19302?foo=bar"
132		const expectedUsername = "username"
133		const expectedPassword = "password"
134
135		cfg := Configuration{
136			ICEServers: []ICEServer{
137				{
138					URLs:       []string{expectedURL},
139					Username:   expectedUsername,
140					Credential: expectedPassword,
141				},
142			},
143		}
144		pc, err := api.NewPeerConnection(cfg)
145		assert.NoError(t, err)
146		assert.NotNil(t, pc)
147
148		pc.configuration.ICEServers[0].Username = util.MathRandAlpha(15) // Tests doesn't need crypto random
149		pc.configuration.ICEServers[0].Credential = util.MathRandAlpha(15)
150		pc.configuration.ICEServers[0].URLs[0] = util.MathRandAlpha(15)
151
152		assert.Equal(t, expectedUsername, cfg.ICEServers[0].Username)
153		assert.Equal(t, expectedPassword, cfg.ICEServers[0].Credential)
154		assert.Equal(t, expectedURL, cfg.ICEServers[0].URLs[0])
155
156		assert.NoError(t, pc.Close())
157	})
158}
159
160func TestPeerConnection_SetConfiguration_Go(t *testing.T) {
161	// Note: this test includes all SetConfiguration features that are supported
162	// by Go but not the WASM bindings, namely: ICEServer.Credential,
163	// ICEServer.CredentialType, and Certificates.
164	report := test.CheckRoutines(t)
165	defer report()
166
167	api := NewAPI()
168
169	secretKey1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
170	assert.Nil(t, err)
171
172	certificate1, err := GenerateCertificate(secretKey1)
173	assert.Nil(t, err)
174
175	secretKey2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
176	assert.Nil(t, err)
177
178	certificate2, err := GenerateCertificate(secretKey2)
179	assert.Nil(t, err)
180
181	for _, test := range []struct {
182		name    string
183		init    func() (*PeerConnection, error)
184		config  Configuration
185		wantErr error
186	}{
187		{
188			name: "valid",
189			init: func() (*PeerConnection, error) {
190				pc, err := api.NewPeerConnection(Configuration{
191					PeerIdentity:         "unittest",
192					Certificates:         []Certificate{*certificate1},
193					ICECandidatePoolSize: 5,
194				})
195				if err != nil {
196					return pc, err
197				}
198
199				err = pc.SetConfiguration(Configuration{
200					ICEServers: []ICEServer{
201						{
202							URLs: []string{
203								"stun:stun.l.google.com:19302",
204								"turns:google.de?transport=tcp",
205							},
206							Username: "unittest",
207							Credential: OAuthCredential{
208								MACKey:      "WmtzanB3ZW9peFhtdm42NzUzNG0=",
209								AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ==",
210							},
211							CredentialType: ICECredentialTypeOauth,
212						},
213					},
214					ICETransportPolicy:   ICETransportPolicyAll,
215					BundlePolicy:         BundlePolicyBalanced,
216					RTCPMuxPolicy:        RTCPMuxPolicyRequire,
217					PeerIdentity:         "unittest",
218					Certificates:         []Certificate{*certificate1},
219					ICECandidatePoolSize: 5,
220				})
221				if err != nil {
222					return pc, err
223				}
224
225				return pc, nil
226			},
227			config:  Configuration{},
228			wantErr: nil,
229		},
230		{
231			name: "update multiple certificates",
232			init: func() (*PeerConnection, error) {
233				return api.NewPeerConnection(Configuration{})
234			},
235			config: Configuration{
236				Certificates: []Certificate{*certificate1, *certificate2},
237			},
238			wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates},
239		},
240		{
241			name: "update certificate",
242			init: func() (*PeerConnection, error) {
243				return api.NewPeerConnection(Configuration{})
244			},
245			config: Configuration{
246				Certificates: []Certificate{*certificate1},
247			},
248			wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates},
249		},
250		{
251			name: "update ICEServers, no TURN credentials",
252			init: func() (*PeerConnection, error) {
253				return NewPeerConnection(Configuration{})
254			},
255			config: Configuration{
256				ICEServers: []ICEServer{
257					{
258						URLs: []string{
259							"stun:stun.l.google.com:19302",
260							"turns:google.de?transport=tcp",
261						},
262						Username: "unittest",
263					},
264				},
265			},
266			wantErr: &rtcerr.InvalidAccessError{Err: ErrNoTurnCredentials},
267		},
268	} {
269		pc, err := test.init()
270		if err != nil {
271			t.Errorf("SetConfiguration %q: init failed: %v", test.name, err)
272		}
273
274		err = pc.SetConfiguration(test.config)
275		if got, want := err, test.wantErr; !reflect.DeepEqual(got, want) {
276			t.Errorf("SetConfiguration %q: err = %v, want %v", test.name, got, want)
277		}
278
279		assert.NoError(t, pc.Close())
280	}
281}
282
283func TestPeerConnection_EventHandlers_Go(t *testing.T) {
284	lim := test.TimeOut(time.Second * 5)
285	defer lim.Stop()
286
287	report := test.CheckRoutines(t)
288	defer report()
289
290	// Note: When testing the Go event handlers we peer into the state a bit more
291	// than what is possible for the environment agnostic (Go or WASM/JavaScript)
292	// EventHandlers test.
293	api := NewAPI()
294	pc, err := api.NewPeerConnection(Configuration{})
295	assert.Nil(t, err)
296
297	onTrackCalled := make(chan struct{})
298	onICEConnectionStateChangeCalled := make(chan struct{})
299	onDataChannelCalled := make(chan struct{})
300
301	// Verify that the noop case works
302	assert.NotPanics(t, func() { pc.onTrack(nil, nil) })
303	assert.NotPanics(t, func() { pc.onICEConnectionStateChange(ice.ConnectionStateNew) })
304
305	pc.OnTrack(func(t *TrackRemote, r *RTPReceiver) {
306		close(onTrackCalled)
307	})
308
309	pc.OnICEConnectionStateChange(func(cs ICEConnectionState) {
310		close(onICEConnectionStateChangeCalled)
311	})
312
313	pc.OnDataChannel(func(dc *DataChannel) {
314		// Questions:
315		//  (1) How come this callback is made with dc being nil?
316		//  (2) How come this callback is made without CreateDataChannel?
317		if dc != nil {
318			close(onDataChannelCalled)
319		}
320	})
321
322	// Verify that the handlers deal with nil inputs
323	assert.NotPanics(t, func() { pc.onTrack(nil, nil) })
324	assert.NotPanics(t, func() { go pc.onDataChannelHandler(nil) })
325
326	// Verify that the set handlers are called
327	assert.NotPanics(t, func() { pc.onTrack(&TrackRemote{}, &RTPReceiver{}) })
328	assert.NotPanics(t, func() { pc.onICEConnectionStateChange(ice.ConnectionStateNew) })
329	assert.NotPanics(t, func() { go pc.onDataChannelHandler(&DataChannel{api: api}) })
330
331	<-onTrackCalled
332	<-onICEConnectionStateChangeCalled
333	<-onDataChannelCalled
334	assert.NoError(t, pc.Close())
335}
336
337// This test asserts that nothing deadlocks we try to shutdown when DTLS is in flight
338// We ensure that DTLS is in flight by removing the mux func for it, so all inbound DTLS is lost
339func TestPeerConnection_ShutdownNoDTLS(t *testing.T) {
340	lim := test.TimeOut(time.Second * 10)
341	defer lim.Stop()
342
343	report := test.CheckRoutines(t)
344	defer report()
345
346	api := NewAPI()
347	offerPC, answerPC, err := api.newPair(Configuration{})
348	if err != nil {
349		t.Fatal(err)
350	}
351
352	// Drop all incoming DTLS traffic
353	dropAllDTLS := func([]byte) bool {
354		return false
355	}
356	offerPC.dtlsTransport.dtlsMatcher = dropAllDTLS
357	answerPC.dtlsTransport.dtlsMatcher = dropAllDTLS
358
359	if err = signalPair(offerPC, answerPC); err != nil {
360		t.Fatal(err)
361	}
362
363	iceComplete := make(chan interface{})
364	answerPC.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
365		if iceState == ICEConnectionStateConnected {
366			time.Sleep(time.Second) // Give time for DTLS to start
367
368			select {
369			case <-iceComplete:
370			default:
371				close(iceComplete)
372			}
373		}
374	})
375
376	<-iceComplete
377	closePairNow(t, offerPC, answerPC)
378}
379
380func TestPeerConnection_PropertyGetters(t *testing.T) {
381	pc := &PeerConnection{
382		currentLocalDescription:  &SessionDescription{},
383		pendingLocalDescription:  &SessionDescription{},
384		currentRemoteDescription: &SessionDescription{},
385		pendingRemoteDescription: &SessionDescription{},
386		signalingState:           SignalingStateHaveLocalOffer,
387		iceConnectionState:       ICEConnectionStateChecking,
388		connectionState:          PeerConnectionStateConnecting,
389	}
390
391	assert.Equal(t, pc.currentLocalDescription, pc.CurrentLocalDescription(), "should match")
392	assert.Equal(t, pc.pendingLocalDescription, pc.PendingLocalDescription(), "should match")
393	assert.Equal(t, pc.currentRemoteDescription, pc.CurrentRemoteDescription(), "should match")
394	assert.Equal(t, pc.pendingRemoteDescription, pc.PendingRemoteDescription(), "should match")
395	assert.Equal(t, pc.signalingState, pc.SignalingState(), "should match")
396	assert.Equal(t, pc.iceConnectionState, pc.ICEConnectionState(), "should match")
397	assert.Equal(t, pc.connectionState, pc.ConnectionState(), "should match")
398}
399
400func TestPeerConnection_AnswerWithoutOffer(t *testing.T) {
401	report := test.CheckRoutines(t)
402	defer report()
403
404	pc, err := NewPeerConnection(Configuration{})
405	if err != nil {
406		t.Errorf("New PeerConnection: got error: %v", err)
407	}
408	_, err = pc.CreateAnswer(nil)
409	if !reflect.DeepEqual(&rtcerr.InvalidStateError{Err: ErrNoRemoteDescription}, err) {
410		t.Errorf("CreateAnswer without RemoteDescription: got error: %v", err)
411	}
412
413	assert.NoError(t, pc.Close())
414}
415
416func TestPeerConnection_AnswerWithClosedConnection(t *testing.T) {
417	report := test.CheckRoutines(t)
418	defer report()
419
420	offerPeerConn, answerPeerConn, err := newPair()
421	assert.NoError(t, err)
422
423	inChecking, inCheckingCancel := context.WithCancel(context.Background())
424	answerPeerConn.OnICEConnectionStateChange(func(i ICEConnectionState) {
425		if i == ICEConnectionStateChecking {
426			inCheckingCancel()
427		}
428	})
429
430	_, err = offerPeerConn.CreateDataChannel("test-channel", nil)
431	assert.NoError(t, err)
432
433	offer, err := offerPeerConn.CreateOffer(nil)
434	assert.NoError(t, err)
435	assert.NoError(t, offerPeerConn.SetLocalDescription(offer))
436
437	assert.NoError(t, offerPeerConn.Close())
438
439	assert.NoError(t, answerPeerConn.SetRemoteDescription(offer))
440
441	<-inChecking.Done()
442	assert.NoError(t, answerPeerConn.Close())
443
444	_, err = answerPeerConn.CreateAnswer(nil)
445	assert.Error(t, err, &rtcerr.InvalidStateError{Err: ErrConnectionClosed})
446}
447
448func TestPeerConnection_satisfyTypeAndDirection(t *testing.T) {
449	createTransceiver := func(kind RTPCodecType, direction RTPTransceiverDirection) *RTPTransceiver {
450		r := &RTPTransceiver{kind: kind}
451		r.setDirection(direction)
452
453		return r
454	}
455
456	for _, test := range []struct {
457		name string
458
459		kinds      []RTPCodecType
460		directions []RTPTransceiverDirection
461
462		localTransceivers []*RTPTransceiver
463		want              []*RTPTransceiver
464	}{
465		{
466			"Audio and Video Transceivers can not satisfy each other",
467			[]RTPCodecType{RTPCodecTypeVideo},
468			[]RTPTransceiverDirection{RTPTransceiverDirectionSendrecv},
469			[]*RTPTransceiver{createTransceiver(RTPCodecTypeAudio, RTPTransceiverDirectionSendrecv)},
470			[]*RTPTransceiver{nil},
471		},
472		{
473			"No local Transceivers, every remote should get nil",
474			[]RTPCodecType{RTPCodecTypeVideo, RTPCodecTypeAudio, RTPCodecTypeVideo, RTPCodecTypeVideo},
475			[]RTPTransceiverDirection{RTPTransceiverDirectionSendrecv, RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendonly, RTPTransceiverDirectionInactive},
476
477			[]*RTPTransceiver{},
478
479			[]*RTPTransceiver{
480				nil,
481				nil,
482				nil,
483				nil,
484			},
485		},
486		{
487			"Local Recv can satisfy remote SendRecv",
488			[]RTPCodecType{RTPCodecTypeVideo},
489			[]RTPTransceiverDirection{RTPTransceiverDirectionSendrecv},
490
491			[]*RTPTransceiver{createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionRecvonly)},
492
493			[]*RTPTransceiver{createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionRecvonly)},
494		},
495		{
496			"Don't satisfy a Sendonly with a SendRecv, later SendRecv will be marked as Inactive",
497			[]RTPCodecType{RTPCodecTypeVideo, RTPCodecTypeVideo},
498			[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionSendrecv},
499
500			[]*RTPTransceiver{
501				createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionSendrecv),
502				createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionRecvonly),
503			},
504
505			[]*RTPTransceiver{
506				createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionRecvonly),
507				createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionSendrecv),
508			},
509		},
510	} {
511		if len(test.kinds) != len(test.directions) {
512			t.Fatal("Kinds and Directions must be the same length")
513		}
514
515		got := []*RTPTransceiver{}
516		for i := range test.kinds {
517			res, filteredLocalTransceivers := satisfyTypeAndDirection(test.kinds[i], test.directions[i], test.localTransceivers)
518
519			got = append(got, res)
520			test.localTransceivers = filteredLocalTransceivers
521		}
522
523		if !reflect.DeepEqual(got, test.want) {
524			gotStr := ""
525			for _, t := range got {
526				gotStr += fmt.Sprintf("%+v\n", t)
527			}
528
529			wantStr := ""
530			for _, t := range test.want {
531				wantStr += fmt.Sprintf("%+v\n", t)
532			}
533			t.Errorf("satisfyTypeAndDirection %q: \ngot\n%s \nwant\n%s", test.name, gotStr, wantStr)
534		}
535	}
536}
537
538func TestOneAttrKeyConnectionSetupPerMediaDescriptionInSDP(t *testing.T) {
539	pc, err := NewPeerConnection(Configuration{})
540	assert.NoError(t, err)
541
542	_, err = pc.AddTransceiverFromKind(RTPCodecTypeVideo)
543	assert.NoError(t, err)
544
545	_, err = pc.AddTransceiverFromKind(RTPCodecTypeAudio)
546	assert.NoError(t, err)
547
548	_, err = pc.AddTransceiverFromKind(RTPCodecTypeAudio)
549	assert.NoError(t, err)
550
551	_, err = pc.AddTransceiverFromKind(RTPCodecTypeVideo)
552	assert.NoError(t, err)
553
554	sdp, err := pc.CreateOffer(nil)
555	assert.NoError(t, err)
556
557	re := regexp.MustCompile(`a=setup:[[:alpha:]]+`)
558
559	matches := re.FindAllStringIndex(sdp.SDP, -1)
560
561	assert.Len(t, matches, 4)
562	assert.NoError(t, pc.Close())
563}
564
565func TestPeerConnection_OfferingLite(t *testing.T) {
566	report := test.CheckRoutines(t)
567	defer report()
568
569	lim := test.TimeOut(time.Second * 10)
570	defer lim.Stop()
571
572	s := SettingEngine{}
573	s.SetLite(true)
574	offerPC, err := NewAPI(WithSettingEngine(s)).NewPeerConnection(Configuration{})
575	if err != nil {
576		t.Fatal(err)
577	}
578
579	answerPC, err := NewAPI().NewPeerConnection(Configuration{})
580	if err != nil {
581		t.Fatal(err)
582	}
583
584	if err = signalPair(offerPC, answerPC); err != nil {
585		t.Fatal(err)
586	}
587
588	iceComplete := make(chan interface{})
589	answerPC.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
590		if iceState == ICEConnectionStateConnected {
591			select {
592			case <-iceComplete:
593			default:
594				close(iceComplete)
595			}
596		}
597	})
598
599	<-iceComplete
600	closePairNow(t, offerPC, answerPC)
601}
602
603func TestPeerConnection_AnsweringLite(t *testing.T) {
604	report := test.CheckRoutines(t)
605	defer report()
606
607	lim := test.TimeOut(time.Second * 10)
608	defer lim.Stop()
609
610	offerPC, err := NewAPI().NewPeerConnection(Configuration{})
611	if err != nil {
612		t.Fatal(err)
613	}
614
615	s := SettingEngine{}
616	s.SetLite(true)
617	answerPC, err := NewAPI(WithSettingEngine(s)).NewPeerConnection(Configuration{})
618	if err != nil {
619		t.Fatal(err)
620	}
621
622	if err = signalPair(offerPC, answerPC); err != nil {
623		t.Fatal(err)
624	}
625
626	iceComplete := make(chan interface{})
627	answerPC.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
628		if iceState == ICEConnectionStateConnected {
629			select {
630			case <-iceComplete:
631			default:
632				close(iceComplete)
633			}
634		}
635	})
636
637	<-iceComplete
638	closePairNow(t, offerPC, answerPC)
639}
640
641func TestOnICEGatheringStateChange(t *testing.T) {
642	seenGathering := &atomicBool{}
643	seenComplete := &atomicBool{}
644
645	seenGatheringAndComplete := make(chan interface{})
646	seenClosed := make(chan interface{})
647
648	peerConn, err := NewPeerConnection(Configuration{})
649	assert.NoError(t, err)
650
651	var onStateChange func(s ICEGathererState)
652	onStateChange = func(s ICEGathererState) {
653		// Access to ICEGatherer in the callback must not cause dead lock.
654		peerConn.OnICEGatheringStateChange(onStateChange)
655		if state := peerConn.iceGatherer.State(); state != s {
656			t.Errorf("State change callback argument (%s) and State() (%s) result differs",
657				s, state,
658			)
659		}
660
661		switch s { // nolint:exhaustive
662		case ICEGathererStateClosed:
663			close(seenClosed)
664			return
665		case ICEGathererStateGathering:
666			if seenComplete.get() {
667				t.Error("Completed before gathering")
668			}
669			seenGathering.set(true)
670		case ICEGathererStateComplete:
671			seenComplete.set(true)
672		}
673
674		if seenGathering.get() && seenComplete.get() {
675			close(seenGatheringAndComplete)
676		}
677	}
678	peerConn.OnICEGatheringStateChange(onStateChange)
679
680	offer, err := peerConn.CreateOffer(nil)
681	assert.NoError(t, err)
682	assert.NoError(t, peerConn.SetLocalDescription(offer))
683
684	select {
685	case <-time.After(time.Second * 10):
686		t.Fatal("Gathering and Complete were never seen")
687	case <-seenClosed:
688		t.Fatal("Closed before PeerConnection Close")
689	case <-seenGatheringAndComplete:
690	}
691
692	assert.NoError(t, peerConn.Close())
693
694	select {
695	case <-time.After(time.Second * 10):
696		t.Fatal("Closed was never seen")
697	case <-seenClosed:
698	}
699}
700
701// Assert Trickle ICE behaviors
702func TestPeerConnectionTrickle(t *testing.T) {
703	offerPC, answerPC, err := newPair()
704	assert.NoError(t, err)
705
706	_, err = offerPC.CreateDataChannel("test-channel", nil)
707	assert.NoError(t, err)
708
709	addOrCacheCandidate := func(pc *PeerConnection, c *ICECandidate, candidateCache []ICECandidateInit) []ICECandidateInit {
710		if c == nil {
711			return candidateCache
712		}
713
714		if pc.RemoteDescription() == nil {
715			return append(candidateCache, c.ToJSON())
716		}
717
718		assert.NoError(t, pc.AddICECandidate(c.ToJSON()))
719		return candidateCache
720	}
721
722	candidateLock := sync.RWMutex{}
723	var offerCandidateDone, answerCandidateDone bool
724
725	cachedOfferCandidates := []ICECandidateInit{}
726	offerPC.OnICECandidate(func(c *ICECandidate) {
727		if offerCandidateDone {
728			t.Error("Received OnICECandidate after finishing gathering")
729		}
730		if c == nil {
731			offerCandidateDone = true
732		}
733
734		candidateLock.Lock()
735		defer candidateLock.Unlock()
736
737		cachedOfferCandidates = addOrCacheCandidate(answerPC, c, cachedOfferCandidates)
738	})
739
740	cachedAnswerCandidates := []ICECandidateInit{}
741	answerPC.OnICECandidate(func(c *ICECandidate) {
742		if answerCandidateDone {
743			t.Error("Received OnICECandidate after finishing gathering")
744		}
745		if c == nil {
746			answerCandidateDone = true
747		}
748
749		candidateLock.Lock()
750		defer candidateLock.Unlock()
751
752		cachedAnswerCandidates = addOrCacheCandidate(offerPC, c, cachedAnswerCandidates)
753	})
754
755	offerPCConnected, offerPCConnectedCancel := context.WithCancel(context.Background())
756	offerPC.OnICEConnectionStateChange(func(i ICEConnectionState) {
757		if i == ICEConnectionStateConnected {
758			offerPCConnectedCancel()
759		}
760	})
761
762	answerPCConnected, answerPCConnectedCancel := context.WithCancel(context.Background())
763	answerPC.OnICEConnectionStateChange(func(i ICEConnectionState) {
764		if i == ICEConnectionStateConnected {
765			answerPCConnectedCancel()
766		}
767	})
768
769	offer, err := offerPC.CreateOffer(nil)
770	assert.NoError(t, err)
771
772	assert.NoError(t, offerPC.SetLocalDescription(offer))
773	assert.NoError(t, answerPC.SetRemoteDescription(offer))
774
775	answer, err := answerPC.CreateAnswer(nil)
776	assert.NoError(t, err)
777
778	assert.NoError(t, answerPC.SetLocalDescription(answer))
779	assert.NoError(t, offerPC.SetRemoteDescription(answer))
780
781	candidateLock.Lock()
782	for _, c := range cachedAnswerCandidates {
783		assert.NoError(t, offerPC.AddICECandidate(c))
784	}
785	for _, c := range cachedOfferCandidates {
786		assert.NoError(t, answerPC.AddICECandidate(c))
787	}
788	candidateLock.Unlock()
789
790	<-answerPCConnected.Done()
791	<-offerPCConnected.Done()
792	closePairNow(t, offerPC, answerPC)
793}
794
795// Issue #1121, assert populateLocalCandidates doesn't mutate
796func TestPopulateLocalCandidates(t *testing.T) {
797	t.Run("PendingLocalDescription shouldn't add extra mutations", func(t *testing.T) {
798		pc, err := NewPeerConnection(Configuration{})
799		assert.NoError(t, err)
800
801		offer, err := pc.CreateOffer(nil)
802		assert.NoError(t, err)
803
804		offerGatheringComplete := GatheringCompletePromise(pc)
805		assert.NoError(t, pc.SetLocalDescription(offer))
806		<-offerGatheringComplete
807
808		assert.Equal(t, pc.PendingLocalDescription(), pc.PendingLocalDescription())
809		assert.NoError(t, pc.Close())
810	})
811
812	t.Run("end-of-candidates only when gathering is complete", func(t *testing.T) {
813		pc, err := NewAPI().NewPeerConnection(Configuration{})
814		assert.NoError(t, err)
815
816		_, err = pc.CreateDataChannel("test-channel", nil)
817		assert.NoError(t, err)
818
819		offer, err := pc.CreateOffer(nil)
820		assert.NoError(t, err)
821		assert.NotContains(t, offer.SDP, "a=candidate")
822		assert.NotContains(t, offer.SDP, "a=end-of-candidates")
823
824		offerGatheringComplete := GatheringCompletePromise(pc)
825		assert.NoError(t, pc.SetLocalDescription(offer))
826		<-offerGatheringComplete
827
828		assert.Contains(t, pc.PendingLocalDescription().SDP, "a=candidate")
829		assert.Contains(t, pc.PendingLocalDescription().SDP, "a=end-of-candidates")
830
831		assert.NoError(t, pc.Close())
832	})
833}
834
835// Assert that two agents that only generate mDNS candidates can connect
836func TestMulticastDNSCandidates(t *testing.T) {
837	lim := test.TimeOut(time.Second * 30)
838	defer lim.Stop()
839
840	report := test.CheckRoutines(t)
841	defer report()
842
843	s := SettingEngine{}
844	s.SetICEMulticastDNSMode(ice.MulticastDNSModeQueryAndGather)
845
846	pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(s)).newPair(Configuration{})
847	assert.NoError(t, err)
848
849	assert.NoError(t, signalPair(pcOffer, pcAnswer))
850
851	onDataChannel, onDataChannelCancel := context.WithCancel(context.Background())
852	pcAnswer.OnDataChannel(func(d *DataChannel) {
853		onDataChannelCancel()
854	})
855	<-onDataChannel.Done()
856
857	closePairNow(t, pcOffer, pcAnswer)
858}
859
860func TestICERestart(t *testing.T) {
861	extractCandidates := func(sdp string) (candidates []string) {
862		sc := bufio.NewScanner(strings.NewReader(sdp))
863		for sc.Scan() {
864			if strings.HasPrefix(sc.Text(), "a=candidate:") {
865				candidates = append(candidates, sc.Text())
866			}
867		}
868
869		return
870	}
871
872	lim := test.TimeOut(time.Second * 30)
873	defer lim.Stop()
874
875	report := test.CheckRoutines(t)
876	defer report()
877
878	offerPC, answerPC, err := newPair()
879	assert.NoError(t, err)
880
881	var connectedWaitGroup sync.WaitGroup
882	connectedWaitGroup.Add(2)
883
884	offerPC.OnICEConnectionStateChange(func(state ICEConnectionState) {
885		if state == ICEConnectionStateConnected {
886			connectedWaitGroup.Done()
887		}
888	})
889	answerPC.OnICEConnectionStateChange(func(state ICEConnectionState) {
890		if state == ICEConnectionStateConnected {
891			connectedWaitGroup.Done()
892		}
893	})
894
895	// Connect two PeerConnections and block until ICEConnectionStateConnected
896	assert.NoError(t, signalPair(offerPC, answerPC))
897	connectedWaitGroup.Wait()
898
899	// Store candidates from first Offer/Answer, compare later to make sure we re-gathered
900	firstOfferCandidates := extractCandidates(offerPC.LocalDescription().SDP)
901	firstAnswerCandidates := extractCandidates(answerPC.LocalDescription().SDP)
902
903	// Use Trickle ICE for ICE Restart
904	offerPC.OnICECandidate(func(c *ICECandidate) {
905		if c != nil {
906			assert.NoError(t, answerPC.AddICECandidate(c.ToJSON()))
907		}
908	})
909
910	answerPC.OnICECandidate(func(c *ICECandidate) {
911		if c != nil {
912			assert.NoError(t, offerPC.AddICECandidate(c.ToJSON()))
913		}
914	})
915
916	// Re-signal with ICE Restart, block until ICEConnectionStateConnected
917	connectedWaitGroup.Add(2)
918	offer, err := offerPC.CreateOffer(&OfferOptions{ICERestart: true})
919	assert.NoError(t, err)
920
921	assert.NoError(t, offerPC.SetLocalDescription(offer))
922	assert.NoError(t, answerPC.SetRemoteDescription(offer))
923
924	answer, err := answerPC.CreateAnswer(nil)
925	assert.NoError(t, err)
926
927	assert.NoError(t, answerPC.SetLocalDescription(answer))
928	assert.NoError(t, offerPC.SetRemoteDescription(answer))
929
930	// Block until we have connected again
931	connectedWaitGroup.Wait()
932
933	// Compare ICE Candidates across each run, fail if they haven't changed
934	assert.NotEqual(t, firstOfferCandidates, extractCandidates(offerPC.LocalDescription().SDP))
935	assert.NotEqual(t, firstAnswerCandidates, extractCandidates(answerPC.LocalDescription().SDP))
936	closePairNow(t, offerPC, answerPC)
937}
938
939// Assert error handling when an Agent is restart
940func TestICERestart_Error_Handling(t *testing.T) {
941	iceStates := make(chan ICEConnectionState, 100)
942	blockUntilICEState := func(wantedState ICEConnectionState) {
943		stateCount := 0
944		for i := range iceStates {
945			if i == wantedState {
946				stateCount++
947			}
948
949			if stateCount == 2 {
950				return
951			}
952		}
953	}
954
955	connectWithICERestart := func(offerPeerConnection, answerPeerConnection *PeerConnection) {
956		offer, err := offerPeerConnection.CreateOffer(&OfferOptions{ICERestart: true})
957		assert.NoError(t, err)
958
959		assert.NoError(t, offerPeerConnection.SetLocalDescription(offer))
960		assert.NoError(t, answerPeerConnection.SetRemoteDescription(*offerPeerConnection.LocalDescription()))
961
962		answer, err := answerPeerConnection.CreateAnswer(nil)
963		assert.NoError(t, err)
964
965		assert.NoError(t, answerPeerConnection.SetLocalDescription(answer))
966		assert.NoError(t, offerPeerConnection.SetRemoteDescription(*answerPeerConnection.LocalDescription()))
967	}
968
969	lim := test.TimeOut(time.Second * 30)
970	defer lim.Stop()
971
972	report := test.CheckRoutines(t)
973	defer report()
974
975	offerPeerConnection, answerPeerConnection, wan := createVNetPair(t)
976
977	pushICEState := func(i ICEConnectionState) { iceStates <- i }
978	offerPeerConnection.OnICEConnectionStateChange(pushICEState)
979	answerPeerConnection.OnICEConnectionStateChange(pushICEState)
980
981	keepPackets := &atomicBool{}
982	keepPackets.set(true)
983
984	// Add a filter that monitors the traffic on the router
985	wan.AddChunkFilter(func(c vnet.Chunk) bool {
986		return keepPackets.get()
987	})
988
989	const testMessage = "testMessage"
990
991	d, err := answerPeerConnection.CreateDataChannel("foo", nil)
992	assert.NoError(t, err)
993
994	dataChannelMessages := make(chan string, 100)
995	d.OnMessage(func(m DataChannelMessage) {
996		dataChannelMessages <- string(m.Data)
997	})
998
999	dataChannelAnswerer := make(chan *DataChannel)
1000	offerPeerConnection.OnDataChannel(func(d *DataChannel) {
1001		d.OnOpen(func() {
1002			dataChannelAnswerer <- d
1003		})
1004	})
1005
1006	// Connect and Assert we have connected
1007	assert.NoError(t, signalPair(offerPeerConnection, answerPeerConnection))
1008	blockUntilICEState(ICEConnectionStateConnected)
1009
1010	offerPeerConnection.OnICECandidate(func(c *ICECandidate) {
1011		if c != nil {
1012			assert.NoError(t, answerPeerConnection.AddICECandidate(c.ToJSON()))
1013		}
1014	})
1015
1016	answerPeerConnection.OnICECandidate(func(c *ICECandidate) {
1017		if c != nil {
1018			assert.NoError(t, offerPeerConnection.AddICECandidate(c.ToJSON()))
1019		}
1020	})
1021
1022	dataChannel := <-dataChannelAnswerer
1023	assert.NoError(t, dataChannel.SendText(testMessage))
1024	assert.Equal(t, testMessage, <-dataChannelMessages)
1025
1026	// Drop all packets, assert we have disconnected
1027	// and send a DataChannel message when disconnected
1028	keepPackets.set(false)
1029	blockUntilICEState(ICEConnectionStateFailed)
1030	assert.NoError(t, dataChannel.SendText(testMessage))
1031
1032	// ICE Restart and assert we have reconnected
1033	// block until our DataChannel message is delivered
1034	keepPackets.set(true)
1035	connectWithICERestart(offerPeerConnection, answerPeerConnection)
1036	blockUntilICEState(ICEConnectionStateConnected)
1037	assert.Equal(t, testMessage, <-dataChannelMessages)
1038
1039	assert.NoError(t, wan.Stop())
1040	closePairNow(t, offerPeerConnection, answerPeerConnection)
1041}
1042
1043type trackRecords struct {
1044	mu               sync.Mutex
1045	trackIDs         map[string]struct{}
1046	receivedTrackIDs map[string]struct{}
1047}
1048
1049func (r *trackRecords) newTrack() (*TrackLocalStaticRTP, error) {
1050	trackID := fmt.Sprintf("pion-track-%d", len(r.trackIDs))
1051	track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: "video/vp8"}, trackID, "pion")
1052	r.trackIDs[trackID] = struct{}{}
1053	return track, err
1054}
1055
1056func (r *trackRecords) handleTrack(t *TrackRemote, _ *RTPReceiver) {
1057	r.mu.Lock()
1058	defer r.mu.Unlock()
1059	tID := t.ID()
1060	if _, exist := r.trackIDs[tID]; exist {
1061		r.receivedTrackIDs[tID] = struct{}{}
1062	}
1063}
1064
1065func (r *trackRecords) remains() int {
1066	r.mu.Lock()
1067	defer r.mu.Unlock()
1068	return len(r.trackIDs) - len(r.receivedTrackIDs)
1069}
1070
1071// This test assure that all track events emits.
1072func TestPeerConnection_MassiveTracks(t *testing.T) {
1073	var (
1074		api   = NewAPI()
1075		tRecs = &trackRecords{
1076			trackIDs:         make(map[string]struct{}),
1077			receivedTrackIDs: make(map[string]struct{}),
1078		}
1079		tracks          = []*TrackLocalStaticRTP{}
1080		trackCount      = 256
1081		pingInterval    = 1 * time.Second
1082		noiseInterval   = 100 * time.Microsecond
1083		timeoutDuration = 20 * time.Second
1084		rawPkt          = []byte{
1085			0x90, 0xe0, 0x69, 0x8f, 0xd9, 0xc2, 0x93, 0xda, 0x1c, 0x64,
1086			0x27, 0x82, 0x00, 0x01, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x98, 0x36, 0xbe, 0x88, 0x9e,
1087		}
1088		samplePkt = &rtp.Packet{
1089			Header: rtp.Header{
1090				Marker:           true,
1091				Extension:        false,
1092				ExtensionProfile: 1,
1093				Version:          2,
1094				PayloadOffset:    20,
1095				SequenceNumber:   27023,
1096				Timestamp:        3653407706,
1097				CSRC:             []uint32{},
1098			},
1099			Payload: rawPkt[20:],
1100		}
1101		connected = make(chan struct{})
1102		stopped   = make(chan struct{})
1103	)
1104	assert.NoError(t, api.mediaEngine.RegisterDefaultCodecs())
1105	offerPC, answerPC, err := api.newPair(Configuration{})
1106	assert.NoError(t, err)
1107	// Create massive tracks.
1108	for range make([]struct{}, trackCount) {
1109		track, err := tRecs.newTrack()
1110		assert.NoError(t, err)
1111		_, err = offerPC.AddTrack(track)
1112		assert.NoError(t, err)
1113		tracks = append(tracks, track)
1114	}
1115	answerPC.OnTrack(tRecs.handleTrack)
1116	offerPC.OnICEConnectionStateChange(func(s ICEConnectionState) {
1117		if s == ICEConnectionStateConnected {
1118			close(connected)
1119		}
1120	})
1121	// A routine to periodically call GetTransceivers. This action might cause
1122	// the deadlock and prevent track event to emit.
1123	go func() {
1124		for {
1125			answerPC.GetTransceivers()
1126			time.Sleep(noiseInterval)
1127			select {
1128			case <-stopped:
1129				return
1130			default:
1131			}
1132		}
1133	}()
1134	assert.NoError(t, signalPair(offerPC, answerPC))
1135	// Send a RTP packets to each track to trigger track event after connected.
1136	<-connected
1137	time.Sleep(1 * time.Second)
1138	for _, track := range tracks {
1139		assert.NoError(t, track.WriteRTP(samplePkt))
1140	}
1141	// Ping trackRecords to see if any track event not received yet.
1142	tooLong := time.After(timeoutDuration)
1143	for {
1144		remains := tRecs.remains()
1145		if remains == 0 {
1146			break
1147		}
1148		t.Log("remain tracks", remains)
1149		time.Sleep(pingInterval)
1150		select {
1151		case <-tooLong:
1152			t.Error("unable to receive all track events in time")
1153		default:
1154		}
1155	}
1156	close(stopped)
1157	closePairNow(t, offerPC, answerPC)
1158}
1159
1160func TestEmptyCandidate(t *testing.T) {
1161	testCases := []struct {
1162		ICECandidate ICECandidateInit
1163		expectError  bool
1164	}{
1165		{ICECandidateInit{"", nil, nil, nil}, false},
1166		{ICECandidateInit{
1167			"211962667 1 udp 2122194687 10.0.3.1 40864 typ host generation 0",
1168			nil, nil, nil,
1169		}, false},
1170		{ICECandidateInit{
1171			"1234567",
1172			nil, nil, nil,
1173		}, true},
1174	}
1175
1176	for i, testCase := range testCases {
1177		peerConn, err := NewPeerConnection(Configuration{})
1178		if err != nil {
1179			t.Errorf("Case %d: got error: %v", i, err)
1180		}
1181
1182		err = peerConn.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: minimalOffer})
1183		if err != nil {
1184			t.Errorf("Case %d: got error: %v", i, err)
1185		}
1186
1187		if testCase.expectError {
1188			assert.Error(t, peerConn.AddICECandidate(testCase.ICECandidate))
1189		} else {
1190			assert.NoError(t, peerConn.AddICECandidate(testCase.ICECandidate))
1191		}
1192
1193		assert.NoError(t, peerConn.Close())
1194	}
1195}
1196
1197const liteOffer = `v=0
1198o=- 4596489990601351948 2 IN IP4 127.0.0.1
1199s=-
1200t=0 0
1201a=msid-semantic: WMS
1202a=ice-lite
1203m=application 47299 DTLS/SCTP 5000
1204c=IN IP4 192.168.20.129
1205a=ice-ufrag:1/MvHwjAyVf27aLu
1206a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
1207a=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
1208a=mid:data
1209`
1210
1211// this test asserts that if an ice-lite offer is received,
1212// pion will take the ICE-CONTROLLING role
1213func TestICELite(t *testing.T) {
1214	peerConnection, err := NewPeerConnection(Configuration{})
1215	assert.NoError(t, err)
1216
1217	assert.NoError(t, peerConnection.SetRemoteDescription(
1218		SessionDescription{SDP: liteOffer, Type: SDPTypeOffer},
1219	))
1220
1221	SDPAnswer, err := peerConnection.CreateAnswer(nil)
1222	assert.NoError(t, err)
1223
1224	assert.NoError(t, peerConnection.SetLocalDescription(SDPAnswer))
1225
1226	assert.Equal(t, ICERoleControlling, peerConnection.iceTransport.role,
1227		"pion did not set state to ICE-CONTROLLED against ice-light offer")
1228
1229	assert.NoError(t, peerConnection.Close())
1230}
1231
1232func TestPeerConnection_TransceiverDirection(t *testing.T) {
1233	lim := test.TimeOut(time.Second * 30)
1234	defer lim.Stop()
1235
1236	report := test.CheckRoutines(t)
1237	defer report()
1238
1239	createTransceiver := func(pc *PeerConnection, dir RTPTransceiverDirection) error {
1240		// AddTransceiverFromKind() can't be used with sendonly
1241		if dir == RTPTransceiverDirectionSendonly {
1242			codecs := pc.api.mediaEngine.getCodecsByKind(RTPCodecTypeVideo)
1243
1244			track, err := NewTrackLocalStaticSample(codecs[0].RTPCodecCapability, util.MathRandAlpha(16), util.MathRandAlpha(16))
1245			if err != nil {
1246				return err
1247			}
1248
1249			_, err = pc.AddTransceiverFromTrack(track, []RtpTransceiverInit{
1250				{Direction: dir},
1251			}...)
1252			return err
1253		}
1254
1255		_, err := pc.AddTransceiverFromKind(
1256			RTPCodecTypeVideo,
1257			RtpTransceiverInit{Direction: dir},
1258		)
1259		return err
1260	}
1261
1262	for _, test := range []struct {
1263		name                  string
1264		offerDirection        RTPTransceiverDirection
1265		answerStartDirection  RTPTransceiverDirection
1266		answerFinalDirections []RTPTransceiverDirection
1267	}{
1268		{
1269			"offer sendrecv answer sendrecv",
1270			RTPTransceiverDirectionSendrecv,
1271			RTPTransceiverDirectionSendrecv,
1272			[]RTPTransceiverDirection{RTPTransceiverDirectionSendrecv},
1273		},
1274		{
1275			"offer sendonly answer sendrecv",
1276			RTPTransceiverDirectionSendonly,
1277			RTPTransceiverDirectionSendrecv,
1278			[]RTPTransceiverDirection{RTPTransceiverDirectionSendrecv, RTPTransceiverDirectionRecvonly},
1279		},
1280		{
1281			"offer recvonly answer sendrecv",
1282			RTPTransceiverDirectionRecvonly,
1283			RTPTransceiverDirectionSendrecv,
1284			[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly},
1285		},
1286		{
1287			"offer sendrecv answer sendonly",
1288			RTPTransceiverDirectionSendrecv,
1289			RTPTransceiverDirectionSendonly,
1290			[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionRecvonly},
1291		},
1292		{
1293			"offer sendonly answer sendonly",
1294			RTPTransceiverDirectionSendonly,
1295			RTPTransceiverDirectionSendonly,
1296			[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionRecvonly},
1297		},
1298		{
1299			"offer recvonly answer sendonly",
1300			RTPTransceiverDirectionRecvonly,
1301			RTPTransceiverDirectionSendonly,
1302			[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly},
1303		},
1304		{
1305			"offer sendrecv answer recvonly",
1306			RTPTransceiverDirectionSendrecv,
1307			RTPTransceiverDirectionRecvonly,
1308			[]RTPTransceiverDirection{RTPTransceiverDirectionRecvonly},
1309		},
1310		{
1311			"offer sendonly answer recvonly",
1312			RTPTransceiverDirectionSendonly,
1313			RTPTransceiverDirectionRecvonly,
1314			[]RTPTransceiverDirection{RTPTransceiverDirectionRecvonly},
1315		},
1316		{
1317			"offer recvonly answer recvonly",
1318			RTPTransceiverDirectionRecvonly,
1319			RTPTransceiverDirectionRecvonly,
1320			[]RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendonly},
1321		},
1322	} {
1323		offerDirection := test.offerDirection
1324		answerStartDirection := test.answerStartDirection
1325		answerFinalDirections := test.answerFinalDirections
1326
1327		t.Run(test.name, func(t *testing.T) {
1328			pcOffer, pcAnswer, err := newPair()
1329			assert.NoError(t, err)
1330
1331			err = createTransceiver(pcOffer, offerDirection)
1332			assert.NoError(t, err)
1333
1334			offer, err := pcOffer.CreateOffer(nil)
1335			assert.NoError(t, err)
1336
1337			err = createTransceiver(pcAnswer, answerStartDirection)
1338			assert.NoError(t, err)
1339
1340			assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
1341
1342			assert.Equal(t, len(answerFinalDirections), len(pcAnswer.GetTransceivers()))
1343
1344			for i, tr := range pcAnswer.GetTransceivers() {
1345				assert.Equal(t, answerFinalDirections[i], tr.Direction())
1346			}
1347
1348			assert.NoError(t, pcOffer.Close())
1349			assert.NoError(t, pcAnswer.Close())
1350		})
1351	}
1352}
1353