Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TerminateAgent #106

Merged
merged 3 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Editor/Localization/ja.po
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,33 @@ msgid "Log messages containing the specified strings will be ignored from the st
msgstr "指定された文字列を含むログメッセージは停止条件から無視されます。正規表現も使用できます。エスケープは単一のバックスラッシュ (\) です"


#: Editor/UI/Agents/TerminateAgentEditor.cs

# exitCode
msgid "Exit Code"
msgstr "終了コード"

# exitCode tooltip
msgid "Select the exit code used when this agent terminates the Autopilot."
msgstr "このAgentがオートパイロットを停止するときに使用される終了コードを選択します"

# customExitCode
msgid "Custom Exit Code"
msgstr "カスタム終了コード"

# customExitCode tooltip
msgid "Input exit code by integer value."
msgstr "終了コードを整数値で入力します"

# exitMessage
msgid "Message"
msgstr "メッセージ"

# exitMessage tooltip
msgid "Message sent by the Reporter when this agent terminates the Autopilot."
msgstr "オートパイロットを停止するとき、Reporterから送信されるメッセージ"


#: Editor/UI/Loggers/ 共通

# description (same as AutopilotSettingsEditor.cs)
Expand Down
51 changes: 51 additions & 0 deletions Editor/UI/Agents/TerminateAgentEditor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) 2023-2024 DeNA Co., Ltd.
// This software is released under the MIT License.

using DeNA.Anjin.Agents;
using UnityEditor;
using UnityEngine;

namespace DeNA.Anjin.Editor.UI.Agents
{
/// <summary>
/// Editor GUI for TerminateAgent.
/// </summary>
[CustomEditor(typeof(TerminateAgent))]
public class TerminateAgentEditor : UnityEditor.Editor
{
// @formatter:off
private static readonly string s_description = L10n.Tr("Description");
private static readonly string s_descriptionTooltip = L10n.Tr("Description about this agent instance");

private static readonly string s_exitCode = L10n.Tr("Exit Code");
private static readonly string s_exitCodeTooltip = L10n.Tr("Select the exit code used when this agent terminates the Autopilot.");
private static readonly string s_customExitCode = L10n.Tr("Custom Exit Code");
private static readonly string s_customExitCodeTooltip = L10n.Tr("Input exit code by integer value.");
private static readonly string s_exitMessage = L10n.Tr("Message");
private static readonly string s_exitMessageTooltip = L10n.Tr("Message sent by the Reporter when this agent terminates the Autopilot.");
// @formatter:on

private const float SpacerPixels = 10f;

/// <inheritdoc/>
public override void OnInspectorGUI()
{
serializedObject.Update();

EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(TerminateAgent.description)),
new GUIContent(s_description, s_descriptionTooltip));
GUILayout.Space(SpacerPixels);

EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(TerminateAgent.exitCode)),
new GUIContent(s_exitCode, s_exitCodeTooltip));
EditorGUI.BeginDisabledGroup(((TerminateAgent)target).exitCode != ExitCodeByTerminateAgent.Custom);
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(TerminateAgent.customExitCode)),
new GUIContent(s_customExitCode, s_customExitCodeTooltip));
EditorGUI.EndDisabledGroup();
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(TerminateAgent.exitMessage)),
new GUIContent(s_exitMessage, s_exitMessageTooltip));

serializedObject.ApplyModifiedProperties();
}
}
}
3 changes: 3 additions & 0 deletions Editor/UI/Agents/TerminateAgentEditor.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,19 @@ The following settings can be configured for this Agent instance (.asset file).
</dl>


### TerminateAgent

Terminates the Autopilot running. It is intended to be executed after the goal of a test scenario is achieved.

The following settings can be configured for this Agent instance (.asset file).

<dl>
<dt>Exit Code</dt><dd>Select the exit code used when this agent terminates the Autopilot.</dd>
<dt>Custom Exit Code</dt><dd>Input exit code by integer value.</dd>
<dt>Message</dt><dd>Message sent by the Reporter when this agent terminates the Autopilot.</dd>
</dl>


### ParallelCompositeAgent

An Agent that executes multiple Agents in parallel.
Expand Down
13 changes: 13 additions & 0 deletions README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,19 @@ Automated QAによる操作のレコーディングは、Unityエディターの
</dl>


### TerminateAgent

オートパイロットの実行を停止します。テストシナリオの目標を達成した後などに実行されることを想定しています。

このAgentのインスタンス(.assetファイル)には以下を設定できます。

<dl>
<dt>終了コード</dt><dd>このAgentがオートパイロットを停止するときに使用される終了コードを選択します</dd>
<dt>カスタム終了コード</dt><dd>終了コードを整数値で入力します</dd>
<dt>メッセージ</dt><dd>オートパイロットを停止するとき、Reporterから送信されるメッセージ</dd>
</dl>


### ParallelCompositeAgent

複数のAgentを登録し、それを並列実行するAgentです。
Expand Down
107 changes: 107 additions & 0 deletions Runtime/Agents/TerminateAgent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) 2023-2024 DeNA Co., Ltd.
// This software is released under the MIT License.

using System.Threading;
using Cysharp.Threading.Tasks;
using DeNA.Anjin.Attributes;
using UnityEngine;

