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