seeseekey.net - Invictus Deus Ex Machina

Manch­mal ergibt es Sinn Quell­code der in Visual Basic vor­liegt nach C# zu kon­ver­tie­ren. Ein freies Tool, wel­ches auch große Men­gen kon­ver­tie­ren kann ist dabei „Econ Net­Vert“. Neben der Mög­lich­keit Visual Basic nach C# zu kon­ver­tie­ren, steht auch der umge­kehrte Weg offen.

NetVert in Aktion

Net­Vert in Aktion

Auch das Kon­ver­tie­ren von meh­re­ren Dateien ist kein Pro­blem, genauso wie die Umwand­lung gan­zer Pro­jekte. Bei „Econ Net­Vert“ han­delt es sich um freie Soft­ware wel­che unter der GPLv2 steht. Bezo­gen wer­den kann die Anwen­dung und der Quell­text unter http://econnetvert.codeplex.com .

Wenn man mal auf der Suche nach einem klei­nen Zeit­ver­treib ist, kann man natür­lich so eini­ges aus­den­ken, so z.B. eine eigene CPU Archi­tek­tur. Dabei muss man diese nicht in Hard­ware gie­ßen, son­dern es reicht wenn man diese emu­liert. Und so ent­stand in eini­ger Zeit eine CPU wel­che auf den Namen „Struc­tura“ hört. Ein Design­ziel war es dabei, die CPU nur mit den nötigs­ten Maschi­nen­be­feh­len aus­zu­stat­ten. Das führte bei der „Struc­tura“ zu fol­gen­den Befeh­len (hier als Assem­bler Mne­mo­nics dargestellt):

  • [0] - JUMP
  • [1] - ADD
  • [2] - COPY

Das bedeu­tet das es unter ande­rem keine Nichtope­ra­tion wie z.B. NOOP bzw. NOP im Befehls­satz der CPU gibt. Der Grund dafür ist ganz ein­fach. Eine Nichtope­ra­tion lässt sich durch einen JUMP um null Byte emu­lie­ren, was im End­ef­fekt nur eine Erhö­hung des „Instruc­tion Coun­ters“ kurz „IC“ zur Folge hat. Da JUMP auf den Opcode 0 gelegt wurde führt, dies zu einer inter­es­san­ten Reak­tion der CPU wenn sie das Ende des Pro­gramms erreicht und ver­sucht den rest­li­chen Spei­cher zu inter­pre­tie­ren. Die CPU inter­pre­tiert das als Sprung an die Adresse 0 und beginnt mit dem Pro­gramm von vorne.

Der Befehls­auf­bau der Maschi­nen­be­fehle stellt sich dabei so da:

JUMP (Breite: 40 Byte)
  [Int64|Befehlswort - 0]
  [Int64|Adressinterpretation - 0/ANCTAAV 1/ACTAAV 2/RNCTAAV 3/RCTAAV]
      ANCTAAV - Adress not contains target adress as value
      ACTAAV - Adress contains target adress as value
      RNCTAAV - Register not contains target adress as value
      RCTAAV - Register contains target adress as value
  [Int64|Sprungbedingung - 0/NONE 1/ZERO 2/POS 3/NEG 4/OVF]
  [Int64|Sprungadressierung - 0/ABS 1/REL] 
  [Int64|Adresse oder Wert]

ADD (Breite: 32 Byte)
  [Int64|Befehlswort - 1]
  [Int64|Modus - 0/RAR 1/RANR 2/RAV] 
      RAR - Register and register
      RANR - Register and negative register
      RAV - Register and value
  [Int64|Register] 
  [Int64|Register oder Wert]

COPY (Breite: 40 Byte)
  [Int64|Befehlswort - 2] 
  [Int64|Modus - 0/NACTAAV 1/FACTAAV 2/SACTAAV 3/BACTAAV]
    NACTAAV - No adress contains target adress as value
    FACTAAV - First adress contains target adress as value
    SACTAAV - Second adress contains target adress as value
    BACTAAV - Both adress contains target adress as value
  [Int64|Menge an kopierenden Daten in Byte]
  [Int64|Register, Speicheradresse oder ZERO] 
  [Int64|Register oder Speicheradresse]

