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