mirror of
https://gitlab.com/freepascal.org/fpc/pas2js.git
synced 2025-04-07 16:58:10 +02:00
420 lines
11 KiB
ObjectPascal
420 lines
11 KiB
ObjectPascal
{ This unit contains the TTestRunner class, a base class for the console test
|
|
runner for fpcunit.
|
|
|
|
This file is part of the Free Component Library (FCL)
|
|
Copyright (C) 2006 Vincent Snijders
|
|
|
|
Port to Pas2JS by Mattias Gaertner in 2017.
|
|
|
|
This library is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Library General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or (at your
|
|
option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; if not, write to the Free Software Foundation,
|
|
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
**********************************************************************}
|
|
unit ConsoleTestRunner;
|
|
|
|
{$mode objfpc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF NODEJS}
|
|
NodeJSApp,
|
|
{$else}
|
|
BrowserApp,
|
|
{$endif}
|
|
Classes, SysUtils,
|
|
FPCUnit, TestRegistry, TestDecorator,
|
|
//testutils,
|
|
FPCUnitReport,
|
|
//latextestreport,
|
|
//xmltestreport,
|
|
PlainTestReport
|
|
//dom
|
|
;
|
|
|
|
const
|
|
Version = '0.3';
|
|
|
|
type
|
|
TFormat = (
|
|
fPlain,
|
|
//fLatex,
|
|
//fXML,
|
|
fPlainNoTiming
|
|
);
|
|
|
|
var
|
|
DefaultFormat : TFormat = fPlain; // fXML;
|
|
DefaultRunAllTests : Boolean = False;
|
|
|
|
type
|
|
{ TTestRunner }
|
|
|
|
{ TRunForm }
|
|
|
|
TTestRunner = class({$IFDEF NODEJS}TNodeJSApplication{$ELSE}TBrowserApplication {$ENDIF})
|
|
private
|
|
FRunFormClass: TRunFormClass;
|
|
FLastTest : TTest;
|
|
FShowProgress: boolean;
|
|
FFileName: string;
|
|
FStyleSheet: string;
|
|
FLongOpts: TStrings;
|
|
FFormatParam: TFormat;
|
|
procedure DoRunAgain(Sender: TObject);
|
|
protected
|
|
property FileName: string read FFileName write FFileName;
|
|
property LongOpts: TStrings read FLongOpts write FLongOpts;
|
|
property ShowProgress: boolean read FShowProgress write FShowProgress;
|
|
property StyleSheet: string read FStyleSheet write FStyleSheet;
|
|
property FormatParam: TFormat read FFormatParam write FFormatParam;
|
|
procedure DoRun; override;
|
|
procedure DoTestRun(ATest: TTest); virtual;
|
|
function GetShortOpts: string; virtual;
|
|
procedure AppendLongOpts; virtual;
|
|
procedure WriteCustomHelp; virtual;
|
|
procedure ParseOptions; virtual;
|
|
//procedure ExtendXmlDocument(Doc: TXMLDocument); virtual;
|
|
function GetResultsWriter: TCustomResultsWriter; virtual;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
Property RunFormClass : TRunFormClass Read FRunFormClass Write FRunFormClass;
|
|
end;
|
|
|
|
implementation
|
|
|
|
const
|
|
ShortOpts = 'alhp';
|
|
DefaultLongOpts: array of string =
|
|
('all', 'list', 'progress', 'help', 'skiptiming',
|
|
'suite:', 'format:', 'file:', 'stylesheet:','sparse','no-addresses');
|
|
type
|
|
{ TDecoratorTestSuite }
|
|
|
|
TDecoratorTestSuite = Class(TTestSuite)
|
|
public
|
|
Procedure FreeDecorators(T : TTest);
|
|
Destructor Destroy; override;
|
|
end;
|
|
|
|
procedure TDecoratorTestSuite.FreeDecorators(T: TTest);
|
|
Var
|
|
I : Integer;
|
|
begin
|
|
If (T is TTestSuite) then
|
|
for I:=0 to TTestSuite(t).ChildTestCount-1 do
|
|
FreeDecorators(TTest(TTestSuite(t).Test[i]));
|
|
if (T is TTestDecorator) and (TTestDecorator(T).Test is TDecoratorTestSuite) then
|
|
T.Destroy;
|
|
end;
|
|
|
|
destructor TDecoratorTestSuite.Destroy;
|
|
begin
|
|
FreeDecorators(Self);
|
|
// We need to find something for this.
|
|
ClearTests;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
type
|
|
{ TProgressWriter }
|
|
|
|
TProgressWriter = class({TNoRefCountObject, }ITestListener)
|
|
private
|
|
FSuccess: boolean;
|
|
procedure WriteChar(c: char);
|
|
public
|
|
destructor Destroy; override;
|
|
|
|
{ ITestListener interface requirements }
|
|
procedure AddFailure(ATest: TTest; AFailure: TTestFailure); override;
|
|
procedure AddError(ATest: TTest; AError: TTestFailure); override;
|
|
procedure StartTest(ATest: TTest); override;
|
|
procedure EndTest(ATest: TTest); override;
|
|
procedure StartTestSuite(ATestSuite: TTestSuite); override;
|
|
procedure EndTestSuite(ATestSuite: TTestSuite); override;
|
|
end;
|
|
|
|
procedure TProgressWriter.WriteChar(c: char);
|
|
begin
|
|
write(c);
|
|
// flush output, so that we see the char immediately, even it is written to file
|
|
//Flush(output);
|
|
end;
|
|
|
|
destructor TProgressWriter.Destroy;
|
|
begin
|
|
// on destruction, just write the missing line ending
|
|
writeln;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TProgressWriter.AddFailure(ATest: TTest; AFailure: TTestFailure);
|
|
begin
|
|
FSuccess := false;
|
|
writechar('F');
|
|
if ATest=nil then;
|
|
if AFailure=nil then;
|
|
end;
|
|
|
|
procedure TProgressWriter.AddError(ATest: TTest; AError: TTestFailure);
|
|
begin
|
|
FSuccess := false;
|
|
writechar('E');
|
|
if ATest=nil then;
|
|
if AError=nil then ;
|
|
end;
|
|
|
|
procedure TProgressWriter.StartTest(ATest: TTest);
|
|
begin
|
|
FSuccess := true; // assume success, until proven otherwise
|
|
if ATest=nil then;
|
|
end;
|
|
|
|
procedure TProgressWriter.EndTest(ATest: TTest);
|
|
begin
|
|
if FSuccess then
|
|
writechar('.');
|
|
if ATest=nil then ;
|
|
end;
|
|
|
|
procedure TProgressWriter.StartTestSuite(ATestSuite: TTestSuite);
|
|
begin
|
|
// do nothing
|
|
if ATestSuite=nil then;
|
|
end;
|
|
|
|
procedure TProgressWriter.EndTestSuite(ATestSuite: TTestSuite);
|
|
begin
|
|
// do nothing
|
|
if ATestSuite=nil then;
|
|
end;
|
|
|
|
{ TTestRunner }
|
|
|
|
procedure TTestRunner.DoRun;
|
|
var
|
|
I,P : integer;
|
|
S,TN : string;
|
|
TS : TDecoratorTestSuite;
|
|
T : TTest;
|
|
|
|
R : TRunForm;
|
|
|
|
begin
|
|
S := CheckOptions(GetShortOpts, LongOpts);
|
|
if (S <> '') then
|
|
Writeln(S);
|
|
|
|
ParseOptions;
|
|
|
|
//get a list of all registed tests
|
|
if HasOption('l', 'list') then
|
|
case FormatParam of
|
|
//fLatex: Write(GetSuiteAsLatex(GetTestRegistry));
|
|
fPlain: Write(GetSuiteAsPlain(GetTestRegistry));
|
|
fPlainNoTiming: Write(GetSuiteAsPlain(GetTestRegistry));
|
|
else
|
|
//Write(GetSuiteAsXml(GetTestRegistry));
|
|
Write(GetSuiteAsPlain(GetTestRegistry));
|
|
end;
|
|
If Assigned(RunFormClass) then
|
|
begin
|
|
R:=RunFormClass.Create(Self);
|
|
R.OnRun:=@DoRunAgain;
|
|
R.Initialize;
|
|
end;
|
|
//run the tests
|
|
if HasOption('suite') then
|
|
begin
|
|
S := '';
|
|
S := GetOptionValue('suite');
|
|
if S = '' then
|
|
for I := 0 to GetTestRegistry.ChildTestCount - 1 do
|
|
writeln(GetTestRegistry[i].TestName)
|
|
else
|
|
begin
|
|
TS:=TDecoratorTestSuite.Create('SuiteList');
|
|
try
|
|
while Not(S = '') Do
|
|
begin
|
|
P:=Pos(',',S);
|
|
If P=0 then
|
|
P:=Length(S)+1;
|
|
TN:=Copy(S,1,P-1);
|
|
Delete(S,1,P);
|
|
if (TN<>'') then
|
|
begin
|
|
T:=GetTestRegistry.FindTest(TN);
|
|
if Assigned(T) then
|
|
TS.AddTest(T);
|
|
end;
|
|
end;
|
|
if (TS.CountTestCases>1) then
|
|
DoTestRun(TS)
|
|
else if TS.CountTestCases=1 then
|
|
DoTestRun(TS[0])
|
|
else
|
|
Writeln('No tests selected.');
|
|
finally
|
|
FreeAndNil(TS);
|
|
end;
|
|
end;
|
|
end
|
|
else if HasOption('a', 'all') or (DefaultRunAllTests and Not HasOption('l','list')) then
|
|
DoTestRun(GetTestRegistry) ;
|
|
Terminate;
|
|
end;
|
|
|
|
procedure TTestRunner.DoTestRun(ATest: TTest);
|
|
|
|
var
|
|
ResultsWriter: TCustomResultsWriter;
|
|
ProgressWriter: TProgressWriter;
|
|
TestResult: TTestResult;
|
|
begin
|
|
FLastTest:=aTest;
|
|
ResultsWriter := GetResultsWriter;
|
|
ResultsWriter.Filename := FileName;
|
|
TestResult := TTestResult.Create;
|
|
ProgressWriter:=nil;
|
|
try
|
|
if ShowProgress then
|
|
begin
|
|
ProgressWriter := TProgressWriter.Create;
|
|
TestResult.AddListener(ProgressWriter);
|
|
end
|
|
else
|
|
ProgressWriter := nil;
|
|
TestResult.AddListener(ResultsWriter.TestListener);
|
|
ATest.Run(TestResult);
|
|
ResultsWriter.WriteResult(TestResult);
|
|
finally
|
|
FreeAndNil(TestResult);
|
|
FreeAndNil(ResultsWriter);
|
|
FreeAndNil(ProgressWriter);
|
|
end;
|
|
end;
|
|
|
|
function TTestRunner.GetShortOpts: string;
|
|
begin
|
|
Result := ShortOpts;
|
|
end;
|
|
|
|
procedure TTestRunner.AppendLongOpts;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := low(DefaultLongOpts) to Length(DefaultLongOpts)-1 do
|
|
LongOpts.Add(DefaultLongOpts[i]);
|
|
end;
|
|
|
|
procedure TTestRunner.WriteCustomHelp;
|
|
begin
|
|
// no custom help options in base class
|
|
end;
|
|
|
|
procedure TTestRunner.ParseOptions;
|
|
begin
|
|
if HasOption('h', 'help') or ((ParamCount = 0) and not DefaultRunAllTests) then
|
|
begin
|
|
writeln(Title);
|
|
writeln(Version);
|
|
writeln;
|
|
writeln('Usage: ');
|
|
writeln(' --format=latex output as latex source (only list implemented)');
|
|
writeln(' --format=plain output as plain ASCII source');
|
|
writeln(' --format=xml output as XML source (default)');
|
|
writeln(' --skiptiming Do not output timings (useful for diffs of testruns)');
|
|
writeln(' --sparse Produce Less output (errors/failures only)');
|
|
writeln(' --no-addresses Do not display address info');
|
|
writeln(' --stylesheet=<reference> add stylesheet reference');
|
|
writeln(' --file=<filename> output results to file');
|
|
writeln;
|
|
writeln(' -l or --list show a list of registered tests');
|
|
writeln(' -a or --all run all tests');
|
|
writeln(' -p or --progress show progress');
|
|
writeln(' --suite=MyTestSuiteName run single test suite class');
|
|
WriteCustomHelp;
|
|
writeln;
|
|
writeln('The results can be redirected to an xml file,');
|
|
writeln('for example: ', ParamStr(0),' --all > results.xml');
|
|
end;
|
|
|
|
//get the format parameter
|
|
FormatParam := DefaultFormat;
|
|
if HasOption('format') then
|
|
begin
|
|
//if CompareText(GetOptionValue('format'),'latex')=0 then
|
|
// FormatParam := fLatex
|
|
if CompareText(GetOptionValue('format'),'plain')=0 then
|
|
FormatParam := fPlain
|
|
else if CompareText(GetOptionValue('format'),'plainnotiming')=0 then
|
|
FormatParam := fPlainNoTiming;
|
|
//else if CompareText(GetOptionValue('format'),'xml')=0 then
|
|
// FormatParam := fXML;
|
|
end;
|
|
|
|
ShowProgress := HasOption('p', 'progress');
|
|
|
|
if HasOption('file') then
|
|
FileName := GetOptionValue('file');
|
|
if HasOption('stylesheet') then
|
|
StyleSheet := GetOptionValue('stylesheet');
|
|
end;
|
|
|
|
function TTestRunner.GetResultsWriter: TCustomResultsWriter;
|
|
begin
|
|
case FormatParam of
|
|
//fLatex: Result := TLatexResultsWriter.Create(nil);
|
|
fPlain: Result := TPlainResultsWriter.Create(nil);
|
|
else
|
|
begin
|
|
Result := TPlainResultsWriter.Create(nil);
|
|
//Result := TXmlResultsWriter.Create(nil);
|
|
//ExtendXmlDocument(TXMLResultsWriter(Result).Document);
|
|
end;
|
|
end;
|
|
Result.SkipTiming:=HasOption('skiptiming');
|
|
Result.Sparse:=HasOption('sparse');
|
|
Result.SkipAddressInfo:=HasOption('no-addresses');
|
|
end;
|
|
|
|
constructor TTestRunner.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FLongOpts := TStringList.Create;
|
|
AppendLongOpts;
|
|
end;
|
|
|
|
destructor TTestRunner.Destroy;
|
|
begin
|
|
FreeAndNil(FLongOpts);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TTestRunner.DoRunAgain(Sender : TObject);
|
|
|
|
begin
|
|
if Assigned(FLastTest) then
|
|
DoTestRun(FLastTest);
|
|
end;
|
|
|
|
initialization
|
|
DefaultFormat:=fplain;
|
|
DefaultRunAllTests:=True;
|
|
end.
|
|
|