Merge remote-tracking branch 'origin/master'

This commit is contained in:
Yoshifumi Kawai
2020-04-16 20:37:26 +09:00
14 changed files with 1348 additions and 24 deletions

8
Assets/Tests/Editor.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 649f696fcc0c3104a8de82a2550d248c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,405 @@
#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2)
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine.UI;
using UnityEngine.Scripting;
using UniRx;
using UniRx.Async;
using UnityEngine.SceneManagement;
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
using System.Threading.Tasks;
#endif
using UnityEngine.Networking;
#if !UNITY_2019_3_OR_NEWER
using UnityEngine.Experimental.LowLevel;
#else
using UnityEngine.LowLevel;
#endif
#if !UNITY_WSA
using Unity.Jobs;
#endif
using Unity.Collections;
using System.Threading;
using NUnit.Framework;
using UnityEngine.TestTools;
using FluentAssertions;
namespace UniRx.AsyncTests
{
public class AsyncTest
{
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
#if !UNITY_WSA
public struct MyJob : IJob
{
public int loopCount;
public NativeArray<int> inOut;
public int result;
public void Execute()
{
result = 0;
for (int i = 0; i < loopCount; i++)
{
result++;
}
inOut[0] = result;
}
}
[UnityTest]
public IEnumerator DelayAnd() => UniTask.ToCoroutine(async () =>
{
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
var time = Time.realtimeSinceStartup;
Time.timeScale = 0.5f;
try
{
await UniTask.Delay(TimeSpan.FromSeconds(3));
var elapsed = Time.realtimeSinceStartup - time;
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(6);
}
finally
{
Time.timeScale = 1.0f;
}
});
[UnityTest]
public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
{
var time = Time.realtimeSinceStartup;
Time.timeScale = 0.5f;
try
{
await UniTask.Delay(TimeSpan.FromSeconds(3), ignoreTimeScale: true);
var elapsed = Time.realtimeSinceStartup - time;
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3);
}
finally
{
Time.timeScale = 1.0f;
}
});
[UnityTest]
public IEnumerator WhenAll() => UniTask.ToCoroutine(async () =>
{
var a = UniTask.FromResult(999);
var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask();
var c = UniTask.DelayFrame(99);
var (a2, b2, c2) = await UniTask.WhenAll(a, b, c);
a2.Should().Be(999);
b2.Should().Be(AsyncUnit.Default);
c2.Should().Be(99);
});
[UnityTest]
public IEnumerator WhenAny() => UniTask.ToCoroutine(async () =>
{
var a = UniTask.FromResult(999);
var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask();
var c = UniTask.DelayFrame(99);
var (win, a2, b2, c2) = await UniTask.WhenAny(a, b, c);
win.Should().Be(0);
a2.hasResult.Should().Be(true);
a2.result0.Should().Be(999);
b2.hasResult.Should().Be(false);
c2.hasResult.Should().Be(false);
});
[UnityTest]
public IEnumerator BothEnumeratorCheck() => UniTask.ToCoroutine(async () =>
{
await ToaruCoroutineEnumerator(); // wait 5 frame:)
await ToaruCoroutineEnumerator().ConfigureAwait(PlayerLoopTiming.PostLateUpdate);
});
[UnityTest]
public IEnumerator JobSystem() => UniTask.ToCoroutine(async () =>
{
var job = new MyJob() { loopCount = 999, inOut = new NativeArray<int>(1, Allocator.TempJob) };
JobHandle.ScheduleBatchedJobs();
await job.Schedule();
job.inOut[0].Should().Be(999);
job.inOut.Dispose();
});
class MyMyClass
{
public int MyProperty { get; set; }
}
[UnityTest]
public IEnumerator WaitUntil() => UniTask.ToCoroutine(async () =>
{
bool t = false;
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
UniTask.DelayFrame(10,PlayerLoopTiming.PostLateUpdate).ContinueWith(_ => t = true).Forget();
var startFrame = Time.frameCount;
await UniTask.WaitUntil(() => t, PlayerLoopTiming.EarlyUpdate);
var diff = Time.frameCount - startFrame;
diff.Should().Be(11);
});
[UnityTest]
public IEnumerator WaitWhile() => UniTask.ToCoroutine(async () =>
{
bool t = true;
UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(_ => t = false).Forget();
var startFrame = Time.frameCount;
await UniTask.WaitWhile(() => t, PlayerLoopTiming.EarlyUpdate);
var diff = Time.frameCount - startFrame;
diff.Should().Be(11);
});
[UnityTest]
public IEnumerator WaitUntilValueChanged() => UniTask.ToCoroutine(async () =>
{
var v = new MyMyClass { MyProperty = 99 };
UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(_ => v.MyProperty = 1000).Forget();
var startFrame = Time.frameCount;
await UniTask.WaitUntilValueChanged(v, x => x.MyProperty, PlayerLoopTiming.EarlyUpdate);
var diff = Time.frameCount - startFrame;
diff.Should().Be(11);
});
[UnityTest]
public IEnumerator SwitchTo() => UniTask.ToCoroutine(async () =>
{
await UniTask.Yield();
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
await UniTask.SwitchToThreadPool();
//await UniTask.SwitchToThreadPool();
//await UniTask.SwitchToThreadPool();
var switchedThreadId = Thread.CurrentThread.ManagedThreadId;
currentThreadId.Should().NotBe(switchedThreadId);
await UniTask.Yield();
var switchedThreadId2 = Thread.CurrentThread.ManagedThreadId;
currentThreadId.Should().Be(switchedThreadId2);
});
//[UnityTest]
//public IEnumerator ObservableConversion() => UniTask.ToCoroutine(async () =>
//{
// var v = await Observable.Range(1, 10).ToUniTask();
// v.Is(10);
// v = await Observable.Range(1, 10).ToUniTask(useFirstValue: true);
// v.Is(1);
// v = await UniTask.DelayFrame(10).ToObservable().ToTask();
// v.Is(10);
// v = await UniTask.FromResult(99).ToObservable();
// v.Is(99);
//});
//[UnityTest]
//public IEnumerator AwaitableReactiveProperty() => UniTask.ToCoroutine(async () =>
//{
// var rp1 = new ReactiveProperty<int>(99);
// UniTask.DelayFrame(100).ContinueWith(x => rp1.Value = x).Forget();
// await rp1;
// rp1.Value.Is(100);
// // var delay2 = UniTask.DelayFrame(10);
// // var (a, b ) = await UniTask.WhenAll(rp1.WaitUntilValueChangedAsync(), delay2);
//});
//[UnityTest]
//public IEnumerator AwaitableReactiveCommand() => UniTask.ToCoroutine(async () =>
//{
// var rc = new ReactiveCommand<int>();
// UniTask.DelayFrame(100).ContinueWith(x => rc.Execute(x)).Forget();
// var v = await rc;
// v.Is(100);
//});
[UnityTest]
public IEnumerator ExceptionlessCancellation() => UniTask.ToCoroutine(async () =>
{
var cts = new CancellationTokenSource();
UniTask.DelayFrame(10).ContinueWith(_ => cts.Cancel()).Forget();
var first = Time.frameCount;
var (canceled, value) = await UniTask.DelayFrame(100, cancellationToken: cts.Token).SuppressCancellationThrow();
(Time.frameCount - first).Should().Be(11); // 10 frame canceled
canceled.Should().Be(true);
});
[UnityTest]
public IEnumerator ExceptionCancellation() => UniTask.ToCoroutine(async () =>
{
var cts = new CancellationTokenSource();
UniTask.DelayFrame(10).ContinueWith(_ => cts.Cancel()).Forget();
bool occur = false;
try
{
var value = await UniTask.DelayFrame(100, cancellationToken: cts.Token);
}
catch (OperationCanceledException)
{
occur = true;
}
occur.Should().BeTrue();
});
IEnumerator ToaruCoroutineEnumerator()
{
yield return null;
yield return null;
yield return null;
yield return null;
yield return null;
}
[UnityTest]
public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () =>
{
bool calledEx = false;
Action<Exception> action = exx =>
{
calledEx = true;
exx.Message.Should().Be("MyException");
};
UniTaskScheduler.UnobservedTaskException += action;
var ex = InException1();
ex = default(UniTask);
await UniTask.DelayFrame(3);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
await UniTask.DelayFrame(1);
calledEx.Should().BeTrue();
UniTaskScheduler.UnobservedTaskException -= action;
});
[UnityTest]
public IEnumerator ExceptionUnobserved2() => UniTask.ToCoroutine(async () =>
{
bool calledEx = false;
Action<Exception> action = exx =>
{
calledEx = true;
exx.Message.Should().Be("MyException");
};
UniTaskScheduler.UnobservedTaskException += action;
var ex = InException2();
ex = default(UniTask<int>);
await UniTask.DelayFrame(3);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
await UniTask.DelayFrame(1);
calledEx.Should().BeTrue();
UniTaskScheduler.UnobservedTaskException -= action;
});
async UniTask InException1()
{
await UniTask.Yield();
throw new Exception("MyException");
}
async UniTask<int> InException2()
{
await UniTask.Yield();
throw new Exception("MyException");
}
[UnityTest]
public IEnumerator NestedEnumerator() => UniTask.ToCoroutine(async () =>
{
var time = Time.realtimeSinceStartup;
await ParentCoroutineEnumerator();
var elapsed = Time.realtimeSinceStartup - time;
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3);
});
IEnumerator ParentCoroutineEnumerator()
{
yield return ChildCoroutineEnumerator();
}
IEnumerator ChildCoroutineEnumerator()
{
yield return new WaitForSeconds(3);
}
#endif
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 851899ba3337ac24a8331cb18fabe771
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,96 @@
#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2)
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine.UI;
using UnityEngine.Scripting;
using UniRx;
using UniRx.Async;
using UnityEngine.SceneManagement;
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
using System.Threading.Tasks;
#endif
using UnityEngine.Networking;
#if !UNITY_2019_3_OR_NEWER
using UnityEngine.Experimental.LowLevel;
#else
using UnityEngine.LowLevel;
#endif
#if !UNITY_WSA
using Unity.Jobs;
#endif
using Unity.Collections;
using System.Threading;
using NUnit.Framework;
using UnityEngine.TestTools;
using FluentAssertions;
namespace UniRx.AsyncTests
{
public class RunTest
{
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
#if !UNITY_WSA
//[UnityTest]
//public IEnumerator RunThread() => UniTask.ToCoroutine(async () =>
//{
// var main = Thread.CurrentThread.ManagedThreadId;
// var v = await UniTask.Run(() => { return System.Threading.Thread.CurrentThread.ManagedThreadId; }, false);
// UnityEngine.Debug.Log("Ret Value is:" + v);
// UnityEngine.Debug.Log("Run Here and id:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
// //v.Should().Be(3);
// main.Should().NotBe(Thread.CurrentThread.ManagedThreadId);
//});
[UnityTest]
public IEnumerator RunThreadConfigure() => UniTask.ToCoroutine(async () =>
{
var main = Thread.CurrentThread.ManagedThreadId;
var v = await UniTask.Run(() => 3, true);
v.Should().Be(3);
main.Should().Be(Thread.CurrentThread.ManagedThreadId);
});
//[UnityTest]
//public IEnumerator RunThreadException() => UniTask.ToCoroutine(async () =>
//{
// var main = Thread.CurrentThread.ManagedThreadId;
// try
// {
// await UniTask.Run<int>(() => throw new Exception(), false);
// }
// catch
// {
// main.Should().NotBe(Thread.CurrentThread.ManagedThreadId);
// }
//});
[UnityTest]
public IEnumerator RunThreadExceptionConfigure() => UniTask.ToCoroutine(async () =>
{
var main = Thread.CurrentThread.ManagedThreadId;
try
{
await UniTask.Run<int>(() => throw new Exception(), true);
}
catch
{
main.Should().Be(Thread.CurrentThread.ManagedThreadId);
}
});
#endif
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76d078e5d960ac2429ce67a930c29ade
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
{
"name": "UniRx.Async.Tests.Editor",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"UniRx.Async",
"UniRx.Async.Tests"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c2431de67d1d97a48afcaf18f333a0b4
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine.UI;
using UnityEngine.Scripting;
using UniRx;
using UniRx.Async;
using Unity.Collections;
using System.Threading;
using NUnit.Framework;
using UnityEngine.TestTools;
using FluentAssertions;
namespace UniRx.AsyncTests
{
public class WhenAnyTest
{
[UnityTest]
public IEnumerator WhenAnyCanceled() => UniTask.ToCoroutine(async () =>
{
var cts = new CancellationTokenSource();
var successDelayTask = UniTask.Delay(TimeSpan.FromSeconds(1));
var cancelTask = UniTask.Delay(TimeSpan.FromSeconds(1), cancellationToken: cts.Token);
cts.CancelAfterSlim(200);
try
{
var r = await UniTask.WhenAny(new[] { successDelayTask, cancelTask });
}
catch (Exception ex)
{
ex.Should().BeAssignableTo<OperationCanceledException>();
}
});
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b5addd9aa1a794441af032a71208b495
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -64,6 +64,15 @@ namespace UniRx.Async.Internal
}
}
public void Clear()
{
actionListCount = 0;
actionList = new Action[InitialSize];
waitingListCount = 0;
waitingList = new Action[InitialSize];
}
public void Run()
{
{

View File

@@ -45,6 +45,17 @@ namespace UniRx.Async.Internal
}
}
public void Clear()
{
lock (arrayLock)
{
for (var index = 0; index < loopItems.Length; index++)
{
loopItems[index] = null;
}
}
}
public void Run()
{
lock (runningAndQueueLock)

View File

@@ -13,6 +13,10 @@ using UnityEngine.LowLevel;
using UnityEngine.Experimental.LowLevel;
#endif
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UniRx.Async
{
public static class UniTaskLoopRunners
@@ -62,38 +66,32 @@ namespace UniRx.Async
static ContinuationQueue[] yielders;
static PlayerLoopRunner[] runners;
static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem, Type loopRunnerYieldType, ContinuationQueue cq, Type loopRunnerType, PlayerLoopRunner runner)
static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem, Type loopRunnerYieldType,
ContinuationQueue cq, Type loopRunnerType, PlayerLoopRunner runner)
{
#if UNITY_EDITOR
EditorApplication.playModeStateChanged += (state) =>
{
if (state == PlayModeStateChange.EnteredEditMode ||
state == PlayModeStateChange.EnteredPlayMode) return;
if (runner != null)
runner.Clear();
if (cq != null)
cq.Clear();
};
#endif
var yieldLoop = new PlayerLoopSystem
{
type = loopRunnerYieldType,
#if UNITY_EDITOR
updateDelegate = () =>
{
if (Application.isPlaying)
{
cq.Run();
}
}
#else
updateDelegate = cq.Run
#endif
};
var runnerLoop = new PlayerLoopSystem
{
type = loopRunnerType,
#if UNITY_EDITOR
updateDelegate = () =>
{
if (Application.isPlaying)
{
runner.Run();
}
}
#else
updateDelegate = runner.Run
#endif
};
var source = loopSystem.subSystemList // Remove items from previous initializations.
@@ -132,6 +130,34 @@ namespace UniRx.Async
Initialize(ref playerLoop);
}
#if UNITY_EDITOR
[InitializeOnLoadMethod]
static void InitOnEditor()
{
//Execute the play mode init method
Init();
//register an Editor update delegate, used to forcing playerLoop update
EditorApplication.update += ForceEditorPlayerLoopUpdate;
}
private static void ForceEditorPlayerLoopUpdate()
{
if (EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isCompiling ||
EditorApplication.isUpdating)
{
// Not in Edit mode, don't interfere
return;
}
//force unity to update PlayerLoop callbacks
EditorApplication.QueuePlayerLoopUpdate();
}
#endif
public static void Initialize(ref PlayerLoopSystem playerLoop)
{
yielders = new ContinuationQueue[7];