github.com/pion/webrtc/v3@v3.2.24/peerconnection_go_test.go (about)

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