blob: 2a5d45e3ac21a3d636c6ab6e0c61b4c5bd5bfa43 [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.activemq;
import java.lang.reflect.Field;
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.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Poor mans way of getting JUnit to run a test case through a few different
* combinations of options. Usage: If you have a test case called testFoo what
* you want to run through a few combinations, of of values for the attributes
* age and color, you would something like: <code>
* public void initCombosForTestFoo() {
* addCombinationValues( "age", new Object[]{ new Integer(21), new Integer(30) } );
* addCombinationValues( "color", new Object[]{"blue", "green"} );
* }
* </code>
* The testFoo test case would be run for each possible combination of age and
* color that you setup in the initCombosForTestFoo method. Before each
* combination is run, the age and color fields of the test class are set to one
* of the values defined. This is done before the normal setUp method is called.
* If you want the test combinations to show up as separate test runs in the
* JUnit reports, add a suite method to your test case similar to: <code>
* public static Test suite() {
* return suite(FooTest.class);
* }
* </code>
*
*
*/
public abstract class CombinationTestSupport extends AutoFailTestSupport {
private static final Logger LOG = LoggerFactory.getLogger(CombinationTestSupport.class);
private HashMap<String, ComboOption> comboOptions = new HashMap<String, ComboOption>();
private boolean combosEvaluated;
private Map options;
static class ComboOption {
final String attribute;
final LinkedHashSet<Object> values = new LinkedHashSet<Object>();
public ComboOption(String attribute, Collection<Object> options) {
this.attribute = attribute;
this.values.addAll(options);
}
}
public void addCombinationValues(String attribute, Object[] options) {
ComboOption co = this.comboOptions.get(attribute);
if (co == null) {
this.comboOptions.put(attribute, new ComboOption(attribute, Arrays.asList(options)));
} else {
co.values.addAll(Arrays.asList(options));
}
}
public void runBare() throws Throwable {
if (combosEvaluated) {
super.runBare();
} else {
CombinationTestSupport[] combinations = getCombinations();
for (int i = 0; i < combinations.length; i++) {
CombinationTestSupport test = combinations[i];
if (getName() == null || getName().equals(test.getName())) {
test.runBare();
}
}
}
}
private void setOptions(Map options) throws NoSuchFieldException, IllegalAccessException {
this.options = options;
for (Iterator iterator = options.keySet().iterator(); iterator.hasNext();) {
String attribute = (String)iterator.next();
Object value = options.get(attribute);
try {
Field field = getClass().getField(attribute);
field.set(this, value);
} catch (Throwable e) {
LOG.info("Could not set field '" + attribute + "' to value '" + value + "', make sure the field exists and is public.");
}
}
}
private CombinationTestSupport[] getCombinations() {
try {
Method method = getClass().getMethod("initCombos", (Class[])null);
method.invoke(this, (Object[])null);
} catch (Throwable e) {
}
String name = getName().split(" ")[0];
String comboSetupMethodName = "initCombosFor" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
try {
Method method = getClass().getMethod(comboSetupMethodName, (Class[])null);
method.invoke(this, (Object[])null);
} catch (Throwable e) {
}
try {
ArrayList<HashMap<String, Object>> expandedOptions = new ArrayList<HashMap<String, Object>>();
expandCombinations(new ArrayList<ComboOption>(comboOptions.values()), expandedOptions);
if (expandedOptions.isEmpty()) {
combosEvaluated = true;
return new CombinationTestSupport[] {this};
} else {
ArrayList<CombinationTestSupport> result = new ArrayList<CombinationTestSupport>();
// Run the test case for each possible combination
for (Iterator<HashMap<String, Object>> iter = expandedOptions.iterator(); iter.hasNext();) {
CombinationTestSupport combo = (CombinationTestSupport)TestSuite.createTest(getClass(), name);
combo.combosEvaluated = true;
combo.setOptions(iter.next());
result.add(combo);
}
CombinationTestSupport rc[] = new CombinationTestSupport[result.size()];
result.toArray(rc);
return rc;
}
} catch (Throwable e) {
combosEvaluated = true;
return new CombinationTestSupport[] {this};
}
}
private void expandCombinations(List<ComboOption> optionsLeft, List<HashMap<String, Object>> expandedCombos) {
if (!optionsLeft.isEmpty()) {
HashMap<String, Object> map;
if (comboOptions.size() == optionsLeft.size()) {
map = new HashMap<String, Object>();
expandedCombos.add(map);
} else {
map = expandedCombos.get(expandedCombos.size() - 1);
}
LinkedList<ComboOption> l = new LinkedList<ComboOption>(optionsLeft);
ComboOption comboOption = l.removeLast();
int i = 0;
for (Iterator<Object> iter = comboOption.values.iterator(); iter.hasNext();) {
Object value = iter.next();
if (i != 0) {
map = new HashMap<String, Object>(map);
expandedCombos.add(map);
}
map.put(comboOption.attribute, value);
expandCombinations(l, expandedCombos);
i++;
}
}
}
public static Test suite(Class<? extends CombinationTestSupport> clazz) {
TestSuite suite = new TestSuite();
ArrayList<String> names = new ArrayList<String>();
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
String name = methods[i].getName();
if (names.contains(name) || !isPublicTestMethod(methods[i])) {
continue;
}
names.add(name);
Test test = TestSuite.createTest(clazz, name);
if (test instanceof CombinationTestSupport) {
CombinationTestSupport[] combinations = ((CombinationTestSupport)test).getCombinations();
for (int j = 0; j < combinations.length; j++) {
suite.addTest(combinations[j]);
}
} else {
suite.addTest(test);
}
}
return suite;
}
private static boolean isPublicTestMethod(Method m) {
return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
}
private static boolean isTestMethod(Method m) {
String name = m.getName();
Class[] parameters = m.getParameterTypes();
Class returnType = m.getReturnType();
return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE);
}
public String getName() {
return getName(false);
}
public String getName(boolean original) {
if (options != null && !original) {
return super.getName() + " " + options;
}
return super.getName();
}
}