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