воскресенье, 15 марта 2015 г.

GUI-testing 3. Thinking of testing №2

Original in Russian: http://18delphi.blogspot.ru/2013/11/2.html

GUI-testing. Table of contents

Now that I have been developing a bit I began to think about parsing for tokens.


The code has become like this:

unit Script.Parser;
 
interface
 
uses
 Classes,
 CoreObject
 ;
 
{$IfNDef NoTesting}
 {$Define TestParser}
{$EndIf  NoTesting}
 
type
 TscriptParser = class(TCoreObject)
  private
   f_Stream : TStream;
   {$IfDef TestParser}
   f_GetCharLog : TStream;
   // - right away we think of testing, we’ll output in this stream
   //   the result of function GetChar
   f_ReadLnLog : TStream;
   // - right away we think of testing, we’ll output in this stream
   //   the result of function ReadLn
   f_TokenLog : TStream;
   // - right away we think of testing, we’ll output in this stream
   //   the result of function NextToken
   {$EndIf TestParser}
   f_EOF : Boolean;
   f_CurrentLine : String;
   f_PosInCurrentLine : Integer;
   f_Token : String;
  {$IfDef TestParser}
  private
   procedure WriteLineToLog(const aLine: AnsiString; aLog: TStream);
  {$EndIf TestParser}
  protected
   procedure Cleanup; override;
   function ReadLn: String;
  protected
   function GetChar(out aChar: AnsiChar): Boolean;
  public
   constructor Create(const aStream : TStream; const aFileName : String); overload;
   constructor Create(const aFileName : String); overload;
   function EOF: Boolean;
   procedure NextToken;
 end;//TscriptParser
 
implementation
 
uses
 System.SysUtils
 ;
 
constructor TscriptParser.Create(const aStream : TStream; const aFileName : String);
begin
 inherited Create;
 f_PosInCurrentLine := 1;
 f_EOF := false;
 f_Stream := aStream;
 {$IfDef TestParser}
 f_GetCharLog := TFileStream.Create(aFileName + '.GetChar.log', fmCreate);
 f_ReadLnLog := TFileStream.Create(aFileName + '.ReadLn.log', fmCreate);
 f_TokenLog := TFileStream.Create(aFileName + '.Token.log', fmCreate);
 {$EndIf TestParser}
end;
 
constructor TscriptParser.Create(const aFileName : String);
var
 l_FileName : String;
begin
 l_FileName := ExtractFilePath(ParamStr(0)) + '\' + aFileName;
 Create(TFileStream.Create(l_FileName, fmOpenRead), l_FileName);
end;
 
procedure TscriptParser.Cleanup;
begin
 FreeAndNil(f_Stream);
 {$IfDef TestParser}
 FreeAndNil(f_TokenLog);
 FreeAndNil(f_GetCharLog);
 FreeAndNil(f_ReadLnLog);
 {$EndIf TestParser}
 inherited;
end;
 
function TscriptParser.GetChar(out aChar: AnsiChar): Boolean;
begin
 if (f_Stream.Read(aChar, SizeOf(aChar)) = SizeOf(aChar)) then
 begin
  Result := true;
  {$IfDef TestParser}
  f_GetCharLog.Write(aChar, SizeOf(aChar));
  {$EndIf TestParser}
 end
 else
  Result := false;
end;
 
{$IfDef TestParser}
procedure TscriptParser.WriteLineToLog(const aLine: AnsiString; aLog: TStream);
const
 cEOL : AnsiString = #13#10;
begin
  aLog.Write(@aLine[1], Length(aLine));
  aLog.Write(@cEOL[1], Length(cEOL));
end;
{$EndIf TestParser}
 
function TscriptParser.ReadLn: String;
{$IfDef TestParser}
var
 l_Result : AnsiString;
{$EndIf TestParser}
var
 l_Char : AnsiChar;
 l_Line : String;
 l_LineCommentPos : Integer;
begin
 {$IfDef TestParser}
 try
 {$EndIf TestParser}
  try
   l_Line := '';
   while GetChar(l_Char) do
   begin
    if (l_Char = #13) then
    begin
     if GetChar(l_Char) then
     begin
      if (l_Char = #10) then
      begin
       Result := l_Line;
       Exit;
      end//l_Char = #10
      else
       Assert(false, 'Something has gone wrong, there is no character 10 after 13');
     end//GetChar(l_Char)
     else
      Assert(false, 'Something has gone wrong, at once the file end after character 13');
    end;//l_Char = #13
    l_Line := l_Line + l_Char;
   end;//while GetChar(l_Char)
   f_EOF := true;
   Result := l_Line;
  finally
   l_LineCommentPos := Pos('//', Result);
   if (l_LineCommentPos > 0) then
   begin
    Delete(Result, l_LineCommentPos, Length(Result) - l_LineCommentPos + 1);
   end;//l_LineCommentPos > 0
  end;//try..finally
 {$IfDef TestParser}
 finally
  WriteLineToLog(Result, f_ReadLnLog);
 end;//try..finally
 {$EndIf TestParser}
end;
 
procedure TscriptParser.NextToken;
begin
 if (f_PosInCurrentLine > Length(f_CurrentLine)) then
 begin
  // - The WHOLE current line kind of has been processed
  f_CurrentLine := '';
  f_PosInCurrentLine := 1;
 end;//f_PosInCurrentLine > Length(f_CurrentLine)
 while(f_CurrentLine = '') do
 begin
  f_CurrentLine := ReadLn;
  if (f_CurrentLine = '') then
   if f_EOF then
    Exit;
 end;//while(f_NextToken = '')
 f_Token := '';
 {$IfDef TestParser}
 WriteLineToLog(f_Token, f_TokenLog);
 {$EndIf TestParser}
 f_CurrentLine := '';
end;
 
function TscriptParser.EOF: Boolean;
begin
 Result := f_EOF;
end;
 
end.

In some way this is - TDD (https://en.wikipedia.org/wiki/Test-driven_development).

When you write the code and think of testing it THEN AND THERE.

And about “where to see the results of the code”.

It is better to do it on FILE system, NOT in the debugger.

If you load such “etalons” in CVS/SVN, you can see REGRESSION AT ONCE (https://en.wikipedia.org/wiki/Regression_testing).

How it can be added to DUnit and how to “automatically compare etalons” – I will tell a bit later.

Комментариев нет:

Отправить комментарий