SLING-2727 - Allow PerformanceRunner to run tests provided by a factory method

* applied patch from Andrei Dulvac (thanks)

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1551535 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/base/src/main/java/org/apache/sling/performance/FrameworkPerformanceMethod.java b/base/src/main/java/org/apache/sling/performance/FrameworkPerformanceMethod.java
index 8010a3a..3532afa 100644
--- a/base/src/main/java/org/apache/sling/performance/FrameworkPerformanceMethod.java
+++ b/base/src/main/java/org/apache/sling/performance/FrameworkPerformanceMethod.java
@@ -38,6 +38,7 @@
 	private Object target;
 	private PerformanceSuiteState performanceSuiteState;
 	private PerformanceRunner.ReportLevel reportLevel = PerformanceRunner.ReportLevel.ClassLevel;
+    private String testCaseName = "";
 
 	public FrameworkPerformanceMethod(Method method, Object target,
 			PerformanceSuiteState performanceSuiteState, PerformanceRunner.ReportLevel reportLevel) {
@@ -45,7 +46,10 @@
 		this.target = target;
 		this.performanceSuiteState = performanceSuiteState;
 		this.reportLevel = reportLevel;
+        if (target instanceof IdentifiableTestCase) {
+            this.testCaseName = ((IdentifiableTestCase) target).testCaseName();
 	}
+    }
 
 	@Override
 	public Object invokeExplosively(Object target, Object... params)
@@ -173,11 +177,8 @@
 		}
 
 		if (statistics.getN() > 0) {
-			ReportLogger
-					.writeReport(this.target.getClass().getName(),
-							this.performanceSuiteState.testSuiteName,
-							getMethod().getName(), statistics,
-							ReportLogger.ReportType.TXT, reportLevel);
+            ReportLogger.writeReport(this.performanceSuiteState.testSuiteName, testCaseName, this.target.getClass().getName(),
+                    getMethod().getName(), statistics, ReportLogger.ReportType.TXT, reportLevel);
 		}
 
 		// In case of a PerformanceSuite we need to run the methods annotated
@@ -345,4 +346,12 @@
 		return methodListResult.toArray(new Method[] {});
 	}
 
+    @Override
+    public String getName() {
+        System.out.println(testCaseName);
+        if (testCaseName == null || "".equals(testCaseName.trim())) { return super.getName(); }
+        return String.format("%s  [%s.%s]", testCaseName, target.getClass().getSimpleName(),
+                getMethod().getName());
+}
+
 }
diff --git a/base/src/main/java/org/apache/sling/performance/IdentifiableTestCase.java b/base/src/main/java/org/apache/sling/performance/IdentifiableTestCase.java
new file mode 100644
index 0000000..cfae459
--- /dev/null
+++ b/base/src/main/java/org/apache/sling/performance/IdentifiableTestCase.java
@@ -0,0 +1,10 @@
+package org.apache.sling.performance;
+
+/**
+ * Interface to be implemented by a class with PerformanceTests.
+ * <p></p>The provided method @{link #testCaseName()} exposes the possibility to give a name to each instance of the
+ * implementing class</p>
+ */
+public interface IdentifiableTestCase {
+    public String testCaseName();
+}
diff --git a/base/src/main/java/org/apache/sling/performance/PerformanceRunner.java b/base/src/main/java/org/apache/sling/performance/PerformanceRunner.java
index 68e2926..a2ff109 100644
--- a/base/src/main/java/org/apache/sling/performance/PerformanceRunner.java
+++ b/base/src/main/java/org/apache/sling/performance/PerformanceRunner.java
@@ -16,26 +16,31 @@
  */
 package org.apache.sling.performance;
 
-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.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
 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
@@ -82,9 +87,10 @@
 	 * @throws Exception
 	 */
 	protected void computeTests() throws Exception {
+		// add normal JUnit tests
 		tests.addAll(super.computeTestMethods());
 
-		// count the performance tests
+		// add the performance tests
 		tests.addAll(computePerformanceTests());
 
 		// This is called here to ensure the test class constructor is called at
@@ -101,9 +107,12 @@
 	 */
 	protected Collection<? extends FrameworkMethod> computePerformanceTests()
 			throws Exception {
-		List<FrameworkPerformanceMethod> tests = new LinkedList<FrameworkPerformanceMethod>();
+		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
@@ -114,10 +123,42 @@
 					.equals(ParameterizedTestList.class)) {
 				testCenter = (ParameterizedTestList) method.getMethod().invoke(
 						targetObject);
-				testObjects = testCenter.getTestObjectList();
+				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 @PerformanceSuite method");
+						"Wrong signature for the @PerformanceTestSuite method");
 			}
 		}
 
@@ -172,22 +213,25 @@
 		// 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 PerformaceSuite
+		// 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);
 
-				if (!suiteAlreadyRegistered) {
-					newSuite.incrementNumberOfTestMethodsInSuite();
-				}
 
 				for (Method method : testMethods) {
-					FrameworkPerformanceMethod performaceTestMethod = new FrameworkPerformanceMethod(
+					FrameworkPerformanceMethod performaceTestMethod = new
+							FrameworkPerformanceMethod(
 							method, testObject, current, reportLevel);
 					tests.add(performaceTestMethod);
 				}
+
+                if (!suiteAlreadyRegistered) {
+					newSuite.incrementNumberOfTestMethodsInSuite();
+				}
 			}
 
 			// add the new suite to the list of suites
@@ -199,15 +243,16 @@
 		for (FrameworkMethod method : getTestClass().getAnnotatedMethods(
 				PerformanceTest.class)) {
 			Object targetObject = getTestClass().getJavaClass().newInstance();
-			FrameworkPerformanceMethod performaceTestMethod = new FrameworkPerformanceMethod(
+			FrameworkPerformanceMethod performanceTestMethod = new FrameworkPerformanceMethod(
 					method.getMethod(), targetObject, current, reportLevel);
-			tests.add(performaceTestMethod);
+			tests.add(performanceTestMethod);
 		}
 
 		return tests;
 	}
 
-	/**
+
+		/**
 	 * Retrieve specific method from test class
 	 * 
 	 * @param testClass
diff --git a/base/src/main/java/org/apache/sling/performance/annotation/PerformanceTestFactory.java b/base/src/main/java/org/apache/sling/performance/annotation/PerformanceTestFactory.java
new file mode 100644
index 0000000..b7341b3
--- /dev/null
+++ b/base/src/main/java/org/apache/sling/performance/annotation/PerformanceTestFactory.java
@@ -0,0 +1,11 @@
+package org.apache.sling.performance.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PerformanceTestFactory {
+}