using System.Collections; using System.Reflection; using NUnit.Framework; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.TestTools; using UnityEngine.UI; [UnityPlatform()] internal class InputModuleTests { EventSystem m_EventSystem; FakeBaseInput m_FakeBaseInput; StandaloneInputModule m_StandaloneInputModule; Canvas m_Canvas; Image m_Image; Image m_NestedImage; [SetUp] public void TestSetup() { // Camera | Canvas (Image) | Event System m_Canvas = new GameObject("Canvas").AddComponent(); m_Canvas.renderMode = RenderMode.ScreenSpaceOverlay; m_Canvas.gameObject.AddComponent(); m_Image = new GameObject("Image").AddComponent(); m_Image.gameObject.transform.SetParent(m_Canvas.transform); RectTransform imageRectTransform = m_Image.GetComponent(); imageRectTransform.sizeDelta = new Vector2(400f, 400f); imageRectTransform.localPosition = Vector3.zero; m_NestedImage = new GameObject("NestedImage").AddComponent(); m_NestedImage.gameObject.transform.SetParent(m_Image.transform); RectTransform nestedImageRectTransform = m_NestedImage.GetComponent(); nestedImageRectTransform.sizeDelta = new Vector2(200f, 200f); nestedImageRectTransform.localPosition = Vector3.zero; GameObject go = new GameObject("Event System"); m_EventSystem = go.AddComponent(); m_EventSystem.pixelDragThreshold = 1; m_StandaloneInputModule = go.AddComponent(); m_FakeBaseInput = go.AddComponent(); // Override input with FakeBaseInput so we can send fake mouse/keyboards button presses and touches m_StandaloneInputModule.inputOverride = m_FakeBaseInput; Cursor.lockState = CursorLockMode.None; } [UnityTest] public IEnumerator DragCallbacksDoGetCalled() { // While left mouse button is pressed and the mouse is moving, OnBeginDrag and OnDrag callbacks should be called // Then when the left mouse button is released, OnEndDrag callback should be called // Add script to EventSystem to update the mouse position m_EventSystem.gameObject.AddComponent(); // Add script to Image which implements OnBeginDrag, OnDrag & OnEndDrag callbacks DragCallbackCheck callbackCheck = m_Image.gameObject.AddComponent(); // Setting required input.mousePresent to fake mouse presence m_FakeBaseInput.MousePresent = true; var canvasRT = m_Canvas.gameObject.transform as RectTransform; m_FakeBaseInput.MousePosition = new Vector2(Screen.width / 2, Screen.height / 2); yield return null; // Left mouse button down simulation m_FakeBaseInput.MouseButtonDown[0] = true; yield return null; // Left mouse button down flag needs to reset in the next frame m_FakeBaseInput.MouseButtonDown[0] = false; yield return null; // Left mouse button up simulation m_FakeBaseInput.MouseButtonUp[0] = true; yield return null; // Left mouse button up flag needs to reset in the next frame m_FakeBaseInput.MouseButtonUp[0] = false; yield return null; Assert.IsTrue(callbackCheck.onBeginDragCalled, "OnBeginDrag not called"); Assert.IsTrue(callbackCheck.onDragCalled, "OnDragCalled not called"); Assert.IsTrue(callbackCheck.onEndDragCalled, "OnEndDragCalled not called"); Assert.IsTrue(callbackCheck.onDropCalled, "OnDrop not called"); } [UnityTest] public IEnumerator MouseOutsideMaskRectTransform_WhileInsidePaddedArea_PerformsClick() { var mask = new GameObject("Panel").AddComponent(); mask.gameObject.transform.SetParent(m_Canvas.transform); RectTransform panelRectTransform = mask.GetComponent(); panelRectTransform.sizeDelta = new Vector2(100, 100f); panelRectTransform.localPosition = Vector3.zero; m_Image.gameObject.transform.SetParent(mask.transform, true); mask.padding = new Vector4(-30, -30, -30, -30); PointerClickCallbackCheck callbackCheck = m_Image.gameObject.AddComponent(); var canvasRT = m_Canvas.gameObject.transform as RectTransform; var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2); m_FakeBaseInput.MousePresent = true; m_FakeBaseInput.MousePosition = screenMiddle; yield return null; // Click the center of the screen should hit the middle of the image. m_FakeBaseInput.MouseButtonDown[0] = true; yield return null; m_FakeBaseInput.MouseButtonDown[0] = false; yield return null; m_FakeBaseInput.MouseButtonUp[0] = true; yield return null; m_FakeBaseInput.MouseButtonUp[0] = false; yield return null; Assert.IsTrue(callbackCheck.pointerDown); //Reset the callbackcheck and click outside the mask but still in the image. callbackCheck.pointerDown = false; m_FakeBaseInput.MousePosition = new Vector2(screenMiddle.x - 60, screenMiddle.y); yield return null; m_FakeBaseInput.MouseButtonDown[0] = true; yield return null; m_FakeBaseInput.MouseButtonDown[0] = false; yield return null; m_FakeBaseInput.MouseButtonUp[0] = true; yield return null; m_FakeBaseInput.MouseButtonUp[0] = false; yield return null; Assert.IsTrue(callbackCheck.pointerDown); //Reset the callbackcheck and click outside the mask and outside in the image. callbackCheck.pointerDown = false; m_FakeBaseInput.MousePosition = new Vector2(screenMiddle.x - 100, screenMiddle.y); yield return null; m_FakeBaseInput.MouseButtonDown[0] = true; yield return null; m_FakeBaseInput.MouseButtonDown[0] = false; yield return null; m_FakeBaseInput.MouseButtonUp[0] = true; yield return null; m_FakeBaseInput.MouseButtonUp[0] = false; yield return null; Assert.IsFalse(callbackCheck.pointerDown); } [UnityTest] public IEnumerator PointerEnterChildShouldNotFullyExit_NotSendPointerEventToParent() { m_StandaloneInputModule.sendPointerHoverToParent = false; PointerExitCallbackCheck callbackCheck = m_Image.gameObject.AddComponent(); m_NestedImage.gameObject.AddComponent(); var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2); m_FakeBaseInput.MousePresent = true; m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150); yield return null; m_FakeBaseInput.MousePosition = screenMiddle; yield return null; Assert.IsTrue(callbackCheck.pointerData.fullyExited == false); } [UnityTest] public IEnumerator PointerEnterChildShouldNotExit_SendPointerEventToParent() { m_StandaloneInputModule.sendPointerHoverToParent = true; PointerExitCallbackCheck callbackCheck = m_Image.gameObject.AddComponent(); m_NestedImage.gameObject.AddComponent(); var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2); m_FakeBaseInput.MousePresent = true; m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150); yield return null; m_FakeBaseInput.MousePosition = screenMiddle; yield return null; Assert.IsTrue(callbackCheck.pointerData == null); } [UnityTest] public IEnumerator PointerEnterChildShouldNotReenter() { PointerEnterCallbackCheck callbackCheck = m_NestedImage.gameObject.AddComponent(); var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2); m_FakeBaseInput.MousePresent = true; m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150); yield return null; m_FakeBaseInput.MousePosition = screenMiddle; yield return null; Assert.IsTrue(callbackCheck.pointerData.reentered == false); } [UnityTest] public IEnumerator PointerExitChildShouldReenter_NotSendPointerEventToParent() { m_StandaloneInputModule.sendPointerHoverToParent = false; PointerEnterCallbackCheck callbackCheck = m_Image.gameObject.AddComponent(); var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2); m_FakeBaseInput.MousePresent = true; m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150); yield return null; m_FakeBaseInput.MousePosition = screenMiddle; yield return null; m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150); yield return null; Assert.IsTrue(callbackCheck.pointerData.reentered == true); } [UnityTest] public IEnumerator PointerExitChildShouldNotSendEnter_SendPointerEventToParent() { m_StandaloneInputModule.sendPointerHoverToParent = true; m_NestedImage.gameObject.AddComponent(); var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2); m_FakeBaseInput.MousePresent = true; m_FakeBaseInput.MousePosition = screenMiddle; yield return null; PointerEnterCallbackCheck callbackCheck = m_Image.gameObject.AddComponent(); m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150); yield return null; Assert.IsTrue(callbackCheck.pointerData == null); } [UnityTest] public IEnumerator PointerExitChildShouldFullyExit() { PointerExitCallbackCheck callbackCheck = m_NestedImage.gameObject.AddComponent(); var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2); m_FakeBaseInput.MousePresent = true; m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150); yield return null; m_FakeBaseInput.MousePosition = screenMiddle; yield return null; m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150); yield return null; Assert.IsTrue(callbackCheck.pointerData.fullyExited == true); } [UnityTest] public IEnumerator AllDragsAreReleasedOnLoseFocus() { // When the application loses focus, OnEndDrag and OnDrop should be called for all pointers // involved in an active drag. var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2); // Add scripts to Image which implement OnBeginDrag, OnDrag & OnEndDrag callbacks var callbackCheckLmb = m_Image.gameObject.AddComponent(); callbackCheckLmb.pointerId = -1; var callbackCheckRmb = m_Image.gameObject.AddComponent(); callbackCheckRmb.pointerId = -2; // Setting required to fake mouse presence m_FakeBaseInput.MousePresent = true; // Press both mouse buttons m_FakeBaseInput.MouseButtonDown[0] = true; m_FakeBaseInput.MouseButtonDown[1] = true; m_FakeBaseInput.MousePosition = screenMiddle; yield return null; // Reset mouse down events and move pointer somewhere else -> initiates drag m_FakeBaseInput.MouseButtonDown[0] = false; m_FakeBaseInput.MouseButtonDown[1] = false; m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150); yield return null; // We should be mid-drag for both mouse buttons now Assert.IsTrue(callbackCheckLmb.onBeginDragCalled, "OnBeginDrag not called for LMB"); Assert.IsTrue(callbackCheckRmb.onBeginDragCalled, "OnBeginDrag not called for RMB"); Assert.IsTrue(callbackCheckLmb.onDragCalled, "OnDrag not called for LMB"); Assert.IsTrue(callbackCheckRmb.onDragCalled, "OnDrag not called for RMB"); Assert.IsFalse(callbackCheckLmb.onEndDragCalled, "OnEndDrag called prematurely for LMB"); Assert.IsFalse(callbackCheckRmb.onEndDragCalled, "OnEndDrag called prematurely for RMB"); Assert.IsFalse(callbackCheckLmb.onDropCalled, "OnDrop called prematurely for LMB"); Assert.IsFalse(callbackCheckRmb.onDropCalled, "OnDrop called prematurely for RMB"); // Fake the application losing focus typeof(EventSystem) .GetMethod("OnApplicationFocus", BindingFlags.Instance | BindingFlags.NonPublic) .Invoke(m_EventSystem, new object[] { false }); yield return null; // Losing focus should have released the drags for both mouse buttons Assert.IsTrue(callbackCheckLmb.onEndDragCalled, "OnEndDrag not called for LMB"); Assert.IsTrue(callbackCheckRmb.onEndDragCalled, "OnEndDrag not called for RMB"); Assert.IsTrue(callbackCheckLmb.onDropCalled, "OnDrop not called for LMB"); Assert.IsTrue(callbackCheckRmb.onDropCalled, "OnDrop not called for RMB"); } [TearDown] public void TearDown() { GameObject.DestroyImmediate(m_EventSystem.gameObject); GameObject.DestroyImmediate(m_Canvas.gameObject); } }