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:
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.
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.
Комментариев нет:
Отправить комментарий