namespace DeNA.Anjin.Agents
{
/// <summary>
/// Autopilot exit code that terminates by <c>TerminateAgent</c>.
/// </summary>
public enum ExitCodeByTerminateAgent
{
/// <summary>
/// Normally terminated.
/// </summary>
Normally = ExitCode.Normally,

/// <summary>
/// Terminate by Autopilot scenario running failure.
/// By default, the exit code at expiration will be <c>Normally</c>.
/// This exit code is only used if set by the user.
/// </summary>
AutopilotFailed = ExitCode.AutopilotFailed,

/// <summary>
/// Input custom exit code by integer value.
/// </summary>
Custom = 1024,
}

/// <summary>
/// An Agent that terminates the Autopilot running when the scenario goal is achieved.
/// </summary>
[CreateAssetMenu(fileName = "New TerminateAgent", menuName = "Anjin/Terminate Agent", order = 22)]
public class TerminateAgent : AbstractAgent
{
/// <summary>
/// Exit Code when terminated by this Agent.
/// When using it from within code, use the <code>ExitCode</code> property.
/// </summary>
public ExitCodeByTerminateAgent exitCode = ExitCodeByTerminateAgent.Normally;

/// <summary>
/// Custom exit code to be used if <code>Custom</code> is selected for <c>exitCode</c>.
/// Please enter an integer value.
/// </summary>
public string customExitCode;

public ExitCode ExitCode
{
get
{
if (exitCode == ExitCodeByTerminateAgent.Custom)
{
return int.TryParse(customExitCode, out var intExitCode)
? (ExitCode)intExitCode
: (ExitCode)exitCode;
}

return (ExitCode)exitCode;
}
}

/// <summary>
/// Message used by Reporter.
/// </summary>
[Multiline]
public string exitMessage = "Terminated by TerminateAgent";

internal ITerminatable _autopilot; // can inject for testing

[InitializeOnLaunchAutopilot]
private static void ResetInstances()
{
// Reset runtime instances
var agents = Resources.FindObjectsOfTypeAll<TerminateAgent>();
foreach (var agent in agents)
{
agent._autopilot = null;
}
}

/// <inheritdoc/>
public override async UniTask Run(CancellationToken token)
{
try
{
Logger.Log($"Enter {this.name}.Run()");

_autopilot = _autopilot ?? Autopilot.Instance;

// ReSharper disable once MethodSupportsCancellation
_autopilot.TerminateAsync(ExitCode, exitMessage).Forget();
// Note: Do not use this Agent's CancellationToken.
}
finally
{
Logger.Log($"Exit {this.name}.Run()");
}

await UniTask.CompletedTask;
}
}
}
3 changes: 3 additions & 0 deletions Runtime/Agents/TerminateAgent.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 75 additions & 0 deletions Tests/Runtime/Agents/TerminateAgentTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) 2023-2024 DeNA Co., Ltd.
// This software is released under the MIT License.

using System.Threading;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using DeNA.Anjin.TestDoubles;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

namespace DeNA.Anjin.Agents
{
[TestFixture]
public class TerminateAgentTest
{
[Test]
public void ExitCode_NotCustom_ReturnsExitCode()
{
var sut = ScriptableObject.CreateInstance<TerminateAgent>();
sut.exitCode = ExitCodeByTerminateAgent.AutopilotFailed;
sut.customExitCode = "100"; // dummy

Assert.That(sut.ExitCode, Is.EqualTo(ExitCode.AutopilotFailed));
}

[Test]
public void ExitCode_Custom_ReturnsCustomExitCode()
{
var sut = ScriptableObject.CreateInstance<TerminateAgent>();
sut.exitCode = ExitCodeByTerminateAgent.Custom;
sut.customExitCode = "100";

Assert.That(sut.ExitCode, Is.EqualTo((ExitCode)100));
}

[Test]
public void ExitCode_CustomButNotValid_ReturnsExitCodeByTerminateAgentCustom()
{
var sut = ScriptableObject.CreateInstance<TerminateAgent>();
sut.exitCode = ExitCodeByTerminateAgent.Custom;
sut.customExitCode = "not valid";

Assert.That(sut.ExitCode, Is.EqualTo((ExitCode)ExitCodeByTerminateAgent.Custom));
}

[Test]
public async Task Run_CallTerminateAsync()
{
var agent = ScriptableObject.CreateInstance<TerminateAgent>();
agent.Logger = Debug.unityLogger;
agent.name = TestContext.CurrentContext.Test.Name;
agent.exitCode = ExitCodeByTerminateAgent.Custom;
agent.customExitCode = "100";
agent.exitMessage = "Terminated!";

var spyTerminatable = new SpyTerminatable();
agent._autopilot = spyTerminatable;

using (var cts = new CancellationTokenSource())
{
await agent.Run(cts.Token);
await UniTask.NextFrame();
}

Assert.That(spyTerminatable.IsCalled, Is.True);
Assert.That(spyTerminatable.CapturedExitCode, Is.EqualTo((ExitCode)100));
Assert.That(spyTerminatable.CapturedMessage, Is.EqualTo("Terminated!"));
Assert.That(spyTerminatable.CapturedReporting, Is.True);

LogAssert.Expect(LogType.Log, $"Enter {agent.name}.Run()");
LogAssert.Expect(LogType.Log, $"Exit {agent.name}.Run()");
}
}
}
3 changes: 3 additions & 0 deletions Tests/Runtime/Agents/TerminateAgentTest.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading