2 Stimmen

Reaktive Erweiterungen Test Scheduler Simulation des Zeitablaufs

Ich arbeite mit RX-Scheduler-Klassen mit dem .Schedule(DateTimeOffset, Action>) Zeug. Im Grunde habe ich eine geplante Aktion, die sich selbst wieder planen kann.

Code:

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);
}

Meine Frage bezieht sich auf die Simulation der Zeitspanne, die variableAmountofTime benötigen könnte, und auf das Testen, dass sich mein Code wie erwartet verhält und nur den Aufruf wie erwartet auslöst.

Ich habe versucht, die Zeit des Testplaners innerhalb des Delegaten zu verlängern, aber das funktioniert nicht. Beispiel für Code, den ich geschrieben habe und der nicht funktioniert. Angenommen, GetNextTime() ist nur eine Minute aus planen.

[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));
}

Da ich 3 Minuten in die Zukunft gehen möchte, die Ausführung aber 3 Minuten dauert, möchte ich, dass dies nur 1 Mal ausgelöst wird stattdessen wird es 3 Mal ausgelöst.

Wie kann ich simulieren, dass etwas während der Ausführung Zeit in Anspruch nimmt, wenn ich den Test Scheduler verwende?

7voto

Bart De Smet Punkte 776

Gute Frage. Leider ist dies derzeit nicht unterstützt in Rx v1.x und Rx v2.0 Beta ( aber lesen Sie weiter ). Lassen Sie mich Ihnen die Komplikation von verschachtelten Advance*-Aufrufen erklären.

Im Grunde bedeutet Advance*, dass der Scheduler gestartet wird, um die Arbeit bis zum angegebenen Zeitpunkt auszuführen. Dabei wird die Arbeit der Reihe nach in einem einzigen logischen Thread ausgeführt, der den Zeitablauf im virtuellen Scheduler darstellt. Das Zulassen von verschachtelten Advance*-Aufrufen wirft einige Fragen auf.

Erstens, sollte ein verschachtelter Advance*-Aufruf dazu führen, dass eine verschachtelte Arbeitsschleife ausgeführt wird? Wenn das der Fall wäre, würden wir nicht länger einen einzelnen logischen Ausführungsstrang imitieren, da das aktuelle Workitem zugunsten der Ausführung der inneren Schleife unterbrochen würde. Tatsächlich würde Advance* zu einem impliziten Yield führen, bei dem der Rest der Arbeit (der jetzt fällig wäre) nach dem Advance*-Aufruf nicht ausgeführt werden dürfte, bis alle verschachtelten Arbeiten verarbeitet wurden. Dies führt dazu, dass die zukünftige Arbeit nicht von der vergangenen Arbeit abhängen kann (oder darauf warten muss), dass diese ihre Ausführung beendet. Ein Ausweg besteht darin, echte physische Gleichzeitigkeit einzuführen, was verschiedene Designpunkte der virtuellen Zeit- und historischen Scheduler von vornherein zunichte macht.

Sollte ein verschachtelter Advance*-Aufruf dem obersten Worker-Loop-Dispatching-Aufruf (Advance* oder Start) irgendwie mitteilen, dass er seine Fälligkeitszeit verlängern muss, weil ein verschachtelter Aufruf darum gebeten hat, bis zu einem Punkt jenseits der ursprünglichen Fälligkeitszeit vorzurücken. Jetzt werden allerdings alle möglichen Dinge seltsam. Die Uhr spiegelt die Änderungen nach der Rückkehr von Advance* nicht wider, und der oberste Aufruf wird nicht mehr zu einer vorhersehbaren Zeit beendet.

Para Rx v2.0 RC (nächsten Monat) haben wir uns dieses Szenario angeschaut und beschlossen, dass Advance* nicht das Richtige ist, um "Zeitverschiebung" zu emulieren, da es eine überladene Bedeutung braucht, die vom Kontext abhängt, in dem es aufgerufen wird. Stattdessen führen wir eine Schlaf-Methode die dazu verwendet werden kann, die Zeit aus jedem Kontext heraus nach vorne zu verschieben, ohne dass dies zu einer Verzögerung der Arbeit führt. Stellen Sie sich das als eine Möglichkeit vor, die Eigenschaft "Uhr" zu setzen, aber mit einer Absicherung gegen das Zurückgehen in der Zeit. Auch der Name spiegelt die Absicht klar wider.

Um den Überraschungseffekt zu verringern, dass verschachtelte Advance*-Aufrufe keine Wirkung zeigen, haben wir außerdem dafür gesorgt, dass das Programm diese Situation erkennt und eine InvalidOperationException in einem verschachtelten Kontext. Sleep hingegen kann von überall aus aufgerufen werden.

Eine letzte Anmerkung. Es hat sich herausgestellt, dass wir genau die gleiche Funktion für unsere Arbeit in Rx v2.0 RC benötigen, und zwar im Hinblick auf unsere Behandlung von Zeit. Mehrere Tests erforderten eine deterministische Methode zur Emulation von Zeitverschiebungen aufgrund der Ausführung von Anwendercode, der beliebig lange dauern kann (man denke an den OnNext-Handler für z. B. Observable.Interval).

Ich hoffe, das hilft... Bleiben Sie dran für unsere Rx v2.0 RC Release in den nächsten Wochen!

-Bart (Rx-Team)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X