Jeder Opcode auf der CPU ist 8 Byte lang und wird als Int64 inter­pre­tiert. Die „Struc­tura“ ist mit den Regis­tern A bis Z aus­ge­stat­tet, was 26 All­zweck­re­gis­ter mit einer Breite von 8 Byte sind. Neben die­sen All­zweck­re­gis­tern besitzt es das Spe­zi­al­re­gis­ter IC bei wel­chem es sich um den „Instruc­tion Coun­ter“ han­delt und einige Flags mit den Namen „Zero“, „Posi­tive“, „Nega­tive“ und „Over­flow“. Beim Start beginnt die CPU mit der Aus­füh­rung des Pro­gramms ab der Adresse 0. Eine Beson­der­heit ist, das die CPU kei­nen Stack und keine Inter­rupts unterstützt.

Damit Peri­phe­rie ange­spro­chen wer­den kann, gibt es bestimmte Berei­che im Spei­cher in wel­che sich diese Geräte ein­blen­den. Der Bereich für Ein­blen­dun­gen ist dabei ab der Adresse 9.000.000.000.000.000.000 (7CE66C50E2840000) bis 9.223.372.036.854.775.807 (7FFFFFFFFFFFFFFF) zu finden.

Die Gra­fik­karte wird dabei ab der Adresse 9.000.000.000.000.000.000 bis ein­schließ­lich 9.099.999.999.999.999.999 ein­ge­blen­det. In den ers­ten 4096 Byte befin­det sich dabei der Kon­fi­gu­ra­ti­ons­block der Gra­fik­karte, wel­cher Infor­ma­tio­nen über die Auf­lö­sung und andere Ein­stel­lun­gen enthält.

Ab der Adresse 9.100.000.000.000.000.000 bis ein­schließ­lich 9100000000000008191 wird die Tas­ta­tur bzw. deren Ein­ga­be­puf­fer ein­ge­blen­det. Die ers­ten zwei Byte im Spei­cher der CPU sind dabei ein UInt16 wel­cher den aktu­el­len Zei­chen­in­dex der Tas­ta­tur in ihrem Tas­ta­tur­puf­fer angibt. Jede Ein­gabe auf der wird dabei vom Tas­ta­tur­ge­rät in Form eines Bytear­rays mit der Größe von fünf Byte über­tra­gen. Das erste Byte gibt dabei den Modi­fier an, die rest­li­chen 4 Bytes ent­hal­ten das erzeugte Zei­chen in Form eines UTF-32 Zeichens.

Der schematische Aufbau der CPU und deren Peripherie

Der sche­ma­ti­sche Auf­bau der CPU und deren Peripherie

Der Assem­bler für die CPU unter­stützt eine Reihe von Mne­mo­nics, wel­che anschlie­ßend in die Maschi­nen­spra­che über­setzt wer­den. Diese sind:

ABS [Register]
ADD [Register] [Register oder Wert]
CLR [Register oder ALL]
COPY [Int64|Menge an kopierenden Daten in Byte] [Register oder Speicheradresse] [Register oder Speicheradresse]
DIV [Register] [Register oder Wert]
LOAD [Speicheradresse] [Register]
DEC [Register]
INC [Register]
JUMP [Sprungbedingung|NONE|ZERO|POS|NEG|OVL] [Sprungadressierung|ABS|REL] [Adresse oder Wert]
MOD [Register] [Register oder Wert]
MUL [Register] [Register oder Wert]
NEG [Register]
NOOP
SHIFTL [Register] [Register oder Wert]
SHIFTR [Register] [Register oder Wert]
WRITE [Register] [Speicheradresse]

