datachannel_go_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. //go:build !js
  4. // +build !js
  5. package webrtc
  6. import (
  7. "bytes"
  8. "crypto/rand"
  9. "encoding/binary"
  10. "io"
  11. "io/ioutil"
  12. "math/big"
  13. "reflect"
  14. "regexp"
  15. "strings"
  16. "sync"
  17. "sync/atomic"
  18. "testing"
  19. "time"
  20. "github.com/pion/datachannel"
  21. "github.com/pion/logging"
  22. "github.com/pion/transport/v2/test"
  23. "github.com/stretchr/testify/assert"
  24. )
  25. func TestDataChannel_EventHandlers(t *testing.T) {
  26. to := test.TimeOut(time.Second * 20)
  27. defer to.Stop()
  28. report := test.CheckRoutines(t)
  29. defer report()
  30. api := NewAPI()
  31. dc := &DataChannel{api: api}
  32. onDialCalled := make(chan struct{})
  33. onOpenCalled := make(chan struct{})
  34. onMessageCalled := make(chan struct{})
  35. // Verify that the noop case works
  36. assert.NotPanics(t, func() { dc.onOpen() })
  37. dc.OnDial(func() {
  38. close(onDialCalled)
  39. })
  40. dc.OnOpen(func() {
  41. close(onOpenCalled)
  42. })
  43. dc.OnMessage(func(p DataChannelMessage) {
  44. close(onMessageCalled)
  45. })
  46. // Verify that the set handlers are called
  47. assert.NotPanics(t, func() { dc.onDial() })
  48. assert.NotPanics(t, func() { dc.onOpen() })
  49. assert.NotPanics(t, func() { dc.onMessage(DataChannelMessage{Data: []byte("o hai")}) })
  50. // Wait for all handlers to be called
  51. <-onDialCalled
  52. <-onOpenCalled
  53. <-onMessageCalled
  54. }
  55. func TestDataChannel_MessagesAreOrdered(t *testing.T) {
  56. report := test.CheckRoutines(t)
  57. defer report()
  58. api := NewAPI()
  59. dc := &DataChannel{api: api}
  60. max := 512
  61. out := make(chan int)
  62. inner := func(msg DataChannelMessage) {
  63. // randomly sleep
  64. // math/rand a weak RNG, but this does not need to be secure. Ignore with #nosec
  65. /* #nosec */
  66. randInt, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
  67. /* #nosec */ if err != nil {
  68. t.Fatalf("Failed to get random sleep duration: %s", err)
  69. }
  70. time.Sleep(time.Duration(randInt.Int64()) * time.Microsecond)
  71. s, _ := binary.Varint(msg.Data)
  72. out <- int(s)
  73. }
  74. dc.OnMessage(func(p DataChannelMessage) {
  75. inner(p)
  76. })
  77. go func() {
  78. for i := 1; i <= max; i++ {
  79. buf := make([]byte, 8)
  80. binary.PutVarint(buf, int64(i))
  81. dc.onMessage(DataChannelMessage{Data: buf})
  82. // Change the registered handler a couple of times to make sure
  83. // that everything continues to work, we don't lose messages, etc.
  84. if i%2 == 0 {
  85. handler := func(msg DataChannelMessage) {
  86. inner(msg)
  87. }
  88. dc.OnMessage(handler)
  89. }
  90. }
  91. }()
  92. values := make([]int, 0, max)
  93. for v := range out {
  94. values = append(values, v)
  95. if len(values) == max {
  96. close(out)
  97. }
  98. }
  99. expected := make([]int, max)
  100. for i := 1; i <= max; i++ {
  101. expected[i-1] = i
  102. }
  103. assert.EqualValues(t, expected, values)
  104. }
  105. // Note(albrow): This test includes some features that aren't supported by the
  106. // Wasm bindings (at least for now).
  107. func TestDataChannelParamters_Go(t *testing.T) {
  108. report := test.CheckRoutines(t)
  109. defer report()
  110. t.Run("MaxPacketLifeTime exchange", func(t *testing.T) {
  111. ordered := true
  112. var maxPacketLifeTime uint16 = 3
  113. options := &DataChannelInit{
  114. Ordered: &ordered,
  115. MaxPacketLifeTime: &maxPacketLifeTime,
  116. }
  117. offerPC, answerPC, dc, done := setUpDataChannelParametersTest(t, options)
  118. // Check if parameters are correctly set
  119. assert.True(t, dc.Ordered(), "Ordered should be set to true")
  120. if assert.NotNil(t, dc.MaxPacketLifeTime(), "should not be nil") {
  121. assert.Equal(t, maxPacketLifeTime, *dc.MaxPacketLifeTime(), "should match")
  122. }
  123. answerPC.OnDataChannel(func(d *DataChannel) {
  124. // Make sure this is the data channel we were looking for. (Not the one
  125. // created in signalPair).
  126. if d.Label() != expectedLabel {
  127. return
  128. }
  129. // Check if parameters are correctly set
  130. assert.True(t, d.ordered, "Ordered should be set to true")
  131. if assert.NotNil(t, d.maxPacketLifeTime, "should not be nil") {
  132. assert.Equal(t, maxPacketLifeTime, *d.maxPacketLifeTime, "should match")
  133. }
  134. done <- true
  135. })
  136. closeReliabilityParamTest(t, offerPC, answerPC, done)
  137. })
  138. t.Run("All other property methods", func(t *testing.T) {
  139. id := uint16(123)
  140. dc := &DataChannel{}
  141. dc.id = &id
  142. dc.label = "mylabel"
  143. dc.protocol = "myprotocol"
  144. dc.negotiated = true
  145. assert.Equal(t, dc.id, dc.ID(), "should match")
  146. assert.Equal(t, dc.label, dc.Label(), "should match")
  147. assert.Equal(t, dc.protocol, dc.Protocol(), "should match")
  148. assert.Equal(t, dc.negotiated, dc.Negotiated(), "should match")
  149. assert.Equal(t, uint64(0), dc.BufferedAmount(), "should match")
  150. dc.SetBufferedAmountLowThreshold(1500)
  151. assert.Equal(t, uint64(1500), dc.BufferedAmountLowThreshold(), "should match")
  152. })
  153. }
  154. func TestDataChannelBufferedAmount(t *testing.T) {
  155. t.Run("set before datachannel becomes open", func(t *testing.T) {
  156. report := test.CheckRoutines(t)
  157. defer report()
  158. var nOfferBufferedAmountLowCbs uint32
  159. var offerBufferedAmountLowThreshold uint64 = 1500
  160. var nAnswerBufferedAmountLowCbs uint32
  161. var answerBufferedAmountLowThreshold uint64 = 1400
  162. buf := make([]byte, 1000)
  163. offerPC, answerPC, err := newPair()
  164. if err != nil {
  165. t.Fatalf("Failed to create a PC pair for testing")
  166. }
  167. nPacketsToSend := int(10)
  168. var nOfferReceived uint32
  169. var nAnswerReceived uint32
  170. done := make(chan bool)
  171. answerPC.OnDataChannel(func(answerDC *DataChannel) {
  172. // Make sure this is the data channel we were looking for. (Not the one
  173. // created in signalPair).
  174. if answerDC.Label() != expectedLabel {
  175. return
  176. }
  177. answerDC.OnOpen(func() {
  178. assert.Equal(t, answerBufferedAmountLowThreshold, answerDC.BufferedAmountLowThreshold(), "value mismatch")
  179. for i := 0; i < nPacketsToSend; i++ {
  180. e := answerDC.Send(buf)
  181. if e != nil {
  182. t.Fatalf("Failed to send string on data channel")
  183. }
  184. }
  185. })
  186. answerDC.OnMessage(func(msg DataChannelMessage) {
  187. atomic.AddUint32(&nAnswerReceived, 1)
  188. })
  189. assert.True(t, answerDC.Ordered(), "Ordered should be set to true")
  190. // The value is temporarily stored in the answerDC object
  191. // until the answerDC gets opened
  192. answerDC.SetBufferedAmountLowThreshold(answerBufferedAmountLowThreshold)
  193. // The callback function is temporarily stored in the answerDC object
  194. // until the answerDC gets opened
  195. answerDC.OnBufferedAmountLow(func() {
  196. atomic.AddUint32(&nAnswerBufferedAmountLowCbs, 1)
  197. if atomic.LoadUint32(&nOfferBufferedAmountLowCbs) > 0 {
  198. done <- true
  199. }
  200. })
  201. })
  202. offerDC, err := offerPC.CreateDataChannel(expectedLabel, nil)
  203. if err != nil {
  204. t.Fatalf("Failed to create a PC pair for testing")
  205. }
  206. assert.True(t, offerDC.Ordered(), "Ordered should be set to true")
  207. offerDC.OnOpen(func() {
  208. assert.Equal(t, offerBufferedAmountLowThreshold, offerDC.BufferedAmountLowThreshold(), "value mismatch")
  209. for i := 0; i < nPacketsToSend; i++ {
  210. e := offerDC.Send(buf)
  211. if e != nil {
  212. t.Fatalf("Failed to send string on data channel")
  213. }
  214. // assert.Equal(t, (i+1)*len(buf), int(offerDC.BufferedAmount()), "unexpected bufferedAmount")
  215. }
  216. })
  217. offerDC.OnMessage(func(msg DataChannelMessage) {
  218. atomic.AddUint32(&nOfferReceived, 1)
  219. })
  220. // The value is temporarily stored in the offerDC object
  221. // until the offerDC gets opened
  222. offerDC.SetBufferedAmountLowThreshold(offerBufferedAmountLowThreshold)
  223. // The callback function is temporarily stored in the offerDC object
  224. // until the offerDC gets opened
  225. offerDC.OnBufferedAmountLow(func() {
  226. atomic.AddUint32(&nOfferBufferedAmountLowCbs, 1)
  227. if atomic.LoadUint32(&nAnswerBufferedAmountLowCbs) > 0 {
  228. done <- true
  229. }
  230. })
  231. err = signalPair(offerPC, answerPC)
  232. if err != nil {
  233. t.Fatalf("Failed to signal our PC pair for testing")
  234. }
  235. closePair(t, offerPC, answerPC, done)
  236. t.Logf("nOfferBufferedAmountLowCbs : %d", nOfferBufferedAmountLowCbs)
  237. t.Logf("nAnswerBufferedAmountLowCbs: %d", nAnswerBufferedAmountLowCbs)
  238. assert.True(t, nOfferBufferedAmountLowCbs > uint32(0), "callback should be made at least once")
  239. assert.True(t, nAnswerBufferedAmountLowCbs > uint32(0), "callback should be made at least once")
  240. })
  241. t.Run("set after datachannel becomes open", func(t *testing.T) {
  242. report := test.CheckRoutines(t)
  243. defer report()
  244. var nCbs int
  245. buf := make([]byte, 1000)
  246. offerPC, answerPC, err := newPair()
  247. if err != nil {
  248. t.Fatalf("Failed to create a PC pair for testing")
  249. }
  250. done := make(chan bool)
  251. answerPC.OnDataChannel(func(d *DataChannel) {
  252. // Make sure this is the data channel we were looking for. (Not the one
  253. // created in signalPair).
  254. if d.Label() != expectedLabel {
  255. return
  256. }
  257. var nPacketsReceived int
  258. d.OnMessage(func(msg DataChannelMessage) {
  259. nPacketsReceived++
  260. if nPacketsReceived == 10 {
  261. go func() {
  262. time.Sleep(time.Second)
  263. done <- true
  264. }()
  265. }
  266. })
  267. assert.True(t, d.Ordered(), "Ordered should be set to true")
  268. })
  269. dc, err := offerPC.CreateDataChannel(expectedLabel, nil)
  270. if err != nil {
  271. t.Fatalf("Failed to create a PC pair for testing")
  272. }
  273. assert.True(t, dc.Ordered(), "Ordered should be set to true")
  274. dc.OnOpen(func() {
  275. // The value should directly be passed to sctp
  276. dc.SetBufferedAmountLowThreshold(1500)
  277. // The callback function should directly be passed to sctp
  278. dc.OnBufferedAmountLow(func() {
  279. nCbs++
  280. })
  281. for i := 0; i < 10; i++ {
  282. e := dc.Send(buf)
  283. if e != nil {
  284. t.Fatalf("Failed to send string on data channel")
  285. }
  286. assert.Equal(t, uint64(1500), dc.BufferedAmountLowThreshold(), "value mismatch")
  287. // assert.Equal(t, (i+1)*len(buf), int(dc.BufferedAmount()), "unexpected bufferedAmount")
  288. }
  289. })
  290. dc.OnMessage(func(msg DataChannelMessage) {
  291. })
  292. err = signalPair(offerPC, answerPC)
  293. if err != nil {
  294. t.Fatalf("Failed to signal our PC pair for testing")
  295. }
  296. closePair(t, offerPC, answerPC, done)
  297. assert.True(t, nCbs > 0, "callback should be made at least once")
  298. })
  299. }
  300. func TestEOF(t *testing.T) {
  301. report := test.CheckRoutines(t)
  302. defer report()
  303. log := logging.NewDefaultLoggerFactory().NewLogger("test")
  304. label := "test-channel"
  305. testData := []byte("this is some test data")
  306. t.Run("Detach", func(t *testing.T) {
  307. // Use Detach data channels mode
  308. s := SettingEngine{}
  309. s.DetachDataChannels()
  310. api := NewAPI(WithSettingEngine(s))
  311. // Set up two peer connections.
  312. config := Configuration{}
  313. pca, err := api.NewPeerConnection(config)
  314. if err != nil {
  315. t.Fatal(err)
  316. }
  317. pcb, err := api.NewPeerConnection(config)
  318. if err != nil {
  319. t.Fatal(err)
  320. }
  321. defer closePairNow(t, pca, pcb)
  322. var wg sync.WaitGroup
  323. dcChan := make(chan datachannel.ReadWriteCloser)
  324. pcb.OnDataChannel(func(dc *DataChannel) {
  325. if dc.Label() != label {
  326. return
  327. }
  328. log.Debug("OnDataChannel was called")
  329. dc.OnOpen(func() {
  330. detached, err2 := dc.Detach()
  331. if err2 != nil {
  332. log.Debugf("Detach failed: %s", err2.Error())
  333. t.Error(err2)
  334. }
  335. dcChan <- detached
  336. })
  337. })
  338. wg.Add(1)
  339. go func() {
  340. defer wg.Done()
  341. var msg []byte
  342. log.Debug("Waiting for OnDataChannel")
  343. dc := <-dcChan
  344. log.Debug("data channel opened")
  345. defer func() { assert.NoError(t, dc.Close(), "should succeed") }()
  346. log.Debug("Waiting for ping...")
  347. msg, err2 := ioutil.ReadAll(dc)
  348. log.Debugf("Received ping! \"%s\"", string(msg))
  349. if err2 != nil {
  350. t.Error(err2)
  351. }
  352. if !bytes.Equal(msg, testData) {
  353. t.Errorf("expected %q, got %q", string(msg), string(testData))
  354. } else {
  355. log.Debug("Received ping successfully!")
  356. }
  357. }()
  358. if err = signalPair(pca, pcb); err != nil {
  359. t.Fatal(err)
  360. }
  361. attached, err := pca.CreateDataChannel(label, nil)
  362. if err != nil {
  363. t.Fatal(err)
  364. }
  365. log.Debug("Waiting for data channel to open")
  366. open := make(chan struct{})
  367. attached.OnOpen(func() {
  368. open <- struct{}{}
  369. })
  370. <-open
  371. log.Debug("data channel opened")
  372. var dc io.ReadWriteCloser
  373. dc, err = attached.Detach()
  374. if err != nil {
  375. t.Fatal(err)
  376. }
  377. wg.Add(1)
  378. go func() {
  379. defer wg.Done()
  380. log.Debug("Sending ping...")
  381. if _, err2 := dc.Write(testData); err2 != nil {
  382. t.Error(err2)
  383. }
  384. log.Debug("Sent ping")
  385. assert.NoError(t, dc.Close(), "should succeed")
  386. log.Debug("Wating for EOF")
  387. ret, err2 := ioutil.ReadAll(dc)
  388. assert.Nil(t, err2, "should succeed")
  389. assert.Equal(t, 0, len(ret), "should be empty")
  390. }()
  391. wg.Wait()
  392. })
  393. t.Run("No detach", func(t *testing.T) {
  394. lim := test.TimeOut(time.Second * 5)
  395. defer lim.Stop()
  396. // Set up two peer connections.
  397. config := Configuration{}
  398. pca, err := NewPeerConnection(config)
  399. if err != nil {
  400. t.Fatal(err)
  401. }
  402. pcb, err := NewPeerConnection(config)
  403. if err != nil {
  404. t.Fatal(err)
  405. }
  406. defer closePairNow(t, pca, pcb)
  407. var dca, dcb *DataChannel
  408. dcaClosedCh := make(chan struct{})
  409. dcbClosedCh := make(chan struct{})
  410. pcb.OnDataChannel(func(dc *DataChannel) {
  411. if dc.Label() != label {
  412. return
  413. }
  414. log.Debugf("pcb: new datachannel: %s", dc.Label())
  415. dcb = dc
  416. // Register channel opening handling
  417. dcb.OnOpen(func() {
  418. log.Debug("pcb: datachannel opened")
  419. })
  420. dcb.OnClose(func() {
  421. // (2)
  422. log.Debug("pcb: data channel closed")
  423. close(dcbClosedCh)
  424. })
  425. // Register the OnMessage to handle incoming messages
  426. log.Debug("pcb: registering onMessage callback")
  427. dcb.OnMessage(func(dcMsg DataChannelMessage) {
  428. log.Debugf("pcb: received ping: %s", string(dcMsg.Data))
  429. if !reflect.DeepEqual(dcMsg.Data, testData) {
  430. t.Error("data mismatch")
  431. }
  432. })
  433. })
  434. dca, err = pca.CreateDataChannel(label, nil)
  435. if err != nil {
  436. t.Fatal(err)
  437. }
  438. dca.OnOpen(func() {
  439. log.Debug("pca: data channel opened")
  440. log.Debugf("pca: sending \"%s\"", string(testData))
  441. if err := dca.Send(testData); err != nil {
  442. t.Fatal(err)
  443. }
  444. log.Debug("pca: sent ping")
  445. assert.NoError(t, dca.Close(), "should succeed") // <-- dca closes
  446. })
  447. dca.OnClose(func() {
  448. // (1)
  449. log.Debug("pca: data channel closed")
  450. close(dcaClosedCh)
  451. })
  452. // Register the OnMessage to handle incoming messages
  453. log.Debug("pca: registering onMessage callback")
  454. dca.OnMessage(func(dcMsg DataChannelMessage) {
  455. log.Debugf("pca: received pong: %s", string(dcMsg.Data))
  456. if !reflect.DeepEqual(dcMsg.Data, testData) {
  457. t.Error("data mismatch")
  458. }
  459. })
  460. if err := signalPair(pca, pcb); err != nil {
  461. t.Fatal(err)
  462. }
  463. // When dca closes the channel,
  464. // (1) dca.Onclose() will fire immediately, then
  465. // (2) dcb.OnClose will also fire
  466. <-dcaClosedCh // (1)
  467. <-dcbClosedCh // (2)
  468. })
  469. }
  470. // Assert that a Session Description that doesn't follow
  471. // draft-ietf-mmusic-sctp-sdp is still accepted
  472. func TestDataChannel_NonStandardSessionDescription(t *testing.T) {
  473. to := test.TimeOut(time.Second * 20)
  474. defer to.Stop()
  475. report := test.CheckRoutines(t)
  476. defer report()
  477. offerPC, answerPC, err := newPair()
  478. assert.NoError(t, err)
  479. _, err = offerPC.CreateDataChannel("foo", nil)
  480. assert.NoError(t, err)
  481. onDataChannelCalled := make(chan struct{})
  482. answerPC.OnDataChannel(func(_ *DataChannel) {
  483. close(onDataChannelCalled)
  484. })
  485. offer, err := offerPC.CreateOffer(nil)
  486. assert.NoError(t, err)
  487. offerGatheringComplete := GatheringCompletePromise(offerPC)
  488. assert.NoError(t, offerPC.SetLocalDescription(offer))
  489. <-offerGatheringComplete
  490. offer = *offerPC.LocalDescription()
  491. // Replace with old values
  492. const (
  493. oldApplication = "m=application 63743 DTLS/SCTP 5000\r"
  494. oldAttribute = "a=sctpmap:5000 webrtc-datachannel 256\r"
  495. )
  496. offer.SDP = regexp.MustCompile(`m=application (.*?)\r`).ReplaceAllString(offer.SDP, oldApplication)
  497. offer.SDP = regexp.MustCompile(`a=sctp-port(.*?)\r`).ReplaceAllString(offer.SDP, oldAttribute)
  498. // Assert that replace worked
  499. assert.True(t, strings.Contains(offer.SDP, oldApplication))
  500. assert.True(t, strings.Contains(offer.SDP, oldAttribute))
  501. assert.NoError(t, answerPC.SetRemoteDescription(offer))
  502. answer, err := answerPC.CreateAnswer(nil)
  503. assert.NoError(t, err)
  504. answerGatheringComplete := GatheringCompletePromise(answerPC)
  505. assert.NoError(t, answerPC.SetLocalDescription(answer))
  506. <-answerGatheringComplete
  507. assert.NoError(t, offerPC.SetRemoteDescription(*answerPC.LocalDescription()))
  508. <-onDataChannelCalled
  509. closePairNow(t, offerPC, answerPC)
  510. }
  511. func TestDataChannel_Dial(t *testing.T) {
  512. t.Run("handler should be called once, by dialing peer only", func(t *testing.T) {
  513. report := test.CheckRoutines(t)
  514. defer report()
  515. dialCalls := make(chan bool, 2)
  516. wg := new(sync.WaitGroup)
  517. wg.Add(2)
  518. offerPC, answerPC, err := newPair()
  519. if err != nil {
  520. t.Fatalf("Failed to create a PC pair for testing")
  521. }
  522. answerPC.OnDataChannel(func(d *DataChannel) {
  523. if d.Label() != expectedLabel {
  524. return
  525. }
  526. d.OnDial(func() {
  527. // only dialing side should fire OnDial
  528. t.Fatalf("answering side should not call on dial")
  529. })
  530. d.OnOpen(wg.Done)
  531. })
  532. d, err := offerPC.CreateDataChannel(expectedLabel, nil)
  533. assert.NoError(t, err)
  534. d.OnDial(func() {
  535. dialCalls <- true
  536. wg.Done()
  537. })
  538. assert.NoError(t, signalPair(offerPC, answerPC))
  539. wg.Wait()
  540. closePairNow(t, offerPC, answerPC)
  541. assert.Len(t, dialCalls, 1)
  542. })
  543. t.Run("handler should be called immediately if already dialed", func(t *testing.T) {
  544. report := test.CheckRoutines(t)
  545. defer report()
  546. done := make(chan bool)
  547. offerPC, answerPC, err := newPair()
  548. if err != nil {
  549. t.Fatalf("Failed to create a PC pair for testing")
  550. }
  551. d, err := offerPC.CreateDataChannel(expectedLabel, nil)
  552. assert.NoError(t, err)
  553. d.OnOpen(func() {
  554. // when the offer DC has been opened, its guaranteed to have dialed since it has
  555. // received a response to said dial. this test represents an unrealistic usage,
  556. // but its the best way to guarantee we "missed" the dial event and still invoke
  557. // the handler.
  558. d.OnDial(func() {
  559. done <- true
  560. })
  561. })
  562. assert.NoError(t, signalPair(offerPC, answerPC))
  563. closePair(t, offerPC, answerPC, done)
  564. })
  565. }