public bool TryGetDatareader(IInteraction parameters, IInteraction until, out TextReader reader)
        {
            IInteraction candidate;
            bool         success;

            if (IsForwardSourcing)
            {
                MemoryStream dataTarget = new MemoryStream();
                SimpleOutgoingInteraction dataTargetInteraction;
                dataTargetInteraction = new SimpleOutgoingInteraction(
                    dataTarget, this.Encoding, parameters);

                success = ForwardSource.TryProcess(dataTargetInteraction);

                dataTargetInteraction.Done();
                dataTarget.Position = 0;

                reader = new StreamReader(dataTarget, this.Encoding);
            }
            else if (IsVariableSourcing)
            {
                string value;

                success = parameters.TryGetFallbackString(this.Variable, out value);

                if (success)
                {
                    reader = new StringReader(value);
                }
                else
                {
                    reader = null;
                }
            }
            else if (parameters.TryGetClosest(typeof(IIncomingBodiedInteraction), until, out candidate))
            {
                IIncomingBodiedInteraction source = (IIncomingBodiedInteraction)candidate;

                success = CheckMimetype(source.ContentType);
                reader  = source.GetIncomingBodyReader();
            }
            else
            {
                success = false;
                reader  = null;
            }

            return(success);
        }
        IEnumerator MoveInDirection(ForwardSource forwardSource)
        {
            // Create a stick control to serve as the input action source for the move provider
            var gamepad = InputSystem.InputSystem.AddDevice <Gamepad>();

            var asset     = ScriptableObject.CreateInstance <InputActionAsset>();
            var actionMap = asset.AddActionMap("Locomotion");
            var action    = actionMap.AddAction("Move",
                                                InputActionType.Value,
                                                "<Gamepad>/leftStick");

            var xrRig           = TestUtilities.CreateXRRig();
            var rigTransform    = xrRig.rig.transform;
            var cameraTransform = xrRig.cameraGameObject.transform;

            // Rotate the camera to face a different direction than rig forward to test
            // that the move provider will move with respect to a selected forward object.
            cameraTransform.Rotate(0f, 45f, 0f);
            var cameraForward = cameraTransform.forward;

            Assert.That(rigTransform.forward, Is.Not.EqualTo(cameraForward).Using(Vector3ComparerWithEqualsOperator.Instance));

            // Create a controller object to serve as another forward source
            var controllerGO = new GameObject("Controller");

            controllerGO.transform.SetParent(xrRig.cameraFloorOffsetObject.transform, false);
            controllerGO.transform.Rotate(0f, -45f, 0f);
            var controllerForward = controllerGO.transform.forward;

            Assert.That(rigTransform.forward, Is.Not.EqualTo(controllerForward).Using(Vector3ComparerWithEqualsOperator.Instance));

            // Config continuous move on XR rig
            var locoSys = xrRig.gameObject.AddComponent <LocomotionSystem>();

            locoSys.xrRig = xrRig;
            var moveProvider = xrRig.gameObject.AddComponent <ActionBasedContinuousMoveProvider>();

            moveProvider.system             = locoSys;
            moveProvider.leftHandMoveAction = new InputActionProperty(action);
            moveProvider.moveSpeed          = 1f;

            switch (forwardSource)
            {
            case ForwardSource.Default:
                break;

            case ForwardSource.Camera:
                moveProvider.forwardSource = xrRig.cameraGameObject.transform;
                break;

            case ForwardSource.Controller:
                moveProvider.forwardSource = controllerGO.transform;
                break;

            default:
                Assert.Fail($"Unhandled {nameof(ForwardSource)}={forwardSource}");
                break;
            }

            // See Script Execution Order diagram https://docs.unity3d.com/Manual/ExecutionOrder.html
            // This test will begin after Update() during the yield null/yield WaitForSeconds/yield StartCoroutine stage.
            // The move provider will process input during Update() of the next frame, and scale the move based on Time.deltaTime.
            // After yielding for 1 second with the stick pushed forward, the stick will be released back to center.
            // The move provider will process the release during Update() of the next frame, and should not apply any more movement.

            // Partially push stick directly forward.
            // This tests that the move speed will be scaled by the input magnitude.
            var input          = new Vector2(0f, 0.5f);
            var processedInput = new StickDeadzoneProcessor().Process(input);

            Set(gamepad.leftStick, input);
            var startTime = Time.time;

            yield return(new WaitForSeconds(1f));

            var actualPosition   = rigTransform.position;
            var actualDistance   = Vector3.Distance(Vector3.zero, actualPosition);
            var expectedDistance = processedInput.magnitude * moveProvider.moveSpeed * (Time.time - startTime);

            Assert.That(actualDistance, Is.EqualTo(expectedDistance).Within(1e-5f));

            switch (forwardSource)
            {
            case ForwardSource.Default:
            case ForwardSource.Camera:
                Assert.That(actualPosition, Is.EqualTo(cameraForward * expectedDistance).Using(Vector3ComparerWithEqualsOperator.Instance));
                break;

            case ForwardSource.Controller:
                Assert.That(actualPosition, Is.EqualTo(controllerForward * expectedDistance).Using(Vector3ComparerWithEqualsOperator.Instance));
                break;

            default:
                Assert.Fail($"Unhandled {nameof(ForwardSource)}={forwardSource}");
                break;
            }

            // Stop moving
            Set(gamepad.leftStick, Vector2.zero);

            yield return(new WaitForSeconds(0.1f));

            // ReSharper disable Unity.InefficientPropertyAccess -- Property value accessed after yield
            Assert.That(Vector3.Distance(actualPosition, rigTransform.position), Is.EqualTo(0f));
            // ReSharper restore Unity.InefficientPropertyAccess
        }