| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 1 | <!doctype html> | 
|  | 2 | <meta charset=utf-8> | 
|  | 3 | <title>RTCPeerConnection.prototype.getStats</title> | 
|  | 4 | <script src="/resources/testharness.js"></script> | 
|  | 5 | <script src="/resources/testharnessreport.js"></script> | 
|  | 6 | <script src="RTCPeerConnection-helper.js"></script> | 
| Soares Chen | 4f92d91 | 2017-07-19 05:10:40 | [diff] [blame] | 7 | <script src="dictionary-helper.js"></script> | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 8 | <script src="RTCStats-helper.js"></script> | 
|  | 9 | <script> | 
|  | 10 | 'use strict'; | 
|  | 11 |  | 
|  | 12 | // Test is based on the following editor draft: | 
|  | 13 | // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 14 | // https://w3c.github.io/webrtc-stats/archives/20170614/webrtc-stats.html | 
|  | 15 |  | 
|  | 16 | // The following helper function is called from RTCPeerConnection-helper.js | 
|  | 17 | // getTrackFromUserMedia | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 18 |  | 
| Soares Chen | 4f92d91 | 2017-07-19 05:10:40 | [diff] [blame] | 19 | // The following helper function is called from RTCStats-helper.js | 
|  | 20 | // validateStatsReport | 
|  | 21 | // assert_stats_report_has_stats | 
|  | 22 |  | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 23 | /* | 
|  | 24 | 8.2. RTCPeerConnection Interface Extensions | 
|  | 25 | partial interface RTCPeerConnection { | 
|  | 26 | Promise<RTCStatsReport> getStats(optional MediaStreamTrack? selector = null); | 
|  | 27 | }; | 
|  | 28 |  | 
|  | 29 | 8.3. RTCStatsReport Object | 
|  | 30 | interface RTCStatsReport { | 
|  | 31 | readonly maplike<DOMString, object>; | 
|  | 32 | }; | 
|  | 33 |  | 
|  | 34 | 8.4. RTCStats Dictionary | 
|  | 35 | dictionary RTCStats { | 
|  | 36 | DOMHighResTimeStamp timestamp; | 
|  | 37 | RTCStatsType type; | 
|  | 38 | DOMString id; | 
|  | 39 | }; | 
|  | 40 |  | 
|  | 41 | id | 
|  | 42 | Two RTCStats objects, extracted from two different RTCStatsReport objects, MUST | 
|  | 43 | have the same id if they were produced by inspecting the same underlying object. | 
|  | 44 |  | 
|  | 45 | 8.2. getStats | 
|  | 46 | 1. Let selectorArg be the method's first argument. | 
|  | 47 | 2. Let connection be the RTCPeerConnection object on which the method was invoked. | 
|  | 48 | 3. If selectorArg is neither null nor a valid MediaStreamTrack, return a promise | 
|  | 49 | rejected with a newly created TypeError. | 
|  | 50 | 5. Let p be a new promise. | 
|  | 51 | 6. Run the following steps in parallel: | 
|  | 52 | 1. Gather the stats indicated by selector according to the stats selection algorithm. | 
|  | 53 | 2. Resolve p with the resulting RTCStatsReport object, containing the gathered stats. | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 54 | */ | 
|  | 55 |  | 
|  | 56 | promise_test(() => { | 
|  | 57 | const pc = new RTCPeerConnection(); | 
|  | 58 | return pc.getStats(); | 
|  | 59 | }, 'getStats() with no argument should succeed'); | 
|  | 60 |  | 
|  | 61 | promise_test(() => { | 
|  | 62 | const pc = new RTCPeerConnection(); | 
|  | 63 | return pc.getStats(null); | 
|  | 64 | }, 'getStats(null) should succeed'); | 
|  | 65 |  | 
|  | 66 | /* | 
|  | 67 | 8.2. getStats | 
|  | 68 | 4. Let selector be a RTCRtpSender or RTCRtpReceiver on connection which track | 
|  | 69 | member matches selectorArg. If no such sender or receiver exists, return a promise | 
|  | 70 | rejected with a newly created InvalidAccessError. | 
|  | 71 | */ | 
|  | 72 | promise_test(t => { | 
|  | 73 | const pc = new RTCPeerConnection(); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 74 | return getTrackFromUserMedia('audio') | 
|  | 75 | .then(([track, mediaStream]) => { | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 76 | return promise_rejects(t, 'InvalidAccessError', pc.getStats(track)); | 
|  | 77 | }); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 78 | }, 'getStats() with track not added to connection should reject with InvalidAccessError'); | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 79 |  | 
|  | 80 | promise_test(t => { | 
|  | 81 | const pc = new RTCPeerConnection(); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 82 | return getTrackFromUserMedia('audio') | 
|  | 83 | .then(([track, mediaStream]) => { | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 84 | pc.addTrack(track, mediaStream); | 
|  | 85 | return pc.getStats(track); | 
|  | 86 | }); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 87 | }, 'getStats() with track added via addTrack should succeed'); | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 88 |  | 
|  | 89 | promise_test(t => { | 
|  | 90 | const pc = new RTCPeerConnection(); | 
|  | 91 | const track = generateMediaStreamTrack(); | 
|  | 92 | pc.addTransceiver(track); | 
|  | 93 |  | 
|  | 94 | return pc.getStats(track); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 95 | }, 'getStats() with track added via addTransceiver should succeed'); | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 96 |  | 
|  | 97 | /* | 
|  | 98 | 8.2. getStats | 
|  | 99 | 4. Let selector be a RTCRtpSender or RTCRtpReceiver on connection which track | 
|  | 100 | member matches selectorArg. If more than one sender or receiver fit this criteria, | 
|  | 101 | return a promise rejected with a newly created InvalidAccessError. | 
|  | 102 | */ | 
|  | 103 | promise_test(t => { | 
|  | 104 | const pc = new RTCPeerConnection(); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 105 | return getTrackFromUserMedia('audio') | 
|  | 106 | .then(([track, mediaStream]) => { | 
|  | 107 | // addTransceiver allows adding same track multiple times | 
|  | 108 | const transceiver1 = pc.addTransceiver(track); | 
|  | 109 | const transceiver2 = pc.addTransceiver(track); | 
|  | 110 |  | 
|  | 111 | assert_not_equals(transceiver1, transceiver2); | 
|  | 112 | assert_not_equals(transciever1.sender, transceiver2.sender); | 
|  | 113 | assert_equals(transceiver1.sender.track, transceiver2.sender.track); | 
|  | 114 |  | 
|  | 115 | return promise_rejects(t, 'InvalidAccessError', pc.getStats(track)); | 
|  | 116 | }); | 
|  | 117 | }, `getStats() with track associated with more than one sender should reject with InvalidAccessError`); | 
|  | 118 |  | 
|  | 119 | promise_test(t => { | 
|  | 120 | const pc = new RTCPeerConnection(); | 
|  | 121 | const transceiver1 = pc.addTransceiver('audio'); | 
|  | 122 |  | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 123 | // Create another transceiver that resends what | 
|  | 124 | // is being received, kind of like echo | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 125 | const transceiver2 = pc.addTransceiver(transceiver1.receiver.track); | 
|  | 126 | assert_equals(transceiver1.receiver.track, transceiver2.sender.track); | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 127 |  | 
|  | 128 | return promise_rejects(t, 'InvalidAccessError', pc.getStats(track)); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 129 | }, 'getStats() with track associated with both sender and receiver should reject with InvalidAccessError'); | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 130 |  | 
|  | 131 | /* | 
|  | 132 | 8.5. The stats selection algorithm | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 133 | 2. If selector is null, gather stats for the whole connection, add them to result, | 
|  | 134 | return result, and abort these steps. | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 135 | */ | 
|  | 136 | promise_test(t => { | 
|  | 137 | const pc = new RTCPeerConnection(); | 
|  | 138 | return pc.getStats() | 
|  | 139 | .then(statsReport => { | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 140 | validateStatsReport(statsReport); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 141 | assert_stats_report_has_stats(statsReport, ['peer-connection']); | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 142 | }); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 143 | }, 'getStats() with no argument should return stats report containing peer-connection stats'); | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 144 |  | 
|  | 145 | /* | 
|  | 146 | 8.5. The stats selection algorithm | 
|  | 147 | 3. If selector is an RTCRtpSender, gather stats for and add the following objects | 
|  | 148 | to result: | 
|  | 149 | - All RTCOutboundRTPStreamStats objects corresponding to selector. | 
|  | 150 | - All stats objects referenced directly or indirectly by the RTCOutboundRTPStreamStats | 
|  | 151 | objects added. | 
|  | 152 | */ | 
|  | 153 | promise_test(() => { | 
|  | 154 | const pc = new RTCPeerConnection(); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 155 | return getTrackFromUserMedia('audio') | 
|  | 156 | .then(([track, mediaStream]) => { | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 157 | pc.addTrack(track, mediaStream); | 
|  | 158 |  | 
|  | 159 | return pc.getStats(track) | 
|  | 160 | .then(statsReport => { | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 161 | validateStatsReport(statsReport); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 162 | assert_stats_report_has_stats(statsReport, ['outbound-rtp']); | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 163 | }); | 
|  | 164 | }); | 
| Soares Chen | 389b21a | 2017-07-14 04:54:44 | [diff] [blame] | 165 | }, `getStats() on track associated with RtpSender should return stats report containing outbound-rtp stats`); | 
|  | 166 |  | 
|  | 167 |  | 
|  | 168 | /* | 
|  | 169 | 8.5. The stats selection algorithm | 
|  | 170 | 4. If selector is an RTCRtpReceiver, gather stats for and add the following objects | 
|  | 171 | to result: | 
|  | 172 | - All RTCInboundRTPStreamStats objects corresponding to selector. | 
|  | 173 | - All stats objects referenced directly or indirectly by the RTCInboundRTPStreamStats | 
|  | 174 | added. | 
|  | 175 | */ | 
|  | 176 | promise_test(() => { | 
|  | 177 | const pc = new RTCPeerConnection(); | 
|  | 178 | const transceiver = pc.addTransceiver('audio'); | 
|  | 179 |  | 
|  | 180 | return pc.getStats(transceiver.receiver.track) | 
|  | 181 | .then(statsReport => { | 
|  | 182 | validateStatsReport(statsReport); | 
|  | 183 | assert_stats_report_has_stats(statsReport, ['inbound-rtp']); | 
|  | 184 | }); | 
|  | 185 | }, `getStats() on track associated with RtpReceiver should return stats report containing inbound-rtp stats`); | 
| Soares Chen | 0ac9ba4 | 2017-07-13 10:08:11 | [diff] [blame] | 186 |  | 
|  | 187 | </script> |