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