1 package org.apache.maven.plugin.failsafe.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
46
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
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 }