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  }