From d9f2984b9c5dcb19dae1d7f7ad48adf237ac70e7 Mon Sep 17 00:00:00 2001 From: Koji Hasegawa Date: Sun, 21 Apr 2024 02:27:22 +0900 Subject: [PATCH] Fix agents do not output exit log if canceled --- Runtime/Agents/DoNothingAgent.cs | 17 +++++--- Runtime/Agents/EmergencyExitAgent.cs | 33 ++++++++------- Runtime/Agents/OneTimeAgent.cs | 11 +++-- Runtime/Agents/ParallelCompositeAgent.cs | 11 +++-- Runtime/Agents/RepeatAgent.cs | 17 +++++--- Runtime/Agents/SerialCompositeAgent.cs | 17 +++++--- Runtime/Agents/TimeBombAgent.cs | 42 +++++++++++-------- Runtime/Agents/UGUIMonkeyAgent.cs | 10 ++++- Tests/Runtime/Agents/DoNothingAgentTest.cs | 16 ++++--- .../Runtime/Agents/EmergencyExitAgentTest.cs | 39 +++++++++++------ Tests/Runtime/Agents/OneTimeAgentTest.cs | 28 ++++++++----- .../Agents/ParallelCompositeAgentTest.cs | 32 +++++++++----- Tests/Runtime/Agents/RepeatAgentTest.cs | 32 +++++++------- .../Agents/SerialCompositeAgentTest.cs | 32 +++++++++----- Tests/Runtime/Agents/TimeBombAgentTest.cs | 19 +++++++-- Tests/Runtime/Agents/UGUIMonkeyAgentTest.cs | 17 ++++---- Tests/Runtime/Agents/UGUIPlaybackAgentTest.cs | 15 ++++--- 17 files changed, 248 insertions(+), 140 deletions(-) diff --git a/Runtime/Agents/DoNothingAgent.cs b/Runtime/Agents/DoNothingAgent.cs index 8c9e6be..979a5ff 100644 --- a/Runtime/Agents/DoNothingAgent.cs +++ b/Runtime/Agents/DoNothingAgent.cs @@ -24,16 +24,21 @@ public override async UniTask Run(CancellationToken token) { Logger.Log($"Enter {this.name}.Run()"); - if (lifespanSec > 0) + try { - await UniTask.Delay(TimeSpan.FromSeconds(lifespanSec), cancellationToken: token); + if (lifespanSec > 0) + { + await UniTask.Delay(TimeSpan.FromSeconds(lifespanSec), cancellationToken: token); + } + else + { + await UniTask.WaitWhile(() => true, cancellationToken: token); // Wait indefinitely + } } - else + finally { - await UniTask.WaitWhile(() => true, cancellationToken: token); // 無期限に待機 + Logger.Log($"Exit {this.name}.Run()"); } - - Logger.Log($"Exit {this.name}.Run()"); } } } diff --git a/Runtime/Agents/EmergencyExitAgent.cs b/Runtime/Agents/EmergencyExitAgent.cs index 12d0ac7..38bcca5 100644 --- a/Runtime/Agents/EmergencyExitAgent.cs +++ b/Runtime/Agents/EmergencyExitAgent.cs @@ -26,27 +26,32 @@ public override async UniTask Run(CancellationToken token) Logger.Log($"Enter {this.name}.Run()"); var selectables = new Selectable[20]; - while (true) + try { - var allSelectableCount = Selectable.allSelectableCount; - if (selectables.Length < allSelectableCount) + while (true) // Note: This agent is not terminate myself { - selectables = new Selectable[allSelectableCount]; - } + var allSelectableCount = Selectable.allSelectableCount; + if (selectables.Length < allSelectableCount) + { + selectables = new Selectable[allSelectableCount]; + } - Selectable.AllSelectablesNoAlloc(selectables); - for (var i = 0; i < allSelectableCount; i++) - { - if (selectables[i].TryGetComponent(out var emergencyExit)) + Selectable.AllSelectablesNoAlloc(selectables); + for (var i = 0; i < allSelectableCount; i++) { - ClickEmergencyExitButton(emergencyExit); + if (selectables[i].TryGetComponent(out var emergencyExit)) + { + ClickEmergencyExitButton(emergencyExit); + } } - } - await UniTask.NextFrame(token); + await UniTask.NextFrame(token); + } + } + finally + { + Logger.Log($"Exit {this.name}.Run()"); } - - // Note: This agent is not terminate myself } [SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")] diff --git a/Runtime/Agents/OneTimeAgent.cs b/Runtime/Agents/OneTimeAgent.cs index 38e675c..2741f76 100644 --- a/Runtime/Agents/OneTimeAgent.cs +++ b/Runtime/Agents/OneTimeAgent.cs @@ -47,9 +47,14 @@ public override async UniTask Run(CancellationToken token) agent.Logger = Logger; agent.Random = Random; // This Agent does not consume pseudo-random numbers, so passed on as is. - await agent.Run(token); - - Logger.Log($"Exit {this.name}.Run()"); + try + { + await agent.Run(token); + } + finally + { + Logger.Log($"Exit {this.name}.Run()"); + } } } } diff --git a/Runtime/Agents/ParallelCompositeAgent.cs b/Runtime/Agents/ParallelCompositeAgent.cs index da1dc9e..de95d2d 100644 --- a/Runtime/Agents/ParallelCompositeAgent.cs +++ b/Runtime/Agents/ParallelCompositeAgent.cs @@ -28,9 +28,14 @@ public override async UniTask Run(CancellationToken token) tasks[i] = agent.Run(token); } - await UniTask.WhenAll(tasks); - - Logger.Log($"Exit {this.name}.Run()"); + try + { + await UniTask.WhenAll(tasks); + } + finally + { + Logger.Log($"Exit {this.name}.Run()"); + } } } } diff --git a/Runtime/Agents/RepeatAgent.cs b/Runtime/Agents/RepeatAgent.cs index aaec858..15934f6 100644 --- a/Runtime/Agents/RepeatAgent.cs +++ b/Runtime/Agents/RepeatAgent.cs @@ -26,14 +26,19 @@ public override async UniTask Run(CancellationToken token) { Logger.Log($"Enter {this.name}.Run()"); - while (true) + agent.Logger = Logger; + agent.Random = Random; // This Agent does not consume pseudo-random numbers, so passed on as is. + try { - agent.Logger = Logger; - agent.Random = Random; // This Agent does not consume pseudo-random numbers, so passed on as is. - await agent.Run(token); + while (true) // Note: This agent is not terminate myself + { + await agent.Run(token); + } + } + finally + { + Logger.Log($"Exit {this.name}.Run()"); } - - // Note: This agent is not terminate myself } } } diff --git a/Runtime/Agents/SerialCompositeAgent.cs b/Runtime/Agents/SerialCompositeAgent.cs index d950f61..5849c23 100644 --- a/Runtime/Agents/SerialCompositeAgent.cs +++ b/Runtime/Agents/SerialCompositeAgent.cs @@ -18,14 +18,19 @@ public override async UniTask Run(CancellationToken token) { Logger.Log($"Enter {this.name}.Run()"); - foreach (var agent in agents) + try { - agent.Logger = Logger; - agent.Random = RandomFactory.CreateRandom(); - await agent.Run(token); + foreach (var agent in agents) + { + agent.Logger = Logger; + agent.Random = RandomFactory.CreateRandom(); + await agent.Run(token); + } + } + finally + { + Logger.Log($"Exit {this.name}.Run()"); } - - Logger.Log($"Exit {this.name}.Run()"); } } } diff --git a/Runtime/Agents/TimeBombAgent.cs b/Runtime/Agents/TimeBombAgent.cs index 1c8b7a5..0b0a09a 100644 --- a/Runtime/Agents/TimeBombAgent.cs +++ b/Runtime/Agents/TimeBombAgent.cs @@ -63,34 +63,40 @@ public override async UniTask Run(CancellationToken token) { Logger.Log($"Enter {this.name}.Run()"); - using (var agentCts = new CancellationTokenSource()) // To cancel only the Working Agent. + try { - _cts = agentCts; - - using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(token, agentCts.Token)) + using (var agentCts = new CancellationTokenSource()) // To cancel only the Working Agent. { - try - { - agent.Logger = Logger; - agent.Random = Random; // This Agent does not consume pseudo-random numbers, so passed on as is. - await agent.Run(linkedCts.Token); + _cts = agentCts; - throw new TimeoutException( - $"Could not receive defuse message `{defuseMessage}` before the agent terminated."); - } - catch (OperationCanceledException e) + using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(token, agentCts.Token)) { - if (token.IsCancellationRequested) // The parent was cancelled. + try { - throw; + agent.Logger = Logger; + agent.Random = Random; + // Note: This Agent does not consume pseudo-random numbers, so passed on as is. + await agent.Run(linkedCts.Token); + + throw new TimeoutException( + $"Could not receive defuse message `{defuseMessage}` before the agent terminated."); } + catch (OperationCanceledException e) + { + if (token.IsCancellationRequested) // The parent was cancelled. + { + throw; + } - Logger.Log($"Working agent {agent.name} was cancelled."); + Logger.Log($"Working agent {agent.name} was cancelled."); + } } } } - - Logger.Log($"Exit {this.name}.Run()"); + finally + { + Logger.Log($"Exit {this.name}.Run()"); + } } } } diff --git a/Runtime/Agents/UGUIMonkeyAgent.cs b/Runtime/Agents/UGUIMonkeyAgent.cs index f985c6a..2bcff8c 100644 --- a/Runtime/Agents/UGUIMonkeyAgent.cs +++ b/Runtime/Agents/UGUIMonkeyAgent.cs @@ -127,9 +127,15 @@ public override async UniTask Run(CancellationToken token) randomString: new RandomStringImpl(random)), }, }; - await Monkey.Run(config, token); - Logger.Log($"Exit {this.name}.Run()"); + try + { + await Monkey.Run(config, token); + } + finally + { + Logger.Log($"Exit {this.name}.Run()"); + } } private RandomStringParameters GetRandomStringParameters(GameObject gameObject) diff --git a/Tests/Runtime/Agents/DoNothingAgentTest.cs b/Tests/Runtime/Agents/DoNothingAgentTest.cs index f941328..8e4f48e 100644 --- a/Tests/Runtime/Agents/DoNothingAgentTest.cs +++ b/Tests/Runtime/Agents/DoNothingAgentTest.cs @@ -1,13 +1,13 @@ // Copyright (c) 2023 DeNA Co., Ltd. // This software is released under the MIT License. -using System; using System.Threading; using System.Threading.Tasks; using Cysharp.Threading.Tasks; using DeNA.Anjin.Utilities; using NUnit.Framework; using UnityEngine; +using UnityEngine.TestTools; using Object = UnityEngine.Object; namespace DeNA.Anjin.Agents @@ -23,17 +23,18 @@ public async Task Run_cancelTask_stopAgent() agent.name = nameof(Run_cancelTask_stopAgent); agent.lifespanSec = 0; // Expect indefinite execution - var agentName = agent.GetType().Name; - var gameObject = new GameObject(agentName); + var gameObject = new GameObject(); var token = gameObject.GetCancellationTokenOnDestroy(); var task = agent.Run(token); - await UniTask.Delay(TimeSpan.FromSeconds(1), cancellationToken: token); + await UniTask.NextFrame(); Object.DestroyImmediate(gameObject); - // ReSharper disable once MethodSupportsCancellation await UniTask.NextFrame(); Assert.That(task.Status, Is.EqualTo(UniTaskStatus.Canceled)); + + LogAssert.Expect(LogType.Log, $"Enter {agent.name}.Run()"); + LogAssert.Expect(LogType.Log, $"Exit {agent.name}.Run()"); } [Test] @@ -49,10 +50,13 @@ public async Task Run_lifespanPassed_stopAgent() { var token = cancellationTokenSource.Token; var task = agent.Run(token); - await UniTask.Delay(TimeSpan.FromSeconds(2), cancellationToken: token); // Consider overhead + await UniTask.Delay(2000); // Consider overhead Assert.That(task.Status, Is.EqualTo(UniTaskStatus.Succeeded)); } + + LogAssert.Expect(LogType.Log, $"Enter {agent.name}.Run()"); + LogAssert.Expect(LogType.Log, $"Exit {agent.name}.Run()"); } } } diff --git a/Tests/Runtime/Agents/EmergencyExitAgentTest.cs b/Tests/Runtime/Agents/EmergencyExitAgentTest.cs index e88a569..dbff6b2 100644 --- a/Tests/Runtime/Agents/EmergencyExitAgentTest.cs +++ b/Tests/Runtime/Agents/EmergencyExitAgentTest.cs @@ -1,7 +1,6 @@ // Copyright (c) 2023 DeNA Co., Ltd. // This software is released under the MIT License. -using System; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; @@ -11,6 +10,7 @@ using DeNA.Anjin.Utilities; using NUnit.Framework; using UnityEngine; +using UnityEngine.TestTools; using UnityEngine.UI; using Object = UnityEngine.Object; @@ -27,24 +27,26 @@ public async Task Run_cancelTask_stopAgent() agent.Random = new RandomFactory(0).CreateRandom(); agent.name = nameof(Run_cancelTask_stopAgent); - var agentName = agent.GetType().Name; - var gameObject = new GameObject(agentName); + var gameObject = new GameObject(); var token = gameObject.GetCancellationTokenOnDestroy(); var task = agent.Run(token); - await UniTask.Delay(TimeSpan.FromMilliseconds(500), cancellationToken: token); + await UniTask.NextFrame(); Object.DestroyImmediate(gameObject); - // ReSharper disable once MethodSupportsCancellation await UniTask.NextFrame(); Assert.That(task.Status, Is.EqualTo(UniTaskStatus.Canceled)); + + LogAssert.Expect(LogType.Log, $"Enter {agent.name}.Run()"); + LogAssert.Expect(LogType.Log, $"Exit {agent.name}.Run()"); } - private static SpyButton CreateButtonWithEmergencyExitAnnotation() + private static SpyButton CreateButtonWithEmergencyExitAnnotation(bool interactable = true) { - var button = new GameObject(); - button.AddComponent(); - return button.AddComponent(); + var button = new GameObject().AddComponent(); + button.GetComponent().interactable = interactable; + button.gameObject.AddComponent(); + return button; } [Test] @@ -59,14 +61,20 @@ public async Task Run_existEmergencyExitButton_ClickEmergencyExitButton() { var token = cancellationTokenSource.Token; var task = agent.Run(token); - await UniTask.Delay(TimeSpan.FromMilliseconds(500), cancellationToken: token); + await UniTask.NextFrame(); var emergencyButton = CreateButtonWithEmergencyExitAnnotation(); await UniTask.NextFrame(token); Assert.That(emergencyButton.IsClicked, Is.True, "Clicked button with EmergencyExit annotation"); Assert.That(task.Status, Is.EqualTo(UniTaskStatus.Pending), "Keep agent running"); + + cancellationTokenSource.Cancel(); + await UniTask.NextFrame(); } + + LogAssert.Expect(LogType.Log, $"Enter {agent.name}.Run()"); + LogAssert.Expect(LogType.Log, $"Exit {agent.name}.Run()"); } [Test] @@ -81,15 +89,20 @@ public async Task Run_existNotInteractableEmergencyExitButton_DoesNotClickEmerge { var token = cancellationTokenSource.Token; var task = agent.Run(token); - await UniTask.Delay(TimeSpan.FromMilliseconds(500), cancellationToken: token); + await UniTask.NextFrame(); - var emergencyButton = CreateButtonWithEmergencyExitAnnotation(); - emergencyButton.gameObject.GetComponent