latextestreport.pp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. {$mode objfpc}
  2. {$h+}
  3. {
  4. This file is part of the Free Component Library (FCL)
  5. Copyright (c) 2006 by Dean Zobec
  6. an example of latex report for FPCUnit tests.
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. unit latextestreport;
  14. interface
  15. uses
  16. classes, SysUtils, fpcunit, fpcunitreport, strutils;
  17. type
  18. { TLatexResultsWriter }
  19. TLatexResultsWriter = class(TCustomResultsWriter)
  20. private
  21. FDoc: TStringList;
  22. FSuiteHeaderIdx: TFPList;
  23. FTempFailure: TTestFailure;
  24. function TimeFormat(ATiming: TDateTime): String;
  25. protected
  26. class function EscapeText(const S: string): String; virtual;
  27. procedure WriteTestHeader(ATest: TTest; ALevel: integer; ACount: integer); override;
  28. procedure WriteTestFooter(ATest: TTest; ALevel: integer; ATiming: TDateTime); override;
  29. procedure WriteSuiteHeader(ATestSuite: TTestSuite; ALevel: integer); override;
  30. procedure WriteSuiteFooter(ATestSuite: TTestSuite; ALevel: integer;
  31. ATiming: TDateTime; ANumRuns: integer; ANumErrors: integer;
  32. ANumFailures: integer; ANumIgnores: integer); override;
  33. public
  34. constructor Create(aOwner: TComponent); override;
  35. destructor Destroy; override;
  36. procedure WriteHeader; override;
  37. procedure WriteFooter; override;
  38. procedure WriteResult(aResult: TTestResult); override;
  39. procedure AddFailure(ATest: TTest; AFailure: TTestFailure); override;
  40. procedure AddError(ATest: TTest; AError: TTestFailure); override;
  41. procedure StartTest(ATest: TTest); override;
  42. procedure EndTest(ATest: TTest); override;
  43. end;
  44. function TestSuiteAsLatex(aSuite:TTest): string;
  45. function GetSuiteAsLatex(aSuite: TTestSuite): string;
  46. implementation
  47. uses dateutils;
  48. function TLatexResultsWriter.TimeFormat(ATiming: TDateTime): String;
  49. Var
  50. M : Int64;
  51. begin
  52. Result:='ss.zzz';
  53. M:=MinutesBetween(ATiming,0);
  54. if M>60 then
  55. Result:='hh:mm:'+Result
  56. else if M>1 then
  57. Result:='mm:'+Result;
  58. end;
  59. class function TLatexResultsWriter.EscapeText(const S: string): String;
  60. var
  61. i: integer;
  62. begin
  63. SetLength(Result, 0);
  64. for i := 1 to Length(S) do
  65. case S[i] of
  66. '&','{','}','#','_','$','%': // Escape these characters
  67. Result := Result + '\' + S[i];
  68. '~','^':
  69. Result := Result + '\'+S[i]+' ';
  70. '\':
  71. Result := Result + '$\backslash$';
  72. '<':
  73. Result := Result + '$<$';
  74. '>':
  75. Result := Result + '$>$'
  76. else
  77. Result := Result + S[i];
  78. end;
  79. end;
  80. constructor TLatexResultsWriter.Create(aOwner: TComponent);
  81. begin
  82. inherited Create(aOwner);
  83. FDoc := TStringList.Create;
  84. FSuiteHeaderIdx := TFPList.Create;
  85. FTempFailure := nil;
  86. end;
  87. destructor TLatexResultsWriter.Destroy;
  88. begin
  89. FDoc.Free;
  90. FSuiteHeaderIdx.Free;
  91. inherited Destroy;
  92. end;
  93. procedure TLatexResultsWriter.WriteHeader;
  94. begin
  95. inherited WriteHeader;
  96. FDoc.Add('\documentclass[a4paper,12pt]{report}');
  97. FDoc.Add('\usepackage{fullpage}');
  98. FDoc.Add('\usepackage{color}');
  99. FDoc.Add('\definecolor{Blue}{rgb}{0.3,0.3,0.9}');
  100. FDoc.Add('\definecolor{Red}{rgb}{1,0,0}');
  101. FDoc.Add('\definecolor{Pink}{rgb}{1,0,1}');
  102. FDoc.Add('\definecolor{Yellow}{rgb}{1,1,0}');
  103. FDoc.Add('\author{FPCUnit}');
  104. FDoc.Add('\title{Unit tests run by FPCUnit}');
  105. FDoc.Add('\begin{document}');
  106. FDoc.Add('\maketitle');
  107. FDoc.Add('\flushleft');
  108. end;
  109. procedure TLatexResultsWriter.WriteFooter;
  110. begin
  111. inherited WriteFooter;
  112. end;
  113. procedure TLatexResultsWriter.WriteResult(aResult: TTestResult);
  114. var
  115. f: text;
  116. begin
  117. inherited WriteResult(aResult);
  118. with aResult do
  119. begin
  120. FDoc.Insert(11, '\begin{tabular}{ll}');
  121. FDoc.Insert(12, '{\bf Number of run tests:} &' + intToStr(RunTests)+ '\\');
  122. FDoc.Insert(13, '{\bf Number of errors:} &' + intToStr(NumberOfErrors)+ '\\');
  123. FDoc.Insert(14, '{\bf Number of failures:} &' + intToStr(NumberOfFailures)+ '\\');
  124. FDoc.Insert(15, '{\bf Number of ignored tests:} &' + intToStr(NumberOfIgnoredTests)+ '\\');
  125. FDoc.Insert(16, '\end{tabular}');
  126. end;
  127. FDoc.Add('\end{document}');
  128. system.Assign(f, FileName);
  129. rewrite(f);
  130. writeln(f, FDoc.Text);
  131. close(f);
  132. end;
  133. {ITestListener}
  134. procedure TLatexResultsWriter.AddFailure(ATest: TTest; AFailure: TTestFailure);
  135. begin
  136. inherited AddFailure(ATest, AFailure);
  137. FTempFailure := AFailure;
  138. end;
  139. procedure TLatexResultsWriter.AddError(ATest: TTest; AError: TTestFailure);
  140. begin
  141. inherited;
  142. FTempFailure := AError;
  143. end;
  144. procedure TLatexResultsWriter.WriteTestHeader(ATest: TTest; ALevel: integer; ACount: integer);
  145. begin
  146. inherited;
  147. end;
  148. procedure TLatexResultsWriter.WriteTestFooter(ATest: TTest; ALevel: integer; ATiming: TDateTime);
  149. Var
  150. S : String;
  151. begin
  152. inherited;
  153. S:=StringOfChar(' ',ALevel*2)+ ' '+ '\item[-] ';
  154. if Not SkipTiming then
  155. S:=S+FormatDateTime(TimeFormat(ATiming), ATiming);
  156. S:=S+ ' ' + EscapeText(ATest.TestName);
  157. FDoc.Add(S);
  158. if Assigned(FTempFailure) then
  159. begin
  160. //check if it's an error
  161. if not FTempFailure.IsFailure then
  162. begin
  163. FDoc[FDoc.Count -1] := '{\color{Red}'+FDoc[FDoc.Count -1];
  164. FDoc.Add('\begin{description}');
  165. FDoc.Add('\item[Error:] '+ EscapeText(FTempFailure.ExceptionClassName));
  166. FDoc.Add('\item[Exception:] '+ EscapeText(FTempFailure.ExceptionMessage));
  167. FDoc.Add('\item[Source unit:] '+ EscapeText(FTempFailure.SourceUnitName));
  168. FDoc.Add('\item[Method name:] '+ EscapeText(FTempFailure.FailedMethodName));
  169. FDoc.Add('\item[Line number:] '+ IntToStr(FTempFailure.LineNumber));
  170. FDoc.Add('\end{description}}');
  171. end
  172. else
  173. if FTempFailure.IsIgnoredTest then
  174. begin
  175. FDoc[FDoc.Count -1] := '{\color{Yellow}'+FDoc[FDoc.Count -1] + ' {\bf IGNORED TEST: ' +
  176. EscapeText(FTempFailure.ExceptionMessage) +'}}'
  177. end
  178. else
  179. //is a failure
  180. FDoc[FDoc.Count -1] := '{\color{Pink}'+FDoc[FDoc.Count -1] + ' {\bf FAILED: ' +
  181. EscapeText(FTempFailure.ExceptionMessage) +'}}';
  182. end;
  183. FTempFailure := nil;
  184. end;
  185. procedure TLatexResultsWriter.WriteSuiteHeader(ATestSuite: TTestSuite; ALevel: integer);
  186. begin
  187. inherited;
  188. FDoc.Add('{\bf {\color{Blue}'+ StringOfChar(' ',ALevel*2)+ '\item[-] '+
  189. EscapeText(ATestSuite.TestName)+ '}}');
  190. FSuiteHeaderIdx.Add(Pointer(FDoc.Count - 1));
  191. FDoc.Add(StringOfChar(' ',ALevel*2)+ '\begin{itemize}');
  192. end;
  193. procedure TLatexResultsWriter.WriteSuiteFooter(ATestSuite: TTestSuite; ALevel: integer;
  194. ATiming: TDateTime; ANumRuns: integer; ANumErrors: integer; ANumFailures: integer;
  195. ANumIgnores: integer);
  196. var
  197. idx: integer;
  198. S : String;
  199. begin
  200. inherited;
  201. FDoc.Add(StringOfChar(' ',ALevel*2)+ ' \end{itemize}');
  202. idx := Integer(FSuiteHeaderIdx[FSuiteHeaderIdx.Count -1]);
  203. S:= ' {\color{Blue}';
  204. if Not SkipTiming then
  205. S:=S+ ' Time: '+FormatDateTime('ss.zzz', ATiming);
  206. S:=S+' N:'+ IntToStr(ANumRuns)+ ' E:'+ IntToStr(ANumErrors)+ ' F:'+ IntToStr(ANumFailures)+
  207. ' I:'+ IntToStr(ANumIgnores)+'}';
  208. FDoc[idx] := FDoc[idx] +S;
  209. FSuiteHeaderIdx.Delete(FSuiteHeaderIdx.Count -1);
  210. end;
  211. procedure TLatexResultsWriter.StartTest(ATest: TTest);
  212. begin
  213. inherited StartTest(ATest);
  214. end;
  215. procedure TLatexResultsWriter.EndTest(ATest: TTest);
  216. begin
  217. inherited EndTest(ATest);
  218. end;
  219. function TestSuiteAsLatex(aSuite:TTest): string;
  220. var
  221. i,j: integer;
  222. s: TTestSuite;
  223. begin
  224. Result:='';
  225. if (aSuite.TestName<>'') then
  226. begin
  227. Result:=Result + '\item[-] ';
  228. Result:=Result+TLatexResultsWriter.EscapeText(ASuite.TestName)+slineBreak
  229. end;
  230. if aSuite.GetChildTestCount>0 then
  231. begin
  232. Result := Result + '\begin{itemize}'+ System.sLineBreak;
  233. for i:=0 to Pred(aSuite.GetChildTestCount) do
  234. Result:=Result+TestSuiteAsLatex(aSuite.GetChildTest(i));
  235. if (aSuite.TestName<>'') then
  236. Result := Result +'\end{itemize}' + System.sLineBreak;
  237. end
  238. end;
  239. function GetSuiteAsLatex(aSuite: TTestSuite): string;
  240. begin
  241. if aSuite <> nil then
  242. begin
  243. Result := '\documentclass[a4paper,12pt]{article}' + System.sLineBreak;
  244. Result := Result + '\usepackage{array}' + System.sLineBreak;
  245. Result := Result + '\usepackage{mdwlist}' + System.sLineBreak + System.sLineBreak;
  246. Result := Result + '\begin{document}' + System.sLineBreak + System.sLineBreak;
  247. if aSuite.TestName = '' then
  248. aSuite.TestName := 'Test Suite';
  249. Result := Result + '\begin{itemize}'+ System.sLineBreak;
  250. Result := Result + TestSuiteAsLatex(aSuite);
  251. Result := Result +'\end{itemize}' + System.sLineBreak;
  252. Result := Result + '\end{document}';
  253. end
  254. else
  255. Result := '';
  256. end;
  257. end.