Beim Assem­bler han­delt es sich genau­ge­nom­men nicht um einen Assem­bler der rei­nen Lehre, son­dern um eine Kreu­zung aus Assem­bler, Macroas­sem­bler und Hoch­spra­che. Für die kom­ple­xe­ren Kom­man­dos wie „MUL“ oder „SHIFTL“ wer­den bestimmte Regis­ter wäh­rend der Berech­nung belegt (im Fall von „MUL“ z.B. das W, X, Y und Z Regis­ter). Die vom Kom­mando beleg­ten Regis­ter sind dabei in der Doku­men­ta­tion auf­ge­führt. Das führt natür­lich dazu das eine sol­che Mul­ti­pli­ka­tion nach der Auf­lö­sung aus sehr vie­len Befeh­len besteht:

COPY 8 Y A;
COPY 8 Z ZERO;
ADD Z 3;
COPY 8 X ZERO;
ADD Y 0;
JUMP POS REL 72;
JUMP ZERO REL 32;
ADD X 1;
ADD Z 0;
JUMP POS REL 72;
JUMP ZERO REL 32;
ADD X 1;
COPY 8 A ZERO;
ADD Y 0;
JUMP POS REL 184;
JUMP ZERO REL 144;
COPY 8 W Y;
ADD Y -W;
ADD Y -W;
COPY 8 W ZERO;
ADD Z 0;
JUMP POS REL 184;
JUMP ZERO REL 144;
COPY 8 W Z;
ADD Z -W;
ADD Z -W;
COPY 8 W ZERO;
ADD A Y;
ADD Z -1;
JUMP POS REL -104;
COPY 8 W X;
ADD W -2;
JUMP ZERO REL 368;
COPY 8 W X;
ADD W -1;
JUMP ZERO REL 112;
COPY 8 W X;
ADD W 0;
JUMP ZERO REL 144;
COPY 8 W A;
ADD A -W;
ADD A -W;
COPY 8 W ZERO;
COPY 8 X ZERO;
COPY 8 Y ZERO;
COPY 8 Z ZERO;

Wäh­rend der Ent­wick­lung gab es dabei einige inter­es­sante Ideen wie man bestimmte Sachen im Assem­bler lösen könnte. So zum Bei­spiel fol­gen­des Problem:

ADD A 555;  //Fülle den Register A mit dem Wert 555
COPY 8 B A; //Kopiere Register A zu B
DEC B; //Reduziere B um 1
COPY 8 *A B; //kopiere der Wert von B an die Adresse welche im A Register A definiert ist (555)
COPY 8 D *A; //Kopiere das was ab 555 steht in den Register D

Im D Regis­ter sollte nun der Wert 554 ste­hen. Mög­lich wird dies dadurch, das die CPU weiß das sie nicht den Wert nach A kopie­ren soll, son­dern den Wert wel­cher in A ent­hal­ten ist, als Ziel­adresse benutzt. Theo­re­tisch wäre dies auch anders lös­bar gewe­sen. So hätte der Assem­bler das * in selbst­mo­di­fiz­tie­ren­den Quell­code auf­lö­sen kön­nen. Aus einem „COPY 8 *A D“ wäre dann in etwa fol­gen­der Quell­text erzeugt worden:

COPY 8 (IC+32) A;
COPY B [IC+32];

Das Pro­blem an die­ser Geschichte ist das der Assem­bler bei die­ser Vari­ante sehr viel über die Inne­reien der CPU wis­sen muss. So muss z.B. genau defi­niert sein wann der IC erhöht wird, da die­ser für den selbst­mo­di­fi­zier­ten Code benö­tigt wird. Zur Erkä­rung: Die erste Zeile kopiert die Adresse wel­che in A liegt in die nächste Zeile und modi­fi­ziert somit den COPY Befehl wäh­rend der Lauf­zeit. Dadurch wird die Adres­sie­rung über den Regis­ter­wert möglich.

Inter­es­sant ist auch die Berech­nung eines Sprung­zie­les. Möchte man z.B. fol­gen­des Pro­gramm ausführen:

ADD A 7;
JUMP NONE REL -104

