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 }