/* | |
* Licensed to the Apache Software Foundation (ASF) under one or more | |
* contributor license agreements. See the NOTICE file distributed with | |
* this work for additional information regarding copyright ownership. | |
* The ASF licenses this file to You under the Apache License, Version 2.0 | |
* (the "License"); you may not use this file except in compliance with | |
* the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package org.apache.commons.collections; | |
import java.io.ByteArrayInputStream; | |
import java.io.ByteArrayOutputStream; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.ObjectInputStream; | |
import java.io.ObjectOutputStream; | |
import java.io.OutputStream; | |
import java.io.Serializable; | |
/** | |
* Abstract test class for {@link java.lang.Object} methods and contracts. | |
* <p> | |
* To use, simply extend this class, and implement | |
* the {@link #makeObject()} method. | |
* <p> | |
* If your {@link Object} fails one of these tests by design, | |
* you may still use this base set of cases. Simply override the | |
* test case (method) your {@link Object} fails. | |
* | |
* @version $Revision: 646780 $ $Date: 2008-04-10 13:48:07 +0100 (Thu, 10 Apr 2008) $ | |
* | |
* @author Rodney Waldhoff | |
* @author Stephen Colebourne | |
* @author Anonymous | |
*/ | |
public abstract class AbstractTestObject extends BulkTest { | |
/** Current major release for Collections */ | |
public static final int COLLECTIONS_MAJOR_VERSION = 3; | |
/** | |
* JUnit constructor. | |
* | |
* @param testName the test class name | |
*/ | |
public AbstractTestObject(String testName) { | |
super(testName); | |
} | |
//----------------------------------------------------------------------- | |
/** | |
* Implement this method to return the object to test. | |
* | |
* @return the object to test | |
*/ | |
public abstract Object makeObject(); | |
/** | |
* Override this method if a subclass is testing an object | |
* that cannot serialize an "empty" Collection. | |
* (e.g. Comparators have no contents) | |
* | |
* @return true | |
*/ | |
public boolean supportsEmptyCollections() { | |
return true; | |
} | |
/** | |
* Override this method if a subclass is testing an object | |
* that cannot serialize a "full" Collection. | |
* (e.g. Comparators have no contents) | |
* | |
* @return true | |
*/ | |
public boolean supportsFullCollections() { | |
return true; | |
} | |
/** | |
* Is serialization testing supported. | |
* Default is true. | |
*/ | |
public boolean isTestSerialization() { | |
return true; | |
} | |
/** | |
* Returns true to indicate that the collection supports equals() comparisons. | |
* This implementation returns true; | |
*/ | |
public boolean isEqualsCheckable() { | |
return true; | |
} | |
//----------------------------------------------------------------------- | |
public void testObjectEqualsSelf() { | |
Object obj = makeObject(); | |
assertEquals("A Object should equal itself", obj, obj); | |
} | |
public void testEqualsNull() { | |
Object obj = makeObject(); | |
assertEquals(false, obj.equals(null)); // make sure this doesn't throw NPE either | |
} | |
public void testObjectHashCodeEqualsSelfHashCode() { | |
Object obj = makeObject(); | |
assertEquals("hashCode should be repeatable", obj.hashCode(), obj.hashCode()); | |
} | |
public void testObjectHashCodeEqualsContract() { | |
Object obj1 = makeObject(); | |
if (obj1.equals(obj1)) { | |
assertEquals( | |
"[1] When two objects are equal, their hashCodes should be also.", | |
obj1.hashCode(), obj1.hashCode()); | |
} | |
Object obj2 = makeObject(); | |
if (obj1.equals(obj2)) { | |
assertEquals( | |
"[2] When two objects are equal, their hashCodes should be also.", | |
obj1.hashCode(), obj2.hashCode()); | |
assertTrue( | |
"When obj1.equals(obj2) is true, then obj2.equals(obj1) should also be true", | |
obj2.equals(obj1)); | |
} | |
} | |
public void testSerializeDeserializeThenCompare() throws Exception { | |
Object obj = makeObject(); | |
if (obj instanceof Serializable && isTestSerialization()) { | |
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); | |
ObjectOutputStream out = new ObjectOutputStream(buffer); | |
out.writeObject(obj); | |
out.close(); | |
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray())); | |
Object dest = in.readObject(); | |
in.close(); | |
if (isEqualsCheckable()) { | |
assertEquals("obj != deserialize(serialize(obj))", obj, dest); | |
} | |
} | |
} | |
/** | |
* Sanity check method, makes sure that any Serializable | |
* class can be serialized and de-serialized in memory, | |
* using the handy makeObject() method | |
* | |
* @throws IOException | |
* @throws ClassNotFoundException | |
*/ | |
public void testSimpleSerialization() throws Exception { | |
Object o = makeObject(); | |
if (o instanceof Serializable && isTestSerialization()) { | |
byte[] objekt = writeExternalFormToBytes((Serializable) o); | |
Object p = readExternalFormFromBytes(objekt); | |
} | |
} | |
/** | |
* Tests serialization by comparing against a previously stored version in CVS. | |
* If the test object is serializable, confirm that a canonical form exists. | |
*/ | |
public void testCanonicalEmptyCollectionExists() { | |
if (supportsEmptyCollections() && isTestSerialization() && !skipSerializedCanonicalTests()) { | |
Object object = makeObject(); | |
if (object instanceof Serializable) { | |
String name = getCanonicalEmptyCollectionName(object); | |
assertTrue( | |
"Canonical empty collection (" + name + ") is not in CVS", | |
new File(name).exists()); | |
} | |
} | |
} | |
/** | |
* Tests serialization by comparing against a previously stored version in CVS. | |
* If the test object is serializable, confirm that a canonical form exists. | |
*/ | |
public void testCanonicalFullCollectionExists() { | |
if (supportsFullCollections() && isTestSerialization() && !skipSerializedCanonicalTests()) { | |
Object object = makeObject(); | |
if (object instanceof Serializable) { | |
String name = getCanonicalFullCollectionName(object); | |
assertTrue( | |
"Canonical full collection (" + name + ") is not in CVS", | |
new File(name).exists()); | |
} | |
} | |
} | |
// protected implementation | |
//----------------------------------------------------------------------- | |
/** | |
* Get the version of Collections that this object tries to | |
* maintain serialization compatibility with. Defaults to 1, the | |
* earliest Collections version. (Note: some collections did not | |
* even exist in this version). | |
* | |
* This constant makes it possible for TestMap (and other subclasses, | |
* if necessary) to automatically check CVS for a versionX copy of a | |
* Serialized object, so we can make sure that compatibility is maintained. | |
* See, for example, TestMap.getCanonicalFullMapName(Map map). | |
* Subclasses can override this variable, indicating compatibility | |
* with earlier Collections versions. | |
* | |
* @return The version, or <code>null</code> if this object shouldn't be | |
* tested for compatibility with previous versions. | |
*/ | |
public String getCompatibilityVersion() { | |
return "1"; | |
} | |
protected String getCanonicalEmptyCollectionName(Object object) { | |
StringBuffer retval = new StringBuffer(); | |
retval.append("data/test/"); | |
String colName = object.getClass().getName(); | |
colName = colName.substring(colName.lastIndexOf(".") + 1, colName.length()); | |
retval.append(colName); | |
retval.append(".emptyCollection.version"); | |
retval.append(getCompatibilityVersion()); | |
retval.append(".obj"); | |
return retval.toString(); | |
} | |
protected String getCanonicalFullCollectionName(Object object) { | |
StringBuffer retval = new StringBuffer(); | |
retval.append("data/test/"); | |
String colName = object.getClass().getName(); | |
colName = colName.substring(colName.lastIndexOf(".") + 1, colName.length()); | |
retval.append(colName); | |
retval.append(".fullCollection.version"); | |
retval.append(getCompatibilityVersion()); | |
retval.append(".obj"); | |
return retval.toString(); | |
} | |
/** | |
* Write a Serializable or Externalizable object as | |
* a file at the given path. NOT USEFUL as part | |
* of a unit test; this is just a utility method | |
* for creating disk-based objects in CVS that can become | |
* the basis for compatibility tests using | |
* readExternalFormFromDisk(String path) | |
* | |
* @param o Object to serialize | |
* @param path path to write the serialized Object | |
* @exception IOException | |
*/ | |
protected void writeExternalFormToDisk(Serializable o, String path) throws IOException { | |
FileOutputStream fileStream = new FileOutputStream(path); | |
writeExternalFormToStream(o, fileStream); | |
} | |
/** | |
* Converts a Serializable or Externalizable object to | |
* bytes. Useful for in-memory tests of serialization | |
* | |
* @param o Object to convert to bytes | |
* @return serialized form of the Object | |
* @exception IOException | |
*/ | |
protected byte[] writeExternalFormToBytes(Serializable o) throws IOException { | |
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); | |
writeExternalFormToStream(o, byteStream); | |
return byteStream.toByteArray(); | |
} | |
/** | |
* Reads a Serialized or Externalized Object from disk. | |
* Useful for creating compatibility tests between | |
* different CVS versions of the same class | |
* | |
* @param path path to the serialized Object | |
* @return the Object at the given path | |
* @exception IOException | |
* @exception ClassNotFoundException | |
*/ | |
protected Object readExternalFormFromDisk(String path) throws IOException, ClassNotFoundException { | |
FileInputStream stream = new FileInputStream(path); | |
return readExternalFormFromStream(stream); | |
} | |
/** | |
* Read a Serialized or Externalized Object from bytes. | |
* Useful for verifying serialization in memory. | |
* | |
* @param b byte array containing a serialized Object | |
* @return Object contained in the bytes | |
* @exception IOException | |
* @exception ClassNotFoundException | |
*/ | |
protected Object readExternalFormFromBytes(byte[] b) throws IOException, ClassNotFoundException { | |
ByteArrayInputStream stream = new ByteArrayInputStream(b); | |
return readExternalFormFromStream(stream); | |
} | |
protected boolean skipSerializedCanonicalTests() { | |
return Boolean.getBoolean("org.apache.commons.collections:with-clover"); | |
} | |
// private implementation | |
//----------------------------------------------------------------------- | |
private Object readExternalFormFromStream(InputStream stream) throws IOException, ClassNotFoundException { | |
ObjectInputStream oStream = new ObjectInputStream(stream); | |
return oStream.readObject(); | |
} | |
private void writeExternalFormToStream(Serializable o, OutputStream stream) throws IOException { | |
ObjectOutputStream oStream = new ObjectOutputStream(stream); | |
oStream.writeObject(o); | |
} | |
} |