Agile Modellierung mit
UML
Loading

8.3 Behandlung der Zeit

In verteilten Echtzeitsystemen spielt die kontinuierlich voranschreitende Zeit eine wesentliche Rolle. Im Auktionssystem wird zum Beispiel anhand der aktuellen Zeit entschieden, in welchen Zustand eine Auktion übergehen soll, ob ein Gebot angenommen oder abgelehnt wird oder ob Mitteilungen an die Bieter versandt werden.

In Java kann mit System.currentTimeMillis() die aktuelle Zeit in Form von Millisekunden ermittelt werden. Je nach Laufzeit eines Tests werden so auch innerhalb eines Tests unterschiedliche Zeiten und Zeitdifferenzen gemessen, die das Testergebnis beeinflussen. Beispielsweise differiert dann bereits die Protokollausgabe in Abhängigkeit des Zeitraums, in dem ein Test ausgeführt wird.

Durch die Einbeziehung der aktuellen Zeit geht die in Kapitel 6 geforderte Determiniertheit des Testablaufs verloren. Deshalb ist es notwendig, die während des Testablaufs herrschende Zeit durch den Testtreiber zu kontrollieren. Als Nebeneffekt können damit auch Tests, die sich in der echten Zeitrechnung über mehrere Stunden hinziehen, wie etwa eine gesamte Auktion, effektiv in Sekundenbruchteilen durchgeführt werden.

8.3.1 Simulation der Zeit im Dummy

Aufgrund der genannten Überlegungen wird die Abfrage nach der Zeit in einer eigenen Klasse konzentriert und durch ein voreinstellbares Dummy-Objekt kontrollierbar gemacht. Abbildung 8.13 beschreibt diese Konstruktion mit Ausschnitten der Dummy-Klasse. Im Auktionsprojekt wurde zusätzlich das Muster für Singletons hinter statischen Methoden aus Tabelle 8.9 angewandt.

       Java   
 class Time  {
  Time() {  }
  long now() {            // Holt die aktuelle Systemzeit
    return System.currentTimeMillis();
  }
}
 
class TimeDummy extends Time {
  TimeDummy(long time) {
    storedTime = time;
  }
  long now() {
    return storedTime;    // Liefert die gespeicherte statt der echten Zeit
  }
  void setTime(long time) {
    storedTime = time;    // Erlaubt es, die Zeit zu verändern
  }
  void incTime() {
    storedTime++;         // Erhöhung um 1msek
  }
}
Abbildung 8.13: Simulation der Zeit durch TimeDummy

Die Funktion setTime erlaubt es Testtreibern, die Zeit beliebig zu besetzen. Allerdings bleibt während der Durchführung eines Tests die Zeit konstant. Dies entspricht einer idealisierenden Annahme, dass während der Berechnung einer Reaktion keine tatsächliche Zeit vergeht, die zum Beispiel in Sprachen für eingebettete Systeme wie Esterel [Hal93] angewandt wird. Tatsächlich fordert das vorgeschlagene Testmuster Einschränkungen bezüglich der Verwendung der angeforderten Zeit. Idealerweise sollte nur eine Zeitanfrage stattfinden, die dann als Referenzzeit während der Berechnung genutzt wird. Sollte es notwendig sein, dass innerhalb des Rumpfs einer Methode die Zeit voranschreitet, so kann durch Aufruf von incTime() bei jeder Anfrage von now() ein Voranschreiten simuliert werden.

8.3.2 Variable Zeiteinstellung im Sequenzdiagramm

Parallel zum Einsatz der Zeitsimulation kann ein Sequenzdiagramm genutzt werden, um die Uhr für jeden neuen Aufruf des Testlings umzustellen. Beispielsweise kann das Sequenzdiagramm aus Abbildung 7.12 so erweitert werden, dass die Zeit jeweils explizit gesetzt wird. Es entsteht das Sequenzdiagramm aus Abbildung 8.14.


Abbildung 8.14: Besetzung der Zeit für jeden Aufruf des Testlings

Durch die Verwendung von Merkmalen für Methodenaufrufe lassen sich diese Zeitangaben kompakter modellieren. Im Testsystem werden diese zeitlichen Merkmale konstruktiv interpretiert, indem sie nicht gemessen, sondern als Vorgabe für die jeweils gültige Systemzeit verwendet werden. Damit lässt sich eine mit Systemzeiten annotierte Form eines Auktionsablaufs in Abbildung 8.15 darstellen.


Abbildung 8.15: Annotation als Zeitvorgabe

Die Instrumentierung des Testlings für die Beobachtung von Methodenaufrufen und Returns erlaubt damit gleichzeitig die Anpassung der jeweils aktuellen Zeit. Tabelle 8.16 beschreibt die Anwendung des Merkmals {time} und seiner additiven Form {time+}.



Merkmal {time}


Modellelement

Interaktionen im Sequenzdiagramm.


Motivation

