1{$mode objfpc}
2{$h+}
3{
4    This file is part of the Free Component Library (FCL)
5    Copyright (c) 2004 by Dean Zobec, Michael Van Canneyt
6
7    an example of a console test runner of FPCUnit tests.
8
9    See the file COPYING.FPC, included in this distribution,
10    for details about the copyright.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
16 **********************************************************************}
17unit testreport;
18
19interface
20
21uses
22  classes, SysUtils, fpcunit, testutils;
23
24type
25
26  { TXMLResultsWriter }
27
28  TXMLResultsWriter = class(TNoRefCountObject, ITestListener)
29  public
30    procedure WriteHeader;
31    procedure WriteResult(aResult: TTestResult);
32  {ITestListener}
33    procedure AddFailure(ATest: TTest; AFailure: TTestFailure);
34    procedure AddError(ATest: TTest; AError: TTestFailure);
35    procedure StartTest(ATest: TTest);
36    procedure EndTest(ATest: TTest);
37    procedure StartTestSuite(ATestSuite: TTestSuite);
38    procedure EndTestSuite(ATestSuite: TTestSuite);
39  end;
40
41  { TPlainResultsWriter }
42
43  TPlainResultsWriter = class(TNoRefCountObject, ITestListener)
44  public
45    procedure WriteHeader;
46    procedure WriteResult(aResult: TTestResult);
47  {ITestListener}
48    procedure AddFailure(ATest: TTest; AFailure: TTestFailure);
49    procedure AddError(ATest: TTest; AError: TTestFailure);
50    procedure StartTest(ATest: TTest);
51    procedure EndTest(ATest: TTest);
52    procedure StartTestSuite(ATestSuite: TTestSuite);
53    procedure EndTestSuite(ATestSuite: TTestSuite);
54  end;
55
56 {
57  TLatexResultsWriter = class(TNoRefCountObject, ITestListener)
58  public
59    procedure AddFailure(ATest: TTest; AFailure: TTestFailure);
60    procedure AddError(ATest: TTest; AError: TTestFailure);
61    procedure StartTest(ATest: TTest);
62    procedure EndTest(ATest: TTest);
63    procedure StartTestSuite(ATestSuite: TTestSuite);
64    procedure EndTestSuite(ATestSuite: TTestSuite);
65  end;}
66
67function TestSuiteAsXML(aSuite:TTestSuite; Indent : Integer): string;
68function TestSuiteAsXML(aSuite: TTestSuite): string;
69function TestSuiteAsLatex(aSuite:TTestSuite): string;
70function TestSuiteAsPlain(aSuite:TTestSuite): string;
71function GetSuiteAsXML(aSuite: TTestSuite): string;
72function GetSuiteAsLatex(aSuite: TTestSuite): string;
73function GetSuiteAsPlain(aSuite: TTestSuite): string;
74function TestResultAsXML(aTestResult: TTestResult): string;
75function TestResultAsPlain(aTestResult: TTestResult): string;
76
77implementation
78
79{TXMLResultsWriter}
80procedure TXMLResultsWriter.WriteHeader;
81begin
82  writeln('<testresults>');
83  writeln('<testlisting>');
84end;
85
86procedure TXMLResultsWriter.WriteResult(aResult: TTestResult);
87begin
88  writeln('</testlisting>');
89  writeln(TestResultAsXML(aResult));
90  writeln('</testresults>');
91end;
92
93procedure TXMLResultsWriter.AddFailure(ATest: TTest; AFailure: TTestFailure);
94begin
95  writeln('<failure ExceptionClassName="', AFailure.ExceptionClassName, '">');
96  writeln('<message>', AFailure.ExceptionMessage, '</message>');
97  writeln('</failure>');
98end;
99
100procedure TXMLResultsWriter.AddError(ATest: TTest; AError: TTestFailure);
101begin
102writeln('<error ExceptionClassName="', AError.ExceptionClassName, '">');
103  writeln('<message>', AError.ExceptionMessage, '</message>');
104  writeln('<sourceunit>', AError.SourceUnitName, '</sourceunit>');
105  writeln('<methodname>', AError.FailedMethodName, '</methodname>');
106  writeln('<linenumber>', AError.LineNumber, '</linenumber>');
107  writeln('</error>');
108end;
109
110procedure TXMLResultsWriter.StartTest(ATest: TTest);
111begin
112  writeln('<test name="' , ATest.TestSuiteName + '.' + ATest.TestName, '">');
113end;
114
115procedure TXMLResultsWriter.EndTest(ATest: TTest);
116begin
117  writeln('</test>');
118end;
119
120procedure TXMLResultsWriter.StartTestSuite(ATestSuite: TTestSuite);
121begin
122
123end;
124
125procedure TXMLResultsWriter.EndTestSuite(ATestSuite: TTestSuite);
126begin
127
128end;
129
130{TPlainResultsWriter}
131procedure TPlainResultsWriter.WriteHeader;
132begin
133end;
134
135procedure TPlainResultsWriter.WriteResult(aResult: TTestResult);
136begin
137  writeln('', TestResultAsPlain(aResult));
138end;
139
140procedure TPlainResultsWriter.AddFailure(ATest: TTest; AFailure: TTestFailure);
141begin
142  writeln('', AFailure.ExceptionMessage);
143end;
144
145procedure TPlainResultsWriter.AddError(ATest: TTest; AError: TTestFailure);
146begin
147  writeln('  Error: ', AError.ExceptionClassName);
148  writeln('    Exception:   ', AError.ExceptionMessage);
149  writeln('    Source unit: ', AError.SourceUnitName);
150  writeln('    Method name: ', AError.FailedMethodName);
151  writeln('    Line number: ', AError.LineNumber);
152end;
153
154procedure TPlainResultsWriter.StartTest(ATest: TTest);
155begin
156  write('Test: ', ATest.TestSuiteName + '.' + ATest.TestName);
157end;
158
159procedure TPlainResultsWriter.EndTest(ATest: TTest);
160begin
161  writeln;
162end;
163
164procedure TPlainResultsWriter.StartTestSuite(ATestSuite: TTestSuite);
165begin
166  { example output }
167//  Writeln('TestSuite: ' + ATestSuite.TestName);
168end;
169
170procedure TPlainResultsWriter.EndTestSuite(ATestSuite: TTestSuite);
171begin
172  { example output }
173//  Writeln('TestSuite: ' + ATestSuite.TestName + ' - END ');
174end;
175
176function TestSuiteAsXML(aSuite:TTestSuite): string;
177
178begin
179  Result:=TestSuiteAsXML(ASuite,0);
180end;
181
182function TestSuiteAsXML(aSuite:TTestSuite; Indent : Integer): string;
183
184var
185  i: integer;
186begin
187  Result := StringOfChar(' ',Indent) + '<TestSuite name="' + ASuite.TestName + '">' + System.sLineBreak;
188  Inc(Indent, 2);
189  for i := 0 to aSuite.ChildTestCount - 1 do
190    if TTest(aSuite.Test[i]) is TTestSuite then
191      Result := Result + TestSuiteAsXML(TTestSuite(aSuite.Test[i]),Indent)
192    else
193      if TTest(aSuite.Test[i]) is TTestCase then
194        Result := Result + StringOfChar(' ',Indent) + '<test>' + TTestcase(aSuite.Test[i]).TestName + '</test>' + System.sLineBreak;
195  Dec(Indent, 2);
196  Result := Result + StringOfChar(' ',Indent) + '</TestSuite>' + System.sLineBreak;
197end;
198
199function EscapeText(const S: string): String;
200var
201  i: integer;
202begin
203  SetLength(Result, 0);
204    for i := 1 to Length(S) do
205      case S[i] of
206        '&','{','}','#','_','$','%':     // Escape these characters
207          Result := Result + '\' + S[i];
208        '~','^':
209          Result := Result + '\'+S[i]+' ';
210        '\':
211          Result := Result + '$\backslash$';
212        '<':
213          Result := Result + '$<$';
214        '>':
215          Result := Result + '$>$'
216        else
217          Result := Result + S[i];
218      end;
219end;
220
221function TestSuiteAsLatex(aSuite:TTestSuite): string;
222var
223  i,j: integer;
224  s: TTestSuite;
225begin
226  Result := EscapeText(aSuite.TestSuiteName) + System.sLineBreak;
227  Result := Result + '\begin{itemize}'+ System.sLineBreak;
228  for i := 0 to aSuite.ChildTestCount - 1 do
229    if ASuite.Test[i] is TTestSuite then
230      begin
231      Result:=Result + '\item[-] ';
232      Result := Result + '\flushleft' + System.sLineBreak;
233      Result:=Result+TestSuiteAsLatex(TTestSuite(ASuite.Test[i]))+System.sLineBreak;
234      end
235    else
236      begin
237      Result := Result + '\item[-] ' +
238               EscapeText(TTestcase(aSuite.Test[i]).TestName)
239               + System.sLineBreak;
240      end;
241  Result := Result +'\end{itemize}' + System.sLineBreak;
242end;
243
244function TestSuiteAsPlain(aSuite:TTestSuite): string;
245var
246  i,j: integer;
247  s: TTestSuite;
248begin
249  for i := 0 to aSuite.ChildTestCount - 1 do
250    if TTest(aSuite.Test[i]) is TTestSuite then
251      Result := Result + TestSuiteAsPlain(TTestSuite(aSuite.Test[i]))
252    else
253      if TTest(aSuite.Test[i]) is TTestCase then
254        Result := Result + '  ' + ASuite.TestName+'.' + TTestcase(aSuite.Test[i]).TestName + System.sLineBreak;
255end;
256
257function GetSuiteAsXML(aSuite: TTestSuite): string;
258begin
259  if aSuite <> nil then
260    begin
261      if aSuite.TestName = '' then
262        aSuite.TestName := 'Test Suite';
263      Result := TestSuiteAsXML(aSuite)
264    end
265  else
266    Result := '';
267end;
268
269function GetSuiteAsLatex(aSuite: TTestSuite): string;
270begin
271  if aSuite <> nil then
272    begin
273      Result := '\documentclass[a4paper,12pt]{article}' + System.sLineBreak;
274      Result := Result + '\usepackage{array}' + System.sLineBreak;
275      Result := Result + '\usepackage{mdwlist}' + System.sLineBreak + System.sLineBreak;
276      Result := Result + '\begin{document}' + System.sLineBreak + System.sLineBreak;
277      if aSuite.TestName = '' then
278        aSuite.TestName := 'Test Suite';
279      Result := Result + TestSuiteAsLatex(aSuite);
280      Result := Result + '\end{document}';
281    end
282  else
283    Result := '';
284end;
285
286function GetSuiteAsPlain(aSuite: TTestSuite): string;
287begin
288  Result := '';
289
290  if aSuite <> nil then
291    Result := 'TestSuites: ' + System.sLineBreak + TestSuiteAsPlain(aSuite);
292end;
293
294function TestResultAsXML(aTestResult: TTestResult): string;
295var
296  i: longint;
297  f: TTestFailure;
298begin
299  with aTestResult do
300  begin
301    Result := '<NumberOfRunnedTests>' + intToStr(RunTests) + '</NumberOfRunnedTests>' + System.sLineBreak;
302    Result := Result + '<NumberOfErrors>' + intToStr(NumberOfErrors) + '</NumberOfErrors>' + System.sLineBreak;
303    Result := Result + '<NumberOfFailures>' + intToStr(NumberOfFailures) + '</NumberOfFailures>';
304    if NumberOfErrors <> 0 then
305    begin
306      Result := Result + System.sLineBreak;
307      Result := Result + '<ListOfErrors>';
308      for i := 0 to Errors.Count - 1 do
309      begin
310        Result := Result + System.sLineBreak;
311        Result := Result + '<Error>' + System.sLineBreak;
312        f := TTestFailure(Errors.Items[i]);
313        Result := Result + '  <Message>' + f.AsString + '</Message>' + System.sLineBreak;
314        Result := Result + '  <ExceptionClass>' + f.ExceptionClassName + '</ExceptionClass>' + System.sLineBreak;
315        Result := Result + '  <ExceptionMessage>' + f.ExceptionMessage + '</ExceptionMessage>' + System.sLineBreak;
316        Result := Result + '  <SourceUnitName>' + f.SourceUnitName + '</SourceUnitName>' + System.sLineBreak;
317        Result := Result + '  <LineNumber>' + IntToStr(f.LineNumber) + '</LineNumber>' + System.sLineBreak;
318        Result := Result + '  <FailedMethodName>' + f.FailedMethodName + '</FailedMethodName>' + System.sLineBreak;
319        Result := Result + '</Error>' + System.sLineBreak;
320      end;
321      Result := Result + '</ListOfErrors>';
322    end;
323    if NumberOfFailures <> 0 then
324    begin
325      Result := Result + System.sLineBreak;
326      Result := Result + '<ListOfFailures>' + System.sLineBreak;
327      for i := 0 to Failures.Count - 1 do
328      begin
329        Result := Result + '<Failure>' + System.sLineBreak;
330        f := TTestFailure(Failures.Items[i]);
331        Result := Result + '  <Message>' + f.AsString + '</Message>' + System.sLineBreak;
332        Result := Result + '  <ExceptionClass>' + f.ExceptionClassName + '</ExceptionClass>' + System.sLineBreak;
333        Result := Result + '  <ExceptionMessage>' + f.ExceptionMessage + '</ExceptionMessage>' + System.sLineBreak;
334        Result := Result + '</Failure>' + System.sLineBreak;
335      end;
336      Result := Result + '</ListOfFailures>';
337    end;
338  end;
339end;
340
341function TestResultAsPlain(aTestResult: TTestResult): string;
342var
343  i: longint;
344  f: TTestFailure;
345begin
346  with aTestResult do
347  begin
348    Result :=          'Number of run tests: ' + intToStr(RunTests) + System.sLineBreak;
349    Result := Result + 'Number of errors:    ' + intToStr(NumberOfErrors) + System.sLineBreak;
350    Result := Result + 'Number of failures:  ' + intToStr(NumberOfFailures);
351    if NumberOfErrors <> 0 then
352    begin
353      Result := Result + System.sLineBreak;
354      Result := Result + System.sLineBreak;
355      Result := Result + 'List of errors:';
356      for i := 0 to Errors.Count - 1 do
357      begin
358        Result := Result + System.sLineBreak;
359        Result := Result + '  Error: ' + System.sLineBreak;
360        f := TTestFailure(Errors.Items[i]);
361        Result := Result + '    Message:           ' + f.AsString + System.sLineBreak;
362        Result := Result + '    Exception class:   ' + f.ExceptionClassName + System.sLineBreak;
363        Result := Result + '    Exception message: ' + f.ExceptionMessage + System.sLineBreak;
364        Result := Result + '    Source unitname:   ' + f.SourceUnitName + System.sLineBreak;
365        Result := Result + '    Line number:       ' + IntToStr(f.LineNumber) + System.sLineBreak;
366        Result := Result + '    Failed methodname: ' + f.FailedMethodName + System.sLineBreak;
367      end;
368    end;
369    if NumberOfFailures <> 0 then
370    begin
371      Result := Result + System.sLineBreak;
372      Result := Result + System.sLineBreak;
373      Result := Result + 'List of failures:' + System.sLineBreak;
374      for i := 0 to Failures.Count - 1 do
375      begin
376        Result := Result + '  Failure: ' + System.sLineBreak;
377        f := TTestFailure(Failures.Items[i]);
378        Result := Result + '    Message:           ' + f.AsString + System.sLineBreak;
379        Result := Result + '    Exception class:   ' + f.ExceptionClassName + System.sLineBreak;
380        Result := Result + '    Exception message: ' + f.ExceptionMessage + System.sLineBreak;
381      end;
382    end;
383  end;
384  Result := Result + System.sLineBreak;
385end;
386
387
388end.
389