ALLINSIGHT

Home of the AlmostImplementedException

Wie arbeitet man mit Templates

Eines der Schlüssel-Feature von TTCC-3 sind Templates. Man kann sie zum füllen von Records mit vordefinierten Werten verwenden, oder sie als matching-Tamplates zum überprüfen der erwarteten Werte einsetzen.
Wenn man eine komplexe Struktur hat und in dieser jeden Wert überprüfen möchte, müsste man normalerweise für jeden Wert eine If-Abfrage schreiben. Mit matching-Templates geht dies in einer Zeile.

Zunächst benötigen wir aber eine komplexe Datenstruktur. Dafür verwenden wir einfach den Message-Record aus meinem letzten Post und füllen diesen mithilfe eines Templates.

[ttcn3]
type record Value {
charstring path, charstring value optional
}

type record Message {
charstring file optional,
charstring portTypeName optional,
charstring operationName optional,
charstring bindingName optional,
set of Value values
}

template Message m_messageTemplate:={
portTypeName:=”b”,
operationName:=”c”,
count:=5,
values:={}
}
[/ttcn3]

Die ersten beiden Code-Blöcke sollten jedem klar sein, falls nicht einfach nochmal das vorherige Post lesen. Der letzte Code-Block ist hier der wichtige. Wir definieren ein Template für den Typ Message mit dem Namen m_messageTemplate. Der Prefix m_ (wie vom ETSI empfohlen) wird für Templates verwendet, die einen Datentypen füllen sollen. M steht für Message, da ein Template eine spezifische Nachricht ist (Ich hätte ja t_ erwartet, aber das wird für Timer verwendet).
Wie man sieht funktioniert ein Template genauso wie das direkte befüllen eines Records (siehe meinen letzten Post), kann aber mehrmals verwendet werden.
Und hier ein kleines Snippet wie man sie verwendet:

[ttcn3]
var Message v_variable := m_messageTemplate;
[/ttcn3]

Das ist hilfreich falls man immer die selben Werte in vielen Message-Objekten verwenden will. Aber was macht man wenn sich ein Wert ändern soll? Hier kann man Parameter verwenden!
Templates, genauso wie Funktionen unterstützen Parameter. Lasst uns das gleich einmal an einem Beispiel ausprobieren

[ttcn3]
template Message m_messageTemplate (charstring p_bindingName, charstring p_portTypeName:=”port”):={
portTypeName:=p_portTypeName,
operationName:=”c”,
bindingName:=p_bindingName,
count:=5,
values:={}
}

testcase simpleTest() runs on SimpleType{
var Message v_variable := m_messageTemplate(“test”);
log (v_variable);

v_variable := m_messageTemplate(“test”, “port”);
log (v_variable);

v_variable := m_messageTemplate(p_bindingName:=”test”, p_portTypeName:=”port”);
log (v_variable);
}
[/ttcn3]

Jetzt habe ich zu m_messageTemplate zwei Parameter hinzugefügt. Und wie man sieht benutzen beide den Prefix p_ (see ETSI naming conventions).
Der erste Parameter ist Pflicht, der zweite hat einen Default-Wert und ist somit optional.
Ich habe außerdem den simpleTest aus Dein erstes TTCN-3 Script angepasst, um zu zeigen wie man diese Parameter verwendet.
Der erste Aufruf füllt nur einen Parameter (p_bindingName). Wenn der erste Parameter einen Default-Wert hätte und der zweite nicht, würde dieser Aufruf nicht funktionieren.
Der zweite setzt beide Parameter. Wenn man diesen Weg verwendet, sollte man sicherstellen das niemand die Reihenfolge der Template-Parameter ändert, sonst ändert sich das Ergebnis ohne das man es merkt.
Der letzte Aufruf ist der beste Weg für Template mit vielen (optionalen) Parameter. Hier wird jeder Parameter angegeben der gesetzt werden soll. Wenn man mehrere optionale Parameter hat, kann man so nur die benötigten setzen.

Wenn man nun den values-Teil des Message-Objekts füllen möchte, wird es etwas tricky. Man muss genau wissen wie viele Einträge man benötigt und diese als Parameter anbieten. Da man ein Template oder eine Funktion nicht überladen kann (den selben Namen mit verschiedenen Parametern) muss man diese Parameter als Optional anlegen.

