diff --git a/Editor/Localization/ja.po b/Editor/Localization/ja.po
index 051f51b..3409f1d 100644
--- a/Editor/Localization/ja.po
+++ b/Editor/Localization/ja.po
@@ -31,8 +31,8 @@ msgstr "名前"
msgid "Custom name of this setting used by Reporter. If omitted, the asset file name is used."
msgstr "このAutopilotSettingsインスタンスの名前。Reporterで使用されます。省略時はアセットファイル名がデフォルトとして使用されます"
-# Header: Agent Assignment
-msgid "Agent Assignment"
+# Header: Agent Assignment Settings
+msgid "Agent Assignment Settings"
msgstr "Agent割り当て設定"
# sceneAgentMapping
@@ -59,18 +59,46 @@ msgstr "Scene横断Agents"
msgid "Agents running by scene crossing. The specified agents will have the same lifespan as Autopilot (i.e., use DontDestroyOnLoad) for specifying, e.g., ErrorHandlerAgent and UGUIEmergencyExitAgent."
msgstr "Sceneを横断して実行されるAgent。指定されたAgentの寿命は Autopilot と同じになります(つまり、DontDestroyOnLoad を使用します)。たとえば、ErrorHandlerAgent や UGUIEmergencyExitAgent などを指定します"
-# Header: Autopilot Run Settings
-msgid "Autopilot Run Settings"
-msgstr "オートパイロット実行設定"
+# Header: Autopilot Lifespan Settings
+msgid "Autopilot Lifespan Settings"
+msgstr "オートパイロット実行時間設定"
# lifespanSec
-msgid "Lifespan Sec"
+msgid "Lifespan [sec]"
msgstr "実行時間[秒]"
# lifespanSec tooltip
-msgid "Autopilot running lifespan [sec]. When specified zero, so unlimited running"
+msgid "Autopilot running lifespan [sec]. When specified zero, so unlimited running."
msgstr "実行時間上限を秒で指定します。0を指定すると無制限に動作します"
+# exitCode
+msgid "Exit Code"
+msgstr "終了コード"
+
+# exitCode tooltip
+msgid "Select the exit code used when Autopilot lifespan expires."
+msgstr "オートパイロットの実行時間が満了したときに使用される終了コードを選択します"
+
+# 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 Autopilot lifespan expires."
+msgstr "オートパイロットの実行時間が満了したとき、Reporterから送信されるメッセージ"
+
+# Header: Autopilot Run Settings
+msgid "Autopilot Run Settings"
+msgstr "オートパイロット実行設定"
+
# randomSeed
msgid "Random Seed"
msgstr "擬似乱数シード"
@@ -133,11 +161,11 @@ msgstr "このAgentインスタンスの説明"
#: Editor/UI/Agents/DoNothingAgentEditor.cs
# lifespanSec (same as AutopilotSettingsEditor.cs)
-msgid "Lifespan Sec"
+msgid "Lifespan [sec]"
msgstr "実行時間[秒]"
# lifespanSec tooltip
-msgid "Agent running lifespan [sec]. When specified zero, so unlimited running"
+msgid "Agent running lifespan [sec]. When specified zero, so unlimited running."
msgstr "実行時間上限を秒で指定します。0を指定すると無制限に動作します"
@@ -207,15 +235,15 @@ msgstr "連続実行するAgentを指定します"
#: Editor/UI/Agents/UGUIMonkeyAgentEditor.cs
# lifespanSec (same as DoNothingAgentEditor.cs)
-msgid "Lifespan Sec"
+msgid "Lifespan [sec]"
msgstr "実行時間[秒]"
# lifespanSec tooltip (same as DoNothingAgentEditor.cs)
-msgid "Agent running lifespan [sec]. When specified zero, so unlimited running"
+msgid "Agent running lifespan [sec]. When specified zero, so unlimited running."
msgstr "実行時間上限を秒で指定します。0を指定すると無制限に動作します"
# delayMillis
-msgid "Delay Millis"
+msgid "Delay [millis]"
msgstr "操作間隔[ミリ秒]"
# delayMillis tooltip
diff --git a/Editor/UI/Agents/DoNothingAgentEditor.cs b/Editor/UI/Agents/DoNothingAgentEditor.cs
index c42b3a0..2e902cb 100644
--- a/Editor/UI/Agents/DoNothingAgentEditor.cs
+++ b/Editor/UI/Agents/DoNothingAgentEditor.cs
@@ -15,10 +15,10 @@ public class DoNothingAgentEditor : UnityEditor.Editor
{
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_lifespanSec = L10n.Tr("Lifespan Sec");
+ private static readonly string s_lifespanSec = L10n.Tr("Lifespan [sec]");
private static readonly string s_lifespanSecTooltip =
- L10n.Tr("Agent running lifespan [sec]. When specified zero, so unlimited running");
+ L10n.Tr("Agent running lifespan [sec]. When specified zero, so unlimited running.");
///
public override void OnInspectorGUI()
diff --git a/Editor/UI/Agents/UGUIMonkeyAgentEditor.cs b/Editor/UI/Agents/UGUIMonkeyAgentEditor.cs
index 10878fa..38b69f1 100644
--- a/Editor/UI/Agents/UGUIMonkeyAgentEditor.cs
+++ b/Editor/UI/Agents/UGUIMonkeyAgentEditor.cs
@@ -21,15 +21,15 @@ public class UGUIMonkeyAgentEditor : UnityEditor.Editor
private SerializedProperty _descriptionProp;
private GUIContent _descriptionGUIContent;
- private static readonly string s_lifespanSec = L10n.Tr("Lifespan Sec");
+ private static readonly string s_lifespanSec = L10n.Tr("Lifespan [sec]");
private static readonly string s_lifespanSecTooltip =
- L10n.Tr("Agent running lifespan [sec]. When specified zero, so unlimited running");
+ L10n.Tr("Agent running lifespan [sec]. When specified zero, so unlimited running.");
private SerializedProperty _lifespanProp;
private GUIContent _lifespanGUIContent;
- private static readonly string s_delayMillis = L10n.Tr("Delay Millis");
+ private static readonly string s_delayMillis = L10n.Tr("Delay [millis]");
private static readonly string s_delayMillisTooltip = L10n.Tr("Delay time between random operations [ms]");
private SerializedProperty _delayMillisProp;
private GUIContent _delayMillisGUIContent;
diff --git a/Editor/UI/Settings/AutopilotSettingsEditor.cs b/Editor/UI/Settings/AutopilotSettingsEditor.cs
index 4df6549..2d6ab22 100644
--- a/Editor/UI/Settings/AutopilotSettingsEditor.cs
+++ b/Editor/UI/Settings/AutopilotSettingsEditor.cs
@@ -21,7 +21,7 @@ public class AutopilotSettingsEditor : UnityEditor.Editor
private static readonly string s_name = L10n.Tr("Name");
private static readonly string s_nameTooltip = L10n.Tr("Custom name of this setting used by Reporter. If omitted, the asset file name is used.");
- private static readonly string s_agentAssignmentHeader = L10n.Tr("Agent Assignment");
+ private static readonly string s_agentAssignmentHeader = L10n.Tr("Agent Assignment Settings");
private static readonly string s_sceneAgentMaps = L10n.Tr("Scene Agent Mapping");
private static readonly string s_sceneAgentMapsTooltip = L10n.Tr("Scene to Agent assign mapping");
private static readonly string s_fallbackAgent = L10n.Tr("Fallback Agent");
@@ -30,10 +30,17 @@ public class AutopilotSettingsEditor : UnityEditor.Editor
private static readonly string s_sceneCrossingAgents = L10n.Tr("Scene Crossing Agents");
private static readonly string s_sceneCrossingAgentsTooltip = L10n.Tr("Agents running by scene crossing. The specified agents will have the same lifespan as Autopilot (i.e., use DontDestroyOnLoad) for specifying, e.g., ErrorHandlerAgent and UGUIEmergencyExitAgent.");
- private static readonly string s_autopilotRunSettingsHeader = L10n.Tr("Autopilot Run Settings");
- private static readonly string s_lifespanSec = L10n.Tr("Lifespan Sec");
- private static readonly string s_lifespanSecTooltip = L10n.Tr("Autopilot running lifespan [sec]. When specified zero, so unlimited running");
+ private static readonly string s_autopilotLifespanSettingsHeader = L10n.Tr("Autopilot Lifespan Settings");
+ private static readonly string s_lifespanSec = L10n.Tr("Lifespan [sec]");
+ private static readonly string s_lifespanSecTooltip = L10n.Tr("Autopilot running lifespan [sec]. When specified zero, so unlimited running.");
+ private static readonly string s_exitCode = L10n.Tr("Exit Code");
+ private static readonly string s_exitCodeTooltip = L10n.Tr("Select the exit code used when Autopilot lifespan expires.");
+ 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 Autopilot lifespan expires.");
+ private static readonly string s_autopilotRunSettingsHeader = L10n.Tr("Autopilot Run Settings");
private static readonly string s_randomSeed = L10n.Tr("Random Seed");
private static readonly string s_randomSeedTooltip = L10n.Tr("Random using the specified seed value");
private static readonly string s_timeScale = L10n.Tr("Time Scale");
@@ -69,9 +76,19 @@ public override void OnInspectorGUI()
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(AutopilotSettings.sceneCrossingAgents)),
new GUIContent(s_sceneCrossingAgents, s_sceneCrossingAgentsTooltip));
- DrawHeader(s_autopilotRunSettingsHeader);
+ DrawHeader(s_autopilotLifespanSettingsHeader);
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(AutopilotSettings.lifespanSec)),
new GUIContent(s_lifespanSec, s_lifespanSecTooltip));
+ EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(AutopilotSettings.exitCode)),
+ new GUIContent(s_exitCode, s_exitCodeTooltip));
+ EditorGUI.BeginDisabledGroup(((AutopilotSettings)target).exitCode != ExitCodeWhenLifespanExpired.Custom);
+ EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(AutopilotSettings.customExitCode)),
+ new GUIContent(s_customExitCode, s_customExitCodeTooltip));
+ EditorGUI.EndDisabledGroup();
+ EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(AutopilotSettings.exitMessage)),
+ new GUIContent(s_exitMessage, s_exitMessageTooltip));
+
+ DrawHeader(s_autopilotRunSettingsHeader);
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(AutopilotSettings.randomSeed)),
new GUIContent(s_randomSeed, s_randomSeedTooltip));
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(AutopilotSettings.timeScale)),
diff --git a/README.md b/README.md
index 207cebb..a2f7271 100644
--- a/README.md
+++ b/README.md
@@ -113,14 +113,23 @@ for specifying, e.g., `ErrorHandlerAgent` and `UGUIEmergencyExitAgent`.
> [!WARNING]
> Recommend set an [ErrorHandlerAgent](#ErrorHandlerAgent) to interrupt Autopilot if an exception occurs during execution.
-#### Autopilot Run Settings
+#### Autopilot Lifespan Settings
-This item can also be overridden from the commandline (see below).
+
+ - Lifespan
- Specifies the execution time limit in seconds. Defaults to 300 seconds, 0 specifies unlimited operation.
+ This item can also be overridden from the commandline (see below).
+ - Exit Code
- Select the exit code used when Autopilot lifespan expires.
+ - Custom Exit Code
- Specify the exit code as an integer value.
+ - Message
- Message sent by the Reporter when Autopilot lifespan expires.
+
+
+#### Autopilot Run Settings
- - Lifespan
- Specifies the execution time limit in seconds. Defaults to 300 seconds, 0 specifies unlimited operation
- - Random Seed
- Specify when you want to fix the seed given to the pseudo-random number generator (optional). This is a setting related to the pseudo-random number generator used by the autopilot. To fix the seed of the pseudo-random number generator in the game itself, it is necessary to implement this setting on the game title side.
- - Time Scale
- Time.timeScale. Default is 1.0
+ - Random Seed
- Specify when you want to fix the seed given to the pseudo-random number generator (optional). This is a setting related to the pseudo-random number generator used by the autopilot. To fix the seed of the pseudo-random number generator in the game itself, it is necessary to implement this setting on the game title side.
+ This item can also be overridden from the commandline (see below).
+ - Time Scale
- Time.timeScale. Default is 1.0.
+ This item can also be overridden from the commandline (see below).
- Loggers
- Logger used for this autopilot settings. If omitted,
Debug.unityLogger
will be used as default.
- Reporters
- Reporter to be called on Autopilot terminate.
@@ -237,8 +246,8 @@ This Agent implementation uses open source [test-helper.monkey](https://github.c
An instance of this Agent (.asset file) can contain the following.
- - Lifespan Sec
- Duration of random operation execution time in secounds. If 0 is specified, the operation is almost unlimited (TimeSpan.MaxValue). With this setting, neither Autopilot nor the app itself will exit when the Agent exits. It will not do anything until the next Scene switch
- - Delay Millis
- Wait interval [milliseconds] between random operations
+ - Lifespan
- Duration of random operation execution time in secounds. If 0 is specified, the operation is almost unlimited (TimeSpan.MaxValue). With this setting, neither Autopilot nor the app itself will exit when the Agent exits. It will not do anything until the next Scene switch
+ - Delay
- Wait interval [milliseconds] between random operations
- Secs Searching Components
- Seconds to determine that an error has occurred when an object that can be interacted with does not exist
- Touch and Hold Millis
- Delay time for touch-and-hold [ms]
- Enable Gizmos
- Show Gizmos on GameView during running monkey test if true
@@ -296,7 +305,7 @@ An Agent that does nothing.
The following settings can be configured for this Agent instance (.asset file).
- - Lifespan Sec
- Specifies the do nothing time in seconds. 0 means unlimited time to do nothing. If 0 is specified, an unlimited amount of time for no action is taken. It will not do anything until the next Scene is switched.
+ - Lifespan
- Specifies the do nothing time in seconds. 0 means unlimited time to do nothing. If 0 is specified, an unlimited amount of time for no action is taken. It will not do anything until the next Scene is switched.
diff --git a/README_ja.md b/README_ja.md
index 2e4e8c1..b1076f5 100644
--- a/README_ja.md
+++ b/README_ja.md
@@ -111,14 +111,23 @@ Sceneごとに自動実行を行なうAgent設定ファイル(.asset)の対
> [!WARNING]
> 実行中に例外が発生した時点でオートパイロットを中断するために、[ErrorHandlerAgent](#ErrorHandlerAgent) の設定をお勧めします。
-#### オートパイロット実行設定
+#### オートパイロット実行時間設定
-この項目は、コマンドラインから上書きもできます(後述)。
+
+ - 実行時間
- 実行時間上限を秒で指定します。デフォルトは300秒で、0を指定すると無制限に動作します。
+ この項目は、コマンドラインから上書きもできます(後述)。
+ - 終了コード
- オートパイロットの実行時間が満了したときに使用される終了コードを選択します
+ - カスタム終了コード
- 終了コードを整数値で指定します
+ - メッセージ
- オートパイロットの実行時間が満了したとき、Reporterから送信されるメッセージ
+
+
+#### オートパイロット実行設定
- - Lifespan
- 実行時間上限を秒で指定します。デフォルトは300秒、0を指定すると無制限に動作します
- - Random Seed
- 疑似乱数発生器に与えるシードを固定したいときに指定します(省略可)。なお、これはオートパイロットの使用する疑似乱数発生器に関する設定であり、ゲーム本体の疑似乱数発生器シードを固定するにはゲームタイトル側での実装が必要です。
- - Time Scale
- Time.timeScaleを指定します。デフォルトは1.0
+ - Random Seed
- 疑似乱数発生器に与えるシードを固定したいときに指定します(省略可)。なお、これはオートパイロットの使用する疑似乱数発生器に関する設定であり、ゲーム本体の疑似乱数発生器シードを固定するにはゲームタイトル側での実装が必要です。
+ この項目は、コマンドラインから上書きもできます(後述)。
+ - Time Scale
- Time.timeScaleを指定します。デフォルトは1.0。
+ この項目は、コマンドラインから上書きもできます(後述)。
- Loggers
- オートパイロットが使用するLogger指定します。省略時は
Debug.unityLogger
がデフォルトとして使用されます
- Reporters
- オートパイロット終了時に通知を行なうReporterを指定します
@@ -301,7 +310,7 @@ Automated QAによる操作のレコーディングは、Unityエディターの
このAgentのインスタンス(.assetファイル)には以下を設定できます。
- - Lifespan Sec
- なにもしない時間を秒で指定します。0を指定すると無制限になにもしません。この設定でAgentが終了してもオートパイロットおよびアプリ自体は終了しません。次にSceneが切り替わるまでなにもしない状態になります
+ - 実行時間
- なにもしない時間を秒で指定します。0を指定すると無制限になにもしません。この設定でAgentが終了してもオートパイロットおよびアプリ自体は終了しません。次にSceneが切り替わるまでなにもしない状態になります
diff --git a/Runtime/AgentDispatcher.cs b/Runtime/AgentDispatcher.cs
index 791a9c0..47f6e72 100644
--- a/Runtime/AgentDispatcher.cs
+++ b/Runtime/AgentDispatcher.cs
@@ -55,8 +55,7 @@ public AgentDispatcher(AutopilotSettings settings, ILogger logger, RandomFactory
public void Dispose()
{
- var inspectors = Object.FindObjectsOfType();
- foreach (var inspector in inspectors)
+ foreach (var inspector in AgentInspector.Instances)
{
_logger.Log($"Destroy running agent: {inspector.gameObject.name}");
Object.Destroy(inspector.gameObject);
diff --git a/Runtime/AgentInspector.cs b/Runtime/AgentInspector.cs
index 0899499..dff908b 100644
--- a/Runtime/AgentInspector.cs
+++ b/Runtime/AgentInspector.cs
@@ -14,5 +14,10 @@ namespace DeNA.Anjin
///
public class AgentInspector : MonoBehaviour
{
+ ///
+ /// Returns the running Agent instance array.
+ /// No caching.
+ ///
+ public static AgentInspector[] Instances => FindObjectsOfType();
}
}
diff --git a/Runtime/Autopilot.cs b/Runtime/Autopilot.cs
index 31891e7..441e410 100644
--- a/Runtime/Autopilot.cs
+++ b/Runtime/Autopilot.cs
@@ -92,7 +92,10 @@ private void Start()
if (_settings.lifespanSec > 0)
{
- StartCoroutine(Lifespan(_settings.lifespanSec));
+ StartCoroutine(Lifespan(
+ _settings.lifespanSec,
+ _settings.ExitCode,
+ _settings.exitMessage));
}
if (Math.Abs(_settings.timeScale - 1.0f) > 0.001 && _settings.timeScale > 0) // 0 is ignored
@@ -118,13 +121,10 @@ private void DispatchByLoadedScenes()
///
/// Terminate when ran specified time.
///
- ///
- ///
- private IEnumerator Lifespan(int timeoutSec)
+ private IEnumerator Lifespan(int timeoutSec, ExitCode exitCode, string message)
{
yield return new WaitForSecondsRealtime(timeoutSec);
- yield return UniTask.ToCoroutine(() =>
- TerminateAsync(ExitCode.Normally, "Autopilot has reached the end of its lifespan."));
+ yield return UniTask.ToCoroutine(() => TerminateAsync(exitCode, message));
}
private void OnDestroy()
diff --git a/Runtime/ExitCode.cs b/Runtime/ExitCode.cs
index 8c518a2..c7ed942 100644
--- a/Runtime/ExitCode.cs
+++ b/Runtime/ExitCode.cs
@@ -9,28 +9,35 @@ namespace DeNA.Anjin
public enum ExitCode
{
///
- /// Normally terminate.
+ /// Normally terminated.
///
Normally = 0,
///
- /// Terminate by uncaught exceptions.
+ /// Terminated by uncaught exceptions.
///
UnCatchExceptions = 1,
///
- /// Terminate by ErrorHandlerAgent.
+ /// Terminated by ErrorHandlerAgent.
///
DetectErrorsInLog,
///
- /// Terminate by Autopilot launching failure.
+ /// Terminated by Autopilot launching failure.
///
AutopilotLaunchingFailed,
///
- /// Terminate by Autopilot scenario running failure.
+ /// Terminated by Autopilot scenario running failure.
///
AutopilotFailed,
+
+ ///
+ /// Terminated by Autopilot lifespan expired.
+ /// By default, the exit code at expiration will be Normally.
+ /// This exit code is only used if set by the user.
+ ///
+ AutopilotLifespanExpired,
}
}
diff --git a/Runtime/Reporters/JUnitXmlReporter.cs b/Runtime/Reporters/JUnitXmlReporter.cs
index 5be5af3..8fdad00 100644
--- a/Runtime/Reporters/JUnitXmlReporter.cs
+++ b/Runtime/Reporters/JUnitXmlReporter.cs
@@ -97,6 +97,9 @@ internal static string GetOutputPath(string outputPathField, Arguments args = nu
internal static XElement CreateTestCase(string name, float time, string message, string stackTrace,
ExitCode exitCode)
{
+ message = message ?? string.Empty;
+ stackTrace = stackTrace ?? string.Empty;
+
var element = new XElement("testcase",
new XAttribute("name", name),
new XAttribute("classname", "DeNA.Anjin.Autopilot"),
@@ -116,13 +119,18 @@ internal static XElement CreateTestCase(string name, float time, string message,
new XCData(stackTrace)));
break;
case ExitCode.AutopilotFailed:
+ case ExitCode.AutopilotLifespanExpired:
element.Add(new XElement("failure",
new XAttribute("message", message),
new XAttribute("type", exitCode.ToString()),
new XCData(stackTrace)));
break;
default:
- throw new ArgumentOutOfRangeException(nameof(exitCode), exitCode, null);
+ element.Add(new XElement("failure",
+ new XAttribute("message", message),
+ new XAttribute("type", (int)exitCode),
+ new XCData(stackTrace)));
+ break;
}
return element;
diff --git a/Runtime/Settings/AutopilotSettings.cs b/Runtime/Settings/AutopilotSettings.cs
index f8c15c6..d7978e0 100644
--- a/Runtime/Settings/AutopilotSettings.cs
+++ b/Runtime/Settings/AutopilotSettings.cs
@@ -1,4 +1,4 @@
-// Copyright (c) 2023 DeNA Co., Ltd.
+// Copyright (c) 2023-2024 DeNA Co., Ltd.
// This software is released under the MIT License.
using System;
@@ -35,6 +35,29 @@ public struct SceneAgentMap
public AbstractAgent agent;
}
+ ///
+ /// Autopilot exit code options when lifespan expired.
+ ///
+ public enum ExitCodeWhenLifespanExpired
+ {
+ ///
+ /// Normally terminated.
+ ///
+ Normally = ExitCode.Normally,
+
+ ///
+ /// Terminated by Autopilot lifespan expired.
+ /// By default, the exit code at expiration will be Normally.
+ /// This exit code is only used if set by the user.
+ ///
+ LifespanExpired = ExitCode.AutopilotLifespanExpired,
+
+ ///
+ /// Input custom exit code by integer value.
+ ///
+ Custom = 1024,
+ }
+
///
/// Autopilot run settings.
///
@@ -87,17 +110,51 @@ public class AutopilotSettings : ScriptableObject
public List sceneCrossingAgents = new List();
///
- /// Autopilot running lifespan [sec]. When specified zero, so unlimited running
+ /// Autopilot running lifespan [sec]. When specified zero, so unlimited running.
///
public int lifespanSec = 300;
///
- /// Random using the specified seed value
+ /// Autopilot exit code options when lifespan expired.
+ /// When using it from within code, use the ExitCode
property.
+ ///
+ public ExitCodeWhenLifespanExpired exitCode = ExitCodeWhenLifespanExpired.Normally;
+
+ ///
+ /// Custom exit code to be used if Custom
is selected for exitCode.
+ /// Please enter an integer value.
+ ///
+ public string customExitCode;
+
+ public ExitCode ExitCode
+ {
+ get
+ {
+ if (exitCode == ExitCodeWhenLifespanExpired.Custom)
+ {
+ return int.TryParse(customExitCode, out var intExitCode)
+ ? (ExitCode)intExitCode
+ : (ExitCode)exitCode;
+ }
+
+ return (ExitCode)exitCode;
+ }
+ }
+
+ ///
+ /// Message used by Reporter.
+ ///
+ [Multiline]
+ public string exitMessage = "Autopilot lifespan expired.";
+
+ ///
+ /// Random using the specified seed value.
+ /// Please enter an integer value or empty.
///
public string randomSeed;
///
- /// Time.timeScale
+ /// Time.timeScale.
///
public float timeScale = 1.0f;
diff --git a/Tests/Runtime/AutopilotTest.cs b/Tests/Runtime/AutopilotTest.cs
index e56b53a..ccf1ece 100644
--- a/Tests/Runtime/AutopilotTest.cs
+++ b/Tests/Runtime/AutopilotTest.cs
@@ -40,16 +40,54 @@ private static AutopilotSettings CreateAutopilotSettings(int lifespanSec)
}
[Test]
- public async Task TerminateAsync_Normally_DestroyedAutopilotAndAgentObjects()
+ public async Task Lifespan_Expire_CallTerminateAsync()
+ {
+ var spyReporter = ScriptableObject.CreateInstance();
+ var settings = CreateAutopilotSettings(1);
+ settings.reporters.Add(spyReporter);
+ settings.exitCode = ExitCodeWhenLifespanExpired.Normally;
+ settings.customExitCode = "100"; // dummy
+ settings.exitMessage = "Lifespan expired";
+
+ await Launcher.LaunchAutopilotAsync(settings);
+ await UniTask.NextFrame(); // wait reporter
+
+ Assert.That(spyReporter.IsCalled, Is.True);
+ Assert.That(spyReporter.Arguments["exitCode"], Is.EqualTo(ExitCode.Normally.ToString()));
+ Assert.That(spyReporter.Arguments["message"], Is.EqualTo("Lifespan expired"));
+ }
+
+ [Test]
+ public async Task Lifespan_Expire_CallTerminateAsyncWithExitCodeAndMessage()
+ {
+ var spyReporter = ScriptableObject.CreateInstance();
+ var settings = CreateAutopilotSettings(1);
+ settings.reporters.Add(spyReporter);
+ settings.exitCode = ExitCodeWhenLifespanExpired.Custom;
+ settings.customExitCode = "100";
+ settings.exitMessage = "Lifespan expired";
+
+ await Launcher.LaunchAutopilotAsync(settings);
+ await UniTask.NextFrame(); // wait reporter
+
+ Assert.That(spyReporter.IsCalled, Is.True);
+ Assert.That(spyReporter.Arguments["exitCode"], Is.EqualTo("100"));
+ Assert.That(spyReporter.Arguments["message"], Is.EqualTo("Lifespan expired"));
+
+ LogAssert.Expect(LogType.Exception, "AssertionException: Autopilot failed with exit code 100");
+ }
+
+ [Test]
+ public async Task TerminateAsync_DestroyedAutopilotAndAgentObjects()
{
var settings = CreateAutopilotSettings(2);
Launcher.LaunchAutopilotAsync(settings).Forget();
await UniTask.Delay(500); // wait for launch
- var autopilot = Object.FindObjectOfType();
+ var autopilot = Autopilot.Instance;
Assume.That(autopilot, Is.Not.Null, "Autopilot is running");
- var agents = Object.FindObjectsOfType();
+ var agents = AgentInspector.Instances;
Assume.That(agents, Is.Not.Empty, "Agents are running");
await autopilot.TerminateAsync(ExitCode.Normally, reporting: false);
@@ -58,10 +96,64 @@ public async Task TerminateAsync_Normally_DestroyedAutopilotAndAgentObjects()
autopilot = Object.FindObjectOfType(); // re-find after terminated
Assert.That(autopilot, Is.Null, "Autopilot was destroyed");
- agents = Object.FindObjectsOfType(); // re-find after terminated
+ agents = AgentInspector.Instances; // re-find after terminated
Assert.That(agents, Is.Empty, "Agents were destroyed");
}
+ [Test]
+ public async Task TerminateAsync_DuplicateCalls()
+ {
+ var spyReporter = ScriptableObject.CreateInstance();
+ var settings = CreateAutopilotSettings(2);
+ settings.reporters.Add(spyReporter);
+ Launcher.LaunchAutopilotAsync(settings).Forget();
+ await UniTask.Delay(500); // wait for launch
+
+ var autopilot = Autopilot.Instance;
+ autopilot.TerminateAsync(ExitCode.Normally, "1st call", null, true).Forget();
+ autopilot.TerminateAsync(ExitCode.Normally, "2nd call", null, true).Forget();
+ await UniTask.DelayFrame(2); // wait for destroy
+
+ autopilot = Object.FindObjectOfType(); // re-find after terminated
+ Assert.That(autopilot, Is.Null, "Autopilot was destroyed");
+
+ Assert.That(spyReporter.CallCount, Is.EqualTo(1));
+ Assert.That(spyReporter.Arguments["message"], Is.EqualTo("1st call"));
+ }
+
+ [Test]
+ public async Task TerminateAsync_NoReporting_NotCallReporter()
+ {
+ var spyReporter = ScriptableObject.CreateInstance();
+ var settings = CreateAutopilotSettings(2);
+ settings.reporters.Add(spyReporter);
+ Launcher.LaunchAutopilotAsync(settings).Forget();
+ await UniTask.Delay(500); // wait for launch
+
+ var autopilot = Autopilot.Instance;
+ await autopilot.TerminateAsync(ExitCode.Normally, null, null, false);
+
+ Assert.That(spyReporter.IsCalled, Is.False);
+ }
+
+ [Test]
+ public async Task TerminateAsync_Reporting_CallReporter()
+ {
+ var spyReporter = ScriptableObject.CreateInstance();
+ var settings = CreateAutopilotSettings(2);
+ settings.reporters.Add(spyReporter);
+ Launcher.LaunchAutopilotAsync(settings).Forget();
+ await UniTask.Delay(500); // wait for launch
+
+ var autopilot = Autopilot.Instance;
+ await autopilot.TerminateAsync(ExitCode.Normally, "message", "stack trace", true);
+
+ Assert.That(spyReporter.IsCalled, Is.True);
+ Assert.That(spyReporter.Arguments["exitCode"], Is.EqualTo("Normally"));
+ Assert.That(spyReporter.Arguments["message"], Is.EqualTo("message"));
+ Assert.That(spyReporter.Arguments["stackTrace"], Is.EqualTo("stack trace"));
+ }
+
[Test]
public async Task Start_LoggerIsNotSet_UsingDefaultLogger()
{
diff --git a/Tests/Runtime/Settings/AutopilotSettingsTest.cs b/Tests/Runtime/Settings/AutopilotSettingsTest.cs
index 1c3488f..3f6ee8e 100644
--- a/Tests/Runtime/Settings/AutopilotSettingsTest.cs
+++ b/Tests/Runtime/Settings/AutopilotSettingsTest.cs
@@ -32,6 +32,36 @@ private static Arguments CreateNotCapturedArguments()
return arguments;
}
+ [Test]
+ public void ExitCode_NotCustom_ReturnsExitCode()
+ {
+ var settings = CreateAutopilotSettings();
+ settings.exitCode = ExitCodeWhenLifespanExpired.LifespanExpired;
+ settings.customExitCode = "100"; // dummy
+
+ Assert.That(settings.ExitCode, Is.EqualTo(ExitCode.AutopilotLifespanExpired));
+ }
+
+ [Test]
+ public void ExitCode_Custom_ReturnsCustomExitCode()
+ {
+ var settings = CreateAutopilotSettings();
+ settings.exitCode = ExitCodeWhenLifespanExpired.Custom;
+ settings.customExitCode = "100";
+
+ Assert.That(settings.ExitCode, Is.EqualTo((ExitCode)100));
+ }
+
+ [Test]
+ public void ExitCode_CustomButNotValid_ReturnsExitCodeWhenLifespanExpiredCustom()
+ {
+ var settings = CreateAutopilotSettings();
+ settings.exitCode = ExitCodeWhenLifespanExpired.Custom;
+ settings.customExitCode = "not valid";
+
+ Assert.That(settings.ExitCode, Is.EqualTo((ExitCode)ExitCodeWhenLifespanExpired.Custom));
+ }
+
[Test]
public void OverwriteByCommandLineArguments_HasNotCommandlineArguments_KeepScriptableObjectValues()
{
diff --git a/Tests/Runtime/TestDoubles/SpyReporter.cs b/Tests/Runtime/TestDoubles/SpyReporter.cs
index 8e3c7bc..2a08d82 100644
--- a/Tests/Runtime/TestDoubles/SpyReporter.cs
+++ b/Tests/Runtime/TestDoubles/SpyReporter.cs
@@ -13,7 +13,8 @@ namespace DeNA.Anjin.TestDoubles
///
public class SpyReporter : AbstractReporter
{
- public bool IsCalled { get; private set; }
+ public int CallCount { get; private set; }
+ public bool IsCalled => CallCount > 0;
public Dictionary Arguments { get; } = new Dictionary();
public override async UniTask PostReportAsync(
@@ -23,7 +24,7 @@ public override async UniTask PostReportAsync(
CancellationToken cancellationToken = default
)
{
- this.IsCalled = true;
+ this.CallCount++;
Arguments.Add("message", message);
Arguments.Add("stackTrace", stackTrace);
Arguments.Add("exitCode", exitCode.ToString());