templates.js (17060B)
1 /** 2 * Default list of commands to execute for a PeerConnection test. 3 */ 4 5 const STABLE = "stable"; 6 const HAVE_LOCAL_OFFER = "have-local-offer"; 7 const HAVE_REMOTE_OFFER = "have-remote-offer"; 8 const CLOSED = "closed"; 9 10 const ICE_NEW = "new"; 11 const GATH_NEW = "new"; 12 const GATH_GATH = "gathering"; 13 const GATH_COMPLETE = "complete"; 14 15 function deltaSeconds(date1, date2) { 16 return (date2.getTime() - date1.getTime()) / 1000; 17 } 18 19 function dumpSdp(test) { 20 if (typeof test._local_offer !== "undefined") { 21 dump("ERROR: SDP offer: " + test._local_offer.sdp.replace(/[\r]/g, "")); 22 } 23 if (typeof test._remote_answer !== "undefined") { 24 dump("ERROR: SDP answer: " + test._remote_answer.sdp.replace(/[\r]/g, "")); 25 } 26 27 if ( 28 test.pcLocal && 29 typeof test.pcLocal._local_ice_candidates !== "undefined" 30 ) { 31 dump( 32 "pcLocal._local_ice_candidates: " + 33 JSON.stringify(test.pcLocal._local_ice_candidates) + 34 "\n" 35 ); 36 dump( 37 "pcLocal._remote_ice_candidates: " + 38 JSON.stringify(test.pcLocal._remote_ice_candidates) + 39 "\n" 40 ); 41 dump( 42 "pcLocal._ice_candidates_to_add: " + 43 JSON.stringify(test.pcLocal._ice_candidates_to_add) + 44 "\n" 45 ); 46 } 47 if ( 48 test.pcRemote && 49 typeof test.pcRemote._local_ice_candidates !== "undefined" 50 ) { 51 dump( 52 "pcRemote._local_ice_candidates: " + 53 JSON.stringify(test.pcRemote._local_ice_candidates) + 54 "\n" 55 ); 56 dump( 57 "pcRemote._remote_ice_candidates: " + 58 JSON.stringify(test.pcRemote._remote_ice_candidates) + 59 "\n" 60 ); 61 dump( 62 "pcRemote._ice_candidates_to_add: " + 63 JSON.stringify(test.pcRemote._ice_candidates_to_add) + 64 "\n" 65 ); 66 } 67 68 if (test.pcLocal && typeof test.pcLocal.iceConnectionLog !== "undefined") { 69 dump( 70 "pcLocal ICE connection state log: " + 71 test.pcLocal.iceConnectionLog + 72 "\n" 73 ); 74 } 75 if (test.pcRemote && typeof test.pcRemote.iceConnectionLog !== "undefined") { 76 dump( 77 "pcRemote ICE connection state log: " + 78 test.pcRemote.iceConnectionLog + 79 "\n" 80 ); 81 } 82 83 if ( 84 test.pcLocal && 85 test.pcRemote && 86 typeof test.pcLocal.setRemoteDescDate !== "undefined" && 87 typeof test.pcRemote.setLocalDescDate !== "undefined" 88 ) { 89 var delta = deltaSeconds( 90 test.pcLocal.setRemoteDescDate, 91 test.pcRemote.setLocalDescDate 92 ); 93 dump( 94 "Delay between pcLocal.setRemote <-> pcRemote.setLocal: " + delta + "\n" 95 ); 96 } 97 if ( 98 test.pcLocal && 99 test.pcRemote && 100 typeof test.pcLocal.setRemoteDescDate !== "undefined" && 101 typeof test.pcLocal.setRemoteDescStableEventDate !== "undefined" 102 ) { 103 var delta = deltaSeconds( 104 test.pcLocal.setRemoteDescDate, 105 test.pcLocal.setRemoteDescStableEventDate 106 ); 107 dump( 108 "Delay between pcLocal.setRemote <-> pcLocal.signalingStateStable: " + 109 delta + 110 "\n" 111 ); 112 } 113 if ( 114 test.pcLocal && 115 test.pcRemote && 116 typeof test.pcRemote.setLocalDescDate !== "undefined" && 117 typeof test.pcRemote.setLocalDescStableEventDate !== "undefined" 118 ) { 119 var delta = deltaSeconds( 120 test.pcRemote.setLocalDescDate, 121 test.pcRemote.setLocalDescStableEventDate 122 ); 123 dump( 124 "Delay between pcRemote.setLocal <-> pcRemote.signalingStateStable: " + 125 delta + 126 "\n" 127 ); 128 } 129 } 130 131 // We need to verify that at least one candidate has been (or will be) gathered. 132 function waitForAnIceCandidate(pc) { 133 return new Promise(resolve => { 134 if (!pc.localRequiresTrickleIce || pc._new_local_ice_candidates.length) { 135 resolve(); 136 } else { 137 // In some circumstances, especially when both PCs are on the same 138 // browser, even though we are connected, the connection can be 139 // established without receiving a single candidate from one or other 140 // peer. So we wait for at least one... 141 pc._pc.addEventListener("icecandidate", resolve); 142 } 143 }).then(() => { 144 ok( 145 pc._local_ice_candidates.length, 146 pc + " received local trickle ICE candidates" 147 ); 148 isnot( 149 pc._pc.iceGatheringState, 150 GATH_NEW, 151 pc + " ICE gathering state is not 'new'" 152 ); 153 }); 154 } 155 156 async function checkTrackStats(pc, track, outbound) { 157 const audio = track.kind == "audio"; 158 const msg = 159 `${pc} stats ${outbound ? "outbound " : "inbound "}` + 160 `${audio ? "audio" : "video"} rtp track id ${track.id}`; 161 const stats = await pc.getStats(track); 162 ok( 163 pc.hasStat(stats, { 164 type: outbound ? "outbound-rtp" : "inbound-rtp", 165 kind: audio ? "audio" : "video", 166 }), 167 `${msg} - found expected stats` 168 ); 169 ok( 170 !pc.hasStat(stats, { 171 type: outbound ? "inbound-rtp" : "outbound-rtp", 172 }), 173 `${msg} - did not find extra stats with wrong direction` 174 ); 175 ok( 176 !pc.hasStat(stats, { 177 kind: audio ? "video" : "audio", 178 }), 179 `${msg} - did not find extra stats with wrong media type` 180 ); 181 } 182 183 function checkAllTrackStats(pc) { 184 return Promise.all([ 185 ...pc 186 .getExpectedActiveReceivers() 187 .map(({ track }) => checkTrackStats(pc, track, false)), 188 ...pc 189 .getExpectedSenders() 190 .map(({ track }) => checkTrackStats(pc, track, true)), 191 ]); 192 } 193 194 // Commands run once at the beginning of each test, even when performing a 195 // renegotiation test. 196 var commandsPeerConnectionInitial = [ 197 function PC_LOCAL_SETUP_ICE_LOGGER(test) { 198 test.pcLocal.logIceConnectionState(); 199 }, 200 201 function PC_REMOTE_SETUP_ICE_LOGGER(test) { 202 test.pcRemote.logIceConnectionState(); 203 }, 204 205 function PC_LOCAL_SETUP_SIGNALING_LOGGER(test) { 206 test.pcLocal.logSignalingState(); 207 }, 208 209 function PC_REMOTE_SETUP_SIGNALING_LOGGER(test) { 210 test.pcRemote.logSignalingState(); 211 }, 212 213 function PC_LOCAL_SETUP_TRACK_HANDLER(test) { 214 test.pcLocal.setupTrackEventHandler(); 215 }, 216 217 function PC_REMOTE_SETUP_TRACK_HANDLER(test) { 218 test.pcRemote.setupTrackEventHandler(); 219 }, 220 221 function PC_LOCAL_CHECK_INITIAL_SIGNALINGSTATE(test) { 222 is( 223 test.pcLocal.signalingState, 224 STABLE, 225 "Initial local signalingState is 'stable'" 226 ); 227 }, 228 229 function PC_REMOTE_CHECK_INITIAL_SIGNALINGSTATE(test) { 230 is( 231 test.pcRemote.signalingState, 232 STABLE, 233 "Initial remote signalingState is 'stable'" 234 ); 235 }, 236 237 function PC_LOCAL_CHECK_INITIAL_ICE_STATE(test) { 238 is( 239 test.pcLocal.iceConnectionState, 240 ICE_NEW, 241 "Initial local ICE connection state is 'new'" 242 ); 243 }, 244 245 function PC_REMOTE_CHECK_INITIAL_ICE_STATE(test) { 246 is( 247 test.pcRemote.iceConnectionState, 248 ICE_NEW, 249 "Initial remote ICE connection state is 'new'" 250 ); 251 }, 252 253 function PC_LOCAL_CHECK_INITIAL_CAN_TRICKLE_SYNC(test) { 254 is( 255 test.pcLocal._pc.canTrickleIceCandidates, 256 null, 257 "Local trickle status should start out unknown" 258 ); 259 }, 260 261 function PC_REMOTE_CHECK_INITIAL_CAN_TRICKLE_SYNC(test) { 262 is( 263 test.pcRemote._pc.canTrickleIceCandidates, 264 null, 265 "Remote trickle status should start out unknown" 266 ); 267 }, 268 ]; 269 270 var commandsGetUserMedia = [ 271 function PC_LOCAL_GUM(test) { 272 return test.pcLocal.getAllUserMediaAndAddStreams(test.pcLocal.constraints); 273 }, 274 275 function PC_REMOTE_GUM(test) { 276 return test.pcRemote.getAllUserMediaAndAddStreams( 277 test.pcRemote.constraints 278 ); 279 }, 280 ]; 281 282 var commandsPeerConnectionOfferAnswer = [ 283 function PC_LOCAL_SETUP_ICE_HANDLER(test) { 284 test.pcLocal.setupIceCandidateHandler(test); 285 }, 286 287 function PC_REMOTE_SETUP_ICE_HANDLER(test) { 288 test.pcRemote.setupIceCandidateHandler(test); 289 }, 290 291 function PC_LOCAL_CREATE_OFFER(test) { 292 return test.createOffer(test.pcLocal).then(offer => { 293 is( 294 test.pcLocal.signalingState, 295 STABLE, 296 "Local create offer does not change signaling state" 297 ); 298 }); 299 }, 300 301 function PC_LOCAL_SET_LOCAL_DESCRIPTION(test) { 302 return test 303 .setLocalDescription(test.pcLocal, test.originalOffer, HAVE_LOCAL_OFFER) 304 .then(() => { 305 is( 306 test.pcLocal.signalingState, 307 HAVE_LOCAL_OFFER, 308 "signalingState after local setLocalDescription is 'have-local-offer'" 309 ); 310 }); 311 }, 312 313 function PC_REMOTE_GET_OFFER(test) { 314 test._local_offer = test.originalOffer; 315 test._offer_constraints = test.pcLocal.constraints; 316 test._offer_options = test.pcLocal.offerOptions; 317 return Promise.resolve(); 318 }, 319 320 function PC_REMOTE_SET_REMOTE_DESCRIPTION(test) { 321 return test 322 .setRemoteDescription(test.pcRemote, test._local_offer, HAVE_REMOTE_OFFER) 323 .then(() => { 324 is( 325 test.pcRemote.signalingState, 326 HAVE_REMOTE_OFFER, 327 "signalingState after remote setRemoteDescription is 'have-remote-offer'" 328 ); 329 }); 330 }, 331 332 function PC_REMOTE_CHECK_CAN_TRICKLE_SYNC(test) { 333 is( 334 test.pcRemote._pc.canTrickleIceCandidates, 335 true, 336 "Remote thinks that local can trickle" 337 ); 338 }, 339 340 function PC_LOCAL_SANE_LOCAL_SDP(test) { 341 test.pcLocal.localRequiresTrickleIce = sdputils.verifySdp( 342 test._local_offer, 343 "offer", 344 test._offer_constraints, 345 test._offer_options, 346 test.testOptions 347 ); 348 }, 349 350 function PC_REMOTE_SANE_REMOTE_SDP(test) { 351 test.pcRemote.remoteRequiresTrickleIce = sdputils.verifySdp( 352 test._local_offer, 353 "offer", 354 test._offer_constraints, 355 test._offer_options, 356 test.testOptions 357 ); 358 }, 359 360 function PC_REMOTE_CREATE_ANSWER(test) { 361 return test.createAnswer(test.pcRemote).then(answer => { 362 is( 363 test.pcRemote.signalingState, 364 HAVE_REMOTE_OFFER, 365 "Remote createAnswer does not change signaling state" 366 ); 367 }); 368 }, 369 370 function PC_REMOTE_SET_LOCAL_DESCRIPTION(test) { 371 return test 372 .setLocalDescription(test.pcRemote, test.originalAnswer, STABLE) 373 .then(() => { 374 is( 375 test.pcRemote.signalingState, 376 STABLE, 377 "signalingState after remote setLocalDescription is 'stable'" 378 ); 379 }); 380 }, 381 382 function PC_LOCAL_GET_ANSWER(test) { 383 test._remote_answer = test.originalAnswer; 384 test._answer_constraints = test.pcRemote.constraints; 385 return Promise.resolve(); 386 }, 387 388 function PC_LOCAL_SET_REMOTE_DESCRIPTION(test) { 389 return test 390 .setRemoteDescription(test.pcLocal, test._remote_answer, STABLE) 391 .then(() => { 392 is( 393 test.pcLocal.signalingState, 394 STABLE, 395 "signalingState after local setRemoteDescription is 'stable'" 396 ); 397 }); 398 }, 399 400 function PC_REMOTE_SANE_LOCAL_SDP(test) { 401 test.pcRemote.localRequiresTrickleIce = sdputils.verifySdp( 402 test._remote_answer, 403 "answer", 404 test._offer_constraints, 405 test._offer_options, 406 test.testOptions 407 ); 408 }, 409 function PC_LOCAL_SANE_REMOTE_SDP(test) { 410 test.pcLocal.remoteRequiresTrickleIce = sdputils.verifySdp( 411 test._remote_answer, 412 "answer", 413 test._offer_constraints, 414 test._offer_options, 415 test.testOptions 416 ); 417 }, 418 419 function PC_LOCAL_CHECK_CAN_TRICKLE_SYNC(test) { 420 is( 421 test.pcLocal._pc.canTrickleIceCandidates, 422 true, 423 "Local thinks that remote can trickle" 424 ); 425 }, 426 427 function PC_LOCAL_WAIT_FOR_ICE_CONNECTED(test) { 428 return test.pcLocal.waitForIceConnected().then(() => { 429 info( 430 test.pcLocal + 431 ": ICE connection state log: " + 432 test.pcLocal.iceConnectionLog 433 ); 434 }); 435 }, 436 437 function PC_REMOTE_WAIT_FOR_ICE_CONNECTED(test) { 438 return test.pcRemote.waitForIceConnected().then(() => { 439 info( 440 test.pcRemote + 441 ": ICE connection state log: " + 442 test.pcRemote.iceConnectionLog 443 ); 444 }); 445 }, 446 447 function PC_LOCAL_VERIFY_ICE_GATHERING(test) { 448 return waitForAnIceCandidate(test.pcLocal); 449 }, 450 451 function PC_REMOTE_VERIFY_ICE_GATHERING(test) { 452 return waitForAnIceCandidate(test.pcRemote); 453 }, 454 455 function PC_LOCAL_WAIT_FOR_MEDIA_FLOW(test) { 456 return test.pcLocal.waitForMediaFlow(); 457 }, 458 459 function PC_REMOTE_WAIT_FOR_MEDIA_FLOW(test) { 460 return test.pcRemote.waitForMediaFlow(); 461 }, 462 463 function PC_LOCAL_CHECK_STATS(test) { 464 return test.pcLocal.getStats().then(stats => { 465 test.pcLocal.checkStats(stats); 466 }); 467 }, 468 469 function PC_REMOTE_CHECK_STATS(test) { 470 return test.pcRemote.getStats().then(stats => { 471 test.pcRemote.checkStats(stats); 472 }); 473 }, 474 475 function PC_LOCAL_CHECK_ICE_CONNECTION_TYPE(test) { 476 return test.pcLocal.getStats().then(stats => { 477 test.pcLocal.checkStatsIceConnectionType( 478 stats, 479 test.testOptions.expectedLocalCandidateType 480 ); 481 }); 482 }, 483 484 function PC_REMOTE_CHECK_ICE_CONNECTION_TYPE(test) { 485 return test.pcRemote.getStats().then(stats => { 486 test.pcRemote.checkStatsIceConnectionType( 487 stats, 488 test.testOptions.expectedRemoteCandidateType 489 ); 490 }); 491 }, 492 493 function PC_LOCAL_CHECK_ICE_CONNECTIONS(test) { 494 return test.pcLocal.getStats().then(stats => { 495 test.pcLocal.checkStatsIceConnections(stats, test.testOptions); 496 }); 497 }, 498 499 function PC_REMOTE_CHECK_ICE_CONNECTIONS(test) { 500 return test.pcRemote.getStats().then(stats => { 501 test.pcRemote.checkStatsIceConnections(stats, test.testOptions); 502 }); 503 }, 504 505 function PC_LOCAL_CHECK_MSID(test) { 506 return test.pcLocal.checkLocalMsids(); 507 }, 508 function PC_REMOTE_CHECK_MSID(test) { 509 return test.pcRemote.checkLocalMsids(); 510 }, 511 512 function PC_LOCAL_CHECK_TRACK_STATS(test) { 513 return checkAllTrackStats(test.pcLocal); 514 }, 515 function PC_REMOTE_CHECK_TRACK_STATS(test) { 516 return checkAllTrackStats(test.pcRemote); 517 }, 518 function PC_LOCAL_VERIFY_SDP_AFTER_END_OF_TRICKLE(test) { 519 if (test.pcLocal.endOfTrickleSdp) { 520 /* In case the endOfTrickleSdp promise is resolved already it will win the 521 * race because it gets evaluated first. But if endOfTrickleSdp is still 522 * pending the rejection will win the race. */ 523 return Promise.race([ 524 test.pcLocal.endOfTrickleSdp, 525 Promise.reject("No SDP"), 526 ]).then( 527 sdp => 528 sdputils.checkSdpAfterEndOfTrickle( 529 sdp, 530 test.testOptions, 531 test.pcLocal.label 532 ), 533 () => 534 info( 535 "pcLocal: Gathering is not complete yet, skipping post-gathering SDP check" 536 ) 537 ); 538 } 539 }, 540 function PC_REMOTE_VERIFY_SDP_AFTER_END_OF_TRICKLE(test) { 541 if (test.pcRemote.endOfTrickleSdp) { 542 /* In case the endOfTrickleSdp promise is resolved already it will win the 543 * race because it gets evaluated first. But if endOfTrickleSdp is still 544 * pending the rejection will win the race. */ 545 return Promise.race([ 546 test.pcRemote.endOfTrickleSdp, 547 Promise.reject("No SDP"), 548 ]).then( 549 sdp => 550 sdputils.checkSdpAfterEndOfTrickle( 551 sdp, 552 test.testOptions, 553 test.pcRemote.label 554 ), 555 () => 556 info( 557 "pcRemote: Gathering is not complete yet, skipping post-gathering SDP check" 558 ) 559 ); 560 } 561 }, 562 ]; 563 564 function PC_LOCAL_REMOVE_ALL_BUT_H264_FROM_OFFER(test) { 565 isnot( 566 test.originalOffer.sdp.search("H264/90000"), 567 -1, 568 "H.264 should be present in the SDP offer" 569 ); 570 test.originalOffer.sdp = sdputils.removeAllButCodec( 571 test.originalOffer.sdp, 572 "H264" 573 ); 574 info("Updated H264 only offer: " + JSON.stringify(test.originalOffer)); 575 } 576 577 function PC_LOCAL_REMOVE_ALL_BUT_AV1_FROM_OFFER(test) { 578 isnot( 579 test.originalOffer.sdp.search("AV1/90000"), 580 -1, 581 "AV1 should be present in the SDP offer" 582 ); 583 test.originalOffer.sdp = sdputils.removeAllButCodec( 584 test.originalOffer.sdp, 585 "AV1" 586 ); 587 info("Updated AV1 only offer: " + JSON.stringify(test.originalOffer)); 588 } 589 590 function PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER(test) { 591 test.originalOffer.sdp = sdputils.removeBundle(test.originalOffer.sdp); 592 info("Updated no bundle offer: " + JSON.stringify(test.originalOffer)); 593 } 594 595 function PC_LOCAL_REMOVE_RTCPMUX_FROM_OFFER(test) { 596 test.originalOffer.sdp = sdputils.removeRtcpMux(test.originalOffer.sdp); 597 info("Updated no RTCP-Mux offer: " + JSON.stringify(test.originalOffer)); 598 } 599 600 function PC_LOCAL_REMOVE_SSRC_FROM_OFFER(test) { 601 test.originalOffer.sdp = sdputils.removeSSRCs(test.originalOffer.sdp); 602 info("Updated no SSRCs offer: " + JSON.stringify(test.originalOffer)); 603 } 604 605 function PC_REMOTE_REMOVE_SSRC_FROM_ANSWER(test) { 606 test.originalAnswer.sdp = sdputils.removeSSRCs(test.originalAnswer.sdp); 607 info("Updated no SSRCs answer: " + JSON.stringify(test.originalAnswer)); 608 } 609 610 var addRenegotiation = (chain, commands, checks) => { 611 chain.append(commands); 612 chain.append(commandsPeerConnectionOfferAnswer); 613 if (checks) { 614 chain.append(checks); 615 } 616 }; 617 618 var addRenegotiationAnswerer = (chain, commands, checks) => { 619 chain.append(function SWAP_PC_LOCAL_PC_REMOTE(test) { 620 var temp = test.pcLocal; 621 test.pcLocal = test.pcRemote; 622 test.pcRemote = temp; 623 }); 624 addRenegotiation(chain, commands, checks); 625 };