【m】插件上传

This commit is contained in:
2026-04-28 16:48:04 +08:00
parent 459db5ec01
commit 753878bdbb
631 changed files with 91583 additions and 11 deletions

View File

@@ -0,0 +1,3 @@
{
"createSeparatePackage": false
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 638e0ab1bfc544e57b3cbfdeab499e95
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
using UnityEngine;
namespace Unity.RenderStreaming.EditorTest
{
class AudioCodecInfoObject : ScriptableObject
{
[SerializeField]
public AudioCodecInfo info;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: da0addfcdf084a04eb2a503e3acc546f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,238 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using NUnit.Framework.Interfaces;
using UnityEditor;
using UnityEditor.TestTools;
using UnityEngine;
using UnityEngine.TestRunner;
[assembly: TestPlayerBuildModifier(typeof(Unity.RenderStreaming.EditorTest.BuildModifier))]
[assembly: TestRunCallback(typeof(Unity.RenderStreaming.EditorTest.TestListener))]
namespace Unity.RenderStreaming.EditorTest
{
class BuildModifier : ITestPlayerBuildModifier
{
const string path = "Packages/com.unity.renderstreaming/Tests/Editor/RenderStreamingSettings.asset";
public BuildPlayerOptions ModifyOptions(BuildPlayerOptions playerOptions)
{
var settings = AssetDatabase.LoadAssetAtPath<RenderStreamingSettings>(path);
RenderStreaming.Settings = settings;
return playerOptions;
}
}
class TestListener : ITestRunCallback
{
const string path = "Packages/com.unity.renderstreaming/Tests/Editor/RenderStreamingSettings.asset";
RenderStreamingSettings temp = null;
public void RunStarted(ITest testsToRun)
{
var settings = AssetDatabase.LoadAssetAtPath<RenderStreamingSettings>(path);
temp = RenderStreaming.Settings;
RenderStreaming.Settings = settings;
}
public void RunFinished(ITestResult testResults)
{
if (temp != null)
RenderStreaming.Settings = temp;
}
public void TestStarted(ITest test)
{
}
public void TestFinished(ITestResult result)
{
}
}
class VideoCodecInfoTest
{
[Test]
public void EqualityOperator()
{
VideoCodecInfo info = null;
Assert.That(info == null, Is.True);
Assert.That(info != null, Is.False);
VideoCodecInfo otherInfo = info;
Assert.That(info == otherInfo, Is.True);
Assert.That(info != otherInfo, Is.False);
info = VideoStreamSender.GetAvailableCodecs().First();
Assert.That(info == otherInfo, Is.False);
Assert.That(info == (object)otherInfo, Is.False);
otherInfo = info;
Assert.That(info == otherInfo, Is.True);
Assert.That(info == (object)otherInfo, Is.True);
}
[Test]
public void HashCode()
{
VideoCodecInfo info = VideoStreamSender.GetAvailableCodecs().First();
VideoCodecInfo otherInfo = info;
Assert.That(info.GetHashCode() == otherInfo.GetHashCode(), Is.True);
otherInfo = VideoStreamSender.GetAvailableCodecs().Last();
Assert.That(info.GetHashCode() == otherInfo.GetHashCode(), Is.False);
}
}
class AudioCodecInfoTest
{
[Test]
public void EqualityOperator()
{
AudioCodecInfo info = null;
Assert.That(info == null, Is.True);
Assert.That(info != null, Is.False);
AudioCodecInfo otherInfo = info;
Assert.That(info == otherInfo, Is.True);
Assert.That(info != otherInfo, Is.False);
info = AudioStreamSender.GetAvailableCodecs().First();
Assert.That(info == otherInfo, Is.False);
Assert.That(info == (object)otherInfo, Is.False);
otherInfo = info;
Assert.That(info == otherInfo, Is.True);
Assert.That(info == (object)otherInfo, Is.True);
}
[Test]
public void HashCode()
{
AudioCodecInfo info = AudioStreamSender.GetAvailableCodecs().First();
AudioCodecInfo otherInfo = info;
Assert.That(info.GetHashCode() == otherInfo.GetHashCode(), Is.True);
otherInfo = AudioStreamSender.GetAvailableCodecs().Last();
Assert.That(info.GetHashCode() == otherInfo.GetHashCode(), Is.False);
}
}
class VideoStreamSenderTest
{
[Test]
public void GetAvailableCodec()
{
var codecs = VideoStreamSender.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Null.Or.Empty);
Assert.That(codecs, Is.Not.Contains(null));
}
}
class VideoStreamReceiverTest
{
[Test]
public void GetAvailableCodec()
{
var codecs = VideoStreamReceiver.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Null.Or.Empty);
Assert.That(codecs, Is.Not.Contains(null));
}
}
class AudioStreamSenderTest
{
[Test]
public void GetAvailableCodec()
{
var codecs = AudioStreamSender.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Null.Or.Empty);
Assert.That(codecs, Is.Not.Contains(null));
}
}
class AudioStreamReceiverTest
{
[Test]
public void GetAvailableCodec()
{
var codecs = AudioStreamReceiver.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Null.Or.Empty);
Assert.That(codecs, Is.Not.Contains(null));
}
}
class RenderStreamingSettingsTest
{
[Test]
public void CheckDefaultSettings()
{
RenderStreamingSettings defaultSettings = AssetDatabase.LoadAssetAtPath<RenderStreamingSettings>(RenderStreaming.DefaultRenderStreamingSettingsPath);
Assert.That(defaultSettings.automaticStreaming, Is.True);
var defaultSignalingSettings = defaultSettings.signalingSettings as WebSocketSignalingSettings;
Assert.That(defaultSignalingSettings, Is.Not.Null);
Assert.That(defaultSignalingSettings.signalingClass, Is.EqualTo(typeof(Signaling.WebSocketSignaling)));
Assert.That(defaultSignalingSettings.url, Is.EqualTo("ws://127.0.0.1:80"));
Assert.That(defaultSignalingSettings.iceServers.ElementAt(0).urls, Is.EquivalentTo(new string[] { "stun:stun.l.google.com:19302" }));
}
}
class SerializeTest
{
[Test]
public void SerializeVideoCodecInfo()
{
IEnumerable<VideoCodecInfo> codecs = VideoStreamSender.GetAvailableCodecs();
var asset = ScriptableObject.CreateInstance<VideoCodecInfoObject>();
asset.info = codecs.First();
string exportPath = "Assets/test.asset";
AssetDatabase.CreateAsset(asset, exportPath);
EditorUtility.SetDirty(asset);
AssetDatabase.SaveAssets();
var otherAsset = AssetDatabase.LoadAssetAtPath<VideoCodecInfoObject>(exportPath);
Assert.That(asset.info, Is.Not.Null);
Assert.That(otherAsset.info, Is.Not.Null);
Assert.That(asset.info, Is.EqualTo(otherAsset.info));
Assert.That(asset.info.Equals(otherAsset.info), Is.True);
AssetDatabase.DeleteAsset(exportPath);
}
[Test]
public void SerializeAudioCodecInfo()
{
IEnumerable<AudioCodecInfo> codecs = AudioStreamSender.GetAvailableCodecs();
var asset = ScriptableObject.CreateInstance<AudioCodecInfoObject>();
asset.info = codecs.First();
string exportPath = "Assets/test.asset";
AssetDatabase.CreateAsset(asset, exportPath);
EditorUtility.SetDirty(asset);
AssetDatabase.SaveAssets();
var otherAsset = AssetDatabase.LoadAssetAtPath<AudioCodecInfoObject>(exportPath);
Assert.That(asset.info, Is.Not.Null);
Assert.That(otherAsset.info, Is.Not.Null);
Assert.That(asset.info, Is.EqualTo(otherAsset.info));
Assert.That(asset.info.Equals(otherAsset.info), Is.True);
AssetDatabase.DeleteAsset(exportPath);
}
[Test]
public void CreateSignalingSettingsObject()
{
var asset = SignalingSettingsObject.Create();
Assert.That(asset, Is.Not.Null);
Assert.That(asset.settings, Is.Not.Null);
Assert.That(asset.settings, Is.TypeOf<WebSocketSignalingSettings>());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1bc7a3da72adc340852fdcdb0632a94
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
using System;
using NUnit.Framework;
using Unity.RenderStreaming.Editor;
namespace Unity.RenderStreaming.EditorTest
{
class CustomSignalingSettingsEditorTest
{
[Test]
public void FindInspectorTypeByInspectedType()
{
Type type = typeof(WebSocketSignalingSettings);
Assert.That(CustomSignalingSettingsEditor.FindInspectorTypeByInspectedType(type), Is.EqualTo(typeof(WebSocketSignalingSettingsEditor)));
type = typeof(HttpSignalingSettings);
Assert.That(CustomSignalingSettingsEditor.FindInspectorTypeByInspectedType(type), Is.EqualTo(typeof(HttpSignalingSettingsEditor)));
type = typeof(int);
Assert.That(CustomSignalingSettingsEditor.FindInspectorTypeByInspectedType(type), Is.Null);
}
[Test]
public void FindInspectedTypeByLabel()
{
var labels = CustomSignalingSettingsEditor.Labels();
foreach (var label in labels)
{
Assert.That(CustomSignalingSettingsEditor.FindInspectedTypeByLabel(label), Is.Not.Null);
}
Assert.That(CustomSignalingSettingsEditor.FindInspectedTypeByLabel(null), Is.Null);
Assert.That(CustomSignalingSettingsEditor.FindInspectedTypeByLabel(string.Empty), Is.Null);
Assert.That(CustomSignalingSettingsEditor.FindInspectedTypeByLabel("dummy"), Is.Null);
}
[Test]
public void FindLabel()
{
Type inspectorType = typeof(WebSocketSignalingSettingsEditor);
Type inspectedType = typeof(WebSocketSignalingSettings);
Assert.That(CustomSignalingSettingsEditor.FindLabelByInspectorType(inspectorType), Is.EqualTo(CustomSignalingSettingsEditor.FindLabelByInspectedType(inspectedType)));
inspectorType = typeof(HttpSignalingSettingsEditor);
inspectedType = typeof(HttpSignalingSettings);
Assert.That(CustomSignalingSettingsEditor.FindLabelByInspectorType(inspectorType), Is.EqualTo(CustomSignalingSettingsEditor.FindLabelByInspectedType(inspectedType)));
inspectorType = typeof(int);
Assert.That(CustomSignalingSettingsEditor.FindLabelByInspectorType(inspectorType), Is.Null);
inspectedType = typeof(int);
Assert.That(CustomSignalingSettingsEditor.FindLabelByInspectedType(inspectedType), Is.Null);
}
[Test]
public void Labels()
{
var labels = CustomSignalingSettingsEditor.Labels();
Assert.That(labels, Is.Not.Empty);
Assert.That(labels, Is.Not.Contains(string.Empty));
Assert.That(labels, Is.Not.Contains(null));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2d874ab45c92d914fa67a31a981235c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: bc8ba17751ad4107a50fa1415017b6b1, type: 3}
m_Name: RenderStreamingSettings
m_EditorClassIdentifier:
automaticStreaming: 0
signalingSettings:
id: 0
references:
version: 1
00000000:
type: {class: WebSocketSignalingSettings, ns: Unity.RenderStreaming, asm: Unity.RenderStreaming}
data:
m_url: ws://127.0.0.1:80
m_iceServers:
- m_urls:
- stun:stun.l.google.com:19302
m_username:
m_credentialType: 0
m_credential:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 63a426df4894b1f4b94574475120a261
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
using System.Linq;
using NUnit.Framework;
using Unity.RenderStreaming.Editor;
using UnityEditor;
using UnityEngine;
namespace Unity.RenderStreaming.EditorTest
{
class RenderStreamingTest
{
private RenderStreamingSettings temp;
[SetUp]
public void Setup()
{
temp = RenderStreaming.Settings;
RenderStreaming.Settings =
AssetDatabase.LoadAssetAtPath<RenderStreamingSettings>(RenderStreaming.DefaultRenderStreamingSettingsPath);
}
[TearDown]
public void TearDown()
{
if (temp != null)
{
RenderStreaming.Settings = temp;
}
}
[Test]
public void RenderStreamingSettings()
{
Assert.That(() => RenderStreamingEditor.SetRenderStreamingSettings(null), Throws.ArgumentNullException);
var settings = ScriptableObject.CreateInstance<RenderStreamingSettings>();
settings.signalingSettings = new WebSocketSignalingSettings();
RenderStreamingEditor.SetRenderStreamingSettings(settings);
Assert.That(RenderStreaming.Settings.automaticStreaming, Is.EqualTo(settings.automaticStreaming));
Assert.That(RenderStreaming.Settings.signalingSettings, Is.EqualTo(settings.signalingSettings));
Object.DestroyImmediate(settings);
}
[Test]
public void SignalingSettings()
{
Assert.That(() => RenderStreamingEditor.SetSignalingSettings(null), Throws.ArgumentNullException);
var url = "wss://127.0.0.1:8081";
var iceServers = new IceServer[] { new IceServer(new string[] { "stun:stun.l.google.com:19302" }) };
var signalingSettings = new WebSocketSignalingSettings(url, iceServers);
Assert.That(() => RenderStreamingEditor.SetSignalingSettings(signalingSettings), Throws.Nothing);
var settings = RenderStreaming.GetSignalingSettings<WebSocketSignalingSettings>();
Assert.That(settings.url, Is.EqualTo(url));
Assert.That(settings.iceServers.ElementAt(0).urls, Is.EquivalentTo(iceServers[0].urls));
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7107edca028c4640b99957dff2e7b6aa
timeCreated: 1674443070

View File

@@ -0,0 +1,44 @@
using System.Collections; //IEnumerator
using NUnit.Framework; //Timeout, Assert
using Unity.RenderStreaming.Editor; //RequestJobManager
using UnityEditor.PackageManager; //PackageCollection
using UnityEditor.PackageManager.Requests; //ListRequest, AddRequest, etc
using UnityEngine.TestTools; //UnityTest
namespace Unity.RenderStreaming.EditorTest
{
class RequestJobTest
{
[UnityTest]
[Timeout(5000)]
public IEnumerator VerifyRenderStreamingPackage()
{
RequestJobManager.CreateListRequest(false, false, OnListRequestSucceeded, OnListRequestFailed);
while (!m_listRequestCompleted)
yield return null;
Assert.True(m_packageFound);
}
//---------------------------------------------------------------------------------------------------------------------
void OnListRequestSucceeded(Request<PackageCollection> packageCollection)
{
m_listRequestCompleted = true;
m_packageFound = null != packageCollection.FindPackage("com.unity.renderstreaming");
}
//---------------------------------------------------------------------------------------------------------------------
void OnListRequestFailed(Request<PackageCollection> packageCollection)
{
m_listRequestCompleted = true;
}
//---------------------------------------------------------------------------------------------------------------------
bool m_listRequestCompleted = false;
bool m_packageFound = false;
}
} //namespace Unity.RenderStreaming

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 25ffa51c9dba7594f85585e87a6de48d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
{
"name": "Unity.RenderStreaming.EditorTests",
"rootNamespace": "",
"references": [
"GUID:40a5acf76f04c4c8ebb69605e4b0d5c7",
"GUID:7e479a0c97f111c48b6a279fad867f28",
"GUID:27619889b8ba8c24980f49ee34dbb44a",
"GUID:0acc523941302664db1f4e527237feb3",
"GUID:f12aafacab75a87499e7e45c873ffab8"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6d0147213c4314f249ef115d9cd8589d
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
using UnityEngine;
namespace Unity.RenderStreaming.EditorTest
{
class VideoCodecInfoObject : ScriptableObject
{
[SerializeField]
public VideoCodecInfo info;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5af727ab10be4f04bb58e0669047d3a2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cbbc7eb4c6b544a9090aa2860d00da41
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,91 @@
using NUnit.Framework;
namespace Unity.RenderStreaming.RuntimeTest
{
class ArrayHelpersTest
{
[Test]
public void LengthSafe()
{
var array = new int[] { 1, 2, 3 };
Assert.That(ArrayHelpers.LengthSafe(array), Is.EqualTo(3));
array = null;
Assert.That(ArrayHelpers.LengthSafe(array), Is.EqualTo(0));
}
[Test]
public void Append()
{
var array = new int[] { 1, 2, 3 };
ArrayHelpers.Append(ref array, 4);
Assert.That(array, Is.EqualTo(new int[] { 1, 2, 3, 4 }));
array = null;
ArrayHelpers.Append(ref array, 1);
Assert.That(array, Is.EqualTo(new int[] { 1 }));
}
[Test]
public void AppendArray()
{
var array = new int[] { 1, 2, 3 };
var array2 = new int[] { 4, 5 };
ArrayHelpers.Append(ref array, array2);
Assert.That(array, Is.EqualTo(new int[] { 1, 2, 3, 4, 5 }));
array = null;
ArrayHelpers.Append(ref array, array2);
Assert.That(array, Is.EqualTo(new int[] { 4, 5 }));
}
[Test]
public void IndexOf()
{
var array = new int[] { 1, 2, 3 };
Assert.That(ArrayHelpers.IndexOf(array, 2), Is.EqualTo(1));
Assert.That(ArrayHelpers.IndexOf(array, 4), Is.EqualTo(-1));
array = null;
Assert.That(ArrayHelpers.IndexOf(array, 2), Is.EqualTo(-1));
}
[Test]
public void Erase()
{
var array = new int[] { 1, 2, 3 };
Assert.That(ArrayHelpers.Erase(ref array, 2), Is.True);
Assert.That(array, Is.EqualTo(new int[] { 1, 3 }));
Assert.That(ArrayHelpers.Erase(ref array, 2), Is.False);
Assert.That(array, Is.EqualTo(new int[] { 1, 3 }));
array = null;
Assert.That(ArrayHelpers.Erase(ref array, 2), Is.False);
Assert.That(array, Is.Null);
}
[Test]
public void EraseAt()
{
var array = new int[] { 1, 2, 3 };
ArrayHelpers.EraseAt(ref array, 1);
Assert.That(array, Is.EqualTo(new int[] { 1, 3 }));
array = new int[] { 1 };
ArrayHelpers.EraseAt(ref array, 0);
Assert.That(array, Is.Null);
}
[Test]
public void PutAtIfNotSet()
{
var array = new int[] { 1, 2, 3 };
ArrayHelpers.PutAtIfNotSet(ref array, 3, () => { return 4; });
Assert.That(array, Is.EqualTo(new int[] { 1, 2, 3, 4 }));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 12b8da0816272304f9e0df078d160877
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
using System;
using NUnit.Framework;
namespace Unity.RenderStreaming.RuntimeTest
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
internal class LongRunningAttribute : CategoryAttribute
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f1ba5e8f1d8f954e83f4e56f8d59467
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,150 @@
using System.IO;
using NUnit.Framework;
using Unity.WebRTC;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.RenderStreaming.RuntimeTest
{
/// <note>
/// Commandline parser doesn't support mobile platforms.
/// </note>
[UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer, RuntimePlatform.Android })]
class CommandLineParserTest
{
[Test]
public void NothingArgument()
{
string[] arguments = { };
Assert.That(CommandLineParser.TryParse(arguments), Is.True);
Assert.That((string)CommandLineParser.SignalingUrl, Is.Null);
Assert.That((int?)CommandLineParser.PollingInterval, Is.Null);
Assert.That((string[])CommandLineParser.IceServerUrls, Is.Null);
}
[Test]
public void SignalingUrlArgument()
{
const string signalingUrl = "localhost:8080";
string[] arguments = new[] { "-signalingUrl", signalingUrl };
Assert.That(CommandLineParser.TryParse(arguments), Is.True);
Assert.That((string)CommandLineParser.SignalingUrl, Is.EqualTo(signalingUrl));
}
[Test]
public void PollingIntervalArgument()
{
const int pollingInterval = 5000;
string[] arguments = { "-pollingInterval", pollingInterval.ToString() };
Assert.That(CommandLineParser.TryParse(arguments), Is.True);
Assert.That((int)CommandLineParser.PollingInterval, Is.EqualTo(pollingInterval));
}
[Test]
public void SignalingTypeArgument()
{
string signalingType = "websocket";
string[] arguments = { "-signalingType", signalingType };
Assert.That(CommandLineParser.TryParse(arguments), Is.True);
Assert.That((string)CommandLineParser.SignalingType, Is.EqualTo(signalingType));
signalingType = "dummy";
arguments = new[] { "-signalingType", signalingType };
Assert.That(CommandLineParser.TryParse(arguments), Is.True);
Assert.That((string)CommandLineParser.SignalingType, Is.EqualTo(signalingType));
}
[Test]
public void IceServerUrlArgument()
{
string[] iceServerUrls = { "stun:stun.l.google.com:19302", "stun:stun.l.google.com:19303" };
string[] arguments = { "-iceServerUrl", iceServerUrls[0], "-iceServerUrl", iceServerUrls[1] };
Assert.That(CommandLineParser.TryParse(arguments), Is.True);
Assert.That(CommandLineParser.IceServerUrls.Value, Has.Length.EqualTo(2));
Assert.That(CommandLineParser.IceServerUrls.Value[0], Is.EqualTo(iceServerUrls[0]));
Assert.That(CommandLineParser.IceServerUrls.Value[1], Is.EqualTo(iceServerUrls[1]));
}
[Test]
public void IceServerUserNameArgument()
{
string iceServerUsername = "username";
string[] arguments = { "-iceServerUsername", iceServerUsername };
Assert.That(CommandLineParser.TryParse(arguments), Is.True);
Assert.That((string)CommandLineParser.IceServerUsername, Is.EqualTo(iceServerUsername));
}
[Test]
public void IceServerCredentialArgument()
{
string iceServerCredential = "password";
string[] arguments = { "-iceServerCredential", iceServerCredential };
Assert.That(CommandLineParser.TryParse(arguments), Is.True);
Assert.That((string)CommandLineParser.IceServerCredential, Is.EqualTo(iceServerCredential));
}
[Test]
public void IceServerCredentialTypeArgument()
{
string iceServerCredentialType = "password";
string[] arguments = { "-iceServerCredentialType", iceServerCredentialType };
Assert.That(CommandLineParser.TryParse(arguments), Is.True);
Assert.That((IceCredentialType)CommandLineParser.IceServerCredentialType, Is.EqualTo(IceCredentialType.Password));
iceServerCredentialType = "oauth";
arguments = new[] { "-iceServerCredentialType", iceServerCredentialType };
Assert.That(CommandLineParser.TryParse(arguments), Is.True);
Assert.That((IceCredentialType)CommandLineParser.IceServerCredentialType, Is.EqualTo(IceCredentialType.OAuth));
iceServerCredentialType = "dummy";
arguments = new[] { "-iceServerCredentialType", iceServerCredentialType };
Assert.That(CommandLineParser.TryParse(arguments), Is.False);
Assert.That(CommandLineParser.IceServerCredentialType.Value, Is.Null);
}
[Test]
public void ImportJsonArgument()
{
string filepath = "dummy.json";
var file = File.Create(filepath);
file.Close();
string[] arguments = { "-importJson", filepath };
Assert.That(CommandLineParser.TryParse(arguments), Is.False);
Assert.That(CommandLineParser.ImportJson.Value, Is.Null);
string json = "{\"signalingType\":\"websocket\",\"signalingUrl\":\"ws://localhost\",\"pollingInterval\":\"1\"}";
File.WriteAllText(filepath, json);
Assert.That(CommandLineParser.TryParse(arguments), Is.True);
Assert.That(CommandLineParser.ImportJson.Value, Is.Not.Null);
var info = CommandLineParser.ImportJson.Value.Value;
Assert.That(info.signalingUrl, Is.EqualTo("ws://localhost"));
Assert.That(info.signalingType, Is.EqualTo("websocket"));
Assert.That(info.iceServers, Is.Null);
Assert.That(info.pollingInterval, Is.EqualTo("1"));
File.Delete(filepath);
}
[Test]
public void ParseJson()
{
string json = "{\"signalingType\":\"websocket\",\"signalingUrl\":\"ws://localhost\",\"pollingInterval\":\"1\"}";
var settings = JsonUtility.FromJson<CommandLineInfo>(json);
Assert.That(settings.signalingUrl, Is.EqualTo("ws://localhost"));
Assert.That(settings.signalingType, Is.EqualTo("websocket"));
Assert.That(settings.iceServers, Is.Null);
Assert.That(settings.pollingInterval, Is.EqualTo("1"));
string json2 = "{\"iceServers\":[{\"credential\":\"pass\",\"username\":\"user\",\"credentialType\":\"password\"," +
"\"urls\":[\"turn:192.168.10.10:3478?transport=udp\"]}]}";
settings = JsonUtility.FromJson<CommandLineInfo>(json2);
Assert.That(settings.signalingUrl, Is.Null);
Assert.That(settings.signalingType, Is.Null);
Assert.That(settings.iceServers, Has.Length.EqualTo(1));
Assert.That(settings.iceServers[0].credential, Is.EqualTo("pass"));
Assert.That(settings.iceServers[0].credentialType, Is.EqualTo(RTCIceCredentialType.Password));
Assert.That(settings.iceServers[0].username, Is.EqualTo("user"));
Assert.That(settings.iceServers[0].urls, Has.Length.EqualTo(1));
Assert.That(settings.iceServers[0].urls[0], Is.EqualTo("turn:192.168.10.10:3478?transport=udp"));
Assert.That(settings.pollingInterval, Is.Null);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f48ad77b30bc9124aaf2a391f696603b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,21 @@
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.RenderStreaming.RuntimeTest
{
internal class ConditionalIgnore
{
public const string IL2CPP = "IgnoreIL2CPP";
[RuntimeInitializeOnLoadMethod]
static void OnLoad()
{
#if ENABLE_IL2CPP
ConditionalIgnoreAttribute.AddConditionalIgnoreMapping(IL2CPP, true);
#else
ConditionalIgnoreAttribute.AddConditionalIgnoreMapping(IL2CPP, false);
#endif
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5b6ef4d8d4a64791951abcc8b5a8decf
timeCreated: 1607924977

View File

@@ -0,0 +1,15 @@
using System;
using NUnit.Framework;
namespace Unity.RenderStreaming.RuntimeTest
{
public class DataTimeExtensionTest
{
[Test]
public void ToJsMilliseconds()
{
Assert.That(DateTime.Now.ToJsMilliseconds(), Is.GreaterThan(0));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2e4d23d9326e94e46ad84b32ceb0d7b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using System.Linq;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.LowLevel;
namespace Unity.RenderStreaming.RuntimeTest
{
class InputPositionCorrectorTest
{
void OnEvent(InputEventPtr ptr, InputDevice device)
{
}
[Test]
public void Invoke()
{
System.Action<InputEventPtr, InputDevice> onEvent = OnEvent;
var corrector = new InputPositionCorrector(onEvent);
Assert.That(corrector.inputRegion, Is.EqualTo(Rect.zero));
Assert.That(corrector.outputRegion, Is.EqualTo(Rect.zero));
var device = UnityEngine.InputSystem.InputSystem.devices.First(_ => _ is Pointer);
var inputEvent = new InputEvent();
unsafe
{
var ptr = InputEventPtr.From(&inputEvent);
corrector.Invoke(ptr, device);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 968452e4b48884a4d8f3c9374ad112da
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b3acede1c97bb3a479ee41cbcaf738aa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
using NUnit.Framework;
using Unity.RenderStreaming.InputSystem;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Layouts;
using Assert = NUnit.Framework.Assert;
namespace Unity.RenderStreaming.RuntimeTest
{
using InputSystem = UnityEngine.InputSystem.InputSystem;
class InputDeviceExtensionTest
{
[Test]
public void SetDescription()
{
var device = InputSystem.AddDevice<Mouse>();
InputDeviceDescription empty;
InputDeviceDescription origin = device.description;
device.SetDescription(empty);
Assert.That(device.description, Is.EqualTo(empty));
device.SetDescription(origin);
Assert.That(device.description, Is.EqualTo(origin));
}
[Test]
public void SetParticipantId()
{
var device = InputSystem.AddDevice<Mouse>();
int participantId = 0;
int origin = device.GetParticipantId();
device.SetParticipantId(participantId);
Assert.That(device.GetParticipantId(), Is.EqualTo(participantId));
device.SetParticipantId(origin);
Assert.That(device.GetParticipantId(), Is.EqualTo(origin));
}
[Test]
public void SetDeviceFlags()
{
var device = InputSystem.AddDevice<Mouse>();
int deviceFlags = 0;
int origin = device.GetDeviceFlags();
device.SetDeviceFlags(deviceFlags);
Assert.That(device.GetDeviceFlags(), Is.EqualTo(deviceFlags));
device.SetDeviceFlags(origin);
Assert.That(device.GetDeviceFlags(), Is.EqualTo(origin));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 385b386710aac644991171f0a1f789d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
namespace Unity.RenderStreaming.RuntimeTest
{
#if UNITY_EDITOR && !INPUTSYSTEM_1_1_OR_NEWER
class InputEditorUserSettingsTest
{
[SetUp]
public void SetUp()
{
InputEditorUserSettings.Load();
}
[TearDown]
public void TearDown()
{
InputEditorUserSettings.Delete();
}
[Test]
public void LockInputToGameView()
{
Assert.That(InputEditorUserSettings.lockInputToGameView, Is.False);
InputEditorUserSettings.lockInputToGameView = true;
Assert.That(InputEditorUserSettings.lockInputToGameView, Is.True);
}
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dcb16a2d6c29aaa4586616b57ea3cc9e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,254 @@
using System.Collections;
using NUnit.Framework;
using Unity.RenderStreaming.InputSystem;
using Unity.RenderStreaming.RuntimeTest.Signaling;
using Unity.WebRTC;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.TestTools;
using Assert = NUnit.Framework.Assert;
namespace Unity.RenderStreaming.RuntimeTest
{
using InputRemoting = InputSystem.InputRemoting;
class MessageSerializerTest
{
[Test]
public void Serialize()
{
InputRemoting.Message message1 = new InputRemoting.Message
{
participantId = 1,
type = InputRemoting.MessageType.NewEvents,
data = new byte[] { 1, 2, 3, 4, 5 },
};
var bytes = MessageSerializer.Serialize(ref message1);
Assert.That(bytes, Is.Not.Null);
Assert.That(bytes, Has.Length.GreaterThan(0));
MessageSerializer.Deserialize(bytes, out var message2);
Assert.That(message2.participantId, Is.EqualTo(message1.participantId));
Assert.That(message2.type, Is.EqualTo(message1.type));
Assert.That(message2.data, Is.EqualTo(message1.data));
}
}
/// todo(kazuki):workaround
class InputRemotingTest
{
class MyMonoBehaviourTest : MonoBehaviour, IMonoBehaviourTest
{
public bool IsTestFinished
{
get { return true; }
}
}
private MonoBehaviourTest<MyMonoBehaviourTest> _test;
private SignalingManagerInternal _target1, _target2;
private RTCDataChannel _channel1, _channel2;
private string connectionId = "12345";
const float ResendOfferInterval = 3f;
private RenderStreamingDependencies CreateDependencies()
{
return new RenderStreamingDependencies
{
signaling = new MockSignaling(),
config = new RTCConfiguration
{
iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } },
},
startCoroutine = _test.component.StartCoroutine,
stopCoroutine = _test.component.StopCoroutine,
resentOfferInterval = ResendOfferInterval,
};
}
[UnitySetUp]
public IEnumerator UnitySetUp()
{
MockSignaling.Reset(true);
_test = new MonoBehaviourTest<MyMonoBehaviourTest>();
var dependencies1 = CreateDependencies();
var dependencies2 = CreateDependencies();
_target1 = new SignalingManagerInternal(ref dependencies1);
_target2 = new SignalingManagerInternal(ref dependencies2);
bool isStarted1 = false;
bool isStarted2 = false;
_target1.onStart += () => { isStarted1 = true; };
_target2.onStart += () => { isStarted2 = true; };
yield return new WaitUntil(() => isStarted1 && isStarted2);
bool isCreatedConnection1 = false;
bool isCreatedConnection2 = false;
_target1.onCreatedConnection += _ => { isCreatedConnection1 = true; };
_target2.onCreatedConnection += _ => { isCreatedConnection2 = true; };
// _target1 is Receiver in private mode
_target1.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection1);
// _target2 is sender in private mode
_target2.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection2);
_target1.onAddChannel += (_, channel) => { _channel1 = channel; };
// send offer automatically after creating channel
_channel2 = _target2.CreateChannel(connectionId, "_test");
bool isGotOffer1 = false;
bool isGotAnswer2 = false;
_target1.onGotOffer += (_, sdp) => { isGotOffer1 = true; };
_target2.onGotAnswer += (_, sdp) => { isGotAnswer2 = true; };
// each peer are not stable, signaling process not complete.
yield return new WaitUntil(() => isGotOffer1);
_target1.SendAnswer(connectionId);
yield return new WaitUntil(() => isGotAnswer2);
Assert.That(isGotAnswer2, Is.True);
// If target1 processes resent Offer from target2, target1 is not stable.
Assert.That(_target2.IsStable(connectionId), Is.True);
yield return new WaitUntil(() => _channel1 != null);
Assert.That(_channel1.ReadyState, Is.EqualTo(RTCDataChannelState.Open));
}
[UnityTearDown]
public IEnumerator UnityTearDown()
{
_target1.DeleteConnection(connectionId);
_target2.DeleteConnection(connectionId);
bool isDeletedConnection1 = false;
bool isDeletedConnection2 = false;
_target1.onDeletedConnection += _ => { isDeletedConnection1 = true; };
_target2.onDeletedConnection += _ => { isDeletedConnection2 = true; };
yield return new WaitUntil(() => isDeletedConnection1 && isDeletedConnection2);
_channel1.Dispose();
_channel2.Dispose();
_channel1 = null;
_channel2 = null;
_test.component.StopAllCoroutines();
_target1.Dispose();
_target2.Dispose();
UnityEngine.Object.DestroyImmediate(_test.gameObject);
}
[Test]
public void DoNothing()
{
}
[Test]
public void Sender()
{
var sender = new Sender();
Assert.That(sender.layouts, Is.Not.Empty);
Assert.That(sender.devices, Is.Not.Empty);
Assert.That(sender.GetDeviceById(0), Is.Null);
var senderInput = new InputRemoting(sender);
var senderDisposer = senderInput.Subscribe(new Observer(_channel1));
senderInput.StartSending();
senderInput.StopSending();
senderDisposer.Dispose();
sender.Dispose();
}
[Test]
public void Receiver()
{
var receiver = new Receiver(_channel1);
Assert.That(receiver.remoteDevices, Is.Empty);
Assert.That(receiver.remoteLayouts, Is.Empty);
Assert.That(receiver.GetDeviceById(0), Is.Null);
var receiverInput = new InputRemoting(receiver);
var receiverDisposer = receiverInput.Subscribe(receiverInput);
receiverInput.StartSending();
receiverInput.StopSending();
receiverDisposer.Dispose();
}
/// todo(kazuki): This test is failed for timeout on macOS.
/// todo(kazuki): This test is failed for timeout on iPhonePlayer.
[UnityTest, Timeout(3000)]
[UnityPlatform(exclude = new[] { RuntimePlatform.OSXPlayer, RuntimePlatform.IPhonePlayer })]
public IEnumerator AddDevice()
{
var sender = new Sender();
var senderInput = new InputRemoting(sender);
var senderSubscriberDisposer = senderInput.Subscribe(new Observer(_channel1));
var receiver = new Receiver(_channel2);
var receiverInput = new InputRemoting(receiver);
var receiverSubscriberDisposer = receiverInput.Subscribe(receiverInput);
InputDevice device = null;
InputDeviceChange change = default;
receiver.onDeviceChange += (_device, _change) =>
{
device = _device;
change = _change;
};
string layoutName = null;
InputControlLayoutChange layoutChange = default;
receiver.onLayoutChange += (_name, _change) =>
{
layoutName = _name;
layoutChange = _change;
};
receiverInput.StartSending();
senderInput.StartSending();
yield return new WaitUntil(() => device != null);
yield return new WaitUntil(() => layoutName != null);
Assert.That(device, Is.Not.Null);
Assert.That(change, Is.EqualTo(InputDeviceChange.Added));
Assert.That(layoutName, Is.Not.Null);
Assert.That(layoutChange, Is.EqualTo(InputControlLayoutChange.Added));
Assert.That(receiver.remoteLayouts, Is.Not.Empty);
Assert.That(receiver.remoteDevices, Is.Not.Empty);
Assert.That(receiver.remoteDevices, Has.All.Matches<InputDevice>(d => d.remote));
senderInput.StopSending();
receiverInput.StopSending();
senderSubscriberDisposer.Dispose();
receiverSubscriberDisposer.Dispose();
sender.Dispose();
receiver.Dispose();
}
[Test]
public void RemoveRemoteDevices()
{
var sender = new Sender();
var senderInput = new InputRemoting(sender);
senderInput.RemoveRemoteDevices(0);
sender.Dispose();
}
[Test]
public void SaveState()
{
var sender = new Sender();
var inputRemoting = new InputRemoting(sender);
var state = inputRemoting.SaveState();
inputRemoting.RestoreState(state, sender);
sender.Dispose();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: adb3bfa611b70d243a3d5d87b5990736
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,93 @@
using System;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Unity.RenderStreaming.RuntimeTest
{
public class MockLogger : ILogger
{
public void LogFormat(LogType logType, Object context, string format, params object[] args)
{
throw new NotImplementedException();
}
public void LogException(Exception exception, Object context)
{
throw new NotImplementedException();
}
public bool IsLogTypeAllowed(LogType logType)
{
throw new NotImplementedException();
}
public void Log(LogType logType, object message)
{
throw new NotImplementedException();
}
public void Log(LogType logType, object message, Object context)
{
throw new NotImplementedException();
}
public void Log(LogType logType, string tag, object message)
{
throw new NotImplementedException();
}
public void Log(LogType logType, string tag, object message, Object context)
{
throw new NotImplementedException();
}
public void Log(object message)
{
throw new NotImplementedException();
}
public void Log(string tag, object message)
{
throw new NotImplementedException();
}
public void Log(string tag, object message, Object context)
{
throw new NotImplementedException();
}
public void LogWarning(string tag, object message)
{
throw new NotImplementedException();
}
public void LogWarning(string tag, object message, Object context)
{
throw new NotImplementedException();
}
public void LogError(string tag, object message)
{
throw new NotImplementedException();
}
public void LogError(string tag, object message, Object context)
{
throw new NotImplementedException();
}
public void LogFormat(LogType logType, string format, params object[] args)
{
throw new NotImplementedException();
}
public void LogException(Exception exception)
{
throw new NotImplementedException();
}
public ILogHandler logHandler { get; set; }
public bool logEnabled { get; set; }
public LogType filterLogType { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e2595de35a1ada64f923eed1a5e42aa3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,963 @@
using System.Collections;
using NUnit.Framework;
using Unity.WebRTC;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.RenderStreaming.RuntimeTest
{
class PeerConnectionTest
{
class MyMonoBehaviourTest : MonoBehaviour, IMonoBehaviourTest
{
public bool IsTestFinished
{
get { return true; }
}
}
private const float ResendOfferInterval = 1.0f;
private MonoBehaviourTest<MyMonoBehaviourTest> test;
private RTCConfiguration config;
[SetUp]
public void SetUp()
{
test = new MonoBehaviourTest<MyMonoBehaviourTest>();
config = new RTCConfiguration
{
iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } },
};
}
[TearDown]
public void TearDown()
{
test.component.StopAllCoroutines();
Object.Destroy(test.gameObject);
}
[Test, Timeout(5000)]
public void Construct()
{
var peer = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer, Is.Not.Null);
var rtcPeer = peer.peer;
Assert.That(rtcPeer, Is.Not.Null);
Assert.That(rtcPeer.OnTrack, Is.Not.Null);
Assert.That(rtcPeer.OnIceCandidate, Is.Not.Null);
Assert.That(rtcPeer.OnNegotiationNeeded, Is.Not.Null);
Assert.That(rtcPeer.OnDataChannel, Is.Not.Null);
Assert.That(rtcPeer.OnConnectionStateChange, Is.Not.Null);
peer.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator CalledSendOfferWhenAddTrack()
{
var peer = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer, Is.Not.Null);
var isGotSendOffer = false;
RTCSessionDescription offerDesc = default;
peer.SendOfferHandler += description =>
{
isGotSendOffer = true;
offerDesc = description;
};
var track = new AudioStreamTrack();
peer.peer.AddTrack(track);
yield return new WaitUntil(() => isGotSendOffer);
Assert.That(isGotSendOffer, Is.True);
Assert.That(offerDesc.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc.sdp, Is.Not.Null.Or.Empty);
track.Dispose();
peer.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator CalledSendOfferWhenAddTransceiver()
{
var peer = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer, Is.Not.Null);
var isGotSendOffer = false;
RTCSessionDescription offerDesc = default;
peer.SendOfferHandler += description =>
{
isGotSendOffer = true;
offerDesc = description;
};
var transceiver = peer.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver, Is.Not.Null);
yield return new WaitUntil(() => isGotSendOffer);
Assert.That(isGotSendOffer, Is.True);
Assert.That(offerDesc.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc.sdp, Is.Not.Null.Or.Empty);
transceiver.Dispose();
peer.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator CalledSendOfferWhenCreateChannel()
{
var peer = new PeerConnection(
true,
config,
ResendOfferInterval,
test.component.StartCoroutine,
test.component.StopCoroutine
);
var isGotSendOffer = false;
RTCSessionDescription offerDesc = default;
peer.SendOfferHandler += description =>
{
isGotSendOffer = true;
offerDesc = description;
};
var channel = peer.peer.CreateDataChannel("test");
Assert.That(channel, Is.Not.Null);
yield return new WaitUntil(() => isGotSendOffer);
Assert.That(isGotSendOffer, Is.True);
Assert.That(offerDesc.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc.sdp, Is.Not.Null.Or.Empty);
channel.Dispose();
peer.Dispose();
}
[UnityTest, Timeout(5000), LongRunning]
public IEnumerator CalledSendOfferTwiceIfGetAnswerNotYet()
{
var peer = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer, Is.Not.Null);
var sendOfferCount = 0;
RTCSessionDescription offerDesc = default;
peer.SendOfferHandler += description =>
{
sendOfferCount++;
offerDesc = description;
};
var track = new AudioStreamTrack();
peer.peer.AddTrack(track);
while (sendOfferCount <= 2)
{
yield return new WaitForSeconds(ResendOfferInterval);
Assert.That(peer.waitingAnswer, Is.True);
peer.SendOffer();
}
yield return new WaitUntil(() => sendOfferCount > 2);
Assert.That(sendOfferCount, Is.GreaterThan(2));
Assert.That(offerDesc.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc.sdp, Is.Not.Null.Or.Empty);
track.Dispose();
peer.Dispose();
}
[UnityTest, Timeout(5000)]
[TestCase(true, ExpectedResult = null)]
[TestCase(false, ExpectedResult = null)]
public IEnumerator CalledSendAnswerWhenGotDescriptionWhenStable(bool polite)
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(polite, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer = false;
RTCSessionDescription offerDesc = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer = true;
offerDesc = description;
};
var isGotSendAnswer = false;
RTCSessionDescription answerDesc = default;
peer2.SendAnswerHandler += description =>
{
isGotSendAnswer = true;
answerDesc = description;
};
var track = new AudioStreamTrack();
peer1.peer.AddTrack(track);
yield return new WaitUntil(() => isGotSendOffer);
Assert.That(isGotSendOffer, Is.True);
Assert.That(offerDesc.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc.sdp, Is.Not.Null.Or.Empty);
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc, () => isComplete = true);
Assert.That(isComplete, Is.True);
peer2.SendAnswer();
yield return new WaitUntil(() => isGotSendAnswer);
Assert.That(isGotSendAnswer, Is.True);
Assert.That(answerDesc.type, Is.EqualTo(RTCSdpType.Answer));
Assert.That(answerDesc.sdp, Is.Not.Null.Or.Empty);
track.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator CalledSendAnswerWhenGotDescriptionThatHaveLocalOfferInPolite()
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer1 = false;
RTCSessionDescription offerDesc1 = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer1 = true;
offerDesc1 = description;
};
var isGotSendOffer2 = false;
RTCSessionDescription offerDesc2 = default;
peer2.SendOfferHandler += description =>
{
isGotSendOffer2 = true;
offerDesc2 = description;
};
var isGotSendAnswer = false;
RTCSessionDescription answerDesc = default;
peer2.SendAnswerHandler += description =>
{
isGotSendAnswer = true;
answerDesc = description;
};
var track1 = new AudioStreamTrack();
peer1.peer.AddTrack(track1);
var track2 = new AudioStreamTrack();
peer2.peer.AddTrack(track2);
yield return new WaitUntil(() => isGotSendOffer1);
Assert.That(isGotSendOffer1, Is.True);
Assert.That(offerDesc1.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc1.sdp, Is.Not.Null.Or.Empty);
yield return new WaitUntil(() => isGotSendOffer2);
Assert.That(isGotSendOffer1, Is.True);
Assert.That(offerDesc2.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc2.sdp, Is.Not.Null.Or.Empty);
Assert.That(peer2.peer.SignalingState, Is.EqualTo(RTCSignalingState.HaveLocalOffer));
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc1, () => isComplete = true);
Assert.That(isComplete, Is.True);
peer2.SendAnswer();
yield return new WaitUntil(() => isGotSendAnswer);
Assert.That(isGotSendAnswer, Is.True);
Assert.That(answerDesc.type, Is.EqualTo(RTCSdpType.Answer));
Assert.That(answerDesc.sdp, Is.Not.Null.Or.Empty);
Assert.That(peer2.peer.SignalingState, Is.EqualTo(RTCSignalingState.Stable));
track1.Dispose();
track2.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator NotCalledSendAnswerWhenGotDescriptionThatHaveLocalOfferInImpolite()
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(false, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer1 = false;
RTCSessionDescription offerDesc1 = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer1 = true;
offerDesc1 = description;
};
var isGotSendOffer2 = false;
RTCSessionDescription offerDesc2 = default;
peer2.SendOfferHandler += description =>
{
isGotSendOffer2 = true;
offerDesc2 = description;
};
var track1 = new AudioStreamTrack();
peer1.peer.AddTrack(track1);
var track2 = new AudioStreamTrack();
peer2.peer.AddTrack(track2);
yield return new WaitUntil(() => isGotSendOffer1);
Assert.That(isGotSendOffer1, Is.True);
Assert.That(offerDesc1.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc1.sdp, Is.Not.Null.Or.Empty);
yield return new WaitUntil(() => isGotSendOffer2);
Assert.That(isGotSendOffer1, Is.True);
Assert.That(offerDesc2.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc2.sdp, Is.Not.Null.Or.Empty);
Assert.That(peer2.peer.SignalingState, Is.EqualTo(RTCSignalingState.HaveLocalOffer));
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc1, () => isComplete = true);
Assert.That(isComplete, Is.False);
Assert.That(peer2.peer.SignalingState, Is.EqualTo(RTCSignalingState.HaveLocalOffer));
track1.Dispose();
track2.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator CalledTrackEventWhenGotSdpIncludeTrack()
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer = false;
RTCSessionDescription offerDesc = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer = true;
offerDesc = description;
};
var isGotTrackEvent = false;
RTCTrackEvent trackEvent = null;
peer2.OnTrackEventHandler += e =>
{
isGotTrackEvent = true;
trackEvent = e;
};
var track = new AudioStreamTrack();
peer1.peer.AddTrack(track);
yield return new WaitUntil(() => isGotSendOffer);
Assert.That(isGotSendOffer, Is.True);
Assert.That(offerDesc.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc.sdp, Is.Not.Null.Or.Empty);
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc, () => isComplete = true);
Assert.That(isComplete, Is.True);
yield return new WaitUntil(() => isGotTrackEvent);
Assert.That(isGotTrackEvent, Is.True);
Assert.That(trackEvent, Is.Not.Null);
Assert.That(trackEvent.Track.Id, Is.EqualTo(track.Id));
track.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator CalledOnDataChannelWhenGotSdpIncludeDataChannel()
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer = false;
RTCSessionDescription offerDesc = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer = true;
offerDesc = description;
};
var isGotSendAnswer = false;
RTCSessionDescription answerDesc = default;
peer2.SendAnswerHandler += description =>
{
isGotSendAnswer = true;
answerDesc = description;
};
var isGotDataChannel = false;
RTCDataChannel dataChannel = null;
peer2.OnDataChannelHandler += e =>
{
isGotDataChannel = true;
dataChannel = e;
};
var channel = peer1.peer.CreateDataChannel("test");
Assert.That(channel, Is.Not.Null);
yield return new WaitUntil(() => isGotSendOffer);
Assert.That(isGotSendOffer, Is.True);
Assert.That(offerDesc.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc.sdp, Is.Not.Null.Or.Empty);
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc, () => isComplete = true);
Assert.That(isComplete, Is.True);
peer2.SendAnswer();
yield return new WaitUntil(() => isGotSendAnswer);
Assert.That(isGotSendAnswer, Is.True);
Assert.That(answerDesc.type, Is.EqualTo(RTCSdpType.Answer));
Assert.That(answerDesc.sdp, Is.Not.Null.Or.Empty);
isComplete = false;
yield return peer1.OnGotDescription(answerDesc, () => isComplete = true);
Assert.That(isComplete, Is.True);
yield return new WaitUntil(() => isGotDataChannel);
Assert.That(isGotDataChannel, Is.True);
Assert.That(dataChannel, Is.Not.Null);
Assert.That(dataChannel.Id, Is.EqualTo(channel.Id));
channel.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator CalledSendCandidateWhenAddTransceiver()
{
var peer = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer, Is.Not.Null);
var isGotSendCandidate = false;
RTCIceCandidate candidate = null;
peer.SendCandidateHandler += e =>
{
isGotSendCandidate = true;
candidate = e;
};
var transceiver = peer.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver, Is.Not.Null);
yield return new WaitUntil(() => isGotSendCandidate);
Assert.That(isGotSendCandidate, Is.True);
Assert.That(candidate, Is.Not.Null);
transceiver.Dispose();
peer.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator AcceptWhenGotCandidateThatHaveRemoteDescription()
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer1 = false;
RTCSessionDescription offerDesc1 = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer1 = true;
offerDesc1 = description;
};
var isGotSendCandidate = false;
RTCIceCandidate candidate = null;
peer1.SendCandidateHandler += e =>
{
isGotSendCandidate = true;
candidate = e;
};
var transceiver = peer1.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver, Is.Not.Null);
yield return new WaitUntil(() => isGotSendOffer1);
Assert.That(isGotSendOffer1, Is.True);
Assert.That(offerDesc1.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc1.sdp, Is.Not.Null.Or.Empty);
yield return new WaitUntil(() => isGotSendCandidate);
Assert.That(isGotSendCandidate, Is.True);
Assert.That(candidate, Is.Not.Null);
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc1, () => isComplete = true);
Assert.That(isComplete, Is.True);
Assert.That(peer2.OnGotIceCandidate(candidate), Is.True);
transceiver.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator NotAcceptWhenGotCandidateThatDontHaveRemoteDescription()
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer1 = false;
RTCSessionDescription offerDesc1 = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer1 = true;
offerDesc1 = description;
};
var isGotSendCandidate = false;
RTCIceCandidate candidate = null;
peer1.SendCandidateHandler += e =>
{
isGotSendCandidate = true;
candidate = e;
};
var transceiver = peer1.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver, Is.Not.Null);
yield return new WaitUntil(() => isGotSendOffer1);
Assert.That(isGotSendOffer1, Is.True);
Assert.That(offerDesc1.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc1.sdp, Is.Not.Null.Or.Empty);
yield return new WaitUntil(() => isGotSendCandidate);
Assert.That(isGotSendCandidate, Is.True);
Assert.That(candidate, Is.Not.Null);
Assert.That(peer2.OnGotIceCandidate(candidate), Is.False);
transceiver.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator CalledOnConnect()
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer = false;
RTCSessionDescription offerDesc = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer = true;
offerDesc = description;
};
var isGotConnect1 = false;
peer1.OnConnectHandler += () => isGotConnect1 = true;
var isGotSendAnswer = false;
RTCSessionDescription answerDesc = default;
peer2.SendAnswerHandler += description =>
{
isGotSendAnswer = true;
answerDesc = description;
};
var isGotConnect2 = false;
peer2.OnConnectHandler += () => isGotConnect2 = true;
var transceiver = peer1.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver, Is.Not.Null);
yield return new WaitUntil(() => isGotSendOffer);
Assert.That(isGotSendOffer, Is.True);
Assert.That(offerDesc.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc.sdp, Is.Not.Null.Or.Empty);
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc, () => isComplete = true);
Assert.That(isComplete, Is.True);
peer2.SendAnswer();
yield return new WaitUntil(() => isGotSendAnswer);
Assert.That(isGotSendAnswer, Is.True);
Assert.That(answerDesc.type, Is.EqualTo(RTCSdpType.Answer));
Assert.That(answerDesc.sdp, Is.Not.Null.Or.Empty);
isComplete = false;
yield return peer1.OnGotDescription(answerDesc, () => isComplete = true);
Assert.That(isComplete, Is.True);
yield return new WaitUntil(() => isGotConnect1 && isGotConnect2);
Assert.That(isGotConnect1, Is.True);
Assert.That(isGotConnect2, Is.True);
transceiver.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator SendOfferTwiceImmediately()
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var isGotSendOffer = false;
RTCSessionDescription offerDesc = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer = true;
offerDesc = description;
};
var transceiver = peer1.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver, Is.Not.Null);
peer1.SendOffer();
peer1.SendOffer();
yield return new WaitUntil(() => isGotSendOffer);
Assert.That(isGotSendOffer, Is.True);
Assert.That(offerDesc.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc.sdp, Is.Not.Null.Or.Empty);
transceiver.Dispose();
peer1.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator SendAnswerTwiceImmediately()
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer = false;
RTCSessionDescription offerDesc = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer = true;
offerDesc = description;
};
var isGotSendAnswer = false;
RTCSessionDescription answerDesc = default;
peer2.SendAnswerHandler += description =>
{
isGotSendAnswer = true;
answerDesc = description;
};
var transceiver = peer1.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver, Is.Not.Null);
yield return new WaitUntil(() => isGotSendOffer);
Assert.That(isGotSendOffer, Is.True);
Assert.That(offerDesc.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc.sdp, Is.Not.Null.Or.Empty);
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc, () => isComplete = true);
Assert.That(isComplete, Is.True);
peer2.SendAnswer();
peer2.SendAnswer();
yield return new WaitUntil(() => isGotSendAnswer);
Assert.That(isGotSendAnswer, Is.True);
Assert.That(answerDesc.type, Is.EqualTo(RTCSdpType.Answer));
Assert.That(answerDesc.sdp, Is.Not.Null.Or.Empty);
transceiver.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator OnGotOfferDescriptionAfterSendOfferImmediatelyInPolite()
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer1 = false;
RTCSessionDescription offerDesc1 = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer1 = true;
offerDesc1 = description;
};
var isGotSendOffer2 = false;
RTCSessionDescription offerDesc2 = default;
peer2.SendOfferHandler += description =>
{
isGotSendOffer2 = true;
offerDesc2 = description;
};
var isGotSendAnswer = false;
RTCSessionDescription answerDesc = default;
peer2.SendAnswerHandler += description =>
{
isGotSendAnswer = true;
answerDesc = description;
};
var transceiver1 = peer1.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver1, Is.Not.Null);
yield return new WaitUntil(() => isGotSendOffer1);
Assert.That(isGotSendOffer1, Is.True);
Assert.That(offerDesc1.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc1.sdp, Is.Not.Null.Or.Empty);
var transceiver2 = peer2.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver2, Is.Not.Null);
//workaround: Need to wait 1 frame for negotiationneeded to be processed
yield return 0;
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc1, () => isComplete = true);
Assert.That(isComplete, Is.True);
yield return new WaitForSeconds(ResendOfferInterval);
Assert.That(isGotSendOffer2, Is.False, "need waiting offer cause receive remote offer");
peer2.SendAnswer();
yield return new WaitUntil(() => isGotSendAnswer);
Assert.That(isGotSendAnswer, Is.True);
Assert.That(answerDesc.type, Is.EqualTo(RTCSdpType.Answer));
Assert.That(answerDesc.sdp, Is.Not.Null.Or.Empty);
yield return new WaitUntil(() => isGotSendOffer2);
Assert.That(isGotSendOffer2, Is.True);
Assert.That(offerDesc2.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc2.sdp, Is.Not.Null.Or.Empty);
transceiver1.Dispose();
transceiver2.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000)]
public IEnumerator OnGotOfferDescriptionAfterSendOfferImmediatelyInImPolite()
{
var peer1 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(false, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer1 = false;
RTCSessionDescription offerDesc1 = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer1 = true;
offerDesc1 = description;
};
var isGotSendOffer2 = false;
RTCSessionDescription offerDesc2 = default;
peer2.SendOfferHandler += description =>
{
isGotSendOffer2 = true;
offerDesc2 = description;
};
var transceiver1 = peer1.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver1, Is.Not.Null);
yield return new WaitUntil(() => isGotSendOffer1);
Assert.That(isGotSendOffer1, Is.True);
Assert.That(offerDesc1.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc1.sdp, Is.Not.Null.Or.Empty);
var transceiver2 = peer2.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver2, Is.Not.Null);
//workaround: Need to wait 1 frame for negotiationneeded to be processed
yield return 0;
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc1, () => isComplete = true);
Assert.That(isComplete, Is.False, "need ignore offer cause peer2 is impolite");
yield return new WaitUntil(() => isGotSendOffer2);
Assert.That(isGotSendOffer2, Is.True);
Assert.That(offerDesc2.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc2.sdp, Is.Not.Null.Or.Empty);
transceiver1.Dispose();
transceiver2.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000), LongRunning]
[TestCase(true, ExpectedResult = null)]
[TestCase(false, ExpectedResult = null)]
public IEnumerator OnGotAnswerDescriptionAfterSendOfferImmediately(bool polite)
{
var peer1 = new PeerConnection(polite, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer1 = false;
RTCSessionDescription offerDesc1 = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer1 = true;
offerDesc1 = description;
};
var isGotSendAnswer = false;
RTCSessionDescription answerDesc = default;
peer2.SendAnswerHandler += description =>
{
isGotSendAnswer = true;
answerDesc = description;
};
var transceiver1 = peer1.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver1, Is.Not.Null);
yield return new WaitUntil(() => isGotSendOffer1);
Assert.That(isGotSendOffer1, Is.True);
Assert.That(offerDesc1.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc1.sdp, Is.Not.Null.Or.Empty);
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc1, () => isComplete = true);
Assert.That(isComplete, Is.True);
peer2.SendAnswer();
yield return new WaitUntil(() => isGotSendAnswer);
Assert.That(isGotSendAnswer, Is.True);
Assert.That(answerDesc.type, Is.EqualTo(RTCSdpType.Answer));
Assert.That(answerDesc.sdp, Is.Not.Null.Or.Empty);
yield return new WaitForSeconds(ResendOfferInterval);
peer1.SendOffer();
isComplete = false;
yield return peer1.OnGotDescription(answerDesc, () => isComplete = true);
Assert.That(isComplete, Is.True);
transceiver1.Dispose();
peer1.Dispose();
peer2.Dispose();
}
[UnityTest, Timeout(5000)]
[TestCase(true, ExpectedResult = null)]
[TestCase(false, ExpectedResult = null)]
public IEnumerator OnGotOfferDescriptionAfterSendAnswerImmediately(bool polite)
{
var peer1 = new PeerConnection(polite, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer1, Is.Not.Null);
var peer2 = new PeerConnection(true, config, ResendOfferInterval,
test.component.StartCoroutine, test.component.StopCoroutine);
Assert.That(peer2, Is.Not.Null);
var isGotSendOffer1 = false;
RTCSessionDescription offerDesc1 = default;
peer1.SendOfferHandler += description =>
{
isGotSendOffer1 = true;
offerDesc1 = description;
};
var isGotSendAnswer = false;
RTCSessionDescription answerDesc = default;
peer2.SendAnswerHandler += description =>
{
isGotSendAnswer = true;
answerDesc = description;
};
var transceiver1 = peer1.peer.AddTransceiver(TrackKind.Video);
Assert.That(transceiver1, Is.Not.Null);
yield return new WaitUntil(() => isGotSendOffer1);
Assert.That(isGotSendOffer1, Is.True);
Assert.That(offerDesc1.type, Is.EqualTo(RTCSdpType.Offer));
Assert.That(offerDesc1.sdp, Is.Not.Null.Or.Empty);
var isComplete = false;
yield return peer2.OnGotDescription(offerDesc1, () => isComplete = true);
Assert.That(isComplete, Is.True);
peer2.SendAnswer();
isComplete = false;
yield return peer2.OnGotDescription(offerDesc1, () => isComplete = true);
Assert.That(isComplete, Is.True);
yield return new WaitUntil(() => isGotSendAnswer);
Assert.That(isGotSendAnswer, Is.True);
Assert.That(answerDesc.type, Is.EqualTo(RTCSdpType.Answer));
Assert.That(answerDesc.sdp, Is.Not.Null.Or.Empty);
transceiver1.Dispose();
peer1.Dispose();
peer2.Dispose();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ba9cd9098d364eecbabbf6d1c678b2d5
timeCreated: 1661734624

View File

@@ -0,0 +1,556 @@
using System;
using System.Collections;
using System.Diagnostics;
using System.Threading;
using NUnit.Framework;
using Unity.RenderStreaming.RuntimeTest.Signaling;
using Unity.RenderStreaming.Signaling;
using Unity.WebRTC;
using UnityEngine;
using UnityEngine.TestTools;
using Debug = UnityEngine.Debug;
namespace Unity.RenderStreaming.RuntimeTest
{
[TestFixture(typeof(WebSocketSignaling))]
[TestFixture(typeof(HttpSignaling))]
[TestFixture(typeof(MockSignaling))]
[UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })]
[ConditionalIgnore(ConditionalIgnore.IL2CPP, "Process.Start does not implement in IL2CPP.")]
class PrivateSignalingTest : IPrebuildSetup
{
private readonly Type m_SignalingType;
private Process m_ServerProcess;
private RTCSessionDescription m_DescOffer;
private RTCSessionDescription m_DescAnswer;
private RTCIceCandidate m_candidate;
private SynchronizationContext m_Context;
private ISignaling signaling1;
private ISignaling signaling2;
public PrivateSignalingTest()
{
}
public PrivateSignalingTest(Type type)
{
m_SignalingType = type;
}
public void Setup()
{
if (m_SignalingType == typeof(MockSignaling))
{
return;
}
#if UNITY_EDITOR
string dir = System.IO.Directory.GetCurrentDirectory();
string fileName = System.IO.Path.Combine(dir, Editor.WebAppDownloader.GetFileName());
if (System.IO.File.Exists(fileName) || System.IO.File.Exists(TestUtility.GetWebAppLocationFromEnv()))
{
// already exists.
return;
}
bool downloadRaised = false;
Editor.WebAppDownloader.DownloadCurrentVersionWebApp(dir, success => { downloadRaised = true; });
TestUtility.Wait(() => downloadRaised, 10000);
#endif
}
[OneTimeSetUp]
public void OneTimeSetUp()
{
if (m_SignalingType == typeof(MockSignaling))
{
MockSignaling.Reset(true);
return;
}
m_ServerProcess = new Process();
string fileName = TestUtility.GetWebAppLocationFromEnv();
if (string.IsNullOrEmpty(fileName))
{
Debug.Log($"webapp file not found in {fileName}");
string dir = System.IO.Directory.GetCurrentDirectory();
fileName = System.IO.Path.Combine(dir, TestUtility.GetFileName());
}
Assert.IsTrue(System.IO.File.Exists(fileName), $"webapp file not found in {fileName}");
ProcessStartInfo startInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
FileName = fileName,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
string arguments = $"-m private -p {TestUtility.PortNumber}";
if (m_SignalingType == typeof(HttpSignaling))
{
arguments += " -t http";
}
startInfo.Arguments = arguments;
m_ServerProcess.StartInfo = startInfo;
m_ServerProcess.OutputDataReceived += (sender, e) =>
{
Debug.Log(e.Data);
};
m_ServerProcess.ErrorDataReceived += (sender, e) =>
{
Debug.Log(e.Data);
};
bool success = m_ServerProcess.Start();
m_ServerProcess.BeginErrorReadLine();
m_ServerProcess.BeginOutputReadLine();
Assert.True(success);
Thread.Sleep(1000);
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
if (m_SignalingType == typeof(MockSignaling))
{
return;
}
m_ServerProcess?.Kill();
m_ServerProcess?.WaitForExit();
m_ServerProcess?.Dispose();
m_ServerProcess = null;
}
ISignaling CreateSignaling(Type type, SynchronizationContext mainThread)
{
if (type == typeof(WebSocketSignaling))
{
var settings = new WebSocketSignalingSettings
(
url: $"ws://localhost:{TestUtility.PortNumber}"
);
return new WebSocketSignaling(settings, mainThread);
}
if (type == typeof(HttpSignaling))
{
var settings = new HttpSignalingSettings
(
url: $"http://localhost:{TestUtility.PortNumber}",
interval: 100
);
return new HttpSignaling(settings, mainThread);
}
if (type == typeof(MockSignaling))
{
return new MockSignaling();
}
throw new ArgumentException();
}
[UnitySetUp, Timeout(1000)]
public IEnumerator UnitySetUp()
{
RTCConfiguration config = default;
RTCIceCandidate candidate_ = null;
config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
var peer1 = new RTCPeerConnection(ref config);
var peer2 = new RTCPeerConnection(ref config);
peer1.OnIceCandidate = candidate => { candidate_ = candidate; };
AudioStreamTrack track = new AudioStreamTrack();
peer1.AddTrack(track);
var op1 = peer1.CreateOffer();
yield return op1;
m_DescOffer = op1.Desc;
var op2 = peer1.SetLocalDescription(ref m_DescOffer);
yield return op2;
var op3 = peer2.SetRemoteDescription(ref m_DescOffer);
yield return op3;
var op4 = peer2.CreateAnswer();
yield return op4;
m_DescAnswer = op4.Desc;
var op5 = peer2.SetLocalDescription(ref m_DescAnswer);
yield return op5;
var op6 = peer1.SetRemoteDescription(ref m_DescAnswer);
yield return op6;
yield return new WaitUntil(() => candidate_ != null);
m_candidate = candidate_;
track.Dispose();
peer1.Close();
peer2.Close();
m_Context = SynchronizationContext.Current;
signaling1 = CreateSignaling(m_SignalingType, m_Context);
signaling2 = CreateSignaling(m_SignalingType, m_Context);
}
[TearDown]
public void TearDown()
{
signaling1.Stop();
signaling2.Stop();
m_Context = null;
}
[UnityTest, Timeout(10000)]
public IEnumerator OnConnect()
{
bool startRaised1 = false;
bool startRaised2 = false;
signaling1.OnStart += s => { startRaised1 = true; };
signaling1.Start();
signaling2.OnStart += s => { startRaised2 = true; };
signaling2.Start();
yield return new WaitUntil(() => startRaised1 && startRaised2);
const string connectionId = "12345";
string receiveConnectionId1 = null;
string receiveConnectionId2 = null;
bool receivePolite1 = false;
bool receivePolite2 = false;
bool raiseOnDestroy1 = false;
bool raiseOnDestroy2 = false;
signaling1.OnCreateConnection += (s, id, polite) =>
{
receiveConnectionId1 = id;
receivePolite1 = polite;
};
signaling1.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy1 = id == receiveConnectionId1;
};
signaling1.OpenConnection(connectionId);
yield return new WaitUntil(() => !string.IsNullOrEmpty(receiveConnectionId1));
signaling2.OnCreateConnection += (s, id, polite) =>
{
receiveConnectionId2 = id;
receivePolite2 = polite;
};
signaling2.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy2 = id == receiveConnectionId2;
};
signaling2.OpenConnection(connectionId);
yield return new WaitUntil(() => !string.IsNullOrEmpty(receiveConnectionId2));
Assert.That(receiveConnectionId1, Is.EqualTo(connectionId));
Assert.That(receiveConnectionId2, Is.EqualTo(connectionId));
Assert.That(receivePolite1, Is.False);
Assert.That(receivePolite2, Is.True);
signaling1.CloseConnection(receiveConnectionId1);
yield return new WaitUntil(() => raiseOnDestroy1 && raiseOnDestroy2);
Assert.That(raiseOnDestroy1, Is.True);
Assert.That(raiseOnDestroy2, Is.True);
signaling2.CloseConnection(receiveConnectionId2);
signaling1.Stop();
signaling2.Stop();
}
[UnityTest, Timeout(10000)]
public IEnumerator OnOffer()
{
bool startRaised1 = false;
bool startRaised2 = false;
bool offerRaised2 = false;
const string connectionId = "12345";
bool raiseOnDestroy1 = false;
bool raiseOnDestroy2 = false;
string connectionId1 = null;
string connectionId2 = null;
signaling1.OnStart += s => { startRaised1 = true; };
signaling2.OnStart += s => { startRaised2 = true; };
signaling1.Start();
signaling2.Start();
yield return new WaitUntil(() => startRaised1 && startRaised2);
signaling1.OnCreateConnection += (s, id, polite) =>
{
connectionId1 = id;
};
signaling1.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy1 = id == connectionId1;
};
signaling1.OpenConnection(connectionId);
yield return new WaitUntil(() => !string.IsNullOrEmpty(connectionId1));
signaling2.OnOffer += (s, e) => { offerRaised2 = true; };
signaling1.SendOffer(connectionId, m_DescOffer);
// Do not receive offer other signaling if not connected same sendoffer connectionId in private mode
Assert.IsFalse(offerRaised2);
signaling2.OnCreateConnection += (s, id, polite) =>
{
connectionId2 = id;
};
signaling2.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy2 = id == connectionId2;
};
signaling2.OpenConnection(connectionId);
yield return new WaitUntil(() => !string.IsNullOrEmpty(connectionId2));
Assert.That(connectionId1, Is.EqualTo(connectionId));
Assert.That(connectionId2, Is.EqualTo(connectionId));
signaling1.SendOffer(connectionId, m_DescOffer);
yield return new WaitUntil(() => offerRaised2);
signaling1.CloseConnection(connectionId1);
yield return new WaitUntil(() => raiseOnDestroy1 && raiseOnDestroy2);
Assert.That(raiseOnDestroy1, Is.True);
Assert.That(raiseOnDestroy2, Is.True);
signaling2.CloseConnection(connectionId2);
signaling1.Stop();
signaling2.Stop();
}
[UnityTest, Timeout(10000)]
public IEnumerator OnAnswer()
{
bool startRaised1 = false;
bool startRaised2 = false;
bool offerRaised = false;
bool answerRaised = false;
const string connectionId = "12345";
bool raiseOnDestroy1 = false;
bool raiseOnDestroy2 = false;
string connectionId1 = null;
string connectionId2 = null;
signaling1.OnStart += s => { startRaised1 = true; };
signaling2.OnStart += s => { startRaised2 = true; };
signaling1.Start();
signaling2.Start();
yield return new WaitUntil(() => startRaised1 && startRaised2);
signaling1.OnCreateConnection += (s, id, polite) =>
{
connectionId1 = id;
};
signaling1.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy1 = id == connectionId1;
};
signaling1.OpenConnection(connectionId);
signaling2.OnCreateConnection += (s, id, polite) =>
{
connectionId2 = id;
};
signaling2.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy2 = id == connectionId2;
};
signaling2.OpenConnection(connectionId);
yield return new WaitUntil(() =>
!string.IsNullOrEmpty(connectionId1) && !string.IsNullOrEmpty(connectionId2));
Assert.That(connectionId1, Is.EqualTo(connectionId));
Assert.That(connectionId2, Is.EqualTo(connectionId));
signaling2.OnOffer += (s, e) => { offerRaised = true; };
signaling1.SendOffer(connectionId1, m_DescOffer);
yield return new WaitUntil(() => offerRaised);
signaling1.OnAnswer += (s, e) => { answerRaised = true; };
signaling2.SendAnswer(connectionId1, m_DescAnswer);
yield return new WaitUntil(() => answerRaised);
signaling1.CloseConnection(connectionId1);
yield return new WaitUntil(() => raiseOnDestroy1 && raiseOnDestroy2);
Assert.That(raiseOnDestroy1, Is.True);
Assert.That(raiseOnDestroy2, Is.True);
signaling2.CloseConnection(connectionId2);
signaling1.Stop();
signaling2.Stop();
}
[UnityTest, Timeout(10000)]
public IEnumerator OnCandidate()
{
bool startRaised1 = false;
bool startRaised2 = false;
bool offerRaised = false;
bool answerRaised = false;
bool candidateRaised1 = false;
bool candidateRaised2 = false;
const string connectionId = "12345";
bool raiseOnDestroy1 = false;
bool raiseOnDestroy2 = false;
string connectionId1 = null;
string connectionId2 = null;
signaling1.OnStart += s => { startRaised1 = true; };
signaling2.OnStart += s => { startRaised2 = true; };
signaling1.Start();
signaling2.Start();
yield return new WaitUntil(() => startRaised1 && startRaised2);
signaling1.OnCreateConnection += (s, id, polite) =>
{
connectionId1 = id;
};
signaling1.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy1 = id == connectionId1;
};
signaling1.OpenConnection(connectionId);
signaling2.OnCreateConnection += (s, id, polite) =>
{
connectionId2 = id;
};
signaling2.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy2 = id == connectionId2;
};
signaling2.OpenConnection(connectionId);
yield return new WaitUntil(() =>
!string.IsNullOrEmpty(connectionId1) && !string.IsNullOrEmpty(connectionId2));
Assert.That(connectionId1, Is.EqualTo(connectionId));
Assert.That(connectionId2, Is.EqualTo(connectionId));
signaling2.OnOffer += (s, e) => { offerRaised = true; };
signaling1.SendOffer(connectionId1, m_DescOffer);
yield return new WaitUntil(() => offerRaised);
signaling1.OnAnswer += (s, e) => { answerRaised = true; };
signaling2.SendAnswer(connectionId1, m_DescAnswer);
yield return new WaitUntil(() => answerRaised);
signaling2.OnIceCandidate += (s, e) => { candidateRaised1 = true; };
signaling1.SendCandidate(connectionId1, m_candidate);
yield return new WaitUntil(() => candidateRaised1);
signaling1.OnIceCandidate += (s, e) => { candidateRaised2 = true; };
signaling2.SendCandidate(connectionId1, m_candidate);
yield return new WaitUntil(() => candidateRaised2);
signaling1.CloseConnection(connectionId1);
yield return new WaitUntil(() => raiseOnDestroy1 && raiseOnDestroy2);
Assert.That(raiseOnDestroy1, Is.True);
Assert.That(raiseOnDestroy2, Is.True);
signaling2.CloseConnection(connectionId2);
signaling1.Stop();
signaling2.Stop();
}
[UnityTest, Timeout(10000), LongRunning]
public IEnumerator NotReceiveOwnOfferAnswer()
{
bool startRaised1 = false;
bool startRaised2 = false;
bool offerRaised1 = false;
bool offerRaised2 = false;
bool answerRaised1 = false;
bool answerRaised2 = false;
bool candidateRaised1 = false;
bool candidateRaised2 = false;
const string connectionId = "12345";
bool raiseOnDestroy1 = false;
bool raiseOnDestroy2 = false;
string connectionId1 = null;
string connectionId2 = null;
signaling1.OnStart += s => { startRaised1 = true; };
signaling2.OnStart += s => { startRaised2 = true; };
signaling1.Start();
signaling2.Start();
yield return new WaitUntil(() => startRaised1 && startRaised2);
signaling1.OnCreateConnection += (s, id, polite) =>
{
connectionId1 = id;
};
signaling1.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy1 = id == connectionId1;
};
signaling1.OpenConnection(connectionId);
signaling2.OnCreateConnection += (s, id, polite) =>
{
connectionId2 = id;
};
signaling2.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy2 = id == connectionId2;
};
signaling2.OpenConnection(connectionId);
yield return new WaitUntil(() =>
!string.IsNullOrEmpty(connectionId1) && !string.IsNullOrEmpty(connectionId2));
Assert.That(connectionId1, Is.EqualTo(connectionId));
Assert.That(connectionId2, Is.EqualTo(connectionId));
const float resendInterval = 0.1f;
signaling1.OnOffer += (s, e) => { offerRaised1 = true; };
signaling2.OnOffer += (s, e) => { offerRaised2 = true; };
signaling1.SendOffer(connectionId1, m_DescOffer);
// check each signaling invoke onOffer
yield return new WaitForSeconds(resendInterval * 5);
Assert.That(offerRaised1, Is.False, () => "Receive own offer on private mode");
Assert.That(offerRaised2, Is.True);
signaling1.OnAnswer += (s, e) => { answerRaised1 = true; };
signaling2.OnAnswer += (s, e) => { answerRaised2 = true; };
signaling2.SendAnswer(connectionId1, m_DescAnswer);
// check each signaling invoke onAnswer
yield return new WaitForSeconds(resendInterval * 5);
Assert.That(answerRaised1, Is.True);
Assert.That(answerRaised2, Is.False, () => "Receive own answer on private mode");
signaling2.OnIceCandidate += (s, e) => { candidateRaised1 = true; };
signaling1.SendCandidate(connectionId1, m_candidate);
yield return new WaitUntil(() => candidateRaised1);
signaling1.OnIceCandidate += (s, e) => { candidateRaised2 = true; };
signaling2.SendCandidate(connectionId1, m_candidate);
yield return new WaitUntil(() => candidateRaised2);
signaling1.CloseConnection(connectionId1);
yield return new WaitUntil(() => raiseOnDestroy1 && raiseOnDestroy2);
Assert.That(raiseOnDestroy1, Is.True);
Assert.That(raiseOnDestroy2, Is.True);
signaling2.CloseConnection(connectionId2);
signaling1.Stop();
signaling2.Stop();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 814f2e203b3b488b8e189da9bec934d5
timeCreated: 1606972537

