View Javadoc
1   package org.apache.maven.plugin.failsafe.util;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.io.IOUtils;
23  import org.apache.maven.surefire.suite.RunResult;
24  import org.w3c.dom.Node;
25  import org.xml.sax.InputSource;
26  
27  import javax.xml.xpath.XPath;
28  import javax.xml.xpath.XPathConstants;
29  import javax.xml.xpath.XPathFactory;
30  import java.io.File;
31  import java.io.FileInputStream;
32  import java.io.FileOutputStream;
33  import java.io.IOException;
34  import java.util.Locale;
35  
36  import static java.lang.Boolean.parseBoolean;
37  import static java.lang.Integer.parseInt;
38  import static org.apache.commons.lang3.StringEscapeUtils.escapeXml10;
39  import static org.apache.commons.lang3.StringEscapeUtils.unescapeXml;
40  import static org.apache.maven.surefire.util.internal.StringUtils.UTF_8;
41  import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
42  
43  /**
44   * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
45   * @since 2.20
46   */
47  public final class FailsafeSummaryXmlUtils
48  {
49      private static final String FAILSAFE_SUMMARY_XML_SCHEMA_LOCATION =
50              "https://maven.apache.org/surefire/maven-surefire-plugin/xsd/failsafe-summary.xsd";
51  
52      private static final String FAILSAFE_SUMMARY_XML_NIL_ATTR =
53              " xsi:nil=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"";
54  
55      private static final String FAILSAFE_SUMMARY_XML_TEMPLATE =
56              "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
57                      + "<failsafe-summary xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
58                      + " xsi:noNamespaceSchemaLocation=\"" + FAILSAFE_SUMMARY_XML_SCHEMA_LOCATION + "\""
59                      + " result=\"%s\" timeout=\"%s\">\n"
60                      + "    <completed>%d</completed>\n"
61                      + "    <errors>%d</errors>\n"
62                      + "    <failures>%d</failures>\n"
63                      + "    <skipped>%d</skipped>\n"
64                      + "    <failureMessage%s>%s</failureMessage>\n"
65                      + "</failsafe-summary>";
66  
67      private FailsafeSummaryXmlUtils()
68      {
69          throw new IllegalStateException( "No instantiable constructor." );
70      }
71  
72      public static RunResult toRunResult( File failsafeSummaryXml ) throws Exception
73      {
74          XPathFactory xpathFactory = XPathFactory.newInstance();
75          XPath xpath = xpathFactory.newXPath();
76  
77          FileInputStream is = new FileInputStream( failsafeSummaryXml );
78  
79          try
80          {
81              Node root = (Node) xpath.evaluate( "/", new InputSource( is ), XPathConstants.NODE );
82  
83              String completed = xpath.evaluate( "/failsafe-summary/completed", root );
84              String errors = xpath.evaluate( "/failsafe-summary/errors", root );
85              String failures = xpath.evaluate( "/failsafe-summary/failures", root );
86              String skipped = xpath.evaluate( "/failsafe-summary/skipped", root );
87              String failureMessage = xpath.evaluate( "/failsafe-summary/failureMessage", root );
88              String timeout = xpath.evaluate( "/failsafe-summary/@timeout", root );
89  
90              return new RunResult( parseInt( completed ), parseInt( errors ), parseInt( failures ), parseInt( skipped ),
91                                          isBlank( failureMessage ) ? null : unescapeXml( failureMessage ),
92                                          parseBoolean( timeout )
93              );
94          }
95          finally
96          {
97              is.close();
98          }
99      }
100 
101     public static void fromRunResultToFile( RunResult fromRunResult, File toFailsafeSummaryXml )
102             throws IOException
103     {
104         String failure = fromRunResult.getFailure();
105         String xml = String.format( Locale.ROOT, FAILSAFE_SUMMARY_XML_TEMPLATE,
106                                           fromRunResult.getFailsafeCode(),
107                                           String.valueOf( fromRunResult.isTimeout() ),
108                                           fromRunResult.getCompletedCount(),
109                                           fromRunResult.getErrors(),
110                                           fromRunResult.getFailures(),
111                                           fromRunResult.getSkipped(),
112                                           isBlank( failure ) ? FAILSAFE_SUMMARY_XML_NIL_ATTR : "",
113                                           isBlank( failure ) ? "" : escapeXml10( failure )
114         );
115 
116         FileOutputStream os = new FileOutputStream( toFailsafeSummaryXml );
117         try
118         {
119             IOUtils.write( xml, os, UTF_8 );
120         }
121         finally
122         {
123             os.close();
124         }
125     }
126 
127     public static void writeSummary( RunResult mergedSummary, File mergedSummaryFile, boolean inProgress )
128             throws Exception
129     {
130         if ( !mergedSummaryFile.getParentFile().isDirectory() )
131         {
132             //noinspection ResultOfMethodCallIgnored
133             mergedSummaryFile.getParentFile().mkdirs();
134         }
135 
136         if ( mergedSummaryFile.exists() && inProgress )
137         {
138             RunResult runResult = toRunResult( mergedSummaryFile );
139             mergedSummary = mergedSummary.aggregate( runResult );
140         }
141 
142         fromRunResultToFile( mergedSummary, mergedSummaryFile );
143     }
144 }