-
Notifications
You must be signed in to change notification settings - Fork 119
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 support for using AsyncAwaitUtil in Editor scripts #18
base: master
Are you sure you want to change the base?
Conversation
As described modesttree#9, using Unity3dAsyncAwaitUtil in Editor scripts causes an NPE, because `SyncContextUtil.Install` is never called to initialize global variables `SyncContextUtil.UnitySynchronizationContext` and `SyncContextUtil.UnityThreadId`. Fix this by adding the `[InitializeOnLoadMethod()]` attribute to the `SyncContextUtil.Install` method. Note that the existing `[RuntimeInitializeOnLoadMethod]` attribute is still needed in order for `SyncContextUtil.Install` to be correctly invoked in Play Mode. Fixes modesttree#9
See #9 (comment) for a much more minimal change for editor async support |
* Copy existing `AsyncCoroutineRunner` -> `PlayModeCoroutineRunner` * Add new class `EditModeCoroutineRunner` for running coroutines in Edit Mode * Modify `AsyncCoroutineRunner` to delegate to `PlayModeCoroutineRunner` or `EditModeCoroutineRunner`, depending on whether Unity is in Play Mode or Edit Mode The main purpose of `EditModeCoroutineRunner` is to call `MoveNext()` on in-flight coroutines at regular intervals. Unlike Play Mode, Unity does *not* call `MoveNext()` on coroutines at regular intervals in Edit Mode. In addition to pumping the coroutines by calling `MoveNext()`, `EditModeCoroutineRunner` must also handle the various types of "yield instructions" (i.e. `yield return` values) returned by the coroutines, which may have specially-defined behaviour in Unity. (Two examples of yield instructions with specially defined behaviour are `WaitForSeconds` and `WaitForSecondsRealtime`.) The handling for the different types of yield instructions is performed in `EditorModeCoroutineRunner.Update()`.
Added a new editor window for running tests in Edit Mode. Similar to the existing tests in Play Mode, the Edit Mode tests are triggered by clicking buttons in a GUI. The new Edit Mode Tests window can be opened from the Unity menu by going to: Window => AsyncAwaitUtil => "Edit Mode Tests". The test methods are shared between the Play Mode GUI and Edit Mode GUI and are located in `AsyncUtilTests.cs` (as before). Note 1: In order for all of the Edit Mode tests to run successfully, the Unity version must be 2018.2.0 or higher! Earlier versions of Unity have fundamental issues regarding usage of async/await in Editor scripts. See the following Unity Forum thread for details: https://forum.unity.com/threads/async-await-in-editor-script.481276/ Note 2: For maintenance purposes, it would be better if the Edit Mode tests were implemented as normal unit tests that could be run by the Unity Test Runner. I attempted to do this in Unity 2017.1.1f1, but found it impossible due to poor async/await support in that version of Unity. However, it may be possible in newer versions of Unity (I haven't tried).
Thanks for the pointer to your patch. I actually didn't see it before I started writing my own solution, and I was a bit depressed to find out that I had spent ~4 days implementing/debugging/testing for nothing. Gah! However! I tried running my Edit Mode tests with your changes and most of the tests do not run to completion (I tried with Unity 2018.3.0f2 and Unity 2019.1.9f1). It seems to be related to the use of In case you (or anyone else) wants to try it, I have combined your changes with my Edit Mode tests on the I would actually prefer use your solution as it is a lot simpler, but I guess I will leave this pull request open for the time being. |
I forgot to mention: I tried (again) to write proper unit tests, this time with Unity 2018.3.0f2, but it still doesn't seem possible due to poor async/await support in the version of NUnit that ships with Unity. See this thread for further discussion: https://forum.unity.com/threads/async-await-in-unittests.513857/ |
Fixes compile error when doing standalone builds.
I spent yesterday adding AsyncAwaitUtil into my project and learning how to unit test async code with Unity's somewhat old version of NUnit and only ran across this when I was thinking of sending a PR. Wish I had checked first! Anyway, maybe these data points will be useful:
For example, here's a couple tests I wrote today:
Edit: to use AsIEnumerator() so exceptions propagate properly. |
Oh, I forgot I also had to put this awkward hack into AsyncAwaitUtil to integrate it with com.unity.editorcoroutines.
|
Thanks very much for sharing your knowledge, @dubois! I was not aware of the com.unity.editorcoroutines package, and I'm happy to hear that there is already a solution available that is written/supported by Unity themselves! I guess that probably renders this PR obsolete, but as a sanity check I will try out the package with my own project before I close this PR. The unit test examples are also super helpful, so thank you! |
add support for using AsyncAwaitUtil in Editor scripts
minimum version requirement: Unity 2018.2.0
Please note that in addition to the changes contained in this pull request, safe use of AsyncAwaitUtil in Editor scripts requires Unity 2018.2.0 or newer. Older versions of Unity have fundamental issues with usage of async/await in Editor scripts, as discussed in this thread: https://forum.unity.com/threads/async-await-in-editor-script.481276/
problems solved by this pull request
This pull request adds support for using AsyncAwaitUtil in Editor scripts.
Unity version aside, there are currently two issues regarding usage of AsyncAwaitUtil in Editor scripts:
SyncContextUtil.Install
is only invoked when entering Play Mode (see Cannot use for Editor unit testing, as SyncContextUtil.Install is not called except at runtime #9)MoveNext()
on them at regular intervals.Issue 1 is minor and is easily fixed in this pull request by adding the
[InitializeOnLoad]
attribute toSyncContextUtil.Install
.Issue 2 required a bit more work. To solve it, I added a new class called
EditModeCoroutineRunner
that tracks running coroutines and callsMoveNext()
on them after eachEditorApplication.update
event. In addition,EditModeCoroutineRunner
must correctly handle the various types of "yield instructions" (i.e. objects return byyield return
) that have specially defined behaviour in Play Mode (e.g.WaitForSeconds
).tests
To test that AsyncAwaitUtil works correctly in Edit Mode, I have added a new editor GUI that runs the existing tests in Edit Mode, and which can be accessed from the Unity menu under
Window => AsyncAwaitUtil => "Edit Mode Tests"
. Similar to the GUI for Play Mode tests in theAsyncTests
scene, the user must manually run individual tests by clicking buttons in the GUI, and then interpret resulting output in the console to determine if that test succeeded/failed.As per my notes about Unity versions above, many of the edit tests fail in Unity versions older than 2018.2.0. I have confirmed that all of the Edit Mode tests behave correctly in Unity versions 2018.2.0f2, 2018.3.0f2, 2019.1.9f1 with the following exceptions:
It may be possible to convert the Edit Mode tests into normal, automated unit tests that can be run with the Unity Test Runner. I attempted to do this using Unity 2017.1.1f1 but I ran into too many problems caused by poor Editor support for async/await support in that version (see my notes about Unity versions above). However, I haven't attempted to write unit tests in newer Unity versions and it may be worth a try.