github.com/pion/webrtc/v4@v4.0.1/datachannel_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 "fmt" 8 "io" 9 "sync" 10 "testing" 11 "time" 12 13 "github.com/pion/transport/v3/test" 14 "github.com/stretchr/testify/assert" 15 ) 16 17 // expectedLabel represents the label of the data channel we are trying to test. 18 // Some other channels may have been created during initialization (in the Wasm 19 // bindings this is a requirement). 20 const expectedLabel = "data" 21 22 func closePairNow(t testing.TB, pc1, pc2 io.Closer) { 23 var fail bool 24 if err := pc1.Close(); err != nil { 25 t.Errorf("Failed to close PeerConnection: %v", err) 26 fail = true 27 } 28 if err := pc2.Close(); err != nil { 29 t.Errorf("Failed to close PeerConnection: %v", err) 30 fail = true 31 } 32 if fail { 33 t.FailNow() 34 } 35 } 36 37 func closePair(t *testing.T, pc1, pc2 io.Closer, done <-chan bool) { 38 select { 39 case <-time.After(10 * time.Second): 40 t.Fatalf("closePair timed out waiting for done signal") 41 case <-done: 42 closePairNow(t, pc1, pc2) 43 } 44 } 45 46 func setUpDataChannelParametersTest(t *testing.T, options *DataChannelInit) (*PeerConnection, *PeerConnection, *DataChannel, chan bool) { 47 offerPC, answerPC, err := newPair() 48 if err != nil { 49 t.Fatalf("Failed to create a PC pair for testing") 50 } 51 done := make(chan bool) 52 53 dc, err := offerPC.CreateDataChannel(expectedLabel, options) 54 if err != nil { 55 t.Fatalf("Failed to create a PC pair for testing") 56 } 57 58 return offerPC, answerPC, dc, done 59 } 60 61 func closeReliabilityParamTest(t *testing.T, pc1, pc2 *PeerConnection, done chan bool) { 62 err := signalPair(pc1, pc2) 63 if err != nil { 64 t.Fatalf("Failed to signal our PC pair for testing") 65 } 66 67 closePair(t, pc1, pc2, done) 68 } 69 70 func BenchmarkDataChannelSend2(b *testing.B) { benchmarkDataChannelSend(b, 2) } 71 func BenchmarkDataChannelSend4(b *testing.B) { benchmarkDataChannelSend(b, 4) } 72 func BenchmarkDataChannelSend8(b *testing.B) { benchmarkDataChannelSend(b, 8) } 73 func BenchmarkDataChannelSend16(b *testing.B) { benchmarkDataChannelSend(b, 16) } 74 func BenchmarkDataChannelSend32(b *testing.B) { benchmarkDataChannelSend(b, 32) } 75 76 // See https://github.com/pion/webrtc/issues/1516 77 func benchmarkDataChannelSend(b *testing.B, numChannels int) { 78 offerPC, answerPC, err := newPair() 79 if err != nil { 80 b.Fatalf("Failed to create a PC pair for testing") 81 } 82 83 open := make(map[string]chan bool) 84 answerPC.OnDataChannel(func(d *DataChannel) { 85 if _, ok := open[d.Label()]; !ok { 86 // Ignore anything unknown channel label. 87 return 88 } 89 d.OnOpen(func() { open[d.Label()] <- true }) 90 }) 91 92 var wg sync.WaitGroup 93 for i := 0; i < numChannels; i++ { 94 label := fmt.Sprintf("dc-%d", i) 95 open[label] = make(chan bool) 96 wg.Add(1) 97 dc, err := offerPC.CreateDataChannel(label, nil) 98 assert.NoError(b, err) 99 100 dc.OnOpen(func() { 101 <-open[label] 102 for n := 0; n < b.N/numChannels; n++ { 103 if err := dc.SendText("Ping"); err != nil { 104 b.Fatalf("Unexpected error sending data (label=%q): %v", label, err) 105 } 106 } 107 wg.Done() 108 }) 109 } 110 111 assert.NoError(b, signalPair(offerPC, answerPC)) 112 wg.Wait() 113 closePairNow(b, offerPC, answerPC) 114 } 115 116 func TestDataChannel_Open(t *testing.T) { 117 const openOnceChannelCapacity = 2 118 119 t.Run("handler should be called once", func(t *testing.T) { 120 report := test.CheckRoutines(t) 121 defer report() 122 123 offerPC, answerPC, err := newPair() 124 if err != nil { 125 t.Fatalf("Failed to create a PC pair for testing") 126 } 127 128 done := make(chan bool) 129 openCalls := make(chan bool, openOnceChannelCapacity) 130 131 answerPC.OnDataChannel(func(d *DataChannel) { 132 if d.Label() != expectedLabel { 133 return 134 } 135 d.OnOpen(func() { 136 openCalls <- true 137 }) 138 d.OnMessage(func(DataChannelMessage) { 139 go func() { 140 // Wait a little bit to ensure all messages are processed. 141 time.Sleep(100 * time.Millisecond) 142 done <- true 143 }() 144 }) 145 }) 146 147 dc, err := offerPC.CreateDataChannel(expectedLabel, nil) 148 assert.NoError(t, err) 149 150 dc.OnOpen(func() { 151 e := dc.SendText("Ping") 152 if e != nil { 153 t.Fatalf("Failed to send string on data channel") 154 } 155 }) 156 157 assert.NoError(t, signalPair(offerPC, answerPC)) 158 159 closePair(t, offerPC, answerPC, done) 160 161 assert.Len(t, openCalls, 1) 162 }) 163 164 t.Run("handler should be called once when already negotiated", func(t *testing.T) { 165 report := test.CheckRoutines(t) 166 defer report() 167 168 offerPC, answerPC, err := newPair() 169 if err != nil { 170 t.Fatalf("Failed to create a PC pair for testing") 171 } 172 173 done := make(chan bool) 174 answerOpenCalls := make(chan bool, openOnceChannelCapacity) 175 offerOpenCalls := make(chan bool, openOnceChannelCapacity) 176 177 negotiated := true 178 ordered := true 179 dataChannelID := uint16(0) 180 181 answerDC, err := answerPC.CreateDataChannel(expectedLabel, &DataChannelInit{ 182 ID: &dataChannelID, 183 Negotiated: &negotiated, 184 Ordered: &ordered, 185 }) 186 assert.NoError(t, err) 187 offerDC, err := offerPC.CreateDataChannel(expectedLabel, &DataChannelInit{ 188 ID: &dataChannelID, 189 Negotiated: &negotiated, 190 Ordered: &ordered, 191 }) 192 assert.NoError(t, err) 193 194 answerDC.OnMessage(func(DataChannelMessage) { 195 go func() { 196 // Wait a little bit to ensure all messages are processed. 197 time.Sleep(100 * time.Millisecond) 198 done <- true 199 }() 200 }) 201 answerDC.OnOpen(func() { 202 answerOpenCalls <- true 203 }) 204 205 offerDC.OnOpen(func() { 206 offerOpenCalls <- true 207 e := offerDC.SendText("Ping") 208 if e != nil { 209 t.Fatalf("Failed to send string on data channel") 210 } 211 }) 212 213 assert.NoError(t, signalPair(offerPC, answerPC)) 214 215 closePair(t, offerPC, answerPC, done) 216 217 assert.Len(t, answerOpenCalls, 1) 218 assert.Len(t, offerOpenCalls, 1) 219 }) 220 } 221 222 func TestDataChannel_Send(t *testing.T) { 223 t.Run("before signaling", func(t *testing.T) { 224 report := test.CheckRoutines(t) 225 defer report() 226 227 offerPC, answerPC, err := newPair() 228 if err != nil { 229 t.Fatalf("Failed to create a PC pair for testing") 230 } 231 232 done := make(chan bool) 233 234 answerPC.OnDataChannel(func(d *DataChannel) { 235 // Make sure this is the data channel we were looking for. (Not the one 236 // created in signalPair). 237 if d.Label() != expectedLabel { 238 return 239 } 240 d.OnMessage(func(DataChannelMessage) { 241 e := d.Send([]byte("Pong")) 242 if e != nil { 243 t.Fatalf("Failed to send string on data channel") 244 } 245 }) 246 assert.True(t, d.Ordered(), "Ordered should be set to true") 247 }) 248 249 dc, err := offerPC.CreateDataChannel(expectedLabel, nil) 250 if err != nil { 251 t.Fatalf("Failed to create a PC pair for testing") 252 } 253 254 assert.True(t, dc.Ordered(), "Ordered should be set to true") 255 256 dc.OnOpen(func() { 257 e := dc.SendText("Ping") 258 if e != nil { 259 t.Fatalf("Failed to send string on data channel") 260 } 261 }) 262 dc.OnMessage(func(DataChannelMessage) { 263 done <- true 264 }) 265 266 err = signalPair(offerPC, answerPC) 267 if err != nil { 268 t.Fatalf("Failed to signal our PC pair for testing: %+v", err) 269 } 270 271 closePair(t, offerPC, answerPC, done) 272 }) 273 274 t.Run("after connected", func(t *testing.T) { 275 report := test.CheckRoutines(t) 276 defer report() 277 278 offerPC, answerPC, err := newPair() 279 if err != nil { 280 t.Fatalf("Failed to create a PC pair for testing") 281 } 282 283 done := make(chan bool) 284 285 answerPC.OnDataChannel(func(d *DataChannel) { 286 // Make sure this is the data channel we were looking for. (Not the one 287 // created in signalPair). 288 if d.Label() != expectedLabel { 289 return 290 } 291 d.OnMessage(func(DataChannelMessage) { 292 e := d.Send([]byte("Pong")) 293 if e != nil { 294 t.Fatalf("Failed to send string on data channel") 295 } 296 }) 297 assert.True(t, d.Ordered(), "Ordered should be set to true") 298 }) 299 300 once := &sync.Once{} 301 offerPC.OnICEConnectionStateChange(func(state ICEConnectionState) { 302 if state == ICEConnectionStateConnected || state == ICEConnectionStateCompleted { 303 // wasm fires completed state multiple times 304 once.Do(func() { 305 dc, createErr := offerPC.CreateDataChannel(expectedLabel, nil) 306 if createErr != nil { 307 t.Fatalf("Failed to create a PC pair for testing") 308 } 309 310 assert.True(t, dc.Ordered(), "Ordered should be set to true") 311 312 dc.OnMessage(func(DataChannelMessage) { 313 done <- true 314 }) 315 316 if e := dc.SendText("Ping"); e != nil { 317 // wasm binding doesn't fire OnOpen (we probably already missed it) 318 dc.OnOpen(func() { 319 e = dc.SendText("Ping") 320 if e != nil { 321 t.Fatalf("Failed to send string on data channel") 322 } 323 }) 324 } 325 }) 326 } 327 }) 328 329 err = signalPair(offerPC, answerPC) 330 if err != nil { 331 t.Fatalf("Failed to signal our PC pair for testing") 332 } 333 334 closePair(t, offerPC, answerPC, done) 335 }) 336 } 337 338 func TestDataChannel_Close(t *testing.T) { 339 report := test.CheckRoutines(t) 340 defer report() 341 342 t.Run("Close after PeerConnection Closed", func(t *testing.T) { 343 offerPC, answerPC, err := newPair() 344 assert.NoError(t, err) 345 346 dc, err := offerPC.CreateDataChannel(expectedLabel, nil) 347 assert.NoError(t, err) 348 349 closePairNow(t, offerPC, answerPC) 350 assert.NoError(t, dc.Close()) 351 }) 352 353 t.Run("Close before connected", func(t *testing.T) { 354 offerPC, answerPC, err := newPair() 355 assert.NoError(t, err) 356 357 dc, err := offerPC.CreateDataChannel(expectedLabel, nil) 358 assert.NoError(t, err) 359 360 assert.NoError(t, dc.Close()) 361 closePairNow(t, offerPC, answerPC) 362 }) 363 } 364 365 func TestDataChannelParameters(t *testing.T) { 366 report := test.CheckRoutines(t) 367 defer report() 368 369 t.Run("MaxPacketLifeTime exchange", func(t *testing.T) { 370 ordered := true 371 maxPacketLifeTime := uint16(3) 372 options := &DataChannelInit{ 373 Ordered: &ordered, 374 MaxPacketLifeTime: &maxPacketLifeTime, 375 } 376 377 offerPC, answerPC, dc, done := setUpDataChannelParametersTest(t, options) 378 379 // Check if parameters are correctly set 380 assert.Equal(t, dc.Ordered(), ordered, "Ordered should be same value as set in DataChannelInit") 381 if assert.NotNil(t, dc.MaxPacketLifeTime(), "should not be nil") { 382 assert.Equal(t, maxPacketLifeTime, *dc.MaxPacketLifeTime(), "should match") 383 } 384 385 answerPC.OnDataChannel(func(d *DataChannel) { 386 if d.Label() != expectedLabel { 387 return 388 } 389 // Check if parameters are correctly set 390 assert.Equal(t, d.Ordered(), ordered, "Ordered should be same value as set in DataChannelInit") 391 if assert.NotNil(t, d.MaxPacketLifeTime(), "should not be nil") { 392 assert.Equal(t, maxPacketLifeTime, *d.MaxPacketLifeTime(), "should match") 393 } 394 done <- true 395 }) 396 397 closeReliabilityParamTest(t, offerPC, answerPC, done) 398 }) 399 400 t.Run("MaxRetransmits exchange", func(t *testing.T) { 401 ordered := false 402 maxRetransmits := uint16(3000) 403 options := &DataChannelInit{ 404 Ordered: &ordered, 405 MaxRetransmits: &maxRetransmits, 406 } 407 408 offerPC, answerPC, dc, done := setUpDataChannelParametersTest(t, options) 409 410 // Check if parameters are correctly set 411 assert.False(t, dc.Ordered(), "Ordered should be set to false") 412 if assert.NotNil(t, dc.MaxRetransmits(), "should not be nil") { 413 assert.Equal(t, maxRetransmits, *dc.MaxRetransmits(), "should match") 414 } 415 416 answerPC.OnDataChannel(func(d *DataChannel) { 417 // Make sure this is the data channel we were looking for. (Not the one 418 // created in signalPair). 419 if d.Label() != expectedLabel { 420 return 421 } 422 // Check if parameters are correctly set 423 assert.False(t, d.Ordered(), "Ordered should be set to false") 424 if assert.NotNil(t, d.MaxRetransmits(), "should not be nil") { 425 assert.Equal(t, maxRetransmits, *d.MaxRetransmits(), "should match") 426 } 427 done <- true 428 }) 429 430 closeReliabilityParamTest(t, offerPC, answerPC, done) 431 }) 432 433 t.Run("Protocol exchange", func(t *testing.T) { 434 protocol := "json" 435 options := &DataChannelInit{ 436 Protocol: &protocol, 437 } 438 439 offerPC, answerPC, dc, done := setUpDataChannelParametersTest(t, options) 440 441 // Check if parameters are correctly set 442 assert.Equal(t, protocol, dc.Protocol(), "Protocol should match DataChannelInit") 443 444 answerPC.OnDataChannel(func(d *DataChannel) { 445 // Make sure this is the data channel we were looking for. (Not the one 446 // created in signalPair). 447 if d.Label() != expectedLabel { 448 return 449 } 450 // Check if parameters are correctly set 451 assert.Equal(t, protocol, d.Protocol(), "Protocol should match what channel creator declared") 452 done <- true 453 }) 454 455 closeReliabilityParamTest(t, offerPC, answerPC, done) 456 }) 457 458 t.Run("Negotiated exchange", func(t *testing.T) { 459 const expectedMessage = "Hello World" 460 461 negotiated := true 462 var id uint16 = 500 463 options := &DataChannelInit{ 464 Negotiated: &negotiated, 465 ID: &id, 466 } 467 468 offerPC, answerPC, offerDatachannel, done := setUpDataChannelParametersTest(t, options) 469 answerDatachannel, err := answerPC.CreateDataChannel(expectedLabel, options) 470 assert.NoError(t, err) 471 472 answerPC.OnDataChannel(func(d *DataChannel) { 473 // Ignore our default channel, exists to force ICE candidates. See signalPair for more info 474 if d.Label() == "initial_data_channel" { 475 return 476 } 477 478 t.Fatal("OnDataChannel must not be fired when negotiated == true") 479 }) 480 offerPC.OnDataChannel(func(*DataChannel) { 481 t.Fatal("OnDataChannel must not be fired when negotiated == true") 482 }) 483 484 seenAnswerMessage := &atomicBool{} 485 seenOfferMessage := &atomicBool{} 486 487 answerDatachannel.OnMessage(func(msg DataChannelMessage) { 488 if msg.IsString && string(msg.Data) == expectedMessage { 489 seenAnswerMessage.set(true) 490 } 491 }) 492 493 offerDatachannel.OnMessage(func(msg DataChannelMessage) { 494 if msg.IsString && string(msg.Data) == expectedMessage { 495 seenOfferMessage.set(true) 496 } 497 }) 498 499 go func() { 500 for { 501 if seenAnswerMessage.get() && seenOfferMessage.get() { 502 break 503 } 504 505 if offerDatachannel.ReadyState() == DataChannelStateOpen { 506 assert.NoError(t, offerDatachannel.SendText(expectedMessage)) 507 } 508 if answerDatachannel.ReadyState() == DataChannelStateOpen { 509 assert.NoError(t, answerDatachannel.SendText(expectedMessage)) 510 } 511 512 time.Sleep(500 * time.Millisecond) 513 } 514 515 done <- true 516 }() 517 518 closeReliabilityParamTest(t, offerPC, answerPC, done) 519 }) 520 }