From 1da8dba4d76769838b98b853c2b5e243cf6feb0c Mon Sep 17 00:00:00 2001 From: chenhao Date: Thu, 5 Dec 2024 16:38:24 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=E7=9A=84=E9=80=BB=E8=BE=91=EF=BC=8C=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E7=94=A8=E4=BE=8B=E5=8F=91=E7=8E=B0=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chenhao --- .../ohosTest/ets/test/DataChannel.test.ets | 327 +- .../ohosTest/ets/test/PeerConnection.test.ets | 2934 +++++++++-------- .../ets/test/RTCDtlsTransport.test.ets | 387 ++- .../ohosTest/ets/test/RTCDtmfSender.test.ets | 26 +- .../ets/test/RTCIceTransport.test.ets | 364 +- .../ets/test/RTCSctpTransport.test.ets | 350 +- sdk/ohos/src/ohos_webrtc/ice_transport.cpp | 24 +- 7 files changed, 2271 insertions(+), 2141 deletions(-) diff --git a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/DataChannel.test.ets b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/DataChannel.test.ets index f8b9e437ab..237da081b7 100644 --- a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/DataChannel.test.ets +++ b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/DataChannel.test.ets @@ -16,6 +16,7 @@ import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' import webrtc from 'libohos_webrtc.so'; import { Logging, LoggingSeverity } from '../../../main/ets/log/Logging'; +import { sleep } from './Helper'; const TAG: string = '[DataChannelTest]'; const STUN_SERVER = "stun:stun.l.google.com:19302"; @@ -89,31 +90,6 @@ async function listenToConnected(pc: webrtc.RTCPeerConnection) { } } -async function didRemotePcClose(flag: Boolean) { - addTracks(); - let channel1 = caller.createDataChannel(""); - channel1.onopen = () => { - console.info(TAG, "channel.readyState:", channel1.readyState); - } - let closed = false; - channel1.onclose = () => { - closed = true; - } - exchangeIceCandidates(caller, callee); - await exchangeOfferAnswer(caller, callee); - await listenToConnected(callee); - await sleep(5000); - if (flag) { - callee.close(); - } - await sleep(1000); - return closed; -} - -function sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); -} - export default function DataChannelTest() { describe('DataChannelTest', () => { // Defines a test suite. Two parameters are supported: test suite name and test suite function. @@ -155,7 +131,6 @@ export default function DataChannelTest() { it('attribute', 0, async () => { let channel = caller.createDataChannel("audio", dataChannel); console.debug(TAG, "channel attribute: " + JSON.stringify(channel)); - await sleep(1000); expect(channel.label).assertEqual("audio"); expect(channel.ordered).assertEqual(true); expect(channel.maxPacketLifeTime).assertEqual(3000); @@ -173,11 +148,14 @@ export default function DataChannelTest() { addTracks(); let channel = caller.createDataChannel("onBufferedAmountLow"); console.debug(TAG, "onBufferedAmountLow: " + JSON.stringify(channel)); - let flag: Boolean = false; - channel.onbufferedamountlow = () => { - flag = true; - console.info(TAG, "onBufferedAmountLow on"); - }; + let flag = false; + let promise = new Promise((resolve) => { + channel.onbufferedamountlow = () => { + flag = true; + resolve(); + }; + }); + channel.bufferedAmountLowThreshold = 1024; channel.onopen = () => { const largeData = new Array(1000).join('A'); @@ -186,43 +164,57 @@ export default function DataChannelTest() { exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); await listenToConnected(callee); - await sleep(1000); - expect(flag).assertEqual(true); + await promise; + expect(flag).assertTrue(); }) - it("onclose", 0, async () => { + it("theCloseEventBeTriggeredWhenTheConversationClose", 0, async () => { let channel = caller.createDataChannel("onclose", dataChannel); - console.debug(TAG, "onclose: " + JSON.stringify(channel)); - channel.onclose = () => { - console.info(TAG, "onclose on", channel.readyState); - }; + console.debug(TAG, "onclose: " + JSON.stringify(channel)) + let flag: boolean = false; + let promise = new Promise((resolve) => { + channel.onclose = () => { + flag = true; + resolve(); + }; + }); channel.close(); - await sleep(1000); - console.info(TAG, "channel.readyState:", channel.readyState); - expect(channel.readyState).assertEqual("closed"); + await promise; + expect(flag).assertEqual(true); }) it("onclosing", 0, async () => { let channel = caller.createDataChannel("onclosing", dataChannel); console.debug(TAG, "onclosing: " + JSON.stringify(channel)); - channel.onclosing = () => { - console.info(TAG, "channel.readyState:", channel.readyState); - }; + let flag = false; + let promise = new Promise((resolve) => { + channel.onclosing = () => { + console.info(TAG, "channel.readyState:", channel.readyState); + flag = true; + resolve(); + }; + }) channel.close(); + await promise; + expect(flag).assertTrue(); }) - it("onopen", 0, async () => { + it("afterConnectionOnopenWillTriggered", 0, async () => { addTracks(); let channel = caller.createDataChannel("onopen"); - channel.onopen = () => { - console.info(TAG, "channel.readyState:", channel.readyState); - expect(channel.readyState).assertEqual("open"); - } + let flag = false; + let promise = new Promise((resolve) => { + channel.onopen = () => { + flag = true; + resolve(); + } + }); exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); await listenToConnected(callee); - await sleep(5000); + await promise; + expect(flag).assertTrue(); }) it("onmessage", 0, async () => { @@ -236,22 +228,25 @@ export default function DataChannelTest() { console.info(TAG, "message from callee", event.data); expect(event.data).assertEqual("channel from callee") }; - callee.ondatachannel = (event) => { - console.info(TAG, "ondatachannel"); - let receiveDataChannel = event.channel; - receiveDataChannel.onopen = () => { - console.info(TAG, "The channel from callee is open"); - receiveDataChannel.send("channel from callee"); - } - receiveDataChannel.onmessage = (event: webrtc.MessageEvent) => { - console.info(TAG, "message from caller", event.data); - expect(event.data).assertEqual("message from caller") + let promise = new Promise((resolve) => { + callee.ondatachannel = (event) => { + console.info(TAG, "ondatachannel"); + let receiveDataChannel = event.channel; + receiveDataChannel.onopen = () => { + console.info(TAG, "The channel from callee is open"); + receiveDataChannel.send("channel from callee"); + } + receiveDataChannel.onmessage = (event: webrtc.MessageEvent) => { + console.info(TAG, "message from caller", event.data); + expect(event.data).assertEqual("message from caller"); + resolve(); + } } - } + }); exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); await listenToConnected(callee); - await sleep(5000); + await promise; }) it("sendWhenClosed", 0, async () => { @@ -270,17 +265,19 @@ export default function DataChannelTest() { } }) - it("close", 0, async () => { + it("afterConnectionOncloseWillTriggered", 0, async () => { let channel = caller.createDataChannel("18", dataChannel); console.debug(TAG, "close: " + JSON.stringify(channel)); - let flag: Boolean = false; - channel.onclose = () => { - flag = true; - console.info(TAG, "onclose on"); - }; + let flag = false; + let promise = new Promise((resolve) => { + channel.onclose = () => { + flag = true; + resolve(); + }; + }); channel.close(); - await sleep(5000); - expect(flag).assertEqual(true); + await promise; + expect(flag).assertTrue(); }) it("sendString", 0, async () => { @@ -290,41 +287,43 @@ export default function DataChannelTest() { console.info(TAG, "channel.readyState:", channel.readyState); channel.send("a"); } - callee.ondatachannel = (event) => { - let receiveDataChannel = event.channel; - receiveDataChannel.onmessage = (event: webrtc.MessageEvent) => { - console.info(TAG, "message from caller", event.data); - expect(event.data).assertEqual("a") + let promise = new Promise((resolve) => { + callee.ondatachannel = (event) => { + let receiveDataChannel = event.channel; + receiveDataChannel.onmessage = (event: webrtc.MessageEvent) => { + console.info(TAG, "message from caller", event.data); + expect(event.data).assertEqual("a"); + resolve(); + } } - } + }); exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); await listenToConnected(callee); - await sleep(5000); + await promise; }) it("sendArrayBuffer", 0, async () => { addTracks(); let channel = caller.createDataChannel("sendArrayBuffer"); - channel.onopen = () => { - console.info(TAG, "channel.readyState:", channel.readyState); - const buffer = new ArrayBuffer(1); - const uint8View = new Uint8Array(buffer); - uint8View[0] = 97; - try { - channel.send(buffer); - } catch (e) { - expect().assertFail() + let promise = new Promise((resolve) => { + channel.onopen = () => { + console.info(TAG, "channel.readyState:", channel.readyState); + const buffer = new ArrayBuffer(1); + const uint8View = new Uint8Array(buffer); + uint8View[0] = 97; + try { + channel.send(buffer); + } catch (e) { + expect().assertFail() + } + resolve(); } - } + }); exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); await listenToConnected(callee); - await sleep(5000); - }) - - it("onerror", 0, () => { - expect(1).assertEqual(1); + await promise; }) it("bufferedAmountChange", 0, async () => { @@ -359,21 +358,25 @@ export default function DataChannelTest() { const buffer = new ArrayBuffer(10); channel.send(buffer); } - callee.ondatachannel = (event) => { - let receiveDataChannel = event.channel; - receiveDataChannel.onmessage = (event: webrtc.MessageEvent) => { - console.info(TAG, "message from caller:", event.data); - if (event.data instanceof ArrayBuffer) { - const uint8View = new Uint8Array(event.data); - console.info(TAG, "ArrayBuffer length:", uint8View.length); - expect(uint8View.length).assertEqual(10); + let promise = new Promise((resolve) => { + callee.ondatachannel = (event) => { + let receiveDataChannel = event.channel; + receiveDataChannel.onmessage = (event: webrtc.MessageEvent) => { + console.info(TAG, "message from caller:", event.data); + if (event.data instanceof ArrayBuffer) { + const uint8View = new Uint8Array(event.data); + console.info(TAG, "ArrayBuffer length:", uint8View.length); + expect(uint8View.length).assertEqual(10); + resolve(); + } } } - } + }); + exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); await listenToConnected(callee); - await sleep(1000); + await promise; }) it("sendUnicodeString", 0, async () => { @@ -384,17 +387,20 @@ export default function DataChannelTest() { const unicodeString = "你好"; channel.send(unicodeString); } - callee.ondatachannel = (event) => { - let receiveDataChannel = event.channel; - receiveDataChannel.onmessage = (event: webrtc.MessageEvent) => { - console.info(TAG, "message from caller: ", event.data); - expect(event.data).assertEqual("你好"); + let promise = new Promise((resolve) => { + callee.ondatachannel = (event) => { + let receiveDataChannel = event.channel; + receiveDataChannel.onmessage = (event: webrtc.MessageEvent) => { + console.info(TAG, "message from caller: ", event.data); + expect(event.data).assertEqual("你好"); + resolve(); + } } - } + }) exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); await listenToConnected(callee); - await sleep(1000); + await promise; }) it("sendBeforeCloseChannel", 0, async () => { @@ -405,31 +411,28 @@ export default function DataChannelTest() { const largeString = " ".repeat(64 * 1024); // channel2.binaryType = "arraybuffer"; - callee.ondatachannel = async (event) => { - channel2 = event.channel; - channel2.onmessage = (event: webrtc.MessageEvent) => { - console.info(TAG, "receivedSize: " + receivedSize, " sentSize: " + sentSize); - receivedSize += event.data.length; - } - channel2.onclose = () => { - console.info(TAG, "channel.readyState:", channel2.readyState); + let promise = new Promise((resolve) => { + callee.ondatachannel = async (event) => { + channel2 = event.channel; + channel2.onmessage = (event: webrtc.MessageEvent) => { + receivedSize += event.data.length; + resolve(); + } + channel2.onclose = () => { + console.info(TAG, "channel.readyState:", channel2.readyState); + } } - } - + }) exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); await listenToConnected(callee); - await sleep(1000); - channel1.onopen = async () => { - try { - channel1.send(largeString); - sentSize += largeString.length; - channel1.close(); - } catch (e) { - console.info(TAG, "e: ", e); - } + + channel1.onopen = () => { + channel1.send(largeString); + sentSize += largeString.length; + channel1.close(); } - await sleep(1000); + await promise; console.info(TAG, "after close receivedSize: " + receivedSize, " sentSize: " + sentSize); expect(receivedSize).assertEqual(sentSize); }) @@ -440,9 +443,12 @@ export default function DataChannelTest() { id: 42 }); - channel1.onopen = () => { - channel1.send("a"); - } + let promise = new Promise((resolve) => { + channel1.onopen = () => { + channel1.send("a"); + resolve(); + } + }) let channel2 = callee.createDataChannel('', { negotiated: false, @@ -454,9 +460,8 @@ export default function DataChannelTest() { exchangeIceCandidates(caller, callee); await exchangeOfferAnswer(caller, callee); - await listenToConnected(callee); - expect(channel1.id).not().assertEqual(42); - expect(channel2.id).not().assertEqual(42); + await promise; + expect(channel1.id == 42 && channel2.id == 42).assertFalse(); }) it("restartIceChannelIsOpen", 0, async () => { @@ -472,23 +477,51 @@ export default function DataChannelTest() { channel1.send("a"); } - callee.ondatachannel = (event) => { - event.channel.onmessage = (e: webrtc.MessageEvent) => { - console.info(TAG, "e: ", e.data.toString()); - expect(e.data.toString()).assertEqual("a"); + let promise = new Promise((resolve) => { + callee.ondatachannel = (event) => { + event.channel.onmessage = (e: webrtc.MessageEvent) => { + console.info(TAG, "e: ", e.data.toString()); + expect(e.data.toString()).assertEqual("a"); + resolve(); + } } - } - await sleep(1000); + }); + await promise; }) - it("ChannelWhenPcClose", 0, async () => { - let res = await didRemotePcClose(true); - expect(res).assertEqual(true); + it("afterPcCloseChannelWillClose", 0, async () => { + addTracks(); + let channel1 = caller.createDataChannel(""); + channel1.onopen = () => { + console.info(TAG, "channel.readyState:", channel1.readyState); + } + let closed = false; + let promise = new Promise((resolve) => { + channel1.onclose = () => { + closed = true; + resolve(); + } + }); + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + await listenToConnected(callee); + callee.close(); + await promise; + expect(closed).assertEqual(true); }) - it("ChannelWhenPcOpen", 0, async () => { - let res = await didRemotePcClose(false); - expect(res).assertEqual(false); + it("beforePcCloseChannelWillNotClose", 0, async () => { + addTracks(); + let channel1 = caller.createDataChannel(""); + let closed = false; + channel1.onclose = () => { + closed = true; + } + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + await listenToConnected(callee); + await sleep(1000); + expect(closed).assertEqual(false); }) it("changeBinaryType", 0, () => { diff --git a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/PeerConnection.test.ets b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/PeerConnection.test.ets index 45210fdade..c44cc3caaa 100644 --- a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/PeerConnection.test.ets +++ b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/PeerConnection.test.ets @@ -1,1406 +1,1530 @@ -/** - * Copyright (c) 2024 Archermind Technology (Nanjing) Co. Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' -import webrtc from 'libohos_webrtc.so'; -import { Logging, LoggingSeverity } from '../../../main/ets/log/Logging'; - -const DEFAULT = 0 -const TAG: string = '[PeerConnectionTest]'; - -const pcf = new webrtc.PeerConnectionFactory(); - -let caller: webrtc.RTCPeerConnection; -let callee: webrtc.RTCPeerConnection; - -const STUN_SERVER = "stun:stun.l.google.com:19302"; - -const sdp = 'v=0\r\n' + - 'o=- 166855176514521964 2 IN IP4 127.0.0.1\r\n' + - 's=-\r\n' + - 't=0 0\r\n' + - 'a=ice-options:trickle\r\n' + - 'm=audio 9 UDP/TLS/RTP/SAVPF 111\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:someufrag\r\n' + - 'a=ice-pwd:somelongpwdwithenoughrandomness\r\n' + - 'a=fingerprint:sha-256 8C:71:B3:8D:A5:38:FD:8F:A4:2E:A2:65:6C:86:52:BC:E0:6E:94:F2:9F:7C:4D:B5:DF:AF:AA:6F:44:90:8D:F4\r\n' + - 'a=setup:actpass\r\n' + - 'a=rtcp-mux\r\n' + - 'a=mid:mid1\r\n' + - 'a=sendonly\r\n' + - 'a=msid:stream1 track1\r\n' + - 'a=ssrc:1001 cname:some\r\n' + - 'a=rtpmap:111 opus/48000/2\r\n'; - -let offer = { - sdp: "v=0\r\no=- 8926124112408190582 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS\r\n", - type: "offer" -} as webrtc.RTCSessionDescriptionInit; - -let answer = { - sdp: "v=0\r\no=- 1824345164880213868 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS\r\n", - type: "answer" -} as webrtc.RTCSessionDescriptionInit; - -let transceiverInit = { - direction: 'sendrecv', - streams: [], - sendEncodings: [{ - maxBitrate: 100000, - }], -} as webrtc.RTCRtpTransceiverInit; - -let dataChannel: webrtc.RTCDataChannelInit = { - ordered: true, // 设置为 true 表示消息是有序的 - maxPacketLifeTime: 3000, // 可选,设置数据包的最大生存时间(毫秒) - protocol: "sctp-protocol", // 可选,设置协议 -}; - -function sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -async function exchangeOffer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - await caller.setLocalDescription(await caller.createOffer()); - console.log(TAG, "caller.localDescription: " + caller.localDescription!.type); - await callee.setRemoteDescription(caller.localDescription); -} - -async function exchangeAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - const answer = await callee.createAnswer(); - await caller.setRemoteDescription(answer); - await callee.setLocalDescription(answer); -} - -async function exchangeOfferAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - await exchangeOffer(caller, callee); - await exchangeAnswer(caller, callee); -} - -function exchangeIceCandidates(pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) { - const doExchange = (pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) => { - pc1.onicecandidate = (event) => { - const candidate = event.candidate; - if (pc2.signalingState !== 'closed') { - pc2.addIceCandidate(candidate); - } - }; - }; - - doExchange(pc1, pc2); - doExchange(pc2, pc1); -} - -async function generateAudioReceiveOnlyOffer(pc: webrtc.RTCPeerConnection) { - try { - pc.addTransceiver('audio', { - direction: 'recvonly' - }); - return pc.createOffer(); - } catch (e) { - return pc.createOffer({ - iceRestart: true - }); - } -} - -async function generateAnswer(offer: webrtc.RTCSessionDescriptionInit) { - let pc = pcf.createPeerConnection({ - iceServers: [{ - urls: STUN_SERVER - }] - }); - await pc.setRemoteDescription(offer); - let answer = await pc.createAnswer(); - pc.close(); - return answer; -} - -async function generateOffer() { - let pc = pcf.createPeerConnection({ - iceServers: [{ - urls: STUN_SERVER - }] - }); - let offer = await pc.createOffer(); - pc.close(); - return offer; -} - -async function CreatePeerConnection() { - if (!pcf) { - console.error(TAG, 'pcf is not initialized'); - return; - } - caller = pcf.createPeerConnection({ - iceServers: [{ - urls: STUN_SERVER - }] - }); - callee = pcf.createPeerConnection({ - iceServers: [{ - urls: STUN_SERVER - }] - }); -} - -async function createSetOffer(options?: webrtc.RTCOfferOptions) { - try { - let offer = await caller?.createOffer(options); - console.info(TAG, 'createOffer: ' + JSON.stringify(offer)); - expect(offer.type).assertEqual("offer"); - } catch (e) { - console.error(TAG, 'createOffer: ' + JSON.stringify(e)); - expect().assertFail(); - } -} - -async function createSetAnswer(options?: webrtc.RTCAnswerOptions) { - try { - let answer = await caller?.createAnswer(options); - console.info(TAG, 'createAnswer: ' + JSON.stringify(answer)); - expect(answer.type).assertEqual("answer"); - } catch (e) { - console.error(TAG, 'createAnswer: ' + JSON.stringify(e)); - expect().assertFail(); - } -} - -export default function PeerConnectionTest() { - - describe('PeerConnectionTest', () => { - // Defines a test suite. Two parameters are supported: test suite name and test suite function. - - beforeAll(() => { - // Presets an action, which is performed only once before all test cases of the test suite start. - // This API supports only one parameter: preset action function. - Logging.enableLogToDebugOutput(LoggingSeverity.VERBOSE); - }) - beforeEach(() => { - // Presets an action, which is performed before each unit test case starts. - // The number of execution times is the same as the number of test cases defined by **it**. - // This API supports only one parameter: preset action function. - CreatePeerConnection(); - }) - afterEach(() => { - // Presets a clear action, which is performed after each unit test case ends. - // The number of execution times is the same as the number of test cases defined by **it**. - // This API supports only one parameter: clear action function. - if (caller) { - caller.close(); - } - if (callee) { - callee.close(); - } - }) - afterAll(() => { - // Presets a clear action, which is performed after all test cases of the test suite end. - // This API supports only one parameter: clear action function. - }) - - it('addAndRemoveTrack', DEFAULT, () => { - let audioSource = pcf?.createAudioSource(); - let audioTrack = pcf?.createAudioTrack("audio", audioSource); - expect(audioTrack.kind).assertEqual("audio"); - let rtpSender = caller.addTrack(audioTrack); - - expect(caller.getSenders().length).assertEqual(1); - console.info(TAG, 'getSenders: ' + caller.getSenders().length); - try { - // removeTrack后,不能通过senders的长度来判断,因为长度不变 - caller?.removeTrack(rtpSender); - expect(caller.getSenders().length).assertEqual(1); - expect(caller.getSenders()[0].track).assertNull(); - } catch (e) { - console.error(TAG, 'Error while removing track: ', JSON.stringify(e)); - expect().assertFail(); - } - }) - - it("addIceCandidate", DEFAULT, async () => { - let candidate: webrtc.RTCIceCandidate | undefined = undefined; - caller.onicecandidate = (event) => { - console.info(TAG, 'onicecandidate: ', event.candidate); - candidate = event.candidate; - }; - caller.onicegatheringstatechange = (event) => { - console.info(TAG, 'onicegatheringstatechange: ', event); - }; - - let audioSource = pcf.createAudioSource(); - let audioTrack = pcf.createAudioTrack("id", audioSource); - - caller.addTrack(audioTrack); - - let offer = await caller?.createOffer(); - await caller?.setRemoteDescription(offer); - console.info(TAG, 'caller.signalingState changed:', caller.signalingState); - - let answer = await caller?.createAnswer(); - await caller?.setLocalDescription(answer); - console.info(TAG, 'caller.signalingState changed:', caller.signalingState); - - await sleep(1000); - expect(candidate).not().assertUndefined(); - - try { - await caller.addIceCandidate(candidate); - } catch (e) { - console.error(TAG, 'addIceCandidate error :', JSON.stringify(e)); - expect().assertFail(); - } - }) - - it("AddTransceiverWithStringNoInit", DEFAULT, () => { - let tranSceiver = caller?.addTransceiver("audio"); - console.info(TAG, 'addTransceiver: ' + JSON.stringify(tranSceiver)); - expect(caller.getTransceivers().length).assertEqual(1); - }) - - it("AddTransceiverWithTrackNoInit", DEFAULT, () => { - let audioSource = pcf?.createAudioSource({ - echoCancellation: false - }); - let audioTrack = pcf?.createAudioTrack("audio", audioSource); - let tranSceiver = caller?.addTransceiver(audioTrack); - console.info(TAG, 'addTransceiver: ' + JSON.stringify(tranSceiver)); - expect(caller.getTransceivers().length).assertEqual(1); - }) - - it("AddTransceiverWithTrackAndInit", DEFAULT, () => { - let audioSource = pcf?.createAudioSource({ - echoCancellation: false - }); - let audioTrack = pcf?.createAudioTrack("audio", audioSource); - let tranSceiver = caller?.addTransceiver(audioTrack, transceiverInit); - console.info(TAG, 'addTransceiver: ' + JSON.stringify(tranSceiver)); - expect(caller.getTransceivers().length).assertEqual(1); - }) - - it("AddTransceiverWithStringAndInit", DEFAULT, () => { - let tranSceiver = caller?.addTransceiver("audio", transceiverInit); - console.info(TAG, 'addTransceiver: ' + JSON.stringify(tranSceiver)); - expect(caller.getTransceivers().length).assertEqual(1); - }) - - it("createOffer", DEFAULT, async () => { - createSetOffer(); - }) - - it("createOfferAddOptions", DEFAULT, async () => { - let options: webrtc.RTCOfferOptions = { - iceRestart: true - } - createSetOffer(options); - }) - - it("createAnswer", DEFAULT, async () => { - await caller?.setRemoteDescription(offer); - createSetAnswer(); - }) - - it("createAnswerAddOptions", DEFAULT, async () => { - await caller?.setRemoteDescription(offer); - let options: webrtc.RTCAnswerOptions = {}; - createSetAnswer(options); - }) - - it("setLocalDescriptionWithOffer", DEFAULT, async () => { - await caller?.setLocalDescription(offer); - expect(caller.signalingState).assertEqual("have-local-offer"); - }) - - it("setLocalDescriptionNoOffer", DEFAULT, async () => { - await caller?.setLocalDescription(); - expect(caller.signalingState).assertEqual("have-local-offer"); - expect(caller.pendingLocalDescription).not().assertNull(); - }) - - it("setLocalDescriptionWithAnswer", DEFAULT, async () => { - await caller?.setRemoteDescription(offer); - await caller?.setLocalDescription(answer); - expect(caller.signalingState).assertEqual("stable"); - }) - - it("setLocalDescriptionNoAnswer", DEFAULT, async () => { - await callee.setRemoteDescription(await caller.createOffer()); - await callee.setLocalDescription(); - expect(callee.currentLocalDescription).not().assertNull(); - expect(caller.signalingState).assertEqual("stable"); - }) - - it("setRemoteDescription", DEFAULT, async () => { - expect(caller.signalingState).assertEqual("stable"); - caller.onsignalingstatechange = () => { - console.log(TAG, 'Signaling state change: ', caller.signalingState); - }; - await caller?.setRemoteDescription(offer); - expect(caller.signalingState).assertEqual("have-remote-offer"); - }) - - it("getReceivers", DEFAULT, () => { - let receiver = caller.getReceivers(); - expect(receiver.length).assertEqual(0); - - let audioSource = pcf?.createAudioSource(); - let audioTrack = pcf?.createAudioTrack("audio", audioSource); - - let rtpSender = caller.addTrack(audioTrack); - expect(caller.getSenders().length).assertEqual(1); - - let newReceiver = caller.getReceivers(); - expect(newReceiver.length).assertEqual(1); - }) - - it("createDataChannelWithInit", DEFAULT, () => { - let channel = caller.createDataChannel("1", dataChannel); - - expect(channel.label).assertEqual("1"); - expect(channel.ordered).assertTrue(); - expect(channel.maxPacketLifeTime).assertEqual(3000); - expect(channel.protocol).assertEqual("sctp-protocol"); - }) - - it("createDataChannelNoInit", DEFAULT, () => { - let channel = caller.createDataChannel("1"); - - console.info(TAG, 'channel: ', JSON.stringify(channel)); - - expect(channel.label).assertEqual("1"); - expect(channel.ordered).assertTrue(); - }) - - it("setAndGetConfiguration", DEFAULT, async () => { - expect(caller.getConfiguration().iceCandidatePoolSize).assertEqual(0) - caller.setConfiguration(); - let config = caller.getConfiguration(); - - expect(config.iceCandidatePoolSize).assertEqual(0); - - let newConfig: webrtc.RTCConfiguration = { - iceServers: [{ - urls: "stun:stun1.l.google.com:19302" - },], - iceCandidatePoolSize: 10 - }; - caller.setConfiguration(newConfig); - - caller.getConfiguration(); - expect(caller.getConfiguration().iceCandidatePoolSize).assertEqual(10); - - try { - let stat = await caller.getStats(); - console.info(TAG, 'getStats: ' + JSON.stringify(stat)); - } catch (e) { - console.error(TAG, 'getStats error : ', JSON.stringify(e)) - expect().assertFail() - } - }) - - it("restartIce", DEFAULT, async () => { - try { - let flag = false; - caller.onnegotiationneeded = (event) => { - console.info(TAG, "onnegotiationneeded"); - flag = true; - }; - caller.restartIce(); - await sleep(1000); - expect(flag).assertEqual(true); - } catch (e) { - console.error(TAG, "ICE connection state before restart: ", caller.iceConnectionState); - expect().assertFail() - } - }) - - it("close", DEFAULT, () => { - caller.close() - try { - caller.setConfiguration() - } catch (e) { - console.info(TAG, "close:", e) - } - }) - - it("orderOfOnicecandidateAndSetLocalDescription", DEFAULT, async () => { - caller.addTransceiver('audio'); - let events: string[] = []; - let pendingPromises: Promise[] = []; - - caller.onicecandidate = (event) => { - console.info(TAG, 'onicecandidate: ', event.candidate); - }; - let onCandidatePromise = new Promise((resolve, reject) => { - caller.onicecandidate = resolve; - }); - await sleep(1000); - pendingPromises.push(onCandidatePromise.then(() => { - events.push('candidate generated'); - })); - pendingPromises.push(caller.setLocalDescription().then(() => { - events.push('setLocalDescription'); - })); - await Promise.all(pendingPromises); - expect(events).assertDeepEquals(['setLocalDescription', 'candidate generated']); - }) - - it("directionWillChanged", DEFAULT, async () => { - let calleePromise = callee.createOffer().then((offer) => { - return caller.setRemoteDescription(offer); - }).then(() => { - return caller.createAnswer(); - }); - caller.addTransceiver('audio', { - direction: 'recvonly' - }); - - let callerPromise = caller.createOffer().then(() => { - caller.getTransceivers()[0].direction = 'inactive'; - }); - await Promise.all([callerPromise, calleePromise]); - expect(caller.getTransceivers()[0].direction).assertEqual('inactive'); - }) - - it("transceiverSenderTrackDoesNotRevertToAnOldState", DEFAULT, async () => { - let offer = await callee.createOffer(); - await caller.setRemoteDescription(offer); - let answer = await caller.createAnswer(); - await caller.setLocalDescription(answer); - caller.addTransceiver('audio', { - direction: 'recvonly' - }); - await caller.createOffer(); - caller.addTrack(caller.getReceivers()[0].track); - expect(caller.getSenders()[0].track!.id).assertEqual(caller.getReceivers()[0].track.id); - }) - - /*it("orderOfSignalingState", DEFAULT, async () => { - let signalingStates: webrtc.RTCSignalingState[] = []; - - caller.onsignalingstatechange = () => { - console.info(TAG, "add signalingStates: ", caller.signalingState); - signalingStates.push(caller.signalingState); - } - - caller.addTransceiver('audio', { - direction: 'recvonly' - }); - let offer = await caller.createOffer(); - let sldPromise = caller.setLocalDescription(offer); - // offer state为stable or have-remote-offer - // https://www.w3.org/TR/webrtc/#dom-peerconnection-setremotedescription - let srdPromise = caller.setRemoteDescription(offer); - await Promise.all([sldPromise, srdPromise]); - - console.info(TAG, "signalingStates: ", signalingStates.toString()) - // expect(signalingStates).assertDeepEquals(['have-local-offer', 'stable', 'have-remote-offer']); - })*/ - - /*it("stateChanged", DEFAULT, async () => { - let eventCount = 0; - const states = [ - 'stable', 'have-local-offer', 'stable', 'have-remote-offer', - ]; - - caller.onsignalingstatechange = () => { - expect(caller.signalingState).assertEqual(states[++eventCount]); - } - let offer = await generateAudioReceiveOnlyOffer(caller); - console.info(TAG, "signalingStates: ", caller.signalingState) - // expect(caller.signalingState).assertEqual('stable'); - await caller.setLocalDescription(offer); - console.info(TAG, "signalingStates: ", caller.signalingState) - // expect(caller.signalingState).assertEqual('have-local-offer'); - - // offer state为stable or have-remote-offer - // https://www.w3.org/TR/webrtc/#dom-peerconnection-setremotedescription - await caller.setRemoteDescription(offer); - await exchangeAnswer(caller, callee); - console.info(TAG, "signalingStates: ", caller.signalingState) - // expect(caller.signalingState).assertEqual('stable'); - await exchangeOffer(caller, callee); - console.info(TAG, "signalingStates: ", caller.signalingState) - // expect(caller.signalingState).assertEqual('have-remote-offer'); - })*/ - - it("inValidSdp", DEFAULT, async () => { - try { - await caller.setRemoteDescription({ - sdp: 'bogus', type: 'offer' - }); - } catch (e) { - console.error(TAG, "e: ", e); - expect(e).assertInstanceOf("Error"); - } - }) - - it("rollbackInvalidSdp", DEFAULT, async () => { - try { - const offer = await generateAudioReceiveOnlyOffer(caller); - await caller.setRemoteDescription(offer); - await caller.setRemoteDescription({ - type: 'rollback', - sdp: '!;' - }); - } catch (e) { - console.error(TAG, "e: ", e); - expect(e).assertInstanceOf("Error"); - } - }); - - it("localOfferCreatedBeforeSetRemoteDescriptionThenRollbackShouldStillBeUsable", DEFAULT, async () => { - caller.addTransceiver('audio', { - direction: 'recvonly' - }); - let offer1 = await caller.createOffer(); - callee.addTransceiver('audio', { - direction: 'recvonly' - }); - let offer2 = await callee.createOffer(); - try { - await caller.setRemoteDescription(offer2); - await caller.setRemoteDescription({ - type: "rollback" - }); - await caller.setLocalDescription(offer1); - } catch (e) { - console.error(TAG, "e: ", e); - expect().assertFail(); - } - }) - - it("removeTrackWithASenderBeingRolledBackDoesNotCrashOrThrow", DEFAULT, async () => { - callee.addTransceiver('audio'); - let offer = await callee.createOffer(); - await caller.setRemoteDescription(offer); - let transceiver: webrtc.RTCRtpTransceiver[] = caller.getTransceivers(); - caller.setRemoteDescription({ - type: 'rollback' - }); - caller.removeTrack(transceiver[0].sender); - }) - - it("setRemoteDescriptionPranswerWhenStable", DEFAULT, async () => { - let offer = await caller.createOffer(); - console.info(TAG, "signalingState: ", caller.signalingState); - try { - caller.setRemoteDescription({ - type: 'pranswer', sdp: offer.sdp - }); - } catch (e) { - console.error(TAG, "e: ", e); - expect(e).assertEqual("InvalidStateError"); - } - }) - - it("setRemoteDescriptionWithValidOfferShouldSucceed", DEFAULT, async () => { - caller.createDataChannel('datachannel'); - let states: webrtc.RTCSignalingState[] = []; - callee.onsignalingstatechange = () => { - console.info(TAG, "callee.signalingState: ", callee.signalingState); - states.push(callee.signalingState); - }; - let offer = await caller.createOffer(); - await callee.setRemoteDescription(offer); - await sleep(1000); - expect(callee.signalingState).assertEqual('have-remote-offer'); - // expect(callee.remoteDescription).assertDeepEquals(offer); - // expect(callee.pendingRemoteDescription).assertDeepEquals(offer); - expect(callee.currentRemoteDescription).assertUndefined(); - expect(states).assertDeepEquals(['have-remote-offer']); - }) - - it("setRemoteDescriptionWithInvalidSdp", DEFAULT, async () => { - try { - await caller.setRemoteDescription({ - type: 'offer', - sdp: 'Invalid SDP' - }); - console.error(TAG, 'Expect promise to be rejected'); - expect().assertFail(); - } catch (e) { - console.info(TAG, "errorDetail: ", e); - // expect(e.errorDetail).assertEqual('sdp-syntax-error'); - expect(e).assertInstanceOf("Error"); - } - }); - - it("onsignalingstatechangeBeforeSetLocalDescription", DEFAULT, async () => { - const offer = await caller.createOffer(); - let eventSequence = ''; - caller.onsignalingstatechange = () => { - eventSequence += 'onsignalingstatechange;'; - console.info(TAG, "signalingState: ", caller.signalingState); - }; - - await caller.setLocalDescription(offer); - await sleep(1000); - eventSequence += 'setLocalDescription;'; - expect(eventSequence).assertEqual('onsignalingstatechange;setLocalDescription;'); - }); - - it("setLocalDescriptionRollbackFromLocalOffer", DEFAULT, async () => { - let states: webrtc.RTCSignalingState [] = []; - caller.onsignalingstatechange = () => { - console.info(TAG, "caller.signalingState: ", caller.signalingState); - states.push(caller.signalingState); - } - let offer = await caller.createOffer(); - await caller.setLocalDescription(offer); - await sleep(1000); - expect(caller.signalingState).assertEqual('have-local-offer'); - expect(caller.localDescription).not().assertNull(); - expect(caller.pendingLocalDescription).assertDeepEquals(caller.localDescription); - expect(caller.currentLocalDescription).assertUndefined(); - await caller.setLocalDescription({ - type: 'rollback' - }); - await sleep(1000); - expect(caller.signalingState).assertEqual('stable'); - expect(caller.localDescription).assertUndefined(); - expect(caller.pendingLocalDescription).assertUndefined(); - expect(caller.currentLocalDescription).assertUndefined(); - expect(states).assertDeepEquals(['have-local-offer', 'stable']); - }) - - it("setLocalDescriptionRollbackFromState", DEFAULT, async () => { - try { - await caller.setLocalDescription({ - type: 'rollback' - }) - expect().assertFail(); - } catch (e) { - console.error(TAG, "e: ", e); - } - }) - - it("setLocalDescriptionRollbackAfterSettingAnswerDescriptionShouldRejectWithInvalidStateError", DEFAULT, - async () => { - let offer = await generateAudioReceiveOnlyOffer(caller); - await caller.setRemoteDescription(offer); - let answer = await caller.createAnswer(); - await caller.setLocalDescription(answer); - try { - await caller.setLocalDescription({ - type: 'rollback' - }); - expect().assertFail(); - } catch (e) { - console.info(TAG, "e: ", e); - } - }) - - it("setLocalDescriptionRollbackIgnoreInvalidSdp", DEFAULT, async () => { - let offer = await caller.createOffer() - caller.setLocalDescription(offer) - try { - caller.setLocalDescription({ - type: 'rollback', - sdp: '!;' - }); - } catch (e) { - expect().assertFail(); - } - }) - - it("setLocalDescriptionRollbackShouldUpdateInternalStateWithAQueuedTaskInTheRightOrder", DEFAULT, async () => { - caller.addTransceiver('audio', { - direction: 'recvonly' - }); - await caller.setLocalDescription(await caller.createOffer()); - expect(caller.signalingState).assertEqual("have-local-offer"); - expect(caller.pendingLocalDescription).not().assertNull(); - // expect(caller.pendingLocalDescription.type).assertEqual("offer"); - expect(caller.pendingLocalDescription).assertDeepEquals(caller.localDescription); - expect(caller.pendingRemoteDescription).assertUndefined() - const sldPromise = caller.setLocalDescription({ - type: "rollback" - }); - const stablePromise = new Promise((resolve, reject) => { - caller.onsignalingstatechange = () => { - resolve(caller.signalingState); - } - }); - let raceValue = await Promise.race([stablePromise, sldPromise]); - expect(caller.signalingState).assertEqual("stable"); - expect(caller.pendingLocalDescription).assertUndefined(); - expect(caller.pendingRemoteDescription).assertUndefined(); - - await sldPromise; - }) - - it("setLocalDescriptionPranswerFromStable", DEFAULT, async () => { - let offer = await caller.createOffer(); - try { - await caller.setLocalDescription({ - type: 'pranswer', sdp: offer.sdp - }); - } catch (e) { - console.error(TAG, "e: ", e); - expect(e).assertInstanceOf("Error"); - } - }) - - it("ParameterlessSldStable", DEFAULT, async () => { - let transceiver = caller.addTransceiver('audio'); - expect(transceiver.mid).assertNull(); - await caller.setLocalDescription(); - expect(transceiver.mid).not().assertNull(); - }) - - it("ParameterlessRemoteoffer", DEFAULT, async () => { - caller.addTransceiver('audio'); - callee.ontrack = () => { - console.info(TAG, "currentDirection: ", callee.getTransceivers()[0].currentDirection); - } - await callee.setRemoteDescription(await caller.createOffer()); - await callee.setLocalDescription(); - - await sleep(5000); - expect(callee.getTransceivers()[0].currentDirection).assertEqual('recvonly'); - }) - - it("exchangeOA", DEFAULT, async () => { - try { - await caller.setLocalDescription(); - await callee.setRemoteDescription(caller.pendingLocalDescription); - - await callee.setLocalDescription(); - await caller.setRemoteDescription(callee.currentLocalDescription); - } catch (e) { - console.error(TAG, "e: ", e); - expect().assertFail(); - } - }) - - it("setLocalDescriptionWithValidOfferShouldSucceed", DEFAULT, async () => { - let states: String = ""; - - let signalingStateChangePromise = new Promise((resolve) => { - caller.onsignalingstatechange = () => { - states = caller.signalingState; - resolve(); - }; - }); - - let offer = await generateAudioReceiveOnlyOffer(caller); - await caller.setLocalDescription(offer); - expect(caller.signalingState).assertEqual('have-local-offer'); - // localDescription与offer相似的比较没有对于的方式 - // expect(caller.localDescription).assertDeepEquals(offer); - expect(JSON.stringify(caller.pendingLocalDescription)).assertEqual(JSON.stringify(caller.localDescription)); - expect(caller.currentLocalDescription).assertUndefined(); - - await signalingStateChangePromise; - - expect(states).assertEqual('have-local-offer'); - }) - - it("localOfferCreatedBeforeSetRemoteDescriptionRemoteOfferThenRollbackShouldStillBeUsable", DEFAULT, async () => { - try { - caller.addTransceiver('audio', { - direction: 'recvonly' - }); - let offer = await caller.createOffer(); - callee.addTransceiver('audio', { - direction: 'recvonly' - }); - let offer2 = await callee.createOffer(); - - await caller.setRemoteDescription(offer2); - await caller.setRemoteDescription({ - type: "rollback" - }); - await caller.setLocalDescription(offer); - } catch (e) { - console.error(TAG, "e: ", e); - expect().assertFail(); - } - }) - - it("offerSetRepeatedly", DEFAULT, async () => { - caller.addTransceiver('audio', { - direction: 'recvonly' - }); - await caller.setLocalDescription(await caller.createOffer()); - - const offer = await caller.createOffer(); - await caller.setLocalDescription(offer); - await callee.setRemoteDescription(offer); - const answer = await callee.createAnswer(); - await callee.setLocalDescription(answer); - await caller.setRemoteDescription(answer); - - expect(caller.getTransceivers().length).assertEqual(1); - expect(caller.getTransceivers()[0].receiver.track.kind).assertEqual("audio"); - expect(callee.getTransceivers().length).assertEqual(1); - expect(callee.getTransceivers()[0].receiver.track.kind).assertEqual("audio"); - }) - - it("setLocalDescriptionAnswerStateInvalidStateError", DEFAULT, async () => { - let offer = await caller.createOffer() - console.info(TAG, "signalingState: ", caller.signalingState); - try { - await caller.setLocalDescription({ - type: 'answer', sdp: offer.sdp - }); - expect().assertFail() - } catch (e) { - console.error(TAG, "error: ", e); - } - }) - - it("setLocalDescriptionAnswerWhenLocalOffer", DEFAULT, async () => { - let offer = await caller.createOffer(); - await caller.setLocalDescription(offer); - let answer = await generateAnswer(offer); - console.info(TAG, "signalingState: ", caller.signalingState); - try { - await caller.setLocalDescription(answer); - expect().assertFail() - } catch (e) { - console.error(TAG, "error: ", e); - } - }) - - it("setPreviouslyAnswerCreateOfferShouldWork", DEFAULT, async () => { - try { - caller.addTransceiver('audio', { - direction: 'recvonly' - }); - let offer = await caller.createOffer(); - await callee.setRemoteDescription(offer); - let answer = await callee.createAnswer(); - await callee.setRemoteDescription({ - type: "rollback" - }); - callee.addTransceiver('video', { - direction: 'recvonly' - }); - await callee.createOffer(); - await callee.setRemoteDescription(offer); - await callee.setLocalDescription(answer); - } catch (e) { - console.error(TAG, "e: ", e); - expect().assertFail(); - } - }) - - it("setTransceiverInactive", DEFAULT, async () => { - caller.addTransceiver('audio'); - const offer = await caller.createOffer(); - await caller.setLocalDescription(offer); - - await callee.setRemoteDescription(offer); - callee.getTransceivers()[0].stop(); - const answer = await callee.createAnswer(); - - await caller.setRemoteDescription(answer); - - expect(caller.getTransceivers()[0].currentDirection).assertEqual('inactive'); - }) - - it("inStateOnnegotiationneededNotPremature", DEFAULT, async () => { - caller.addTransceiver("audio"); - caller.onnegotiationneeded = () => { - console.info(TAG, "signalingState: ", caller.signalingState); - } - await caller.setLocalDescription(await caller.createOffer()); - caller.restartIce(); - caller.onnegotiationneeded = () => { - console.info(TAG, "signalingState: ", caller.signalingState); - } - await callee.setRemoteDescription(caller.localDescription); - await callee.setLocalDescription(await callee.createAnswer()); - await caller.setRemoteDescription(callee.localDescription); - }) - - it("addTransceiverWithInactiveDirectionNotCauseOntrack", DEFAULT, async () => { - caller.addTransceiver('audio', { - direction: 'inactive' - }); - let flag: Boolean = false; - callee.ontrack = () => { - flag = true; - } - await callee.setRemoteDescription(await caller.createOffer()); - await sleep(1000); - expect(flag).assertEqual(false); - }) - - it("negotiationneededFireWhenSignalingBackStateAfterSetRemoteDescription", DEFAULT, async () => { - caller.addTransceiver('audio'); - const offer = await caller.createOffer(); - await caller.setLocalDescription(offer); - let fired = false; - caller.onnegotiationneeded = e => { - fired = true; - } - caller.createDataChannel('test'); - - await caller.setRemoteDescription(await generateAnswer(offer)); - console.info(TAG, "onnegotiationneeded", caller.signalingState); - await sleep(1000); - expect(fired).assertEqual(true); - }) - - it("negotiationneededFireWhenSignalingBackStateAfterSetLocalDescription", DEFAULT, async () => { - caller.addTransceiver('audio'); - let fired = false; - caller.onnegotiationneeded = e => { - fired = true; - } - await caller.setRemoteDescription(await generateOffer()); - caller.createDataChannel('test'); - await caller.setLocalDescription(await caller.createAnswer()); - console.info(TAG, "signalingState: ", caller.signalingState); - await sleep(1000); - expect(fired).assertEqual(true); - }) - - it("UpdatingDirectionTransceiverShouldCauseNegotiationneeded", DEFAULT, async () => { - let transceiver = caller.addTransceiver('audio', { - direction: 'sendrecv' - }); - let onNegotiationNeededPromise = new Promise((resolve, reject) => { - caller.onnegotiationneeded = resolve; - }); - let offer = await caller.createOffer(); - await caller.setLocalDescription(offer); - let answer = await generateAnswer(offer); - await caller.setRemoteDescription(answer); - transceiver.direction = 'recvonly'; - - await onNegotiationNeededPromise; - console.info(TAG, "direction: ", transceiver.direction); - expect(transceiver.direction).assertEqual('recvonly'); - }) - - it("setStreamsShouldCauseNegotiationneeded", DEFAULT, async () => { - let transceiver = caller.addTransceiver('audio', { - direction: 'sendrecv' - }); - let offer = await caller.createOffer(); - await caller.setLocalDescription(offer); - let answer = await generateAnswer(offer); - await caller.setRemoteDescription(answer); - - let stream = new webrtc.MediaStream(); - let flag = false; - caller.onnegotiationneeded = e => { - flag = true; - } - transceiver.sender.setStreams(stream); - await sleep(1000); - expect(flag).assertEqual(true); - }) - - it("setStreamsDifferentStreamShouldCauseNegotiationneeded", DEFAULT, async () => { - let transceiver = caller.addTransceiver('audio', { - direction: 'sendrecv' - }); - let flag = false; - let stream = new webrtc.MediaStream(); - transceiver.sender.setStreams(stream); - await sleep(1000); - let offer = await caller.createOffer(); - await caller.setLocalDescription(offer); - let answer = await generateAnswer(offer); - await caller.setRemoteDescription(answer); - - let stream2 = new webrtc.MediaStream(); - caller.onnegotiationneeded = e => { - flag = true; - } - transceiver.sender.setStreams(stream2); - await sleep(1000); - expect(flag).assertEqual(true); - }) - - it("setStreamsWithAdditionalStreamShouldCauseNegotiationneeded", DEFAULT, async () => { - let transceiver = caller.addTransceiver('audio', { - direction: 'sendrecv' - }); - let flag = false; - let stream = new webrtc.MediaStream(); - transceiver.sender.setStreams(stream); - - let offer = await caller.createOffer(); - await caller.setLocalDescription(offer); - let answer = await generateAnswer(offer); - await caller.setRemoteDescription(answer); - - let stream2 = new webrtc.MediaStream(); - caller.onnegotiationneeded = e => { - flag = true; - } - transceiver.sender.setStreams(stream, stream2); - await sleep(1000); - expect(flag).assertEqual(true); - }) - - it("sendDataInDatachannelEvent", DEFAULT, async () => { - let message = 'meow meow!'; - - callee.ondatachannel = (event) => { - console.info(TAG, "send data"); - let dc2 = event.channel; - dc2.send(message); - }; - - const dc1 = caller.createDataChannel('fire-me!'); - dc1.onmessage = (event: webrtc.MessageEvent) => { - console.info(TAG, "data: ", event.data); - expect(event.data).assertEqual(message); - }; - - exchangeIceCandidates(caller, callee); - await exchangeOfferAnswer(caller, callee); - await sleep(1000); - }) - - it("closeChannelNotCauseOnopenInDatachannelEvent", DEFAULT, async () => { - let flag = false; - callee.ondatachannel = async (event) => { - console.info(TAG, "ondatachannel in"); - const dc = event.channel; - dc.onopen = () => { - flag = true; - expect().assertFail(); - }; - dc.close(); - await sleep(500); - }; - caller.createDataChannel('fire-me!'); - - exchangeIceCandidates(caller, callee); - await exchangeOfferAnswer(caller, callee); - expect(flag).assertEqual(false); - await sleep(1000); - }) - - it("closeChannelWhenSendNotCauseOnopenInDataChannelEvent", DEFAULT, async () => { - let message = 'meow meow!'; - - callee.ondatachannel = async (event) => { - let dc2 = event.channel; - dc2.onopen = () => { - expect().assertFail(); - }; - dc2.send(message); - dc2.close(); - await sleep(500); - }; - - let dc1 = caller.createDataChannel('fire-me!'); - dc1.onmessage = (event: webrtc.MessageEvent) => { - console.info(TAG, "data: ", event.data); - expect(event.data).assertEqual(message); - }; - exchangeIceCandidates(caller, callee); - await exchangeOfferAnswer(caller, callee); - await sleep(1000); - }) - - it("inBandNegotiatedChannelMatchSameConfigurationAsLocalPeer", DEFAULT, async () => { - let dc1 = caller.createDataChannel('test', { - ordered: false, - maxRetransmits: 1, - protocol: 'custom' - }); - expect(dc1.label).assertEqual('test'); - expect(dc1.ordered).assertEqual(false); - expect(dc1.maxPacketLifeTime).assertUndefined(); - expect(dc1.maxRetransmits).assertEqual(1); - expect(dc1.protocol).assertEqual('custom'); - expect(dc1.negotiated).assertEqual(false); - - callee.ondatachannel = async (event) => { - const dc2 = event.channel; - expect(dc2.label).assertEqual('test'); - expect(dc2.ordered).assertEqual(false); - expect(dc2.maxPacketLifeTime).assertUndefined(); - expect(dc2.maxRetransmits).assertEqual(1); - expect(dc2.protocol).assertEqual('custom'); - expect(dc2.negotiated).assertEqual(false); - expect(dc2.id).assertEqual(dc1.id); - await sleep(1000); - }; - - exchangeIceCandidates(caller, callee); - await exchangeOfferAnswer(caller, callee); - await sleep(1000); - }) - - it("inBandNegotiatedChannelMatchSameDefaultConfigurationAsLocalPeer", DEFAULT, async () => { - let dc1 = caller.createDataChannel(''); - - expect(dc1.label).assertEqual(''); - expect(dc1.ordered).assertEqual(true); - expect(dc1.maxPacketLifeTime).assertUndefined(); - expect(dc1.maxRetransmits).assertUndefined(); - expect(dc1.protocol).assertEqual(''); - expect(dc1.negotiated).assertEqual(false); - - callee.ondatachannel = async (event) => { - let dc2 = event.channel; - - expect(dc2.label).assertEqual(''); - expect(dc2.ordered).assertEqual(true); - expect(dc2.maxPacketLifeTime).assertUndefined(); - expect(dc2.maxRetransmits).assertUndefined(); - expect(dc2.protocol).assertEqual(''); - expect(dc2.negotiated).assertEqual(false); - expect(dc2.id).assertEqual(dc1.id); - await sleep(1000); - } - exchangeIceCandidates(caller, callee); - await exchangeOfferAnswer(caller, callee); - await sleep(1000); - }) - - it("negotiatedChannelShouldNotFireDatachannelEventOnRemotePeer", DEFAULT, async () => { - callee.ondatachannel = () => { - console.error(TAG, "datachannel event should not be fired"); - expect().assertFail(); - } - - caller.createDataChannel('test', { - negotiated: true, - id: 42 - }); - - exchangeIceCandidates(caller, callee); - await exchangeOfferAnswer(caller, callee); - await sleep(500); - }) - - it("initialIceGatheringStateShouldBeNew", DEFAULT, () => { - expect(caller.iceGatheringState).assertEqual('new'); - }) - - it("stateInIceConnectionState", DEFAULT, () => { - expect(caller.iceConnectionState).assertEqual('new'); - caller.close(); - expect(caller.iceConnectionState).assertEqual('closed'); - }) - - it("initialPeerConnectionShouldHaveListOfZeroSendersReceiversTransceivers", DEFAULT, async () => { - let senders = caller.getSenders(); - expect(senders).assertDeepEquals([]); - let receivers = caller.getReceivers(); - expect(receivers).assertDeepEquals([]); - let transceivers = caller.getTransceivers(); - expect(transceivers).assertDeepEquals([]); - }) - - it("pendingLocalDescriptionIsSurfacedAtTheRightTime", DEFAULT, async () => { - let offer = await caller.createOffer(); - expect(caller.pendingLocalDescription).assertUndefined(); - await caller.setLocalDescription(offer); - expect(caller.pendingLocalDescription).not().assertUndefined(); - expect(caller.pendingLocalDescription).assertDeepEquals(caller.localDescription); - }) - - it("pendingRemoteDescriptionIsSurfacedAtTheRightTime", DEFAULT, async () => { - let offer = await caller.createOffer(); - expect(caller.pendingRemoteDescription).assertUndefined(); - await caller.setRemoteDescription(offer); - expect(caller.pendingRemoteDescription).not().assertUndefined(); - expect(caller.pendingRemoteDescription).assertDeepEquals(caller.remoteDescription); - }) - - it("currentLocalDescriptionIsSurfacedAtTheRightTime", DEFAULT, async () => { - let offer = await caller.createOffer(); - await caller.setLocalDescription(offer); - await callee.setRemoteDescription(offer); - let answer = await callee.createAnswer(); - - expect(callee.currentLocalDescription).assertUndefined(); - await callee.setLocalDescription(answer); - expect(callee.currentLocalDescription).not().assertNull(); - expect(callee.currentLocalDescription).assertDeepEquals(callee.localDescription); - }) - - it("currentRemoteDescriptionIsSurfacedAtTheRightTime", DEFAULT, async () => { - let offer = await caller.createOffer(); - await caller.setLocalDescription(offer); - await callee.setRemoteDescription(offer); - let answer = await callee.createAnswer(); - - expect(caller.currentRemoteDescription).assertUndefined(); - await caller.setRemoteDescription(answer); - expect(caller.currentRemoteDescription).not().assertNull(); - expect(caller.currentRemoteDescription).assertDeepEquals(caller.remoteDescription); - }) - - it("createDataChannelAttributeDefaultValues", DEFAULT, () => { - let dc = caller.createDataChannel(''); - - expect(dc instanceof webrtc.RTCDataChannel).assertTrue(); - expect(dc.label).assertEqual(''); - expect(dc.ordered).assertEqual(true); - expect(dc.maxPacketLifeTime).assertUndefined(); - expect(dc.maxRetransmits).assertUndefined(); - expect(dc.protocol).assertEqual(''); - expect(dc.negotiated).assertFalse() - expect(dc.id).assertUndefined() - expect(dc.readyState).assertEqual('connecting'); - expect(dc.bufferedAmount).assertEqual(0); - expect(dc.bufferedAmountLowThreshold).assertEqual(0); - expect(dc.binaryType).assertEqual('blob'); - }) - - it("createDataChannelWithProvidedParametersShouldInitializeAttributesToProvidedValues", DEFAULT, async () => { - let dc = caller.createDataChannel('test', { - ordered: false, - maxRetransmits: 1, - // Note: maxPacketLifeTime is not set in this test. - protocol: 'custom', - negotiated: true, - id: 3 - }); - expect(dc instanceof webrtc.RTCDataChannel).assertTrue(); - expect(dc.label).assertEqual('test'); - expect(dc.ordered).assertEqual(false); - expect(dc.maxPacketLifeTime).assertUndefined(); - expect(dc.maxRetransmits).assertEqual(1); - expect(dc.protocol).assertEqual('custom'); - expect(dc.negotiated).assertEqual(true); - expect(dc.id).assertEqual(3); - expect(dc.readyState).assertEqual('connecting'); - expect(dc.bufferedAmount).assertEqual(0); - expect(dc.bufferedAmountLowThreshold).assertEqual(0); - // 创建RTCDataChannel对象时,binaryType属性必须初始化为字符串"blob" - expect(dc.binaryType).assertEqual('blob'); - - let dc2 = caller.createDataChannel('test2', { - ordered: false, - maxPacketLifeTime: 42 - }); - expect(dc2.label).assertEqual('test2'); - expect(dc2.maxPacketLifeTime).assertEqual(42); - expect(dc2.maxRetransmits).assertUndefined(); - }) - - it("createDataChannelWithOrderedUndefinedShouldSucceed", DEFAULT, () => { - let dc2 = caller.createDataChannel('', { - ordered: undefined - }); - expect(dc2.ordered).assertTrue(); - }) - - it("createDataChannelWithMaxPacketLifeTime0ShouldSucceed", DEFAULT, () => { - let dc = caller.createDataChannel('', { - maxPacketLifeTime: 0 - }); - expect(dc.maxPacketLifeTime).assertEqual(0); - }) - - it("createDataChannelWithMaxRetransmits0ShouldSucceed", DEFAULT, () => { - const dc = caller.createDataChannel('', { - maxRetransmits: 0 - }); - expect(dc.maxRetransmits).assertEqual(0); - }) - - it("createDataChannelWithBothMaxPacketLifeTimeAndMaxRetransmitsUndefinedShouldSucceed", DEFAULT, () => { - try { - caller.createDataChannel('', { - maxPacketLifeTime: undefined, - maxRetransmits: undefined - }); - } catch (e) { - expect().assertFail() - } - }) - - it("channelsCreatedAfterSetRemoteDescriptionShouldHaveIdAssigned", DEFAULT, async () => { - let negotiatedDc = caller.createDataChannel('negotiated-channel', { - negotiated: true, - id: 42, - }); - expect(negotiatedDc.id).assertEqual(42); - - let dc1 = caller.createDataChannel('channel'); - expect(dc1.id).assertUndefined(); - - let offer = await caller.createOffer(); - await Promise.all([caller.setLocalDescription(offer), callee.setRemoteDescription(offer)]); - let answer = await callee.createAnswer(); - await caller.setRemoteDescription(answer); - - expect(dc1.id).not().assertUndefined(); - expect(dc1.id).assertLargerOrEqual(0); - expect(dc1.id).assertLess(65535); - - let dc2 = caller.createDataChannel('channel'); - - expect(dc2.id).not().assertUndefined(); - expect(dc2.id).assertLarger(0); - expect(dc2.id).assertLess(65535); - expect(dc2).not().assertEqual(dc1); - expect(dc2.label).assertEqual(dc1.label); - expect(dc2.id).not().assertEqual(dc1.id); - expect(negotiatedDc.id).assertEqual(42); - }) - - it("createAnswerReturnsRTCSessionDescriptionInit", DEFAULT, async () => { - let offer = await caller.createOffer(); - await caller.setRemoteDescription(offer); - let answer = await caller.createAnswer(); - expect(typeof answer).assertEqual('object'); - expect(answer instanceof webrtc.RTCSessionDescription).assertFalse(); - }) - - it("canTrickleIceCandidatesPropertyIsTrueAfterSetRemoteDescriptionWithA", DEFAULT, () => { - caller.setRemoteDescription(new webrtc.RTCSessionDescription({ - type: 'offer', sdp: sdp - })); - expect(caller.canTrickleIceCandidates).assertTrue(); - }) - - it("addTransceiverAudioShouldReturnAnAudioTransceiver", DEFAULT, () => { - let transceiver = caller.addTransceiver('audio'); - expect(transceiver instanceof webrtc.RTCRtpTransceiver).assertTrue(); - - expect(transceiver.mid).assertNull(); - - expect(transceiver.direction).assertEqual('sendrecv'); - expect(transceiver.currentDirection).assertNull(); - expect([transceiver]).assertDeepEquals(caller.getTransceivers()); - - let sender = transceiver.sender; - - expect(sender instanceof webrtc.RTCRtpSender).assertTrue(); - expect(sender.track).assertNull(); - expect([sender]).assertDeepEquals(caller.getSenders()); - - let receiver = transceiver.receiver; - expect(receiver instanceof webrtc.RTCRtpReceiver); - - let track = receiver.track; - expect(track instanceof webrtc.MediaStreamTrack).assertTrue(); - expect(track.kind).assertEqual('audio'); - expect(track.readyState).assertEqual('live'); - expect([receiver]).assertDeepEquals(caller.getReceivers()); - }) - - } - - ) +/** + * Copyright (c) 2024 Archermind Technology (Nanjing) Co. Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' +import webrtc from 'libohos_webrtc.so'; +import { Logging, LoggingSeverity } from '../../../main/ets/log/Logging'; +import { sleep } from './Helper'; + +const DEFAULT = 0 +const TAG: string = '[PeerConnectionTest]'; + +const pcf = new webrtc.PeerConnectionFactory(); + +let caller: webrtc.RTCPeerConnection; +let callee: webrtc.RTCPeerConnection; + +const STUN_SERVER = "stun:stun.l.google.com:19302"; + +const sdp = 'v=0\r\n' + + 'o=- 166855176514521964 2 IN IP4 127.0.0.1\r\n' + + 's=-\r\n' + + 't=0 0\r\n' + + 'a=ice-options:trickle\r\n' + + 'm=audio 9 UDP/TLS/RTP/SAVPF 111\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:someufrag\r\n' + + 'a=ice-pwd:somelongpwdwithenoughrandomness\r\n' + + 'a=fingerprint:sha-256 8C:71:B3:8D:A5:38:FD:8F:A4:2E:A2:65:6C:86:52:BC:E0:6E:94:F2:9F:7C:4D:B5:DF:AF:AA:6F:44:90:8D:F4\r\n' + + 'a=setup:actpass\r\n' + + 'a=rtcp-mux\r\n' + + 'a=mid:mid1\r\n' + + 'a=sendonly\r\n' + + 'a=msid:stream1 track1\r\n' + + 'a=ssrc:1001 cname:some\r\n' + + 'a=rtpmap:111 opus/48000/2\r\n'; + +let offer = { + sdp: "v=0\r\no=- 8926124112408190582 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS\r\n", + type: "offer" +} as webrtc.RTCSessionDescriptionInit; + +let answer = { + sdp: "v=0\r\no=- 1824345164880213868 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS\r\n", + type: "answer" +} as webrtc.RTCSessionDescriptionInit; + +let transceiverInit = { + direction: 'sendrecv', + streams: [], + sendEncodings: [{ + maxBitrate: 100000, + }], +} as webrtc.RTCRtpTransceiverInit; + +let dataChannel: webrtc.RTCDataChannelInit = { + ordered: true, // 设置为 true 表示消息是有序的 + maxPacketLifeTime: 3000, // 可选,设置数据包的最大生存时间(毫秒) + protocol: "sctp-protocol", // 可选,设置协议 +}; + +async function exchangeOffer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + await caller.setLocalDescription(await caller.createOffer()); + console.log(TAG, "caller.localDescription: " + caller.localDescription!.type); + await callee.setRemoteDescription(caller.localDescription); +} + +async function exchangeAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + const answer = await callee.createAnswer(); + await caller.setRemoteDescription(answer); + await callee.setLocalDescription(answer); +} + +async function exchangeOfferAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + await exchangeOffer(caller, callee); + await exchangeAnswer(caller, callee); +} + +function exchangeIceCandidates(pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) { + const doExchange = (pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) => { + pc1.onicecandidate = (event) => { + const candidate = event.candidate; + if (pc2.signalingState !== 'closed') { + pc2.addIceCandidate(candidate); + } + }; + }; + + doExchange(pc1, pc2); + doExchange(pc2, pc1); +} + +async function generateAudioReceiveOnlyOffer(pc: webrtc.RTCPeerConnection) { + try { + pc.addTransceiver('audio', { + direction: 'recvonly' + }); + return pc.createOffer(); + } catch (e) { + return pc.createOffer({ + iceRestart: true + }); + } +} + +async function generateAnswer(offer: webrtc.RTCSessionDescriptionInit) { + let pc = pcf.createPeerConnection({ + iceServers: [{ + urls: STUN_SERVER + }] + }); + await pc.setRemoteDescription(offer); + let answer = await pc.createAnswer(); + pc.close(); + return answer; +} + +async function generateOffer() { + let pc = pcf.createPeerConnection({ + iceServers: [{ + urls: STUN_SERVER + }] + }); + let offer = await pc.createOffer(); + pc.close(); + return offer; +} + +async function createSetOffer(options?: webrtc.RTCOfferOptions) { + try { + let offer: webrtc.RTCSessionDescriptionInit; + if (typeof options !== 'object') { + offer = await caller?.createOffer(); + } else { + offer = await caller?.createOffer(options); + } + console.info(TAG, 'createOffer: ' + JSON.stringify(offer)); + expect(offer.type).assertEqual("offer"); + } catch (e) { + console.error(TAG, 'createOffer: ' + JSON.stringify(e)); + expect().assertFail(); + } +} + +async function createSetAnswer(options?: webrtc.RTCAnswerOptions) { + try { + let answer = await caller?.createAnswer(options); + console.info(TAG, 'createAnswer: ' + JSON.stringify(answer)); + expect(answer.type).assertEqual("answer"); + } catch (e) { + console.error(TAG, 'createAnswer: ' + JSON.stringify(e)); + expect().assertFail(); + } +} + +export default function PeerConnectionTest() { + describe('PeerConnectionTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + Logging.enableLogToDebugOutput(LoggingSeverity.VERBOSE); + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + caller = pcf.createPeerConnection({ + iceServers: [{ + urls: STUN_SERVER + }] + }); + callee = pcf.createPeerConnection({ + iceServers: [{ + urls: STUN_SERVER + }] + }); + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + if (caller) { + caller.close(); + } + if (callee) { + callee.close(); + } + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + + it('addAndRemoveTrack', DEFAULT, () => { + let audioSource = pcf?.createAudioSource(); + let audioTrack = pcf?.createAudioTrack("audio", audioSource); + expect(audioTrack.kind).assertEqual("audio"); + let rtpSender = caller.addTrack(audioTrack); + + expect(caller.getSenders().length).assertEqual(1); + console.info(TAG, 'getSenders: ' + caller.getSenders().length); + try { + // removeTrack后,不能通过senders的长度来判断,因为长度不变 + caller?.removeTrack(rtpSender); + expect(caller.getSenders().length).assertEqual(1); + expect(caller.getSenders()[0].track).assertNull(); + } catch (e) { + console.error(TAG, 'Error while removing track: ', JSON.stringify(e)); + expect().assertFail(); + } + }) + + it("addIceCandidate", DEFAULT, async () => { + let candidate: webrtc.RTCIceCandidate | undefined = undefined; + let pormise = new Promise((resolve) => { + caller.onicecandidate = (event) => { + console.info(TAG, 'onicecandidate: ', event.candidate); + candidate = event.candidate; + resolve(); + }; + }); + + caller.onicegatheringstatechange = (event) => { + console.info(TAG, 'onicegatheringstatechange: ', event); + }; + + let audioSource = pcf.createAudioSource(); + let audioTrack = pcf.createAudioTrack("id", audioSource); + + caller.addTrack(audioTrack); + + let offer = await caller?.createOffer(); + await caller?.setRemoteDescription(offer); + console.info(TAG, 'caller.signalingState changed:', caller.signalingState); + + let answer = await caller?.createAnswer(); + await caller?.setLocalDescription(answer); + console.info(TAG, 'caller.signalingState changed:', caller.signalingState); + + await pormise; + expect(candidate).not().assertUndefined(); + + try { + await caller.addIceCandidate(candidate); + } catch (e) { + console.error(TAG, 'addIceCandidate error :', JSON.stringify(e)); + expect().assertFail(); + } + }) + + it("AddTransceiverWithStringNoInit", DEFAULT, () => { + let tranSceiver = caller?.addTransceiver("audio"); + console.info(TAG, 'addTransceiver: ' + JSON.stringify(tranSceiver)); + expect(caller.getTransceivers().length).assertEqual(1); + }) + + it("AddTransceiverWithTrackNoInit", DEFAULT, () => { + let audioSource = pcf?.createAudioSource({ + echoCancellation: false + }); + let audioTrack = pcf?.createAudioTrack("audio", audioSource); + let tranSceiver = caller?.addTransceiver(audioTrack); + console.info(TAG, 'addTransceiver: ' + JSON.stringify(tranSceiver)); + expect(caller.getTransceivers().length).assertEqual(1); + }) + + it("AddTransceiverWithTrackAndInit", DEFAULT, () => { + let audioSource = pcf?.createAudioSource({ + echoCancellation: false + }); + let audioTrack = pcf?.createAudioTrack("audio", audioSource); + let tranSceiver = caller?.addTransceiver(audioTrack, transceiverInit); + console.info(TAG, 'addTransceiver: ' + JSON.stringify(tranSceiver)); + expect(caller.getTransceivers().length).assertEqual(1); + }) + + it("AddTransceiverWithStringAndInit", DEFAULT, () => { + let tranSceiver = caller?.addTransceiver("audio", transceiverInit); + console.info(TAG, 'addTransceiver: ' + JSON.stringify(tranSceiver)); + expect(caller.getTransceivers().length).assertEqual(1); + }) + + it("createOffer", DEFAULT, async () => { + createSetOffer(); + }) + + it("createOfferAddOptions", DEFAULT, async () => { + let options: webrtc.RTCOfferOptions = { + iceRestart: true + } + createSetOffer(options); + }) + + it("createAnswer", DEFAULT, async () => { + await caller?.setRemoteDescription(offer); + createSetAnswer(); + }) + + it("createAnswerAddOptions", DEFAULT, async () => { + await caller?.setRemoteDescription(offer); + let options: webrtc.RTCAnswerOptions = {}; + createSetAnswer(options); + }) + + it("setLocalDescriptionWithOffer", DEFAULT, async () => { + await caller?.setLocalDescription(offer); + expect(caller.signalingState).assertEqual("have-local-offer"); + }) + + it("setLocalDescriptionNoOffer", DEFAULT, async () => { + await caller?.setLocalDescription(); + expect(caller.signalingState).assertEqual("have-local-offer"); + expect(caller.pendingLocalDescription).not().assertNull(); + }) + + it("setLocalDescriptionWithAnswer", DEFAULT, async () => { + await caller?.setRemoteDescription(offer); + await caller?.setLocalDescription(answer); + expect(caller.signalingState).assertEqual("stable"); + }) + + it("setLocalDescriptionNoAnswer", DEFAULT, async () => { + await callee.setRemoteDescription(await caller.createOffer()); + await callee.setLocalDescription(); + expect(callee.currentLocalDescription).not().assertNull(); + expect(caller.signalingState).assertEqual("stable"); + }) + + it("setRemoteDescription", DEFAULT, async () => { + expect(caller.signalingState).assertEqual("stable"); + caller.onsignalingstatechange = () => { + console.log(TAG, 'Signaling state change: ', caller.signalingState); + }; + await caller?.setRemoteDescription(offer); + expect(caller.signalingState).assertEqual("have-remote-offer"); + }) + + it("getReceivers", DEFAULT, () => { + let receiver = caller.getReceivers(); + expect(receiver.length).assertEqual(0); + + let audioSource = pcf?.createAudioSource(); + let audioTrack = pcf?.createAudioTrack("audio", audioSource); + + let rtpSender = caller.addTrack(audioTrack); + expect(caller.getSenders().length).assertEqual(1); + + let newReceiver = caller.getReceivers(); + expect(newReceiver.length).assertEqual(1); + }) + + it("createDataChannelWithInit", DEFAULT, () => { + let channel = caller.createDataChannel("1", dataChannel); + + expect(channel.label).assertEqual("1"); + expect(channel.ordered).assertTrue(); + expect(channel.maxPacketLifeTime).assertEqual(3000); + expect(channel.protocol).assertEqual("sctp-protocol"); + }) + + it("createDataChannelNoInit", DEFAULT, () => { + let channel = caller.createDataChannel("1"); + + console.info(TAG, 'channel: ', JSON.stringify(channel)); + + expect(channel.label).assertEqual("1"); + expect(channel.ordered).assertTrue(); + }) + + it("setAndGetConfiguration", DEFAULT, async () => { + expect(caller.getConfiguration().iceCandidatePoolSize).assertEqual(0) + caller.setConfiguration(); + let config = caller.getConfiguration(); + + expect(config.iceCandidatePoolSize).assertEqual(0); + + let newConfig: webrtc.RTCConfiguration = { + iceServers: [{ + urls: "stun:stun1.l.google.com:19302" + },], + iceCandidatePoolSize: 10 + }; + caller.setConfiguration(newConfig); + + caller.getConfiguration(); + expect(caller.getConfiguration().iceCandidatePoolSize).assertEqual(10); + + try { + let stat = await caller.getStats(); + console.info(TAG, 'getStats: ' + JSON.stringify(stat)); + } catch (e) { + console.error(TAG, 'getStats error : ', JSON.stringify(e)) + expect().assertFail() + } + }) + + it("restartIce", DEFAULT, async () => { + let flag = false; + let promise = new Promise((resolve) => { + caller.onnegotiationneeded = async () => { + flag = true; + resolve(); + } + }); + caller.restartIce(); + await promise; + expect(flag).assertTrue(); + }) + + it("afterCloseCallerSetConfigurationWillFail", DEFAULT, () => { + caller.close(); + try { + caller.setConfiguration(); + expect().assertFail(); + } catch (e) { + console.error(TAG, "close:", e); + } + }) + + /*it("orderInSetLocalDescriptionAndAddIceCandidate", DEFAULT, async () => { + let candidate: webrtc.RTCIceCandidate | undefined = undefined; + caller.onicecandidate = (event) => { + console.info(TAG, 'onicecandidate: ', event.candidate); + candidate = event.candidate; + }; + caller.addTransceiver('audio'); + await exchangeOffer(caller, callee); + await sleep(1000); + let pendingPromises: Promise[] = []; + let resolveOrder: string[] = []; + pendingPromises.push(callee.setLocalDescription().then(() => { + resolveOrder.push('setLocalDescription 1'); + })); + pendingPromises.push(callee.setLocalDescription().then(() => { + resolveOrder.push('setLocalDescription 2'); + })); + pendingPromises.push(callee.addIceCandidate(candidate).then(() => { + resolveOrder.push('addIceCandidate'); + })); + await Promise.all(pendingPromises); + expect(resolveOrder[0]).assertEqual('setLocalDescription 1'); + // ToDo 这里顺序有些问题 + expect(resolveOrder).assertEqual(['setLocalDescription 1', 'setLocalDescription 2', 'addIceCandidate']); + })*/ + + it("orderOfOnicecandidateAndSetLocalDescription", DEFAULT, async () => { + caller.addTransceiver('audio'); + let events: string[] = []; + let pendingPromises: Promise[] = []; + + caller.onicecandidate = (event) => { + console.info(TAG, 'onicecandidate: ', event.candidate); + }; + let onCandidatePromise = new Promise((resolve) => { + caller.onicecandidate = resolve; + }); + await sleep(1000); + pendingPromises.push(onCandidatePromise.then(() => { + events.push('candidate generated'); + })); + pendingPromises.push(caller.setLocalDescription().then(() => { + events.push('setLocalDescription'); + })); + await Promise.all(pendingPromises); + expect(events).assertDeepEquals(['setLocalDescription', 'candidate generated']); + }) + + it("directionWillChanged", DEFAULT, async () => { + let calleePromise = callee.createOffer().then((offer) => { + return caller.setRemoteDescription(offer); + }).then(() => { + return caller.createAnswer(); + }); + caller.addTransceiver('audio', { + direction: 'recvonly' + }); + + let callerPromise = caller.createOffer().then(() => { + caller.getTransceivers()[0].direction = 'inactive'; + }); + await Promise.all([callerPromise, calleePromise]); + expect(caller.getTransceivers()[0].direction).assertEqual('inactive'); + }) + + it("transceiverSenderTrackDoesNotRevertToAnOldState", DEFAULT, async () => { + let offer = await callee.createOffer(); + await caller.setRemoteDescription(offer); + let answer = await caller.createAnswer(); + await caller.setLocalDescription(answer); + caller.addTransceiver('audio', { + direction: 'recvonly' + }); + await caller.createOffer(); + caller.addTrack(caller.getReceivers()[0].track); + expect(caller.getSenders()[0].track!.id).assertEqual(caller.getReceivers()[0].track.id); + }) + + /*it("orderOfSignalingState", DEFAULT, async () => { + let signalingStates: webrtc.RTCSignalingState[] = []; + + caller.onsignalingstatechange = () => { + console.info(TAG, "add signalingStates: ", caller.signalingState); + signalingStates.push(caller.signalingState); + } + + caller.addTransceiver('audio', { + direction: 'recvonly' + }); + let offer = await caller.createOffer(); + let sldPromise = caller.setLocalDescription(offer); + // offer state为stable or have-remote-offer + // https://www.w3.org/TR/webrtc/#dom-peerconnection-setremotedescription + let srdPromise = caller.setRemoteDescription(offer); + await Promise.all([sldPromise, srdPromise]); + + console.info(TAG, "signalingStates: ", signalingStates.toString()) + // expect(signalingStates).assertDeepEquals(['have-local-offer', 'stable', 'have-remote-offer']); + })*/ + + /*it("stateChanged", DEFAULT, async () => { + let eventCount = 0; + const states = [ + 'stable', 'have-local-offer', 'stable', 'have-remote-offer', + ]; + + caller.onsignalingstatechange = () => { + expect(caller.signalingState).assertEqual(states[++eventCount]); + } + let offer = await generateAudioReceiveOnlyOffer(caller); + console.info(TAG, "signalingStates: ", caller.signalingState) + // expect(caller.signalingState).assertEqual('stable'); + await caller.setLocalDescription(offer); + console.info(TAG, "signalingStates: ", caller.signalingState) + // expect(caller.signalingState).assertEqual('have-local-offer'); + + // offer state为stable or have-remote-offer + // https://www.w3.org/TR/webrtc/#dom-peerconnection-setremotedescription + await caller.setRemoteDescription(offer); + await exchangeAnswer(caller, callee); + console.info(TAG, "signalingStates: ", caller.signalingState) + // expect(caller.signalingState).assertEqual('stable'); + await exchangeOffer(caller, callee); + console.info(TAG, "signalingStates: ", caller.signalingState) + // expect(caller.signalingState).assertEqual('have-remote-offer'); + })*/ + + it("inValidSdp", DEFAULT, async () => { + try { + await caller.setRemoteDescription({ + sdp: 'bogus', type: 'offer' + }); + } catch (e) { + console.error(TAG, "e: ", e); + expect(e).assertInstanceOf("Error"); + } + }) + + it("rollbackInvalidSdp", DEFAULT, async () => { + try { + const offer = await generateAudioReceiveOnlyOffer(caller); + await caller.setRemoteDescription(offer); + await caller.setRemoteDescription({ + type: 'rollback', + sdp: '!;' + }); + } catch (e) { + console.error(TAG, "e: ", e); + expect(e).assertInstanceOf("Error"); + } + }); + + it("localOfferCreatedBeforeSetRemoteDescriptionThenRollbackShouldStillBeUsable", DEFAULT, async () => { + caller.addTransceiver('audio', { + direction: 'recvonly' + }); + let offer1 = await caller.createOffer(); + callee.addTransceiver('audio', { + direction: 'recvonly' + }); + let offer2 = await callee.createOffer(); + try { + await caller.setRemoteDescription(offer2); + await caller.setRemoteDescription({ + type: "rollback" + }); + await caller.setLocalDescription(offer1); + } catch (e) { + console.error(TAG, "e: ", e); + expect().assertFail(); + } + }) + + it("removeTrackWithASenderBeingRolledBackDoesNotCrashOrThrow", DEFAULT, async () => { + callee.addTransceiver('audio'); + let offer = await callee.createOffer(); + await caller.setRemoteDescription(offer); + let transceiver: webrtc.RTCRtpTransceiver[] = caller.getTransceivers(); + caller.setRemoteDescription({ + type: 'rollback' + }); + caller.removeTrack(transceiver[0].sender); + }) + + it("setRemoteDescriptionPranswerWhenStable", DEFAULT, async () => { + let offer = await caller.createOffer(); + console.info(TAG, "signalingState: ", caller.signalingState); + try { + caller.setRemoteDescription({ + type: 'pranswer', sdp: offer.sdp + }); + } catch (e) { + console.error(TAG, "e: ", e); + expect(e).assertEqual("InvalidStateError"); + } + }) + + it("setRemoteDescriptionWithValidOfferShouldSucceed", DEFAULT, async () => { + caller.createDataChannel('datachannel'); + let states: webrtc.RTCSignalingState[] = []; + let promise = new Promise((resolve) => { + callee.onsignalingstatechange = () => { + console.info(TAG, "callee.signalingState: ", callee.signalingState); + states.push(callee.signalingState); + resolve(); + }; + }); + let offer = await caller.createOffer(); + await callee.setRemoteDescription(offer); + await promise; + expect(callee.signalingState).assertEqual('have-remote-offer'); + // expect(callee.remoteDescription).assertDeepEquals(offer); + // expect(callee.pendingRemoteDescription).assertDeepEquals(offer); + expect(callee.currentRemoteDescription).assertUndefined(); + expect(states).assertDeepEquals(['have-remote-offer']); + }) + + it("setRemoteDescriptionWithInvalidSdp", DEFAULT, async () => { + try { + await caller.setRemoteDescription({ + type: 'offer', + sdp: 'Invalid SDP' + }); + console.error(TAG, 'Expect promise to be rejected'); + expect().assertFail(); + } catch (e) { + console.error(TAG, "errorDetail: ", e); + } + }); + + it("onsignalingstatechangeBeforeSetLocalDescription", DEFAULT, async () => { + const offer = await caller.createOffer(); + let eventSequence = ''; + + let promise = new Promise((resolve) => { + caller.onsignalingstatechange = () => { + console.info(TAG, "signalingState: ", caller.signalingState); + eventSequence += 'onsignalingstatechange;'; + resolve(); + }; + }) + + await caller.setLocalDescription(offer); + await promise; + eventSequence += 'setLocalDescription;'; + expect(eventSequence).assertEqual('onsignalingstatechange;setLocalDescription;'); + }); + + it("setLocalDescriptionRollbackFromLocalOffer", DEFAULT, async () => { + let states: webrtc.RTCSignalingState [] = []; + + let promise = new Promise((resolve) => { + caller.onsignalingstatechange = () => { + console.info(TAG, "caller.signalingState: ", caller.signalingState); + states.push(caller.signalingState); + resolve(); + } + }) + + let offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + await promise; + expect(caller.signalingState).assertEqual('have-local-offer'); + expect(caller.localDescription).not().assertNull(); + expect(caller.pendingLocalDescription).assertDeepEquals(caller.localDescription); + expect(caller.currentLocalDescription).assertUndefined(); + let sldPromise = new Promise(async (resolve) => { + await caller.setLocalDescription({ + type: 'rollback' + }); + resolve(); + }) + await sldPromise; + expect(caller.signalingState).assertEqual('stable'); + expect(caller.localDescription).assertUndefined(); + expect(caller.pendingLocalDescription).assertUndefined(); + expect(caller.currentLocalDescription).assertUndefined(); + expect(states).assertDeepEquals(['have-local-offer', 'stable']); + }) + + it("setLocalDescriptionRollbackFromState", DEFAULT, async () => { + try { + await caller.setLocalDescription({ + type: 'rollback' + }) + expect().assertFail(); + } catch (e) { + console.error(TAG, "e: ", e); + } + }) + + it("setLocalDescriptionRollbackAfterSettingAnswerDescriptionShouldRejectWithInvalidStateError", DEFAULT, + async () => { + let offer = await generateAudioReceiveOnlyOffer(caller); + await caller.setRemoteDescription(offer); + let answer = await caller.createAnswer(); + await caller.setLocalDescription(answer); + try { + await caller.setLocalDescription({ + type: 'rollback' + }); + expect().assertFail(); + } catch (e) { + console.error(TAG, "e: ", e); + } + }) + + /*it("setLocalDescriptionRollbackAfterSettingARemoteOfferShouldRejectWithError", DEFAULT, async () => { + let offer = await generateAudioReceiveOnlyOffer(caller); + await caller.setRemoteDescription(offer); + try { + // ToDo 测试预期应该抛出,但是没有抛出,标准中也没有详细的规范表示一定会抛出,先暂停 + await caller.setLocalDescription({ + type: 'rollback' + }); + expect().assertFail(); + } catch (e) { + console.info(TAG, "e: ", e); + } + })*/ + + it("setLocalDescriptionRollbackIgnoreInvalidSdp", DEFAULT, async () => { + let offer = await caller.createOffer() + caller.setLocalDescription(offer) + try { + caller.setLocalDescription({ + type: 'rollback', + sdp: '!;' + }); + } catch (e) { + expect().assertFail(); + } + }) + + it("setLocalDescriptionRollbackShouldUpdateInternalStateWithAQueuedTaskInTheRightOrder", DEFAULT, async () => { + caller.addTransceiver('audio', { + direction: 'recvonly' + }); + await caller.setLocalDescription(await caller.createOffer()); + expect(caller.signalingState).assertEqual("have-local-offer"); + expect(caller.pendingLocalDescription).not().assertNull(); + // expect(caller.pendingLocalDescription.type).assertEqual("offer"); + expect(caller.pendingLocalDescription).assertDeepEquals(caller.localDescription); + expect(caller.pendingRemoteDescription).assertUndefined() + const sldPromise = caller.setLocalDescription({ + type: "rollback" + }); + const stablePromise = new Promise((resolve, reject) => { + caller.onsignalingstatechange = () => { + resolve(caller.signalingState); + } + }); + let raceValue = await Promise.race([stablePromise, sldPromise]); + expect(caller.signalingState).assertEqual("stable"); + expect(caller.pendingLocalDescription).assertUndefined(); + expect(caller.pendingRemoteDescription).assertUndefined(); + + await sldPromise; + }) + + it("setLocalDescriptionPranswerFromStable", DEFAULT, async () => { + let offer = await caller.createOffer(); + try { + await caller.setLocalDescription({ + type: 'pranswer', sdp: offer.sdp + }); + } catch (e) { + console.error(TAG, "e: ", e); + expect(e).assertInstanceOf("Error"); + } + }) + + it("ParameterlessSldStable", DEFAULT, async () => { + let transceiver = caller.addTransceiver('audio'); + expect(transceiver.mid).assertNull(); + await caller.setLocalDescription(); + expect(transceiver.mid).not().assertNull(); + }) + + it("ParameterlessRemoteoffer", DEFAULT, async () => { + caller.addTransceiver('audio'); + + let promise = new Promise((resolve) => { + callee.ontrack = async () => { + console.info(TAG, "currentDirection: ", callee.getTransceivers()[0].currentDirection); + resolve(); + } + }) + await callee.setRemoteDescription(await caller.createOffer()); + await callee.setLocalDescription(); + await promise; + expect(callee.getTransceivers()[0].currentDirection).assertEqual('recvonly'); + }) + + /*it("ParameterlessSLDUsesLastCreatedAnswerIfItIsStillValid", DEFAULT, async () => { + let offer = await caller.createOffer(); + await callee.setRemoteDescription(await caller.createOffer()); + let answer = await callee.createAnswer(); + await callee.setLocalDescription(); + console.info(TAG, "offer.sdp: ", JSON.stringify(offer?.sdp)); + console.info(TAG, "currentRemoteDescription: ", JSON.stringify(callee?.currentRemoteDescription!.sdp)); + console.info(TAG, "answer.sdp: ", JSON.stringify(answer?.sdp)); + console.info(TAG, "currentLocalDescription: ", JSON.stringify(callee?.currentLocalDescription!.sdp)); + // ToDo 会话id会变化导致sdp不一致 + expect(callee?.currentLocalDescription!.sdp.toString()).assertEqual(answer?.sdp.toString()); + })*/ + + it("exchangeOA", DEFAULT, async () => { + try { + await caller.setLocalDescription(); + await callee.setRemoteDescription(caller.pendingLocalDescription); + + await callee.setLocalDescription(); + await caller.setRemoteDescription(callee.currentLocalDescription); + } catch (e) { + console.error(TAG, "e: ", e); + expect().assertFail(); + } + }) + + it("setLocalDescriptionWithValidOfferShouldSucceed", DEFAULT, async () => { + let states: String = ""; + let promise = new Promise((resolve) => { + caller.onsignalingstatechange = () => { + states = caller.signalingState; + resolve(); + }; + }) + + let offer = await generateAudioReceiveOnlyOffer(caller); + await caller.setLocalDescription(offer); + expect(caller.signalingState).assertEqual('have-local-offer'); + // expect(JSON.stringify(caller.pendingLocalDescription)).assertEqual(JSON.stringify(caller.localDescription)); + expect(caller.currentLocalDescription).assertUndefined(); + await promise; + expect(states).assertEqual('have-local-offer'); + }) + + it("localOfferCreatedBeforeSetRemoteDescriptionRemoteOfferThenRollbackShouldStillBeUsable", DEFAULT, async () => { + try { + caller.addTransceiver('audio', { + direction: 'recvonly' + }); + let offer = await caller.createOffer(); + callee.addTransceiver('audio', { + direction: 'recvonly' + }); + let offer2 = await callee.createOffer(); + + await caller.setRemoteDescription(offer2); + await caller.setRemoteDescription({ + type: "rollback" + }); + await caller.setLocalDescription(offer); + } catch (e) { + console.error(TAG, "e: ", e); + expect().assertFail(); + } + }) + + it("offerSetRepeatedly", DEFAULT, async () => { + caller.addTransceiver('audio', { + direction: 'recvonly' + }); + await caller.setLocalDescription(await caller.createOffer()); + + const offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + await callee.setRemoteDescription(offer); + const answer = await callee.createAnswer(); + await callee.setLocalDescription(answer); + await caller.setRemoteDescription(answer); + + expect(caller.getTransceivers().length).assertEqual(1); + expect(caller.getTransceivers()[0].receiver.track.kind).assertEqual("audio"); + expect(callee.getTransceivers().length).assertEqual(1); + expect(callee.getTransceivers()[0].receiver.track.kind).assertEqual("audio"); + }) + + it("setLocalDescriptionAnswerStateInvalidStateError", DEFAULT, async () => { + let offer = await caller.createOffer() + console.info(TAG, "signalingState: ", caller.signalingState); + try { + await caller.setLocalDescription({ + type: 'answer', sdp: offer.sdp + }); + expect().assertFail() + } catch (e) { + console.error(TAG, "error: ", e); + } + }) + + it("setLocalDescriptionAnswerWhenLocalOffer", DEFAULT, async () => { + let offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + let answer = await generateAnswer(offer); + console.info(TAG, "signalingState: ", caller.signalingState); + try { + await caller.setLocalDescription(answer); + expect().assertFail() + } catch (e) { + console.error(TAG, "error: ", e); + } + }) + + it("setPreviouslyAnswerCreateOfferShouldWork", DEFAULT, async () => { + try { + caller.addTransceiver('audio', { + direction: 'recvonly' + }); + let offer = await caller.createOffer(); + await callee.setRemoteDescription(offer); + let answer = await callee.createAnswer(); + await callee.setRemoteDescription({ + type: "rollback" + }); + callee.addTransceiver('video', { + direction: 'recvonly' + }); + await callee.createOffer(); + await callee.setRemoteDescription(offer); + await callee.setLocalDescription(answer); + } catch (e) { + console.error(TAG, "e: ", e); + expect().assertFail(); + } + }) + + it("setTransceiverInactive", DEFAULT, async () => { + caller.addTransceiver('audio'); + const offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + + await callee.setRemoteDescription(offer); + callee.getTransceivers()[0].stop(); + const answer = await callee.createAnswer(); + + await caller.setRemoteDescription(answer); + + expect(caller.getTransceivers()[0].currentDirection).assertEqual('inactive'); + }) + + it("inStateOnnegotiationneededNotPremature", DEFAULT, async () => { + caller.addTransceiver("audio"); + caller.onnegotiationneeded = () => { + console.info(TAG, "signalingState: ", caller.signalingState); + } + await caller.setLocalDescription(await caller.createOffer()); + caller.restartIce(); + caller.onnegotiationneeded = () => { + console.info(TAG, "signalingState: ", caller.signalingState); + } + await callee.setRemoteDescription(caller.localDescription); + await callee.setLocalDescription(await callee.createAnswer()); + await caller.setRemoteDescription(callee.localDescription); + }) + + it("addTransceiverWithInactiveDirectionNotCauseOntrack", DEFAULT, async () => { + caller.addTransceiver('audio', { + direction: 'inactive' + }); + let flag: Boolean = false; + callee.ontrack = () => { + flag = true; + } + await callee.setRemoteDescription(await caller.createOffer()); + await sleep(1000); + expect(flag).assertEqual(false); + }) + + it("negotiationneededFireWhenSignalingBackStateAfterSetRemoteDescription", DEFAULT, async () => { + caller.addTransceiver('audio'); + const offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + let flag = false; + let promise = new Promise((resolve) => { + caller.onnegotiationneeded = () => { + flag = true; + resolve(); + } + }) + caller.createDataChannel('test'); + await caller.setRemoteDescription(await generateAnswer(offer)); + await promise; + expect(flag).assertTrue(); + }) + + it("negotiationneededFireWhenSignalingBackStateAfterSetLocalDescription", DEFAULT, async () => { + caller.addTransceiver('audio'); + let flag = false; + let promise = new Promise((resolve) => { + caller.onnegotiationneeded = () => { + flag = true; + resolve(); + } + }) + await caller.setRemoteDescription(await generateOffer()); + caller.createDataChannel('test'); + await caller.setLocalDescription(await caller.createAnswer()); + console.info(TAG, "signalingState: ", caller.signalingState); + await promise; + expect(flag).assertTrue(); + }) + + it("UpdatingDirectionTransceiverShouldCauseNegotiationneeded", DEFAULT, async () => { + let transceiver = caller.addTransceiver('audio', { + direction: 'sendrecv' + }); + let event = ""; + let promise = new Promise((resolve) => { + caller.onnegotiationneeded = () => { + event = 'recvonly'; + resolve(); + } + }); + let offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + let answer = await generateAnswer(offer); + await caller.setRemoteDescription(answer); + transceiver.direction = 'recvonly'; + await promise; + expect(event).assertEqual('recvonly'); + }) + + it("setStreamsShouldCauseNegotiationneeded", DEFAULT, async () => { + let transceiver = caller.addTransceiver('audio', { + direction: 'sendrecv' + }); + let offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + let answer = await generateAnswer(offer); + await caller.setRemoteDescription(answer); + + let stream = new webrtc.MediaStream(); + let flag = false; + let promise = new Promise((resolve) => { + caller.onnegotiationneeded = () => { + flag = true; + resolve(); + } + }); + transceiver.sender.setStreams(stream); + await promise; + expect(flag).assertTrue(); + }) + + it("setStreamsDifferentStreamShouldCauseNegotiationneeded", DEFAULT, async () => { + let transceiver = caller.addTransceiver('audio', { + direction: 'sendrecv' + }); + let stream = new webrtc.MediaStream(); + transceiver.sender.setStreams(stream); + + let offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + let answer = await generateAnswer(offer); + await caller.setRemoteDescription(answer); + + let stream2 = new webrtc.MediaStream(); + let flag = false; + let promise = new Promise((resolve) => { + caller.onnegotiationneeded = () => { + flag = true; + resolve(); + } + }); + transceiver.sender.setStreams(stream2); + await promise; + expect(flag).assertTrue(); + }) + + it("setStreamsWithAdditionalStreamShouldCauseNegotiationneeded", DEFAULT, async () => { + let transceiver = caller.addTransceiver('audio', { + direction: 'sendrecv' + }); + let stream = new webrtc.MediaStream(); + transceiver.sender.setStreams(stream); + + let offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + let answer = await generateAnswer(offer); + await caller.setRemoteDescription(answer); + + let stream2 = new webrtc.MediaStream(); + let flag = false; + let promise = new Promise((resolve) => { + caller.onnegotiationneeded = () => { + flag = true; + resolve(); + } + }) + transceiver.sender.setStreams(stream, stream2); + await promise; + expect(flag).assertTrue(); + }) + + it("sendDataInDatachannelEvent", DEFAULT, async () => { + let message = 'meow meow!'; + + callee.ondatachannel = (event) => { + console.info(TAG, "send data"); + let dc2 = event.channel; + dc2.send(message); + }; + + const dc1 = caller.createDataChannel('fire-me!'); + let data: string = ""; + let promise = new Promise((resolve) => { + dc1.onmessage = (event: webrtc.MessageEvent) => { + data = event.data; + resolve(); + }; + }); + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + await promise; + expect(data).assertEqual(message); + }) + + it("closeChannelNotCauseOnopenInDatachannelEvent", DEFAULT, async () => { + let flag = false; + let promise = new Promise((resolve) => { + callee.ondatachannel = async (event) => { + const dc = event.channel; + dc.onopen = () => { + flag = true; + expect().assertFail(); + }; + dc.close(); + resolve(); + }; + }) + caller.createDataChannel('fire-me!'); + + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + await promise; + expect(flag).assertEqual(false); + }) + + it("closeChannelWhenSendNotCauseOnopenInDataChannelEvent", DEFAULT, async () => { + let message = 'meow meow!'; + callee.ondatachannel = async (event) => { + let dc2 = event.channel; + dc2.onopen = () => { + expect().assertFail(); + }; + dc2.send(message); + dc2.close(); + }; + + let dc1 = caller.createDataChannel('fire-me!'); + let res = ""; + let promise = new Promise((resolve) => { + dc1.onmessage = (event: webrtc.MessageEvent) => { + res = event.data; + resolve(); + }; + }) + + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + await promise; + expect(res).assertEqual(message); + }) + + it("inBandNegotiatedChannelMatchSameConfigurationAsLocalPeer", DEFAULT, async () => { + let dc1 = caller.createDataChannel('test', { + ordered: false, + maxRetransmits: 1, + protocol: 'custom' + }); + expect(dc1.label).assertEqual('test'); + expect(dc1.ordered).assertEqual(false); + expect(dc1.maxPacketLifeTime).assertUndefined(); + expect(dc1.maxRetransmits).assertEqual(1); + expect(dc1.protocol).assertEqual('custom'); + expect(dc1.negotiated).assertEqual(false); + + let promise = new Promise((resolve) => { + callee.ondatachannel = async (event) => { + const dc2 = event.channel; + expect(dc2.label).assertEqual('test'); + expect(dc2.ordered).assertEqual(false); + expect(dc2.maxPacketLifeTime).assertUndefined(); + expect(dc2.maxRetransmits).assertEqual(1); + expect(dc2.protocol).assertEqual('custom'); + expect(dc2.negotiated).assertEqual(false); + expect(dc2.id).assertEqual(dc1.id); + resolve(); + }; + }); + + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + await promise; + }) + + it("inBandNegotiatedChannelMatchSameDefaultConfigurationAsLocalPeer", DEFAULT, async () => { + let dc1 = caller.createDataChannel(''); + + expect(dc1.label).assertEqual(''); + expect(dc1.ordered).assertEqual(true); + expect(dc1.maxPacketLifeTime).assertUndefined(); + expect(dc1.maxRetransmits).assertUndefined(); + expect(dc1.protocol).assertEqual(''); + expect(dc1.negotiated).assertEqual(false); + + let promise = new Promise((resolve) => { + callee.ondatachannel = async (event) => { + let dc2 = event.channel; + expect(dc2.label).assertEqual(''); + expect(dc2.ordered).assertEqual(true); + expect(dc2.maxPacketLifeTime).assertUndefined(); + expect(dc2.maxRetransmits).assertUndefined(); + expect(dc2.protocol).assertEqual(''); + expect(dc2.negotiated).assertEqual(false); + expect(dc2.id).assertEqual(dc1.id); + resolve(); + } + }); + + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + await promise; + }) + + it("negotiatedChannelShouldNotFireDatachannelEventOnRemotePeer", DEFAULT, async () => { + callee.ondatachannel = () => { + console.error(TAG, "datachannel event should not be fired"); + expect().assertFail(); + } + + caller.createDataChannel('test', { + negotiated: true, + id: 42 + }); + + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + await sleep(1000); + }) + + it("initialIceGatheringStateShouldBeNew", DEFAULT, () => { + expect(caller.iceGatheringState).assertEqual('new'); + }) + + /*it("gatheringStateAndCandidateCallbacksShouldFireInTheCorrectOrder", DEFAULT, async () => { + // ToDo addTransceiver后续实现内部 + let transceiver = caller.addTransceiver('audio', { + direction: 'recvonly' + }); + await caller.setLocalDescription(); + let iceTransport = transceiver.sender.transport!.iceTransport; + while (true) { + const candidateEvent = await new Promise((resolve, reject) => { + caller.onicecandidate = resolve; + }); + expect(candidateEvent).not().assertNull(); + const candidate = candidateEvent!.candidate; + if (candidate!.candidate == '') { + break; + } + } + expect(iceTransport.gatheringState).assertEqual('gathering'); + expect(caller.iceGatheringState).assertEqual('gathering'); + + let events: string[] = []; + + iceTransport.ongatheringstatechange = () => { + expect(iceTransport.gatheringState).assertEqual('complete'); + expect(caller.iceGatheringState).assertEqual('complete'); + events.push('gatheringstatechange'); + }; + caller.onicegatheringstatechange = () => { + expect(iceTransport.gatheringState).assertEqual('complete'); + expect(caller.iceGatheringState).assertEqual('complete'); + events.push('icegatheringstatechange'); + } + caller.onicecandidate = e => { + expect(e.candidate).assertEqual(null); + expect(iceTransport.gatheringState).assertEqual('complete'); + expect(caller.iceGatheringState).assertEqual('complete'); + events.push('icecandidate'); + }; + await sleep(1000); + expect(events).assertDeepEquals(['gatheringstatechange', 'icegatheringstatechange', 'icecandidate']); + })*/ + + it("stateInIceConnectionState", DEFAULT, () => { + expect(caller.iceConnectionState).assertEqual('new'); + caller.close(); + expect(caller.iceConnectionState).assertEqual('closed'); + }) + + it("initialPeerConnectionShouldHaveListOfZeroSendersReceiversTransceivers", DEFAULT, async () => { + let senders = caller.getSenders(); + expect(senders).assertDeepEquals([]); + let receivers = caller.getReceivers(); + expect(receivers).assertDeepEquals([]); + let transceivers = caller.getTransceivers(); + expect(transceivers).assertDeepEquals([]); + }) + + it("pendingLocalDescriptionIsSurfacedAtTheRightTime", DEFAULT, async () => { + let offer = await caller.createOffer(); + expect(caller.pendingLocalDescription).assertUndefined(); + await caller.setLocalDescription(offer); + expect(caller.pendingLocalDescription).not().assertUndefined(); + expect(caller.pendingLocalDescription).assertDeepEquals(caller.localDescription); + }) + + it("pendingRemoteDescriptionIsSurfacedAtTheRightTime", DEFAULT, async () => { + let offer = await caller.createOffer(); + expect(caller.pendingRemoteDescription).assertUndefined(); + await caller.setRemoteDescription(offer); + expect(caller.pendingRemoteDescription).not().assertUndefined(); + expect(caller.pendingRemoteDescription).assertDeepEquals(caller.remoteDescription); + }) + + it("currentLocalDescriptionIsSurfacedAtTheRightTime", DEFAULT, async () => { + let offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + await callee.setRemoteDescription(offer); + let answer = await callee.createAnswer(); + + expect(callee.currentLocalDescription).assertUndefined(); + await callee.setLocalDescription(answer); + expect(callee.currentLocalDescription).not().assertNull(); + expect(callee.currentLocalDescription).assertDeepEquals(callee.localDescription); + }) + + it("currentRemoteDescriptionIsSurfacedAtTheRightTime", DEFAULT, async () => { + let offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + await callee.setRemoteDescription(offer); + let answer = await callee.createAnswer(); + + expect(caller.currentRemoteDescription).assertUndefined(); + await caller.setRemoteDescription(answer); + expect(caller.currentRemoteDescription).not().assertNull(); + expect(caller.currentRemoteDescription).assertDeepEquals(caller.remoteDescription); + }) + + it("createDataChannelAttributeDefaultValues", DEFAULT, () => { + let dc = caller.createDataChannel(''); + + expect(dc instanceof webrtc.RTCDataChannel).assertTrue(); + expect(dc.label).assertEqual(''); + expect(dc.ordered).assertEqual(true); + expect(dc.maxPacketLifeTime).assertUndefined(); + expect(dc.maxRetransmits).assertUndefined(); + expect(dc.protocol).assertEqual(''); + expect(dc.negotiated).assertFalse() + expect(dc.id).assertUndefined() + expect(dc.readyState).assertEqual('connecting'); + expect(dc.bufferedAmount).assertEqual(0); + expect(dc.bufferedAmountLowThreshold).assertEqual(0); + expect(dc.binaryType).assertEqual('blob'); + }) + + it("createDataChannelWithProvidedParametersShouldInitializeAttributesToProvidedValues", DEFAULT, async () => { + let dc = caller.createDataChannel('test', { + ordered: false, + maxRetransmits: 1, + // Note: maxPacketLifeTime is not set in this test. + protocol: 'custom', + negotiated: true, + id: 3 + }); + expect(dc instanceof webrtc.RTCDataChannel).assertTrue(); + expect(dc.label).assertEqual('test'); + expect(dc.ordered).assertEqual(false); + expect(dc.maxPacketLifeTime).assertUndefined(); + expect(dc.maxRetransmits).assertEqual(1); + expect(dc.protocol).assertEqual('custom'); + expect(dc.negotiated).assertEqual(true); + expect(dc.id).assertEqual(3); + expect(dc.readyState).assertEqual('connecting'); + expect(dc.bufferedAmount).assertEqual(0); + expect(dc.bufferedAmountLowThreshold).assertEqual(0); + // 创建RTCDataChannel对象时,binaryType属性必须初始化为字符串"blob" + expect(dc.binaryType).assertEqual('blob'); + + let dc2 = caller.createDataChannel('test2', { + ordered: false, + maxPacketLifeTime: 42 + }); + expect(dc2.label).assertEqual('test2'); + expect(dc2.maxPacketLifeTime).assertEqual(42); + expect(dc2.maxRetransmits).assertUndefined(); + }) + + it("createDataChannelWithOrderedUndefinedShouldSucceed", DEFAULT, () => { + let dc2 = caller.createDataChannel('', { + ordered: undefined + }); + expect(dc2.ordered).assertTrue(); + }) + + it("createDataChannelWithMaxPacketLifeTime0ShouldSucceed", DEFAULT, () => { + let dc = caller.createDataChannel('', { + maxPacketLifeTime: 0 + }); + expect(dc.maxPacketLifeTime).assertEqual(0); + }) + + it("createDataChannelWithMaxRetransmits0ShouldSucceed", DEFAULT, () => { + const dc = caller.createDataChannel('', { + maxRetransmits: 0 + }); + expect(dc.maxRetransmits).assertEqual(0); + }) + + it("createDataChannelWithBothMaxPacketLifeTimeAndMaxRetransmitsUndefinedShouldSucceed", DEFAULT, () => { + try { + caller.createDataChannel('', { + maxPacketLifeTime: undefined, + maxRetransmits: undefined + }); + } catch (e) { + expect().assertFail() + } + }) + + it("channelsCreatedAfterSetRemoteDescriptionShouldHaveIdAssigned", DEFAULT, async () => { + let negotiatedDc = caller.createDataChannel('negotiated-channel', { + negotiated: true, + id: 42, + }); + expect(negotiatedDc.id).assertEqual(42); + + let dc1 = caller.createDataChannel('channel'); + expect(dc1.id).assertUndefined(); + + let offer = await caller.createOffer(); + await Promise.all([caller.setLocalDescription(offer), callee.setRemoteDescription(offer)]); + let answer = await callee.createAnswer(); + await caller.setRemoteDescription(answer); + + expect(dc1.id).not().assertUndefined(); + expect(dc1.id).assertLargerOrEqual(0); + expect(dc1.id).assertLess(65535); + + let dc2 = caller.createDataChannel('channel'); + + expect(dc2.id).not().assertUndefined(); + expect(dc2.id).assertLarger(0); + expect(dc2.id).assertLess(65535); + expect(dc2).not().assertEqual(dc1); + expect(dc2.label).assertEqual(dc1.label); + expect(dc2.id).not().assertEqual(dc1.id); + expect(negotiatedDc.id).assertEqual(42); + }) + + it("createAnswerReturnsRTCSessionDescriptionInit", DEFAULT, async () => { + let offer = await caller.createOffer(); + await caller.setRemoteDescription(offer); + let answer = await caller.createAnswer(); + expect(typeof answer).assertEqual('object'); + expect(answer instanceof webrtc.RTCSessionDescription).assertFalse(); + }) + + it("canTrickleIceCandidatesPropertyIsTrueAfterSetRemoteDescriptionWithA", DEFAULT, () => { + caller.setRemoteDescription(new webrtc.RTCSessionDescription({ + type: 'offer', sdp: sdp + })); + expect(caller.canTrickleIceCandidates).assertTrue(); + }) + + it("addTransceiverAudioShouldReturnAnAudioTransceiver", DEFAULT, () => { + let transceiver = caller.addTransceiver('audio'); + expect(transceiver instanceof webrtc.RTCRtpTransceiver).assertTrue(); + + expect(transceiver.mid).assertNull(); + + expect(transceiver.direction).assertEqual('sendrecv'); + expect(transceiver.currentDirection).assertNull(); + expect([transceiver]).assertDeepEquals(caller.getTransceivers()); + + let sender = transceiver.sender; + + expect(sender instanceof webrtc.RTCRtpSender).assertTrue(); + expect(sender.track).assertNull(); + expect([sender]).assertDeepEquals(caller.getSenders()); + + let receiver = transceiver.receiver; + expect(receiver instanceof webrtc.RTCRtpReceiver); + + let track = receiver.track; + expect(track instanceof webrtc.MediaStreamTrack).assertTrue(); + expect(track.kind).assertEqual('audio'); + expect(track.readyState).assertEqual('live'); + expect([receiver]).assertDeepEquals(caller.getReceivers()); + }) + + }) } \ No newline at end of file diff --git a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCDtlsTransport.test.ets b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCDtlsTransport.test.ets index 64bfa7065f..5a71684a61 100644 --- a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCDtlsTransport.test.ets +++ b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCDtlsTransport.test.ets @@ -1,197 +1,192 @@ -/** - * Copyright (c) 2024 Archermind Technology (Nanjing) Co. Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' -import webrtc from 'libohos_webrtc.so'; -import { Logging, LoggingSeverity } from '../../../main/ets/log/Logging'; - -const DEFAULT = 0 -const TAG: string = '[RTCDtlsTransportTest]'; - -const pcf = new webrtc.PeerConnectionFactory(); - -let caller: webrtc.RTCPeerConnection; -let callee: webrtc.RTCPeerConnection; - -const STUN_SERVER = "stun:stun.l.google.com:19302"; - -function exchangeIceCandidates(pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) { - const doExchange = (pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) => { - pc1.onicecandidate = (event) => { - const candidate = event.candidate; - if (pc2.signalingState !== 'closed') { - pc2.addIceCandidate(candidate); - } - }; - }; - - doExchange(pc1, pc2); - doExchange(pc2, pc1); -} - -async function exchangeOffer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - await caller.setLocalDescription(await caller.createOffer()); - console.log(TAG, "caller.localDescription: " + caller.localDescription!.type); - await callee.setRemoteDescription(caller.localDescription); -} - -async function exchangeAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - // 必须先执行caller.setRemoteDescription,否则callee的candidates有可能在caller的remote description设置之前到达 - const answer = await callee.createAnswer(); - await caller.setRemoteDescription(answer); - await callee.setLocalDescription(answer); -} - -async function exchangeOfferAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - await exchangeOffer(caller, callee); - await exchangeAnswer(caller, callee); -} - -function sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -async function createDtlsTransport(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - let audioSource = pcf?.createAudioSource(); - let audioTrack = pcf?.createAudioTrack("audio", audioSource); - caller.addTrack(audioTrack); - exchangeIceCandidates(caller, callee); - await exchangeOfferAnswer(caller, callee); -} - -export default function RTCDtlsTransportTest() { - describe('RTCDtlsTransportTest', () => { - // Defines a test suite. Two parameters are supported: test suite name and test suite function. - beforeAll(() => { - // Presets an action, which is performed only once before all test cases of the test suite start. - // This API supports only one parameter: preset action function. - Logging.enableLogToDebugOutput(LoggingSeverity.VERBOSE); - }) - beforeEach(() => { - // Presets an action, which is performed before each unit test case starts. - // The number of execution times is the same as the number of test cases defined by **it**. - // This API supports only one parameter: preset action function. - caller = pcf.createPeerConnection({ - iceServers: [{ - urls: STUN_SERVER - }] - }); - callee = pcf.createPeerConnection({ - iceServers: [{ - urls: STUN_SERVER - }] - }); - }) - afterEach(() => { - // Presets a clear action, which is performed after each unit test case ends. - // The number of execution times is the same as the number of test cases defined by **it**. - // This API supports only one parameter: clear action function. - if (caller) { - caller.close(); - } - if (callee) { - callee.close(); - } - }) - afterAll(() => { - // Presets a clear action, which is performed after all test cases of the test suite end. - // This API supports only one parameter: clear action function. - }) - - it("transportGoesToConnectedState", DEFAULT, async () => { - createDtlsTransport(caller, callee); - await sleep(300); - let transport1 = caller.getTransceivers()[0].sender.transport; - let transport2 = callee.getTransceivers()[0].sender.transport; - let flag1: string | undefined = ""; - let flag2: string | undefined = ""; - transport1!.onstatechange = () => { - console.info(TAG, "state: ", transport1?.state); - if (transport1?.state == "connected") { - flag1 = transport1?.state; - } - } - transport2!.onstatechange = () => { - console.info(TAG, "state: ", transport2?.state); - if (transport2?.state == "connected") { - flag2 = transport2?.state; - } - } - await sleep(5000); - expect(flag1).assertEqual("connected"); - expect(flag2).assertEqual("connected"); - }) - - it("closeCausesTheLocalTransportToCloseImmediately", DEFAULT, async () => { - createDtlsTransport(caller, callee); - await sleep(300); - let transport1 = caller.getTransceivers()[0].sender.transport; - let transport2 = callee.getTransceivers()[0].sender.transport; - let flag: string | undefined = ""; - transport1!.onstatechange = () => { - console.info(TAG, "state: ", transport1?.state); - if (transport1?.state == "closed") { - flag = transport1?.state; - } - } - caller.close(); - await sleep(1000); - expect(flag).assertEqual("closed"); - }) - - it("closeCausesTheOtherEndTransportToClose", DEFAULT, async () => { - createDtlsTransport(caller, callee); - await sleep(300); - let transport1 = caller.getTransceivers()[0].sender.transport; - let transport2 = callee.getTransceivers()[0].sender.transport; - let flag: string | undefined = ""; - transport1!.onstatechange = () => { - console.info(TAG, "state1: ", transport1?.state); - } - transport2!.onstatechange = () => { - console.info(TAG, "state2: ", transport2?.state); - if (transport2?.state == "closed") { - flag = transport2?.state; - } - } - await sleep(1000); - caller.close(); - await sleep(1000); - expect(flag).assertEqual("closed"); - }) - - it("iceTransportIsNotNullAfterCreateDtlsTransport", DEFAULT, async () => { - createDtlsTransport(caller, callee); - await sleep(1000); - expect(caller.getSenders()[0]!.transport).not().assertNull(); - }) - - it("stateIsNotNullAfterCreateDtlsTransport", DEFAULT, async () => { - createDtlsTransport(caller, callee); - await sleep(1000); - console.info(TAG, "state: ", caller.getSenders()[0]!.transport!.state); - expect(caller.getSenders()[0]!.transport!.state).not().assertNull(); - }) - - it("getRemoteCertificatesIsNotNullAfterCreateDtlsTransport", DEFAULT, async () => { - createDtlsTransport(caller, callee); - await sleep(1000); - console.info(TAG, "getRemoteCertificates: ", - JSON.stringify(caller.getSenders()[0]?.transport?.getRemoteCertificates())); - expect(caller.getSenders()[0]?.transport?.getRemoteCertificates()).not().assertNull(); - }) - - }) +/** + * Copyright (c) 2024 Archermind Technology (Nanjing) Co. Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' +import webrtc from 'libohos_webrtc.so'; +import { Logging, LoggingSeverity } from '../../../main/ets/log/Logging'; +import { sleep } from './Helper'; + +const DEFAULT = 0 +const TAG: string = '[RTCDtlsTransportTest]'; + +const pcf = new webrtc.PeerConnectionFactory(); + +let caller: webrtc.RTCPeerConnection; +let callee: webrtc.RTCPeerConnection; + +const STUN_SERVER = "stun:stun.l.google.com:19302"; + +function exchangeIceCandidates(pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) { + const doExchange = (pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) => { + pc1.onicecandidate = (event) => { + const candidate = event.candidate; + if (pc2.signalingState !== 'closed') { + pc2.addIceCandidate(candidate); + } + }; + }; + + doExchange(pc1, pc2); + doExchange(pc2, pc1); +} + +async function exchangeOffer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + await caller.setLocalDescription(await caller.createOffer()); + console.log(TAG, "caller.localDescription: " + caller.localDescription!.type); + await callee.setRemoteDescription(caller.localDescription); +} + +async function exchangeAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + // 必须先执行caller.setRemoteDescription,否则callee的candidates有可能在caller的remote description设置之前到达 + const answer = await callee.createAnswer(); + await caller.setRemoteDescription(answer); + await callee.setLocalDescription(answer); +} + +async function exchangeOfferAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + await exchangeOffer(caller, callee); + await exchangeAnswer(caller, callee); +} + +async function createDtlsTransport(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + let audioSource = pcf?.createAudioSource(); + let audioTrack = pcf?.createAudioTrack("audio", audioSource); + caller.addTrack(audioTrack); + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); +} + +export default function RTCDtlsTransportTest() { + describe('RTCDtlsTransportTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + Logging.enableLogToDebugOutput(LoggingSeverity.VERBOSE); + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + caller = pcf.createPeerConnection({ + iceServers: [{ + urls: STUN_SERVER + }] + }); + callee = pcf.createPeerConnection({ + iceServers: [{ + urls: STUN_SERVER + }] + }); + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + if (caller) { + caller.close(); + } + if (callee) { + callee.close(); + } + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + + it("transportGoesToConnectedState", DEFAULT, async () => { + await createDtlsTransport(caller, callee); + let transport1 = caller.getTransceivers()[0].sender.transport; + let transport2 = callee.getTransceivers()[0].sender.transport; + let flag1: string | undefined = ""; + let flag2: string | undefined = ""; + transport1!.onstatechange = () => { + console.info(TAG, "state: ", transport1?.state); + if (transport1?.state == "connected") { + flag1 = transport1?.state; + } + } + transport2!.onstatechange = () => { + console.info(TAG, "state: ", transport2?.state); + if (transport2?.state == "connected") { + flag2 = transport2?.state; + } + } + await sleep(5000); + expect(flag1).assertEqual("connected"); + expect(flag2).assertEqual("connected"); + }) + + it("closeCausesTheLocalTransportToCloseImmediately", DEFAULT, async () => { + await createDtlsTransport(caller, callee); + let transport1 = caller.getTransceivers()[0].sender.transport; + let transport2 = callee.getTransceivers()[0].sender.transport; + let flag: string | undefined = ""; + let promise = new Promise((resolve) => { + transport1!.onstatechange = () => { + console.info(TAG, "state: ", transport1?.state); + if (transport1?.state == "closed") { + flag = transport1?.state; + resolve(); + } + } + }); + caller.close(); + await promise; + expect(flag).assertEqual("closed"); + }) + + it("closeCausesTheOtherEndTransportToClose", DEFAULT, async () => { + await createDtlsTransport(caller, callee); + let transport1 = caller.getTransceivers()[0].sender.transport; + let transport2 = callee.getTransceivers()[0].sender.transport; + let flag: string | undefined = ""; + transport1!.onstatechange = () => { + console.info(TAG, "state1: ", transport1?.state); + } + transport2!.onstatechange = () => { + console.info(TAG, "state2: ", transport2?.state); + if (transport2?.state == "closed") { + flag = transport2?.state; + } + } + await sleep(1000); + caller.close(); + await sleep(1000); + expect(flag).assertEqual("closed"); + }) + + it("iceTransportIsNotNullAfterCreateDtlsTransport", DEFAULT, async () => { + await createDtlsTransport(caller, callee); + expect(caller.getSenders()[0]!.transport).not().assertNull(); + }) + + it("stateIsNotNullAfterCreateDtlsTransport", DEFAULT, async () => { + await createDtlsTransport(caller, callee); + console.info(TAG, "state: ", caller.getSenders()[0]!.transport!.state); + expect(caller.getSenders()[0]!.transport!.state).not().assertNull(); + }) + + it("getRemoteCertificatesIsNotNullAfterCreateDtlsTransport", DEFAULT, async () => { + await createDtlsTransport(caller, callee); + await sleep(1000); + console.info(TAG, "getRemoteCertificates: ", + JSON.stringify(caller.getSenders()[0]?.transport?.getRemoteCertificates())); + expect(caller.getSenders()[0]?.transport?.getRemoteCertificates()).not().assertNull(); + }) + + }) } \ No newline at end of file diff --git a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCDtmfSender.test.ets b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCDtmfSender.test.ets index 5d443e3662..c0a3ca6996 100644 --- a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCDtmfSender.test.ets +++ b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCDtmfSender.test.ets @@ -16,6 +16,7 @@ import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' import webrtc from 'libohos_webrtc.so'; import { Logging, LoggingSeverity } from '../../../main/ets/log/Logging'; +import { sleep } from './Helper'; const DEFAULT = 0 const TAG: string = '[RTCDtmfSenderTest]'; @@ -91,10 +92,6 @@ async function createDtmfSender(caller: webrtc.RTCPeerConnection, return sender; } -function sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); -} - export default function RTCDtmfSenderTest() { describe('RTCDtmfSenderTest', () => { // Defines a test suite. Two parameters are supported: test suite name and test suite function. @@ -135,7 +132,6 @@ export default function RTCDtmfSenderTest() { it("toneBufferAfterCreateDtmfSender", DEFAULT, async () => { let sender = await createDtmfSender(caller, callee); let dtmfSender = sender?.dtmf; - await sleep(1000); expect(dtmfSender!.toneBuffer).assertEqual(""); }) @@ -187,15 +183,17 @@ export default function RTCDtmfSenderTest() { let sender = await createDtmfSender(caller, callee); let dtmfSender = sender?.dtmf; let res: string; - dtmfSender!.ontonechange = async (event) => { - if (event.tone == 'A') { - res = event.tone; - } - console.info(TAG, "Tone changed:", event.tone); - }; + let promise = new Promise((resolve) => { + dtmfSender!.ontonechange = async (event) => { + if (event.tone == 'A') { + res = event.tone; + resolve(); + } + }; + }) await sleep(1000); dtmfSender?.insertDTMF('A'); - await sleep(1000); + await promise; expect(res!).assertEqual('A') }) @@ -215,7 +213,5 @@ export default function RTCDtmfSenderTest() { expect(res).not().assertEqual('F') }) - } - - ) + }) } \ No newline at end of file diff --git a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCIceTransport.test.ets b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCIceTransport.test.ets index fc805c5f93..9f9ce63ebe 100644 --- a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCIceTransport.test.ets +++ b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCIceTransport.test.ets @@ -1,197 +1,169 @@ -/** - * Copyright (c) 2024 Archermind Technology (Nanjing) Co. Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' -import webrtc from 'libohos_webrtc.so'; -import { Logging, LoggingSeverity } from '../../../main/ets/log/Logging'; - -const DEFAULT = 0 -const TAG: string = '[RTCIceTransportTest]'; - -const pcf = new webrtc.PeerConnectionFactory(); - -let caller: webrtc.RTCPeerConnection; -let callee: webrtc.RTCPeerConnection; - -const STUN_SERVER = "stun:stun.l.google.com:19302"; - -function exchangeIceCandidates(pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) { - const doExchange = (pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) => { - pc1.onicecandidate = (event) => { - const candidate = event.candidate; - if (pc2.signalingState !== 'closed') { - pc2.addIceCandidate(candidate); - } - }; - }; - - doExchange(pc1, pc2); - doExchange(pc2, pc1); -} - -async function exchangeOffer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - await caller.setLocalDescription(await caller.createOffer()); - console.log(TAG, "caller.localDescription: " + caller.localDescription!.type); - await callee.setRemoteDescription(caller.localDescription); -} - -async function exchangeAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - // 必须先执行caller.setRemoteDescription,否则callee的candidates有可能在caller的remote description设置之前到达 - const answer = await callee.createAnswer(); - await caller.setRemoteDescription(answer); - await callee.setLocalDescription(answer); -} - -async function exchangeOfferAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - await exchangeOffer(caller, callee); - await exchangeAnswer(caller, callee); -} - -function sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -async function createIceTransport(caller: webrtc.RTCPeerConnection, - callee: webrtc.RTCPeerConnection): Promise { - let audioSource = pcf?.createAudioSource(); - let audioTrack = pcf?.createAudioTrack("audio", audioSource); - - caller.addTrack(audioTrack); - exchangeIceCandidates(caller, callee); - await exchangeOfferAnswer(caller, callee); - let transport = caller.getSenders()[0]!.transport!.iceTransport; - return transport; -} - -export default function RTCIceTransportTest() { - describe('RTCIceTransportTest', () => { - // Defines a test suite. Two parameters are supported: test suite name and test suite function. - beforeAll(() => { - // Presets an action, which is performed only once before all test cases of the test suite start. - // This API supports only one parameter: preset action function. - Logging.enableLogToDebugOutput(LoggingSeverity.VERBOSE); - }) - beforeEach(() => { - // Presets an action, which is performed before each unit test case starts. - // The number of execution times is the same as the number of test cases defined by **it**. - // This API supports only one parameter: preset action function. - caller = pcf.createPeerConnection({ - iceServers: [{ - urls: STUN_SERVER - }] - }); - callee = pcf.createPeerConnection({ - iceServers: [{ - urls: STUN_SERVER - }] - }); - }) - afterEach(() => { - // Presets a clear action, which is performed after each unit test case ends. - // The number of execution times is the same as the number of test cases defined by **it**. - // This API supports only one parameter: clear action function. - if (caller) { - caller.close(); - } - if (callee) { - callee.close(); - } - }) - afterAll(() => { - // Presets a clear action, which is performed after all test cases of the test suite end. - // This API supports only one parameter: clear action function. - }) - - it("unconnectedIceTransportShouldHaveEmptySelectedPair", DEFAULT, async () => { - caller.createDataChannel(''); - await exchangeOfferAnswer(caller, callee); - let iceTransport = caller.sctp?.transport.iceTransport; - expect(iceTransport?.getSelectedCandidatePair()?.local?.usernameFragment).assertEqual(""); - expect(iceTransport?.getSelectedCandidatePair()?.remote?.usernameFragment).assertEqual(""); - }) - - it("iceTransportShouldBeInStateNewInitially", DEFAULT, async () => { - const transceiver = caller.addTransceiver('audio'); - await caller.setLocalDescription(); - const iceTransport = transceiver.sender.transport?.iceTransport; - console.info(TAG, "gatheringState: ", iceTransport!.gatheringState); - - iceTransport!.ongatheringstatechange = () => { - console.info(TAG, "gatheringState: ", iceTransport!.gatheringState); - } - expect(iceTransport?.state).assertEqual('new'); - }) - - it("iceTransportShouldTransitionToGatheringThenCompleteAfterSLD", DEFAULT, async () => { - const transceiver = caller.addTransceiver('audio'); - await caller.setLocalDescription(); - const iceTransport = transceiver.sender.transport?.iceTransport; - - iceTransport!.ongatheringstatechange = () => { - console.info(TAG, 'gatheringState: ', iceTransport?.gatheringState); - }; - await sleep(1000); - expect(iceTransport?.gatheringState).not().assertEqual("new"); - }) - - it("afterInitializationAllAttributesAreNotNull", DEFAULT, async () => { - let transport = await createIceTransport(caller, callee); - await sleep(1000); - expect(transport.role).not().assertUndefined(); - expect(transport.component).not().assertUndefined(); - expect(transport.state).not().assertUndefined(); - expect(transport.gatheringState).not().assertUndefined(); - expect(transport.getSelectedCandidatePair()).not().assertUndefined(); - }) - - it("onstatechange", DEFAULT, async () => { - let transport = await createIceTransport(caller, callee); - let events: string[] = []; - transport.onstatechange = () => { - events.push(transport.state); - console.info(TAG, 'state change: ', transport.state); - }; - await sleep(5000); - expect(events.length).not().assertEqual(0); - }) - - it("onSelectedCandidatePairChange", DEFAULT, async () => { - let transport = await createIceTransport(caller, callee); - let events: string[] = []; - transport.onselectedcandidatepairchange = () => { - events.push("1"); - }; - await sleep(1000); - expect(events.length).not().assertEqual(0); - }) - - it("onGatheringStateChange", DEFAULT, async () => { - const transceiver = caller.addTransceiver('audio', { direction: 'recvonly' }); - await caller.setLocalDescription(); - const iceTransport = transceiver.sender.transport!.iceTransport; - - let events: string[] = []; - await new Promise((resolve, reject) => { - iceTransport.ongatheringstatechange = () => { - events.push(iceTransport.gatheringState); - console.info(TAG, '2 gatheringState change: ', iceTransport.gatheringState); - resolve(); - }; - }); - expect(events.length).assertLarger(0); - }) - - }) +/** + * Copyright (c) 2024 Archermind Technology (Nanjing) Co. Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' +import webrtc from 'libohos_webrtc.so'; +import { Logging, LoggingSeverity } from '../../../main/ets/log/Logging'; +import { sleep } from './Helper'; + +const DEFAULT = 0 +const TAG: string = '[RTCIceTransportTest]'; + +const pcf = new webrtc.PeerConnectionFactory(); + +let caller: webrtc.RTCPeerConnection; +let callee: webrtc.RTCPeerConnection; + +const STUN_SERVER = "stun:stun.l.google.com:19302"; + +function exchangeIceCandidates(pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) { + const doExchange = (pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) => { + pc1.onicecandidate = (event) => { + const candidate = event.candidate; + if (pc2.signalingState !== 'closed') { + pc2.addIceCandidate(candidate); + } + }; + }; + + doExchange(pc1, pc2); + doExchange(pc2, pc1); +} + +async function exchangeOffer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + await caller.setLocalDescription(await caller.createOffer()); + console.log(TAG, "caller.localDescription: " + caller.localDescription!.type); + await callee.setRemoteDescription(caller.localDescription); +} + +async function exchangeAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + // 必须先执行caller.setRemoteDescription,否则callee的candidates有可能在caller的remote description设置之前到达 + const answer = await callee.createAnswer(); + await caller.setRemoteDescription(answer); + await callee.setLocalDescription(answer); +} + +async function exchangeOfferAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + await exchangeOffer(caller, callee); + await exchangeAnswer(caller, callee); +} + +async function createIceTransport(caller: webrtc.RTCPeerConnection, + callee: webrtc.RTCPeerConnection): Promise { + let audioSource = pcf?.createAudioSource(); + let audioTrack = pcf?.createAudioTrack("audio", audioSource); + + caller.addTrack(audioTrack); + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + let transport = caller.getSenders()[0]!.transport!.iceTransport; + return transport; +} + +export default function RTCIceTransportTest() { + describe('RTCIceTransportTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + Logging.enableLogToDebugOutput(LoggingSeverity.VERBOSE); + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + caller = pcf.createPeerConnection({ + iceServers: [{ + urls: STUN_SERVER + }] + }); + callee = pcf.createPeerConnection({ + iceServers: [{ + urls: STUN_SERVER + }] + }); + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + if (caller) { + caller.close(); + } + if (callee) { + callee.close(); + } + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + + it("unconnectedIceTransportShouldHaveEmptySelectedPair", DEFAULT, async () => { + caller.createDataChannel(''); + let offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + await callee.setRemoteDescription(offer); + let answer = await callee.createAnswer(); + await caller.setRemoteDescription(answer); + let iceTransport = caller.sctp?.transport.iceTransport; + expect(iceTransport?.getSelectedCandidatePair()).assertNull(); + }) + + it("iceTransportShouldBeInStateNewInitially", DEFAULT, async () => { + const transceiver = caller.addTransceiver('audio'); + await caller.setLocalDescription(); + const iceTransport = transceiver.sender.transport?.iceTransport; + expect(iceTransport?.state).assertEqual('new'); + }) + + it("afterInitializationAllAttributesAreNotNull", DEFAULT, async () => { + let transport = await createIceTransport(caller, callee); + while (transport.state != 'connected') { + await sleep(100); + } + console.info(TAG, 'transport.state: ', transport.state); + expect(transport.getSelectedCandidatePair()).not().assertNull(); + }) + + it("onstatechange", DEFAULT, async () => { + let transport = await createIceTransport(caller, callee); + let events: string[] = []; + let promise = new Promise((resolve) => { + transport.onstatechange = () => { + events.push(transport.state); + console.info(TAG, 'state change: ', transport.state); + resolve(); + }; + }); + await promise; + expect(events.length).not().assertEqual(0); + }) + + it("onSelectedCandidatePairChange", DEFAULT, async () => { + let transport = await createIceTransport(caller, callee); + let events: string[] = []; + let promise = new Promise((resolve) => { + transport.onselectedcandidatepairchange = () => { + events.push("1"); + resolve(); + }; + }); + await promise; + expect(events.length).not().assertEqual(0); + }) + + }) } \ No newline at end of file diff --git a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCSctpTransport.test.ets b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCSctpTransport.test.ets index f8ffd542fa..e147977b11 100644 --- a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCSctpTransport.test.ets +++ b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCSctpTransport.test.ets @@ -1,170 +1,182 @@ -/** - * Copyright (c) 2024 Archermind Technology (Nanjing) Co. Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' -import webrtc from 'libohos_webrtc.so'; -import { Logging, LoggingSeverity } from '../../../main/ets/log/Logging'; - -const DEFAULT = 0 -const TAG: string = '[RTCSctpTransportTest]'; - -const pcf = new webrtc.PeerConnectionFactory(); - -let caller: webrtc.RTCPeerConnection; -let callee: webrtc.RTCPeerConnection; - -const STUN_SERVER = "stun:stun.l.google.com:19302"; - -function exchangeIceCandidates(pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) { - const doExchange = (pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) => { - pc1.onicecandidate = (event) => { - const candidate = event.candidate; - if (pc2.signalingState !== 'closed') { - pc2.addIceCandidate(candidate); - } - }; - }; - - doExchange(pc1, pc2); - doExchange(pc2, pc1); -} - -async function exchangeOffer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - await caller.setLocalDescription(await caller.createOffer()); - console.log(TAG, "caller.localDescription: " + caller.localDescription!.type); - await callee.setRemoteDescription(caller.localDescription); -} - -async function exchangeAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - // 必须先执行caller.setRemoteDescription,否则callee的candidates有可能在caller的remote description设置之前到达 - const answer = await callee.createAnswer(); - await caller.setRemoteDescription(answer); - await callee.setLocalDescription(answer); -} - -async function exchangeOfferAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - await exchangeOffer(caller, callee); - await exchangeAnswer(caller, callee); -} - -function sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -async function createSctpTransport(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { - let audioSource = pcf?.createAudioSource(); - let audioTrack = pcf?.createAudioTrack("audio", audioSource); - - caller.addTrack(audioTrack); - - caller.createDataChannel("myDataChannel"); - caller.oniceconnectionstatechange = () => { - console.info(TAG, "ICE state: ", caller.iceConnectionState); - }; - exchangeIceCandidates(caller, callee); - await exchangeOfferAnswer(caller, callee); -} - -export default function RTCSctpTransportTest() { - describe('RTCSctpTransportTest', () => { - // Defines a test suite. Two parameters are supported: test suite name and test suite function. - beforeAll(() => { - // Presets an action, which is performed only once before all test cases of the test suite start. - // This API supports only one parameter: preset action function. - Logging.enableLogToDebugOutput(LoggingSeverity.VERBOSE); - }) - beforeEach(() => { - // Presets an action, which is performed before each unit test case starts. - // The number of execution times is the same as the number of test cases defined by **it**. - // This API supports only one parameter: preset action function. - caller = pcf.createPeerConnection({ - iceServers: [{ - urls: STUN_SERVER - }] - }); - callee = pcf.createPeerConnection({ - iceServers: [{ - urls: STUN_SERVER - }] - }); - }) - afterEach(() => { - // Presets a clear action, which is performed after each unit test case ends. - // The number of execution times is the same as the number of test cases defined by **it**. - // This API supports only one parameter: clear action function. - if (caller) { - caller.close(); - } - if (callee) { - callee.close(); - } - }) - afterAll(() => { - // Presets a clear action, which is performed after all test cases of the test suite end. - // This API supports only one parameter: clear action function. - }) - - it("anUnconnectedPeerConnectionMustNotHaveMaxChannelsSet", DEFAULT, async () => { - caller.createDataChannel('test'); - const offer = await caller.createOffer(); - await caller.setRemoteDescription(offer); - const answer = await caller.createAnswer(); - await caller.setLocalDescription(answer); - - expect(caller.sctp).not().assertUndefined(); - await sleep(1000); - console.info(TAG, "state: ", caller.sctp?.state); - expect(caller.sctp?.maxChannels).assertUndefined(); - }) - - it("maxChannelsGetsInstantiatedAfterConnecting", DEFAULT, async () => { - createSctpTransport(caller, callee); - await sleep(1000); - console.info(TAG, "state: ", caller.sctp?.state); - expect(caller.sctp?.maxChannels).not().assertUndefined(); - expect(callee.sctp?.maxChannels).not().assertUndefined(); - expect(caller.sctp?.maxChannels).assertEqual(caller.sctp?.maxChannels); - }) - - it("maxMessageSizeAfterInitializationNotNull", DEFAULT, async () => { - caller.createDataChannel(''); - await exchangeOfferAnswer(caller, callee); - await sleep(1000); - console.info(TAG, "maxMessageSize: ", caller.sctp?.maxMessageSize); - expect(caller.sctp?.maxMessageSize).not().assertUndefined(); - }) - - it("RTCDtlsTransportAfterInitializationNotNull", DEFAULT, async () => { - createSctpTransport(caller, callee); - await sleep(1000); - expect(caller.sctp?.transport).not().assertUndefined(); - }) - - it("afterInitializationStateIsConnected", DEFAULT, async () => { - createSctpTransport(caller, callee); - await sleep(1000); - console.info(TAG, "state: ", caller.sctp?.state); - }) - - it("whenTheStateChangesTheEventWillTrigger", DEFAULT, async () => { - createSctpTransport(caller, callee); - await sleep(1000); - caller.sctp!.onstatechange = () => { - console.info(TAG, "state changed: ", caller.sctp?.state); - } - await sleep(5000); - }) - }) +/** + * Copyright (c) 2024 Archermind Technology (Nanjing) Co. Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' +import webrtc from 'libohos_webrtc.so'; +import { Logging, LoggingSeverity } from '../../../main/ets/log/Logging'; +import { sleep } from './Helper'; + +const DEFAULT = 0 +const TAG: string = '[RTCSctpTransportTest]'; + +const pcf = new webrtc.PeerConnectionFactory(); + +let caller: webrtc.RTCPeerConnection; +let callee: webrtc.RTCPeerConnection; + +const STUN_SERVER = "stun:stun.l.google.com:19302"; + +function exchangeIceCandidates(pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) { + const doExchange = (pc1: webrtc.RTCPeerConnection, pc2: webrtc.RTCPeerConnection) => { + pc1.onicecandidate = (event) => { + const candidate = event.candidate; + if (pc2.signalingState !== 'closed') { + pc2.addIceCandidate(candidate); + } + }; + }; + + doExchange(pc1, pc2); + doExchange(pc2, pc1); +} + +async function exchangeOffer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + await caller.setLocalDescription(await caller.createOffer()); + console.log(TAG, "caller.localDescription: " + caller.localDescription!.type); + await callee.setRemoteDescription(caller.localDescription); +} + +async function exchangeAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + // 必须先执行caller.setRemoteDescription,否则callee的candidates有可能在caller的remote description设置之前到达 + const answer = await callee.createAnswer(); + await caller.setRemoteDescription(answer); + await callee.setLocalDescription(answer); +} + +async function exchangeOfferAnswer(caller: webrtc.RTCPeerConnection, callee: webrtc.RTCPeerConnection) { + await exchangeOffer(caller, callee); + await exchangeAnswer(caller, callee); +} + +export default function RTCSctpTransportTest() { + describe('RTCSctpTransportTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + Logging.enableLogToDebugOutput(LoggingSeverity.VERBOSE); + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + caller = pcf.createPeerConnection({ + iceServers: [{ + urls: STUN_SERVER + }] + }); + callee = pcf.createPeerConnection({ + iceServers: [{ + urls: STUN_SERVER + }] + }); + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + if (caller) { + caller.close(); + } + if (callee) { + callee.close(); + } + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + + it("anUnconnectedPeerConnectionMustNotHaveMaxChannelsSet", DEFAULT, async () => { + caller.createDataChannel('test'); + const offer = await caller.createOffer(); + await caller.setRemoteDescription(offer); + const answer = await caller.createAnswer(); + await caller.setLocalDescription(answer); + + console.info(TAG, "state: ", caller.sctp?.state); + expect(caller.sctp?.maxChannels).assertUndefined(); + }) + + it("maxChannelsGetsInstantiatedAfterConnecting", DEFAULT, async () => { + let audioSource = pcf?.createAudioSource(); + let audioTrack = pcf?.createAudioTrack("audio", audioSource); + + caller.addTrack(audioTrack); + + caller.createDataChannel("myDataChannel"); + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + console.info(TAG, "maxChannelsGetsInstantiatedAfterConnecting state: ", caller.sctp?.state); + // wait the SCTP transport goes into the "connected" state + // await sleep(1000); + // + // await new Promise((resolve) => { + // caller.sctp!.onstatechange = () => { + // if (caller.sctp?.state == 'connected') { + // resolve(); + // } + // } + // }); + while (true) { + if (caller.sctp?.state === 'connected') { + break; + } + await sleep(100); + } + console.info(TAG, "maxChannelsGetsInstantiatedAfterConnecting state: ", caller.sctp?.state); + expect(caller.sctp?.maxChannels).assertEqual(callee.sctp?.maxChannels); + }) + + it("maxMessageSizeAfterInitializationNotNull", DEFAULT, async () => { + caller.createDataChannel(''); + await exchangeOfferAnswer(caller, callee); + console.info(TAG, "maxMessageSize: ", caller.sctp?.maxMessageSize); + expect(caller.sctp?.maxMessageSize).not().assertUndefined(); + }) + + it("RTCDtlsTransportAfterInitializationNotNull", DEFAULT, async () => { + let audioSource = pcf?.createAudioSource(); + let audioTrack = pcf?.createAudioTrack("audio", audioSource); + + caller.addTrack(audioTrack); + + caller.createDataChannel("myDataChannel"); + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + expect(caller.sctp?.transport).not().assertUndefined(); + }) + + it("whenTheStateChangesTheEventWillTrigger", DEFAULT, async () => { + let audioSource = pcf?.createAudioSource(); + let audioTrack = pcf?.createAudioTrack("audio", audioSource); + + caller.addTrack(audioTrack); + + caller.createDataChannel("myDataChannel"); + exchangeIceCandidates(caller, callee); + await exchangeOfferAnswer(caller, callee); + let flag = false; + let promise = new Promise((resolve) => { + caller.sctp!.onstatechange = () => { + flag = true; + resolve(); + } + }); + await promise; + expect(flag).assertTrue(); + }) + }) } \ No newline at end of file diff --git a/sdk/ohos/src/ohos_webrtc/ice_transport.cpp b/sdk/ohos/src/ohos_webrtc/ice_transport.cpp index 196f2c666a..b3cb31ac34 100644 --- a/sdk/ohos/src/ohos_webrtc/ice_transport.cpp +++ b/sdk/ohos/src/ohos_webrtc/ice_transport.cpp @@ -273,8 +273,8 @@ void NapiIceTransport::SetEventHandler(const Napi::CallbackInfo& info, const Nap EventHandler handler; handler.ref = Persistent(fn); - handler.tsfn = ThreadSafeFunction::New( - fn.Env(), fn, type, 0, 1, context, [](Napi::Env env, Reference* ctx) { + handler.tsfn = + ThreadSafeFunction::New(fn.Env(), fn, type, 0, 1, context, [](Napi::Env env, Reference* ctx) { ctx->Reset(); delete ctx; }); @@ -292,20 +292,18 @@ Napi::Value NapiIceTransport::GetSelectedCandidatePair(const Napi::CallbackInfo& RTC_LOG(LS_VERBOSE) << __FUNCTION__; auto factoryWrapper = pcWrapper_->GetPeerConnectionFactoryWrapper(); - cricket::CandidatePair candidatePair; - auto obj = Object::New(info.Env()); - factoryWrapper->GetNetworkThread()->BlockingCall([&candidatePair, this] { - auto candidate = iceTransport_->internal()->GetSelectedCandidatePair(); - if (candidate) { - candidatePair.local = candidate->local_candidate(); - candidatePair.remote = candidate->remote_candidate(); - } - }); + absl::optional candidatePair; + factoryWrapper->GetNetworkThread()->BlockingCall( + [&candidatePair, this] { candidatePair = iceTransport_->internal()->GetSelectedCandidatePair(); }); - obj.Set("local", NativeToJsCandidate(info.Env(), candidatePair.local)); - obj.Set("remote", NativeToJsCandidate(info.Env(), candidatePair.remote)); + if (!candidatePair) { + return info.Env().Null(); + } + auto obj = Object::New(info.Env()); + obj.Set("local", NativeToJsCandidate(info.Env(), candidatePair->local_candidate())); + obj.Set("remote", NativeToJsCandidate(info.Env(), candidatePair->remote_candidate())); return obj; } -- Gitee From 197e29d55878577efe68fb3eb1068cf4a105bbb8 Mon Sep 17 00:00:00 2001 From: chenhao Date: Thu, 5 Dec 2024 16:40:28 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=96=B0=E5=A2=9ERTCIceCandidate=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=EF=BC=8C=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=E5=8F=91=E7=8E=B0=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chenhao --- .../src/ohosTest/ets/test/List.test.ets | 2 + .../ets/test/RTCIceCandidate.test.ets | 160 ++++++++++++++++++ sdk/ohos/src/ohos_webrtc/ice_candidate.cpp | 4 + 3 files changed, 166 insertions(+) create mode 100644 sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCIceCandidate.test.ets diff --git a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/List.test.ets b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/List.test.ets index 7fe645a060..20770294c6 100644 --- a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/List.test.ets +++ b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/List.test.ets @@ -25,6 +25,7 @@ import RTCSctpTransportTest from './RTCSctpTransport.test'; import RTCCertificateTest from './RTCCertificate.test'; import RTCIceTransportTest from './RTCIceTransport.test'; import RTCDtlsTransportTest from './RTCDtlsTransport.test'; +import RTCIceCandidateTest from './RTCIceCandidate.test'; export default function testsuite() { abilityTest(); @@ -39,4 +40,5 @@ export default function testsuite() { RTCCertificateTest(); RTCIceTransportTest(); RTCDtlsTransportTest(); + RTCIceCandidateTest(); } \ No newline at end of file diff --git a/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCIceCandidate.test.ets b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCIceCandidate.test.ets new file mode 100644 index 0000000000..3bbddb99e7 --- /dev/null +++ b/sdk/ohos/har_hap/ohos_webrtc/src/ohosTest/ets/test/RTCIceCandidate.test.ets @@ -0,0 +1,160 @@ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' +import webrtc from 'libohos_webrtc.so'; +import { Logging, LoggingSeverity } from '../../../main/ets/log/Logging'; + +const DEFAULT = 0 + +const candidateString = 'candidate:1905690388 1 udp 2113937151 192.168.0.1 58041 typ host generation 0 ufrag thC8 network-cost 50'; +const candidateString2 = 'candidate:435653019 2 tcp 1845501695 192.168.0.196 4444 typ srflx raddr www.example.com rport 22222 tcptype active'; +const arbitraryString = ';'; + +export default function RTCIceCandidateTest() { + describe('RTCIceCandidateTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + Logging.enableLogToDebugOutput(LoggingSeverity.VERBOSE); + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + + it("newRTCIceCandidateSetSdpMidAudio", DEFAULT, () => { + let iceCandidateInit: webrtc.RTCIceCandidateInit = { + candidate: "", + sdpMid: "audio", + }; + let res = new webrtc.RTCIceCandidate(iceCandidateInit); + expect(res.candidate).assertEqual(""); + expect(res.sdpMid).assertEqual("audio"); + expect(res.sdpMLineIndex).assertUndefined(); + expect(res.usernameFragment).assertUndefined(); + }) + + it("newRTCIceCandidateSdpMLineIndexZero", DEFAULT, () => { + let iceCandidateInit: webrtc.RTCIceCandidateInit = { + candidate: "", + sdpMLineIndex: 0, + }; + let res = new webrtc.RTCIceCandidate(iceCandidateInit); + expect(res.candidate).assertEqual(""); + expect(res.sdpMid).assertUndefined(); + expect(res.sdpMLineIndex).assertEqual(0); + expect(res.usernameFragment).assertUndefined(); + }) + + it("newRTCIceCandidateSetSdpMidAudioSdpMLineIndexZero", DEFAULT, () => { + let iceCandidateInit: webrtc.RTCIceCandidateInit = { + candidate: "", + sdpMLineIndex: 0, + sdpMid: "audio", + }; + let res = new webrtc.RTCIceCandidate(iceCandidateInit); + expect(res.candidate).assertEqual(""); + expect(res.sdpMid).assertEqual("audio"); + expect(res.sdpMLineIndex).assertEqual(0); + expect(res.usernameFragment).assertUndefined(); + }) + + it("newRTCIceCandidateWithValidCandidateStringAndSdpMid", DEFAULT, () => { + let iceCandidateInit: webrtc.RTCIceCandidateInit = { + candidate: candidateString, + sdpMid: "audio", + }; + let res = new webrtc.RTCIceCandidate(iceCandidateInit); + expect(res.candidate).assertEqual(candidateString); + expect(res.sdpMid).assertEqual("audio"); + expect(res.sdpMLineIndex).assertUndefined(); + expect(res.usernameFragment).assertEqual("thC8"); + }) + + it("newRTCIceCandidateWithNonDefaultValuesForAllFields", DEFAULT, () => { + let iceCandidateInit: webrtc.RTCIceCandidateInit = { + candidate: candidateString, + sdpMid: 'video', + sdpMLineIndex: 1, + usernameFragment: 'test' + }; + let res = new webrtc.RTCIceCandidate(iceCandidateInit); + expect(res.candidate).assertEqual(candidateString); + expect(res.sdpMid).assertEqual('video'); + expect(res.sdpMLineIndex).assertEqual(1); + // TODO usernameFragment 是获取的 candidate 的 username + // expect(res.usernameFragment).assertEqual('test'); + expect(res.foundation).assertEqual('1905690388'); + expect(res.component).assertEqual('rtp'); + expect(res.priority).assertEqual(2113937151); + expect(res.address).assertEqual('192.168.0.1'); + expect(res.protocol).assertEqual('udp'); + expect(res.port).assertEqual(58041); + expect(res.type).assertEqual("host"); + expect(res.tcpType).assertUndefined(); + expect(res.relatedAddress).assertUndefined(); + expect(res.relatedPort).assertEqual(0); + }) + + it("newRTCIceCandidateWithNonDefaultValuesForAllFieldsTcpCandidate", DEFAULT, () => { + let iceCandidateInit: webrtc.RTCIceCandidateInit = { + candidate: candidateString2, + sdpMid: 'video', + sdpMLineIndex: 1, + usernameFragment: 'user1' + }; + let res = new webrtc.RTCIceCandidate(iceCandidateInit); + + expect(res.candidate).assertEqual(candidateString2); + expect(res.sdpMid).assertEqual('video'); + expect(res.sdpMLineIndex).assertEqual(1); + // TODO usernameFragment 是获取的 candidate 的 username + // expect(res.usernameFragment).assertEqual('user1'); + expect(res.foundation).assertEqual('435653019'); + expect(res.component).assertEqual('rtcp'); + expect(res.priority).assertEqual(1845501695); + expect(res.address).assertEqual('192.168.0.196'); + expect(res.protocol).assertEqual('tcp'); + expect(res.port).assertEqual(4444); + expect(res.type).assertEqual("srflx"); + expect(res.tcpType).assertEqual("active"); + expect(res.relatedAddress).assertEqual('www.example.com'); + expect(res.relatedPort).assertEqual(22222); + }) + + it("newRTCIceCandidateWithInvalidSdpMid", DEFAULT, () => { + let iceCandidateInit: webrtc.RTCIceCandidateInit = { + candidate: "", + sdpMid: arbitraryString + }; + let res = new webrtc.RTCIceCandidate(iceCandidateInit); + + expect(res.candidate).assertEqual(""); + expect(res.sdpMid).assertEqual(arbitraryString); + expect(res.sdpMLineIndex).assertUndefined(); + expect(res.usernameFragment).assertUndefined(); + }) + + it("newRTCIceCandidateWithInvalidSdpMLineIndex", DEFAULT, () => { + let iceCandidateInit: webrtc.RTCIceCandidateInit = { + candidate: "", + sdpMLineIndex: 65535 + }; + let res = new webrtc.RTCIceCandidate(iceCandidateInit); + + expect(res.candidate).assertEqual(""); + expect(res.sdpMid).assertUndefined(); + expect(res.sdpMLineIndex).assertEqual(65535); + expect(res.usernameFragment).assertUndefined(); + }) + }) +} \ No newline at end of file diff --git a/sdk/ohos/src/ohos_webrtc/ice_candidate.cpp b/sdk/ohos/src/ohos_webrtc/ice_candidate.cpp index 1bd64e68ba..dd3bf00fab 100644 --- a/sdk/ohos/src/ohos_webrtc/ice_candidate.cpp +++ b/sdk/ohos/src/ohos_webrtc/ice_candidate.cpp @@ -93,6 +93,10 @@ NapiIceCandidate::NapiIceCandidate(const Napi::CallbackInfo& info) : ObjectWrap< if (!SdpDeserializeCandidate(sdpMid, sdp, &candidate_, nullptr)) { NAPI_THROW_VOID(Error::New(info.Env(), "SdpDescrializeCandidate failed with sdp")); } + + if (from.Has("usernameFragment")) { + candidate_.set_username(from.Get("usernameFragment").As().Utf8Value()); + } } } else { NAPI_THROW_VOID(Error::New(info.Env(), "candidate is null")); -- Gitee