0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one
0003: * or more contributor license agreements. See the NOTICE file
0004: * distributed with this work for additional information
0005: * regarding copyright ownership. The ASF licenses this file
0006: * to you under the Apache License, Version 2.0 (the
0007: * "License"); you may not use this file except in compliance
0008: * with the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing,
0013: * software distributed under the License is distributed on an
0014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015: * KIND, either express or implied. See the License for the
0016: * specific language governing permissions and limitations
0017: * under the License.
0018: */
0019: package org.apache.openjpa.lib.test;
0020:
0021: import java.beans.BeanInfo;
0022: import java.beans.IntrospectionException;
0023: import java.beans.Introspector;
0024: import java.beans.PropertyDescriptor;
0025: import java.io.ByteArrayInputStream;
0026: import java.io.ByteArrayOutputStream;
0027: import java.io.File;
0028: import java.io.FileInputStream;
0029: import java.io.FileNotFoundException;
0030: import java.io.FileOutputStream;
0031: import java.io.IOException;
0032: import java.io.InputStream;
0033: import java.io.ObjectInputStream;
0034: import java.io.ObjectOutputStream;
0035: import java.io.PrintStream;
0036: import java.io.PrintWriter;
0037: import java.io.StringWriter;
0038: import java.lang.reflect.InvocationTargetException;
0039: import java.lang.reflect.Method;
0040: import java.math.BigDecimal;
0041: import java.math.BigInteger;
0042: import java.net.URL;
0043: import java.text.NumberFormat;
0044: import java.util.ArrayList;
0045: import java.util.Arrays;
0046: import java.util.Collection;
0047: import java.util.Collections;
0048: import java.util.Comparator;
0049: import java.util.Date;
0050: import java.util.HashMap;
0051: import java.util.Iterator;
0052: import java.util.LinkedList;
0053: import java.util.List;
0054: import java.util.ListIterator;
0055: import java.util.Map;
0056: import java.util.NoSuchElementException;
0057: import java.util.StringTokenizer;
0058:
0059: import junit.framework.TestCase;
0060: import junit.framework.TestResult;
0061: import junit.textui.TestRunner;
0062: import org.apache.regexp.RE;
0063: import org.apache.regexp.RESyntaxException;
0064: import org.apache.regexp.REUtil;
0065: import org.apache.tools.ant.AntClassLoader;
0066: import org.apache.tools.ant.Project;
0067: import org.apache.tools.ant.ProjectHelper;
0068: import org.apache.openjpa.lib.log.Log;
0069: import org.apache.openjpa.lib.log.LogFactoryImpl;
0070: import org.apache.openjpa.lib.util.Localizer;
0071:
0072: /**
0073: * TestCase framework to run various tests against solarmetric code.
0074: * This class contains various utility methods for the following functions:
0075: * <ul>
0076: * <li>Using multiple, isolated ClassLoaders</li>
0077: * <li>Running a test in multiple concurrent threads</li>
0078: * <li>Assertion helpers</li>
0079: * <li>Creating random Strings, numbers, etc.</li>
0080: * </ul>
0081: *
0082: * @author Marc Prud'hommeaux
0083: * @author Patrick Linskey
0084: */
0085: public abstract class AbstractTestCase extends TestCase {
0086:
0087: public static final String TEST_METHODS = System
0088: .getProperty(AbstractTestCase.class.getName()
0089: + ".testMethods");
0090: public static final long PLATFORM_ALL = 2 << 1;
0091: public static final long PLATFORM_UNKNOWN = 2 << 2;
0092:
0093: public static final String SKIP_TOKEN = "SOLARSKIP";
0094: public static final String SKIP_DELIMITER = "|";
0095:
0096: private static final Localizer _loc = Localizer
0097: .forPackage(AbstractTestCase.class);
0098:
0099: private Log log = null;
0100:
0101: private static Map _times = new HashMap();
0102:
0103: private static AbstractTestCase _lastTest = null;
0104:
0105: private static WatchdogThread _watchdog = new WatchdogThread();
0106: private long _timeout;
0107:
0108: /**
0109: * Constructor. Create a test case with the specified name.
0110: */
0111: public AbstractTestCase(String test) {
0112: super (test);
0113: }
0114:
0115: public AbstractTestCase() {
0116: }
0117:
0118: protected final Log getLog() {
0119: if (log == null)
0120: log = newLog();
0121: return log;
0122: }
0123:
0124: protected Log newLog() {
0125: // this implementation leaves much to be desired, as it just
0126: // creates a new LogFactoryImpl each time, and does not apply
0127: // any configurations.
0128: return new LogFactoryImpl().getLog(getLogName());
0129: }
0130:
0131: protected String getLogName() {
0132: return "com.solarmetric.Runtime";
0133: }
0134:
0135: /**
0136: * Called before the watchdog thread is about to kill the entire
0137: * JVM due to a test case's timeout. This method offers the
0138: * ability to try to resolve whatever contention is taking place
0139: * in the test. It will be given 10 seconds to try to end the
0140: * test peacefully before the watchdog exits the JVM.
0141: */
0142: protected void preTimeout() {
0143: }
0144:
0145: public void run(TestResult result) {
0146: if (skipTest()) {
0147: // keep track of the tests we skip so that we can get an
0148: // idea in the autobuild status
0149: System.err.println(SKIP_TOKEN + SKIP_DELIMITER
0150: + ("" + getClass().getName()) + "." + getName()
0151: + SKIP_DELIMITER);
0152: return;
0153: }
0154:
0155: if (_lastTest != null && _lastTest.getClass() != getClass()) {
0156: try {
0157: _lastTest.tearDownTestClass();
0158: } catch (Throwable t) {
0159: getLog().error(null, t);
0160: }
0161: }
0162:
0163: if (_lastTest == null || _lastTest.getClass() != getClass()) {
0164: try {
0165: setUpTestClass();
0166: } catch (Throwable t) {
0167: getLog().error(null, t);
0168: }
0169: }
0170:
0171: _lastTest = this ;
0172:
0173: // inform the watchdog thread that we are entering the test
0174: _watchdog.enteringTest(this );
0175: try {
0176: super .run(result);
0177: } finally {
0178: _watchdog.leavingTest(this );
0179: }
0180: }
0181:
0182: /**
0183: * If this test should be skipped given the current
0184: * environment, return <code>true</code>. This allows a unit test
0185: * class to disable test cases on a per-method granularity, and
0186: * prevents the test from showing up as a passed test just
0187: * because it was skipped.
0188: * For example, if a particular test case method should not be
0189: * run against a certain database, this method could check the
0190: * name of the test result and the current database configuration
0191: * in order to make the decision:
0192: * <p/>
0193: * <code> protected boolean skipTest() {
0194: * // don't run with pointbase: it uses a DataSource, which
0195: * // can't be translated into a JBoss DataSource configuration.
0196: * if ("testJBoss".equals(getName()) &&
0197: * getCurrentPlatform() == PLATFORM_POINTBASE)
0198: * return true;
0199: * }
0200: * </code>
0201: * If you want to disable execution of an entire test case
0202: * class for a given database, you might want to add the class to
0203: * the excluded test list in that database's properties file.
0204: */
0205: protected boolean skipTest() {
0206: if (TEST_METHODS != null && TEST_METHODS.length() > 0)
0207: return TEST_METHODS.indexOf(getName()) == -1;
0208:
0209: return false;
0210: }
0211:
0212: /**
0213: * This method is called before the first test in this test class
0214: * is executed.
0215: */
0216: public void setUpTestClass() throws Exception {
0217: }
0218:
0219: /**
0220: * This method is called after the last test in this test class
0221: * is executed. It can be used to do things like clean up
0222: * large, slow processes that may have been started.
0223: */
0224: public void tearDownTestClass() throws Exception {
0225: }
0226:
0227: public void tearDown() throws Exception {
0228: if ("true".equals(System.getProperty("meminfo")))
0229: printMemoryInfo();
0230:
0231: super .tearDown();
0232: }
0233:
0234: //////////////////////////
0235: // Generating random data
0236: //////////////////////////
0237:
0238: /**
0239: * Support method to get a random Integer for testing.
0240: */
0241: public static Integer randomInt() {
0242: return new Integer((int) (Math.random() * Integer.MAX_VALUE));
0243: }
0244:
0245: /**
0246: * Support method to get a random Character for testing.
0247: */
0248: public static Character randomChar() {
0249: char[] TEST_CHAR_ARRAY = new char[] { 'a', 'b', 'c', 'd', 'e',
0250: 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
0251: 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1',
0252: '2', '3', '4', '5', '6', '7', '8', '9' };
0253:
0254: return new Character(
0255: TEST_CHAR_ARRAY[(int) (Math.random() * TEST_CHAR_ARRAY.length)]);
0256: }
0257:
0258: /**
0259: * Support method to get a random Long for testing.
0260: */
0261: public static Long randomLong() {
0262: return new Long((long) (Math.random() * Long.MAX_VALUE));
0263: }
0264:
0265: /**
0266: * Support method to get a random Short for testing.
0267: */
0268: public static Short randomShort() {
0269: return new Short((short) (Math.random() * Short.MAX_VALUE));
0270: }
0271:
0272: /**
0273: * Support method to get a random Double for testing.
0274: */
0275: public static Double randomDouble() {
0276: return new Double(
0277: (double) (Math.round(Math.random() * 5000d)) / 1000d);
0278: }
0279:
0280: /**
0281: * Support method to get a random Float for testing.
0282: */
0283: public static Float randomFloat() {
0284: return new Float(
0285: (float) (Math.round(Math.random() * 5000f)) / 1000f);
0286: }
0287:
0288: /**
0289: * Support method to get a random Byte for testing.
0290: */
0291: public static Byte randomByte() {
0292: return new Byte((byte) (Math.random() * Byte.MAX_VALUE));
0293: }
0294:
0295: /**
0296: * Support method to get a random Boolean for testing.
0297: */
0298: public static Boolean randomBoolean() {
0299: return new Boolean(Math.random() > 0.5 ? true : false);
0300: }
0301:
0302: /**
0303: * Support method to get a random Date for testing.
0304: */
0305: public static Date randomDate() {
0306: long millis = (long) (Math.random() * System
0307: .currentTimeMillis());
0308:
0309: // round millis to the nearest 1000: this is because some
0310: // databases do not store the milliseconds correctly(e.g., MySQL).
0311: // This is a really a bug we should fix. FC #27.
0312: millis -= (millis % 1000);
0313:
0314: return new Date(millis);
0315: }
0316:
0317: /**
0318: * Support method to get a random String for testing.
0319: */
0320: public static String randomString() {
0321: // default to a small string, in case column sizes are
0322: // limited(such as with a string primary key)
0323: return randomString(50);
0324: }
0325:
0326: /**
0327: * Support method to get a random String for testing.
0328: */
0329: public static String randomString(int len) {
0330: StringBuffer buf = new StringBuffer();
0331: for (int i = 0; i < (int) (Math.random() * len) + 1; i++)
0332: buf.append(randomChar());
0333: return buf.toString();
0334: }
0335:
0336: /**
0337: * Support method to get a random clob for testing.
0338: */
0339: public static String randomClob() {
0340: StringBuffer sbuf = new StringBuffer();
0341: while (sbuf.length() < (5 * 1024)) { // at least 5K
0342: sbuf.append(randomString(1024));
0343: }
0344:
0345: return sbuf.toString();
0346: }
0347:
0348: /**
0349: * Support method to get a random BigInteger for testing.
0350: */
0351: public static BigInteger randomBigInteger() {
0352: // too many of our test databases don't support bigints > MAX_LONG:
0353: // I don't like it, but for now, let's only test below MAX_LONG
0354: BigInteger lng = new BigInteger(
0355: ((long) (Math.random() * Long.MAX_VALUE)) + "");
0356:
0357: BigInteger multiplier = new BigInteger("1");
0358: // (1 + (int)(Math.random() * 10000)) + "");
0359: if (Math.random() < 0.5)
0360: multiplier = multiplier.multiply(new BigInteger("-1"));
0361:
0362: return lng.multiply(multiplier);
0363: }
0364:
0365: /**
0366: * Support method to get a random BigDecimal for testing.
0367: */
0368: public static BigDecimal randomBigDecimal() {
0369: BigInteger start = randomBigInteger();
0370: String str = start.toString();
0371: // truncate off the last 8 digits: we still get some
0372: // overflows with lame databases.
0373: for (int i = 0; i < 8; i++)
0374: if (str.length() > 2)
0375: str = str.substring(0, str.length() - 1);
0376: start = new BigInteger(str);
0377:
0378: String val = start + "." + ((int) (Math.random() * 10))
0379: + ((int) (Math.random() * 10))
0380: + ((int) (Math.random() * 10))
0381: + ((int) (Math.random() * 10))
0382: + ((int) (Math.random() * 10))
0383: + ((int) (Math.random() * 10))
0384: + ((int) (Math.random() * 10))
0385: + ((int) (Math.random() * 10))
0386: + ((int) (Math.random() * 10))
0387: + ((int) (Math.random() * 10));
0388:
0389: return new BigDecimal(val);
0390: }
0391:
0392: /**
0393: * Support method to get a random blob for testing.
0394: */
0395: public static byte[] randomBlob() {
0396: // up to 100K blob
0397: byte[] blob = new byte[(int) (Math.random() * 1024 * 100)];
0398: for (int i = 0; i < blob.length; i++)
0399: blob[i] = randomByte().byteValue();
0400:
0401: return blob;
0402: }
0403:
0404: /**
0405: * Invoke setters for pimitives and primitive wrappers on the
0406: * specified object.
0407: */
0408: public static Object randomizeBean(Object bean)
0409: throws IntrospectionException, IllegalAccessException,
0410: InvocationTargetException {
0411: BeanInfo info = Introspector.getBeanInfo(bean.getClass());
0412: PropertyDescriptor[] props = info.getPropertyDescriptors();
0413: for (int i = 0; i < props.length; i++) {
0414: Method write = props[i].getWriteMethod();
0415: if (write == null)
0416: continue;
0417:
0418: Class[] params = write.getParameterTypes();
0419: if (params == null || params.length != 1)
0420: continue;
0421:
0422: Class paramType = params[0];
0423: Object arg = null;
0424:
0425: if (paramType == boolean.class
0426: || paramType == Boolean.class)
0427: arg = randomBoolean();
0428: else if (paramType == byte.class || paramType == Byte.class)
0429: arg = randomByte();
0430: else if (paramType == char.class
0431: || paramType == Character.class)
0432: arg = randomChar();
0433: else if (paramType == short.class
0434: || paramType == Short.class)
0435: arg = randomShort();
0436: else if (paramType == int.class
0437: || paramType == Integer.class)
0438: arg = randomInt();
0439: else if (paramType == long.class || paramType == Long.class)
0440: arg = randomLong();
0441: else if (paramType == double.class
0442: || paramType == Double.class)
0443: arg = randomDouble();
0444: else if (paramType == float.class
0445: || paramType == Float.class)
0446: arg = randomFloat();
0447: else if (paramType == String.class)
0448: arg = randomString();
0449: else if (paramType == BigInteger.class)
0450: arg = randomBigInteger();
0451: else if (paramType == BigDecimal.class)
0452: arg = randomBigDecimal();
0453: else if (paramType == Date.class)
0454: arg = randomDate();
0455:
0456: if (arg != null)
0457: write.invoke(bean, new Object[] { arg });
0458: }
0459:
0460: return bean;
0461: }
0462:
0463: /**
0464: * Utility method to start a profile.
0465: *
0466: * @see #endProfile(String)
0467: */
0468: public void startProfile(String name) {
0469: _times.put(name, new Long(System.currentTimeMillis()));
0470: }
0471:
0472: /**
0473: * Utility to end the profile and print out the time. Example usage:
0474: * <p/>
0475: * <pre><code> startProfile("Some long task"); doSomeLongTask();
0476: * endProfile("Some long task");
0477: * </code></pre>
0478: *
0479: * @param name
0480: * @return the amount of time that this profile invocation took, or
0481: * -1 if <code>name</code> was never started.
0482: */
0483: public long endProfile(String name) {
0484: Long time = (Long) _times.remove(name);
0485:
0486: long elapsed = -1;
0487: if (time != null)
0488: elapsed = System.currentTimeMillis() - time.longValue();
0489:
0490: getLog().info(
0491: _loc.get("profile-info", name, (time == null ? "???"
0492: : "" + elapsed)));
0493: return elapsed;
0494: }
0495:
0496: /////////////////////////
0497: // ClassLoader functions
0498: /////////////////////////
0499:
0500: /**
0501: * Create a ClassLoader that will not use the parent
0502: * ClassLoader to resolve classes. This is useful for
0503: * testing interactions between Kodo in running
0504: * in ClassLoaderA and instances in ClassLoaderB.
0505: */
0506: public ClassLoader createIsolatedClassLoader() {
0507: return new IsolatedClassLoader();
0508: }
0509:
0510: public NestedClassLoader createNestedClassLoader() {
0511: return new NestedClassLoader(false);
0512: }
0513:
0514: public NestedClassLoader createNestedParentClassLoader() {
0515: return new NestedClassLoader(true);
0516: }
0517:
0518: /**
0519: * Reload the specified class in an isolated ClassLoader.
0520: *
0521: * @param target the target class to load
0522: * @return the Class as reloaded in an new ClassLoader
0523: */
0524: public Class isolate(Class target) throws ClassNotFoundException {
0525: Class result = isolate(target.getName());
0526: assertTrue(result != target);
0527: assertNotEquals(result, target);
0528: assertTrue(result.getClassLoader() != target.getClassLoader());
0529: return result;
0530: }
0531:
0532: public Class isolate(String target) throws ClassNotFoundException {
0533: ClassLoader il = createIsolatedClassLoader();
0534: Class result = il.loadClass(target);
0535: assertEquals(result.getName(), target);
0536:
0537: return result;
0538: }
0539:
0540: public Class nest(Class target) throws ClassNotFoundException {
0541: ClassLoader il = createNestedClassLoader();
0542: Class result = il.loadClass(target.getName());
0543: assertTrue(result != target);
0544: assertNotEquals(result, target);
0545: assertTrue(result.getClassLoader() != target.getClassLoader());
0546: assertEquals(result.getName(), target.getName());
0547:
0548: return result;
0549: }
0550:
0551: public Object isolateNew(Class target)
0552: throws ClassNotFoundException, IllegalAccessException,
0553: InstantiationException {
0554: return isolate(target).newInstance();
0555: }
0556:
0557: private static class NestedClassLoader extends AntClassLoader {
0558:
0559: public NestedClassLoader(boolean useParent) {
0560: super (ClassLoader.getSystemClassLoader(), useParent);
0561:
0562: for (StringTokenizer cltok = new StringTokenizer(System
0563: .getProperty("java.class.path"), File.pathSeparator); cltok
0564: .hasMoreTokens();) {
0565: String path = cltok.nextToken();
0566:
0567: // only load test paths, not jar files
0568: if (path.indexOf(".jar") != -1)
0569: continue;
0570: if (path.indexOf(".zip") != -1)
0571: continue;
0572:
0573: addPathElement(path);
0574: }
0575:
0576: try {
0577: if (!useParent) {
0578: assertTrue(loadClass(
0579: AbstractTestCase.class.getName())
0580: .getClassLoader() != AbstractTestCase.class
0581: .getClassLoader());
0582: }
0583: } catch (ClassNotFoundException cnfe) {
0584: fail(cnfe.toString());
0585: }
0586: }
0587:
0588: public Class findClass(String name)
0589: throws ClassNotFoundException {
0590: // don't isolate PC and related classes in kodo.enhnace
0591: if (name.indexOf(".enhance.") != -1)
0592: throw new ClassNotFoundException(name);
0593: if (name.indexOf("/enhance/") != -1)
0594: throw new ClassNotFoundException(name);
0595: return super .findClass(name);
0596: }
0597: }
0598:
0599: /**
0600: * A ClassLoader that is completely isolated with respect to
0601: * any classes that are loaded in the System ClassLoader.
0602: *
0603: * @author Marc Prud'hommeaux
0604: */
0605: private static class IsolatedClassLoader extends NestedClassLoader {
0606:
0607: public IsolatedClassLoader() {
0608: super (false);
0609: setIsolated(false);
0610: }
0611: }
0612:
0613: ///////////////
0614: // Collections
0615: ///////////////
0616:
0617: /**
0618: * Validate that the specified {@link Collection} fulfills the
0619: * Collection contract as specified by the Collections API.
0620: * <p/>
0621: * <strong>Note</strong>: does not validate mutable operations
0622: */
0623: public static void validateCollection(Collection collection) {
0624: int size = collection.size();
0625: int iterated = 0;
0626: // ensure we can walk along the iterator
0627: for (Iterator i = collection.iterator(); i.hasNext();) {
0628: iterated++;
0629: i.next();
0630: }
0631:
0632: // ensure the number of values iterated is the same as the list size
0633: assertEquals(size, iterated);
0634:
0635: // also validate the list
0636: if (collection instanceof List) {
0637: List ll = new ArrayList();
0638: for (int i = 0; i < 100; i++)
0639: ll.add(new Integer(i));
0640: validateList((List) ll);
0641: validateList((List) collection);
0642: }
0643: }
0644:
0645: /**
0646: * Validate that the specified {@link List} fulfills the
0647: * List contract as specified by the Collections API.
0648: * <p/>
0649: * <strong>Note</strong>: does not validate mutable operations
0650: */
0651: public static void validateList(List list) {
0652: Object[] coreValues = list.toArray();
0653: Object[] values1 = new Object[list.size()];
0654: Object[] values2 = new Object[list.size()];
0655: Object[] values3 = new Object[list.size()];
0656: Object[] values4 = new Object[list.size()];
0657:
0658: // fill sequential index access list
0659: for (int i = 0; i < list.size(); i++)
0660: values1[i] = list.get(i);
0661:
0662: // fill sequential list
0663: int index = 0;
0664: ListIterator iter;
0665: for (iter = list.listIterator(0); iter.hasNext();) {
0666: assertEquals(index, iter.nextIndex());
0667: assertEquals(index, iter.previousIndex() + 1);
0668: values2[index] = iter.next();
0669: assertTrue(list.contains(values2[index]));
0670: index++;
0671: }
0672:
0673: // ensure NoSuchElementException is thrown as appropriate
0674: try {
0675: iter.next();
0676: fail("next() should have resulted in a NoSuchElementException");
0677: } catch (NoSuchElementException e) {
0678: } // as expected
0679:
0680: // fill reverse sequential list
0681: int back = 0;
0682: for (iter = list.listIterator(list.size()); iter.hasPrevious();) {
0683: assertEquals(index, iter.previousIndex() + 1);
0684: assertEquals(index, iter.nextIndex());
0685: values3[--index] = iter.previous();
0686: back++;
0687: }
0688: assertEquals(list.size(), back);
0689:
0690: // ensure NoSuchElementException is thrown as appropriate
0691: try {
0692: iter.previous();
0693: fail("previous() should have resulted in a "
0694: + "NoSuchElementException");
0695: } catch (NoSuchElementException e) {
0696: } // as expected
0697:
0698: // fill random access list
0699: List indices = new LinkedList();
0700: for (int i = 0; i < list.size(); i++)
0701: indices.add(new Integer(i));
0702:
0703: for (int i = 0; i < list.size(); i++) {
0704: int rand = (int) (Math.random() * indices.size());
0705: Integer randIndex = (Integer) indices.remove(rand);
0706: values4[randIndex.intValue()] = list.get(randIndex
0707: .intValue());
0708: }
0709:
0710: assertEquals(Arrays.asList(coreValues), Arrays.asList(values1));
0711: assertIdentical(Arrays.asList(coreValues), Arrays
0712: .asList(values1));
0713: assertEquals(Arrays.asList(coreValues), Arrays.asList(values2));
0714: assertIdentical(Arrays.asList(coreValues), Arrays
0715: .asList(values2));
0716: assertEquals(Arrays.asList(coreValues), Arrays.asList(values4));
0717: assertIdentical(Arrays.asList(coreValues), Arrays
0718: .asList(values4));
0719: assertEquals(Arrays.asList(coreValues), Arrays.asList(values3));
0720: assertIdentical(Arrays.asList(coreValues), Arrays
0721: .asList(values3));
0722: }
0723:
0724: /**
0725: * Assert that the given List contain the exact same
0726: * elements. This is different than the normal List contract, which
0727: * states that list1.equals(list2) if each element e1.equals(e2).
0728: * This method asserts that e1 == n2.
0729: */
0730: public static void assertIdentical(List c1, List c2) {
0731: assertEquals(c1.size(), c2.size());
0732: for (Iterator i1 = c1.iterator(), i2 = c2.iterator(); i1
0733: .hasNext()
0734: && i2.hasNext();)
0735: assertTrue(i1.next() == i2.next());
0736: }
0737:
0738: /**
0739: * Assert that the collection parameter is already ordered
0740: * according to the specified comparator.
0741: */
0742: public void assertOrdered(Collection c, Comparator comp) {
0743: List l1 = new LinkedList(c);
0744: List l2 = new LinkedList(c);
0745: assertEquals(l1, l2);
0746: Collections.sort(l2, comp);
0747: assertEquals(l1, l2);
0748: Collections.sort(l1, comp);
0749: assertEquals(l1, l2);
0750: }
0751:
0752: ////////////////////
0753: // Assertion Helpers
0754: ////////////////////
0755:
0756: public void assertNotEquals(Object a, Object b) {
0757: if (a == null && b != null)
0758: return;
0759: if (a != null && b == null)
0760: return;
0761: if (!(a.equals(b)))
0762: return;
0763: if (!(b.equals(a)))
0764: return;
0765:
0766: fail("expected !<" + a + ">.equals(<" + b + ">)");
0767: }
0768:
0769: public void assertSize(int size, Object ob) {
0770: if (ob == null) {
0771: assertEquals(size, 0);
0772: return;
0773: }
0774:
0775: if (ob instanceof Collection)
0776: ob = ((Collection) ob).iterator();
0777: if (ob instanceof Iterator) {
0778: Iterator i = (Iterator) ob;
0779: int count = 0;
0780: while (i.hasNext()) {
0781: count++;
0782: i.next();
0783: }
0784:
0785: assertEquals(size, count);
0786: } else
0787: fail("assertSize: expected Collection, Iterator, "
0788: + "Query, or Extent, but got "
0789: + ob.getClass().getName());
0790: }
0791:
0792: /////////////////////
0793: // Generic utilities
0794: /////////////////////
0795:
0796: public void copy(File from, File to) throws IOException {
0797: copy(new FileInputStream(from), to);
0798: }
0799:
0800: public void copy(InputStream in, File to) throws IOException {
0801: FileOutputStream fout = new FileOutputStream(to);
0802:
0803: byte[] b = new byte[1024];
0804:
0805: for (int n = 0; (n = in.read(b)) != -1;)
0806: fout.write(b, 0, n);
0807: }
0808:
0809: /**
0810: * Print out information on memory usage.
0811: */
0812: public void printMemoryInfo() {
0813: Runtime rt = Runtime.getRuntime();
0814: long total = rt.totalMemory();
0815: long free = rt.freeMemory();
0816: long used = total - free;
0817:
0818: NumberFormat nf = NumberFormat.getInstance();
0819: getLog().warn(
0820: _loc.get("mem-info", nf.format(used), nf.format(total),
0821: nf.format(free)));
0822: }
0823:
0824: /**
0825: * Return a list of all values iterated by the given iterator.
0826: */
0827: public static List iteratorToList(Iterator i) {
0828: LinkedList list = new LinkedList();
0829: while (i.hasNext())
0830: list.add(i.next());
0831: return list;
0832: }
0833:
0834: /**
0835: * Return an array of the objects iterated by the given iterator.
0836: */
0837: public static Object[] iteratorToArray(Iterator i, Class[] clazz) {
0838: return iteratorToList(i).toArray(clazz);
0839: }
0840:
0841: /**
0842: * Run ant on the specified build file.
0843: *
0844: * @param buildFile the build file to use
0845: * @param target the name of the target to invoke
0846: */
0847: public void ant(File buildFile, String target) {
0848: assertTrue(buildFile.isFile());
0849:
0850: Project project = new Project();
0851: project.init();
0852: project
0853: .setUserProperty("ant.file", buildFile
0854: .getAbsolutePath());
0855: ProjectHelper.configureProject(project, buildFile);
0856: project.executeTarget(target);
0857: }
0858:
0859: /**
0860: * Serialize and deserialize the object.
0861: *
0862: * @param validateEquality make sure the hashCode and equals
0863: * methods hold true
0864: */
0865: public static Object roundtrip(Object orig, boolean validateEquality)
0866: throws IOException, ClassNotFoundException {
0867: assertNotNull(orig);
0868:
0869: ByteArrayOutputStream bout = new ByteArrayOutputStream();
0870: ObjectOutputStream out = new ObjectOutputStream(bout);
0871: out.writeObject(orig);
0872: ByteArrayInputStream bin = new ByteArrayInputStream(bout
0873: .toByteArray());
0874: ObjectInputStream in = new ObjectInputStream(bin);
0875: Object result = in.readObject();
0876:
0877: if (validateEquality) {
0878: assertEquals(orig.hashCode(), result.hashCode());
0879: assertEquals(orig, result);
0880: }
0881:
0882: return result;
0883: }
0884:
0885: /**
0886: * @return true if the specified input matches the regular expression regex.
0887: */
0888: public static boolean matches(String regex, String input)
0889: throws RESyntaxException {
0890: RE re = REUtil.createRE(regex);
0891: return re.match(input);
0892: }
0893:
0894: public static void assertMatches(String regex, String input) {
0895: try {
0896: if (!(matches(regex, input)))
0897: fail("Expected regular expression: <" + regex + ">"
0898: + " did not match: <" + input + ">");
0899: } catch (RESyntaxException e) {
0900: throw new IllegalArgumentException(e.toString());
0901: }
0902: }
0903:
0904: public static void assertNotMatches(String regex, String input) {
0905: try {
0906: if (matches(regex, input))
0907: fail("Regular expression: <" + regex + ">"
0908: + " should not match: <" + input + ">");
0909: } catch (RESyntaxException e) {
0910: throw new IllegalArgumentException(e.toString());
0911: }
0912: }
0913:
0914: /**
0915: * Check the list if strings and return the ones that match
0916: * the specified match.
0917: */
0918: public static List matches(String regex, Collection input)
0919: throws RESyntaxException {
0920: List matches = new ArrayList();
0921: for (Iterator i = input.iterator(); i.hasNext();) {
0922: String check = (String) i.next();
0923: if (matches(regex, check))
0924: matches.add(check);
0925: }
0926:
0927: return matches;
0928: }
0929:
0930: /**
0931: * Assert that the specified collection of Strings contains at least
0932: * one string that matches the specified regular expression.
0933: */
0934: public static void assertMatches(String regex, Collection input) {
0935: try {
0936: if (matches(regex, input).size() == 0)
0937: fail("The specified list of size " + input.size()
0938: + " did not contain any strings that match the"
0939: + " specified regular expression(\"" + regex
0940: + "\")");
0941: } catch (RESyntaxException e) {
0942: throw new IllegalArgumentException(e.toString());
0943: }
0944: }
0945:
0946: /**
0947: * Assert that the specified collection of Strings does not match
0948: * the specified regular expression.
0949: */
0950: public static void assertNotMatches(String regex, Collection input) {
0951: try {
0952: List matches;
0953:
0954: if (((matches = matches(regex, input))).size() > 0)
0955: fail("The specified list of size "
0956: + input.size()
0957: + " did contain one or more strings that matchs the"
0958: + " specified illegal regular expression"
0959: + " (\"" + regex + "\")."
0960: + " First example of a matching message is: "
0961: + matches.iterator().next());
0962: } catch (RESyntaxException e) {
0963: throw new IllegalArgumentException(e.toString());
0964: }
0965: }
0966:
0967: /**
0968: * To be called by the child. E.g.:
0969: * <code> public static void main(String [] args) { main(TestBug375.class);
0970: * }
0971: * </code>
0972: */
0973: public static void main(Class c) {
0974: TestRunner.run(c);
0975: }
0976:
0977: /**
0978: * To be called by child. Figures out the class from the calling context.
0979: */
0980: public static void main() {
0981: String caller = new SecurityManager() {
0982: public String toString() {
0983: return getClassContext()[2].getName();
0984: }
0985: }.toString();
0986:
0987: try {
0988: main(Class.forName(caller));
0989: } catch (ClassNotFoundException cnfe) {
0990: throw new RuntimeException(cnfe.toString());
0991: }
0992: }
0993:
0994: /**
0995: * Returns the jar file in which the class is contained.
0996: *
0997: * @return the jar file, or none if the class is not in a jar
0998: * @throws FileNotFoundException if the jar file cannot located
0999: */
1000: public static File getJarFile(Class clazz)
1001: throws FileNotFoundException {
1002: URL url = clazz.getResource(clazz.getName().substring(
1003: clazz.getName().lastIndexOf(".") + 1)
1004: + ".class");
1005: if (url == null)
1006: throw new FileNotFoundException(clazz.toString());
1007:
1008: String file = url.getFile();
1009: if (file == null)
1010: throw new FileNotFoundException(url.toString());
1011: int index = file.indexOf("!");
1012: if (index == -1)
1013: throw new FileNotFoundException(file);
1014:
1015: file = file.substring(0, index);
1016: file = file.substring("file:".length());
1017:
1018: File f = new File(file);
1019: if (!(f.isFile()))
1020: throw new FileNotFoundException(file);
1021:
1022: return f.getAbsoluteFile();
1023: }
1024:
1025: /**
1026: * The number of milliseconds each test case will have for a timeout.
1027: */
1028: public void setTimeout(long timeout) {
1029: _timeout = timeout;
1030: }
1031:
1032: /**
1033: * The number of milliseconds each test case will have for a timeout.
1034: */
1035: public long getTimeout() {
1036: return _timeout;
1037: }
1038:
1039: /**
1040: * A watchdog that just exits the JVM if a test has not completed in
1041: * a certain amount of time. This speeds up the mechanism of determining
1042: * if a timeout has occurred, since we can exit the entire test run
1043: * if a test hasn't completed in a shorted amount of time than
1044: * the global test timeout.
1045: *
1046: * @author Marc Prud'hommeaux
1047: */
1048: private static class WatchdogThread extends Thread {
1049:
1050: private final long _timeoutms;
1051: private long _endtime = -1;
1052: private AbstractTestCase _curtest = null;
1053:
1054: public WatchdogThread() {
1055: super ("Kodo test case watchdog thread");
1056: setDaemon(true);
1057:
1058: int timeoutMin = new Integer(System.getProperty(
1059: "autobuild.testcase.timeout", "20")).intValue();
1060:
1061: _timeoutms = timeoutMin * 60 * 1000;
1062: }
1063:
1064: public void run() {
1065: while (true) {
1066: try {
1067: sleep(200);
1068: } catch (InterruptedException ie) {
1069: }
1070:
1071: if (_endtime > 0
1072: && System.currentTimeMillis() > _endtime) {
1073: Thread preTimeout = new Thread(
1074: "Attempting pre-timeout for " + _curtest) {
1075: public void run() {
1076: _curtest.preTimeout();
1077: }
1078: };
1079: preTimeout.start();
1080:
1081: // wait a little while for the pre-timeout
1082: // thread to complete
1083: try {
1084: preTimeout.join(10 * 1000);
1085: } catch (Exception e) {
1086: }
1087:
1088: // give it a few more seconds...
1089: try {
1090: sleep(5 * 1000);
1091: } catch (Exception e) {
1092: }
1093:
1094: // new endtime? resume...
1095: if (System.currentTimeMillis() < _endtime)
1096: continue;
1097:
1098: new Exception("test case "
1099: + (_curtest != null ? _curtest.getName()
1100: : "UNKNOWN") + " timed out after "
1101: + _timeoutms + "ms").printStackTrace();
1102:
1103: // also run "killall -QUIT java" to try to grab
1104: // a stack trace
1105: try {
1106: Runtime.getRuntime().exec(
1107: new String[] { "killall", "-QUIT",
1108: "java" });
1109: } catch (Exception e) {
1110: }
1111:
1112: try {
1113: sleep(1000);
1114: } catch (InterruptedException ie) {
1115: }
1116:
1117: // now actually exit
1118: System.exit(111);
1119: }
1120: }
1121: }
1122:
1123: public synchronized void enteringTest(AbstractTestCase test) {
1124: long timeout = test.getTimeout();
1125: if (timeout <= 0)
1126: timeout = _timeoutms;
1127:
1128: _endtime = System.currentTimeMillis() + timeout;
1129: _curtest = test;
1130:
1131: if (!isAlive())
1132: start();
1133: }
1134:
1135: public synchronized void leavingTest(AbstractTestCase test) {
1136: _endtime = -1;
1137: _curtest = null;
1138: }
1139: }
1140: }
|