xmltestreport.pp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2006 by Dean Zobec, Graeme Geldenhuys
  4. An example of an XML report writer for FPCUnit tests.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************
  11. Purpose:
  12. This unit contains a XML TestListener for use with the fpcUnit testing
  13. framework. It uses the XMLWrite unit (part of FPC) to generate
  14. the XML document. The benefit of using XMLWrite is that the data generated
  15. is valid XML, with reserved characters correctly escaped.
  16. This allows the XML document to be further processed with XSLT etc without
  17. any issues.
  18. Notes:
  19. Specify 'null' as the filename if you don't want to output to file (e.g.
  20. used by the GUI test runner which instead reads the Document property).
  21. }
  22. unit xmltestreport;
  23. {$mode objfpc}{$H+}
  24. interface
  25. uses
  26. Classes, SysUtils,fpcunit, fpcunitreport, testutils, dom, XMLWrite;
  27. type
  28. { TXMLResultsWriter }
  29. TXMLResultsWriter = class(TCustomResultsWriter)
  30. private
  31. FDoc: TXMLDocument;
  32. FResults, FListing: TDOMNode;
  33. FSuitePath: TFPList;
  34. FCurrentTest: TDOMElement;
  35. protected
  36. procedure WriteTestHeader(ATest: TTest; ALevel: integer; ACount: integer); override;
  37. procedure WriteTestFooter(ATest: TTest; ALevel: integer; ATiming: TDateTime); override;
  38. procedure WriteSuiteHeader(ATestSuite: TTestSuite; ALevel: integer); override;
  39. procedure WriteSuiteFooter(ATestSuite: TTestSuite; ALevel: integer;
  40. ATiming: TDateTime; ANumRuns: integer; ANumErrors: integer;
  41. ANumFailures: integer; ANumIgnores: integer); override;
  42. public
  43. constructor Create(aOwner: TComponent); override;
  44. destructor Destroy; override;
  45. procedure WriteHeader; override;
  46. procedure WriteFooter; override;
  47. procedure AddFailure(ATest: TTest; AFailure: TTestFailure); override;
  48. procedure AddError(ATest: TTest; AError: TTestFailure); override;
  49. procedure StartTest(ATest: TTest); override;
  50. procedure EndTest(ATest: TTest); override;
  51. procedure WriteResult(aResult: TTestResult); override;
  52. { A public property to the internal XML document }
  53. property Document: TXMLDocument read FDoc;
  54. end;
  55. function GetSuiteAsXML(aSuite: TTestSuite): string;
  56. function TestSuiteAsXML(n: TDOMElement; FDoc: TXMLDocument; aSuite:TTestSuite): string;
  57. implementation
  58. function GetSuiteAsXML(aSuite: TTestSuite): string;
  59. var
  60. FDoc: TXMLDocument;
  61. n: TDOMElement;
  62. stream : TStringStream;
  63. begin
  64. Result := '';
  65. if aSuite <> nil then
  66. begin
  67. FDoc:= TXMLDocument.Create;
  68. n := FDoc.CreateElement('TestSuites');
  69. FDoc.AppendChild(n);
  70. TestSuiteAsXML(n, FDoc, aSuite);
  71. stream := TStringStream.Create('');
  72. WriteXMLFile(FDoc, stream);
  73. writeln(stream.DataString);
  74. stream.Free;
  75. end;
  76. end;
  77. function TestSuiteAsXML(n: TDOMElement; FDoc: TXMLDocument; aSuite:TTestSuite): string;
  78. var
  79. i: integer;
  80. begin
  81. for i := 0 to Pred(aSuite.Tests.Count) do
  82. if TTest(aSuite.Tests.Items[i]) is TTestSuite then
  83. TestSuiteAsXML(n, FDoc, TTestSuite(aSuite.Tests.Items[i]))
  84. else
  85. if TTest(aSuite.Tests.Items[i]) is TTestCase then
  86. n.AppendChild(FDoc.CreateTextNode(TTestcase(aSuite.Tests.Items[i]).TestName + ' '));
  87. end;
  88. { TXMLResultsWriter }
  89. procedure TXMLResultsWriter.WriteTestHeader(ATest: TTest; ALevel: integer; ACount: integer);
  90. var
  91. n: TDOMElement;
  92. begin
  93. inherited;
  94. n := FDoc.CreateElement('Test');
  95. n['Name'] := ATest.TestName;
  96. n['Result'] := 'OK';
  97. if FSuitePath.Count > 0 then
  98. //test is included in a suite
  99. TDOMElement(FSuitePath[FSuitePath.Count -1]).AppendChild(n)
  100. else
  101. //no suite to append so append directly to the listing node
  102. FListing.AppendChild(n);
  103. FCurrentTest := n;
  104. end;
  105. procedure TXMLResultsWriter.WriteTestFooter(ATest: TTest; ALevel: integer; ATiming: TDateTime);
  106. begin
  107. inherited;
  108. if not SkipTiming then
  109. FCurrentTest['ElapsedTime'] := FormatDateTime('hh:nn:ss.zzz', ATiming);
  110. end;
  111. procedure TXMLResultsWriter.WriteSuiteHeader(ATestSuite: TTestSuite; ALevel: integer);
  112. var
  113. n: TDOMElement;
  114. begin
  115. inherited;
  116. n := FDoc.CreateElement('TestSuite');
  117. FSuitePath.Add(n);
  118. n['Name'] := ATestSuite.TestName;
  119. if FSuitePath.Count = 1 then
  120. FListing.AppendChild(n)
  121. else
  122. TDOMElement(FSuitePath[FSuitePath.Count -2]).AppendChild(n);
  123. end;
  124. procedure TXMLResultsWriter.WriteSuiteFooter(ATestSuite: TTestSuite; ALevel: integer;
  125. ATiming: TDateTime; ANumRuns: integer; ANumErrors: integer; ANumFailures: integer;
  126. ANumIgnores: integer);
  127. var
  128. n: TDOMElement;
  129. begin
  130. inherited;
  131. n := TDomElement(FSuitePath[FSuitePath.Count -1]);
  132. if not SkipTiming then
  133. n['ElapsedTime'] := FormatDateTime('hh:nn:ss.zzz', ATiming);
  134. n['NumberOfRunTests'] := IntToStr(ANumRuns);
  135. n['NumberOfErrors'] := IntToStr(ANumErrors);
  136. n['NumberOfFailures'] := IntToStr(ANumFailures);
  137. n['NumberOfIgnoredTests'] := IntToStr(ANumIgnores);
  138. FSuitePath.Delete(FSuitePath.Count -1);
  139. end;
  140. constructor TXMLResultsWriter.Create(aOwner: TComponent);
  141. begin
  142. inherited Create(aOwner);
  143. FDoc:= TXMLDocument.Create;
  144. FSuitePath := TFPList.Create;
  145. FResults := nil;
  146. FListing := nil;
  147. end;
  148. destructor TXMLResultsWriter.Destroy;
  149. begin
  150. FResults := nil;
  151. FListing := nil;
  152. FSuitePath.Free;
  153. FDoc.Free;
  154. inherited Destroy;
  155. end;
  156. procedure TXMLResultsWriter.WriteHeader;
  157. begin
  158. inherited;
  159. FResults := FDoc.CreateElement('TestResults');
  160. FResults.AppendChild(FDoc.CreateComment(' Generated using FPCUnit on '
  161. + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) ));
  162. FDoc.AppendChild(FResults);
  163. FListing := FDoc.CreateElement('TestListing');
  164. FResults.AppendChild(FListing);
  165. end;
  166. procedure TXMLResultsWriter.WriteFooter;
  167. begin
  168. inherited;
  169. end;
  170. procedure TXMLResultsWriter.AddFailure(ATest: TTest; AFailure: TTestFailure);
  171. begin
  172. inherited;
  173. if AFailure.IsIgnoredTest then
  174. FCurrentTest['Result'] := 'Ignored'
  175. else
  176. FCurrentTest['Result'] := 'Failed';
  177. FCurrentTest.AppendChild(FDoc.CreateElement('Message')).AppendChild
  178. (FDoc.CreateTextNode(AFailure.AsString));
  179. FCurrentTest.AppendChild(FDoc.CreateElement('ExceptionClass')).AppendChild
  180. (FDoc.CreateTextNode(AFailure.ExceptionClassName));
  181. FCurrentTest.AppendChild(FDoc.CreateElement('ExceptionMessage')).AppendChild
  182. (FDoc.CreateTextNode(AFailure.ExceptionMessage));
  183. end;
  184. procedure TXMLResultsWriter.AddError(ATest: TTest; AError: TTestFailure);
  185. begin
  186. inherited;
  187. FCurrentTest['Result'] := 'Error';
  188. FCurrentTest.AppendChild(FDoc.CreateElement('Message')).AppendChild
  189. (FDoc.CreateTextNode(AError.AsString));
  190. FCurrentTest.AppendChild(FDoc.CreateElement('ExceptionClass')).AppendChild
  191. (FDoc.CreateTextNode(AError.ExceptionClassName));
  192. FCurrentTest.AppendChild(FDoc.CreateElement('ExceptionMessage')).AppendChild
  193. (FDoc.CreateTextNode(AError.ExceptionMessage));
  194. FCurrentTest.AppendChild(FDoc.CreateElement('SourceUnitName')).AppendChild
  195. (FDoc.CreateTextNode(AError.SourceUnitName));
  196. FCurrentTest.AppendChild(FDoc.CreateElement('LineNumber')).AppendChild
  197. (FDoc.CreateTextNode(IntToStr(AError.LineNumber)));
  198. FCurrentTest.AppendChild(FDoc.CreateElement('FailedMethodName')).AppendChild
  199. (FDoc.CreateTextNode(AError.FailedMethodName));
  200. end;
  201. procedure TXMLResultsWriter.StartTest(ATest: TTest);
  202. begin
  203. inherited;
  204. end;
  205. procedure TXMLResultsWriter.EndTest(ATest: TTest);
  206. begin
  207. inherited;
  208. end;
  209. procedure TXMLResultsWriter.WriteResult(aResult: TTestResult);
  210. var
  211. n, lResults: TDOMNode;
  212. f: text;
  213. begin
  214. lResults := FDoc.FindNode('TestResults');
  215. n := FDoc.CreateElement('NumberOfRunTests');
  216. n.AppendChild(FDoc.CreateTextNode(IntToStr(aResult.RunTests)));
  217. lResults.AppendChild(n);
  218. n := FDoc.CreateElement('NumberOfErrors');
  219. n.AppendChild(FDoc.CreateTextNode(IntToStr(aResult.NumberOfErrors)));
  220. lResults.AppendChild(n);
  221. n := FDoc.CreateElement('NumberOfFailures');
  222. n.AppendChild(FDoc.CreateTextNode(IntToStr(aResult.NumberOfFailures)));
  223. lResults.AppendChild(n);
  224. n := FDoc.CreateElement('NumberOfIgnoredTests');
  225. n.AppendChild(FDoc.CreateTextNode(IntToStr(aResult.NumberOfIgnoredTests)));
  226. lResults.AppendChild(n);
  227. if not SkipTiming then
  228. begin
  229. n := FDoc.CreateElement('TotalElapsedTime');
  230. n.AppendChild(FDoc.CreateTextNode(FormatDateTime('hh:nn:ss.zzz',
  231. Now - aResult.StartingTime)));
  232. lResults.AppendChild(n);
  233. end;
  234. { Summary of ISO 8601 http://www.cl.cam.ac.uk/~mgk25/iso-time.html }
  235. n := FDoc.CreateElement('DateTimeRan');
  236. n.AppendChild(FDoc.CreateTextNode(FormatDateTime('yyyy-mm-dd hh:nn:ss', Now)));
  237. lResults.AppendChild(n);
  238. // This is so that the GUI Test Runner doesn't output text as well.
  239. if FileName <> 'null' then
  240. begin
  241. system.Assign(f, FileName);
  242. rewrite(f);
  243. WriteXMLFile(FDoc, f);
  244. close(f);
  245. end;
  246. end;
  247. end.