[ttcn3]
template Message m_messageTemplate (
charstring p_bindingName, charstring p_portTypeName:=”port”,
charstring valuePath1:=””, template charstring valueFile1:=omit,
charstring valuePath2:=””, template charstring valueFile2:=omit,
charstring valuePath3:=””, template charstring valueFile3:=omit
):={
portTypeName:=p_portTypeName,
operationName:=”c”,
bindingName:=p_bindingName,
count:=5,
values:={
{valuePath1, valueFile1},
{valuePath2, valueFile2},
{valuePath3, valueFile3}
}
}
[/ttcn3]

Nun habe ich 3 path-file-Paare definiert. WEnn man mehr benötigt, muss man noch Parameter hinzufügen. Da file als Optional definiert wurde, habe ich den dazugehörigen Parameter als template mit dem Default-Wert omit erstellt. Es gibt leider kein Optional-Keyword für Parameter und nur Templates können auf omit gesetzt werden.
Es gibt zwei Probleme mit diesem Ansatz. Der erste ist, das man 2 zusammengehörige Parameter (path und file) unabhängig setzt. Hierfür kann man aber auch das Value-Objekt als Parametertyp verwenden.
Das zweite ist schon eher ein reales Problem. Egal ob man einen Parameter übergibt oder nicht, er wird in values eingetragen. Daher hat man immer 3 Einträge mit einem leere path und ein omit als file.

Um ein values-Record nur mit den Werten zu füllen die man benötigt, muss man eine funktion verwenden um das Template zu erzeugen und brauch noch einige Tricks und Workarounds. Diese stell ich in einem späteren Post vor.

Zum Abschluss möchte ich noch Matching-Templates vorstellen. Sie arbeiten genauso wie die Template oben, können aber Wildcards anstatt konkreter Werte verwenden. Also erzeugen wir doch einfach mal ein Matching-Template für unseren Message-Type und nutzen die selben Parameter wie oben und vergleichen es mit unseren Variablen.
Ich habe bereits im ersten Testfall ein Match verwendet, aber diesmal nutzen wir es mit einem komplexen Typen.

[ttcn3]
template Message mw_messageTemplateExpected (
template charstring p_bindingName, template charstring p_portTypeName:=*,
template charstring valuePath1:=*, template charstring valueFile1:=*,
template charstring valuePath2:=*, template charstring valueFile2:=*
):={
portTypeName:=p_portTypeName,
operationName:=”c”,
bindingName:=p_bindingName,
count:=5,
values:={
{valuePath2, valueFile2},
{valuePath1, valueFile1},
*
}
}

testcase simpleTest() runs on SimpleType{
var Message v_variable := m_messageTemplate(p_bindingName:=”test”, valuePath1:=”path”, valueFile1:=”file”);
var template Message expectedData := mw_messageTemplateExpected(p_bindingName:=”test”, valuePath1:=?, valueFile1:=?);

if (match(v_variable, expectedData)){
setverdict(pass);
}else{
setverdict(fail);
}
}
[/ttcn3]

Diesmal habe ich mw_ als Prefix verwendet (see ETSI naming conventions) , da es ein matching Template ist. Ich deklariere außerdem jeden Parameter als template, um omit, * und ? als Werte verwenden zu können. Mit dem ? sage ich das ich einen Wert erwarte, mir aber der eigentliche Inhalt egal ist (also alles außer omit). Mit * ist mir auch egal ob überhaupt ein Wert vorhanden ist.
In values habe ich außerdem ein * hinzugefügt um anzugeben das noch mehr Einträge vorhanden sein dürfen. Wenn man es entfernt, wird der Vergleich fehlschlagen, da wir 3 Einträge haben.
Ich habe zusätzlich noch die Reihenfolge der Einträge 1 und 2 vertauscht, um zu zeigen das diese keine Rolle spielt.

Wenn du diesen Test ausführt, siehst du einen erfolgreichen Vergleich. !Wichtig! Der match-Befehl verwendet als ersten Parameter die variable und als zweiten das Template. Es können keine 2 Template verglichen oder die Reihenfolge vertauscht werden.
Wenn du die TTWorkcbench verwendest, kannst du auf den match Logeintrag im grafischen Log klicken um die beiden Strukturen (Variable und Template) zu sehen, Warnung! Beide sind in dieser Anzeige vertauscht. Das Template steht links und die Variable rechts.
Diese Tabelle ist sehr hilfreich, da sie Abweichungen anzeigt. Etwas enttäuschend ist die Tatsache, das sie in record of oder set of nicht die konkrete Abweichung anzeigt.

Dies ist alles für diesmal. Bis zum nächsten Post.

Share :

Leave a Reply