fpts2junit.pp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. {
  2. This file is part of the Free Pascal test suite.
  3. Copyright (c) 2013 by Karoly Balogh <[email protected]>
  4. Copyright (c) 2013 by Viprinet Europe GmbH
  5. This program can convert Free Pascal Testsute results to JUnit
  6. results to be used (for example) with Jenkins CI suite.
  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. {$MODE DELPHI}
  14. {$I+}
  15. program fpts2junit;
  16. uses
  17. classes, sysutils, strutils,
  18. DOM, XMLWrite;
  19. const
  20. LOG_SHORT = 'log';
  21. LOG_LONG = 'longlog';
  22. DEFAULT_JUNIT_XML = 'fpc_testsuite.xml';
  23. PATTERN_SUCCESS = 'Success';
  24. PATTERN_FAILED = 'Fail';
  25. PATTERN_SKIPPED = 'Skip';
  26. IE_FUBAR = 'internalerror generated';
  27. function getIndexInList(l: TStringList; className: String; caseName: String): LongInt;
  28. var
  29. line: String;
  30. filename: String;
  31. begin
  32. result:=-1;
  33. filename:=className+'/'+caseName;
  34. for line in l do
  35. if (Pos(filename, line) > 0) then
  36. begin
  37. result:=l.IndexOf(line);
  38. break;
  39. end;
  40. end;
  41. procedure convertLogFiles(testOutputPath: String; junitXMLName: String);
  42. var
  43. logShort: TStringList;
  44. logLong: TStringList;
  45. junitXML: TXMLDocument;
  46. rootNode: TDOMNode;
  47. caseNode: TDOMNode;
  48. tmpNode: TDOMNode;
  49. failed: LongInt;
  50. error: LongInt;
  51. skipped: LongInt;
  52. success: LongInt;
  53. tmpLine: String;
  54. startIdx: LongInt;
  55. tmpString: String;
  56. className: String;
  57. caseName: String;
  58. begin
  59. logShort:=TStringList.Create;
  60. logLong:=TStringList.Create;
  61. // sanity check arguments
  62. testOutputPath:=IncludeTrailingPathDelimiter(ExpandFileName(testOutputPath));
  63. if not DirectoryExists(testOutputPath) then
  64. begin
  65. writeln('Path: ',testOutputPath,' is not valid.');
  66. halt(1);
  67. end;
  68. if not AnsiEndsText('.xml', junitXMLName) then
  69. junitXMLName:=DEFAULT_JUNIT_XML;
  70. // read *ALL* the logs! (ok, some of them)
  71. writeln('Reading logs from directory: ',testOutputPath);
  72. logShort.LoadFromFile(testOutputPath+LOG_SHORT);
  73. logLong.LoadFromFile(testOutputPath+LOG_LONG);
  74. junitXML:=TXMLDocument.Create;
  75. // convert
  76. failed:=0;
  77. error:=0;
  78. skipped:=0;
  79. success:=0;
  80. rootNode:=junitXML.CreateElement('testsuite');
  81. junitXML.AppendChild(rootNode);
  82. for tmpLine in logShort do
  83. begin
  84. // this is pretty fubar in the logfile, to break the format
  85. // lets fix it up...
  86. if AnsiEndsText(IE_FUBAR, tmpLine) then
  87. begin
  88. tmpLine:=AnsiReplaceText(tmpLine, IE_FUBAR, '');
  89. end;
  90. // extract useful stuff
  91. tmpString:=ExtractWord(WordCount(tmpLine,[' '])-2,tmpLine,[' ']);
  92. className:=AnsiLeftStr(tmpString,RPos(DirectorySeparator,tmpString)-1);
  93. caseName:=ExtractWord(WordCount(tmpString,[DirectorySeparator]),tmpString,[DirectorySeparator]);
  94. // create testcase node
  95. caseNode:=junitXML.CreateElement('testcase');
  96. TDOMElement(caseNode).SetAttribute('classname',className);
  97. TDOMElement(caseNode).SetAttribute('name',caseName);
  98. rootNode.AppendChild(caseNode);
  99. if AnsiStartsText(PATTERN_FAILED, tmpLine) then
  100. begin
  101. tmpString:=TrimSet(AnsiLeftStr(tmpLine, AnsiPos(className, tmpLine)-1),[' ']);
  102. // handle compiler errors as errors, otherwise failures
  103. if AnsiPos('compile',tmpString) <> 0 then
  104. begin
  105. Inc(error);
  106. tmpNode:=junitXML.CreateElement('error');
  107. end
  108. else
  109. begin
  110. Inc(failed);
  111. tmpNode:=junitXML.CreateElement('failure');
  112. end;
  113. TDOMElement(tmpNode).SetAttribute('message',tmpString);
  114. startIdx:=getIndexInList(logLong, className, caseName);
  115. tmpString:='';
  116. while startIdx > 0 do
  117. begin
  118. tmpString:=tmpString + #10 + logLong[startIdx];
  119. Inc(startIdx);
  120. if (startIdx >= logLong.Count) or
  121. AnsiStartsText('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', logLong[startIdx]) then break;
  122. end;
  123. if tmpString <> '' then
  124. tmpNode.AppendChild(junitXML.CreateTextNode(tmpString+#10));
  125. caseNode.AppendChild(tmpNode);
  126. continue;
  127. end;
  128. if AnsiStartsText(PATTERN_SKIPPED, tmpLine) then
  129. begin
  130. Inc(skipped);
  131. caseNode.AppendChild(junitXML.CreateElement('skipped'));
  132. continue;
  133. end;
  134. if AnsiStartsText(PATTERN_SUCCESS, tmpLine) then
  135. begin
  136. Inc(success);
  137. continue;
  138. end;
  139. writeln('Unparseable line: [',tmpLine,']');
  140. Halt(1);
  141. end;
  142. // set required elements in the root node
  143. TDOMElement(rootNode).SetAttribute('errors',IntToStr(error));
  144. TDOMElement(rootNode).SetAttribute('failures',IntToStr(failed));
  145. TDOMElement(rootNode).SetAttribute('tests',IntToStr(logShort.Count));
  146. TDOMElement(rootNode).SetAttribute('name','Compiler.Testsuite');
  147. TDOMElement(rootNode).SetAttribute('package','FPC');
  148. writeln('Writing results to file: ',junitXMLName);
  149. writeXMLFile(junitXML, junitXMLName);
  150. end;
  151. procedure printusage;
  152. begin
  153. writeln('Usage:');
  154. writeln(' ',ExtractFileName(ParamStr(0)),' <path_to_test_output_dir> [output.xml]');
  155. writeln(' * if no output filename is specified, "',DEFAULT_JUNIT_XML,'" will be used.');
  156. writeln(' * if specified, output filename must end with ".xml", otherwise default ');
  157. writeln(' name will be used.');
  158. halt(1);
  159. end;
  160. begin
  161. if (ParamCount < 1) or (ParamCount > 2) then
  162. printusage;
  163. convertLogFiles(ParamStr(1),ParamStr(2));
  164. end.