160 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using UnityEditor.Networking.PlayerConnection;
 | |
| using UnityEditor.TestTools.TestRunner;
 | |
| using UnityEditor.TestTools.TestRunner.Api;
 | |
| using UnityEngine;
 | |
| using UnityEngine.Networking.PlayerConnection;
 | |
| using UnityEngine.TestRunner.TestLaunchers;
 | |
| 
 | |
| namespace UnityEditor.TestRunner.TestLaunchers
 | |
| {
 | |
|     [Serializable]
 | |
|     internal class RemoteTestRunController : ScriptableSingleton<RemoteTestRunController>
 | |
|     {
 | |
|         internal const int k_HeartbeatTimeout = 60 * 10;
 | |
| 
 | |
|         [SerializeField]
 | |
|         internal bool isRunning;
 | |
| 
 | |
|         [SerializeField]
 | |
|         private bool m_RegisteredConnectionCallbacks;
 | |
| 
 | |
|         [SerializeField]
 | |
|         private int m_HearbeatTimeOut;
 | |
| 
 | |
|         private enum MessageType
 | |
|         {
 | |
|             TestStarted,
 | |
|             TestFinished,
 | |
|             RunStarted,
 | |
|             RunFinished
 | |
|         }
 | |
|         [Serializable]
 | |
|         private struct Message
 | |
|         {
 | |
|             public MessageEventArgs MessageArgs;
 | |
|             public MessageType Type;
 | |
| 
 | |
|             public Message(MessageEventArgs messageArgs, MessageType type)
 | |
|             {
 | |
|                 MessageArgs = messageArgs;
 | |
|                 Type = type;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [SerializeField]
 | |
|         private List<Message> m_IncomingMessages = new List<Message>();
 | |
| 
 | |
|         [SerializeField]
 | |
|         private bool m_RegisteredMessageCallback;
 | |
| 
 | |
|         private TestTools.TestRunner.DelayedCallback m_TimeoutCallback;
 | |
| 
 | |
|         public void Init(BuildTarget buildTarget, int heartbeatTimeout)
 | |
|         {
 | |
|             isRunning = true;
 | |
|             m_HearbeatTimeOut = heartbeatTimeout;
 | |
|             EditorConnection.instance.Initialize();
 | |
|             if (!m_RegisteredConnectionCallbacks)
 | |
|             {
 | |
|                 EditorConnection.instance.Initialize();
 | |
|                 DelegateEditorConnectionEvents();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void DelegateEditorConnectionEvents()
 | |
|         {
 | |
|             m_RegisteredConnectionCallbacks = true;
 | |
|             //This is needed because RemoteTestResultReceiver is not a ScriptableObject
 | |
|             EditorConnection.instance.Register(PlayerConnectionMessageIds.playerAliveHeartbeat, PlayerAliveHeartbeat);
 | |
| 
 | |
|             // When a message comes in, we should not immediately process it but instead enqueue it for processing later
 | |
|             // in the frame. The problem this solves is that Unity only reserves about 1ms worth of time every frame to
 | |
|             // process message from the player connection. When some tests run in a player, it can take the editor
 | |
|             // minutes to react to all messages we receive because we only do 1ms of processing, then render all of the
 | |
|             // editor etc. -- Instead, we use that 1ms time-window to enqueue messages and then react to them later
 | |
|             // during the frame. This reduces the waiting time from minutes to seconds.
 | |
|             EditorConnection.instance.Register(PlayerConnectionMessageIds.testStartedMessageId, args => EnqueueMessage(new Message(args, MessageType.TestStarted)));
 | |
|             EditorConnection.instance.Register(PlayerConnectionMessageIds.testFinishedMessageId, args => EnqueueMessage(new Message(args, MessageType.TestFinished)));
 | |
|             EditorConnection.instance.Register(PlayerConnectionMessageIds.runStartedMessageId, args => EnqueueMessage(new Message(args, MessageType.RunStarted)));
 | |
|             EditorConnection.instance.Register(PlayerConnectionMessageIds.runFinishedMessageId, args => EnqueueMessage(new Message(args, MessageType.RunFinished)));
 | |
|         }
 | |
| 
 | |
|         private void FlushMessageQueue()
 | |
|         {
 | |
|             EditorApplication.update -= FlushMessageQueue;
 | |
|             m_RegisteredMessageCallback = false;
 | |
|             foreach (var msg in m_IncomingMessages)
 | |
|             {
 | |
|                 switch (msg.Type)
 | |
|                 {
 | |
|                     case MessageType.TestFinished:
 | |
|                     {
 | |
|                         CallbacksDelegator.instance.TestFinishedRemotely(msg.MessageArgs.data);
 | |
|                         break;
 | |
|                     }
 | |
|                     case MessageType.TestStarted:
 | |
|                     {
 | |
|                         CallbacksDelegator.instance.TestStartedRemotely(msg.MessageArgs.data);
 | |
|                         break;
 | |
|                     }
 | |
|                     case MessageType.RunStarted:
 | |
|                     {
 | |
|                         RunStarted(msg.MessageArgs);
 | |
|                         break;
 | |
|                     }
 | |
|                     case MessageType.RunFinished:
 | |
|                     {
 | |
|                         RunFinished(msg.MessageArgs);
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             m_IncomingMessages.Clear();
 | |
|         }
 | |
| 
 | |
|         private void EnqueueMessage(Message message)
 | |
|         {
 | |
|             m_TimeoutCallback?.Reset();
 | |
|             if (!m_RegisteredMessageCallback)
 | |
|             {
 | |
|                 EditorApplication.update += FlushMessageQueue;
 | |
|                 m_RegisteredMessageCallback = true;
 | |
|             }
 | |
|             m_IncomingMessages.Add(message);
 | |
|         }
 | |
| 
 | |
|         private void RunStarted(MessageEventArgs messageEventArgs)
 | |
|         {
 | |
|             m_TimeoutCallback?.Reset();
 | |
|             CallbacksDelegator.instance.RunStartedRemotely(messageEventArgs.data);
 | |
|         }
 | |
| 
 | |
|         private void RunFinished(MessageEventArgs messageEventArgs)
 | |
|         {
 | |
|             m_TimeoutCallback?.Clear();
 | |
|             EditorConnection.instance.Send(PlayerConnectionMessageIds.quitPlayerMessageId, null, messageEventArgs.playerId);
 | |
|             EditorConnection.instance.DisconnectAll();
 | |
| 
 | |
|             CallbacksDelegator.instance.RunFinishedRemotely(messageEventArgs.data);
 | |
|             isRunning = false;
 | |
|         }
 | |
| 
 | |
|         private void PlayerAliveHeartbeat(MessageEventArgs messageEventArgs)
 | |
|         {
 | |
|             m_TimeoutCallback?.Reset();
 | |
|         }
 | |
| 
 | |
|         private void TimeoutCallback()
 | |
|         {
 | |
|             CallbacksDelegator.instance.RunFailed($"Test execution timed out. No activity received from the player in {m_HearbeatTimeOut} seconds.");
 | |
|         }
 | |
| 
 | |
|         public void PostSuccessfulBuildAction()
 | |
|         {
 | |
|             m_TimeoutCallback = new TestTools.TestRunner.DelayedCallback(TimeoutCallback, m_HearbeatTimeOut);
 | |
|         }
 | |
|     }
 | |
| }
 |