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