diff --git a/Scripts/Runtime/Assets/BuildingSettings.cs b/Scripts/Runtime/Assets/BuildingSettings.cs index e65f796..22250b0 100644 --- a/Scripts/Runtime/Assets/BuildingSettings.cs +++ b/Scripts/Runtime/Assets/BuildingSettings.cs @@ -43,6 +43,9 @@ public static BuildingSettings Singleton [SerializeField] private bool showFolder = true; + [SerializeField] + private bool runTests = true; + [FormerlySerializedAs("targetName")] [SerializeField] private string appName; @@ -86,6 +89,12 @@ public bool ShowFolder internal set => showFolder = value; } + public bool RunTests + { + get => runTests; + internal set => runTests = value; + } + #endregion #region Builtin Methods diff --git a/Scripts/Runtime/Provider/BuildingToolbar.cs b/Scripts/Runtime/Provider/BuildingToolbar.cs index 52489cc..30c7eaa 100644 --- a/Scripts/Runtime/Provider/BuildingToolbar.cs +++ b/Scripts/Runtime/Provider/BuildingToolbar.cs @@ -57,12 +57,12 @@ static void OnToolbarGUI() GUILayout.Space(5f); - BuildingSettings.Clean = GUILayout.Toggle(BuildingSettings.Clean, new GUIContent("Clean", "Clean complete build cache"), ToolbarStyles.toggleStyle); - + BuildingSettings.Clean = GUILayout.Toggle(BuildingSettings.Clean, new GUIContent(EditorGUIUtility.IconContent("Grid.EraserTool").image, "Clean complete build cache"), ToolbarStyles.commandButtonStyle); + BuildingSettings.ShowFolder = GUILayout.Toggle(BuildingSettings.ShowFolder, new GUIContent(EditorGUIUtility.IconContent("d_FolderOpened Icon").image, "Open the build folder"), ToolbarStyles.commandButtonStyle); + BuildingSettings.RunTests = GUILayout.Toggle(BuildingSettings.RunTests, new GUIContent(EditorGUIUtility.IconContent("FilterSelectedOnly").image, "Run tests before build starts"), ToolbarStyles.commandButtonStyle); + GUILayout.Space(5f); - BuildingSettings.ShowFolder = GUILayout.Toggle(BuildingSettings.ShowFolder, new GUIContent("Show Folder", "Open the build folder"), ToolbarStyles.toggleStyle); - if (GUILayout.Button(new GUIContent("", (Texture2D)EditorGUIUtility.IconContent("d_Settings").image, "Build the project"), ToolbarStyles.commandButtonStyle)) { BuildMenu.ShowAsContext(); diff --git a/Scripts/Runtime/Utils/UnityBuilding.cs b/Scripts/Runtime/Utils/UnityBuilding.cs index a33608e..40938d7 100644 --- a/Scripts/Runtime/Utils/UnityBuilding.cs +++ b/Scripts/Runtime/Utils/UnityBuilding.cs @@ -3,7 +3,6 @@ using System.Linq; using UnityBuildTooling.Editor.build_tooling.Scripts.Runtime.Assets; using UnityEditor; -using UnityEditor.Build; using UnityEditor.Build.Reporting; namespace UnityBuildTooling.Editor.build_tooling.Scripts.Runtime.Utils @@ -13,69 +12,94 @@ internal static class UnityBuilding private const string TargetKey = "${TARGET}"; internal const string DefaultTargetPath = "Builds/" + TargetKey; - public static void Build(BuildingGroup @group) + public static void Build(BuildingGroup @group, bool runTest = true) { - foreach (var item in group.Items) + if (runTest && BuildingSettings.Singleton.RunTests) { - Build(BuildBehavior.BuildOnly, item); + UnityTesting.RunTests(@group); + } + else + { + RunBuild(); + } + + void RunBuild() + { + foreach (var item in @group.Items) + { + Build(BuildBehavior.BuildOnly, item, false); + } } } - public static void Build(BuildBehavior behavior, BuildingData overwriteData = null) + public static void Build(BuildBehavior behavior, BuildingData overwriteData = null, bool runTest = true) { var buildingSettings = BuildingSettings.Singleton; - var buildingData = overwriteData ?? buildingSettings.BuildingData; - var buildingType = buildingSettings.TypeItems[buildingData.BuildType]; - var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildingData.BuildTarget); - var cppCompilerConfiguration = CalculateConfiguration(buildingType); - if (cppCompilerConfiguration.HasValue) + if (runTest && buildingSettings.RunTests) { - PlayerSettings.SetScriptingBackend(buildTargetGroup, ScriptingImplementation.IL2CPP); - PlayerSettings.SetIl2CppCompilerConfiguration(buildTargetGroup, cppCompilerConfiguration.Value); - PlayerSettings.SetIncrementalIl2CppBuild(buildTargetGroup, buildingType.CppIncrementalBuild); -#if UNITY_2021_2_OR_NEWER - EditorUserBuildSettings.il2CppCodeGeneration = buildingType.CppCodeGeneration; -#endif + UnityTesting.RunTests(behavior, overwriteData); } else { - PlayerSettings.SetScriptingBackend(buildTargetGroup, ScriptingImplementation.Mono2x); + RunBuild(); } - PlayerSettings.Android.targetArchitectures = buildingType.AndroidArchitecture; + void RunBuild() + { + var buildingData = overwriteData ?? buildingSettings.BuildingData; + var buildingType = buildingSettings.TypeItems[buildingData.BuildType]; + + var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildingData.BuildTarget); + var cppCompilerConfiguration = CalculateConfiguration(buildingType); + if (cppCompilerConfiguration.HasValue) + { + PlayerSettings.SetScriptingBackend(buildTargetGroup, ScriptingImplementation.IL2CPP); + PlayerSettings.SetIl2CppCompilerConfiguration(buildTargetGroup, cppCompilerConfiguration.Value); + PlayerSettings.SetIncrementalIl2CppBuild(buildTargetGroup, buildingType.CppIncrementalBuild); +#if UNITY_2021_2_OR_NEWER + EditorUserBuildSettings.il2CppCodeGeneration = buildingType.CppCodeGeneration; +#endif + } + else + { + PlayerSettings.SetScriptingBackend(buildTargetGroup, ScriptingImplementation.Mono2x); + } - EditorUserBuildSettings.buildAppBundle = buildingType.BuildAppBundle; + PlayerSettings.Android.targetArchitectures = buildingType.AndroidArchitecture; - var targetPath = DefaultTargetPath.Replace(TargetKey, buildingData.BuildTarget.ToString()) + "/" + buildingType.TargetPath; - var appName = buildingSettings.AppName + GetExtension(buildingData.BuildTarget); - var options = new BuildPlayerOptions - { - scenes = KnownScenes, - target = buildingData.BuildTarget, - locationPathName = targetPath + "/" + appName, - options = CalculateOptions(buildingType, buildingData.BuildExtras, behavior, buildingSettings.Clean, buildingSettings.ShowFolder), - extraScriptingDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup).Split(',').Concat(buildingType.Defines).Where(x => !string.IsNullOrWhiteSpace(x)).ToArray() - }; + EditorUserBuildSettings.buildAppBundle = buildingType.BuildAppBundle; - if (buildingSettings.Clean && Directory.Exists(targetPath)) - { - Directory.Delete(targetPath, true); - } + var targetPath = DefaultTargetPath.Replace(TargetKey, buildingData.BuildTarget.ToString()) + "/" + buildingType.TargetPath; + var appName = buildingSettings.AppName + GetExtension(buildingData.BuildTarget); + var options = new BuildPlayerOptions + { + scenes = KnownScenes, + target = buildingData.BuildTarget, + locationPathName = targetPath + "/" + appName, + options = CalculateOptions(buildingType, buildingData.BuildExtras, behavior, buildingSettings.Clean, buildingSettings.ShowFolder), + extraScriptingDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup).Split(',').Concat(buildingType.Defines).Where(x => !string.IsNullOrWhiteSpace(x)).ToArray() + }; + + if (buildingSettings.Clean && Directory.Exists(targetPath)) + { + Directory.Delete(targetPath, true); + } - var oldBuildTarget = EditorUserBuildSettings.activeBuildTarget; - var oldBuildGroup = EditorUserBuildSettings.selectedBuildTargetGroup; - try - { - var buildReport = BuildPipeline.BuildPlayer(options); - if (buildReport.summary.result != BuildResult.Succeeded) + var oldBuildTarget = EditorUserBuildSettings.activeBuildTarget; + var oldBuildGroup = EditorUserBuildSettings.selectedBuildTargetGroup; + try { - EditorUtility.DisplayDialog("Build", "Build has failed", "OK"); + var buildReport = BuildPipeline.BuildPlayer(options); + if (buildReport.summary.result != BuildResult.Succeeded) + { + EditorUtility.DisplayDialog("Build", "Build has failed", "OK"); + } + } + finally + { + EditorUserBuildSettings.SwitchActiveBuildTarget(oldBuildGroup, oldBuildTarget); } - } - finally - { - EditorUserBuildSettings.SwitchActiveBuildTarget(oldBuildGroup, oldBuildTarget); } } diff --git a/Scripts/Runtime/Utils/UnityTesting.cs b/Scripts/Runtime/Utils/UnityTesting.cs new file mode 100644 index 0000000..feba3e0 --- /dev/null +++ b/Scripts/Runtime/Utils/UnityTesting.cs @@ -0,0 +1,174 @@ +using System; +using System.IO; +using UnityBuildTooling.Editor.build_tooling.Scripts.Runtime.Assets; +using UnityEditor; +using UnityEditor.TestTools.TestRunner.Api; +using UnityEngine; + +namespace UnityBuildTooling.Editor.build_tooling.Scripts.Runtime.Utils +{ + [InitializeOnLoad] + internal static class UnityTesting + { + private const string GroupFileName = "group.dat"; + private const string BaseFileName = "base.dat"; + + private static TestRunnerApi _testRunnerApi; + private static readonly CallbackHandler _callbackHandler = new CallbackHandler(); + + static UnityTesting() + { + InitTest(); + } + + public static void InitTest() + { + if (_testRunnerApi == null) + { + _testRunnerApi = ScriptableObject.CreateInstance(); + _testRunnerApi.RegisterCallbacks(_callbackHandler); + } + } + + public static void RunTests(BuildingGroup @group) + { + Debug.Log("Start tests"); + + StoreGroup(@group); + InitTest(); + + _testRunnerApi.Execute(new ExecutionSettings(new Filter { testMode = TestMode.PlayMode })); + } + + public static void RunTests(UnityBuilding.BuildBehavior behavior, BuildingData data) + { + Debug.Log("Start tests"); + + StoreBase(behavior, data); + InitTest(); + + _testRunnerApi.Execute(new ExecutionSettings(new Filter { testMode = TestMode.PlayMode })); + } + + private static void StoreGroup(BuildingGroup @group) + { + var fileName = Environment.GetFolderPath(Environment.SpecialFolder.Templates) + "/" + GroupFileName; + using (var stream = new FileStream(fileName, FileMode.Create)) + { + using (var writer = new StreamWriter(stream)) + { + writer.WriteLine(JsonUtility.ToJson(@group)); + } + } + } + + private static bool LoadGroup(out BuildingGroup @group) + { + @group = null; + var fileName = Environment.GetFolderPath(Environment.SpecialFolder.Templates) + "/" + GroupFileName; + + if (!File.Exists(fileName)) + return false; + + try + { + using (var stream = new FileStream(fileName, FileMode.Open)) + { + using (var reader = new StreamReader(stream)) + { + @group = JsonUtility.FromJson(reader.ReadLine()); + } + } + } + finally + { + File.Delete(fileName); + } + + return true; + } + + private static void StoreBase(UnityBuilding.BuildBehavior behavior, BuildingData overwriteData) + { + var fileName = Environment.GetFolderPath(Environment.SpecialFolder.Templates) + "/" + BaseFileName; + using (var stream = new FileStream(fileName, FileMode.Create)) + { + using (var writer = new StreamWriter(stream)) + { + writer.WriteLine(JsonUtility.ToJson(behavior)); + writer.WriteLine(JsonUtility.ToJson(overwriteData)); + } + } + } + + private static bool LoadBase(out UnityBuilding.BuildBehavior behavior, out BuildingData overwriteData) + { + behavior = UnityBuilding.BuildBehavior.BuildOnly; + overwriteData = null; + + var fileName = Environment.GetFolderPath(Environment.SpecialFolder.Templates) + "/" + BaseFileName; + + if (!File.Exists(fileName)) + return false; + + try + { + using (var stream = new FileStream(fileName, FileMode.Open)) + { + using (var reader = new StreamReader(stream)) + { + behavior = JsonUtility.FromJson(reader.ReadLine()); + overwriteData = JsonUtility.FromJson(reader.ReadLine()); + } + } + } + finally + { + File.Delete(fileName); + } + + return true; + } + + private sealed class CallbackHandler : ICallbacks + { + public void RunStarted(ITestAdaptor testsToRun) + { + EditorUtility.DisplayProgressBar("Run Tests", "Test is running now", -1f); + } + + public void RunFinished(ITestResultAdaptor result) + { + EditorUtility.ClearProgressBar(); + + Debug.Log("Finished test with success: " + result.PassCount + ", skipped: " + result.SkipCount + ", failed: " + result.FailCount); + if (result.TestStatus == TestStatus.Failed) + { + EditorUtility.DisplayDialog("Test failures", "There are test failures: " + result.TestStatus, "OK"); + return; + } + + if (LoadGroup(out var @group)) + { + UnityBuilding.Build(@group, false); + } + else if (LoadBase(out var behavior, out var overwriteData)) + { + UnityBuilding.Build(behavior, overwriteData, false); + } + else + { + Debug.LogError("Unable to find base or group file for build execution!"); + } + } + + public void TestStarted(ITestAdaptor test) + { + } + + public void TestFinished(ITestResultAdaptor result) + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Runtime/Utils/UnityTesting.cs.meta b/Scripts/Runtime/Utils/UnityTesting.cs.meta new file mode 100644 index 0000000..a6d8390 --- /dev/null +++ b/Scripts/Runtime/Utils/UnityTesting.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8c032150275f455dac17c9bc957fd9a2 +timeCreated: 1643225666 \ No newline at end of file diff --git a/package.json b/package.json index 16dd1b2..3dead81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "org.pcsoft.build-tooling", - "version": "1.3.5", + "version": "1.4.0", "displayName": "Build Tooling", "description": "Add a build toolbar into unity to create and run build very easy.", "unity": "2021.1", @@ -12,7 +12,7 @@ ], "dependencies": { "com.marijnzwemmer.unity-toolbar-extender": "1.4.1", - "org.pcsoft.editor-ex": "1.1.2" + "org.pcsoft.editor-ex": "1.1.6" }, "author": { "name": "Pfeiffer C Soft",