View Javadoc
1   package org.apache.maven.plugin.surefire.report;
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 junit.framework.TestCase;
23  import org.apache.maven.plugin.surefire.booterclient.output.DeserializedStacktraceWriter;
24  import org.apache.maven.shared.utils.StringUtils;
25  import org.apache.maven.shared.utils.xml.Xpp3Dom;
26  import org.apache.maven.shared.utils.xml.Xpp3DomBuilder;
27  import org.apache.maven.surefire.report.ReportEntry;
28  import org.apache.maven.surefire.report.SimpleReportEntry;
29  import org.apache.maven.surefire.report.StackTraceWriter;
30  
31  import java.io.File;
32  import java.io.FileInputStream;
33  import java.io.IOException;
34  import java.io.InputStreamReader;
35  import java.util.HashMap;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.concurrent.ConcurrentHashMap;
39  import java.util.concurrent.atomic.AtomicInteger;
40  
41  import static java.nio.charset.StandardCharsets.UTF_8;
42  import static org.apache.maven.surefire.util.internal.ObjectUtils.systemProps;
43  
44  @SuppressWarnings( "ResultOfMethodCallIgnored" )
45  public class StatelessXmlReporterTest
46      extends TestCase
47  {
48      private static final String XSD =
49              "https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report-3.0.xsd";
50      private final static String TEST_ONE = "aTestMethod";
51      private final static String TEST_TWO = "bTestMethod";
52      private final static String TEST_THREE = "cTestMethod";
53      private static final AtomicInteger FOLDER_POSTFIX = new AtomicInteger();
54  
55      private TestSetStats stats;
56      private TestSetStats rerunStats;
57      private File expectedReportFile;
58      private File reportDir;
59  
60      @Override
61      protected void setUp()
62          throws Exception
63      {
64          stats = new TestSetStats( false, true );
65          rerunStats = new TestSetStats( false, true );
66  
67          File basedir = new File( "." );
68          File target = new File( basedir.getCanonicalFile(), "target" );
69          target.mkdir();
70          String reportRelDir = getClass().getSimpleName() + "-" + FOLDER_POSTFIX.incrementAndGet();
71          reportDir = new File( target, reportRelDir );
72          reportDir.mkdir();
73      }
74  
75      @Override
76      protected void tearDown()
77      {
78          if ( expectedReportFile != null )
79          {
80              expectedReportFile.delete();
81          }
82      }
83  
84      public void testFileNameWithoutSuffix()
85      {
86          StatelessXmlReporter reporter =
87              new StatelessXmlReporter( reportDir, null, false, 0,
88                                        new ConcurrentHashMap<String, Map<String, List<WrappedReportEntry>>>(), XSD );
89          reporter.cleanTestHistoryMap();
90  
91          ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), getClass().getName(), 12 );
92          WrappedReportEntry testSetReportEntry =
93              new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null, systemProps() );
94          stats.testSucceeded( testSetReportEntry );
95          reporter.testSetCompleted( testSetReportEntry, stats );
96  
97          expectedReportFile = new File( reportDir, "TEST-" + getClass().getName() + ".xml" );
98          assertTrue( "Report file (" + expectedReportFile.getAbsolutePath() + ") doesn't exist",
99                      expectedReportFile.exists() );
100     }
101 
102 
103     public void testAllFieldsSerialized()
104         throws IOException
105     {
106         ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), TEST_ONE, 12 );
107         WrappedReportEntry testSetReportEntry =
108             new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null, systemProps() );
109         expectedReportFile = new File( reportDir, "TEST-" + TEST_ONE + ".xml" );
110 
111         stats.testSucceeded( testSetReportEntry );
112         StackTraceWriter stackTraceWriter = new DeserializedStacktraceWriter( "A fud msg", "trimmed", "fail at foo" );
113         Utf8RecodingDeferredFileOutputStream stdOut = new Utf8RecodingDeferredFileOutputStream( "fds" );
114         String stdOutPrefix;
115         String stdErrPrefix;
116         if ( defaultCharsetSupportsSpecialChar() )
117         {
118             stdErrPrefix = "std-\u0115rr";
119             stdOutPrefix = "st]]>d-o\u00DCt";
120         }
121         else
122         {
123             stdErrPrefix = "std-err";
124             stdOutPrefix = "st]]>d-out";
125         }
126 
127         byte[] stdOutBytes = (stdOutPrefix + "<null>!\u0020\u0000\u001F").getBytes();
128         stdOut.write( stdOutBytes, 0, stdOutBytes.length );
129 
130         Utf8RecodingDeferredFileOutputStream stdErr = new Utf8RecodingDeferredFileOutputStream( "fds" );
131 
132 
133         byte[] stdErrBytes = (stdErrPrefix + "?&-&amp;&#163;\u0020\u0000\u001F").getBytes();
134         stdErr.write( stdErrBytes, 0, stdErrBytes.length );
135         WrappedReportEntry t2 =
136             new WrappedReportEntry( new SimpleReportEntry( Inner.class.getName(), TEST_TWO, stackTraceWriter, 13 ),
137                                     ReportEntryType.ERROR, 13, stdOut, stdErr );
138 
139         stats.testSucceeded( t2 );
140         StatelessXmlReporter reporter = new StatelessXmlReporter( reportDir, null, false, 0,
141                         new ConcurrentHashMap<String, Map<String, List<WrappedReportEntry>>>(), XSD );
142         reporter.testSetCompleted( testSetReportEntry, stats );
143 
144         FileInputStream fileInputStream = new FileInputStream( expectedReportFile );
145 
146         Xpp3Dom testSuite = Xpp3DomBuilder.build( new InputStreamReader( fileInputStream, UTF_8) );
147         assertEquals( "testsuite", testSuite.getName() );
148         Xpp3Dom properties = testSuite.getChild( "properties" );
149         assertEquals( System.getProperties().size(), properties.getChildCount() );
150         Xpp3Dom child = properties.getChild( 1 );
151         assertFalse( StringUtils.isEmpty( child.getAttribute( "value" ) ) );
152         assertFalse( StringUtils.isEmpty( child.getAttribute( "name" ) ) );
153 
154         Xpp3Dom[] testcase = testSuite.getChildren( "testcase" );
155         Xpp3Dom tca = testcase[0];
156         assertEquals( TEST_ONE, tca.getAttribute( "name" ) ); // Hopefully same order on jdk5
157         assertEquals( "0.012", tca.getAttribute( "time" ) );
158         assertEquals( getClass().getName(), tca.getAttribute( "classname" ) );
159 
160         Xpp3Dom tcb = testcase[1];
161         assertEquals( TEST_TWO, tcb.getAttribute( "name" ) );
162         assertEquals( "0.013", tcb.getAttribute( "time" ) );
163         assertEquals( Inner.class.getName(), tcb.getAttribute( "classname" ) );
164         Xpp3Dom errorNode = tcb.getChild( "error" );
165         assertNotNull( errorNode );
166         assertEquals( "A fud msg", errorNode.getAttribute( "message" ) );
167         assertEquals( "fail at foo", errorNode.getAttribute( "type" ) );
168         assertEquals( stdOutPrefix + "<null>! &amp#0;&amp#31;", tcb.getChild( "system-out" ).getValue() );
169 
170 
171         assertEquals( stdErrPrefix + "?&-&amp;&#163; &amp#0;&amp#31;", tcb.getChild( "system-err" ).getValue() );
172     }
173 
174     public void testOutputRerunFlakyFailure()
175         throws IOException
176     {
177         ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), TEST_ONE, 12 );
178 
179         WrappedReportEntry testSetReportEntry =
180             new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null, systemProps() );
181         expectedReportFile = new File( reportDir, "TEST-" + TEST_ONE + ".xml" );
182 
183         stats.testSucceeded( testSetReportEntry );
184         StackTraceWriter stackTraceWriterOne = new DeserializedStacktraceWriter( "A fud msg", "trimmed",
185                                                                                  "fail at foo" );
186         StackTraceWriter stackTraceWriterTwo =
187             new DeserializedStacktraceWriter( "A fud msg two", "trimmed two", "fail at foo two" );
188 
189         String firstRunOut = "first run out";
190         String firstRunErr = "first run err";
191         String secondRunOut = "second run out";
192         String secondRunErr = "second run err";
193 
194         WrappedReportEntry testTwoFirstError =
195             new WrappedReportEntry( new SimpleReportEntry( Inner.class.getName(), TEST_TWO, stackTraceWriterOne, 5 ),
196                                     ReportEntryType.ERROR, 5, createStdOutput( firstRunOut ),
197                                     createStdOutput( firstRunErr ) );
198 
199         WrappedReportEntry testTwoSecondError =
200             new WrappedReportEntry( new SimpleReportEntry( Inner.class.getName(), TEST_TWO, stackTraceWriterTwo, 13 ),
201                                     ReportEntryType.ERROR, 13, createStdOutput( secondRunOut ),
202                                     createStdOutput( secondRunErr ) );
203 
204         WrappedReportEntry testThreeFirstRun =
205             new WrappedReportEntry( new SimpleReportEntry( Inner.class.getName(), TEST_THREE, stackTraceWriterOne, 13 ),
206                                     ReportEntryType.FAILURE, 13, createStdOutput( firstRunOut ),
207                                     createStdOutput( firstRunErr ) );
208 
209         WrappedReportEntry testThreeSecondRun =
210             new WrappedReportEntry( new SimpleReportEntry( Inner.class.getName(), TEST_THREE, stackTraceWriterTwo, 2 ),
211                                     ReportEntryType.SUCCESS, 2, createStdOutput( secondRunOut ),
212                                     createStdOutput( secondRunErr ) );
213 
214         stats.testSucceeded( testTwoFirstError );
215         stats.testSucceeded( testThreeFirstRun );
216         rerunStats.testSucceeded( testTwoSecondError );
217         rerunStats.testSucceeded( testThreeSecondRun );
218 
219         StatelessXmlReporter reporter =
220             new StatelessXmlReporter( reportDir, null, false, 1,
221                                       new HashMap<String, Map<String, List<WrappedReportEntry>>>(), XSD );
222 
223         reporter.testSetCompleted( testSetReportEntry, stats );
224         reporter.testSetCompleted( testSetReportEntry, rerunStats );
225 
226         FileInputStream fileInputStream = new FileInputStream( expectedReportFile );
227 
228         Xpp3Dom testSuite = Xpp3DomBuilder.build( new InputStreamReader( fileInputStream, UTF_8 ) );
229         assertEquals( "testsuite", testSuite.getName() );
230         assertEquals( "0.012", testSuite.getAttribute( "time" ) );
231         Xpp3Dom properties = testSuite.getChild( "properties" );
232         assertEquals( System.getProperties().size(), properties.getChildCount() );
233         Xpp3Dom child = properties.getChild( 1 );
234         assertFalse( StringUtils.isEmpty( child.getAttribute( "value" ) ) );
235         assertFalse( StringUtils.isEmpty( child.getAttribute( "name" ) ) );
236 
237         Xpp3Dom[] testcase = testSuite.getChildren( "testcase" );
238         Xpp3Dom testCaseOne = testcase[0];
239         assertEquals( TEST_ONE, testCaseOne.getAttribute( "name" ) );
240         assertEquals( "0.012", testCaseOne.getAttribute( "time" ) );
241         assertEquals( getClass().getName(), testCaseOne.getAttribute( "classname" ) );
242 
243         Xpp3Dom testCaseTwo = testcase[1];
244         assertEquals( TEST_TWO, testCaseTwo.getAttribute( "name" ) );
245         // Run time for a rerun failing test is the run time of the first run
246         assertEquals( "0.005", testCaseTwo.getAttribute( "time" ) );
247         assertEquals( Inner.class.getName(), testCaseTwo.getAttribute( "classname" ) );
248         Xpp3Dom errorNode = testCaseTwo.getChild( "error" );
249         Xpp3Dom rerunErrorNode = testCaseTwo.getChild( "rerunError" );
250         assertNotNull( errorNode );
251         assertNotNull( rerunErrorNode );
252 
253         assertEquals( "A fud msg", errorNode.getAttribute( "message" ) );
254         assertEquals( "fail at foo", errorNode.getAttribute( "type" ) );
255 
256         // Check rerun error node contains all the information
257         assertEquals( firstRunOut, testCaseTwo.getChild( "system-out" ).getValue() );
258         assertEquals( firstRunErr, testCaseTwo.getChild( "system-err" ).getValue() );
259         assertEquals( secondRunOut, rerunErrorNode.getChild( "system-out" ).getValue() );
260         assertEquals( secondRunErr, rerunErrorNode.getChild( "system-err" ).getValue() );
261         assertEquals( "A fud msg two", rerunErrorNode.getAttribute( "message" ) );
262         assertEquals( "fail at foo two", rerunErrorNode.getAttribute( "type" ) );
263 
264         // Check flaky failure node
265         Xpp3Dom testCaseThree = testcase[2];
266         assertEquals( TEST_THREE, testCaseThree.getAttribute( "name" ) );
267         // Run time for a flaky test is the run time of the first successful run
268         assertEquals( "0.002", testCaseThree.getAttribute( "time" ) );
269         assertEquals( Inner.class.getName(), testCaseThree.getAttribute( "classname" ) );
270         Xpp3Dom flakyFailureNode = testCaseThree.getChild( "flakyFailure" );
271         assertNotNull( flakyFailureNode );
272         assertEquals( firstRunOut, flakyFailureNode.getChild( "system-out" ).getValue() );
273         assertEquals( firstRunErr, flakyFailureNode.getChild( "system-err" ).getValue() );
274         // system-out and system-err should not be present for flaky failures
275         assertNull( testCaseThree.getChild( "system-out" ) );
276         assertNull( testCaseThree.getChild( "system-err" ) );
277     }
278 
279     private boolean defaultCharsetSupportsSpecialChar()
280     {
281         // some charsets are not able to deal with \u0115 on both ways of the conversion
282         return "\u0115\u00DC".equals( new String( "\u0115\u00DC".getBytes() ) );
283     }
284 
285     private Utf8RecodingDeferredFileOutputStream createStdOutput( String content )
286         throws IOException
287     {
288         Utf8RecodingDeferredFileOutputStream stdOut = new Utf8RecodingDeferredFileOutputStream( "fds2" );
289         stdOut.write( content.getBytes(), 0, content.length() );
290         return stdOut;
291     }
292 
293     class Inner
294     {
295 
296     }
297 }