123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- 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);
- }
- }
- }
|