stellt sich die Frage wie man die rela­tive Sprung­weite aus­rech­net. Hier­bei kommt es auf die Breite der Befehle an. Bei den Grund­be­feh­len (ADD, COPY und JUMP) ent­spricht diese den in der Maschi­nen­co­de­be­schrei­bung ange­ge­be­nen Breite. Andere Befehle wie „MUL“ oder „SHIFTL“ zäh­len zu den kom­ple­xen Befeh­len, da diese im Maschi­nen­code aus meh­re­ren Anwei­sun­gen bestehen.

Hier besteht die ein­fachste Mög­lich­keit darin, den JUMP Befehl im ers­ten Moment mit Fan­ta­sie­wer­ten zu fül­len. Anschlie­ßend wird der Emu­la­tor mittels:

Structura.exe program.asm -disassemble -withIC

auf­ge­ru­fen. Bei der Dar­stel­lung mit­tels „withIC“ wird der Wert des ICs am Anfang des Befehls und am Ende des Befehls angezeigt.

(256/296)            JUMP ZERO REL 32;
(296/328)            ADD X 1;
(328/360)            ADD Z 0;

Möchte man nun also zum Befehl „ADD Z 0;“ sprin­gen so rech­net man 328-296 und hat so das rich­tige Sprung­ziel errechnet.

Und natür­lich fehlt auch noch eine Umset­zung in Hard­ware, aber bis das soweit ist, kann es natur­ge­mäß dau­ern. Bei der Struc­tura han­delt es sich natür­lich nicht um eine effi­zi­ente CPU, son­dern um eine bei wel­cher ver­sucht wurde, die CPU inter­nen Befehle auf ein Mini­mum zu begren­zen und das Design als sol­ches ein­fach zu hal­ten. Dies schlägt sich unter ande­rem darin nie­der, das jedes Befehls­wort 8 Byte lang ist, und somit für 3 Befehle über­di­men­sio­niert, aber dafür die Ein­fach­heit der Maschine intern gege­ben ist.

Und natür­lich erkauft man sich dies mit eini­gen Nach­tei­len, so sind die Bit­ver­schie­bungs­ope­ra­tion eigent­lich eine der schnells­ten in einer CPU. Durch die Emu­la­tion über zusätz­li­chen Assem­bler­code wird es eine der lang­sams­ten Ope­ra­tion. Aber eine CPU mit einem gro­ßen Befehls­satz zu emu­lie­ren, ist schließ­lich ein­fach und das war nicht der Sinn der Übung.

Anschauen kann man sich das unter der GPLv3 ste­hende Pro­jekt unter https://github.com/seeseekey/Structura/. Eine vor­kom­pi­lierte Ver­sion zum tes­ten gibt es hier zum Down­load. In der Imple­men­ta­tion befin­den sich sicher­lich noch einige Feh­ler auf wel­che mich gerne hin­ge­wie­sen wer­den darf. Jetzt müsste nur noch jemand Linux auf das Sys­tem por­tie­ren, wobei dies ohne wei­te­res nicht funk­tio­nie­ren sollte, das die CPU Dinge wie Inter­rupts nicht unter­stützt. Aller­dings wäre es natür­lich durch­aus mög­lich ein eige­nes mini­ma­les Betriebs­sys­tem für das emu­lierte Sys­tem zu schreiben.

Bei der Ent­wick­lung wun­dert man sich ab und an, was für inter­es­sante Pro­jekte in den Wei­ten des Net­zes so umher­schwir­ren. So unter ande­rem das Pro­jekt Script#. Dabei han­delt es sich um eine Erwei­te­rung für das Visual Stu­dio 2012 mit wel­cher es mög­lich ist C# Quell­text (so er gewis­sen Kri­te­rien genügt) in Java­Script umzu­wan­deln, bzw. zu compilieren.

