/**
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.bigtop.itest.junit;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.Parameterized;
import org.junit.runners.Suite;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* This is a modification of a Parameterized JUnit runner (which has been relicensed
* under APL for this very hack BIGTOP-18) that takes care of two things:
* 1. it lets arrange individual tests into ordered sequence of run stages via adding a
* a @RunStage(level=X) annotation to the desired testcases (default run stage
* is 0). Later on run stages are executed according to the order of their levels
* and testcases within the same run stage have no guaranteed order of execution.
* 2. it lets give names to the parameterized testcases via making the factory method
* @Parameters return a Map (mapping names to testcases) instead of a List.
* <p/>
* Here's how to use it:
* <pre>
* public class Example {
* <b>@RunStage(level=-1)</b>
* <b>@Test</b>
* public void earlyTest() {
* }
*
* <b>@RunStage(level=1)</b>
* <b>@Test</b>
* public void lateTest() {
* }
*
* <b>@Test</b>
* public void defaultTest() {
* // this test will be executed at run stage 0 (default of level)
* }
*
* @Parameters
* public static Map<String, Object[]> generateTests() {
* HashMap<String, Object[]> res = new HashMap();
* res.put("test name", new Object[] {1, 2});
* return res;
* }
* }
* </pre>
*/
public class OrderedParameterized extends Suite {
/**
* Annotation for a method which provides parameters to be injected into the
* test class constructor by <code>Parameterized</code>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RunStage {
int level() default 0;
}
;
private class TestClassRunnerForParameters extends
BlockJUnit4ClassRunner {
private final String fParameterSetNumber;
private final Map<String, Object[]> fParameterList;
TestClassRunnerForParameters(Class<?> type,
Map<String, Object[]> parameterList, String i) throws InitializationError {
super(type);
fParameterList = parameterList;
fParameterSetNumber = i;
}
@Override
public Object createTest() throws Exception {
return getTestClass().getOnlyConstructor().newInstance(
computeParams());
}
private Object[] computeParams() throws Exception {
try {
return fParameterList.get(fParameterSetNumber);
} catch (ClassCastException e) {
throw new Exception(String.format(
"%s.%s() must return a Map from Strings to arrays.",
getTestClass().getName(), getParametersMethod(
getTestClass()).getName()));
}
}
@Override
protected List<FrameworkMethod> getChildren() {
List<FrameworkMethod> c = super.getChildren();
Collections.sort(c, new Comparator<FrameworkMethod>() {
public int compare(FrameworkMethod m1, FrameworkMethod m2) {
RunStage r1 = m1.getAnnotation(RunStage.class);
RunStage r2 = m2.getAnnotation(RunStage.class);
return ((r1 != null) ? r1.level() : 0) -
((r2 != null) ? r2.level() : 0);
}
});
return c;
}
@Override
protected String getName() {
return String.format("[%s]", fParameterSetNumber);
}
@Override
protected String testName(final FrameworkMethod method) {
return String.format("%s[%s]", method.getName(),
fParameterSetNumber);
}
@Override
protected void validateConstructor(List<Throwable> errors) {
validateOnlyOneConstructor(errors);
}
@Override
protected Statement classBlock(RunNotifier notifier) {
return childrenInvoker(notifier);
}
}
private FrameworkMethod getParametersMethod(TestClass testClass)
throws Exception {
List<FrameworkMethod> methods = testClass
.getAnnotatedMethods(Parameterized.Parameters.class);
for (FrameworkMethod each : methods) {
int modifiers = each.getMethod().getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))
return each;
}
throw new Exception("No public static parameters method on class "
+ testClass.getName());
}
private final ArrayList<Runner> runners = new ArrayList<Runner>();
@Override
protected List<Runner> getChildren() {
return runners;
}
@SuppressWarnings("unchecked")
private Map<String, Object[]> getParametersList(TestClass klass) throws Throwable {
return (Map<String, Object[]>) getParametersMethod(klass).invokeExplosively(null);
}
public OrderedParameterized(Class<?> klass) throws Throwable {
super(klass, Collections.<Runner>emptyList());
Map<String, Object[]> parametersMap = getParametersList(getTestClass());
for (Map.Entry<String, Object[]> entry : parametersMap.entrySet())
runners.add(new TestClassRunnerForParameters(getTestClass().getJavaClass(),
parametersMap, entry.getKey()));
}
}