github.com/pion/webrtc/v4@v4.0.1/peerconnection_close_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  	"fmt"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/pion/transport/v3/test"
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  func TestPeerConnection_Close(t *testing.T) {
    20  	// Limit runtime in case of deadlocks
    21  	lim := test.TimeOut(time.Second * 20)
    22  	defer lim.Stop()
    23  
    24  	report := test.CheckRoutines(t)
    25  	defer report()
    26  
    27  	pcOffer, pcAnswer, err := newPair()
    28  	if err != nil {
    29  		t.Fatal(err)
    30  	}
    31  
    32  	awaitSetup := make(chan struct{})
    33  	pcAnswer.OnDataChannel(func(d *DataChannel) {
    34  		// Make sure this is the data channel we were looking for. (Not the one
    35  		// created in signalPair).
    36  		if d.Label() != "data" {
    37  			return
    38  		}
    39  		close(awaitSetup)
    40  	})
    41  
    42  	awaitICEClosed := make(chan struct{})
    43  	pcAnswer.OnICEConnectionStateChange(func(i ICEConnectionState) {
    44  		if i == ICEConnectionStateClosed {
    45  			close(awaitICEClosed)
    46  		}
    47  	})
    48  
    49  	_, err = pcOffer.CreateDataChannel("data", nil)
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  
    54  	err = signalPair(pcOffer, pcAnswer)
    55  	if err != nil {
    56  		t.Fatal(err)
    57  	}
    58  
    59  	<-awaitSetup
    60  
    61  	closePairNow(t, pcOffer, pcAnswer)
    62  
    63  	<-awaitICEClosed
    64  }
    65  
    66  // Assert that a PeerConnection that is shutdown before ICE starts doesn't leak
    67  func TestPeerConnection_Close_PreICE(t *testing.T) {
    68  	// Limit runtime in case of deadlocks
    69  	lim := test.TimeOut(time.Second * 30)
    70  	defer lim.Stop()
    71  
    72  	report := test.CheckRoutines(t)
    73  	defer report()
    74  
    75  	pcOffer, pcAnswer, err := newPair()
    76  	if err != nil {
    77  		t.Fatal(err)
    78  	}
    79  
    80  	_, err = pcOffer.CreateDataChannel("test-channel", nil)
    81  	if err != nil {
    82  		t.Fatal(err)
    83  	}
    84  
    85  	answer, err := pcOffer.CreateOffer(nil)
    86  	if err != nil {
    87  		t.Fatal(err)
    88  	}
    89  
    90  	assert.NoError(t, pcOffer.Close())
    91  
    92  	if err = pcAnswer.SetRemoteDescription(answer); err != nil {
    93  		t.Fatal(err)
    94  	}
    95  
    96  	for {
    97  		if pcAnswer.iceTransport.State() == ICETransportStateChecking {
    98  			break
    99  		}
   100  		time.Sleep(time.Second / 4)
   101  	}
   102  
   103  	assert.NoError(t, pcAnswer.Close())
   104  
   105  	// Assert that ICETransport is shutdown, test timeout will prevent deadlock
   106  	for {
   107  		if pcAnswer.iceTransport.State() == ICETransportStateClosed {
   108  			return
   109  		}
   110  		time.Sleep(time.Second / 4)
   111  	}
   112  }
   113  
   114  func TestPeerConnection_Close_DuringICE(t *testing.T) {
   115  	// Limit runtime in case of deadlocks
   116  	lim := test.TimeOut(time.Second * 30)
   117  	defer lim.Stop()
   118  
   119  	report := test.CheckRoutines(t)
   120  	defer report()
   121  
   122  	pcOffer, pcAnswer, err := newPair()
   123  	if err != nil {
   124  		t.Fatal(err)
   125  	}
   126  	closedOffer := make(chan struct{})
   127  	closedAnswer := make(chan struct{})
   128  	pcAnswer.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
   129  		if iceState == ICEConnectionStateConnected {
   130  			go func() {
   131  				assert.NoError(t, pcAnswer.Close())
   132  				close(closedAnswer)
   133  
   134  				assert.NoError(t, pcOffer.Close())
   135  				close(closedOffer)
   136  			}()
   137  		}
   138  	})
   139  
   140  	_, err = pcOffer.CreateDataChannel("test-channel", nil)
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  
   145  	offer, err := pcOffer.CreateOffer(nil)
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  
   150  	offerGatheringComplete := GatheringCompletePromise(pcOffer)
   151  	if err = pcOffer.SetLocalDescription(offer); err != nil {
   152  		t.Fatal(err)
   153  	}
   154  	<-offerGatheringComplete
   155  
   156  	if err = pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()); err != nil {
   157  		t.Fatal(err)
   158  	}
   159  
   160  	answer, err := pcAnswer.CreateAnswer(nil)
   161  	if err != nil {
   162  		t.Fatal(err)
   163  	}
   164  	answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   165  	if err = pcAnswer.SetLocalDescription(answer); err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	<-answerGatheringComplete
   169  	if err = pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()); err != nil {
   170  		t.Fatal(err)
   171  	}
   172  
   173  	select {
   174  	case <-closedAnswer:
   175  	case <-time.After(5 * time.Second):
   176  		t.Error("pcAnswer.Close() Timeout")
   177  	}
   178  	select {
   179  	case <-closedOffer:
   180  	case <-time.After(5 * time.Second):
   181  		t.Error("pcOffer.Close() Timeout")
   182  	}
   183  }
   184  
   185  func TestPeerConnection_GracefulCloseWithIncomingMessages(t *testing.T) {
   186  	// Limit runtime in case of deadlocks
   187  	lim := test.TimeOut(time.Second * 20)
   188  	defer lim.Stop()
   189  
   190  	report := test.CheckRoutinesStrict(t)
   191  	defer report()
   192  
   193  	pcOffer, pcAnswer, err := newPair()
   194  	if err != nil {
   195  		t.Fatal(err)
   196  	}
   197  
   198  	var dcAnswer *DataChannel
   199  	answerDataChannelOpened := make(chan struct{})
   200  	pcAnswer.OnDataChannel(func(d *DataChannel) {
   201  		// Make sure this is the data channel we were looking for. (Not the one
   202  		// created in signalPair).
   203  		if d.Label() != "data" {
   204  			return
   205  		}
   206  		dcAnswer = d
   207  		close(answerDataChannelOpened)
   208  	})
   209  
   210  	dcOffer, err := pcOffer.CreateDataChannel("data", nil)
   211  	if err != nil {
   212  		t.Fatal(err)
   213  	}
   214  
   215  	offerDataChannelOpened := make(chan struct{})
   216  	dcOffer.OnOpen(func() {
   217  		close(offerDataChannelOpened)
   218  	})
   219  
   220  	err = signalPair(pcOffer, pcAnswer)
   221  	if err != nil {
   222  		t.Fatal(err)
   223  	}
   224  
   225  	<-offerDataChannelOpened
   226  	<-answerDataChannelOpened
   227  
   228  	msgNum := 0
   229  	dcOffer.OnMessage(func(_ DataChannelMessage) {
   230  		t.Log("msg", msgNum)
   231  		msgNum++
   232  	})
   233  
   234  	// send 50 messages, then close pcOffer, and then send another 50
   235  	for i := 0; i < 100; i++ {
   236  		if i == 50 {
   237  			err = pcOffer.GracefulClose()
   238  			if err != nil {
   239  				t.Fatal(err)
   240  			}
   241  		}
   242  		_ = dcAnswer.Send([]byte("hello!"))
   243  	}
   244  
   245  	err = pcAnswer.GracefulClose()
   246  	if err != nil {
   247  		t.Fatal(err)
   248  	}
   249  }
   250  
   251  func TestPeerConnection_GracefulCloseWhileOpening(t *testing.T) {
   252  	// Limit runtime in case of deadlocks
   253  	lim := test.TimeOut(time.Second * 5)
   254  	defer lim.Stop()
   255  
   256  	report := test.CheckRoutinesStrict(t)
   257  	defer report()
   258  
   259  	pcOffer, pcAnswer, err := newPair()
   260  	if err != nil {
   261  		t.Fatal(err)
   262  	}
   263  
   264  	if _, err = pcOffer.CreateDataChannel("initial_data_channel", nil); err != nil {
   265  		t.Fatal(err)
   266  	}
   267  
   268  	offer, err := pcOffer.CreateOffer(nil)
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  	offerGatheringComplete := GatheringCompletePromise(pcOffer)
   273  	if err = pcOffer.SetLocalDescription(offer); err != nil {
   274  		t.Fatal(err)
   275  	}
   276  	<-offerGatheringComplete
   277  
   278  	err = pcOffer.GracefulClose()
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  
   283  	if err = pcAnswer.SetRemoteDescription(offer); err != nil {
   284  		t.Fatal(err)
   285  	}
   286  
   287  	err = pcAnswer.GracefulClose()
   288  	if err != nil {
   289  		t.Fatal(err)
   290  	}
   291  }
   292  
   293  func TestPeerConnection_GracefulCloseConcurrent(t *testing.T) {
   294  	// Limit runtime in case of deadlocks
   295  	lim := test.TimeOut(time.Second * 10)
   296  	defer lim.Stop()
   297  
   298  	for _, mixed := range []bool{false, true} {
   299  		t.Run(fmt.Sprintf("mixed_graceful=%t", mixed), func(t *testing.T) {
   300  			report := test.CheckRoutinesStrict(t)
   301  			defer report()
   302  
   303  			pc, err := NewPeerConnection(Configuration{})
   304  			if err != nil {
   305  				t.Fatal(err)
   306  			}
   307  
   308  			const gracefulCloseConcurrency = 50
   309  			var wg sync.WaitGroup
   310  			wg.Add(gracefulCloseConcurrency)
   311  			for i := 0; i < gracefulCloseConcurrency; i++ {
   312  				go func() {
   313  					defer wg.Done()
   314  					assert.NoError(t, pc.GracefulClose())
   315  				}()
   316  			}
   317  			if !mixed {
   318  				if err := pc.Close(); err != nil {
   319  					t.Fatal(err)
   320  				}
   321  			} else {
   322  				if err := pc.GracefulClose(); err != nil {
   323  					t.Fatal(err)
   324  				}
   325  			}
   326  			wg.Wait()
   327  		})
   328  	}
   329  }