View Javadoc
1   package org.apache.maven.surefire.junit;
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 java.lang.reflect.InvocationHandler;
23  import java.lang.reflect.InvocationTargetException;
24  import java.lang.reflect.Method;
25  import java.util.HashSet;
26  import java.util.Set;
27  import org.apache.maven.surefire.report.LegacyPojoStackTraceWriter;
28  import org.apache.maven.surefire.report.ReportEntry;
29  import org.apache.maven.surefire.report.RunListener;
30  import org.apache.maven.surefire.report.SimpleReportEntry;
31  
32  /**
33   * Invocation Handler for TestListener proxies to delegate to our {@link RunListener}
34   *
35   */
36  public class TestListenerInvocationHandler
37      implements InvocationHandler
38  {
39      // The String names of the four methods in interface junit.framework.TestListener
40      private static final String START_TEST = "startTest";
41  
42      private static final String ADD_FAILURE = "addFailure";
43  
44      private static final String ADD_ERROR = "addError";
45  
46      private static final String END_TEST = "endTest";
47  
48      private final Set<FailedTest> failedTestsSet = new HashSet<>();
49  
50      private RunListener reporter;
51  
52      private static final Class[] EMPTY_CLASS_ARRAY = { };
53  
54      private static final Object[] EMPTY_STRING_ARRAY = { };
55  
56      private static class FailedTest
57      {
58          private Object testThatFailed;
59  
60          private Thread threadOnWhichTestFailed;
61  
62          FailedTest( Object testThatFailed, Thread threadOnWhichTestFailed )
63          {
64              if ( testThatFailed == null )
65              {
66                  throw new NullPointerException( "testThatFailed is null" );
67              }
68  
69              if ( threadOnWhichTestFailed == null )
70              {
71                  throw new NullPointerException( "threadOnWhichTestFailed is null" );
72              }
73  
74              this.testThatFailed = testThatFailed;
75  
76              this.threadOnWhichTestFailed = threadOnWhichTestFailed;
77          }
78  
79          @Override
80          public boolean equals( Object obj )
81          {
82              boolean retVal = true;
83  
84              if ( obj == null || getClass() != obj.getClass() )
85              {
86                  retVal = false;
87              }
88              else
89              {
90                  FailedTest ft = (FailedTest) obj;
91  
92                  if ( ft.testThatFailed != testThatFailed )
93                  {
94                      retVal = false;
95                  }
96                  else if ( !ft.threadOnWhichTestFailed.equals( threadOnWhichTestFailed ) )
97                  {
98                      retVal = false;
99                  }
100             }
101 
102             return retVal;
103         }
104 
105         @Override
106         public int hashCode()
107         {
108             return threadOnWhichTestFailed.hashCode();
109         }
110     }
111 
112     public TestListenerInvocationHandler( RunListener reporter )
113     {
114         if ( reporter == null )
115         {
116             throw new NullPointerException( "reporter is null" );
117         }
118 
119         this.reporter = reporter;
120     }
121 
122     @Override
123     public Object invoke( Object proxy, Method method, Object[] args )
124         throws Throwable
125     {
126         String methodName = method.getName();
127 
128         switch ( methodName )
129         {
130             case START_TEST:
131                 handleStartTest( args );
132                 break;
133             case ADD_ERROR:
134                 handleAddError( args );
135                 break;
136             case ADD_FAILURE:
137                 handleAddFailure( args );
138                 break;
139             case END_TEST:
140                 handleEndTest( args );
141                 break;
142             default:
143                 break;
144         }
145 
146         return null;
147     }
148 
149     // Handler for TestListener.startTest(Test)
150     private void handleStartTest( Object[] args )
151     {
152         ReportEntry report = new SimpleReportEntry( args[0].getClass().getName(), args[0].toString() );
153 
154         reporter.testStarting( report );
155     }
156 
157     // Handler for TestListener.addFailure(Test, Throwable)
158     private void handleAddError( Object[] args )
159         throws IllegalAccessException, InvocationTargetException
160     {
161         ReportEntry report = SimpleReportEntry.withException( args[0].getClass().getName(), args[0].toString(),
162                                                               getStackTraceWriter( args ) );
163 
164         reporter.testError( report );
165 
166         failedTestsSet.add( new FailedTest( args[0], Thread.currentThread() ) );
167     }
168 
169     private LegacyPojoStackTraceWriter getStackTraceWriter( Object[] args )
170         throws IllegalAccessException, InvocationTargetException
171     {
172         String testName;
173 
174         try
175         {
176             Method m = args[0].getClass().getMethod( "getName", EMPTY_CLASS_ARRAY );
177             testName = (String) m.invoke( args[0], EMPTY_STRING_ARRAY );
178         }
179         catch ( NoSuchMethodException e )
180         {
181             testName = "UNKNOWN";
182         }
183 
184         return new LegacyPojoStackTraceWriter( args[0].getClass().getName(), testName, (Throwable) args[1] );
185     }
186 
187     private void handleAddFailure( Object[] args )
188         throws IllegalAccessException, InvocationTargetException
189     {
190         ReportEntry report = SimpleReportEntry.withException( args[0].getClass().getName(), args[0].toString(),
191                                                               getStackTraceWriter( args ) );
192 
193         reporter.testFailed( report );
194 
195         failedTestsSet.add( new FailedTest( args[0], Thread.currentThread() ) );
196     }
197 
198     private void handleEndTest( Object[] args )
199     {
200         boolean testHadFailed = failedTestsSet.remove( new FailedTest( args[0], Thread.currentThread() ) );
201 
202         if ( !testHadFailed )
203         {
204             ReportEntry report = new SimpleReportEntry( args[0].getClass().getName(), args[0].toString() );
205 
206             reporter.testSucceeded( report );
207         }
208     }
209 }