diff --git a/Editor/Localization/ja.po b/Editor/Localization/ja.po
index 75233f7..6daa19a 100644
--- a/Editor/Localization/ja.po
+++ b/Editor/Localization/ja.po
@@ -87,6 +87,18 @@ msgstr "JUnitレポート出力パス"
msgid "JUnit report output path"
msgstr "JUnit形式のレポートファイル出力パス(省略時は出力されない)"
+# reporter
+msgid "Reporter"
+msgstr "レポータ"
+
+# reporter tooltip
+msgid "Reporter that called when some errors occurred in target application"
+msgstr "対象のアプリケーションで発生したエラーを通知するレポータ"
+
+# obsolete slack settings
+msgid "Slack settings will be moved to SlackReporter"
+msgstr "Slack 設定は SlackReporter へ移動しました"
+
# slackToken
msgid "Slack Token"
msgstr "Slackトークン"
@@ -108,11 +120,11 @@ msgid "Slack Mention Settings"
msgstr "Slackメンション設定"
# mentionSubTeamIDs
-msgid "Mention Sub Team IDs"
+msgid "Sub Team IDs to Mention"
msgstr "メンション宛先"
# mentionSubTeamIDs tooltip
-msgid "Mention to sub team ID (comma separates)"
+msgid "Sub team IDs to mention (comma separates)"
msgstr "Slack通知メッセージでメンションするチームのIDをカンマ区切りで指定します"
# addHereInSlackMessage
@@ -120,7 +132,7 @@ msgid "Add @here Into Slack Message"
msgstr "@hereをメッセージにつける"
# addHereInSlackMessage tooltip
-msgid "Add @here into Slack message"
+msgid "Whether adding @here into Slack messages or not"
msgstr "Slack通知メッセージに@hereを付けます"
# Header: Error Handling Settings
@@ -320,3 +332,7 @@ msgstr "操作を記録したJSONファイル"
# recordedJson tooltip
msgid "JSON file recorded by AutomatedQA package"
msgstr "Automated QAパッケージのRecorded Playbackウィンドウで記録したjsonファイルを設定します"
+
+# composite reporters
+msgid "Reporters"
+msgstr "レポータ"
diff --git a/Editor/UI/Reporters.meta b/Editor/UI/Reporters.meta
new file mode 100644
index 0000000..82538c7
--- /dev/null
+++ b/Editor/UI/Reporters.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 3c549ea2f5314b23a625a59b05a24220
+timeCreated: 1699484044
\ No newline at end of file
diff --git a/Editor/UI/Reporters/CompositeReporterEditor.cs b/Editor/UI/Reporters/CompositeReporterEditor.cs
new file mode 100644
index 0000000..0fe7f5f
--- /dev/null
+++ b/Editor/UI/Reporters/CompositeReporterEditor.cs
@@ -0,0 +1,61 @@
+// Copyright (c) 2023 DeNA Co., Ltd.
+// This software is released under the MIT License.
+
+using DeNA.Anjin.Reporters;
+using UnityEditor;
+using UnityEditorInternal;
+using UnityEngine;
+
+namespace DeNA.Anjin.Editor.UI.Reporters
+{
+ ///
+ /// Editor for
+ ///
+ [CustomEditor(typeof(CompositeReporter))]
+ public class CompositeReporterEditor : UnityEditor.Editor
+ {
+ private SerializedProperty _reportersProp;
+ private ReorderableList _reorderableList;
+ private static readonly string s_reporters = L10n.Tr("Reporters");
+ private GUIContent _reportersGUIContent;
+ private static readonly string s_reporter = L10n.Tr("Reporter");
+ private GUIContent _reporterGUIContent;
+
+
+ private void OnEnable()
+ {
+ Initialize();
+ }
+
+
+ private void Initialize()
+ {
+ _reportersProp = serializedObject.FindProperty(nameof(CompositeReporter.reporters));
+ _reportersGUIContent = new GUIContent(s_reporters);
+ _reporterGUIContent = new GUIContent(s_reporter);
+ _reorderableList = new ReorderableList(serializedObject, _reportersProp)
+ {
+ drawHeaderCallback = rect => EditorGUI.LabelField(rect, _reportersGUIContent),
+ elementHeightCallback = _ => EditorGUIUtility.singleLineHeight,
+ // XXX: Dont use discarded parameter to treat Unity 2019.x
+ // ReSharper disable UnusedParameter.Local
+ drawElementCallback = (rect, index, active, focused) =>
+ // ReSharper restore UnusedParameter.Local
+ {
+ var elemProp = _reportersProp.GetArrayElementAtIndex(index);
+ EditorGUI.PropertyField(rect, elemProp, _reporterGUIContent);
+ }
+ };
+ }
+
+
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+
+ _reorderableList.DoLayoutList();
+
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
diff --git a/Editor/UI/Reporters/CompositeReporterEditor.cs.meta b/Editor/UI/Reporters/CompositeReporterEditor.cs.meta
new file mode 100644
index 0000000..e6783ba
--- /dev/null
+++ b/Editor/UI/Reporters/CompositeReporterEditor.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: ccf304a0e83345eba515322942248039
+timeCreated: 1699484060
\ No newline at end of file
diff --git a/Editor/UI/Reporters/SlackReporterEditor.cs b/Editor/UI/Reporters/SlackReporterEditor.cs
new file mode 100644
index 0000000..292b382
--- /dev/null
+++ b/Editor/UI/Reporters/SlackReporterEditor.cs
@@ -0,0 +1,81 @@
+// Copyright (c) 2023 DeNA Co., Ltd.
+// This software is released under the MIT License.
+
+using DeNA.Anjin.Reporters;
+using UnityEditor;
+using UnityEngine;
+
+namespace DeNA.Anjin.Editor.UI.Reporters
+{
+ ///
+ /// Editor for
+ ///
+ [CustomEditor(typeof(SlackReporter))]
+ public class SlackReporterEditor : UnityEditor.Editor
+ {
+ private const float SpacerPixels = 10f;
+ private const float SpacerPixelsUnderHeader = 4f;
+
+ private static readonly string s_slackToken = L10n.Tr("Slack Token");
+ private static readonly string s_slackTokenTooltip = L10n.Tr("Slack API token");
+ private SerializedProperty _slackTokenProp;
+ private GUIContent _slackTokenGUIContent;
+
+ private static readonly string s_slackChannels = L10n.Tr("Slack Channels");
+ private static readonly string s_slackChannelsTooltip = L10n.Tr("Slack channels to send notification");
+ private SerializedProperty _slackChannelsProp;
+ private GUIContent _slackChannelsGUIContent;
+
+ private static readonly string s_slackMentionSettingsHeader = L10n.Tr("Slack Mention Settings");
+
+ private static readonly string s_mentionSubTeamIDs = L10n.Tr("Sub Team IDs to Mention");
+ private static readonly string s_mentionSubTeamIDsTooltip = L10n.Tr("Sub team IDs to mention (comma separates)");
+ private SerializedProperty _mentionSubTeamIDsProp;
+ private GUIContent _mentionSubTeamIDsGUIContent;
+
+ private static readonly string s_addHereInSlackMessage = L10n.Tr("Add @here Into Slack Message");
+ private static readonly string s_addHereInSlackMessageTooltip = L10n.Tr("Whether adding @here into Slack messages or not");
+ private SerializedProperty _addHereInSlackMessageProp;
+ private GUIContent _addHereInSlackMessageGUIContent;
+
+
+ private void OnEnable()
+ {
+ Initialize();
+ }
+
+
+ private void Initialize()
+ {
+ _slackTokenProp = serializedObject.FindProperty(nameof(SlackReporter.slackToken));
+ _slackTokenGUIContent = new GUIContent(s_slackToken, s_slackTokenTooltip);
+
+ _slackChannelsProp = serializedObject.FindProperty(nameof(SlackReporter.slackChannels));
+ _slackChannelsGUIContent = new GUIContent(s_slackChannels, s_slackChannelsTooltip);
+
+ _mentionSubTeamIDsProp = serializedObject.FindProperty(nameof(SlackReporter.mentionSubTeamIDs));
+ _mentionSubTeamIDsGUIContent = new GUIContent(s_mentionSubTeamIDs, s_mentionSubTeamIDsTooltip);
+
+ _addHereInSlackMessageProp = serializedObject.FindProperty(nameof(SlackReporter.addHereInSlackMessage));
+ _addHereInSlackMessageGUIContent = new GUIContent(s_addHereInSlackMessage, s_addHereInSlackMessageTooltip);
+ }
+
+
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+
+ EditorGUILayout.PropertyField(_slackTokenProp, _slackTokenGUIContent);
+ EditorGUILayout.PropertyField(_slackChannelsProp, _slackChannelsGUIContent);
+
+ GUILayout.Space(SpacerPixels);
+ GUILayout.Label(s_slackMentionSettingsHeader);
+ GUILayout.Space(SpacerPixelsUnderHeader);
+
+ EditorGUILayout.PropertyField(_mentionSubTeamIDsProp, _mentionSubTeamIDsGUIContent);
+ EditorGUILayout.PropertyField(_addHereInSlackMessageProp, _addHereInSlackMessageGUIContent);
+
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
diff --git a/Editor/UI/Reporters/SlackReporterEditor.cs.meta b/Editor/UI/Reporters/SlackReporterEditor.cs.meta
new file mode 100644
index 0000000..d061868
--- /dev/null
+++ b/Editor/UI/Reporters/SlackReporterEditor.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 99b990767b4d4abe95b829b1d62b7698
+timeCreated: 1699484320
\ No newline at end of file
diff --git a/Editor/UI/Settings/AutopilotSettingsEditor.cs b/Editor/UI/Settings/AutopilotSettingsEditor.cs
index 86a127a..abd5f44 100644
--- a/Editor/UI/Settings/AutopilotSettingsEditor.cs
+++ b/Editor/UI/Settings/AutopilotSettingsEditor.cs
@@ -42,16 +42,19 @@ public class AutopilotSettingsEditor : UnityEditor.Editor
private static readonly string s_junitReportPath = L10n.Tr("JUnit Report Path");
private static readonly string s_junitReportPathTooltip = L10n.Tr("JUnit report output path");
+ private static readonly string s_reporter = L10n.Tr("Reporter");
+ private static readonly string s_reporterTooltip = L10n.Tr("Reporter that called when some errors occurred in target application");
+
private static readonly string s_slackToken = L10n.Tr("Slack Token");
private static readonly string s_slackTokenTooltip = L10n.Tr("Slack API token");
private static readonly string s_slackChannels = L10n.Tr("Slack Channels");
private static readonly string s_slackChannelsTooltip = L10n.Tr("Slack channels to send notification");
private static readonly string s_slackMentionSettingsHeader = L10n.Tr("Slack Mention Settings");
- private static readonly string s_mentionSubTeamIDs = L10n.Tr("Mention Sub Team IDs");
- private static readonly string s_mentionSubTeamIDsTooltip = L10n.Tr("Mention to sub team ID (comma separates)");
+ private static readonly string s_mentionSubTeamIDs = L10n.Tr("Sub Team IDs to Mention");
+ private static readonly string s_mentionSubTeamIDsTooltip = L10n.Tr("Sub team IDs to mention (comma separates)");
private static readonly string s_addHereInSlackMessage = L10n.Tr("Add @here Into Slack Message");
- private static readonly string s_addHereInSlackMessageTooltip = L10n.Tr("Add @here into Slack message");
+ private static readonly string s_addHereInSlackMessageTooltip = L10n.Tr("Whether adding @here into Slack messages or not");
private static readonly string s_errorHandlingSettingsHeader = L10n.Tr("Error Handling Settings");
private static readonly string s_handleException = L10n.Tr("Handle Exception");
@@ -64,6 +67,9 @@ public class AutopilotSettingsEditor : UnityEditor.Editor
private static readonly string s_handleWarningTooltip = L10n.Tr("Notify when Warning detected in log");
private static readonly string s_ignoreMessages = L10n.Tr("Ignore Messages");
+ private static readonly string s_obsoletedSlackParamsHelpBox =
+ L10n.Tr("Slack settings will be moved to SlackReporter");
+
private static readonly string s_ignoreMessagesTooltip =
L10n.Tr("Do not send notifications when log messages contain this string");
@@ -105,6 +111,9 @@ public override void OnInspectorGUI()
new GUIContent(s_timeScale, s_timeScaleTooltip));
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(AutopilotSettings.junitReportPath)),
new GUIContent(s_junitReportPath, s_junitReportPathTooltip));
+ EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(AutopilotSettings.reporter)),
+ new GUIContent(s_reporter, s_reporterTooltip));
+ EditorGUILayout.HelpBox(s_obsoletedSlackParamsHelpBox, MessageType.Warning);
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(AutopilotSettings.slackToken)),
new GUIContent(s_slackToken, s_slackTokenTooltip));
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(AutopilotSettings.slackChannels)),
diff --git a/Runtime/Autopilot.cs b/Runtime/Autopilot.cs
index 84a99c8..f6d931e 100644
--- a/Runtime/Autopilot.cs
+++ b/Runtime/Autopilot.cs
@@ -43,7 +43,6 @@ public enum ExitCode
private ILogger _logger;
private RandomFactory _randomFactory;
private IAgentDispatcher _dispatcher;
- private IReporter _reporter;
private LogMessageHandler _logMessageHandler;
private AutopilotState _state;
private AutopilotSettings _settings;
@@ -55,7 +54,7 @@ private void Start()
_settings = _state.settings;
Assert.IsNotNull(_settings);
- _logger = new ConsoleLogger(Debug.unityLogger.logHandler);
+ _logger = CreateLogger();
if (!int.TryParse(_settings.randomSeed, out var seed))
{
@@ -68,8 +67,7 @@ private void Start()
// 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);
+ _logMessageHandler = new LogMessageHandler(_settings, _settings.reporter);
Application.logMessageReceivedThreaded += _logMessageHandler.HandleLog;
_dispatcher = new AgentDispatcher(_settings, _logger, _randomFactory);
@@ -89,6 +87,15 @@ private void Start()
_startTime = Time.realtimeSinceStartup;
}
+ ///
+ /// Returns an agent dispatcher that autopilot uses. You can change a logger by overriding this method
+ ///
+ /// A new logger
+ protected virtual ILogger CreateLogger()
+ {
+ return new ConsoleLogger(Debug.unityLogger.logHandler);
+ }
+
///
/// Terminate when ran specified time.
///
diff --git a/Runtime/Reporters/IReporter.cs b/Runtime/Reporters/AbstractReporter.cs
similarity index 52%
rename from Runtime/Reporters/IReporter.cs
rename to Runtime/Reporters/AbstractReporter.cs
index 7f85a8a..c40db21 100644
--- a/Runtime/Reporters/IReporter.cs
+++ b/Runtime/Reporters/AbstractReporter.cs
@@ -1,24 +1,35 @@
// Copyright (c) 2023 DeNA Co., Ltd.
// This software is released under the MIT License.
+using System.Threading;
using Cysharp.Threading.Tasks;
+using DeNA.Anjin.Settings;
using UnityEngine;
namespace DeNA.Anjin.Reporters
{
///
- /// Reporter interface
+ /// Reporter base class
///
- public interface IReporter
+ public abstract class AbstractReporter : ScriptableObject
{
///
/// Post report log message, stacktrace and screenshot
///
+ /// Autopilot settings
/// Log message
/// Stack trace
/// Log message type
/// With screenshot
+ /// Cancellation token
///
- UniTask PostReportAsync(string logString, string stackTrace, LogType type, bool withScreenshot);
+ public abstract UniTask PostReportAsync(
+ AutopilotSettings settings,
+ string logString,
+ string stackTrace,
+ LogType type,
+ bool withScreenshot,
+ CancellationToken cancellationToken = default
+ );
}
}
diff --git a/Runtime/Reporters/IReporter.cs.meta b/Runtime/Reporters/AbstractReporter.cs.meta
similarity index 100%
rename from Runtime/Reporters/IReporter.cs.meta
rename to Runtime/Reporters/AbstractReporter.cs.meta
diff --git a/Runtime/Reporters/CompositeReporter.cs b/Runtime/Reporters/CompositeReporter.cs
new file mode 100644
index 0000000..e9fea76
--- /dev/null
+++ b/Runtime/Reporters/CompositeReporter.cs
@@ -0,0 +1,44 @@
+// Copyright (c) 2023 DeNA Co., Ltd.
+// This software is released under the MIT License.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Cysharp.Threading.Tasks;
+using DeNA.Anjin.Settings;
+using UnityEngine;
+
+namespace DeNA.Anjin.Reporters
+{
+ ///
+ /// A class for reporters that delegate to multiple reporters
+ ///
+ [CreateAssetMenu(fileName = "New CompositeReporter", menuName = "Anjin/Composite Reporter", order = 51)]
+ public class CompositeReporter : AbstractReporter
+ {
+ ///
+ /// Reporters to delegate
+ ///
+ public List reporters = new List();
+
+
+ ///
+ public override async UniTask PostReportAsync(
+ AutopilotSettings settings,
+ string logString,
+ string stackTrace,
+ LogType type,
+ bool withScreenshot,
+ CancellationToken cancellationToken = default
+ )
+ {
+ await UniTask.WhenAll(
+ reporters
+ .Where(r => r != this && r != null)
+ .Select(
+ r => r.PostReportAsync(settings, logString, stackTrace, type, withScreenshot, cancellationToken)
+ )
+ );
+ }
+ }
+}
diff --git a/Runtime/Reporters/CompositeReporter.cs.meta b/Runtime/Reporters/CompositeReporter.cs.meta
new file mode 100644
index 0000000..7aeb2c1
--- /dev/null
+++ b/Runtime/Reporters/CompositeReporter.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 107581b5731b4e6d824ae77242f55dd3
+timeCreated: 1699480101
\ No newline at end of file
diff --git a/Runtime/Reporters/SlackMessageSender.cs b/Runtime/Reporters/SlackMessageSender.cs
new file mode 100644
index 0000000..c97eb7f
--- /dev/null
+++ b/Runtime/Reporters/SlackMessageSender.cs
@@ -0,0 +1,154 @@
+// Copyright (c) 2023 DeNA Co., Ltd.
+// This software is released under the MIT License.
+
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+
+namespace DeNA.Anjin.Reporters
+{
+ ///
+ /// An interface for Slack message senders. The derived class of this interface must define message format, and
+ /// delegate touching Slack API to .
+ /// Purpose of introducing this interface is making be a humble object.
+ ///
+ public interface ISlackMessageSender
+ {
+ ///
+ /// Post report log message, stacktrace and screenshot
+ ///
+ /// Slack API token
+ /// Slack Channel to send notification
+ /// Sub team IDs to mention
+ /// Whether adding @here or not
+ /// Log message
+ /// Stack trace
+ /// With screenshot
+ /// Cancellation token
+ ///
+ UniTask Send(
+ string slackToken,
+ string slackChannel,
+ IEnumerable mentionSubTeamIDs,
+ bool addHereInSlackMessage,
+ string logString,
+ string stackTrace,
+ bool withScreenshot,
+ CancellationToken cancellationToken = default
+ );
+ }
+
+ ///
+ /// A class for Slack message senders. This class defines a message format and delegates touching Slack API to
+ /// .
+ ///
+ public class SlackMessageSender : ISlackMessageSender
+ {
+ private readonly SlackAPI _slackAPI;
+ private readonly string _slackToken;
+
+
+ ///
+ /// Creates a new instance for .
+ ///
+ /// Slack API client
+ public SlackMessageSender(SlackAPI api)
+ {
+ _slackAPI = api;
+ }
+
+
+ ///
+ public async UniTask Send(
+ string slackToken,
+ string slackChannel,
+ IEnumerable mentionSubTeamIDs,
+ bool addHereInSlackMessage,
+ string logString,
+ string stackTrace,
+ bool withScreenshot,
+ CancellationToken cancellationToken = default
+ )
+ {
+ if (string.IsNullOrEmpty(slackToken) || string.IsNullOrEmpty(slackChannel))
+ {
+ return;
+ }
+
+ var title = Title(logString, mentionSubTeamIDs, addHereInSlackMessage);
+
+ await UniTask.SwitchToMainThread();
+
+ var postTitleTask = await _slackAPI.Post(
+ slackToken,
+ slackChannel,
+ title,
+ cancellationToken: cancellationToken
+ );
+ if (!postTitleTask.Success)
+ {
+ return;
+ }
+
+ if (withScreenshot && !Application.isBatchMode)
+ {
+ var coroutineRunner = new GameObject().AddComponent();
+ await UniTask.WaitForEndOfFrame(coroutineRunner);
+ Object.Destroy(coroutineRunner);
+
+ var screenshot = ScreenCapture.CaptureScreenshotAsTexture();
+ var withoutAlpha = new Texture2D(screenshot.width, screenshot.height, TextureFormat.RGB24, false);
+ withoutAlpha.SetPixels(screenshot.GetPixels());
+ withoutAlpha.Apply();
+
+ var postScreenshotTask = await _slackAPI.Post(
+ slackToken,
+ slackChannel,
+ withoutAlpha.EncodeToPNG(),
+ postTitleTask.Ts
+ );
+ if (!postScreenshotTask.Success)
+ {
+ return;
+ }
+ }
+
+ var body = Body(logString, stackTrace);
+ await _slackAPI.Post(slackToken, slackChannel, body, postTitleTask.Ts);
+ }
+
+
+ private static string Title(string logString, IEnumerable mentionSubTeamIDs, bool withHere)
+ {
+ var sb = new StringBuilder();
+ foreach (var s in mentionSubTeamIDs)
+ {
+ if (!string.IsNullOrEmpty(s))
+ {
+ // ReSharper disable once StringLiteralTypo
+ sb.Append($" ");
+ }
+ }
+
+ if (withHere)
+ {
+ sb.Append(" ");
+ }
+
+ sb.Append(logString);
+ return sb.ToString();
+ }
+
+
+ private static string Body(string logString, string stackTrace)
+ {
+ return $"{logString}\n\n```{stackTrace}```";
+ }
+
+ private class CoroutineRunner : MonoBehaviour
+ {
+ }
+ }
+}
diff --git a/Runtime/Reporters/SlackMessageSender.cs.meta b/Runtime/Reporters/SlackMessageSender.cs.meta
new file mode 100644
index 0000000..87e16a2
--- /dev/null
+++ b/Runtime/Reporters/SlackMessageSender.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 4bd66e2697dd4869b5a7fc506dc5a027
+timeCreated: 1699480963
\ No newline at end of file
diff --git a/Runtime/Reporters/SlackReporter.cs b/Runtime/Reporters/SlackReporter.cs
index b7014ba..0574388 100644
--- a/Runtime/Reporters/SlackReporter.cs
+++ b/Runtime/Reporters/SlackReporter.cs
@@ -1,7 +1,7 @@
// Copyright (c) 2023 DeNA Co., Ltd.
// This software is released under the MIT License.
-using System.Text;
+using System.Threading;
using Cysharp.Threading.Tasks;
using DeNA.Anjin.Settings;
using UnityEngine;
@@ -11,111 +11,62 @@ namespace DeNA.Anjin.Reporters
///
/// Post report to Slack
///
- public class SlackReporter : IReporter
+ [CreateAssetMenu(fileName = "New SlackReporter", menuName = "Anjin/Slack Reporter", order = 50)]
+ public class SlackReporter : AbstractReporter
{
- private readonly AutopilotSettings _settings;
- private readonly SlackAPI _slackAPI;
-
///
- /// Constructor
+ /// Slack API token
///
- ///
- ///
- public SlackReporter(AutopilotSettings settings, SlackAPI slackAPI)
- {
- _settings = settings;
- _slackAPI = slackAPI;
- }
+ public string slackToken;
///
- /// Post report log message, stacktrace and screenshot to Slack
+ /// Slack channels to send notification (comma separated)
///
- /// Log message string
- /// Stack trace
- /// Log message type
- /// With screenshot
- ///
- public async UniTask PostReportAsync(string logString, string stackTrace, LogType type, bool withScreenshot)
- {
- if (string.IsNullOrEmpty(_settings.slackToken) || string.IsNullOrEmpty(_settings.slackChannels))
- {
- return;
- }
-
- var title = Title(logString, _settings.mentionSubTeamIDs, _settings.addHereInSlackMessage);
- var body = Body(logString, stackTrace);
-
- await UniTask.SwitchToMainThread();
-
- foreach (var channel in _settings.slackChannels.Split(','))
- {
- if (string.IsNullOrEmpty(channel))
- {
- continue;
- }
-
- var postTitleTask = await _slackAPI.Post(_settings.slackToken, channel, title);
- if (!postTitleTask.Success)
- {
- return;
- }
-
- if (withScreenshot && !Application.isBatchMode)
- {
- var coroutineRunner = new GameObject().AddComponent();
- await UniTask.WaitForEndOfFrame(coroutineRunner);
- Object.Destroy(coroutineRunner);
-
- var screenshot = ScreenCapture.CaptureScreenshotAsTexture();
- var withoutAlpha = new Texture2D(screenshot.width, screenshot.height, TextureFormat.RGB24, false);
- withoutAlpha.SetPixels(screenshot.GetPixels());
- withoutAlpha.Apply();
-
- var postScreenshotTask = await _slackAPI.Post(_settings.slackToken, channel,
- withoutAlpha.EncodeToPNG(), postTitleTask.Ts);
- if (!postScreenshotTask.Success)
- {
- return;
- }
- }
+ public string slackChannels;
- var postBodyTask = await _slackAPI.Post(_settings.slackToken, channel, body,
- postTitleTask.Ts);
- if (!postBodyTask.Success)
- {
- return;
- }
- }
- }
+ ///
+ /// Sub team IDs to mention (comma separated)
+ ///
+ public string mentionSubTeamIDs;
- private static string Title(string logString, string mentionSubTeamIDs, bool withHere)
+ ///
+ /// Whether adding @here or not
+ ///
+ public bool addHereInSlackMessage;
+
+ private readonly ISlackMessageSender _sender = new SlackMessageSender(new SlackAPI());
+
+ ///
+ public override async UniTask PostReportAsync(
+ AutopilotSettings settings,
+ string logString,
+ string stackTrace,
+ LogType type,
+ bool withScreenshot,
+ CancellationToken cancellationToken = default
+ )
{
- var title = new StringBuilder();
-
- foreach (var s in mentionSubTeamIDs.Split(','))
+ // NOTE: In _sender.send, switch the execution thread to the main thread, so UniTask.WhenAll is meaningless.
+ foreach (var slackChannel in (string.IsNullOrEmpty(settings.slackChannels)
+ ? slackChannels
+ : settings.slackChannels).Split(","))
{
- if (!string.IsNullOrEmpty(s))
+ if (cancellationToken.IsCancellationRequested)
{
- // ReSharper disable once StringLiteralTypo
- title.Append($" ");
+ return;
}
+
+ await _sender.Send(
+ string.IsNullOrEmpty(settings.slackToken) ? slackToken : settings.slackToken,
+ slackChannel,
+ mentionSubTeamIDs.Split(","),
+ addHereInSlackMessage,
+ logString,
+ stackTrace,
+ withScreenshot,
+ cancellationToken
+ );
}
-
- if (withHere)
- {
- title.Append(" ");
- }
-
- return title.Append(logString).ToString();
- }
-
- private static string Body(string logString, string stackTrace)
- {
- return $"{logString}\n\n```{stackTrace}```";
- }
-
- private class CoroutineRunner : MonoBehaviour
- {
}
}
}
diff --git a/Runtime/Settings/AutopilotSettings.cs b/Runtime/Settings/AutopilotSettings.cs
index 1a4a79b..78279a0 100644
--- a/Runtime/Settings/AutopilotSettings.cs
+++ b/Runtime/Settings/AutopilotSettings.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using DeNA.Anjin.Agents;
+using DeNA.Anjin.Reporters;
using UnityEngine;
namespace DeNA.Anjin.Settings
@@ -78,21 +79,25 @@ public class AutopilotSettings : ScriptableObject
///
/// Slack API token
///
+ [Obsolete("this option moved to SlackReporter")]
public string slackToken;
///
/// Slack channels to send notification
///
+ [Obsolete("this option moved to SlackReporter")]
public string slackChannels;
///
/// Mention to sub team ID (comma separates)
///
+ [Obsolete("this option moved to SlackReporter")]
public string mentionSubTeamIDs;
///
/// Add @here
///
+ [Obsolete("this option moved to SlackReporter")]
public bool addHereInSlackMessage;
///
@@ -120,6 +125,11 @@ public class AutopilotSettings : ScriptableObject
///
public string[] ignoreMessages;
+ ///
+ /// Reporter that called when some errors occurred
+ ///
+ public AbstractReporter reporter;
+
///
/// Overwrites specified values in the command line arguments
///
@@ -147,12 +157,16 @@ public void OverrideByCommandLineArguments(Arguments args)
if (args.SlackToken.IsCaptured())
{
+#pragma warning disable CS0618 // Type or member is obsolete
slackToken = args.SlackToken.Value();
+#pragma warning restore CS0618 // Type or member is obsolete
}
if (args.SlackChannels.IsCaptured())
{
+#pragma warning disable CS0618 // Type or member is obsolete
slackChannels = args.SlackChannels.Value();
+#pragma warning restore CS0618 // Type or member is obsolete
}
}
}
diff --git a/Runtime/Utilities/LogMessageHandler.cs b/Runtime/Utilities/LogMessageHandler.cs
index 1a369e6..6563470 100644
--- a/Runtime/Utilities/LogMessageHandler.cs
+++ b/Runtime/Utilities/LogMessageHandler.cs
@@ -14,14 +14,14 @@ namespace DeNA.Anjin.Utilities
public class LogMessageHandler
{
private readonly AutopilotSettings _settings;
- private readonly IReporter _reporter;
+ private readonly AbstractReporter _reporter;
///
/// Constructor
///
/// Autopilot settings
/// Reporter implementation
- public LogMessageHandler(AutopilotSettings settings, IReporter reporter)
+ public LogMessageHandler(AutopilotSettings settings, AbstractReporter reporter)
{
_settings = settings;
_reporter = reporter;
@@ -42,7 +42,11 @@ public async void HandleLog(string logString, string stackTrace, LogType type)
// NOTE: HandleLog may called by non-main thread because it subscribe Application.logMessageReceivedThreaded
await UniTask.SwitchToMainThread();
- await _reporter.PostReportAsync(logString, stackTrace, type, true);
+
+ if (_reporter != null)
+ {
+ await _reporter.PostReportAsync(_settings, logString, stackTrace, type, true);
+ }
var autopilot = Object.FindObjectOfType();
if (autopilot != null)
diff --git a/Tests/Editor/DeNA.Anjin.Editor.Tests.asmdef b/Tests/Editor/DeNA.Anjin.Editor.Tests.asmdef
index 6c7f934..c4492e6 100644
--- a/Tests/Editor/DeNA.Anjin.Editor.Tests.asmdef
+++ b/Tests/Editor/DeNA.Anjin.Editor.Tests.asmdef
@@ -7,7 +7,8 @@
"DeNA.Anjin",
"DeNA.Anjin.Annotations",
"DeNA.Anjin.Editor",
- "UniTask"
+ "UniTask",
+ "DeNA.Anjin.Tests"
],
"includePlatforms": [
"Editor"
diff --git a/Tests/Runtime/Reporters.meta b/Tests/Runtime/Reporters.meta
new file mode 100644
index 0000000..3b23278
--- /dev/null
+++ b/Tests/Runtime/Reporters.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 84a88262c36d4c7e8b5e92add0851edf
+timeCreated: 1699486146
\ No newline at end of file
diff --git a/Tests/Runtime/Reporters/SlackMessageSenderTest.cs b/Tests/Runtime/Reporters/SlackMessageSenderTest.cs
new file mode 100644
index 0000000..359ac18
--- /dev/null
+++ b/Tests/Runtime/Reporters/SlackMessageSenderTest.cs
@@ -0,0 +1,182 @@
+// Copyright (c) 2023 DeNA Co., Ltd.
+// This software is released under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using DeNA.Anjin.TestDoubles;
+using NUnit.Framework;
+
+namespace DeNA.Anjin.Reporters
+{
+ [TestFixture]
+ public class SlackMessageSenderTest
+ {
+ [Test]
+ public async Task NoMentionsAndNoScreenshotsAndNoAtHere()
+ {
+ var spySlackAPI = new SpySlackAPI();
+ var sender = new SlackMessageSender(spySlackAPI);
+
+ await sender.Send(
+ "TOKEN",
+ "CHANNEL",
+ Array.Empty(),
+ false,
+ "MESSAGE",
+ "STACKTRACE",
+ false
+ );
+
+ var actual = spySlackAPI.Arguments;
+ var expected = new List>
+ {
+ new Dictionary
+ {
+ { "token", "TOKEN" }, { "channel", "CHANNEL" }, { "message", "MESSAGE" }, { "ts", null }
+ },
+ new Dictionary
+ {
+ { "token", "TOKEN" },
+ { "channel", "CHANNEL" },
+ { "message", "MESSAGE\n\n```STACKTRACE```" },
+ { "ts", "1" }
+ },
+ };
+ Assert.That(actual, Is.EqualTo(expected), Format(actual));
+ }
+
+ [Test]
+ public async Task WithMention()
+ {
+ var spySlackAPI = new SpySlackAPI();
+ var sender = new SlackMessageSender(spySlackAPI);
+
+ await sender.Send(
+ "TOKEN",
+ "CHANNEL",
+ new []{"MENTION1", "MENTION2"},
+ false,
+ "MESSAGE",
+ "STACKTRACE",
+ false
+ );
+
+ var actual = spySlackAPI.Arguments;
+ var expected = new List>
+ {
+ new Dictionary
+ {
+ { "token", "TOKEN" }, { "channel", "CHANNEL" }, { "message", " MESSAGE" }, { "ts", null }
+ },
+ new Dictionary
+ {
+ { "token", "TOKEN" },
+ { "channel", "CHANNEL" },
+ { "message", "MESSAGE\n\n```STACKTRACE```" },
+ { "ts", "1" }
+ },
+ };
+ Assert.That(actual, Is.EqualTo(expected), Format(actual));
+ }
+
+ [Test]
+ public async Task WithScreenshot()
+ {
+ var spySlackAPI = new SpySlackAPI();
+ var sender = new SlackMessageSender(spySlackAPI);
+
+ await sender.Send(
+ "TOKEN",
+ "CHANNEL",
+ new []{"MENTION1", "MENTION2"},
+ false,
+ "MESSAGE",
+ "STACKTRACE",
+ true
+ );
+
+ var actual = spySlackAPI.Arguments;
+ var expected = new List>
+ {
+ new Dictionary
+ {
+ { "token", "TOKEN" }, { "channel", "CHANNEL" }, { "message", " MESSAGE" }, { "ts", null }
+ },
+ new Dictionary
+ {
+ { "token", "TOKEN" }, { "channel", "CHANNEL" }, { "message", "IMAGE" }, { "ts", "1" }
+ },
+ new Dictionary
+ {
+ { "token", "TOKEN" },
+ { "channel", "CHANNEL" },
+ { "message", "MESSAGE\n\n```STACKTRACE```" },
+ { "ts", "1" }
+ },
+ };
+ Assert.That(actual, Is.EqualTo(expected));
+ }
+
+ [Test]
+ public async Task WithAtHere()
+ {
+ var spySlackAPI = new SpySlackAPI();
+ var sender = new SlackMessageSender(spySlackAPI);
+
+ await sender.Send(
+ "TOKEN",
+ "CHANNEL",
+ Array.Empty(),
+ true,
+ "MESSAGE",
+ "STACKTRACE",
+ false
+ );
+
+ var actual = spySlackAPI.Arguments;
+ var expected = new List>
+ {
+ new Dictionary
+ {
+ { "token", "TOKEN" }, { "channel", "CHANNEL" }, { "message", " MESSAGE" }, { "ts", null }
+ },
+ new Dictionary
+ {
+ { "token", "TOKEN" },
+ { "channel", "CHANNEL" },
+ { "message", "MESSAGE\n\n```STACKTRACE```" },
+ { "ts", "1" }
+ },
+ };
+ Assert.That(actual, Is.EqualTo(expected), Format(actual));
+ }
+
+
+ private static string Format(List> dicts)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine("[");
+ foreach (var dict in dicts)
+ {
+ sb.AppendLine("\t{");
+ var keys = dict.Keys.ToArray();
+ Array.Sort(keys);
+ foreach (var key in keys)
+ {
+ var value = dict[key];
+ sb.Append("\t\t");
+ sb.Append(key);
+ sb.Append(": ");
+ sb.Append(value);
+ sb.AppendLine(",");
+ }
+ sb.AppendLine("\t},");
+ }
+ sb.AppendLine("]");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/Tests/Runtime/Reporters/SlackMessageSenderTest.cs.meta b/Tests/Runtime/Reporters/SlackMessageSenderTest.cs.meta
new file mode 100644
index 0000000..b8d2786
--- /dev/null
+++ b/Tests/Runtime/Reporters/SlackMessageSenderTest.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 69a1be0e7f9c4e0aa62cb7d0884e5459
+timeCreated: 1699486165
\ No newline at end of file
diff --git a/Tests/Runtime/TestDoubles/SpyReporter.cs b/Tests/Runtime/TestDoubles/SpyReporter.cs
new file mode 100644
index 0000000..a1aca42
--- /dev/null
+++ b/Tests/Runtime/TestDoubles/SpyReporter.cs
@@ -0,0 +1,42 @@
+// Copyright (c) 2023 DeNA Co., Ltd.
+// This software is released under the MIT License.
+
+using System.Collections.Generic;
+using System.Threading;
+using Cysharp.Threading.Tasks;
+using DeNA.Anjin.Reporters;
+using DeNA.Anjin.Settings;
+using UnityEngine;
+
+namespace DeNA.Anjin.TestDoubles
+{
+ ///
+ /// A spy for
+ ///
+ // [CreateAssetMenu(fileName = "New SpyReporter", menuName = "Anjin/Spy Reporter", order = 34)]
+ public class SpyReporter : AbstractReporter
+ {
+ public List> Arguments { get; } = new List>();
+
+ public override async UniTask PostReportAsync(
+ AutopilotSettings settings,
+ string logString,
+ string stackTrace,
+ LogType type,
+ bool withScreenshot,
+ CancellationToken cancellationToken = default
+ )
+ {
+ Debug.Log("Reporter called");
+ Arguments.Add(new Dictionary
+ {
+ {"settings", settings.ToString()},
+ {"logString", logString},
+ {"stackTrace", stackTrace},
+ {"type", type.ToString()},
+ {"withScreenshot", withScreenshot.ToString()}
+ });
+ await UniTask.NextFrame(cancellationToken);
+ }
+ }
+}
diff --git a/Tests/Runtime/TestDoubles/SpyReporter.cs.meta b/Tests/Runtime/TestDoubles/SpyReporter.cs.meta
new file mode 100644
index 0000000..fcd1990
--- /dev/null
+++ b/Tests/Runtime/TestDoubles/SpyReporter.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: fbb88a2914494167b3e9f91d4dc51fdf
+timeCreated: 1699485033
\ No newline at end of file
diff --git a/Tests/Runtime/TestDoubles/StubThrowingErrorFromNotMainThreadOnClick.cs.meta b/Tests/Runtime/TestDoubles/StubThrowingErrorFromNotMainThreadOnClick.cs.meta
index 37b8bb9..b7e3fd4 100644
--- a/Tests/Runtime/TestDoubles/StubThrowingErrorFromNotMainThreadOnClick.cs.meta
+++ b/Tests/Runtime/TestDoubles/StubThrowingErrorFromNotMainThreadOnClick.cs.meta
@@ -1,3 +1,3 @@
fileFormatVersion: 2
guid: c733c26bf53b475ea76992de2fcc418a
-timeCreated: 1699513479
\ No newline at end of file
+timeCreated: 1699513479
diff --git a/Tests/Runtime/Utilities/LogMessageHandlerTest.cs b/Tests/Runtime/Utilities/LogMessageHandlerTest.cs
index d59cfdd..af45411 100644
--- a/Tests/Runtime/Utilities/LogMessageHandlerTest.cs
+++ b/Tests/Runtime/Utilities/LogMessageHandlerTest.cs
@@ -1,10 +1,8 @@
// Copyright (c) 2023 DeNA Co., Ltd.
// This software is released under the MIT License.
-using System.Collections.Generic;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
-using DeNA.Anjin.Reporters;
using DeNA.Anjin.Settings;
using DeNA.Anjin.TestDoubles;
using NUnit.Framework;
@@ -19,339 +17,158 @@ public class LogMessageHandlerTest
public async Task HandleLog_LogTypeIsLog_notReported()
{
var settings = ScriptableObject.CreateInstance();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
+ var spyReporter = ScriptableObject.CreateInstance();
+ var sut = new LogMessageHandler(settings, spyReporter);
sut.HandleLog(string.Empty, string.Empty, LogType.Log);
await UniTask.NextFrame();
- Assert.That(spySlackAPI.Arguments, Is.Empty);
+ Assert.That(spyReporter.Arguments, Is.Empty);
}
[Test]
public async Task HandleLog_LogTypeExceptionHandle_reported()
{
var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
+ var spyReporter = ScriptableObject.CreateInstance();
+ var sut = new LogMessageHandler(settings, spyReporter);
settings.handleException = true;
sut.HandleLog(string.Empty, string.Empty, LogType.Exception);
await UniTask.NextFrame();
- Assert.That(spySlackAPI.Arguments, Is.Not.Empty);
+ Assert.That(spyReporter.Arguments, Is.Not.Empty);
}
[Test]
public async Task HandleLog_LogTypeExceptionNotHandle_notReported()
{
var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
+ var spyReporter = ScriptableObject.CreateInstance();
+ var sut = new LogMessageHandler(settings, spyReporter);
settings.handleException = false;
sut.HandleLog(string.Empty, string.Empty, LogType.Exception);
await UniTask.NextFrame();
- Assert.That(spySlackAPI.Arguments, Is.Empty);
+ Assert.That(spyReporter.Arguments, Is.Empty);
}
[Test]
public async Task HandleLog_LogTypeAssertHandle_reported()
{
var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
+ var spyReporter = ScriptableObject.CreateInstance();
+ var sut = new LogMessageHandler(settings, spyReporter);
settings.handleAssert = true;
sut.HandleLog(string.Empty, string.Empty, LogType.Assert);
await UniTask.NextFrame();
- Assert.That(spySlackAPI.Arguments, Is.Not.Empty);
+ Assert.That(spyReporter.Arguments, Is.Not.Empty);
}
[Test]
public async Task HandleLog_LogTypeAssertNotHandle_notReported()
{
var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
+ var spyReporter = ScriptableObject.CreateInstance();
+ var sut = new LogMessageHandler(settings, spyReporter);
settings.handleAssert = false;
sut.HandleLog(string.Empty, string.Empty, LogType.Assert);
await UniTask.NextFrame();
- Assert.That(spySlackAPI.Arguments, Is.Empty);
+ Assert.That(spyReporter.Arguments, Is.Empty);
}
[Test]
public async Task HandleLog_LogTypeErrorHandle_reported()
{
var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
+ var spyReporter = ScriptableObject.CreateInstance();
+ var sut = new LogMessageHandler(settings, spyReporter);
settings.handleError = true;
sut.HandleLog(string.Empty, string.Empty, LogType.Error);
await UniTask.NextFrame();
- Assert.That(spySlackAPI.Arguments, Is.Not.Empty);
+ Assert.That(spyReporter.Arguments, Is.Not.Empty);
}
[Test]
public async Task HandleLog_LogTypeErrorNotHandle_notReported()
{
var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
+ var spyReporter = ScriptableObject.CreateInstance();
+ var sut = new LogMessageHandler(settings, spyReporter);
settings.handleError = false;
sut.HandleLog(string.Empty, string.Empty, LogType.Error);
await UniTask.NextFrame();
- Assert.That(spySlackAPI.Arguments, Is.Empty);
+ Assert.That(spyReporter.Arguments, Is.Empty);
}
[Test]
public async Task HandleLog_LogTypeWarningHandle_reported()
{
var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
+ var spyReporter = ScriptableObject.CreateInstance();
+ var sut = new LogMessageHandler(settings, spyReporter);
settings.handleWarning = true;
sut.HandleLog(string.Empty, string.Empty, LogType.Warning);
await UniTask.NextFrame();
- Assert.That(spySlackAPI.Arguments, Is.Not.Empty);
+ Assert.That(spyReporter.Arguments, Is.Not.Empty);
}
[Test]
public async Task HandleLog_LogTypeWarningNotHandle_notReported()
{
var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
+ var spyReporter = ScriptableObject.CreateInstance();
+ var sut = new LogMessageHandler(settings, spyReporter);
settings.handleWarning = false;
sut.HandleLog(string.Empty, string.Empty, LogType.Warning);
await UniTask.NextFrame();
- Assert.That(spySlackAPI.Arguments, Is.Empty);
+ Assert.That(spyReporter.Arguments, Is.Empty);
}
[Test]
public async Task HandleLog_ContainsIgnoreMessage_notReported()
{
var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
+ var spyReporter = ScriptableObject.CreateInstance();
+ var sut = new LogMessageHandler(settings, spyReporter);
settings.ignoreMessages = new[] { "ignore" };
settings.handleException = true;
sut.HandleLog("xxx_ignore_xxx", string.Empty, LogType.Exception);
await UniTask.NextFrame();
- Assert.That(spySlackAPI.Arguments, Is.Empty);
+ Assert.That(spyReporter.Arguments, Is.Empty);
}
[Test]
public async Task HandleLog_StacktraceByLogMessageHandler_notReported()
{
var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
+ var spyReporter = ScriptableObject.CreateInstance();
+ var sut = new LogMessageHandler(settings, spyReporter);
settings.handleException = true;
sut.HandleLog(string.Empty, "at DeNA.Anjin.Utilities.LogMessageHandler", LogType.Exception);
await UniTask.NextFrame();
- Assert.That(spySlackAPI.Arguments, Is.Empty);
- }
-
- [Test]
- public async Task HandleLog_StacktraceBySlackAPI_notReported()
- {
- var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
-
- settings.handleException = true;
- sut.HandleLog(string.Empty, "at DeNA.Anjin.Reporters.SlackAPI", LogType.Exception);
- await UniTask.NextFrame();
-
- Assert.That(spySlackAPI.Arguments, Is.Empty);
- }
-
- [Test]
- [Category("IgnoreCI")] // Reason: screen capture is not work run on batchmode
- public async Task HandleLog_SingleChannel_PostToSlack()
- {
- var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
-
- settings.slackToken = "TOKEN";
- settings.slackChannels = "CHANNEL";
- settings.mentionSubTeamIDs = string.Empty;
- settings.addHereInSlackMessage = false;
- settings.handleException = true;
- sut.HandleLog("MESSAGE", "STACKTRACE", LogType.Exception);
- await UniTask.DelayFrame(5);
-
- var expected1 = new Dictionary
- {
- { "token", "TOKEN" }, { "channel", "CHANNEL" }, { "message", "MESSAGE" }, { "ts", null }
- };
- var expected2 = new Dictionary
- {
- { "token", "TOKEN" }, { "channel", "CHANNEL" }, { "message", "IMAGE" }, { "ts", "1" }
- };
- var expected3 = new Dictionary
- {
- { "token", "TOKEN" },
- { "channel", "CHANNEL" },
- { "message", "MESSAGE\n\n```STACKTRACE```" },
- { "ts", "1" }
- };
- var expected = new List> { expected1, expected2, expected3 };
- var actual = spySlackAPI.Arguments;
-
- Assert.That(actual, Is.EqualTo(expected));
- }
-
- [Test]
- public async Task HandleLog_SingleChannelWithMention_PostToSlack()
- {
- var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
-
- settings.slackToken = "TOKEN";
- settings.slackChannels = "CHANNEL";
- settings.mentionSubTeamIDs = "MENTION1,MENTION2";
- settings.addHereInSlackMessage = false;
- settings.handleException = true;
- sut.HandleLog("MESSAGE", "STACKTRACE", LogType.Exception);
- await UniTask.DelayFrame(5);
-
- var expected1 = new Dictionary
- {
- { "token", "TOKEN" },
- { "channel", "CHANNEL" },
- { "message", " MESSAGE" },
- { "ts", null }
- };
- var actual = spySlackAPI.Arguments;
-
- Assert.That(actual[0], Is.EqualTo(expected1));
- }
-
- [Test]
- public async Task HandleLog_SingleChannelWithHere_PostToSlack()
- {
- var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
-
- settings.slackToken = "TOKEN";
- settings.slackChannels = "CHANNEL";
- settings.mentionSubTeamIDs = string.Empty;
- settings.addHereInSlackMessage = true; // add `@here`
- settings.handleException = true;
- sut.HandleLog("MESSAGE", "STACKTRACE", LogType.Exception);
- await UniTask.DelayFrame(5);
-
- var expected1 = new Dictionary
- {
- { "token", "TOKEN" }, { "channel", "CHANNEL" }, { "message", " MESSAGE" }, { "ts", null }
- };
- var actual = spySlackAPI.Arguments;
-
- Assert.That(actual[0], Is.EqualTo(expected1));
- }
-
- [Test]
- [Category("IgnoreCI")] // Reason: screen capture is not work run on batchmode
- public async Task HandleLog_MultiChannel_PostToSlack()
- {
- var settings = CreateEmptyAutopilotSettings();
- var spySlackAPI = new SpySlackAPI();
- var reporter = new SlackReporter(settings, spySlackAPI);
- var sut = new LogMessageHandler(settings, reporter);
-
- settings.slackToken = "TOKEN";
- settings.slackChannels = "CHANNEL1,CHANNEL2";
- settings.mentionSubTeamIDs = string.Empty;
- settings.addHereInSlackMessage = false;
- settings.handleException = true;
- sut.HandleLog("MESSAGE", "STACKTRACE", LogType.Exception);
- await UniTask.DelayFrame(8);
-
- var expected1 = new Dictionary
- {
- { "token", "TOKEN" }, { "channel", "CHANNEL1" }, { "message", "MESSAGE" }, { "ts", null }
- };
- var expected2 = new Dictionary
- {
- { "token", "TOKEN" }, { "channel", "CHANNEL1" }, { "message", "IMAGE" }, { "ts", "1" }
- };
- var expected3 = new Dictionary
- {
- { "token", "TOKEN" },
- { "channel", "CHANNEL1" },
- { "message", "MESSAGE\n\n```STACKTRACE```" },
- { "ts", "1" }
- };
- var expected4 = new Dictionary
- {
- { "token", "TOKEN" }, { "channel", "CHANNEL2" }, { "message", "MESSAGE" }, { "ts", null }
- };
- var expected5 = new Dictionary
- {
- { "token", "TOKEN" }, { "channel", "CHANNEL2" }, { "message", "IMAGE" }, { "ts", "1" }
- };
- var expected6 = new Dictionary
- {
- { "token", "TOKEN" },
- { "channel", "CHANNEL2" },
- { "message", "MESSAGE\n\n```STACKTRACE```" },
- { "ts", "1" }
- };
- var expected = new List>
- {
- expected1,
- expected2,
- expected3,
- expected4,
- expected5,
- expected6
- };
- var actual = spySlackAPI.Arguments;
-
- Assert.That(actual, Is.EqualTo(expected));
+ Assert.That(spyReporter.Arguments, Is.Empty);
}
private static AutopilotSettings CreateEmptyAutopilotSettings()
{
var settings = ScriptableObject.CreateInstance();
- settings.slackToken = "dummy token";
- settings.slackChannels = "dummy channel";
- settings.mentionSubTeamIDs = string.Empty;
settings.ignoreMessages = new string[] { };
return settings;
}
diff --git a/Tests/TestAssets/AutopilotSettingsForImmediatlyFacingErrors.asset b/Tests/TestAssets/AutopilotSettingsForImmediatlyFacingErrors.asset
new file mode 100644
index 0000000..51727c3
--- /dev/null
+++ b/Tests/TestAssets/AutopilotSettingsForImmediatlyFacingErrors.asset
@@ -0,0 +1,32 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: c7a1731caa574ed4b9334b7d52febe27, type: 3}
+ m_Name: AutopilotSettingsForImmediatlyFacingErrors
+ m_EditorClassIdentifier:
+ description:
+ sceneAgentMaps: []
+ fallbackAgent: {fileID: 0}
+ observerAgent: {fileID: 0}
+ lifespanSec: 300
+ randomSeed:
+ timeScale: 1
+ junitReportPath:
+ slackToken:
+ slackChannels:
+ mentionSubTeamIDs:
+ addHereInSlackMessage: 0
+ handleException: 1
+ handleError: 1
+ handleAssert: 1
+ handleWarning: 0
+ ignoreMessages: []
+ reporter: {fileID: 0}
diff --git a/Tests/TestAssets/AutopilotSettingsForImmediatlyFacingErrors.asset.meta b/Tests/TestAssets/AutopilotSettingsForImmediatlyFacingErrors.asset.meta
new file mode 100644
index 0000000..43a69ed
--- /dev/null
+++ b/Tests/TestAssets/AutopilotSettingsForImmediatlyFacingErrors.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a0d887db4a0ca470f845d1c3d0236d31
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant: