consoletestrunner.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. { This unit contains the TTestRunner class, a base class for the console test
  2. runner for fpcunit.
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (C) 2006 Vincent Snijders
  5. Port to Pas2JS by Mattias Gaertner in 2017.
  6. This library is free software; you can redistribute it and/or modify it
  7. under the terms of the GNU Library General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or (at your
  9. option) any later version.
  10. This program is distributed in the hope that it will be useful, but WITHOUT
  11. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  13. for more details.
  14. You should have received a copy of the GNU Library General Public License
  15. along with this library; if not, write to the Free Software Foundation,
  16. Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. **********************************************************************}
  18. unit ConsoleTestRunner;
  19. {$mode objfpc}
  20. interface
  21. uses
  22. NodeJSApp, Classes, SysUtils,
  23. FPCUnit, TestRegistry, TestDecorator,
  24. //testutils,
  25. FPCUnitReport,
  26. //latextestreport,
  27. //xmltestreport,
  28. PlainTestReport
  29. //dom
  30. ;
  31. const
  32. Version = '0.3';
  33. type
  34. TFormat = (
  35. fPlain,
  36. //fLatex,
  37. //fXML,
  38. fPlainNoTiming
  39. );
  40. var
  41. DefaultFormat : TFormat = fPlain; // fXML;
  42. DefaultRunAllTests : Boolean = False;
  43. type
  44. { TTestRunner }
  45. TTestRunner = class(TNodeJSApplication)
  46. private
  47. FShowProgress: boolean;
  48. FFileName: string;
  49. FStyleSheet: string;
  50. FLongOpts: TStrings;
  51. FFormatParam: TFormat;
  52. protected
  53. property FileName: string read FFileName write FFileName;
  54. property LongOpts: TStrings read FLongOpts write FLongOpts;
  55. property ShowProgress: boolean read FShowProgress write FShowProgress;
  56. property StyleSheet: string read FStyleSheet write FStyleSheet;
  57. property FormatParam: TFormat read FFormatParam write FFormatParam;
  58. procedure DoRun; override;
  59. procedure DoTestRun(ATest: TTest); virtual;
  60. function GetShortOpts: string; virtual;
  61. procedure AppendLongOpts; virtual;
  62. procedure WriteCustomHelp; virtual;
  63. procedure ParseOptions; virtual;
  64. //procedure ExtendXmlDocument(Doc: TXMLDocument); virtual;
  65. function GetResultsWriter: TCustomResultsWriter; virtual;
  66. public
  67. constructor Create(AOwner: TComponent); override;
  68. destructor Destroy; override;
  69. end;
  70. implementation
  71. const
  72. ShortOpts = 'alhp';
  73. DefaultLongOpts: array of string =
  74. ('all', 'list', 'progress', 'help', 'skiptiming',
  75. 'suite:', 'format:', 'file:', 'stylesheet:','sparse','no-addresses');
  76. type
  77. { TDecoratorTestSuite }
  78. TDecoratorTestSuite = Class(TTestSuite)
  79. public
  80. Procedure FreeDecorators(T : TTest);
  81. Destructor Destroy; override;
  82. end;
  83. procedure TDecoratorTestSuite.FreeDecorators(T: TTest);
  84. Var
  85. I : Integer;
  86. begin
  87. If (T is TTestSuite) then
  88. for I:=0 to TTestSuite(t).ChildTestCount-1 do
  89. FreeDecorators(TTest(TTestSuite(t).Test[i]));
  90. if (T is TTestDecorator) and (TTestDecorator(T).Test is TDecoratorTestSuite) then
  91. T.Destroy;
  92. end;
  93. destructor TDecoratorTestSuite.Destroy;
  94. begin
  95. FreeDecorators(Self);
  96. // We need to find something for this.
  97. ClearTests;
  98. inherited Destroy;
  99. end;
  100. type
  101. { TProgressWriter }
  102. TProgressWriter = class({TNoRefCountObject, }ITestListener)
  103. private
  104. FSuccess: boolean;
  105. procedure WriteChar(c: char);
  106. public
  107. destructor Destroy; override;
  108. { ITestListener interface requirements }
  109. procedure AddFailure(ATest: TTest; AFailure: TTestFailure); override;
  110. procedure AddError(ATest: TTest; AError: TTestFailure); override;
  111. procedure StartTest(ATest: TTest); override;
  112. procedure EndTest(ATest: TTest); override;
  113. procedure StartTestSuite(ATestSuite: TTestSuite); override;
  114. procedure EndTestSuite(ATestSuite: TTestSuite); override;
  115. end;
  116. procedure TProgressWriter.WriteChar(c: char);
  117. begin
  118. write(c);
  119. // flush output, so that we see the char immediately, even it is written to file
  120. //Flush(output);
  121. end;
  122. destructor TProgressWriter.Destroy;
  123. begin
  124. // on destruction, just write the missing line ending
  125. writeln;
  126. inherited Destroy;
  127. end;
  128. procedure TProgressWriter.AddFailure(ATest: TTest; AFailure: TTestFailure);
  129. begin
  130. FSuccess := false;
  131. writechar('F');
  132. if ATest=nil then;
  133. if AFailure=nil then;
  134. end;
  135. procedure TProgressWriter.AddError(ATest: TTest; AError: TTestFailure);
  136. begin
  137. FSuccess := false;
  138. writechar('E');
  139. if ATest=nil then;
  140. if AError=nil then ;
  141. end;
  142. procedure TProgressWriter.StartTest(ATest: TTest);
  143. begin
  144. FSuccess := true; // assume success, until proven otherwise
  145. if ATest=nil then;
  146. end;
  147. procedure TProgressWriter.EndTest(ATest: TTest);
  148. begin
  149. if FSuccess then
  150. writechar('.');
  151. if ATest=nil then ;
  152. end;
  153. procedure TProgressWriter.StartTestSuite(ATestSuite: TTestSuite);
  154. begin
  155. // do nothing
  156. if ATestSuite=nil then;
  157. end;
  158. procedure TProgressWriter.EndTestSuite(ATestSuite: TTestSuite);
  159. begin
  160. // do nothing
  161. if ATestSuite=nil then;
  162. end;
  163. { TTestRunner }
  164. procedure TTestRunner.DoRun;
  165. var
  166. I,P : integer;
  167. S,TN : string;
  168. TS : TDecoratorTestSuite;
  169. T : TTest;
  170. begin
  171. S := CheckOptions(GetShortOpts, LongOpts);
  172. if (S <> '') then
  173. Writeln(S);
  174. ParseOptions;
  175. //get a list of all registed tests
  176. if HasOption('l', 'list') then
  177. case FormatParam of
  178. //fLatex: Write(GetSuiteAsLatex(GetTestRegistry));
  179. fPlain: Write(GetSuiteAsPlain(GetTestRegistry));
  180. fPlainNoTiming: Write(GetSuiteAsPlain(GetTestRegistry));
  181. else
  182. //Write(GetSuiteAsXml(GetTestRegistry));
  183. Write(GetSuiteAsPlain(GetTestRegistry));
  184. end;
  185. //run the tests
  186. if HasOption('suite') then
  187. begin
  188. S := '';
  189. S := GetOptionValue('suite');
  190. if S = '' then
  191. for I := 0 to GetTestRegistry.ChildTestCount - 1 do
  192. writeln(GetTestRegistry[i].TestName)
  193. else
  194. begin
  195. TS:=TDecoratorTestSuite.Create('SuiteList');
  196. try
  197. while Not(S = '') Do
  198. begin
  199. P:=Pos(',',S);
  200. If P=0 then
  201. P:=Length(S)+1;
  202. TN:=Copy(S,1,P-1);
  203. Delete(S,1,P);
  204. if (TN<>'') then
  205. begin
  206. T:=GetTestRegistry.FindTest(TN);
  207. if Assigned(T) then
  208. TS.AddTest(T);
  209. end;
  210. end;
  211. if (TS.CountTestCases>1) then
  212. DoTestRun(TS)
  213. else if TS.CountTestCases=1 then
  214. DoTestRun(TS[0])
  215. else
  216. Writeln('No tests selected.');
  217. finally
  218. FreeAndNil(TS);
  219. end;
  220. end;
  221. end
  222. else if HasOption('a', 'all') or (DefaultRunAllTests and Not HasOption('l','list')) then
  223. DoTestRun(GetTestRegistry) ;
  224. Terminate;
  225. end;
  226. procedure TTestRunner.DoTestRun(ATest: TTest);
  227. var
  228. ResultsWriter: TCustomResultsWriter;
  229. ProgressWriter: TProgressWriter;
  230. TestResult: TTestResult;
  231. begin
  232. ResultsWriter := GetResultsWriter;
  233. ResultsWriter.Filename := FileName;
  234. TestResult := TTestResult.Create;
  235. ProgressWriter:=nil;
  236. try
  237. if ShowProgress then
  238. begin
  239. ProgressWriter := TProgressWriter.Create;
  240. TestResult.AddListener(ProgressWriter);
  241. end
  242. else
  243. ProgressWriter := nil;
  244. TestResult.AddListener(ResultsWriter.TestListener);
  245. ATest.Run(TestResult);
  246. ResultsWriter.WriteResult(TestResult);
  247. finally
  248. FreeAndNil(TestResult);
  249. FreeAndNil(ResultsWriter);
  250. FreeAndNil(ProgressWriter);
  251. end;
  252. end;
  253. function TTestRunner.GetShortOpts: string;
  254. begin
  255. Result := ShortOpts;
  256. end;
  257. procedure TTestRunner.AppendLongOpts;
  258. var
  259. i: Integer;
  260. begin
  261. for i := low(DefaultLongOpts) to Length(DefaultLongOpts)-1 do
  262. LongOpts.Add(DefaultLongOpts[i]);
  263. end;
  264. procedure TTestRunner.WriteCustomHelp;
  265. begin
  266. // no custom help options in base class
  267. end;
  268. procedure TTestRunner.ParseOptions;
  269. begin
  270. if HasOption('h', 'help') or ((ParamCount = 0) and not DefaultRunAllTests) then
  271. begin
  272. writeln(Title);
  273. writeln(Version);
  274. writeln;
  275. writeln('Usage: ');
  276. writeln(' --format=latex output as latex source (only list implemented)');
  277. writeln(' --format=plain output as plain ASCII source');
  278. writeln(' --format=xml output as XML source (default)');
  279. writeln(' --skiptiming Do not output timings (useful for diffs of testruns)');
  280. writeln(' --sparse Produce Less output (errors/failures only)');
  281. writeln(' --no-addresses Do not display address info');
  282. writeln(' --stylesheet=<reference> add stylesheet reference');
  283. writeln(' --file=<filename> output results to file');
  284. writeln;
  285. writeln(' -l or --list show a list of registered tests');
  286. writeln(' -a or --all run all tests');
  287. writeln(' -p or --progress show progress');
  288. writeln(' --suite=MyTestSuiteName run single test suite class');
  289. WriteCustomHelp;
  290. writeln;
  291. writeln('The results can be redirected to an xml file,');
  292. writeln('for example: ', ParamStr(0),' --all > results.xml');
  293. end;
  294. //get the format parameter
  295. FormatParam := DefaultFormat;
  296. if HasOption('format') then
  297. begin
  298. //if CompareText(GetOptionValue('format'),'latex')=0 then
  299. // FormatParam := fLatex
  300. if CompareText(GetOptionValue('format'),'plain')=0 then
  301. FormatParam := fPlain
  302. else if CompareText(GetOptionValue('format'),'plainnotiming')=0 then
  303. FormatParam := fPlainNoTiming;
  304. //else if CompareText(GetOptionValue('format'),'xml')=0 then
  305. // FormatParam := fXML;
  306. end;
  307. ShowProgress := HasOption('p', 'progress');
  308. if HasOption('file') then
  309. FileName := GetOptionValue('file');
  310. if HasOption('stylesheet') then
  311. StyleSheet := GetOptionValue('stylesheet');
  312. end;
  313. function TTestRunner.GetResultsWriter: TCustomResultsWriter;
  314. begin
  315. case FormatParam of
  316. //fLatex: Result := TLatexResultsWriter.Create(nil);
  317. fPlain: Result := TPlainResultsWriter.Create(nil);
  318. else
  319. begin
  320. Result := TPlainResultsWriter.Create(nil);
  321. //Result := TXmlResultsWriter.Create(nil);
  322. //ExtendXmlDocument(TXMLResultsWriter(Result).Document);
  323. end;
  324. end;
  325. Result.SkipTiming:=HasOption('skiptiming');
  326. Result.Sparse:=HasOption('sparse');
  327. Result.SkipAddressInfo:=HasOption('no-addresses');
  328. end;
  329. constructor TTestRunner.Create(AOwner: TComponent);
  330. begin
  331. inherited Create(AOwner);
  332. FLongOpts := TStringList.Create;
  333. AppendLongOpts;
  334. end;
  335. destructor TTestRunner.Destroy;
  336. begin
  337. FreeAndNil(FLongOpts);
  338. inherited Destroy;
  339. end;
  340. end.