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.Collection;
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 String SETUP_METHOD_NAME = "setUp";
45  
46      private static final String TEARDOWN_METHOD_NAME = "tearDown";
47  
48      private static final Object[] EMPTY_OBJECT_ARRAY = {};
49  
50      private final Class<?> testClass;
51  
52      private final Collection<Method> testMethods = new ArrayList<>();
53  
54      private Method setUpMethod;
55  
56      private Method tearDownMethod;
57  
58      public PojoTestSet( final Class<?> testClass )
59      {
60          if ( testClass == null )
61          {
62              throw new IllegalArgumentException( "testClass is null" );
63          }
64  
65          this.testClass = testClass;
66      }
67  
68      @Override
69      public void execute( RunListener reportManager, ClassLoader loader )
70              throws TestSetFailedException
71      {
72          if ( reportManager == null )
73          {
74              throw new NullPointerException( "reportManager is null" );
75          }
76  
77          executeTestMethods( reportManager );
78      }
79  
80      private void executeTestMethods( RunListener reportManager )
81              throws TestSetFailedException
82      {
83          if ( reportManager == null )
84          {
85              throw new NullPointerException( "reportManager is null" );
86          }
87  
88          discoverTestMethods();
89  
90          for ( Method testMethod : testMethods )
91          {
92              boolean abort = executeTestMethod( testMethod, reportManager );
93              if ( abort )
94              {
95                  break;
96              }
97          }
98      }
99  
100     private boolean executeTestMethod( Method method, RunListener reportManager )
101             throws TestSetFailedException
102     {
103         if ( method == null || reportManager == null )
104         {
105             throw new NullPointerException();
106         }
107 
108         final Object testObject;
109 
110         try
111         {
112             testObject = testClass.newInstance();
113         }
114         catch ( ReflectiveOperationException e )
115         {
116             throw new TestSetFailedException( "Unable to instantiate POJO '" + testClass + "'.", e );
117         }
118 
119         final String testClassName = getTestClass().getName();
120         final String methodName = method.getName();
121         final String userFriendlyMethodName = methodName + "()";
122         final String testName = getTestName( userFriendlyMethodName );
123 
124         reportManager.testStarting( new SimpleReportEntry( testClassName, null, testName, null ) );
125 
126         try
127         {
128             setUpFixture( testObject );
129         }
130         catch ( Throwable e )
131         {
132             StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( testClassName, methodName, e );
133             reportManager.testFailed( withException( testClassName, null, testName, null, 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, EMPTY_OBJECT_ARRAY );
147             reportManager.testSucceeded( new SimpleReportEntry( testClassName, null, testName, null ) );
148         }
149         catch ( InvocationTargetException e )
150         {
151             Throwable t = e.getTargetException();
152             StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( testClassName, methodName, t );
153             reportManager.testFailed( withException( testClassName, null, testName, null, 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, null, testName, null, 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( testObject );
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, null, testName, null, 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( Object testObject )
202         throws Throwable
203     {
204         if ( setUpMethod != null )
205         {
206             setUpMethod.invoke( testObject );
207         }
208     }
209 
210     private void tearDownFixture( Object testObject )
211         throws Throwable
212     {
213         if ( tearDownMethod != null )
214         {
215             tearDownMethod.invoke( testObject );
216         }
217     }
218 
219     private void discoverTestMethods()
220     {
221         for ( Method m : getTestClass().getMethods() )
222         {
223             if ( isNoArgsInstanceMethod( m ) )
224             {
225                 if ( isValidTestMethod( m ) )
226                 {
227                     testMethods.add( m );
228                 }
229                 else if ( SETUP_METHOD_NAME.equals( m.getName() ) )
230                 {
231                     setUpMethod = m;
232                 }
233                 else if ( TEARDOWN_METHOD_NAME.equals( m.getName() ) )
234                 {
235                     tearDownMethod = m;
236                 }
237             }
238         }
239     }
240 
241     private static boolean isNoArgsInstanceMethod( Method m )
242     {
243         boolean isInstanceMethod = !Modifier.isStatic( m.getModifiers() );
244         boolean returnsVoid = m.getReturnType().equals( void.class );
245         boolean hasNoParams = m.getParameterTypes().length == 0;
246         return isInstanceMethod && returnsVoid && hasNoParams;
247     }
248 
249     private static boolean isValidTestMethod( Method m )
250     {
251         return m.getName().startsWith( TEST_METHOD_PREFIX );
252     }
253 
254     @Override
255     public String getName()
256     {
257         return getTestClass().getName();
258     }
259 
260     private Class<?> getTestClass()
261     {
262         return testClass;
263     }
264 }