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.InvocationTargetException;
23  import java.lang.reflect.Method;
24  import java.lang.reflect.Modifier;
25  import java.util.ArrayList;
26  import java.util.List;
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  import org.apache.maven.surefire.testset.TestSetFailedException;
32  
33  /**
34   * Executes a JUnit3 test class
35   *
36   */
37  public class PojoTestSet
38      implements SurefireTestSet
39  {
40  
41      private static final String TEST_METHOD_PREFIX = "test";
42  
43      private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
44  
45      private final Object testObject;
46  
47      private List<Method> testMethods;
48  
49      private Method setUpMethod;
50  
51      private Method tearDownMethod;
52  
53      private final Class testClass;
54  
55      public PojoTestSet( Class testClass )
56          throws TestSetFailedException
57      {
58          if ( testClass == null )
59          {
60              throw new NullPointerException( "testClass is null" );
61          }
62  
63          this.testClass = testClass;
64  
65          try
66          {
67              testObject = testClass.newInstance();
68          }
69          catch ( InstantiationException e )
70          {
71              throw new TestSetFailedException( "Unable to instantiate POJO '" + testClass + "'", e );
72          }
73          catch ( IllegalAccessException e )
74          {
75              throw new TestSetFailedException( "Unable to instantiate POJO '" + testClass + "'", e );
76          }
77      }
78  
79      public void execute( RunListener reportManager, ClassLoader loader )
80          throws TestSetFailedException
81      {
82          if ( reportManager == null )
83          {
84              throw new NullPointerException( "reportManager is null" );
85          }
86  
87          executeTestMethods( reportManager );
88      }
89  
90      private void executeTestMethods( RunListener reportManager )
91      {
92          if ( reportManager == null )
93          {
94              throw new NullPointerException( "reportManager is null" );
95          }
96  
97          if ( testMethods == null )
98          {
99              discoverTestMethods();
100         }
101 
102         boolean abort = false;
103 
104         for ( int i = 0; i < testMethods.size() && !abort; ++i )
105         {
106             abort = executeTestMethod( testMethods.get( i ), EMPTY_OBJECT_ARRAY, reportManager );
107         }
108     }
109 
110     private boolean executeTestMethod( Method method, Object[] args, RunListener reportManager )
111     {
112         if ( method == null || args == null || reportManager == null )
113         {
114             throw new NullPointerException();
115         }
116 
117         String userFriendlyMethodName = method.getName() + '(';
118 
119         if ( args.length != 0 )
120         {
121             userFriendlyMethodName += "Reporter";
122         }
123 
124         userFriendlyMethodName += ')';
125 
126         ReportEntry report =
127             new SimpleReportEntry( testObject.getClass().getName(), getTestName( userFriendlyMethodName ) );
128 
129         reportManager.testStarting( report );
130 
131         try
132         {
133             setUpFixture();
134         }
135         catch ( Throwable e )
136         {
137             report =
138                 SimpleReportEntry.withException( testObject.getClass().getName(), getTestName( userFriendlyMethodName ),
139                                                  new LegacyPojoStackTraceWriter( testObject.getClass().getName(),
140                                                                                  method.getName(), e ) );
141 
142             reportManager.testFailed( report );
143 
144             // A return value of true indicates to this class's executeTestMethods
145             // method that it should abort and not attempt to execute
146             // any other test methods. The other caller of this method,
147             // TestRerunner.rerun, ignores this return value, because it is
148             // only running one test.
149             return true;
150         }
151 
152         // Make sure that tearDownFixture
153         try
154         {
155             method.invoke( testObject, args );
156 
157             report = new SimpleReportEntry( testObject.getClass().getName(), getTestName( userFriendlyMethodName ) );
158 
159             reportManager.testSucceeded( report );
160         }
161         catch ( InvocationTargetException ite )
162         {
163             Throwable t = ite.getTargetException();
164 
165             report =
166                 SimpleReportEntry.withException( testObject.getClass().getName(), getTestName( userFriendlyMethodName ),
167                                                  new LegacyPojoStackTraceWriter( testObject.getClass().getName(),
168                                                                                  method.getName(), t ) );
169 
170             reportManager.testFailed( report );
171             // Don't return  here, because tearDownFixture should be called even
172             // if the test method throws an exception.
173         }
174         catch ( Throwable t )
175         {
176             report =
177                 SimpleReportEntry.withException( testObject.getClass().getName(), getTestName( userFriendlyMethodName ),
178                                                  new LegacyPojoStackTraceWriter( testObject.getClass().getName(),
179                                                                                  method.getName(), t ) );
180 
181             reportManager.testFailed( report );
182             // Don't return  here, because tearDownFixture should be called even
183             // if the test method throws an exception.
184         }
185 
186         try
187         {
188             tearDownFixture();
189         }
190         catch ( Throwable t )
191         {
192             // Treat any exception from tearDownFixture as a failure of the test.
193             report =
194                 SimpleReportEntry.withException( testObject.getClass().getName(), getTestName( userFriendlyMethodName ),
195                                                  new LegacyPojoStackTraceWriter( testObject.getClass().getName(),
196                                                                                  method.getName(), t ) );
197 
198             reportManager.testFailed( report );
199 
200             // A return value of true indicates to this class's executeTestMethods
201             // method that it should abort and not attempt to execute
202             // any other test methods. The other caller of this method,
203             // TestRerunner.rerun, ignores this return value, because it is
204             // only running one test.
205             return true;
206         }
207 
208         // A return value of false indicates to this class's executeTestMethods
209         // method that it should keep plowing ahead and invoke more test methods.
210         // The other caller of this method,
211         // TestRerunner.rerun, ignores this return value, because it is
212         // only running one test.
213         return false;
214     }
215 
216     private String getTestName( String testMethodName )
217     {
218         if ( testMethodName == null )
219         {
220             throw new NullPointerException( "testMethodName is null" );
221         }
222 
223         return getTestClass().getName() + "." + testMethodName;
224     }
225 
226     private void setUpFixture()
227         throws Throwable
228     {
229         if ( setUpMethod != null )
230         {
231             setUpMethod.invoke( testObject );
232         }
233     }
234 
235     private void tearDownFixture()
236         throws Throwable
237     {
238         if ( tearDownMethod != null )
239         {
240             tearDownMethod.invoke( testObject );
241         }
242     }
243 
244     private void discoverTestMethods()
245     {
246         if ( testMethods == null )
247         {
248             testMethods = new ArrayList<Method>();
249 
250             Method[] methods = getTestClass().getMethods();
251 
252             for ( Method m : methods )
253             {
254                 if ( isValidTestMethod( m ) )
255                 {
256                     String simpleName = m.getName();
257 
258                     // name must have 5 or more chars
259                     if ( simpleName.length() > 4 )
260                     {
261                         String firstFour = simpleName.substring( 0, 4 );
262 
263                         // name must start with "test"
264                         if ( firstFour.equals( TEST_METHOD_PREFIX ) )
265                         {
266                             testMethods.add( m );
267                         }
268                     }
269                 }
270                 else if ( m.getName().equals( "setUp" ) && m.getParameterTypes().length == 0 )
271                 {
272                     setUpMethod = m;
273                 }
274                 else if ( m.getName().equals( "tearDown" ) && m.getParameterTypes().length == 0 )
275                 {
276                     tearDownMethod = m;
277                 }
278             }
279         }
280     }
281 
282     private static boolean isValidTestMethod( Method m )
283     {
284         boolean isInstanceMethod = !Modifier.isStatic( m.getModifiers() );
285 
286         boolean returnsVoid = m.getReturnType().equals( void.class );
287 
288         boolean hasNoParams = m.getParameterTypes().length == 0;
289 
290         return isInstanceMethod && returnsVoid && hasNoParams;
291     }
292 
293     public String getName()
294     {
295         return testClass.getName();
296     }
297 
298     public Class getTestClass()
299     {
300         return testClass;
301     }
302 }