Das Merkmal {time} gibt in einem Sequenzdiagramm vor, welche Zeit ab Aufruf einer Methode beziehungsweise eines Returns gilt. Damit kann die Systemzeit simuliert werden.


Rahmenbedingung

Um die Vorgabe der Zeit umzusetzen, muss das Sequenzdiagramm als Testtreiber in einer Umgebung mit simulierter Zeit eingesetzt werden.

 

Die Uhrzeiten müssen mit der Zeitlinie nach unten größer werden.

 

Die Vorgabe von Zeiten ist im konstruktiven Sequenzdiagramm in der Interpretation mit dem Stereotyp match:free verboten.


Wirkung

Der instrumentierte Produktionscode setzt die jeweils angegebene Zeit, wenn der entsprechende Aufruf beziehungsweise das Return erfolgt. Wie nachfolgend in dem in Tabelle 8.17 beschriebenen Muster zur Simulation der Zeit festgelegt, bleibt diese Zeit bis zum nächsten Merkmal konstant.

 

Das Merkmal {time} erlaubt als Argument verschiedene Datums- und Zeitformate, die zur Beschreibung der zu setzenden Zeit gelten. Fehlt das Datum, so wird der bereits gültige Tag beibehalten. Es ist möglich, nicht nur konstante Werte anzugeben, sondern in Berechnungen auch Variablen- und Attributwerte einzubeziehen.

 

Die Variante {time+} erlaubt es, relative Zeitangaben vorzunehmen, die ebenfalls in verschiedenen Formaten angegeben sein können.


Beispiel

Abbildung 8.15 nutzt diese Merkmale.



Tabelle 8.16: Merkmal {time}

8.3.3 Muster zur Simulation von Zeit

Das diskutierte Muster zur Behandlung der Zeitproblematik wird in Tabelle 8.17 zusammengefasst.



Muster: Simulation von Zeit


Intention

Um deterministische Ergebnisse bei Tests zeitabhängigen Verhaltens sicherzustellen, wird die Zeit simuliert. Während des Ablaufs des Testlings kann die Zeit vom Treiber geändert werden.


Motivation

Zeitabhängiges Verhalten kann damit simuliert werden und wird wiederholbar deterministisch.


Anwendung

Eine Anwendung dieses Musters ist sinnvoll, wenn das Systemverhalten von der aktuellen Zeit abhängt, wie zum Beispiel der Protokollierung von Vorgängen, oder auch für Tests von Interaktionsmustern, die Timeouts besitzen.


Struktur

Siehe Abbildung 8.13. Zur Anwendung des Musters stehen zum Beispiel Sequenzdiagramme wie in Abbildung 8.15 zur Verfügung, die durch Merkmale die jeweils aktuelle Zeit bestimmen.


Implementierung

Siehe Abbildung 8.13. Typischerweise wird dies kombiniert mit dem in Tabelle 8.9 beschriebenen Muster zur Kapselung eines Singletons hinter statischen Methoden, so dass Time.now() als Aufruf verwendet werden kann.


Beachtenswert

Eine Implementierung darf nicht davon ausgehen, dass die Zeit während ihrer Aktivität voranschreitet. Insbesondere dürfen keine Warteschleifen der Form t=Time.now(); while(Time.now()<t+1000); verwendet werden.



Tabelle 8.17: Muster: Simulation von Zeit

Eine Verfeinerung dieses Konzepts wurde im Auktionsprojekt eingesetzt, indem bei Testläufen mehrere Clients und der Server im gemeinsamen Prozessraum laufen. Da Clients unterschiedliche Systemzeiten besitzen sowie Signallaufzeiten im Internet nicht zu unterschätzen sind, wurde für jeden Client eine eigene Zeit simuliert, indem beim Wechsel der Aktivität zwischen Clients die Zeit jeweils umgesetzt wurde. Dazu wurde das in Abschnitt 8.5 besprochene Muster 8.25 eingesetzt.

Damit lassen sich auch relativistische Phänomene modellieren und zum Beispiel die (notwendigerweise nur ungefähre und netzbedingt manchmal unzureichende) Zeitsynchronisation zwischen Clients und Server testen, denn eines der im Internet kritischen Probleme resultiert aus den signifikanten und teilweise sehr unterschiedlichen Signallaufzeiten, die sich bei einer Echtzeitanwendung, wie es beim Auktionssystem der Fall ist, bemerkbar macht.

8.3.4 Timer

Eng verwandt mit der Abfrage der aktuellen Zeit ist die Überwachung von Zeitüberschreitungen. Diese sind grundsätzlich mit denselben Prinzipien realisierbar. Typischerweise werden Timer für jede Aufgabe neu erzeugt, weshalb eine Timer-Factory als globales Singleton realisiert wird. Timeouts können so gezielt erwirkt oder verhindert und durch Timer oder direkt vom Testtreiber übermittelt werden.


Bernhard Rumpe. Agile Modellierung mit UML. Springer 2012