github.com/pion/webrtc/v3@v3.2.24/peerconnection_test.go (about) 1 // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly> 2 // SPDX-License-Identifier: MIT 3 4 package webrtc 5 6 import ( 7 "reflect" 8 "sync" 9 "sync/atomic" 10 "testing" 11 "time" 12 13 "github.com/pion/sdp/v3" 14 "github.com/pion/transport/v2/test" 15 "github.com/pion/webrtc/v3/pkg/rtcerr" 16 "github.com/stretchr/testify/assert" 17 ) 18 19 // newPair creates two new peer connections (an offerer and an answerer) 20 // *without* using an api (i.e. using the default settings). 21 func newPair() (pcOffer *PeerConnection, pcAnswer *PeerConnection, err error) { 22 pca, err := NewPeerConnection(Configuration{}) 23 if err != nil { 24 return nil, nil, err 25 } 26 27 pcb, err := NewPeerConnection(Configuration{}) 28 if err != nil { 29 return nil, nil, err 30 } 31 32 return pca, pcb, nil 33 } 34 35 func signalPairWithModification(pcOffer *PeerConnection, pcAnswer *PeerConnection, modificationFunc func(string) string) error { 36 // Note(albrow): We need to create a data channel in order to trigger ICE 37 // candidate gathering in the background for the JavaScript/Wasm bindings. If 38 // we don't do this, the complete offer including ICE candidates will never be 39 // generated. 40 if _, err := pcOffer.CreateDataChannel("initial_data_channel", nil); err != nil { 41 return err 42 } 43 44 offer, err := pcOffer.CreateOffer(nil) 45 if err != nil { 46 return err 47 } 48 offerGatheringComplete := GatheringCompletePromise(pcOffer) 49 if err = pcOffer.SetLocalDescription(offer); err != nil { 50 return err 51 } 52 <-offerGatheringComplete 53 54 offer.SDP = modificationFunc(pcOffer.LocalDescription().SDP) 55 if err = pcAnswer.SetRemoteDescription(offer); err != nil { 56 return err 57 } 58 59 answer, err := pcAnswer.CreateAnswer(nil) 60 if err != nil { 61 return err 62 } 63 answerGatheringComplete := GatheringCompletePromise(pcAnswer) 64 if err = pcAnswer.SetLocalDescription(answer); err != nil { 65 return err 66 } 67 <-answerGatheringComplete 68 return pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()) 69 } 70 71 func signalPair(pcOffer *PeerConnection, pcAnswer *PeerConnection) error { 72 return signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string { return sessionDescription }) 73 } 74 75 func offerMediaHasDirection(offer SessionDescription, kind RTPCodecType, direction RTPTransceiverDirection) bool { 76 parsed := &sdp.SessionDescription{} 77 if err := parsed.Unmarshal([]byte(offer.SDP)); err != nil { 78 return false 79 } 80 81 for _, media := range parsed.MediaDescriptions { 82 if media.MediaName.Media == kind.String() { 83 _, exists := media.Attribute(direction.String()) 84 return exists 85 } 86 } 87 return false 88 } 89 90 func untilConnectionState(state PeerConnectionState, peers ...*PeerConnection) *sync.WaitGroup { 91 var triggered sync.WaitGroup 92 triggered.Add(len(peers)) 93 94 for _, p := range peers { 95 var done atomic.Value 96 done.Store(false) 97 hdlr := func(p PeerConnectionState) { 98 if val, ok := done.Load().(bool); ok && (!val && p == state) { 99 done.Store(true) 100 triggered.Done() 101 } 102 } 103 104 p.OnConnectionStateChange(hdlr) 105 } 106 return &triggered 107 } 108 109 func TestNew(t *testing.T) { 110 pc, err := NewPeerConnection(Configuration{ 111 ICEServers: []ICEServer{ 112 { 113 URLs: []string{ 114 "stun:stun.l.google.com:19302", 115 }, 116 Username: "unittest", 117 }, 118 }, 119 ICETransportPolicy: ICETransportPolicyRelay, 120 BundlePolicy: BundlePolicyMaxCompat, 121 RTCPMuxPolicy: RTCPMuxPolicyNegotiate, 122 PeerIdentity: "unittest", 123 ICECandidatePoolSize: 5, 124 }) 125 assert.NoError(t, err) 126 assert.NotNil(t, pc) 127 assert.NoError(t, pc.Close()) 128 } 129 130 func TestPeerConnection_SetConfiguration(t *testing.T) { 131 // Note: These tests don't include ICEServer.Credential, 132 // ICEServer.CredentialType, or Certificates because those are not supported 133 // in the WASM bindings. 134 135 for _, test := range []struct { 136 name string 137 init func() (*PeerConnection, error) 138 config Configuration 139 wantErr error 140 }{ 141 { 142 name: "valid", 143 init: func() (*PeerConnection, error) { 144 pc, err := NewPeerConnection(Configuration{ 145 ICECandidatePoolSize: 5, 146 }) 147 if err != nil { 148 return pc, err 149 } 150 151 err = pc.SetConfiguration(Configuration{ 152 ICEServers: []ICEServer{ 153 { 154 URLs: []string{ 155 "stun:stun.l.google.com:19302", 156 }, 157 Username: "unittest", 158 }, 159 }, 160 ICETransportPolicy: ICETransportPolicyAll, 161 BundlePolicy: BundlePolicyBalanced, 162 RTCPMuxPolicy: RTCPMuxPolicyRequire, 163 ICECandidatePoolSize: 5, 164 }) 165 if err != nil { 166 return pc, err 167 } 168 169 return pc, nil 170 }, 171 config: Configuration{}, 172 wantErr: nil, 173 }, 174 { 175 name: "closed connection", 176 init: func() (*PeerConnection, error) { 177 pc, err := NewPeerConnection(Configuration{}) 178 assert.Nil(t, err) 179 180 err = pc.Close() 181 assert.Nil(t, err) 182 return pc, err 183 }, 184 config: Configuration{}, 185 wantErr: &rtcerr.InvalidStateError{Err: ErrConnectionClosed}, 186 }, 187 { 188 name: "update PeerIdentity", 189 init: func() (*PeerConnection, error) { 190 return NewPeerConnection(Configuration{}) 191 }, 192 config: Configuration{ 193 PeerIdentity: "unittest", 194 }, 195 wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingPeerIdentity}, 196 }, 197 { 198 name: "update BundlePolicy", 199 init: func() (*PeerConnection, error) { 200 return NewPeerConnection(Configuration{}) 201 }, 202 config: Configuration{ 203 BundlePolicy: BundlePolicyMaxCompat, 204 }, 205 wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingBundlePolicy}, 206 }, 207 { 208 name: "update RTCPMuxPolicy", 209 init: func() (*PeerConnection, error) { 210 return NewPeerConnection(Configuration{}) 211 }, 212 config: Configuration{ 213 RTCPMuxPolicy: RTCPMuxPolicyNegotiate, 214 }, 215 wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingRTCPMuxPolicy}, 216 }, 217 { 218 name: "update ICECandidatePoolSize", 219 init: func() (*PeerConnection, error) { 220 pc, err := NewPeerConnection(Configuration{ 221 ICECandidatePoolSize: 0, 222 }) 223 if err != nil { 224 return pc, err 225 } 226 offer, err := pc.CreateOffer(nil) 227 if err != nil { 228 return pc, err 229 } 230 err = pc.SetLocalDescription(offer) 231 if err != nil { 232 return pc, err 233 } 234 return pc, nil 235 }, 236 config: Configuration{ 237 ICECandidatePoolSize: 1, 238 }, 239 wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingICECandidatePoolSize}, 240 }, 241 } { 242 pc, err := test.init() 243 if err != nil { 244 t.Errorf("SetConfiguration %q: init failed: %v", test.name, err) 245 } 246 247 err = pc.SetConfiguration(test.config) 248 if got, want := err, test.wantErr; !reflect.DeepEqual(got, want) { 249 t.Errorf("SetConfiguration %q: err = %v, want %v", test.name, got, want) 250 } 251 252 assert.NoError(t, pc.Close()) 253 } 254 } 255 256 func TestPeerConnection_GetConfiguration(t *testing.T) { 257 pc, err := NewPeerConnection(Configuration{}) 258 assert.NoError(t, err) 259 260 expected := Configuration{ 261 ICEServers: []ICEServer{}, 262 ICETransportPolicy: ICETransportPolicyAll, 263 BundlePolicy: BundlePolicyBalanced, 264 RTCPMuxPolicy: RTCPMuxPolicyRequire, 265 ICECandidatePoolSize: 0, 266 } 267 actual := pc.GetConfiguration() 268 assert.True(t, &expected != &actual) 269 assert.Equal(t, expected.ICEServers, actual.ICEServers) 270 assert.Equal(t, expected.ICETransportPolicy, actual.ICETransportPolicy) 271 assert.Equal(t, expected.BundlePolicy, actual.BundlePolicy) 272 assert.Equal(t, expected.RTCPMuxPolicy, actual.RTCPMuxPolicy) 273 // nolint:godox 274 // TODO(albrow): Uncomment this after #513 is fixed. 275 // See: https://github.com/pion/webrtc/issues/513. 276 // assert.Equal(t, len(expected.Certificates), len(actual.Certificates)) 277 assert.Equal(t, expected.ICECandidatePoolSize, actual.ICECandidatePoolSize) 278 assert.NoError(t, pc.Close()) 279 } 280 281 const minimalOffer = `v=0 282 o=- 4596489990601351948 2 IN IP4 127.0.0.1 283 s=- 284 t=0 0 285 a=msid-semantic: WMS 286 m=application 47299 DTLS/SCTP 5000 287 c=IN IP4 192.168.20.129 288 a=candidate:1966762134 1 udp 2122260223 192.168.20.129 47299 typ host generation 0 289 a=candidate:1966762134 1 udp 2122262783 2001:db8::1 47199 typ host generation 0 290 a=candidate:211962667 1 udp 2122194687 10.0.3.1 40864 typ host generation 0 291 a=candidate:1002017894 1 tcp 1518280447 192.168.20.129 0 typ host tcptype active generation 0 292 a=candidate:1109506011 1 tcp 1518214911 10.0.3.1 0 typ host tcptype active generation 0 293 a=ice-ufrag:1/MvHwjAyVf27aLu 294 a=ice-pwd:3dBU7cFOBl120v33cynDvN1E 295 a=ice-options:google-ice 296 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 297 a=setup:actpass 298 a=mid:data 299 a=sctpmap:5000 webrtc-datachannel 1024 300 ` 301 302 func TestSetRemoteDescription(t *testing.T) { 303 testCases := []struct { 304 desc SessionDescription 305 expectError bool 306 }{ 307 {SessionDescription{Type: SDPTypeOffer, SDP: minimalOffer}, false}, 308 {SessionDescription{Type: 0, SDP: ""}, true}, 309 } 310 311 for i, testCase := range testCases { 312 peerConn, err := NewPeerConnection(Configuration{}) 313 if err != nil { 314 t.Errorf("Case %d: got error: %v", i, err) 315 } 316 317 if testCase.expectError { 318 assert.Error(t, peerConn.SetRemoteDescription(testCase.desc)) 319 } else { 320 assert.NoError(t, peerConn.SetRemoteDescription(testCase.desc)) 321 } 322 323 assert.NoError(t, peerConn.Close()) 324 } 325 } 326 327 func TestCreateOfferAnswer(t *testing.T) { 328 offerPeerConn, err := NewPeerConnection(Configuration{}) 329 assert.NoError(t, err) 330 331 answerPeerConn, err := NewPeerConnection(Configuration{}) 332 assert.NoError(t, err) 333 334 _, err = offerPeerConn.CreateDataChannel("test-channel", nil) 335 assert.NoError(t, err) 336 337 offer, err := offerPeerConn.CreateOffer(nil) 338 assert.NoError(t, err) 339 assert.NoError(t, offerPeerConn.SetLocalDescription(offer)) 340 341 assert.NoError(t, answerPeerConn.SetRemoteDescription(offer)) 342 343 answer, err := answerPeerConn.CreateAnswer(nil) 344 assert.NoError(t, err) 345 346 assert.NoError(t, answerPeerConn.SetLocalDescription(answer)) 347 assert.NoError(t, offerPeerConn.SetRemoteDescription(answer)) 348 349 // after setLocalDescription(answer), signaling state should be stable. 350 // so CreateAnswer should return an InvalidStateError 351 assert.Equal(t, answerPeerConn.SignalingState(), SignalingStateStable) 352 _, err = answerPeerConn.CreateAnswer(nil) 353 assert.Error(t, err) 354 355 closePairNow(t, offerPeerConn, answerPeerConn) 356 } 357 358 func TestPeerConnection_EventHandlers(t *testing.T) { 359 pcOffer, err := NewPeerConnection(Configuration{}) 360 assert.NoError(t, err) 361 pcAnswer, err := NewPeerConnection(Configuration{}) 362 assert.NoError(t, err) 363 364 // wasCalled is a list of event handlers that were called. 365 wasCalled := []string{} 366 wasCalledMut := &sync.Mutex{} 367 // wg is used to wait for all event handlers to be called. 368 wg := &sync.WaitGroup{} 369 wg.Add(6) 370 371 // Each sync.Once is used to ensure that we call wg.Done once for each event 372 // handler and don't add multiple entries to wasCalled. The event handlers can 373 // be called more than once in some cases. 374 onceOffererOnICEConnectionStateChange := &sync.Once{} 375 onceOffererOnConnectionStateChange := &sync.Once{} 376 onceOffererOnSignalingStateChange := &sync.Once{} 377 onceAnswererOnICEConnectionStateChange := &sync.Once{} 378 onceAnswererOnConnectionStateChange := &sync.Once{} 379 onceAnswererOnSignalingStateChange := &sync.Once{} 380 381 // Register all the event handlers. 382 pcOffer.OnICEConnectionStateChange(func(ICEConnectionState) { 383 onceOffererOnICEConnectionStateChange.Do(func() { 384 wasCalledMut.Lock() 385 defer wasCalledMut.Unlock() 386 wasCalled = append(wasCalled, "offerer OnICEConnectionStateChange") 387 wg.Done() 388 }) 389 }) 390 pcOffer.OnConnectionStateChange(func(callbackState PeerConnectionState) { 391 if storedState := pcOffer.ConnectionState(); callbackState != storedState { 392 t.Errorf("State in callback argument is different from ConnectionState(): callbackState=%s, storedState=%s", callbackState, storedState) 393 } 394 395 onceOffererOnConnectionStateChange.Do(func() { 396 wasCalledMut.Lock() 397 defer wasCalledMut.Unlock() 398 wasCalled = append(wasCalled, "offerer OnConnectionStateChange") 399 wg.Done() 400 }) 401 }) 402 pcOffer.OnSignalingStateChange(func(SignalingState) { 403 onceOffererOnSignalingStateChange.Do(func() { 404 wasCalledMut.Lock() 405 defer wasCalledMut.Unlock() 406 wasCalled = append(wasCalled, "offerer OnSignalingStateChange") 407 wg.Done() 408 }) 409 }) 410 pcAnswer.OnICEConnectionStateChange(func(ICEConnectionState) { 411 onceAnswererOnICEConnectionStateChange.Do(func() { 412 wasCalledMut.Lock() 413 defer wasCalledMut.Unlock() 414 wasCalled = append(wasCalled, "answerer OnICEConnectionStateChange") 415 wg.Done() 416 }) 417 }) 418 pcAnswer.OnConnectionStateChange(func(PeerConnectionState) { 419 onceAnswererOnConnectionStateChange.Do(func() { 420 wasCalledMut.Lock() 421 defer wasCalledMut.Unlock() 422 wasCalled = append(wasCalled, "answerer OnConnectionStateChange") 423 wg.Done() 424 }) 425 }) 426 pcAnswer.OnSignalingStateChange(func(SignalingState) { 427 onceAnswererOnSignalingStateChange.Do(func() { 428 wasCalledMut.Lock() 429 defer wasCalledMut.Unlock() 430 wasCalled = append(wasCalled, "answerer OnSignalingStateChange") 431 wg.Done() 432 }) 433 }) 434 435 // Use signalPair to establish a connection between pcOffer and pcAnswer. This 436 // process should trigger the above event handlers. 437 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 438 439 // Wait for all of the event handlers to be triggered. 440 done := make(chan struct{}) 441 go func() { 442 wg.Wait() 443 done <- struct{}{} 444 }() 445 timeout := time.After(5 * time.Second) 446 select { 447 case <-done: 448 break 449 case <-timeout: 450 t.Fatalf("timed out waiting for one or more events handlers to be called (these *were* called: %+v)", wasCalled) 451 } 452 453 closePairNow(t, pcOffer, pcAnswer) 454 } 455 456 func TestMultipleOfferAnswer(t *testing.T) { 457 firstPeerConn, err := NewPeerConnection(Configuration{}) 458 if err != nil { 459 t.Errorf("New PeerConnection: got error: %v", err) 460 } 461 462 if _, err = firstPeerConn.CreateOffer(nil); err != nil { 463 t.Errorf("First Offer: got error: %v", err) 464 } 465 if _, err = firstPeerConn.CreateOffer(nil); err != nil { 466 t.Errorf("Second Offer: got error: %v", err) 467 } 468 469 secondPeerConn, err := NewPeerConnection(Configuration{}) 470 if err != nil { 471 t.Errorf("New PeerConnection: got error: %v", err) 472 } 473 secondPeerConn.OnICECandidate(func(i *ICECandidate) { 474 }) 475 476 if _, err = secondPeerConn.CreateOffer(nil); err != nil { 477 t.Errorf("First Offer: got error: %v", err) 478 } 479 if _, err = secondPeerConn.CreateOffer(nil); err != nil { 480 t.Errorf("Second Offer: got error: %v", err) 481 } 482 483 closePairNow(t, firstPeerConn, secondPeerConn) 484 } 485 486 func TestNoFingerprintInFirstMediaIfSetRemoteDescription(t *testing.T) { 487 const sdpNoFingerprintInFirstMedia = `v=0 488 o=- 143087887 1561022767 IN IP4 192.168.84.254 489 s=VideoRoom 404986692241682 490 t=0 0 491 a=group:BUNDLE audio 492 a=msid-semantic: WMS 2867270241552712 493 m=video 0 UDP/TLS/RTP/SAVPF 0 494 a=mid:video 495 c=IN IP4 192.168.84.254 496 a=inactive 497 m=audio 9 UDP/TLS/RTP/SAVPF 111 498 c=IN IP4 192.168.84.254 499 a=recvonly 500 a=mid:audio 501 a=rtcp-mux 502 a=ice-ufrag:AS/w 503 a=ice-pwd:9NOgoAOMALYu/LOpA1iqg/ 504 a=ice-options:trickle 505 a=fingerprint:sha-256 D2:B9:31:8F:DF:24:D8:0E:ED:D2:EF:25:9E:AF:6F:B8:34:AE:53:9C:E6:F3:8F:F2:64:15:FA:E8:7F:53:2D:38 506 a=setup:active 507 a=rtpmap:111 opus/48000/2 508 a=candidate:1 1 udp 2013266431 192.168.84.254 46492 typ host 509 a=end-of-candidates 510 ` 511 512 report := test.CheckRoutines(t) 513 defer report() 514 515 pc, err := NewPeerConnection(Configuration{}) 516 if err != nil { 517 t.Error(err.Error()) 518 } 519 520 desc := SessionDescription{ 521 Type: SDPTypeOffer, 522 SDP: sdpNoFingerprintInFirstMedia, 523 } 524 525 if err = pc.SetRemoteDescription(desc); err != nil { 526 t.Error(err.Error()) 527 } 528 529 assert.NoError(t, pc.Close()) 530 } 531 532 func TestNegotiationNeeded(t *testing.T) { 533 lim := test.TimeOut(time.Second * 30) 534 defer lim.Stop() 535 536 report := test.CheckRoutines(t) 537 defer report() 538 539 pc, err := NewPeerConnection(Configuration{}) 540 if err != nil { 541 t.Error(err.Error()) 542 } 543 544 var wg sync.WaitGroup 545 wg.Add(1) 546 547 pc.OnNegotiationNeeded(wg.Done) 548 _, err = pc.CreateDataChannel("initial_data_channel", nil) 549 assert.NoError(t, err) 550 551 wg.Wait() 552 553 assert.NoError(t, pc.Close()) 554 } 555 556 func TestMultipleCreateChannel(t *testing.T) { 557 var wg sync.WaitGroup 558 559 report := test.CheckRoutines(t) 560 defer report() 561 562 // Two OnDataChannel 563 // One OnNegotiationNeeded 564 wg.Add(3) 565 566 pcOffer, _ := NewPeerConnection(Configuration{}) 567 pcAnswer, _ := NewPeerConnection(Configuration{}) 568 569 pcAnswer.OnDataChannel(func(d *DataChannel) { 570 wg.Done() 571 }) 572 573 pcOffer.OnNegotiationNeeded(func() { 574 offer, err := pcOffer.CreateOffer(nil) 575 assert.NoError(t, err) 576 577 offerGatheringComplete := GatheringCompletePromise(pcOffer) 578 if err = pcOffer.SetLocalDescription(offer); err != nil { 579 t.Error(err) 580 } 581 <-offerGatheringComplete 582 if err = pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()); err != nil { 583 t.Error(err) 584 } 585 586 answer, err := pcAnswer.CreateAnswer(nil) 587 assert.NoError(t, err) 588 589 answerGatheringComplete := GatheringCompletePromise(pcAnswer) 590 if err = pcAnswer.SetLocalDescription(answer); err != nil { 591 t.Error(err) 592 } 593 <-answerGatheringComplete 594 if err = pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()); err != nil { 595 t.Error(err) 596 } 597 wg.Done() 598 }) 599 600 if _, err := pcOffer.CreateDataChannel("initial_data_channel_0", nil); err != nil { 601 t.Error(err) 602 } 603 604 if _, err := pcOffer.CreateDataChannel("initial_data_channel_1", nil); err != nil { 605 t.Error(err) 606 } 607 608 wg.Wait() 609 610 closePairNow(t, pcOffer, pcAnswer) 611 } 612 613 // Assert that candidates are gathered by calling SetLocalDescription, not SetRemoteDescription 614 func TestGatherOnSetLocalDescription(t *testing.T) { 615 lim := test.TimeOut(time.Second * 30) 616 defer lim.Stop() 617 618 report := test.CheckRoutines(t) 619 defer report() 620 621 pcOfferGathered := make(chan SessionDescription) 622 pcAnswerGathered := make(chan SessionDescription) 623 624 s := SettingEngine{} 625 api := NewAPI(WithSettingEngine(s)) 626 627 pcOffer, err := api.NewPeerConnection(Configuration{}) 628 if err != nil { 629 t.Error(err.Error()) 630 } 631 632 // We need to create a data channel in order to trigger ICE 633 if _, err = pcOffer.CreateDataChannel("initial_data_channel", nil); err != nil { 634 t.Error(err.Error()) 635 } 636 637 pcOffer.OnICECandidate(func(i *ICECandidate) { 638 if i == nil { 639 close(pcOfferGathered) 640 } 641 }) 642 643 offer, err := pcOffer.CreateOffer(nil) 644 if err != nil { 645 t.Error(err.Error()) 646 } else if err = pcOffer.SetLocalDescription(offer); err != nil { 647 t.Error(err.Error()) 648 } 649 650 <-pcOfferGathered 651 652 pcAnswer, err := api.NewPeerConnection(Configuration{}) 653 if err != nil { 654 t.Error(err.Error()) 655 } 656 657 pcAnswer.OnICECandidate(func(i *ICECandidate) { 658 if i == nil { 659 close(pcAnswerGathered) 660 } 661 }) 662 663 if err = pcAnswer.SetRemoteDescription(offer); err != nil { 664 t.Error(err.Error()) 665 } 666 667 select { 668 case <-pcAnswerGathered: 669 t.Fatal("pcAnswer started gathering with no SetLocalDescription") 670 // Gathering is async, not sure of a better way to catch this currently 671 case <-time.After(3 * time.Second): 672 } 673 674 answer, err := pcAnswer.CreateAnswer(nil) 675 if err != nil { 676 t.Error(err.Error()) 677 } else if err = pcAnswer.SetLocalDescription(answer); err != nil { 678 t.Error(err.Error()) 679 } 680 <-pcAnswerGathered 681 closePairNow(t, pcOffer, pcAnswer) 682 } 683 684 // Assert that SetRemoteDescription handles invalid states 685 func TestSetRemoteDescriptionInvalid(t *testing.T) { 686 t.Run("local-offer+SetRemoteDescription(Offer)", func(t *testing.T) { 687 pc, err := NewPeerConnection(Configuration{}) 688 assert.NoError(t, err) 689 690 offer, err := pc.CreateOffer(nil) 691 assert.NoError(t, err) 692 693 assert.NoError(t, pc.SetLocalDescription(offer)) 694 assert.Error(t, pc.SetRemoteDescription(offer)) 695 696 assert.NoError(t, pc.Close()) 697 }) 698 } 699 700 func TestAddTransceiver(t *testing.T) { 701 lim := test.TimeOut(time.Second * 30) 702 defer lim.Stop() 703 704 report := test.CheckRoutines(t) 705 defer report() 706 707 for _, testCase := range []struct { 708 expectSender, expectReceiver bool 709 direction RTPTransceiverDirection 710 }{ 711 {true, true, RTPTransceiverDirectionSendrecv}, 712 // Go and WASM diverge 713 // {true, false, RTPTransceiverDirectionSendonly}, 714 // {false, true, RTPTransceiverDirectionRecvonly}, 715 } { 716 pc, err := NewPeerConnection(Configuration{}) 717 assert.NoError(t, err) 718 719 transceiver, err := pc.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{ 720 Direction: testCase.direction, 721 }) 722 assert.NoError(t, err) 723 724 if testCase.expectReceiver { 725 assert.NotNil(t, transceiver.Receiver()) 726 } else { 727 assert.Nil(t, transceiver.Receiver()) 728 } 729 730 if testCase.expectSender { 731 assert.NotNil(t, transceiver.Sender()) 732 } else { 733 assert.Nil(t, transceiver.Sender()) 734 } 735 736 offer, err := pc.CreateOffer(nil) 737 assert.NoError(t, err) 738 739 assert.True(t, offerMediaHasDirection(offer, RTPCodecTypeVideo, testCase.direction)) 740 assert.NoError(t, pc.Close()) 741 } 742 } 743 744 // Assert that SCTPTransport -> DTLSTransport -> ICETransport works after connected 745 func TestTransportChain(t *testing.T) { 746 offer, answer, err := newPair() 747 assert.NoError(t, err) 748 749 peerConnectionsConnected := untilConnectionState(PeerConnectionStateConnected, offer, answer) 750 assert.NoError(t, signalPair(offer, answer)) 751 peerConnectionsConnected.Wait() 752 753 assert.NotNil(t, offer.SCTP().Transport().ICETransport()) 754 755 closePairNow(t, offer, answer) 756 }