blob: bb646fe1c47747a5ce4243228965a01b30434bfe [file] [log] [blame]
/*
* 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);
}
}