#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