Skip to content

Commit

Permalink
Add support calling async method to InitializeOnLaunchAutopilotAttribute
Browse files Browse the repository at this point in the history
  • Loading branch information
nowsprinting committed Sep 17, 2024
1 parent 1c05186 commit fa6c0dc
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 23 deletions.
2 changes: 1 addition & 1 deletion Editor/UI/Settings/AutopilotSettingsEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ internal void Launch(AutopilotState state)
if (EditorApplication.isPlaying)
{
state.launchFrom = LaunchType.EditorPlayMode;
Launcher.Run();
Launcher.Run().Forget();
}
else
{
Expand Down
22 changes: 18 additions & 4 deletions Runtime/Launcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

using System;
using System.Linq;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using DeNA.Anjin.Attributes;
using DeNA.Anjin.Settings;
using DeNA.Anjin.Utilities;
Expand Down Expand Up @@ -33,7 +35,8 @@ internal static void ResetEventHandlers()
/// Run autopilot
/// </summary>
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
public static void Run()
// ReSharper disable once Unity.IncorrectMethodSignature
public static async UniTask Run()
{
var state = AutopilotState.Instance;
if (!state.IsRunning)
Expand All @@ -48,20 +51,31 @@ public static void Run()

ScreenshotStore.CleanDirectories();

CallAttachedInitializeOnLaunchAutopilotAttributeMethods();
await CallAttachedInitializeOnLaunchAutopilotAttributeMethods();

var autopilot = new GameObject(nameof(Autopilot)).AddComponent<Autopilot>();
Object.DontDestroyOnLoad(autopilot);
}

private static void CallAttachedInitializeOnLaunchAutopilotAttributeMethods()
private static async UniTask CallAttachedInitializeOnLaunchAutopilotAttributeMethods()
{
foreach (var methodInfo in AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.SelectMany(x => x.GetMethods())
.Where(x => x.GetCustomAttributes(typeof(InitializeOnLaunchAutopilotAttribute), false).Any()))
{
methodInfo.Invoke(null, null); // static method only
switch (methodInfo.ReturnType.Name)
{
case nameof(Task):
await (Task)methodInfo.Invoke(null, null);
break;
case nameof(UniTask):
await (UniTask)methodInfo.Invoke(null, null);
break;
default:
methodInfo.Invoke(null, null); // static method only
break;
}
}
}

Expand Down
17 changes: 0 additions & 17 deletions Tests/Editor/UI/Settings/AutopilotSettingsEditorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using DeNA.Anjin.Settings;
using DeNA.Anjin.TestDoubles;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
Expand Down Expand Up @@ -66,21 +65,5 @@ public IEnumerator Launch_OnEditMode_RunAutopilotOnPlayMode()
var autopilot = Object.FindObjectOfType<Autopilot>();
Assert.That((bool)autopilot.gameObject, Is.True, "Autopilot object is alive");
}

[UnityTest]
public IEnumerator Launch_CallMethodWithInitializeOnLaunchAutopilotAttribute()
{
SpyInitializeOnLaunchAutopilot.Reset();

var testSettings = AssetDatabase.LoadAssetAtPath<AutopilotSettings>(
"Packages/com.dena.anjin/Tests/TestAssets/AutopilotSettingsForTests.asset");
var editor = (AutopilotSettingsEditor)UnityEditor.Editor.CreateEditor(testSettings);
var state = AutopilotState.Instance;
editor.Launch(state); // Note: Can not call editor.OnInspectorGUI() and GUILayout.Button()

yield return new WaitForDomainReload(); // Wait for domain reloading by switching play mode

Assert.That(SpyInitializeOnLaunchAutopilot.IsCallInitializeOnLaunchAutopilotMethod, Is.True);
}
}
}
44 changes: 43 additions & 1 deletion Tests/Runtime/LauncherTest.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// Copyright (c) 2023 DeNA Co., Ltd.
// This software is released under the MIT License.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using DeNA.Anjin.Editor.UI.Settings;
using DeNA.Anjin.Settings;
using DeNA.Anjin.TestDoubles;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
Expand Down Expand Up @@ -55,7 +57,7 @@ public async Task Launch_StopAutopilotThatRunInPlayMode_KeepPlayMode()
var state = AutopilotState.Instance;
editor.Launch(state);

await Task.Delay(2200); // 2sec+overhead
await Task.Delay(5000); // 2sec+overhead

