Skip to content

Commit a9a3f8e

Browse files
authored
FIX: UITK UI input tests and re-enable test on CI (#2367)
1 parent 7524f4f commit a9a3f8e

1 file changed

Lines changed: 192 additions & 60 deletions

File tree

Assets/Tests/InputSystem/Plugins/UITests.cs

Lines changed: 192 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3922,22 +3922,13 @@ public void UI_CanDriveVirtualMouseCursorFromGamepad()
39223922
// can have a reference to UITK that doesn't break things in previous versions of Unity.
39233923
[UnityTest]
39243924
[Category("UI")]
3925-
[TestCase(UIPointerBehavior.AllPointersAsIs, ExpectedResult = 1)]
3926-
[TestCase(UIPointerBehavior.SingleMouseOrPenButMultiTouchAndTrack, ExpectedResult = 1)]
3927-
[TestCase(UIPointerBehavior.SingleUnifiedPointer, ExpectedResult = 1)]
3928-
#if UNITY_ANDROID || UNITY_IOS || UNITY_TVOS
3929-
[Ignore("Currently fails on the farm but succeeds locally on Note 10+; needs looking into.")]
3930-
#endif
3931-
#if UNITY_STANDALONE_LINUX || UNITY_EDITOR_LINUX
3932-
[Ignore("Disabled to make test suite pass on Linux")]
3925+
#if UNITY_2022_3 && (UNITY_ANDROID || UNITY_IOS)
3926+
[Ignore("Issue with mouse support on Android and iOS for 2022.3.")]
39333927
#endif
39343928
[PrebuildSetup(typeof(UI_CanOperateUIToolkitInterface_UsingInputSystemUIInputModule_Setup))]
3935-
public IEnumerator UI_CanOperateUIToolkitInterface_UsingInputSystemUIInputModule(UIPointerBehavior pointerBehavior)
3929+
public IEnumerator UI_UIToolkitInputModule_MouseClick_CapturesAndClicksButton()
39363930
{
39373931
var mouse = InputSystem.AddDevice<Mouse>();
3938-
var gamepad = InputSystem.AddDevice<Gamepad>();
3939-
var touchscreen = InputSystem.AddDevice<Touchscreen>();
3940-
39413932
var scene = SceneManager.LoadScene("UITKTestScene", new LoadSceneParameters(LoadSceneMode.Additive));
39423933
yield return null;
39433934
Assert.That(scene.isLoaded, Is.True, "UITKTestScene did not load as expected");
@@ -3946,88 +3937,158 @@ public IEnumerator UI_CanOperateUIToolkitInterface_UsingInputSystemUIInputModule
39463937
{
39473938
var objects = scene.GetRootGameObjects();
39483939
var uiModule = objects.First(x => x.name == "EventSystem").GetComponent<InputSystemUIInputModule>();
3949-
InputSystem.settings.backgroundBehavior = InputSettings.BackgroundBehavior.IgnoreFocus;
39503940
var uiDocument = objects.First(x => x.name == "UIDocument").GetComponent<UIDocument>();
3951-
var uiRoot = uiDocument.rootVisualElement;
3952-
var uiButton = uiRoot.Query<UnityEngine.UIElements.Button>("Button").First();
3953-
var scrollView = uiRoot.Query<ScrollView>("ScrollView").First();
3954-
3955-
uiModule.pointerBehavior = pointerBehavior;
3941+
var uiButton = uiDocument.rootVisualElement.Query<UnityEngine.UIElements.Button>("Button").First();
39563942

39573943
var clickReceived = false;
39583944
uiButton.clicked += () => clickReceived = true;
3959-
// NOTE: We do *NOT* do the following as the gamepad submit action will *not* trigger a ClickEvent.
3960-
//uiButton.RegisterCallback<ClickEvent>(_ => clickReceived = true);
39613945

39623946
yield return null;
39633947

39643948
var buttonCenter = new Vector2(uiButton.worldBound.center.x, Screen.height - uiButton.worldBound.center.y);
3965-
var buttonOutside = new Vector2(uiButton.worldBound.max.x + 10, Screen.height - uiButton.worldBound.center.y);
3966-
var scrollViewCenter = new Vector2(scrollView.worldBound.center.x, Screen.height - scrollView.worldBound.center.y);
3967-
39683949
Set(mouse.position, buttonCenter, queueEventOnly: true);
39693950
Press(mouse.leftButton, queueEventOnly: true);
3970-
3971-
////TODO: look at BaseInput and whether we need to override it in order for IME to go through our codepaths
3972-
////TODO: look into or document raycasting aspect (GraphicRaycaster) when using UITK (disable raycaster?)
3973-
////TODO: fix scroll wheel bindings on virtual cursor sample
3974-
39753951
yield return null;
3976-
39773952
Assert.That(uiButton.HasMouseCapture(), Is.True, "Expected uiButton to have mouse capture");
39783953

39793954
Release(mouse.leftButton, queueEventOnly: true);
3980-
39813955
yield return null;
3982-
39833956
Assert.That(uiButton.HasMouseCapture(), Is.False, "Expected uiButton to no longer have mouse capture");
3984-
Assert.That(clickReceived, Is.True);
3957+
Assert.That(clickReceived, Is.True, "Expected mouse click callback on UITK button");
3958+
}
3959+
finally
3960+
{
3961+
if (mouse.added)
3962+
InputSystem.RemoveDevice(mouse);
3963+
SceneManager.UnloadSceneAsync(scene);
3964+
}
3965+
3966+
yield return null;
3967+
}
3968+
3969+
[UnityTest]
3970+
[Category("UI")]
3971+
[PrebuildSetup(typeof(UI_CanOperateUIToolkitInterface_UsingInputSystemUIInputModule_Setup))]
3972+
public IEnumerator UI_UIToolkitInputModule_MouseScroll_MovesScrollView()
3973+
{
3974+
var mouse = InputSystem.AddDevice<Mouse>();
3975+
var scene = SceneManager.LoadScene("UITKTestScene", new LoadSceneParameters(LoadSceneMode.Additive));
3976+
yield return null;
3977+
Assert.That(scene.isLoaded, Is.True, "UITKTestScene did not load as expected");
39853978

3986-
// Put mouse in upper right corner and scroll down.
3979+
try
3980+
{
3981+
var objects = scene.GetRootGameObjects();
3982+
var uiModule = objects.First(x => x.name == "EventSystem").GetComponent<InputSystemUIInputModule>();
3983+
var uiDocument = objects.First(x => x.name == "UIDocument").GetComponent<UIDocument>();
3984+
var scrollView = uiDocument.rootVisualElement.Query<ScrollView>("ScrollView").First();
3985+
3986+
yield return null;
3987+
3988+
var scrollViewCenter = new Vector2(scrollView.worldBound.center.x, Screen.height - scrollView.worldBound.center.y);
39873989
Assert.That(scrollView.verticalScroller.value, Is.Zero, "Expected verticalScroller to be all the way up");
39883990
Set(mouse.position, scrollViewCenter, queueEventOnly: true);
39893991
yield return null;
39903992
Set(mouse.scroll, new Vector2(0, -100), queueEventOnly: true);
39913993
yield return null;
3992-
3993-
////FIXME: as of a time of writing, this line is broken on trunk due to the bug in UITK
3994-
// The bug is https://fogbugz.unity3d.com/f/cases/1323488/
3995-
// just adding a define as a safeguard measure to reenable it when trunk goes to next version cycle
39963994
Assert.That(scrollView.verticalScroller.value, Is.GreaterThan(0));
3995+
}
3996+
finally
3997+
{
3998+
if (mouse.added)
3999+
InputSystem.RemoveDevice(mouse);
4000+
SceneManager.UnloadSceneAsync(scene);
4001+
}
39974002

3998-
// Try a button press with the gamepad.
3999-
// NOTE: The current version of UITK does not focus the button automatically. Fix for that is in the pipe.
4000-
// For now focus the button manually.
4003+
yield return null;
4004+
}
4005+
4006+
[UnityTest]
4007+
[Category("UI")]
4008+
[PrebuildSetup(typeof(UI_CanOperateUIToolkitInterface_UsingInputSystemUIInputModule_Setup))]
4009+
public IEnumerator UI_UIToolkitInputModule_GamepadSubmit_ClicksFocusedButton()
4010+
{
4011+
var gamepad = InputSystem.AddDevice<Gamepad>();
4012+
var scene = SceneManager.LoadScene("UITKTestScene", new LoadSceneParameters(LoadSceneMode.Additive));
4013+
yield return null;
4014+
Assert.That(scene.isLoaded, Is.True, "UITKTestScene did not load as expected");
4015+
4016+
try
4017+
{
4018+
var objects = scene.GetRootGameObjects();
4019+
var uiModule = objects.First(x => x.name == "EventSystem").GetComponent<InputSystemUIInputModule>();
4020+
var uiDocument = objects.First(x => x.name == "UIDocument").GetComponent<UIDocument>();
4021+
var uiButton = uiDocument.rootVisualElement.Query<UnityEngine.UIElements.Button>("Button").First();
4022+
4023+
yield return null;
4024+
4025+
var clickReceived = false;
4026+
uiButton.clicked += () => clickReceived = true;
40014027
uiButton.Focus();
4002-
clickReceived = false;
4028+
40034029
PressAndRelease(gamepad.buttonSouth, queueEventOnly: true);
40044030
yield return null;
4031+
Assert.That(clickReceived, Is.True, "Expected gamepad submit to click focused UITK button");
4032+
}
4033+
finally
4034+
{
4035+
if (gamepad.added)
4036+
InputSystem.RemoveDevice(gamepad);
4037+
SceneManager.UnloadSceneAsync(scene);
4038+
}
40054039

4006-
Assert.That(clickReceived, Is.True, "Expected to have received click");
4040+
yield return null;
4041+
}
40074042

4008-
////TODO: tracked device support (not yet supported by UITK)
4043+
[UnityTest]
4044+
[Category("UI")]
4045+
[TestCase(UIPointerBehavior.AllPointersAsIs, ExpectedResult = 1)]
4046+
[TestCase(UIPointerBehavior.SingleMouseOrPenButMultiTouchAndTrack, ExpectedResult = 1)]
4047+
[TestCase(UIPointerBehavior.SingleUnifiedPointer, ExpectedResult = 1)]
4048+
#if UNITY_2022_3 && (UNITY_ANDROID || UNITY_IOS)
4049+
[Ignore("Fails on CI for 2022.3 on Android and iOS.")]
4050+
#endif
4051+
[PrebuildSetup(typeof(UI_CanOperateUIToolkitInterface_UsingInputSystemUIInputModule_Setup))]
4052+
public IEnumerator UI_UIToolkitInputModule_MultiTouchPointerOwnership(UIPointerBehavior pointerBehavior)
4053+
{
4054+
var touchscreen = InputSystem.AddDevice<Touchscreen>();
4055+
var scene = SceneManager.LoadScene("UITKTestScene", new LoadSceneParameters(LoadSceneMode.Additive));
4056+
yield return null;
4057+
Assert.That(scene.isLoaded, Is.True, "UITKTestScene did not load as expected");
40094058

4010-
static bool IsActive(VisualElement ve)
4011-
{
4012-
return ve.Query<VisualElement>().Active().ToList().Contains(ve);
4013-
}
4059+
try
4060+
{
4061+
var objects = scene.GetRootGameObjects();
4062+
var uiModule = objects.First(x => x.name == "EventSystem").GetComponent<InputSystemUIInputModule>();
4063+
var uiDocument = objects.First(x => x.name == "UIDocument").GetComponent<UIDocument>();
4064+
var uiButton = uiDocument.rootVisualElement.Query<UnityEngine.UIElements.Button>("Button").First();
40144065

4015-
// Move the mouse away from the button to check that touch inputs are also able to activate it.
4016-
Set(mouse.position, buttonOutside, queueEventOnly: true);
4066+
uiModule.pointerBehavior = pointerBehavior;
40174067
yield return null;
4018-
InputSystem.RemoveDevice(mouse);
4068+
4069+
var buttonCenter = new Vector2(uiButton.worldBound.center.x, Screen.height - uiButton.worldBound.center.y);
4070+
var buttonOutside = new Vector2(uiButton.worldBound.max.x + 10, Screen.height - uiButton.worldBound.center.y);
40194071

40204072
var uiButtonDownCount = 0;
40214073
var uiButtonUpCount = 0;
4022-
uiButton.RegisterCallback<PointerDownEvent>(e => uiButtonDownCount++, TrickleDown.TrickleDown);
4023-
uiButton.RegisterCallback<PointerUpEvent>(e => uiButtonUpCount++, TrickleDown.TrickleDown);
4074+
var uiButtonDownPointerIds = new List<int>();
4075+
var uiButtonUpPointerIds = new List<int>();
4076+
uiButton.RegisterCallback<PointerDownEvent>(eventData =>
4077+
{
4078+
uiButtonDownCount++;
4079+
uiButtonDownPointerIds.Add(eventData.pointerId);
4080+
}, TrickleDown.TrickleDown);
4081+
uiButton.RegisterCallback<PointerUpEvent>(eventData =>
4082+
{
4083+
uiButtonUpCount++;
4084+
uiButtonUpPointerIds.Add(eventData.pointerId);
4085+
}, TrickleDown.TrickleDown);
40244086

4025-
// Case 1369081: Make sure button doesn't get "stuck" in an active state when multiple fingers are used.
40264087
BeginTouch(1, buttonCenter, screen: touchscreen);
40274088
yield return null;
40284089
Assert.That(uiButtonDownCount, Is.EqualTo(1), "Expected uiButtonDownCount to be 1");
40294090
Assert.That(uiButtonUpCount, Is.EqualTo(0), "Expected uiButtonUpCount to be 0");
4030-
Assert.That(IsActive(uiButton), Is.True, "Expected uiButton to be active");
4091+
Assert.That(uiButtonDownPointerIds, Has.Count.EqualTo(1), "Expected one PointerDown pointerId");
40314092

40324093
BeginTouch(2, buttonOutside, screen: touchscreen);
40334094
yield return null;
@@ -4038,31 +4099,102 @@ static bool IsActive(VisualElement ve)
40384099
if (pointerBehavior == UIPointerBehavior.SingleUnifiedPointer)
40394100
{
40404101
Assert.That(uiButtonUpCount, Is.EqualTo(1), "Expected uiButtonUpCount to be 1");
4041-
Assert.That(IsActive(uiButton), Is.False, "Expected uiButton to no longer be active");
4102+
Assert.That(uiButtonUpPointerIds, Has.Count.EqualTo(1), "Expected one PointerUp pointerId");
40424103
}
40434104
else
40444105
{
40454106
Assert.That(uiButtonUpCount, Is.EqualTo(0), "Expected uiButtonUpCount to be 0");
4046-
Assert.That(IsActive(uiButton), Is.True, "Expected uiButton to be active");
4107+
Assert.That(uiButtonUpPointerIds, Is.Empty, "Expected no PointerUp pointerId from outside touch");
40474108
}
40484109

40494110
EndTouch(1, buttonCenter, screen: touchscreen);
40504111
yield return null;
40514112
Assert.That(uiButtonDownCount, Is.EqualTo(1), "Expected uiButtonDownCount to be 1");
40524113
Assert.That(uiButtonUpCount, Is.EqualTo(1), "Expected uiButtonUpCount to be 1");
4053-
Assert.That(IsActive(uiButton), Is.False, "Expected uiButton to no longer be active");
4114+
Assert.That(uiButtonUpPointerIds, Has.Count.EqualTo(1), "Expected one PointerUp pointerId after releasing touch #1");
4115+
Assert.That(uiButtonUpPointerIds[0], Is.EqualTo(uiButtonDownPointerIds[0]),
4116+
"Expected PointerUp ownership to match the pointer that pressed the button");
4117+
Assert.That(IsActiveVisualElement(uiButton), Is.False, "Expected uiButton to no longer be active");
4118+
}
4119+
finally
4120+
{
4121+
if (touchscreen.added)
4122+
InputSystem.RemoveDevice(touchscreen);
4123+
SceneManager.UnloadSceneAsync(scene);
4124+
}
4125+
4126+
yield return null;
4127+
}
40544128

4055-
InputSystem.RemoveDevice(touchscreen);
4129+
[UnityTest]
4130+
[Category("UI")]
4131+
#if UNITY_2022_3 && (UNITY_ANDROID || UNITY_IOS)
4132+
[Ignore("Issue with mouse support on Android and iOS for 2022.3.")]
4133+
#endif
4134+
[TestCase(UIPointerBehavior.AllPointersAsIs, ExpectedResult = 1)]
4135+
[TestCase(UIPointerBehavior.SingleMouseOrPenButMultiTouchAndTrack, ExpectedResult = 1)]
4136+
[TestCase(UIPointerBehavior.SingleUnifiedPointer, ExpectedResult = 1)]
4137+
[PrebuildSetup(typeof(UI_CanOperateUIToolkitInterface_UsingInputSystemUIInputModule_Setup))]
4138+
public IEnumerator UI_UIToolkitInputModule_MultiTouchVisualActiveState_FollowsPointerBehavior(UIPointerBehavior pointerBehavior)
4139+
{
4140+
var touchscreen = InputSystem.AddDevice<Touchscreen>();
4141+
var scene = SceneManager.LoadScene("UITKTestScene", new LoadSceneParameters(LoadSceneMode.Additive));
4142+
yield return null;
4143+
Assert.That(scene.isLoaded, Is.True, "UITKTestScene did not load as expected");
4144+
4145+
try
4146+
{
4147+
var objects = scene.GetRootGameObjects();
4148+
var uiModule = objects.First(x => x.name == "EventSystem").GetComponent<InputSystemUIInputModule>();
4149+
var uiDocument = objects.First(x => x.name == "UIDocument").GetComponent<UIDocument>();
4150+
var uiRoot = uiDocument.rootVisualElement;
4151+
var uiButton = uiRoot.Query<UnityEngine.UIElements.Button>("Button").First();
4152+
4153+
uiModule.pointerBehavior = pointerBehavior;
4154+
4155+
yield return null;
4156+
4157+
var buttonCenter = new Vector2(uiButton.worldBound.center.x, Screen.height - uiButton.worldBound.center.y);
4158+
var buttonOutside = new Vector2(uiButton.worldBound.max.x + 10, Screen.height - uiButton.worldBound.center.y);
4159+
4160+
// Finger #1 presses and holds on the button.
4161+
BeginTouch(1, buttonCenter, screen: touchscreen);
4162+
yield return null;
4163+
Assert.That(IsActiveVisualElement(uiButton), Is.True, "Expected uiButton to be active while touch #1 is still pressed.");
4164+
4165+
// Finger #2 taps outside of the button while finger #1 is still held.
4166+
BeginTouch(2, buttonOutside, screen: touchscreen);
4167+
yield return null;
4168+
EndTouch(2, buttonOutside, screen: touchscreen);
4169+
yield return null;
4170+
4171+
// Desired contract:
4172+
// - SingleUnifiedPointer: touch #2 can replace current pointer and clear active state.
4173+
// - Non-unified behaviors: touch #1 is still pressed and should keep the button visually active.
4174+
if (pointerBehavior == UIPointerBehavior.SingleUnifiedPointer)
4175+
Assert.That(IsActiveVisualElement(uiButton), Is.False, "Expected uiButton to no longer be active in SingleUnifiedPointer mode.");
4176+
else
4177+
Assert.That(IsActiveVisualElement(uiButton), Is.True, "Expected uiButton to remain active while touch #1 is still pressed.");
4178+
4179+
EndTouch(1, buttonCenter, screen: touchscreen);
4180+
yield return null;
4181+
Assert.That(IsActiveVisualElement(uiButton), Is.False, "Expected uiButton to no longer be active after touch #1 is released.");
40564182
}
40574183
finally
40584184
{
4185+
if (touchscreen.added)
4186+
InputSystem.RemoveDevice(touchscreen);
40594187
SceneManager.UnloadSceneAsync(scene);
40604188
}
40614189

4062-
// Wait for unload to complete.
40634190
yield return null;
40644191
}
40654192

4193+
private static bool IsActiveVisualElement(VisualElement visualElement)
4194+
{
4195+
return visualElement.Query<VisualElement>().Active().ToList().Contains(visualElement);
4196+
}
4197+
40664198
private class UI_CanOperateUIToolkitInterface_UsingInputSystemUIInputModule_Setup : IPrebuildSetup
40674199
{
40684200
public void Setup()
@@ -4509,7 +4641,7 @@ public IEnumerator UI_DisplayIndexMatchesDisplayMultiplePointers()
45094641
}
45104642

45114643
#endif
4512-
#endregion
4644+
#endregion
45134645

45144646
public class MyButton : UnityEngine.UI.Button
45154647
{

0 commit comments

Comments
 (0)