From 2da9430478673b0d77b0d1b691ad13eaef87e2b6 Mon Sep 17 00:00:00 2001 From: Kuniwak Date: Thu, 9 Nov 2023 15:28:37 +0900 Subject: [PATCH] Fix not stopping and reporting when error occurred --- Runtime/AgentDispatcher.cs | 15 +++++++-- Runtime/Autopilot.cs | 36 +++++++++++++++++---- Tests/Runtime/TestDoubles/StubClickAgent.cs | 9 ++++++ 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/Runtime/AgentDispatcher.cs b/Runtime/AgentDispatcher.cs index 3046046..1352ae3 100644 --- a/Runtime/AgentDispatcher.cs +++ b/Runtime/AgentDispatcher.cs @@ -1,6 +1,7 @@ // Copyright (c) 2023 DeNA Co., Ltd. // This software is released under the MIT License. +using System; using DeNA.Anjin.Agents; using DeNA.Anjin.Settings; using DeNA.Anjin.Utilities; @@ -106,9 +107,17 @@ private void DispatchAgent(AbstractAgent agent) agent.Logger = _logger; agent.Random = _randomFactory.CreateRandom(); - agent.Run(token).Forget(); - - _logger.Log($"Agent {agentName} dispatched!"); + + try + { + agent.Run(token).Forget(); + _logger.Log($"Agent {agentName} dispatched!"); + } + catch (Exception e) + { + Debug.LogException(e); + _logger.Log($"Agent {agentName} dispatched but immediately threw an error!"); + } } } } diff --git a/Runtime/Autopilot.cs b/Runtime/Autopilot.cs index 7bf1ac7..c8cce8f 100644 --- a/Runtime/Autopilot.cs +++ b/Runtime/Autopilot.cs @@ -3,6 +3,8 @@ using System; using System.Collections; +using System.Threading; +using Cysharp.Threading.Tasks; using DeNA.Anjin.Reporters; using DeNA.Anjin.Settings; using DeNA.Anjin.Utilities; @@ -62,15 +64,18 @@ private void Start() _randomFactory = new RandomFactory(seed); _logger.Log($"Random seed is {seed}"); + + // NOTE: Registering logMessageReceived must be placed before DispatchByScene. + // Because some agent can throw an error immediately, so reporter can miss the error if + // registering logMessageReceived is placed after DispatchByScene. + _reporter = new SlackReporter(_settings, new SlackAPI()); + _logMessageHandler = new LogMessageHandler(_settings, _reporter); + Application.logMessageReceived += _logMessageHandler.HandleLog; _dispatcher = new AgentDispatcher(_settings, _logger, _randomFactory); _dispatcher.DispatchByScene(SceneManager.GetActiveScene()); SceneManager.activeSceneChanged += _dispatcher.DispatchByScene; - _reporter = new SlackReporter(_settings, new SlackAPI()); - _logMessageHandler = new LogMessageHandler(_settings, _reporter); - Application.logMessageReceived += _logMessageHandler.HandleLog; - if (_settings.lifespanSec > 0) { StartCoroutine(Lifespan(_settings.lifespanSec)); @@ -92,7 +97,7 @@ private void Start() private IEnumerator Lifespan(int timeoutSec) { yield return new WaitForSecondsRealtime(timeoutSec); - Terminate(ExitCode.Normally); + yield return UniTask.ToCoroutine(() => TerminateAsync(ExitCode.Normally)); } /// @@ -101,7 +106,8 @@ private IEnumerator Lifespan(int timeoutSec) /// Exit code for Unity Editor /// Log message string when terminate by the log message /// Stack trace when terminate by the log message - public void Terminate(ExitCode exitCode, string logString = null, string stackTrace = null) + /// A task awaits termination get completed + public async UniTask TerminateAsync(ExitCode exitCode, string logString = null, string stackTrace = null, CancellationToken token = default) { if (_dispatcher != null) { @@ -132,12 +138,30 @@ public void Terminate(ExitCode exitCode, string logString = null, string stackTr // Terminate when ran specified time. _logger.Log("Stop playing by autopilot"); _state.exitCode = exitCode; + // XXX: Avoid a problem that Editor stay playing despite isPlaying get assigned false. + // SEE: https://github.com/DeNA/Anjin/issues/20 + // XXX: To keep current signature, dont use await + await UniTask.DelayFrame(1, cancellationToken: token); UnityEditor.EditorApplication.isPlaying = false; // Call Launcher.OnChangePlayModeState() so terminates Unity editor, when launch from CLI. #else _logger.Log($"Exit Unity-editor by autopilot, exit code={exitCode}"); Application.Quit((int)exitCode); + await UniTask.CompletedTask; #endif } + + + /// + /// Terminate autopilot + /// + /// Exit code for Unity Editor + /// Log message string when terminate by the log message + /// Stack trace when terminate by the log message + [Obsolete("Use " + nameof(TerminateAsync))] + public void Terminate(ExitCode exitCode, string logString = null, string stackTrace = null) + { + TerminateAsync(exitCode, logString, stackTrace).Forget(); + } } } diff --git a/Tests/Runtime/TestDoubles/StubClickAgent.cs b/Tests/Runtime/TestDoubles/StubClickAgent.cs index d384def..e03b57e 100644 --- a/Tests/Runtime/TestDoubles/StubClickAgent.cs +++ b/Tests/Runtime/TestDoubles/StubClickAgent.cs @@ -9,10 +9,18 @@ namespace DeNA.Anjin.TestDoubles { + /// + /// A test double for agent. This agent immediately click game objects that have the name specified + /// public class StubClickAgent : AbstractAgent { + /// + /// A name of game objects to click + /// [SerializeField] public string targetName; + + /// public override UniTask Run(CancellationToken token) { foreach (var obj in FindObjectsByType(FindObjectsSortMode.None)) @@ -33,6 +41,7 @@ public override UniTask Run(CancellationToken token) handler.OnPointerClick(new PointerEventData(EventSystem.current)); } + return UniTask.CompletedTask; } }