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 java.lang.String.format;
39  import static java.nio.charset.StandardCharsets.UTF_8;
40  import static org.apache.commons.lang3.StringEscapeUtils.escapeXml10;
41  import static org.apache.commons.lang3.StringEscapeUtils.unescapeXml;
42  import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
43  
44  /**
45   * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
46   * @since 2.20
47   */
48  public final class FailsafeSummaryXmlUtils
49  {
50      private static final String FAILSAFE_SUMMARY_XML_SCHEMA_LOCATION =
51              "https://maven.apache.org/surefire/maven-surefire-plugin/xsd/failsafe-summary.xsd";
52  
53      private static final String MESSAGE_NIL_ELEMENT =
54              "<failureMessage xsi:nil=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"/>";
55  
56      private static final String MESSAGE_ELEMENT =
57              "<failureMessage>%s</failureMessage>";
58  
59      private static final String FAILSAFE_SUMMARY_XML_TEMPLATE =
60              "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
61                      + "<failsafe-summary xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
62                      + " xsi:noNamespaceSchemaLocation=\"" + FAILSAFE_SUMMARY_XML_SCHEMA_LOCATION + "\""
63                      + " result=\"%s\" timeout=\"%s\">\n"
64                      + "    <completed>%d</completed>\n"
65                      + "    <errors>%d</errors>\n"
66                      + "    <failures>%d</failures>\n"
67                      + "    <skipped>%d</skipped>\n"
68                      + "    %s\n"
69                      + "</failsafe-summary>";
70  
71      private FailsafeSummaryXmlUtils()
72      {
73          throw new IllegalStateException( "No instantiable constructor." );
74      }
75  
76      public static RunResult toRunResult( File failsafeSummaryXml ) throws Exception
77      {
78          XPathFactory xpathFactory = XPathFactory.newInstance();
79          XPath xpath = xpathFactory.newXPath();
80  
81          try ( FileInputStream is = new FileInputStream( failsafeSummaryXml ) )
82          {
83              Node root = ( Node ) xpath.evaluate( "/", new InputSource( is ), XPathConstants.NODE );
84  
85              String completed = xpath.evaluate( "/failsafe-summary/completed", root );
86              String errors = xpath.evaluate( "/failsafe-summary/errors", root );
87              String failures = xpath.evaluate( "/failsafe-summary/failures", root );
88              String skipped = xpath.evaluate( "/failsafe-summary/skipped", root );
89              String failureMessage = xpath.evaluate( "/failsafe-summary/failureMessage", root );
90              String timeout = xpath.evaluate( "/failsafe-summary/@timeout", root );
91  
92              return new RunResult( parseInt( completed ), parseInt( errors ), parseInt( failures ), parseInt( skipped ),
93                      isBlank( failureMessage ) ? null : unescapeXml( failureMessage ),
94                      parseBoolean( timeout )
95              );
96          }
97      }
98  
99      public static void fromRunResultToFile( RunResult fromRunResult, File toFailsafeSummaryXml )
100             throws IOException
101     {
102         String failure = fromRunResult.getFailure();
103         String msg = isBlank( failure ) ? MESSAGE_NIL_ELEMENT : format( MESSAGE_ELEMENT, escapeXml10( failure ) );
104         String xml = format( Locale.ROOT, FAILSAFE_SUMMARY_XML_TEMPLATE,
105                 fromRunResult.getFailsafeCode(),
106                 String.valueOf( fromRunResult.isTimeout() ),
107                 fromRunResult.getCompletedCount(),
108                 fromRunResult.getErrors(),
109                 fromRunResult.getFailures(),
110                 fromRunResult.getSkipped(),
111                 msg );
112 
113         try ( FileOutputStream os = new FileOutputStream( toFailsafeSummaryXml ) )
114         {
115             IOUtils.write( xml, os, UTF_8 );
116         }
117     }
118 
119     public static void writeSummary( RunResult mergedSummary, File mergedSummaryFile, boolean inProgress )
120             throws Exception
121     {
122         if ( !mergedSummaryFile.getParentFile().isDirectory() )
123         {
124             //noinspection ResultOfMethodCallIgnored
125             mergedSummaryFile.getParentFile().mkdirs();
126         }
127 
128         if ( mergedSummaryFile.exists() && inProgress )
129         {
130             RunResult runResult = toRunResult( mergedSummaryFile );
131             mergedSummary = mergedSummary.aggregate( runResult );
132         }
133 
134         fromRunResultToFile( mergedSummary, mergedSummaryFile );
135     }
136 }