/* * Licensed to the SYSTAP, LLC under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * SYSTAP, LLC 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 junit.framework; // Test resource loader. import java.io.Externalizable; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.Random; import java.util.TreeMap; import junit.util.PropertyUtil; import org.apache.log4j.Level; import org.apache.log4j.Logger; /** * Extension of {@link TestCase} that supports logging, loading test * resources, and hierarchical properties.<p> * * Note: When using Maven (or Ant), you need to be aware that that * your <code>project.xml</code> has an impact on which files are * recognized as tests to be submitted to a test runner. For example, * it is common practice that all files beginning or ending with * "Test" will be submitted to JUnit, though I suggest that the * practice of naming things "FooTestCase" will serve you better. * Also note that the maven test plugin explictly skips files whose * name matches the pattern "*AbstractTestSuite".<p> */ abstract public class TestCase2 extends TestCase { public TestCase2() { super(); } public TestCase2(String name) { super(name); } /** * The default {@link Logger} for this class, which is named * "junit.framework.Test". */ protected static final Logger log = Logger.getLogger ( junit.framework.Test.class ); /** * This convenience method provides a version of {@link #fail( * String Message )} that also excepts a {@link Throwable} cause * for the test failure.<p> */ static public void fail ( String message, Throwable t ) { AssertionFailedError ex = new AssertionFailedError ( message ); ex.initCause( t ); throw ex; } //************************************************************ //************************************************************ //************************************************************ static public void assertEquals( boolean[] expected, boolean[] actual ) { assertEquals( null, expected, actual ); } /** * Compares arrays of primitive values. */ static public void assertEquals( String msg, boolean[] expected, boolean[] actual ) { if( msg == null ) { msg = ""; } else { msg = msg + " : "; } if( expected == actual ) { return; } else if( expected == null && actual != null ) { fail( msg+"Expected a null array." ); } else if( expected != null && actual == null ) { fail( msg+"Not expecting a null array." ); } else if (expected.length != actual.length) { /* * Only do message construction if we know that the assert will * fail. */ assertEquals(msg + "length differs.", expected.length, actual.length); } for( int i=0; i<expected.length; i++ ) { if (expected[i] != actual[i]) { /* * Only do message construction if we know that the assert will * fail. */ assertEquals(msg + "values differ: index=" + i, expected[i], actual[i]); } } } /** * <p> * Compares byte[]s by value (not reference). * </p> * <p> * Note: This method will only be invoked if both arguments can be typed as * byte[] by the compiler. If either argument is not strongly typed, you * MUST case it to a byte[] or {@link #assertEquals(Object, Object)} will be * invoked instead. * </p> * * @param expected * @param actual */ static public void assertEquals( byte[] expected, byte[] actual ) { assertEquals( null, expected, actual ); } /** * <p> * Compares byte[]s by value (not reference). * </p> * <p> * Note: This method will only be invoked if both arguments can be typed as * byte[] by the compiler. If either argument is not strongly typed, you * MUST case it to a byte[] or {@link #assertEquals(Object, Object)} will be * invoked instead. * </p> * * @param msg * @param expected * @param actual */ static public void assertEquals( String msg, byte[] expected, byte[] actual ) { if( msg == null ) { msg = ""; } else { msg = msg + " : "; } if( expected == null && actual == null ) { return; } if( expected == null && actual != null ) { fail( msg+"Expected a null array." ); } if( expected != null && actual == null ) { fail( msg+"Not expecting a null array." ); } if (expected.length != actual.length) { /* * Only do message construction if we know that the assert will * fail. */ assertEquals(msg + "length differs.", expected.length, actual.length); } for( int i=0; i<expected.length; i++ ) { if (expected[i] != actual[i]) { /* * Only do the message construction if we know that the assert * will fail. */ assertEquals(msg + "values differ: index=" + i, expected[i], actual[i]); } } } static public void assertEquals( char[] expected, char[] actual ) { assertEquals( null, expected, actual ); } static public void assertEquals( String msg, char[] expected, char[] actual ) { if( msg == null ) { msg = ""; } else { msg = msg + " : "; } if( expected == null && actual == null ) { return; } if( expected == null && actual != null ) { fail( msg+"Expected a null array." ); } if( expected != null && actual == null ) { fail( msg+"Not expecting a null array." ); } if (expected.length != actual.length) { /* * Only do message construction if we know that the assert will * fail. */ assertEquals(msg + "length differs.", expected.length, actual.length); } for( int i=0; i<expected.length; i++ ) { if (expected[i] != actual[i]) { /* * Only do the message construction if we know that the assert * will fail. */ assertEquals(msg + "values differ: index=" + i, expected[i], actual[i]); } } } static public void assertEquals( short[] expected, short[] actual ) { assertEquals( null, expected, actual ); } static public void assertEquals( String msg, short[] expected, short[] actual ) { if( msg == null ) { msg = ""; } else { msg = msg + " : "; } if( expected == null && actual == null ) { return; } if( expected == null && actual != null ) { fail( msg+"Expected a null array." ); } if( expected != null && actual == null ) { fail( msg+"Not expecting a null array." ); } if (expected.length != actual.length) { /* * Only do message construction if we know that the assert will * fail. */ assertEquals(msg + "length differs.", expected.length, actual.length); } for( int i=0; i<expected.length; i++ ) { if (expected[i] != actual[i]) { /* * Only do the message construction if we know that the assert * will fail. */ assertEquals(msg + "values differ: index=" + i, expected[i], actual[i]); } } } static public void assertEquals( int[] expected, int[] actual ) { assertEquals( null, expected, actual ); } static public void assertEquals( String msg, int[] expected, int[] actual ) { if( msg == null ) { msg = ""; } else { msg = msg + " : "; } if( expected == null && actual == null ) { return; } if( expected == null && actual != null ) { fail( msg+"Expected a null array." ); } if( expected != null && actual == null ) { fail( msg+"Not expecting a null array." ); } if (expected.length != actual.length) { /* * Only do message construction if we know that the assert will * fail. */ assertEquals(msg + "length differs.", expected.length, actual.length); } for( int i=0; i<expected.length; i++ ) { if (expected[i] != actual[i]) { /* * Only do the message construction if we know that the assert * will fail. */ assertEquals(msg + "values differ: index=" + i, expected[i], actual[i]); } } } static public void assertEquals( long[] expected, long[] actual ) { assertEquals( null, expected, actual ); } static public void assertEquals( String msg, long[] expected, long[] actual ) { if( msg == null ) { msg = ""; } else { msg = msg + " : "; } if( expected == null && actual == null ) { return; } if( expected == null && actual != null ) { fail( msg+"Expected a null array." ); } if( expected != null && actual == null ) { fail( msg+"Not expecting a null array." ); } if (expected.length != actual.length) { /* * Only do message construction if we know that the assert will * fail. */ assertEquals(msg + "length differs.", expected.length, actual.length); } for( int i=0; i<expected.length; i++ ) { if (expected[i] != actual[i]) { /* * Only do the message construction if we know that the assert * will fail. */ assertEquals(msg + "values differ: index=" + i, expected[i], actual[i]); } } } static public void assertEquals( float[] expected, float[] actual ) { assertEquals( null, expected, actual ); } /** * TODO Use smarter floating point comparison and offer parameter * for controlling the floating point comparison. */ static public void assertEquals( String msg, float[] expected, float[] actual ) { if( msg == null ) { msg = ""; } else { msg = msg + " : "; } if( expected == null && actual == null ) { return; } if( expected == null && actual != null ) { fail( msg+"Expected a null array." ); } if( expected != null && actual == null ) { fail( msg+"Not expecting a null array." ); } if (expected.length != actual.length) { /* * Only do message construction if we know that the assert will * fail. */ assertEquals(msg + "length differs.", expected.length, actual.length); } for( int i=0; i<expected.length; i++ ) { float delta = 0f; try { assertEquals(expected[i], actual[i], delta); } catch (AssertionFailedError ex) { /* * Only do the message construction once the assertion is known * to fail. */ fail(msg + "values differ: index=" + i, ex); } } } static public void assertEquals( double[] expected, double[] actual ) { assertEquals( null, expected, actual ); } /** * TODO Use smarter floating point comparison and offer parameter * for controlling the floating point comparison. */ static public void assertEquals( String msg, double[] expected, double[] actual ) { if( msg == null ) { msg = ""; } else { msg = msg + " : "; } if( expected == null && actual == null ) { return; } if( expected == null && actual != null ) { fail( msg+"Expected a null array." ); } if( expected != null && actual == null ) { fail( msg+"Not expecting a null array." ); } if (expected.length != actual.length) { /* * Only do message construction if we know that the assert will * fail. */ assertEquals(msg + "length differs.", expected.length, actual.length); } for( int i=0; i<expected.length; i++ ) { double delta = 0.0d; try { assertEquals(expected[i], actual[i], delta); } catch (AssertionFailedError ex) { /* * Only do the message construction once the assertion is known * to fail. */ fail(msg + "values differ: index=" + i, ex); } } } static public void assertEquals( Object[] expected, Object[] actual ) { assertEquals( null, expected, actual ); } /** * Compares arrays of {@link Object}s. The length of the arrays * must agree, and each array element must agree. However the * class of the arrays does NOT need to agree, e.g., an Object[] * MAY compare as equals with a String[]. */ static public void assertEquals( String msg, Object[] expected, Object[] actual ) { if( msg == null ) { msg = ""; } else { msg = msg + " : "; } if( expected == null && actual == null ) { return; } if( expected == null && actual != null ) { fail( msg+"Expected a null array." ); } if( expected != null && actual == null ) { fail( msg+"Not expecting a null array." ); } if (expected.length != actual.length) { /* * Only do message construction if we know that the assert will * fail. */ assertEquals(msg + "length differs.", expected.length, actual.length); } for( int i=0; i<expected.length; i++ ) { try { assertEquals(expected[i], actual[i]); } catch (AssertionFailedError ex) { /* * Only do the message construction once the assertion is known * to fail. */ fail(msg + "values differ: index=" + i, ex); } } } //************************************************************ //************************************************************ //************************************************************ /** * Test helper that can correctly compare arrays of primitives and * arrays of objects as well as primitives and objects. */ static public void assertSameValue( Object expected, Object actual ) { assertSameValue( null, expected, actual ); } /** * Test helper that can correctly compare arrays of primitives and * arrays of objects as well as primitives and objects. * * TODO This will throw a {@link ClassCastException} if * <i>actual</i> is of a different primitive array type. Change * the code to throw {@link AssertionFailedError} instead. */ static public void assertSameValue( String msg, Object expected, Object actual ) { if( expected != null && expected.getClass().isArray() ) { if( expected.getClass().getComponentType().isPrimitive() ) { Class componentType = expected.getClass().getComponentType(); if( componentType.equals( Boolean.TYPE ) ) { assertEquals ( msg, (boolean[]) expected, (boolean[]) actual ); } else if( componentType.equals( Byte.TYPE ) ) { assertEquals ( msg, (byte[]) expected, (byte[]) actual ); } else if( componentType.equals( Character.TYPE ) ) { assertEquals ( msg, (char[]) expected, (char[]) actual ); } else if( componentType.equals( Short.TYPE ) ) { assertEquals ( msg, (short[]) expected, (short[]) actual ); } else if( componentType.equals( Integer.TYPE ) ) { assertEquals ( msg, (int[]) expected, (int[]) actual ); } else if( componentType.equals( Long.TYPE ) ) { assertEquals ( msg, (long[]) expected, (long[]) actual ); } else if( componentType.equals( Float.TYPE ) ) { assertEquals ( msg, (float[]) expected, (float[]) actual ); } else if( componentType.equals( Double.TYPE ) ) { assertEquals ( msg, (double[]) expected, (double[]) actual ); } else { throw new AssertionError(); } } else { assertTrue ( msg, java.util.Arrays.equals ( (Object[]) expected, (Object[]) actual ) ); } } else { assertEquals ( msg, expected, actual ); } } //************************************************************ //************************************************************ //************************************************************ /** * Method verifies that the <i>actual</i> {@link Iterator} * produces the expected objects in the expected order. Objects * are compared using {@link Object#equals( Object other )}. Errors * are reported if too few or too many objects are produced, etc. */ static public void assertSameIterator ( Object[] expected, Iterator actual ) { assertSameIterator ( "", expected, actual ); } /** * Method verifies that the <i>actual</i> {@link Iterator} * produces the expected objects in the expected order. Objects * are compared using {@link Object#equals( Object other )}. Errors * are reported if too few or too many objects are produced, etc. */ static public void assertSameIterator ( String msg, Object[] expected, Iterator actual ) { int i = 0; while( actual.hasNext() ) { if( i >= expected.length ) { fail( msg+": The iterator is willing to visit more than "+ expected.length+ " objects." ); } Object g = actual.next(); // if (!expected[i].equals(g)) { try { assertSameValue(expected[i],g); } catch(AssertionFailedError ex) { /* * Only do message construction if we know that the assert will * fail. */ fail(msg + ": Different objects at index=" + i + ": expected=" + expected[i] + ", actual=" + g); } i++; } if( i < expected.length ) { fail( msg+": The iterator SHOULD have visited "+expected.length+ " objects, but only visited "+i+ " objects." ); } } /** * Verifies that the iterator visits the specified objects in some arbitrary * ordering and that the iterator is exhausted once all expected objects * have been visited. The implementation uses a selection without * replacement "pattern". */ @SuppressWarnings("unchecked") static public void assertSameIteratorAnyOrder(final Object[] expected, final Iterator actual) { assertSameIteratorAnyOrder("", expected, actual); } /** * Verifies that the iterator visits the specified objects in some arbitrary * ordering and that the iterator is exhausted once all expected objects * have been visited. The implementation uses a selection without * replacement "pattern". */ @SuppressWarnings("unchecked") static public void assertSameIteratorAnyOrder(final String msg, final Object[] expected, final Iterator actual) { // Populate a map that we will use to realize the match and // selection without replacement logic. final int nrange = expected.length; java.util.Map range = new java.util.HashMap(); for (int j = 0; j < nrange; j++) { range.put(expected[j], expected[j]); } // Do selection without replacement for the objects visited by // iterator. for (int j = 0; j < nrange; j++) { if (!actual.hasNext()) { fail(msg + ": Index exhausted while expecting more object(s)" + ": index=" + j); } Object actualObject = actual.next(); if (range.remove(actualObject) == null) { fail("Object not expected" + ": index=" + j + ", object=" + actualObject); } } if (actual.hasNext()) { fail("Iterator will deliver too many objects."); } } // /** // * Verifies that the iterator visits the specified objects in some // * arbitrary ordering and that the iterator is exhausted once all // * expected objects have been visited. The implementation uses a // * selection without replacement "pattern". // */ // // static public void assertSameIteratorAnyOrder // ( Comparable[] expected, // Iterator actual // ) // { // // assertSameIteratorAnyOrder // ( "", // expected, // actual // ); // // } // // /** // * Verifies that the iterator visits the specified objects in some // * arbitrary ordering and that the iterator is exhausted once all // * expected objects have been visited. The implementation uses a // * selection without replacement "pattern". // * // * FIXME Write test cases for this one. // * // * FIXME Can we we this without requiring the objects to implement // * {@link Comparable}? // */ // // static public void assertSameIteratorAnyOrder // ( String msg, // Comparable[] expected, // Iterator actual // ) // { // // // Populate a map that we will use to realize the match and // // selection without replacement logic. // // final int nrange = expected.length; // // java.util.Map range = new java.util.TreeMap(); // // for( int j=0; j<nrange; j++ ) { // // range.put // ( expected[ j ], // expected[ j ] // ); // // } // // // Do selection without replacement for the objects visited by // // iterator. // // for( int j=0; j<nrange; j++ ) { // // if( ! actual.hasNext() ) { // // fail( msg+": Index exhausted while expecting more object(s)"+ // ": index="+j // ); // // } // // Object actualObject = actual.next(); // // if( range.remove( actualObject ) == null ) { // // fail( "Object not expected"+ // ": index="+j+ // ", object="+actualObject // ); // // } // // } // // if( actual.hasNext() ) { // // fail( "Iterator will deliver too many objects." // ); // // } // // } //************************************************************ //************************************************************ //************************************************************ /** * Test helper produces a random sequence of indices in the range [0:n-1] * suitable for visiting the elements of an array of n elements in a random * order. This is useful when you want to randomize the presentation of * elements from two or more arrays. For example, known keys and values can * be generated and their presentation order randomized by indexing with the * returned array. */ public static int[] getRandomOrder( final int n ) { final class Pair implements Comparable<Pair> { final public double r = Math.random(); final public int val; public Pair( int val ) {this.val = val;} public int compareTo(final Pair other) { if( this == other ) return 0; if( this.r < ((Pair)other).r ) return -1; else return 1; } } final Pair[] pairs = new Pair[ n ]; for( int i=0; i<n; i++ ) { pairs[ i ] = new Pair( i ); } java.util.Arrays.sort( pairs ); int order[] = new int[ n ]; for( int i=0; i<n; i++ ) { order[ i ] = pairs[ i ].val; } return order; } /** * Random number generator used by {@link #getNormalInt( int range * )}. */ private Random m_random = new Random(); /** * Returns a random integer normally distributed in [0:range]. */ public int getNormalInt( int range ) { final double bound = 3d; double rand = m_random.nextGaussian(); if( rand < -bound ) rand = -bound; else if( rand > bound ) rand = bound; rand = ( rand + bound ) / ( 2 * bound ); // normal distribution in [0:1]. if( rand < 0d ) rand = 0d; // make sure it is not slightly // negative int r = (int)( rand * range ); if( r > range ) { throw new AssertionError(); } return r; // TODO develop a test case for this method based on the code // in comments below and migrate into TestCase2 (junit-ext // package). // int[] r = new int[10000]; // for( int i=0; i<r.length; i++ ) { // r[ i ] = getNormalInt( len ); // } // java.util.Arrays.sort( r ); } /** * Returns a random but unique string of Unicode characters with a maximum * length of len and a minimum length. Only alphanumeric characters are * present in the string so you can form a unique string by appending a * delimiter and an one-up counter. * * @param len * The maximum length of the string. Each generated literal will * have a mean length of <code>len/2</code> and the lengths * will be distributed using a normal distribution (bell curve). * * @param id * A unique index used to obtain a unique string. Typically this * is a one up identifier. */ public String getRandomString(int len, int id) { // final String data = // "0123456789!@#$%^&*()`~-_=+[{]}\\|;:'\",<.>/?QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"; final String data = "0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"; int[] order = getRandomOrder(data.length()); int n = getNormalInt(len); StringBuilder sb = new StringBuilder(n); int index = 0; for (int i = 0; i < n; i++) { sb.append(data.charAt(order[index++])); if (index == order.length) { index = 0; } } sb.append(id); return sb.toString(); } /** * Used to create objects of random type. * * TODO support generation of random array types and dates. * * @author thompsonbry */ protected class RandomType { /** * An explicit NULL property value. */ // final public static short NULL = 0; final public static short BOOLEAN = 20; final public static short BYTE = 21; final public static short CHAR = 22; final public static short SHORT = 23; final public static short INT = 24; final public static short LONG = 25; final public static short FLOAT = 26; final public static short DOUBLE = 27; final public static short STRING = 28; /** * Java Object (must implement {@link Serializable} or {@link * Externalizable}). */ final public static short OBJECT = 30; /** * Array of Java objects (but not Java primitives). */ final public static short OBJECT_ARRAY = 31; /** * Array of Java primitives. */ final public static short BOOLEAN_ARRAY = 40; final public static short BYTE_ARRAY = 41; final public static short CHAR_ARRAY = 42; final public static short SHORT_ARRAY = 43; final public static short INT_ARRAY = 44; final public static short LONG_ARRAY = 45; final public static short FLOAT_ARRAY = 46; final public static short DOUBLE_ARRAY = 47; final public int[] randomType = new int[] { // LINK, // BLOB, BOOLEAN, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE, STRING, OBJECT, // OBJECT_ARRAY, // BOOLEAN_ARRAY, // BYTE_ARRAY, // CHAR_ARRAY, // SHORT_ARRAY, // INT_ARRAY, // LONG_ARRAY, // FLOAT_ARRAY, // DOUBLE_ARRAY, // NULL // Note: null at end to make easy to exclude. }; /** * Return the type code for the specified class. * * @param cls * A class. * @return The type code. * * @throws UnsupportedOperationException * if the class is not one of those that is supported by * {@link RandomType}. */ public int getType(Class cls) { if(cls==null) { throw new IllegalArgumentException(); } if(cls.equals(Boolean.class)) return BOOLEAN; if(cls.equals(Byte.class)) return BYTE; if(cls.equals(Character.class)) return CHAR; if(cls.equals(Short.class)) return SHORT; if(cls.equals(Integer.class)) return INT; if(cls.equals(Long.class)) return LONG; if(cls.equals(Float.class)) return FLOAT; if(cls.equals(Double.class)) return DOUBLE; if(cls.equals(String.class)) return STRING; if(cls.equals(Object.class)) return OBJECT; throw new UnsupportedOperationException("class="+cls); } /** * For random characters. */ static final private String alphabet = "0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"; /** * Returns an object with a random type and random value. * * @param rnd * The random# generator to use. * * @param allowNull * When true, a null object reference may be returned. * * @return The random object. */ public Object nextObject( Random rnd, boolean allowNull ) { int[] types = randomType; int range = types.length; if( ! allowNull ) range--; // exclude null from types. int type = types[ rnd.nextInt( range ) ]; return nextObject( rnd, type , allowNull ); } /** * Return an instance of the specified type with a random value. * * @param rnd * The random number generator. * * @param cls * The class of the random instance to be returned -or- * <code>null</code> to return an instance of any of the * classes that can be generated by this method. * * @param allowNull * When true, a null object reference may be returned. * * @return The random object. */ public Object nextObject(Random rnd, Class cls, boolean allowNull) { if(cls==null) { // random object type. return nextObject(rnd, allowNull); } else { // specific object type. return nextObject(rnd, getType(cls), allowNull); } } /** * Return an instance of the specified type with a random value. * * @param rnd * The random number generator. * * @param type * One of the values declared by this class. * * @param allowNull * When true, a null object reference may be returned. * * @return The random object. */ public Object nextObject( Random rnd, int type, boolean allowNull ) { Object obj = null; switch( type ) { // case NULL: { // return null; // } case BOOLEAN: { obj = ( rnd.nextBoolean() ? Boolean.TRUE : Boolean.FALSE ); break; } case BYTE: { obj = Byte.valueOf( (byte) ( rnd.nextInt( 255 ) - 127 ) ); break; } case CHAR: { obj = Character.valueOf( alphabet.charAt( rnd.nextInt( alphabet.length() ) ) ); break; } case SHORT: { obj = Short.valueOf( (short) rnd.nextInt() ); break; } case INT: { obj = Integer.valueOf( rnd.nextInt() ); break; } case LONG: { obj = Long.valueOf( rnd.nextLong() ); break; } case FLOAT: { obj = Float.valueOf( rnd.nextFloat() ); break; } case DOUBLE: { obj = Double.valueOf( rnd.nextDouble() ); break; } case STRING: { obj = getRandomString( 40, rnd.nextInt() ); break; } case OBJECT: { obj = getRandomString( 40, rnd.nextInt() ); break; } // OBJECT_ARRAY, // BOOLEAN_ARRAY, // BYTE_ARRAY, // CHAR_ARRAY, // SHORT_ARRAY, // INT_ARRAY, // LONG_ARRAY, // FLOAT_ARRAY, // DOUBLE_ARRAY default: { throw new AssertionError( "unknown type="+type ); } } return obj; } } protected RandomType _randomType = new RandomType(); /** * Returns an object with a random type and random value. * * @param allowNull When true, a null object reference may be * returned. * * @return The random object. */ public Object getRandomObject( boolean allowNull ) { return _randomType.nextObject( m_random, allowNull ); } /** * Returns an object with a random type and random value. * * @param rnd The random generator. * * @param allowNull When true, a null object reference may be * returned. * * @return The random object. */ public Object getRandomObject( Random rnd, boolean allowNull ) { return _randomType.nextObject( rnd, allowNull ); } //************************************************************ //****************** Loading Test Resources ****************** //************************************************************ /** * Returns an {@link InputStream} that may be used to read from a * test resource found along the CLASSPATH.<p> * * @param resourceName A resource identified by a series of '/' * delimited path components <strong>without</strong> a leading * '/'. Such resources are normally part of the JAR in which the * test is bundled and correspond to the package naming convention * with the exception that the '/' delimiter is used in place of * the '.' delimiter used in package names. * * @exception AssertionError If the resource could not be read for * any reason. * * @see ClassLoader#getResourceAsStream( String resourceName ) */ public InputStream getTestInputStream ( // Object context, String resourceName ) { // * @param context An arbitrary object whose {@link ClassLoader} // * will be used to locate the identified resource. // * Object context = this; log.info ( "Getting input stream for: "+resourceName ); try { /* Used to locate test resources. */ ClassLoader cl = context.getClass().getClassLoader(); if( cl == null ) { throw new AssertionError ( "Could not get ClassLoader"+ ": "+context.getClass() ); } InputStream is = cl.getResourceAsStream ( resourceName ); if( is == null ) { log.error ( "Could not locate resource: '"+resourceName+"'" ); throw new AssertionError ( "Could not locate resource: '"+resourceName+"'" ); } return is; } catch( Exception ex ) { log.error ( "Could not read from "+resourceName, ex ); throw new AssertionFailedError ( "Could not read from "+resourceName+ " : "+ex.toString() ); } } /** * Read a test character resource from along the CLASSPATH.<p> * * @param resourceName A resource identified by a series of path * components beginning <strong>without</strong> a leading '/'. * Such resources are normally part of the JAR in which the test * is bundled. * * @param encoding The character set encoding that will be used to * read the text resource. If <code>null</code> then the platform * default Java encoding will be used (which is not recommended as * it is non-portable).<p> * * @exception AssertionError If the resource could not be read for * any reason. * * @see ClassLoader#getResourceAsStream( String resourceName ) */ public String getTestResource ( // Object context, String resourceName, String encoding ) { log.info ( "Reading local test resource: "+resourceName ); try { InputStream is = getTestInputStream ( // context, resourceName ); // Note: There is no way to know the character encoding // for the resource we are reading. We have to assume // that it is the default Java character encoding unless // the caller has explicitly specified the encoding to be // used. final Reader r = ( encoding == null ? new InputStreamReader ( is ) : new InputStreamReader ( is, encoding ) ); try { final StringBuilder sb = new StringBuilder(); while( true ) { final int ch = r.read(); if( ch == -1 ) break; sb.append( (char)ch ); } if(log.isInfoEnabled()) log.info ( "Read "+sb.length()+" characters from "+ resourceName ); return sb.toString(); } finally { r.close(); } } catch( Exception ex ) { log.error ( "Could not read from "+resourceName, ex ); throw new AssertionFailedError ( "Could not read from "+resourceName+ " : "+ex.toString() ); } } /** * Convenience method for {@link #getTestResource( String * resourceName, String encoding )} that uses the platform * specific default encoding, which is NOT recommended since it is * non-portable. */ public String getTestResource ( String resourceName ) { return getTestResource ( resourceName, null ); } //************************************************************ //********************** Properties ************************** //************************************************************ /** * Reads in the configuration properties for the test from a * variety of resources and returns a properties hierarchy. If a * property name is not bound at the top-level of the returned * {@link Properties} object, then the lower levels of the * hierarchy are recursively searched.<p> * * The hierarchy is constructed from the following properties * files in the following order. The first property file in this * list corresponds to the top of the property hierarchy. The * last property file in this list corresponds to the bottom of * the property hierarchy. The property resources are:<ol> * * <li> The System properties specified using the -D argument on * the command line. * * <li> ${user.home}/build.properties. ${user.home} is the value * of the Java system environment variable named "user.home" and * corresponds to the user home directory. A warning is logged if * this properties file does not exist. <em>Note: The JUnit UI * can wind up with a different value for this property than when * you execute tests without the UI.</em> It is an error if the * properties file exists but can not be read. * * <li> {class}.properties, where {class} is the class name * reported by Java reflection, is loaded iff it is found. * * <li> {class} is repeatedly truncated by dropping the last * package name component to generate a package name. If a * properties file named "test.properties" exists in that package * then those properties are loaded. * * </ol> * * @return A {@link Properties} object that supplies bindings for * property names according to the described hierarchy among * property resources. The returned {@link Properties} is NOT * cached. * * TODO This does not handle the recursive truncation of the * class name to search for "test.properties" yet. */ public Properties getProperties() { MyProperties m_properties = null; if( m_properties == null ) { try { log.info ( "Will read properties." ); /* Setup a bottom layer of the default system. This * is just an empty map. * * Note that we go in backwards order in order to * setup the default hierarchy correctly, so this * empty map winds up on the bottom of the hierarchy. */ m_properties = new MyProperties ( "<none>" ); /* Use reflection to the get name of the concrete * instance of this class, which will be the TestCase * that is being used to run the test suite. */ String className = this.getClass().getName(); /* If a resource exists named <class>.properties then * we create a new layer in the properties hierarchy * and load the properties from that resource into the * new properties layer. */ classNameProperties: { /* Convert the class name into a resource identifier. */ String resourceName = className.replace('.','/') + ".properties" ; // Note: requires Java 1.5. // String resourceName = this.getClass().getSimpleName(); // Try to load properties from the resource into // the new layer. This uses a helper method that // searchs along the CLASSPATH, rather than the // file system, so that it can find the resource // in a JAR if necessary. log.info ( "Will try to read properties from resource: "+ resourceName ); ClassLoader cl = getClass().getClassLoader(); InputStream is = cl.getResourceAsStream ( resourceName ); if( is != null ) { // Add a new properties layer that inherits // from the last properties layer. MyProperties newLayer = new MyProperties ( resourceName, m_properties ); newLayer.load ( is ); // Update the reference iff everything // succeeded. m_properties = newLayer; } else { log.debug ( "No properties resource named: "+resourceName ); } } // classNameProperties /* The last thing that we do is incorporate the properties * file named by ${user.home}/build.properties so that it * will wind up on top of the properties hierarchy. */ user_home: { String userHome = System.getProperty ( "user.home" ); if( userHome == null ) { log.error ( "The System property 'user.home' is not defined." ); throw new AssertionError ( "The System property 'user.home' is not defined." ); } File file = new File ( userHome+File.separator+"build.properties" ); //See BLZG-1731 if (log.isInfoEnabled()) { log.info("Looking for properties file in home directory: " + file); } if( ! file.exists() ) { //See BLZG-1731 if (log.isInfoEnabled()) { log.info("Looking for properties file in home directory: " + file); } //See BLZG-1731 // log.warn // ( "File does not exist: "+file // ); // throw new IOException // ( "File does not exist: "+file // ); } else { if( ! file.isFile() ) { log.error ( "Not a normal file: "+file ); throw new IOException ( "Not a normal file: "+file ); } if( ! file.canRead() ) { log.error ( "Can not read file: "+file ); throw new IOException ( "Can not read file: "+file ); } // New layer in the hierarchy inherits from the old // layer. m_properties = new MyProperties ( file.toString(), m_properties ); // Read in properties from the file. m_properties.load ( new FileInputStream( file ) ); } } // user.home. if( true ) { // New layer in the hierarchy inherits from the old // layer. m_properties = new MyProperties ( "System.getProperties()", m_properties ); m_properties.putAll( PropertyUtil.flatten( System.getProperties() ) ); } // Log the properties that we just loaded. if( isDEBUG() ) { logProperties ( m_properties, 0 ); } } catch( IOException ex ) { /* Hide the IOException as a RuntimeException. */ ex.printStackTrace(); RuntimeException ex2 = new RuntimeException ( "Could not load properties." ); ex2.initCause( ex ); throw ex2; } } // load property hierarchy. return m_properties; } // MyProperties m_properties; /** * Helper class gives us access to the default {@link Properties}. * We use this class exclusively when loading properties so that * we can correctly report what properties are bound at what level * and what resource or file the properties were loaded from. */ protected static class MyProperties extends Properties { /** * */ private static final long serialVersionUID = 3980787538394715754L; String m_source = null; /** * The file or CLASSPATH resource from which the properties * were loaded. */ public String getSource() {return m_source;} /** * Exposes the protected member in the {@link Properties} * class. */ public MyProperties getDefaults() { return (MyProperties) defaults; } public MyProperties( String source ) { super(); m_source = source; } public MyProperties( String source, MyProperties defaults ) { super( defaults ); m_source = source; } } /** * Recursively logs properties together with the resource or file * in which they were defined at the DEBUG level. The properties * at a given level are presented in alpha ordering in order to * make it easier to locate a specific binding.<p> * * This method is invoked by {@link #getProperties()}.<p> */ protected void logProperties ( MyProperties properties, int level ) { StringBuilder sb = new StringBuilder(); sb.append ( "Properties:: [ source = '"+properties.getSource()+"' ]\n" ); TreeMap map = new TreeMap // sorted view. ( properties ); Iterator itr = map.entrySet().iterator(); // in sorted order. while( itr.hasNext() ) { Map.Entry entry = (Map.Entry) itr.next(); String name = (String) entry.getKey(); String value = (String) entry.getValue(); sb.append ( indent(level)+ "'"+name+"'"+ " = "+ ( value != null ? "'"+value+"'" : "<none>" )+ "\n" ); } log.info ( sb.toString() ); /* If there are defaults for these properties, then * recursively log those as well. */ MyProperties defaults = properties.getDefaults(); if( defaults != null ) { logProperties ( defaults, level + 1 ); } } /** * Generates an indentation string. */ private String indent( int level ) { final String s = "..."; if( level == 0 ) return ""; StringBuilder sb = new StringBuilder ( level * s.length() ); for( int i=0; i<level; i++ ) { sb.append( s ); } return s.toString(); } /** * Returns the project build directory, even in a multiproject * build. This is normally the <i>target</i> subdirectory beneath * the project root directory.<p> * * Note that the current working directory in a multiproject build * is normally NOT the same as the individual project directory * when running the unit tests. This method is designed to give * you access to project local resources (whether consumed or * created) during unit test, even when running a multiproject * goal.<p> * * In order for this method to succeed, you MUST declare * <i>maven.junit.sysproperties="maven.build.dir ..."</i> when you * invoke the test suite. See the properties page for the <a * href="http://maven.apache.org/reference/plugins/test/properties.html"> * test-plugin </a> for more information on how to specify this * property.<p> * * @throws UnsupportedOperationException if the project build * directory could NOT be determined since the * <i>maven.junit.sysproperties</i> property was NOT defined. * * @return The path to the project build directory. */ static public String getProjectBuildPath() throws UnsupportedOperationException { String x = System.getProperty ( "maven.build.dir" ); if( x == null ) { final String msg = "The 'maven.build.dir' property is NOT defined. Did you forget to defined 'maven.junit.sysproperties=maven.build.dir ...'"; log.error ( msg ); throw new UnsupportedOperationException ( msg ); } else { log.info ( "maven.build.dir='"+x+"'" ); return x; } } /** * Utility method returns true iff the effective logger level is * DEBUG or less. This may be used to make logging requiring * significant formatting conditional on the logging level (so * that the formatting is not done if the message would not be * logged). * * @param log The logger. * * @return True if the logger would log messages at the DEBUG level. */ static final public boolean isDEBUG( Logger log ) { return log.getEffectiveLevel().toInt() <= Level.DEBUG.toInt(); } /** * Utility method returns true iff the effective logger level is * INFO or less. This may be used to make logging requiring * significant formatting conditional on the logging level (so * that the formatting is not done if the message would not be * logged). * * @param log The logger. * * @return True if the logger would log messages at the INFO level. */ static final public boolean isINFO( Logger log ) { return log.getEffectiveLevel().toInt() <= Level.INFO.toInt(); } /** * @return True if the logger would log messages at the DEBUG level. */ final public boolean isDEBUG() {return isDEBUG( log );} /** * @return True if the logger would log messages at the INFO level. */ final public boolean isINFO() {return isINFO( log );} //************************************************************ //************************************************************ //************************************************************ /** * Derived from <a * href="http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm"> * Comparing floating point numbers </a> by Bruce Dawson. * * @param A * A floating point value. * @param B * Another floating point value */ // * @param maxUlps // * The maximum error in terms of Units in the Last Place. This // * specifies how big an error we are willing to accept in terms // * of the value of the least significant digit of the floating // * point number's representation. <i>maxUlps</i> can also be // * interpreted in terms of how many representable floats we are // * willing to accept between A and B. This function will allow // * <code>maxUlps-1</code> floats between <i>A</i> and <i>B</i>. static protected int getUlps(float A, float B) { int aInt = Float.floatToIntBits(A); // *(int*)&A; // Make aInt lexicographically ordered as a twos-complement // int. if (aInt < 0) { aInt = 0x80000000 - aInt; } // Make bInt lexicographically ordered as a twos-complement // int. int bInt = Float.floatToIntBits(B); // *(int*)&B; if (bInt < 0) { bInt = 0x80000000 - bInt; } int intDiff = Math.abs(aInt - bInt); return intDiff; } /** * Derived from <a * href="http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm"> * Comparing floating point numbers </a> by Bruce Dawson. * * @param A * A double precision floating point value. * @param B * Another double precision floating point value */ // * @param maxUlps // * The maximum error in terms of Units in the Last Place. This // * specifies how big an error we are willing to accept in terms // * of the value of the least significant digit of the floating // * point number's representation. <i>maxUlps</i> can also be // * interpreted in terms of how many representable doubles we are // * willing to accept between A and B. This function will allow // * <code>maxUlps-1</code> doubles between <i>A</i> and <i>B</i>. static protected long getUlps(double A, double B) { long aLong = Double.doubleToLongBits(A); // *(int*)&A; // Make aInt lexicographically ordered as a twos-complement // long. if (aLong < 0) { aLong = 0x8000000000000000L - aLong; } // Make bInt lexicographically ordered as a twos-complement // int. long bLong = Double.doubleToLongBits(B); // *(int*)&B; if (bLong < 0) { bLong = 0x8000000000000000L - bLong; } long longDiff = Math.abs(aLong - bLong); return longDiff; } /** * Verify zero ULPs difference between the values. * @param f1 * @param f2 */ protected void assertZeroUlps(float f1, float f2) { int ulps = getUlps(f1,f2); if( ulps != 0 ) { fail("Expecting zero ulps, but found: "+ulps+"; f1="+f1+", f2="+f2); } } /** * Verify zero ULPs difference between the values. * @param d1 * @param d2 */ protected void assertZeroUlps(double d1, double d2) { long ulps = getUlps(d1,d2); if( ulps != 0L ) { fail("Expecting zero ulps, but found: "+ulps+"; f1="+d1+", f2="+d2); } } // /** // * Derived from <a // * href="http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm"> // * Comparing floating point numbers </a> by Bruce Dawson. // * // * @param A A floating point value. // * @param B Another floating point value // * @param maxUlps The maximum error in terms of Units in the Last // * Place. This specifies how big an error we are willing to accept // * in terms of the value of the least significant digit of the // * floating point numbers representation. <i>maxUlps</i> can // * also be interpreted in terms of how many representable floats // * we are willing to accept between A and B. This function will // * allow <code>maxUlps-1</code> floats between <i>A</i> and // * <i>B</i>. // */ // // static protected int getUlps //// static public boolean AlmostEqual2sComplement // ( float A, // float B //// , //// int maxUlps // ) // { // //// // Make sure maxUlps is non-negative and small enough that the //// // default NAN won't compare as equal to anything. // //// if( ! (maxUlps > 0 && maxUlps < 4 * 1024 * 1024) ) { // //// throw new IllegalArgumentException //// ( "maxUlps is out of range: "+maxUlps //// ); // //// } // // int aInt = Float.floatToIntBits( A ); // *(int*)&A; // // // Make aInt lexicographically ordered as a twos-complement // // int. // // if (aInt < 0) { // // aInt = 0x80000000 - aInt; // // } // // // Make bInt lexicographically ordered as a twos-complement // // int. // // int bInt = Float.floatToIntBits( B ); // *(int*)&B; // // if (bInt < 0) { // // bInt = 0x80000000 - bInt; // // } // // int intDiff = Math.abs // ( aInt - bInt // ); // // return intDiff; // //// if (intDiff <= maxUlps) { // //// return true; // //// } // //// return false; // // } // // /** // * Derived from <a // * href="http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm"> // * Comparing floating point numbers </a> by Bruce Dawson. // * // * @param A A double precision floating point value. // * @param B Another double precision floating point value // * @param maxUlps The maximum error in terms of Units in the Last // * Place. This specifies how big an error we are willing to accept // * in terms of the value of the least significant digit of the // * floating point numbers representation. <i>maxUlps</i> can // * also be interpreted in terms of how many representable doubles // * we are willing to accept between A and B. This function will // * allow <code>maxUlps-1</code> doubles between <i>A</i> and // * <i>B</i>. // */ // // static protected long getUlps //// static public boolean AlmostEqual2sComplement // ( double A, // double B //// , //// long maxUlps // ) // { // // // Make sure maxUlps is non-negative and small enough that the // // default NAN won't compare as equal to anything. // // // // TODO The range check here is for float. The max possible // // legal range is larger for double. // //// if( ! (maxUlps > 0 && maxUlps < 4 * 1024 * 1024) ) { // //// throw new IllegalArgumentException //// ( "maxUlps is out of range: "+maxUlps //// ); // //// } // // long aLong = Double.doubleToLongBits( A ); // *(int*)&A; // // // Make aInt lexicographically ordered as a twos-complement // // long. // // if (aLong < 0) { // // aLong = 0x8000000000000000L - aLong; // // } // // // Make bInt lexicographically ordered as a twos-complement // // int. // // long bLong = Double.doubleToLongBits( B ); // *(int*)&B; // // if (bLong < 0) { // // bLong = 0x8000000000000000L - bLong; // // } // // long longDiff = Math.abs // ( aLong - bLong // ); // // // return longDiff; // //// if (longDiff <= maxUlps) { // //// return true; // //// } // //// return false; // // } /** * Provides a proper basis for comparing floating point numbers (the values * must be within 10 ulps of one another). * * @see #getUlps(float, float) */ public static void assertEquals(float expected, float actual) { assertEquals("", expected, actual); } /** * Provides a proper basis for comparing floating point numbers (the values * must be within 10 ulps of one another). * * @see #getUlps(float, float) */ public static void assertEquals(String message, float expected, float actual) { if (expected == actual) return; final int maxUlps = 10; int ulps = getUlps(expected, actual); if (ulps <= maxUlps) { return; } fail("Expecting " + expected + ", not " + actual + ": abs(difference)=" + Math.abs(expected - actual) + ": ulps=" + ulps); } /** * Provides a proper basis for comparing floating point numbers (the values * must be within 10 ulps of one another). * * @param expected * @param actual * * @see #getUlps(double, double) */ public static void assertEquals(double expected, double actual) { assertEquals("", expected, actual); } /** * Provides a proper basis for comparing floating point numbers (the values * must be within 10 ulps of one another). * * @param expected * @param actual * * @see #getUlps(double, double) */ public static void assertEquals(String message, double expected, double actual) { assertEqualsWithinUlps(message, expected, actual, 10/*maxUlps*/); } /** * Provides a proper basis for comparing floating point numbers (the values * must be within <i>maxUlps</i> of one another). * * @param expected * @param actual * @param maxUlps The maximum #of ulps difference which will be considered * as equivalent. * * @see #getUlps(double, double) */ public static void assertEqualsWithinUlps(String message, double expected, double actual, final long maxUlps) { if (expected == actual) return; long ulps = getUlps(expected, actual); if (ulps <= maxUlps) { return; } fail("Expecting " + expected + ", not " + actual + ": abs(difference)=" + Math.abs(expected - actual) + ", ulps=" + ulps); } public static void assertSameBigInteger(BigInteger expected, BigInteger actual) { assertSameBigInteger("", expected, actual); } public static void assertSameBigInteger(String message, BigInteger expected, BigInteger actual) { assertTrue(message + ": expected " + expected + ", not " + actual, expected.equals(actual)); } /** * This uses {@link BigDecimal#compareTo( Object other )}, which * considers that two {@link BigDecimal}s that are equal in * <i>value</i> but have a different <i>scale</i> are the same. * (This is NOT true of {@link BigDecimal#equals( Object other )}, * which also requires the values to have the same <i>scale</i>.) */ public static void assertSameBigDecimal(BigDecimal expected, BigDecimal actual) { assertEquals("", expected, actual); } /** * This uses {@link BigDecimal#compareTo( Object other )}, which * considers that two {@link BigDecimal}s that are equal in * <i>value</i> but have a different <i>scale</i> are the same. * (This is NOT true of {@link BigDecimal#equals( Object other )}, * which also requires the values to ave the same <i>scale</i>.) */ public static void assertSameBigDecimal(String message, BigDecimal expected, BigDecimal actual) { assertTrue(message + ": expected " + expected + ", not " + actual, expected.compareTo(actual) == 0); } /** * Helper method verifies that the arrays are consistent in length * and that their elements are consistent under {@link * Object#equals( Object other )}. */ public static void assertSameArray(Object[] expected, Object[] actual) { assertSameArray("", expected, actual); } /** * Helper method verifies that the arrays are consistent in length * and that their elements are consistent under {@link * Object#equals( Object other )}. */ public static void assertSameArray(String message, Object[] expected, Object[] actual) { if (expected == null && actual == null) { return; } if (expected != null && actual == null) { fail(message + " : expecting actual to be non-null."); } if (expected == null && actual != null) { fail(message + " : expecting actual to be null."); } assertEquals(message + " : length is wrong.", expected.length, actual.length); for (int i = 0; i < expected.length; i++) { if (expected[i] == null && actual[i] == null) { continue; } if (expected[i] != null && actual[i] == null) { fail(message + " : expecting actual[" + i + "] to be non-null"); } if (expected[i] == null && actual[i] != null) { fail(message + " : expecting actual[" + i + "] to be null."); } assertTrue(message + " : expected[" + i + "]=" + expected[i] + ", but actual[" + i + "]=" + actual[i], expected[i] .equals(actual[i])); } } /** * Examines a stack trace for an instance of the specified cause nested to * any level within that stack trace. * * @param t * The stack trace. * @param cls * The class of exception that you are looking for in the stack * trace. * * @return An exception that is an instance of that class iff one exists in * the stack trace and <code>null</code> otherwise. * * @throws IllegalArgumentException * if any parameter is null. */ static public Throwable getInnerCause(Throwable t, Class cls) { // Note: Use of generics commented out for 1.4 compatibility. // static public Throwable getInnerCause(Throwable t, Class<? extends Throwable> cls) { if (t == null) throw new IllegalArgumentException(); if (cls == null) throw new IllegalArgumentException(); { Class x = t.getClass(); while(x != null){ if( x == cls) return t; x = x.getSuperclass(); } } t = t.getCause(); if (t == null) return null; return getInnerCause(t, cls); } /** * Examines a stack trace for an instance of the specified cause nested to * any level within that stack trace. * * @param t * The stack trace. * @param cls * The class of exception that you are looking for in the stack * trace. * * @return <code>true</code> iff an exception that is an instance of that * class iff one exists in the stack trace. * * @throws IllegalArgumentException * if any parameter is null. */ static public boolean isInnerCause(Throwable t, Class cls) { // Note: Use of generics commented out for 1.4 compatibility. // static public boolean isInnerCause(Throwable t, Class<? extends Throwable>cls) { return getInnerCause(t, cls) != null; } }