1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.collections; 18 19 import java.io.ByteArrayInputStream; 20 import java.io.ByteArrayOutputStream; 21 import java.io.File; 22 import java.io.FileInputStream; 23 import java.io.FileOutputStream; 24 import java.io.IOException; 25 import java.io.InputStream; 26 import java.io.ObjectInputStream; 27 import java.io.ObjectOutputStream; 28 import java.io.OutputStream; 29 import java.io.Serializable; 30 31 /** 32 * Abstract test class for {@link java.lang.Object} methods and contracts. 33 * <p> 34 * To use, simply extend this class, and implement 35 * the {@link #makeObject()} method. 36 * <p> 37 * If your {@link Object} fails one of these tests by design, 38 * you may still use this base set of cases. Simply override the 39 * test case (method) your {@link Object} fails. 40 * 41 * @version $Revision: 646780 $ $Date: 2008-04-10 14:48:07 +0200 (Thu, 10 Apr 2008) $ 42 * 43 * @author Rodney Waldhoff 44 * @author Stephen Colebourne 45 * @author Anonymous 46 */ 47 public abstract class AbstractTestObject extends BulkTest { 48 49 /** Current major release for Collections */ 50 public static final int COLLECTIONS_MAJOR_VERSION = 3; 51 52 /** 53 * JUnit constructor. 54 * 55 * @param testName the test class name 56 */ AbstractTestObject(String testName)57 public AbstractTestObject(String testName) { 58 super(testName); 59 } 60 61 //----------------------------------------------------------------------- 62 /** 63 * Implement this method to return the object to test. 64 * 65 * @return the object to test 66 */ makeObject()67 public abstract Object makeObject(); 68 69 /** 70 * Override this method if a subclass is testing an object 71 * that cannot serialize an "empty" Collection. 72 * (e.g. Comparators have no contents) 73 * 74 * @return true 75 */ supportsEmptyCollections()76 public boolean supportsEmptyCollections() { 77 return true; 78 } 79 80 /** 81 * Override this method if a subclass is testing an object 82 * that cannot serialize a "full" Collection. 83 * (e.g. Comparators have no contents) 84 * 85 * @return true 86 */ supportsFullCollections()87 public boolean supportsFullCollections() { 88 return true; 89 } 90 91 /** 92 * Is serialization testing supported. 93 * Default is true. 94 */ isTestSerialization()95 public boolean isTestSerialization() { 96 return true; 97 } 98 99 /** 100 * Returns true to indicate that the collection supports equals() comparisons. 101 * This implementation returns true; 102 */ isEqualsCheckable()103 public boolean isEqualsCheckable() { 104 return true; 105 } 106 107 //----------------------------------------------------------------------- testObjectEqualsSelf()108 public void testObjectEqualsSelf() { 109 Object obj = makeObject(); 110 assertEquals("A Object should equal itself", obj, obj); 111 } 112 testEqualsNull()113 public void testEqualsNull() { 114 Object obj = makeObject(); 115 assertEquals(false, obj.equals(null)); // make sure this doesn't throw NPE either 116 } 117 testObjectHashCodeEqualsSelfHashCode()118 public void testObjectHashCodeEqualsSelfHashCode() { 119 Object obj = makeObject(); 120 assertEquals("hashCode should be repeatable", obj.hashCode(), obj.hashCode()); 121 } 122 testObjectHashCodeEqualsContract()123 public void testObjectHashCodeEqualsContract() { 124 Object obj1 = makeObject(); 125 if (obj1.equals(obj1)) { 126 assertEquals( 127 "[1] When two objects are equal, their hashCodes should be also.", 128 obj1.hashCode(), obj1.hashCode()); 129 } 130 Object obj2 = makeObject(); 131 if (obj1.equals(obj2)) { 132 assertEquals( 133 "[2] When two objects are equal, their hashCodes should be also.", 134 obj1.hashCode(), obj2.hashCode()); 135 assertTrue( 136 "When obj1.equals(obj2) is true, then obj2.equals(obj1) should also be true", 137 obj2.equals(obj1)); 138 } 139 } 140 testSerializeDeserializeThenCompare()141 public void testSerializeDeserializeThenCompare() throws Exception { 142 Object obj = makeObject(); 143 if (obj instanceof Serializable && isTestSerialization()) { 144 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 145 ObjectOutputStream out = new ObjectOutputStream(buffer); 146 out.writeObject(obj); 147 out.close(); 148 149 ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray())); 150 Object dest = in.readObject(); 151 in.close(); 152 if (isEqualsCheckable()) { 153 assertEquals("obj != deserialize(serialize(obj))", obj, dest); 154 } 155 } 156 } 157 158 /** 159 * Sanity check method, makes sure that any Serializable 160 * class can be serialized and de-serialized in memory, 161 * using the handy makeObject() method 162 * 163 * @throws IOException 164 * @throws ClassNotFoundException 165 */ testSimpleSerialization()166 public void testSimpleSerialization() throws Exception { 167 Object o = makeObject(); 168 if (o instanceof Serializable && isTestSerialization()) { 169 byte[] objekt = writeExternalFormToBytes((Serializable) o); 170 Object p = readExternalFormFromBytes(objekt); 171 } 172 } 173 174 /** 175 * Tests serialization by comparing against a previously stored version in CVS. 176 * If the test object is serializable, confirm that a canonical form exists. 177 */ testCanonicalEmptyCollectionExists()178 public void testCanonicalEmptyCollectionExists() { 179 if (supportsEmptyCollections() && isTestSerialization() && !skipSerializedCanonicalTests()) { 180 Object object = makeObject(); 181 if (object instanceof Serializable) { 182 String name = getCanonicalEmptyCollectionName(object); 183 assertTrue( 184 "Canonical empty collection (" + name + ") is not in CVS", 185 new File(name).exists()); 186 } 187 } 188 } 189 190 /** 191 * Tests serialization by comparing against a previously stored version in CVS. 192 * If the test object is serializable, confirm that a canonical form exists. 193 */ testCanonicalFullCollectionExists()194 public void testCanonicalFullCollectionExists() { 195 if (supportsFullCollections() && isTestSerialization() && !skipSerializedCanonicalTests()) { 196 Object object = makeObject(); 197 if (object instanceof Serializable) { 198 String name = getCanonicalFullCollectionName(object); 199 assertTrue( 200 "Canonical full collection (" + name + ") is not in CVS", 201 new File(name).exists()); 202 } 203 } 204 } 205 206 // protected implementation 207 //----------------------------------------------------------------------- 208 /** 209 * Get the version of Collections that this object tries to 210 * maintain serialization compatibility with. Defaults to 1, the 211 * earliest Collections version. (Note: some collections did not 212 * even exist in this version). 213 * 214 * This constant makes it possible for TestMap (and other subclasses, 215 * if necessary) to automatically check CVS for a versionX copy of a 216 * Serialized object, so we can make sure that compatibility is maintained. 217 * See, for example, TestMap.getCanonicalFullMapName(Map map). 218 * Subclasses can override this variable, indicating compatibility 219 * with earlier Collections versions. 220 * 221 * @return The version, or <code>null</code> if this object shouldn't be 222 * tested for compatibility with previous versions. 223 */ getCompatibilityVersion()224 public String getCompatibilityVersion() { 225 return "1"; 226 } 227 getCanonicalEmptyCollectionName(Object object)228 protected String getCanonicalEmptyCollectionName(Object object) { 229 StringBuffer retval = new StringBuffer(); 230 retval.append("data/test/"); 231 String colName = object.getClass().getName(); 232 colName = colName.substring(colName.lastIndexOf(".") + 1, colName.length()); 233 retval.append(colName); 234 retval.append(".emptyCollection.version"); 235 retval.append(getCompatibilityVersion()); 236 retval.append(".obj"); 237 return retval.toString(); 238 } 239 getCanonicalFullCollectionName(Object object)240 protected String getCanonicalFullCollectionName(Object object) { 241 StringBuffer retval = new StringBuffer(); 242 retval.append("data/test/"); 243 String colName = object.getClass().getName(); 244 colName = colName.substring(colName.lastIndexOf(".") + 1, colName.length()); 245 retval.append(colName); 246 retval.append(".fullCollection.version"); 247 retval.append(getCompatibilityVersion()); 248 retval.append(".obj"); 249 return retval.toString(); 250 } 251 252 /** 253 * Write a Serializable or Externalizable object as 254 * a file at the given path. NOT USEFUL as part 255 * of a unit test; this is just a utility method 256 * for creating disk-based objects in CVS that can become 257 * the basis for compatibility tests using 258 * readExternalFormFromDisk(String path) 259 * 260 * @param o Object to serialize 261 * @param path path to write the serialized Object 262 * @exception IOException 263 */ writeExternalFormToDisk(Serializable o, String path)264 protected void writeExternalFormToDisk(Serializable o, String path) throws IOException { 265 FileOutputStream fileStream = new FileOutputStream(path); 266 writeExternalFormToStream(o, fileStream); 267 } 268 269 /** 270 * Converts a Serializable or Externalizable object to 271 * bytes. Useful for in-memory tests of serialization 272 * 273 * @param o Object to convert to bytes 274 * @return serialized form of the Object 275 * @exception IOException 276 */ writeExternalFormToBytes(Serializable o)277 protected byte[] writeExternalFormToBytes(Serializable o) throws IOException { 278 ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); 279 writeExternalFormToStream(o, byteStream); 280 return byteStream.toByteArray(); 281 } 282 283 /** 284 * Reads a Serialized or Externalized Object from disk. 285 * Useful for creating compatibility tests between 286 * different CVS versions of the same class 287 * 288 * @param path path to the serialized Object 289 * @return the Object at the given path 290 * @exception IOException 291 * @exception ClassNotFoundException 292 */ readExternalFormFromDisk(String path)293 protected Object readExternalFormFromDisk(String path) throws IOException, ClassNotFoundException { 294 FileInputStream stream = new FileInputStream(path); 295 return readExternalFormFromStream(stream); 296 } 297 298 /** 299 * Read a Serialized or Externalized Object from bytes. 300 * Useful for verifying serialization in memory. 301 * 302 * @param b byte array containing a serialized Object 303 * @return Object contained in the bytes 304 * @exception IOException 305 * @exception ClassNotFoundException 306 */ readExternalFormFromBytes(byte[] b)307 protected Object readExternalFormFromBytes(byte[] b) throws IOException, ClassNotFoundException { 308 ByteArrayInputStream stream = new ByteArrayInputStream(b); 309 return readExternalFormFromStream(stream); 310 } 311 skipSerializedCanonicalTests()312 protected boolean skipSerializedCanonicalTests() { 313 return Boolean.getBoolean("org.apache.commons.collections:with-clover"); 314 } 315 316 // private implementation 317 //----------------------------------------------------------------------- readExternalFormFromStream(InputStream stream)318 private Object readExternalFormFromStream(InputStream stream) throws IOException, ClassNotFoundException { 319 ObjectInputStream oStream = new ObjectInputStream(stream); 320 return oStream.readObject(); 321 } 322 writeExternalFormToStream(Serializable o, OutputStream stream)323 private void writeExternalFormToStream(Serializable o, OutputStream stream) throws IOException { 324 ObjectOutputStream oStream = new ObjectOutputStream(stream); 325 oStream.writeObject(o); 326 } 327 328 } 329