2

我正在使用 .Schedule(DateTimeOffset, Action>) 东西处理 RX 调度程序类。基本上我有一个预定的动作,可以再次安排自己。

代码:

public SomeObject(IScheduler sch, Action variableAmountofTime)
{
    this.sch = sch;
    sch.Schedule(GetNextTime(), (Action<DateTimeOffset> runAgain =>
    {
        //Something that takes an unknown variable amount of time.
        variableAmountofTime();

        runAgain(GetNextTime());
    });
}

public DateTimeOffset GetNextTime()
{
    //Return some time offset based on scheduler's 
    //current time which is irregular based on other inputs that i have left out.
    return this.sch.now.AddMinutes(1);
}

我的问题是关于模拟 variableAmountofTime 可能花费的时间量并测试我的代码是否按预期运行并且只触发按预期调用它。

我已经尝试在委托内推进测试调度程序的时间,但这不起作用。我编写的代码示例不起作用。假设 GetNextTime() 只是安排一分钟。

[Test]
public void TestCallsAppropriateNumberOfTimes()
{
    var sch = new TestScheduler();

    var timesCalled = 0;

    var variableAmountOfTime = () => 
        { 
            sch.AdvanceBy(TimeSpan.FromMinutes(3).Ticks); 
            timescalled++; 
        };

    var someObject = new SomeObject(sch, variableAmountOfTime);

    sch.AdvanceTo(TimeSpan.FromMinutes(3).Ticks);

    Assert.That(timescalled, Is.EqualTo(1));
}

由于我想在未来 3 分钟内执行,但执行需要 3 分钟,我希望看到这只触发 1 次......而不是触发 3 次。

如何使用测试调度程序模拟执行过程中需要时间的东西。

4

1 回答 1

7

好问题。不幸的是,目前Rx v1.x 和 Rx v2.0 Beta不支持此功能(但请继续阅读)。让我向您解释嵌套 Advance* 调用的复杂性。

基本上,Advance* 意味着启动调度程序以运行工作直到指定的点。这涉及在表示虚拟调度程序中时间流的单个逻辑线程上按顺序运行工作。允许嵌套 Advance* 调用会引发一些问题。

首先,嵌套的 Advance* 调用是否应该导致嵌套的工作循环运行?如果是这种情况,我们将不再模拟单个逻辑执行线程,因为当前工作项将被中断以支持运行内部循环。事实上,Advance* 会导致隐式让步,即在处理完所有嵌套工作之前,不允许运行 Advance* 调用之后的其余工作(现在到期)。这导致未来的工作不能依赖(或等待)过去的工作来完成其执行的情况。一种出路是引入真正的物理并发,它从一开始就打败了虚拟时间和历史调度器的各种设计点。

或者,如果嵌套的 Advance* 调用以某种方式与最顶层的工作循环调度调用(Advance* 或 Start)通信,它可能需要延长其到期时间,因为嵌套调用已要求前进到超出原始到期时间的点。现在各种事情都变得奇怪了。时钟不会反映从 Advance* 返回后的更改,并且最顶部的呼叫不再在可预测的时间结束。

对于Rx v2.0 RC(下个月推出),我们查看了这种情况并决定 Advance* 不是模拟“时间滑移”的正确方法,因为它需要一个重载的含义,具体取决于调用它的上下文. 取而代之的是,我们引入了一种Sleep 方法,该方法可用于从任何上下文中向前滑动时间,而不会产生运行工作的副作用。将其视为设置 Clock 属性的一种方式,但可以防止时间倒流。这个名字也清楚地反映了意图。

除了上述之外,为了减少嵌套 Advance* 调用无效的意外因素,我们让它检测到这种情况并在嵌套上下文中抛出InvalidOperationException 。另一方面,睡眠可以从任何地方调用。

最后一点。事实证明,在处理时间方面,我们需要与在 Rx v2.0 RC 中所做的工作完全相同的功能。一些测试需要一种确定性的方法来模拟时间滑移,因为用户代码的执行可能需要任意长的时间(想想 OnNext 处理程序,例如 Observable.Interval)。

希望这会有所帮助……请继续关注我们在接下来的几周内发布的 Rx v2.0 RC 版本!

-巴特(Rx 团队)

于 2012-05-28T09:07:26.130 回答