summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webrtc/RTCDtlsTransport-state.html
blob: ca49fcc95f297e07889ecf32c21a35b9e311b68b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<!doctype html>
<meta charset="utf-8">
<title>RTCDtlsTransport</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="RTCPeerConnection-helper.js"></script>
<script>
'use strict';

// The following helper functions are called from RTCPeerConnection-helper.js:
//   exchangeIceCandidates
//   exchangeOfferAnswer
//   trackFactories.audio()

/*
    5.5.  RTCDtlsTransport Interface
      interface RTCDtlsTransport : EventTarget {
        readonly attribute RTCDtlsTransportState state;
        sequence<ArrayBuffer> getRemoteCertificates();
                 attribute EventHandler          onstatechange;
                 attribute EventHandler          onerror;
        ...
      };

      enum RTCDtlsTransportState {
        "new",
        "connecting",
        "connected",
        "closed",
        "failed"
      };

*/
function resolveWhen(t, dtlstransport, state) {
  return new Promise((resolve, reject) => {
    if (dtlstransport.state == state) { resolve(); }
    dtlstransport.addEventListener('statechange', t.step_func(e => {
      if (dtlstransport.state == state) {
        resolve();
      }
    }));
  });
}


async function setupConnections(t) {
  const pc1 = new RTCPeerConnection();
  t.add_cleanup(() => pc1.close());
  const pc2 = new RTCPeerConnection();
  t.add_cleanup(() => pc2.close());

  pc1.addTrack(trackFactories.audio());
  const channels = exchangeIceCandidates(pc1, pc2);
  await exchangeOfferAnswer(pc1, pc2);
  return [pc1, pc2];
}

promise_test(async t => {
  const [pc1, pc2] = await setupConnections(t);
  const dtlsTransport1 = pc1.getTransceivers()[0].sender.transport;
  const dtlsTransport2 = pc2.getTransceivers()[0].sender.transport;
  assert_true(dtlsTransport1 instanceof RTCDtlsTransport);
  assert_true(dtlsTransport2 instanceof RTCDtlsTransport);
  await Promise.all([resolveWhen(t, dtlsTransport1, 'connected'),
                     resolveWhen(t, dtlsTransport2, 'connected')]);
}, 'DTLS transport goes to connected state');

promise_test(async t => {
  const [pc1, pc2] = await setupConnections(t);

  const dtlsTransport1 = pc1.getTransceivers()[0].sender.transport;
  const dtlsTransport2 = pc2.getTransceivers()[0].sender.transport;
  await Promise.all([resolveWhen(t, dtlsTransport1, 'connected'),
                     resolveWhen(t, dtlsTransport2, 'connected')]);
  pc1.close();
  assert_equals(dtlsTransport1.state, 'closed');
}, 'close() causes the local transport to close immediately');

promise_test(async t => {
  const [pc1, pc2] = await setupConnections(t);
  const dtlsTransport1 = pc1.getTransceivers()[0].sender.transport;
  const dtlsTransport2 = pc2.getTransceivers()[0].sender.transport;
  await Promise.all([resolveWhen(t, dtlsTransport1, 'connected'),
                     resolveWhen(t, dtlsTransport2, 'connected')]);
  pc1.close();
  await resolveWhen(t, dtlsTransport2, 'closed');
}, 'close() causes the other end\'s DTLS transport to close');

promise_test(async t => {
  const config = {bundlePolicy: "max-bundle"};
  const pc1 = new RTCPeerConnection(config);
  const pc2 = new RTCPeerConnection(config);
  t.add_cleanup(() => pc1.close());
  t.add_cleanup(() => pc2.close());

  pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
  pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);

  pc1.addTransceiver("video");
  pc1.addTransceiver("audio");
  await pc1.setLocalDescription(await pc1.createOffer());
  await pc2.setRemoteDescription(pc1.localDescription);
  await pc2.setLocalDescription(await pc2.createAnswer());
  await pc1.setRemoteDescription(pc2.localDescription);

  const [videoTc, audioTc] = pc1.getTransceivers();
  const [videoTp, audioTp] =
      pc1.getTransceivers().map(tc => tc.sender.transport);

  const [videoPc2Tp, audioPc2Tp] =
      pc2.getTransceivers().map(tc => tc.sender.transport);

  assert_equals(pc1.getTransceivers().length, 2, 'pc1 transceiver count');
  assert_equals(pc2.getTransceivers().length, 2, 'pc2 transceiver count');
  assert_equals(videoTc.sender.transport, videoTc.receiver.transport);
  assert_equals(videoTc.sender.transport, audioTc.sender.transport);

  await Promise.all([resolveWhen(t, videoTp, 'connected'),
                     resolveWhen(t, videoPc2Tp, 'connected')]);

  assert_equals(audioTc.sender, pc1.getSenders()[1]);

  let stoppedTransceiver = pc1.getTransceivers()[0];
  assert_equals(stoppedTransceiver, videoTc); // sanity
  let onended = new Promise(resolve => {
    stoppedTransceiver.receiver.track.onended = resolve;
  });
  stoppedTransceiver.stop();
  await onended;

  assert_equals(
      pc1.getReceivers().length, 1,
      'getReceivers does not expose a receiver of a stopped transceiver');
  assert_equals(
      pc1.getSenders().length, 1,
      'getSenders does not expose a sender of a stopped transceiver');
  assert_equals(audioTc.sender, pc1.getSenders()[0]); // sanity
  assert_equals(audioTc.sender.transport, audioTp); // sanity
  assert_equals(audioTp.state, 'connected');
}, 'stop bundled transceiver retains dtls transport state');

</script>