Assert.That(state.IsRunning, Is.False, "AutopilotState is terminated");
Assert.That(EditorApplication.isPlaying, Is.True, "Keep play mode");
Expand All @@ -69,12 +71,52 @@ public async Task Stop_TerminateAutopilotAndKeepPlayMode()
var editor = (AutopilotSettingsEditor)UnityEditor.Editor.CreateEditor(testSettings);
var state = AutopilotState.Instance;
editor.Launch(state);

await Task.Delay(2000);
await editor.Stop();
// Note: If Autopilot stops for life before Stop, a NullReference exception is raised here.

Assert.That(state.IsRunning, Is.False, "AutopilotState is terminated");
Assert.That(EditorApplication.isPlaying, Is.True, "Keep play mode");
}

[Test]
public async Task Launch_InitializeOnLaunchAutopilotAttribute_Called()
{
SpyInitializeOnLaunchAutopilot.Reset();

var settings = ScriptableObject.CreateInstance(typeof(AutopilotSettings)) as AutopilotSettings;
settings.sceneAgentMaps = new List<SceneAgentMap>();
settings.lifespanSec = 1;
await LauncherFromTest.AutopilotAsync(settings); // TODO: Renamed in another PR

Assert.That(SpyInitializeOnLaunchAutopilot.IsCallInitializeOnLaunchAutopilotMethod, Is.True);
}

[Test]
public async Task Launch_InitializeOnLaunchAutopilotAttributeAttachToAsyncMethod_Called()
{
SpyInitializeOnLaunchAutopilot.Reset();

var settings = ScriptableObject.CreateInstance(typeof(AutopilotSettings)) as AutopilotSettings;
settings.sceneAgentMaps = new List<SceneAgentMap>();
settings.lifespanSec = 1;
await LauncherFromTest.AutopilotAsync(settings); // TODO: Renamed in another PR

Assert.That(SpyInitializeOnLaunchAutopilot.IsCallInitializeOnLaunchAutopilotMethodAsync, Is.True);
}

[Test]
public async Task Launch_InitializeOnLaunchAutopilotAttributeAttachToUniTaskAsyncMethod_Called()
{
SpyInitializeOnLaunchAutopilot.Reset();

var settings = ScriptableObject.CreateInstance(typeof(AutopilotSettings)) as AutopilotSettings;
settings.sceneAgentMaps = new List<SceneAgentMap>();
settings.lifespanSec = 1;
await LauncherFromTest.AutopilotAsync(settings); // TODO: Renamed in another PR

Assert.That(SpyInitializeOnLaunchAutopilot.IsCallInitializeOnLaunchAutopilotMethodUniTaskAsync, Is.True);
}
}
}
20 changes: 20 additions & 0 deletions Tests/Runtime/TestDoubles/SpyInitializeOnLaunchAutopilot.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,43 @@
// Copyright (c) 2023 DeNA Co., Ltd.
// This software is released under the MIT License.

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

namespace DeNA.Anjin.TestDoubles
{
public static class SpyInitializeOnLaunchAutopilot
{
public static bool IsCallInitializeOnLaunchAutopilotMethod { get; private set; }
public static bool IsCallInitializeOnLaunchAutopilotMethodAsync { get; private set; }
public static bool IsCallInitializeOnLaunchAutopilotMethodUniTaskAsync { get; private set; }

public static void Reset()
{
IsCallInitializeOnLaunchAutopilotMethod = false;
IsCallInitializeOnLaunchAutopilotMethodAsync = false;
IsCallInitializeOnLaunchAutopilotMethodUniTaskAsync = false;
}

[InitializeOnLaunchAutopilot]
public static void InitializeOnLaunchAutopilotMethod()
{
IsCallInitializeOnLaunchAutopilotMethod = true;
}

[InitializeOnLaunchAutopilot]
public static async Task InitializeOnLaunchAutopilotMethodAsync()
{
await Task.Delay(0);
IsCallInitializeOnLaunchAutopilotMethodAsync = true;
}

[InitializeOnLaunchAutopilot]
public static async UniTask InitializeOnLaunchAutopilotMethodUniTaskAsync()
{
await UniTask.Delay(0);
IsCallInitializeOnLaunchAutopilotMethodUniTaskAsync = true;
}
}
}

0 comments on commit fa6c0dc

Please sign in to comment.