using System; using System.Collections; using System.IO; using NUnit.Framework; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.Perception.GroundTruth; using UnityEngine.SceneManagement; using UnityEngine.TestTools; #if MOQ_PRESENT using Moq; using Moq.Protected; #endif namespace EditorTests { [TestFixture] public class PerceptionCameraEditorTests { [UnityTest] public IEnumerator EditorPause_DoesNotLogErrors() { ResetScene(); var cameraObject = SetupCamera(p => { var idLabelConfig = ScriptableObject.CreateInstance(); p.captureRgbImages = true; p.AddLabeler(new BoundingBox2DLabeler(idLabelConfig)); p.AddLabeler(new RenderedObjectInfoLabeler(idLabelConfig)); }); cameraObject.name = "Camera"; yield return new EnterPlayMode(); var expectedFirstFrame = Time.frameCount; yield return null; EditorApplication.isPaused = true; //Wait a few editor frames to ensure the issue has a chance to trigger. yield return null; yield return null; yield return null; yield return null; EditorApplication.isPaused = false; var expectedLastFrame = Time.frameCount; yield return null; DatasetCapture.ResetSimulation(); var capturesPath = Path.Combine(DatasetCapture.OutputDirectory, "captures_000.json"); var capturesJson = File.ReadAllText(capturesPath); for (int iFrameCount = expectedFirstFrame; iFrameCount <= expectedLastFrame; iFrameCount++) { var imagePath = $"{GameObject.Find("Camera").GetComponent().rgbDirectory}/rgb_{iFrameCount}"; StringAssert.Contains(imagePath, capturesJson); } yield return new ExitPlayMode(); } static void ResetScene() { int sceneCount = SceneManager.sceneCount; for (int i = sceneCount - 1; i >= 0; i--) { EditorSceneManager.CloseScene(SceneManager.GetSceneAt(i), true); } EditorSceneManager.NewScene(NewSceneSetup.EmptyScene); } #if MOQ_PRESENT [UnityTest] public IEnumerator AddLabelerAfterStart_ShouldInitialize() { ResetScene(); yield return new EnterPlayMode(); var camera = SetupCamera(null); var mockLabeler = new Mock(); yield return null; camera.GetComponent().AddLabeler(mockLabeler.Object); yield return null; mockLabeler.Protected().Verify("Setup", Times.Once()); yield return new ExitPlayMode(); } [UnityTest] public IEnumerator Labeler_ShouldRunCallbacksInFirstFrame() { ResetScene(); yield return new EnterPlayMode(); var mockLabeler = new Mock(); var camera = SetupCamera(null); camera.GetComponent().AddLabeler(mockLabeler.Object); yield return null; mockLabeler.Protected().Verify("Setup", Times.Once()); mockLabeler.Protected().Verify("OnUpdate", Times.Once()); mockLabeler.Protected().Verify("OnBeginRendering", Times.Once()); mockLabeler.Protected().Verify("OnEndRendering", Times.Once()); yield return new ExitPlayMode(); } [UnityTest] public IEnumerator Labeler_ShouldNotRunCallbacksWhenCameraDisabled() { ResetScene(); yield return new EnterPlayMode(); var mockLabeler = new Mock(); var camera = SetupCamera(null); var perceptionCamera = camera.GetComponent(); perceptionCamera.AddLabeler(mockLabeler.Object); yield return null; perceptionCamera.enabled = false; yield return null; mockLabeler.Protected().Verify("Setup", Times.Once()); mockLabeler.Protected().Verify("OnUpdate", Times.Once()); mockLabeler.Protected().Verify("OnBeginRendering", Times.Once()); mockLabeler.Protected().Verify("OnEndRendering", Times.Once()); yield return new ExitPlayMode(); } [UnityTest] public IEnumerator AddAndRemoveLabelerInSameFrame_ShouldDoNothing() { ResetScene(); yield return new EnterPlayMode(); var mockLabeler = new Mock(); var cameraObject = SetupCamera(null); var perceptionCamera = cameraObject.GetComponent(); perceptionCamera.AddLabeler(mockLabeler.Object); perceptionCamera.RemoveLabeler(mockLabeler.Object); yield return null; mockLabeler.Protected().Verify("Setup", Times.Never()); mockLabeler.Protected().Verify("OnUpdate", Times.Never()); mockLabeler.Protected().Verify("OnBeginRendering", Times.Never()); mockLabeler.Protected().Verify("OnEndRendering", Times.Never()); mockLabeler.Protected().Verify("Cleanup", Times.Never()); yield return new ExitPlayMode(); } [UnityTest] public IEnumerator RemoveLabeler_ShouldCallCleanup() { ResetScene(); yield return new EnterPlayMode(); var mockLabeler = new Mock(); var cameraObject = SetupCamera(null); var perceptionCamera = cameraObject.GetComponent(); perceptionCamera.AddLabeler(mockLabeler.Object); yield return null; Assert.IsTrue(perceptionCamera.RemoveLabeler(mockLabeler.Object)); mockLabeler.Protected().Verify("Cleanup", Times.Once()); yield return new ExitPlayMode(); } [UnityTest] public IEnumerator RemoveLabeler_OnLabelerNotAdded_ShouldNotCallCleanup() { ResetScene(); yield return new EnterPlayMode(); var mockLabeler = new Mock(); var cameraObject = SetupCamera(null); var perceptionCamera = cameraObject.GetComponent(); yield return null; Assert.IsFalse(perceptionCamera.RemoveLabeler(mockLabeler.Object)); mockLabeler.Protected().Verify("Cleanup", Times.Never()); yield return new ExitPlayMode(); } [UnityTest] public IEnumerator DestroyPerceptionCameraObject_ShouldCallCleanup() { ResetScene(); yield return new EnterPlayMode(); var mockLabeler = new Mock(); var cameraObject = SetupCamera(null); var perceptionCamera = cameraObject.GetComponent(); perceptionCamera.AddLabeler(mockLabeler.Object); yield return null; UnityEngine.Object.DestroyImmediate(cameraObject); mockLabeler.Protected().Verify("Cleanup", Times.Once()); yield return new ExitPlayMode(); } [UnityTest] public IEnumerator SetupThrows_ShouldDisable() { ResetScene(); yield return new EnterPlayMode(); var mockLabeler = new Mock(); mockLabeler.Protected().Setup("Setup").Throws(); var labeler = mockLabeler.Object; var camera = SetupCamera(null); camera.GetComponent().AddLabeler(labeler); LogAssert.Expect(LogType.Exception, "InvalidOperationException: Operation is not valid due to the current state of the object."); yield return null; mockLabeler.Protected().Verify("Setup", Times.Once()); mockLabeler.Protected().Verify("OnUpdate", Times.Never()); mockLabeler.Protected().Verify("OnBeginRendering", Times.Never()); mockLabeler.Protected().Verify("OnEndRendering", Times.Never()); Assert.IsFalse(labeler.enabled); yield return new ExitPlayMode(); } #endif static GameObject SetupCamera(Action initPerceptionCameraCallback) { var cameraObject = new GameObject(); cameraObject.SetActive(false); var camera = cameraObject.AddComponent(); camera.orthographic = true; camera.orthographicSize = 1; #if HDRP_PRESENT cameraObject.AddComponent(); #endif var perceptionCamera = cameraObject.AddComponent(); initPerceptionCameraCallback?.Invoke(perceptionCamera); cameraObject.SetActive(true); return cameraObject; } } }