/* | |
* 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; | |
} | |
} | |
} |