View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.surefire.report;
20  
21  import javax.xml.parsers.ParserConfigurationException;
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Map;
30  
31  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
32  import org.apache.maven.shared.utils.io.DirectoryScanner;
33  import org.xml.sax.SAXException;
34  
35  import static org.apache.maven.shared.utils.StringUtils.split;
36  
37  /**
38   *
39   */
40  public final class SurefireReportParser {
41      private static final String INCLUDES = "*.xml";
42  
43      private static final String EXCLUDES =
44              "*.txt, testng-failed.xml, testng-failures.xml, testng-results.xml, failsafe-summary*.xml";
45  
46      private final List<ReportTestSuite> testSuites = new ArrayList<>();
47  
48      private final ConsoleLogger consoleLogger;
49  
50      private final List<File> reportsDirectories;
51  
52      public SurefireReportParser(List<File> reportsDirectories, ConsoleLogger consoleLogger) {
53          this.reportsDirectories = reportsDirectories;
54          this.consoleLogger = consoleLogger;
55      }
56  
57      public List<ReportTestSuite> parseXMLReportFiles() {
58          final Collection<File> xmlReportFiles = new ArrayList<>();
59          for (File reportsDirectory : reportsDirectories) {
60              if (reportsDirectory.exists()) {
61                  for (String xmlReportFile : getIncludedFiles(reportsDirectory, INCLUDES, EXCLUDES)) {
62                      xmlReportFiles.add(new File(reportsDirectory, xmlReportFile));
63                  }
64              }
65          }
66          final TestSuiteXmlParser parser = new TestSuiteXmlParser(consoleLogger);
67          for (File aXmlReportFileList : xmlReportFiles) {
68              try {
69                  testSuites.addAll(parser.parse(aXmlReportFileList.getAbsolutePath()));
70              } catch (ParserConfigurationException e) {
71                  throw new RuntimeException("Error setting up parser for JUnit XML report", e);
72              } catch (SAXException e) {
73                  throw new RuntimeException("Error parsing JUnit XML report " + aXmlReportFileList, e);
74              } catch (IOException e) {
75                  throw new RuntimeException("Error reading JUnit XML report " + aXmlReportFileList, e);
76              }
77          }
78  
79          return testSuites;
80      }
81  
82      public Map<String, Object> getSummary(List<ReportTestSuite> suites) {
83          Map<String, Object> totalSummary = new HashMap<>();
84  
85          int totalNumberOfTests = 0;
86  
87          int totalNumberOfErrors = 0;
88  
89          int totalNumberOfFailures = 0;
90  
91          int totalNumberOfSkipped = 0;
92  
93          float totalElapsedTime = 0.0f;
94  
95          for (ReportTestSuite suite : suites) {
96              totalNumberOfTests += suite.getNumberOfTests();
97  
98              totalNumberOfErrors += suite.getNumberOfErrors();
99  
100             totalNumberOfFailures += suite.getNumberOfFailures();
101 
102             totalNumberOfSkipped += suite.getNumberOfSkipped();
103 
104             totalElapsedTime += suite.getTimeElapsed();
105         }
106 
107         float totalPercentage =
108                 computePercentage(totalNumberOfTests, totalNumberOfErrors, totalNumberOfFailures, totalNumberOfSkipped);
109 
110         totalSummary.put("totalTests", totalNumberOfTests);
111 
112         totalSummary.put("totalErrors", totalNumberOfErrors);
113 
114         totalSummary.put("totalFailures", totalNumberOfFailures);
115 
116         totalSummary.put("totalSkipped", totalNumberOfSkipped);
117 
118         totalSummary.put("totalElapsedTime", totalElapsedTime);
119 
120         totalSummary.put("totalPercentage", totalPercentage);
121 
122         return totalSummary;
123     }
124 
125     public Map<String, List<ReportTestSuite>> getSuitesGroupByPackage(List<ReportTestSuite> testSuitesList) {
126         Map<String, List<ReportTestSuite>> suitePackage = new HashMap<>();
127 
128         for (ReportTestSuite suite : testSuitesList) {
129             List<ReportTestSuite> suiteList = new ArrayList<>();
130 
131             if (suitePackage.get(suite.getPackageName()) != null) {
132                 suiteList = suitePackage.get(suite.getPackageName());
133             }
134 
135             suiteList.add(suite);
136 
137             suitePackage.put(suite.getPackageName(), suiteList);
138         }
139 
140         return suitePackage;
141     }
142 
143     /**
144      * @return a float between 0.0 and 1.0
145      */
146     public float computePercentage(int tests, int errors, int failures, int skipped) {
147         float percentage = tests == 0 ? 0 : ((float) (tests - errors - failures - skipped) / (float) tests);
148         return percentage;
149     }
150 
151     public List<ReportTestCase> getFailureDetails(List<ReportTestSuite> testSuites) {
152         List<ReportTestCase> failureDetails = new ArrayList<>();
153 
154         for (ReportTestSuite suite : testSuites) {
155             for (ReportTestCase tCase : suite.getTestCases()) {
156                 if (!tCase.isSuccessful()) {
157                     failureDetails.add(tCase);
158                 }
159             }
160         }
161 
162         return failureDetails;
163     }
164 
165     /**
166      * Returns {@code true} if the specified directory contains at least one report file.
167      *
168      * @param directory the directory
169      * @return {@code true} if the specified directory contains at least one report file.
170      */
171     public static boolean hasReportFiles(File directory) {
172         return directory != null
173                 && directory.isDirectory()
174                 && getIncludedFiles(directory, INCLUDES, EXCLUDES).length != 0;
175     }
176 
177     private static String[] getIncludedFiles(File directory, String includes, String excludes) {
178         DirectoryScanner scanner = new DirectoryScanner();
179 
180         scanner.setBasedir(directory);
181 
182         scanner.setIncludes(split(includes, ","));
183 
184         scanner.setExcludes(split(excludes, ","));
185 
186         scanner.scan();
187 
188         return scanner.getIncludedFiles();
189     }
190 }