consoletestrunner.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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. {$IFDEF NODEJS}
  23. NodeJSApp,
  24. {$else}
  25. BrowserApp,
  26. {$endif}
  27. Classes, SysUtils,
  28. FPCUnit, TestRegistry, TestDecorator,
  29. //testutils,
  30. FPCUnitReport,
  31. //latextestreport,
  32. //xmltestreport,
  33. PlainTestReport
  34. //dom
  35. ;
  36. const
  37. Version = '0.3';
  38. type
  39. TFormat = (
  40. fPlain,
  41. //fLatex,
  42. //fXML,
  43. fPlainNoTiming
  44. );
  45. var
  46. DefaultFormat : TFormat = fPlain; // fXML;
  47. DefaultRunAllTests : Boolean = False;
  48. type
  49. { TTestRunner }
  50. { TRunForm }
  51. TTestRunner = class({$IFDEF NODEJS}TNodeJSApplication{$ELSE}TBrowserApplication {$ENDIF})
  52. private
  53. FRunFormClass: TRunFormClass;
  54. FLastTest : TTest;
  55. FShowProgress: boolean;
  56. FFileName: string;
  57. FStyleSheet: string;
  58. FLongOpts: TStrings;
  59. FFormatParam: TFormat;
  60. procedure DoRunAgain(Sender: TObject);
  61. protected
  62. property FileName: string read FFileName write FFileName;
  63. property LongOpts: TStrings read FLongOpts write FLongOpts;
  64. property ShowProgress: boolean read FShowProgress write FShowProgress;
  65. property StyleSheet: string read FStyleSheet write FStyleSheet;
  66. property FormatParam: TFormat read FFormatParam write FFormatParam;
  67. procedure DoRun; override;
  68. procedure DoTestRun(ATest: TTest); virtual;
  69. function GetShortOpts: string; virtual;
  70. procedure AppendLongOpts; virtual;
  71. procedure WriteCustomHelp; virtual;
  72. procedure ParseOptions; virtual;
  73. //procedure ExtendXmlDocument(Doc: TXMLDocument); virtual;
  74. function GetResultsWriter: TCustomResultsWriter; virtual;
  75. public
  76. constructor Create(AOwner: TComponent); override;
  77. destructor Destroy; override;
  78. Property RunFormClass : TRunFormClass Read FRunFormClass Write FRunFormClass;
  79. end;
  80. implementation
  81. const
  82. ShortOpts = 'alhp';
  83. DefaultLongOpts: array of string =
  84. ('all', 'list', 'progress', 'help', 'skiptiming',
  85. 'suite:', 'format:', 'file:', 'stylesheet:','sparse','no-addresses');
  86. type
  87. { TDecoratorTestSuite }
  88. TDecoratorTestSuite = Class(TTestSuite)
  89. public
  90. Procedure FreeDecorators(T : TTest);
  91. Destructor Destroy; override;
  92. end;
  93. procedure TDecoratorTestSuite.FreeDecorators(T: TTest);
  94. Var
  95. I : Integer;
  96. begin
  97. If (T is TTestSuite) then
  98. for I:=0 to TTestSuite(t).ChildTestCount-1 do
  99. FreeDecorators(TTest(TTestSuite(t).Test[i]));
  100. if (T is TTestDecorator) and (TTestDecorator(T).Test is TDecoratorTestSuite) then
  101. T.Destroy;
  102. end;
  103. destructor TDecoratorTestSuite.Destroy;
  104. begin
  105. FreeDecorators(Self);
  106. // We need to find something for this.
  107. ClearTests;
  108. inherited Destroy;
  109. end;
  110. type
  111. { TProgressWriter }
  112. TProgressWriter = class({TNoRefCountObject, }ITestListener)
  113. private
  114. FSuccess: boolean;
  115. procedure WriteChar(c: char);
  116. public
  117. destructor Destroy; override;
  118. { ITestListener interface requirements }
  119. procedure AddFailure(ATest: TTest; AFailure: TTestFailure); override;
  120. procedure AddError(ATest: TTest; AError: TTestFailure); override;
  121. procedure StartTest(ATest: TTest); override;
  122. procedure EndTest(ATest: TTest); override;
  123. procedure StartTestSuite(ATestSuite: TTestSuite); override;
  124. procedure EndTestSuite(ATestSuite: TTestSuite); override;
  125. end;
  126. procedure TProgressWriter.WriteChar(c: char);
  127. begin
  128. write(c);
  129. // flush output, so that we see the char immediately, even it is written to file
  130. //Flush(output);
  131. end;
  132. destructor TProgressWriter.Destroy;
  133. begin
  134. // on destruction, just write the missing line ending
  135. writeln;
  136. inherited Destroy;
  137. end;
  138. procedure TProgressWriter.AddFailure(ATest: TTest; AFailure: TTestFailure);
  139. begin
  140. FSuccess := false;
  141. writechar('F');
  142. if ATest=nil then;
  143. if AFailure=nil then;
  144. end;
  145. procedure TProgressWriter.AddError(ATest: TTest; AError: TTestFailure);
  146. begin
  147. FSuccess := false;
  148. writechar('E');
  149. if ATest=nil then;
  150. if AError=nil then ;
  151. end;
  152. procedure TProgressWriter.StartTest(ATest: TTest);
  153. begin
  154. FSuccess := true; // assume success, until proven otherwise
  155. if ATest=nil then;
  156. end;
  157. procedure TProgressWriter.EndTest(ATest: TTest);
  158. begin
  159. if FSuccess then
  160. writechar('.');
  161. if ATest=nil then ;
  162. end;
  163. procedure TProgressWriter.StartTestSuite(ATestSuite: TTestSuite);
  164. begin
  165. // do nothing
  166. if ATestSuite=nil then;
  167. end;
  168. procedure TProgressWriter.EndTestSuite(ATestSuite: TTestSuite);
  169. begin
  170. // do nothing
  171. if ATestSuite=nil then;
  172. end;
  173. { TTestRunner }
  174. procedure TTestRunner.DoRun;
  175. var
  176. I,P : integer;
  177. S,TN : string;
  178. TS : TDecoratorTestSuite;
  179. T : TTest;
  180. R : TRunForm;
  181. begin
  182. S := CheckOptions(GetShortOpts, LongOpts);
  183. if (S <> '') then
  184. Writeln(S);
  185. ParseOptions;
  186. //get a list of all registed tests
  187. if HasOption('l', 'list') then
  188. case FormatParam of
  189. //fLatex: Write(GetSuiteAsLatex(GetTestRegistry));
  190. fPlain: Write(GetSuiteAsPlain(GetTestRegistry));
  191. fPlainNoTiming: Write(GetSuiteAsPlain(GetTestRegistry));
  192. else
  193. //Write(GetSuiteAsXml(GetTestRegistry));
  194. Write(GetSuiteAsPlain(GetTestRegistry));
  195. end;
  196. If Assigned(RunFormClass) then
  197. begin
  198. R:=RunFormClass.Create(Self);
  199. R.OnRun:=@DoRunAgain;
  200. R.Initialize;
  201. end;
  202. //run the tests
  203. if HasOption('suite') then
  204. begin
  205. S := '';
  206. S := GetOptionValue('suite');
  207. if S = '' then
  208. for I := 0 to GetTestRegistry.ChildTestCount - 1 do
  209. writeln(GetTestRegistry[i].TestName)
  210. else
  211. begin
  212. TS:=TDecoratorTestSuite.Create('SuiteList');
  213. try
  214. while Not(S = '') Do
  215. begin
  216. P:=Pos(',',S);
  217. If P=0 then
  218. P:=Length(S)+1;
  219. TN:=Copy(S,1,P-1);
  220. Delete(S,1,P);
  221. if (TN<>'') then
  222. begin
  223. T:=GetTestRegistry.FindTest(TN);
  224. if Assigned(T) then
  225. TS.AddTest(T);
  226. end;
  227. end;
  228. if (TS.CountTestCases>1) then
  229. DoTestRun(TS)
  230. else if TS.CountTestCases=1 then
  231. DoTestRun(TS[0])
  232. else
  233. Writeln('No tests selected.');
  234. finally
  235. FreeAndNil(TS);
  236. end;
  237. end;
  238. end
  239. else if HasOption('a', 'all') or (DefaultRunAllTests and Not HasOption('l','list')) then
  240. DoTestRun(GetTestRegistry) ;
  241. Terminate;
  242. end;
  243. procedure TTestRunner.DoTestRun(ATest: TTest);
  244. var
  245. ResultsWriter: TCustomResultsWriter;
  246. ProgressWriter: TProgressWriter;
  247. TestResult: TTestResult;
  248. begin
  249. FLastTest:=aTest;
  250. ResultsWriter := GetResultsWriter;
  251. ResultsWriter.Filename := FileName;
  252. TestResult := TTestResult.Create;
  253. ProgressWriter:=nil;
  254. try
  255. if ShowProgress then
  256. begin
  257. ProgressWriter := TProgressWriter.Create;
  258. TestResult.AddListener(ProgressWriter);
  259. end
  260. else
  261. ProgressWriter := nil;
  262. TestResult.AddListener(ResultsWriter.TestListener);
  263. ATest.Run(TestResult);
  264. ResultsWriter.WriteResult(TestResult);
  265. finally
  266. FreeAndNil(TestResult);
  267. FreeAndNil(ResultsWriter);
  268. FreeAndNil(ProgressWriter);
  269. end;
  270. end;
  271. function TTestRunner.GetShortOpts: string;
  272. begin
  273. Result := ShortOpts;
  274. end;
  275. procedure TTestRunner.AppendLongOpts;
  276. var
  277. i: Integer;
  278. begin
  279. for i := low(DefaultLongOpts) to Length(DefaultLongOpts)-1 do
  280. LongOpts.Add(DefaultLongOpts[i]);
  281. end;
  282. procedure TTestRunner.WriteCustomHelp;
  283. begin
  284. // no custom help options in base class
  285. end;
  286. procedure TTestRunner.ParseOptions;
  287. begin
  288. if HasOption('h', 'help') or ((ParamCount = 0) and not DefaultRunAllTests) then
  289. begin
  290. writeln(Title);
  291. writeln(Version);
  292. writeln;
  293. writeln('Usage: ');
  294. writeln(' --format=latex output as latex source (only list implemented)');
  295. writeln(' --format=plain output as plain ASCII source');
  296. writeln(' --format=xml output as XML source (default)');
  297. writeln(' --skiptiming Do not output timings (useful for diffs of testruns)');
  298. writeln(' --sparse Produce Less output (errors/failures only)');
  299. writeln(' --no-addresses Do not display address info');
  300. writeln(' --stylesheet=<reference> add stylesheet reference');
  301. writeln(' --file=<filename> output results to file');
  302. writeln;
  303. writeln(' -l or --list show a list of registered tests');
  304. writeln(' -a or --all run all tests');
  305. writeln(' -p or --progress show progress');
  306. writeln(' --suite=MyTestSuiteName run single test suite class');
  307. WriteCustomHelp;
  308. writeln;
  309. writeln('The results can be redirected to an xml file,');
  310. writeln('for example: ', ParamStr(0),' --all > results.xml');
  311. end;
  312. //get the format parameter
  313. FormatParam := DefaultFormat;
  314. if HasOption('format') then
  315. begin
  316. //if CompareText(GetOptionValue('format'),'latex')=0 then
  317. // FormatParam := fLatex
  318. if CompareText(GetOptionValue('format'),'plain')=0 then
  319. FormatParam := fPlain
  320. else if CompareText(GetOptionValue('format'),'plainnotiming')=0 then
  321. FormatParam := fPlainNoTiming;
  322. //else if CompareText(GetOptionValue('format'),'xml')=0 then
  323. // FormatParam := fXML;
  324. end;
  325. ShowProgress := HasOption('p', 'progress');
  326. if HasOption('file') then
  327. FileName := GetOptionValue('file');
  328. if HasOption('stylesheet') then
  329. StyleSheet := GetOptionValue('stylesheet');
  330. end;
  331. function TTestRunner.GetResultsWriter: TCustomResultsWriter;
  332. begin
  333. case FormatParam of
  334. //fLatex: Result := TLatexResultsWriter.Create(nil);
  335. fPlain: Result := TPlainResultsWriter.Create(nil);
  336. else
  337. begin
  338. Result := TPlainResultsWriter.Create(nil);
  339. //Result := TXmlResultsWriter.Create(nil);
  340. //ExtendXmlDocument(TXMLResultsWriter(Result).Document);
  341. end;
  342. end;
  343. Result.SkipTiming:=HasOption('skiptiming');
  344. Result.Sparse:=HasOption('sparse');
  345. Result.SkipAddressInfo:=HasOption('no-addresses');
  346. end;
  347. constructor TTestRunner.Create(AOwner: TComponent);
  348. begin
  349. inherited Create(AOwner);
  350. FLongOpts := TStringList.Create;
  351. AppendLongOpts;
  352. end;
  353. destructor TTestRunner.Destroy;
  354. begin
  355. FreeAndNil(FLongOpts);
  356. inherited Destroy;
  357. end;
  358. procedure TTestRunner.DoRunAgain(Sender : TObject);
  359. begin
  360. if Assigned(FLastTest) then
  361. DoTestRun(FLastTest);
  362. end;
  363. initialization
  364. DefaultFormat:=fplain;
  365. DefaultRunAllTests:=True;
  366. end.