diff --git a/.github/workflows/create-unitypackage.yml b/.github/workflows/create-unitypackage.yml index 04d5ac1e2..87a2f7a68 100644 --- a/.github/workflows/create-unitypackage.yml +++ b/.github/workflows/create-unitypackage.yml @@ -2,9 +2,9 @@ name: Create UnityPackage on: workflow_dispatch: - push: - branches: - - workflow-wip + # push: + # branches: + # - workflow-wip-2 env: UNITY_PROJECT_PATH: . @@ -16,6 +16,7 @@ defaults: jobs: checkout: runs-on: [self-hosted, Windows, X64, Unity] + timeout-minutes: 10 steps: - id: checkout uses: actions/checkout@v4 @@ -26,13 +27,14 @@ jobs: detect-unity-version: needs: checkout runs-on: [self-hosted, Windows, X64, Unity] + timeout-minutes: 10 outputs: unity-editor-executable: ${{ steps.detect-unity-version.outputs.unity-editor-executable }} steps: - name: Detect Unity Version id: detect-unity-version run: | - PROJECT_VERSION_PATH="${UNITY_PROJECT_PATH}/ProjectSettings/ProjectVersion.txt" + PROJECT_VERSION_PATH="${{ env.UNITY_PROJECT_PATH }}/ProjectSettings/ProjectVersion.txt" UNITY_HUB="C:\Program Files\Unity Hub\Unity Hub.exe" UNITY_VERSION=`cat ${PROJECT_VERSION_PATH} | sed -n -E "s/^m_EditorVersion:\s+//p" | head -n 1` @@ -53,11 +55,13 @@ jobs: --childModules" fi + echo "${UNITY_EDITOR_EXECUTABLE} is installed." echo "unity-editor-executable=${UNITY_EDITOR_EXECUTABLE}" >> "${GITHUB_OUTPUT}" run-edit-mode-tests: needs: detect-unity-version runs-on: [self-hosted, Windows, X64, Unity] + timeout-minutes: 10 steps: - name: Run EditMode Tests id: run-edit-mode-tests @@ -68,14 +72,17 @@ jobs: "${{ needs.detect-unity-version.outputs.unity-editor-executable }}" \ -batchmode \ -silent-crashes \ - -projectPath "${UNITY_PROJECT_PATH}" \ + -projectPath "${{ env.UNITY_PROJECT_PATH }}" \ -executeMethod "UniGLTF.TestRunner.RunEditModeTests" \ - -logFile output.log + -logFile run-edit-mode-tests.log \ + -testRunnerNUnitXmlFile run-edit-mode-tests.xml RET=$? set -e echo "Output Log..." - cat output.log | egrep "^\[\[TestRunnerLog\]\]" + cat run-edit-mode-tests.log | egrep "^\[\[TestRunnerLog\]\]" + + # TODO: テスト結果を NUnit3 形式の run-edit-mode-tests.xml から読み込んで表示する if [ ${RET:-1} -eq 0 ]; then echo "Test succeeded." @@ -85,6 +92,29 @@ jobs: exit 1 fi + create-unitypackage: + needs: [detect-unity-version, run-edit-mode-tests] + runs-on: [self-hosted, Windows, X64, Unity] + timeout-minutes: 20 + steps: + - name: Create UnityPackage + id: create-unitypackage + run: | + "${{ needs.detect-unity-version.outputs.unity-editor-executable }}" \ + -batchmode \ + -silent-crashes \ + -projectPath "${{ env.UNITY_PROJECT_PATH }}" \ + -executeMethod "VRM.DevOnly.PackageExporter.VRMExportUnityPackage.CreateUnityPackageWithBuild" \ + -logFile create-unitypackage.log + + echo "Success to create UnityPackage." + + - name: Upload UnityPackage + uses: actions/upload-artifact@v4 + with: + name: unitypackage + path: ${{ env.UNITY_PROJECT_PATH }}/*.unitypackage + diff --git a/Assets/UniGLTF/Editor/TestRunner/TestRunner.cs b/Assets/UniGLTF/Editor/TestRunner/TestRunner.cs index 1885bf5c9..39e371477 100644 --- a/Assets/UniGLTF/Editor/TestRunner/TestRunner.cs +++ b/Assets/UniGLTF/Editor/TestRunner/TestRunner.cs @@ -1,7 +1,13 @@ +using System.Globalization; +using System.IO; +using System.Text; +using System.Xml; +using NUnit.Framework.Interfaces; using UnityEditor; using UnityEditor.TestTools.TestRunner.Api; using UnityEngine; +using TestStatus = UnityEditor.TestTools.TestRunner.Api.TestStatus; namespace UniGLTF { @@ -21,8 +27,28 @@ namespace UniGLTF { private static readonly string LogPrefix = $"[[TestRunnerLog]] "; private static readonly string ResultLogPrefix = $"[[TestRunnerResult]] "; + + private readonly string _xmlFilePath; private StackTraceLogType _tmpStackTraceLogType; + public TestCallback() + { + if (!Application.isBatchMode) return; + + var arguments = System.Environment.GetCommandLineArgs(); + for (var idx = 0; idx < arguments.Length; idx++) + { + if (arguments[idx] == "-testRunnerNUnitXmlFile" && idx + 1 < arguments.Length) + { + if (!arguments[idx + 1].StartsWith("-")) + { + _xmlFilePath = arguments[idx + 1]; + break; + } + } + } + } + public void RunStarted(ITestAdaptor testsToRun) { _tmpStackTraceLogType = Application.GetStackTraceLogType(LogType.Log); @@ -33,6 +59,7 @@ namespace UniGLTF public void RunFinished(ITestResultAdaptor result) { + Debug.Log($"{LogPrefix}Edit Mode Tests Finished."); Debug.Log($"{LogPrefix}Passed: {result.PassCount}, Skipped: {result.SkipCount}, Failed: {result.FailCount}"); Debug.Log($"{ResultLogPrefix}{result.FailCount}"); @@ -40,6 +67,17 @@ namespace UniGLTF if (Application.isBatchMode) { + if (!string.IsNullOrEmpty(_xmlFilePath)) + { + Debug.Log($"{LogPrefix}Write NUnit XML to {_xmlFilePath}"); + var xmlNode = CreateNUnitXmlTree(result); + + using var xmlWriter = new XmlTextWriter(_xmlFilePath, Encoding.UTF8); + xmlWriter.Formatting = Formatting.Indented; + xmlWriter.WriteStartDocument(); + xmlNode.WriteTo(xmlWriter); + xmlWriter.WriteEndDocument(); + } EditorApplication.Exit(result.FailCount > 0 ? 1 : 0); } } @@ -58,6 +96,32 @@ namespace UniGLTF Debug.Log($"{LogPrefix}{result.StackTrace}"); } } + + /// + /// https://forum.unity.com/threads/generating-nunit-compatible-xml-output.769757/ + /// + private static TNode CreateNUnitXmlTree(ITestResultAdaptor result) + { + var testRunNode = new TNode("test-run"); + testRunNode.AddAttribute("id", "2"); + testRunNode.AddAttribute("testcasecount", (result.PassCount + result.FailCount + result.SkipCount + result.InconclusiveCount).ToString()); + testRunNode.AddAttribute("result", result.ResultState); + testRunNode.AddAttribute("total", (result.PassCount + result.FailCount + result.SkipCount + result.InconclusiveCount).ToString()); + testRunNode.AddAttribute("passed", result.PassCount.ToString()); + testRunNode.AddAttribute("failed", result.FailCount.ToString()); + testRunNode.AddAttribute("inconclusive", result.InconclusiveCount.ToString()); + testRunNode.AddAttribute("skipped", result.SkipCount.ToString()); + testRunNode.AddAttribute("asserts", result.AssertCount.ToString()); + testRunNode.AddAttribute("engine-version", "3.5.0.0"); + testRunNode.AddAttribute("clr-version", System.Environment.Version.ToString()); + testRunNode.AddAttribute("start-time", result.StartTime.ToString("u")); + testRunNode.AddAttribute("end-time", result.EndTime.ToString("u")); + testRunNode.AddAttribute("duration", result.Duration.ToString(CultureInfo.InvariantCulture)); + + testRunNode.ChildNodes.Add(result.ToXml()); + + return testRunNode; + } } } } \ No newline at end of file diff --git a/Assets/VRM/Editor/VRMExportUnityPackage.cs b/Assets/VRM/Editor/VRMExportUnityPackage.cs index 35be05607..1ad482342 100644 --- a/Assets/VRM/Editor/VRMExportUnityPackage.cs +++ b/Assets/VRM/Editor/VRMExportUnityPackage.cs @@ -22,6 +22,9 @@ namespace VRM.DevOnly.PackageExporter } } + /// + /// TODO: 本来このクラスは「パッケージとしての UniVRM」のスコープのクラスであるが、「UPM Package VRM」のスコープにコードがあるので変 + /// public static class VRMExportUnityPackage { static string GetProjectRoot() @@ -136,21 +139,39 @@ namespace VRM.DevOnly.PackageExporter /// /// This is call from Jenkins build /// - /// -quit -batchMode -executeMethod VRM.DevOnly.PackageExporter.VRMExportUnityPackage.CreateUnityPackageWithBuild + /// -batchMode -silent-crashes -projectPath . -executeMethod VRM.DevOnly.PackageExporter.VRMExportUnityPackage.CreateUnityPackageWithBuild /// public static void CreateUnityPackageWithBuild() { - var folder = GetProjectRoot(); - if (!Directory.Exists(folder)) + try { - Directory.CreateDirectory(folder); - } + Debug.Log($"[{nameof(VRMExportUnityPackage)}] Start CreateUnityPackageWithBuild..."); + var folder = GetProjectRoot(); + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } - if (!BuildTestScene()) - { - Debug.LogError("Failed to build test scenes"); + Debug.Log($"[{nameof(VRMExportUnityPackage)}] Try to build test scenes..."); + BuildTestScene(); + + Debug.Log($"[{nameof(VRMExportUnityPackage)}] Create UnityPackages..."); + CreateUnityPackages(folder); + + Debug.Log($"[{nameof(VRMExportUnityPackage)}] Finish CreateUnityPackageWithBuild"); + if (Application.isBatchMode) + { + EditorApplication.Exit(0); + } + } + catch (Exception e) + { + Debug.LogException(e); + if (Application.isBatchMode) + { + EditorApplication.Exit(1); + } } - CreateUnityPackages(folder); } public class GlobList @@ -185,7 +206,7 @@ namespace VRM.DevOnly.PackageExporter } } - public static void CreateUnityPackages(string outputDir) + private static void CreateUnityPackages(string outputDir) { if (!VRMSampleCopy.Validate()) { @@ -234,7 +255,7 @@ namespace VRM.DevOnly.PackageExporter } } - public static void CreateUnityPackage( + private static void CreateUnityPackage( string outputDir, PackageInfo package ) @@ -248,13 +269,18 @@ namespace VRM.DevOnly.PackageExporter AssetDatabase.ExportPackage(targetFileNames, path, ExportPackageOptions.Default); } - public static bool BuildTestScene() + private static void BuildTestScene() { - var levels = new string[] { "Assets/VRM.Samples/Scenes/VRMRuntimeLoaderSample.unity" }; - return Build(levels); + var levels = new string[] + { + "Assets/UniGLTF_Samples/GltfViewer/GltfViewer.unity", + "Assets/VRM_Samples/SimpleViewer/SimpleViewer.unity", + "Assets/VRM10_Samples/VRM10Viewer/VRM10Viewer.unity", + }; + Build(levels); } - public static bool Build(string[] levels) + private static void Build(string[] levels) { var buildPath = Path.GetFullPath(Application.dataPath + "/../build/build.exe"); Debug.LogFormat("BuildPath: {0}", buildPath); @@ -263,12 +289,10 @@ namespace VRM.DevOnly.PackageExporter BuildTarget.StandaloneWindows, BuildOptions.None ); -#if UNITY_2018_1_OR_NEWER - var isSuccess = build.summary.result == BuildResult.Succeeded; -#else - var isSuccess = !string.IsNullOrEmpty(build); -#endif - return isSuccess; + if (build.summary.result != BuildResult.Succeeded) + { + throw new Exception("Failed to build scenes"); + } } } }