Skip to content

Commit 336d39b

Browse files
committed
Add a new test to check various select modes and select/deselect ordering with events
1 parent 20d6987 commit 336d39b

File tree

1 file changed

+271
-0
lines changed

1 file changed

+271
-0
lines changed

org.mixedrealitytoolkit.input/Tests/Runtime/BasicInputTests.cs

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,277 @@ public IEnumerator GazePinchSmokeTest()
315315
Assert.IsTrue(interactable.IsGazePinchHovered);
316316
}
317317

318+
[UnityTest]
319+
public IEnumerator TestStatefulInteractableSelectMode(
320+
[Values(InteractableSelectMode.Single, InteractableSelectMode.Multiple)] InteractableSelectMode selectMode,
321+
[Values(true, false)] bool releaseInSelectOrder)
322+
{
323+
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
324+
StatefulInteractable interactable = cube.AddComponent<StatefulInteractable>();
325+
cube.transform.position = InputTestUtilities.InFrontOfUser(new Vector3(0.2f, 0.2f, 0.5f));
326+
cube.transform.localScale = Vector3.one * 0.1f;
327+
328+
bool isSelected = false;
329+
bool selectEntered = false;
330+
bool selectExited = false;
331+
332+
// For this test, we won't use poke or grab selection
333+
interactable.DisableInteractorType(typeof(PokeInteractor));
334+
interactable.DisableInteractorType(typeof(GrabInteractor));
335+
interactable.selectMode = selectMode;
336+
337+
interactable.firstSelectEntered.AddListener((SelectEnterEventArgs) => { isSelected = true; });
338+
interactable.lastSelectExited.AddListener((SelectEnterEventArgs) => { isSelected = false; });
339+
340+
interactable.selectEntered.AddListener((SelectEnterEventArgs) => { selectEntered = true; });
341+
interactable.selectExited.AddListener((SelectEnterEventArgs) => { selectExited = true; });
342+
343+
// Introduce the first hand
344+
var rightHand = new TestHand(Handedness.Right);
345+
yield return rightHand.Show(InputTestUtilities.InFrontOfUser(0.4f));
346+
yield return RuntimeTestUtilities.WaitForUpdates();
347+
348+
Assert.IsFalse(interactable.IsRayHovered,
349+
"StatefulInteractable was already RayHovered.");
350+
Assert.IsFalse(interactable.isHovered,
351+
"StatefulInteractable was already hovered.");
352+
353+
// Aim the first hand to hover the cube
354+
yield return rightHand.AimAt(cube.transform.position);
355+
yield return RuntimeTestUtilities.WaitForUpdates();
356+
Assert.IsTrue(interactable.IsRayHovered,
357+
"StatefulInteractable did not get RayHovered.");
358+
Assert.IsTrue(interactable.isHovered,
359+
"StatefulInteractable did not get hovered.");
360+
361+
Assert.IsTrue(interactable.HoveringRayInteractors.Count == 1,
362+
"StatefulInteractable should only have 1 hovering RayInteractor.");
363+
Assert.IsTrue(interactable.interactorsHovering.Count == 1,
364+
"StatefulInteractable should only have 1 hovering interactor.");
365+
366+
Assert.IsFalse(isSelected,
367+
"StatefulInteractable should not be selected.");
368+
Assert.IsFalse(selectEntered,
369+
"StatefulInteractable should not have had a select enter.");
370+
Assert.IsFalse(selectExited,
371+
"StatefulInteractable should not have had a select exit.");
372+
373+
// Pinch the first hand to select the cube
374+
yield return rightHand.SetHandshape(HandshapeId.Pinch);
375+
yield return RuntimeTestUtilities.WaitForUpdates();
376+
Assert.IsTrue(interactable.IsRaySelected,
377+
"StatefulInteractable did not get RaySelected.");
378+
Assert.IsTrue(interactable.isSelected,
379+
"StatefulInteractable did not get selected.");
380+
Assert.IsTrue(interactable.interactorsSelecting.Count == 1,
381+
"StatefulInteractable should only have 1 selecting interactor.");
382+
Assert.IsTrue(isSelected,
383+
"StatefulInteractable did not get selected.");
384+
Assert.IsTrue(selectEntered,
385+
"StatefulInteractable should have had a select enter.");
386+
Assert.IsFalse(selectExited,
387+
"StatefulInteractable should not have had a select exit.");
388+
389+
// Reset to continue testing
390+
selectEntered = false;
391+
392+
// Release the first hand to deselect the cube
393+
yield return rightHand.SetHandshape(HandshapeId.Open);
394+
yield return RuntimeTestUtilities.WaitForUpdates();
395+
Assert.IsFalse(interactable.IsRaySelected,
396+
"StatefulInteractable did not get de-RaySelected.");
397+
Assert.IsFalse(interactable.isSelected,
398+
"StatefulInteractable did not get deselected.");
399+
Assert.IsTrue(interactable.interactorsSelecting.Count == 0,
400+
"StatefulInteractable should not have any selecting interactors.");
401+
Assert.IsFalse(isSelected,
402+
"StatefulInteractable should not be selected.");
403+
Assert.IsFalse(selectEntered,
404+
"StatefulInteractable should not have had a select enter.");
405+
Assert.IsTrue(selectExited,
406+
"StatefulInteractable should have had a select exit.");
407+
408+
// Reset to continue testing
409+
selectExited = false;
410+
411+
// Introduce the second hand
412+
var leftHand = new TestHand(Handedness.Left);
413+
yield return leftHand.Show(InputTestUtilities.InFrontOfUser(0.4f));
414+
yield return RuntimeTestUtilities.WaitForUpdates();
415+
416+
// Aim the second hand to hover the cube
417+
yield return leftHand.AimAt(cube.transform.position);
418+
yield return RuntimeTestUtilities.WaitForUpdates();
419+
Assert.IsTrue(interactable.IsRayHovered,
420+
"StatefulInteractable did not stay RayHovered.");
421+
Assert.IsTrue(interactable.isHovered,
422+
"StatefulInteractable did not stay hovered.");
423+
424+
Assert.IsTrue(interactable.HoveringRayInteractors.Count == 2,
425+
"StatefulInteractable should have 2 hovering RayInteractors.");
426+
Assert.IsTrue(interactable.interactorsHovering.Count == 2,
427+
"StatefulInteractable should have 2 hovering interactors.");
428+
429+
Assert.IsFalse(isSelected,
430+
"StatefulInteractable should not be selected.");
431+
Assert.IsFalse(selectEntered,
432+
"StatefulInteractable should not have had a select enter.");
433+
Assert.IsFalse(selectExited,
434+
"StatefulInteractable should not have had a select exit.");
435+
436+
// Pinch the first hand to select the cube
437+
yield return rightHand.SetHandshape(HandshapeId.Pinch);
438+
yield return RuntimeTestUtilities.WaitForUpdates();
439+
Assert.IsTrue(interactable.IsRaySelected,
440+
"StatefulInteractable did not get RaySelected.");
441+
Assert.IsTrue(interactable.isSelected,
442+
"StatefulInteractable did not get selected.");
443+
Assert.IsTrue(interactable.interactorsSelecting.Count == 1,
444+
"StatefulInteractable should only have 1 selecting interactor.");
445+
Assert.IsTrue(isSelected,
446+
"StatefulInteractable did not get selected.");
447+
Assert.IsTrue(selectEntered,
448+
"StatefulInteractable should have had a select enter.");
449+
Assert.IsFalse(selectExited,
450+
"StatefulInteractable should not have had a select exit.");
451+
452+
// Reset to continue testing
453+
selectEntered = false;
454+
455+
// Pinch the second hand to select the cube
456+
yield return leftHand.SetHandshape(HandshapeId.Pinch);
457+
yield return RuntimeTestUtilities.WaitForUpdates();
458+
Assert.IsTrue(interactable.IsRaySelected,
459+
"StatefulInteractable did not stay RaySelected.");
460+
Assert.IsTrue(interactable.isSelected,
461+
"StatefulInteractable did not stay selected.");
462+
Assert.IsTrue(isSelected,
463+
"StatefulInteractable did not stay selected.");
464+
Assert.IsTrue(selectEntered,
465+
"StatefulInteractable should have had a select enter.");
466+
467+
// Reset to continue testing
468+
selectEntered = false;
469+
470+
// Both hands are pinching, so we check the select state based on the mode
471+
switch (selectMode)
472+
{
473+
case InteractableSelectMode.Single:
474+
Assert.IsTrue(interactable.interactorsSelecting.Count == 1,
475+
"StatefulInteractable should only have 1 selecting interactor.");
476+
Assert.IsTrue(selectExited,
477+
"StatefulInteractable should have had a select exit.");
478+
// Reset to continue testing
479+
selectExited = false;
480+
break;
481+
case InteractableSelectMode.Multiple:
482+
Assert.IsTrue(interactable.interactorsSelecting.Count == 2,
483+
"StatefulInteractable should have 2 selecting interactors.");
484+
Assert.IsFalse(selectExited,
485+
"StatefulInteractable should not have had a select exit.");
486+
break;
487+
default:
488+
Assert.Fail($"Unhandled {nameof(InteractableSelectMode)}={selectMode}");
489+
break;
490+
}
491+
492+
TestHand firstReleasedHand;
493+
TestHand secondReleasedHand;
494+
if (releaseInSelectOrder)
495+
{
496+
firstReleasedHand = rightHand;
497+
secondReleasedHand = leftHand;
498+
}
499+
else
500+
{
501+
firstReleasedHand = leftHand;
502+
secondReleasedHand = rightHand;
503+
}
504+
505+
// Release a hand to deselect the cube
506+
yield return firstReleasedHand.SetHandshape(HandshapeId.Open);
507+
yield return RuntimeTestUtilities.WaitForUpdates();
508+
509+
Assert.IsFalse(selectEntered,
510+
"StatefulInteractable should not have had a select enter.");
511+
512+
// The first hand was no longer selecting in Single mode
513+
// If we're releasing in the reverse order we selected,
514+
// releasing the second pinch should release the select fully
515+
if (!releaseInSelectOrder && selectMode == InteractableSelectMode.Single)
516+
{
517+
Assert.IsFalse(interactable.IsRaySelected,
518+
"StatefulInteractable did not get de-RaySelected.");
519+
Assert.IsFalse(interactable.isSelected,
520+
"StatefulInteractable did not get deselected.");
521+
Assert.IsTrue(interactable.interactorsSelecting.Count == 0,
522+
"StatefulInteractable should not have any selecting interactors.");
523+
Assert.IsFalse(isSelected,
524+
"StatefulInteractable should not be selected.");
525+
Assert.IsTrue(selectExited,
526+
"StatefulInteractable should have had a select exit.");
527+
// Reset to continue testing
528+
selectExited = false;
529+
}
530+
// The first hand was no longer selecting in Single mode
531+
// If we're releasing in the same order we selected,
532+
// we should still be selected regardless of the mode
533+
else
534+
{
535+
Assert.IsTrue(interactable.IsRaySelected,
536+
"StatefulInteractable did not stay RaySelected.");
537+
Assert.IsTrue(interactable.isSelected,
538+
"StatefulInteractable did not stay selected.");
539+
Assert.IsTrue(interactable.interactorsSelecting.Count == 1,
540+
"StatefulInteractable should only have 1 selecting interactor.");
541+
Assert.IsTrue(isSelected,
542+
"StatefulInteractable should be selected.");
543+
544+
if (selectMode == InteractableSelectMode.Multiple)
545+
{
546+
Assert.IsTrue(selectExited,
547+
"StatefulInteractable should have had a select exit.");
548+
// Reset to continue testing
549+
selectExited = false;
550+
}
551+
else
552+
{
553+
// This select exit happened when the second hand pinched, releasing the first
554+
Assert.IsFalse(selectExited,
555+
"StatefulInteractable should not have had a select exit.");
556+
}
557+
}
558+
559+
// Release the last hand to deselect the cube
560+
yield return secondReleasedHand.SetHandshape(HandshapeId.Open);
561+
yield return RuntimeTestUtilities.WaitForUpdates();
562+
Assert.IsFalse(interactable.IsRaySelected,
563+
"StatefulInteractable did not get de-RaySelected.");
564+
Assert.IsFalse(interactable.isSelected,
565+
"StatefulInteractable did not get deselected.");
566+
Assert.IsTrue(interactable.interactorsSelecting.Count == 0,
567+
"StatefulInteractable should not have any selecting interactors.");
568+
Assert.IsFalse(isSelected,
569+
"StatefulInteractable should not be selected.");
570+
Assert.IsFalse(selectEntered,
571+
"StatefulInteractable should not have had a select enter.");
572+
573+
if (!releaseInSelectOrder && selectMode == InteractableSelectMode.Single)
574+
{
575+
Assert.IsFalse(selectExited,
576+
"StatefulInteractable should not have had a select exit.");
577+
}
578+
else
579+
{
580+
Assert.IsTrue(selectExited,
581+
"StatefulInteractable should have had a select exit.");
582+
// Reset to continue testing
583+
selectExited = false;
584+
}
585+
586+
yield return RuntimeTestUtilities.WaitForUpdates();
587+
}
588+
318589
/// <summary>
319590
/// A dummy interactor used to test basic selection/toggle logic.
320591
/// </summary>

0 commit comments

Comments
 (0)