Die Erwei­te­rung wel­che im Quell­text unter https://github.com/nikhilk/scriptsharp zu fin­den ist, steht dabei unter der Apa­che Lizenz und ist somit freie Soft­ware. Nach der Instal­la­tion fügt sie dem Visual Stu­dio neue Pro­jekt­ty­pen hinzu, mit wel­chen man anschlie­ßend arbei­ten kann. Lei­der gibt es von Script# keine Mono­De­ve­lop Vari­ante, so das man im Moment zwin­gend an das Visual Stu­dio gebun­den ist. Aller­dings fin­det sich in der Road­map fol­gen­der Satz:

In terms of code con­tri­bu­tion, it would be espe­cially inte­res­ting to see the deve­lop­ment of import libra­ries for com­mon libra­ries, so they are easily usa­ble right out of the box. It would also be inte­res­ting to see the deve­lop­ment of com­ple­men­tary tools/addins, adding sup­port to other IDEs (esp. Mono­De­ve­lop) and other such com­ple­men­tary pro­jects. Or you might be inte­res­ted in the very core, i.e. the com­pi­ler itself.

Also wenn sich jemand bemü­ßigt fühlt, das ganze für Mono­De­ve­lop in Angriff zu neh­men, der muss nun nicht mehr auf die Ein­la­dung war­ten. Die offi­zi­elle Pro­jekt­seite von Script# ist unter http://scriptsharp.com/ zu finden.

Events unter C# sind so eine Sache. Im Nor­mal­fall funk­tio­nie­ren sie ohne Pro­bleme. Span­nend wird das ganze wenn man Events in einer Anwen­dung zwi­schen ver­schie­de­nen Thre­ads ver­schi­cken möchte. Dabei kann es näm­lich pas­sie­ren das Events ver­lo­ren gehen weil sie nicht emp­fan­gen wer­den. Abhilfe schafft hier die Klasse „ThreadSafeEvent“:

public class ThreadSafeEvent
{
  EventHandler internalEventHandler;
  readonly object internalEventHandlerLock=new object();

  public event EventHandler Event
  {
    add
    {
      lock(internalEventHandlerLock)
      {
        internalEventHandler+=value;
      }
    }
    remove
    {
      lock(internalEventHandlerLock)
      {
        internalEventHandler-=value;
      }
    }
  }

  public virtual void Fire(object sender, EventArgs e)
  {
    EventHandler handler;

    lock(internalEventHandlerLock)
    {
      handler=internalEventHandler;
    }

    if(handler!=null)
    {
      handler(sender, e);
    }
  }
}

Möchte man nun z.B. der Klasse „Ent­ries“ eine sol­ches Event hin­zu­fü­gen, so sieht das ganze wie folgt aus:

public ThreadSafeEvent EntrySelected=new ThreadSafeEvent();

Nun kann das Event gefeu­ert wer­den, das ganze wird mit­tels der „Fire“ Methode bewerk­stel­ligt. Die­ser über­gibt man den Sen­der und zusätz­li­che Argu­mente in Form eines „Even­tArgs“ bzw. einer davon abge­lei­te­ten Klasse.

EntrySelected.Fire(this, new EntryEventArgs(entry));

Jede Klasse wel­che das Event nun emp­fan­gen möchte hängt sich an das ent­spre­chende Event.

Entries entries=new Entries();
Entries.EntrySelected.Event+=OnEntrySelected;

...

private void OnEntrySelected(object sender, EventArgs e)
{
  EntryEventArgs args=(EntryEventArgs)e;
  Console.WriteLine(args.Entry);
}

Und schon haben wir in unse­rer Anwen­dung ein sau­be­res und thre­ad­si­che­res Eventsystem.

Im Nor­mal­fall hat es gute Gründe das man auf bestimmte Eigen­schaf­ten einer Klasse nicht zugrei­fen kann. Manch­mal ist es aber den­noch nütz­lich genau dies zu tun. So zum Bei­spiel bei der „Net­workStream“ Klasse wel­che im .NET/Mono Frame­work zu fin­den ist. Diese hat dabei die Eigen­schaft „Socket“ wel­che „pro­tec­ted“ ist. Möchte man nun doch auf diese Eigen­schaft zugrei­fen, so muss man zu etwas Magie in Form von Reflec­tion greifen:

NetworkStream stream;
...
PropertyInfo pi=stream.GetType().GetProperty("Socket", BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance);
Socket socket=(Socket)pi.GetValue(stream, null);

Hier holt man sich mit­tels Reflec­tion die Eigen­schaft und cas­tet sie. Anschlie­ßend hat man dann in der Varia­ble „socket“ die ent­spre­chende Eigen­schaft und kann mit die­ser dann machen was man möchte.

Es gibt Dinge wel­che man immer wie­der gebrau­chen kann. In diese fal­len unter ande­rem Mathe­par­ser. Einer die­ser Par­ser ist der Yet Ano­ther Math Par­ser kurz YAMP. Dabei han­delt es sich um einen von Flo­rian Rappl in C# geschrie­be­nen Par­ser. Der Par­ser beherscht dabei die Grund­re­chen­ar­ten, sowie tri­go­me­tri­sche Funk­tio­nen (Sinus, Cosi­nus et cetera) und einige andere Dinge. Die Biblio­thek steht dabei unter der BSD Lizenz, nach­dem sie vor­her nur unter der CPOL Lizenz ver­füg­bar war. Bezo­gen wer­den kann sie unter https://github.com/FlorianRappl/YAMP.

Wei­tere Infor­ma­tio­nen gibt es unter:
http://www.florian-rappl.de/Articles/Page/143/YAMP

Es gibt die ein­fa­che Art und die nicht ganz so ein­fa­che Art. So ist es zum Bei­spiel unter C# ziem­lich ein­fach eine Biblio­thek zu einem Pro­jekt hin­zu­zu­fü­gen und diese anschlie­ßend zu benut­zen. Also was liegt näher als das glei­che unter Objec­tive C mit­tels Xcode zu versuchen.

Und dort merkt man dann das Objec­tive C ein Super­set von C ist. Es ist lei­der nicht ganz so ein­fach wie man es sich wünscht. Aber fan­gen wir von vorne an. Gege­ben seien zwei Projekte:

  • Taschen­rech­ner (iOS App)
  • LibCore (Cocoa Touch Bibliothek)

Die Biblio­thek „LibCore“ soll dabei dem Pro­jekt „Taschen­rech­ner“ hin­zu­ge­fügt wer­den, damit man die­ses die ent­spre­chen­den Funk­tio­nen nut­zen kann. Unter Objec­tive C gibt es Frame­works und Biblio­the­ken. Frame­works kön­nen auf den iOS Gerä­ten nicht benutzt wer­den, damit blei­ben nur noch sta­ti­sche Bibliotheken.

Die Ein­stel­lun­gen für die Suchpfade

Im ers­ten Schritt müs­sen im Pro­jekt die Such­pfade defi­niert wer­den. Dazu gehen wir in die Pro­jekt­ein­stel­lun­gen in den Punkt „Build Set­tings“ und suchen dort nach „Hea­der“. Die Suche wird dann den Punkt „Hea­der Search Paths“ fin­den. Dort tra­gen wir den Pfad zur ent­spre­chen­den Biblio­thek ein.

Nun wer­den Hea­der­da­teien wel­che mittels:

#import "LibCore.h";

ein­ge­bun­den wer­den vom Com­pi­ler gefun­den. Beim Lin­ker kommt es aller­dings noch zu Feh­lern. Des­halb zie­hen wir das Pro­jekt (die „LibCore.xcodeprj“) auf unse­rer Taschen­rech­ner­pro­jekt. Dadurch ist die­ses Pro­jekt nun ein „Unter­pro­jekt“ von Taschen­rech­ner. In den Pro­jekt­ein­stel­lun­gen suchen wir nun den Tab „Build Pha­ses“ auf „Link Binary With Libra­ries“ und fügen dort die „LibCore.a“ hinzu.

Die Biblio­thek wird dem Lin­ker bekannt gemacht

Danach sollte das Pro­jekt mit der Biblio­thek ohne Pro­bleme kompilieren.

Wei­tere Infor­ma­tio­nen gibt es unter:
http://de.wikipedia.org/wiki/Xcode
http://de.wikipedia.org/wiki/Objective_C

Auf der Suche nach ein paar C#/CLR Interna bin ich auf die Web­seite „C# in Depth“ gesto­ßen, wobei man sagen muss das es sich dabei vor­ran­gig um ein Buch han­delt bzw. um die Web­seite zum gleich­na­mi­gen Buch han­delt. Aber auch die Web­seite gibt einige sehr inter­es­sante Infor­ma­tio­nen von sich, wel­che man vor allem in der „Arti­cles“ Sek­tion fin­det. So lege ich jedem, der sich für das Thema inter­es­siert, den Arti­kel Dele­ga­tes und Events ans Herz. Es gibt dort defi­ni­tiv die eine oder andere Erleuch­tung ;) Die Web­seite ist dabei unter http://csharpindepth.com zu finden.

Möchte man mit Webso­ckets unter .NET/Mono arbei­ten, so sollte man sich die ent­spre­chen­den Bibli­to­he­ken anschauen. Dabei gibt es die Aus­wahl zwi­schen eini­gen Bibliotheken:

Bei den jewei­li­gen Biblio­the­ken muss man dar­auf ach­ten, das die gewählte Biblio­thek den ent­spre­chen­den Webso­cket Stan­dard (den es mitt­ler­weile in 17 Revi­sio­nen gibt) unter­stützt. Ansons­ten funk­tio­nie­ren die Biblio­the­ken im gro­ßen und gan­zen ähn­lich, so das es sicher­lich auch eine Geschmacks­frage ist für wel­che Biblio­thek man sich entscheidet.

Wei­tere Infor­ma­tio­nen gibt es unter:
http://tools.ietf.org/html/rfc6455
http://de.wikipedia.org/wiki/WebSockets
http://en.wikipedia.org/wiki/WebSocket

Test­ba­sierte Ent­wick­lung ist schon eine schöne Sache, vor allem bei grö­ße­ren Pro­jek­ten. Mit Hilfe soge­nann­ter Unit-Tests kann man dabei auto­ma­ti­siert über­prü­fen ob der zu tes­tende Soft­ware­teil noch immer die Ergeb­nisse lie­fert, die erwar­tet wer­den. Meist ist es so, dass man in grö­ße­ren Soft­ware­pro­jek­ten etwas ändert und damit impli­zit an ande­ren Stel­len Feh­ler ein­baut, wel­che ohne Unit-Tests gar nicht bzw. erst spä­ter auffallen.

Für die Ent­wick­lung unter .NET/Mono mit Hilfe des Visual Stu­dios habe ich mich dabei für NUnit ent­schie­den, da man die­ses im Gegen­satz zu MsTest auch unter Linux und Mac OS X nut­zen kann. Damit sich das ganze bequem in das Visual Stu­dio inte­griert nutze ich TestDriven.NET wel­ches unter http://www.testdriven.net/ bezo­gen wer­den kann. Der per­sön­li­che Gebrauch ist dabei kostenlos.

Nach der Instal­la­tion von TestDriven.NET kann man sein Visual Stu­dio star­ten und mit der test­ba­sier­ten Ent­wick­lung begin­nen. Dabei hat man zwei Mög­lich­kei­ten dies zu lösen. Die erste Mög­lich­keit ist es die Tests in ein extra Pro­jekt zu ver­la­gern so das diese nicht in der zu tes­ten­den Soft­ware vor­kom­men. Bei der zwei­ten Mög­lich­keit wer­den die Tests direkt im zu tes­ten­den Pro­jekt geschrie­ben (z.B. in einen Name­space „Tests“).

Als Bei­spiel sei fol­gende Klasse gegeben:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Project
{
	public class FooBar
	{
		public int A { get; private set; }
		public int B { get; private set; }
		public int C { get; private set; }

		public FooBar(int a, int b, int c)
		{
			A=a;
			B=b;
			C=c;
		}
	}
}

