diff --git a/.gitignore b/.gitignore index 9de9409..174b65e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,143 @@ -/[Ll]ibrary/ -/[Tt]emp/ -/[Oo]bj/ -/[Bb]uild/ +# Unity -# Autogenerated VS/MD solution and project files -*.csproj -*.unityproj -*.sln -*.suo -*.tmp -*.user -*.userprefs *.pidb -*.booproj +*.suo +*.userprefs +*.vsmdi +*.testsettings +*/bin +*/obj +*/publish +$tf +TestResults +!*.sln +!*.csproj +!*/*.csproj +[Ll]ibrary/ +[Tt]emp/ +[Oo]bj/ -# Unity3D generated meta files -*.pidb.meta +# VS2013 -# Unity3D Generated File On Crash Reports -sysinfo.txt +# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) +[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 diff --git a/Assets/RuntimeUnitTestToolkit.meta b/Assets/RuntimeUnitTestToolkit.meta new file mode 100644 index 0000000..aa095c6 --- /dev/null +++ b/Assets/RuntimeUnitTestToolkit.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 894f21dfce4e82343a91661e1ec1a455 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RuntimeUnitTestToolkit/Editor.meta b/Assets/RuntimeUnitTestToolkit/Editor.meta new file mode 100644 index 0000000..903f676 --- /dev/null +++ b/Assets/RuntimeUnitTestToolkit/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 959c1472a5d812843bedf9341e87af3b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs b/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs new file mode 100644 index 0000000..9a2adc2 --- /dev/null +++ b/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs @@ -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(); + } + + internal class Builder : 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(); } } + + public Action 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(); + } + + public Builder(string name, out T referenceSelf) // out primary reference. + { + this.GameObject = new GameObject(name); + this.Component1 = GameObject.AddComponent(); + referenceSelf = this.Component1; + } + + public TComponent GetComponent() + { + return this.GameObject.GetComponent(); + } + } + + internal class Builder : Builder + where T1 : Component + where T2 : Component + { + public T2 Component2 { get; private set; } + + public Builder(string name) + : base(name) + { + this.Component2 = GameObject.AddComponent(); + } + + public Builder(string name, out T1 referenceSelf) + : base(name, out referenceSelf) + { + this.Component2 = GameObject.AddComponent(); + } + } + + internal class Builder : Builder + 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(); + } + + public Builder(string name, out T1 referenceSelf) + : base(name, out referenceSelf) + { + this.Component3 = GameObject.AddComponent(); + } + } + + internal class Builder : Builder + 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(); + } + + public Builder(string name, out T1 referenceSelf) + : base(name, out referenceSelf) + { + this.Component4 = GameObject.AddComponent(); + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs.meta b/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs.meta new file mode 100644 index 0000000..82c8e9b --- /dev/null +++ b/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8760bbbab905a534eb6fb7b61b736926 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs b/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs new file mode 100644 index 0000000..9ec1c54 --- /dev/null +++ b/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs @@ -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 diff --git a/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs.meta b/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs.meta new file mode 100644 index 0000000..7c99172 --- /dev/null +++ b/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12bdad0556e999f4aa82da29415d361f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs b/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs new file mode 100644 index 0000000..8ca1068 --- /dev/null +++ b/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs @@ -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(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(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(kStandardSpritePath); + var background = AssetDatabase.GetBuiltinExtraResource(kBackgroundSpritePath); + + ScrollRect buttonList; + VerticalLayoutGroup listLayout; + Scrollbar refListScrollbar; + ScrollRect logList; + Scrollbar refLogScrollbar; + Button clearButton; + Text logText; + + // Flutter like coded build utility + + var rootObject = new Builder("SceneRoot") + { + Children = new IBuilder[] { + new Builder("EventSystem"), + new Builder("Canvas") { + Component1 = { renderMode = RenderMode.ScreenSpaceOverlay }, + Children = new IBuilder[] { + new Builder("HorizontalSplitter") { + RectTransform = { anchorMin = new Vector2(0, 0), anchorMax = new Vector2(1, 1) }, + Component1 = { childControlWidth = true, childControlHeight = true, spacing = 10 }, + Children = new IBuilder[] { + new Builder("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("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(); }, + Child = new Builder("ClearButton", out clearButton) { + Component2 = { sprite = uisprite, type = Image.Type.Sliced }, + Component3 = { minHeight = 50 }, + SetTarget = self => { self.GetComponent