diff --git a/src/test/java/org/apache/commons/lang3/compare/BulkTest.java b/src/test/java/org/apache/commons/lang3/compare/BulkTest.java new file mode 100644 index 000000000..bd7d61b03 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/compare/BulkTest.java @@ -0,0 +1,464 @@ +/* + * 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.commons.collections; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * A {@link TestCase} that can define both simple and bulk test methods. + *
+ * A simple test method is the type of test traditionally
+ * supplied by by {@link TestCase}. To define a simple test, create a public
+ * no-argument method whose name starts with "test". You can specify the
+ * the name of simple test in the constructor of BulkTest
;
+ * a subsequent call to {@link TestCase#run} will run that simple test.
+ *
+ * A bulk test method, on the other hand, returns a new instance
+ * of BulkTest
, which can itself define new simple and bulk
+ * test methods. By using the {@link #makeSuite} method, you can
+ * automatically create a hierarchal suite of tests and child bulk tests.
+ *
+ * For instance, consider the following two classes: + * + *
+ * public class TestSet extends BulkTest { + * + * private Set set; + * + * public TestSet(Set set) { + * this.set = set; + * } + * + * public void testContains() { + * boolean r = set.contains(set.iterator().next())); + * assertTrue("Set should contain first element, r); + * } + * + * public void testClear() { + * set.clear(); + * assertTrue("Set should be empty after clear", set.isEmpty()); + * } + * } + * + * + * public class TestHashMap extends BulkTest { + * + * private Map makeFullMap() { + * HashMap result = new HashMap(); + * result.put("1", "One"); + * result.put("2", "Two"); + * return result; + * } + * + * public void testClear() { + * Map map = makeFullMap(); + * map.clear(); + * assertTrue("Map empty after clear", map.isEmpty()); + * } + * + * public BulkTest bulkTestKeySet() { + * return new TestSet(makeFullMap().keySet()); + * } + * + * public BulkTest bulkTestEntrySet() { + * return new TestSet(makeFullMap().entrySet()); + * } + * } + *+ * + * In the above examples,
TestSet
defines two
+ * simple test methods and no bulk test methods; TestHashMap
+ * defines one simple test method and two bulk test methods. When
+ * makeSuite(TestHashMap.class).run
is executed,
+ * five simple test methods will be run, in this order:+ * + *
+ * + *
null
from the bulk test method. If you only
+ * want to override specific simple tests within a bulk test, use the
+ * {@link #ignoredTests} method.
+ *
+ * Note that if you want to use the bulk test methods, you must
+ * define your suite()
method to use {@link #makeSuite}.
+ * The ordinary {@link TestSuite} constructor doesn't know how to
+ * interpret bulk test methods.
+ *
+ * @author Paul Jack
+ * @version $Id$
+ */
+public class BulkTest extends TestCase implements Cloneable {
+
+
+ // Note: BulkTest is Cloneable to make it easier to construct
+ // BulkTest instances for simple test methods that are defined in
+ // anonymous inner classes. Basically we don't have to worry about
+ // finding weird constructors. (And even if we found them, technically
+ // it'd be illegal for anyone but the outer class to invoke them).
+ // Given one BulkTest instance, we can just clone it and reset the
+ // method name for every simple test it defines.
+
+
+ /**
+ * The full name of this bulk test instance. This is the full name
+ * that is compared to {@link #ignoredTests} to see if this
+ * test should be ignored. It's also displayed in the text runner
+ * to ease debugging.
+ */
+ String verboseName;
+
+
+ /**
+ * Constructs a new BulkTest
instance that will run the
+ * specified simple test.
+ *
+ * @param name the name of the simple test method to run
+ */
+ public BulkTest(String name) {
+ super(name);
+ this.verboseName = getClass().getName();
+ }
+
+
+ /**
+ * Creates a clone of this BulkTest
.
+ *
+ * @return a clone of this BulkTest
+ */
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new Error(); // should never happen
+ }
+ }
+
+
+ /**
+ * Returns an array of test names to ignore.
+ *
+ * If a test that's defined by this BulkTest
or
+ * by one of its bulk test methods has a name that's in the returned
+ * array, then that simple test will not be executed.
+ *
+ * A test's name is formed by taking the class name of the
+ * root BulkTest
, eliminating the package name, then
+ * appending the names of any bulk test methods that were invoked
+ * to get to the simple test, and then appending the simple test
+ * method name. The method names are delimited by periods:
+ *
+ *
+ * TestHashMap.bulkTestEntrySet.testClear + *+ * + * is the name of one of the simple tests defined in the sample classes + * described above. If the sample
TestHashMap
class
+ * included this method:
+ *
+ * + * public String[] ignoredTests() { + * return new String[] { "TestHashMap.bulkTestEntrySet.testClear" }; + * } + *+ * + * then the entry set's clear method wouldn't be tested, but the key + * set's clear method would. + * + * @return an array of the names of tests to ignore, or null if + * no tests should be ignored + */ + public String[] ignoredTests() { + return null; + } + + + /** + * Returns the display name of this
BulkTest
.
+ *
+ * @return the display name of this BulkTest
+ */
+ @Override
+ public String toString() {
+ return getName() + "(" + verboseName + ") ";
+ }
+
+
+ /**
+ * Returns a {@link TestSuite} for testing all of the simple tests
+ * and all the bulk tests defined by the given class.+ * + * The class is examined for simple and bulk test methods; any child + * bulk tests are also examined recursively; and the results are stored + * in a hierarchal {@link TestSuite}.
+ *
+ * The given class must be a subclass of BulkTest
and must
+ * not be abstract.
+ *
+ * @param c the class to examine for simple and bulk tests
+ * @return a {@link TestSuite} containing all the simple and bulk tests
+ * defined by that class
+ */
+ public static TestSuite makeSuite(Class extends BulkTest> c) {
+ if (Modifier.isAbstract(c.getModifiers())) {
+ throw new IllegalArgumentException("Class must not be abstract.");
+ }
+ if (!BulkTest.class.isAssignableFrom(c)) {
+ throw new IllegalArgumentException("Class must extend BulkTest.");
+ }
+ return new BulkTestSuiteMaker(c).make();
+ }
+
+}
+
+
+// It was easier to use a separate class to do all the reflection stuff
+// for making the TestSuite instances. Having permanent state around makes
+// it easier to handle the recursion.
+class BulkTestSuiteMaker {
+
+ /** The class that defines simple and bulk tests methods. */
+ private Class extends BulkTest> startingClass;
+
+ /** List of ignored simple test names. */
+ private List