github.com/pion/webrtc/v4@v4.0.1/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/v3/test" 15 "github.com/pion/webrtc/v4/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(PeerConnectionState) { 391 onceOffererOnConnectionStateChange.Do(func() { 392 wasCalledMut.Lock() 393 defer wasCalledMut.Unlock() 394 wasCalled = append(wasCalled, "offerer OnConnectionStateChange") 395 wg.Done() 396 }) 397 }) 398 pcOffer.OnSignalingStateChange(func(SignalingState) { 399 onceOffererOnSignalingStateChange.Do(func() { 400 wasCalledMut.Lock() 401 defer wasCalledMut.Unlock() 402 wasCalled = append(wasCalled, "offerer OnSignalingStateChange") 403 wg.Done() 404 }) 405 }) 406 pcAnswer.OnICEConnectionStateChange(func(ICEConnectionState) { 407 onceAnswererOnICEConnectionStateChange.Do(func() { 408 wasCalledMut.Lock() 409 defer wasCalledMut.Unlock() 410 wasCalled = append(wasCalled, "answerer OnICEConnectionStateChange") 411 wg.Done() 412 }) 413 }) 414 pcAnswer.OnConnectionStateChange(func(PeerConnectionState) { 415 onceAnswererOnConnectionStateChange.Do(func() { 416 wasCalledMut.Lock() 417 defer wasCalledMut.Unlock() 418 wasCalled = append(wasCalled, "answerer OnConnectionStateChange") 419 wg.Done() 420 }) 421 }) 422 pcAnswer.OnSignalingStateChange(func(SignalingState) { 423 onceAnswererOnSignalingStateChange.Do(func() { 424 wasCalledMut.Lock() 425 defer wasCalledMut.Unlock() 426 wasCalled = append(wasCalled, "answerer OnSignalingStateChange") 427 wg.Done() 428 }) 429 }) 430 431 // Use signalPair to establish a connection between pcOffer and pcAnswer. This 432 // process should trigger the above event handlers. 433 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 434 435 // Wait for all of the event handlers to be triggered. 436 done := make(chan struct{}) 437 go func() { 438 wg.Wait() 439 done <- struct{}{} 440 }() 441 timeout := time.After(5 * time.Second) 442 select { 443 case <-done: 444 break 445 case <-timeout: 446 t.Fatalf("timed out waiting for one or more events handlers to be called (these *were* called: %+v)", wasCalled) 447 } 448 449 closePairNow(t, pcOffer, pcAnswer) 450 } 451 452 func TestMultipleOfferAnswer(t *testing.T) { 453 firstPeerConn, err := NewPeerConnection(Configuration{}) 454 if err != nil { 455 t.Errorf("New PeerConnection: got error: %v", err) 456 } 457 458 if _, err = firstPeerConn.CreateOffer(nil); err != nil { 459 t.Errorf("First Offer: got error: %v", err) 460 } 461 if _, err = firstPeerConn.CreateOffer(nil); err != nil { 462 t.Errorf("Second Offer: got error: %v", err) 463 } 464 465 secondPeerConn, err := NewPeerConnection(Configuration{}) 466 if err != nil { 467 t.Errorf("New PeerConnection: got error: %v", err) 468 } 469 secondPeerConn.OnICECandidate(func(*ICECandidate) { 470 }) 471 472 if _, err = secondPeerConn.CreateOffer(nil); err != nil { 473 t.Errorf("First Offer: got error: %v", err) 474 } 475 if _, err = secondPeerConn.CreateOffer(nil); err != nil { 476 t.Errorf("Second Offer: got error: %v", err) 477 } 478 479 closePairNow(t, firstPeerConn, secondPeerConn) 480 } 481 482 func TestNoFingerprintInFirstMediaIfSetRemoteDescription(t *testing.T) { 483 const sdpNoFingerprintInFirstMedia = `v=0 484 o=- 143087887 1561022767 IN IP4 192.168.84.254 485 s=VideoRoom 404986692241682 486 t=0 0 487 a=group:BUNDLE audio 488 a=msid-semantic: WMS 2867270241552712 489 m=video 0 UDP/TLS/RTP/SAVPF 0 490 a=mid:video 491 c=IN IP4 192.168.84.254 492 a=inactive 493 m=audio 9 UDP/TLS/RTP/SAVPF 111 494 c=IN IP4 192.168.84.254 495 a=recvonly 496 a=mid:audio 497 a=rtcp-mux 498 a=ice-ufrag:AS/w 499 a=ice-pwd:9NOgoAOMALYu/LOpA1iqg/ 500 a=ice-options:trickle 501 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 502 a=setup:active 503 a=rtpmap:111 opus/48000/2 504 a=candidate:1 1 udp 2013266431 192.168.84.254 46492 typ host 505 a=end-of-candidates 506 ` 507 508 report := test.CheckRoutines(t) 509 defer report() 510 511 pc, err := NewPeerConnection(Configuration{}) 512 if err != nil { 513 t.Error(err.Error()) 514 } 515 516 desc := SessionDescription{ 517 Type: SDPTypeOffer, 518 SDP: sdpNoFingerprintInFirstMedia, 519 } 520 521 if err = pc.SetRemoteDescription(desc); err != nil { 522 t.Error(err.Error()) 523 } 524 525 assert.NoError(t, pc.Close()) 526 } 527 528 func TestNegotiationNeeded(t *testing.T) { 529 lim := test.TimeOut(time.Second * 30) 530 defer lim.Stop() 531 532 report := test.CheckRoutines(t) 533 defer report() 534 535 pc, err := NewPeerConnection(Configuration{}) 536 if err != nil { 537 t.Error(err.Error()) 538 } 539 540 var wg sync.WaitGroup 541 wg.Add(1) 542 543 pc.OnNegotiationNeeded(wg.Done) 544 _, err = pc.CreateDataChannel("initial_data_channel", nil) 545 assert.NoError(t, err) 546 547 wg.Wait() 548 549 assert.NoError(t, pc.Close()) 550 } 551 552 func TestMultipleCreateChannel(t *testing.T) { 553 var wg sync.WaitGroup 554 555 report := test.CheckRoutines(t) 556 defer report() 557 558 // Two OnDataChannel 559 // One OnNegotiationNeeded 560 wg.Add(3) 561 562 pcOffer, _ := NewPeerConnection(Configuration{}) 563 pcAnswer, _ := NewPeerConnection(Configuration{}) 564 565 pcAnswer.OnDataChannel(func(*DataChannel) { 566 wg.Done() 567 }) 568 569 pcOffer.OnNegotiationNeeded(func() { 570 offer, err := pcOffer.CreateOffer(nil) 571 assert.NoError(t, err) 572 573 offerGatheringComplete := GatheringCompletePromise(pcOffer) 574 if err = pcOffer.SetLocalDescription(offer); err != nil { 575 t.Error(err) 576 } 577 <-offerGatheringComplete 578 if err = pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()); err != nil { 579 t.Error(err) 580 } 581 582 answer, err := pcAnswer.CreateAnswer(nil) 583 assert.NoError(t, err) 584 585 answerGatheringComplete := GatheringCompletePromise(pcAnswer) 586 if err = pcAnswer.SetLocalDescription(answer); err != nil { 587 t.Error(err) 588 } 589 <-answerGatheringComplete 590 if err = pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()); err != nil { 591 t.Error(err) 592 } 593 wg.Done() 594 }) 595 596 if _, err := pcOffer.CreateDataChannel("initial_data_channel_0", nil); err != nil { 597 t.Error(err) 598 } 599 600 if _, err := pcOffer.CreateDataChannel("initial_data_channel_1", nil); err != nil { 601 t.Error(err) 602 } 603 604 wg.Wait() 605 606 closePairNow(t, pcOffer, pcAnswer) 607 } 608 609 // Assert that candidates are gathered by calling SetLocalDescription, not SetRemoteDescription 610 func TestGatherOnSetLocalDescription(t *testing.T) { 611 lim := test.TimeOut(time.Second * 30) 612 defer lim.Stop() 613 614 report := test.CheckRoutines(t) 615 defer report() 616 617 pcOfferGathered := make(chan SessionDescription) 618 pcAnswerGathered := make(chan SessionDescription) 619 620 s := SettingEngine{} 621 api := NewAPI(WithSettingEngine(s)) 622 623 pcOffer, err := api.NewPeerConnection(Configuration{}) 624 if err != nil { 625 t.Error(err.Error()) 626 } 627 628 // We need to create a data channel in order to trigger ICE 629 if _, err = pcOffer.CreateDataChannel("initial_data_channel", nil); err != nil { 630 t.Error(err.Error()) 631 } 632 633 pcOffer.OnICECandidate(func(i *ICECandidate) { 634 if i == nil { 635 close(pcOfferGathered) 636 } 637 }) 638 639 offer, err := pcOffer.CreateOffer(nil) 640 if err != nil { 641 t.Error(err.Error()) 642 } else if err = pcOffer.SetLocalDescription(offer); err != nil { 643 t.Error(err.Error()) 644 } 645 646 <-pcOfferGathered 647 648 pcAnswer, err := api.NewPeerConnection(Configuration{}) 649 if err != nil { 650 t.Error(err.Error()) 651 } 652 653 pcAnswer.OnICECandidate(func(i *ICECandidate) { 654 if i == nil { 655 close(pcAnswerGathered) 656 } 657 }) 658 659 if err = pcAnswer.SetRemoteDescription(offer); err != nil { 660 t.Error(err.Error()) 661 } 662 663 select { 664 case <-pcAnswerGathered: 665 t.Fatal("pcAnswer started gathering with no SetLocalDescription") 666 // Gathering is async, not sure of a better way to catch this currently 667 case <-time.After(3 * time.Second): 668 } 669 670 answer, err := pcAnswer.CreateAnswer(nil) 671 if err != nil { 672 t.Error(err.Error()) 673 } else if err = pcAnswer.SetLocalDescription(answer); err != nil { 674 t.Error(err.Error()) 675 } 676 <-pcAnswerGathered 677 closePairNow(t, pcOffer, pcAnswer) 678 } 679 680 // Assert that SetRemoteDescription handles invalid states 681 func TestSetRemoteDescriptionInvalid(t *testing.T) { 682 t.Run("local-offer+SetRemoteDescription(Offer)", func(t *testing.T) { 683 pc, err := NewPeerConnection(Configuration{}) 684 assert.NoError(t, err) 685 686 offer, err := pc.CreateOffer(nil) 687 assert.NoError(t, err) 688 689 assert.NoError(t, pc.SetLocalDescription(offer)) 690 assert.Error(t, pc.SetRemoteDescription(offer)) 691 692 assert.NoError(t, pc.Close()) 693 }) 694 } 695 696 func TestAddTransceiver(t *testing.T) { 697 lim := test.TimeOut(time.Second * 30) 698 defer lim.Stop() 699 700 report := test.CheckRoutines(t) 701 defer report() 702 703 for _, testCase := range []struct { 704 expectSender, expectReceiver bool 705 direction RTPTransceiverDirection 706 }{ 707 {true, true, RTPTransceiverDirectionSendrecv}, 708 // Go and WASM diverge 709 // {true, false, RTPTransceiverDirectionSendonly}, 710 // {false, true, RTPTransceiverDirectionRecvonly}, 711 } { 712 pc, err := NewPeerConnection(Configuration{}) 713 assert.NoError(t, err) 714 715 transceiver, err := pc.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{ 716 Direction: testCase.direction, 717 }) 718 assert.NoError(t, err) 719 720 if testCase.expectReceiver { 721 assert.NotNil(t, transceiver.Receiver()) 722 } else { 723 assert.Nil(t, transceiver.Receiver()) 724 } 725 726 if testCase.expectSender { 727 assert.NotNil(t, transceiver.Sender()) 728 } else { 729 assert.Nil(t, transceiver.Sender()) 730 } 731 732 offer, err := pc.CreateOffer(nil) 733 assert.NoError(t, err) 734 735 assert.True(t, offerMediaHasDirection(offer, RTPCodecTypeVideo, testCase.direction)) 736 assert.NoError(t, pc.Close()) 737 } 738 } 739 740 // Assert that SCTPTransport -> DTLSTransport -> ICETransport works after connected 741 func TestTransportChain(t *testing.T) { 742 offer, answer, err := newPair() 743 assert.NoError(t, err) 744 745 peerConnectionsConnected := untilConnectionState(PeerConnectionStateConnected, offer, answer) 746 assert.NoError(t, signalPair(offer, answer)) 747 peerConnectionsConnected.Wait() 748 749 assert.NotNil(t, offer.SCTP().Transport().ICETransport()) 750 751 closePairNow(t, offer, answer) 752 } 753 754 // Assert that the PeerConnection closes via DTLS (and not ICE) 755 func TestDTLSClose(t *testing.T) { 756 lim := test.TimeOut(time.Second * 10) 757 defer lim.Stop() 758 759 report := test.CheckRoutines(t) 760 defer report() 761 762 pcOffer, pcAnswer, err := newPair() 763 assert.NoError(t, err) 764 765 _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo) 766 assert.NoError(t, err) 767 768 peerConnectionsConnected := untilConnectionState(PeerConnectionStateConnected, pcOffer, pcAnswer) 769 770 offer, err := pcOffer.CreateOffer(nil) 771 assert.NoError(t, err) 772 773 offerGatheringComplete := GatheringCompletePromise(pcOffer) 774 assert.NoError(t, pcOffer.SetLocalDescription(offer)) 775 <-offerGatheringComplete 776 777 assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription())) 778 779 answer, err := pcAnswer.CreateAnswer(nil) 780 assert.NoError(t, err) 781 782 answerGatheringComplete := GatheringCompletePromise(pcAnswer) 783 assert.NoError(t, pcAnswer.SetLocalDescription(answer)) 784 <-answerGatheringComplete 785 786 assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription())) 787 788 peerConnectionsConnected.Wait() 789 assert.NoError(t, pcOffer.Close()) 790 }