VR4Medical/ICI/Library/PackageCache/com.unity.xr.interaction.toolkit@42ef3600567b/Tests/Runtime/SocketInteractorTests.cs
2025-07-29 13:45:50 +03:00

389 lines
18 KiB
C#

using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine.TestTools;
using UnityEngine.XR.Interaction.Toolkit.Interactables;
namespace UnityEngine.XR.Interaction.Toolkit.Tests
{
[TestFixture]
class SocketInteractorTests
{
[TearDown]
public void TearDown()
{
TestUtilities.DestroyAllSceneObjects();
}
[UnityTest]
public IEnumerator SocketInteractorCanSelectInteractable()
{
TestUtilities.CreateInteractionManager();
var socketInteractor = TestUtilities.CreateSocketInteractor();
var interactable = TestUtilities.CreateGrabInteractable();
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(socketInteractor.interactablesSelected, Is.EqualTo(new[] { interactable }));
Assert.That(socketInteractor.hasSelection, Is.True);
}
[UnityTest]
public IEnumerator SocketInteractorStartingSelectedInteractableMovesToInteractorPosition()
{
TestUtilities.CreateInteractionManager();
var interactable = TestUtilities.CreateGrabInteractable();
interactable.transform.position = Vector3.one;
TestUtilities.DisableDelayProperties(interactable);
var socketInteractor = TestUtilities.CreateSocketInteractor();
socketInteractor.startingSelectedInteractable = interactable;
Assert.That(socketInteractor.interactablesSelected, Is.Empty);
Assert.That(socketInteractor.hasSelection, Is.False);
Assert.That(interactable.transform.position, Is.Not.EqualTo(socketInteractor.transform.position));
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(socketInteractor.interactablesSelected, Is.EqualTo(new[] { interactable }));
Assert.That(socketInteractor.hasSelection, Is.True);
Assert.That(interactable.transform.position, Is.EqualTo(socketInteractor.transform.position));
}
[UnityTest]
public IEnumerator SocketInteractorHandlesUnregisteredInteractable()
{
var manager = TestUtilities.CreateInteractionManager();
var socketInteractor = TestUtilities.CreateSocketInteractor();
var selectedInteractable = TestUtilities.CreateGrabInteractable();
var hoveredInteractable = TestUtilities.CreateGrabInteractable();
// Move to a position so it won't be the closest to ensure selectedInteractable is the one selected
hoveredInteractable.transform.localPosition = new Vector3(0.001f, 0f, 0f);
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(socketInteractor.interactablesSelected, Is.EqualTo(new[] { selectedInteractable }));
var validTargets = new List<IXRInteractable>();
manager.GetValidTargets(socketInteractor, validTargets);
Assert.That(validTargets, Is.EquivalentTo(new[] { selectedInteractable, hoveredInteractable }));
socketInteractor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.EquivalentTo(new[] { selectedInteractable, hoveredInteractable }));
Assert.That(socketInteractor.interactablesHovered, Is.EquivalentTo(new[] { selectedInteractable, hoveredInteractable }));
Object.Destroy(hoveredInteractable);
yield return null;
// ReSharper disable once ConditionIsAlwaysTrueOrFalse -- Object operator==
Assert.That(hoveredInteractable == null, Is.True);
manager.GetValidTargets(socketInteractor, validTargets);
Assert.That(validTargets, Is.EquivalentTo(new[] { selectedInteractable }));
socketInteractor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.EquivalentTo(new[] { selectedInteractable }));
Assert.That(socketInteractor.interactablesHovered, Is.EquivalentTo(new[] { selectedInteractable }));
Object.Destroy(selectedInteractable);
yield return null;
Assert.That(selectedInteractable == null, Is.True);
Assert.That(socketInteractor.interactablesSelected, Is.Empty);
}
[UnityTest]
public IEnumerator SocketInteractorCanDirectInteractorStealSingleModeInteractableFromSocket()
{
TestUtilities.CreateInteractionManager();
var socketInteractor = TestUtilities.CreateSocketInteractor();
var interactable = TestUtilities.CreateGrabInteractable();
Assert.That(interactable.selectMode, Is.EqualTo(InteractableSelectMode.Single));
var directInteractor = TestUtilities.CreateDirectInteractor();
var controllerRecorder = TestUtilities.CreateControllerRecorder(directInteractor, (recording) =>
{
recording.AddRecordingFrameNonAlloc(new XRControllerState(0.0f, Vector3.zero, Quaternion.identity, InputTrackingState.All, true,
false, false, false));
recording.AddRecordingFrameNonAlloc(new XRControllerState(0.1f, Vector3.zero, Quaternion.identity, InputTrackingState.All, true,
true, false, false));
recording.AddRecordingFrameNonAlloc(new XRControllerState(float.MaxValue, Vector3.zero, Quaternion.identity, InputTrackingState.All, true,
true, false, false));
});
controllerRecorder.isPlaying = true;
// Only need to wait one frame since the single select mode should cause the socket selection to first exit
// before being selected by the direct interactor.
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(socketInteractor.interactablesSelected, Is.Empty);
Assert.That(directInteractor.interactablesSelected, Is.EqualTo(new[] { interactable }));
}
[UnityTest]
public IEnumerator SocketInteractorCanDirectInteractorStealMultipleModeInteractableFromSocket()
{
TestUtilities.CreateInteractionManager();
var socketInteractor = TestUtilities.CreateSocketInteractor();
var interactable = TestUtilities.CreateGrabInteractable();
interactable.selectMode = InteractableSelectMode.Multiple;
var directInteractor = TestUtilities.CreateDirectInteractor();
var controllerRecorder = TestUtilities.CreateControllerRecorder(directInteractor, (recording) =>
{
recording.AddRecordingFrameNonAlloc(new XRControllerState(0.0f, Vector3.zero, Quaternion.identity, InputTrackingState.All, true,
false, false, false));
recording.AddRecordingFrameNonAlloc(new XRControllerState(0.1f, Vector3.zero, Quaternion.identity, InputTrackingState.All, true,
true, false, false));
recording.AddRecordingFrameNonAlloc(new XRControllerState(float.MaxValue, Vector3.zero, Quaternion.identity, InputTrackingState.All, true,
true, false, false));
});
controllerRecorder.isPlaying = true;
// May need to wait two frames.
// One for XRInteractionManager to enter the selection of the socket and direct interactors.
// Two for XRInteractionManager.ClearInteractorSelection to exit the selection of the socket
// since the CanSelect should now want to clear the selection due to no longer being the exclusive
// interactor selecting.
yield return new WaitForFixedUpdate();
yield return null;
yield return null;
Assert.That(socketInteractor.interactablesSelected, Is.Empty);
Assert.That(directInteractor.interactablesSelected, Is.EqualTo(new[] { interactable }));
}
[UnityTest]
public IEnumerator SocketInteractorReportsValidTargetWhenInteractableRegisteredAfterContact()
{
TestUtilities.CreateInteractionManager();
var interactor = TestUtilities.CreateSocketInteractor();
var interactable = TestUtilities.CreateGrabInteractable();
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(interactor.interactablesSelected, Is.EqualTo(new[] { interactable }));
Assert.That(interactable.interactorsSelecting, Is.EqualTo(new[] { interactor }));
var validTargets = new List<IXRInteractable>();
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.EquivalentTo(new[] { interactable }));
// Disable the Interactable so it will be removed as a valid target
interactable.enabled = false;
yield return null;
Assert.That(interactor.interactablesSelected, Is.Empty);
Assert.That(interactable.interactorsSelecting, Is.Empty);
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.Empty);
// Re-enable the Interactable. It should not be required to leave and enter the collider to be selected again.
interactable.enabled = true;
yield return null;
Assert.That(interactor.interactablesSelected, Is.EqualTo(new[] { interactable }));
Assert.That(interactable.interactorsSelecting, Is.EqualTo(new[] { interactor }));
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.EquivalentTo(new[] { interactable }));
}
[UnityTest]
public IEnumerator SocketInteractorUpdatesStayedCollidersOnDisablingInteractableCollider()
{
TestUtilities.CreateInteractionManager();
var interactor = TestUtilities.CreateSocketInteractor();
var interactable = TestUtilities.CreateGrabInteractable();
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(interactor, Is.Not.Null);
// Check that the interactor is hovering the interactable.
var validTargets = new List<IXRInteractable>();
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.interactablesHovered, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.hasHover, Is.True);
// Disable the collider component.
interactable.GetComponent<SphereCollider>().enabled = false;
yield return new WaitForFixedUpdate();
yield return null;
// Since interactable's collider is disabled, it should not show up in the list of valid targets.
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.Empty);
Assert.That(interactor.interactablesHovered, Is.Empty);
yield return new WaitForFixedUpdate();
// Additional test: re-enable the collider component, the interactor should re-hover it.
interactable.GetComponent<SphereCollider>().enabled = true;
yield return new WaitForFixedUpdate();
yield return null;
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.interactablesHovered, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.hasHover, Is.True);
}
[UnityTest]
public IEnumerator SocketInteractorUpdatesStayedCollidersOnDeactivatingInteractableObject()
{
TestUtilities.CreateInteractionManager();
var interactor = TestUtilities.CreateSocketInteractor();
var interactable = TestUtilities.CreateGrabInteractable();
// Set the socket's recycleDelayTime to 0 instead of the default 1s.
interactor.recycleDelayTime = 0f;
yield return new WaitForFixedUpdate();
yield return null;
// Check that the interactable is a valid target of and can be hovered by the interactor.
var validTargets = new List<IXRInteractable>();
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.interactablesHovered, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.hasHover, Is.True);
// De-activate the interactable's gameObject.
interactable.gameObject.SetActive(false);
// Test to make sure that the interactable gameObject would no longer be a valid target
// after its gameObject is set to disable.
yield return new WaitForFixedUpdate();
yield return null;
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.Empty);
Assert.That(interactor.hasHover, Is.False);
yield return new WaitForFixedUpdate();
// Additional test: re-enabling the interactable gameObject, the interactor should re-hover it.
interactable.gameObject.SetActive(true);
yield return new WaitForFixedUpdate();
yield return null;
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.interactablesHovered, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.hasHover, Is.True);
}
[UnityTest]
public IEnumerator SocketInteractorUpdatesStayedCollidersOnDestroyingInteractableObject()
{
TestUtilities.CreateInteractionManager();
var interactor = TestUtilities.CreateSocketInteractor();
var interactable = TestUtilities.CreateGrabInteractable();
// Set the socket's recycleDelayTime to 0 instead of the default 1s.
interactor.recycleDelayTime = 0f;
yield return new WaitForFixedUpdate();
yield return null;
// Check that the interactable is a ValidTarget of and can be hovered by the interactor.
var validTargets = new List<IXRInteractable>();
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.interactablesHovered, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.hasHover, Is.True);
// Destroy the interactable's gameObject.
Object.Destroy(interactable.gameObject);
// Test to make sure that the interactable gameObject would no longer be a valid target.
yield return new WaitForFixedUpdate();
yield return null;
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.Empty);
Assert.That(interactor.hasHover, Is.False);
yield return new WaitForFixedUpdate();
// Additional test: re-spawning the interactable gameObject, the interactor should re-hover it.
interactable = TestUtilities.CreateGrabInteractable();
yield return new WaitForFixedUpdate();
yield return null;
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.interactablesHovered, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.hasHover, Is.True);
}
[UnityTest]
public IEnumerator SocketInteractorCanUseTargetFilter()
{
TestUtilities.CreateInteractionManager();
var interactor = TestUtilities.CreateSocketInteractor();
var interactable = TestUtilities.CreateGrabInteractable();
yield return new WaitForFixedUpdate();
yield return null;
var filter = new MockTargetFilter();
Assert.That(filter.callbackExecution, Is.EqualTo(new List<TargetFilterCallback>()));
// Link the filter
interactor.targetFilter = filter;
Assert.That(interactor.targetFilter, Is.EqualTo(filter));
Assert.That(filter.callbackExecution, Is.EqualTo(new List<TargetFilterCallback>
{
TargetFilterCallback.Link
}));
// Process the filter
var validTargets = new List<IXRInteractable>();
interactor.GetValidTargets(validTargets);
Assert.That(validTargets, Is.EqualTo(new[] { interactable }));
Assert.That(interactor.targetFilter, Is.EqualTo(filter));
Assert.That(filter.callbackExecution, Is.EqualTo(new List<TargetFilterCallback>
{
TargetFilterCallback.Link,
TargetFilterCallback.Process
}));
// Disable the filter and check if it will no longer be processed
filter.canProcess = false;
interactor.GetValidTargets(validTargets);
Assert.That(filter.callbackExecution, Is.EqualTo(new List<TargetFilterCallback>
{
TargetFilterCallback.Link,
TargetFilterCallback.Process
}));
// Unlink the filter
interactor.targetFilter = null;
Assert.That(interactor.targetFilter, Is.EqualTo(null));
Assert.That(filter.callbackExecution, Is.EqualTo(new List<TargetFilterCallback>
{
TargetFilterCallback.Link,
TargetFilterCallback.Process,
TargetFilterCallback.Unlink
}));
}
}
}