mirror of https://github.com/Cysharp/UniTask
import from UniRx and some modified.
parent
d5dab7fd1a
commit
5aaeb13c5d
|
@ -1,21 +1,143 @@
|
||||||
/[Ll]ibrary/
|
# Unity
|
||||||
/[Tt]emp/
|
|
||||||
/[Oo]bj/
|
|
||||||
/[Bb]uild/
|
|
||||||
|
|
||||||
# Autogenerated VS/MD solution and project files
|
|
||||||
*.csproj
|
|
||||||
*.unityproj
|
|
||||||
*.sln
|
|
||||||
*.suo
|
|
||||||
*.tmp
|
|
||||||
*.user
|
|
||||||
*.userprefs
|
|
||||||
*.pidb
|
*.pidb
|
||||||
*.booproj
|
*.suo
|
||||||
|
*.userprefs
|
||||||
|
*.vsmdi
|
||||||
|
*.testsettings
|
||||||
|
*/bin
|
||||||
|
*/obj
|
||||||
|
*/publish
|
||||||
|
$tf
|
||||||
|
TestResults
|
||||||
|
!*.sln
|
||||||
|
!*.csproj
|
||||||
|
!*/*.csproj
|
||||||
|
[Ll]ibrary/
|
||||||
|
[Tt]emp/
|
||||||
|
[Oo]bj/
|
||||||
|
|
||||||
# Unity3D generated meta files
|
# VS2013
|
||||||
*.pidb.meta
|
|
||||||
|
|
||||||
# Unity3D Generated File On Crash Reports
|
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
|
||||||
sysinfo.txt
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
|
||||||
|
# mstest test results
|
||||||
|
TestResults
|
||||||
|
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Rr]elease/
|
||||||
|
x64/
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*.ilk
|
||||||
|
# *.meta # already ignored in Unity section
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
*.ncrunch*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.Publish.xml
|
||||||
|
|
||||||
|
# NuGet Packages Directory
|
||||||
|
packages
|
||||||
|
|
||||||
|
# Windows Azure Build Output
|
||||||
|
csx
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
[Bb]in
|
||||||
|
[Oo]bj
|
||||||
|
sql
|
||||||
|
TestResults
|
||||||
|
[Tt]est[Rr]esult*
|
||||||
|
*.Cache
|
||||||
|
ClientBin
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*.dbmdl
|
||||||
|
Generated_Code #added for RIA/Silverlight projects
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file to a newer
|
||||||
|
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
Assets/WSATestCertificate.pfx
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
Assembly-CSharp\.csproj
|
||||||
|
|
||||||
|
UniRx\.Async\.csproj
|
||||||
|
|
||||||
|
UniRx\.Async\.Editor\.csproj
|
||||||
|
|
||||||
|
UniRx\.Async\.Tests\.csproj
|
||||||
|
|
||||||
|
UniTask\.sln
|
||||||
|
|
||||||
|
RuntimeUnitTestToolkit\.csproj
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 894f21dfce4e82343a91661e1ec1a455
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 959c1472a5d812843bedf9341e87af3b
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,133 @@
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace RuntimeUnitTestToolkit.Editor
|
||||||
|
{
|
||||||
|
// functional declarative construction like flutter.
|
||||||
|
|
||||||
|
internal interface IBuilder
|
||||||
|
{
|
||||||
|
GameObject GameObject { get; }
|
||||||
|
T GetComponent<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Builder<T> : IBuilder
|
||||||
|
where T : Component
|
||||||
|
{
|
||||||
|
public T Component1 { get; private set; }
|
||||||
|
public GameObject GameObject { get; private set; }
|
||||||
|
|
||||||
|
public Transform Transform { get { return GameObject.transform; } }
|
||||||
|
public RectTransform RectTransform { get { return GameObject.GetComponent<RectTransform>(); } }
|
||||||
|
|
||||||
|
public Action<GameObject> SetTarget
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
value(this.GameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public IBuilder Child
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
value.GameObject.transform.SetParent(GameObject.transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBuilder[] Children
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
foreach (var item in value)
|
||||||
|
{
|
||||||
|
item.GameObject.transform.SetParent(GameObject.transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder(string name)
|
||||||
|
{
|
||||||
|
this.GameObject = new GameObject(name);
|
||||||
|
this.Component1 = GameObject.AddComponent<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder(string name, out T referenceSelf) // out primary reference.
|
||||||
|
{
|
||||||
|
this.GameObject = new GameObject(name);
|
||||||
|
this.Component1 = GameObject.AddComponent<T>();
|
||||||
|
referenceSelf = this.Component1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TComponent GetComponent<TComponent>()
|
||||||
|
{
|
||||||
|
return this.GameObject.GetComponent<TComponent>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Builder<T1, T2> : Builder<T1>
|
||||||
|
where T1 : Component
|
||||||
|
where T2 : Component
|
||||||
|
{
|
||||||
|
public T2 Component2 { get; private set; }
|
||||||
|
|
||||||
|
public Builder(string name)
|
||||||
|
: base(name)
|
||||||
|
{
|
||||||
|
this.Component2 = GameObject.AddComponent<T2>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder(string name, out T1 referenceSelf)
|
||||||
|
: base(name, out referenceSelf)
|
||||||
|
{
|
||||||
|
this.Component2 = GameObject.AddComponent<T2>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Builder<T1, T2, T3> : Builder<T1, T2>
|
||||||
|
where T1 : Component
|
||||||
|
where T2 : Component
|
||||||
|
where T3 : Component
|
||||||
|
{
|
||||||
|
public T3 Component3 { get; private set; }
|
||||||
|
|
||||||
|
public Builder(string name)
|
||||||
|
: base(name)
|
||||||
|
{
|
||||||
|
this.Component3 = GameObject.AddComponent<T3>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder(string name, out T1 referenceSelf)
|
||||||
|
: base(name, out referenceSelf)
|
||||||
|
{
|
||||||
|
this.Component3 = GameObject.AddComponent<T3>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Builder<T1, T2, T3, T4> : Builder<T1, T2, T3>
|
||||||
|
where T1 : Component
|
||||||
|
where T2 : Component
|
||||||
|
where T3 : Component
|
||||||
|
where T4 : Component
|
||||||
|
{
|
||||||
|
public T4 Component4 { get; private set; }
|
||||||
|
|
||||||
|
public Builder(string name)
|
||||||
|
: base(name)
|
||||||
|
{
|
||||||
|
this.Component4 = GameObject.AddComponent<T4>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder(string name, out T1 referenceSelf)
|
||||||
|
: base(name, out referenceSelf)
|
||||||
|
{
|
||||||
|
this.Component4 = GameObject.AddComponent<T4>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8760bbbab905a534eb6fb7b61b736926
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,341 @@
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
// Settings MenuItems.
|
||||||
|
|
||||||
|
public static partial class UnitTestBuilder
|
||||||
|
{
|
||||||
|
[MenuItem("Test/Settings/ScriptBackend/Mono", validate = true, priority = 1)]
|
||||||
|
static bool ValidateScriptBackendMono()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/ScriptBackend/Mono", LoadOrGetDefaultSettings().ScriptBackend == ScriptingImplementation.Mono2x);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/ScriptBackend/Mono", validate = false, priority = 1)]
|
||||||
|
static void ScriptBackendMono()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentScriptBackend = false;
|
||||||
|
settings.ScriptBackend = ScriptingImplementation.Mono2x;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/ScriptBackend/IL2CPP", validate = true, priority = 2)]
|
||||||
|
static bool ValidateScriptBackendIL2CPP()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/ScriptBackend/IL2CPP", LoadOrGetDefaultSettings().ScriptBackend == ScriptingImplementation.IL2CPP);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/ScriptBackend/IL2CPP", validate = false, priority = 2)]
|
||||||
|
static void ScriptBackendIL2CPP()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentScriptBackend = false;
|
||||||
|
settings.ScriptBackend = ScriptingImplementation.IL2CPP;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/AutoRunPlayer", validate = true, priority = 3)]
|
||||||
|
static bool ValidateAutoRun()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/AutoRunPlayer", LoadOrGetDefaultSettings().AutoRunPlayer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/AutoRunPlayer", validate = false, priority = 3)]
|
||||||
|
static void AutoRun()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.AutoRunPlayer = !settings.AutoRunPlayer;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/Headless", validate = true, priority = 4)]
|
||||||
|
static bool ValidateHeadless()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/Headless", LoadOrGetDefaultSettings().Headless);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/Headless", validate = false, priority = 4)]
|
||||||
|
static void Headless()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.Headless = !settings.Headless;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/DisableAutoClose", validate = true, priority = 5)]
|
||||||
|
static bool ValidateDisableAutoClose()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/DisableAutoClose", LoadOrGetDefaultSettings().DisableAutoClose);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/DisableAutoClose", validate = false, priority = 5)]
|
||||||
|
static void DisableAutoClose()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.DisableAutoClose = !settings.DisableAutoClose;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generated
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
void Main()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
var p = 1;
|
||||||
|
foreach (var target in Enum.GetNames(typeof(BuildTarget)))
|
||||||
|
{
|
||||||
|
var path = $"Test/Settings/BuildTarget/{target}";
|
||||||
|
var priority = p++;
|
||||||
|
|
||||||
|
var template = $@"
|
||||||
|
[MenuItem(""{path}"", validate = true, priority = {priority})]
|
||||||
|
static bool ValidateBuildTarget{target}()
|
||||||
|
{{
|
||||||
|
Menu.SetChecked(""{path}"", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.{target});
|
||||||
|
return true;
|
||||||
|
}}
|
||||||
|
|
||||||
|
[MenuItem(""{path}"", validate = false, priority = {priority})]
|
||||||
|
static void BuildTarget{target}()
|
||||||
|
{{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.{target};
|
||||||
|
SaveSettings(settings);
|
||||||
|
}}";
|
||||||
|
|
||||||
|
sb.AppendLine(template);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.ToString().Dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BuildTarget
|
||||||
|
{
|
||||||
|
StandaloneWindows,
|
||||||
|
StandaloneWindows64,
|
||||||
|
StandaloneLinux,
|
||||||
|
StandaloneLinux64,
|
||||||
|
StandaloneOSX,
|
||||||
|
WebGL,
|
||||||
|
iOS,
|
||||||
|
Android,
|
||||||
|
WSAPlayer,
|
||||||
|
PS4,
|
||||||
|
XboxOne,
|
||||||
|
Switch,
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/StandaloneWindows", validate = true, priority = 1)]
|
||||||
|
static bool ValidateBuildTargetStandaloneWindows()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/StandaloneWindows", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneWindows);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/StandaloneWindows", validate = false, priority = 1)]
|
||||||
|
static void BuildTargetStandaloneWindows()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.StandaloneWindows;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/StandaloneWindows64", validate = true, priority = 2)]
|
||||||
|
static bool ValidateBuildTargetStandaloneWindows64()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/StandaloneWindows64", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneWindows64);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/StandaloneWindows64", validate = false, priority = 2)]
|
||||||
|
static void BuildTargetStandaloneWindows64()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.StandaloneWindows64;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux", validate = true, priority = 3)]
|
||||||
|
static bool ValidateBuildTargetStandaloneLinux()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/StandaloneLinux", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneLinux);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux", validate = false, priority = 3)]
|
||||||
|
static void BuildTargetStandaloneLinux()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.StandaloneLinux;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux64", validate = true, priority = 4)]
|
||||||
|
static bool ValidateBuildTargetStandaloneLinux64()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/StandaloneLinux64", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneLinux64);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux64", validate = false, priority = 4)]
|
||||||
|
static void BuildTargetStandaloneLinux64()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.StandaloneLinux64;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/StandaloneOSX", validate = true, priority = 5)]
|
||||||
|
static bool ValidateBuildTargetStandaloneOSX()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/StandaloneOSX", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneOSX);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/StandaloneOSX", validate = false, priority = 5)]
|
||||||
|
static void BuildTargetStandaloneOSX()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.StandaloneOSX;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/WebGL", validate = true, priority = 6)]
|
||||||
|
static bool ValidateBuildTargetWebGL()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/WebGL", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.WebGL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/WebGL", validate = false, priority = 6)]
|
||||||
|
static void BuildTargetWebGL()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.WebGL;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/iOS", validate = true, priority = 7)]
|
||||||
|
static bool ValidateBuildTargetiOS()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/iOS", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.iOS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/iOS", validate = false, priority = 7)]
|
||||||
|
static void BuildTargetiOS()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.iOS;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/Android", validate = true, priority = 8)]
|
||||||
|
static bool ValidateBuildTargetAndroid()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/Android", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.Android);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/Android", validate = false, priority = 8)]
|
||||||
|
static void BuildTargetAndroid()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.Android;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/WSAPlayer", validate = true, priority = 9)]
|
||||||
|
static bool ValidateBuildTargetWSAPlayer()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/WSAPlayer", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.WSAPlayer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/WSAPlayer", validate = false, priority = 9)]
|
||||||
|
static void BuildTargetWSAPlayer()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.WSAPlayer;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/PS4", validate = true, priority = 10)]
|
||||||
|
static bool ValidateBuildTargetPS4()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/PS4", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.PS4);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/PS4", validate = false, priority = 10)]
|
||||||
|
static void BuildTargetPS4()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.PS4;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/XboxOne", validate = true, priority = 11)]
|
||||||
|
static bool ValidateBuildTargetXboxOne()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/XboxOne", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.XboxOne);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/XboxOne", validate = false, priority = 11)]
|
||||||
|
static void BuildTargetXboxOne()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.XboxOne;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/Switch", validate = true, priority = 12)]
|
||||||
|
static bool ValidateBuildTargetSwitch()
|
||||||
|
{
|
||||||
|
Menu.SetChecked("Test/Settings/BuildTarget/Switch", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.Switch);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Test/Settings/BuildTarget/Switch", validate = false, priority = 12)]
|
||||||
|
static void BuildTargetSwitch()
|
||||||
|
{
|
||||||
|
var settings = LoadOrGetDefaultSettings();
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = BuildTarget.Switch;
|
||||||
|
SaveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 12bdad0556e999f4aa82da29415d361f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,515 @@
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
|
||||||
|
using RuntimeUnitTestToolkit;
|
||||||
|
using RuntimeUnitTestToolkit.Editor;
|
||||||
|
using System;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Build.Reporting;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
internal class RuntimeUnitTestSettings
|
||||||
|
{
|
||||||
|
public ScriptingImplementation ScriptBackend;
|
||||||
|
public bool UseCurrentScriptBackend;
|
||||||
|
public BuildTarget BuildTarget;
|
||||||
|
public bool UseCurrentBuildTarget;
|
||||||
|
|
||||||
|
public bool Headless;
|
||||||
|
public bool AutoRunPlayer;
|
||||||
|
public bool DisableAutoClose;
|
||||||
|
|
||||||
|
public RuntimeUnitTestSettings()
|
||||||
|
{
|
||||||
|
UseCurrentBuildTarget = true;
|
||||||
|
UseCurrentScriptBackend = true;
|
||||||
|
Headless = false;
|
||||||
|
AutoRunPlayer = true;
|
||||||
|
DisableAutoClose = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{ScriptBackend} {BuildTarget} Headless:{Headless} AutoRunPlayer:{AutoRunPlayer} DisableAutoClose:{DisableAutoClose}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no namespace(because invoke from commandline)
|
||||||
|
public static partial class UnitTestBuilder
|
||||||
|
{
|
||||||
|
const string SettingsKeyBase = "RuntimeUnitTest.Settings.";
|
||||||
|
|
||||||
|
[MenuItem("Test/BuildUnitTest")]
|
||||||
|
public static void BuildUnitTest()
|
||||||
|
{
|
||||||
|
var settings = new RuntimeUnitTestSettings(); // default
|
||||||
|
|
||||||
|
string buildPath = null;
|
||||||
|
|
||||||
|
if (Application.isBatchMode) // from commandline
|
||||||
|
{
|
||||||
|
settings.AutoRunPlayer = false;
|
||||||
|
settings.DisableAutoClose = false;
|
||||||
|
|
||||||
|
var cmdArgs = Environment.GetCommandLineArgs();
|
||||||
|
for (int i = 0; i < cmdArgs.Length; i++)
|
||||||
|
{
|
||||||
|
if (string.Equals(cmdArgs[i].Trim('-', '/'), "ScriptBackend", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
settings.UseCurrentScriptBackend = false;
|
||||||
|
var str = cmdArgs[++i];
|
||||||
|
if (str.StartsWith("mono", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
settings.ScriptBackend = ScriptingImplementation.Mono2x;
|
||||||
|
}
|
||||||
|
else if (str.StartsWith("IL2CPP", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
settings.ScriptBackend = ScriptingImplementation.IL2CPP;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
settings.ScriptBackend = (ScriptingImplementation)Enum.Parse(typeof(ScriptingImplementation), str, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (string.Equals(cmdArgs[i].Trim('-', '/'), "BuildTarget", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
settings.UseCurrentBuildTarget = false;
|
||||||
|
settings.BuildTarget = (BuildTarget)Enum.Parse(typeof(BuildTarget), cmdArgs[++i], true);
|
||||||
|
}
|
||||||
|
else if (string.Equals(cmdArgs[i].Trim('-', '/'), "Headless", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
settings.Headless = true;
|
||||||
|
}
|
||||||
|
else if (string.Equals(cmdArgs[i].Trim('-', '/'), "buildPath", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
buildPath = cmdArgs[++i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var key = SettingsKeyBase + Application.productName;
|
||||||
|
var settingsValue = EditorPrefs.GetString(key, null);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(settingsValue))
|
||||||
|
{
|
||||||
|
settings = JsonUtility.FromJson<RuntimeUnitTestSettings>(settingsValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogError("Fail to load RuntimeUnitTest settings");
|
||||||
|
EditorPrefs.SetString(key, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.UseCurrentBuildTarget)
|
||||||
|
{
|
||||||
|
settings.BuildTarget = EditorUserBuildSettings.activeBuildTarget;
|
||||||
|
}
|
||||||
|
if (settings.UseCurrentScriptBackend)
|
||||||
|
{
|
||||||
|
settings.ScriptBackend = PlayerSettings.GetScriptingBackend(ToBuildTargetGroup(settings.BuildTarget));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildPath == null)
|
||||||
|
{
|
||||||
|
buildPath = $"bin/UnitTest/{settings.BuildTarget}_{settings.ScriptBackend}/test" + (IsWindows(settings.BuildTarget) ? ".exe" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalScene = SceneManager.GetActiveScene().path;
|
||||||
|
|
||||||
|
BuildUnitTest(buildPath, settings.ScriptBackend, settings.BuildTarget, settings.Headless, settings.AutoRunPlayer, settings.DisableAutoClose);
|
||||||
|
|
||||||
|
// reopen original scene
|
||||||
|
if (!string.IsNullOrWhiteSpace(originalScene))
|
||||||
|
{
|
||||||
|
EditorSceneManager.OpenScene(originalScene, OpenSceneMode.Single);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static RuntimeUnitTestSettings LoadOrGetDefaultSettings()
|
||||||
|
{
|
||||||
|
var key = SettingsKeyBase + Application.productName;
|
||||||
|
|
||||||
|
var settingsValue = EditorPrefs.GetString(key, null);
|
||||||
|
RuntimeUnitTestSettings settings = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(settingsValue))
|
||||||
|
{
|
||||||
|
settings = JsonUtility.FromJson<RuntimeUnitTestSettings>(settingsValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogError("Fail to load RuntimeUnitTest settings");
|
||||||
|
EditorPrefs.SetString(key, null);
|
||||||
|
settings = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings == null)
|
||||||
|
{
|
||||||
|
// default
|
||||||
|
settings = new RuntimeUnitTestSettings
|
||||||
|
{
|
||||||
|
UseCurrentBuildTarget = true,
|
||||||
|
UseCurrentScriptBackend = true,
|
||||||
|
Headless = false,
|
||||||
|
AutoRunPlayer = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SaveSettings(RuntimeUnitTestSettings settings)
|
||||||
|
{
|
||||||
|
var key = SettingsKeyBase + Application.productName;
|
||||||
|
EditorPrefs.SetString(key, JsonUtility.ToJson(settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void BuildUnitTest(string buildPath, ScriptingImplementation scriptBackend, BuildTarget buildTarget, bool headless, bool autoRunPlayer, bool disableAutoClose)
|
||||||
|
{
|
||||||
|
var sceneName = "Assets/TempRuntimeUnitTestScene_" + DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
|
if (disableAutoClose)
|
||||||
|
{
|
||||||
|
sceneName += "_DisableAutoClose";
|
||||||
|
}
|
||||||
|
sceneName += ".unity";
|
||||||
|
|
||||||
|
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||||
|
|
||||||
|
BuildUnitTestRunnerScene();
|
||||||
|
|
||||||
|
EditorSceneManager.MarkSceneDirty(scene);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
EditorSceneManager.SaveScene(scene, sceneName, false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Build(sceneName, buildPath, new RuntimeUnitTestSettings { ScriptBackend = scriptBackend, BuildTarget = buildTarget, Headless = headless, AutoRunPlayer = autoRunPlayer, DisableAutoClose = disableAutoClose });
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
AssetDatabase.DeleteAsset(sceneName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UnitTestRunner BuildUnitTestRunnerScene()
|
||||||
|
{
|
||||||
|
const string kStandardSpritePath = "UI/Skin/UISprite.psd";
|
||||||
|
const string kBackgroundSpritePath = "UI/Skin/Background.psd";
|
||||||
|
var uisprite = AssetDatabase.GetBuiltinExtraResource<Sprite>(kStandardSpritePath);
|
||||||
|
var background = AssetDatabase.GetBuiltinExtraResource<Sprite>(kBackgroundSpritePath);
|
||||||
|
|
||||||
|
ScrollRect buttonList;
|
||||||
|
VerticalLayoutGroup listLayout;
|
||||||
|
Scrollbar refListScrollbar;
|
||||||
|
ScrollRect logList;
|
||||||
|
Scrollbar refLogScrollbar;
|
||||||
|
Button clearButton;
|
||||||
|
Text logText;
|
||||||
|
|
||||||
|
// Flutter like coded build utility
|
||||||
|
|
||||||
|
var rootObject = new Builder<Camera>("SceneRoot")
|
||||||
|
{
|
||||||
|
Children = new IBuilder[] {
|
||||||
|
new Builder<EventSystem, StandaloneInputModule>("EventSystem"),
|
||||||
|
new Builder<Canvas, CanvasScaler, GraphicRaycaster>("Canvas") {
|
||||||
|
Component1 = { renderMode = RenderMode.ScreenSpaceOverlay },
|
||||||
|
Children = new IBuilder[] {
|
||||||
|
new Builder<HorizontalLayoutGroup, CanvasRenderer>("HorizontalSplitter") {
|
||||||
|
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1) },
|
||||||
|
Component1 = { childControlWidth = true, childControlHeight = true, spacing = 10 },
|
||||||
|
Children = new IBuilder[] {
|
||||||
|
new Builder<ScrollRect, CanvasRenderer>("ButtonList", out buttonList) {
|
||||||
|
RectTransform = { pivot = new Vector2(0.5f, 0.5f) },
|
||||||
|
Component1 = { horizontal =false, vertical = true, movementType = ScrollRect.MovementType.Clamped },
|
||||||
|
Children = new IBuilder[] {
|
||||||
|
new Builder<VerticalLayoutGroup, ContentSizeFitter>("ListLayoutToAttach", out listLayout) {
|
||||||
|
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1), pivot = new Vector2(0, 1) },
|
||||||
|
Component1 = { childControlWidth = true, childControlHeight = true, childForceExpandWidth = true, childForceExpandHeight = false, spacing = 10, padding = new RectOffset(10,20,10,10) },
|
||||||
|
Component2 = { horizontalFit = ContentSizeFitter.FitMode.Unconstrained, verticalFit = ContentSizeFitter.FitMode.PreferredSize },
|
||||||
|
SetTarget = self => { buttonList.content = self.GetComponent<RectTransform>(); },
|
||||||
|
Child = new Builder<Button, Image, LayoutElement>("ClearButton", out clearButton) {
|
||||||
|
Component2 = { sprite = uisprite, type = Image.Type.Sliced },
|
||||||
|
Component3 = { minHeight = 50 },
|
||||||
|
SetTarget = self => { self.GetComponent<Button>().targetGraphic = self.GetComponent<Graphic>(); },
|
||||||
|
Child = new Builder<Text>("ButtonText") {
|
||||||
|
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1), pivot = new Vector2(0.5f, 0.5f) },
|
||||||
|
Component1 = { text = "Clear", color = FromRGB(50, 50, 50), alignment = TextAnchor.MiddleCenter, fontSize = 24, lineSpacing = 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Builder<Scrollbar,Image>("ListScrollbar", out refListScrollbar) {
|
||||||
|
RectTransform = { anchorMin = new Vector2(1, 0), anchorMax = new Vector2(1, 1) },
|
||||||
|
Component1 = { navigation = new Navigation{ mode = Navigation.Mode.None }, direction = Scrollbar.Direction.BottomToTop, size = 1.0f },
|
||||||
|
Component2 = { sprite = background, type = Image.Type.Sliced },
|
||||||
|
SetTarget = self => { buttonList.verticalScrollbar = self.GetComponent<Scrollbar>(); },
|
||||||
|
Child = new Builder<RectTransform>("Sliding Area") {
|
||||||
|
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1) },
|
||||||
|
Child = new Builder<Image>("Handle") {
|
||||||
|
Component1 = { sprite = uisprite, type = Image.Type.Sliced },
|
||||||
|
SetTarget = self =>
|
||||||
|
{
|
||||||
|
refListScrollbar.targetGraphic = self.GetComponent<Graphic>();
|
||||||
|
refListScrollbar.handleRect = self.GetComponent<RectTransform>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Builder<ScrollRect, CanvasRenderer>("ScrollableText", out logList) {
|
||||||
|
RectTransform = { pivot = new Vector2(0.5f, 0.5f) },
|
||||||
|
Component1 = { horizontal =false, vertical = true, movementType = ScrollRect.MovementType.Elastic, elasticity = 0.1f },
|
||||||
|
Children = new IBuilder[] {
|
||||||
|
new Builder<Text, ContentSizeFitter>("Log", out logText) {
|
||||||
|
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1), pivot = new Vector2(0, 1) },
|
||||||
|
Component1 = { fontSize = 24, lineSpacing = 1, supportRichText = true, alignment = TextAnchor.UpperLeft, horizontalOverflow = HorizontalWrapMode.Wrap, verticalOverflow = VerticalWrapMode.Truncate },
|
||||||
|
Component2 = { horizontalFit = ContentSizeFitter.FitMode.Unconstrained, verticalFit = ContentSizeFitter.FitMode.PreferredSize },
|
||||||
|
SetTarget = self => { logList.content = self.GetComponent<RectTransform>(); }
|
||||||
|
},
|
||||||
|
new Builder<Scrollbar,Image>("LogScrollbar", out refLogScrollbar) {
|
||||||
|
RectTransform = { anchorMin = new Vector2(1, 0), anchorMax = new Vector2(1, 1) },
|
||||||
|
Component1 = { navigation = new Navigation{ mode = Navigation.Mode.None }, direction = Scrollbar.Direction.BottomToTop, size = 1.0f },
|
||||||
|
Component2 = { sprite = background, type = Image.Type.Sliced },
|
||||||
|
SetTarget = self => { logList.verticalScrollbar = self.GetComponent<Scrollbar>(); },
|
||||||
|
Child = new Builder<RectTransform>("Sliding Area2") {
|
||||||
|
RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1) },
|
||||||
|
Child = new Builder<Image>("Handle2") {
|
||||||
|
Component1 = { sprite = uisprite, type = Image.Type.Sliced },
|
||||||
|
SetTarget = self =>
|
||||||
|
{
|
||||||
|
refLogScrollbar.targetGraphic = self.GetComponent<Graphic>();
|
||||||
|
refLogScrollbar.handleRect = self.GetComponent<RectTransform>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// size modify after build complete:)
|
||||||
|
{
|
||||||
|
var rect = GameObject.Find("HorizontalSplitter").GetComponent<RectTransform>();
|
||||||
|
rect.offsetMin = new Vector2(0, 0);
|
||||||
|
rect.offsetMax = new Vector2(0, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var rect = GameObject.Find("ListLayoutToAttach").GetComponent<RectTransform>();
|
||||||
|
rect.offsetMin = new Vector2(0, 0);
|
||||||
|
rect.offsetMax = new Vector2(0, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var rect = GameObject.Find("ListScrollbar").GetComponent<RectTransform>();
|
||||||
|
rect.offsetMin = new Vector2(0, 0);
|
||||||
|
rect.offsetMax = new Vector2(0, 0);
|
||||||
|
rect.sizeDelta = new Vector2(30, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var rect = GameObject.Find("ClearButton").GetComponent<RectTransform>();
|
||||||
|
rect.offsetMin = new Vector2(0, 0);
|
||||||
|
rect.offsetMax = new Vector2(0, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var rect = GameObject.Find("Sliding Area").GetComponent<RectTransform>();
|
||||||
|
rect.offsetMin = new Vector2(0, 0);
|
||||||
|
rect.offsetMax = new Vector2(0, 0);
|
||||||
|
rect.sizeDelta = new Vector2(-20, -20);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var rect = GameObject.Find("Handle").GetComponent<RectTransform>();
|
||||||
|
rect.offsetMin = new Vector2(0, 0);
|
||||||
|
rect.offsetMax = new Vector2(0, 0);
|
||||||
|
rect.sizeDelta = new Vector2(20, 20);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var rect = GameObject.Find("ButtonText").GetComponent<RectTransform>();
|
||||||
|
rect.offsetMin = new Vector2(0, 0);
|
||||||
|
rect.offsetMax = new Vector2(0, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var rect = GameObject.Find("Log").GetComponent<RectTransform>();
|
||||||
|
rect.offsetMin = new Vector2(15, 0);
|
||||||
|
rect.offsetMax = new Vector2(-20, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var rect = GameObject.Find("LogScrollbar").GetComponent<RectTransform>();
|
||||||
|
rect.offsetMin = new Vector2(-30, 0);
|
||||||
|
rect.offsetMax = new Vector2(0, 0);
|
||||||
|
rect.sizeDelta = new Vector2(30, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var rect = GameObject.Find("Sliding Area2").GetComponent<RectTransform>();
|
||||||
|
rect.offsetMin = new Vector2(0, 0);
|
||||||
|
rect.offsetMax = new Vector2(0, 0);
|
||||||
|
rect.sizeDelta = new Vector2(-20, -20);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var rect = GameObject.Find("Handle2").GetComponent<RectTransform>();
|
||||||
|
rect.offsetMin = new Vector2(0, 0);
|
||||||
|
rect.offsetMax = new Vector2(0, 0);
|
||||||
|
rect.sizeDelta = new Vector2(20, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add test script
|
||||||
|
var runner = rootObject.GameObject.AddComponent<UnitTestRunner>();
|
||||||
|
runner.clearButton = clearButton;
|
||||||
|
runner.list = listLayout.gameObject.GetComponent<RectTransform>();
|
||||||
|
runner.listScrollBar = refListScrollbar;
|
||||||
|
runner.logText = logText;
|
||||||
|
runner.logScrollBar = refLogScrollbar;
|
||||||
|
|
||||||
|
return runner;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Build(string sceneName, string buildPath, RuntimeUnitTestSettings settings)
|
||||||
|
{
|
||||||
|
var options = BuildOptions.BuildScriptsOnly | BuildOptions.IncludeTestAssemblies;
|
||||||
|
if (settings.AutoRunPlayer)
|
||||||
|
{
|
||||||
|
options |= BuildOptions.AutoRunPlayer;
|
||||||
|
}
|
||||||
|
if (settings.Headless)
|
||||||
|
{
|
||||||
|
options |= BuildOptions.EnableHeadlessMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetGroup = ToBuildTargetGroup(settings.BuildTarget);
|
||||||
|
var currentBackend = PlayerSettings.GetScriptingBackend(targetGroup);
|
||||||
|
if (currentBackend != settings.ScriptBackend)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.Log("Modify ScriptBackend to " + settings.ScriptBackend);
|
||||||
|
PlayerSettings.SetScriptingBackend(targetGroup, settings.ScriptBackend);
|
||||||
|
}
|
||||||
|
|
||||||
|
var buildOptions = new BuildPlayerOptions
|
||||||
|
{
|
||||||
|
target = settings.BuildTarget,
|
||||||
|
targetGroup = targetGroup,
|
||||||
|
options = options,
|
||||||
|
scenes = new[] { sceneName },
|
||||||
|
locationPathName = buildPath
|
||||||
|
};
|
||||||
|
|
||||||
|
UnityEngine.Debug.Log("UnitTest Build Start, " + settings.ToString());
|
||||||
|
|
||||||
|
var buildReport = BuildPipeline.BuildPlayer(buildOptions);
|
||||||
|
|
||||||
|
if (currentBackend != settings.ScriptBackend)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.Log("Restore ScriptBackend to " + currentBackend);
|
||||||
|
PlayerSettings.SetScriptingBackend(targetGroup, currentBackend);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildReport.summary.result != BuildResult.Succeeded)
|
||||||
|
{
|
||||||
|
// Note: show error summary?
|
||||||
|
// Debug.LogError(buildReport.SummarizeErrors());
|
||||||
|
UnityEngine.Debug.LogError("UnitTest Build Failed.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.Log("UnitTest Build Completed, binary located: " + buildOptions.locationPathName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Color FromRGB(int r, int g, int b)
|
||||||
|
{
|
||||||
|
return new Color(r / 255f, g / 255f, b / 255f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsWindows(BuildTarget buildTarget)
|
||||||
|
{
|
||||||
|
switch (buildTarget)
|
||||||
|
{
|
||||||
|
case BuildTarget.StandaloneWindows:
|
||||||
|
case BuildTarget.StandaloneWindows64:
|
||||||
|
case BuildTarget.WSAPlayer:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuildTargetGroup ToBuildTargetGroup(BuildTarget buildTarget)
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
switch (buildTarget)
|
||||||
|
{
|
||||||
|
case BuildTarget.StandaloneOSX:
|
||||||
|
case (BuildTarget)3:
|
||||||
|
case BuildTarget.StandaloneOSXIntel:
|
||||||
|
case BuildTarget.StandaloneOSXIntel64:
|
||||||
|
case BuildTarget.StandaloneWindows:
|
||||||
|
case BuildTarget.StandaloneWindows64:
|
||||||
|
case BuildTarget.StandaloneLinux:
|
||||||
|
case BuildTarget.StandaloneLinux64:
|
||||||
|
case BuildTarget.StandaloneLinuxUniversal:
|
||||||
|
return BuildTargetGroup.Standalone;
|
||||||
|
case (BuildTarget)6:
|
||||||
|
case (BuildTarget)7:
|
||||||
|
case BuildTarget.WebGL:
|
||||||
|
return BuildTargetGroup.WebGL;
|
||||||
|
case BuildTarget.iOS:
|
||||||
|
return BuildTargetGroup.iOS;
|
||||||
|
case BuildTarget.PS3:
|
||||||
|
return BuildTargetGroup.PS3;
|
||||||
|
case BuildTarget.PS4:
|
||||||
|
return BuildTargetGroup.PS4;
|
||||||
|
case BuildTarget.XBOX360:
|
||||||
|
return BuildTargetGroup.XBOX360;
|
||||||
|
case BuildTarget.Android:
|
||||||
|
return BuildTargetGroup.Android;
|
||||||
|
case BuildTarget.WSAPlayer:
|
||||||
|
return BuildTargetGroup.WSA;
|
||||||
|
case BuildTarget.WP8Player:
|
||||||
|
return BuildTargetGroup.WP8;
|
||||||
|
case BuildTarget.Tizen:
|
||||||
|
return BuildTargetGroup.Tizen;
|
||||||
|
case BuildTarget.PSP2:
|
||||||
|
return BuildTargetGroup.PSP2;
|
||||||
|
case BuildTarget.PSM:
|
||||||
|
return BuildTargetGroup.PSM;
|
||||||
|
case BuildTarget.XboxOne:
|
||||||
|
return BuildTargetGroup.XboxOne;
|
||||||
|
case BuildTarget.SamsungTV:
|
||||||
|
return BuildTargetGroup.SamsungTV;
|
||||||
|
case BuildTarget.N3DS:
|
||||||
|
return BuildTargetGroup.N3DS;
|
||||||
|
case BuildTarget.WiiU:
|
||||||
|
return BuildTargetGroup.WiiU;
|
||||||
|
case BuildTarget.tvOS:
|
||||||
|
return BuildTargetGroup.tvOS;
|
||||||
|
case BuildTarget.Switch:
|
||||||
|
return BuildTargetGroup.Switch;
|
||||||
|
case BuildTarget.Lumin:
|
||||||
|
return BuildTargetGroup.Lumin;
|
||||||
|
case BuildTarget.BlackBerry:
|
||||||
|
return BuildTargetGroup.BlackBerry;
|
||||||
|
case BuildTarget.NoTarget:
|
||||||
|
default:
|
||||||
|
return BuildTargetGroup.Unknown;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3518da33b6245d341a0ef3670ee9268b
|
||||||
|
timeCreated: 1488689723
|
||||||
|
licenseType: Pro
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "RuntimeUnitTestToolkit",
|
||||||
|
"references": [
|
||||||
|
],
|
||||||
|
"optionalUnityReferences": [
|
||||||
|
"TestAssemblies"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": []
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 14c4fea4b238088479114ba2ffe195f9
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,450 @@
|
||||||
|
using NUnit.Framework;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Events;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace RuntimeUnitTestToolkit
|
||||||
|
{
|
||||||
|
public class UnitTestRunner : MonoBehaviour
|
||||||
|
{
|
||||||
|
// object is IEnumerator or Func<IEnumerator>
|
||||||
|
Dictionary<string, List<KeyValuePair<string, object>>> tests = new Dictionary<string, List<KeyValuePair<string, object>>>();
|
||||||
|
|
||||||
|
List<Pair> additionalActionsOnFirst = new List<Pair>();
|
||||||
|
|
||||||
|
public Button clearButton;
|
||||||
|
public RectTransform list;
|
||||||
|
public Scrollbar listScrollBar;
|
||||||
|
|
||||||
|
public Text logText;
|
||||||
|
public Scrollbar logScrollBar;
|
||||||
|
|
||||||
|
readonly Color passColor = new Color(0f, 1f, 0f, 1f); // green
|
||||||
|
readonly Color failColor = new Color(1f, 0f, 0f, 1f); // red
|
||||||
|
readonly Color normalColor = new Color(1f, 1f, 1f, 1f); // white
|
||||||
|
|
||||||
|
bool allTestGreen = true;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UnityEngine.Application.logMessageReceived += (a, b, c) =>
|
||||||
|
{
|
||||||
|
logText.text += "[" + c + "]" + a + "\n";
|
||||||
|
};
|
||||||
|
|
||||||
|
// register all test types
|
||||||
|
foreach (var item in GetTestTargetTypes())
|
||||||
|
{
|
||||||
|
RegisterAllMethods(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
var executeAll = new List<Func<Coroutine>>();
|
||||||
|
foreach (var ___item in tests)
|
||||||
|
{
|
||||||
|
var actionList = ___item; // be careful, capture in lambda
|
||||||
|
|
||||||
|
executeAll.Add(() => StartCoroutine(RunTestInCoroutine(actionList)));
|
||||||
|
Add(actionList.Key, () => StartCoroutine(RunTestInCoroutine(actionList)));
|
||||||
|
}
|
||||||
|
|
||||||
|
var executeAllButton = Add("Run All Tests", () => StartCoroutine(ExecuteAllInCoroutine(executeAll)));
|
||||||
|
|
||||||
|
clearButton.gameObject.GetComponent<Image>().color = new Color(170 / 255f, 170 / 255f, 170 / 255f, 1);
|
||||||
|
executeAllButton.gameObject.GetComponent<Image>().color = new Color(250 / 255f, 150 / 255f, 150 / 255f, 1);
|
||||||
|
executeAllButton.transform.SetSiblingIndex(1);
|
||||||
|
|
||||||
|
additionalActionsOnFirst.Reverse();
|
||||||
|
foreach (var item in additionalActionsOnFirst)
|
||||||
|
{
|
||||||
|
var newButton = GameObject.Instantiate(clearButton);
|
||||||
|
newButton.name = item.Name;
|
||||||
|
newButton.onClick.RemoveAllListeners();
|
||||||
|
newButton.GetComponentInChildren<Text>().text = item.Name;
|
||||||
|
newButton.onClick.AddListener(item.Action);
|
||||||
|
newButton.transform.SetParent(list);
|
||||||
|
newButton.transform.SetSiblingIndex(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearButton.onClick.AddListener(() =>
|
||||||
|
{
|
||||||
|
logText.text = "";
|
||||||
|
foreach (var btn in list.GetComponentsInChildren<Button>())
|
||||||
|
{
|
||||||
|
btn.interactable = true;
|
||||||
|
btn.GetComponent<Image>().color = normalColor;
|
||||||
|
}
|
||||||
|
executeAllButton.gameObject.GetComponent<Image>().color = new Color(250 / 255f, 150 / 255f, 150 / 255f, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
listScrollBar.value = 1;
|
||||||
|
logScrollBar.value = 1;
|
||||||
|
|
||||||
|
if (Application.isBatchMode)
|
||||||
|
{
|
||||||
|
// run immediately in player
|
||||||
|
StartCoroutine(ExecuteAllInCoroutine(executeAll));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (Application.isBatchMode)
|
||||||
|
{
|
||||||
|
// when failed(can not start runner), quit immediately.
|
||||||
|
WriteToConsole(ex.ToString());
|
||||||
|
Application.Quit(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button Add(string title, UnityAction test)
|
||||||
|
{
|
||||||
|
var newButton = GameObject.Instantiate(clearButton);
|
||||||
|
newButton.name = title;
|
||||||
|
newButton.onClick.RemoveAllListeners();
|
||||||
|
newButton.GetComponentInChildren<Text>().text = title;
|
||||||
|
newButton.onClick.AddListener(test);
|
||||||
|
|
||||||
|
newButton.transform.SetParent(list);
|
||||||
|
return newButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IEnumerable<Type> GetTestTargetTypes()
|
||||||
|
{
|
||||||
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
|
{
|
||||||
|
var n = assembly.FullName;
|
||||||
|
if (n.StartsWith("UnityEngine")) continue;
|
||||||
|
if (n.StartsWith("mscorlib")) continue;
|
||||||
|
if (n.StartsWith("System")) continue;
|
||||||
|
|
||||||
|
foreach (var item in assembly.GetTypes())
|
||||||
|
{
|
||||||
|
foreach (var method in item.GetMethods())
|
||||||
|
{
|
||||||
|
var t1 = method.GetCustomAttribute<TestAttribute>(true);
|
||||||
|
if (t1 != null)
|
||||||
|
{
|
||||||
|
yield return item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var t2 = method.GetCustomAttribute<UnityTestAttribute>(true);
|
||||||
|
if (t2 != null)
|
||||||
|
{
|
||||||
|
yield return item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddTest(string group, string title, Action test)
|
||||||
|
{
|
||||||
|
List<KeyValuePair<string, object>> list;
|
||||||
|
if (!tests.TryGetValue(group, out list))
|
||||||
|
{
|
||||||
|
list = new List<KeyValuePair<string, object>>();
|
||||||
|
tests[group] = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(new KeyValuePair<string, object>(title, test));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddAsyncTest(string group, string title, Func<IEnumerator> asyncTestCoroutine)
|
||||||
|
{
|
||||||
|
List<KeyValuePair<string, object>> list;
|
||||||
|
if (!tests.TryGetValue(group, out list))
|
||||||
|
{
|
||||||
|
list = new List<KeyValuePair<string, object>>();
|
||||||
|
tests[group] = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(new KeyValuePair<string, object>(title, asyncTestCoroutine));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddCutomAction(string name, UnityAction action)
|
||||||
|
{
|
||||||
|
additionalActionsOnFirst.Add(new Pair { Name = name, Action = action });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void RegisterAllMethods<T>()
|
||||||
|
where T : new()
|
||||||
|
{
|
||||||
|
RegisterAllMethods(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterAllMethods(Type testType)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var test = Activator.CreateInstance(testType);
|
||||||
|
|
||||||
|
var methods = testType.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
|
||||||
|
foreach (var item in methods)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var iteratorTest = item.GetCustomAttribute<UnityEngine.TestTools.UnityTestAttribute>(true);
|
||||||
|
if (iteratorTest != null)
|
||||||
|
{
|
||||||
|
if (item.GetParameters().Length == 0 && item.ReturnType == typeof(IEnumerator))
|
||||||
|
{
|
||||||
|
var factory = (Func<IEnumerator>)Delegate.CreateDelegate(typeof(Func<IEnumerator>), test, item);
|
||||||
|
AddAsyncTest(factory.Target.GetType().Name, factory.Method.Name, factory);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter or return type is invalid).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var standardTest = item.GetCustomAttribute<NUnit.Framework.TestAttribute>(true);
|
||||||
|
if (standardTest != null)
|
||||||
|
{
|
||||||
|
if (item.GetParameters().Length == 0 && item.ReturnType == typeof(void))
|
||||||
|
{
|
||||||
|
var invoke = (Action)Delegate.CreateDelegate(typeof(Action), test, item);
|
||||||
|
AddTest(invoke.Target.GetType().Name, invoke.Method.Name, invoke);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter or return type is invalid).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogError(testType.Name + "." + item.Name + " failed to register method, exception: " + e.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Collections.IEnumerator ScrollLogToEndNextFrame()
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
yield return null;
|
||||||
|
logScrollBar.value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator RunTestInCoroutine(KeyValuePair<string, List<KeyValuePair<string, object>>> actionList)
|
||||||
|
{
|
||||||
|
Button self = null;
|
||||||
|
foreach (var btn in list.GetComponentsInChildren<Button>())
|
||||||
|
{
|
||||||
|
btn.interactable = false;
|
||||||
|
if (btn.name == actionList.Key) self = btn;
|
||||||
|
}
|
||||||
|
if (self != null)
|
||||||
|
{
|
||||||
|
self.GetComponent<Image>().color = normalColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
var allGreen = true;
|
||||||
|
|
||||||
|
logText.text += "<color=yellow>" + actionList.Key + "</color>\n";
|
||||||
|
WriteToConsole("Begin Test Class: " + actionList.Key);
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
var totalExecutionTime = new List<double>();
|
||||||
|
foreach (var item2 in actionList.Value)
|
||||||
|
{
|
||||||
|
// before start, cleanup
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
GC.Collect();
|
||||||
|
|
||||||
|
logText.text += "<color=teal>" + item2.Key + "</color>\n";
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
var v = item2.Value;
|
||||||
|
|
||||||
|
var methodStopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
Exception exception = null;
|
||||||
|
if (v is Action)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
((Action)v).Invoke();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var coroutineFactory = (Func<IEnumerator>)v;
|
||||||
|
IEnumerator coroutine = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
coroutine = coroutineFactory();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
}
|
||||||
|
if (exception == null)
|
||||||
|
{
|
||||||
|
yield return StartCoroutine(UnwrapEnumerator(coroutine, ex =>
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
methodStopwatch.Stop();
|
||||||
|
totalExecutionTime.Add(methodStopwatch.Elapsed.TotalMilliseconds);
|
||||||
|
if (exception == null)
|
||||||
|
{
|
||||||
|
logText.text += "OK, " + methodStopwatch.Elapsed.TotalMilliseconds.ToString("0.00") + "ms\n";
|
||||||
|
WriteToConsoleResult(item2.Key + ", " + methodStopwatch.Elapsed.TotalMilliseconds.ToString("0.00") + "ms", true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// found match line...
|
||||||
|
var line = string.Join("\n", exception.StackTrace.Split('\n').Where(x => x.Contains(actionList.Key) || x.Contains(item2.Key)).ToArray());
|
||||||
|
logText.text += "<color=red>" + exception.Message + "\n" + line + "</color>\n";
|
||||||
|
WriteToConsoleResult(item2.Key + ", " + exception.Message, false);
|
||||||
|
WriteToConsole(line);
|
||||||
|
allGreen = false;
|
||||||
|
allTestGreen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logText.text += "[" + actionList.Key + "]" + totalExecutionTime.Sum().ToString("0.00") + "ms\n\n";
|
||||||
|
foreach (var btn in list.GetComponentsInChildren<Button>()) btn.interactable = true;
|
||||||
|
if (self != null)
|
||||||
|
{
|
||||||
|
self.GetComponent<Image>().color = allGreen ? passColor : failColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return StartCoroutine(ScrollLogToEndNextFrame());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator ExecuteAllInCoroutine(List<Func<Coroutine>> tests)
|
||||||
|
{
|
||||||
|
allTestGreen = true;
|
||||||
|
|
||||||
|
foreach (var item in tests)
|
||||||
|
{
|
||||||
|
yield return item();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Application.isBatchMode)
|
||||||
|
{
|
||||||
|
var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
||||||
|
bool disableAutoClose = (scene.name.Contains("DisableAutoClose"));
|
||||||
|
|
||||||
|
if (allTestGreen)
|
||||||
|
{
|
||||||
|
WriteToConsole("Test Complete Successfully");
|
||||||
|
if (!disableAutoClose)
|
||||||
|
{
|
||||||
|
Application.Quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteToConsole("Test Failed, please see [NG] log.");
|
||||||
|
if (!disableAutoClose)
|
||||||
|
{
|
||||||
|
Application.Quit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator UnwrapEnumerator(IEnumerator enumerator, Action<Exception> exceptionCallback)
|
||||||
|
{
|
||||||
|
var hasNext = true;
|
||||||
|
while (hasNext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
hasNext = enumerator.MoveNext();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exceptionCallback(ex);
|
||||||
|
hasNext = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNext)
|
||||||
|
{
|
||||||
|
// unwrap self for bug of Unity
|
||||||
|
// https://issuetracker.unity3d.com/issues/does-not-stop-coroutine-when-it-throws-exception-in-movenext-at-first-frame
|
||||||
|
var moreCoroutine = enumerator.Current as IEnumerator;
|
||||||
|
if (moreCoroutine != null)
|
||||||
|
{
|
||||||
|
yield return StartCoroutine(UnwrapEnumerator(moreCoroutine, ex =>
|
||||||
|
{
|
||||||
|
exceptionCallback(ex);
|
||||||
|
hasNext = false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yield return enumerator.Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteToConsole(string msg)
|
||||||
|
{
|
||||||
|
if (Application.isBatchMode)
|
||||||
|
{
|
||||||
|
Console.WriteLine(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteToConsoleResult(string msg, bool green)
|
||||||
|
{
|
||||||
|
if (Application.isBatchMode)
|
||||||
|
{
|
||||||
|
if (!green)
|
||||||
|
{
|
||||||
|
var currentForeground = Console.ForegroundColor;
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.Write("[NG]");
|
||||||
|
Console.ForegroundColor = currentForeground;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var currentForeground = Console.ForegroundColor;
|
||||||
|
Console.ForegroundColor = ConsoleColor.Green;
|
||||||
|
Console.Write("[OK]");
|
||||||
|
Console.ForegroundColor = currentForeground;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Console.WriteLine(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Pair
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public UnityAction Action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 660baed073888b8438569f57e42679b2
|
||||||
|
timeCreated: 1476793308
|
||||||
|
licenseType: Pro
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "jp.cysharp.runtimeunittesttoolkit",
|
||||||
|
"displayName": "RuntimeUnitTestToolkit",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"unity": "2018.3",
|
||||||
|
"description": "CLI/GUI Frontend of Unity Test Runner to test on any platform.",
|
||||||
|
"keywords": ["test"],
|
||||||
|
"category": "Tests",
|
||||||
|
"dependencies": {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b7883c7ac5d6ea4409a229aeab14e796
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3e4672a57ce755a44805bc58b4ddea29
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UniRx.Async;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class SandboxMain : MonoBehaviour
|
||||||
|
{
|
||||||
|
// Start is called before the first frame update
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
RunAsync().Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
async UniTaskVoid RunAsync()
|
||||||
|
{
|
||||||
|
var id = await UniTask.Run(() => System.Threading.Thread.CurrentThread.ManagedThreadId, configureAwait: false);
|
||||||
|
UnityEngine.Debug.Log("ReturnId:" + id);
|
||||||
|
UnityEngine.Debug.Log("CurrentId:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f0bc6c75abb2e0b47a25aa49bfd488ed
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,218 @@
|
||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!29 &1
|
||||||
|
OcclusionCullingSettings:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 2
|
||||||
|
m_OcclusionBakeSettings:
|
||||||
|
smallestOccluder: 5
|
||||||
|
smallestHole: 0.25
|
||||||
|
backfaceThreshold: 100
|
||||||
|
m_SceneGUID: 00000000000000000000000000000000
|
||||||
|
m_OcclusionCullingData: {fileID: 0}
|
||||||
|
--- !u!104 &2
|
||||||
|
RenderSettings:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 9
|
||||||
|
m_Fog: 0
|
||||||
|
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||||
|
m_FogMode: 3
|
||||||
|
m_FogDensity: 0.01
|
||||||
|
m_LinearFogStart: 0
|
||||||
|
m_LinearFogEnd: 300
|
||||||
|
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||||
|
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||||
|
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||||
|
m_AmbientIntensity: 1
|
||||||
|
m_AmbientMode: 3
|
||||||
|
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||||
|
m_SkyboxMaterial: {fileID: 0}
|
||||||
|
m_HaloStrength: 0.5
|
||||||
|
m_FlareStrength: 1
|
||||||
|
m_FlareFadeSpeed: 3
|
||||||
|
m_HaloTexture: {fileID: 0}
|
||||||
|
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
|
m_DefaultReflectionMode: 0
|
||||||
|
m_DefaultReflectionResolution: 128
|
||||||
|
m_ReflectionBounces: 1
|
||||||
|
m_ReflectionIntensity: 1
|
||||||
|
m_CustomReflection: {fileID: 0}
|
||||||
|
m_Sun: {fileID: 0}
|
||||||
|
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
|
||||||
|
m_UseRadianceAmbientProbe: 0
|
||||||
|
--- !u!157 &3
|
||||||
|
LightmapSettings:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 11
|
||||||
|
m_GIWorkflowMode: 1
|
||||||
|
m_GISettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_BounceScale: 1
|
||||||
|
m_IndirectOutputScale: 1
|
||||||
|
m_AlbedoBoost: 1
|
||||||
|
m_EnvironmentLightingMode: 0
|
||||||
|
m_EnableBakedLightmaps: 0
|
||||||
|
m_EnableRealtimeLightmaps: 0
|
||||||
|
m_LightmapEditorSettings:
|
||||||
|
serializedVersion: 12
|
||||||
|
m_Resolution: 2
|
||||||
|
m_BakeResolution: 40
|
||||||
|
m_AtlasSize: 1024
|
||||||
|
m_AO: 0
|
||||||
|
m_AOMaxDistance: 1
|
||||||
|
m_CompAOExponent: 1
|
||||||
|
m_CompAOExponentDirect: 0
|
||||||
|
m_ExtractAmbientOcclusion: 0
|
||||||
|
m_Padding: 2
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_LightmapsBakeMode: 1
|
||||||
|
m_TextureCompression: 1
|
||||||
|
m_FinalGather: 0
|
||||||
|
m_FinalGatherFiltering: 1
|
||||||
|
m_FinalGatherRayCount: 256
|
||||||
|
m_ReflectionCompression: 2
|
||||||
|
m_MixedBakeMode: 2
|
||||||
|
m_BakeBackend: 0
|
||||||
|
m_PVRSampling: 1
|
||||||
|
m_PVRDirectSampleCount: 32
|
||||||
|
m_PVRSampleCount: 500
|
||||||
|
m_PVRBounces: 2
|
||||||
|
m_PVREnvironmentSampleCount: 500
|
||||||
|
m_PVREnvironmentReferencePointCount: 2048
|
||||||
|
m_PVRFilteringMode: 2
|
||||||
|
m_PVRDenoiserTypeDirect: 0
|
||||||
|
m_PVRDenoiserTypeIndirect: 0
|
||||||
|
m_PVRDenoiserTypeAO: 0
|
||||||
|
m_PVRFilterTypeDirect: 0
|
||||||
|
m_PVRFilterTypeIndirect: 0
|
||||||
|
m_PVRFilterTypeAO: 0
|
||||||
|
m_PVREnvironmentMIS: 0
|
||||||
|
m_PVRCulling: 1
|
||||||
|
m_PVRFilteringGaussRadiusDirect: 1
|
||||||
|
m_PVRFilteringGaussRadiusIndirect: 5
|
||||||
|
m_PVRFilteringGaussRadiusAO: 2
|
||||||
|
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||||
|
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||||
|
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||||
|
m_ExportTrainingData: 0
|
||||||
|
m_TrainingDataDestination: TrainingData
|
||||||
|
m_LightingDataAsset: {fileID: 0}
|
||||||
|
m_UseShadowmask: 1
|
||||||
|
--- !u!196 &4
|
||||||
|
NavMeshSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_BuildSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
agentTypeID: 0
|
||||||
|
agentRadius: 0.5
|
||||||
|
agentHeight: 2
|
||||||
|
agentSlope: 45
|
||||||
|
agentClimb: 0.4
|
||||||
|
ledgeDropHeight: 0
|
||||||
|
maxJumpAcrossDistance: 0
|
||||||
|
minRegionArea: 2
|
||||||
|
manualCellSize: 0
|
||||||
|
cellSize: 0.16666667
|
||||||
|
manualTileSize: 0
|
||||||
|
tileSize: 256
|
||||||
|
accuratePlacement: 0
|
||||||
|
debug:
|
||||||
|
m_Flags: 0
|
||||||
|
m_NavMeshData: {fileID: 0}
|
||||||
|
--- !u!1 &519420028
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 519420032}
|
||||||
|
- component: {fileID: 519420031}
|
||||||
|
- component: {fileID: 519420029}
|
||||||
|
- component: {fileID: 519420030}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: Main Camera
|
||||||
|
m_TagString: MainCamera
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!81 &519420029
|
||||||
|
AudioListener:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 519420028}
|
||||||
|
m_Enabled: 1
|
||||||
|
--- !u!114 &519420030
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 519420028}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: f0bc6c75abb2e0b47a25aa49bfd488ed, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
--- !u!20 &519420031
|
||||||
|
Camera:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 519420028}
|
||||||
|
m_Enabled: 1
|
||||||
|
serializedVersion: 2
|
||||||
|
m_ClearFlags: 2
|
||||||
|
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||||
|
m_projectionMatrixMode: 1
|
||||||
|
m_GateFitMode: 2
|
||||||
|
m_FOVAxisMode: 0
|
||||||
|
m_SensorSize: {x: 36, y: 24}
|
||||||
|
m_LensShift: {x: 0, y: 0}
|
||||||
|
m_FocalLength: 50
|
||||||
|
m_NormalizedViewPortRect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: 1
|
||||||
|
height: 1
|
||||||
|
near clip plane: 0.3
|
||||||
|
far clip plane: 1000
|
||||||
|
field of view: 60
|
||||||
|
orthographic: 1
|
||||||
|
orthographic size: 5
|
||||||
|
m_Depth: -1
|
||||||
|
m_CullingMask:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_RenderingPath: -1
|
||||||
|
m_TargetTexture: {fileID: 0}
|
||||||
|
m_TargetDisplay: 0
|
||||||
|
m_TargetEye: 0
|
||||||
|
m_HDR: 1
|
||||||
|
m_AllowMSAA: 0
|
||||||
|
m_AllowDynamicResolution: 0
|
||||||
|
m_ForceIntoRT: 0
|
||||||
|
m_OcclusionCulling: 0
|
||||||
|
m_StereoConvergence: 10
|
||||||
|
m_StereoSeparation: 0.022
|
||||||
|
--- !u!4 &519420032
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 519420028}
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: -10}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_RootOrder: 0
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2cda990e2423bbf4892e6590ba056729
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b831a4cf04cbe4b48ae74498484ec8c1
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,384 @@
|
||||||
|
#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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
UnityEngine.Debug.Log("Before:" + currentThreadId);
|
||||||
|
|
||||||
|
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
//await UniTask.SwitchToThreadPool();
|
||||||
|
//await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var switchedThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
|
||||||
|
UnityEngine.Debug.Log("After:" + switchedThreadId);
|
||||||
|
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5e9960810a8f0634cb4aea38c563d947
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3510f39953ed3074cb2e0f04d3a3f807
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,144 @@
|
||||||
|
using NUnit.Framework;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Xunit
|
||||||
|
{
|
||||||
|
public class FactAttribute : TestAttribute
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shims of FluentAssertions
|
||||||
|
namespace FluentAssertions
|
||||||
|
{
|
||||||
|
public static class FluentAssertionsExtensions
|
||||||
|
{
|
||||||
|
public static Int Should(this int value)
|
||||||
|
{
|
||||||
|
return new Int(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Bool Should(this bool value)
|
||||||
|
{
|
||||||
|
return new Bool(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExceptionAssertion Should(this Exception value)
|
||||||
|
{
|
||||||
|
return new ExceptionAssertion(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Generic<T> Should<T>(this T value)
|
||||||
|
{
|
||||||
|
return new Generic<T>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Generic<T>
|
||||||
|
{
|
||||||
|
readonly T actual;
|
||||||
|
|
||||||
|
public Generic(T value)
|
||||||
|
{
|
||||||
|
actual = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Be(T expected)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotBe(T expected)
|
||||||
|
{
|
||||||
|
Assert.AreNotEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeNull()
|
||||||
|
{
|
||||||
|
Assert.IsNull(actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotBeNull()
|
||||||
|
{
|
||||||
|
Assert.IsNotNull(actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Bool
|
||||||
|
{
|
||||||
|
readonly bool actual;
|
||||||
|
|
||||||
|
public Bool(bool value)
|
||||||
|
{
|
||||||
|
actual = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Be(bool expected)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotBe(bool expected)
|
||||||
|
{
|
||||||
|
Assert.AreNotEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeTrue()
|
||||||
|
{
|
||||||
|
Assert.AreEqual(true, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeFalse()
|
||||||
|
{
|
||||||
|
Assert.AreEqual(false, actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Int
|
||||||
|
{
|
||||||
|
readonly int actual;
|
||||||
|
|
||||||
|
public Int(int value)
|
||||||
|
{
|
||||||
|
actual = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Be(int expected)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotBe(int expected)
|
||||||
|
{
|
||||||
|
Assert.AreNotEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeCloseTo(int expected, int delta)
|
||||||
|
{
|
||||||
|
if (expected - delta <= actual && actual <= expected + delta)
|
||||||
|
{
|
||||||
|
// OK.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Fail($"Fail BeCloseTo, actual {actual} but expected:{expected} +- {delta}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExceptionAssertion
|
||||||
|
{
|
||||||
|
readonly Exception actual;
|
||||||
|
|
||||||
|
public ExceptionAssertion(Exception actual)
|
||||||
|
{
|
||||||
|
this.actual = actual;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeAssignableTo<T>()
|
||||||
|
{
|
||||||
|
typeof(T).IsAssignableFrom(actual.GetType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f76a88025d2f76a4bae09aaa268da103
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "UniRx.Async.Tests",
|
||||||
|
"references": [
|
||||||
|
"UnityEngine.TestRunner",
|
||||||
|
"UnityEditor.TestRunner",
|
||||||
|
"UniRx.Async"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": true,
|
||||||
|
"precompiledReferences": [
|
||||||
|
"nunit.framework.dll"
|
||||||
|
],
|
||||||
|
"autoReferenced": false,
|
||||||
|
"defineConstraints": [
|
||||||
|
"UNITY_INCLUDE_TESTS"
|
||||||
|
],
|
||||||
|
"versionDefines": []
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9fbc684c31ac15b42809d42b62f6eb5f
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0a07d2ea8baf390408279a29510f4953
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 38249c6f05d584f44a219b4c97790f99
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,28 @@
|
||||||
|
#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
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace UniRx.Async
|
||||||
|
{
|
||||||
|
public struct AsyncUnit : IEquatable<AsyncUnit>
|
||||||
|
{
|
||||||
|
public static readonly AsyncUnit Default = new AsyncUnit();
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(AsyncUnit other)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "()";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4f95ac245430d304bb5128d13b6becc8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,25 @@
|
||||||
|
#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 System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniRx.Async
|
||||||
|
{
|
||||||
|
public class CancellationTokenEqualityComparer : IEqualityComparer<CancellationToken>
|
||||||
|
{
|
||||||
|
public static readonly IEqualityComparer<CancellationToken> Default = new CancellationTokenEqualityComparer();
|
||||||
|
|
||||||
|
public bool Equals(CancellationToken x, CancellationToken y)
|
||||||
|
{
|
||||||
|
return x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(CancellationToken obj)
|
||||||
|
{
|
||||||
|
return obj.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7d739f510b125b74fa7290ac4335e46e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,76 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniRx.Async
|
||||||
|
{
|
||||||
|
public static class CancellationTokenExtensions
|
||||||
|
{
|
||||||
|
static readonly Action<object> cancellationTokenCallback = Callback;
|
||||||
|
|
||||||
|
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cts)
|
||||||
|
{
|
||||||
|
if (cts.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return (UniTask.FromCanceled(cts), default(CancellationTokenRegistration));
|
||||||
|
}
|
||||||
|
|
||||||
|
var promise = new UniTaskCompletionSource<AsyncUnit>();
|
||||||
|
return (promise.Task, cts.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Callback(object state)
|
||||||
|
{
|
||||||
|
var promise = (UniTaskCompletionSource<AsyncUnit>)state;
|
||||||
|
promise.TrySetResult(AsyncUnit.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action callback)
|
||||||
|
{
|
||||||
|
var restoreFlow = false;
|
||||||
|
if (!ExecutionContext.IsFlowSuppressed())
|
||||||
|
{
|
||||||
|
ExecutionContext.SuppressFlow();
|
||||||
|
restoreFlow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return cancellationToken.Register(callback, false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (restoreFlow)
|
||||||
|
{
|
||||||
|
ExecutionContext.RestoreFlow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action<object> callback, object state)
|
||||||
|
{
|
||||||
|
var restoreFlow = false;
|
||||||
|
if (!ExecutionContext.IsFlowSuppressed())
|
||||||
|
{
|
||||||
|
ExecutionContext.SuppressFlow();
|
||||||
|
restoreFlow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return cancellationToken.Register(callback, state, false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (restoreFlow)
|
||||||
|
{
|
||||||
|
ExecutionContext.RestoreFlow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4be7209f04146bd45ac5ee775a5f7c26
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,48 @@
|
||||||
|
#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 System.Threading;
|
||||||
|
using UnityEngine;
|
||||||
|
using UniRx.Async.Triggers;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace UniRx.Async
|
||||||
|
{
|
||||||
|
public static class CancellationTokenSourceExtensions
|
||||||
|
{
|
||||||
|
public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||||
|
{
|
||||||
|
var delay = UniTask.Delay(millisecondsDelay, ignoreTimeScale, delayTiming, cts.Token);
|
||||||
|
CancelAfterCore(cts, delay).Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||||
|
{
|
||||||
|
var delay = UniTask.Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cts.Token);
|
||||||
|
CancelAfterCore(cts, delay).Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTaskVoid CancelAfterCore(CancellationTokenSource cts, UniTask delayTask)
|
||||||
|
{
|
||||||
|
var alreadyCanceled = await delayTask.SuppressCancellationThrow();
|
||||||
|
if (!alreadyCanceled)
|
||||||
|
{
|
||||||
|
cts.Cancel();
|
||||||
|
cts.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, Component component)
|
||||||
|
{
|
||||||
|
RegisterRaiseCancelOnDestroy(cts, component.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject)
|
||||||
|
{
|
||||||
|
var trigger = gameObject.GetAsyncDestroyTrigger();
|
||||||
|
trigger.AddCancellationTriggerOnDestory(cts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 22d85d07f1e70ab42a7a4c25bd65e661
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 64b064347ca7a404494a996b072e2e29
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
||||||
|
#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
|
||||||
|
|
||||||
|
namespace System.Runtime.CompilerServices
|
||||||
|
{
|
||||||
|
public sealed class AsyncMethodBuilderAttribute : Attribute
|
||||||
|
{
|
||||||
|
public Type BuilderType { get; }
|
||||||
|
|
||||||
|
public AsyncMethodBuilderAttribute(Type builderType)
|
||||||
|
{
|
||||||
|
BuilderType = builderType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 02ce354d37b10454e8376062f7cbe57a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,274 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Security;
|
||||||
|
|
||||||
|
namespace UniRx.Async.CompilerServices
|
||||||
|
{
|
||||||
|
public struct AsyncUniTaskMethodBuilder
|
||||||
|
{
|
||||||
|
UniTaskCompletionSource promise;
|
||||||
|
Action moveNext;
|
||||||
|
|
||||||
|
// 1. Static Create method.
|
||||||
|
[DebuggerHidden]
|
||||||
|
public static AsyncUniTaskMethodBuilder Create()
|
||||||
|
{
|
||||||
|
var builder = new AsyncUniTaskMethodBuilder();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. TaskLike Task property.
|
||||||
|
[DebuggerHidden]
|
||||||
|
public UniTask Task
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (promise != null)
|
||||||
|
{
|
||||||
|
return promise.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moveNext == null)
|
||||||
|
{
|
||||||
|
return UniTask.CompletedTask;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
promise = new UniTaskCompletionSource();
|
||||||
|
return promise.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. SetException
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetException(Exception exception)
|
||||||
|
{
|
||||||
|
if (promise == null)
|
||||||
|
{
|
||||||
|
promise = new UniTaskCompletionSource();
|
||||||
|
}
|
||||||
|
if (exception is OperationCanceledException ex)
|
||||||
|
{
|
||||||
|
promise.TrySetCanceled(ex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
promise.TrySetException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. SetResult
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetResult()
|
||||||
|
{
|
||||||
|
if (moveNext == null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (promise == null)
|
||||||
|
{
|
||||||
|
promise = new UniTaskCompletionSource();
|
||||||
|
}
|
||||||
|
promise.TrySetResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. AwaitOnCompleted
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
|
where TAwaiter : INotifyCompletion
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
if (moveNext == null)
|
||||||
|
{
|
||||||
|
if (promise == null)
|
||||||
|
{
|
||||||
|
promise = new UniTaskCompletionSource(); // built future.
|
||||||
|
}
|
||||||
|
|
||||||
|
var runner = new MoveNextRunner<TStateMachine>();
|
||||||
|
moveNext = runner.Run;
|
||||||
|
runner.StateMachine = stateMachine; // set after create delegate.
|
||||||
|
}
|
||||||
|
|
||||||
|
awaiter.OnCompleted(moveNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. AwaitUnsafeOnCompleted
|
||||||
|
[DebuggerHidden]
|
||||||
|
[SecuritySafeCritical]
|
||||||
|
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
|
where TAwaiter : ICriticalNotifyCompletion
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
if (moveNext == null)
|
||||||
|
{
|
||||||
|
if (promise == null)
|
||||||
|
{
|
||||||
|
promise = new UniTaskCompletionSource(); // built future.
|
||||||
|
}
|
||||||
|
|
||||||
|
var runner = new MoveNextRunner<TStateMachine>();
|
||||||
|
moveNext = runner.Run;
|
||||||
|
runner.StateMachine = stateMachine; // set after create delegate.
|
||||||
|
}
|
||||||
|
|
||||||
|
awaiter.UnsafeOnCompleted(moveNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Start
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
stateMachine.MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. SetStateMachine
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public struct AsyncUniTaskMethodBuilder<T>
|
||||||
|
{
|
||||||
|
T result;
|
||||||
|
UniTaskCompletionSource<T> promise;
|
||||||
|
Action moveNext;
|
||||||
|
|
||||||
|
// 1. Static Create method.
|
||||||
|
[DebuggerHidden]
|
||||||
|
public static AsyncUniTaskMethodBuilder<T> Create()
|
||||||
|
{
|
||||||
|
var builder = new AsyncUniTaskMethodBuilder<T>();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. TaskLike Task property.
|
||||||
|
[DebuggerHidden]
|
||||||
|
public UniTask<T> Task
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (promise != null)
|
||||||
|
{
|
||||||
|
return new UniTask<T>(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moveNext == null)
|
||||||
|
{
|
||||||
|
return new UniTask<T>(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
promise = new UniTaskCompletionSource<T>();
|
||||||
|
return new UniTask<T>(promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. SetException
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetException(Exception exception)
|
||||||
|
{
|
||||||
|
if (promise == null)
|
||||||
|
{
|
||||||
|
promise = new UniTaskCompletionSource<T>();
|
||||||
|
}
|
||||||
|
if (exception is OperationCanceledException ex)
|
||||||
|
{
|
||||||
|
promise.TrySetCanceled(ex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
promise.TrySetException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. SetResult
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetResult(T result)
|
||||||
|
{
|
||||||
|
if (moveNext == null)
|
||||||
|
{
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (promise == null)
|
||||||
|
{
|
||||||
|
promise = new UniTaskCompletionSource<T>();
|
||||||
|
}
|
||||||
|
promise.TrySetResult(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. AwaitOnCompleted
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
|
where TAwaiter : INotifyCompletion
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
if (moveNext == null)
|
||||||
|
{
|
||||||
|
if (promise == null)
|
||||||
|
{
|
||||||
|
promise = new UniTaskCompletionSource<T>(); // built future.
|
||||||
|
}
|
||||||
|
|
||||||
|
var runner = new MoveNextRunner<TStateMachine>();
|
||||||
|
moveNext = runner.Run;
|
||||||
|
runner.StateMachine = stateMachine; // set after create delegate.
|
||||||
|
}
|
||||||
|
|
||||||
|
awaiter.OnCompleted(moveNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. AwaitUnsafeOnCompleted
|
||||||
|
[DebuggerHidden]
|
||||||
|
[SecuritySafeCritical]
|
||||||
|
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
|
where TAwaiter : ICriticalNotifyCompletion
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
if (moveNext == null)
|
||||||
|
{
|
||||||
|
if (promise == null)
|
||||||
|
{
|
||||||
|
promise = new UniTaskCompletionSource<T>(); // built future.
|
||||||
|
}
|
||||||
|
|
||||||
|
var runner = new MoveNextRunner<TStateMachine>();
|
||||||
|
moveNext = runner.Run;
|
||||||
|
runner.StateMachine = stateMachine; // set after create delegate.
|
||||||
|
}
|
||||||
|
|
||||||
|
awaiter.UnsafeOnCompleted(moveNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Start
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
stateMachine.MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. SetStateMachine
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 68d72a45afdec574ebc26e7de2c38330
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,90 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Security;
|
||||||
|
|
||||||
|
namespace UniRx.Async.CompilerServices
|
||||||
|
{
|
||||||
|
public struct AsyncUniTaskVoidMethodBuilder
|
||||||
|
{
|
||||||
|
Action moveNext;
|
||||||
|
|
||||||
|
// 1. Static Create method.
|
||||||
|
[DebuggerHidden]
|
||||||
|
public static AsyncUniTaskVoidMethodBuilder Create()
|
||||||
|
{
|
||||||
|
var builder = new AsyncUniTaskVoidMethodBuilder();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. TaskLike Task property(void)
|
||||||
|
public UniTaskVoid Task => default(UniTaskVoid);
|
||||||
|
|
||||||
|
// 3. SetException
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetException(Exception exception)
|
||||||
|
{
|
||||||
|
UniTaskScheduler.PublishUnobservedTaskException(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. SetResult
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetResult()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. AwaitOnCompleted
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
|
where TAwaiter : INotifyCompletion
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
if (moveNext == null)
|
||||||
|
{
|
||||||
|
var runner = new MoveNextRunner<TStateMachine>();
|
||||||
|
moveNext = runner.Run;
|
||||||
|
runner.StateMachine = stateMachine; // set after create delegate.
|
||||||
|
}
|
||||||
|
|
||||||
|
awaiter.OnCompleted(moveNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. AwaitUnsafeOnCompleted
|
||||||
|
[DebuggerHidden]
|
||||||
|
[SecuritySafeCritical]
|
||||||
|
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
|
where TAwaiter : ICriticalNotifyCompletion
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
if (moveNext == null)
|
||||||
|
{
|
||||||
|
var runner = new MoveNextRunner<TStateMachine>();
|
||||||
|
moveNext = runner.Run;
|
||||||
|
runner.StateMachine = stateMachine; // set after create delegate.
|
||||||
|
}
|
||||||
|
|
||||||
|
awaiter.UnsafeOnCompleted(moveNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Start
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
stateMachine.MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. SetStateMachine
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e891aaac17b933a47a9d7fa3b8e1226f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,23 @@
|
||||||
|
#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 System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace UniRx.Async.CompilerServices
|
||||||
|
{
|
||||||
|
internal class MoveNextRunner<TStateMachine>
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
public TStateMachine StateMachine;
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
StateMachine.MoveNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 98649642833cabf44a9dc060ce4c84a1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,270 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Security;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace UniRx.Async
|
||||||
|
{
|
||||||
|
public static class DiagnosticsExtensions
|
||||||
|
{
|
||||||
|
static bool displayFilenames = true;
|
||||||
|
|
||||||
|
static readonly Regex typeBeautifyRegex = new Regex("`.+$", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
static readonly Dictionary<Type, string> builtInTypeNames = new Dictionary<Type, string>
|
||||||
|
{
|
||||||
|
{ typeof(void), "void" },
|
||||||
|
{ typeof(bool), "bool" },
|
||||||
|
{ typeof(byte), "byte" },
|
||||||
|
{ typeof(char), "char" },
|
||||||
|
{ typeof(decimal), "decimal" },
|
||||||
|
{ typeof(double), "double" },
|
||||||
|
{ typeof(float), "float" },
|
||||||
|
{ typeof(int), "int" },
|
||||||
|
{ typeof(long), "long" },
|
||||||
|
{ typeof(object), "object" },
|
||||||
|
{ typeof(sbyte), "sbyte" },
|
||||||
|
{ typeof(short), "short" },
|
||||||
|
{ typeof(string), "string" },
|
||||||
|
{ typeof(uint), "uint" },
|
||||||
|
{ typeof(ulong), "ulong" },
|
||||||
|
{ typeof(ushort), "ushort" },
|
||||||
|
{ typeof(Task), "Task" },
|
||||||
|
{ typeof(UniTask), "UniTask" },
|
||||||
|
{ typeof(UniTaskVoid), "UniTaskVoid" }
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string ToStringWithCleanupAsyncStackTrace(this Exception exception)
|
||||||
|
{
|
||||||
|
if (exception == null) return "";
|
||||||
|
|
||||||
|
String message = exception.Message;
|
||||||
|
String s;
|
||||||
|
|
||||||
|
if (message == null || message.Length <= 0)
|
||||||
|
{
|
||||||
|
s = exception.GetType().ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s = exception.GetType().ToString() + ": " + message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception.InnerException != null)
|
||||||
|
{
|
||||||
|
s = s + " ---> " + exception.InnerException.ToString() + Environment.NewLine + " Exception_EndOfInnerExceptionStack";
|
||||||
|
}
|
||||||
|
|
||||||
|
string stackTrace = new StackTrace(exception).CleanupAsyncStackTrace();
|
||||||
|
if (stackTrace != null)
|
||||||
|
{
|
||||||
|
s += Environment.NewLine + stackTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CleanupAsyncStackTrace(this StackTrace stackTrace)
|
||||||
|
{
|
||||||
|
if (stackTrace == null) return "";
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < stackTrace.FrameCount; i++)
|
||||||
|
{
|
||||||
|
var sf = stackTrace.GetFrame(i);
|
||||||
|
|
||||||
|
var mb = sf.GetMethod();
|
||||||
|
|
||||||
|
if (IgnoreLine(mb)) continue;
|
||||||
|
if (IsAsync(mb))
|
||||||
|
{
|
||||||
|
sb.Append("async ");
|
||||||
|
TryResolveStateMachineMethod(ref mb, out var decType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return type
|
||||||
|
if (mb is MethodInfo mi)
|
||||||
|
{
|
||||||
|
sb.Append(BeautifyType(mi.ReturnType, false));
|
||||||
|
sb.Append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// method name
|
||||||
|
sb.Append(BeautifyType(mb.DeclaringType, false));
|
||||||
|
if (!mb.IsConstructor)
|
||||||
|
{
|
||||||
|
sb.Append(".");
|
||||||
|
}
|
||||||
|
sb.Append(mb.Name);
|
||||||
|
if (mb.IsGenericMethod)
|
||||||
|
{
|
||||||
|
sb.Append("<");
|
||||||
|
foreach (var item in mb.GetGenericArguments())
|
||||||
|
{
|
||||||
|
sb.Append(BeautifyType(item, true));
|
||||||
|
}
|
||||||
|
sb.Append(">");
|
||||||
|
}
|
||||||
|
|
||||||
|
// parameter
|
||||||
|
sb.Append("(");
|
||||||
|
sb.Append(string.Join(", ", mb.GetParameters().Select(p => BeautifyType(p.ParameterType, true) + " " + p.Name)));
|
||||||
|
sb.Append(")");
|
||||||
|
|
||||||
|
// file name
|
||||||
|
if (displayFilenames && (sf.GetILOffset() != -1))
|
||||||
|
{
|
||||||
|
String fileName = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fileName = sf.GetFileName();
|
||||||
|
}
|
||||||
|
catch (NotSupportedException)
|
||||||
|
{
|
||||||
|
displayFilenames = false;
|
||||||
|
}
|
||||||
|
catch (SecurityException)
|
||||||
|
{
|
||||||
|
displayFilenames = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName != null)
|
||||||
|
{
|
||||||
|
sb.Append(' ');
|
||||||
|
sb.AppendFormat(CultureInfo.InvariantCulture, "in {0}:{1}", SimplifyPath(fileName), sf.GetFileLineNumber());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool IsAsync(MethodBase methodInfo)
|
||||||
|
{
|
||||||
|
var declareType = methodInfo.DeclaringType;
|
||||||
|
return typeof(IAsyncStateMachine).IsAssignableFrom(declareType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// code from Ben.Demystifier/EnhancedStackTrace.Frame.cs
|
||||||
|
static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType)
|
||||||
|
{
|
||||||
|
declaringType = method.DeclaringType;
|
||||||
|
|
||||||
|
var parentType = declaringType.DeclaringType;
|
||||||
|
if (parentType == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||||
|
if (methods == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var candidateMethod in methods)
|
||||||
|
{
|
||||||
|
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>();
|
||||||
|
if (attributes == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var asma in attributes)
|
||||||
|
{
|
||||||
|
if (asma.StateMachineType == declaringType)
|
||||||
|
{
|
||||||
|
method = candidateMethod;
|
||||||
|
declaringType = candidateMethod.DeclaringType;
|
||||||
|
// Mark the iterator as changed; so it gets the + annotation of the original method
|
||||||
|
// async statemachines resolve directly to their builder methods so aren't marked as changed
|
||||||
|
return asma is IteratorStateMachineAttribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string BeautifyType(Type t, bool shortName)
|
||||||
|
{
|
||||||
|
if (builtInTypeNames.TryGetValue(t, out var builtin))
|
||||||
|
{
|
||||||
|
return builtin;
|
||||||
|
}
|
||||||
|
if (t.IsGenericParameter) return t.Name;
|
||||||
|
if (t.IsArray) return BeautifyType(t.GetElementType(), shortName) + "[]";
|
||||||
|
if (t.FullName?.StartsWith("System.ValueTuple") ?? false)
|
||||||
|
{
|
||||||
|
return "(" + string.Join(", ", t.GetGenericArguments().Select(x => BeautifyType(x, true))) + ")";
|
||||||
|
}
|
||||||
|
if (!t.IsGenericType) return shortName ? t.Name : t.FullName ?? t.Name;
|
||||||
|
|
||||||
|
var innerFormat = string.Join(", ", t.GetGenericArguments().Select(x => BeautifyType(x, true)));
|
||||||
|
|
||||||
|
var genericType = t.GetGenericTypeDefinition().FullName;
|
||||||
|
if (genericType == "System.Threading.Tasks.Task`1")
|
||||||
|
{
|
||||||
|
genericType = "Task";
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeBeautifyRegex.Replace(genericType, "") + "<" + innerFormat + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IgnoreLine(MethodBase methodInfo)
|
||||||
|
{
|
||||||
|
var declareType = methodInfo.DeclaringType.FullName;
|
||||||
|
if (declareType == "System.Threading.ExecutionContext")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (declareType.StartsWith("System.Runtime.CompilerServices"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (declareType.StartsWith("UniRx.Async.CompilerServices"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (declareType == "System.Threading.Tasks.AwaitTaskContinuation")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (declareType.StartsWith("System.Threading.Tasks.Task"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string SimplifyPath(string path)
|
||||||
|
{
|
||||||
|
var fi = new FileInfo(path);
|
||||||
|
if (fi.Directory == null)
|
||||||
|
{
|
||||||
|
return fi.Name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return fi.Directory.Name + "/" + fi.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f80fb1c9ed4c99447be1b0a47a8d980b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 275b87293edc6634f9d72387851dbbdf
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,64 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Editor
|
||||||
|
{
|
||||||
|
// reflection call of UnityEditor.SplitterGUILayout
|
||||||
|
internal static class SplitterGUILayout
|
||||||
|
{
|
||||||
|
static BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
|
||||||
|
|
||||||
|
static Lazy<Type> splitterStateType = new Lazy<Type>(() =>
|
||||||
|
{
|
||||||
|
var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterState");
|
||||||
|
return type;
|
||||||
|
});
|
||||||
|
|
||||||
|
static Lazy<ConstructorInfo> splitterStateCtor = new Lazy<ConstructorInfo>(() =>
|
||||||
|
{
|
||||||
|
var type = splitterStateType.Value;
|
||||||
|
return type.GetConstructor(flags, null, new Type[] { typeof(float[]), typeof(int[]), typeof(int[]) }, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
static Lazy<Type> splitterGUILayoutType = new Lazy<Type>(() =>
|
||||||
|
{
|
||||||
|
var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterGUILayout");
|
||||||
|
return type;
|
||||||
|
});
|
||||||
|
|
||||||
|
static Lazy<MethodInfo> beginVerticalSplit = new Lazy<MethodInfo>(() =>
|
||||||
|
{
|
||||||
|
var type = splitterGUILayoutType.Value;
|
||||||
|
return type.GetMethod("BeginVerticalSplit", flags, null, new Type[] { splitterStateType.Value, typeof(GUILayoutOption[]) }, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
static Lazy<MethodInfo> endVerticalSplit = new Lazy<MethodInfo>(() =>
|
||||||
|
{
|
||||||
|
var type = splitterGUILayoutType.Value;
|
||||||
|
return type.GetMethod("EndVerticalSplit", flags, null, Type.EmptyTypes, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
public static object CreateSplitterState(float[] relativeSizes, int[] minSizes, int[] maxSizes)
|
||||||
|
{
|
||||||
|
return splitterStateCtor.Value.Invoke(new object[] { relativeSizes, minSizes, maxSizes });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void BeginVerticalSplit(object splitterState, params GUILayoutOption[] options)
|
||||||
|
{
|
||||||
|
beginVerticalSplit.Value.Invoke(null, new object[] { splitterState, options });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EndVerticalSplit()
|
||||||
|
{
|
||||||
|
endVerticalSplit.Value.Invoke(null, Type.EmptyTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 40ef2e46f900131419e869398a8d3c9d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "UniRx.Async.Editor",
|
||||||
|
"references": [
|
||||||
|
"GUID:f51ebe6a0ceec4240a699833d6309b23"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": []
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4129704b5a1a13841ba16f230bf24a57
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,180 @@
|
||||||
|
#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 UnityEditor;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System;
|
||||||
|
using UnityEditor.IMGUI.Controls;
|
||||||
|
using UniRx.Async.Internal;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Editor
|
||||||
|
{
|
||||||
|
public class UniTaskTrackerViewItem : TreeViewItem
|
||||||
|
{
|
||||||
|
public string TaskType { get; set; }
|
||||||
|
public string Elapsed { get; set; }
|
||||||
|
public string Status { get; set; }
|
||||||
|
|
||||||
|
string position;
|
||||||
|
public string Position
|
||||||
|
{
|
||||||
|
get { return position; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
position = value;
|
||||||
|
PositionFirstLine = GetFirstLine(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PositionFirstLine { get; private set; }
|
||||||
|
|
||||||
|
static string GetFirstLine(string str)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < str.Length; i++)
|
||||||
|
{
|
||||||
|
if (str[i] == '\r' || str[i] == '\n')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.Append(str[i]);
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskTrackerViewItem(int id) : base(id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UniTaskTrackerTreeView : TreeView
|
||||||
|
{
|
||||||
|
const string sortedColumnIndexStateKey = "UniTaskTrackerTreeView_sortedColumnIndex";
|
||||||
|
|
||||||
|
public IReadOnlyList<TreeViewItem> CurrentBindingItems;
|
||||||
|
|
||||||
|
public UniTaskTrackerTreeView()
|
||||||
|
: this(new TreeViewState(), new MultiColumnHeader(new MultiColumnHeaderState(new[]
|
||||||
|
{
|
||||||
|
new MultiColumnHeaderState.Column() { headerContent = new GUIContent("TaskType"), width = 20},
|
||||||
|
new MultiColumnHeaderState.Column() { headerContent = new GUIContent("Elapsed"), width = 10},
|
||||||
|
new MultiColumnHeaderState.Column() { headerContent = new GUIContent("Status"), width = 10},
|
||||||
|
new MultiColumnHeaderState.Column() { headerContent = new GUIContent("Position")},
|
||||||
|
})))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
UniTaskTrackerTreeView(TreeViewState state, MultiColumnHeader header)
|
||||||
|
: base(state, header)
|
||||||
|
{
|
||||||
|
rowHeight = 20;
|
||||||
|
showAlternatingRowBackgrounds = true;
|
||||||
|
showBorder = true;
|
||||||
|
header.sortingChanged += Header_sortingChanged;
|
||||||
|
|
||||||
|
header.ResizeToFit();
|
||||||
|
Reload();
|
||||||
|
|
||||||
|
header.sortedColumnIndex = SessionState.GetInt(sortedColumnIndexStateKey, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReloadAndSort()
|
||||||
|
{
|
||||||
|
var currentSelected = this.state.selectedIDs;
|
||||||
|
Reload();
|
||||||
|
Header_sortingChanged(this.multiColumnHeader);
|
||||||
|
this.state.selectedIDs = currentSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Header_sortingChanged(MultiColumnHeader multiColumnHeader)
|
||||||
|
{
|
||||||
|
SessionState.SetInt(sortedColumnIndexStateKey, multiColumnHeader.sortedColumnIndex);
|
||||||
|
var index = multiColumnHeader.sortedColumnIndex;
|
||||||
|
var ascending = multiColumnHeader.IsSortedAscending(multiColumnHeader.sortedColumnIndex);
|
||||||
|
|
||||||
|
var items = rootItem.children.Cast<UniTaskTrackerViewItem>();
|
||||||
|
|
||||||
|
IOrderedEnumerable<UniTaskTrackerViewItem> orderedEnumerable;
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
orderedEnumerable = ascending ? items.OrderBy(item => item.TaskType) : items.OrderByDescending(item => item.TaskType);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
orderedEnumerable = ascending ? items.OrderBy(item => double.Parse(item.Elapsed)) : items.OrderByDescending(item => double.Parse(item.Elapsed));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
orderedEnumerable = ascending ? items.OrderBy(item => item.Status) : items.OrderByDescending(item => item.Elapsed);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
orderedEnumerable = ascending ? items.OrderBy(item => item.Position) : items.OrderByDescending(item => item.PositionFirstLine);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index), index, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentBindingItems = rootItem.children = orderedEnumerable.Cast<TreeViewItem>().ToList();
|
||||||
|
BuildRows(rootItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TreeViewItem BuildRoot()
|
||||||
|
{
|
||||||
|
var root = new TreeViewItem { depth = -1 };
|
||||||
|
|
||||||
|
var children = new List<TreeViewItem>();
|
||||||
|
|
||||||
|
TaskTracker.ForEachActiveTask((trackingId, awaiterType, status, created, stackTrace) =>
|
||||||
|
{
|
||||||
|
children.Add(new UniTaskTrackerViewItem(trackingId) { TaskType = awaiterType, Status = status.ToString(), Elapsed = (DateTime.UtcNow - created).TotalSeconds.ToString("00.00"), Position = stackTrace });
|
||||||
|
});
|
||||||
|
|
||||||
|
CurrentBindingItems = children;
|
||||||
|
root.children = CurrentBindingItems as List<TreeViewItem>;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool CanMultiSelect(TreeViewItem item)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RowGUI(RowGUIArgs args)
|
||||||
|
{
|
||||||
|
var item = args.item as UniTaskTrackerViewItem;
|
||||||
|
|
||||||
|
for (var visibleColumnIndex = 0; visibleColumnIndex < args.GetNumVisibleColumns(); visibleColumnIndex++)
|
||||||
|
{
|
||||||
|
var rect = args.GetCellRect(visibleColumnIndex);
|
||||||
|
var columnIndex = args.GetColumn(visibleColumnIndex);
|
||||||
|
|
||||||
|
var labelStyle = args.selected ? EditorStyles.whiteLabel : EditorStyles.label;
|
||||||
|
labelStyle.alignment = TextAnchor.MiddleLeft;
|
||||||
|
switch (columnIndex)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
EditorGUI.LabelField(rect, item.TaskType, labelStyle);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
EditorGUI.LabelField(rect, item.Elapsed, labelStyle);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
EditorGUI.LabelField(rect, item.Status, labelStyle);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
EditorGUI.LabelField(rect, item.PositionFirstLine, labelStyle);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(columnIndex), columnIndex, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 52e2d973a2156674e8c1c9433ed031f7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,211 @@
|
||||||
|
#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 UnityEditor;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System;
|
||||||
|
using UnityEditor.IMGUI.Controls;
|
||||||
|
using UniRx.Async.Internal;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Editor
|
||||||
|
{
|
||||||
|
public class UniTaskTrackerWindow : EditorWindow
|
||||||
|
{
|
||||||
|
static int interval;
|
||||||
|
|
||||||
|
static UniTaskTrackerWindow window;
|
||||||
|
|
||||||
|
[MenuItem("Window/UniRx/UniTask Tracker")]
|
||||||
|
public static void OpenWindow()
|
||||||
|
{
|
||||||
|
if (window != null)
|
||||||
|
{
|
||||||
|
window.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// will called OnEnable(singleton instance will be set).
|
||||||
|
GetWindow<UniTaskTrackerWindow>("UniTask Tracker").Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly GUILayoutOption[] EmptyLayoutOption = new GUILayoutOption[0];
|
||||||
|
|
||||||
|
UniTaskTrackerTreeView treeView;
|
||||||
|
object splitterState;
|
||||||
|
|
||||||
|
void OnEnable()
|
||||||
|
{
|
||||||
|
window = this; // set singleton.
|
||||||
|
splitterState = SplitterGUILayout.CreateSplitterState(new float[] { 75f, 25f }, new int[] { 32, 32 }, null);
|
||||||
|
treeView = new UniTaskTrackerTreeView();
|
||||||
|
TaskTracker.EditorEnableState.EnableAutoReload = EditorPrefs.GetBool(TaskTracker.EnableAutoReloadKey, false);
|
||||||
|
TaskTracker.EditorEnableState.EnableTracking = EditorPrefs.GetBool(TaskTracker.EnableTrackingKey, false);
|
||||||
|
TaskTracker.EditorEnableState.EnableStackTrace = EditorPrefs.GetBool(TaskTracker.EnableStackTraceKey, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnGUI()
|
||||||
|
{
|
||||||
|
// Head
|
||||||
|
RenderHeadPanel();
|
||||||
|
|
||||||
|
// Splittable
|
||||||
|
SplitterGUILayout.BeginVerticalSplit(this.splitterState, EmptyLayoutOption);
|
||||||
|
{
|
||||||
|
// Column Tabble
|
||||||
|
RenderTable();
|
||||||
|
|
||||||
|
// StackTrace details
|
||||||
|
RenderDetailsPanel();
|
||||||
|
}
|
||||||
|
SplitterGUILayout.EndVerticalSplit();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region HeadPanel
|
||||||
|
|
||||||
|
public static bool EnableAutoReload => TaskTracker.EditorEnableState.EnableAutoReload;
|
||||||
|
public static bool EnableTracking => TaskTracker.EditorEnableState.EnableTracking;
|
||||||
|
public static bool EnableStackTrace => TaskTracker.EditorEnableState.EnableStackTrace;
|
||||||
|
static readonly GUIContent EnableAutoReloadHeadContent = EditorGUIUtility.TrTextContent("Enable AutoReload", "Reload automatically.", (Texture)null);
|
||||||
|
static readonly GUIContent ReloadHeadContent = EditorGUIUtility.TrTextContent("Reload", "Reload View.", (Texture)null);
|
||||||
|
static readonly GUIContent GCHeadContent = EditorGUIUtility.TrTextContent("GC.Collect", "Invoke GC.Collect.", (Texture)null);
|
||||||
|
static readonly GUIContent EnableTrackingHeadContent = EditorGUIUtility.TrTextContent("Enable Tracking", "Start to track async/await UniTask. Performance impact: low", (Texture)null);
|
||||||
|
static readonly GUIContent EnableStackTraceHeadContent = EditorGUIUtility.TrTextContent("Enable StackTrace", "Capture StackTrace when task is started. Performance impact: high", (Texture)null);
|
||||||
|
|
||||||
|
// [Enable Tracking] | [Enable StackTrace]
|
||||||
|
void RenderHeadPanel()
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical(EmptyLayoutOption);
|
||||||
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar, EmptyLayoutOption);
|
||||||
|
|
||||||
|
if (GUILayout.Toggle(EnableAutoReload, EnableAutoReloadHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableAutoReload)
|
||||||
|
{
|
||||||
|
TaskTracker.EditorEnableState.EnableAutoReload = !EnableAutoReload;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Toggle(EnableTracking, EnableTrackingHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableTracking)
|
||||||
|
{
|
||||||
|
TaskTracker.EditorEnableState.EnableTracking = !EnableTracking;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Toggle(EnableStackTrace, EnableStackTraceHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableStackTrace)
|
||||||
|
{
|
||||||
|
TaskTracker.EditorEnableState.EnableStackTrace = !EnableStackTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (GUILayout.Button(ReloadHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption))
|
||||||
|
{
|
||||||
|
TaskTracker.CheckAndResetDirty();
|
||||||
|
treeView.ReloadAndSort();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button(GCHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption))
|
||||||
|
{
|
||||||
|
GC.Collect(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region TableColumn
|
||||||
|
|
||||||
|
Vector2 tableScroll;
|
||||||
|
GUIStyle tableListStyle;
|
||||||
|
|
||||||
|
void RenderTable()
|
||||||
|
{
|
||||||
|
if (tableListStyle == null)
|
||||||
|
{
|
||||||
|
tableListStyle = new GUIStyle("CN Box");
|
||||||
|
tableListStyle.margin.top = 0;
|
||||||
|
tableListStyle.padding.left = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.BeginVertical(tableListStyle, EmptyLayoutOption);
|
||||||
|
|
||||||
|
this.tableScroll = EditorGUILayout.BeginScrollView(this.tableScroll, new GUILayoutOption[]
|
||||||
|
{
|
||||||
|
GUILayout.ExpandWidth(true),
|
||||||
|
GUILayout.MaxWidth(2000f)
|
||||||
|
});
|
||||||
|
var controlRect = EditorGUILayout.GetControlRect(new GUILayoutOption[]
|
||||||
|
{
|
||||||
|
GUILayout.ExpandHeight(true),
|
||||||
|
GUILayout.ExpandWidth(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
treeView?.OnGUI(controlRect);
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (EnableAutoReload)
|
||||||
|
{
|
||||||
|
if (interval++ % 120 == 0)
|
||||||
|
{
|
||||||
|
if (TaskTracker.CheckAndResetDirty())
|
||||||
|
{
|
||||||
|
treeView.ReloadAndSort();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Details
|
||||||
|
|
||||||
|
static GUIStyle detailsStyle;
|
||||||
|
Vector2 detailsScroll;
|
||||||
|
|
||||||
|
void RenderDetailsPanel()
|
||||||
|
{
|
||||||
|
if (detailsStyle == null)
|
||||||
|
{
|
||||||
|
detailsStyle = new GUIStyle(EditorStyles.wordWrappedLabel);
|
||||||
|
detailsStyle.wordWrap = false;
|
||||||
|
detailsStyle.stretchHeight = true;
|
||||||
|
detailsStyle.margin.right = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
string message = "";
|
||||||
|
var selected = treeView.state.selectedIDs;
|
||||||
|
if (selected.Count > 0)
|
||||||
|
{
|
||||||
|
var first = selected[0];
|
||||||
|
var item = treeView.CurrentBindingItems.FirstOrDefault(x => x.id == first) as UniTaskTrackerViewItem;
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
message = item.Position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detailsScroll = EditorGUILayout.BeginScrollView(this.detailsScroll, EmptyLayoutOption);
|
||||||
|
var vector = detailsStyle.CalcSize(new GUIContent(message));
|
||||||
|
EditorGUILayout.SelectableLabel(message, detailsStyle, new GUILayoutOption[]
|
||||||
|
{
|
||||||
|
GUILayout.ExpandHeight(true),
|
||||||
|
GUILayout.ExpandWidth(true),
|
||||||
|
GUILayout.MinWidth(vector.x),
|
||||||
|
GUILayout.MinHeight(vector.y)
|
||||||
|
});
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5bee3e3860e37484aa3b861bf76d129f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,36 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace UniRx.Async
|
||||||
|
{
|
||||||
|
public static class EnumerableAsyncExtensions
|
||||||
|
{
|
||||||
|
// overload resolver - .Select(async x => { }) : IEnumerable<UniTask<T>>
|
||||||
|
|
||||||
|
public static IEnumerable<UniTask> Select<T>(this IEnumerable<T> source, Func<T, UniTask> selector)
|
||||||
|
{
|
||||||
|
return System.Linq.Enumerable.Select(source, selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<UniTask<TR>> Select<T, TR>(this IEnumerable<T> source, Func<T, UniTask<TR>> selector)
|
||||||
|
{
|
||||||
|
return System.Linq.Enumerable.Select(source, selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<UniTask> Select<T>(this IEnumerable<T> source, Func<T, int, UniTask> selector)
|
||||||
|
{
|
||||||
|
return System.Linq.Enumerable.Select(source, selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<UniTask<TR>> Select<T, TR>(this IEnumerable<T> source, Func<T, int, UniTask<TR>> selector)
|
||||||
|
{
|
||||||
|
return System.Linq.Enumerable.Select(source, selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ff50260d74bd54c4b92cf99895549445
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,149 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
using System.Threading;
|
||||||
|
using UniRx.Async.Internal;
|
||||||
|
|
||||||
|
namespace UniRx.Async
|
||||||
|
{
|
||||||
|
public static class EnumeratorAsyncExtensions
|
||||||
|
{
|
||||||
|
public static IAwaiter GetAwaiter(this IEnumerator enumerator)
|
||||||
|
{
|
||||||
|
var awaiter = new EnumeratorAwaiter(enumerator, CancellationToken.None);
|
||||||
|
if (!awaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, awaiter);
|
||||||
|
}
|
||||||
|
return awaiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask ToUniTask(this IEnumerator enumerator)
|
||||||
|
{
|
||||||
|
var awaiter = new EnumeratorAwaiter(enumerator, CancellationToken.None);
|
||||||
|
if (!awaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, awaiter);
|
||||||
|
}
|
||||||
|
return new UniTask(awaiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask ConfigureAwait(this IEnumerator enumerator, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
var awaiter = new EnumeratorAwaiter(enumerator, cancellationToken);
|
||||||
|
if (!awaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
PlayerLoopHelper.AddAction(timing, awaiter);
|
||||||
|
}
|
||||||
|
return new UniTask(awaiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
class EnumeratorAwaiter : IAwaiter, IPlayerLoopItem
|
||||||
|
{
|
||||||
|
IEnumerator innerEnumerator;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
Action continuation;
|
||||||
|
AwaiterStatus status;
|
||||||
|
ExceptionDispatchInfo exception;
|
||||||
|
|
||||||
|
public EnumeratorAwaiter(IEnumerator innerEnumerator, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Canceled;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.innerEnumerator = innerEnumerator;
|
||||||
|
this.status = AwaiterStatus.Pending;
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
this.continuation = null;
|
||||||
|
|
||||||
|
TaskTracker.TrackActiveTask(this, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted => status.IsCompleted();
|
||||||
|
|
||||||
|
public AwaiterStatus Status => status;
|
||||||
|
|
||||||
|
public void GetResult()
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case AwaiterStatus.Succeeded:
|
||||||
|
break;
|
||||||
|
case AwaiterStatus.Pending:
|
||||||
|
Error.ThrowNotYetCompleted();
|
||||||
|
break;
|
||||||
|
case AwaiterStatus.Faulted:
|
||||||
|
exception.Throw();
|
||||||
|
break;
|
||||||
|
case AwaiterStatus.Canceled:
|
||||||
|
Error.ThrowOperationCanceledException();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
InvokeContinuation(AwaiterStatus.Canceled);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var success = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (innerEnumerator.MoveNext())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exception = ExceptionDispatchInfo.Capture(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
InvokeContinuation(success ? AwaiterStatus.Succeeded : AwaiterStatus.Faulted);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvokeContinuation(AwaiterStatus status)
|
||||||
|
{
|
||||||
|
this.status = status;
|
||||||
|
var cont = this.continuation;
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
this.continuation = null;
|
||||||
|
this.cancellationToken = CancellationToken.None;
|
||||||
|
this.innerEnumerator = null;
|
||||||
|
|
||||||
|
if (cont != null) cont.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
UnsafeOnCompleted(continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation);
|
||||||
|
this.continuation = continuation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bc661232f11e4a741af54ba1c175d5ee
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
||||||
|
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace UniRx.Async
|
||||||
|
{
|
||||||
|
public static class ExceptionExtensions
|
||||||
|
{
|
||||||
|
public static bool IsOperationCanceledException(this Exception exception)
|
||||||
|
{
|
||||||
|
return exception is OperationCanceledException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 930800098504c0d46958ce23a0495202
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,64 @@
|
||||||
|
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace UniRx.Async
|
||||||
|
{
|
||||||
|
public enum AwaiterStatus
|
||||||
|
{
|
||||||
|
/// <summary>The operation has not yet completed.</summary>
|
||||||
|
Pending = 0,
|
||||||
|
/// <summary>The operation completed successfully.</summary>
|
||||||
|
Succeeded = 1,
|
||||||
|
/// <summary>The operation completed with an error.</summary>
|
||||||
|
Faulted = 2,
|
||||||
|
/// <summary>The operation completed due to cancellation.</summary>
|
||||||
|
Canceled = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IAwaiter : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
AwaiterStatus Status { get; }
|
||||||
|
bool IsCompleted { get; }
|
||||||
|
void GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IAwaiter<out T> : IAwaiter
|
||||||
|
{
|
||||||
|
new T GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AwaiterStatusExtensions
|
||||||
|
{
|
||||||
|
/// <summary>!= Pending.</summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool IsCompleted(this AwaiterStatus status)
|
||||||
|
{
|
||||||
|
return status != AwaiterStatus.Pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>== Succeeded.</summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool IsCompletedSuccessfully(this AwaiterStatus status)
|
||||||
|
{
|
||||||
|
return status == AwaiterStatus.Succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>== Canceled.</summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool IsCanceled(this AwaiterStatus status)
|
||||||
|
{
|
||||||
|
return status == AwaiterStatus.Canceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>== Faulted.</summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool IsFaulted(this AwaiterStatus status)
|
||||||
|
{
|
||||||
|
return status == AwaiterStatus.Faulted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3e4d023d8404ab742b5e808c98097c3c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 633f49a8aafb6fa43894cd4646c71743
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,152 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
// Same interface as System.Buffers.ArrayPool<T> but only provides Shared.
|
||||||
|
|
||||||
|
internal sealed class ArrayPool<T>
|
||||||
|
{
|
||||||
|
// Same size as System.Buffers.DefaultArrayPool<T>
|
||||||
|
const int DefaultMaxNumberOfArraysPerBucket = 50;
|
||||||
|
|
||||||
|
static readonly T[] EmptyArray = new T[0];
|
||||||
|
|
||||||
|
public static readonly ArrayPool<T> Shared = new ArrayPool<T>();
|
||||||
|
|
||||||
|
readonly MinimumQueue<T[]>[] buckets;
|
||||||
|
readonly SpinLock[] locks;
|
||||||
|
|
||||||
|
ArrayPool()
|
||||||
|
{
|
||||||
|
// see: GetQueueIndex
|
||||||
|
buckets = new MinimumQueue<T[]>[18];
|
||||||
|
locks = new SpinLock[18];
|
||||||
|
for (int i = 0; i < buckets.Length; i++)
|
||||||
|
{
|
||||||
|
buckets[i] = new MinimumQueue<T[]>(4);
|
||||||
|
locks[i] = new SpinLock(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T[] Rent(int minimumLength)
|
||||||
|
{
|
||||||
|
if (minimumLength < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException("minimumLength");
|
||||||
|
}
|
||||||
|
else if (minimumLength == 0)
|
||||||
|
{
|
||||||
|
return EmptyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
var size = CalculateSize(minimumLength);
|
||||||
|
var index = GetQueueIndex(size);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
var q = buckets[index];
|
||||||
|
var lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
locks[index].Enter(ref lockTaken);
|
||||||
|
|
||||||
|
if (q.Count != 0)
|
||||||
|
{
|
||||||
|
return q.Dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken) locks[index].Exit(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new T[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Return(T[] array, bool clearArray = false)
|
||||||
|
{
|
||||||
|
if (array == null || array.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = GetQueueIndex(array.Length);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
if (clearArray)
|
||||||
|
{
|
||||||
|
Array.Clear(array, 0, array.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
var q = buckets[index];
|
||||||
|
var lockTaken = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
locks[index].Enter(ref lockTaken);
|
||||||
|
|
||||||
|
if (q.Count > DefaultMaxNumberOfArraysPerBucket)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
q.Enqueue(array);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken) locks[index].Exit(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CalculateSize(int size)
|
||||||
|
{
|
||||||
|
size--;
|
||||||
|
size |= size >> 1;
|
||||||
|
size |= size >> 2;
|
||||||
|
size |= size >> 4;
|
||||||
|
size |= size >> 8;
|
||||||
|
size |= size >> 16;
|
||||||
|
size += 1;
|
||||||
|
|
||||||
|
if (size < 8)
|
||||||
|
{
|
||||||
|
size = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetQueueIndex(int size)
|
||||||
|
{
|
||||||
|
switch (size)
|
||||||
|
{
|
||||||
|
case 8: return 0;
|
||||||
|
case 16: return 1;
|
||||||
|
case 32: return 2;
|
||||||
|
case 64: return 3;
|
||||||
|
case 128: return 4;
|
||||||
|
case 256: return 5;
|
||||||
|
case 512: return 6;
|
||||||
|
case 1024: return 7;
|
||||||
|
case 2048: return 8;
|
||||||
|
case 4096: return 9;
|
||||||
|
case 8192: return 10;
|
||||||
|
case 16384: return 11;
|
||||||
|
case 32768: return 12;
|
||||||
|
case 65536: return 13;
|
||||||
|
case 131072: return 14;
|
||||||
|
case 262144: return 15;
|
||||||
|
case 524288: return 16;
|
||||||
|
case 1048576: return 17; // max array length
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f83ebad81fb89fb4882331616ca6d248
|
||||||
|
timeCreated: 1532361008
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,112 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
internal static class ArrayPoolUtil
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static void EnsureCapacity<T>(ref T[] array, int index, ArrayPool<T> pool)
|
||||||
|
{
|
||||||
|
if (array.Length <= index)
|
||||||
|
{
|
||||||
|
EnsureCapacityCore(ref array, index, pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
static void EnsureCapacityCore<T>(ref T[] array, int index, ArrayPool<T> pool)
|
||||||
|
{
|
||||||
|
if (array.Length <= index)
|
||||||
|
{
|
||||||
|
var newSize = array.Length * 2;
|
||||||
|
var newArray = pool.Rent((index < newSize) ? newSize : (index * 2));
|
||||||
|
Array.Copy(array, 0, newArray, 0, array.Length);
|
||||||
|
|
||||||
|
pool.Return(array, clearArray: !RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType<T>());
|
||||||
|
|
||||||
|
array = newArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RentArray<T> Materialize<T>(IEnumerable<T> source)
|
||||||
|
{
|
||||||
|
if (source is T[] array)
|
||||||
|
{
|
||||||
|
return new RentArray<T>(array, array.Length, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultCount = 4;
|
||||||
|
if (source is ICollection<T> coll)
|
||||||
|
{
|
||||||
|
defaultCount = coll.Count;
|
||||||
|
var pool = ArrayPool<T>.Shared;
|
||||||
|
var buffer = pool.Rent(defaultCount);
|
||||||
|
coll.CopyTo(buffer, 0);
|
||||||
|
return new RentArray<T>(buffer, coll.Count, pool);
|
||||||
|
}
|
||||||
|
else if (source is IReadOnlyCollection<T> rcoll)
|
||||||
|
{
|
||||||
|
defaultCount = rcoll.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultCount == 0)
|
||||||
|
{
|
||||||
|
return new RentArray<T>(Array.Empty<T>(), 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var pool = ArrayPool<T>.Shared;
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
var buffer = pool.Rent(defaultCount);
|
||||||
|
foreach (var item in source)
|
||||||
|
{
|
||||||
|
EnsureCapacity(ref buffer, index, pool);
|
||||||
|
buffer[index++] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RentArray<T>(buffer, index, pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RentArray<T> : IDisposable
|
||||||
|
{
|
||||||
|
public readonly T[] Array;
|
||||||
|
public readonly int Length;
|
||||||
|
ArrayPool<T> pool;
|
||||||
|
|
||||||
|
public RentArray(T[] array, int length, ArrayPool<T> pool)
|
||||||
|
{
|
||||||
|
this.Array = array;
|
||||||
|
this.Length = length;
|
||||||
|
this.pool = pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
DisposeManually(!RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisposeManually(bool clearArray)
|
||||||
|
{
|
||||||
|
if (pool != null)
|
||||||
|
{
|
||||||
|
if (clearArray)
|
||||||
|
{
|
||||||
|
System.Array.Clear(Array, 0, Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.Return(Array, clearArray: false);
|
||||||
|
pool = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 424cc208fb61d4e448b08fcfa0eee25e
|
||||||
|
timeCreated: 1532361007
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,75 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
internal static class ArrayUtil
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void EnsureCapacity<T>(ref T[] array, int index)
|
||||||
|
{
|
||||||
|
if (array.Length <= index)
|
||||||
|
{
|
||||||
|
EnsureCore(ref array, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rare case, no inlining.
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
static void EnsureCore<T>(ref T[] array, int index)
|
||||||
|
{
|
||||||
|
var newSize = array.Length * 2;
|
||||||
|
var newArray = new T[(index < newSize) ? newSize : (index * 2)];
|
||||||
|
Array.Copy(array, 0, newArray, 0, array.Length);
|
||||||
|
|
||||||
|
array = newArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optimizing utility to avoid .ToArray() that creates buffer copy(cut to just size).
|
||||||
|
/// </summary>
|
||||||
|
public static (T[] array, int length) Materialize<T>(IEnumerable<T> source)
|
||||||
|
{
|
||||||
|
if (source is T[] array)
|
||||||
|
{
|
||||||
|
return (array, array.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultCount = 4;
|
||||||
|
if (source is ICollection<T> coll)
|
||||||
|
{
|
||||||
|
defaultCount = coll.Count;
|
||||||
|
var buffer = new T[defaultCount];
|
||||||
|
coll.CopyTo(buffer, 0);
|
||||||
|
return (buffer, defaultCount);
|
||||||
|
}
|
||||||
|
else if (source is IReadOnlyCollection<T> rcoll)
|
||||||
|
{
|
||||||
|
defaultCount = rcoll.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultCount == 0)
|
||||||
|
{
|
||||||
|
return (Array.Empty<T>(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var buffer = new T[defaultCount];
|
||||||
|
foreach (var item in source)
|
||||||
|
{
|
||||||
|
EnsureCapacity(ref buffer, index);
|
||||||
|
buffer[index++] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (buffer, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 23146a82ec99f2542a87971c8d3d7988
|
||||||
|
timeCreated: 1532361007
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,32 @@
|
||||||
|
#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 System.Threading;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
internal static class CancellationTokenHelper
|
||||||
|
{
|
||||||
|
public static bool TrySetOrLinkCancellationToken(ref CancellationToken field, CancellationToken newCancellationToken)
|
||||||
|
{
|
||||||
|
if (newCancellationToken == CancellationToken.None)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (field == CancellationToken.None)
|
||||||
|
{
|
||||||
|
field = newCancellationToken;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (field == newCancellationToken)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
field = CancellationTokenSource.CreateLinkedTokenSource(field, newCancellationToken).Token;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 53d1b536fc7e2d4458294ee2c7d9b743
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,115 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
internal class ContinuationQueue
|
||||||
|
{
|
||||||
|
const int MaxArrayLength = 0X7FEFFFFF;
|
||||||
|
const int InitialSize = 16;
|
||||||
|
|
||||||
|
SpinLock gate = new SpinLock();
|
||||||
|
bool dequing = false;
|
||||||
|
|
||||||
|
int actionListCount = 0;
|
||||||
|
Action[] actionList = new Action[InitialSize];
|
||||||
|
|
||||||
|
int waitingListCount = 0;
|
||||||
|
Action[] waitingList = new Action[InitialSize];
|
||||||
|
|
||||||
|
public void Enqueue(Action continuation)
|
||||||
|
{
|
||||||
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gate.Enter(ref lockTaken);
|
||||||
|
|
||||||
|
if (dequing)
|
||||||
|
{
|
||||||
|
// Ensure Capacity
|
||||||
|
if (waitingList.Length == waitingListCount)
|
||||||
|
{
|
||||||
|
var newLength = waitingListCount * 2;
|
||||||
|
if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
|
||||||
|
|
||||||
|
var newArray = new Action[newLength];
|
||||||
|
Array.Copy(waitingList, newArray, waitingListCount);
|
||||||
|
waitingList = newArray;
|
||||||
|
}
|
||||||
|
waitingList[waitingListCount] = continuation;
|
||||||
|
waitingListCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Ensure Capacity
|
||||||
|
if (actionList.Length == actionListCount)
|
||||||
|
{
|
||||||
|
var newLength = actionListCount * 2;
|
||||||
|
if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
|
||||||
|
|
||||||
|
var newArray = new Action[newLength];
|
||||||
|
Array.Copy(actionList, newArray, actionListCount);
|
||||||
|
actionList = newArray;
|
||||||
|
}
|
||||||
|
actionList[actionListCount] = continuation;
|
||||||
|
actionListCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken) gate.Exit(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gate.Enter(ref lockTaken);
|
||||||
|
if (actionListCount == 0) return;
|
||||||
|
dequing = true;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken) gate.Exit(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < actionListCount; i++)
|
||||||
|
{
|
||||||
|
var action = actionList[i];
|
||||||
|
actionList[i] = null;
|
||||||
|
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gate.Enter(ref lockTaken);
|
||||||
|
dequing = false;
|
||||||
|
|
||||||
|
var swapTempActionList = actionList;
|
||||||
|
|
||||||
|
actionListCount = waitingListCount;
|
||||||
|
actionList = waitingList;
|
||||||
|
|
||||||
|
waitingListCount = 0;
|
||||||
|
waitingList = swapTempActionList;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken) gate.Exit(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f66c32454e50f2546b17deadc80a4c77
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,63 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
internal static class Error
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void ThrowArgumentNullException<T>(T value, string paramName)
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
if (value == null) ThrowArgumentNullExceptionCore(paramName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
static void ThrowArgumentNullExceptionCore(string paramName)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(paramName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public static void ThrowArgumentException<T>(string message)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public static void ThrowNotYetCompleted()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Not yet completed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public static T ThrowNotYetCompleted<T>()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Not yet completed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void ThrowWhenContinuationIsAlreadyRegistered<T>(T continuationField)
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
if (continuationField != null) ThrowInvalidOperationExceptionCore("continuation is already registered.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
static void ThrowInvalidOperationExceptionCore(string message)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public static void ThrowOperationCanceledException()
|
||||||
|
{
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5f39f495294d4604b8082202faf98554
|
||||||
|
timeCreated: 1532361007
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,23 @@
|
||||||
|
#if NET_4_6 || NET_STANDARD_2_0 || CSHARP_7_OR_LATER
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
internal static class FuncExtensions
|
||||||
|
{
|
||||||
|
// avoid lambda capture
|
||||||
|
|
||||||
|
internal static Action<T> AsFuncOfT<T>(this Action action)
|
||||||
|
{
|
||||||
|
return new Action<T>(action.Invoke);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Invoke<T>(this Action action, T unused)
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4d5a9a3e1f0f069478969f752fde29a9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,130 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
internal sealed class LazyPromise : IAwaiter
|
||||||
|
{
|
||||||
|
Func<UniTask> factory;
|
||||||
|
UniTask value;
|
||||||
|
|
||||||
|
public LazyPromise(Func<UniTask> factory)
|
||||||
|
{
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Create()
|
||||||
|
{
|
||||||
|
var f = Interlocked.Exchange(ref factory, null);
|
||||||
|
if (f != null)
|
||||||
|
{
|
||||||
|
value = f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Create();
|
||||||
|
return value.IsCompleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus Status
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Create();
|
||||||
|
return value.Status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult()
|
||||||
|
{
|
||||||
|
Create();
|
||||||
|
value.GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IAwaiter.GetResult()
|
||||||
|
{
|
||||||
|
GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
Create();
|
||||||
|
value.GetAwaiter().UnsafeOnCompleted(continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
UnsafeOnCompleted(continuation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class LazyPromise<T> : IAwaiter<T>
|
||||||
|
{
|
||||||
|
Func<UniTask<T>> factory;
|
||||||
|
UniTask<T> value;
|
||||||
|
|
||||||
|
public LazyPromise(Func<UniTask<T>> factory)
|
||||||
|
{
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Create()
|
||||||
|
{
|
||||||
|
var f = Interlocked.Exchange(ref factory, null);
|
||||||
|
if (f != null)
|
||||||
|
{
|
||||||
|
value = f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Create();
|
||||||
|
return value.IsCompleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus Status
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Create();
|
||||||
|
return value.Status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetResult()
|
||||||
|
{
|
||||||
|
Create();
|
||||||
|
return value.Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IAwaiter.GetResult()
|
||||||
|
{
|
||||||
|
GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
Create();
|
||||||
|
value.GetAwaiter().UnsafeOnCompleted(continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
UnsafeOnCompleted(continuation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fe7a4187b7f89f84582fd1e466a7f27e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,122 @@
|
||||||
|
#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
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
// optimized version of Standard Queue<T>.
|
||||||
|
internal class MinimumQueue<T>
|
||||||
|
{
|
||||||
|
const int MinimumGrow = 4;
|
||||||
|
const int GrowFactor = 200;
|
||||||
|
|
||||||
|
T[] array;
|
||||||
|
int head;
|
||||||
|
int tail;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
public MinimumQueue(int capacity)
|
||||||
|
{
|
||||||
|
if (capacity < 0) throw new ArgumentOutOfRangeException("capacity");
|
||||||
|
array = new T[capacity];
|
||||||
|
head = tail = size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
#if NET_4_6 || NET_STANDARD_2_0
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#endif
|
||||||
|
get { return size; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Peek()
|
||||||
|
{
|
||||||
|
if (size == 0) ThrowForEmptyQueue();
|
||||||
|
return array[head];
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NET_4_6 || NET_STANDARD_2_0
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#endif
|
||||||
|
public void Enqueue(T item)
|
||||||
|
{
|
||||||
|
if (size == array.Length)
|
||||||
|
{
|
||||||
|
Grow();
|
||||||
|
}
|
||||||
|
|
||||||
|
array[tail] = item;
|
||||||
|
MoveNext(ref tail);
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NET_4_6 || NET_STANDARD_2_0
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#endif
|
||||||
|
public T Dequeue()
|
||||||
|
{
|
||||||
|
if (size == 0) ThrowForEmptyQueue();
|
||||||
|
|
||||||
|
int head = this.head;
|
||||||
|
T[] array = this.array;
|
||||||
|
T removed = array[head];
|
||||||
|
array[head] = default(T);
|
||||||
|
MoveNext(ref this.head);
|
||||||
|
size--;
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Grow()
|
||||||
|
{
|
||||||
|
int newcapacity = (int)((long)array.Length * (long)GrowFactor / 100);
|
||||||
|
if (newcapacity < array.Length + MinimumGrow)
|
||||||
|
{
|
||||||
|
newcapacity = array.Length + MinimumGrow;
|
||||||
|
}
|
||||||
|
SetCapacity(newcapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCapacity(int capacity)
|
||||||
|
{
|
||||||
|
T[] newarray = new T[capacity];
|
||||||
|
if (size > 0)
|
||||||
|
{
|
||||||
|
if (head < tail)
|
||||||
|
{
|
||||||
|
Array.Copy(array, head, newarray, 0, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Array.Copy(array, head, newarray, 0, array.Length - head);
|
||||||
|
Array.Copy(array, 0, newarray, array.Length - head, tail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array = newarray;
|
||||||
|
head = 0;
|
||||||
|
tail = (size == capacity) ? 0 : size;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NET_4_6 || NET_STANDARD_2_0
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#endif
|
||||||
|
void MoveNext(ref int index)
|
||||||
|
{
|
||||||
|
int tmp = index + 1;
|
||||||
|
if (tmp == array.Length)
|
||||||
|
{
|
||||||
|
tmp = 0;
|
||||||
|
}
|
||||||
|
index = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThrowForEmptyQueue()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("EmptyQueue");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7d63add489ccc99498114d79702b904d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,153 @@
|
||||||
|
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
internal sealed class PlayerLoopRunner
|
||||||
|
{
|
||||||
|
const int InitialSize = 16;
|
||||||
|
|
||||||
|
readonly object runningAndQueueLock = new object();
|
||||||
|
readonly object arrayLock = new object();
|
||||||
|
readonly Action<Exception> unhandledExceptionCallback;
|
||||||
|
|
||||||
|
int tail = 0;
|
||||||
|
bool running = false;
|
||||||
|
IPlayerLoopItem[] loopItems = new IPlayerLoopItem[InitialSize];
|
||||||
|
MinimumQueue<IPlayerLoopItem> waitQueue = new MinimumQueue<IPlayerLoopItem>(InitialSize);
|
||||||
|
|
||||||
|
public PlayerLoopRunner()
|
||||||
|
{
|
||||||
|
this.unhandledExceptionCallback = ex => Debug.LogException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddAction(IPlayerLoopItem item)
|
||||||
|
{
|
||||||
|
lock (runningAndQueueLock)
|
||||||
|
{
|
||||||
|
if (running)
|
||||||
|
{
|
||||||
|
waitQueue.Enqueue(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (arrayLock)
|
||||||
|
{
|
||||||
|
// Ensure Capacity
|
||||||
|
if (loopItems.Length == tail)
|
||||||
|
{
|
||||||
|
Array.Resize(ref loopItems, checked(tail * 2));
|
||||||
|
}
|
||||||
|
loopItems[tail++] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
lock (runningAndQueueLock)
|
||||||
|
{
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (arrayLock)
|
||||||
|
{
|
||||||
|
var j = tail - 1;
|
||||||
|
|
||||||
|
// eliminate array-bound check for i
|
||||||
|
for (int i = 0; i < loopItems.Length; i++)
|
||||||
|
{
|
||||||
|
var action = loopItems[i];
|
||||||
|
if (action != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!action.MoveNext())
|
||||||
|
{
|
||||||
|
loopItems[i] = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue; // next i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
loopItems[i] = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
unhandledExceptionCallback(ex);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find null, loop from tail
|
||||||
|
while (i < j)
|
||||||
|
{
|
||||||
|
var fromTail = loopItems[j];
|
||||||
|
if (fromTail != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!fromTail.MoveNext())
|
||||||
|
{
|
||||||
|
loopItems[j] = null;
|
||||||
|
j--;
|
||||||
|
continue; // next j
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// swap
|
||||||
|
loopItems[i] = fromTail;
|
||||||
|
loopItems[j] = null;
|
||||||
|
j--;
|
||||||
|
goto NEXT_LOOP; // next i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
loopItems[j] = null;
|
||||||
|
j--;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
unhandledExceptionCallback(ex);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
continue; // next j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tail = i; // loop end
|
||||||
|
break; // LOOP END
|
||||||
|
|
||||||
|
NEXT_LOOP:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
lock (runningAndQueueLock)
|
||||||
|
{
|
||||||
|
running = false;
|
||||||
|
while (waitQueue.Count != 0)
|
||||||
|
{
|
||||||
|
if (loopItems.Length == tail)
|
||||||
|
{
|
||||||
|
Array.Resize(ref loopItems, checked(tail * 2));
|
||||||
|
}
|
||||||
|
loopItems[tail++] = waitQueue.Dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 340c6d420bb4f484aa8683415ea92571
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,34 @@
|
||||||
|
#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 System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
internal static class PromiseHelper
|
||||||
|
{
|
||||||
|
internal static void TrySetResultAll<TPromise, T>(IEnumerable<TPromise> source, T value)
|
||||||
|
where TPromise : class, IResolvePromise<T>
|
||||||
|
{
|
||||||
|
var rentArray = ArrayPoolUtil.Materialize(source);
|
||||||
|
var clearArray = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var array = rentArray.Array;
|
||||||
|
var len = rentArray.Length;
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
array[i].TrySetResult(value);
|
||||||
|
array[i] = null;
|
||||||
|
}
|
||||||
|
clearArray = false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
rentArray.DisposeManually(clearArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 173f9b763911bf847b7dfbf31ee87fc4
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,395 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
// public for some types uses it.
|
||||||
|
|
||||||
|
public abstract class ReusablePromise : IAwaiter
|
||||||
|
{
|
||||||
|
ExceptionDispatchInfo exception;
|
||||||
|
object continuation; // Action or Queue<Action>
|
||||||
|
AwaiterStatus status;
|
||||||
|
|
||||||
|
public UniTask Task => new UniTask(this);
|
||||||
|
|
||||||
|
// can override for control 'start/reset' timing.
|
||||||
|
public virtual bool IsCompleted => status.IsCompleted();
|
||||||
|
|
||||||
|
public virtual void GetResult()
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case AwaiterStatus.Succeeded:
|
||||||
|
return;
|
||||||
|
case AwaiterStatus.Faulted:
|
||||||
|
exception.Throw();
|
||||||
|
break;
|
||||||
|
case AwaiterStatus.Canceled:
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("Invalid Status:" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus Status => status;
|
||||||
|
|
||||||
|
void IAwaiter.GetResult()
|
||||||
|
{
|
||||||
|
GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetStatus(bool forceReset)
|
||||||
|
{
|
||||||
|
if (forceReset)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Pending;
|
||||||
|
}
|
||||||
|
else if (status == AwaiterStatus.Succeeded)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Pending;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool TrySetCanceled()
|
||||||
|
{
|
||||||
|
if (status == AwaiterStatus.Pending)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Canceled;
|
||||||
|
TryInvokeContinuation();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool TrySetException(Exception ex)
|
||||||
|
{
|
||||||
|
if (status == AwaiterStatus.Pending)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Faulted;
|
||||||
|
exception = ExceptionDispatchInfo.Capture(ex);
|
||||||
|
TryInvokeContinuation();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool TrySetResult()
|
||||||
|
{
|
||||||
|
if (status == AwaiterStatus.Pending)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Succeeded;
|
||||||
|
TryInvokeContinuation();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TryInvokeContinuation()
|
||||||
|
{
|
||||||
|
if (continuation == null) return;
|
||||||
|
|
||||||
|
if (continuation is Action act)
|
||||||
|
{
|
||||||
|
continuation = null;
|
||||||
|
act();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// reuse Queue(don't null clear)
|
||||||
|
var q = (MinimumQueue<Action>)continuation;
|
||||||
|
var size = q.Count;
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
q.Dequeue().Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action action)
|
||||||
|
{
|
||||||
|
UnsafeOnCompleted(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action action)
|
||||||
|
{
|
||||||
|
if (continuation == null)
|
||||||
|
{
|
||||||
|
continuation = action;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (continuation is Action act)
|
||||||
|
{
|
||||||
|
var q = new MinimumQueue<Action>(4);
|
||||||
|
q.Enqueue(act);
|
||||||
|
q.Enqueue(action);
|
||||||
|
continuation = q;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
((MinimumQueue<Action>)continuation).Enqueue(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class ReusablePromise<T> : IAwaiter<T>
|
||||||
|
{
|
||||||
|
T result;
|
||||||
|
ExceptionDispatchInfo exception;
|
||||||
|
object continuation; // Action or Queue<Action>
|
||||||
|
AwaiterStatus status;
|
||||||
|
|
||||||
|
public UniTask<T> Task => new UniTask<T>(this);
|
||||||
|
|
||||||
|
// can override for control 'start/reset' timing.
|
||||||
|
public virtual bool IsCompleted => status.IsCompleted();
|
||||||
|
|
||||||
|
protected T RawResult => result;
|
||||||
|
|
||||||
|
protected void ForceSetResult(T result)
|
||||||
|
{
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual T GetResult()
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case AwaiterStatus.Succeeded:
|
||||||
|
return result;
|
||||||
|
case AwaiterStatus.Faulted:
|
||||||
|
exception.Throw();
|
||||||
|
break;
|
||||||
|
case AwaiterStatus.Canceled:
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("Invalid Status:" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AwaiterStatus Status => status;
|
||||||
|
|
||||||
|
void IAwaiter.GetResult()
|
||||||
|
{
|
||||||
|
GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetStatus(bool forceReset)
|
||||||
|
{
|
||||||
|
if (forceReset)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Pending;
|
||||||
|
}
|
||||||
|
else if (status == AwaiterStatus.Succeeded)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Pending;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool TrySetCanceled()
|
||||||
|
{
|
||||||
|
if (status == AwaiterStatus.Pending)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Canceled;
|
||||||
|
TryInvokeContinuation();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool TrySetException(Exception ex)
|
||||||
|
{
|
||||||
|
if (status == AwaiterStatus.Pending)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Faulted;
|
||||||
|
exception = ExceptionDispatchInfo.Capture(ex);
|
||||||
|
TryInvokeContinuation();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool TrySetResult(T result)
|
||||||
|
{
|
||||||
|
if (status == AwaiterStatus.Pending)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Succeeded;
|
||||||
|
this.result = result;
|
||||||
|
TryInvokeContinuation();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void TryInvokeContinuation()
|
||||||
|
{
|
||||||
|
if (continuation == null) return;
|
||||||
|
|
||||||
|
if (continuation is Action act)
|
||||||
|
{
|
||||||
|
continuation = null;
|
||||||
|
act();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// reuse Queue(don't null clear)
|
||||||
|
var q = (MinimumQueue<Action>)continuation;
|
||||||
|
var size = q.Count;
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
q.Dequeue().Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action action)
|
||||||
|
{
|
||||||
|
UnsafeOnCompleted(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action action)
|
||||||
|
{
|
||||||
|
if (continuation == null)
|
||||||
|
{
|
||||||
|
continuation = action;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (continuation is Action act)
|
||||||
|
{
|
||||||
|
var q = new MinimumQueue<Action>(4);
|
||||||
|
q.Enqueue(act);
|
||||||
|
q.Enqueue(action);
|
||||||
|
continuation = q;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
((MinimumQueue<Action>)continuation).Enqueue(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class PlayerLoopReusablePromiseBase : ReusablePromise, IPlayerLoopItem
|
||||||
|
{
|
||||||
|
readonly PlayerLoopTiming timing;
|
||||||
|
protected readonly CancellationToken cancellationToken;
|
||||||
|
bool isRunning = false;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
string capturedStackTraceForDebugging;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public PlayerLoopReusablePromiseBase(PlayerLoopTiming timing, CancellationToken cancellationToken, int skipTrackFrameCountAdditive)
|
||||||
|
{
|
||||||
|
this.timing = timing;
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
this.capturedStackTraceForDebugging = TaskTracker.CaptureStackTrace(skipTrackFrameCountAdditive + 1); // 1 is self,
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsCompleted
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Status == AwaiterStatus.Canceled || Status == AwaiterStatus.Faulted) return true;
|
||||||
|
|
||||||
|
if (!isRunning)
|
||||||
|
{
|
||||||
|
isRunning = true;
|
||||||
|
ResetStatus(false);
|
||||||
|
OnRunningStart();
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
TaskTracker.TrackActiveTask(this, capturedStackTraceForDebugging);
|
||||||
|
#endif
|
||||||
|
PlayerLoopHelper.AddAction(timing, this);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void OnRunningStart();
|
||||||
|
|
||||||
|
protected void Complete()
|
||||||
|
{
|
||||||
|
isRunning = false;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract bool MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class PlayerLoopReusablePromiseBase<T> : ReusablePromise<T>, IPlayerLoopItem
|
||||||
|
{
|
||||||
|
readonly PlayerLoopTiming timing;
|
||||||
|
protected readonly CancellationToken cancellationToken;
|
||||||
|
bool isRunning = false;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
string capturedStackTraceForDebugging;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public PlayerLoopReusablePromiseBase(PlayerLoopTiming timing, CancellationToken cancellationToken, int skipTrackFrameCountAdditive)
|
||||||
|
{
|
||||||
|
this.timing = timing;
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
this.capturedStackTraceForDebugging = TaskTracker.CaptureStackTrace(skipTrackFrameCountAdditive + 1); // 1 is self,
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsCompleted
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Status == AwaiterStatus.Canceled || Status == AwaiterStatus.Faulted) return true;
|
||||||
|
|
||||||
|
if (!isRunning)
|
||||||
|
{
|
||||||
|
isRunning = true;
|
||||||
|
ResetStatus(false);
|
||||||
|
OnRunningStart();
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
TaskTracker.TrackActiveTask(this, capturedStackTraceForDebugging);
|
||||||
|
#endif
|
||||||
|
PlayerLoopHelper.AddAction(timing, this);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void OnRunningStart();
|
||||||
|
|
||||||
|
protected void Complete()
|
||||||
|
{
|
||||||
|
isRunning = false;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract bool MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a8cfc99b5928c0242919aac2121b02bb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,60 @@
|
||||||
|
#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 System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
internal static class RuntimeHelpersAbstraction
|
||||||
|
{
|
||||||
|
// If we can use RuntimeHelpers.IsReferenceOrContainsReferences(.NET Core 2.0), use it.
|
||||||
|
public static bool IsWellKnownNoReferenceContainsType<T>()
|
||||||
|
{
|
||||||
|
return WellKnownNoReferenceContainsType<T>.IsWellKnownType;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool WellKnownNoReferenceContainsTypeInitialize(Type t)
|
||||||
|
{
|
||||||
|
// The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.
|
||||||
|
if (t.IsPrimitive) return true;
|
||||||
|
|
||||||
|
if (t.IsEnum) return true;
|
||||||
|
if (t == typeof(DateTime)) return true;
|
||||||
|
if (t == typeof(DateTimeOffset)) return true;
|
||||||
|
if (t == typeof(Guid)) return true;
|
||||||
|
if (t == typeof(decimal)) return true;
|
||||||
|
|
||||||
|
// unwrap nullable
|
||||||
|
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||||
|
{
|
||||||
|
return WellKnownNoReferenceContainsTypeInitialize(t.GetGenericArguments()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// or add other wellknown types(Vector, etc...) here
|
||||||
|
if (t == typeof(Vector2)) return true;
|
||||||
|
if (t == typeof(Vector3)) return true;
|
||||||
|
if (t == typeof(Vector4)) return true;
|
||||||
|
if (t == typeof(Color)) return true;
|
||||||
|
if (t == typeof(Rect)) return true;
|
||||||
|
if (t == typeof(Bounds)) return true;
|
||||||
|
if (t == typeof(Quaternion)) return true;
|
||||||
|
if (t == typeof(Vector2Int)) return true;
|
||||||
|
if (t == typeof(Vector3Int)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class WellKnownNoReferenceContainsType<T>
|
||||||
|
{
|
||||||
|
public static readonly bool IsWellKnownType;
|
||||||
|
|
||||||
|
static WellKnownNoReferenceContainsType()
|
||||||
|
{
|
||||||
|
IsWellKnownType = WellKnownNoReferenceContainsTypeInitialize(typeof(T));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 94975e4d4e0c0ea4ba787d3872ce9bb4
|
||||||
|
timeCreated: 1532361007
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,152 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
// public for add user custom.
|
||||||
|
|
||||||
|
public static class TaskTracker
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
|
||||||
|
static int trackingId = 0;
|
||||||
|
|
||||||
|
public const string EnableAutoReloadKey = "UniTaskTrackerWindow_EnableAutoReloadKey";
|
||||||
|
public const string EnableTrackingKey = "UniTaskTrackerWindow_EnableTrackingKey";
|
||||||
|
public const string EnableStackTraceKey = "UniTaskTrackerWindow_EnableStackTraceKey";
|
||||||
|
|
||||||
|
public static class EditorEnableState
|
||||||
|
{
|
||||||
|
static bool enableAutoReload;
|
||||||
|
public static bool EnableAutoReload
|
||||||
|
{
|
||||||
|
get { return enableAutoReload; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
enableAutoReload = value;
|
||||||
|
UnityEditor.EditorPrefs.SetBool(EnableAutoReloadKey, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool enableTracking;
|
||||||
|
public static bool EnableTracking
|
||||||
|
{
|
||||||
|
get { return enableTracking; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
enableTracking = value;
|
||||||
|
UnityEditor.EditorPrefs.SetBool(EnableTrackingKey, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool enableStackTrace;
|
||||||
|
public static bool EnableStackTrace
|
||||||
|
{
|
||||||
|
get { return enableStackTrace; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
enableStackTrace = value;
|
||||||
|
UnityEditor.EditorPrefs.SetBool(EnableStackTraceKey, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static List<KeyValuePair<IAwaiter, (int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IAwaiter, (int trackingId, DateTime addTime, string stackTrace)>>();
|
||||||
|
|
||||||
|
static readonly WeakDictionary<IAwaiter, (int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IAwaiter, (int trackingId, DateTime addTime, string stackTrace)>();
|
||||||
|
|
||||||
|
[Conditional("UNITY_EDITOR")]
|
||||||
|
public static void TrackActiveTask(IAwaiter task, int skipFrame = 1)
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
dirty = true;
|
||||||
|
if (!EditorEnableState.EnableTracking) return;
|
||||||
|
var stackTrace = EditorEnableState.EnableStackTrace ? new StackTrace(skipFrame, true).CleanupAsyncStackTrace() : "";
|
||||||
|
tracking.TryAdd(task, (Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("UNITY_EDITOR")]
|
||||||
|
public static void TrackActiveTask(IAwaiter task, string stackTrace)
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
dirty = true;
|
||||||
|
if (!EditorEnableState.EnableTracking) return;
|
||||||
|
var success = tracking.TryAdd(task, (Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CaptureStackTrace(int skipFrame)
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (!EditorEnableState.EnableTracking) return "";
|
||||||
|
var stackTrace = EditorEnableState.EnableStackTrace ? new StackTrace(skipFrame + 1, true).CleanupAsyncStackTrace() : "";
|
||||||
|
return stackTrace;
|
||||||
|
#else
|
||||||
|
return null;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("UNITY_EDITOR")]
|
||||||
|
public static void RemoveTracking(IAwaiter task)
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
dirty = true;
|
||||||
|
if (!EditorEnableState.EnableTracking) return;
|
||||||
|
var success = tracking.TryRemove(task);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool dirty;
|
||||||
|
|
||||||
|
public static bool CheckAndResetDirty()
|
||||||
|
{
|
||||||
|
var current = dirty;
|
||||||
|
dirty = false;
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>(trackingId, awaiterType, awaiterStatus, createdTime, stackTrace)</summary>
|
||||||
|
public static void ForEachActiveTask(Action<int, string, AwaiterStatus, DateTime, string> action)
|
||||||
|
{
|
||||||
|
lock (listPool)
|
||||||
|
{
|
||||||
|
var count = tracking.ToList(ref listPool, clear: false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
string typeName = null;
|
||||||
|
var keyType = listPool[i].Key.GetType();
|
||||||
|
if (keyType.IsNested)
|
||||||
|
{
|
||||||
|
typeName = keyType.DeclaringType.Name + "." + keyType.Name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
typeName = keyType.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
action(listPool[i].Value.trackingId, typeName, listPool[i].Key.Status, listPool[i].Value.addTime, listPool[i].Value.stackTrace);
|
||||||
|
listPool[i] = new KeyValuePair<IAwaiter, (int trackingId, DateTime addTime, string stackTrace)>(null, (0, default(DateTime), null)); // clear
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
listPool.Clear();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a203c73eb4ccdbb44bddfd82d38fdda9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,271 @@
|
||||||
|
#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
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
internal static class UnityEqualityComparer
|
||||||
|
{
|
||||||
|
public static readonly IEqualityComparer<Vector2> Vector2 = new Vector2EqualityComparer();
|
||||||
|
public static readonly IEqualityComparer<Vector3> Vector3 = new Vector3EqualityComparer();
|
||||||
|
public static readonly IEqualityComparer<Vector4> Vector4 = new Vector4EqualityComparer();
|
||||||
|
public static readonly IEqualityComparer<Color> Color = new ColorEqualityComparer();
|
||||||
|
public static readonly IEqualityComparer<Color32> Color32 = new Color32EqualityComparer();
|
||||||
|
public static readonly IEqualityComparer<Rect> Rect = new RectEqualityComparer();
|
||||||
|
public static readonly IEqualityComparer<Bounds> Bounds = new BoundsEqualityComparer();
|
||||||
|
public static readonly IEqualityComparer<Quaternion> Quaternion = new QuaternionEqualityComparer();
|
||||||
|
|
||||||
|
static readonly RuntimeTypeHandle vector2Type = typeof(Vector2).TypeHandle;
|
||||||
|
static readonly RuntimeTypeHandle vector3Type = typeof(Vector3).TypeHandle;
|
||||||
|
static readonly RuntimeTypeHandle vector4Type = typeof(Vector4).TypeHandle;
|
||||||
|
static readonly RuntimeTypeHandle colorType = typeof(Color).TypeHandle;
|
||||||
|
static readonly RuntimeTypeHandle color32Type = typeof(Color32).TypeHandle;
|
||||||
|
static readonly RuntimeTypeHandle rectType = typeof(Rect).TypeHandle;
|
||||||
|
static readonly RuntimeTypeHandle boundsType = typeof(Bounds).TypeHandle;
|
||||||
|
static readonly RuntimeTypeHandle quaternionType = typeof(Quaternion).TypeHandle;
|
||||||
|
|
||||||
|
#if UNITY_2017_2_OR_NEWER
|
||||||
|
|
||||||
|
public static readonly IEqualityComparer<Vector2Int> Vector2Int = new Vector2IntEqualityComparer();
|
||||||
|
public static readonly IEqualityComparer<Vector3Int> Vector3Int = new Vector3IntEqualityComparer();
|
||||||
|
public static readonly IEqualityComparer<RangeInt> RangeInt = new RangeIntEqualityComparer();
|
||||||
|
public static readonly IEqualityComparer<RectInt> RectInt = new RectIntEqualityComparer();
|
||||||
|
public static readonly IEqualityComparer<BoundsInt> BoundsInt = new BoundsIntEqualityComparer();
|
||||||
|
|
||||||
|
static readonly RuntimeTypeHandle vector2IntType = typeof(Vector2Int).TypeHandle;
|
||||||
|
static readonly RuntimeTypeHandle vector3IntType = typeof(Vector3Int).TypeHandle;
|
||||||
|
static readonly RuntimeTypeHandle rangeIntType = typeof(RangeInt).TypeHandle;
|
||||||
|
static readonly RuntimeTypeHandle rectIntType = typeof(RectInt).TypeHandle;
|
||||||
|
static readonly RuntimeTypeHandle boundsIntType = typeof(BoundsInt).TypeHandle;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static class Cache<T>
|
||||||
|
{
|
||||||
|
public static readonly IEqualityComparer<T> Comparer;
|
||||||
|
|
||||||
|
static Cache()
|
||||||
|
{
|
||||||
|
var comparer = GetDefaultHelper(typeof(T));
|
||||||
|
if (comparer == null)
|
||||||
|
{
|
||||||
|
Comparer = EqualityComparer<T>.Default;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Comparer = (IEqualityComparer<T>)comparer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEqualityComparer<T> GetDefault<T>()
|
||||||
|
{
|
||||||
|
return Cache<T>.Comparer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static object GetDefaultHelper(Type type)
|
||||||
|
{
|
||||||
|
var t = type.TypeHandle;
|
||||||
|
|
||||||
|
if (t.Equals(vector2Type)) return (object)UnityEqualityComparer.Vector2;
|
||||||
|
if (t.Equals(vector3Type)) return (object)UnityEqualityComparer.Vector3;
|
||||||
|
if (t.Equals(vector4Type)) return (object)UnityEqualityComparer.Vector4;
|
||||||
|
if (t.Equals(colorType)) return (object)UnityEqualityComparer.Color;
|
||||||
|
if (t.Equals(color32Type)) return (object)UnityEqualityComparer.Color32;
|
||||||
|
if (t.Equals(rectType)) return (object)UnityEqualityComparer.Rect;
|
||||||
|
if (t.Equals(boundsType)) return (object)UnityEqualityComparer.Bounds;
|
||||||
|
if (t.Equals(quaternionType)) return (object)UnityEqualityComparer.Quaternion;
|
||||||
|
|
||||||
|
#if UNITY_2017_2_OR_NEWER
|
||||||
|
|
||||||
|
if (t.Equals(vector2IntType)) return (object)UnityEqualityComparer.Vector2Int;
|
||||||
|
if (t.Equals(vector3IntType)) return (object)UnityEqualityComparer.Vector3Int;
|
||||||
|
if (t.Equals(rangeIntType)) return (object)UnityEqualityComparer.RangeInt;
|
||||||
|
if (t.Equals(rectIntType)) return (object)UnityEqualityComparer.RectInt;
|
||||||
|
if (t.Equals(boundsIntType)) return (object)UnityEqualityComparer.BoundsInt;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Vector2EqualityComparer : IEqualityComparer<Vector2>
|
||||||
|
{
|
||||||
|
public bool Equals(Vector2 self, Vector2 vector)
|
||||||
|
{
|
||||||
|
return self.x.Equals(vector.x) && self.y.Equals(vector.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(Vector2 obj)
|
||||||
|
{
|
||||||
|
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Vector3EqualityComparer : IEqualityComparer<Vector3>
|
||||||
|
{
|
||||||
|
public bool Equals(Vector3 self, Vector3 vector)
|
||||||
|
{
|
||||||
|
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(Vector3 obj)
|
||||||
|
{
|
||||||
|
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Vector4EqualityComparer : IEqualityComparer<Vector4>
|
||||||
|
{
|
||||||
|
public bool Equals(Vector4 self, Vector4 vector)
|
||||||
|
{
|
||||||
|
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z) && self.w.Equals(vector.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(Vector4 obj)
|
||||||
|
{
|
||||||
|
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2 ^ obj.w.GetHashCode() >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class ColorEqualityComparer : IEqualityComparer<Color>
|
||||||
|
{
|
||||||
|
public bool Equals(Color self, Color other)
|
||||||
|
{
|
||||||
|
return self.r.Equals(other.r) && self.g.Equals(other.g) && self.b.Equals(other.b) && self.a.Equals(other.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(Color obj)
|
||||||
|
{
|
||||||
|
return obj.r.GetHashCode() ^ obj.g.GetHashCode() << 2 ^ obj.b.GetHashCode() >> 2 ^ obj.a.GetHashCode() >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class RectEqualityComparer : IEqualityComparer<Rect>
|
||||||
|
{
|
||||||
|
public bool Equals(Rect self, Rect other)
|
||||||
|
{
|
||||||
|
return self.x.Equals(other.x) && self.width.Equals(other.width) && self.y.Equals(other.y) && self.height.Equals(other.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(Rect obj)
|
||||||
|
{
|
||||||
|
return obj.x.GetHashCode() ^ obj.width.GetHashCode() << 2 ^ obj.y.GetHashCode() >> 2 ^ obj.height.GetHashCode() >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class BoundsEqualityComparer : IEqualityComparer<Bounds>
|
||||||
|
{
|
||||||
|
public bool Equals(Bounds self, Bounds vector)
|
||||||
|
{
|
||||||
|
return self.center.Equals(vector.center) && self.extents.Equals(vector.extents);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(Bounds obj)
|
||||||
|
{
|
||||||
|
return obj.center.GetHashCode() ^ obj.extents.GetHashCode() << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class QuaternionEqualityComparer : IEqualityComparer<Quaternion>
|
||||||
|
{
|
||||||
|
public bool Equals(Quaternion self, Quaternion vector)
|
||||||
|
{
|
||||||
|
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z) && self.w.Equals(vector.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(Quaternion obj)
|
||||||
|
{
|
||||||
|
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2 ^ obj.w.GetHashCode() >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Color32EqualityComparer : IEqualityComparer<Color32>
|
||||||
|
{
|
||||||
|
public bool Equals(Color32 self, Color32 vector)
|
||||||
|
{
|
||||||
|
return self.a.Equals(vector.a) && self.r.Equals(vector.r) && self.g.Equals(vector.g) && self.b.Equals(vector.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(Color32 obj)
|
||||||
|
{
|
||||||
|
return obj.a.GetHashCode() ^ obj.r.GetHashCode() << 2 ^ obj.g.GetHashCode() >> 2 ^ obj.b.GetHashCode() >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_2017_2_OR_NEWER
|
||||||
|
|
||||||
|
sealed class Vector2IntEqualityComparer : IEqualityComparer<Vector2Int>
|
||||||
|
{
|
||||||
|
public bool Equals(Vector2Int self, Vector2Int vector)
|
||||||
|
{
|
||||||
|
return self.x.Equals(vector.x) && self.y.Equals(vector.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(Vector2Int obj)
|
||||||
|
{
|
||||||
|
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Vector3IntEqualityComparer : IEqualityComparer<Vector3Int>
|
||||||
|
{
|
||||||
|
public static readonly Vector3IntEqualityComparer Default = new Vector3IntEqualityComparer();
|
||||||
|
|
||||||
|
public bool Equals(Vector3Int self, Vector3Int vector)
|
||||||
|
{
|
||||||
|
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(Vector3Int obj)
|
||||||
|
{
|
||||||
|
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class RangeIntEqualityComparer : IEqualityComparer<RangeInt>
|
||||||
|
{
|
||||||
|
public bool Equals(RangeInt self, RangeInt vector)
|
||||||
|
{
|
||||||
|
return self.start.Equals(vector.start) && self.length.Equals(vector.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(RangeInt obj)
|
||||||
|
{
|
||||||
|
return obj.start.GetHashCode() ^ obj.length.GetHashCode() << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class RectIntEqualityComparer : IEqualityComparer<RectInt>
|
||||||
|
{
|
||||||
|
public bool Equals(RectInt self, RectInt other)
|
||||||
|
{
|
||||||
|
return self.x.Equals(other.x) && self.width.Equals(other.width) && self.y.Equals(other.y) && self.height.Equals(other.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(RectInt obj)
|
||||||
|
{
|
||||||
|
return obj.x.GetHashCode() ^ obj.width.GetHashCode() << 2 ^ obj.y.GetHashCode() >> 2 ^ obj.height.GetHashCode() >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class BoundsIntEqualityComparer : IEqualityComparer<BoundsInt>
|
||||||
|
{
|
||||||
|
public bool Equals(BoundsInt self, BoundsInt vector)
|
||||||
|
{
|
||||||
|
return Vector3IntEqualityComparer.Default.Equals(self.position, vector.position)
|
||||||
|
&& Vector3IntEqualityComparer.Default.Equals(self.size, vector.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(BoundsInt obj)
|
||||||
|
{
|
||||||
|
return Vector3IntEqualityComparer.Default.GetHashCode(obj.position) ^ Vector3IntEqualityComparer.Default.GetHashCode(obj.size) << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ebaaf14253c9cfb47b23283218ff9b67
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,336 @@
|
||||||
|
#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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniRx.Async.Internal
|
||||||
|
{
|
||||||
|
// Add, Remove, Enumerate with sweep. All operations are thread safe(in spinlock).
|
||||||
|
internal class WeakDictionary<TKey, TValue>
|
||||||
|
where TKey : class
|
||||||
|
{
|
||||||
|
Entry[] buckets;
|
||||||
|
int size;
|
||||||
|
SpinLock gate; // mutable struct(not readonly)
|
||||||
|
|
||||||
|
readonly float loadFactor;
|
||||||
|
readonly IEqualityComparer<TKey> keyEqualityComparer;
|
||||||
|
|
||||||
|
public WeakDictionary(int capacity = 4, float loadFactor = 0.75f, IEqualityComparer<TKey> keyComparer = null)
|
||||||
|
{
|
||||||
|
var tableSize = CalculateCapacity(capacity, loadFactor);
|
||||||
|
this.buckets = new Entry[tableSize];
|
||||||
|
this.loadFactor = loadFactor;
|
||||||
|
this.gate = new SpinLock(false);
|
||||||
|
this.keyEqualityComparer = keyComparer ?? EqualityComparer<TKey>.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryAdd(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gate.Enter(ref lockTaken);
|
||||||
|
return TryAddInternal(key, value);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken) gate.Exit(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(TKey key, out TValue value)
|
||||||
|
{
|
||||||
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gate.Enter(ref lockTaken);
|
||||||
|
if (TryGetEntry(key, out _, out var entry))
|
||||||
|
{
|
||||||
|
value = entry.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = default(TValue);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken) gate.Exit(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryRemove(TKey key)
|
||||||
|
{
|
||||||
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gate.Enter(ref lockTaken);
|
||||||
|
if (TryGetEntry(key, out var hashIndex, out var entry))
|
||||||
|
{
|
||||||
|
Remove(hashIndex, entry);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken) gate.Exit(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryAddInternal(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
var nextCapacity = CalculateCapacity(size + 1, loadFactor);
|
||||||
|
|
||||||
|
TRY_ADD_AGAIN:
|
||||||
|
if (buckets.Length < nextCapacity)
|
||||||
|
{
|
||||||
|
// rehash
|
||||||
|
var nextBucket = new Entry[nextCapacity];
|
||||||
|
for (int i = 0; i < buckets.Length; i++)
|
||||||
|
{
|
||||||
|
var e = buckets[i];
|
||||||
|
while (e != null)
|
||||||
|
{
|
||||||
|
AddToBuckets(nextBucket, key, e.Value, e.Hash);
|
||||||
|
e = e.Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buckets = nextBucket;
|
||||||
|
goto TRY_ADD_AGAIN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// add entry
|
||||||
|
var successAdd = AddToBuckets(buckets, key, value, keyEqualityComparer.GetHashCode(key));
|
||||||
|
if (successAdd) size++;
|
||||||
|
return successAdd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddToBuckets(Entry[] targetBuckets, TKey newKey, TValue value, int keyHash)
|
||||||
|
{
|
||||||
|
var h = keyHash;
|
||||||
|
var hashIndex = h & (targetBuckets.Length - 1);
|
||||||
|
|
||||||
|
TRY_ADD_AGAIN:
|
||||||
|
if (targetBuckets[hashIndex] == null)
|
||||||
|
{
|
||||||
|
targetBuckets[hashIndex] = new Entry
|
||||||
|
{
|
||||||
|
Key = new WeakReference<TKey>(newKey, false),
|
||||||
|
Value = value,
|
||||||
|
Hash = h
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// add to last.
|
||||||
|
var entry = targetBuckets[hashIndex];
|
||||||
|
while (entry != null)
|
||||||
|
{
|
||||||
|
if (entry.Key.TryGetTarget(out var target))
|
||||||
|
{
|
||||||
|
if (keyEqualityComparer.Equals(newKey, target))
|
||||||
|
{
|
||||||
|
return false; // duplicate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Remove(hashIndex, entry);
|
||||||
|
if (targetBuckets[hashIndex] == null) goto TRY_ADD_AGAIN; // add new entry
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.Next != null)
|
||||||
|
{
|
||||||
|
entry = entry.Next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// found last
|
||||||
|
entry.Next = new Entry
|
||||||
|
{
|
||||||
|
Key = new WeakReference<TKey>(newKey, false),
|
||||||
|
Value = value,
|
||||||
|
Hash = h
|
||||||
|
};
|
||||||
|
entry.Next.Prev = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryGetEntry(TKey key, out int hashIndex, out Entry entry)
|
||||||
|
{
|
||||||
|
var table = buckets;
|
||||||
|
var hash = keyEqualityComparer.GetHashCode(key);
|
||||||
|
hashIndex = hash & table.Length - 1;
|
||||||
|
entry = table[hashIndex];
|
||||||
|
|
||||||
|
while (entry != null)
|
||||||
|
{
|
||||||
|
if (entry.Key.TryGetTarget(out var target))
|
||||||
|
{
|
||||||
|
if (keyEqualityComparer.Equals(key, target))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// sweap
|
||||||
|
Remove(hashIndex, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = entry.Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Remove(int hashIndex, Entry entry)
|
||||||
|
{
|
||||||
|
if (entry.Prev == null && entry.Next == null)
|
||||||
|
{
|
||||||
|
buckets[hashIndex] = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (entry.Prev == null)
|
||||||
|
{
|
||||||
|
buckets[hashIndex] = entry.Next;
|
||||||
|
}
|
||||||
|
if (entry.Prev != null)
|
||||||
|
{
|
||||||
|
entry.Prev.Next = entry.Next;
|
||||||
|
}
|
||||||
|
if (entry.Next != null)
|
||||||
|
{
|
||||||
|
entry.Next.Prev = entry.Prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KeyValuePair<TKey, TValue>> ToList()
|
||||||
|
{
|
||||||
|
var list = new List<KeyValuePair<TKey, TValue>>(size);
|
||||||
|
ToList(ref list, false);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid allocate everytime.
|
||||||
|
public int ToList(ref List<KeyValuePair<TKey, TValue>> list, bool clear = true)
|
||||||
|
{
|
||||||
|
if (clear)
|
||||||
|
{
|
||||||
|
list.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
var listIndex = 0;
|
||||||
|
|
||||||
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < buckets.Length; i++)
|
||||||
|
{
|
||||||
|
var entry = buckets[i];
|
||||||
|
while (entry != null)
|
||||||
|
{
|
||||||
|
if (entry.Key.TryGetTarget(out var target))
|
||||||
|
{
|
||||||
|
var item = new KeyValuePair<TKey, TValue>(target, entry.Value);
|
||||||
|
if (listIndex < list.Count)
|
||||||
|
{
|
||||||
|
list[listIndex++] = item;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list.Add(item);
|
||||||
|
listIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// sweap
|
||||||
|
Remove(i, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = entry.Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken) gate.Exit(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return listIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CalculateCapacity(int collectionSize, float loadFactor)
|
||||||
|
{
|
||||||
|
var size = (int)(((float)collectionSize) / loadFactor);
|
||||||
|
|
||||||
|
size--;
|
||||||
|
size |= size >> 1;
|
||||||
|
size |= size >> 2;
|
||||||
|
size |= size >> 4;
|
||||||
|
size |= size >> 8;
|
||||||
|
size |= size >> 16;
|
||||||
|
size += 1;
|
||||||
|
|
||||||
|
if (size < 8)
|
||||||
|
{
|
||||||
|
size = 8;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Entry
|
||||||
|
{
|
||||||
|
public WeakReference<TKey> Key;
|
||||||
|
public TValue Value;
|
||||||
|
public int Hash;
|
||||||
|
public Entry Prev;
|
||||||
|
public Entry Next;
|
||||||
|
|
||||||
|
// debug only
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (Key.TryGetTarget(out var target))
|
||||||
|
{
|
||||||
|
return target + "(" + Count() + ")";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "(Dead)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Count()
|
||||||
|
{
|
||||||
|
var count = 1;
|
||||||
|
var n = this;
|
||||||
|
while (n.Next != null)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
n = n.Next;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue