 Okay. So, the first question is, do we do this in English or in German? Is there anyone in here who does not understand German? No. Okay, gut. Dann kann ich auch auf Deutsch weitermachen. Er hat es ja auf Englisch eingereicht, aber ist mir letztlich gleich. Wenn wir alle hier deutschsprachig sind, dann können wir das auch auf Deutsch machen. Ja, herzlich willkommen zu meinem Vortrag zu TTCN3 und Eclipse Titan. Es ist ein sehr exotisches Thema. Demnach ist das Publikum auch etwas, die rein sind, etwas lichte. Es freut mich trotzdem, dass so viele Leute hierhergefunden haben. Es ist eine Sprache und ein Thema, was mir erst vor so wirklich vor einem Jahr oder was begegnet ist. Und zwar mit dem Hintergrund. Ich bin ja im Osmokom-Projekt sehr aktiv, wo wir Mobilfunkprotokolle unterschiedlicher Couleur implementieren. Und irgendwann will man die ja auch mal testen auf Funktionalität oder auch eben sowohl die erfolgreichen Fälle als auch die jene Fälle, die eben nicht erfolgreich sein sollen. Und hatte mich eben umgeguckt, was es das alles gibt. Und ich hatte ab und zu schon mal in der Vergangenheit bei der Lektüre von entsprechenden Spezifikationen TTCN irgendwo gelesen. Und hatte das dann immer irgendwie, hatte mal ein bisschen geguckt und festgestellt, da gibt es keine Open Source Compiler und keine Open Source Tools dafür. Also ist es irrelevant irgendwie so für mich und hatte das dann quasi immer links liegen lassen. Das hat sich dann aber eben geändert. Und jetzt gehen wir mal so ein bisschen ins Thema rein. Warum wollen wir Protokolle überhaupt testen, wenn wir jetzt irgendwelche Kommunikationsprotokolle implementieren? Es ist irgendwie klar, gibt es unterschiedliche Motivationen dazu. Man kann das machen, um Konformitätstests zu bestehen, je nach Umgebung und Anwendung jetzt im klassischen Internetbereich nicht unbedingt so weit verbreitet, im klassischen Telekombereich schon sehr viel mehr. Interoperabilität ist natürlich immer wichtig. Da kann ich Interop-Tests machen mit unterschiedlichen Implementationen, aber ich kann natürlich auch versuchen, das nachzustellen in Tests. Security ist auch immer ein Grund, warum Protokoll-Tests wichtig sind. Ganz klar, Regression-Testing sowieso. Ich will Fehler, die ich irgendwo mal gesehen habe in Testcases nachstellen, um sicherzustellen, dass ich in Zukunft nicht mehr nochmal den gleichen Fehler in meiner Implementation sehe. Performance-Tests sind natürlich auch Protokoll-Tests, die mit dazu zählen. Das kann man natürlich in jeder beliebigen Methode machen. Man kann die in jeder Programmiersprache schreiben, kann die mit jedem Tool schreiben, was einem irgendwie so vor die Füße kommt. Der einfachste Weg ist natürlich, wenn das symmetrische Protokolle sind, die Implementation gegen sich selbst zu testen, ist nur leider nicht sonderlich gut, wenn man den Fehler in der Implementation hat, weil dann funktioniert es, aber es spricht halt mit niemandem anders. Das ist uns in OsmoCom auch öfter mal passiert. Das funktioniert dann prima, solange man beide Elemente, die da miteinander reden, selber geschrieben hat und beide Male genau gleich falsch gedacht hat. Aber in dem Moment, wo man dann irgendwie andere Geräte oder andere Implementationen dagegen testen will, funktioniert das nicht mehr. Testen gegen Weierschark ist halt auch immer so ein problembehaffter Ansatz, weil Weierschark halt auch leider sehr oft erstens viel toleranter ist als jegliches Bex und zweitens auch einfach unglaublich oft kaputt ist. Also ich habe schon, also in diesem Jahr glaube ich schon wieder drei Bug Fixes irgendwie gefunden und eingereicht, was dann Weierschark kaputt war, wo man sich dann irgendwie, man denkt sich irgendwie, man hat Fehler gemacht, aber nein, Weierschark macht es einfach falsch. Ist natürlich auch nicht immer richtig. Dann kann man anfangen, das selber zu implementieren. Ja, natürlich Peissen ist als Scrippsprache der Wahl in diesem Jahrtausend oder so, wahrscheinlich weit verbreitet. Escapee ist ja ein Framework, was viele kennen, um irgendwie Pakete zu generieren, zu senden, irgendwie Fussing oder Ähnliches zu tun. Kann man machen, muss man natürlich immer erst mal die Protokolle implementieren. Also man implementiert es erst mal irgendwo in seinem eigentlichen Target und dann will man das testen, dann muss man es noch mal irgendwo implementieren. Erlang finde ich auch einen sehr guten Match eigentlich für solche Themen, weil man einen unglaublich fähigen und unglaublich universell einsetzbaren Binary-Encoder-Decoder hat. Also wenn man in Erlang irgendwelche Binärdachrichten zerlegen muss in abstrakte Daten oder umgekehrt, wie wieder abstrakte Daten in Binärdaten umsetzen muss, hat man da sehr gute Sprachfeatures. Kann man also toll was tun. Im Linux Kernelbereich ist sehr weit verbreitet ein Tool namens Packet Drill, was glaube ich Google ursprünglich mal entwickelt hatte, mit dem sehr viel getestet wird. Da kann ich halt im Prinzip so ein bisschen den Text, so was ähnliches wie den Text-Output von TCP-Dunk nehmen und damit wieder TCP-Pakete zusammenbauen. Also ich habe ein textuelles Format, mit dem ich mir speziell TCP-IP-Pakete beschreiben kann und die können dann durch Packet Drill replayed werden und damit kann ich dann mein TCP-Stack im Kernel testen. Es ist aber halt sehr spezifisch für derartige Protokolle relevant. Ja, das kann man alles tun, wird ja auch alles getan. Ja, dieses Leiter hatte ich jetzt gerade schon ein bisschen darauf abgestellt. Wir haben sehr viele Telekom-Protokolle in OsmoCom implementiert. Ja, ich weiß gar nicht, wie viele Dutzende, die wir da insgesamt implementieren mussten auf den unterschiedlichen Schnittstellen. Ja, und die Frage ist mit, was macht man das irgendwie sinnvoller und automatischer? Ja, es geht mir hierbei vor allem um Funktionales-Testen, nicht zu sehr um Performance. Also um irgendwie, sag ich mal, Performance zu testen, da kann man andere Werkzeuge dann wieder verwenden. Da nimmt man vielleicht eher eine echte produktive Implementation gegen die man testet. Aber Funktionales-Testing, wo ich halt Sequenzen und Transaktionen von Solverhalten, aber auch von abnormalen Verhalten irgendwie nachstellen kann und damit die Korrektheit oder halt Speck-Comformance, Speck-Compliance, meine Implementation testen kann. Und das ideale Testwerkzeug, was man sich da so vorstellen kann, erlaubt halt einem in sehr produktiver und ausdrückstarker Weise Encoding, Decoding von Paketen und Nachrichtenformaten auszudrücken. Ja, das ist sicherlich einer der wesentlichen Aspekte, weil letztlich habe ich irgendwo ein Aspekt, die sagt mir, wie es mein Header oder mein Information-Element oder was auch immer aufgebaut, was ich da generieren muss jetzt aus meinem Test. Ich will da ja nicht irgendwie eine Hex-Kolonne irgendwie mir von Hand zusammenfummeln oder irgendwelche Bitfelder, sondern man will ja irgendwie das in einer einfachen Art und Weise ausdrücken können, was da zusammengebaut werden soll, wenn ich jetzt ein Testpaket senden möchte an so ein Protokollstack oder eine Implementation. Ja, dann habe ich das umgekehrte Problem. Wenn mir dann meine Implementation-Undertest, die ich testen möchte, irgendwas zurücksendet, muss ich das ja irgendwie matchen können. Das heißt, ich brauche Pattern-Matching, sehr gute Möglichkeit da drauf zu matchen. Ich kann ja nicht irgendwie einfach binary 1 zu 1 Dinge vergleichen, weil vielleicht gibt es irgendwie einen Identifier, den die Implementation-Undertest zufällig auswählt. Ich kann das nicht einfach im Mem-Compare machen, sondern ich muss damit erwarten, dass bestimmte Felder werden einfach von der Gegenstelle definiert. Andere sind irgendwie constrained, aber können halt noch irgendwelche Alternativen haben. Ja, also ich kann halt nicht einfach irgendwie fest in Mem-Compare oder in Equals oder sonst irgendwas machen, sondern ich brauche da irgendeine Art von Templating, und Template oder Pattern-Matching, um die Pakete, die ich zurückbekomme, wieder zu vergleichen mit dem Solverhalten. Dann möchte ich nachrichten in der Regel Asynchronen austauschen mit solchen Implementationen, weil halt Dinge nebenläufig sind, parallel passieren können und so weiter. Ja, und TTC3 ist eben was, auf das ich da gestoßen bin. So, was ist nun das? Eine domain-specific Language, die speziell exakt für diesen Anwendungsfall definiert wurde, geht richtig weit zurück. TTCN, also nicht die Version 3, sondern die Vorgänger, wurden also erstmals in 1983 spezifiziert bei der ITU. TTCN3 gibt es auch inzwischen seit 18 Jahren. Es ist also auch alles andere als was Neues. Wird auch tatsächlich sehr ausgiebig benutzt in genau jener Branche, in der wir als Osmo-Com die Exoten sind, dass wir freie Software machen. Aber wenn man eben ein bisschen recherchiert, Ericsson, Nokia und andere große Telekom-Equipment-Supplier benutzen, also nachweislich oder haben zumindest nachweislich TTCN3 benutzt, um ihre ganzen Implementationen irgendwie zu testen. Es ist auch so, dass die Etsy, also das European Telecommunication Standardization Institute, eine ganze Reihe von Testsuits in TTCN3 im Quellcode veröffentlicht hat, die man von der Etsy so bekommt. Also das eine ist ja erst mal eine Sprache, in der ich solche Tests schreiben kann. Das andere sind dann die eigentlichen Testsuits. Da will man ja auch nicht immer das Rad von vorne erfinden und alles von A bis Z implementieren müssen. Und die Etsy hat zum Beispiel für IPv6 Conformance Testing oder für SIP oder Diameter. Also Diameter ist jetzt wieder eher Telekom-Lastik. Also jetzt Morifunklastik, sagen wir mal so, wird auch in anderen Fällen verwendet. IPv6 und SIP ist ja nun jedermann bekannt. Aber auch zum Beispiel die Standards für die biometrischen Ausweise. Da sind auch Kommunikationsprotokolle zwischen einem Ausweislesegrid und dem Ausweis vorhanden. Und da gibt es auch eine Testsuit von der Etsy dazu. Digital Mobile Radio, ein Entschuldige, den Teipo da drin. Also DMR, der Funkstandard, den die Walkie Talkies auch, der CCC Veranstaltungs-GmbH benutzen zum Beispiel. Es sind auch die Conformance-Specs da drin geschrieben. Auch Physix Low-Pen, also gibt es das in den anderen Bereichen auch. Also für MQTT, ich habe auch gesehen, dass es für Autos auch irgendwas gibt, was in TTC N3 irgendwie, also Conformance, ja. Gibt es Leute, die sich das Gesicht sehen, gibt es wohl. Aber eben bis 2015 gab es dafür nur proprietäre Tools, also ob jetzt Entwicklungsumgebung, Compiler, Runtime Engines und so weiter, war alles proprietär. Wie gesagt, demnach für mich irgendwie uninteressant. Es hat sich dann geändert, dass nämlich in 2015 Ericsson ihre interne, bis dahin interne, Implementation, als der Software released hat. Ja, also Ericsson hat intern seit dem Jahr 2000 erstmal so eine Toolchain, Compiler und alle möglichen anderen Tools entwickelt und haben damit dann eben ihre ganzen Telekom-Implementation, ob jetzt irgendwie ein SGSN oder was auch immer getestet und hatten das auch als richtiges Software-Produkt. Also man konnte auch als externe Kunde das Lizenzieren von Ericsson und verwenden, also das haben sie nicht nur intern benutzt und hatten das dann eben 2015 veröffentlicht unter dem Dach der Eclipse Foundation. Das heißt nicht, dass das zwangsläufig was mit der IDE zu tun hat, falls jetzt irgendwie die ersten Leute sich die Haare raufen. Also ich benutze auch keinen Eclipse, um TTC-Ensight-Tests zu schreiben. Das sind ganz normalen Makefiles und Command-Line-Tools. Also da muss man nicht jetzt irgendwie Java-IDIs verwenden. Aber es ist halt ein sehr umfangreiches Projekt. Man sieht da also 1,6 Millionen Zeilen C plus 300.000 Zeilen Java. Das war aber nur der Initialestand. Also es ist inzwischen auch weitergewachsen. Es wird auch maintained. Das ist jetzt nicht so, dass Ericsson das irgendwie gedamped hätte. So von wegen hier ist irgendwie, wir wollen das jetzt nicht mehr machen. Wir schmeißen das mal in die Community und wollen damit nichts mehr zu tun haben, sondern die maintain das genauso wie vorher. Und wenn man das benutzt, man interagiert mit den Entwicklern. Man kann da Fetches einreichen. Wir haben irgendwie ein Gerrit, um Contributions anzunehmen, die fixen Bucks, die man reported. Also es funktioniert das Support nach wie vor da. Und angenehmerweise führt die Leute, die Debian oder Ubuntu verwenden. Man muss nur ein Upget eingeben. Also das Eclipse Titan ist schon als normales Paket im Debian Package Repository drin. Ich weiß nicht, wie es bei anderen Distributionen aussieht. Ist aber auch jetzt nicht so wahnsinnig kompliziert, das von Hand zu bauen. Ja, das heißt, wir haben jetzt eine Toolchain. Und das funktioniert dann wie folgt, wenn man sich das hier anschaut. Ich mach das mal noch ein Tick größer. Ja, wir entwickeln also Code in dieser Sprache, über die wir noch ein bisschen was hören werden im Rahmen dieses Vortrags. Da haben wir dann eine sogenannte RTS, eine Abstract Test Suite. Die haben natürlich auch wieder eigene Namen und Abkürzungen überall. Das ist halt der Quellcode, den man in TTCN3 hat. Den wirft man in den TTCN3-Compiler und kriegt C++ Quellcode generiert. Und den wirft man dann wieder in ganz normalen GCC oder kann man sicherlich auch mit Klang- und LVM-Kompilieren, wenn man das lieber möchte. Hat den angenehmen Vorteil, dass man sich andere Dinge, die man schon in C oder C++ hat, einfach mit dazu linken kann und die auch mit benutzen kann. Wenn ich jetzt schon, was weiß ich, es gibt irgendwelche Prüfsummen zum Beispiel in einem Protokoll und da habe ich schon eine Implementation in C, um diese Prüfsumme zu berechnen über das Paket, dann kann ich die einfach mit rein linken und kann die benutzen aus der Titan Test Suite. Aus der TTCN3-Test Suite, die ich dann damit ausführe. Und hat halt auch den, ja, manche Leute mit irgendwas Nachteil sind. Ich sehe das als Vorteil, also ich sehe das ganz gerne, dass mein Test Suite ein executable Program ist, was ich also auch als solch ist, nicht in der skripteten Sprache irgendwie bin, wo ich dann alle möglichen Runtime exception sonst irgendwas brauche. Wenn man auch dazu kommt, es ist eine stark typisierte Sprache, ganz im Gegenteil zu peisen. Also es ist unglaublich, viele Probleme weiß ich zur Compile-Zeit, dass sie da sind und habe da nicht mitten im Test auf einmal irgendwo ein, oh, da ist jetzt irgendwas nicht so, den Typ konnte ich nicht verstehen. Ja, wir haben also die abstract Test Suite, die wir schreiben, kompilieren das ganze Zeug zusammen, haben dann die executable Test Suite, die ETS. So, was haben wir jetzt eigentlich in dieser Sprache so für Möglichkeiten uns auszudrücken? Wir haben ein sehr umfangreiches Typenkonzept, Datentypen und die Konvertierung von konkreter, encodierter Informationen, wie jetzt mein Netzwerkpaket aussieht, zu einer abstrakten Repräsentation, ist ein ganz wichtiges Konzept. Wir haben parametrische dynamische Templates, die wir hierarchisch aufbauen können, um eben auf eingehende Nachrichten zu matchen oder aber auch um ausgehende Nachrichten parametrisch zu erzeugen. Wir haben jede Menge unterschiedlichen Encoder und Decoder, haben ein sehr schönes, automatisch schon vorhandenes Logging, Framework, was sehr viele Dinge tut. Also ich muss ganz selten in meinem Code jemals irgendwie ein Logstatement explizit reinschreiben, weil die ganze Runtime und die Library und alles halt schon dafür ausgelegt ist, dass er an jeder beliebigen Stelle eben sich, ich bekomme Bineerdaten rein, dann kann sie die Bineerdaten als Hex-Dump loggen und dann wird es dekodiert und dann wird es dekodierte gelockt in der menschenlesbaren Form und das muss ich halt nicht noch alles irgendwie explizit schreiben, sondern das macht das halt alles für mich. Ja, dann habe ich sehr interessante Program-Control-Statements, die ich so noch nie in anderen Programmiersprachen gesehen habe, wenn wir gegen Ende des Vorsachts draufzukommen, was man da so machen kann. Und man hat dann halt auch schon in die Sprache eingebaut, das Konzept von, was ist ein Test-Case, was ist eine Test-Suit, was sind Verticts, also die Ergebnisse von einzelnen Tests, wie werden Verticts aggregiert? Ja, also wenn eine von 15 parallel laufenden Komponenten fehlschlägt, dann soll natürlich der ganze Test fehlschlagen und so weiter. Und ich habe eine entsprechende Run-Time, die diese Tests ausführt oder ein Executor, der diese, es sind sogenannte Test-Komponenten, die man dann entwickelt, ausführen kann. Und zwar parallel, nicht nur auf einem Rechner, sondern ich kann letztlich irgendwie ein ganzes Test-Netz mehr aufbauen, wo überall diese Titan-Executor drauf laufen. Und die Infrastruktur kümmert sich selber drum, dann die entsprechenden Komponenten auf den jeweiligen unterschiedlichen Rechnern im Netzwerk zum Start zu bringen, die Ergebnisse einzusammeln, zu aggregieren und so weiter und so weiter. Also es ist auch für verteilte Tests mit vielen Systemen vorbereitet und bringt das alles mit. So, dann schauen wir uns erstmal ein bisschen das Typensystem an. Ich kann das auch nochmal ein bisschen größer machen. Ja, wir haben natürlich erstmal ganz normale Datentypen wie in jeder Programm, na ja fast jeder Programmi-Sprache, die man halt so kennt, integer-Zahlen, float-Werte, Boolean, ja mit einem A natürlich, ist auch wieder nur ein Tippfehler von mir. Haben Sie ja sich das A gespart an der Stelle. Dann hat man diverse String-Typen, Bit-String ist jetzt auch nicht so überraschend. Gut, hat man auch selten als expliziten Datentypen in der Sprache, aber gibt es da halt so Octet-String, ist am wenigsten überraschend. Sehr sympathisch ist mir der Hex-String, den braucht man im Telefonie-Wereich relativ oft, weil Telefonnummern und Eben-Sies und so was immer als BCD encoded Hex-String ausgedrückt sind. Der natürlich auch eine Ort-Length haben kann, also der muss ja nicht immer nur geradzahlige Zahlen von Hex-Digits haben. Ja, dann hat man ein Character-String und das ist halt nicht wie in C oder in anderen Sprachen irgendwie ASCII, sondern ein Character-String ist ja 5, also das ist halt 7, na ja noch ein bisschen weniger als 7-Bit ASCII und ich kann da halt gar keine anderen Werte ausdrücken, die nicht ein ASCII-Darstellbares Zeichen werden. Also würde jemals irgendwo ein Zeichen dort aufzutreten, das nicht darstellbar ist in dem jeweiligen Alphabet, also im Standardfall IA5, dann gibt es halt eben entsprechende Fails und Exceptions an der Stelle. Gibt Universal-Charst-String, das ist dann halt für UCS4, da kann man dann wirklich alles drin ausdrücken an den unterschiedlichen Datentypen und kann dann letztlich aus diesen elementaren Datentypen, die kann man alle noch constrainen. Also ich kann sagen, na ja, ich definiere mir einen Typ Hex-String von 5 bis 23 Stellen Hex-Digits darin und dann kann ich halt auch nur solche Daten ausdrücken und alles andere wird entsprechend zu Fehlern führen, die dann mein Testfall oder was auch immer zum Abbruch bringen. Baue ich mir daraus natürlich wie strukturierte größere Typen, also ein Record ist halt so etwas wie ein Struct in C, würde man jetzt sagen. Es gibt da noch andere Konstrukte, also ein Record ist ordered, also mit einer definierten Reihenfolge von Feldern, ein Set wäre mit einer beliebigen Reihenfolge der Felder. Record Off ist so ein bisschen so etwas wie ein Array, also eine Sammlung von mit definierter Reihenfolge von gleichartigen Elementen. Ein Set Off ist dann wieder mit beliebiger Reihenfolge, also auch wenn die Reihenfolge der Elemente sich permutiert sind, zwei Sets identisch und solche Dinge und letztlich dann so etwas wie diesen Verdict Type, der ist auch sehr schick. Der kann unterschiedliche Zustände haben, drückt halt das Ergebnis von so einem Test aus, dann ist erstmal der initiale Wert, pass ist der Test des Bestandens, inkonklusiv ist, nah, wir wissen nicht so wirklich, Fail ist ein expliziter Fehler, den der Test so erkannt hat und als solchen markiert hat und Array ist alles, was zur Runtime an Fehlern auftritt, die halt unerwartet waren. Also da habe ich nicht als Programmierer irgendwo explizit gesagt Fail, sondern da hat die Runtime halt festgestellt, oh, da ist jetzt irgendwas komisch, ich bräuche ab. Und ein Verdict, es gibt halt quasi einen Typ, Verdict in dieser Sprache, und ich kann mir Variablen deklarieren, das Verdict Types und der Typ kann immer nur schlechter werden und nie wieder besser. Ich kann also irgendwie von Pass nach Fail, aber ich kann nie wieder von Fail nach irgendwie Pass zurück oder von Error nach Pass zurück oder so. Und da gibt es halt dann entsprechende Merge-Regeln, wonach das alles aggregiert wird. Also wenn ich irgendwie 15 oder was weiß ich, wie viele Testkomponenten parallel laufen hat, die da irgendwelche Dinge tun, das ist aggregiert und das wird natürlich immer das schlechteste von allen genommen als entgültiges. Man hat auch gleich Implicit zu einem Verdict, man muss gar nicht explizit Variablen deklarieren. Ja, da gibt es viele Automatismen, die irgendwie da sind, die dann letztlich einem helfen, mit wenig Zeilencode viel auszudrücken. So, wir sehen jetzt diese Structure Types aus. Na ja, mein Gott, das ist nicht so viel anders als eine C-Struct, guten Optionen habe ich da nicht. Also ich sage jetzt irgendwie, ich habe meinen Message-Type, der hat erstmal ein Integer, der ist optional und dann habe ich noch einen Character-String und einen Boolean-Wert. Optional heißt natürlich, kann da sein, muss aber nicht. Da definiere ich jetzt aber natürlich noch nicht, wie das Ganze inkludiert werden soll. Ich sage nur erstmal, ich habe da einen abstrakten Datentyp, der besteht aus drei Elementen und die haben solche Charakteristika und das war es. Ja, ich habe eine Union natürlich auch, wo ich dann wahlweise Feld 1 oder Feld 2 ausdrücken kann. Also ich habe zwei Datentypen, entweder ich habe einen Integer oder einen Charstring da drin, in dieser My Message Union jetzt in diesem Beispiel. Ein Unterschied zu Leuten, die wie ich aus dem Low-Level-C Entwicklungsbereich kommen, ich brauche jetzt nicht noch irgendwie explizit eine Annotation, welche dabei nicht ausgewählt habe, sondern das ist inherent mit da und ich habe einen IsChosen-Build in Function, wo ich jetzt sagen kann, ich habe jetzt Feld 1 gewählt in dieser Union worden, bei dem konkreten Datentypen, den ich davor mehr habe. Aber das ist jetzt einfach nur mal Datentypen, ist es noch nicht so wahnsinnig spannend. Gibt noch ein paar andere Konzepte, die interessant sind. Ein Problem, was man ja oft hat, nicht nur beim Schreiben von Testcases, dass man möglicherweise uninsialisierten Speicher mit irgendwo überträgt, weil man irgendwo irgendwas nicht, ja, ich definiere mir da eine Struct, setze irgendwie drei von vier ein Member und der vierte hat halt irgendwie ein Null, oder er hat einen random Wert, weil das halt irgendwo aus dem Speicher kam. Kann mit TTCN3 nie passieren, weil immer dann, wenn ich irgendeine Nachricht versenden möchte und irgendeines der Felder, die in diesem Datentypen enthalten sind, noch keinen Wert zugewiesen hat, ist ja eben anbauend und etwas anbauen kann ich nicht versenden oder nicht encodieren, zum Beispiel, wenn ich jetzt einen ungebundenen Wert, also auch wenn es eine komplexe, verschachtliche Struktur ist, wo am letzten Datentyp irgendwo einfällt, nicht gewunden ist, also ich dem keinen expliziten Wert zugewiesen habe, dann sagt der Endkürter irgendwie, kann ich nicht encodieren, weil da noch was anbauen ist. Ja, kann ich also schon mal nicht ausversehen, irgendwas encodieren oder versenden, was nicht 100% definiert ist, was ich da versende. Ja, es gibt sonst eben dann immer diese Runtime Exceptions an der Stelle und wenn ich optionale Felder habe, dann muss ich halt explizit dieses Feld auf um mitsetzen. Ich muss also sagen, dieser Teil der Nachricht explizit ist nicht Teil. Ja, sage ich eben, jetzt in diesem Beispiel, wenn ich mir das hier wieder anschaue, dieses Field1, dann würde ich halt dann sagen, Field1 ist um mit und damit ist dann klar, dass ich diesen optionalen, dieses optionale Feld nicht mit übersagen möchte. Dann kann ich eben subtypen, hatte ich gerade schon mal angesprochen, kann also mir hier definieren, irgendwie Integer, die Werte von 1 bis 100 haben können oder irgendwie Zeichen, die nur zwischen K und W sein dürfen im ASCII-Alphabet oder irgendwelche sozusagen ein Set, also was nur Left oder Right sein kann, als String anders nicht zugewiesen. Ich kann das auch mischen, das sind nicht nur Ranges, sondern ich kann auch Ranges mit expliziten, also 1 bis 5 oder 7 oder 9 kann Längenbeschränkungen machen, also irgendwie, ich sage also, ich hätte gerne einen neuen Typen, der einen Record of Integer der Länge 0 bis 10 ausdrückt oder aber auch Strings, die bestimmten Patterns entsprechen, da kann ich es wirklich noch mit regulären Ausdrücken hingehen, kann sagen, ich habe hier einen Datentyp und der kann immer nur Werte haben, die überhaupt diese Regular Expression irgendwie matchen, ansonsten kann der diesen Wert gar nicht haben. So, dann kommen wir zu Templates. Templates haben zwei wichtige Funktionen. Einmal eben zum Versenden von Nachrichten, gibt es Send Templates und es gibt Received Templates und hatte ich gerade schon mal so ein bisschen angesprochen, ja, in so einem Received Template ist eigentlich so das schwierigere Problem logischerweise, ja, manche Werte weiß ich eben, was sie sein sollen, der Nachlichen Typ, ich erwarte halt einen bestimmten Typ in der Antwort von jetzt irgendeiner Implementation an der Test, bei manchen weiß ich, was es sein sollen, eine bestimmte Quelladresse vielleicht, die da jetzt kommt oder eine aus einem bestimmten Pool von Adressen oder was auch immer, das sind vom Absender, also die Implementation an der Test gewählt, die ich ja nicht kontrolliere, die aber vielleicht irgendwelchen Regeln einhalten müssen, was weiß ich, es ist ein 32-Bit Wert, aber da darf trotzdem nur das oberste Bit darf nicht gesetzt sein oder sonst irgendwas und das kann ich halt alles in solchen Templates ausdrücken, dann in den TTC sein. Kann ich jetzt sagen, naja, das muss so und so aussehen und nur, wenn es so und so aussieht, matcht eine eingehende Nachricht auf dieses Template. Dazu kommt dann, dass sie noch Parameters sein kann, also ich kann wie mit einem Funktionsargument, kann ich halt auch einen Template-Parameter reingeben, die dann dort benutzt werden und ich kann die ganzen noch hierarchisch gestalten. Ich kann mir also erst mal ein ganz einfaches Template für jede beliebige Antwort definieren, dann kann ich immer spezifischere Templates davon ableiten von dem generischen Template und kann dann dagegen matchen. Sieht dann so ein bisschen so aus wie hier, ich sag also Template eines Character Strings, TR ist so eine Notation, die man oft für Template Receive verwendet, es ist also reine Konvention, kann man nennen, wie man möchte und da sage ich halt, naja, kann A, B oder C sein. Und wenn das halt keins davon ist, wird das Template nicht mehr matchen. Oder sowas wie naja in der Nähe von B irgendwo von 3,14 bis 3,15 als Floatwert. Müsste ich halt, wenn ich so ein Template nicht hätte, müsste ich das explizit, wenn ich jetzt eine Nachricht enthalte mit so einem Wert, müsste ich halt explizit, ist es größer oder kleiner wenn man das explizit schreiben muss man hier nicht. Das Template würde nicht mehr matchen und damit würde ich halt sagen, naja, wenn es nicht matcht, dann verdict fail. Kann sich da halt beliebige Konstrukte ausdenken. Ne, ich sage halt, kann unendlich groß sein so. Kann ich, muss ich gestehen, habe ich mir nicht angeguckt an der Stelle, ist letztlich halt, die Sprache lässt das zu, dass ich das definiere, wo jetzt bei der, also es gibt ja unterschiedliche Implementation der Compiler, wo jetzt bei Titan da die Grenze wäre oder welche Integer-Werte oder Float-Werte da ausgedrückt werden können, weiß ich nicht, ob der dann mit Big-Num irgendwas macht, kann ich nicht sagen, hat mich jetzt noch nicht bezoffen bisher, gibt es aber halt in der Sprache. Ist dann halt wahrscheinlich in der Realität das größtmögliche Wert, die Umgebung, also der Compiler oder was auch immer, das kann ich leider auch nicht beantworten, ja, können wir aber noch im Nachgang-Test erklären. Die Spezifikationen sind ja alle öffentlich und man kann ja die, die ist übrigens eine der am angenehmsten, also ich, eine C-Spezifikation als C++ finde ich sehr trocken, aber die TTC-Nasei-Spezifikation kann man echt angenehm lesen, ist voll von Beispielen und Erklärungen und so weiter, also kann man, kann man ganz gut tatsächlich mal lesen. Ja, dann kann ich auch so Bit-String-Templates haben, irgendwie die ersten Bits müssen so sein, aber das sind zwei Bits, die markiere ich mit Fragezeichen, die sind halt irgendwie, können beliebige Werte haben an der Stelle. Fragezeichen heißt aber, muss da sein, also die zwei Bits müssen vorhanden sein, kann jetzt nicht zwei Bit kürzer sein an der Stelle, wenn ich Sternchen hinmachen würde, dann wäre es halt tatsächlich optional, könnte also zwei Bits kürzer sein, oder beliebig viele sogar dann an der Stelle. Ja, dann eben auch wieder bei den, bei den Character-Strings kann ich mit Patterns arbeiten, kann also ich hier sagen, dann gibt es dann noch alle möglichen, ja, noch fortgeschrittenen Möglichkeiten, dass ich dann noch Sub-Sets und Super-Sets und paar Mutationen zulassen kann und so weiter. Es ist ein sehr deklaratives Programmieren, ich muss nicht irgendwie explizit dem Programmablauf so genau definieren, sondern ich sage eher so, was hätte ich denn gerne, oder was ist zulässig und was nicht. So, das ist jetzt hier ein Beispiel von dem parametrisierten Template, ich habe also hier erstmal ein Nachrichtentyp, den hatten wir vorhin schon mal mit Field 1, 2 und 3, Field 1 ist optional davon, dann definiere ich mir hier ein Template davon, sag ich, dieses Template betrifft diesen Typen, den ich da oben gerade definiert habe und das Template hat halt dann noch ein Parameter, den ich hier mit reinzeichen kann, als Template-Parameter und dann sage ich halt, hier Feld 1 muss vorhanden sein, kann aber jeden Wert haben, also optional ist es dann nicht mehr, wie dieses Template matchen soll. Feld 2 habe ich halt hier irgendwie wieder ein paar einbuchstabige Strings, die zulässig sind und Feld 3 ist dann der Parameter, den ich da irgendwie reingereicht habe in diesen parametrisierten Templates und man hat eine Matchfunktion, die mit der ich halt sage, mit der ich vergleichen kann, die Variable dieses Types, match die einen Template dieses Types und gibt entsprechend Bullien zurück. Man muss das aber gar nicht so oft tun, weil meistens will man ja irgendwas matchen, das kann man dann gleich in dem Receive-Statement machen. Also man kann sozusagen sagen, naja, wenn du was empfängst, was dieses Template matcht, dann muss man nicht noch explizit irgendwie eine Match-Expression oder so machen. Gut, hierarchisch hatte ich auch gerade schon erwähnt, geht also damit los, dass man irgendwie als sich ein Basis-Template so irgendwie anyMessage-Type, wo ich sage, Message-Type ist egal, irgendwas anderes muss vielleicht auch noch gesetzt sein und dann sage ich irgendwie mein Message-Type 23-Template was ich da oben definiert habe, muss aber halt den Message-Type 23 haben. Und fu muss immer noch bar sein, weil ich leite das ja ab von diesem Wert. Assignment ist, mit Doppelpunkt ist gleich, hat wahrscheinlich irgendjemand bei Pascal aufgeschnappt. Ansonsten ist die Syntax sehr C-ähnlich, ja, nur man hat halt, ja, so Leute, die aus dem Testing kommen, die haben ja auch so eine eigene Kultur und eine eigene Philosophie und die wollen natürlich bestimmte Programmierfehler vermeiden und deswegen wollten sie halt nicht, dass es ein Equals irgendwie, dass man Assignment und Equality vielleicht zu leicht oder zu schnell verwechselt oder sich vertut, deswegen dann sowas. So, dann kommen wir zu dem interessanten Thema, ja, jetzt habe ich irgendwelche abstrakten Daten-Typen, aber wie encodiere oder dekodiere ich die? Ja, TTCN3 hat in der Sprachspezifikation schon beschrieben, wie man so gängige Ja, Notationen importieren kann. Man kann also alles, was den ASN1 irgendwie spezifiziert, das kann man einfach importieren und der TTCN3-Compiler muss sich darum kümmern, wie er dann Daten von der abstrakten, also in einem bestimmten Encoding-Schieben von ASN1 encodierte Daten dekodieren kann und die ganzen Definitionen der Datentypen werden halt aus dem ASN1 darüber genommen. Genau das Gleiche geht wohl irgendwie mit IDL oder XML-Schema Definitions oder JSON-Schema Definitions, kann man auch alles einfach so reinladen, muss man also keine encoder und decoder schreiben. Das ist ja sowieso eher die langweiligen Fälle. Interessant sind ja die ganzen Legacy-Protokolle oder auch neue Protokolle, die tatsächlich in keiner abstrakten Sündachs definiert sind, wo man also wirklich einfach nur als Mensch irgendwo ein Dokument vor sich hat. Na ja, da sind jetzt zwei Bits so und so und dann sind dieses Feld ist optional und wenn das achte Bit noch irgendwie gesetzt ist, dann wird das Längenfeld nochmal um acht Bit länger und was sich die Leute so alles ausdenken für Encodings und das will man natürlich irgendwie in so eine Test Suite irgendwie reinbringen und das ist eigentlich der Teil der mir persönlich, würde ich fast sagen, am besten gefällt. Es gibt in Titan, das ist jetzt kein TTCN3 Sprachfeature, sondern es ist tatsächlich die Implementation Erics in Titan, die das macht, sogenannten RAW-Kodik und ein Text-Kodik da kann man deklarativ programmieren. Also ich kann, muss jetzt nicht einen Decoder schreiben, so wie ich den klassisch eben schreiben würde, imperativ sich sage, na ja, ich nehme jetzt die obersten zwei Bits und ich schifte mir die um sechs irgendwie nach rechts und dann, wenn die zwei sind, dann passiert das sondern habe ich irgendwie meine verschachtelten if und case und sonst was Statements, sondern ich kann das alles eben in einer Annotation, einer Sündachs ausdrücken, was ich denn meine, wie das aussehen soll und der Encoder und Decoder, der ganze Code definiert. So, wie sieht das jetzt aus, wenn ich den UDP-Header zum Beispiel mehr definieren würde in Titan? Sieht so aus. Ich fange erstmal an, es gibt ja quasi kein U in 16T, da muss ich mir jetzt erstmal so was definieren, da sage ich jetzt halt gut, wie man den nennt, ich hätte das auch U in 16T nennen können, aber ich sage halt, na ja, da gibt es ein Integer von 0 bis 60.535 dessen und jetzt kommt diese Innotation des Binary-Kodik, der eine 16-Bit Feldlänge hat, der kein Sein-Bit hat und der Last Significant-Bite also LSB First und da kann ich mir halt beliebig erstmal meine Datentripen definieren, in ihrer Bitorder und Feldlänge und ob sie irgendwie Sein-Bits haben und so weiter und daraus baue ich dann halt mein UDP-Header zusammen, also Sourceport, Desktop, Länge und Checksam. Und dann sage ich noch, wie die Felder in die Reihenfolge der Felder hier zu encodieren ist und definiere mir dann ein UDP-Paket, was besteht aus Header und Payload und dann kann ich sagen, na ja, in dem Header drin soll das Längenfeld Lennen der Gesamtlänge des Header und des Payloads entsprechen. Und dann wird halt der Inhalt des Längenfels automatisch berechnet, beim Versenden von Paketen und umgekehrt bei Empfang in den Paketen wird das Längenfeld verifiziert. Und ich muss das halt nur irgendwo zu Fuß hinschreiben, dass da irgendwas verglichen wird so. Die Prüfsumme wird natürlich schon schwieriger, da schluß ich dann logischerweise an die Grenzen des Deklarativen. Da muss ich dann halt irgendwie eine selber eine Funktion dafür mit anbieten, die das implementiert. Das kann ich natürlich jetzt nicht deklarativ so einfach machen. Ja, ich wiederhole die Frage. Die Frage war, ob Standardsachen wie CRC16, CCITT und so weiter enthalten sind. Sind sie in der Sprache selber nicht, auch in Zeiten meines Wissens und auch nicht, aber es gibt ja schon jede Menge Testsuits, wo sie da sind, wo ich mir dann die Funktion nehmen kann oder ich nehme halt die C-Implimentation und links sie mir mit rein. Das geht also ohne Probleme und man kann auch eine Kombination machen. Also bei manchen Protokollen, man kann viel darin ausdrücken in dieser deklarativen Syntax, aber irgendwann stößt man natürlich an die Grenzen. Also ich hatte zum Beispiel einen Fall, wo das Längenfeld nicht die tatsächliche Länge der Nachricht inkludiert, sondern die Länge minus 3 oder irgendwas. Und 3 ist eine Arbiträr gewählte Zahl, weil man wollte halt noch in den 8-Bit-Längenfeld, wollte man halt nicht nur bis 256, sondern noch plus 3 mehr irgendwie kommen und das geschiftet den Längenbereich in dem Protokoll. So was konnte ich halt, bis ich das als Feature angeregt habe, bei den Titan-Entwicklern konnte man das nicht ausdrücken in der Syntax. Und dann kann man tatsächlich halt jene Teile, die der Sprache abdeckt, kann man halt automatisch generieren lassen und kann dann halt selber für andere Teile noch C++ Code dazuschreiben. Also es ist nicht entweder oder, sondern man kann die Dinge, die dann halt nicht abgedeckt werden, die kann man selber schreiben. Das Ganze geht jetzt nicht nur mit so einfachen Konstrukten, sondern ich kann da auch TLVs, zum Beispiel TLV-basierte Protokolle, primalen ausdrücken. Und es geht halt so weit, dass sowas wie diese Längenfelder, es kann Längenfelder variabler Länge zum Beispiel auch. Es gibt ja diverse Encoding-Verfahren, wo man dann sagt, na ja, wenn die Länge unter 128 ist, dann nämlich einweit, aber wenn es irgendwie ab 128 aufwärt ist, nämlich eine 16-Bit-Längenfeld, laut aus solchen Dingen kann man da schon in der Syntax von sich aus ausdrücken. Ja, anderes Beispiel sind hier optionale Felder von Headern. Das ist jetzt hier GAE. Ja, warum mache ich immer, ich geht hier wieder oben geschrieben, habe mich ein bisschen durcheinandergekommen. Also es ist GAE in dem Fall tatsächlich. Und da gibt es halt am Anfang ein paar Bits, die irgendwie über die, das Vorhandensein von Feldern später in dem Paket oder in dem Header Auskunft erteilen. Dann sage ich halt, na ja, hier unten, dass das Feld checksam, was ja hier unten irgendwo steht, was ein optionales Feld ist und ein 2-Bit-Lange-Octet-String ist, dass der nur dann da ist, wenn C so ein Present 1 ist da oben oder aber kurioserweise, wenn LT-Present irgendwie 1 ist. So ist dieses Protokoll halt definiert, warum auch immer Sie das so definiert haben. Aber sowas kann ich dann halt auch da drinnen ausdrücken und muss eben nicht wieder von Hand irgendwelche imperativen Ausdrücken ausführliche verschachtelte if-case-else sonst irgendwas Statements schreiben. So, das Ganze kann ich mit auch mit text-basierten Protokollen machen. Da gibt es dann ein Text-Coder. Das ist jetzt hier MGCP, das kennen wahrscheinlich wenige hier im Raum, weiß ich nicht, ob irgendjemand hier MGCP kennt. Das ist das Media Gateway Control Protokoll, RFC 3566, glaube ich. Das ist ein ETF-Protokoll, nichts, was jetzt irgendwie Mobilfunkspezifisch wäre. Ist textorientiert, wie viele ETF-Protokolle, also ein ZIP, HDTP, SMTP oder irgendwas sich anschaut. Ja, und da kann ich halt auch, das ist halt wirklich ein praktisches Beispiel, weil ich es implementiert habe, kann ich dann eben das entsprechend so beschreiben da drinnen, sage also irgendwie, na ja, es gibt einen Verb in diesem Protokoll, das ist so und so. Und dann Transaction IDs, eine Dezimalzahl, die von 1 bis 9 Stellen Länge ist. Ein Endpunktname, der so ein bisschen wie eine E-Mail-Adresse aussieht, mit einem String. Und die ganze Nachricht muss aber mit dem String MGCP vorher beginnen und dann irgendwie eine Kommando-Zeile hat noch irgendwelche Spaces als Trainer oder kann aber auch ein Tabulator haben oder mehrere davon und kann man halt auch wieder sein Textencoding so beschreiben und muss nicht irgendwelche String-Pase selber irgendwie schreiben. Und dann letztlich, ja, diese ganzen Encoder, egal ob die jetzt, ob man sie geschrieben hat mit dieser Text Orcodex-Syntax oder ob man jetzt ein ASN1 importiert hat oder ein JSON-Schema oder sonst irgendwas, letztlich dient es einem immer dazu von dieser konkreten Binären oder Text oder wie auch immer On the Wire-Representation in die abstrakte Repräsentation von TTCN3 zu übersetzen und in der abstrakten Repräsentation habe ich dann diese ganzen Templates, mit denen ich matchen kann oder auch Templates, mit denen ich Nachrichten generieren kann, bevor ich sie versende. Das ist so der Clue der Sache. So, dann gehen wir mal von diesen, da kann man natürlich noch tausend Dinge mehr tun, aber bleiben wir mal bei dem Funktionsumfang, den ich jetzt beschrieben hab, dann will ich ja irgendwann Programme schreiben. So, das ist im Wesentlichen wie ein C, ich hab irgendwie if und else und ich hab Select ist so ein bisschen wie Switch, also Case- und Switch-Statements. Ich hab For-Wilds, ich hab For-Loops, While-Loops, Do-Wild-Loops, ich hab Go-To und Label, das ist also alles erstmal sehr für ein C-Entwickler, alles sehr ähnlich mit Break und Continue und hat dann letztlich, muss man ja irgendwie interagieren mit der Außenwelt, das passiert bei Titan oder bei TD103, muss ich sagen, über sogenannte Test Ports. Ein Test Port hat nun erstmal nichts mit einem UDP Socket oder einem UDP Port oder irgendwas zu tun, das ist ein viel abstrakteres Konzept. Das ist halt irgendein Interface, über dem ich Protokoll-Daten mit der Implementation an der Test austauschen kann. Es gibt einen Test Port, der zum Beispiel die Linux User Space GPIO Abstraktion anspricht. Dann kann ich über den Test Port irgendwie GPIOs ein- und ausschalten. Das ist auch ein Test Port, weil es irgendeine Art von Schnittstelle ist zwischen meiner Test Suite und der Welt, die da außen rum liegt. So, dann kann ich da senden und empfangen auf solchen Ports. Die Ports selber in den Sinn der Regel in C++ implementiert. Ich kann aber auch die in Titan implementieren oder in TD103 implementieren und ich kann dann senden und empfangen. Ein Sende-Statement ist ein Non-Blocking-Send, was ich da immer drauf schicke. Ein Receive-Statement ist defaultmäßig erst mal ein Blocking-Receive, was ich an der Stelle habe. Er wartet, bis ich irgendwas empfangen habe, was dem entspricht, was ich da als Template beschreibe. Ich habe dann also diese Test Ports, wo ich Daten empfange und sende. Ich kann senden, kann ich einen Wert, eine Konstante. Ich kann einen Value-Send-Template versenden und beim Receive kann ich eben gleich so ein Receive-Template mit reinschreiben. Wenn es Receive-Blocking ist, was mache ich dann aber, wenn jetzt irgendwie verschiedene Dinge gerade einsetzen können. Ich habe ja oft, wenn ich so ein Protokoll teste, da kann jetzt irgendwie eins von drei verschiedenen Dingen passieren oder es müssen drei Dinge passieren, aber die Reihenfolge ist irgendwie egal, aber sie müssen halt alle einmal passieren. In den jeweiligen Statements ausdrücken. Wenn wir jetzt hier mal so ein Programm ausschauen, das ist das ganz normales Blocking Receive. Das heißt, irgendwie jetzt hier, P ist der Test Port, d.h. Receive X. Ich muss also erstmal X empfangen aus dem P und danach muss ein Y rauskommen in genau dieser Reihenfolge. Und die blockieren halt unendlich lang an der Stelle. Natürlich kann ich hier Timer starten und sonst irgendwas, aber das ist nicht sonderlich weit. Was es dann gibt sind sogar in der All Statements. Das ist so ein bisschen wie ein Select System Call, man aus der C-Entwicklung kennt. Es ist jetzt hier so ein Beispiel, ich sende erst mal irgendein Request durch den Port P, starte irgendeinen Timer und dann habe ich dieses All Statement und da sage ich dann, naja, entweder es kann jetzt sein, dass ich aus P eine Antwort enthalte, so P.Receive.Response und Rest kann dann wieder irgendwie ein Template oder sowas sein. Dann habe ich danach einen Block, wo ich sagen kann, was alles passieren soll, wenn ich jetzt das empfangen habe. Oder aber auf irgendeinem anderen Port empfange ich irgendetwas, also weil hier nach hinter dem Receive in Klammern nichts angegeben ist, sozusagen irgendwas beliebig ist, was aus diesen Test Ports rauskommt und any port ist halt, naja, jeder. Oder aber mein Timeout läuft ab und dann behandle ich, was halt in dem Timeout passieren soll an der Stelle. Das ist die Ecke in Klammern. Vor und dran ist eine Guard Condition. Kennen Leute vielleicht aus, also wenn man schon Erlang gemacht hat oder so, da kennt man das, ja, dass man halt da noch irgendwelche Conditions reinbauen kann. Kann man wunderbares Date-Maschinen damit bauen, wenn der State so und so ist und nur dann ist halt diese Zeile aktiv, die dieses Guard Expression oder Guard Condition vorher hat. Ja, das empty ist eben true, aber ich kann halt auch sagen, was ich, wenn jetzt x größer 0 ist, nur dann darf diese Zeile aktiv sein und so weiter. So, damit kann ich also solche alternative Events, die passieren können. Man kann das auch mit Repeat noch erweitern, dass sozusagen jetzt, wie das jetzt hier im Beispiel, man hat ja öfter den Anwendungsfall, da kommt irgendwie, man hat seine eigentliche Protokoll Test Logik, aber das Protokoll hat ja noch so ein Keeper Life Ping-Pong, was da im Hintergrund hin und her läuft. Ja, und dann will ich ja, wenn das das Keeper Life empfangen wird, will ich irgendwie da eine Antwort schicken oder irgendwas tun und will dann quasi diese Loop noch mal, dieses All Statement noch mal weiter durchlaufen, so als ob nichts passiert wäre. Weil das Keeper Life ist nicht das, was ich testen will an der Stelle, aber das muss halt im Hintergrund irgendwie passieren. Ja, das heißt also zu sagen, wenn dieses Keeper Life empfangen wird, mache ich irgendwas damit und sage Repeat und dann geht es halt wieder hier los bei dem All Statement am Anfang und der ganze, dieser ganze Block hier wird weiter ausgeführt. Ist schön, ist jetzt noch nicht so wahnsinnig begeisternd, das ist dann schon richtig schick, dass man ein Interleave Statement hat. Damit sage ich jetzt, diese drei Dinge, die ich da beschreibe, müssen alle drei passieren, aber die Reihenfolge ist mir egal. Jedes davon ist exakt einmal passieren, aber halt in beliebiger Reihenfolge. Ja, gerade wenn man irgendwie Asynchron, irgendwie mehrere Dinge passieren gleichzeitig und so und dann müssen halt irgendwie diese drei bestimmten Nachrichten oder was auch immer ankommen auf unterschiedlichen Ports und kann ich damit schön produktiv ausdrücken. Weil das aber alles noch gar nicht genug ist, hat man dann auch noch ein All Step Construct und das ist dann richtig schick. Man kann nämlich Dinge, die man, man fängt dann an Test Cases zu schreiben mit dieser ganzen Mechanik, die ich jetzt bis hierher erklärt habe und dann stellt man fest, na ja, in jedem Test Case muss ich jetzt irgendwie sagen, na ja, wenn ein Keeper Live angekommen ist, dann schick irgendwie was zurück und mache Repeat. Das ist halt irgendwie Etzen, wenn ich das irgendwie in 25 Test Cases, 25 mal hinschreiben muss per Copy und Paste. Deswegen gibt es All Steps. Damit kann man sozusagen einzelne Blöcke, die man jetzt hier in einem Alt drin hat, also einzelne Zeilen oder auch mehrere davon rausnehmen und quasi eine Art wie eine Funktion definieren. Das ist aber keine Funktion, sondern ist ein All Step. Jetzt hier in diesem Fall ist es halt auch wieder so ein Keeper Live Ping Pong. Also wenn ich etwas matche, was dem Received Template Ping entspricht, dann schickt doch das Pong zurück. Oder wenn der globale Guard Timer abläuft, na ja, dann brich halt ab und sagt, der Guard Timer ist abgelaufen. Und das abstrahiere ich mir raus in so ein My All Step hier und den kann ich dann benutzen. Letztlich wieder an anderer Stelle, wo ich dann schreibe, ich dann halt hier rein, na ja, My All Step ist halt auch noch da. Und dann wenn die Statements, die ich jetzt hier in diesem All Step definiert habe, die werden dann halt dynamisch eingefügt an dieser Stelle, wo ich den All Step irgendwo anders dann mit einbaue. Und damit kann ich so generisches Testverhalten, was überall halt immer mit dabei sein muss, mit reinziehen. Also Guard Timer und Keeper Live sind so typische Beispiele an der Stelle. Kann ich aber noch weitergehen? Da muss ich jetzt immer noch diese eine Zeile My All Step in jeden Test Case wieder reinschreiben, damit der aktiviert wird. Deswegen gibt es dann auch noch Default All Step Activation. Das heißt, ich kann irgendwo in meinem Test dann sagen, na ja, aktiviere doch mal diesen All Step und der ist dann im Hintergrund immer aktiv, solange wir sich ihn wieder deaktiviere später. Und damit ist sozusagen an jeder Stelle, wo irgendwie ein anderes All Statement wäre oder tatsächlich sogar ein Standalone Receive Statement, was ja normalerweise blockierend auf dieses eine Ereignis wartet, auf das man da wartet. Aber eben mit dem All Step, der vorher aktiviert wurde, wird implizit so ein Verhalten im Hintergrund aktiv. Und damit kann ich dann wirklich richtig produktiv solche Tests schreiben, weil dieses Standard Verhalten, also eben Guard Timer oder Ping Pong oder was auch immer Keeper Live, was man als im Hintergrund hat, alles dann eben abläuft, ohne dass man es in jedem Testfall nochmal irgendwie einzeln behandeln muss. Ja. Gut, die ganzen Test Cases, die ich da so habe, die schreibe ich dann in Form von TTC in drei Modulen. Das ist tatsächlich, finde ich, mit Python noch so am Artverwandtesten. Da gibt es ja auch Module. Ja und ein Modul ist nicht irgendwie eine Klasse oder beinhaltet nicht nur eine Klasse. Und das, ja, also gibt Module. Man importiert von Modulen. Es ist auch ein Import Statement, so wie man es bei Python kennt. Die syntax ist halt leicht anders, heißt aber auch Import. Und Module haben jetzt also eben Datentypen konstanten Templates, die man so braucht. Man definiert sich die Ports, die irgendwie mit der Außenwelt reden. Man definiert dann Testkomponenten. Komponenten kann man sich so vorstellen, ein bisschen wie vielleicht Threads oder Prozesse, die gleichzeitig ablaufen können. Ja und definiert sich dann die Funktionen da drin, die All Steps, die Test Cases, die man hat und hat dann noch letztlich einen Control Part. Und der Control Part definiert dann, welche der Tests in welcher Reihenfolge wie oft wie auch immer irgendwie ausgeführt werden. Auch ein sehr schönes, ich hatte Parameter gerade angesprochen, sehr schöne Automatismus, den man da hat. Man will ja Tests irgendwie konfigurierbar machen. Da muss man irgendwie eine IP Adresse einstellen oder was auch immer so alles für Parameter irgendwelche Timer oder andere Adress Parameter wie der Test jetzt auszuführen ist. Und man muss jetzt nicht irgendwie noch selber irgendwie eine Config File Library benutzen und die Pausen oder sonst irgendwas, sondern genauso wie ich eine Variable definiere, kann ich einfach einen Modulparameter definieren und der wird dann automatisch exportiert ins Config File. Also macht auch wieder die Sprache alles im Hintergrund. Ja, ich sage halt einfach, bei der Variable habe ich eine gewisse andere Syntax, um die Variable zu definieren und damit kann ich dann genau die Variable auch im Config File definieren und habe nicht mehr den, den, den hat, nicht nur den HardCoded und DefaultWare, den ich eben im Programmcode drin stehen habe. So, dann können wir uns jetzt noch ein paar Beispiele anschauen aus echten Test Cases. Ansonsten bin ich soweit erst mal durch. Ich hoffe, es war nicht zu verwirrend, dass ich wenn jemand vielleicht jetzt noch eine Frage hat, bevor ich auf die Beispiele schaue, können wir auch jetzt gerne schon Fragen nehmen. Gibt's Fragen? Nein, keine Fragen. Gut. Ansonsten, wenn Fragen da sind, bitte aus Mikrofon warten, dann damit wir das in der Aufzeichnung mit drin haben. So, dann werde ich jetzt hier mal den Frontgröße machen. Huge, ja, huge ist noch nicht so wahnsinnig huge. Ist das lesbar oder muss ich mich noch um Größe? Ja, das ist, ich bin nicht so der, kann man das lesen in der Größe oder ist das, ja, ist das genehm? Gut, dann kann ich jetzt so, okay, gut. Dann gehen wir jetzt hier mal Osmo TTC in 3 Hacks. So heißt das, was ich da so alles fabriziert habe in den letzten Monaten an Test Cases fürs Osmocom Projekt. Können wir uns jetzt mal irgendwie einen beliebigen, beliebige Test Suite anschauen. Sind natürlich jetzt alles keine Vortekolle, die man so als nicht Telekom oder nicht Mobilfunkern irgendwie kennen würde, die da implementiert sind. Aber schauen wir uns doch mal irgendwie MSC-Tests an. Ja, ich mache jetzt hier mal das Hauptmodul auf, Modul MSC-Tests. Da sieht man jetzt mal, es importiert von Gott und der Welt. Ja, das sind halt die ganzen anderen Module, die man da irgendwie benutzen wird. Wir sehen dann, da wird irgendwie eine Komponente definiert. Komponente erinnern wir uns so etwas wie ein Prozess. Die Komponente hat einen Namen, das ist jetzt hier MTC-CT, die erweitert eine andere Komponente. Das ist so ein bisschen, also das ist keine objektorientierte Sprache, aber man kann so gewisse Konzepte der Objektorientierung erben. Und zwar ist das jetzt hier, es gibt quasi einen Control-Adapter-Komponente und die erweitern wir mit unserer MTC-Komponente und das ist halt quasi eine abgeleitete Klasse. Sie nennen es halt anders. Ja, und da definiere ich jetzt hier, das sind quasi so etwas wie dann, wie man würde sagen, ich bin nicht der große, also wenn ich jetzt die falsche Thermologie verwende, bitte entschuldigen, ich bin nicht der große objektorientierte Theoretiker, aber das sind jetzt, membervariable in der Klasse, die man jetzt hier sozusagen definiert an der Stelle, ja, auf die ich dann aus allen Methoden, die natürlich nicht Methoden heißen, weil es ist ja nicht objektorientiert, aber aus allen Methoden, die Teil dieser Klasse sind, dann referenzieren kann, weiter unten. Das hier sind diese Modulparameter, von denen ich schon gesprochen habe, also das sind jene Variablen, die hier zwar einen Default-Wert bekommen, aber die ich im Config-File sozusagen einfach mit anderen Werten versehen kann. Und wie man sieht, sind das nicht nur einzelne Integer oder String-Variablen, sondern ich kann tatsächlich komplexe, verschachtelte Datentypen einfach so im Config-File definierbar machen, indem ich sie eben in diese Module-Parasection reinpacke. Ja, und damit ist hier ein Default-Wert vorgegeben, der wird halt genommen, wenn man im Config-File nichts steht, aber wenn ich im Config-File jetzt sage, MP, BSS, AP, CFG hat irgendwelche anderen Werte, dann werden halt die anderen Werte genommen an der Stelle. Im Config-File kann ich natürlich auch, ohne dass ich irgendwas unternehmen muss, genau nochmal overriden, welche Testcases jetzt wie oft in welcher Reihenfolge irgendwie ausgeführt werden. Es gibt halt das Compiled in Default, aber man kann das natürlich anders machen. So, da haben wir jetzt hier auch schon das Beispiel mit dem Guard-Timer. Ich definiere mir hier einen All-Step. Private heißt halt, na ja, ist nicht exportiert, dürft ein anderes Modul darf, den nicht benutzen. Ja, mit diesem Guard-T-Guard-Direct, das ist eben ein Timer, und wenn der abläuft, dann soll er halt das Vertik auch fail setzen. Der Grund dafür ist ein Guard-Timeout und self.stop heißt, die Komponente wird beendet an der Stelle, wenn der Timer abläuft und den All-Step, den benutze ich dann irgendwo wieder anders. Da kommt jetzt jede Menge Prosa-Code, da werde ich jetzt mal drüber gehen. Hier haben wir so Templates zum Beispiel. Ja, also da definiere ich mir eben hier einen Sender-Template. TS ist die Konvention für Sender-Templates. Destypes PDU-BSS-AP, das ist eben ein Protocollayer im Mobilfunk mit Parametern. Da kann ich noch einen Cause-Wert reingeben. Also ein Handover-Request ist das jetzt hier an der Stelle. Da kann ich ein Grund reingeben und irgendwelche Zellen, wohin das Handover eben übergeben wird. Und dann definiere ich halt hier, wie das Template aussehen soll. Oh, mit ist halt immer, soll alles nicht geschickt werden, sind also optionale Elemente und andere sollen eben entsprechend gesetzt werden. Ganz unten habe ich hier mein Control-Statement am Ende der Datei. Das ist halt, na ja, die Liste der Tests, die ausgeführt werden sollen in ihrer Reihenfolge sehr unspektakulär. Hier wird halt jeder nur einmal aufgerufen ohne irgendwelchen anderen Control-Flow. Und hier oben drüber habe ich jetzt tatsächlich Testcases, die man hier sieht. Ja, da definiere ich Testcases. Und wenn ich mir den jetzt mal anschau, da wird halt erst irgendwie eine Initialisierungsfunktion aufgerufen, die irgendwelchen State benutzt, dann werden hier gut wird hier eine Komponente mit weiteren Code gestartet. Das ist letztlich die Funktion, die hier oben definiert wird. Aber mal irgendwas anschauen, wo wir hier genau das hier ist eine schöne Funktion. Da sehen wir nämlich so Send- und Receive Operationen, die jetzt hier überall drin stehen. Ja, ich sende also eben etwas, was irgendwie einem zweierlei verschachtelten, parametrisierten Templates irgendwie spezifiziert wird. Hier unten habe ich dann, was empfangen wird, auch wieder mit parametrisierten Templates. Und das, was empfangen wurde mit dieser Value-Notation, sage ich halt, naja, das, was empfangen wurde, weißt du nochmal, der variable mncc zu. Und dann kann ich natürlich irgendwie hier aus der mncc, kann ich da noch irgendwas rausziehen, was ich da empfangen habe. Ja, oder wenn man sich mal ein Protokoll anschaut, das ist eine Definition jetzt von so einem n-Coder decoder, wenn wir uns da mal das L-Types oder sowas anschauen. Das ist jetzt mit dem raw-Codec beschrieben, ein Protokoll, wo man hier, gut, wenn erst mal die unterschiedlichen Message-Typen, die ich irgendwie hab, definiere ich eine i-Name, habe halt hier dann eine schöne Binär- Notation, wie ich die Werte von den i-Names-Typen irgendwie definiere, wie die unterschiedlichen Nachrichten-Typen heißen. Ja, erst mal die ganzen runter, dann habe ich irgendwie Information-Elemente, das ist also ein TLV-basiertes Protokoll an der Stelle, wo ich einzelne optionale Information- Elemente als TLVs habe, die damit dann kommen, das sind erst mal die Tags dieser TLVs, die ich in der i-Name definiere, dann definiere ich mir hier ein Length-Value-Feld mit einer 8-Bit Länge und einem Payload dahinter als Octet-String und sage, naja, das Längenfeld soll die Länge des Payloads encodieren und so weiter, das gleiche nochmal mit 16-Bit Length-Value-Feldern und so weiter und so weiter. Wenn man sich das dann am Schluss irgendwie zusammen anschaut, habe ich dann irgendwie sowas hier zum Beispiel, genau das ist jetzt ein Information-Element, also sowas wie ein TLV, habe ich mein Information-Element- Identifier, so nennen die das halt, das ist das Tag, habe letztlich meinen Buddy dahinter und für den Buddy definiere ich, naja, wenn der Information-Element Identifier Channel Number ist, dann wird in dem Buddy, der eine Union ist, das Feld-Channel Number benutzt und so beschreibe ich halt wieder sozusagen wie meine TLVs aufgebaut sind und welche Typen bei welchen Feldern verwendet werden und hier bastel ich mir dann meine Send-Templates zusammen, wo ich halt dann sage, naja, irgendwie die Error-Inducation, die hat halt in bestimmten Message-Discriminator mit einem Type und da kommen dann Information-Elemente und so weiter, aber es ist alles sehr, wie ich sage, eben deklarativ und ich musste jetzt nirgendwo Code schreiben, der das inkudiert und dekudiert. Aus diesen ganzen Beschreibungen baut der Compiler mir dann ein Message- Generator und ein Message-Parser und ich muss mich um nichts weiter mehr kümmern an der Stelle. Gut, wir haben noch drei, zwei Minuten sowas ungefähr, wenn jemand doch noch Fragen haben sollte, können wir die gerne klären? Ich bin auch noch der Rest der Veranstaltung da. Der ganze Code ist auch öffentlich verfügbar. Es gibt ausgezeichnete Schulungsunterlagen, habe ich hier im Further Reading in den Slides drin. Gut, da gibt es noch Slides zu Logging, wie das dann aussieht. Ja, das ist mir jetzt nicht im Detail. Ja, was so in etwa sieht das dann aus, wenn ich jetzt so eine Nachricht verschicke auf einem Port, ja, wird geschickt auf GTPC an die Komponente System und dann lockt er mir das halt so schön Menschenlesbar irgendwie runter, was ich da eben gerade verschickt habe oder auch beim Dekodieren würde, damit es dann so locken im Lockfall drin. Ja, das ist, ja genau, das ist jetzt sozusagen die abstrakte Repräsentation und hier unten habe ich dann, was er dann binär encodiert hat, hier als Hex-String drin, steht dann halt schön drin und ja, es gibt ganz viele Protokolle, die schon implementiert sind. Das ist hier nur so ein Überblick über, was man also alles finden kann. Also auch hier h248 oder L2TP oder Websockets oder Protobuffers oder Frame Relay aus den 80ern oder was auch immer man sich so vorstellen kann. Es gibt ganz viel, was schon da ist als Open Source und hier sind eben Further Reading, es gibt dieses Tutorials, sehr empfehlenswert, sind irgendwie ich glaube 350 Slides oder so, ist aber echt gut gemacht, sehr gut, das ist auch das professionelle Schullungsmaterial, was Ericsson halt für die Trainings verwendet hat, haben sie auch released und ganz viel Doku und ja, wie gesagt, auch eine Sprachspezifikation, die man echt lesen kann, die nicht wollen mich die Füße einschlafen. Gut, Frage hier. Ja, Mikro von vorne oder von hinten oder Stereo? Oder Surround? Wie würde man jetzt in dieser Notation, die da die Pakete beschreibt, wie würde man da Telev passen? Also zum Beispiel jetzt ich denke da an DVBSI, wo im Prinzip erst mal ein paar Werte halt kommen und dann kommt Zone, kommen im Prinzip immer wieder Blöcke, die irgendwas beschreiben, die fangen dann mit zum Tech an, haben dann die Lengths und dann kommt eben die Value. Und wie würde ich das jetzt darstellen in der Beschreibungssprache? Das war im Prinzip genau, was ich hier als Protokoll gerade offen hatte, dieses RSL-Type. Habe ich gepennt? Nee, nee, das will ich jetzt gar nicht gesagt haben, aber es ist auch so ein Protokoll, ja, da sage ich halt, also müssen mal die oberste Definition uns anschauen, die RSL-Message an der Stelle, muss ich mal type record as a message, genau so. Da sage ich jetzt halt nicht, die Message hat erst mal ein Message-Discriminator und so weiter und danach habe ich eine Liste von Information Elements, das sind quasi TLVs, die nennen das halt IEE hier, ja. Und das ist jetzt die RSLE-List, die besteht aus einzelnen RSLEs. Und ein RSLE besteht aus einem IEE, dem Information Element Identifier, das ist dein Tag, den hast du halt oben mal definiert, was diese Tags alles sein können, also der IEE-Type, wenn ich mir den jetzt hier suche, das ist halt diese Inam hier sozusagen, also das sind die Tag-Werte und die menschenlesbaren Memonics dafür, ja, also wenn da jetzt ein, was weiß ich, ein 001-1, 0000 binär kommt, dann weiß ich halt, das ist eine Channel Description, ja, und dann habe ich hier unten eben beschrieben, wenn denn, wenn ich jetzt hier wieder nach Channel Description suche, was weiß ich, nehm wir Channel Needed, das ist ja egal was, ja, also wenn dann der Information Element Identifier Channel Needed ist, dann bitte nehm den Channel Needed-Member aus diesem Buddy, und wenn ich mir den Buddy wieder anschaue, da habe ich dann irgendwie einen Channel Needed, das ist das und da sage ich halt, naja das Channel Needed hat den Typ, das IEE Channel Needed und dieser Typ, der habe ich dann halt hier definiert, der ist ein UN6-T, so was kann man sich halt in Zeiten halt auch schön definieren, ja, gibt es natürlich in C nicht, aber hier funktioniert das halt, da sage ich halt, naja ich habe ein UN3-T und ein UN25-T wenn ich den möchte, habe ich also ein 6-Bit Feld, was reserviert ist und habe danach einen RSL Channel Needed, was hier oben in dieser Inam beschrieben ist, was halt irgendwie ein 2-Bit langes Inam-Feld wieder ist, wo ich sage hier, viel längst 2 beim encodieren. Ja, ja genau, das wollte ich im Prinzip wissen, auch wenn es jetzt so schnell war, dass ich es, glaube ich, nochmal nachgucken muss. Okay, wir hatten aber auch nur noch 2 Minuten, gut, hat noch jemand, ich weiß nicht, wie wir zeitlich ist jetzt hier Back to Back gleich wieder was, wir haben noch Zeit. Okay, gut, dann Kann ich von dem 4G-LTE-Standard zum Beispiel die Test-Protokolle mit Titan übersetzen, um zum Beispiel Osmo-Com zu testen oder andere Geräte? Ich stehe dementsprechende Interfaces natürlich schreibe. Also ich weiß jetzt nicht, was für 4G-Conformance in Titan, ob da TTC-Insei veröffentlicht sind. Sind veröffentlicht. Okay, sind veröffentlicht, ja. Wenn es die gibt, dann kann ich mir sozusagen diese Etsy-Test-Suit nehmen und muss tatsächlich, wie du schon sagst, diese Test-Ports, also die abstrakten Test-Suits beschreiben halt nur so alles, was zwischen diesen Test-Ports passiert und die Test-Ports sind ein Implementation spezifisch. Und die musst du halt sozusagen dann dann auch dazuschreiben. Es gibt gerade die Kommunikation in Eclipse Titan findet alles in so einem Web- Forum statt, muss man nicht unbedingt mögen, aber ist halt irgendwie heute modern. Und da hat der, ich glaube, der Chef-Entwickler von Titan, der macht gerade irgendwie, ich glaube, es ist der Etsy IPv6-Test. Da beschreibt er das so Schritt für Schritt in einzelnen Posts, wie man sozusagen diese Etsy-Test-Suit nimmt und was er dann so Stück für Stück noch dazu implementiert an den Dingen außen rum, damit man die dann irgendwie ausführen kann letztlich, ja. Ja, man kann es also nicht gleich direkt ausführen, sondern muss da halt noch diesen, diese Test-Ports mit dran flanschen. Aber da ist ja auch viel schon da. Also man hat 4 Packets-Sockets, für IP-Sockets, für L2TP, PPT, GTP, was auch immer ist ja schon alles da, muss man halt nur noch verbinden. Danke. Gerne. Gut. Noch Fragen? Nö, OK, gut. Ja, also wie gesagt, gerne mich ansprechen bei Fragen. Es gibt ausgezeichnet wirklich echt gute Doku und Slides, viel besser als meine. Ich stehe ja mit Grafiken auf Kriegsfuß. Ja, das Forum ist wunderbar. Da wird immer geantwortet, wird gepostet, was halt so ein bisschen ein Problem ist, wenn man jetzt nicht da rein findet, ist man findet halt nicht irgendwie auf Stack Overflow oder sonst. Also es ist halt einfach sehr exotisch. Man findet relativ wenig Leute, die schon bisher mal Fragen und Antworten dazu gepostet haben außerhalb jetzt halt des Titan-Forums oder so. Das ist so ein bisschen das Problem mit der exotischen Nische. Aber es gibt halt unglaublich viel Code. Also alleine, was Eclipse Titan schon bringt, glaube ich, weiß ich nicht, 30, 40 Git Repositories mit unterschiedlichen Protokollen und Testports und so weiter mit, die ja alle implementiert sind. Dann haben wir im Osmo kommen jetzt auch irgendwie Tonnenweisezeug laufen. Mache ich jetzt nicht mehr. Wir sind ja eh schon über der Zeit, was wir da dann tatsächlich damit machen mit diesem Test-Suits. Also wir bauen die alle aus Jenkins gesteuert. Bauen wir Docker-Container mit den Test-Suits und Docker-Container mit unseren Netzwerkelementen und führen die dann von Jenkins gesteuert aus. Und die Titan-Execution-Engine kann Jone 2 XML generieren und das Jone 2 XML feeden wir dann direkt in den Jenkins-Test-Result-Analyzer. Und dann sieht man halt wunderbar irgendwie über den Zeitverlauf, welche Tests an welcher Stelle irgendwie erfolgreich sind und viel schlagen. Und da musste man echt außer die Tests zu schreiben, kaum mehr was tun für die ganze Infrastruktur- Integration, weil man Titan kommt sogar mit einem Makefile-Generator. Man muss sich noch nicht mal seine Makefiles selber schreiben. Man wirft seine TT-CN-3-Files dahin und seine C++-Files in ein Verzeichnis, ruft den TT-CN-3-Makefile-Gen auf, der erzeugt einem das Makefile, dann macht man Make, der baut dann erst das C++ und baut dann den Object-Code daraus und dann hat man sein Executor-Planter. Also es ist echt sehr angenehm. Gut. Okay, dann ich mal erhoffe, dass vielleicht doch Neugeh gewickt hat bei dem einen oder anderen. Trotz des exotischen Themas und der späten Stunde. Dann dankeschön und ja, viel Spaß am Gerät.