View File

@@ -0,0 +1,112 @@
using System.Linq;
using NUnit.Framework;
using Unity.RenderStreaming.RuntimeTest.Signaling;
using Unity.RenderStreaming.Signaling;
using UnityEngine;
using UnityEngine.TestTools;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Unity.RenderStreaming.RuntimeTest
{
class RenderStreamingTest : IPrebuildSetup, IPostBuildCleanup
{
private RenderStreamingSettings temp;
[SetUp]
public void SetUpTest()
{
temp = RenderStreaming.Settings;
}
[TearDown]
public void TearDown()
{
if (temp != null)
{
RenderStreaming.Settings = temp;
}
RenderStreaming.Logger = Debug.unityLogger;
}
void IPrebuildSetup.Setup()
{
#if UNITY_EDITOR
var defaultSettings = RenderStreaming.Settings;
RenderStreaming.Settings =
AssetDatabase.LoadAssetAtPath<RenderStreamingSettings>(RenderStreaming.DefaultRenderStreamingSettingsPath);
if (defaultSettings != null)
{
EditorBuildSettings.AddConfigObject(RenderStreaming.EditorBuildSettingsConfigKey, defaultSettings, true);
}
#endif
}
void IPostBuildCleanup.Cleanup()
{
#if UNITY_EDITOR
if (EditorBuildSettings.TryGetConfigObject(RenderStreaming.EditorBuildSettingsConfigKey, out RenderStreamingSettings settingsAsset))
{
RenderStreaming.Settings = settingsAsset;
}
else
{
RenderStreaming.Settings =
AssetDatabase.LoadAssetAtPath<RenderStreamingSettings>(RenderStreaming.DefaultRenderStreamingSettingsPath);
}
#endif
}
[Test]
public void Settings()
{
Assert.That(() => RenderStreaming.Settings = null, Throws.ArgumentNullException);
var settings = ScriptableObject.CreateInstance<RenderStreamingSettings>();
settings.signalingSettings = new MockSignalingSettings();
RenderStreaming.Settings = settings;
Assert.That(RenderStreaming.Settings.automaticStreaming, Is.EqualTo(settings.automaticStreaming));
Assert.That(RenderStreaming.Settings.signalingSettings, Is.EqualTo(settings.signalingSettings));
Object.DestroyImmediate(settings);
}
[Test]
public void AutomaticStreaming()
{
var settings = ScriptableObject.CreateInstance<RenderStreamingSettings>();
settings.automaticStreaming = false;
settings.signalingSettings = new WebSocketSignalingSettings();
RenderStreaming.Settings = settings;
RenderStreaming.AutomaticStreaming = true;
var automaticStreaming = Object.FindObjectOfType<AutomaticStreaming>();
Assert.That(automaticStreaming, Is.Not.Null);
RenderStreaming.AutomaticStreaming = false;
automaticStreaming = Object.FindObjectOfType<AutomaticStreaming>();
Assert.That(automaticStreaming, Is.Null);
Object.DestroyImmediate(settings);
}
[Test]
public void Logger()
{
Assert.NotNull(RenderStreaming.Logger);
Assert.AreEqual(RenderStreaming.Logger, Debug.unityLogger);
Assert.That(() => RenderStreaming.Logger = null, Throws.ArgumentNullException);
MockLogger logger = new MockLogger();
Assert.That(() => RenderStreaming.Logger = logger, Throws.Nothing);
Assert.AreEqual(logger, RenderStreaming.Logger);
Assert.That(() => RenderStreaming.Logger = Debug.unityLogger, Throws.Nothing);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c3eedb01fcca994f84ef9382259eb99
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
using NUnit.Framework;
using UnityEngine;
namespace Unity.RenderStreaming.RuntimeTest
{
class RenderpipelineTest
{
[Test]
public void Test()
{
GameObject obj = new GameObject();
obj.AddComponent<Camera>();
obj.AddComponent<RenderTextureBlitter>();
Object.DestroyImmediate(obj);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 21ed31b06b184ab7b3866fc443ca3f87
timeCreated: 1611530539

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bae3ca6280ca530439a146a559f4be8f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,341 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Unity.RenderStreaming.Signaling;
using Unity.WebRTC;
using UnityEngine;
namespace Unity.RenderStreaming.RuntimeTest.Signaling
{
internal class MockSignaling : ISignaling
{
private string participantId ;
interface IMockSignalingManager
{
Task Add(MockSignaling signaling);
Task Remove(MockSignaling signaling);
Task OpenConnection(MockSignaling signaling, string connectionId);
Task CloseConnection(MockSignaling signaling, string connectionId);
Task Offer(MockSignaling owner, DescData data);
Task Answer(MockSignaling owner, DescData data);
Task Candidate(MockSignaling owner, CandidateData data);
}
class MockPublicSignalingManager : IMockSignalingManager
{
private Dictionary<MockSignaling, HashSet<string>> signalingToConnectionLookup = new Dictionary<MockSignaling, HashSet<string>>();
private Dictionary<string, HashSet<MockSignaling>> connectionToSignalingLookup = new Dictionary<string, HashSet<MockSignaling>>();
private const int MillisecondsDelay = 10;
public async Task Add(MockSignaling signaling)
{
await Task.Delay(MillisecondsDelay);
signalingToConnectionLookup[signaling] = new HashSet<string>();
signaling.OnStart?.Invoke(signaling);
}
public async Task Remove(MockSignaling signaling)
{
await Task.Delay(MillisecondsDelay);
if (signalingToConnectionLookup.ContainsKey(signaling))
{
foreach (var connectionId in signalingToConnectionLookup[signaling])
{
foreach (var signalingToDisconnect in connectionToSignalingLookup[connectionId])
{
signalingToDisconnect.OnDestroyConnection?.Invoke(signaling, connectionId);
}
connectionToSignalingLookup.Remove(connectionId);
}
signalingToConnectionLookup.Remove(signaling);
}
}
public async Task OpenConnection(MockSignaling signaling, string connectionId)
{
await Task.Delay(MillisecondsDelay);
addToLookups(signaling, connectionId);
signaling.OnCreateConnection?.Invoke(signaling, connectionId, true);
}
public async Task CloseConnection(MockSignaling signaling, string connectionId)
{
await Task.Delay(MillisecondsDelay);
if (connectionToSignalingLookup.ContainsKey(connectionId))
{
foreach (var signalingToDisconnect in connectionToSignalingLookup[connectionId])
{
signalingToDisconnect.OnDestroyConnection?.Invoke(signalingToDisconnect, connectionId);
}
}
}
public async Task Offer(MockSignaling owner, DescData data)
{
await Task.Delay(MillisecondsDelay);
data.polite = false;
foreach (var signaling in signalingToConnectionLookup.Keys.Where(e => e != owner))
{
signaling.OnOffer?.Invoke(signaling, data);
}
}
public async Task Answer(MockSignaling owner, DescData data)
{
await Task.Delay(MillisecondsDelay);
foreach (var signaling in signalingToConnectionLookup.Keys.Where(e => e != owner))
{
addToLookups(owner, data.connectionId);
signaling.OnAnswer?.Invoke(signaling, data);
}
}
public async Task Candidate(MockSignaling owner, CandidateData data)
{
await Task.Delay(MillisecondsDelay);
foreach (var signaling in signalingToConnectionLookup.Keys.Where(e => e != owner))
{
signaling.OnIceCandidate?.Invoke(signaling, data);
}
}
private void addToLookups(MockSignaling signaling, string connectionId)
{
if (!connectionToSignalingLookup.TryGetValue(connectionId, out var signalingSet))
{
signalingSet = new HashSet<MockSignaling>();
connectionToSignalingLookup[connectionId] = signalingSet;
}
signalingSet.Add(signaling);
if (!signalingToConnectionLookup.TryGetValue(signaling, out var connectionSet))
{
connectionSet = new HashSet<string>();
signalingToConnectionLookup[signaling] = connectionSet;
}
connectionSet.Add(connectionId);
}
}
class MockPrivateSignalingManager : IMockSignalingManager
{
private Dictionary<string, List<MockSignaling>> connectionIds = new Dictionary<string, List<MockSignaling>>();
private const int MillisecondsDelay = 10;
public async Task Add(MockSignaling signaling)
{
await Task.Delay(MillisecondsDelay);
signaling.OnStart?.Invoke(signaling);
}
public async Task Remove(MockSignaling signaling)
{
await Task.Delay(MillisecondsDelay);
}
public async Task OpenConnection(MockSignaling signaling, string connectionId)
{
await Task.Delay(MillisecondsDelay);
bool peerExists = connectionIds.TryGetValue(connectionId, out var list);
if (!peerExists)
{
list = new List<MockSignaling>();
connectionIds.Add(connectionId, list);
}
list.Add(signaling);
signaling.OnCreateConnection?.Invoke(signaling, connectionId, peerExists);
}
public async Task CloseConnection(MockSignaling signaling, string connectionId)
{
await Task.Delay(MillisecondsDelay);
bool peerExists = connectionIds.TryGetValue(connectionId, out var list);
if (!peerExists || !list.Contains(signaling))
{
Debug.LogError($"{connectionId} This connection id is not used.");
}
foreach (var element in list)
{
element.OnDestroyConnection?.Invoke(element, connectionId);
}
list.Remove(signaling);
if (list.Count == 0)
{
connectionIds.Remove(connectionId);
}
}
List<MockSignaling> FindList(MockSignaling owner, string connectionId)
{
if (!connectionIds.TryGetValue(connectionId, out var list))
{
return null;
}
list = list.Where(e => e != owner).ToList();
if (list.Count == 0)
{
return null;
}
return list;
}
public async Task Offer(MockSignaling owner, DescData data)
{
await Task.Delay(MillisecondsDelay);
var list = FindList(owner, data.connectionId);
if (list == null)
{
Debug.LogWarning($"{data.connectionId} This connection id is not ready other session.");
return;
}
data.polite = true;
foreach (var signaling in list.Where(x => x != owner))
{
signaling.OnOffer?.Invoke(signaling, data);
}
}
public async Task Answer(MockSignaling owner, DescData data)
{
await Task.Delay(MillisecondsDelay);
var list = FindList(owner, data.connectionId);
if (list == null)
{
Debug.LogWarning($"{data.connectionId} This connection id is not ready other session.");
return;
}
foreach (var signaling in list.Where(x => x != owner))
{
signaling.OnAnswer?.Invoke(signaling, data);
}
}
public async Task Candidate(MockSignaling owner, CandidateData data)
{
await Task.Delay(MillisecondsDelay);
var list = FindList(owner, data.connectionId);
if (list == null)
{
Debug.LogWarning($"{data.connectionId} This connection id is not ready other session.");
return;
}
foreach (var signaling in list.Where(e => e != owner))
{
signaling.OnIceCandidate?.Invoke(signaling, data);
}
}
}
private static IMockSignalingManager manager = null;
public static void Reset(bool enablePrivateMode)
{
if (enablePrivateMode)
{
manager = new MockPrivateSignalingManager();
}
else
{
manager = new MockPublicSignalingManager();
}
}
public string Url { get { return string.Empty; } }
public float Interval { get { return 0.1f; } }
static MockSignaling()
{
manager = new MockPublicSignalingManager();
}
public MockSignaling(SignalingSettings settings = null, SynchronizationContext context = null)
{
}
public void Start()
{
manager.Add(this);
}
public void Stop()
{
manager.Remove(this);
}
public event OnStartHandler OnStart;
public event OnConnectHandler OnCreateConnection;
public event OnDisconnectHandler OnDestroyConnection;
public event OnOfferHandler OnOffer;
public event OnAnswerHandler OnAnswer;
public event OnIceCandidateHandler OnIceCandidate;
public void OpenConnection(string connectionId)
{
if (string.IsNullOrEmpty(connectionId))
throw new ArgumentException("connectionId is null or empty.");
manager.OpenConnection(this, connectionId);
}
public void CloseConnection(string connectionId)
{
if (string.IsNullOrEmpty(connectionId))
throw new ArgumentException("connectionId is null or empty.");
manager.CloseConnection(this, connectionId);
}
public void SendOffer(string connectionId, RTCSessionDescription offer)
{
if (string.IsNullOrEmpty(connectionId))
throw new ArgumentException("connectionId is null or empty.");
DescData data = new DescData
{
connectionId = connectionId,
sdp = offer.sdp
};
manager.Offer(this, data);
}
public void SendAnswer(string connectionId, RTCSessionDescription answer)
{
if (string.IsNullOrEmpty(connectionId))
throw new ArgumentException("connectionId is null or empty.");
DescData data = new DescData
{
connectionId = connectionId,
sdp = answer.sdp
};
manager.Answer(this, data);
}
public void SendCandidate(string connectionId, RTCIceCandidate candidate)
{
if (string.IsNullOrEmpty(connectionId))
throw new ArgumentException("connectionId is null or empty.");
CandidateData data = new CandidateData
{
connectionId = connectionId,
candidate = candidate.Candidate,
sdpMid = candidate.SdpMid,
sdpMLineIndex = candidate.SdpMLineIndex.GetValueOrDefault()
};
manager.Candidate(this, data);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b720a0c9e77c7a94a918c4148cd4f2ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace Unity.RenderStreaming.RuntimeTest.Signaling
{
internal class MockSignalingSettings : SignalingSettings
{
public override Type signalingClass => typeof(MockSignaling);
public override IReadOnlyCollection<IceServer> iceServers => Array.Empty<IceServer>();
public override bool ParseArguments(string[] arguments)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 957479287b0c4578bf22c6d3f8628642
timeCreated: 1674444139

View File

@@ -0,0 +1,306 @@
using System;
using NUnit.Framework;
using Unity.WebRTC;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
namespace Unity.RenderStreaming.RuntimeTest
{
class CreatedConnectionHandlerTest : MonoBehaviour,
IMonoBehaviourTest, ICreatedConnectionHandler
{
public bool IsTestFinished { get; private set; }
public SignalingEventData Data { get; private set; }
public void OnCreatedConnection(SignalingEventData data)
{
IsTestFinished = true;
this.Data = data;
}
}
class DeletedConnectionHandlerTest : MonoBehaviour,
IMonoBehaviourTest, IDeletedConnectionHandler
{
public bool IsTestFinished { get; private set; }
public SignalingEventData Data { get; private set; }
public void OnDeletedConnection(SignalingEventData data)
{
IsTestFinished = true;
this.Data = data;
}
}
class ConnectHandlerTest : MonoBehaviour,
IMonoBehaviourTest, IConnectHandler
{
public bool IsTestFinished { get; private set; }
public SignalingEventData Data { get; private set; }
public void OnConnect(SignalingEventData data)
{
IsTestFinished = true;
this.Data = data;
}
}
class DisconnectHandlerTest : MonoBehaviour,
IMonoBehaviourTest, IDisconnectHandler
{
public bool IsTestFinished { get; private set; }
public SignalingEventData Data { get; private set; }
public void OnDisconnect(SignalingEventData data)
{
IsTestFinished = true;
this.Data = data;
}
}
class OfferHandlerTest : MonoBehaviour,
IMonoBehaviourTest, IOfferHandler
{
public bool IsTestFinished { get; private set; }
public SignalingEventData Data { get; private set; }
public void OnOffer(SignalingEventData data)
{
IsTestFinished = true;
this.Data = data;
}
}
class AnswerHandlerTest : MonoBehaviour,
IMonoBehaviourTest, IAnswerHandler
{
public bool IsTestFinished { get; private set; }
public SignalingEventData Data { get; private set; }
public void OnAnswer(SignalingEventData data)
{
IsTestFinished = true;
this.Data = data;
}
}
class AddReceiverHandlerTest : MonoBehaviour,
IMonoBehaviourTest, IAddReceiverHandler
{
public bool IsTestFinished { get; private set; }
public SignalingEventData Data { get; private set; }
public void OnAddReceiver(SignalingEventData data)
{
IsTestFinished = true;
this.Data = data;
}
}
class AddChannelHandlerTest : MonoBehaviour,
IMonoBehaviourTest, IAddChannelHandler
{
public bool IsTestFinished { get; private set; }
public SignalingEventData Data { get; private set; }
public void OnAddChannel(SignalingEventData data)
{
IsTestFinished = true;
this.Data = data;
}
}
/// <summary>
/// note:: Moq is not supported IL2CPP platform,
/// this class should be replaced Moq `Raise` method.
/// </summary>
class MockDelegate : IRenderStreamingDelegate
{
public event Action onStart;
public event Action<string> onCreatedConnection;
public event Action<string> onDeletedConnection;
public event Action<string, string> onGotOffer;
public event Action<string, string> onGotAnswer;
public event Action<string> onConnect;
public event Action<string> onDisconnect;
public event Action<string, RTCRtpTransceiver> onAddTransceiver;
public event Action<string, RTCDataChannel> onAddChannel;
public void RaiseOnStart()
{
onStart?.Invoke();
}
public void RaiseOnCreatedConnection(string connectionId)
{
onCreatedConnection?.Invoke(connectionId);
}
public void RaiseOnDeletedConnection(string connectionId)
{
onDeletedConnection?.Invoke(connectionId);
}
public void RaiseOnGotOffer(string connectionId, string sdp)
{
onGotOffer?.Invoke(connectionId, sdp);
}
public void RaiseOnGotAnswer(string connectionId, string sdp)
{
onGotAnswer?.Invoke(connectionId, sdp);
}
public void RaiseOnConnect(string connectionId)
{
onConnect?.Invoke(connectionId);
}
public void RaiseOnDisconnect(string connectionId)
{
onDisconnect?.Invoke(connectionId);
}
public void RaiseOnAddTransceiver(string connectionId, RTCRtpTransceiver transceiver)
{
onAddTransceiver?.Invoke(connectionId, transceiver);
}
public void RaiseOnAddChannel(string connectionId, RTCDataChannel channel)
{
onAddChannel?.Invoke(connectionId, channel);
}
}
[UnityPlatform(exclude = new[] { RuntimePlatform.OSXEditor, RuntimePlatform.OSXPlayer, RuntimePlatform.LinuxEditor, RuntimePlatform.LinuxPlayer })]
class SignalingEventProviderTest
{
private EventSystem m_eventSystem;
private SignalingEventProvider m_provider;
private MockDelegate _mDelegate;
[OneTimeSetUp]
public void OneTimeSetUp()
{
m_eventSystem = new GameObject("EventSystem").AddComponent<EventSystem>();
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
UnityEngine.Object.DestroyImmediate(m_eventSystem);
}
[SetUp]
public void SetUp()
{
_mDelegate = new MockDelegate();
m_provider = new SignalingEventProvider(_mDelegate);
}
[Test]
public void Subscribe()
{
var test = new MonoBehaviourTest<CreatedConnectionHandlerTest>();
Assert.That(m_provider.Subscribe(test.component), Is.True);
// return false if it is already registered.
Assert.That(m_provider.Subscribe(test.component), Is.False);
Assert.That(m_provider.Unsubscribe(test.component), Is.True);
// return false if it is not found.
Assert.That(m_provider.Unsubscribe(test.component), Is.False);
}
[Test]
public void OnCreatedConnection()
{
var connectionId = "12345";
var test = new MonoBehaviourTest<CreatedConnectionHandlerTest>();
m_provider.Subscribe(test.component);
_mDelegate.RaiseOnCreatedConnection(connectionId);
Assert.That(test.component.IsTestFinished, Is.True);
Assert.That(test.component.Data.connectionId, Is.EqualTo(connectionId));
UnityEngine.Object.DestroyImmediate(test.gameObject);
}
[Test]
public void OnDeletedConnection()
{
var connectionId = "12345";
var test = new MonoBehaviourTest<DeletedConnectionHandlerTest>();
m_provider.Subscribe(test.component);
_mDelegate.RaiseOnDeletedConnection(connectionId);
Assert.That(test.component.IsTestFinished, Is.True);
Assert.That(test.component.Data.connectionId, Is.EqualTo(connectionId));
UnityEngine.Object.DestroyImmediate(test.gameObject);
}
[Test]
public void OnConnect()
{
var connectionId = "12345";
var test = new MonoBehaviourTest<ConnectHandlerTest>();
m_provider.Subscribe(test.component);
_mDelegate.RaiseOnConnect(connectionId);
Assert.That(test.component.IsTestFinished, Is.True);
Assert.That(test.component.Data.connectionId, Is.EqualTo(connectionId));
UnityEngine.Object.DestroyImmediate(test.gameObject);
}
[Test]
public void OnDisconnect()
{
var connectionId = "12345";
var test = new MonoBehaviourTest<DisconnectHandlerTest>();
m_provider.Subscribe(test.component);
_mDelegate.RaiseOnDisconnect(connectionId);
Assert.That(test.component.IsTestFinished, Is.True);
Assert.That(test.component.Data.connectionId, Is.EqualTo(connectionId));
UnityEngine.Object.DestroyImmediate(test.gameObject);
}
[Test]
public void OnOffer()
{
var connectionId = "12345";
var sdp = "this is sdp";
var test = new MonoBehaviourTest<OfferHandlerTest>();
m_provider.Subscribe(test.component);
_mDelegate.RaiseOnGotOffer(connectionId, sdp);
Assert.That(test.component.IsTestFinished, Is.True);
Assert.That(test.component.Data.connectionId, Is.EqualTo(connectionId));
Assert.That(test.component.Data.sdp, Is.EqualTo(sdp));
UnityEngine.Object.DestroyImmediate(test.gameObject);
}
[Test]
public void OnAnswer()
{
var connectionId = "12345";
var sdp = "this is sdp";
var test = new MonoBehaviourTest<AnswerHandlerTest>();
m_provider.Subscribe(test.component);
_mDelegate.RaiseOnGotAnswer(connectionId, sdp);
Assert.That(test.component.IsTestFinished, Is.True);
Assert.That(test.component.Data.connectionId, Is.EqualTo(connectionId));
Assert.That(test.component.Data.sdp, Is.EqualTo(sdp));
UnityEngine.Object.DestroyImmediate(test.gameObject);
}
[Test]
public void OnAddReceiver()
{
var connectionId = "12345";
// todo:: create a receiver instance
var test = new MonoBehaviourTest<AddReceiverHandlerTest>();
m_provider.Subscribe(test.component);
_mDelegate.RaiseOnAddTransceiver(connectionId, null);
Assert.That(test.component.IsTestFinished, Is.True);
Assert.That(test.component.Data.connectionId, Is.EqualTo(connectionId));
Assert.That(test.component.Data.transceiver, Is.Null);
UnityEngine.Object.DestroyImmediate(test.gameObject);
}
[Test]
public void OnAddChannel()
{
var connectionId = "12345";
var peer = new RTCPeerConnection();
var channel = peer.CreateDataChannel("test");
var test = new MonoBehaviourTest<AddChannelHandlerTest>();
m_provider.Subscribe(test.component);
_mDelegate.RaiseOnAddChannel(connectionId, channel);
Assert.That(test.component.IsTestFinished, Is.True);
Assert.That(test.component.Data.connectionId, Is.EqualTo(connectionId));
Assert.That(test.component.Data.channel, Is.EqualTo(channel));
UnityEngine.Object.DestroyImmediate(test.gameObject);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6fd8d9ba65d1532458f663ad6806c85a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1176e6020d27027459cecf155a99d46f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,897 @@
using System;
using System.Collections;
using System.Linq;
using NUnit.Framework;
using Unity.RenderStreaming.RuntimeTest.Signaling;
using Unity.WebRTC;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.RenderStreaming.RuntimeTest
{
enum TestMode
{
PrivateMode,
PublicMode
}
static class SignalingManagerInternalExtension
{
public static RTCRtpTransceiver AddSenderTrack(this SignalingManagerInternal target, string connectionId, MediaStreamTrack track)
{
RTCRtpTransceiverInit init = new RTCRtpTransceiverInit() { direction = RTCRtpTransceiverDirection.SendOnly };
return target.AddTransceiver(connectionId, track, init);
}
}
class SignalingManagerInternalTest
{
class MyMonoBehaviourTest : MonoBehaviour, IMonoBehaviourTest
{
public bool IsTestFinished
{
get { return true; }
}
}
private MonoBehaviourTest<MyMonoBehaviourTest> test;
[SetUp]
public void SetUp()
{
test = new MonoBehaviourTest<MyMonoBehaviourTest>();
}
[TearDown]
public void TearDown()
{
test.component.StopAllCoroutines();
UnityEngine.Object.Destroy(test.gameObject);
}
// workaround: More time for SetDescription process
const float ResendOfferInterval = 3f;
private RenderStreamingDependencies CreateDependencies()
{
return new RenderStreamingDependencies
{
signaling = new MockSignaling(),
config = new RTCConfiguration
{
iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } },
},
startCoroutine = test.component.StartCoroutine,
stopCoroutine = test.component.StopCoroutine,
resentOfferInterval = ResendOfferInterval,
};
}
[TestCase(TestMode.PublicMode, ExpectedResult = null)]
[TestCase(TestMode.PrivateMode, ExpectedResult = null)]
[UnityTest, Timeout(10000)]
public IEnumerator Construct(TestMode mode)
{
MockSignaling.Reset(mode == TestMode.PrivateMode);
var dependencies = CreateDependencies();
var target = new SignalingManagerInternal(ref dependencies);
bool isStarted = false;
target.onStart += () => { isStarted = true; };
yield return new WaitUntil(() => isStarted);
Assert.That(isStarted, Is.True);
target.Dispose();
}
[TestCase(TestMode.PublicMode, ExpectedResult = null)]
[TestCase(TestMode.PrivateMode, ExpectedResult = null)]
[UnityTest, Timeout(10000)]
public IEnumerator ConstructMultiple(TestMode mode)
{
MockSignaling.Reset(mode == TestMode.PrivateMode);
var dependencies1 = CreateDependencies();
var dependencies2 = CreateDependencies();
var target1 = new SignalingManagerInternal(ref dependencies1);
var target2 = new SignalingManagerInternal(ref dependencies2);
bool isStarted1 = false;
bool isStarted2 = false;
target1.onStart += () => { isStarted1 = true; };
target2.onStart += () => { isStarted2 = true; };
yield return new WaitUntil(() => isStarted1);
yield return new WaitUntil(() => isStarted2);
Assert.That(isStarted1, Is.True);
Assert.That(isStarted2, Is.True);
target1.Dispose();
target2.Dispose();
}
[TestCase(TestMode.PublicMode, ExpectedResult = null)]
[TestCase(TestMode.PrivateMode, ExpectedResult = null)]
[UnityTest, Timeout(10000)]
public IEnumerator OpenConnection(TestMode mode)
{
MockSignaling.Reset(mode == TestMode.PrivateMode);
var dependencies = CreateDependencies();
var target = new SignalingManagerInternal(ref dependencies);
bool isStarted = false;
target.onStart += () => { isStarted = true; };
yield return new WaitUntil(() => isStarted);
Assert.That(isStarted, Is.True);
string connectionId = "12345";
Assert.That(target.ExistConnection(connectionId), Is.False);
target.CreateConnection(connectionId);
bool isCreatedConnection = false;
target.onCreatedConnection += _ => { isCreatedConnection = true; };
yield return new WaitUntil(() => isCreatedConnection);
Assert.That(isCreatedConnection, Is.True);
Assert.That(target.ExistConnection(connectionId), Is.True);
target.DeleteConnection(connectionId);
bool isDeletedConnection = false;
target.onDeletedConnection += _ => { isDeletedConnection = true; };
yield return new WaitUntil(() => isDeletedConnection);
Assert.That(isDeletedConnection, Is.True);
Assert.That(target.ExistConnection(connectionId), Is.False);
target.Dispose();
}
[TestCase(TestMode.PublicMode, ExpectedResult = null)]
[TestCase(TestMode.PrivateMode, ExpectedResult = null)]
[UnityTest, Timeout(10000)]
public IEnumerator OpenConnectionThrowException(TestMode mode)
{
MockSignaling.Reset(mode == TestMode.PrivateMode);
var dependencies = CreateDependencies();
var target = new SignalingManagerInternal(ref dependencies);
bool isStarted = false;
target.onStart += () => { isStarted = true; };
yield return new WaitUntil(() => isStarted);
Assert.That(isStarted, Is.True);
Assert.That(() => target.CreateConnection(null), Throws.TypeOf<ArgumentException>());
Assert.That(() => target.CreateConnection(string.Empty), Throws.TypeOf<ArgumentException>());
target.Dispose();
}
//todo:: crash in dispose process on standalone linux
[TestCase(TestMode.PublicMode, ExpectedResult = null)]
[TestCase(TestMode.PrivateMode, ExpectedResult = null)]
[UnityTest, Timeout(10000)]
[UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer })]
public IEnumerator AddTrack(TestMode mode)
{
MockSignaling.Reset(mode == TestMode.PrivateMode);
var dependencies = CreateDependencies();
var target = new SignalingManagerInternal(ref dependencies);
bool isStarted = false;
target.onStart += () => { isStarted = true; };
yield return new WaitUntil(() => isStarted);
Assert.That(isStarted, Is.True);
var connectionId = "12345";
bool isCreatedConnection = false;
target.onCreatedConnection += _ => { isCreatedConnection = true; };
target.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection);
Assert.That(isCreatedConnection, Is.True);
Assert.That(target.GetTransceivers(connectionId).Count(), Is.EqualTo(0));
var camObj = new GameObject("Camera");
var camera = camObj.AddComponent<Camera>();
VideoStreamTrack track = camera.CaptureStreamTrack(1280, 720);
var transceiver = target.AddSenderTrack(connectionId, track);
Assert.That(transceiver.Direction, Is.EqualTo(RTCRtpTransceiverDirection.SendOnly));
Assert.That(target.GetTransceivers(connectionId).Count(), Is.EqualTo(1));
target.RemoveSenderTrack(connectionId, track);
bool isDeletedConnection = false;
target.onDeletedConnection += _ => { isDeletedConnection = true; };
target.DeleteConnection(connectionId);
yield return new WaitUntil(() => isDeletedConnection);
Assert.That(isDeletedConnection, Is.True);
target.Dispose();
track.Dispose();
UnityEngine.Object.Destroy(camObj);
}
[TestCase(TestMode.PublicMode, ExpectedResult = null)]
[TestCase(TestMode.PrivateMode, ExpectedResult = null)]
[UnityTest]
public IEnumerator AddTrackThrowException(TestMode mode)
{
MockSignaling.Reset(mode == TestMode.PrivateMode);
var dependencies = CreateDependencies();
var target = new SignalingManagerInternal(ref dependencies);
bool isStarted = false;
target.onStart += () => { isStarted = true; };
yield return new WaitUntil(() => isStarted);
Assert.That(isStarted, Is.True);
var connectionId = "12345";
target.CreateConnection(connectionId);
bool isCreatedConnection = false;
target.onCreatedConnection += _ => { isCreatedConnection = true; };
yield return new WaitUntil(() => isCreatedConnection);
Assert.That(isCreatedConnection, Is.True);
Assert.That(() => target.AddSenderTrack(null, null), Throws.TypeOf<ArgumentNullException>());
Assert.That(() => target.AddSenderTrack(connectionId, null), Throws.TypeOf<ArgumentNullException>());
Assert.That(() => target.RemoveSenderTrack(null, null), Throws.TypeOf<ArgumentNullException>());
Assert.That(() => target.RemoveSenderTrack(connectionId, null), Throws.TypeOf<InvalidOperationException>());
target.DeleteConnection(connectionId);
bool isDeletedConnection = false;
target.onDeletedConnection += _ => { isDeletedConnection = true; };
yield return new WaitUntil(() => isDeletedConnection);
Assert.That(isDeletedConnection, Is.True);
target.Dispose();
}
//todo:: crash in dispose process on standalone linux
[TestCase(TestMode.PublicMode, ExpectedResult = null)]
[TestCase(TestMode.PrivateMode, ExpectedResult = null)]
[UnityTest, Timeout(10000)]
[UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer })]
public IEnumerator AddTrackMultiple(TestMode mode)
{
MockSignaling.Reset(mode == TestMode.PrivateMode);
var dependencies = CreateDependencies();
var target = new SignalingManagerInternal(ref dependencies);
bool isStarted = false;
target.onStart += () => { isStarted = true; };
yield return new WaitUntil(() => isStarted);
Assert.That(isStarted, Is.True);
var connectionId = "12345";
target.CreateConnection(connectionId);
bool isCreatedConnection = false;
target.onCreatedConnection += _ => { isCreatedConnection = true; };
yield return new WaitUntil(() => isCreatedConnection);
Assert.That(isCreatedConnection, Is.True);
var camObj = new GameObject("Camera");
var camera = camObj.AddComponent<Camera>();
VideoStreamTrack track = camera.CaptureStreamTrack(1280, 720);
var transceiver1 = target.AddSenderTrack(connectionId, track);
Assert.That(transceiver1.Direction, Is.EqualTo(RTCRtpTransceiverDirection.SendOnly));
var camObj2 = new GameObject("Camera2");
var camera2 = camObj2.AddComponent<Camera>();
VideoStreamTrack track2 = camera2.CaptureStreamTrack(1280, 720);
var transceiver2 = target.AddSenderTrack(connectionId, track2);
Assert.That(transceiver2.Direction, Is.EqualTo(RTCRtpTransceiverDirection.SendOnly));
target.DeleteConnection(connectionId);
bool isDeletedConnection = false;
target.onDeletedConnection += _ => { isDeletedConnection = true; };
yield return new WaitUntil(() => isDeletedConnection);
Assert.That(isDeletedConnection, Is.True);
target.Dispose();
track.Dispose();
track2.Dispose();
UnityEngine.Object.Destroy(camObj);
UnityEngine.Object.Destroy(camObj2);
}
[TestCase(TestMode.PublicMode, ExpectedResult = null)]
[TestCase(TestMode.PrivateMode, ExpectedResult = null)]
[UnityTest, Timeout(10000)]
public IEnumerator CreateChannel(TestMode mode)
{
MockSignaling.Reset(mode == TestMode.PrivateMode);
var dependencies = CreateDependencies();
var target = new SignalingManagerInternal(ref dependencies);
bool isStarted = false;
target.onStart += () => { isStarted = true; };
yield return new WaitUntil(() => isStarted);
Assert.That(isStarted, Is.True);
var connectionId = "12345";
target.CreateConnection(connectionId);
bool isCreatedConnection = false;
target.onCreatedConnection += _ => { isCreatedConnection = true; };
yield return new WaitUntil(() => isCreatedConnection);
string channelName = "test";
var channel = target.CreateChannel(connectionId, channelName);
Assert.That(channel.Label, Is.EqualTo(channelName));
target.DeleteConnection(connectionId);
bool isDeletedConnection = false;
target.onDeletedConnection += _ => { isDeletedConnection = true; };
yield return new WaitUntil(() => isDeletedConnection);
Assert.That(isDeletedConnection, Is.True);
target.Dispose();
channel.Dispose();
}
//todo:: crash in dispose process on standalone linux
[UnityTest, Timeout(10000)]
[UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.Android })]
public IEnumerator OnAddReceiverPrivateMode()
{
MockSignaling.Reset(true);
var dependencies1 = CreateDependencies();
var dependencies2 = CreateDependencies();
var target1 = new SignalingManagerInternal(ref dependencies1);
var target2 = new SignalingManagerInternal(ref dependencies2);
bool isStarted1 = false;
bool isStarted2 = false;
target1.onStart += () => { isStarted1 = true; };
target2.onStart += () => { isStarted2 = true; };
yield return new WaitUntil(() => isStarted1 && isStarted2);
Assert.That(isStarted1, Is.True);
Assert.That(isStarted2, Is.True);
bool isCreatedConnection1 = false;
bool isCreatedConnection2 = false;
target1.onCreatedConnection += _ => { isCreatedConnection1 = true; };
target2.onCreatedConnection += _ => { isCreatedConnection2 = true; };
var connectionId = "12345";
// target1 is Receiver in private mode
target1.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection1);
Assert.That(isCreatedConnection1, Is.True);
// target2 is sender in private mode
target2.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection2);
Assert.That(isCreatedConnection2, Is.True);
bool isAddReceiver1 = false;
bool isGotAnswer2 = false;
target1.onAddTransceiver += (_, receiver) => { isAddReceiver1 = true; };
target1.onGotOffer += (_, sdp) => { target1.SendAnswer(connectionId); };
target2.onGotAnswer += (_, sdp) => { isGotAnswer2 = true; };
var camObj = new GameObject("Camera");
var camera = camObj.AddComponent<Camera>();
VideoStreamTrack track = camera.CaptureStreamTrack(1280, 720);
// send offer automatically after adding a Track
var transceiver = target2.AddSenderTrack(connectionId, track);
Assert.That(transceiver, Is.Not.Null);
Assert.That(transceiver.Direction, Is.EqualTo(RTCRtpTransceiverDirection.SendOnly));
yield return new WaitUntil(() => isAddReceiver1 && isGotAnswer2);
Assert.That(isAddReceiver1, Is.True);
Assert.That(isGotAnswer2, Is.True);
target1.DeleteConnection(connectionId);
target2.DeleteConnection(connectionId);
bool isDeletedConnection1 = false;
bool isDeletedConnection2 = false;
target1.onDeletedConnection += _ => { isDeletedConnection1 = true; };
target2.onDeletedConnection += _ => { isDeletedConnection2 = true; };
yield return new WaitUntil(() => isDeletedConnection1 && isDeletedConnection2);
Assert.That(isDeletedConnection1, Is.True);
Assert.That(isDeletedConnection2, Is.True);
target1.Dispose();
target2.Dispose();
track.Dispose();
UnityEngine.Object.Destroy(camObj);
}
//todo:: crash in dispose process on standalone linux
[UnityTest, Timeout(10000)]
[UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.Android })]
public IEnumerator OnAddReceiverPublicMode()
{
MockSignaling.Reset(false);
var dependencies1 = CreateDependencies();
var dependencies2 = CreateDependencies();
var target1 = new SignalingManagerInternal(ref dependencies1);
var target2 = new SignalingManagerInternal(ref dependencies2);
bool isStarted1 = false;
bool isStarted2 = false;
target1.onStart += () => { isStarted1 = true; };
target2.onStart += () => { isStarted2 = true; };
yield return new WaitUntil(() => isStarted1 && isStarted2);
Assert.That(isStarted1, Is.True);
Assert.That(isStarted2, Is.True);
bool isCreatedConnection1 = false;
bool isOnGotOffer2 = false;
target1.onCreatedConnection += _ => { isCreatedConnection1 = true; };
target2.onGotOffer += (_, sdp) => { isOnGotOffer2 = true; };
var connectionId = "12345";
// target1 is Receiver in public mode
target1.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection1);
Assert.That(isCreatedConnection1, Is.True);
RTCRtpTransceiverInit init = new RTCRtpTransceiverInit()
{
direction = RTCRtpTransceiverDirection.RecvOnly
};
target1.AddTransceiver(connectionId, TrackKind.Video, init);
// target2 is sender in private mode
yield return new WaitUntil(() => isOnGotOffer2);
Assert.That(isOnGotOffer2, Is.True);
bool isAddReceiver1 = false;
bool isGotAnswer1 = false;
target1.onAddTransceiver += (_, receiver) => { isAddReceiver1 = true; };
target1.onGotAnswer += (_, sdp) => { isGotAnswer1 = true; };
var camObj = new GameObject("Camera");
var camera = camObj.AddComponent<Camera>();
VideoStreamTrack track = camera.CaptureStreamTrack(1280, 720);
var transceiver2 = target2.AddSenderTrack(connectionId, track);
Assert.That(transceiver2.Direction, Is.EqualTo(RTCRtpTransceiverDirection.SendOnly));
target2.SendAnswer(connectionId);
yield return new WaitUntil(() => isAddReceiver1 & isGotAnswer1);
target1.DeleteConnection(connectionId);
target2.DeleteConnection(connectionId);
bool isDeletedConnection1 = false;
bool isDeletedConnection2 = false;
target1.onDeletedConnection += _ => { isDeletedConnection1 = true; };
target2.onDeletedConnection += _ => { isDeletedConnection2 = true; };
yield return new WaitUntil(() => isDeletedConnection1 && isDeletedConnection2);
Assert.That(isDeletedConnection1, Is.True);
Assert.That(isDeletedConnection2, Is.True);
target1.Dispose();
target2.Dispose();
track.Dispose();
UnityEngine.Object.DestroyImmediate(camObj);
}
[UnityTest, Timeout(10000)]
public IEnumerator OnAddChannelPrivateMode()
{
MockSignaling.Reset(true);
var dependencies1 = CreateDependencies();
var dependencies2 = CreateDependencies();
var target1 = new SignalingManagerInternal(ref dependencies1);
var target2 = new SignalingManagerInternal(ref dependencies2);
bool isStarted1 = false;
bool isStarted2 = false;
target1.onStart += () => { isStarted1 = true; };
target2.onStart += () => { isStarted2 = true; };
yield return new WaitUntil(() => isStarted1 && isStarted2);
bool isCreatedConnection1 = false;
bool isCreatedConnection2 = false;
target1.onCreatedConnection += _ => { isCreatedConnection1 = true; };
target2.onCreatedConnection += _ => { isCreatedConnection2 = true; };
var connectionId = "12345";
// target1 is Receiver in private mode
target1.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection1);
// target2 is sender in private mode
target2.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection2);
bool isAddChannel1 = false;
bool isGotOffer1 = false;
bool isGotAnswer2 = false;
target1.onAddChannel += (_, _channel) => { isAddChannel1 = true; };
target1.onGotOffer += (_, sdp) => { isGotOffer1 = true; };
target2.onGotAnswer += (_, sdp) => { isGotAnswer2 = true; };
// send offer automatically after creating channel
RTCDataChannel channel = target2.CreateChannel(connectionId, "test");
Assert.That(channel, Is.Not.Null);
yield return new WaitUntil(() => isGotOffer1);
Assert.That(isGotOffer1, Is.True);
target1.SendAnswer(connectionId);
yield return new WaitUntil(() => isAddChannel1 && isGotAnswer2);
Assert.That(isAddChannel1, Is.True);
Assert.That(isGotAnswer2, Is.True);
target1.DeleteConnection(connectionId);
target2.DeleteConnection(connectionId);
bool isDeletedConnection1 = false;
bool isDeletedConnection2 = false;
target1.onDeletedConnection += _ => { isDeletedConnection1 = true; };
target2.onDeletedConnection += _ => { isDeletedConnection2 = true; };
yield return new WaitUntil(() => isDeletedConnection1 && isDeletedConnection2);
Assert.That(isDeletedConnection1, Is.True);
Assert.That(isDeletedConnection2, Is.True);
target1.Dispose();
target2.Dispose();
}
[UnityTest, Timeout(10000), LongRunning]
public IEnumerator SendOfferThrowExceptionPrivateMode()
{
MockSignaling.Reset(true);
var dependencies1 = CreateDependencies();
var dependencies2 = CreateDependencies();
var target1 = new SignalingManagerInternal(ref dependencies1);
var target2 = new SignalingManagerInternal(ref dependencies2);
bool isStarted1 = false;
bool isStarted2 = false;
target1.onStart += () => { isStarted1 = true; };
target2.onStart += () => { isStarted2 = true; };
yield return new WaitUntil(() => isStarted1 && isStarted2);
bool isCreatedConnection1 = false;
bool isCreatedConnection2 = false;
target1.onCreatedConnection += _ => { isCreatedConnection1 = true; };
target2.onCreatedConnection += _ => { isCreatedConnection2 = true; };
var connectionId = "12345";
// target1 is Receiver in private mode
target1.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection1);
// target2 is sender in private mode
target2.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection2);
bool isGotOffer1 = false;
bool isGotAnswer2 = false;
target1.onGotOffer += (_, sdp) => { isGotOffer1 = true; };
target2.onGotAnswer += (_, sdp) => { isGotAnswer2 = true; };
target2.SendOffer(connectionId);
// each peer are not stable, signaling process not complete.
yield return new WaitUntil(() => isGotOffer1);
Assert.That(target1.IsStable(connectionId), Is.False);
Assert.That(target2.IsStable(connectionId), Is.False);
Assert.That(() => target1.SendOffer(connectionId), Throws.TypeOf<InvalidOperationException>());
target1.SendAnswer(connectionId);
yield return new WaitUntil(() => isGotAnswer2);
Assert.That(isGotAnswer2, Is.True);
// If target1 processes resent Offer from target2, target1 is not stable.
Assert.That(target2.IsStable(connectionId), Is.True);
target1.DeleteConnection(connectionId);
target2.DeleteConnection(connectionId);
bool isDeletedConnection1 = false;
bool isDeletedConnection2 = false;
target1.onDeletedConnection += _ => { isDeletedConnection1 = true; };
target2.onDeletedConnection += _ => { isDeletedConnection2 = true; };
yield return new WaitUntil(() => isDeletedConnection1 && isDeletedConnection2);
Assert.That(isDeletedConnection1, Is.True);
Assert.That(isDeletedConnection2, Is.True);
target1.Dispose();
target2.Dispose();
}
[UnityTest, Timeout(30000), LongRunning]
public IEnumerator SwapTransceiverPrivateMode()
{
MockSignaling.Reset(true);
var dependencies1 = CreateDependencies();
var dependencies2 = CreateDependencies();
var target1 = new SignalingManagerInternal(ref dependencies1);
var target2 = new SignalingManagerInternal(ref dependencies2);
bool isStarted1 = false;
bool isStarted2 = false;
target1.onStart += () => { isStarted1 = true; };
target2.onStart += () => { isStarted2 = true; };
yield return new WaitUntil(() => isStarted1 && isStarted2);
Assert.That(isStarted1, Is.True);
Assert.That(isStarted2, Is.True);
bool isCreatedConnection1 = false;
bool isCreatedConnection2 = false;
target1.onCreatedConnection += _ => { isCreatedConnection1 = true; };
target2.onCreatedConnection += _ => { isCreatedConnection2 = true; };
var connectionId = "12345";
// target1 has impolite peer (request first)
target1.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection1);
Assert.That(isCreatedConnection1, Is.True);
// target2 has polite peer (request second)
target2.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection2);
Assert.That(isCreatedConnection2, Is.True);
bool isGotOffer1 = false;
bool isGotOffer2 = false;
bool isGotAnswer1 = false;
target1.onGotOffer += (_, sdp) => { isGotOffer1 = true; };
target2.onGotOffer += (_, sdp) => { isGotOffer2 = true; };
target1.onGotAnswer += (_, sdp) => { isGotAnswer1 = true; };
RTCRtpTransceiverInit init1 = new RTCRtpTransceiverInit()
{
direction = RTCRtpTransceiverDirection.SendOnly
};
RTCRtpTransceiverInit init2 = new RTCRtpTransceiverInit()
{
direction = RTCRtpTransceiverDirection.SendOnly
};
target1.AddTransceiver(connectionId, TrackKind.Audio, init1);
target2.AddTransceiver(connectionId, TrackKind.Audio, init2);
// check each target invoke onGotOffer
yield return new WaitForSeconds(ResendOfferInterval * 5);
// ignore offer because impolite peer
Assert.That(isGotOffer1, Is.False, $"{nameof(isGotOffer1)} is not False.");
// accept offer because polite peer
Assert.That(isGotOffer2, Is.True, $"{nameof(isGotOffer2)} is not True.");
target2.SendAnswer(connectionId);
yield return new WaitUntil(() => isGotAnswer1);
Assert.That(isGotAnswer1, Is.True, $"{nameof(isGotAnswer1)} is not True.");
target1.DeleteConnection(connectionId);
target2.DeleteConnection(connectionId);
bool isDeletedConnection1 = false;
bool isDeletedConnection2 = false;
target1.onDeletedConnection += _ => { isDeletedConnection1 = true; };
target2.onDeletedConnection += _ => { isDeletedConnection2 = true; };
yield return new WaitUntil(() => isDeletedConnection1 && isDeletedConnection2);
Assert.That(isDeletedConnection1, Is.True, $"{nameof(isDeletedConnection1)} is not True.");
Assert.That(isDeletedConnection2, Is.True, $"{nameof(isDeletedConnection2)} is not True.");
target1.Dispose();
target2.Dispose();
}
[TestCase(TestMode.PublicMode, ExpectedResult = null)]
[TestCase(TestMode.PrivateMode, ExpectedResult = null)]
[UnityTest, Timeout(30000), LongRunning]
public IEnumerator ResendOfferUntilGotAnswer(TestMode mode)
{
MockSignaling.Reset(mode == TestMode.PrivateMode);
var dependencies1 = CreateDependencies();
var dependencies2 = CreateDependencies();
var target1 = new SignalingManagerInternal(ref dependencies1);
var target2 = new SignalingManagerInternal(ref dependencies2);
bool isStarted1 = false;
bool isStarted2 = false;
target1.onStart += () => { isStarted1 = true; };
target2.onStart += () => { isStarted2 = true; };
yield return new WaitUntil(() => isStarted1 && isStarted2);
bool isCreatedConnection1 = false;
bool isCreatedConnection2 = false;
target1.onCreatedConnection += _ => { isCreatedConnection1 = true; };
target2.onCreatedConnection += _ => { isCreatedConnection2 = true; };
var connectionId = "12345";
target1.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection1);
Assert.That(isCreatedConnection1, Is.True);
target2.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection2);
Assert.That(isCreatedConnection2, Is.True);
int countGotOffer2 = 0;
target2.onGotOffer += (_, sdp) => { countGotOffer2++; };
target1.SendOffer(connectionId);
yield return new WaitUntil(() => countGotOffer2 > 1);
Assert.That(countGotOffer2, Is.GreaterThan(1));
bool isGotAnswer1 = false;
target1.onGotAnswer += (_, sdp) => { isGotAnswer1 = true; };
target2.SendAnswer(connectionId);
yield return new WaitUntil(() => isGotAnswer1);
Assert.That(isGotAnswer1, Is.True);
yield return new WaitForSeconds(ResendOfferInterval * 2);
var currentCount = countGotOffer2;
yield return new WaitForSeconds(ResendOfferInterval * 2);
Assert.That(countGotOffer2, Is.EqualTo(currentCount),
$"{nameof(currentCount)} is not Equal {nameof(countGotOffer2)}");
target1.DeleteConnection(connectionId);
target2.DeleteConnection(connectionId);
bool isDeletedConnection1 = false;
bool isDeletedConnection2 = false;
target1.onDeletedConnection += _ => { isDeletedConnection1 = true; };
target2.onDeletedConnection += _ => { isDeletedConnection2 = true; };
yield return new WaitUntil(() => isDeletedConnection1 && isDeletedConnection2);
Assert.That(isDeletedConnection1, Is.True, $"{nameof(isDeletedConnection1)} is not True.");
Assert.That(isDeletedConnection2, Is.True, $"{nameof(isDeletedConnection2)} is not True.");
target1.Dispose();
target2.Dispose();
}
[TestCase(TestMode.PublicMode, ExpectedResult = null)]
[TestCase(TestMode.PrivateMode, ExpectedResult = null)]
[UnityTest, Timeout(30000), LongRunning]
public IEnumerator DeleteFailedPeers(TestMode mode)
{
MockSignaling.Reset(mode == TestMode.PrivateMode);
var dependencies1 = CreateDependencies();
var dependencies2 = CreateDependencies();
var target1 = new SignalingManagerInternal(ref dependencies1);
var target2 = new SignalingManagerInternal(ref dependencies2);
bool isStarted1 = false;
bool isStarted2 = false;
target1.onStart += () => { isStarted1 = true; };
target2.onStart += () => { isStarted2 = true; };
yield return new WaitUntil(() => isStarted1 && isStarted2);
Assert.That(isStarted1, Is.True);
Assert.That(isStarted2, Is.True);
bool isCreatedConnection1 = false;
bool isCreatedConnection2 = false;
target1.onCreatedConnection += _ => { isCreatedConnection1 = true; };
target2.onCreatedConnection += _ => { isCreatedConnection2 = true; };
var connectionId = "12345";
// target1 has impolite peer (request first)
target1.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection1);
Assert.That(isCreatedConnection1, Is.True);
target2.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection2);
Assert.That(isCreatedConnection2, Is.True);
bool isGotOffer2 = false;
bool isGotAnswer1 = false;
target2.onGotOffer += (_, sdp) => { isGotOffer2 = true; };
target1.onGotAnswer += (_, sdp) => { isGotAnswer1 = true; };
RTCRtpTransceiverInit init1 = new RTCRtpTransceiverInit()
{
direction = RTCRtpTransceiverDirection.SendOnly
};
target1.AddTransceiver(connectionId, TrackKind.Video, init1);
yield return new WaitUntil(() => isGotOffer2);
Assert.That(isGotOffer2, Is.True, $"{nameof(isGotOffer2)} is not True.");
target2.SendAnswer(connectionId);
yield return new WaitUntil(() => isGotAnswer1);
Assert.That(isGotAnswer1, Is.True, $"{nameof(isGotAnswer1)} is not True.");
// Improperly dispose of target1 to force failed state on target2
target1.Dispose();
bool isDeletedConnection2 = false;
target2.onDeletedConnection += _ => { isDeletedConnection2 = true; };
yield return new WaitUntil(() => isDeletedConnection2);
Assert.That(isDeletedConnection2, Is.True, $"{nameof(isDeletedConnection2)} is not True.");
target2.Dispose();
}
[UnityTest, Timeout(10000)]
public IEnumerator ReNegotiationAfterReceivingFirstOffer()
{
MockSignaling.Reset(true);
var dependencies1 = CreateDependencies();
var dependencies2 = CreateDependencies();
var target1 = new SignalingManagerInternal(ref dependencies1);
var target2 = new SignalingManagerInternal(ref dependencies2);
bool isStarted1 = false;
bool isStarted2 = false;
target1.onStart += () => { isStarted1 = true; };
target2.onStart += () => { isStarted2 = true; };
yield return new WaitUntil(() => isStarted1 && isStarted2);
bool isCreatedConnection1 = false;
bool isCreatedConnection2 = false;
target1.onCreatedConnection += _ => { isCreatedConnection1 = true; };
target2.onCreatedConnection += _ => { isCreatedConnection2 = true; };
var connectionId = "12345";
// target1 has impolite peer (request first)
target1.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection1);
// target2 has polite peer (request second)
target2.CreateConnection(connectionId);
yield return new WaitUntil(() => isCreatedConnection2);
bool isGotOffer1 = false;
bool isGotOffer2 = false;
bool isGotAnswer1 = false;
bool isGotAnswer2 = false;
target1.onGotOffer += (_, sdp) => { isGotOffer1 = true; };
target2.onGotOffer += (_, sdp) => { isGotOffer2 = true; };
target1.onGotAnswer += (_, sdp) => { isGotAnswer1 = true; };
target2.onGotAnswer += (_, sdp) => { isGotAnswer2 = true; };
var init1 = new RTCRtpTransceiverInit() { direction = RTCRtpTransceiverDirection.SendOnly };
var init2 = new RTCRtpTransceiverInit() { direction = RTCRtpTransceiverDirection.RecvOnly };
var init3 = new RTCRtpTransceiverInit() { direction = RTCRtpTransceiverDirection.SendOnly };
var init4 = new RTCRtpTransceiverInit() { direction = RTCRtpTransceiverDirection.RecvOnly };
target1.AddTransceiver(connectionId, TrackKind.Video, init1);
target1.AddTransceiver(connectionId, TrackKind.Video, init2);
target2.AddTransceiver(connectionId, TrackKind.Video, init3);
target2.AddTransceiver(connectionId, TrackKind.Video, init4);
yield return new WaitUntil(() => isGotOffer2);
Assert.That(isGotOffer2, Is.True, $"{nameof(isGotOffer2)} is not True.");
target2.SendAnswer(connectionId);
yield return new WaitUntil(() => isGotAnswer1);
Assert.That(isGotAnswer1, Is.True, $"{nameof(isGotAnswer1)} is not True.");
yield return new WaitUntil(() => isGotOffer1);
Assert.That(isGotOffer1, Is.True, $"{nameof(isGotOffer1)} is not True.");
target1.SendAnswer(connectionId);
yield return new WaitUntil(() => isGotAnswer2);
Assert.That(isGotAnswer2, Is.True, $"{nameof(isGotAnswer2)} is not True.");
target1.DeleteConnection(connectionId);
target2.DeleteConnection(connectionId);
bool isDeletedConnection1 = false;
bool isDeletedConnection2 = false;
target1.onDeletedConnection += _ => { isDeletedConnection1 = true; };
target2.onDeletedConnection += _ => { isDeletedConnection2 = true; };
yield return new WaitUntil(() => isDeletedConnection1 && isDeletedConnection2);
Assert.That(isDeletedConnection1, Is.True, $"{nameof(isDeletedConnection1)} is not True.");
Assert.That(isDeletedConnection2, Is.True, $"{nameof(isDeletedConnection1)} is not True.");
target1.Dispose();
target2.Dispose();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 982c6d2f26e4a4a4787bd5158b574941
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,191 @@
using System.IO;
using System.Linq;
using NUnit.Framework;
using Unity.RenderStreaming.RuntimeTest.Signaling;
using Unity.RenderStreaming.Signaling;
using UnityEngine;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;
namespace Unity.RenderStreaming.RuntimeTest
{
class SignalingManagerTest
{
SignalingManager component;
[SetUp]
public void SetUp()
{
GameObject obj = new GameObject();
obj.SetActive(false);
component = obj.AddComponent<SignalingManager>();
}
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(component.gameObject);
}
[Test]
public void DoNothing()
{
}
[Test]
public void UseDefaultSettings()
{
Assert.That(component.useDefaultSettings, Is.True);
component.useDefaultSettings = false;
Assert.That(component.useDefaultSettings, Is.False);
}
[Test]
public void Run()
{
var handler = component.gameObject.AddComponent<SingleConnection>();
var handlers = new SignalingHandlerBase[] { handler };
ISignaling mock = new MockSignaling();
component.runOnAwake = false;
component.gameObject.SetActive(true);
component.Run(signaling: mock, handlers: handlers);
}
[Test, Ignore("Failed this test on macOS and Linux platform because of the signaling process.")]
public void RunDefault()
{
var handler = component.gameObject.AddComponent<SingleConnection>();
var handlers = new SignalingHandlerBase[] { handler };
ISignaling mock = new MockSignaling();
component.runOnAwake = false;
component.gameObject.SetActive(true);
component.Run(handlers: handlers);
}
[Test]
public void ThrowExceptionIfHandlerIsNullOrEmpty()
{
ISignaling mock = new MockSignaling();
component.runOnAwake = false;
component.gameObject.SetActive(true);
Assert.That(() => component.Run(signaling: mock), Throws.InvalidOperationException);
var handlers = new SignalingHandlerBase[] { };
Assert.That(() => component.Run(signaling: mock, handlers: handlers),
Throws.InvalidOperationException);
}
[Test]
public void RunAgain()
{
var handler = component.gameObject.AddComponent<SingleConnection>();
var handlers = new SignalingHandlerBase[] { handler };
ISignaling mock = new MockSignaling();
component.runOnAwake = false;
component.gameObject.SetActive(true);
component.Run(signaling: mock, handlers: handlers);
component.Stop();
component.Run(signaling: mock, handlers: handlers);
}
[Test]
public void GetDefaultSignalingSettings()
{
component.runOnAwake = false;
component.gameObject.SetActive(true);
var settings = component.GetSignalingSettings() as WebSocketSignalingSettings;
Assert.That(settings, Is.Not.Null);
}
[Test]
public void AddAndRemoveSignalingHandler()
{
component.runOnAwake = false;
component.gameObject.SetActive(true);
component.SetSignalingSettings(new MockSignalingSettings());
var handler = component.gameObject.AddComponent<SingleConnection>();
component.AddSignalingHandler(handler);
Assert.That(() => component.Run(), Throws.Nothing);
component.RemoveSignalingHandler(handler);
}
[Test]
public void ThrowExceptionSetSignalingOnRunning()
{
component.runOnAwake = false;
component.gameObject.SetActive(true);
component.SetSignalingSettings(new MockSignalingSettings());
var handler = component.gameObject.AddComponent<SingleConnection>();
component.AddSignalingHandler(handler);
Assert.That(() => component.Run(), Throws.Nothing);
Assert.That(component.Running, Is.True);
Assert.That(() => component.SetSignalingSettings(new MockSignalingSettings()), Throws.InvalidOperationException);
}
[Test]
[UnityPlatform(exclude = new[] { RuntimePlatform.Android, RuntimePlatform.IPhonePlayer })]
public void EvaluateCommandlineArguments()
{
// Change signaling type.
SignalingSettings settings = new WebSocketSignalingSettings("ws://127.0.0.1");
Assert.That(settings.iceServers, Is.Empty);
string[] arguments = { "-signalingType", "http" };
Assert.That(SignalingManager.EvaluateCommandlineArguments(ref settings, arguments), Is.True);
Assert.That(settings, Is.TypeOf<HttpSignalingSettings>());
Assert.That(settings.iceServers, Is.Not.Empty);
settings = new HttpSignalingSettings("http://127.0.0.1");
Assert.That(settings.iceServers, Is.Empty);
arguments = new string[] { "-signalingType", "websocket" };
Assert.That(SignalingManager.EvaluateCommandlineArguments(ref settings, arguments), Is.True);
Assert.That(settings, Is.TypeOf<WebSocketSignalingSettings>());
Assert.That(settings.iceServers, Is.Not.Empty);
// Change signaling url.
settings = new WebSocketSignalingSettings("ws://127.0.0.1");
string url = "ws://192.168.10.10";
arguments = new[] { "-signalingUrl", url };
Assert.That(SignalingManager.EvaluateCommandlineArguments(ref settings, arguments), Is.True);
Assert.That(settings, Is.TypeOf<WebSocketSignalingSettings>());
Assert.That((settings as WebSocketSignalingSettings).url, Is.EqualTo(url));
settings = new HttpSignalingSettings("http://127.0.0.1");
url = "http://192.168.10.10";
arguments = new[] { "-signalingUrl", url };
Assert.That(SignalingManager.EvaluateCommandlineArguments(ref settings, arguments), Is.True);
Assert.That(settings, Is.TypeOf<HttpSignalingSettings>());
Assert.That((settings as HttpSignalingSettings).url, Is.EqualTo(url));
// Import json for ice server settings.
settings = new WebSocketSignalingSettings("ws://127.0.0.1");
string json = "{\"iceServers\":[{\"credential\":\"pass\",\"username\":\"user\",\"credentialType\":\"password\"," +
"\"urls\":[\"turn:192.168.10.10:3478?transport=udp\"]}]}";
string filepath = "dummy.json";
File.WriteAllText(filepath, json);
arguments = new[] { "-importJson", filepath };
var info = JsonUtility.FromJson<CommandLineInfo>(json);
Assert.That(SignalingManager.EvaluateCommandlineArguments(ref settings, arguments), Is.True);
Assert.That(settings, Is.TypeOf<WebSocketSignalingSettings>());
Assert.That(settings.iceServers.Count, Is.EqualTo(1));
Assert.That(settings.iceServers.ElementAt(0).credential, Is.EqualTo(info.iceServers[0].credential));
Assert.That(settings.iceServers.ElementAt(0).credentialType, Is.EqualTo((IceCredentialType)info.iceServers[0].credentialType));
File.Delete(filepath);
// Import json to change signaling type.
settings = new WebSocketSignalingSettings("ws://127.0.0.1");
json = "{\"signalingType\":\"websocket\"}";
File.WriteAllText(filepath, json);
arguments = new[] { "-importJson", filepath };
Assert.That(SignalingManager.EvaluateCommandlineArguments(ref settings, arguments), Is.True);
Assert.That(settings, Is.TypeOf<WebSocketSignalingSettings>());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 160df6e38142e48e0ac66ecd055482eb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,80 @@
using System;
using System.Linq;
using NUnit.Framework;
using Unity.RenderStreaming.Signaling;
namespace Unity.RenderStreaming.RuntimeTest
{
class IceServerTest
{
[Test]
public void Clone()
{
var iceServer = new IceServer(
urls: new[] { "stun:stun.l.google.com:19302" },
username: "username",
credentialType: IceCredentialType.Password,
credential: "password");
var copied = iceServer.Clone();
Assert.That(copied.urls, Is.EqualTo(iceServer.urls));
Assert.That(copied.username, Is.EqualTo(iceServer.username));
Assert.That(copied.credentialType, Is.EqualTo(iceServer.credentialType));
Assert.That(copied.credential, Is.EqualTo(iceServer.credential));
copied = iceServer.Clone(
username: "username2",
credential: "password2");
Assert.That(copied.urls, Is.EqualTo(iceServer.urls));
Assert.That(copied.username, Is.Not.EqualTo(iceServer.username));
Assert.That(copied.credentialType, Is.EqualTo(iceServer.credentialType));
Assert.That(copied.credential, Is.Not.EqualTo(iceServer.credential));
}
}
class SignalingSettingsTest
{
[Test]
public void WebSocketSignalingSettings()
{
var settings = new WebSocketSignalingSettings();
Assert.That(settings.signalingClass, Is.EqualTo(typeof(WebSocketSignaling)));
Assert.That(settings.url, Is.Not.Empty);
Assert.That(settings.iceServers, Is.Not.Empty);
Assert.That(() => new WebSocketSignalingSettings(url: null), Throws.Exception.TypeOf<ArgumentNullException>());
var url = "ws://localhost";
settings = new WebSocketSignalingSettings(url: url);
Assert.That(settings.url, Is.EqualTo(url));
Assert.That(settings.iceServers, Is.Empty);
var iceUrl = "stun:stun.l.google.com:19302";
var iceServers = new[] { new IceServer(urls: new[] { iceUrl }) };
settings = new WebSocketSignalingSettings(url: url, iceServers: iceServers);
Assert.That(settings.iceServers.Count, Is.EqualTo(1));
Assert.That(settings.iceServers.ElementAt(0).username, Is.Null);
Assert.That(settings.iceServers.ElementAt(0).credential, Is.Null);
Assert.That(settings.iceServers.ElementAt(0).credentialType, Is.EqualTo(IceCredentialType.Password));
Assert.That(settings.iceServers.ElementAt(0).urls.Count, Is.EqualTo(1));
Assert.That(settings.iceServers.ElementAt(0).urls.ElementAt(0), Is.EqualTo(iceUrl));
}
[Test]
public void HttpSignalingSettings()
{
var settings = new HttpSignalingSettings();
Assert.That(settings.signalingClass, Is.EqualTo(typeof(HttpSignaling)));
Assert.That(settings.url, Is.Not.Empty);
Assert.That(settings.iceServers, Is.Not.Empty);
Assert.That(() => new HttpSignalingSettings(url: null), Throws.Exception.TypeOf<ArgumentNullException>());
var url = "http://localhost";
settings = new HttpSignalingSettings(url: url);
Assert.That(settings.url, Is.EqualTo(url));
Assert.That(settings.iceServers, Is.Empty);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a99597f72c2654c4a8e3b9c82774e1dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,394 @@
using System;
using System.Collections;
using System.Diagnostics;
using System.Threading;
using NUnit.Framework;
using Unity.RenderStreaming.RuntimeTest.Signaling;
using Unity.RenderStreaming.Signaling;
using Unity.WebRTC;
using UnityEngine;
using UnityEngine.TestTools;
using Debug = UnityEngine.Debug;
namespace Unity.RenderStreaming.RuntimeTest
{
[TestFixture(typeof(WebSocketSignaling))]
[TestFixture(typeof(HttpSignaling))]
[TestFixture(typeof(MockSignaling))]
[UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })]
[ConditionalIgnore(ConditionalIgnore.IL2CPP, "Process.Start does not implement in IL2CPP.")]
class SignalingTest : IPrebuildSetup
{
private readonly Type m_SignalingType;
private Process m_ServerProcess;
private RTCSessionDescription m_DescOffer;
private RTCSessionDescription m_DescAnswer;
private RTCIceCandidate m_candidate;
private SynchronizationContext m_Context;
private ISignaling signaling1;
private ISignaling signaling2;
public SignalingTest()
{
}
public SignalingTest(Type type)
{
m_SignalingType = type;
}
public void Setup()
{
if (m_SignalingType == typeof(MockSignaling))
{
return;
}
#if UNITY_EDITOR
string dir = System.IO.Directory.GetCurrentDirectory();
string fileName = System.IO.Path.Combine(dir, Editor.WebAppDownloader.GetFileName());
if (System.IO.File.Exists(fileName) || System.IO.File.Exists(TestUtility.GetWebAppLocationFromEnv()))
{
// already exists.
return;
}
bool downloadRaised = false;
Editor.WebAppDownloader.DownloadCurrentVersionWebApp(dir, success => { downloadRaised = true; });
TestUtility.Wait(() => downloadRaised, 10000);
#endif
}
[OneTimeSetUp]
public void OneTimeSetUp()
{
if (m_SignalingType == typeof(MockSignaling))
{
MockSignaling.Reset(false);
return;
}
m_ServerProcess = new Process();
string fileName = TestUtility.GetWebAppLocationFromEnv();
if (string.IsNullOrEmpty(fileName))
{
Debug.Log($"webapp file not found in {fileName}");
string dir = System.IO.Directory.GetCurrentDirectory();
fileName = System.IO.Path.Combine(dir, TestUtility.GetFileName());
}
Assert.IsTrue(System.IO.File.Exists(fileName), $"webapp file not found in {fileName}");
ProcessStartInfo startInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
FileName = fileName,
UseShellExecute = false
};
string arguments = $"-p {TestUtility.PortNumber}";
if (m_SignalingType == typeof(HttpSignaling))
{
arguments += " -t http";
}
startInfo.Arguments = arguments;
m_ServerProcess.StartInfo = startInfo;
m_ServerProcess.OutputDataReceived += (sender, e) => { Debug.Log(e.Data); };
bool success = m_ServerProcess.Start();
Assert.True(success);
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
if (m_SignalingType == typeof(MockSignaling))
{
return;
}
m_ServerProcess?.Kill();
m_ServerProcess?.WaitForExit();
m_ServerProcess?.Dispose();
m_ServerProcess = null;
}
ISignaling CreateSignaling(Type type, SynchronizationContext mainThread)
{
if (type == typeof(WebSocketSignaling))
{
var settings = new WebSocketSignalingSettings
(
url: $"ws://localhost:{TestUtility.PortNumber}"
);
return new WebSocketSignaling(settings, mainThread);
}
if (type == typeof(HttpSignaling))
{
var settings = new HttpSignalingSettings
(
url: $"http://localhost:{TestUtility.PortNumber}",
interval: 100
);
return new HttpSignaling(settings, mainThread);
}
if (type == typeof(MockSignaling))
{
return new MockSignaling();
}
throw new ArgumentException();
}
[UnitySetUp, Timeout(1000)]
public IEnumerator UnitySetUp()
{
RTCConfiguration config = default;
RTCIceCandidate candidate_ = null;
config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
var peer1 = new RTCPeerConnection(ref config);
var peer2 = new RTCPeerConnection(ref config);
peer1.OnIceCandidate = candidate => { candidate_ = candidate; };
AudioStreamTrack track = new AudioStreamTrack();
peer1.AddTrack(track);
var op1 = peer1.CreateOffer();
yield return op1;
m_DescOffer = op1.Desc;
var op2 = peer1.SetLocalDescription(ref m_DescOffer);
yield return op2;
var op3 = peer2.SetRemoteDescription(ref m_DescOffer);
yield return op3;
var op4 = peer2.CreateAnswer();
yield return op4;
m_DescAnswer = op4.Desc;
var op5 = peer2.SetLocalDescription(ref m_DescAnswer);
yield return op5;
var op6 = peer1.SetRemoteDescription(ref m_DescAnswer);
yield return op6;
yield return new WaitUntil(() => candidate_ != null);
m_candidate = candidate_;
track.Dispose();
peer1.Close();
peer2.Close();
m_Context = SynchronizationContext.Current;
signaling1 = CreateSignaling(m_SignalingType, m_Context);
signaling2 = CreateSignaling(m_SignalingType, m_Context);
}
[TearDown]
public void TearDown()
{
signaling1.Stop();
signaling2.Stop();
m_Context = null;
}
[UnityTest, Timeout(10000)]
public IEnumerator OnConnect()
{
bool startRaised1 = false;
string connectionId1 = null;
signaling1.OnStart += s => { startRaised1 = true; };
signaling1.Start();
yield return new WaitUntil(() => startRaised1);
signaling1.OnCreateConnection += (s, connectionId, polite) => { connectionId1 = connectionId; };
signaling1.OpenConnection(Guid.NewGuid().ToString());
yield return new WaitUntil(() => !string.IsNullOrEmpty(connectionId1));
Assert.IsNotEmpty(connectionId1);
}
[UnityTest, Timeout(10000)]
public IEnumerator OnOffer()
{
bool startRaised1 = false;
bool startRaised2 = false;
bool offerRaised = false;
bool raiseOnDestroy1 = false;
bool raiseOnDestroy2 = false;
string connectionId1 = null;
string connectionId2 = null;
const string _connectionId = "12345";
signaling1.OnStart += s => { startRaised1 = true; };
signaling2.OnStart += s => { startRaised2 = true; };
signaling1.Start();
signaling2.Start();
yield return new WaitUntil(() => startRaised1 && startRaised2);
signaling1.OnCreateConnection += (s, connectionId, polite) =>
{
connectionId1 = connectionId;
};
signaling1.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy1 = id == connectionId1;
};
signaling1.OpenConnection(_connectionId);
signaling2.OnCreateConnection += (s, connectionId, polite) =>
{
connectionId2 = connectionId;
};
signaling1.OnDestroyConnection += (signaling, id) =>
{
raiseOnDestroy2 = id == connectionId2;
};
signaling2.OpenConnection(_connectionId);
yield return new WaitUntil(() =>
!string.IsNullOrEmpty(connectionId1) && !string.IsNullOrEmpty(connectionId2));
Assert.That(connectionId1, Is.EqualTo(_connectionId));
Assert.That(connectionId2, Is.EqualTo(_connectionId));
signaling2.OnOffer += (s, e) => { offerRaised = true; };
signaling1.SendOffer(connectionId1, m_DescOffer);
yield return new WaitUntil(() => offerRaised);
signaling1.CloseConnection(connectionId1);
yield return new WaitUntil(() => raiseOnDestroy1 && raiseOnDestroy2);
Assert.That(raiseOnDestroy1, Is.True);
Assert.That(raiseOnDestroy2, Is.True);
}
[UnityTest, Timeout(10000)]
public IEnumerator OnAnswer()
{
bool startRaised1 = false;
bool startRaised2 = false;
bool offerRaised = false;
bool answerRaised = false;
string connectionId1 = null;
string connectionId2 = null;
signaling1.OnStart += s => { startRaised1 = true; };
signaling2.OnStart += s => { startRaised2 = true; };
signaling1.Start();
signaling2.Start();
yield return new WaitUntil(() => startRaised1 && startRaised2);
signaling1.OnCreateConnection += (s, connectionId, polite) =>
{
connectionId1 = connectionId;
};
signaling1.OpenConnection(Guid.NewGuid().ToString());
signaling2.OnCreateConnection += (s, connectionId, polite) =>
{
connectionId2 = connectionId;
};
signaling2.OpenConnection(Guid.NewGuid().ToString());
yield return new WaitUntil(() =>
!string.IsNullOrEmpty(connectionId1) && !string.IsNullOrEmpty(connectionId2));
signaling2.OnOffer += (s, e) => { offerRaised = true; };
signaling1.SendOffer(connectionId1, m_DescOffer);
yield return new WaitUntil(() => offerRaised);
signaling1.OnAnswer += (s, e) => { answerRaised = true; };
signaling2.SendAnswer(connectionId1, m_DescAnswer);
yield return new WaitUntil(() => answerRaised);
}
[UnityTest, Timeout(10000)]
public IEnumerator OnCandidate()
{
bool startRaised1 = false;
bool startRaised2 = false;
bool offerRaised = false;
bool answerRaised = false;
bool candidateRaised1 = false;
bool candidateRaised2 = false;
string connectionId1 = null;
string connectionId2 = null;
signaling1.OnStart += s => { startRaised1 = true; };
signaling2.OnStart += s => { startRaised2 = true; };
signaling1.Start();
signaling2.Start();
yield return new WaitUntil(() => startRaised1 && startRaised2);
signaling1.OnCreateConnection += (s, connectionId, polite) =>
{
connectionId1 = connectionId;
};
signaling1.OpenConnection(Guid.NewGuid().ToString());
signaling2.OnCreateConnection += (s, connectionId, polite) =>
{
connectionId2 = connectionId;
};
signaling2.OpenConnection(Guid.NewGuid().ToString());
yield return new WaitUntil(() =>
!string.IsNullOrEmpty(connectionId1) && !string.IsNullOrEmpty(connectionId2));
signaling2.OnOffer += (s, e) => { offerRaised = true; };
signaling1.SendOffer(connectionId1, m_DescOffer);
yield return new WaitUntil(() => offerRaised);
signaling1.OnAnswer += (s, e) => { answerRaised = true; };
signaling2.SendAnswer(connectionId1, m_DescAnswer);
yield return new WaitUntil(() => answerRaised);
signaling2.OnIceCandidate += (s, e) => { candidateRaised1 = true; };
signaling1.SendCandidate(connectionId1, m_candidate);
yield return new WaitUntil(() => candidateRaised1);
signaling1.OnIceCandidate += (s, e) => { candidateRaised2 = true; };
signaling2.SendCandidate(connectionId1, m_candidate);
yield return new WaitUntil(() => candidateRaised2);
}
[UnityTest, Timeout(10000)]
public IEnumerator OnStop()
{
bool startRaised1 = false;
bool startRaised2 = false;
bool offerRaised = false;
bool answerRaised = false;
bool connectionClosed2 = false;
string connectionId1 = null;
string connectionId2 = null;
signaling1.OnStart += s => { startRaised1 = true; };
signaling2.OnStart += s => { startRaised2 = true; };
signaling1.Start();
signaling2.Start();
yield return new WaitUntil(() => startRaised1 && startRaised2);
signaling1.OnCreateConnection += (s, connectionId, polite) =>
{
connectionId1 = connectionId;
};
signaling1.OpenConnection(Guid.NewGuid().ToString());
signaling2.OnCreateConnection += (s, connectionId, polite) =>
{
connectionId2 = connectionId;
};
signaling2.OpenConnection(Guid.NewGuid().ToString());
yield return new WaitUntil(() =>
!string.IsNullOrEmpty(connectionId1) && !string.IsNullOrEmpty(connectionId2));
signaling2.OnOffer += (s, e) => { offerRaised = true; };
signaling1.SendOffer(connectionId1, m_DescOffer);
yield return new WaitUntil(() => offerRaised);
signaling1.OnAnswer += (s, e) => { answerRaised = true; };
signaling2.SendAnswer(connectionId1, m_DescAnswer);
yield return new WaitUntil(() => answerRaised);
signaling2.OnDestroyConnection += (s, e) => { connectionClosed2 = true; };
signaling1.Stop();
yield return new WaitUntil(() => connectionClosed2);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fc2fe5ae0229c5a4f94d1148cdd98322
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,632 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Unity.Collections;
using Unity.WebRTC;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Users;
using UnityEngine.TestTools;
namespace Unity.RenderStreaming.RuntimeTest
{
class VideoStreamSenderTest
{
[Test]
public void GetAvailableCodec()
{
IEnumerable<VideoCodecInfo> codecs = VideoStreamSender.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Empty);
Assert.That(codecs.Any(codec => codec.name == "VP8"));
Assert.That(codecs.Any(codec => codec.name == "VP9"));
Assert.That(codecs.Any(codec => codec.name == "AV1"));
foreach (var codec in codecs)
{
Assert.That(codec.name, Is.Not.Empty);
Assert.That(codec.mimeType, Is.Not.Empty);
Assert.That(codec.codecImplementation, Is.Not.Empty);
switch (codec)
{
case VP9CodecInfo vp9codec:
Assert.That(vp9codec.name, Is.EqualTo("VP9"));
Assert.That(vp9codec.profile, Is.Not.Zero);
break;
case H264CodecInfo h264codec:
Assert.That(h264codec.name, Is.EqualTo("H264"));
Assert.That(h264codec.level, Is.GreaterThan(0));
Assert.That(h264codec.profile, Is.Not.Zero);
break;
default:
break;
}
}
}
[Test]
public void SelectCodecCapabilities()
{
var codecs = VideoStreamSender.GetAvailableCodecs();
var caps = RTCRtpSender.GetCapabilities(TrackKind.Video).SelectCodecCapabilities(codecs);
Assert.That(codecs.Count(), Is.EqualTo(caps.Count()));
}
[Test]
public void SetEnabled()
{
var go = new GameObject();
var sender = go.AddComponent<VideoStreamSender>();
sender.enabled = false;
sender.enabled = true;
UnityEngine.Object.DestroyImmediate(go);
}
[UnityTest]
public IEnumerator CreateTrack()
{
var go = new GameObject();
var sender = go.AddComponent<VideoStreamSender>();
MediaStreamTrack track = null;
// With camera
sender.source = VideoStreamSource.Camera;
Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf<ArgumentNullException>());
var camera = go.AddComponent<Camera>();
sender.sourceCamera = camera;
sender.width = 640;
sender.height = 480;
var op = sender.CreateTrack();
yield return op;
track = op.Track;
Assert.That(track, Is.Not.Null);
var videoTrack = track as VideoStreamTrack;
Assert.That(videoTrack.Texture.width, Is.EqualTo(sender.width));
Assert.That(videoTrack.Texture.height, Is.EqualTo(sender.height));
track.Dispose();
track = null;
// With screen
sender.source = VideoStreamSource.Screen;
op = sender.CreateTrack();
yield return op;
track = op.Track;
Assert.That(track, Is.Not.Null);
track.Dispose();
track = null;
// With Texture
sender.source = VideoStreamSource.Texture;
Assert.That(sender.sourceTexture, Is.Null);
Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf<ArgumentNullException>());
var width = 640;
var height = 480;
var format = WebRTC.WebRTC.GetSupportedRenderTextureFormat(SystemInfo.graphicsDeviceType);
var texture = new RenderTexture(width, height, 0, format);
sender.sourceTexture = texture;
Assert.That(sender.sourceTexture, Is.Not.Null);
Assert.That(sender.width, Is.EqualTo(width));
Assert.That(sender.height, Is.EqualTo(height));
Assert.That(() => sender.width = 1280, Throws.Nothing);
Assert.That(() => sender.height = 720, Throws.Nothing);
op = sender.CreateTrack();
yield return op;
track = op.Track;
Assert.That(track, Is.Not.Null);
track.Dispose();
// With WebCam
/// todo:: standalone build is failed by IL2CPP error in Unity 2021.3.
#if !(UNITY_IPHONE || UNITY_ANDROID || (UNITY_2021_3 && ENABLE_IL2CPP))
if (WebCamTexture.devices.Length > 0 && Application.HasUserAuthorization(UserAuthorization.WebCam))
{
sender.source = VideoStreamSource.WebCamera;
Assert.That(sender.sourceDeviceIndex, Is.EqualTo(0));
sender.sourceDeviceIndex = -1;
Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf<ArgumentOutOfRangeException>());
sender.sourceDeviceIndex = 0;
op = sender.CreateTrack();
yield return op;
track = op.Track;
Assert.That(track, Is.Not.Null);
Assert.That(sender.sourceWebCamTexture, Is.Not.Null);
track.Dispose();
}
#endif
UnityEngine.Object.DestroyImmediate(go);
}
[Test]
public void IsPlaying()
{
var go = new GameObject();
var sender = go.AddComponent<VideoStreamSender>();
Assert.That(sender.isPlaying, Is.False);
UnityEngine.Object.DestroyImmediate(go);
}
[Test]
public void SetBitrate()
{
var go = new GameObject();
var sender = go.AddComponent<VideoStreamSender>();
uint minBitrate = 1000;
uint maxBitrate = 2000;
sender.SetBitrate(minBitrate, maxBitrate);
Assert.That(sender.minBitrate, Is.EqualTo(minBitrate));
Assert.That(sender.maxBitrate, Is.EqualTo(maxBitrate));
minBitrate = 3000;
Assert.That(() => sender.SetBitrate(minBitrate, maxBitrate), Throws.Exception.TypeOf<ArgumentException>());
UnityEngine.Object.DestroyImmediate(go);
}
[Test]
public void SetFrameRate()
{
var go = new GameObject();
var sender = go.AddComponent<VideoStreamSender>();
float framerate = 24;
sender.SetFrameRate(framerate);
Assert.That(sender.frameRate, Is.EqualTo(framerate));
framerate = 0;
sender.SetFrameRate(framerate);
Assert.That(sender.frameRate, Is.EqualTo(framerate));
framerate = -1;
Assert.That(() => sender.SetFrameRate(framerate), Throws.Exception.TypeOf<ArgumentOutOfRangeException>());
UnityEngine.Object.DestroyImmediate(go);
}
[Test]
public void SetScaleResolutionDown()
{
var go = new GameObject();
var sender = go.AddComponent<VideoStreamSender>();
float scaleFactor = 2;
sender.SetScaleResolutionDown(scaleFactor);
Assert.That(sender.scaleResolutionDown, Is.EqualTo(scaleFactor));
scaleFactor = 1;
sender.SetScaleResolutionDown(scaleFactor);
Assert.That(sender.scaleResolutionDown, Is.EqualTo(scaleFactor));
scaleFactor = -1;
Assert.That(() => sender.SetScaleResolutionDown(scaleFactor), Throws.Exception.TypeOf<ArgumentOutOfRangeException>());
scaleFactor = 0.5f;
Assert.That(() => sender.SetScaleResolutionDown(scaleFactor), Throws.Exception.TypeOf<ArgumentOutOfRangeException>());
UnityEngine.Object.DestroyImmediate(go);
}
[Test]
public void SetCodec()
{
var go = new GameObject();
var sender = go.AddComponent<VideoStreamSender>();
Assert.That(sender.codec, Is.Null);
var codecs = VideoStreamSender.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Empty);
var codec = codecs.First();
sender.SetCodec(codec);
Assert.That(sender.codec, Is.EqualTo(codec));
sender.SetCodec(null);
Assert.That(sender.codec, Is.Null);
}
}
class VideoStreamReceiverTest
{
[Test]
public void GetAvailableCodec()
{
IEnumerable<VideoCodecInfo> codecs = VideoStreamReceiver.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Empty);
Assert.That(codecs.Any(codec => codec.name == "VP8"));
Assert.That(codecs.Any(codec => codec.name == "VP9"));
Assert.That(codecs.Any(codec => codec.name == "AV1"));
foreach (var codec in codecs)
{
Assert.That(codec.name, Is.Not.Empty);
Assert.That(codec.mimeType, Is.Not.Empty);
Assert.That(codec.codecImplementation, Is.Not.Empty);
switch (codec)
{
case VP9CodecInfo vp9codec:
Assert.That(vp9codec.name, Is.EqualTo("VP9"));
Assert.That(vp9codec.profile, Is.Not.Zero);
break;
case H264CodecInfo h264codec:
Assert.That(h264codec.name, Is.EqualTo("H264"));
Assert.That(h264codec.level, Is.GreaterThan(0));
Assert.That(h264codec.profile, Is.Not.Zero);
break;
}
}
}
[Test]
public void SelectCodecCapabilities()
{
var codecs = VideoStreamReceiver.GetAvailableCodecs();
var caps = RTCRtpReceiver.GetCapabilities(TrackKind.Video).SelectCodecCapabilities(codecs);
Assert.That(codecs.Count(), Is.EqualTo(caps.Count()));
}
[Test]
public void SetCodec()
{
var go = new GameObject();
var receiver = go.AddComponent<VideoStreamReceiver>();
Assert.That(receiver.codec, Is.Null);
var codecs = VideoStreamReceiver.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Empty);
var codec = codecs.First();
receiver.SetCodec(codec);
Assert.That(receiver.codec, Is.EqualTo(codec));
receiver.SetCodec(null);
Assert.That(receiver.codec, Is.Null);
}
}
class AudioStreamSenderTest
{
[Test]
public void GetAvailableCodec()
{
IEnumerable<AudioCodecInfo> codecs = AudioStreamSender.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Empty);
foreach (var codec in codecs)
{
Assert.That(codec.name, Is.Not.Empty);
Assert.That(codec.mimeType, Is.Not.Empty);
Assert.That(codec.channelCount, Is.GreaterThan(0));
Assert.That(codec.sampleRate, Is.GreaterThan(0));
}
}
[UnityTest]
public IEnumerator CreateTrack()
{
var go = new GameObject();
var sender = go.AddComponent<AudioStreamSender>();
MediaStreamTrack track;
// With AudioSource
var go2 = new GameObject();
sender = go2.AddComponent<AudioStreamSender>();
sender.source = AudioStreamSource.AudioSource;
Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf<InvalidOperationException>());
sender.audioSource = go2.AddComponent<AudioSource>();
var op = sender.CreateTrack();
yield return op;
track = op.Track;
Assert.That(track, Is.Not.Null);
track.Dispose();
// APIOnly
var go3 = new GameObject();
sender = go3.AddComponent<AudioStreamSender>();
sender.source = AudioStreamSource.APIOnly;
op = sender.CreateTrack();
yield return op;
track = op.Track;
Assert.That(track, Is.Not.Null);
track.Dispose();
// With AudioListener
// workaround(kazuki): Fix NullReferenceException in AudioStreamTrack.ProcessAudio.
//sender.source = AudioStreamSource.AudioListener;
//Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf<InvalidOperationException>());
//sender.audioListener = go.AddComponent<AudioListener>();
//op = sender.CreateTrack();
//yield return op;
//track = op.Track;
//Assert.That(track, Is.Not.Null);
//track.Dispose();
// With Microphone
#if !(UNITY_IPHONE || UNITY_ANDROID)
if (Microphone.devices.Length > 0 && Application.HasUserAuthorization(UserAuthorization.Microphone))
{
sender.source = AudioStreamSource.Microphone;
op = sender.CreateTrack();
yield return op;
track = op.Track;
Assert.That(track, Is.Not.Null);
track.Dispose();
track = null;
}
#endif
UnityEngine.Object.DestroyImmediate(go);
UnityEngine.Object.DestroyImmediate(go2);
UnityEngine.Object.DestroyImmediate(go3);
}
// workaround(kazuki): Fix NullReferenceException in AudioStreamTrack.ProcessAudio. (WRS-231)
[UnityTest]
[UnityPlatform(exclude = new[] { RuntimePlatform.OSXEditor, RuntimePlatform.OSXPlayer, RuntimePlatform.LinuxEditor, RuntimePlatform.LinuxPlayer })]
public IEnumerator ReplaceTrack()
{
var go = new GameObject();
var sender = go.AddComponent<AudioStreamSender>();
Assert.That(() => sender.ReplaceTrack(null), Throws.Exception.TypeOf<ArgumentNullException>());
// With AudioListener
sender.source = AudioStreamSource.AudioListener;
var audioListener = go.AddComponent<AudioListener>();
sender.audioListener = audioListener;
var op = sender.CreateTrack();
yield return op;
var track = op.Track;
Assert.That(track, Is.Not.Null);
sender.ReplaceTrack(track);
UnityEngine.Object.DestroyImmediate(go);
}
[UnityTest]
public IEnumerator AudioLoopback()
{
var go = new GameObject();
var sender = go.AddComponent<AudioStreamSender>();
sender.source = AudioStreamSource.AudioListener;
var audioListener = go.AddComponent<AudioListener>();
sender.audioListener = audioListener;
var op = sender.CreateTrack();
yield return op;
var track = op.Track as AudioStreamTrack;
Assert.That(track, Is.Not.Null);
sender.SetTrack(track);
sender.loopback = true;
Assert.That(track.Loopback, Is.True);
sender.loopback = false;
Assert.That(track.Loopback, Is.False);
UnityEngine.Object.DestroyImmediate(go);
}
[Test]
public void SelectCodecCapabilities()
{
var codecs = AudioStreamSender.GetAvailableCodecs();
var caps = RTCRtpSender.GetCapabilities(TrackKind.Audio).SelectCodecCapabilities(codecs);
Assert.That(codecs.Count(), Is.EqualTo(caps.Count()));
}
[Test]
public void SetEnabled()
{
var go = new GameObject();
var sender = go.AddComponent<AudioStreamSender>();
sender.enabled = false;
sender.enabled = true;
UnityEngine.Object.DestroyImmediate(go);
}
[Test]
public void IsPlaying()
{
var go = new GameObject();
var sender = go.AddComponent<VideoStreamSender>();
Assert.That(sender.isPlaying, Is.False);
UnityEngine.Object.DestroyImmediate(go);
}
[Test]
public void SetBitrate()
{
var go = new GameObject();
var sender = go.AddComponent<AudioStreamSender>();
uint minBitrate = 1000;
uint maxBitrate = 2000;
sender.SetBitrate(minBitrate, maxBitrate);
Assert.That(sender.minBitrate, Is.EqualTo(minBitrate));
Assert.That(sender.maxBitrate, Is.EqualTo(maxBitrate));
minBitrate = 3000;
Assert.That(() => sender.SetBitrate(minBitrate, maxBitrate), Throws.Exception.TypeOf<ArgumentException>());
UnityEngine.Object.DestroyImmediate(go);
}
[Test]
public void SetCodec()
{
var go = new GameObject();
var sender = go.AddComponent<AudioStreamSender>();
Assert.That(sender.codec, Is.Null);
var codecs = AudioStreamSender.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Empty);
var codec = codecs.First();
sender.SetCodec(codec);
Assert.That(sender.codec, Is.EqualTo(codec));
sender.SetCodec(null);
Assert.That(sender.codec, Is.Null);
}
[Test]
public void SetData()
{
var go = new GameObject();
var sender = go.AddComponent<AudioStreamSender>();
NativeArray<float> nativeArray = new NativeArray<float>(256, Allocator.Temp);
Assert.That(() => sender.SetData(nativeArray.AsReadOnly(), 2), Throws.Exception.TypeOf<InvalidOperationException>());
sender.source = AudioStreamSource.APIOnly;
Assert.That(() => sender.SetData(nativeArray.AsReadOnly(), 2), Throws.Nothing);
nativeArray.Dispose();
}
}
class AudioStreamReceiverTest
{
[Test]
public void GetAvailableCodec()
{
IEnumerable<AudioCodecInfo> codecs = AudioStreamReceiver.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Empty);
foreach (var codec in codecs)
{
Assert.That(codec.name, Is.Not.Empty);
Assert.That(codec.mimeType, Is.Not.Empty);
Assert.That(codec.channelCount, Is.GreaterThan(0));
Assert.That(codec.sampleRate, Is.GreaterThan(0));
}
}
[Test]
public void SelectCodecCapabilities()
{
var codecs = AudioStreamReceiver.GetAvailableCodecs();
var caps = RTCRtpReceiver.GetCapabilities(TrackKind.Audio).SelectCodecCapabilities(codecs);
Assert.That(codecs.Count(), Is.EqualTo(caps.Count()));
}
[Test]
public void SetCodec()
{
var go = new GameObject();
var receiver = go.AddComponent<AudioStreamReceiver>();
Assert.That(receiver.codec, Is.Null);
var codecs = AudioStreamReceiver.GetAvailableCodecs();
Assert.That(codecs, Is.Not.Empty);
var codec = codecs.First();
receiver.SetCodec(codec);
Assert.That(receiver.codec, Is.EqualTo(codec));
receiver.SetCodec(null);
Assert.That(receiver.codec, Is.Null);
}
}
class InputSenderTest
{
[Test]
public void SetChannel()
{
var go = new GameObject();
var sender = go.AddComponent<InputSender>();
Assert.That(sender.Channel, Is.Null);
Assert.That(() => sender.SetChannel(null), Throws.Exception.TypeOf<NullReferenceException>());
sender.enabled = false;
sender.enabled = true;
sender.SetChannel(null, null);
var connection = new RTCPeerConnection();
var channel = connection.CreateDataChannel("test");
sender.SetChannel(null, channel);
UnityEngine.Object.DestroyImmediate(go);
channel.Dispose();
connection.Dispose();
}
}
class InputReceiverTest
{
[Test]
public void SetChannel()
{
var go = new GameObject();
go.SetActive(false);
var receiver = go.AddComponent<InputReceiver>();
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var mapName = "test";
asset.AddActionMap(mapName);
receiver.actions = asset;
go.SetActive(true);
Assert.That(receiver.Channel, Is.Null);
Assert.That(() => receiver.SetChannel(null), Throws.Exception.TypeOf<NullReferenceException>());
receiver.enabled = false;
receiver.enabled = true;
Assert.That(receiver.inputIsActive, Is.True);
Assert.That(receiver.user.id, Is.Not.EqualTo(InputUser.InvalidId));
Assert.That(receiver.devices, Is.Empty);
Assert.That(receiver.defaultActionMap, Is.Null);
Assert.That(receiver.currentActionMap, Is.Null);
receiver.currentActionMap = new InputActionMap();
Assert.That(receiver.actionEvents, Is.Not.Null);
receiver.actionEvents = new PlayerInput.ActionEvent[] { };
receiver.SwitchCurrentActionMap(mapName);
var device = UnityEngine.InputSystem.InputSystem.devices.First();
receiver.PerformPairingWithDevice(device);
receiver.PerformPairingWithAllLocalDevices();
receiver.UnpairDevices(device);
receiver.SetChannel(null, null);
UnityEngine.Object.DestroyImmediate(asset);
UnityEngine.Object.DestroyImmediate(go);
}
[Test]
public void InputUserId()
{
var go = new GameObject();
go.SetActive(false);
var receiver = go.AddComponent<InputReceiver>();
// user.id is InputUser.InvalidId in default.
Assert.That(receiver.actions, Is.Null);
Assert.That(receiver.user.id, Is.EqualTo(InputUser.InvalidId));
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var mapName = "test";
asset.AddActionMap(mapName);
receiver.actions = asset;
// user.id is not InputUser.InvalidId after set actions parameter.
Assert.That(receiver.actions, Is.Not.Null);
Assert.That(receiver.user.id, Is.EqualTo(InputUser.InvalidId));
UnityEngine.Object.DestroyImmediate(go);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e6e8a10ad70dba54c898e9444c0edd13
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using System;
using System.Threading;
using UnityEngine;
namespace Unity.RenderStreaming.RuntimeTest
{
internal class TestUtility
{
const string FileNameWebAppForMac = "webserver_mac";
const string FileNameWebAppForLinux = "webserver";
const string FileNameWebAppForWin = "webserver.exe";
public const int PortNumber = 8081;
public static string GetFileName()
{
switch (Application.platform)
{
case RuntimePlatform.OSXEditor:
case RuntimePlatform.OSXPlayer:
return FileNameWebAppForMac;
case RuntimePlatform.WindowsPlayer:
case RuntimePlatform.WindowsEditor:
return FileNameWebAppForWin;
case RuntimePlatform.LinuxPlayer:
case RuntimePlatform.LinuxEditor:
return FileNameWebAppForLinux;
default:
throw new ArgumentOutOfRangeException($"this platform ({Application.platform} does not support.");
}
}
public static bool Wait(Func<bool> condition, int millisecondsTimeout = 1000, int millisecondsInterval = 100)
{
if (millisecondsTimeout < millisecondsInterval)
{
throw new ArgumentException();
}
int time = 0;
while (!condition() && millisecondsTimeout > time)
{
Thread.Sleep(millisecondsInterval);
time += millisecondsInterval;
}
return millisecondsTimeout > time;
}
public static string GetWebAppLocationFromEnv()
{
var path = Environment.GetEnvironmentVariable("WEBAPP_PATH");
if (!string.IsNullOrEmpty(path))
{
return path;
}
var args = Environment.GetCommandLineArgs();
for (int i = 0; i < args.Length; i++)
{
if (args[i] == "-webapp-path")
{
return args[i + 1];
}
}
return null;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8d9a35324d004430a70f4b1b4f5bf534
timeCreated: 1607479264

View File

@@ -0,0 +1,39 @@
{
"name": "Unity.RenderStreaming.RuntimeTests",
"rootNamespace": "",
"references": [
"GUID:27619889b8ba8c24980f49ee34dbb44a",
"GUID:0acc523941302664db1f4e527237feb3",
"GUID:40a5acf76f04c4c8ebb69605e4b0d5c7",
"GUID:f12aafacab75a87499e7e45c873ffab8",
"GUID:7e479a0c97f111c48b6a279fad867f28",
"GUID:75469ad4d38634e559750d17036d5f7c",
"GUID:dc04f38471c3a459fb4d31124ee9127d"
],
"includePlatforms": [
"Android",
"Editor",
"iOS",
"LinuxStandalone64",
"macOSStandalone",
"WindowsStandalone64"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": true,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [
{
"name": "com.unity.inputsystem",
"expression": "1.1",
"define": "INPUTSYSTEM_1_1_OR_NEWER"
}
],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c7e1d59dddfa048259f54a3cf80eafc5
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: