Unit Test mit Consolenausgabe schreiben? (Linux,Knoppix 7.6)

Für allgemeine Fragen zur Programmierung, welche nicht! direkt mit Lazarus zu tun haben.
Antworten
thosch
Beiträge: 324
Registriert: Mo 10. Jul 2017, 20:32

Unit Test mit Consolenausgabe schreiben? (Linux,Knoppix 7.6)

Beitrag von thosch »

Hallo Leutz!

Ich habe mal einen DUnit Testfall geschrieben. Der Hintergrund ist mein Bestreben die WIN-API Funtionen besser zu verstehen und deshalb einen Teil davon mal selber zu implementieren. Denn wenn ich sie korrekt implementiert habe, dann habe ich deren Arbeitsweise auch korrekt verstanden.

Nun habe ich als erstes die Funktion GetMessage zu implementiren begonnen und möchte die jetzt erst mal testen. Dazu habe ich folgenden Test geschrieben, den ich im Laufe meiner Arbeit schrittweise erweitere.


Code: Alles auswählen

 
unit wapitestcase;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Crt, Graph, Classes, SysUtils, fpcunit, testutils, testregistry, qtwinapi;
 
type
 
  { TWAPITestCase }
 
  TWAPITestCase= class(TTestCase)
  protected
    procedure SetUp; override;
    procedure TearDown; override;
  published
    procedure TestHookUp;
    procedure TestGetMessage;
  end;
 
implementation
 
procedure TWAPITestCase.TestHookUp;
begin
  Fail('Write your own test');
end;
 
procedure TWAPITestCase.TestGetMessage;
var msg: TMsg;
begin
  if GetMessage(msg,0,0,0) then
  case msg.message of
   WM_KEYDOWN: writeln('Message: WM_KEYDOWN');
   WM_KEYUP: writeln('Message: WMKEYUP');
  end;
end;
 
procedure TWAPITestCase.SetUp;
var Gd,Gm: smallint;
begin
  //InitGraph(Detect,Gd,Gm);  Unitpfad für Graph muss erst noch gesetzt werden
end;
 
procedure TWAPITestCase.TearDown;
begin
  //CloseGraph;
end;
 
initialization
 
  RegisterTest(TWAPITestCase);
end.
 



Hier mein Hauptprogramm:

Code: Alles auswählen

 
program wapi;
 
uses qtwinapi, wapitestcase, crt;
 
{$APPTYPE CONSOLE}
 
var TestMsg: qtwinapi.TMsg;
    TestCase: TWAPITestCase;
 
begin
 
  TestCase:= TWAPITestCase.Create;
 
  TestCase.TestGetMessage;
 
end.
 


Ich will das später grafisch machen, für den Test reicht aber erst mal die Konsole aus.

Nur erhalte ich beim Start unter Knoppix 7.6 keine Konsole, obwohl ich im Hauptprogramm die Compilerdirektive {$APPTYPE CONSOLE} gesetzt habe.
Auch die Unit Crt ist eingebunden (für write/writeln)

Wie erhalte ich nun meine Ausgabkonsole für mein Programm. Ich habe Zielplattform Linux eingestellt, das heißt in meinem Lazarus 1.6.2 unter Konoppix die voreingestellte Zielplattform.
Zuletzt geändert von thosch am Do 17. Aug 2017, 21:48, insgesamt 1-mal geändert.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Unit Test mit Consolenausgabe schreiben? (Linux,Knoppix

Beitrag von Mathias »

Ein Haken setzen bei "Startprogramm"
Es konnte sein, das bei dir dort "gnome-terminal", wie dies geht ist es OK.
Ansonsten schreibst du folgendes rein:

Code: Alles auswählen

/usr/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)


Du willst mit InitGraph etwas basteln, da würde ich dir eher den Canvas vom Form empfehlen und die Zeichen mit Canvas.TextOut(...) schreiben.
Dateianhänge
Bildschirmfoto vom 2017-08-16 21-19-49.png
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Unit Test mit Consolenausgabe schreiben? (Linux,Knoppix

Beitrag von af0815 »

fpcunit hat sein eigene Umgebung. Diese wird entweder als Konsole aufgerufen oder als GUI, jenachdem was du beim Erstellen angegeben hast. Ein direkt aufrufen der Testcase, so wie du es machst ist nicht vorgesehen.
Links:
http://wiki.lazarus.freepascal.org/fpcunit
http://wiki.lazarus.freepascal.org/FPTest

http://blog.stevensanderson.com/2009/08 ... practises/
https://www.informatik-aktuell.de/entwi ... g-tdd.html
http://blog.stefan-macke.com/2009/08/18 ... nit-tests/


Ein Testcase beinhaltet normalerweise immer eine Entscheidung wie AssertFail, AssertTrue, Fail, Pass,.... damit wird das Ergebnis dem Testframework bekanntgegeben und der Test somit einer Bewertung zugeführt. Gerade asynchrone Vorgänge sind zum Testen eine Herausforderung, da ja nicht sofort eine Reaktion des Prüflings erfolgt.

Vor allen eines, die wichtigste Regel: Test sollen einfach und verständlich sein und keine zusätzlichen Fehlerquellen sein. Vor allen werden sie immer auf einen 'externen' Prüfling angeandt und nicht im Quelltest mitten drinnen. Das widerspricht dem Wesen der Unit-Tests.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

thosch
Beiträge: 324
Registriert: Mo 10. Jul 2017, 20:32

Re: Unit Test mit Consolenausgabe schreiben? (Linux,Knoppix

Beitrag von thosch »

@Mathias: Danke, so funktioniert das in der Console. Mein Test schlägt noch fehl, zeigt aber jetzt das Testergebenis in der Console.

grafisch mach ich später, da muss ich ja wohl eine GUI Anwendung mit Formular auswählen? Oder geht da auch was mit der
Graphics Unit in meinem aktuellen Projekt?

@af0815: Danke für die Dokus. Werde mir die in den nächsten Tagen durcharbeiten. AssertFail, AssertTrue, & Co.



Momentan hält das Programm gar nicht an.

Code: Alles auswählen

 
procedure TWAPITestCase.TestGetMessage;
var msg: TMsg;
begin
  if GetMessage(msg,0,0,0) then
  case msg.message of
   WM_KEYDOWN: writeln('Message: WM_KEYDOWN');
   WM_KEYUP: writeln('Message: WMKEYUP');
  end;
end; \0
 


Und hier ist GetMessage, meine eigene Implementation, die ich später brauche um andere API Funktionen auch zu implementieren und dann testen zu können.

Code: Alles auswählen

 
function GetMessage(out Msg: TMsg; wnd: HWND; wMsgFilterMin,wMsgFilterMax: UINT): Boolean;
const
  PREV_KEY = $40000000;
  EXT_KEY  = $01000000;
var
 c: char;
 s: char;
 key: word;
 cchr: byte;
 scan: byte;
 kev: TKeyEvent;
 ShftState,Flags: Byte;
 wmkey: TWMKEY;
 wmmou: TWMMOUSE;
 _key: String;
 waspressed: Boolean;
 moupressed: Boolean;
begin
  Result := false;
  moupressed := false;
  waspressed := false;
  if keypressed then
  begin
    Msg.message := WM_KEYDOWN;
    waspressed := true;
    kev := PollKeyEvent;
    {$ifdef linux}
     _key := RawReadKey;
      FindSequence(_key,cchr,scan);
    {$endif}
    Flags := GetKeyEventFlags(kev);
    ShftState := GetKeyEventShiftstate(kev);
    if (ShftState and kbCtrl) = kbCtrl then Include(Shift_State,ssCtrl) else Exclude(Shift_State,ssCtrl);
    if (ShftState and kbAlt) = kbAlt then Include(Shift_State,ssAlt) else Exclude(Shift_State,ssAlt);
    if (ShftState and kbShift) = kbShift then Include(Shift_State,ssShift) else Exclude(Shift_State,ssShift);
    Key := GetKeyEventCode(kev);
    c   := GetKeyEventChar(kev);
    wmkey.Msg := Msg.message;
    wmkey.CharCode := cchr;
    wmkey.Keydata := Longint(scan shl 16);
    wmkey.Keydata := wmkey.Keydata or PREV_KEY;
    Msg.message := wmkey.Msg;
    Msg.WParam  := wmkey.KeyData;
    case scan of
     1,2,4,8: wmkey.Keydata := wmkey.Keydata or 16777216;
     {.$FF01..$FF2A: wmkey.Keydata := wmkey.Keydata or 1073741824;}
    end;
    c := #0; s := #0; key := 0; scan := 0;
    Result := true;
  end
  else
  if waspressed then
  begin
    Msg.message := WM_KEYUP;
    wmkey.Keydata := wmkey.Keydata xor PREV_KEY;
    Result := true;
  end;
  PollMouseEvent(Mouse_Event);
  PutMouseEvent(Mouse_Event);
  case Mouse_Event.Buttons of
   MouseLeftButton:
     begin
       wmmou.msg := WM_LBUTTONDOWN;
       moupressed := true;
       wmmou.keys := MouseLeftButton;
       wmmou.XPos := MouseWhereX;
       wmmou.YPos := MouseWhereY;
       Result := true;
     end;
   MouseMiddleButton:
     begin
       wmmou.msg := WM_MBUTTONDOWN;
       wmmou.keys := MouseMiddleButton;
       wmmou.XPos := MouseWhereX;
       wmmou.YPos := MouseWhereY;
       moupressed := true;
       Result := true;
     end;
   MouseRightButton:
     begin wmmou.msg := WM_RBUTTONDOWN;
       wmmou.keys := MouseRightButton;
       wmmou.XPos := MouseWhereX;
       wmmou.YPos := MouseWhereY;
       moupressed := true;
       Result := true;
     end;
  end;
  case Mouse_Event.Action of
   MouseActionDown:
     begin
       case Mouse_Event.buttons of
        MouseLeftButton:
          begin
            wmmou.msg := WM_LBUTTONDOWN;
            wmmou.keys := MouseLeftButton;
            wmmou.XPos := MouseWhereX;
            wmmou.YPos := MouseWhereY;
            Result := true;
            moupressed := true;
          end;
        MouseMiddleButton:
          begin
            wmmou.msg := WM_MBUTTONDOWN;
            wmmou.keys := MouseMiddleButton;
            wmmou.XPos := MouseWhereX;
            wmmou.YPos := MouseWhereY;
            Result := true;
            moupressed := true;
          end;
        MouseRightButton:
          begin
            wmmou.msg := WM_RBUTTONDOWN;
            wmmou.keys := MouseRightButton;
            wmmou.XPos := MouseWhereX;
            wmmou.YPos := MouseWhereY;
            Result := true;
            moupressed := true;
          end;
       end;
     end;
   MouseActionMove:
     begin
       case Mouse_Event.buttons of
        MouseLeftButton:
          begin
            wmmou.msg := WM_MOUSEMOVE;
            wmmou.keys := MouseLeftButton;
            wmmou.XPos := MouseWhereX;
            wmmou.YPos := MouseWhereY;
            Result := true;
            moupressed := true;
          end;
        MouseMiddleButton:
          begin
            wmmou.msg := WM_MOUSEMOVE;
            wmmou.keys := MouseMiddleButton;
            wmmou.XPos := MouseWhereX;
            wmmou.YPos := MouseWhereY;
            Result := true;
            moupressed := true;
          end;
        MouseRightButton:
          begin
            wmmou.msg := WM_MOUSEMOVE;
            wmmou.keys := MouseRightButton;
            wmmou.XPos := MouseWhereX;
            wmmou.YPos := MouseWhereY;
            Result := true;
            moupressed := true;
          end;
       end;
     end;
   MouseActionUp:
     begin
       case Mouse_Event.Buttons of
        MouseLeftButton:
          begin
            wmmou.msg := WM_LBUTTONUP;
            wmmou.keys := MouseLeftButton;
            wmmou.XPos := MouseWhereX;
            wmmou.YPos := MouseWhereY;
            Result := true;
            moupressed := false;
          end;
        MouseMiddleButton:
          begin
            wmmou.msg := WM_MBUTTONUP;
            wmmou.keys := MouseMiddleButton;
            wmmou.XPos := MouseWhereX;
            wmmou.YPos := MouseWhereY;
            Result := true;
            moupressed := false;
          end;
        MouseRightButton:
          begin
            wmmou.msg := WM_RBUTTONUP;
            wmmou.keys := MouseRightButton;
            wmmou.XPos := MouseWhereX;
            wmmou.YPos := MouseWhereY;
            Result := true;
            moupressed := false;
          end;
       end;
     end;
  end;
end;
\0



Erwarten würde ich, dass GetMessage auf eine Eingabe von der Tastatur oder Maus wartet. Ich erhalte aber eine rollende Ausgabe von EAccessViolation at <Hexzahl>



Der Test schlägt also noch fehl, ich weiß aber nicht wo mein Fehler ist.

Weiß jemand Rat?

.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Unit Test mit Consolenausgabe schreiben? (Linux,Knoppix

Beitrag von Mathias »

Erwarten würde ich, dass GetMessage auf eine Eingabe von der Tastatur wartet.

In einem Meldungsorientierten System wäre dies eigentlich schlecht. Das Programm darf eigentlich anhalten, ansonsten könnte man zum Beispiel keine Maus-Eingabe gleichzeitig abarbeiten.

Wen man ZB. folgendes macht, blockierst du deine ganze Anwendung:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
  ReadLn;
end;
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

thosch
Beiträge: 324
Registriert: Mo 10. Jul 2017, 20:32

Re: Unit Test mit Consolenausgabe schreiben? (Linux,Knoppix

Beitrag von thosch »

Mathias hat geschrieben:
Erwarten würde ich, dass GetMessage auf eine Eingabe von der Tastatur wartet.

In einem Meldungsorientierten System wäre dies eigentlich schlecht.


katastrophal schlecht. Mein erster Test ist somit fehlgeschlagen. Ich muss für dieses Projekt DUnit weiter zum Testen verwenden.

Mathias hat geschrieben:Das Programm darf eigentlich anhalten, ansonsten könnte man zum Beispiel keine Maus-Eingabe gleichzeitig abarbeiten.


Soll es ja auch. Tut es aber nicht. Test fehlgeschlagen!

Mathias hat geschrieben:Wen man ZB. folgendes macht, blockierst du deine ganze Anwendung:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
  ReadLn;
end;


Weiß ich! So habe ich es aber nicht. Ich habe dazu ein Freepascal Konsolenprogramm gebaut. Ganz, wie im Program wapi oben im Eingangsbeitrag zu sehen.

Antworten