#if UNITY_EDITOR || UNITY_STANDALONE //#define KEEP_ARTIFACTS_FOR_INSPECTION using System.Collections; using System.IO; using NUnit.Framework; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.AI; using UnityEngine.SceneManagement; using UnityEngine.TestTools; using Object = UnityEngine.Object; namespace Unity.AI.Navigation.Editor.Tests { [Category("PrefabsWithNavMeshComponents")] class NavMeshSurfaceInPrefabVariantTests { const string k_AutoSaveKey = "AutoSave"; const string k_ParentFolder = "Assets"; const string k_TempFolderName = "TempPrefabVariants"; static readonly string k_TempFolder = Path.Combine(k_ParentFolder, k_TempFolderName); const int k_GrayArea = 7; const int k_BrownArea = 10; const int k_RedArea = 18; const int k_OrangeArea = 26; const int k_YellowArea = 30; const int k_PrefabDefaultArea = k_YellowArea; static bool s_EnterPlayModeOptionsEnabled; static EnterPlayModeOptions s_EnterPlayModeOptions; [SerializeField] string m_PrefabPath; [SerializeField] string m_PrefabVariantPath; [SerializeField] string m_PreviousScenePath; [SerializeField] string m_TempScenePath; [SerializeField] int m_TestCounter; #if KEEP_ARTIFACTS_FOR_INSPECTION const bool k_KeepSceneObjects = true; #else const bool k_KeepSceneObjects = false; #endif [OneTimeSetUp] public void OneTimeSetup() { if (EditorApplication.isPlaying) return; AssetDatabase.DeleteAsset(k_TempFolder); var folderGUID = AssetDatabase.CreateFolder(k_ParentFolder, k_TempFolderName); Assume.That(folderGUID, Is.Not.Empty); SessionState.SetBool(k_AutoSaveKey, PrefabStageAutoSavingUtil.GetPrefabStageAutoSave()); PrefabStageAutoSavingUtil.SetPrefabStageAutoSave(false); StageUtility.GoToMainStage(); m_PreviousScenePath = SceneManager.GetActiveScene().path; m_TempScenePath = Path.Combine(k_TempFolder, "NavMeshSurfacePrefabVariantTestsScene.unity"); var tempScene = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single); EditorSceneManager.SaveScene(tempScene, m_TempScenePath); EditorSceneManager.OpenScene(m_TempScenePath); s_EnterPlayModeOptionsEnabled = EditorSettings.enterPlayModeOptionsEnabled; s_EnterPlayModeOptions = EditorSettings.enterPlayModeOptions; EditorSettings.enterPlayModeOptionsEnabled = true; EditorSettings.enterPlayModeOptions = EnterPlayModeOptions.DisableDomainReload | EnterPlayModeOptions.DisableSceneReload; } [OneTimeTearDown] public void OneTimeTearDown() { if (EditorApplication.isPlaying) return; PrefabStageAutoSavingUtil.SetPrefabStageAutoSave(SessionState.GetBool(k_AutoSaveKey, PrefabStageAutoSavingUtil.GetPrefabStageAutoSave())); StageUtility.GoToMainStage(); EditorSceneManager.SaveScene(SceneManager.GetActiveScene()); if (string.IsNullOrEmpty(m_PreviousScenePath)) EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single); EditorSettings.enterPlayModeOptionsEnabled = s_EnterPlayModeOptionsEnabled; EditorSettings.enterPlayModeOptions = s_EnterPlayModeOptions; #if !KEEP_ARTIFACTS_FOR_INSPECTION AssetDatabase.DeleteAsset(k_TempFolder); #endif } [UnitySetUp] public IEnumerator SetupVariantOfPrefabWithNavMesh() { if (EditorApplication.isPlaying) yield break; var plane = GameObject.CreatePrimitive(PrimitiveType.Plane); plane.name = "NavMeshSurfacePrefab" + (++m_TestCounter); var surface = plane.AddComponent(); surface.collectObjects = CollectObjects.Children; m_PrefabPath = Path.Combine(k_TempFolder, plane.name + ".prefab"); m_PrefabVariantPath = Path.Combine(k_TempFolder, plane.name + "Variant.prefab"); var planePrefab = PrefabUtility.SaveAsPrefabAsset(plane, m_PrefabPath); Object.DestroyImmediate(plane); AssetDatabase.OpenAsset(planePrefab); var theOriginalPrefabStage = PrefabStageUtility.GetCurrentPrefabStage(); var theOriginalPrefabSurface = theOriginalPrefabStage.prefabContentsRoot.GetComponent(); yield return TestUtility.BakeNavMeshAsync(theOriginalPrefabSurface, k_PrefabDefaultArea); PrefabSavingUtil.SavePrefab(theOriginalPrefabStage); StageUtility.GoToMainStage(); var instanceForVariant = PrefabUtility.InstantiatePrefab(planePrefab) as GameObject; PrefabUtility.SaveAsPrefabAsset(instanceForVariant, m_PrefabVariantPath); instanceForVariant!.name += "_Saved"; TestUtility.EliminateFromScene(ref instanceForVariant, k_KeepSceneObjects); NavMesh.RemoveAllNavMeshData(); yield return null; } [UnityTearDown] public IEnumerator TearDown() { if (EditorApplication.isPlaying) yield return new ExitPlayMode(); StageUtility.GoToMainStage(); yield return null; } [UnityTest] public IEnumerator NavMeshSurfacePrefabVariant_WhenFreshAndRebaked_ParentAssetUnchanged( [Values(RunMode.EditMode, RunMode.PlayMode)] RunMode runMode) { var theOriginalPrefab = AssetDatabase.LoadAssetAtPath(m_PrefabPath); AssetDatabase.OpenAsset(theOriginalPrefab); if (runMode == RunMode.PlayMode) yield return new EnterPlayMode(); var theOriginalPrefabStage = PrefabStageUtility.GetCurrentPrefabStage(); var theOriginalPrefabSurface = theOriginalPrefabStage.prefabContentsRoot.GetComponent(); var theOriginalPrefabNavMeshData = theOriginalPrefabSurface.navMeshData; var theOriginalPrefabAssetPath = AssetDatabase.GetAssetPath(theOriginalPrefabSurface.navMeshData); Assert.IsTrue(theOriginalPrefabNavMeshData != null, "Original prefab must have some NavMeshData."); Assert.IsTrue(File.Exists(theOriginalPrefabAssetPath), "NavMeshData file must exist for the original prefab. ({0})", theOriginalPrefabAssetPath); var prefabVariant = AssetDatabase.LoadAssetAtPath(m_PrefabVariantPath); AssetDatabase.OpenAsset(prefabVariant); var prefabVariantStage = PrefabStageUtility.GetCurrentPrefabStage(); var prefabVariantSurface = prefabVariantStage.prefabContentsRoot.GetComponent(); var initialVariantNavMeshData = prefabVariantSurface.navMeshData; var initialVariantAssetPath = AssetDatabase.GetAssetPath(prefabVariantSurface.navMeshData); Assert.AreEqual(theOriginalPrefabNavMeshData, initialVariantNavMeshData, "Fresh variant must have the same NavMeshData as the original prefab."); Assert.IsTrue(initialVariantNavMeshData != null, "Prefab must have some NavMeshData."); Assert.IsTrue(File.Exists(initialVariantAssetPath), "NavMeshData file must exist. ({0})", initialVariantAssetPath); yield return TestUtility.BakeNavMeshAsync(prefabVariantSurface, k_GrayArea); Assert.IsTrue(initialVariantNavMeshData != null, "The initial NavMeshData (from original prefab) must still exist immediately after prefab variant re-bake."); Assert.IsTrue(File.Exists(initialVariantAssetPath), "The initial NavMeshData file (from original prefab) must exist after prefab variant re-bake. ({0})", initialVariantAssetPath); Assert.IsTrue(prefabVariantSurface.navMeshData != null, "NavMeshSurface must have NavMeshData after baking."); var unsavedRebakedNavMeshData = prefabVariantSurface.navMeshData; yield return TestUtility.BakeNavMeshAsync(prefabVariantSurface, k_BrownArea); Assert.IsTrue(unsavedRebakedNavMeshData == null, "An unsaved NavMeshData should not exist after a re-bake."); Assert.IsTrue(prefabVariantSurface.navMeshData != null, "NavMeshSurface must have NavMeshData after baking."); PrefabSavingUtil.SavePrefab(prefabVariantStage); var theNewVariantNavMeshData = prefabVariantSurface.navMeshData; var theNewVariantAssetPath = AssetDatabase.GetAssetPath(theNewVariantNavMeshData); Assert.IsTrue(File.Exists(theNewVariantAssetPath), "Variant's own NavMeshData exists in a file after saving. ({0})", theNewVariantAssetPath); Assert.IsTrue(File.Exists(theOriginalPrefabAssetPath), "NavMeshData file of the original prefab still exists after saving the variant. ({0})", theOriginalPrefabAssetPath); Assert.IsTrue(theOriginalPrefabNavMeshData != null, "Original prefab must still have NavMeshData."); StageUtility.GoToMainStage(); yield return null; if (EditorApplication.isPlaying) yield return new ExitPlayMode(); } [UnityTest] public IEnumerator NavMeshSurfacePrefabVariant_WhenCustomizedAndRebaked_OldAssetDiscardedAndParentAssetUnchanged( [Values(RunMode.EditMode, RunMode.PlayMode)] RunMode runMode) { var prefabVariant = AssetDatabase.LoadAssetAtPath(m_PrefabVariantPath); var theOriginalPrefabPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(prefabVariant); var theOriginalPrefab = AssetDatabase.LoadAssetAtPath(theOriginalPrefabPath); AssetDatabase.OpenAsset(theOriginalPrefab); if (runMode == RunMode.PlayMode) yield return new EnterPlayMode(); var theOriginalPrefabStage = PrefabStageUtility.GetCurrentPrefabStage(); var theOriginalPrefabSurface = theOriginalPrefabStage.prefabContentsRoot.GetComponent(); var theOriginalPrefabNavMeshData = theOriginalPrefabSurface.navMeshData; var theOriginalPrefabAssetPath = AssetDatabase.GetAssetPath(theOriginalPrefabSurface.navMeshData); Assert.IsTrue(theOriginalPrefabNavMeshData != null, "Original prefab must have some NavMeshData."); Assert.IsTrue(File.Exists(theOriginalPrefabAssetPath), "NavMeshData file must exist for the original prefab. ({0})", theOriginalPrefabAssetPath); AssetDatabase.OpenAsset(prefabVariant); var prefabVariantStage = PrefabStageUtility.GetCurrentPrefabStage(); var prefabVariantSurface = prefabVariantStage.prefabContentsRoot.GetComponent(); yield return TestUtility.BakeNavMeshAsync(prefabVariantSurface, k_GrayArea); PrefabSavingUtil.SavePrefab(prefabVariantStage); var modifiedVariantNavMeshData = prefabVariantSurface.navMeshData; var modifiedVariantAssetPath = AssetDatabase.GetAssetPath(prefabVariantSurface.navMeshData); Assert.IsTrue(modifiedVariantNavMeshData != null, "Prefab must have some NavMeshData."); Assert.IsTrue(File.Exists(modifiedVariantAssetPath), "NavMeshData file for modifier variant must exist. ({0})", modifiedVariantAssetPath); Assert.AreNotEqual(theOriginalPrefabNavMeshData, modifiedVariantNavMeshData, "Modified variant must have a NavMeshData different than that of the original prefab."); yield return TestUtility.BakeNavMeshAsync(prefabVariantSurface, k_OrangeArea); Assert.IsTrue(modifiedVariantNavMeshData != null, "The initial NavMeshData of a modified variant must still exist immediately after prefab variant re-bake."); Assert.IsTrue(File.Exists(modifiedVariantAssetPath), "The initial NavMeshData file of a modified variant must exist after prefab variant re-bake. ({0})", modifiedVariantAssetPath); Assert.IsTrue(prefabVariantSurface.navMeshData != null, "NavMeshSurface must have NavMeshData after baking."); var unsavedRebakedNavMeshData = prefabVariantSurface.navMeshData; yield return TestUtility.BakeNavMeshAsync(prefabVariantSurface, k_RedArea); Assert.IsTrue(unsavedRebakedNavMeshData == null, "An unsaved NavMeshData should not exist after a re-bake."); Assert.IsTrue(prefabVariantSurface.navMeshData != null, "NavMeshSurface must have NavMeshData after baking."); PrefabSavingUtil.SavePrefab(prefabVariantStage); var theNewVariantNavMeshData = prefabVariantSurface.navMeshData; var theNewVariantAssetPath = AssetDatabase.GetAssetPath(theNewVariantNavMeshData); if (EditorApplication.isPlaying) { Assert.IsTrue(modifiedVariantNavMeshData != null, "Playmode: Initial NavMeshData of the modified variant must still exist until the end of playmode."); Assert.IsTrue(File.Exists(modifiedVariantAssetPath), "Playmode: Initial NavMeshData file of the modified and saved variant must still exist after saving the variant during playmode. ({0})", modifiedVariantAssetPath); } else { Assert.IsTrue(modifiedVariantNavMeshData == null, "Editmode: Initial NavMeshData of the modified variant must no longer exist after saving the variant."); // This code is still reachable because modifiedVariantNavMeshData has been affected by BakeNavMeshAsync() Assert.IsFalse(File.Exists(modifiedVariantAssetPath), "Editmode: Initial NavMeshData file of the modified and saved variant must no longer exist after saving the variant. ({0})", modifiedVariantAssetPath); } Assert.IsTrue(File.Exists(theNewVariantAssetPath), "Variant's own NavMeshData exists in a file after saving. ({0})", theNewVariantAssetPath); Assert.IsTrue(File.Exists(theOriginalPrefabAssetPath), "NavMeshData file of the original prefab still exists after saving the variant. ({0})", theOriginalPrefabAssetPath); Assert.AreNotEqual(theOriginalPrefabNavMeshData, theNewVariantNavMeshData, "Re-baked modified variant must have a NavMeshData different than that of the original prefab."); StageUtility.GoToMainStage(); yield return null; if (EditorApplication.isPlaying) yield return new ExitPlayMode(); } } } #endif