| /* |
| * 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.openjpa.lib.test; |
| |
| import java.beans.BeanInfo; |
| import java.beans.IntrospectionException; |
| import java.beans.Introspector; |
| import java.beans.PropertyDescriptor; |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.PrintStream; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.net.URL; |
| import java.text.NumberFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import java.util.NoSuchElementException; |
| import java.util.StringTokenizer; |
| |
| import junit.framework.TestCase; |
| import junit.framework.TestResult; |
| import junit.textui.TestRunner; |
| import org.apache.regexp.RE; |
| import org.apache.regexp.RESyntaxException; |
| import org.apache.regexp.REUtil; |
| import org.apache.tools.ant.AntClassLoader; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.ProjectHelper; |
| import org.apache.openjpa.lib.log.Log; |
| import org.apache.openjpa.lib.log.LogFactoryImpl; |
| import org.apache.openjpa.lib.util.Localizer; |
| |
| /** |
| * TestCase framework to run various tests against solarmetric code. |
| * This class contains various utility methods for the following functions: |
| * <ul> |
| * <li>Using multiple, isolated ClassLoaders</li> |
| * <li>Running a test in multiple concurrent threads</li> |
| * <li>Assertion helpers</li> |
| * <li>Creating random Strings, numbers, etc.</li> |
| * </ul> |
| * |
| * @author Marc Prud'hommeaux |
| * @author Patrick Linskey |
| */ |
| public abstract class AbstractTestCase extends TestCase { |
| |
| public static final String TEST_METHODS = |
| System.getProperty(AbstractTestCase.class.getName() + ".testMethods"); |
| public static final long PLATFORM_ALL = 2 << 1; |
| public static final long PLATFORM_UNKNOWN = 2 << 2; |
| |
| public static final String SKIP_TOKEN = "SOLARSKIP"; |
| public static final String SKIP_DELIMITER = "|"; |
| |
| private static final Localizer _loc = |
| Localizer.forPackage(AbstractTestCase.class); |
| |
| private Log log = null; |
| |
| private static Map _times = new HashMap(); |
| |
| private static AbstractTestCase _lastTest = null; |
| |
| private static WatchdogThread _watchdog = new WatchdogThread(); |
| private long _timeout; |
| |
| /** |
| * Constructor. Create a test case with the specified name. |
| */ |
| public AbstractTestCase(String test) { |
| super(test); |
| } |
| |
| public AbstractTestCase() { |
| } |
| |
| protected final Log getLog() { |
| if (log == null) |
| log = newLog(); |
| return log; |
| } |
| |
| protected Log newLog() { |
| // this implementation leaves much to be desired, as it just |
| // creates a new LogFactoryImpl each time, and does not apply |
| // any configurations. |
| return new LogFactoryImpl().getLog(getLogName()); |
| } |
| |
| protected String getLogName() { |
| return "com.solarmetric.Runtime"; |
| } |
| |
| /** |
| * Called before the watchdog thread is about to kill the entire |
| * JVM due to a test case's timeout. This method offers the |
| * ability to try to resolve whatever contention is taking place |
| * in the test. It will be given 10 seconds to try to end the |
| * test peacefully before the watchdog exits the JVM. |
| */ |
| protected void preTimeout() { |
| } |
| |
| public void run(TestResult result) { |
| if (skipTest()) { |
| // keep track of the tests we skip so that we can get an |
| // idea in the autobuild status |
| System.err.println(SKIP_TOKEN + SKIP_DELIMITER |
| + ("" + getClass().getName()) |
| + "." + getName() + SKIP_DELIMITER); |
| return; |
| } |
| |
| if (_lastTest != null && _lastTest.getClass() != getClass()) { |
| try { |
| _lastTest.tearDownTestClass(); |
| } catch (Throwable t) { |
| getLog().error(null, t); |
| } |
| } |
| |
| if (_lastTest == null || _lastTest.getClass() != getClass()) { |
| try { |
| setUpTestClass(); |
| } catch (Throwable t) { |
| getLog().error(null, t); |
| } |
| } |
| |
| _lastTest = this; |
| |
| // inform the watchdog thread that we are entering the test |
| _watchdog.enteringTest(this); |
| try { |
| super.run(result); |
| } finally { |
| _watchdog.leavingTest(this); |
| } |
| } |
| |
| /** |
| * If this test should be skipped given the current |
| * environment, return <code>true</code>. This allows a unit test |
| * class to disable test cases on a per-method granularity, and |
| * prevents the test from showing up as a passed test just |
| * because it was skipped. |
| * For example, if a particular test case method should not be |
| * run against a certain database, this method could check the |
| * name of the test result and the current database configuration |
| * in order to make the decision: |
| * <p/> |
| * <code> protected boolean skipTest() { |
| * // don't run with pointbase: it uses a DataSource, which |
| * // can't be translated into a JBoss DataSource configuration. |
| * if ("testJBoss".equals(getName()) && |
| * getCurrentPlatform() == PLATFORM_POINTBASE) |
| * return true; |
| * } |
| * </code> |
| * If you want to disable execution of an entire test case |
| * class for a given database, you might want to add the class to |
| * the excluded test list in that database's properties file. |
| */ |
| protected boolean skipTest() { |
| if (TEST_METHODS != null && TEST_METHODS.length() > 0) |
| return TEST_METHODS.indexOf(getName()) == -1; |
| |
| return false; |
| } |
| |
| /** |
| * This method is called before the first test in this test class |
| * is executed. |
| */ |
| public void setUpTestClass() throws Exception { |
| } |
| |
| /** |
| * This method is called after the last test in this test class |
| * is executed. It can be used to do things like clean up |
| * large, slow processes that may have been started. |
| */ |
| public void tearDownTestClass() throws Exception { |
| } |
| |
| public void tearDown() throws Exception { |
| if ("true".equals(System.getProperty("meminfo"))) |
| printMemoryInfo(); |
| |
| super.tearDown(); |
| } |
| |
| ////////////////////////// |
| // Generating random data |
| ////////////////////////// |
| |
| /** |
| * Support method to get a random Integer for testing. |
| */ |
| public static Integer randomInt() { |
| return new Integer((int) (Math.random() * Integer.MAX_VALUE)); |
| } |
| |
| /** |
| * Support method to get a random Character for testing. |
| */ |
| public static Character randomChar() { |
| char [] TEST_CHAR_ARRAY = new char []{ |
| 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', |
| 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', |
| 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', |
| '2', '3', '4', '5', '6', '7', '8', '9' }; |
| |
| return new Character(TEST_CHAR_ARRAY[ |
| (int) (Math.random() * TEST_CHAR_ARRAY.length)]); |
| } |
| |
| /** |
| * Support method to get a random Long for testing. |
| */ |
| public static Long randomLong() { |
| return new Long((long) (Math.random() * Long.MAX_VALUE)); |
| } |
| |
| /** |
| * Support method to get a random Short for testing. |
| */ |
| public static Short randomShort() { |
| return new Short((short) (Math.random() * Short.MAX_VALUE)); |
| } |
| |
| /** |
| * Support method to get a random Double for testing. |
| */ |
| public static Double randomDouble() { |
| return new Double((double) (Math.round(Math.random() * 5000d)) / 1000d); |
| } |
| |
| /** |
| * Support method to get a random Float for testing. |
| */ |
| public static Float randomFloat() { |
| return new Float((float) (Math.round(Math.random() * 5000f)) / 1000f); |
| } |
| |
| /** |
| * Support method to get a random Byte for testing. |
| */ |
| public static Byte randomByte() { |
| return new Byte((byte) (Math.random() * Byte.MAX_VALUE)); |
| } |
| |
| /** |
| * Support method to get a random Boolean for testing. |
| */ |
| public static Boolean randomBoolean() { |
| return new Boolean(Math.random() > 0.5 ? true : false); |
| } |
| |
| /** |
| * Support method to get a random Date for testing. |
| */ |
| public static Date randomDate() { |
| long millis = (long) (Math.random() * System.currentTimeMillis()); |
| |
| // round millis to the nearest 1000: this is because some |
| // databases do not store the milliseconds correctly(e.g., MySQL). |
| // This is a really a bug we should fix. FC #27. |
| millis -= (millis % 1000); |
| |
| return new Date(millis); |
| } |
| |
| /** |
| * Support method to get a random String for testing. |
| */ |
| public static String randomString() { |
| // default to a small string, in case column sizes are |
| // limited(such as with a string primary key) |
| return randomString(50); |
| } |
| |
| /** |
| * Support method to get a random String for testing. |
| */ |
| public static String randomString(int len) { |
| StringBuffer buf = new StringBuffer(); |
| for (int i = 0; i < (int) (Math.random() * len) + 1; i++) |
| buf.append(randomChar()); |
| return buf.toString(); |
| } |
| |
| /** |
| * Support method to get a random clob for testing. |
| */ |
| public static String randomClob() { |
| StringBuffer sbuf = new StringBuffer(); |
| while (sbuf.length() < (5 * 1024)) { // at least 5K |
| sbuf.append(randomString(1024)); |
| } |
| |
| return sbuf.toString(); |
| } |
| |
| /** |
| * Support method to get a random BigInteger for testing. |
| */ |
| public static BigInteger randomBigInteger() { |
| // too many of our test databases don't support bigints > MAX_LONG: |
| // I don't like it, but for now, let's only test below MAX_LONG |
| BigInteger lng = new BigInteger( |
| ((long) (Math.random() * Long.MAX_VALUE)) + ""); |
| |
| BigInteger multiplier = new BigInteger("1"); |
| // (1 + (int)(Math.random() * 10000)) + ""); |
| if (Math.random() < 0.5) |
| multiplier = multiplier.multiply(new BigInteger("-1")); |
| |
| return lng.multiply(multiplier); |
| } |
| |
| /** |
| * Support method to get a random BigDecimal for testing. |
| */ |
| public static BigDecimal randomBigDecimal() { |
| BigInteger start = randomBigInteger(); |
| String str = start.toString(); |
| // truncate off the last 8 digits: we still get some |
| // overflows with lame databases. |
| for (int i = 0; i < 8; i++) |
| if (str.length() > 2) |
| str = str.substring(0, str.length() - 1); |
| start = new BigInteger(str); |
| |
| String val = start + "." + ((int) (Math.random() * 10)) |
| + ((int) (Math.random() * 10)) |
| + ((int) (Math.random() * 10)) |
| + ((int) (Math.random() * 10)) |
| + ((int) (Math.random() * 10)) |
| + ((int) (Math.random() * 10)) |
| + ((int) (Math.random() * 10)) |
| + ((int) (Math.random() * 10)) |
| + ((int) (Math.random() * 10)) |
| + ((int) (Math.random() * 10)); |
| |
| return new BigDecimal(val); |
| } |
| |
| /** |
| * Support method to get a random blob for testing. |
| */ |
| public static byte[] randomBlob() { |
| // up to 100K blob |
| byte [] blob = new byte [(int) (Math.random() * 1024 * 100)]; |
| for (int i = 0; i < blob.length; i++) |
| blob[i] = randomByte().byteValue(); |
| |
| return blob; |
| } |
| |
| /** |
| * Invoke setters for pimitives and primitive wrappers on the |
| * specified object. |
| */ |
| public static Object randomizeBean(Object bean) |
| throws IntrospectionException, IllegalAccessException, |
| InvocationTargetException { |
| BeanInfo info = Introspector.getBeanInfo(bean.getClass()); |
| PropertyDescriptor [] props = info.getPropertyDescriptors(); |
| for (int i = 0; i < props.length; i++) { |
| Method write = props[i].getWriteMethod(); |
| if (write == null) |
| continue; |
| |
| Class [] params = write.getParameterTypes(); |
| if (params == null || params.length != 1) |
| continue; |
| |
| Class paramType = params[0]; |
| Object arg = null; |
| |
| if (paramType == boolean.class || paramType == Boolean.class) |
| arg = randomBoolean(); |
| else if (paramType == byte.class || paramType == Byte.class) |
| arg = randomByte(); |
| else if (paramType == char.class || paramType == Character.class) |
| arg = randomChar(); |
| else if (paramType == short.class || paramType == Short.class) |
| arg = randomShort(); |
| else if (paramType == int.class || paramType == Integer.class) |
| arg = randomInt(); |
| else if (paramType == long.class || paramType == Long.class) |
| arg = randomLong(); |
| else if (paramType == double.class || paramType == Double.class) |
| arg = randomDouble(); |
| else if (paramType == float.class || paramType == Float.class) |
| arg = randomFloat(); |
| else if (paramType == String.class) |
| arg = randomString(); |
| else if (paramType == BigInteger.class) |
| arg = randomBigInteger(); |
| else if (paramType == BigDecimal.class) |
| arg = randomBigDecimal(); |
| else if (paramType == Date.class) |
| arg = randomDate(); |
| |
| if (arg != null) |
| write.invoke(bean, new Object []{ arg }); |
| } |
| |
| return bean; |
| } |
| |
| /** |
| * Utility method to start a profile. |
| * |
| * @see #endProfile(String) |
| */ |
| public void startProfile(String name) { |
| _times.put(name, new Long(System.currentTimeMillis())); |
| } |
| |
| /** |
| * Utility to end the profile and print out the time. Example usage: |
| * <p/> |
| * <pre><code> startProfile("Some long task"); doSomeLongTask(); |
| * endProfile("Some long task"); |
| * </code></pre> |
| * |
| * @param name |
| * @return the amount of time that this profile invocation took, or |
| * -1 if <code>name</code> was never started. |
| */ |
| public long endProfile(String name) { |
| Long time = (Long) _times.remove(name); |
| |
| long elapsed = -1; |
| if (time != null) |
| elapsed = System.currentTimeMillis() - time.longValue(); |
| |
| getLog().info(_loc.get("profile-info", name, |
| (time == null ? "???" : "" + elapsed))); |
| return elapsed; |
| } |
| |
| ///////////////////////// |
| // ClassLoader functions |
| ///////////////////////// |
| |
| /** |
| * Create a ClassLoader that will not use the parent |
| * ClassLoader to resolve classes. This is useful for |
| * testing interactions between Kodo in running |
| * in ClassLoaderA and instances in ClassLoaderB. |
| */ |
| public ClassLoader createIsolatedClassLoader() { |
| return new IsolatedClassLoader(); |
| } |
| |
| public NestedClassLoader createNestedClassLoader() { |
| return new NestedClassLoader(false); |
| } |
| |
| public NestedClassLoader createNestedParentClassLoader() { |
| return new NestedClassLoader(true); |
| } |
| |
| /** |
| * Reload the specified class in an isolated ClassLoader. |
| * |
| * @param target the target class to load |
| * @return the Class as reloaded in an new ClassLoader |
| */ |
| public Class isolate(Class target) throws ClassNotFoundException { |
| Class result = isolate(target.getName()); |
| assertTrue(result != target); |
| assertNotEquals(result, target); |
| assertTrue(result.getClassLoader() != target.getClassLoader()); |
| return result; |
| } |
| |
| public Class isolate(String target) throws ClassNotFoundException { |
| ClassLoader il = createIsolatedClassLoader(); |
| Class result = il.loadClass(target); |
| assertEquals(result.getName(), target); |
| |
| return result; |
| } |
| |
| public Class nest(Class target) throws ClassNotFoundException { |
| ClassLoader il = createNestedClassLoader(); |
| Class result = il.loadClass(target.getName()); |
| assertTrue(result != target); |
| assertNotEquals(result, target); |
| assertTrue(result.getClassLoader() != target.getClassLoader()); |
| assertEquals(result.getName(), target.getName()); |
| |
| return result; |
| } |
| |
| public Object isolateNew(Class target) |
| throws ClassNotFoundException, IllegalAccessException, |
| InstantiationException { |
| return isolate(target).newInstance(); |
| } |
| |
| private static class NestedClassLoader extends AntClassLoader { |
| |
| public NestedClassLoader(boolean useParent) { |
| super(ClassLoader.getSystemClassLoader(), useParent); |
| |
| for (StringTokenizer cltok = new StringTokenizer( |
| System.getProperty("java.class.path"), File.pathSeparator); |
| cltok.hasMoreTokens();) { |
| String path = cltok.nextToken(); |
| |
| // only load test paths, not jar files |
| if (path.indexOf(".jar") != -1) |
| continue; |
| if (path.indexOf(".zip") != -1) |
| continue; |
| |
| addPathElement(path); |
| } |
| |
| try { |
| if (!useParent) { |
| assertTrue(loadClass |
| (AbstractTestCase.class.getName()).getClassLoader() |
| != AbstractTestCase.class.getClassLoader()); |
| } |
| } catch (ClassNotFoundException cnfe) { |
| fail(cnfe.toString()); |
| } |
| } |
| |
| public Class findClass(String name) throws ClassNotFoundException { |
| // don't isolate PC and related classes in kodo.enhnace |
| if (name.indexOf(".enhance.") != -1) |
| throw new ClassNotFoundException(name); |
| if (name.indexOf("/enhance/") != -1) |
| throw new ClassNotFoundException(name); |
| return super.findClass(name); |
| } |
| } |
| |
| /** |
| * A ClassLoader that is completely isolated with respect to |
| * any classes that are loaded in the System ClassLoader. |
| * |
| * @author Marc Prud'hommeaux |
| */ |
| private static class IsolatedClassLoader extends NestedClassLoader { |
| |
| public IsolatedClassLoader() { |
| super(false); |
| setIsolated(false); |
| } |
| } |
| |
| /////////////// |
| // Collections |
| /////////////// |
| |
| /** |
| * Validate that the specified {@link Collection} fulfills the |
| * Collection contract as specified by the Collections API. |
| * <p/> |
| * <strong>Note</strong>: does not validate mutable operations |
| */ |
| public static void validateCollection(Collection collection) { |
| int size = collection.size(); |
| int iterated = 0; |
| // ensure we can walk along the iterator |
| for (Iterator i = collection.iterator(); i.hasNext();) { |
| iterated++; |
| i.next(); |
| } |
| |
| // ensure the number of values iterated is the same as the list size |
| assertEquals(size, iterated); |
| |
| // also validate the list |
| if (collection instanceof List) { |
| List ll = new ArrayList(); |
| for (int i = 0; i < 100; i++) |
| ll.add(new Integer(i)); |
| validateList((List) ll); |
| validateList((List) collection); |
| } |
| } |
| |
| /** |
| * Validate that the specified {@link List} fulfills the |
| * List contract as specified by the Collections API. |
| * <p/> |
| * <strong>Note</strong>: does not validate mutable operations |
| */ |
| public static void validateList(List list) { |
| Object [] coreValues = list.toArray(); |
| Object [] values1 = new Object [list.size()]; |
| Object [] values2 = new Object [list.size()]; |
| Object [] values3 = new Object [list.size()]; |
| Object [] values4 = new Object [list.size()]; |
| |
| // fill sequential index access list |
| for (int i = 0; i < list.size(); i++) |
| values1[i] = list.get(i); |
| |
| // fill sequential list |
| int index = 0; |
| ListIterator iter; |
| for (iter = list.listIterator(0); iter.hasNext();) { |
| assertEquals(index, iter.nextIndex()); |
| assertEquals(index, iter.previousIndex() + 1); |
| values2[index] = iter.next(); |
| assertTrue(list.contains(values2[index])); |
| index++; |
| } |
| |
| // ensure NoSuchElementException is thrown as appropriate |
| try { |
| iter.next(); |
| fail("next() should have resulted in a NoSuchElementException"); |
| } catch (NoSuchElementException e) { |
| } // as expected |
| |
| // fill reverse sequential list |
| int back = 0; |
| for (iter = list.listIterator(list.size()); iter.hasPrevious();) { |
| assertEquals(index, iter.previousIndex() + 1); |
| assertEquals(index, iter.nextIndex()); |
| values3[--index] = iter.previous(); |
| back++; |
| } |
| assertEquals(list.size(), back); |
| |
| // ensure NoSuchElementException is thrown as appropriate |
| try { |
| iter.previous(); |
| fail("previous() should have resulted in a " |
| + "NoSuchElementException"); |
| } catch (NoSuchElementException e) { |
| } // as expected |
| |
| // fill random access list |
| List indices = new LinkedList(); |
| for (int i = 0; i < list.size(); i++) |
| indices.add(new Integer(i)); |
| |
| for (int i = 0; i < list.size(); i++) { |
| int rand = (int) (Math.random() * indices.size()); |
| Integer randIndex = (Integer) indices.remove(rand); |
| values4[randIndex.intValue()] = list.get(randIndex.intValue()); |
| } |
| |
| assertEquals(Arrays.asList(coreValues), Arrays.asList(values1)); |
| assertIdentical(Arrays.asList(coreValues), Arrays.asList(values1)); |
| assertEquals(Arrays.asList(coreValues), Arrays.asList(values2)); |
| assertIdentical(Arrays.asList(coreValues), Arrays.asList(values2)); |
| assertEquals(Arrays.asList(coreValues), Arrays.asList(values4)); |
| assertIdentical(Arrays.asList(coreValues), Arrays.asList(values4)); |
| assertEquals(Arrays.asList(coreValues), Arrays.asList(values3)); |
| assertIdentical(Arrays.asList(coreValues), Arrays.asList(values3)); |
| } |
| |
| /** |
| * Assert that the given List contain the exact same |
| * elements. This is different than the normal List contract, which |
| * states that list1.equals(list2) if each element e1.equals(e2). |
| * This method asserts that e1 == n2. |
| */ |
| public static void assertIdentical(List c1, List c2) { |
| assertEquals(c1.size(), c2.size()); |
| for (Iterator i1 = c1.iterator(), i2 = c2.iterator(); |
| i1.hasNext() && i2.hasNext();) |
| assertTrue(i1.next() == i2.next()); |
| } |
| |
| /** |
| * Assert that the collection parameter is already ordered |
| * according to the specified comparator. |
| */ |
| public void assertOrdered(Collection c, Comparator comp) { |
| List l1 = new LinkedList(c); |
| List l2 = new LinkedList(c); |
| assertEquals(l1, l2); |
| Collections.sort(l2, comp); |
| assertEquals(l1, l2); |
| Collections.sort(l1, comp); |
| assertEquals(l1, l2); |
| } |
| |
| //////////////////// |
| // Assertion Helpers |
| //////////////////// |
| |
| public void assertNotEquals(Object a, Object b) { |
| if (a == null && b != null) |
| return; |
| if (a != null && b == null) |
| return; |
| if (!(a.equals(b))) |
| return; |
| if (!(b.equals(a))) |
| return; |
| |
| fail("expected !<" + a + ">.equals(<" + b + ">)"); |
| } |
| |
| public void assertSize(int size, Object ob) { |
| if (ob == null) { |
| assertEquals(size, 0); |
| return; |
| } |
| |
| if (ob instanceof Collection) |
| ob = ((Collection) ob).iterator(); |
| if (ob instanceof Iterator) { |
| Iterator i = (Iterator) ob; |
| int count = 0; |
| while (i.hasNext()) { |
| count++; |
| i.next(); |
| } |
| |
| assertEquals(size, count); |
| } else |
| fail("assertSize: expected Collection, Iterator, " |
| + "Query, or Extent, but got " + ob.getClass().getName()); |
| } |
| |
| ///////////////////// |
| // Generic utilities |
| ///////////////////// |
| |
| public void copy(File from, File to) throws IOException { |
| copy(new FileInputStream(from), to); |
| } |
| |
| public void copy(InputStream in, File to) throws IOException { |
| FileOutputStream fout = new FileOutputStream(to); |
| |
| byte[] b = new byte[1024]; |
| |
| for (int n = 0; (n = in.read(b)) != -1;) |
| fout.write(b, 0, n); |
| } |
| |
| /** |
| * Print out information on memory usage. |
| */ |
| public void printMemoryInfo() { |
| Runtime rt = Runtime.getRuntime(); |
| long total = rt.totalMemory(); |
| long free = rt.freeMemory(); |
| long used = total - free; |
| |
| NumberFormat nf = NumberFormat.getInstance(); |
| getLog().warn(_loc.get("mem-info", |
| nf.format(used), |
| nf.format(total), |
| nf.format(free))); |
| } |
| |
| /** |
| * Return a list of all values iterated by the given iterator. |
| */ |
| public static List iteratorToList(Iterator i) { |
| LinkedList list = new LinkedList(); |
| while (i.hasNext()) |
| list.add(i.next()); |
| return list; |
| } |
| |
| /** |
| * Return an array of the objects iterated by the given iterator. |
| */ |
| public static Object [] iteratorToArray(Iterator i, Class [] clazz) { |
| return iteratorToList(i).toArray(clazz); |
| } |
| |
| /** |
| * Run ant on the specified build file. |
| * |
| * @param buildFile the build file to use |
| * @param target the name of the target to invoke |
| */ |
| public void ant(File buildFile, String target) { |
| assertTrue(buildFile.isFile()); |
| |
| Project project = new Project(); |
| project.init(); |
| project.setUserProperty("ant.file", buildFile.getAbsolutePath()); |
| ProjectHelper.configureProject(project, buildFile); |
| project.executeTarget(target); |
| } |
| |
| /** |
| * Serialize and deserialize the object. |
| * |
| * @param validateEquality make sure the hashCode and equals |
| * methods hold true |
| */ |
| public static Object roundtrip(Object orig, boolean validateEquality) |
| throws IOException, ClassNotFoundException { |
| assertNotNull(orig); |
| |
| ByteArrayOutputStream bout = new ByteArrayOutputStream(); |
| ObjectOutputStream out = new ObjectOutputStream(bout); |
| out.writeObject(orig); |
| ByteArrayInputStream bin = new ByteArrayInputStream( |
| bout.toByteArray()); |
| ObjectInputStream in = new ObjectInputStream(bin); |
| Object result = in.readObject(); |
| |
| if (validateEquality) { |
| assertEquals(orig, result); |
| assertEquals(orig.hashCode(), result.hashCode()); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * @return true if the specified input matches the regular expression regex. |
| */ |
| public static boolean matches(String regex, String input) |
| throws RESyntaxException { |
| RE re = REUtil.createRE(regex); |
| return re.match(input); |
| } |
| |
| public static void assertMatches(String regex, String input) { |
| try { |
| if (!(matches(regex, input))) |
| fail("Expected regular expression: <" + regex + ">" |
| + " did not match: <" + input + ">"); |
| } catch (RESyntaxException e) { |
| throw new IllegalArgumentException(e.toString()); |
| } |
| } |
| |
| public static void assertNotMatches(String regex, String input) { |
| try { |
| if (matches(regex, input)) |
| fail("Regular expression: <" + regex + ">" |
| + " should not match: <" + input + ">"); |
| } catch (RESyntaxException e) { |
| throw new IllegalArgumentException(e.toString()); |
| } |
| } |
| |
| /** |
| * Check the list if strings and return the ones that match |
| * the specified match. |
| */ |
| public static List matches(String regex, Collection input) |
| throws RESyntaxException { |
| List matches = new ArrayList(); |
| for (Iterator i = input.iterator(); i.hasNext();) { |
| String check = (String) i.next(); |
| if (matches(regex, check)) |
| matches.add(check); |
| } |
| |
| return matches; |
| } |
| |
| /** |
| * Assert that the specified collection of Strings contains at least |
| * one string that matches the specified regular expression. |
| */ |
| public static void assertMatches(String regex, Collection input) { |
| try { |
| if (matches(regex, input).size() == 0) |
| fail("The specified list of size " + input.size() |
| + " did not contain any strings that match the" |
| + " specified regular expression(\"" + regex + "\")"); |
| } catch (RESyntaxException e) { |
| throw new IllegalArgumentException(e.toString()); |
| } |
| } |
| |
| /** |
| * Assert that the specified collection of Strings does not match |
| * the specified regular expression. |
| */ |
| public static void assertNotMatches(String regex, Collection input) { |
| try { |
| List matches; |
| |
| if (((matches = matches(regex, input))).size() > 0) |
| fail("The specified list of size " + input.size() |
| + " did contain one or more strings that matchs the" |
| + " specified illegal regular expression" |
| + " (\"" + regex + "\")." |
| + " First example of a matching message is: " |
| + matches.iterator().next()); |
| } catch (RESyntaxException e) { |
| throw new IllegalArgumentException(e.toString()); |
| } |
| } |
| |
| /** |
| * To be called by the child. E.g.: |
| * <code> public static void main(String [] args) { main(TestBug375.class); |
| * } |
| * </code> |
| */ |
| public static void main(Class c) { |
| TestRunner.run(c); |
| } |
| |
| /** |
| * To be called by child. Figures out the class from the calling context. |
| */ |
| public static void main() { |
| String caller = new SecurityManager() { |
| public String toString() { |
| return getClassContext()[2].getName(); |
| } |
| }.toString(); |
| |
| try { |
| main(Class.forName(caller)); |
| } catch (ClassNotFoundException cnfe) { |
| throw new RuntimeException(cnfe.toString()); |
| } |
| } |
| |
| /** |
| * Returns the jar file in which the class is contained. |
| * |
| * @return the jar file, or none if the class is not in a jar |
| * @throws FileNotFoundException if the jar file cannot located |
| */ |
| public static File getJarFile(Class clazz) throws FileNotFoundException { |
| URL url = clazz.getResource(clazz.getName().substring( |
| clazz.getName().lastIndexOf(".") + 1) + ".class"); |
| if (url == null) |
| throw new FileNotFoundException(clazz.toString()); |
| |
| String file = url.getFile(); |
| if (file == null) |
| throw new FileNotFoundException(url.toString()); |
| int index = file.indexOf("!"); |
| if (index == -1) |
| throw new FileNotFoundException(file); |
| |
| file = file.substring(0, index); |
| file = file.substring("file:".length()); |
| |
| File f = new File(file); |
| if (!(f.isFile())) |
| throw new FileNotFoundException(file); |
| |
| return f.getAbsoluteFile(); |
| } |
| |
| /** |
| * The number of milliseconds each test case will have for a timeout. |
| */ |
| public void setTimeout(long timeout) { |
| _timeout = timeout; |
| } |
| |
| /** |
| * The number of milliseconds each test case will have for a timeout. |
| */ |
| public long getTimeout() { |
| return _timeout; |
| } |
| |
| /** |
| * A watchdog that just exits the JVM if a test has not completed in |
| * a certain amount of time. This speeds up the mechanism of determining |
| * if a timeout has occurred, since we can exit the entire test run |
| * if a test hasn't completed in a shorted amount of time than |
| * the global test timeout. |
| * |
| * @author Marc Prud'hommeaux |
| */ |
| private static class WatchdogThread extends Thread { |
| |
| private final long _timeoutms; |
| private long _endtime = -1; |
| private AbstractTestCase _curtest = null; |
| |
| public WatchdogThread() { |
| super("Kodo test case watchdog thread"); |
| setDaemon(true); |
| |
| int timeoutMin = new Integer |
| (System.getProperty("autobuild.testcase.timeout", "20")) |
| .intValue(); |
| |
| _timeoutms = timeoutMin * 60 * 1000; |
| } |
| |
| public void run() { |
| while (true) { |
| try { |
| sleep(200); |
| } catch (InterruptedException ie) { |
| } |
| |
| if (_endtime > 0 && System.currentTimeMillis() > _endtime) { |
| Thread preTimeout = new Thread |
| ("Attempting pre-timeout for " + _curtest) { |
| public void run() { |
| _curtest.preTimeout(); |
| } |
| }; |
| preTimeout.start(); |
| |
| // wait a little while for the pre-timeout |
| // thread to complete |
| try { |
| preTimeout.join(10 * 1000); |
| } catch (Exception e) { |
| } |
| |
| // give it a few more seconds... |
| try { |
| sleep(5 * 1000); |
| } catch (Exception e) { |
| } |
| |
| // new endtime? resume... |
| if (System.currentTimeMillis() < _endtime) |
| continue; |
| |
| new Exception("test case " |
| + (_curtest != null ? _curtest.getName() |
| : "UNKNOWN") + " timed out after " |
| + _timeoutms + "ms").printStackTrace(); |
| |
| // also run "killall -QUIT java" to try to grab |
| // a stack trace |
| try { |
| Runtime.getRuntime().exec |
| (new String[]{ "killall", "-QUIT", "java" }); |
| } catch (Exception e) { |
| } |
| |
| try { |
| sleep(1000); |
| } catch (InterruptedException ie) { |
| } |
| |
| // now actually exit |
| System.exit(111); |
| } |
| } |
| } |
| |
| public synchronized void enteringTest(AbstractTestCase test) { |
| long timeout = test.getTimeout(); |
| if (timeout <= 0) |
| timeout = _timeoutms; |
| |
| _endtime = System.currentTimeMillis() + timeout; |
| _curtest = test; |
| |
| if (!isAlive()) |
| start(); |
| } |
| |
| public synchronized void leavingTest(AbstractTestCase test) { |
| _endtime = -1; |
| _curtest = null; |
| } |
| } |
| } |