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<FailedTest>();
49  
50      private RunListener reporter;
51  
52      private static final Class[] EMPTY_CLASS_ARRAY = new Class[]{ };
53  
54      private static final String[] EMPTY_STRING_ARRAY = new String[]{ };
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         if ( methodName.equals( START_TEST ) )
129         {
130             handleStartTest( args );
131         }
132         else if ( methodName.equals( ADD_ERROR ) )
133         {
134             handleAddError( args );
135         }
136         else if ( methodName.equals( ADD_FAILURE ) )
137         {
138             handleAddFailure( args );
139         }
140         else if ( methodName.equals( END_TEST ) )
141         {
142             handleEndTest( args );
143         }
144 
145         return null;
146     }
147 
148     // Handler for TestListener.startTest(Test)
149     public void handleStartTest( Object[] args )
150     {
151         ReportEntry report = new SimpleReportEntry( args[0].getClass().getName(), args[0].toString() );
152 
153         reporter.testStarting( report );
154     }
155 
156     // Handler for TestListener.addFailure(Test, Throwable)
157     private void handleAddError( Object[] args )
158         throws IllegalAccessException, InvocationTargetException
159     {
160         ReportEntry report = SimpleReportEntry.withException( args[0].getClass().getName(), args[0].toString(),
161                                                               getStackTraceWriter( args ) );
162 
163         reporter.testError( report );
164 
165         failedTestsSet.add( new FailedTest( args[0], Thread.currentThread() ) );
166     }
167 
168     private LegacyPojoStackTraceWriter getStackTraceWriter( Object[] args )
169         throws IllegalAccessException, InvocationTargetException
170     {
171         String testName;
172 
173         try
174         {
175             Method m = args[0].getClass().getMethod( "getName", EMPTY_CLASS_ARRAY );
176             testName = (String) m.invoke( args[0], EMPTY_STRING_ARRAY );
177         }
178         catch ( NoSuchMethodException e )
179         {
180             testName = "UNKNOWN";
181         }
182 
183         return new LegacyPojoStackTraceWriter( args[0].getClass().getName(), testName, (Throwable) args[1] );
184     }
185 
186     private void handleAddFailure( Object[] args )
187         throws IllegalAccessException, InvocationTargetException
188     {
189         ReportEntry report = SimpleReportEntry.withException( args[0].getClass().getName(), args[0].toString(),
190                                                               getStackTraceWriter( args ) );
191 
192         reporter.testFailed( report );
193 
194         failedTestsSet.add( new FailedTest( args[0], Thread.currentThread() ) );
195     }
196 
197     private void handleEndTest( Object[] args )
198     {
199         boolean testHadFailed = failedTestsSet.remove( new FailedTest( args[0], Thread.currentThread() ) );
200 
201         if ( !testHadFailed )
202         {
203             ReportEntry report = new SimpleReportEntry( args[0].getClass().getName(), args[0].toString() );
204 
205             reporter.testSucceeded( report );
206         }
207     }
208 }