| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sling.performance; |
| |
| import org.apache.sling.performance.annotation.AfterSuite; |
| import org.apache.sling.performance.annotation.BeforeSuite; |
| import org.apache.sling.performance.annotation.PerformanceTest; |
| import org.apache.sling.performance.annotation.PerformanceTestFactory; |
| import org.apache.sling.performance.annotation.PerformanceTestSuite; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.runners.BlockJUnit4ClassRunner; |
| import org.junit.runners.model.FrameworkMethod; |
| import org.junit.runners.model.InitializationError; |
| import org.junit.runners.model.TestClass; |
| |
| import java.lang.annotation.Annotation; |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.annotation.Target; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| /** |
| * The custom JUnit runner that collects the performance tests |
| * |
| */ |
| |
| |
| |
| public class PerformanceRunner extends BlockJUnit4ClassRunner { |
| protected LinkedList<FrameworkMethod> tests = new LinkedList<FrameworkMethod>(); |
| private List<PerformanceSuiteState> suitesState = new ArrayList<PerformanceSuiteState>(); |
| public ReportLevel reportLevel = ReportLevel.ClassLevel; |
| |
| public static enum ReportLevel{ |
| ClassLevel, |
| MethodLevel |
| } |
| |
| @Retention(RetentionPolicy.RUNTIME) |
| @Target(ElementType.TYPE) |
| public @interface Parameters { |
| public ReportLevel reportLevel() default ReportLevel.ClassLevel; |
| } |
| |
| public PerformanceRunner(Class<?> clazz) throws InitializationError { |
| super(clazz); |
| |
| // set the report level for the tests that are run with the PerformanceRunner |
| // by default set to class level for legacy tests compatibility |
| if (clazz.getAnnotation(Parameters.class) != null){ |
| reportLevel = clazz.getAnnotation(Parameters.class).reportLevel(); |
| } |
| |
| try { |
| computeTests(); |
| } catch (Exception e) { |
| throw new InitializationError(e); |
| } |
| } |
| |
| /** |
| * Compute the tests that will be run |
| * |
| * @throws Exception |
| */ |
| protected void computeTests() throws Exception { |
| // add normal JUnit tests |
| tests.addAll(super.computeTestMethods()); |
| |
| // add the performance tests |
| tests.addAll(computePerformanceTests()); |
| |
| // This is called here to ensure the test class constructor is called at |
| // least |
| // once during testing. |
| createTest(); |
| } |
| |
| /** |
| * Compute performance tests |
| * |
| * @return the list containing the performance test methods |
| * @throws Exception |
| */ |
| protected Collection<? extends FrameworkMethod> computePerformanceTests() |
| throws Exception { |
| List<FrameworkMethod> tests = new LinkedList<FrameworkMethod>(); |
| |
| List<Object> testObjects = new ArrayList<Object>(); |
| List<Object> testObjectsTmp = new ArrayList<Object>(); |
| |
| |
| ParameterizedTestList testCenter = new ParameterizedTestList(); |
| |
| // Retrieve the test objects included in the Performance test suite |
| for (FrameworkMethod method : getTestClass().getAnnotatedMethods( |
| PerformanceTestSuite.class)) { |
| Object targetObject = getTestClass().getJavaClass().newInstance(); |
| if (method.getMethod().getReturnType() |
| .equals(ParameterizedTestList.class)) { |
| testCenter = (ParameterizedTestList) method.getMethod().invoke( |
| targetObject); |
| testObjectsTmp = testCenter.getTestObjectList(); |
| |
| // Iterate through all the test cases and see if they have a factory |
| for (Object testObject : testObjectsTmp) { |
| Method[] factoryMethods = getSpecificMethods( |
| testObject.getClass(), PerformanceTestFactory.class); |
| |
| // If we have a factory method, get all the instance objects returned by this factory |
| if (factoryMethods.length > 0) { |
| // Make sure there's only one factory method |
| if (factoryMethods.length > 1) { |
| throw new IllegalStateException( |
| "There should be at most one PerformanceTestFactory method"); |
| } |
| Method factoryMethod = factoryMethods[0]; |
| |
| // Execute the method (statically) |
| Object instances = factoryMethod.invoke(testObject); |
| |
| // If the factory returned an array, make a list |
| if (instances.getClass().isArray()) { |
| instances = Arrays.asList((Object[]) instances); |
| } |
| |
| // If the factory returned a single element, put it in a list |
| if (!(instances instanceof Iterable<?>)) { |
| instances = Collections.singletonList(instances); |
| } |
| testObjects.addAll((List) instances); |
| } else { |
| testObjects.add(testObject); |
| } |
| } |
| } else { |
| throw new InitializationError( |
| "Wrong signature for the @PerformanceTestSuite method"); |
| } |
| } |
| |
| // Retrieve the methods before running the methods from the test suite |
| List<FrameworkMethod> beforeSuiteMethods = getTestClass() |
| .getAnnotatedMethods(BeforeSuite.class); |
| if (beforeSuiteMethods.size() > 1) { |
| throw new InitializationError( |
| "Only one @BeforeSuite method is allowed for a @PerformanceSuite"); |
| } |
| |
| // Retrieve the methods before running the methods from the test suite |
| List<FrameworkMethod> afterSuiteMethods = getTestClass() |
| .getAnnotatedMethods(AfterSuite.class); |
| if (afterSuiteMethods.size() > 1) { |
| throw new InitializationError( |
| "Only one @AfterSuite method is allowed for a @PerformanceSuite"); |
| } |
| |
| PerformanceSuiteState current = null; |
| boolean suiteAlreadyRegistered = false; |
| |
| for (PerformanceSuiteState suiteState : suitesState) { |
| if (suiteState.testSuiteName.equals(testCenter.getTestSuiteName())) { |
| suiteAlreadyRegistered = true; |
| suiteState.incrementNumberOfTestMethodsInSuite(); |
| current = suiteState; |
| break; |
| } |
| } |
| |
| // Create a new PerformanceSuiteState object |
| PerformanceSuiteState newSuite = new PerformanceSuiteState( |
| testCenter.getTestSuiteName()); |
| |
| if (!suiteAlreadyRegistered) { |
| if (beforeSuiteMethods.size() == 1) { |
| newSuite.setBeforeSuiteMethod(beforeSuiteMethods.get(0) |
| .getMethod()); |
| } |
| if (afterSuiteMethods.size() == 1) { |
| newSuite.setAfterSuiteMethod(afterSuiteMethods.get(0) |
| .getMethod()); |
| } |
| |
| current = newSuite; |
| newSuite.setTargetObjectSuite(getTestClass().getJavaClass() |
| .newInstance()); |
| |
| } |
| |
| // In case there are any objects retrieved from the Performance Suite |
| // we should add them to the tests that will be run and increase the |
| // number of methods |
| // contained in the PerformanceSuite |
| if (!testObjects.isEmpty()) { |
| for (Object testObject : testObjects) { |
| |
| // retrieve the test methods from the test classes |
| Method[] testMethods = getSpecificMethods( |
| testObject.getClass(), PerformanceTest.class); |
| |
| |
| for (Method method : testMethods) { |
| FrameworkPerformanceMethod performaceTestMethod = new |
| FrameworkPerformanceMethod( |
| method, testObject, current, reportLevel); |
| tests.add(performaceTestMethod); |
| } |
| |
| if (!suiteAlreadyRegistered) { |
| newSuite.incrementNumberOfTestMethodsInSuite(); |
| } |
| } |
| |
| // add the new suite to the list of suites |
| suitesState.add(newSuite); |
| } |
| |
| // Retrieve the performance tests in the case we don't have a |
| // performance test suite |
| for (FrameworkMethod method : getTestClass().getAnnotatedMethods( |
| PerformanceTest.class)) { |
| Object targetObject = getTestClass().getJavaClass().newInstance(); |
| FrameworkPerformanceMethod performanceTestMethod = new FrameworkPerformanceMethod( |
| method.getMethod(), targetObject, current, reportLevel); |
| tests.add(performanceTestMethod); |
| } |
| |
| return tests; |
| } |
| |
| |
| /** |
| * Retrieve specific method from test class |
| * |
| * @param testClass |
| * the test class that we need to search in |
| * @param annotation |
| * the annotation that we should look for |
| * @return the list with the methods that have the specified annotation |
| */ |
| @SuppressWarnings({ "rawtypes" }) |
| private Method[] getSpecificMethods(Class testClass, |
| Class<? extends Annotation> annotation) { |
| Method[] allMethods = testClass.getDeclaredMethods(); |
| |
| List<Method> methodListResult = new ArrayList<Method>(); |
| |
| for (Method testMethod : allMethods) { |
| if (testMethod.isAnnotationPresent(annotation)) { |
| methodListResult.add(testMethod); |
| } |
| } |
| return methodListResult.toArray(new Method[] {}); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.junit.runners.BlockJUnit4ClassRunner#computeTestMethods() |
| */ |
| @Override |
| protected List<FrameworkMethod> computeTestMethods() { |
| return tests; |
| } |
| |
| /** |
| * Need to override method otherwise the validation will fail because of |
| * some hardcoded conditions in JUnit |
| */ |
| @Override |
| protected void validateInstanceMethods(List<Throwable> errors) { |
| validatePublicVoidNoArgMethods(After.class, false, errors); |
| validatePublicVoidNoArgMethods(Before.class, false, errors); |
| validateTestMethods(errors); |
| } |
| |
| } |