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