SignalingTest.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. using System;
  2. using System.Collections;
  3. using System.Threading;
  4. using System.Diagnostics;
  5. using NUnit.Framework;
  6. using Unity.RenderStreaming.RuntimeTest.Signaling;
  7. using Unity.RenderStreaming.Signaling;
  8. using Unity.WebRTC;
  9. using UnityEngine;
  10. using UnityEngine.TestTools;
  11. using Debug = UnityEngine.Debug;
  12. namespace Unity.RenderStreaming.RuntimeTest
  13. {
  14. [TestFixture(typeof(WebSocketSignaling))]
  15. [TestFixture(typeof(HttpSignaling))]
  16. [TestFixture(typeof(MockSignaling))]
  17. [UnityPlatform(exclude = new[] {RuntimePlatform.IPhonePlayer})]
  18. [ConditionalIgnore(ConditionalIgnore.IL2CPP, "Process.Start does not implement in IL2CPP.")]
  19. class SignalingTest : IPrebuildSetup
  20. {
  21. private readonly Type m_SignalingType;
  22. private Process m_ServerProcess;
  23. private RTCSessionDescription m_DescOffer;
  24. private RTCSessionDescription m_DescAnswer;
  25. private RTCIceCandidate m_candidate;
  26. private SynchronizationContext m_Context;
  27. private ISignaling signaling1;
  28. private ISignaling signaling2;
  29. public SignalingTest()
  30. {
  31. }
  32. public SignalingTest(Type type)
  33. {
  34. m_SignalingType = type;
  35. }
  36. public void Setup()
  37. {
  38. if (m_SignalingType == typeof(MockSignaling))
  39. {
  40. return;
  41. }
  42. #if UNITY_EDITOR
  43. string dir = System.IO.Directory.GetCurrentDirectory();
  44. string fileName = System.IO.Path.Combine(dir, Editor.WebAppDownloader.GetFileName());
  45. if (System.IO.File.Exists(fileName) || System.IO.File.Exists(TestUtility.GetWebAppLocationFromEnv()))
  46. {
  47. // already exists.
  48. return;
  49. }
  50. bool downloadRaised = false;
  51. Editor.WebAppDownloader.DownloadCurrentVersionWebApp(dir, success => { downloadRaised = true; });
  52. TestUtility.Wait(() => downloadRaised, 10000);
  53. #endif
  54. }
  55. [OneTimeSetUp]
  56. public void OneTimeSetUp()
  57. {
  58. if (m_SignalingType == typeof(MockSignaling))
  59. {
  60. MockSignaling.Reset(false);
  61. return;
  62. }
  63. m_ServerProcess = new Process();
  64. string fileName = TestUtility.GetWebAppLocationFromEnv();
  65. if (string.IsNullOrEmpty(fileName))
  66. {
  67. Debug.Log($"webapp file not found in {fileName}");
  68. string dir = System.IO.Directory.GetCurrentDirectory();
  69. fileName = System.IO.Path.Combine(dir, TestUtility.GetFileName());
  70. }
  71. Assert.IsTrue(System.IO.File.Exists(fileName), $"webapp file not found in {fileName}");
  72. ProcessStartInfo startInfo = new ProcessStartInfo
  73. {
  74. WindowStyle = ProcessWindowStyle.Hidden,
  75. FileName = fileName,
  76. UseShellExecute = false
  77. };
  78. string arguments = $"-p {TestUtility.PortNumber}";
  79. if (m_SignalingType == typeof(HttpSignaling))
  80. {
  81. arguments += " -t http";
  82. }
  83. startInfo.Arguments = arguments;
  84. m_ServerProcess.StartInfo = startInfo;
  85. m_ServerProcess.OutputDataReceived += (sender, e) => { Debug.Log(e.Data); };
  86. bool success = m_ServerProcess.Start();
  87. Assert.True(success);
  88. }
  89. [OneTimeTearDown]
  90. public void OneTimeTearDown()
  91. {
  92. if (m_SignalingType == typeof(MockSignaling))
  93. {
  94. return;
  95. }
  96. m_ServerProcess?.Kill();
  97. m_ServerProcess?.WaitForExit();
  98. m_ServerProcess?.Dispose();
  99. m_ServerProcess = null;
  100. }
  101. ISignaling CreateSignaling(Type type, SynchronizationContext mainThread)
  102. {
  103. if (type == typeof(WebSocketSignaling))
  104. {
  105. var settings = new WebSocketSignalingSettings
  106. (
  107. url: $"ws://localhost:{TestUtility.PortNumber}"
  108. );
  109. return new WebSocketSignaling(settings, mainThread);
  110. }
  111. if (type == typeof(HttpSignaling))
  112. {
  113. var settings = new HttpSignalingSettings
  114. (
  115. url: $"http://localhost:{TestUtility.PortNumber}",
  116. interval: 100
  117. );
  118. return new HttpSignaling(settings, mainThread);
  119. }
  120. if (type == typeof(MockSignaling))
  121. {
  122. return new MockSignaling();
  123. }
  124. throw new ArgumentException();
  125. }
  126. [UnitySetUp, Timeout(1000)]
  127. public IEnumerator UnitySetUp()
  128. {
  129. RTCConfiguration config = default;
  130. RTCIceCandidate candidate_ = null;
  131. config.iceServers = new[] {new RTCIceServer {urls = new[] {"stun:stun.l.google.com:19302"}}};
  132. var peer1 = new RTCPeerConnection(ref config);
  133. var peer2 = new RTCPeerConnection(ref config);
  134. peer1.OnIceCandidate = candidate => { candidate_ = candidate; };
  135. AudioStreamTrack track = new AudioStreamTrack();
  136. peer1.AddTrack(track);
  137. var op1 = peer1.CreateOffer();
  138. yield return op1;
  139. m_DescOffer = op1.Desc;
  140. var op2 = peer1.SetLocalDescription(ref m_DescOffer);
  141. yield return op2;
  142. var op3 = peer2.SetRemoteDescription(ref m_DescOffer);
  143. yield return op3;
  144. var op4 = peer2.CreateAnswer();
  145. yield return op4;
  146. m_DescAnswer = op4.Desc;
  147. var op5 = peer2.SetLocalDescription(ref m_DescAnswer);
  148. yield return op5;
  149. var op6 = peer1.SetRemoteDescription(ref m_DescAnswer);
  150. yield return op6;
  151. yield return new WaitUntil(() => candidate_ != null);
  152. m_candidate = candidate_;
  153. track.Dispose();
  154. peer1.Close();
  155. peer2.Close();
  156. m_Context = SynchronizationContext.Current;
  157. signaling1 = CreateSignaling(m_SignalingType, m_Context);
  158. signaling2 = CreateSignaling(m_SignalingType, m_Context);
  159. }
  160. [TearDown]
  161. public void TearDown()
  162. {
  163. signaling1.Stop();
  164. signaling2.Stop();
  165. m_Context = null;
  166. }
  167. [UnityTest, Timeout(10000)]
  168. public IEnumerator OnConnect()
  169. {
  170. bool startRaised1 = false;
  171. string connectionId1 = null;
  172. signaling1.OnStart += s => { startRaised1 = true; };
  173. signaling1.Start();
  174. yield return new WaitUntil(() => startRaised1);
  175. signaling1.OnCreateConnection += (s, connectionId, polite) => { connectionId1 = connectionId; };
  176. signaling1.OpenConnection(Guid.NewGuid().ToString());
  177. yield return new WaitUntil(() => !string.IsNullOrEmpty(connectionId1));
  178. Assert.IsNotEmpty(connectionId1);
  179. }
  180. [UnityTest, Timeout(10000)]
  181. public IEnumerator OnOffer()
  182. {
  183. bool startRaised1 = false;
  184. bool startRaised2 = false;
  185. bool offerRaised = false;
  186. bool raiseOnDestroy1 = false;
  187. bool raiseOnDestroy2 = false;
  188. string connectionId1 = null;
  189. string connectionId2 = null;
  190. const string _connectionId = "12345";
  191. signaling1.OnStart += s => { startRaised1 = true; };
  192. signaling2.OnStart += s => { startRaised2 = true; };
  193. signaling1.Start();
  194. signaling2.Start();
  195. yield return new WaitUntil(() => startRaised1 && startRaised2);
  196. signaling1.OnCreateConnection += (s, connectionId, polite) => {
  197. connectionId1 = connectionId;
  198. };
  199. signaling1.OnDestroyConnection += (signaling, id) =>
  200. {
  201. raiseOnDestroy1 = id == connectionId1;
  202. };
  203. signaling1.OpenConnection(_connectionId);
  204. signaling2.OnCreateConnection += (s, connectionId, polite) => {
  205. connectionId2 = connectionId;
  206. };
  207. signaling1.OnDestroyConnection += (signaling, id) =>
  208. {
  209. raiseOnDestroy2 = id == connectionId2;
  210. };
  211. signaling2.OpenConnection(_connectionId);
  212. yield return new WaitUntil(() =>
  213. !string.IsNullOrEmpty(connectionId1) && !string.IsNullOrEmpty(connectionId2));
  214. Assert.That(connectionId1, Is.EqualTo(_connectionId));
  215. Assert.That(connectionId2, Is.EqualTo(_connectionId));
  216. signaling2.OnOffer += (s, e) => { offerRaised = true; };
  217. signaling1.SendOffer(connectionId1, m_DescOffer);
  218. yield return new WaitUntil(() => offerRaised);
  219. signaling1.CloseConnection(connectionId1);
  220. yield return new WaitUntil(() => raiseOnDestroy1 && raiseOnDestroy2);
  221. Assert.That(raiseOnDestroy1, Is.True);
  222. Assert.That(raiseOnDestroy2, Is.True);
  223. }
  224. [UnityTest, Timeout(10000)]
  225. public IEnumerator OnAnswer()
  226. {
  227. bool startRaised1 = false;
  228. bool startRaised2 = false;
  229. bool offerRaised = false;
  230. bool answerRaised = false;
  231. string connectionId1 = null;
  232. string connectionId2 = null;
  233. signaling1.OnStart += s => { startRaised1 = true; };
  234. signaling2.OnStart += s => { startRaised2 = true; };
  235. signaling1.Start();
  236. signaling2.Start();
  237. yield return new WaitUntil(() => startRaised1 && startRaised2);
  238. signaling1.OnCreateConnection += (s, connectionId, polite) => {
  239. connectionId1 = connectionId;
  240. };
  241. signaling1.OpenConnection(Guid.NewGuid().ToString());
  242. signaling2.OnCreateConnection += (s, connectionId, polite) => {
  243. connectionId2 = connectionId;
  244. };
  245. signaling2.OpenConnection(Guid.NewGuid().ToString());
  246. yield return new WaitUntil(() =>
  247. !string.IsNullOrEmpty(connectionId1) && !string.IsNullOrEmpty(connectionId2));
  248. signaling2.OnOffer += (s, e) => { offerRaised = true; };
  249. signaling1.SendOffer(connectionId1, m_DescOffer);
  250. yield return new WaitUntil(() => offerRaised);
  251. signaling1.OnAnswer += (s, e) => { answerRaised = true; };
  252. signaling2.SendAnswer(connectionId1, m_DescAnswer);
  253. yield return new WaitUntil(() => answerRaised);
  254. }
  255. [UnityTest, Timeout(10000)]
  256. public IEnumerator OnCandidate()
  257. {
  258. bool startRaised1 = false;
  259. bool startRaised2 = false;
  260. bool offerRaised = false;
  261. bool answerRaised = false;
  262. bool candidateRaised1 = false;
  263. bool candidateRaised2 = false;
  264. string connectionId1 = null;
  265. string connectionId2 = null;
  266. signaling1.OnStart += s => { startRaised1 = true; };
  267. signaling2.OnStart += s => { startRaised2 = true; };
  268. signaling1.Start();
  269. signaling2.Start();
  270. yield return new WaitUntil(() => startRaised1 && startRaised2);
  271. signaling1.OnCreateConnection += (s, connectionId, polite) => {
  272. connectionId1 = connectionId;
  273. };
  274. signaling1.OpenConnection(Guid.NewGuid().ToString());
  275. signaling2.OnCreateConnection += (s, connectionId, polite) => {
  276. connectionId2 = connectionId;
  277. };
  278. signaling2.OpenConnection(Guid.NewGuid().ToString());
  279. yield return new WaitUntil(() =>
  280. !string.IsNullOrEmpty(connectionId1) && !string.IsNullOrEmpty(connectionId2));
  281. signaling2.OnOffer += (s, e) => { offerRaised = true; };
  282. signaling1.SendOffer(connectionId1, m_DescOffer);
  283. yield return new WaitUntil(() => offerRaised);
  284. signaling1.OnAnswer += (s, e) => { answerRaised = true; };
  285. signaling2.SendAnswer(connectionId1, m_DescAnswer);
  286. yield return new WaitUntil(() => answerRaised);
  287. signaling2.OnIceCandidate += (s, e) => { candidateRaised1 = true; };
  288. signaling1.SendCandidate(connectionId1, m_candidate);
  289. yield return new WaitUntil(() => candidateRaised1);
  290. signaling1.OnIceCandidate += (s, e) => { candidateRaised2 = true; };
  291. signaling2.SendCandidate(connectionId1, m_candidate);
  292. yield return new WaitUntil(() => candidateRaised2);
  293. }
  294. }
  295. }