public void RotateTo_DoesNotOvershoot(RotationSpeed speed)
        {
            var rotorPair = new MockFacingRotorPair {
                CurrentAngleDegrees = 20
            };

            var module = new EngineModule("Test", rotorPair, new RotorLimits(0, 130), new IMyThrust[0]);

            // 10% frame jitter.
            const float jitter = RotationSpeed.TimeStepSeconds * 0.1f;
            var         random = new Random();

            var operation       = module.RotateTo(90, speed);
            var rotorVelocities = new List <float>();

            // First iteration performs setup, so the clock doesn't need to tick until the next.
            while (operation.MoveNext())
            {
                rotorVelocities.Add(rotorPair.TargetVelocityDegreesPerSecond);
                // Accuracy ranges:
                if (rotorVelocities.Count < speed.TimeTargetSeconds * 6)
                {
                    // No target within deadline.
                }
                else if (rotorVelocities.Count < speed.TimeTargetSeconds * 12)
                {
                    // After deadline, expect to be within 5 degrees.
                    Assert.That(rotorPair.CurrentAngleDegrees, Is.EqualTo(90).Within(5f));
                }
                else
                {
                    // After double deadline, expect to be within 0.1 degrees.
                    Assert.That(rotorPair.CurrentAngleDegrees, Is.EqualTo(90).Within(0.1f));
                }
                if (!operation.Current)
                {
                    break;                      // Completed.
                }
                var step = TimeSpan.FromSeconds(MathHelper.Lerp(-jitter, jitter, random.NextDouble()) + RotationSpeed.TimeStepSeconds);
                rotorPair.Step(step);
                Clock.AddTime(step);
            }

            TestContext.WriteLine("Iterations: {0} ({1} sec)", rotorVelocities.Count, rotorVelocities.Count / 6);
            Assert.That(rotorPair.CurrentAngleDegrees, Is.EqualTo(90).Within(0.1f));
            Assert.That(rotorVelocities, Has.None.Negative);
            Assert.That(rotorVelocities, Has.Count.LessThanOrEqualTo(60));
        }
        public void RotatesAllModulesFasterWhenTriggeredTwice()
        {
            var rotorPairs = new[]
            {
                new MockFacingRotorPair {
                    CurrentAngleDegrees = 20
                },
                new MockFacingRotorPair {
                    CurrentAngleDegrees = 20
                },
                new MockFacingRotorPair {
                    CurrentAngleDegrees = 20
                },
            };
            var modules = rotorPairs.Select((r, i) => new EngineModule(i.ToString(), r, new RotorLimits(0, 130), new IMyThrust[0])).ToArray();

            var tier = new EngineTier(modules, new[] { new StaticState.EnginePresetDef("Test", 90) }, 0);

            // 10% frame jitter.
            const float jitter = RotationSpeed.TimeStepSeconds * 0.1f;
            var         random = new Random();

            Assert.That(tier.ActivatePreset("Test"), Is.True);

            var iterations = 0;

            while (tier.Run())
            {
                if (iterations == 6)
                {
                    // After 1 second, activate the preset again.
                    Assert.That(tier.ActivatePreset("Test"), Is.True);
                }
                var step = TimeSpan.FromSeconds(MathHelper.Lerp(-jitter, jitter, random.NextDouble()) + RotationSpeed.TimeStepSeconds);
                foreach (var rotorPair in rotorPairs)
                {
                    rotorPair.Step(step);
                }
                Clock.AddTime(step);
                iterations++;
            }

            TestContext.WriteLine("Iterations: {0} ({1} sec)", iterations, iterations / 6);
            Assert.That(rotorPairs.Select(r => r.CurrentAngleDegrees), Has.All.EqualTo(90).Within(0.1f));
            Assert.That(iterations, Is.LessThanOrEqualTo(30));
        }