Skip to content

Commit 1ce1f9c

Browse files
committed
Add a new test to check various select modes and select/deselect ordering with events
1 parent d05e50f commit 1ce1f9c

1 file changed

Lines changed: 271 additions & 0 deletions

File tree

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

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,277 @@ public IEnumerator GazePinchSmokeTest()
310310
Assert.IsTrue(interactable.IsGazePinchHovered);
311311
}
312312

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

0 commit comments

Comments
 (0)