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