xmlreporter.pas 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. {
  2. Copyright (C) 2006 Graeme Geldenhuys <[email protected]>
  3. This source is free software; you can redistribute it and/or modify it under
  4. the terms of the GNU General Public License as published by the Free
  5. Software Foundation; either version 2 of the License, or (at your option)
  6. any later version.
  7. This code is distributed in the hope that it will be useful, but WITHOUT ANY
  8. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. details.
  11. A copy of the GNU General Public License is available on the World Wide Web
  12. at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
  13. to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  14. MA 02111-1307, USA.
  15. Purpose:
  16. This unit contains a XML TestListener for use with the fpcUnit testing
  17. framework. It uses the XMLWrite unit, which is part of FPC, to generate
  18. the XML document. The benefit of using the XMLWrite unit, is that the
  19. data generated is valid XML, with resevered characters correctly escaped.
  20. This allows the XML document to be further processed with XSLT etc without
  21. any issues.
  22. }
  23. unit xmlreporter;
  24. {$mode objfpc}{$H+}
  25. interface
  26. uses
  27. Classes
  28. ,SysUtils
  29. ,fpcUnit
  30. ,TestUtils
  31. ,dom
  32. ,XMLWrite
  33. ;
  34. type
  35. { XML Test Listner }
  36. { TXMLResultsWriter }
  37. TXMLResultsWriter = class(TNoRefCountObject, ITestListener)
  38. private
  39. FDoc: TXMLDocument;
  40. { These TDOMNodes are for easy access and a bit of optimization }
  41. FResults: TDOMNode;
  42. FListing: TDOMNode;
  43. FFailures: TDOMNode;
  44. FIgnores: TDOMNode;
  45. FErrors: TDOMNode;
  46. FLastTestSuite: TDOMNode;
  47. FStartCrono: TDateTime;
  48. { Converts the actual test results into XML nodes. This gets called
  49. by the public method WriteResult. }
  50. procedure TestResultAsXML(pTestResult: TTestResult);
  51. { This gets called in the class constructor and sets up the starting nodes }
  52. procedure WriteHeader;
  53. public
  54. constructor Create;
  55. destructor Destroy; override;
  56. procedure WriteResult(aResult: TTestResult);
  57. { ITestListener interface requirements }
  58. procedure AddFailure(ATest: TTest; AFailure: TTestFailure);
  59. procedure AddError(ATest: TTest; AError: TTestFailure);
  60. procedure StartTest(ATest: TTest);
  61. procedure EndTest(ATest: TTest);
  62. procedure StartTestSuite(ATestSuite: TTestSuite);
  63. procedure EndTestSuite(ATestSuite: TTestSuite);
  64. { A public property to the internal XML document }
  65. property Document: TXMLDocument read FDoc;
  66. end;
  67. implementation
  68. { TXMLResultsWriter }
  69. procedure TXMLResultsWriter.TestResultAsXML(pTestResult: TTestResult);
  70. var
  71. n, lResults: TDOMNode;
  72. begin
  73. lResults := FDoc.FindNode('TestResults');
  74. n := FDoc.CreateElement('NumberOfRunTests');
  75. n.AppendChild(FDoc.CreateTextNode(IntToStr(pTestResult.RunTests)));
  76. lResults.AppendChild(n);
  77. n := FDoc.CreateElement('NumberOfErrors');
  78. n.AppendChild(FDoc.CreateTextNode(IntToStr(pTestResult.NumberOfErrors)));
  79. lResults.AppendChild(n);
  80. n := FDoc.CreateElement('NumberOfFailures');
  81. n.AppendChild(FDoc.CreateTextNode(IntToStr(pTestResult.NumberOfFailures)));
  82. lResults.AppendChild(n);
  83. n := FDoc.CreateElement('NumberOfIgnoredTests');
  84. n.AppendChild(FDoc.CreateTextNode(IntToStr(pTestResult.NumberOfIgnoredTests)));
  85. lResults.AppendChild(n);
  86. n := FDoc.CreateElement('TotalElapsedTime');
  87. n.AppendChild(FDoc.CreateTextNode(FormatDateTime('hh:nn:ss.zzz', Now - pTestResult.StartingTime)));
  88. lResults.AppendChild(n);
  89. { Summary of ISO 8601 http://www.cl.cam.ac.uk/~mgk25/iso-time.html }
  90. n := FDoc.CreateElement('DateTimeRan');
  91. n.AppendChild(FDoc.CreateTextNode(FormatDateTime('yyyy-mm-dd hh:mm:ss', Now)));
  92. lResults.AppendChild(n);
  93. end;
  94. procedure TXMLResultsWriter.WriteHeader;
  95. begin
  96. FResults := FDoc.CreateElement('TestResults');
  97. FResults.AppendChild(FDoc.CreateComment(' Generated using FPCUnit on '
  98. + FormatDateTime('yyyy-mm-dd hh:mm:ss', Now) ));
  99. FDoc.AppendChild(FResults);
  100. FListing := FDoc.CreateElement('TestListing');
  101. FResults.AppendChild(FListing);
  102. end;
  103. constructor TXMLResultsWriter.Create;
  104. begin
  105. FDoc := TXMLDocument.Create;
  106. FResults := nil;
  107. FFailures := nil;
  108. FIgnores := nil;
  109. FErrors := nil;
  110. FListing := nil;
  111. FLastTestSuite := nil;
  112. WriteHeader;
  113. end;
  114. destructor TXMLResultsWriter.Destroy;
  115. begin
  116. FResults := nil;
  117. FFailures := nil;
  118. FIgnores := nil;
  119. FErrors := nil;
  120. FListing := nil;
  121. FLastTestSuite := nil;
  122. FDoc.Free;
  123. inherited Destroy;
  124. end;
  125. procedure TXMLResultsWriter.WriteResult(aResult: TTestResult);
  126. begin
  127. TestResultAsXML(aResult);
  128. end;
  129. procedure TXMLResultsWriter.AddFailure(ATest: TTest; AFailure: TTestFailure);
  130. var
  131. n: TDOMElement;
  132. begin
  133. if AFailure.IsIgnoredTest then
  134. begin
  135. { Try and find the node first }
  136. if not Assigned(FIgnores) then
  137. FIgnores := FDoc.FindNode('ListOfIgnoredTests');
  138. { If we couldn't find it, create it }
  139. if not Assigned(FIgnores) then
  140. begin
  141. FIgnores := FDoc.CreateElement('ListOfIgnoredTests');
  142. FResults.AppendChild(FIgnores);
  143. end;
  144. n := FDoc.CreateElement('IgnoredTest');
  145. n.AppendChild(FDoc.CreateElement('Message') ).AppendChild(FDoc.CreateTextNode(AFailure.AsString));
  146. n.AppendChild(FDoc.CreateElement('ExceptionClass') ).AppendChild(FDoc.CreateTextNode(AFailure.ExceptionClassName));
  147. n.AppendChild(FDoc.CreateElement('ExceptionMessage')).AppendChild(FDoc.CreateTextNode(AFailure.ExceptionMessage));
  148. FIgnores.AppendChild(n);
  149. end
  150. else
  151. begin
  152. { Try and find the node first }
  153. if not Assigned(FFailures) then
  154. FFailures := FDoc.FindNode('ListOfFailures');
  155. { If we couldn't find it, create it }
  156. if not Assigned(FFailures) then
  157. begin
  158. FFailures := FDoc.CreateElement('ListOfFailures');
  159. FResults.AppendChild(FFailures);
  160. end;
  161. n := FDoc.CreateElement('Failure');
  162. n.AppendChild(FDoc.CreateElement('Message') ).AppendChild(FDoc.CreateTextNode(AFailure.AsString));
  163. n.AppendChild(FDoc.CreateElement('ExceptionClass') ).AppendChild(FDoc.CreateTextNode(AFailure.ExceptionClassName));
  164. n.AppendChild(FDoc.CreateElement('ExceptionMessage')).AppendChild(FDoc.CreateTextNode(AFailure.ExceptionMessage));
  165. FFailures.AppendChild(n);
  166. end;
  167. end;
  168. procedure TXMLResultsWriter.AddError(ATest: TTest; AError: TTestFailure);
  169. var
  170. n: TDOMElement;
  171. begin
  172. { Try and find the node first }
  173. if not Assigned(FErrors) then
  174. FErrors := FDoc.FindNode('ListOfErrors');
  175. { If we couldn't find it, create it }
  176. if not Assigned(FErrors) then
  177. begin
  178. FErrors := FDoc.CreateElement('ListOfErrors');
  179. FResults.AppendChild(FErrors);
  180. end;
  181. n := FDoc.CreateElement('Error');
  182. n.AppendChild(FDoc.CreateElement('Message') ).AppendChild(FDoc.CreateTextNode(AError.AsString));
  183. n.AppendChild(FDoc.CreateElement('ExceptionClass') ).AppendChild(FDoc.CreateTextNode(AError.ExceptionClassName));
  184. n.AppendChild(FDoc.CreateElement('ExceptionMessage')).AppendChild(FDoc.CreateTextNode(AError.ExceptionMessage));
  185. n.AppendChild(FDoc.CreateElement('SourceUnitName') ).AppendChild(FDoc.CreateTextNode(AError.SourceUnitName));
  186. n.AppendChild(FDoc.CreateElement('LineNumber') ).AppendChild(FDoc.CreateTextNode(IntToStr(AError.LineNumber)));
  187. n.AppendChild(FDoc.CreateElement('FailedMethodName')).AppendChild(FDoc.CreateTextNode(AError.FailedMethodName));
  188. FErrors.AppendChild(n);
  189. end;
  190. procedure TXMLResultsWriter.StartTest(ATest: TTest);
  191. var
  192. n: TDOMElement;
  193. begin
  194. n := FDoc.CreateElement('Test');
  195. n['Name'] := ATest.TestSuiteName + '.' + ATest.TestName;
  196. FLastTestSuite.AppendChild(n);
  197. FStartCrono := Now;
  198. end;
  199. procedure TXMLResultsWriter.EndTest(ATest: TTest);
  200. var
  201. n: TDOMNode;
  202. lNew: TDOMElement;
  203. begin
  204. n := FLastTestSuite.LastChild;
  205. lNew := FDoc.CreateElement('ElapsedTime');
  206. lNew.AppendChild(FDoc.CreateTextNode(FormatDateTime('hh:nn:ss.zzz', Now - FStartCrono)));
  207. n.AppendChild(lNew);
  208. end;
  209. procedure TXMLResultsWriter.StartTestSuite(ATestSuite: TTestSuite);
  210. var
  211. n: TDOMElement;
  212. begin
  213. { Try and find the Listings node first }
  214. if not Assigned(FListing) then
  215. FListing := FDoc.FindNode('TestListing');
  216. { If we couldn't find it, create it }
  217. if not Assigned(FListing) then
  218. begin
  219. FListing := FDoc.CreateElement('TestListing');
  220. FResults.AppendChild(FListing);
  221. end;
  222. { The first TestSuite always seem to be blank/empty }
  223. if ATestSuite.TestName <> '' then
  224. begin
  225. n := FDoc.CreateElement('TestSuite');
  226. n['Name'] := ATestSuite.TestName;
  227. FListing.AppendChild(n);
  228. FLastTestSuite := n;
  229. end;
  230. end;
  231. procedure TXMLResultsWriter.EndTestSuite(ATestSuite: TTestSuite);
  232. begin
  233. // do nothing
  234. end;
  235. end.