public void YieldsCorrectValues()
    {
        IEnumerator create()
        {
            yield return(1);

            yield return(2);

            yield return(3);
        }

        var routine = default(Finalizable);
        var stack   = DurationStackFactory.Create(
            _ => new Finalizable {
            wrapped = create()
        },
            onPull: r => routine = r
            );

        stack.Push(new Effect());

        routine.MoveNext();
        var a = (int)(routine.Current ?? -1);

        routine.MoveNext();
        var b = (int)(routine.Current ?? -1);

        routine.MoveNext();
        var c = (int)(routine.Current ?? -1);

        Assert.AreEqual((1, 2, 3), (a, b, c));
    }
    public void CancelCallsRevertOnRunningEffect()
    {
        IEnumerator create()
        {
            yield return(null);
        };
        var called  = (a : false, b : false, c : false);
        var routine = default(Finalizable);
        var stack   = DurationStackFactory.Create(
            _ => new Finalizable {
            wrapped = create()
        },
            onPull: r => routine = r
            );
        var effects = new Effect[] {
            new Effect(revert: () => called.a = true),
            new Effect(revert: () => called.b = true),
            new Effect(revert: () => called.c = true),
        };

        stack.Push(effects[0]);
        stack.Push(effects[1]);
        stack.Push(effects[2]);
        routine.MoveNext();
        routine.MoveNext();
        stack.Cancel();

        Assert.AreEqual((false, true, false), called);
    }
    public void FinishedEffectRemovedFromEffects()
    {
        IEnumerator create()
        {
            yield return(null);

            yield return(null);

            yield return(null);
        }

        var routine = default(Finalizable);
        var stack   = DurationStackFactory.Create(
            _ => new Finalizable {
            wrapped = create()
        },
            onPull: r => routine = r
            );

        stack.Push(new Effect());

        routine.MoveNext();
        routine.MoveNext();
        routine.MoveNext();
        routine.MoveNext();

        Assert.IsEmpty(stack.Effects);
    }
    public void FirstRoutinesAlsoYieldsSubsequentEffects()
    {
        var called = 0;

        IEnumerator create()
        {
            ++called;
            yield return(null);

            ++called;
            yield return(null);

            ++called;
        };
        var routine = default(Finalizable);
        var stack   = DurationStackFactory.Create(
            _ => new Finalizable {
            wrapped = create()
        },
            onPull: r => routine = r
            );

        stack.Push(new Effect());

        routine.MoveNext();

        stack.Push(new Effect());

        routine.MoveNext();
        routine.MoveNext();
        routine.MoveNext();
        routine.MoveNext();

        Assert.AreEqual(6, called);
    }
    public void EmptyPushDoesNotThrow()
    {
        IEnumerator create()
        {
            yield break;
        };
        var stack = DurationStackFactory.Create(_ => new Finalizable {
            wrapped = create()
        });

        Assert.DoesNotThrow(() => stack.Push(new Effect()));
    }
    public void CancelWithNoEffectDoesNotThrow()
    {
        IEnumerator create()
        {
            yield return(null);
        };
        var stack = DurationStackFactory.Create(_ => new Finalizable {
            wrapped = create()
        });

        Assert.DoesNotThrow(() => stack.Cancel());
    }
    public void NoOnCancelWhenNoCancel()
    {
        IEnumerator create()
        {
            yield break;
        };
        var called = false;
        var stack  = DurationStackFactory.Create(
            _ => new Finalizable {
            wrapped = create()
        },
            onCancel: _ => called = true
            );

        stack.Push(new Effect());

        Assert.False(called);
    }
    public void Effects()
    {
        IEnumerator create()
        {
            yield break;
        };
        var stack = DurationStackFactory.Create(_ => new Finalizable {
            wrapped = create()
        });
        var effects = new Effect[] {
            new Effect(),
            new Effect(),
        };

        stack.Push(effects[0]);
        stack.Push(effects[1]);

        CollectionAssert.AreEqual(effects, stack.Effects);
    }
    public void RoutineFromEffect()
    {
        IEnumerator create(Effect effect)
        {
            effect.Apply();
            yield break;
        }

        var called = false;
        var stack  = DurationStackFactory.Create(
            e => new Finalizable {
            wrapped = create(e)
        },
            onPull: r => r.MoveNext()
            );

        stack.Push(new Effect(apply: () => called = true));

        Assert.True(called);
    }
    public void NewEventCallAfterCancel()
    {
        IEnumerator create()
        {
            yield return(null);
        };
        var routines = new List <Finalizable>();
        var stack    = DurationStackFactory.Create(
            _ => new Finalizable {
            wrapped = create()
        },
            onPull: r => routines.Add(r)
            );

        stack.Push(new Effect());
        stack.Cancel();
        stack.Push(new Effect());

        Assert.AreNotSame(routines[0], routines[1]);
    }
    public void CancelCallWithPushRoutine()
    {
        IEnumerator create()
        {
            yield break;
        };
        var routines = (onPush : default(Finalizable), onCancel : default(Finalizable));
        var stack    = DurationStackFactory.Create(
            _ => new Finalizable {
            wrapped = create()
        },
            onPull: r => routines.onPush     = r,
            onCancel: r => routines.onCancel = r
            );

        stack.Push(new Effect());
        stack.Cancel();

        Assert.AreSame(routines.onPush, routines.onCancel);
    }
    public void CancelClearsEffects()
    {
        IEnumerator create()
        {
            yield break;
        };
        var stack = DurationStackFactory.Create(_ => new Finalizable {
            wrapped = create()
        });
        var effects = new Effect[] {
            new Effect(),
            new Effect(),
        };

        stack.Push(effects[0]);
        stack.Push(effects[1]);
        stack.Cancel();

        Assert.IsEmpty(stack.Effects);
    }
    public void OnPullOnlyForFirst()
    {
        IEnumerator create()
        {
            yield return(null);

            yield return(null);
        };
        var called = 0;
        var stack  = DurationStackFactory.Create(
            _ => new Finalizable {
            wrapped = create()
        },
            onPull: _ => ++ called
            );

        stack.Push(new Effect());
        var callA = called;

        stack.Push(new Effect());
        var callB = called;

        Assert.AreEqual((1, 1), (callA, callB));
    }