Die Klasse „Foo­Bar“ nimmt dabei im Kon­struk­tor die drei Varia­blen a, b und c ent­ge­gen und weist sie den jewei­li­gen Eigen­schaf­ten der Klasse zu. Um nun zu tes­ten ob diese Zuwei­sung funk­tio­niert schrei­ben wir eine ent­spre­chende Test-Unit. Dazu erstel­len wir eine Klasse namens: „FooBarTest.cs“:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;

namespace Project
{
	[TestFixture]
	public class FooBarTests
	{
		[Test]
		public void TestNewFooBar()
		{
			FooBar fooBar=new FooBar(13, 24, 47);

			Assert.AreEqual(13, fooBar.A);
			Assert.AreEqual(24, fooBar.B);
			Assert.AreEqual(47, fooBar.C);
		}
	}
}

Diese Test­klasse tes­tet nun ob die Werte die im Kon­struk­tor ange­ge­ben wer­den auch wirk­lich in den ent­spre­chen­den Eigen­schaf­ten lan­den. Dazu wird zuerst das NUnit Frame­work mittels:

using NUnit.Framework;

ein­ge­bun­den. Mit­tels des Attri­bu­tes „[Test­Fix­ture]“ sagen wir dem NUnit Frame­work das sich in die­ser Klasse Unit-Tests befin­den. Bei die­sem Attri­but gibt es einige Ein­schrän­kun­gen, so darf die Klasse z.B. nicht abs­trakt sein, nähe­res dazu erfährt man auch in der Doku­men­ta­tion.

Das Attri­but „[Test]“ über der Funk­tion „Test­Ne­w­Foo­Bar“ teilt dem Frame­work mit das es sich bei die­ser Funk­tion um einen Unit-Test han­delt, wel­cher über­prüft wer­den soll. Dazu legen wir in der Funk­tion eine Instanz von „Foo­Bar“ an und über­ge­ben die ent­spre­chen­den Werte an den Konstruktor.

Mit­tels der „Assert.AreEqual“ Funk­tion (wei­tere „Assert“ Funk­tio­nen fin­det man in der Doku­men­ta­tion) über­prü­fen wir ob die Werte auch bei den Eigen­schaf­ten A, B und C ange­kom­men sind. Wäre dies nicht der Fall so würde der Tests fehlschlagen.

Nach­dem ein Test für die ent­spre­chende Klasse geschrie­ben wurde, klickt man im Visual Stu­dio mit der rech­ten Maus­taste auf die Test­klasse und wählt dort „Run Test(s)“ aus. Möchte man meh­rere Tests lau­fen las­sen so muss einen über­ge­ord­ne­ten Ord­ner aus­wäh­len. Wenn alles rich­tig gemacht wurde, sollte man fol­gende Aus­gabe sehen:

------ Test started: Assembly: Project.exe ------

1 passed, 0 failed, 0 skipped, took 3,07 seconds (NUnit 2.6.0).

Falls ein Feh­ler auf­ge­tre­ten ist so sieht das ganze so aus:

------ Test started: Assembly: Project.exe ------

Test 'Project.FooBarTests.TestNewFooBar' failed: 
  Expected: 47
  But was:  0
	FooBarTests.cs(19,0): bei Project.FooBarTests.TestNewFooBar()

0 passed, 1 failed, 0 skipped, took 1,66 seconds (NUnit 2.6.0).

Fin­det man einen Feh­ler bei einem Test sollte man schauen wodurch die­ser ver­ur­sacht wird und ihn behe­ben. Wenn man nun bei der spä­te­ren Ent­wick­lung Feh­ler fin­det (und besei­tigt), wel­che durch kei­nen Tests abge­deckt sind, so sollte man gleich einen ent­spre­chen­den Test dafür schrei­ben, damit die­ser Feh­ler nicht ein wei­te­res Mal auf­tre­ten kann.

Wei­tere Infor­ma­tio­nen gibt es unter:
http://de.wikipedia.org/wiki/NUnit
http://de.wikipedia.org/wiki/XUnit