View Javadoc
1   package org.apache.maven.surefire.common.junit4;
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.plugin.surefire.log.api.ConsoleLogger;
23  import org.apache.maven.surefire.api.report.OutputReportEntry;
24  import org.apache.maven.surefire.api.report.ReportEntry;
25  import org.apache.maven.surefire.api.report.RunMode;
26  import org.apache.maven.surefire.api.report.SimpleReportEntry;
27  import org.apache.maven.surefire.api.report.StackTraceWriter;
28  import org.apache.maven.surefire.api.report.TestOutputReceiver;
29  import org.apache.maven.surefire.api.report.TestOutputReportEntry;
30  import org.apache.maven.surefire.api.report.TestReportListener;
31  import org.apache.maven.surefire.api.testset.TestSetFailedException;
32  import org.apache.maven.surefire.api.util.internal.ClassMethod;
33  import org.apache.maven.surefire.report.ClassMethodIndexer;
34  import org.apache.maven.surefire.report.RunModeSetter;
35  import org.junit.runner.Description;
36  import org.junit.runner.Result;
37  import org.junit.runner.notification.Failure;
38  import org.junit.runner.notification.RunListener;
39  
40  import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.isFailureInsideJUnitItself;
41  import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.toClassMethod;
42  import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.getAnnotatedIgnoreValue;
43  import static org.apache.maven.surefire.api.report.SimpleReportEntry.assumption;
44  import static org.apache.maven.surefire.api.report.SimpleReportEntry.ignored;
45  import static org.apache.maven.surefire.api.report.SimpleReportEntry.withException;
46  
47  /**
48   * RunListener for JUnit4, delegates to our own RunListener
49   *
50   */
51  public class JUnit4RunListener
52      extends RunListener
53      implements TestOutputReceiver<OutputReportEntry>, RunModeSetter
54  {
55      protected final ClassMethodIndexer classMethodIndexer = new ClassMethodIndexer();
56      protected final TestReportListener<TestOutputReportEntry> reporter;
57      private volatile RunMode runMode;
58  
59      /**
60       * This flag is set after a failure has occurred so that a
61       * {@link org.apache.maven.surefire.api.report.RunListener#testSucceeded} event is not fired.
62       * This is necessary because JUnit4 always fires a
63       * {@link org.junit.runner.notification.RunListener#testRunFinished(Result)}
64       * event-- even if there was a failure.
65       */
66      private final ThreadLocal<Boolean> failureFlag = new InheritableThreadLocal<>();
67  
68      /**
69       * Constructor.
70       *
71       * @param reporter the reporter to log testing events to
72       */
73      public JUnit4RunListener( TestReportListener<TestOutputReportEntry> reporter )
74      {
75          this.reporter = reporter;
76      }
77  
78      public final ConsoleLogger getConsoleLogger()
79      {
80          return reporter;
81      }
82  
83      @Override
84      public void setRunMode( RunMode runMode )
85      {
86          this.runMode = runMode;
87      }
88  
89      protected final RunMode getRunMode()
90      {
91          return runMode;
92      }
93  
94      // Testrun methods are not invoked when using the runner
95  
96      /**
97       * Called when a specific test has been skipped (for whatever reason).
98       *
99       * @see org.junit.runner.notification.RunListener#testIgnored(org.junit.runner.Description)
100      */
101     @Override
102     public void testIgnored( Description description )
103         throws Exception
104     {
105         String reason = getAnnotatedIgnoreValue( description );
106         ClassMethod classMethod = toClassMethod( description );
107         long testRunId = classMethodIndexer.indexClassMethod( classMethod.getClazz(), classMethod.getMethod() );
108         reporter.testSkipped( ignored( runMode, testRunId, classMethod.getClazz(), null,
109             classMethod.getMethod(), null, reason ) );
110     }
111 
112     /**
113      * Called when a specific test has started.
114      *
115      * @see org.junit.runner.notification.RunListener#testStarted(org.junit.runner.Description)
116      */
117     @Override
118     public void testStarted( Description description )
119         throws Exception
120     {
121         try
122         {
123             reporter.testStarting( createReportEntry( description ) );
124         }
125         finally
126         {
127             failureFlag.remove();
128         }
129     }
130 
131     /**
132      * Called when a specific test has failed.
133      *
134      * @see org.junit.runner.notification.RunListener#testFailure(org.junit.runner.notification.Failure)
135      */
136     @Override
137     @SuppressWarnings( { "ThrowableResultOfMethodCallIgnored" } )
138     public void testFailure( Failure failure )
139         throws Exception
140     {
141         try
142         {
143             StackTraceWriter stackTrace = createStackTraceWriter( failure );
144             ClassMethod classMethod = toClassMethod( failure.getDescription() );
145             long testRunId = classMethodIndexer.indexClassMethod( classMethod.getClazz(), classMethod.getMethod() );
146             ReportEntry report = withException( runMode, testRunId, classMethod.getClazz(), null,
147                 classMethod.getMethod(), null, stackTrace );
148 
149             if ( failure.getException() instanceof AssertionError )
150             {
151                 reporter.testFailed( report );
152             }
153             else
154             {
155                 reporter.testError( report );
156             }
157         }
158         finally
159         {
160             failureFlag.set( true );
161         }
162     }
163 
164     public void testAssumptionFailure( Failure failure )
165     {
166         try
167         {
168             Description desc = failure.getDescription();
169             ClassMethod classMethod = toClassMethod( desc );
170             long testRunId = classMethodIndexer.indexClassMethod( classMethod.getClazz(), classMethod.getMethod() );
171             ReportEntry report = assumption( runMode, testRunId, classMethod.getClazz(), null,
172                 classMethod.getMethod(), null, failure.getMessage() );
173             reporter.testAssumptionFailure( report );
174         }
175         finally
176         {
177             failureFlag.set( true );
178         }
179     }
180 
181     /**
182      * Called after a specific test has finished.
183      *
184      * @see org.junit.runner.notification.RunListener#testFinished(org.junit.runner.Description)
185      */
186     @Override
187     public void testFinished( Description description )
188         throws Exception
189     {
190         Boolean failure = failureFlag.get();
191         if ( failure == null )
192         {
193             reporter.testSucceeded( createReportEntry( description ) );
194         }
195     }
196 
197     /**
198      * Delegates to {@link org.apache.maven.surefire.api.report.RunListener#testExecutionSkippedByUser()}.
199      */
200     public void testExecutionSkippedByUser()
201     {
202         reporter.testExecutionSkippedByUser();
203     }
204 
205     protected StackTraceWriter createStackTraceWriter( Failure failure )
206     {
207         return new JUnit4StackTraceWriter( failure );
208     }
209 
210     protected SimpleReportEntry createReportEntry( Description description )
211     {
212         ClassMethod classMethod = toClassMethod( description );
213         long testRunId = classMethodIndexer.indexClassMethod( classMethod.getClazz(), classMethod.getMethod() );
214         return new SimpleReportEntry( runMode, testRunId, classMethod.getClazz(), null,
215             classMethod.getMethod(), null );
216     }
217 
218     public static void rethrowAnyTestMechanismFailures( Result run )
219         throws TestSetFailedException
220     {
221         for ( Failure failure : run.getFailures() )
222         {
223             if ( isFailureInsideJUnitItself( failure.getDescription() ) )
224             {
225                 throw new TestSetFailedException( failure.getTestHeader() + " :: " + failure.getMessage(),
226                                                         failure.getException() );
227             }
228         }
229     }
230 
231     @Override
232     public void writeTestOutput( OutputReportEntry reportEntry )
233     {
234         Long testRunId = classMethodIndexer.getLocalIndex();
235         reporter.writeTestOutput( new TestOutputReportEntry( reportEntry, runMode, testRunId ) );
236     }
237 }