/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.core.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.Writer;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import junit.framework.AssertionFailedError;
import org.junit.Assert;
/**
* This class contains static methods that are routinely and commonly used in many test cases, and related to methods to test
* common functionality or to perform common tests.
*
* <em><b>This class should not be used by plug-in based tests<b><em>
*/
public class UnitTestUtil {
public static final class LogFormatter extends Formatter {
@Override
public String format(LogRecord record) {
final StringBuilder result = new StringBuilder();
result.append(new Timestamp(record.getMillis()));
result.append(" "); //$NON-NLS-1$
result.append(record.getLoggerName());
result.append(" "); //$NON-NLS-1$
result.append(record.getLevel());
result.append(" "); //$NON-NLS-1$
result.append(Thread.currentThread().getName());
result.append(" "); //$NON-NLS-1$
result.append(record.getMessage());
result.append('\n');
if (record.getThrown() != null) {
record.getThrown().printStackTrace(new PrintWriter(new Writer() {
@Override
public void close() throws IOException {
}
@Override
public void flush() throws IOException {
}
@Override
public void write(char[] cbuf, int off, int len)
throws IOException {
result.append(new String(cbuf, off, len));
}
}));
result.append('\n');
}
return result.toString();
}
}
public static final String PATH_SEPARATOR = "/"; //$NON-NLS-1$
private static final String DEFAULT_TESTDATA_PATH = "src/test/resources"; //$NON-NLS-1$
private static final String DEFAULT_TEMP_DIR = "target/scratch"; //$NON-NLS-1$
//============================================================================================================================
// Static Methods
/**
* <p>
* This method attempts to check all of the requirements relating to equivalence.
* </p>
* <p>
* If <code>obj1</code> is not null, then the following tests are performed:
* <li><code>obj1.equals(obj1) == true</code></li>
* <li><code>obj1.equals(null) == false</code></li>
* </p>
* <p>
* If <code>obj2</code> is not null, then the following tests are performed:
* <li><code>obj2.equals(obj2) == true</code></li>
* <li><code>obj2.equals(null) == false</code></li>
* </p>
* <p>
* If both <code>obj1</code> and <code>obj2</code> are not null, then the following tests are performed:
* <li><code>obj1.equals(obj2) == obj2.equals(obj1)</code></li>
* <li><code>obj1.equals(obj2) == true</code> if <code>correctCompareToResult==0</code> or <code>obj1.equals(obj2) == false</code>
* if <code>correctCompareToResult!=0</code></li>
* <li><code>obj2.equals(obj1) == true</code> if <code>correctCompareToResult==0</code> or <code>obj2.equals(obj1) == false</code>
* if <code>correctCompareToResult!=0</code></li>
* <li><code>obj1.hashCode() == obj2.hashCode()</code> if <code>correctCompareToResult==0</code></li>
* </p>
* <p>
* Finally, if either <code>obj1</code> and <code>obj2</code> are instances of {@link Comparable}, then the following
* tests are performed:
* <li><code>obj1.compareTo(obj2) == correctCompareToResult</code>, or <code>obj1.compareTo(obj2) throws an IllegalArgumentException
* if <code>obj2</code> is null</li>
* <li><code>obj2.compareTo(obj1) == (-1 * correctCompareToResult)</code>, or
* <code>obj2.compareTo(obj1) throws an IllegalArgumentException
* if <code>obj2</code> is null</li></li>
* <li><code>obj1.compareTo(obj1) == 0</code> if <code>obj1 != null</code></li>
* <li><code>obj2.compareTo(obj2) == 0</code> if <code>obj2 != null</code></li>
* </p>
* @param correctCompareToResult 0 if the two comparable objects should be
* equivalent; <0 if <code>obj1<code> is to be considered less-than <code>obj2</code>;
* >0 if <code>obj2<code> is to be considered less-than <code>obj1</code>. If <code>obj1</code>
* and <code>obj2</code> are not instances of {@link Comparable}, then it only matters that
* this parameter is zero or non-zero (whether it's greater or less-than zero is not important).
* @param obj1 the reference to the first Object object; may be null, but no tests will be performed
* @param obj2 the reference to the second Object object; may be null
*/
public static void helpTestEquivalence(final int correctCompareToResult,
final Object obj1,
final Object obj2) {
if (obj1 != null) {
if (obj2 != null) {
// Test that equals is reflexive
helpTestEquals(obj1);
// Test that equals is symmetric ...
helpTestEqualsTransitivity(obj1, obj2);
}
// If the objects are considered equal ...
boolean equal = obj1.equals(obj2);
if (equal) {
if (correctCompareToResult != 0) {
Assert.fail("obj1.equals(obj2) returned true but was expected to return false"); //$NON-NLS-1$
}
} else {
// They are not considered equal, so verify that this is what was expected
if (correctCompareToResult == 0) {
Assert.fail("obj1.equals(obj2) returned false but was expected to return true"); //$NON-NLS-1$
}
}
// Test the hashCode and whether it is compatible with equals.
helpTestHashCode(equal, obj1, obj2);
}
if (obj2 != null) {
// Test that equals is reflexive
helpTestEquals(obj2);
}
// Finally (if both are comparable) then compareTo ...
if (obj1 instanceof Comparable || obj2 instanceof Comparable) {
Comparable comp1 = (Comparable)obj1;
Comparable comp2 = (Comparable)obj2;
helpTestCompareTo(correctCompareToResult, comp1, comp2);
// Test that compareTo is reflexive
helpTestReflexiveCompareTo(comp1); // doesn't do anything if obj1 == null
helpTestReflexiveCompareTo(comp2); // doesn't do anything if obj2 == null
}
}
//============================================================================================================================
// Static Utility Methods
/**
* This method checks the 'compareTo' output of two Comparable objects, and uses an expected result to check the logic. This
* test checks whether commutative calls to 'compareTo' are compatible. Note that either or both Comparable references may be
* null; if only one of the references is null, this method checks that the 'compareTo' method throws an
* IllegalArgumentException. If both are null, this method fails if the 'correctCompareToResult' is not 0.
*
* @param correctCompareToResult
* 0 if the two comparable objects should be equivalent; <0 if <code>obj1<code> is to be considered less-than <code>obj2</code>;
* >0 if <code>obj2<code> is to be considered less-than <code>obj1</code>
* @param obj1 the reference to the first Comparable object; may be null
* @param obj2 the reference to the second Comparable object; may be null
*/
protected static void helpTestCompareTo(final int correctCompareToResult,
final Comparable obj1,
final Comparable obj2) {
if (obj1 != null) {
if (obj2 != null) {
// They are both non-null, so analyze the result of 'compareTo'
int result1 = obj1.compareTo(obj2);
int result2 = obj2.compareTo(obj1);
// If the results are different ...
if (result1 != result2) {
// Check that obj1.compareTo(obj2) returned the same sign as the expected result
if (result1 < 0) {
if (!(correctCompareToResult < 0)) {
Assert.fail("obj1.compareTo(obj2) returned <0 (actual=" + result1 //$NON-NLS-1$
+ ") and did not match the expected result (" + correctCompareToResult + ")"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (!(result2 > 0)) {
Assert.fail("The compareTo call was not commutative: obj1.compareTo(obj2) returned <0 (actual=" + result1 //$NON-NLS-1$
+ ") but obj2.compareTo(obj2) did not return >0 (actual=" + result2 + ") "); //$NON-NLS-1$ //$NON-NLS-2$
}
} else if (result1 > 0) {
if (!(correctCompareToResult > 0)) {
Assert.fail("obj1.compareTo(obj2) returned >0 (actual=" + result1 //$NON-NLS-1$
+ ") and did not match the expected result (" + correctCompareToResult + ")"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (!(result2 < 0)) {
Assert.fail("The compareTo call was not commutative: obj1.compareTo(obj2) returned >0 (actual=" + result1 //$NON-NLS-1$
+ ") but obj2.compareTo(obj2) did not return <0 (actual=" + result2 + ") "); //$NON-NLS-1$ //$NON-NLS-2$
}
} else { // result1 == 0
if (correctCompareToResult != 0) {
Assert.fail("obj1.compareTo(obj2) returned 0 but did not match the expected result (" //$NON-NLS-1$
+ correctCompareToResult + ") and the second compareTo call didn't match the first"); //$NON-NLS-1$
}
Assert.fail("The compareTo call was not commutative: obj1.compareTo(obj2) returned 0 while obj2.compareTo(obj2) returned " //$NON-NLS-1$
+ result2);
}
}
// Otherwise the results are the same so correct 'compareTo' result should be 0
else if (correctCompareToResult != 0) {
Assert.fail("obj1.compareTo(obj2) and obj2.compareTo(obj1) both returned " + result1 //$NON-NLS-1$
+ " but did not match the expected result (" + correctCompareToResult + ")"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
// Otherwise, obj1 is NOT null but obj2 IS null
else {
int result = 0;
try {
result = obj1.compareTo(obj2);
Assert.fail("The second comparable object was null but no IllegalArgumentException was thrown in " //$NON-NLS-1$
+ obj1.getClass().getName() + ".compareTo(Object)"); //$NON-NLS-1$
} catch (IllegalArgumentException e) {
}
if (!(correctCompareToResult > 0)) {
Assert.fail("obj1.compareTo(null) returned " + result + " but did not match the expected result (" //$NON-NLS-1$//$NON-NLS-2$
+ correctCompareToResult + ")"); //$NON-NLS-1$
}
}
}
// Else the obj1 IS null but obj2 is NOT null
else if (obj2 != null) {
int result = 0;
try {
result = obj2.compareTo(obj1);
Assert.fail("The first comparable object was null but no IllegalArgumentException was thrown in " //$NON-NLS-1$
+ obj2.getClass().getName() + ".compareTo(Object)"); //$NON-NLS-1$
} catch (IllegalArgumentException e) {
}
if (!(correctCompareToResult < 0)) {
Assert.fail("obj2.compareTo(null) returned " + result + " but did not match the expected result (" //$NON-NLS-1$ //$NON-NLS-2$
+ correctCompareToResult + ")"); //$NON-NLS-1$
}
}
// Otherwise both are null
else {
// The correct result had better be 0
if (correctCompareToResult != 0) {
Assert.fail("The expected result was not 0 even though both references were null"); //$NON-NLS-1$
}
}
}
/**
* Tests properties of the equals method implementation of the <code>obj</code> parameter. The equals method of <code>obj</code>
* is tested for reflexivity, symmetry, and equality with <code>null</code>.
*
* @param obj
* Object whose equals method is tested
* @throws AssertionFailedError
* if parameter is <code>null</code>, or if any test doesn't pass
*/
protected static void helpTestEquals(final Object obj) {
Assert.assertNotNull(obj);
Assert.assertTrue("The equals method of Object " + obj + " (Class " + obj.getClass().getName() //$NON-NLS-1$ //$NON-NLS-2$
+ ") does not return false for null parameter", !obj.equals(null)); //$NON-NLS-1$
Assert.assertTrue("The equals method of Object " + obj + " (Class " + obj.getClass().getName() + ") is not reflexive.", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
obj.equals(obj));
}
/**
* Tests transitivity property of the equals method implementation of the <code>test</code> parameter. The equals method of
* <code>test</code> is tested for transitivity with the equals method of <code>control</code>. Note that <code>control</code>
* can be either equal or not with <code>test</code>; what is tested is that their equals methods are consistent.
*
* @param test
* Object whose equals method is tested
* @param control
* object for testing transitivity of <code>test</code>'s equals method
* @throws AssertionFailedError
* if either parameter is <code>null</code>, or if any test doesn't pass
*/
protected static void helpTestEqualsTransitivity(final Object test,
final Object control) {
Assert.assertNotNull(test);
Assert.assertNotNull(control);
Assert.assertEquals("Equals methods of test Object and control Object are not symmetric: " + test + ", " + control, //$NON-NLS-1$//$NON-NLS-2$
test.equals(control), control.equals(test));
}
/**
* <p>
* This method attempts to check that, if the expected result is that the objects are equal, the hashCode values of two
* objects are also equal. Note that this method does not rely upon the 'equals' method to properly determine whether the two
* objects are equal; rather, the first argument specifies whether the objects are expected to be equal.
* </p>
* <p>
* The hashCode of two objects should be the same if the two objects are considered equal using the
* {@link java.lang.Object#equals(java.lang.Object) equals}method. If the objects are not equal, then it cannot be concluded
* whether the two hash codes must be equivalent or different.
* </p>
*
* @param boolean
* true if the two comparable objects should be equivalent; or false otherwise
* @param obj1
* the reference to the first Object; may be null
* @param obj2
* the reference to the second Object; may be null
*/
protected static void helpTestHashCode(final boolean shouldBeEqual,
final Object obj1,
final Object obj2) {
// Run the test only if both are not null
if (obj1 != null && obj2 != null) {
// Check the hash codes ...
int hash1 = obj1.hashCode();
int hash2 = obj2.hashCode();
if (shouldBeEqual) {
Assert.assertTrue("The two objects are supposed to be equal but do not have the same hash code value; obj1.hashCode()=" //$NON-NLS-1$
+ hash1 + "; obj2.hashCode()=" + hash2, hash1 == hash2); //$NON-NLS-1$
}
// If they should not be equal, then it is NOT necessarily true
// that the hash codes must be different. Therefore, nothing to test.
}
}
/**
* This method checks the reflexive nature of 'compareTo'; no check is performed if the input object is null.
*
* @param obj
* the reference to the Comparable object; may be null
*/
protected static void helpTestReflexiveCompareTo(final Comparable obj) {
if (obj != null && obj.compareTo(obj) != 0) {
Assert.fail("The compareTo method is not reflexive; obj.compareTo(obj) does not equal 0"); //$NON-NLS-1$
}
}
//============================================================================================================================
// Constructors
/**
* Can't construct - just utilities
*/
protected UnitTestUtil() {
}
/**
* Obtain a {@link File}for the file name in the test data directory (given by {@link #getTestDataPath()}).
*
* @param fileName
* A path and name relative to the test data directory; for example, "MyFile.txt" if the file is in the test
* data directory, or "subfolder/MyFile.txt" if the file is in "subfolder".
* @return The File referencing the file with the specified fileName within the test data directory
*/
public static File getTestDataFile(String fileName) {
return new File(UnitTestUtil.getTestDataPath(), fileName);
}
public static File getTestScratchFile(String fileName) {
// Create the input stream ...
String path = UnitTestUtil.getTestScratchPath();
File file = new File(path, fileName);
return file;
}
/**
* Obtain the file path to the root of the test data file tree. This first checks the property
* {@link TEST_DATA_ROOT_PROPERTY}, and if that is not set to a valid path, uses the current directory.
*
* @return File path, never null
*/
public static final String getTestDataPath() {
return DEFAULT_TESTDATA_PATH;
}
/**
* Obtain the file path to a scratch area where files may be created during testing.
*
* @return File path, never null
*/
public static final String getTestScratchPath() {
String filePath = DEFAULT_TEMP_DIR;
File scratch = new File(filePath);
if (!scratch.exists() && !scratch.mkdirs()) {
filePath = System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
if (filePath == null) {
filePath = "."; //$NON-NLS-1$
}
}
File scratchDirectory = new File(filePath);
if(!scratchDirectory.exists()) {
scratchDirectory.mkdir();
}
return filePath;
}
@SuppressWarnings("unchecked")
public static final <T extends Serializable> T helpSerialize(T object) throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
oos.flush();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
return (T)ois.readObject();
}
public static void enableTraceLogging(String loggerName) {
enableLogging(Level.FINEST, loggerName);
}
static Map<String, Logger> loggers = new HashMap<String, Logger>();
public static void enableLogging(Level level, String loggerName) {
Logger logger = null;
synchronized (loggers) {
logger = loggers.get(loggerName);
if (logger == null) {
logger = Logger.getLogger(loggerName);
loggers.put(loggerName, logger);
}
}
logger.setLevel(level);
if (logger.getHandlers().length > 0) {
for (Handler h : logger.getHandlers()) {
h.setLevel(level);
}
} else {
logger.setUseParentHandlers(false);
ConsoleHandler ch = new ConsoleHandler();
ch.setFormatter(new LogFormatter());
ch.setLevel(level);
logger.addHandler(ch);
}
}
}