blob: d198e82cd9bb0afd8a98ad179f47cc370f523711 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h"
#include <string>
#include "base/bind.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/platform/web_rtc_peer_connection_handler.h"
#include "third_party/blink/public/platform/web_rtc_rtp_receiver.h"
#include "third_party/blink/public/platform/web_rtc_rtp_sender.h"
#include "third_party/blink/public/platform/web_rtc_session_description.h"
#include "third_party/blink/public/platform/web_rtc_session_description_request.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/web/web_heap.h"
#include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_void_function.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_peer_connection_error_callback.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_session_description_callback.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_answer_options.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_configuration.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_ice_server.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_offer_options.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_session_description_init.h"
#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h"
#include "third_party/webrtc/api/rtc_error.h"
#include "v8/include/v8.h"
namespace blink {
static const char* kOfferSdpUnifiedPlanSingleAudioSingleVideo =
"v=0\r\n"
"o=- 6676943034916303038 2 IN IP4 127.0.0.1\r\n"
"s=-\r\n"
"t=0 0\r\n"
"a=group:BUNDLE 0 1\r\n"
"a=msid-semantic: WMS\r\n"
"m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
"126\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp:9 IN IP4 0.0.0.0\r\n"
"a=ice-ufrag:pKAt\r\n"
"a=ice-pwd:bDmIGcCbVl+VkMymNfwdE/Mv\r\n"
"a=ice-options:trickle\r\n"
"a=fingerprint:sha-256 "
"F2:D4:95:C5:FC:98:F2:7E:6F:6C:46:BF:5E:05:00:56:4F:A9:BC:4B:1E:56:98:C1:"
"68:BF:5E:7D:01:A3:EC:93\r\n"
"a=setup:actpass\r\n"
"a=mid:0\r\n"
"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
"a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
"a=sendrecv\r\n"
"a=msid:- 36f80301-b634-4c5a-a03b-d1ad79997531\r\n"
"a=rtcp-mux\r\n"
"a=rtpmap:111 opus/48000/2\r\n"
"a=rtcp-fb:111 transport-cc\r\n"
"a=fmtp:111 minptime=10;useinbandfec=1\r\n"
"a=rtpmap:103 ISAC/16000\r\n"
"a=rtpmap:104 ISAC/32000\r\n"
"a=rtpmap:9 G722/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:106 CN/32000\r\n"
"a=rtpmap:105 CN/16000\r\n"
"a=rtpmap:13 CN/8000\r\n"
"a=rtpmap:110 telephone-event/48000\r\n"
"a=rtpmap:112 telephone-event/32000\r\n"
"a=rtpmap:113 telephone-event/16000\r\n"
"a=rtpmap:126 telephone-event/8000\r\n"
"a=ssrc:4264546776 cname:GkUsSfx+DbDplYYT\r\n"
"a=ssrc:4264546776 msid: 36f80301-b634-4c5a-a03b-d1ad79997531\r\n"
"a=ssrc:4264546776 mslabel:\r\n"
"a=ssrc:4264546776 label:36f80301-b634-4c5a-a03b-d1ad79997531\r\n"
"m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp:9 IN IP4 0.0.0.0\r\n"
"a=ice-ufrag:pKAt\r\n"
"a=ice-pwd:bDmIGcCbVl+VkMymNfwdE/Mv\r\n"
"a=ice-options:trickle\r\n"
"a=fingerprint:sha-256 "
"F2:D4:95:C5:FC:98:F2:7E:6F:6C:46:BF:5E:05:00:56:4F:A9:BC:4B:1E:56:98:C1:"
"68:BF:5E:7D:01:A3:EC:93\r\n"
"a=setup:actpass\r\n"
"a=mid:1\r\n"
"a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n"
"a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n"
"a=extmap:4 urn:3gpp:video-orientation\r\n"
"a=extmap:5 "
"http://www.ietf.org/id/"
"draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n"
"a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n"
"a=extmap:7 "
"http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n"
"a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n"
"a=extmap:10 "
"http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07\r\n"
"a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
"a=sendrecv\r\n"
"a=msid:- 0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
"a=rtcp-mux\r\n"
"a=rtcp-rsize\r\n"
"a=rtpmap:96 VP8/90000\r\n"
"a=rtcp-fb:96 goog-remb\r\n"
"a=rtcp-fb:96 transport-cc\r\n"
"a=rtcp-fb:96 ccm fir\r\n"
"a=rtcp-fb:96 nack\r\n"
"a=rtcp-fb:96 nack pli\r\n"
"a=rtpmap:97 rtx/90000\r\n"
"a=fmtp:97 apt=96\r\n"
"a=rtpmap:98 VP9/90000\r\n"
"a=rtcp-fb:98 goog-remb\r\n"
"a=rtcp-fb:98 transport-cc\r\n"
"a=rtcp-fb:98 ccm fir\r\n"
"a=rtcp-fb:98 nack\r\n"
"a=rtcp-fb:98 nack pli\r\n"
"a=fmtp:98 x-google-profile-id=0\r\n"
"a=rtpmap:99 rtx/90000\r\n"
"a=fmtp:99 apt=98\r\n"
"a=rtpmap:100 red/90000\r\n"
"a=rtpmap:101 rtx/90000\r\n"
"a=fmtp:101 apt=100\r\n"
"a=rtpmap:102 ulpfec/90000\r\n"
"a=ssrc-group:FID 680673332 1566706172\r\n"
"a=ssrc:680673332 cname:GkUsSfx+DbDplYYT\r\n"
"a=ssrc:680673332 msid: 0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
"a=ssrc:680673332 mslabel:\r\n"
"a=ssrc:680673332 label:0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
"a=ssrc:1566706172 cname:GkUsSfx+DbDplYYT\r\n"
"a=ssrc:1566706172 msid: 0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
"a=ssrc:1566706172 mslabel:\r\n"
"a=ssrc:1566706172 label:0db71b61-c1ae-4741-bcce-320a254244f3\r\n";
static const char* kOfferSdpUnifiedPlanMultipleAudioTracks =
"v=0\r\n"
"o=- 1821816752660535838 2 IN IP4 127.0.0.1\r\n"
"s=-\r\n"
"t=0 0\r\n"
"a=group:BUNDLE 0 1\r\n"
"a=msid-semantic: WMS\r\n"
"m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
"126\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp:9 IN IP4 0.0.0.0\r\n"
"a=ice-ufrag:rbEc\r\n"
"a=ice-pwd:vmDec3+MrTigDESzNiDuWBnD\r\n"
"a=ice-options:trickle\r\n"
"a=fingerprint:sha-256 "
"05:9B:0A:BC:B3:E1:B9:5C:A6:78:96:23:00:0F:96:71:7B:B0:3E:37:87:1D:3A:62:"
"5E:00:A5:27:22:BB:26:5D\r\n"
"a=setup:actpass\r\n"
"a=mid:0\r\n"
"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
"a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
"a=sendrecv\r\n"
"a=msid:- adcd8158-3ad7-4a1f-ac87-8711db959fe8\r\n"
"a=rtcp-mux\r\n"
"a=rtpmap:111 opus/48000/2\r\n"
"a=rtcp-fb:111 transport-cc\r\n"
"a=fmtp:111 minptime=10;useinbandfec=1\r\n"
"a=rtpmap:103 ISAC/16000\r\n"
"a=rtpmap:104 ISAC/32000\r\n"
"a=rtpmap:9 G722/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:106 CN/32000\r\n"
"a=rtpmap:105 CN/16000\r\n"
"a=rtpmap:13 CN/8000\r\n"
"a=rtpmap:110 telephone-event/48000\r\n"
"a=rtpmap:112 telephone-event/32000\r\n"
"a=rtpmap:113 telephone-event/16000\r\n"
"a=rtpmap:126 telephone-event/8000\r\n"
"a=ssrc:2988156579 cname:gr88KGUzymBvrIaJ\r\n"
"a=ssrc:2988156579 msid: adcd8158-3ad7-4a1f-ac87-8711db959fe8\r\n"
"a=ssrc:2988156579 mslabel:\r\n"
"a=ssrc:2988156579 label:adcd8158-3ad7-4a1f-ac87-8711db959fe8\r\n"
"m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
"126\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp:9 IN IP4 0.0.0.0\r\n"
"a=ice-ufrag:rbEc\r\n"
"a=ice-pwd:vmDec3+MrTigDESzNiDuWBnD\r\n"
"a=ice-options:trickle\r\n"
"a=fingerprint:sha-256 "
"05:9B:0A:BC:B3:E1:B9:5C:A6:78:96:23:00:0F:96:71:7B:B0:3E:37:87:1D:3A:62:"
"5E:00:A5:27:22:BB:26:5D\r\n"
"a=setup:actpass\r\n"
"a=mid:1\r\n"
"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
"a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
"a=sendrecv\r\n"
"a=msid:- b5f69d2c-e753-4eb5-a302-d41ee75f9fcb\r\n"
"a=rtcp-mux\r\n"
"a=rtpmap:111 opus/48000/2\r\n"
"a=rtcp-fb:111 transport-cc\r\n"
"a=fmtp:111 minptime=10;useinbandfec=1\r\n"
"a=rtpmap:103 ISAC/16000\r\n"
"a=rtpmap:104 ISAC/32000\r\n"
"a=rtpmap:9 G722/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:106 CN/32000\r\n"
"a=rtpmap:105 CN/16000\r\n"
"a=rtpmap:13 CN/8000\r\n"
"a=rtpmap:110 telephone-event/48000\r\n"
"a=rtpmap:112 telephone-event/32000\r\n"
"a=rtpmap:113 telephone-event/16000\r\n"
"a=rtpmap:126 telephone-event/8000\r\n"
"a=ssrc:2562757057 cname:gr88KGUzymBvrIaJ\r\n"
"a=ssrc:2562757057 msid: b5f69d2c-e753-4eb5-a302-d41ee75f9fcb\r\n"
"a=ssrc:2562757057 mslabel:\r\n"
"a=ssrc:2562757057 label:b5f69d2c-e753-4eb5-a302-d41ee75f9fcb\r\n";
static const char* kOfferSdpPlanBSingleAudioSingleVideo =
"v=0\r\n"
"o=- 267029810971159627 2 IN IP4 127.0.0.1\r\n"
"s=-\r\n"
"t=0 0\r\n"
"a=group:BUNDLE audio video\r\n"
"a=msid-semantic: WMS 655e92b8-9130-44d8-a188-f5f4633d1a8d "
"b15218e5-f921-4988-9e1f-6e50ecbd24c2\r\n"
"m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
"126\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp:9 IN IP4 0.0.0.0\r\n"
"a=ice-ufrag:ErlQ\r\n"
"a=ice-pwd:VCnwY8XlD9EX4gpcOHRhU0HV\r\n"
"a=ice-options:trickle\r\n"
"a=fingerprint:sha-256 "
"AC:30:90:F9:3B:CB:9A:0D:C6:FB:F3:D6:D6:97:4F:40:A2:B9:5E:4D:F5:32:DC:A7:"
"B0:3A:33:82:C8:67:FF:7A\r\n"
"a=setup:actpass\r\n"
"a=mid:audio\r\n"
"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
"a=sendrecv\r\n"
"a=rtcp-mux\r\n"
"a=rtpmap:111 opus/48000/2\r\n"
"a=rtcp-fb:111 transport-cc\r\n"
"a=fmtp:111 minptime=10;useinbandfec=1\r\n"
"a=rtpmap:103 ISAC/16000\r\n"
"a=rtpmap:104 ISAC/32000\r\n"
"a=rtpmap:9 G722/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:106 CN/32000\r\n"
"a=rtpmap:105 CN/16000\r\n"
"a=rtpmap:13 CN/8000\r\n"
"a=rtpmap:110 telephone-event/48000\r\n"
"a=rtpmap:112 telephone-event/32000\r\n"
"a=rtpmap:113 telephone-event/16000\r\n"
"a=rtpmap:126 telephone-event/8000\r\n"
"a=ssrc:1670492497 cname:rNEKgm1NFupmwR4x\r\n"
"a=ssrc:1670492497 msid:b15218e5-f921-4988-9e1f-6e50ecbd24c2 "
"089fd06c-73e4-4720-a6dc-e182eeaeced7\r\n"
"a=ssrc:1670492497 mslabel:b15218e5-f921-4988-9e1f-6e50ecbd24c2\r\n"
"a=ssrc:1670492497 label:089fd06c-73e4-4720-a6dc-e182eeaeced7\r\n"
"m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp:9 IN IP4 0.0.0.0\r\n"
"a=ice-ufrag:ErlQ\r\n"
"a=ice-pwd:VCnwY8XlD9EX4gpcOHRhU0HV\r\n"
"a=ice-options:trickle\r\n"
"a=fingerprint:sha-256 "
"AC:30:90:F9:3B:CB:9A:0D:C6:FB:F3:D6:D6:97:4F:40:A2:B9:5E:4D:F5:32:DC:A7:"
"B0:3A:33:82:C8:67:FF:7A\r\n"
"a=setup:actpass\r\n"
"a=mid:video\r\n"
"a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n"
"a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n"
"a=extmap:4 urn:3gpp:video-orientation\r\n"
"a=extmap:5 "
"http://www.ietf.org/id/"
"draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n"
"a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n"
"a=extmap:7 "
"http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n"
"a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n"
"a=extmap:10 "
"http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07\r\n"
"a=sendrecv\r\n"
"a=rtcp-mux\r\n"
"a=rtcp-rsize\r\n"
"a=rtpmap:96 VP8/90000\r\n"
"a=rtcp-fb:96 goog-remb\r\n"
"a=rtcp-fb:96 transport-cc\r\n"
"a=rtcp-fb:96 ccm fir\r\n"
"a=rtcp-fb:96 nack\r\n"
"a=rtcp-fb:96 nack pli\r\n"
"a=rtpmap:97 rtx/90000\r\n"
"a=fmtp:97 apt=96\r\n"
"a=rtpmap:98 VP9/90000\r\n"
"a=rtcp-fb:98 goog-remb\r\n"
"a=rtcp-fb:98 transport-cc\r\n"
"a=rtcp-fb:98 ccm fir\r\n"
"a=rtcp-fb:98 nack\r\n"
"a=rtcp-fb:98 nack pli\r\n"
"a=fmtp:98 x-google-profile-id=0\r\n"
"a=rtpmap:99 rtx/90000\r\n"
"a=fmtp:99 apt=98\r\n"
"a=rtpmap:100 red/90000\r\n"
"a=rtpmap:101 rtx/90000\r\n"
"a=fmtp:101 apt=100\r\n"
"a=rtpmap:102 ulpfec/90000\r\n"
"a=ssrc-group:FID 3263949794 2166305097\r\n"
"a=ssrc:3263949794 cname:rNEKgm1NFupmwR4x\r\n"
"a=ssrc:3263949794 msid:655e92b8-9130-44d8-a188-f5f4633d1a8d "
"6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n"
"a=ssrc:3263949794 mslabel:655e92b8-9130-44d8-a188-f5f4633d1a8d\r\n"
"a=ssrc:3263949794 label:6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n"
"a=ssrc:2166305097 cname:rNEKgm1NFupmwR4x\r\n"
"a=ssrc:2166305097 msid:655e92b8-9130-44d8-a188-f5f4633d1a8d "
"6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n"
"a=ssrc:2166305097 mslabel:655e92b8-9130-44d8-a188-f5f4633d1a8d\r\n"
"a=ssrc:2166305097 label:6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n";
static const char* kOfferSdpPlanBMultipleAudioTracks =
"v=0\r\n"
"o=- 6228437149521864740 2 IN IP4 127.0.0.1\r\n"
"s=-\r\n"
"t=0 0\r\n"
"a=group:BUNDLE audio\r\n"
"a=msid-semantic: WMS 46f8615e-7599-49f3-9a45-3cf0faf58614 "
"e01b7c23-2b77-4e09-bee7-4b9140e49647\r\n"
"m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
"126\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp:9 IN IP4 0.0.0.0\r\n"
"a=ice-ufrag:Nzla\r\n"
"a=ice-pwd:PL1APGM2pr773UoUOsj8jzBI\r\n"
"a=ice-options:trickle\r\n"
"a=fingerprint:sha-256 "
"DF:8F:89:33:68:AB:55:26:4E:81:CF:95:8C:71:B7:89:45:E7:05:7A:5D:A8:CF:BF:"
"60:AA:C7:42:F2:85:23:1D\r\n"
"a=setup:actpass\r\n"
"a=mid:audio\r\n"
"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
"a=sendrecv\r\n"
"a=rtcp-mux\r\n"
"a=rtpmap:111 opus/48000/2\r\n"
"a=rtcp-fb:111 transport-cc\r\n"
"a=fmtp:111 minptime=10;useinbandfec=1\r\n"
"a=rtpmap:103 ISAC/16000\r\n"
"a=rtpmap:104 ISAC/32000\r\n"
"a=rtpmap:9 G722/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:106 CN/32000\r\n"
"a=rtpmap:105 CN/16000\r\n"
"a=rtpmap:13 CN/8000\r\n"
"a=rtpmap:110 telephone-event/48000\r\n"
"a=rtpmap:112 telephone-event/32000\r\n"
"a=rtpmap:113 telephone-event/16000\r\n"
"a=rtpmap:126 telephone-event/8000\r\n"
"a=ssrc:2716812081 cname:0QgfsHYGSuZjeg5/\r\n"
"a=ssrc:2716812081 msid:e01b7c23-2b77-4e09-bee7-4b9140e49647 "
"d73d8a47-3d3f-408f-a2ce-2270eb44ffc5\r\n"
"a=ssrc:2716812081 mslabel:e01b7c23-2b77-4e09-bee7-4b9140e49647\r\n"
"a=ssrc:2716812081 label:d73d8a47-3d3f-408f-a2ce-2270eb44ffc5\r\n"
"a=ssrc:4092260337 cname:0QgfsHYGSuZjeg5/\r\n"
"a=ssrc:4092260337 msid:46f8615e-7599-49f3-9a45-3cf0faf58614 "
"6b5f436e-f85d-40a1-83e4-acec63ca4b82\r\n"
"a=ssrc:4092260337 mslabel:46f8615e-7599-49f3-9a45-3cf0faf58614\r\n"
"a=ssrc:4092260337 label:6b5f436e-f85d-40a1-83e4-acec63ca4b82\r\n";
template <typename PlatformSupportType>
class RTCPeerConnectionTestWithPlatformTestingPlatformType
: public testing::Test {
public:
RTCPeerConnection* CreatePC(V8TestingScope& scope,
const String& sdpSemantics = String()) {
RTCConfiguration* config = RTCConfiguration::Create();
config->setSdpSemantics(sdpSemantics);
RTCIceServer* ice_server = RTCIceServer::Create();
ice_server->setURL("stun:fake.stun.url");
HeapVector<Member<RTCIceServer>> ice_servers;
ice_servers.push_back(ice_server);
config->setIceServers(ice_servers);
return RTCPeerConnection::Create(scope.GetExecutionContext(), config,
Dictionary(), scope.GetExceptionState());
}
MediaStreamTrack* CreateTrack(V8TestingScope& scope,
MediaStreamSource::StreamType type,
String id) {
MediaStreamSource* source =
MediaStreamSource::Create("sourceId", type, "sourceName", false);
MediaStreamComponent* component = MediaStreamComponent::Create(id, source);
return MediaStreamTrack::Create(scope.GetExecutionContext(), component);
}
std::string GetExceptionMessage(V8TestingScope& scope) {
ExceptionState& exception_state = scope.GetExceptionState();
return exception_state.HadException()
? exception_state.Message().Utf8().data()
: "";
}
void AddStream(V8TestingScope& scope,
RTCPeerConnection* pc,
MediaStream* stream) {
pc->addStream(scope.GetScriptState(), stream, Dictionary(),
scope.GetExceptionState());
EXPECT_EQ("", GetExceptionMessage(scope));
}
void RemoveStream(V8TestingScope& scope,
RTCPeerConnection* pc,
MediaStream* stream) {
pc->removeStream(stream, scope.GetExceptionState());
EXPECT_EQ("", GetExceptionMessage(scope));
}
protected:
ScopedTestingPlatformSupport<PlatformSupportType> platform_;
};
class RTCPeerConnectionTest
: public RTCPeerConnectionTestWithPlatformTestingPlatformType<
TestingPlatformSupportWithWebRTC> {
public:
};
TEST_F(RTCPeerConnectionTest, GetAudioTrack) {
V8TestingScope scope;
RTCPeerConnection* pc = CreatePC(scope);
EXPECT_EQ("", GetExceptionMessage(scope));
ASSERT_TRUE(pc);
MediaStreamTrack* track =
CreateTrack(scope, MediaStreamSource::kTypeAudio, "audioTrack");
HeapVector<Member<MediaStreamTrack>> tracks;
tracks.push_back(track);
MediaStream* stream =
MediaStream::Create(scope.GetExecutionContext(), tracks);
ASSERT_TRUE(stream);
EXPECT_FALSE(pc->GetTrack(track->Component()));
AddStream(scope, pc, stream);
EXPECT_TRUE(pc->GetTrack(track->Component()));
}
TEST_F(RTCPeerConnectionTest, GetVideoTrack) {
V8TestingScope scope;
RTCPeerConnection* pc = CreatePC(scope);
EXPECT_EQ("", GetExceptionMessage(scope));
ASSERT_TRUE(pc);
MediaStreamTrack* track =
CreateTrack(scope, MediaStreamSource::kTypeVideo, "videoTrack");
HeapVector<Member<MediaStreamTrack>> tracks;
tracks.push_back(track);
MediaStream* stream =
MediaStream::Create(scope.GetExecutionContext(), tracks);
ASSERT_TRUE(stream);
EXPECT_FALSE(pc->GetTrack(track->Component()));
AddStream(scope, pc, stream);
EXPECT_TRUE(pc->GetTrack(track->Component()));
}
TEST_F(RTCPeerConnectionTest, GetAudioAndVideoTrack) {
V8TestingScope scope;
RTCPeerConnection* pc = CreatePC(scope);
EXPECT_EQ("", GetExceptionMessage(scope));
ASSERT_TRUE(pc);
HeapVector<Member<MediaStreamTrack>> tracks;
MediaStreamTrack* audio_track =
CreateTrack(scope, MediaStreamSource::kTypeAudio, "audioTrack");
tracks.push_back(audio_track);
MediaStreamTrack* video_track =
CreateTrack(scope, MediaStreamSource::kTypeVideo, "videoTrack");
tracks.push_back(video_track);
MediaStream* stream =
MediaStream::Create(scope.GetExecutionContext(), tracks);
ASSERT_TRUE(stream);
EXPECT_FALSE(pc->GetTrack(audio_track->Component()));
EXPECT_FALSE(pc->GetTrack(video_track->Component()));
AddStream(scope, pc, stream);
EXPECT_TRUE(pc->GetTrack(audio_track->Component()));
EXPECT_TRUE(pc->GetTrack(video_track->Component()));
}
TEST_F(RTCPeerConnectionTest, GetTrackRemoveStreamAndGCAll) {
V8TestingScope scope;
Persistent<RTCPeerConnection> pc = CreatePC(scope);
EXPECT_EQ("", GetExceptionMessage(scope));
ASSERT_TRUE(pc);
MediaStreamTrack* track =
CreateTrack(scope, MediaStreamSource::kTypeAudio, "audioTrack");
MediaStreamComponent* track_component = track->Component();
{
HeapVector<Member<MediaStreamTrack>> tracks;
tracks.push_back(track);
MediaStream* stream =
MediaStream::Create(scope.GetExecutionContext(), tracks);
ASSERT_TRUE(stream);
EXPECT_FALSE(pc->GetTrack(track_component));
AddStream(scope, pc, stream);
EXPECT_TRUE(pc->GetTrack(track_component));
RemoveStream(scope, pc, stream);
// In Unified Plan, transceivers will still reference the stream even after
// it is "removed". To make the GC tests work, clear the stream from tracks
// so that the stream does not keep tracks alive.
while (!stream->getTracks().IsEmpty())
stream->removeTrack(stream->getTracks()[0], scope.GetExceptionState());
}
// This will destroy |MediaStream|, |MediaStreamTrack| and its
// |MediaStreamComponent|, which will remove its mapping from the peer
// connection.
WebHeap::CollectAllGarbageForTesting();
EXPECT_FALSE(pc->GetTrack(track_component));
}
TEST_F(RTCPeerConnectionTest,
GetTrackRemoveStreamAndGCWithPersistentComponent) {
V8TestingScope scope;
Persistent<RTCPeerConnection> pc = CreatePC(scope);
EXPECT_EQ("", GetExceptionMessage(scope));
ASSERT_TRUE(pc);
MediaStreamTrack* track =
CreateTrack(scope, MediaStreamSource::kTypeAudio, "audioTrack");
Persistent<MediaStreamComponent> track_component = track->Component();
{
HeapVector<Member<MediaStreamTrack>> tracks;
tracks.push_back(track);
MediaStream* stream =
MediaStream::Create(scope.GetExecutionContext(), tracks);
ASSERT_TRUE(stream);
EXPECT_FALSE(pc->GetTrack(track_component.Get()));
AddStream(scope, pc, stream);
EXPECT_TRUE(pc->GetTrack(track_component.Get()));
RemoveStream(scope, pc, stream);
// In Unified Plan, transceivers will still reference the stream even after
// it is "removed". To make the GC tests work, clear the stream from tracks
// so that the stream does not keep tracks alive.
while (!stream->getTracks().IsEmpty())
stream->removeTrack(stream->getTracks()[0], scope.GetExceptionState());
}
// This will destroy |MediaStream| and |MediaStreamTrack| (but not
// |MediaStreamComponent|), which will remove its mapping from the peer
// connection.
WebHeap::CollectAllGarbageForTesting();
EXPECT_FALSE(pc->GetTrack(track_component.Get()));
}
TEST_F(RTCPeerConnectionTest, CheckForComplexSdpWithSdpSemanticsPlanB) {
V8TestingScope scope;
Persistent<RTCPeerConnection> pc = CreatePC(scope, "plan-b");
RTCSessionDescriptionInit* sdp = RTCSessionDescriptionInit::Create();
sdp->setType("offer");
sdp->setSdp(kOfferSdpUnifiedPlanMultipleAudioTracks);
ASSERT_TRUE(pc->CheckForComplexSdp(sdp).has_value());
ASSERT_EQ(pc->CheckForComplexSdp(sdp),
ComplexSdpCategory::kUnifiedPlanExplicitSemantics);
sdp->setSdp(kOfferSdpPlanBMultipleAudioTracks);
ASSERT_TRUE(pc->CheckForComplexSdp(sdp).has_value());
ASSERT_EQ(pc->CheckForComplexSdp(sdp),
ComplexSdpCategory::kPlanBExplicitSemantics);
sdp->setSdp("invalid sdp");
ASSERT_TRUE(pc->CheckForComplexSdp(sdp).has_value());
ASSERT_EQ(pc->CheckForComplexSdp(sdp),
ComplexSdpCategory::kErrorExplicitSemantics);
// No Complex SDP is detected if only a single track per m= section is used.
sdp->setSdp(kOfferSdpUnifiedPlanSingleAudioSingleVideo);
ASSERT_FALSE(pc->CheckForComplexSdp(sdp).has_value());
sdp->setSdp(kOfferSdpPlanBSingleAudioSingleVideo);
ASSERT_FALSE(pc->CheckForComplexSdp(sdp).has_value());
}
TEST_F(RTCPeerConnectionTest, CheckForComplexSdpWithSdpSemanticsUnifiedPlan) {
V8TestingScope scope;
Persistent<RTCPeerConnection> pc = CreatePC(scope, "unified-plan");
RTCSessionDescriptionInit* sdp = RTCSessionDescriptionInit::Create();
sdp->setType("offer");
sdp->setSdp(kOfferSdpUnifiedPlanMultipleAudioTracks);
ASSERT_TRUE(pc->CheckForComplexSdp(sdp).has_value());
ASSERT_EQ(pc->CheckForComplexSdp(sdp),
ComplexSdpCategory::kUnifiedPlanExplicitSemantics);
sdp->setSdp(kOfferSdpPlanBMultipleAudioTracks);
ASSERT_TRUE(pc->CheckForComplexSdp(sdp).has_value());
ASSERT_EQ(pc->CheckForComplexSdp(sdp),
ComplexSdpCategory::kPlanBExplicitSemantics);
sdp->setSdp("invalid sdp");
ASSERT_TRUE(pc->CheckForComplexSdp(sdp).has_value());
ASSERT_EQ(pc->CheckForComplexSdp(sdp),
ComplexSdpCategory::kErrorExplicitSemantics);
// No Complex SDP is detected if only a single track per m= section is used.
sdp->setSdp(kOfferSdpUnifiedPlanSingleAudioSingleVideo);
ASSERT_FALSE(pc->CheckForComplexSdp(sdp).has_value());
sdp->setSdp(kOfferSdpPlanBSingleAudioSingleVideo);
ASSERT_FALSE(pc->CheckForComplexSdp(sdp).has_value());
}
TEST_F(RTCPeerConnectionTest, CheckForComplexSdpWithSdpSemanticsUnspecified) {
V8TestingScope scope;
Persistent<RTCPeerConnection> pc = CreatePC(scope);
RTCSessionDescriptionInit* sdp = RTCSessionDescriptionInit::Create();
sdp->setType("offer");
sdp->setSdp(kOfferSdpPlanBMultipleAudioTracks);
ASSERT_TRUE(pc->CheckForComplexSdp(sdp).has_value());
ASSERT_EQ(pc->CheckForComplexSdp(sdp),
ComplexSdpCategory::kPlanBImplicitSemantics);
sdp->setSdp(kOfferSdpUnifiedPlanMultipleAudioTracks);
ASSERT_TRUE(pc->CheckForComplexSdp(sdp).has_value());
ASSERT_EQ(pc->CheckForComplexSdp(sdp),
ComplexSdpCategory::kUnifiedPlanImplicitSemantics);
sdp->setSdp("invalid sdp");
ASSERT_TRUE(pc->CheckForComplexSdp(sdp).has_value());
ASSERT_EQ(pc->CheckForComplexSdp(sdp),
ComplexSdpCategory::kErrorImplicitSemantics);
// No Complex SDP is detected if only a single track per m= section is used.
sdp->setSdp(kOfferSdpUnifiedPlanSingleAudioSingleVideo);
ASSERT_FALSE(pc->CheckForComplexSdp(sdp).has_value());
sdp->setSdp(kOfferSdpPlanBSingleAudioSingleVideo);
ASSERT_FALSE(pc->CheckForComplexSdp(sdp).has_value());
}
enum class AsyncOperationAction {
kLeavePending,
kResolve,
kReject,
};
template <typename RequestType>
void CompleteRequest(RequestType request, bool resolve);
template <>
void CompleteRequest(WebRTCSessionDescriptionRequest request, bool resolve) {
if (resolve) {
WebRTCSessionDescription description =
WebRTCSessionDescription(WebString(), WebString());
request.RequestSucceeded(description);
} else {
request.RequestFailed(
webrtc::RTCError(webrtc::RTCErrorType::INVALID_MODIFICATION));
}
}
template <>
void CompleteRequest(WebRTCVoidRequest request, bool resolve) {
if (resolve) {
request.RequestSucceeded();
} else {
request.RequestFailed(
webrtc::RTCError(webrtc::RTCErrorType::INVALID_MODIFICATION));
}
}
template <typename RequestType>
void PostToCompleteRequest(AsyncOperationAction action,
const RequestType& request) {
switch (action) {
case AsyncOperationAction::kLeavePending:
return;
case AsyncOperationAction::kResolve:
scheduler::GetSequencedTaskRunnerForTesting()->PostTask(
FROM_HERE,
base::BindOnce(&CompleteRequest<RequestType>, request, true));
return;
case AsyncOperationAction::kReject:
scheduler::GetSequencedTaskRunnerForTesting()->PostTask(
FROM_HERE,
base::BindOnce(&CompleteRequest<RequestType>, request, false));
return;
}
}
class FakeWebRTCPeerConnectionHandler : public MockWebRTCPeerConnectionHandler {
public:
std::vector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
const WebRTCSessionDescriptionRequest& request,
const WebMediaConstraints&) override {
PostToCompleteRequest<WebRTCSessionDescriptionRequest>(
async_operation_action_, request);
return {};
}
std::vector<std::unique_ptr<WebRTCRtpTransceiver>> CreateOffer(
const WebRTCSessionDescriptionRequest& request,
const WebRTCOfferOptions&) override {
PostToCompleteRequest<WebRTCSessionDescriptionRequest>(
async_operation_action_, request);
return {};
}
void CreateAnswer(const WebRTCSessionDescriptionRequest& request,
const WebMediaConstraints&) override {
PostToCompleteRequest<WebRTCSessionDescriptionRequest>(
async_operation_action_, request);
}
void CreateAnswer(const WebRTCSessionDescriptionRequest& request,
const WebRTCAnswerOptions&) override {
PostToCompleteRequest<WebRTCSessionDescriptionRequest>(
async_operation_action_, request);
}
void SetLocalDescription(const WebRTCVoidRequest& request,
const WebRTCSessionDescription&) override {
PostToCompleteRequest<WebRTCVoidRequest>(async_operation_action_, request);
}
void SetRemoteDescription(const WebRTCVoidRequest& request,
const WebRTCSessionDescription&) override {
PostToCompleteRequest<WebRTCVoidRequest>(async_operation_action_, request);
}
void set_async_operation_action(AsyncOperationAction action) {
async_operation_action_ = action;
}
private:
// Decides what to do with future async operations' promises/callbacks.
AsyncOperationAction async_operation_action_ =
AsyncOperationAction::kLeavePending;
};
class TestingPlatformSupportWithFakeWebRTC : public TestingPlatformSupport {
public:
std::unique_ptr<WebRTCPeerConnectionHandler> CreateRTCPeerConnectionHandler(
WebRTCPeerConnectionHandlerClient*,
scoped_refptr<base::SingleThreadTaskRunner>) override {
handler_ = new FakeWebRTCPeerConnectionHandler();
return std::unique_ptr<WebRTCPeerConnectionHandler>(handler_);
}
FakeWebRTCPeerConnectionHandler* handler() const { return handler_; }
private:
FakeWebRTCPeerConnectionHandler* handler_;
};
// These tests verifies the code paths for notifying the peer connection's
// CallSetupStateTracker of offerer and answerer events. Because fakes are used
// the test can pass empty SDP around, deciding whether to resolve or reject
// based on controlling the fake instead of validify of SDP, and
// platform_->RunUntilIdle() is enough to ensure a pending operation has
// completed. Without fakes we would have had to await promises and callbacks,
// passing SDP returned by one operation to the next.
class RTCPeerConnectionCallSetupStateTest
: public RTCPeerConnectionTestWithPlatformTestingPlatformType<
TestingPlatformSupportWithFakeWebRTC> {
public:
RTCPeerConnection* Initialize(V8TestingScope& scope) {
RTCPeerConnection* pc = CreatePC(scope);
tracker_ = &pc->call_setup_state_tracker();
SetNextOperationIsSuccessful(true);
return pc;
}
void SetNextOperationIsSuccessful(bool successful) {
platform_->handler()->set_async_operation_action(
successful ? AsyncOperationAction::kResolve
: AsyncOperationAction::kReject);
}
RTCSessionDescriptionInit* EmptyOffer() {
auto* sdp = RTCSessionDescriptionInit::Create();
sdp->setType("offer");
return sdp;
}
RTCSessionDescriptionInit* EmptyAnswer() {
auto* sdp = RTCSessionDescriptionInit::Create();
sdp->setType("answer");
return sdp;
}
template <typename CallbackType>
CallbackType* CreateEmptyCallback(V8TestingScope& scope) {
v8::Local<v8::Function> v8_function =
v8::Function::New(scope.GetContext(), EmptyHandler).ToLocalChecked();
return CallbackType::Create(v8_function);
}
Dictionary ToDictionary(V8TestingScope& scope,
const IDLDictionaryBase* value) {
return Dictionary(
scope.GetIsolate(),
ToV8(value, scope.GetContext()->Global(), scope.GetIsolate()),
scope.GetExceptionState());
}
private:
static void EmptyHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {}
protected:
const CallSetupStateTracker* tracker_ = nullptr;
};
TEST_F(RTCPeerConnectionCallSetupStateTest, InitialState) {
V8TestingScope scope;
Initialize(scope);
EXPECT_EQ(OffererState::kNotStarted, tracker_->offerer_state());
EXPECT_EQ(AnswererState::kNotStarted, tracker_->answerer_state());
EXPECT_EQ(CallSetupState::kNotStarted, tracker_->GetCallSetupState());
}
TEST_F(RTCPeerConnectionCallSetupStateTest, OffererSucceeded) {
V8TestingScope scope;
RTCPeerConnection* pc = Initialize(scope);
// createOffer()
pc->createOffer(scope.GetScriptState(), RTCOfferOptions::Create());
EXPECT_EQ(OffererState::kCreateOfferPending, tracker_->offerer_state());
EXPECT_EQ(CallSetupState::kStarted, tracker_->GetCallSetupState());
platform_->RunUntilIdle();
EXPECT_EQ(OffererState::kCreateOfferResolved, tracker_->offerer_state());
// setLocalDescription(offer)
pc->setLocalDescription(scope.GetScriptState(), EmptyOffer());
EXPECT_EQ(OffererState::kSetLocalOfferPending, tracker_->offerer_state());
platform_->RunUntilIdle();
EXPECT_EQ(OffererState::kSetLocalOfferResolved, tracker_->offerer_state());
// setRemoteDescription(answer)
pc->setRemoteDescription(scope.GetScriptState(), EmptyAnswer());
EXPECT_EQ(OffererState::kSetRemoteAnswerPending, tracker_->offerer_state());
EXPECT_EQ(CallSetupState::kStarted, tracker_->GetCallSetupState());
platform_->RunUntilIdle();
EXPECT_EQ(OffererState::kSetRemoteAnswerResolved, tracker_->offerer_state());
EXPECT_EQ(CallSetupState::kSucceeded, tracker_->GetCallSetupState());
}
TEST_F(RTCPeerConnectionCallSetupStateTest, OffererFailedAtCreateOffer) {
V8TestingScope scope;
RTCPeerConnection* pc = Initialize(scope);
// createOffer()
SetNextOperationIsSuccessful(false);
pc->createOffer(scope.GetScriptState(), RTCOfferOptions::Create());
platform_->RunUntilIdle();
EXPECT_EQ(OffererState::kCreateOfferRejected, tracker_->offerer_state());
EXPECT_EQ(CallSetupState::kFailed, tracker_->GetCallSetupState());
}
TEST_F(RTCPeerConnectionCallSetupStateTest,
OffererFailedAtSetLocalDescription) {
V8TestingScope scope;
RTCPeerConnection* pc = Initialize(scope);
// createOffer()
pc->createOffer(scope.GetScriptState(), RTCOfferOptions::Create());
platform_->RunUntilIdle();
// setLocalDescription(offer)
SetNextOperationIsSuccessful(false);
pc->setLocalDescription(scope.GetScriptState(), EmptyOffer());
platform_->RunUntilIdle();
EXPECT_EQ(OffererState::kSetLocalOfferRejected, tracker_->offerer_state());
EXPECT_EQ(CallSetupState::kFailed, tracker_->GetCallSetupState());
}
TEST_F(RTCPeerConnectionCallSetupStateTest,
OffererFailedAtSetRemoteDescription) {
V8TestingScope scope;
RTCPeerConnection* pc = Initialize(scope);
// createOffer()
pc->createOffer(scope.GetScriptState(), RTCOfferOptions::Create());
platform_->RunUntilIdle();
// setLocalDescription(offer)
pc->setLocalDescription(scope.GetScriptState(), EmptyOffer());
platform_->RunUntilIdle();
// setRemoteDescription(answer)
SetNextOperationIsSuccessful(false);
pc->setRemoteDescription(scope.GetScriptState(), EmptyAnswer());
platform_->RunUntilIdle();
EXPECT_EQ(OffererState::kSetRemoteAnswerRejected, tracker_->offerer_state());
EXPECT_EQ(CallSetupState::kFailed, tracker_->GetCallSetupState());
}
TEST_F(RTCPeerConnectionCallSetupStateTest, OffererLegacyApiPath) {
V8TestingScope scope;
RTCPeerConnection* pc = Initialize(scope);
// Legacy createOffer() with callbacks
pc->createOffer(scope.GetScriptState(),
CreateEmptyCallback<V8RTCSessionDescriptionCallback>(scope),
CreateEmptyCallback<V8RTCPeerConnectionErrorCallback>(scope),
ToDictionary(scope, RTCOfferOptions::Create()),
scope.GetExceptionState());
EXPECT_EQ(OffererState::kCreateOfferPending, tracker_->offerer_state());
EXPECT_EQ(CallSetupState::kStarted, tracker_->GetCallSetupState());
platform_->RunUntilIdle();
EXPECT_EQ(OffererState::kCreateOfferResolved, tracker_->offerer_state());
// Legacy setLocalDescription(offer) with callbacks
pc->setLocalDescription(
scope.GetScriptState(), EmptyOffer(),
CreateEmptyCallback<V8VoidFunction>(scope),
CreateEmptyCallback<V8RTCPeerConnectionErrorCallback>(scope));
EXPECT_EQ(OffererState::kSetLocalOfferPending, tracker_->offerer_state());
platform_->RunUntilIdle();
EXPECT_EQ(OffererState::kSetLocalOfferResolved, tracker_->offerer_state());
// Legacy setRemoteDescription(answer) with callbacks
pc->setRemoteDescription(
scope.GetScriptState(), EmptyAnswer(),
CreateEmptyCallback<V8VoidFunction>(scope),
CreateEmptyCallback<V8RTCPeerConnectionErrorCallback>(scope));
EXPECT_EQ(OffererState::kSetRemoteAnswerPending, tracker_->offerer_state());
EXPECT_EQ(CallSetupState::kStarted, tracker_->GetCallSetupState());
platform_->RunUntilIdle();
EXPECT_EQ(OffererState::kSetRemoteAnswerResolved, tracker_->offerer_state());
EXPECT_EQ(CallSetupState::kSucceeded, tracker_->GetCallSetupState());
}
TEST_F(RTCPeerConnectionCallSetupStateTest, AnswererSucceeded) {
V8TestingScope scope;
RTCPeerConnection* pc = Initialize(scope);
// setRemoteDescription(offer)
pc->setRemoteDescription(scope.GetScriptState(), EmptyOffer());
EXPECT_EQ(AnswererState::kSetRemoteOfferPending, tracker_->answerer_state());
EXPECT_EQ(CallSetupState::kStarted, tracker_->GetCallSetupState());
platform_->RunUntilIdle();
EXPECT_EQ(AnswererState::kSetRemoteOfferResolved, tracker_->answerer_state());
// createAnswer()
pc->createAnswer(scope.GetScriptState(), RTCAnswerOptions::Create());
EXPECT_EQ(AnswererState::kCreateAnswerPending, tracker_->answerer_state());
platform_->RunUntilIdle();
EXPECT_EQ(AnswererState::kCreateAnswerResolved, tracker_->answerer_state());
// setLocalDescription(answer)
pc->setLocalDescription(scope.GetScriptState(), EmptyAnswer());
EXPECT_EQ(AnswererState::kSetLocalAnswerPending, tracker_->answerer_state());
EXPECT_EQ(CallSetupState::kStarted, tracker_->GetCallSetupState());
platform_->RunUntilIdle();
EXPECT_EQ(AnswererState::kSetLocalAnswerResolved, tracker_->answerer_state());
EXPECT_EQ(CallSetupState::kSucceeded, tracker_->GetCallSetupState());
}
TEST_F(RTCPeerConnectionCallSetupStateTest,
AnswererFailedAtSetRemoteDescription) {
V8TestingScope scope;
RTCPeerConnection* pc = Initialize(scope);
// setRemoteDescription(offer)
SetNextOperationIsSuccessful(false);
pc->setRemoteDescription(scope.GetScriptState(), EmptyOffer());
platform_->RunUntilIdle();
EXPECT_EQ(AnswererState::kSetRemoteOfferRejected, tracker_->answerer_state());
EXPECT_EQ(CallSetupState::kFailed, tracker_->GetCallSetupState());
}
TEST_F(RTCPeerConnectionCallSetupStateTest, AnswererFailedAtCreateAnswer) {
V8TestingScope scope;
RTCPeerConnection* pc = Initialize(scope);
// setRemoteDescription(offer)
pc->setRemoteDescription(scope.GetScriptState(), EmptyOffer());
platform_->RunUntilIdle();
// createAnswer()
SetNextOperationIsSuccessful(false);
pc->createAnswer(scope.GetScriptState(), RTCAnswerOptions::Create());
platform_->RunUntilIdle();
EXPECT_EQ(AnswererState::kCreateAnswerRejected, tracker_->answerer_state());
EXPECT_EQ(CallSetupState::kFailed, tracker_->GetCallSetupState());
}
TEST_F(RTCPeerConnectionCallSetupStateTest,
AnswererFailedAtSetLocalDescription) {
V8TestingScope scope;
RTCPeerConnection* pc = Initialize(scope);
// setRemoteDescription(offer)
pc->setRemoteDescription(scope.GetScriptState(), EmptyOffer());
platform_->RunUntilIdle();
// createAnswer()
pc->createAnswer(scope.GetScriptState(), RTCAnswerOptions::Create());
platform_->RunUntilIdle();
// setLocalDescription(answer)
SetNextOperationIsSuccessful(false);
pc->setLocalDescription(scope.GetScriptState(), EmptyAnswer());
platform_->RunUntilIdle();
EXPECT_EQ(AnswererState::kSetLocalAnswerRejected, tracker_->answerer_state());
EXPECT_EQ(CallSetupState::kFailed, tracker_->GetCallSetupState());
}
TEST_F(RTCPeerConnectionCallSetupStateTest, AnswererLegacyApiPath) {
V8TestingScope scope;
RTCPeerConnection* pc = Initialize(scope);
// Legacy setRemoteDescription(offer) with callbacks
pc->setRemoteDescription(
scope.GetScriptState(), EmptyOffer(),
CreateEmptyCallback<V8VoidFunction>(scope),
CreateEmptyCallback<V8RTCPeerConnectionErrorCallback>(scope));
EXPECT_EQ(AnswererState::kSetRemoteOfferPending, tracker_->answerer_state());
EXPECT_EQ(CallSetupState::kStarted, tracker_->GetCallSetupState());
platform_->RunUntilIdle();
EXPECT_EQ(AnswererState::kSetRemoteOfferResolved, tracker_->answerer_state());
// Legacy createAnswer() with callbacks
pc->createAnswer(scope.GetScriptState(),
CreateEmptyCallback<V8RTCSessionDescriptionCallback>(scope),
CreateEmptyCallback<V8RTCPeerConnectionErrorCallback>(scope),
Dictionary() /* media_constraints */);
EXPECT_EQ(AnswererState::kCreateAnswerPending, tracker_->answerer_state());
platform_->RunUntilIdle();
EXPECT_EQ(AnswererState::kCreateAnswerResolved, tracker_->answerer_state());
// Legacy setLocalDescription(answer) with callbacks
pc->setLocalDescription(
scope.GetScriptState(), EmptyAnswer(),
CreateEmptyCallback<V8VoidFunction>(scope),
CreateEmptyCallback<V8RTCPeerConnectionErrorCallback>(scope));
EXPECT_EQ(AnswererState::kSetLocalAnswerPending, tracker_->answerer_state());
EXPECT_EQ(CallSetupState::kStarted, tracker_->GetCallSetupState());
platform_->RunUntilIdle();
EXPECT_EQ(AnswererState::kSetLocalAnswerResolved, tracker_->answerer_state());
EXPECT_EQ(CallSetupState::kSucceeded, tracker_->GetCallSetupState());
}
// Test that simple Plan B is considered safe regardless of configurations.
TEST(DeduceSdpUsageCategory, SimplePlanBIsAlwaysSafe) {
// If the default is Plan B.
EXPECT_EQ(
SdpUsageCategory::kSafe,
DeduceSdpUsageCategory("offer", kOfferSdpPlanBSingleAudioSingleVideo,
false, webrtc::SdpSemantics::kPlanB));
// If the default is Unified Plan.
EXPECT_EQ(
SdpUsageCategory::kSafe,
DeduceSdpUsageCategory("offer", kOfferSdpPlanBSingleAudioSingleVideo,
false, webrtc::SdpSemantics::kUnifiedPlan));
// If sdpSemantics is explicitly set to Plan B.
EXPECT_EQ(
SdpUsageCategory::kSafe,
DeduceSdpUsageCategory("offer", kOfferSdpPlanBSingleAudioSingleVideo,
true, webrtc::SdpSemantics::kPlanB));
// If sdpSemantics is explicitly set to Unified Plan.
EXPECT_EQ(
SdpUsageCategory::kSafe,
DeduceSdpUsageCategory("offer", kOfferSdpPlanBSingleAudioSingleVideo,
true, webrtc::SdpSemantics::kUnifiedPlan));
}
// Test that simple Unified Plan is considered safe regardless of
// configurations.
TEST(DeduceSdpUsageCategory, SimpleUnifiedPlanIsAlwaysSafe) {
// If the default is Plan B.
EXPECT_EQ(SdpUsageCategory::kSafe,
DeduceSdpUsageCategory("offer",
kOfferSdpUnifiedPlanSingleAudioSingleVideo,
false, webrtc::SdpSemantics::kPlanB));
// If the default is Unified Plan.
EXPECT_EQ(SdpUsageCategory::kSafe,
DeduceSdpUsageCategory("offer",
kOfferSdpUnifiedPlanSingleAudioSingleVideo,
false, webrtc::SdpSemantics::kUnifiedPlan));
// If sdpSemantics is explicitly set to Plan B.
EXPECT_EQ(SdpUsageCategory::kSafe,
DeduceSdpUsageCategory("offer",
kOfferSdpUnifiedPlanSingleAudioSingleVideo,
true, webrtc::SdpSemantics::kPlanB));
// If sdpSemantics is explicitly set to Unified Plan.
EXPECT_EQ(SdpUsageCategory::kSafe,
DeduceSdpUsageCategory("offer",
kOfferSdpUnifiedPlanSingleAudioSingleVideo,
true, webrtc::SdpSemantics::kUnifiedPlan));
}
// Test that complex SDP is always unsafe when relying on default sdpSemantics.
TEST(DeduceSdpUsageCategory, ComplexSdpIsAlwaysUnsafeWithDefaultSdpSemantics) {
// If the default is Plan B and the SDP is complex Plan B.
EXPECT_EQ(SdpUsageCategory::kUnsafe,
DeduceSdpUsageCategory("offer", kOfferSdpPlanBMultipleAudioTracks,
false, webrtc::SdpSemantics::kPlanB));
// If the default is Plan B and the SDP is complex Unified Plan.
EXPECT_EQ(
SdpUsageCategory::kUnsafe,
DeduceSdpUsageCategory("offer", kOfferSdpUnifiedPlanMultipleAudioTracks,
false, webrtc::SdpSemantics::kPlanB));
// If the default is Unified Plan and the SDP is complex Plan B.
EXPECT_EQ(SdpUsageCategory::kUnsafe,
DeduceSdpUsageCategory("offer", kOfferSdpPlanBMultipleAudioTracks,
false, webrtc::SdpSemantics::kUnifiedPlan));
// If the default is Unified Plan and the SDP is complex UNified Plan.
EXPECT_EQ(
SdpUsageCategory::kUnsafe,
DeduceSdpUsageCategory("offer", kOfferSdpUnifiedPlanMultipleAudioTracks,
false, webrtc::SdpSemantics::kUnifiedPlan));
}
// Test that when sdpSemantics is explicitly set, complex SDP is safe if it is
// of the same format and unsafe if the format is different.
TEST(DeduceSdpUsageCategory, ComplexSdpIsSafeIfMatchingExplicitSdpSemantics) {
// If sdpSemantics is explicitly set to Plan B and the SDP is complex Plan B.
EXPECT_EQ(SdpUsageCategory::kSafe,
DeduceSdpUsageCategory("offer", kOfferSdpPlanBMultipleAudioTracks,
true, webrtc::SdpSemantics::kPlanB));
// If sdpSemantics is explicitly set to Unified Plan and the SDP is complex
// Unified Plan.
EXPECT_EQ(
SdpUsageCategory::kSafe,
DeduceSdpUsageCategory("offer", kOfferSdpUnifiedPlanMultipleAudioTracks,
true, webrtc::SdpSemantics::kUnifiedPlan));
// If the sdpSemantics is explicitly set to Plan B but the SDP is complex
// Unified Plan.
EXPECT_EQ(
SdpUsageCategory::kUnsafe,
DeduceSdpUsageCategory("offer", kOfferSdpUnifiedPlanMultipleAudioTracks,
true, webrtc::SdpSemantics::kPlanB));
// If the sdpSemantics is explicitly set to Unified Plan but the SDP is
// complex Plan B.
EXPECT_EQ(SdpUsageCategory::kUnsafe,
DeduceSdpUsageCategory("offer", kOfferSdpPlanBMultipleAudioTracks,
true, webrtc::SdpSemantics::kUnifiedPlan));
}
} // namespace blink