github.com/pion/webrtc/v4@v4.0.1/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/v4"
    26  	"github.com/pion/rtp"
    27  	"github.com/pion/transport/v3/test"
    28  	"github.com/pion/transport/v3/vnet"
    29  	"github.com/pion/webrtc/v4/internal/util"
    30  	"github.com/pion/webrtc/v4/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(*TrackRemote, *RTPReceiver) {
   310  		close(onTrackCalled)
   311  	})
   312  
   313  	pc.OnICEConnectionStateChange(func(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(*testing.T) {
   605  		connectTwoAgents(true, false)
   606  	})
   607  
   608  	t.Run("Answerer", func(*testing.T) {
   609  		connectTwoAgents(false, true)
   610  	})
   611  
   612  	t.Run("Both", func(*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  
   623  	peerConn, err := NewPeerConnection(Configuration{})
   624  	assert.NoError(t, err)
   625  
   626  	var onStateChange func(s ICEGatheringState)
   627  	onStateChange = func(s ICEGatheringState) {
   628  		// Access to ICEGatherer in the callback must not cause dead lock.
   629  		peerConn.OnICEGatheringStateChange(onStateChange)
   630  
   631  		switch s { // nolint:exhaustive
   632  		case ICEGatheringStateGathering:
   633  			if seenComplete.get() {
   634  				t.Error("Completed before gathering")
   635  			}
   636  			seenGathering.set(true)
   637  		case ICEGatheringStateComplete:
   638  			seenComplete.set(true)
   639  		}
   640  
   641  		if seenGathering.get() && seenComplete.get() {
   642  			close(seenGatheringAndComplete)
   643  		}
   644  	}
   645  	peerConn.OnICEGatheringStateChange(onStateChange)
   646  
   647  	offer, err := peerConn.CreateOffer(nil)
   648  	assert.NoError(t, err)
   649  	assert.NoError(t, peerConn.SetLocalDescription(offer))
   650  
   651  	select {
   652  	case <-time.After(time.Second * 10):
   653  		t.Fatal("Gathering and Complete were never seen")
   654  	case <-seenGatheringAndComplete:
   655  	}
   656  
   657  	assert.NoError(t, peerConn.Close())
   658  }
   659  
   660  // Assert Trickle ICE behaviors
   661  func TestPeerConnectionTrickle(t *testing.T) {
   662  	offerPC, answerPC, err := newPair()
   663  	assert.NoError(t, err)
   664  
   665  	_, err = offerPC.CreateDataChannel("test-channel", nil)
   666  	assert.NoError(t, err)
   667  
   668  	addOrCacheCandidate := func(pc *PeerConnection, c *ICECandidate, candidateCache []ICECandidateInit) []ICECandidateInit {
   669  		if c == nil {
   670  			return candidateCache
   671  		}
   672  
   673  		if pc.RemoteDescription() == nil {
   674  			return append(candidateCache, c.ToJSON())
   675  		}
   676  
   677  		assert.NoError(t, pc.AddICECandidate(c.ToJSON()))
   678  		return candidateCache
   679  	}
   680  
   681  	candidateLock := sync.RWMutex{}
   682  	var offerCandidateDone, answerCandidateDone bool
   683  
   684  	cachedOfferCandidates := []ICECandidateInit{}
   685  	offerPC.OnICECandidate(func(c *ICECandidate) {
   686  		if offerCandidateDone {
   687  			t.Error("Received OnICECandidate after finishing gathering")
   688  		}
   689  		if c == nil {
   690  			offerCandidateDone = true
   691  		}
   692  
   693  		candidateLock.Lock()
   694  		defer candidateLock.Unlock()
   695  
   696  		cachedOfferCandidates = addOrCacheCandidate(answerPC, c, cachedOfferCandidates)
   697  	})
   698  
   699  	cachedAnswerCandidates := []ICECandidateInit{}
   700  	answerPC.OnICECandidate(func(c *ICECandidate) {
   701  		if answerCandidateDone {
   702  			t.Error("Received OnICECandidate after finishing gathering")
   703  		}
   704  		if c == nil {
   705  			answerCandidateDone = true
   706  		}
   707  
   708  		candidateLock.Lock()
   709  		defer candidateLock.Unlock()
   710  
   711  		cachedAnswerCandidates = addOrCacheCandidate(offerPC, c, cachedAnswerCandidates)
   712  	})
   713  
   714  	offerPCConnected, offerPCConnectedCancel := context.WithCancel(context.Background())
   715  	offerPC.OnICEConnectionStateChange(func(i ICEConnectionState) {
   716  		if i == ICEConnectionStateConnected {
   717  			offerPCConnectedCancel()
   718  		}
   719  	})
   720  
   721  	answerPCConnected, answerPCConnectedCancel := context.WithCancel(context.Background())
   722  	answerPC.OnICEConnectionStateChange(func(i ICEConnectionState) {
   723  		if i == ICEConnectionStateConnected {
   724  			answerPCConnectedCancel()
   725  		}
   726  	})
   727  
   728  	offer, err := offerPC.CreateOffer(nil)
   729  	assert.NoError(t, err)
   730  
   731  	assert.NoError(t, offerPC.SetLocalDescription(offer))
   732  	assert.NoError(t, answerPC.SetRemoteDescription(offer))
   733  
   734  	answer, err := answerPC.CreateAnswer(nil)
   735  	assert.NoError(t, err)
   736  
   737  	assert.NoError(t, answerPC.SetLocalDescription(answer))
   738  	assert.NoError(t, offerPC.SetRemoteDescription(answer))
   739  
   740  	candidateLock.Lock()
   741  	for _, c := range cachedAnswerCandidates {
   742  		assert.NoError(t, offerPC.AddICECandidate(c))
   743  	}
   744  	for _, c := range cachedOfferCandidates {
   745  		assert.NoError(t, answerPC.AddICECandidate(c))
   746  	}
   747  	candidateLock.Unlock()
   748  
   749  	<-answerPCConnected.Done()
   750  	<-offerPCConnected.Done()
   751  	closePairNow(t, offerPC, answerPC)
   752  }
   753  
   754  // Issue #1121, assert populateLocalCandidates doesn't mutate
   755  func TestPopulateLocalCandidates(t *testing.T) {
   756  	t.Run("PendingLocalDescription shouldn't add extra mutations", func(t *testing.T) {
   757  		pc, err := NewPeerConnection(Configuration{})
   758  		assert.NoError(t, err)
   759  
   760  		offer, err := pc.CreateOffer(nil)
   761  		assert.NoError(t, err)
   762  
   763  		offerGatheringComplete := GatheringCompletePromise(pc)
   764  		assert.NoError(t, pc.SetLocalDescription(offer))
   765  		<-offerGatheringComplete
   766  
   767  		assert.Equal(t, pc.PendingLocalDescription(), pc.PendingLocalDescription())
   768  		assert.NoError(t, pc.Close())
   769  	})
   770  
   771  	t.Run("end-of-candidates only when gathering is complete", func(t *testing.T) {
   772  		pc, err := NewAPI().NewPeerConnection(Configuration{})
   773  		assert.NoError(t, err)
   774  
   775  		_, err = pc.CreateDataChannel("test-channel", nil)
   776  		assert.NoError(t, err)
   777  
   778  		offer, err := pc.CreateOffer(nil)
   779  		assert.NoError(t, err)
   780  		assert.NotContains(t, offer.SDP, "a=candidate")
   781  		assert.NotContains(t, offer.SDP, "a=end-of-candidates")
   782  
   783  		offerGatheringComplete := GatheringCompletePromise(pc)
   784  		assert.NoError(t, pc.SetLocalDescription(offer))
   785  		<-offerGatheringComplete
   786  
   787  		assert.Contains(t, pc.PendingLocalDescription().SDP, "a=candidate")
   788  		assert.Contains(t, pc.PendingLocalDescription().SDP, "a=end-of-candidates")
   789  
   790  		assert.NoError(t, pc.Close())
   791  	})
   792  }
   793  
   794  // Assert that two agents that only generate mDNS candidates can connect
   795  func TestMulticastDNSCandidates(t *testing.T) {
   796  	lim := test.TimeOut(time.Second * 30)
   797  	defer lim.Stop()
   798  
   799  	report := test.CheckRoutines(t)
   800  	defer report()
   801  
   802  	s := SettingEngine{}
   803  	s.SetICEMulticastDNSMode(ice.MulticastDNSModeQueryAndGather)
   804  
   805  	pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(s)).newPair(Configuration{})
   806  	assert.NoError(t, err)
   807  
   808  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   809  
   810  	onDataChannel, onDataChannelCancel := context.WithCancel(context.Background())
   811  	pcAnswer.OnDataChannel(func(*DataChannel) {
   812  		onDataChannelCancel()
   813  	})
   814  	<-onDataChannel.Done()
   815  
   816  	closePairNow(t, pcOffer, pcAnswer)
   817  }
   818  
   819  func TestICERestart(t *testing.T) {
   820  	extractCandidates := func(sdp string) (candidates []string) {
   821  		sc := bufio.NewScanner(strings.NewReader(sdp))
   822  		for sc.Scan() {
   823  			if strings.HasPrefix(sc.Text(), "a=candidate:") {
   824  				candidates = append(candidates, sc.Text())
   825  			}
   826  		}
   827  
   828  		return
   829  	}
   830  
   831  	lim := test.TimeOut(time.Second * 30)
   832  	defer lim.Stop()
   833  
   834  	report := test.CheckRoutines(t)
   835  	defer report()
   836  
   837  	offerPC, answerPC, err := newPair()
   838  	assert.NoError(t, err)
   839  
   840  	var connectedWaitGroup sync.WaitGroup
   841  	connectedWaitGroup.Add(2)
   842  
   843  	offerPC.OnICEConnectionStateChange(func(state ICEConnectionState) {
   844  		if state == ICEConnectionStateConnected {
   845  			connectedWaitGroup.Done()
   846  		}
   847  	})
   848  	answerPC.OnICEConnectionStateChange(func(state ICEConnectionState) {
   849  		if state == ICEConnectionStateConnected {
   850  			connectedWaitGroup.Done()
   851  		}
   852  	})
   853  
   854  	// Connect two PeerConnections and block until ICEConnectionStateConnected
   855  	assert.NoError(t, signalPair(offerPC, answerPC))
   856  	connectedWaitGroup.Wait()
   857  
   858  	// Store candidates from first Offer/Answer, compare later to make sure we re-gathered
   859  	firstOfferCandidates := extractCandidates(offerPC.LocalDescription().SDP)
   860  	firstAnswerCandidates := extractCandidates(answerPC.LocalDescription().SDP)
   861  
   862  	// Use Trickle ICE for ICE Restart
   863  	offerPC.OnICECandidate(func(c *ICECandidate) {
   864  		if c != nil {
   865  			assert.NoError(t, answerPC.AddICECandidate(c.ToJSON()))
   866  		}
   867  	})
   868  
   869  	answerPC.OnICECandidate(func(c *ICECandidate) {
   870  		if c != nil {
   871  			assert.NoError(t, offerPC.AddICECandidate(c.ToJSON()))
   872  		}
   873  	})
   874  
   875  	// Re-signal with ICE Restart, block until ICEConnectionStateConnected
   876  	connectedWaitGroup.Add(2)
   877  	offer, err := offerPC.CreateOffer(&OfferOptions{ICERestart: true})
   878  	assert.NoError(t, err)
   879  
   880  	assert.NoError(t, offerPC.SetLocalDescription(offer))
   881  	assert.NoError(t, answerPC.SetRemoteDescription(offer))
   882  
   883  	answer, err := answerPC.CreateAnswer(nil)
   884  	assert.NoError(t, err)
   885  
   886  	assert.NoError(t, answerPC.SetLocalDescription(answer))
   887  	assert.NoError(t, offerPC.SetRemoteDescription(answer))
   888  
   889  	// Block until we have connected again
   890  	connectedWaitGroup.Wait()
   891  
   892  	// Compare ICE Candidates across each run, fail if they haven't changed
   893  	assert.NotEqual(t, firstOfferCandidates, extractCandidates(offerPC.LocalDescription().SDP))
   894  	assert.NotEqual(t, firstAnswerCandidates, extractCandidates(answerPC.LocalDescription().SDP))
   895  	closePairNow(t, offerPC, answerPC)
   896  }
   897  
   898  // Assert error handling when an Agent is restart
   899  func TestICERestart_Error_Handling(t *testing.T) {
   900  	iceStates := make(chan ICEConnectionState, 100)
   901  	blockUntilICEState := func(wantedState ICEConnectionState) {
   902  		stateCount := 0
   903  		for i := range iceStates {
   904  			if i == wantedState {
   905  				stateCount++
   906  			}
   907  
   908  			if stateCount == 2 {
   909  				return
   910  			}
   911  		}
   912  	}
   913  
   914  	connectWithICERestart := func(offerPeerConnection, answerPeerConnection *PeerConnection) {
   915  		offer, err := offerPeerConnection.CreateOffer(&OfferOptions{ICERestart: true})
   916  		assert.NoError(t, err)
   917  
   918  		assert.NoError(t, offerPeerConnection.SetLocalDescription(offer))
   919  		assert.NoError(t, answerPeerConnection.SetRemoteDescription(*offerPeerConnection.LocalDescription()))
   920  
   921  		answer, err := answerPeerConnection.CreateAnswer(nil)
   922  		assert.NoError(t, err)
   923  
   924  		assert.NoError(t, answerPeerConnection.SetLocalDescription(answer))
   925  		assert.NoError(t, offerPeerConnection.SetRemoteDescription(*answerPeerConnection.LocalDescription()))
   926  	}
   927  
   928  	lim := test.TimeOut(time.Second * 30)
   929  	defer lim.Stop()
   930  
   931  	report := test.CheckRoutines(t)
   932  	defer report()
   933  
   934  	offerPeerConnection, answerPeerConnection, wan := createVNetPair(t, nil)
   935  
   936  	pushICEState := func(i ICEConnectionState) { iceStates <- i }
   937  	offerPeerConnection.OnICEConnectionStateChange(pushICEState)
   938  	answerPeerConnection.OnICEConnectionStateChange(pushICEState)
   939  
   940  	keepPackets := &atomicBool{}
   941  	keepPackets.set(true)
   942  
   943  	// Add a filter that monitors the traffic on the router
   944  	wan.AddChunkFilter(func(vnet.Chunk) bool {
   945  		return keepPackets.get()
   946  	})
   947  
   948  	const testMessage = "testMessage"
   949  
   950  	d, err := answerPeerConnection.CreateDataChannel("foo", nil)
   951  	assert.NoError(t, err)
   952  
   953  	dataChannelMessages := make(chan string, 100)
   954  	d.OnMessage(func(m DataChannelMessage) {
   955  		dataChannelMessages <- string(m.Data)
   956  	})
   957  
   958  	dataChannelAnswerer := make(chan *DataChannel)
   959  	offerPeerConnection.OnDataChannel(func(d *DataChannel) {
   960  		d.OnOpen(func() {
   961  			dataChannelAnswerer <- d
   962  		})
   963  	})
   964  
   965  	// Connect and Assert we have connected
   966  	assert.NoError(t, signalPair(offerPeerConnection, answerPeerConnection))
   967  	blockUntilICEState(ICEConnectionStateConnected)
   968  
   969  	offerPeerConnection.OnICECandidate(func(c *ICECandidate) {
   970  		if c != nil {
   971  			assert.NoError(t, answerPeerConnection.AddICECandidate(c.ToJSON()))
   972  		}
   973  	})
   974  
   975  	answerPeerConnection.OnICECandidate(func(c *ICECandidate) {
   976  		if c != nil {
   977  			assert.NoError(t, offerPeerConnection.AddICECandidate(c.ToJSON()))
   978  		}
   979  	})
   980  
   981  	dataChannel := <-dataChannelAnswerer
   982  	assert.NoError(t, dataChannel.SendText(testMessage))
   983  	assert.Equal(t, testMessage, <-dataChannelMessages)
   984  
   985  	// Drop all packets, assert we have disconnected
   986  	// and send a DataChannel message when disconnected
   987  	keepPackets.set(false)
   988  	blockUntilICEState(ICEConnectionStateFailed)
   989  	assert.NoError(t, dataChannel.SendText(testMessage))
   990  
   991  	// ICE Restart and assert we have reconnected
   992  	// block until our DataChannel message is delivered
   993  	keepPackets.set(true)
   994  	connectWithICERestart(offerPeerConnection, answerPeerConnection)
   995  	blockUntilICEState(ICEConnectionStateConnected)
   996  	assert.Equal(t, testMessage, <-dataChannelMessages)
   997  
   998  	assert.NoError(t, wan.Stop())
   999  	closePairNow(t, offerPeerConnection, answerPeerConnection)
  1000  }
  1001  
  1002  type trackRecords struct {
  1003  	mu               sync.Mutex
  1004  	trackIDs         map[string]struct{}
  1005  	receivedTrackIDs map[string]struct{}
  1006  }
  1007  
  1008  func (r *trackRecords) newTrack() (*TrackLocalStaticRTP, error) {
  1009  	trackID := fmt.Sprintf("pion-track-%d", len(r.trackIDs))
  1010  	track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, trackID, "pion")
  1011  	r.trackIDs[trackID] = struct{}{}
  1012  	return track, err
  1013  }
  1014  
  1015  func (r *trackRecords) handleTrack(t *TrackRemote, _ *RTPReceiver) {
  1016  	r.mu.Lock()
  1017  	defer r.mu.Unlock()
  1018  	tID := t.ID()
  1019  	if _, exist := r.trackIDs[tID]; exist {
  1020  		r.receivedTrackIDs[tID] = struct{}{}
  1021  	}
  1022  }
  1023  
  1024  func (r *trackRecords) remains() int {
  1025  	r.mu.Lock()
  1026  	defer r.mu.Unlock()
  1027  	return len(r.trackIDs) - len(r.receivedTrackIDs)
  1028  }
  1029  
  1030  // This test assure that all track events emits.
  1031  func TestPeerConnection_MassiveTracks(t *testing.T) {
  1032  	var (
  1033  		tRecs = &trackRecords{
  1034  			trackIDs:         make(map[string]struct{}),
  1035  			receivedTrackIDs: make(map[string]struct{}),
  1036  		}
  1037  		tracks          = []*TrackLocalStaticRTP{}
  1038  		trackCount      = 256
  1039  		pingInterval    = 1 * time.Second
  1040  		noiseInterval   = 100 * time.Microsecond
  1041  		timeoutDuration = 20 * time.Second
  1042  		rawPkt          = []byte{
  1043  			0x90, 0xe0, 0x69, 0x8f, 0xd9, 0xc2, 0x93, 0xda, 0x1c, 0x64,
  1044  			0x27, 0x82, 0x00, 0x01, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x98, 0x36, 0xbe, 0x88, 0x9e,
  1045  		}
  1046  		samplePkt = &rtp.Packet{
  1047  			Header: rtp.Header{
  1048  				Marker:           true,
  1049  				Extension:        false,
  1050  				ExtensionProfile: 1,
  1051  				Version:          2,
  1052  				SequenceNumber:   27023,
  1053  				Timestamp:        3653407706,
  1054  				CSRC:             []uint32{},
  1055  			},
  1056  			Payload: rawPkt[20:],
  1057  		}
  1058  		connected = make(chan struct{})
  1059  		stopped   = make(chan struct{})
  1060  	)
  1061  	offerPC, answerPC, err := newPair()
  1062  	assert.NoError(t, err)
  1063  	// Create massive tracks.
  1064  	for range make([]struct{}, trackCount) {
  1065  		track, err := tRecs.newTrack()
  1066  		assert.NoError(t, err)
  1067  		_, err = offerPC.AddTrack(track)
  1068  		assert.NoError(t, err)
  1069  		tracks = append(tracks, track)
  1070  	}
  1071  	answerPC.OnTrack(tRecs.handleTrack)
  1072  	offerPC.OnICEConnectionStateChange(func(s ICEConnectionState) {
  1073  		if s == ICEConnectionStateConnected {
  1074  			close(connected)
  1075  		}
  1076  	})
  1077  	// A routine to periodically call GetTransceivers. This action might cause
  1078  	// the deadlock and prevent track event to emit.
  1079  	go func() {
  1080  		for {
  1081  			answerPC.GetTransceivers()
  1082  			time.Sleep(noiseInterval)
  1083  			select {
  1084  			case <-stopped:
  1085  				return
  1086  			default:
  1087  			}
  1088  		}
  1089  	}()
  1090  	assert.NoError(t, signalPair(offerPC, answerPC))
  1091  	// Send a RTP packets to each track to trigger track event after connected.
  1092  	<-connected
  1093  	time.Sleep(1 * time.Second)
  1094  	for _, track := range tracks {
  1095  		assert.NoError(t, track.WriteRTP(samplePkt))
  1096  	}
  1097  	// Ping trackRecords to see if any track event not received yet.
  1098  	tooLong := time.After(timeoutDuration)
  1099  	for {
  1100  		remains := tRecs.remains()
  1101  		if remains == 0 {
  1102  			break
  1103  		}
  1104  		t.Log("remain tracks", remains)
  1105  		time.Sleep(pingInterval)
  1106  		select {
  1107  		case <-tooLong:
  1108  			t.Error("unable to receive all track events in time")
  1109  		default:
  1110  		}
  1111  	}
  1112  	close(stopped)
  1113  	closePairNow(t, offerPC, answerPC)
  1114  }
  1115  
  1116  func TestEmptyCandidate(t *testing.T) {
  1117  	testCases := []struct {
  1118  		ICECandidate ICECandidateInit
  1119  		expectError  bool
  1120  	}{
  1121  		{ICECandidateInit{"", nil, nil, nil}, false},
  1122  		{ICECandidateInit{
  1123  			"211962667 1 udp 2122194687 10.0.3.1 40864 typ host generation 0",
  1124  			nil, nil, nil,
  1125  		}, false},
  1126  		{ICECandidateInit{
  1127  			"1234567",
  1128  			nil, nil, nil,
  1129  		}, true},
  1130  	}
  1131  
  1132  	for i, testCase := range testCases {
  1133  		peerConn, err := NewPeerConnection(Configuration{})
  1134  		if err != nil {
  1135  			t.Errorf("Case %d: got error: %v", i, err)
  1136  		}
  1137  
  1138  		err = peerConn.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: minimalOffer})
  1139  		if err != nil {
  1140  			t.Errorf("Case %d: got error: %v", i, err)
  1141  		}
  1142  
  1143  		if testCase.expectError {
  1144  			assert.Error(t, peerConn.AddICECandidate(testCase.ICECandidate))
  1145  		} else {
  1146  			assert.NoError(t, peerConn.AddICECandidate(testCase.ICECandidate))
  1147  		}
  1148  
  1149  		assert.NoError(t, peerConn.Close())
  1150  	}
  1151  }
  1152  
  1153  const liteOffer = `v=0
  1154  o=- 4596489990601351948 2 IN IP4 127.0.0.1
  1155  s=-
  1156  t=0 0
  1157  a=msid-semantic: WMS
  1158  a=ice-lite
  1159  m=application 47299 DTLS/SCTP 5000
  1160  c=IN IP4 192.168.20.129
  1161  a=ice-ufrag:1/MvHwjAyVf27aLu
  1162  a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
  1163  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
  1164  a=mid:data
  1165  `
  1166  
  1167  // this test asserts that if an ice-lite offer is received,
  1168  // pion will take the ICE-CONTROLLING role
  1169  func TestICELite(t *testing.T) {
  1170  	peerConnection, err := NewPeerConnection(Configuration{})
  1171  	assert.NoError(t, err)
  1172  
  1173  	assert.NoError(t, peerConnection.SetRemoteDescription(
  1174  		SessionDescription{SDP: liteOffer, Type: SDPTypeOffer},
  1175  	))
  1176  
  1177  	SDPAnswer, err := peerConnection.CreateAnswer(nil)
  1178  	assert.NoError(t, err)
  1179  
  1180  	assert.NoError(t, peerConnection.SetLocalDescription(SDPAnswer))
  1181  
  1182  	assert.Equal(t, ICERoleControlling, peerConnection.iceTransport.Role(),
  1183  		"pion did not set state to ICE-CONTROLLED against ice-light offer")
  1184  
  1185  	assert.NoError(t, peerConnection.Close())
  1186  }
  1187  
  1188  func TestPeerConnection_TransceiverDirection(t *testing.T) {
  1189  	lim := test.TimeOut(time.Second * 30)
  1190  	defer lim.Stop()
  1191  
  1192  	report := test.CheckRoutines(t)
  1193  	defer report()
  1194  
  1195  	createTransceiver := func(pc *PeerConnection, dir RTPTransceiverDirection) error {
  1196  		// AddTransceiverFromKind() can't be used with sendonly
  1197  		if dir == RTPTransceiverDirectionSendonly {
  1198  			codecs := pc.api.mediaEngine.getCodecsByKind(RTPCodecTypeVideo)
  1199  
  1200  			track, err := NewTrackLocalStaticSample(codecs[0].RTPCodecCapability, util.MathRandAlpha(16), util.MathRandAlpha(16))
  1201  			if err != nil {
  1202  				return err
  1203  			}
  1204  
  1205  			_, err = pc.AddTransceiverFromTrack(track, []RTPTransceiverInit{
  1206  				{Direction: dir},
  1207  			}...)
  1208  			return err
  1209  		}
  1210  
  1211  		_, err := pc.AddTransceiverFromKind(
  1212  			RTPCodecTypeVideo,
  1213  			RTPTransceiverInit{Direction: dir},
  1214  		)
  1215  		return err
  1216  	}
  1217  
  1218  	for _, test := range []struct {
  1219  		name                  string
  1220  		offerDirection        RTPTransceiverDirection
  1221  		answerStartDirection  RTPTransceiverDirection
  1222  		answerFinalDirections []RTPTransceiverDirection
  1223  	}{
  1224  		{
  1225  			"offer sendrecv answer sendrecv",
  1226  			RTPTransceiverDirectionSendrecv,
  1227  			RTPTransceiverDirectionSendrecv,
  1228  			[]RTPTransceiverDirection{RTPTransceiverDirectionSendrecv},
  1229  		},
  1230  		{
  1231  			"offer sendonly answer sendrecv",
  1232  			RTPTransceiverDirectionSendonly,
  1233  			RTPTransceiverDirectionSendrecv,
  1234  			[]RTPTransceiverDirection{RTPTransceiverDirectionSendrecv},
  1235  		},
  1236  		{
  1237  			"offer recvonly answer sendrecv",
  1238  			RTPTransceiverDirectionRecvonly,
  1239  			RTPTransceiverDirectionSendrecv,
  1240  			[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly},
  1241  		},
  1242  		{
  1243  			"offer sendrecv answer sendonly",
  1244  			RTPTransceiverDirectionSendrecv,
  1245  			RTPTransceiverDirectionSendonly,
  1246  			[]RTPTransceiverDirection{RTPTransceiverDirectionSendrecv},
  1247  		},
  1248  		{
  1249  			"offer sendonly answer sendonly",
  1250  			RTPTransceiverDirectionSendonly,
  1251  			RTPTransceiverDirectionSendonly,
  1252  			[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionRecvonly},
  1253  		},
  1254  		{
  1255  			"offer recvonly answer sendonly",
  1256  			RTPTransceiverDirectionRecvonly,
  1257  			RTPTransceiverDirectionSendonly,
  1258  			[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly},
  1259  		},
  1260  		{
  1261  			"offer sendrecv answer recvonly",
  1262  			RTPTransceiverDirectionSendrecv,
  1263  			RTPTransceiverDirectionRecvonly,
  1264  			[]RTPTransceiverDirection{RTPTransceiverDirectionRecvonly},
  1265  		},
  1266  		{
  1267  			"offer sendonly answer recvonly",
  1268  			RTPTransceiverDirectionSendonly,
  1269  			RTPTransceiverDirectionRecvonly,
  1270  			[]RTPTransceiverDirection{RTPTransceiverDirectionRecvonly},
  1271  		},
  1272  		{
  1273  			"offer recvonly answer recvonly",
  1274  			RTPTransceiverDirectionRecvonly,
  1275  			RTPTransceiverDirectionRecvonly,
  1276  			[]RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendonly},
  1277  		},
  1278  	} {
  1279  		offerDirection := test.offerDirection
  1280  		answerStartDirection := test.answerStartDirection
  1281  		answerFinalDirections := test.answerFinalDirections
  1282  
  1283  		t.Run(test.name, func(t *testing.T) {
  1284  			pcOffer, pcAnswer, err := newPair()
  1285  			assert.NoError(t, err)
  1286  
  1287  			err = createTransceiver(pcOffer, offerDirection)
  1288  			assert.NoError(t, err)
  1289  
  1290  			offer, err := pcOffer.CreateOffer(nil)
  1291  			assert.NoError(t, err)
  1292  
  1293  			err = createTransceiver(pcAnswer, answerStartDirection)
  1294  			assert.NoError(t, err)
  1295  
  1296  			assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
  1297  
  1298  			assert.Equal(t, len(answerFinalDirections), len(pcAnswer.GetTransceivers()))
  1299  
  1300  			for i, tr := range pcAnswer.GetTransceivers() {
  1301  				assert.Equal(t, answerFinalDirections[i], tr.Direction())
  1302  			}
  1303  
  1304  			assert.NoError(t, pcOffer.Close())
  1305  			assert.NoError(t, pcAnswer.Close())
  1306  		})
  1307  	}
  1308  }
  1309  
  1310  func TestPeerConnection_SessionID(t *testing.T) {
  1311  	defer test.TimeOut(time.Second * 10).Stop()
  1312  	defer test.CheckRoutines(t)()
  1313  
  1314  	pcOffer, pcAnswer, err := newPair()
  1315  	assert.NoError(t, err)
  1316  	var offerSessionID uint64
  1317  	var offerSessionVersion uint64
  1318  	var answerSessionID uint64
  1319  	var answerSessionVersion uint64
  1320  	for i := 0; i < 10; i++ {
  1321  		assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1322  		offer := pcOffer.LocalDescription().parsed
  1323  		sessionID := offer.Origin.SessionID
  1324  		sessionVersion := offer.Origin.SessionVersion
  1325  		if offerSessionID == 0 {
  1326  			offerSessionID = sessionID
  1327  			offerSessionVersion = sessionVersion
  1328  		} else {
  1329  			if offerSessionID != sessionID {
  1330  				t.Errorf("offer[%v] session id mismatch: expected=%v, got=%v", i, offerSessionID, sessionID)
  1331  			}
  1332  			if offerSessionVersion+1 != sessionVersion {
  1333  				t.Errorf("offer[%v] session version mismatch: expected=%v, got=%v", i, offerSessionVersion+1, sessionVersion)
  1334  			}
  1335  			offerSessionVersion++
  1336  		}
  1337  
  1338  		answer := pcAnswer.LocalDescription().parsed
  1339  		sessionID = answer.Origin.SessionID
  1340  		sessionVersion = answer.Origin.SessionVersion
  1341  		if answerSessionID == 0 {
  1342  			answerSessionID = sessionID
  1343  			answerSessionVersion = sessionVersion
  1344  		} else {
  1345  			if answerSessionID != sessionID {
  1346  				t.Errorf("answer[%v] session id mismatch: expected=%v, got=%v", i, answerSessionID, sessionID)
  1347  			}
  1348  			if answerSessionVersion+1 != sessionVersion {
  1349  				t.Errorf("answer[%v] session version mismatch: expected=%v, got=%v", i, answerSessionVersion+1, sessionVersion)
  1350  			}
  1351  			answerSessionVersion++
  1352  		}
  1353  	}
  1354  	closePairNow(t, pcOffer, pcAnswer)
  1355  }
  1356  
  1357  func TestPeerConnectionNilCallback(t *testing.T) {
  1358  	pc, err := NewPeerConnection(Configuration{})
  1359  	assert.NoError(t, err)
  1360  
  1361  	pc.onSignalingStateChange(SignalingStateStable)
  1362  	pc.OnSignalingStateChange(func(SignalingState) {
  1363  		t.Error("OnSignalingStateChange called")
  1364  	})
  1365  	pc.OnSignalingStateChange(nil)
  1366  	pc.onSignalingStateChange(SignalingStateStable)
  1367  
  1368  	pc.onConnectionStateChange(PeerConnectionStateNew)
  1369  	pc.OnConnectionStateChange(func(PeerConnectionState) {
  1370  		t.Error("OnConnectionStateChange called")
  1371  	})
  1372  	pc.OnConnectionStateChange(nil)
  1373  	pc.onConnectionStateChange(PeerConnectionStateNew)
  1374  
  1375  	pc.onICEConnectionStateChange(ICEConnectionStateNew)
  1376  	pc.OnICEConnectionStateChange(func(ICEConnectionState) {
  1377  		t.Error("OnConnectionStateChange called")
  1378  	})
  1379  	pc.OnICEConnectionStateChange(nil)
  1380  	pc.onICEConnectionStateChange(ICEConnectionStateNew)
  1381  
  1382  	pc.onNegotiationNeeded()
  1383  	pc.negotiationNeededOp()
  1384  	pc.OnNegotiationNeeded(func() {
  1385  		t.Error("OnNegotiationNeeded called")
  1386  	})
  1387  	pc.OnNegotiationNeeded(nil)
  1388  	pc.onNegotiationNeeded()
  1389  	pc.negotiationNeededOp()
  1390  
  1391  	assert.NoError(t, pc.Close())
  1392  }
  1393  
  1394  func TestTransceiverCreatedByRemoteSdpHasSameCodecOrderAsRemote(t *testing.T) {
  1395  	t.Run("Codec MatchExact", func(t *testing.T) { //nolint:dupl
  1396  		const remoteSdp = `v=0
  1397  o=- 4596489990601351948 2 IN IP4 127.0.0.1
  1398  s=-
  1399  t=0 0
  1400  m=video 60323 UDP/TLS/RTP/SAVPF 98 94 106
  1401  a=ice-ufrag:1/MvHwjAyVf27aLu
  1402  a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
  1403  a=ice-options:google-ice
  1404  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
  1405  a=mid:0
  1406  a=rtpmap:98 H264/90000
  1407  a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
  1408  a=rtpmap:94 VP8/90000
  1409  a=rtpmap:106 H264/90000
  1410  a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
  1411  a=sendonly
  1412  m=video 60323 UDP/TLS/RTP/SAVPF 108 98 125
  1413  a=ice-ufrag:1/MvHwjAyVf27aLu
  1414  a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
  1415  a=ice-options:google-ice
  1416  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
  1417  a=mid:1
  1418  a=rtpmap:98 H264/90000
  1419  a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
  1420  a=rtpmap:108 VP8/90000
  1421  a=sendonly
  1422  a=rtpmap:125 H264/90000
  1423  a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
  1424  `
  1425  		m := MediaEngine{}
  1426  		assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
  1427  			RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
  1428  			PayloadType:        94,
  1429  		}, RTPCodecTypeVideo))
  1430  		assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
  1431  			RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil},
  1432  			PayloadType:        98,
  1433  		}, RTPCodecTypeVideo))
  1434  
  1435  		api := NewAPI(WithMediaEngine(&m))
  1436  		pc, err := api.NewPeerConnection(Configuration{})
  1437  		assert.NoError(t, err)
  1438  		assert.NoError(t, pc.SetRemoteDescription(SessionDescription{
  1439  			Type: SDPTypeOffer,
  1440  			SDP:  remoteSdp,
  1441  		}))
  1442  		ans, _ := pc.CreateAnswer(nil)
  1443  		assert.NoError(t, pc.SetLocalDescription(ans))
  1444  		codecOfTr1 := pc.GetTransceivers()[0].getCodecs()[0]
  1445  		codecs := pc.api.mediaEngine.getCodecsByKind(RTPCodecTypeVideo)
  1446  		_, matchType := codecParametersFuzzySearch(codecOfTr1, codecs)
  1447  		assert.Equal(t, codecMatchExact, matchType)
  1448  		codecOfTr2 := pc.GetTransceivers()[1].getCodecs()[0]
  1449  		_, matchType = codecParametersFuzzySearch(codecOfTr2, codecs)
  1450  		assert.Equal(t, codecMatchExact, matchType)
  1451  		assert.EqualValues(t, 94, codecOfTr2.PayloadType)
  1452  		assert.NoError(t, pc.Close())
  1453  	})
  1454  
  1455  	t.Run("Codec PartialExact Only", func(t *testing.T) { //nolint:dupl
  1456  		const remoteSdp = `v=0
  1457  o=- 4596489990601351948 2 IN IP4 127.0.0.1
  1458  s=-
  1459  t=0 0
  1460  m=video 60323 UDP/TLS/RTP/SAVPF 98 106
  1461  a=ice-ufrag:1/MvHwjAyVf27aLu
  1462  a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
  1463  a=ice-options:google-ice
  1464  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
  1465  a=mid:0
  1466  a=rtpmap:98 H264/90000
  1467  a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
  1468  a=rtpmap:106 H264/90000
  1469  a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032
  1470  a=sendonly
  1471  m=video 60323 UDP/TLS/RTP/SAVPF 125 98
  1472  a=ice-ufrag:1/MvHwjAyVf27aLu
  1473  a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
  1474  a=ice-options:google-ice
  1475  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
  1476  a=mid:1
  1477  a=rtpmap:125 H264/90000
  1478  a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032
  1479  a=rtpmap:98 H264/90000
  1480  a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
  1481  a=sendonly
  1482  `
  1483  		m := MediaEngine{}
  1484  		assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
  1485  			RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
  1486  			PayloadType:        94,
  1487  		}, RTPCodecTypeVideo))
  1488  		assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
  1489  			RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil},
  1490  			PayloadType:        98,
  1491  		}, RTPCodecTypeVideo))
  1492  
  1493  		api := NewAPI(WithMediaEngine(&m))
  1494  		pc, err := api.NewPeerConnection(Configuration{})
  1495  		assert.NoError(t, err)
  1496  		assert.NoError(t, pc.SetRemoteDescription(SessionDescription{
  1497  			Type: SDPTypeOffer,
  1498  			SDP:  remoteSdp,
  1499  		}))
  1500  		ans, _ := pc.CreateAnswer(nil)
  1501  		assert.NoError(t, pc.SetLocalDescription(ans))
  1502  		codecOfTr1 := pc.GetTransceivers()[0].getCodecs()[0]
  1503  		codecs := pc.api.mediaEngine.getCodecsByKind(RTPCodecTypeVideo)
  1504  		_, matchType := codecParametersFuzzySearch(codecOfTr1, codecs)
  1505  		assert.Equal(t, codecMatchExact, matchType)
  1506  		codecOfTr2 := pc.GetTransceivers()[1].getCodecs()[0]
  1507  		_, matchType = codecParametersFuzzySearch(codecOfTr2, codecs)
  1508  		assert.Equal(t, codecMatchExact, matchType)
  1509  		// h.264/profile-id=640032 should be remap to 106 as same as transceiver 1
  1510  		assert.EqualValues(t, 106, codecOfTr2.PayloadType)
  1511  		assert.NoError(t, pc.Close())
  1512  	})
  1513  }
  1514  
  1515  // Assert that remote candidates with an unknown transport are ignored and logged.
  1516  // This allows us to accept SessionDescriptions with proprietary candidates
  1517  // like `ssltcp`.
  1518  func TestInvalidCandidateTransport(t *testing.T) {
  1519  	const (
  1520  		sslTCPCandidate = `candidate:1 1 ssltcp 1 127.0.0.1 443 typ host generation 0`
  1521  		sslTCPOffer     = `v=0
  1522  o=- 0 2 IN IP4 127.0.0.1
  1523  s=-
  1524  t=0 0
  1525  a=msid-semantic: WMS
  1526  m=application 9 DTLS/SCTP 5000
  1527  c=IN IP4 0.0.0.0
  1528  a=ice-ufrag:1/MvHwjAyVf27aLu
  1529  a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
  1530  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
  1531  a=mid:0
  1532  a=` + sslTCPCandidate + "\n"
  1533  	)
  1534  
  1535  	peerConnection, err := NewPeerConnection(Configuration{})
  1536  	assert.NoError(t, err)
  1537  
  1538  	assert.NoError(t, peerConnection.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: sslTCPOffer}))
  1539  	assert.NoError(t, peerConnection.AddICECandidate(ICECandidateInit{Candidate: sslTCPCandidate}))
  1540  
  1541  	assert.NoError(t, peerConnection.Close())
  1542  }
  1543  
  1544  func TestOfferWithInactiveDirection(t *testing.T) {
  1545  	const remoteSDP = `v=0
  1546  o=- 4596489990601351948 2 IN IP4 127.0.0.1
  1547  s=-
  1548  t=0 0
  1549  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
  1550  a=group:BUNDLE 0 1
  1551  a=msid-semantic:WMS *
  1552  m=video 9 UDP/TLS/RTP/SAVPF 97
  1553  c=IN IP4 0.0.0.0
  1554  a=inactive
  1555  a=ice-pwd:05d682b2902af03db90d9a9a5f2f8d7f
  1556  a=ice-ufrag:93cc7e4d
  1557  a=mid:0
  1558  a=rtpmap:97 H264/90000
  1559  a=setup:actpass
  1560  a=ssrc:1455629982 cname:{61fd3093-0326-4b12-8258-86bdc1fe677a}
  1561  `
  1562  
  1563  	peerConnection, err := NewPeerConnection(Configuration{})
  1564  	assert.NoError(t, err)
  1565  
  1566  	assert.NoError(t, peerConnection.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: remoteSDP}))
  1567  	assert.Equal(t, RTPTransceiverDirectionInactive, peerConnection.rtpTransceivers[0].direction.Load().(RTPTransceiverDirection)) //nolint:forcetypeassert
  1568  
  1569  	assert.NoError(t, peerConnection.Close())
  1570  }
  1571  
  1572  func TestPeerConnectionState(t *testing.T) {
  1573  	pc, err := NewPeerConnection(Configuration{})
  1574  	assert.NoError(t, err)
  1575  	assert.Equal(t, PeerConnectionStateNew, pc.ConnectionState())
  1576  
  1577  	pc.updateConnectionState(ICEConnectionStateChecking, DTLSTransportStateNew)
  1578  	assert.Equal(t, PeerConnectionStateConnecting, pc.ConnectionState())
  1579  
  1580  	pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateNew)
  1581  	assert.Equal(t, PeerConnectionStateConnecting, pc.ConnectionState())
  1582  
  1583  	pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateConnecting)
  1584  	assert.Equal(t, PeerConnectionStateConnecting, pc.ConnectionState())
  1585  
  1586  	pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateConnected)
  1587  	assert.Equal(t, PeerConnectionStateConnected, pc.ConnectionState())
  1588  
  1589  	pc.updateConnectionState(ICEConnectionStateCompleted, DTLSTransportStateConnected)
  1590  	assert.Equal(t, PeerConnectionStateConnected, pc.ConnectionState())
  1591  
  1592  	pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateClosed)
  1593  	assert.Equal(t, PeerConnectionStateConnected, pc.ConnectionState())
  1594  
  1595  	pc.updateConnectionState(ICEConnectionStateDisconnected, DTLSTransportStateConnected)
  1596  	assert.Equal(t, PeerConnectionStateDisconnected, pc.ConnectionState())
  1597  
  1598  	pc.updateConnectionState(ICEConnectionStateFailed, DTLSTransportStateConnected)
  1599  	assert.Equal(t, PeerConnectionStateFailed, pc.ConnectionState())
  1600  
  1601  	pc.updateConnectionState(ICEConnectionStateConnected, DTLSTransportStateFailed)
  1602  	assert.Equal(t, PeerConnectionStateFailed, pc.ConnectionState())
  1603  
  1604  	assert.NoError(t, pc.Close())
  1605  	assert.Equal(t, PeerConnectionStateClosed, pc.ConnectionState())
  1606  }
  1607  
  1608  func TestPeerConnectionDeadlock(t *testing.T) {
  1609  	lim := test.TimeOut(time.Second * 5)
  1610  	defer lim.Stop()
  1611  
  1612  	report := test.CheckRoutines(t)
  1613  	defer report()
  1614  
  1615  	closeHdlr := func(peerConnection *PeerConnection) {
  1616  		peerConnection.OnICEConnectionStateChange(func(i ICEConnectionState) {
  1617  			if i == ICEConnectionStateFailed || i == ICEConnectionStateClosed {
  1618  				if err := peerConnection.Close(); err != nil {
  1619  					assert.NoError(t, err)
  1620  				}
  1621  			}
  1622  		})
  1623  	}
  1624  
  1625  	pcOffer, pcAnswer, err := NewAPI().newPair(Configuration{})
  1626  	assert.NoError(t, err)
  1627  
  1628  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1629  
  1630  	onDataChannel, onDataChannelCancel := context.WithCancel(context.Background())
  1631  	pcAnswer.OnDataChannel(func(*DataChannel) {
  1632  		onDataChannelCancel()
  1633  	})
  1634  	<-onDataChannel.Done()
  1635  
  1636  	closeHdlr(pcOffer)
  1637  	closeHdlr(pcAnswer)
  1638  
  1639  	closePairNow(t, pcOffer, pcAnswer)
  1640  }