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 13:48:07 +0100 (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 */
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 */
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 */
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 */
87 public boolean supportsFullCollections() {
88 return true;
89 }
90
91 /**
92 * Is serialization testing supported.
93 * Default is true.
94 */
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 */
103 public boolean isEqualsCheckable() {
104 return true;
105 }
106
107 //-----------------------------------------------------------------------
108 public void testObjectEqualsSelf() {
109 Object obj = makeObject();
110 assertEquals("A Object should equal itself", obj, obj);
111 }
112
113 public void testEqualsNull() {
114 Object obj = makeObject();
115 assertEquals(false, obj.equals(null)); // make sure this doesn't throw NPE either
116 }
117
118 public void testObjectHashCodeEqualsSelfHashCode() {
119 Object obj = makeObject();
120 assertEquals("hashCode should be repeatable", obj.hashCode(), obj.hashCode());
121 }
122
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
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 */
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 */
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 */
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 */
224 public String getCompatibilityVersion() {
225 return "1";
226 }
227
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
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 */
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 */
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 */
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 */
307 protected Object readExternalFormFromBytes(byte[] b) throws IOException, ClassNotFoundException {
308 ByteArrayInputStream stream = new ByteArrayInputStream(b);
309 return readExternalFormFromStream(stream);
310 }
311
312 protected boolean skipSerializedCanonicalTests() {
313 return Boolean.getBoolean("org.apache.commons.collections:with-clover");
314 }
315
316 // private implementation
317 //-----------------------------------------------------------------------
318 private Object readExternalFormFromStream(InputStream stream) throws IOException, ClassNotFoundException {
319 ObjectInputStream oStream = new ObjectInputStream(stream);
320 return oStream.readObject();
321 }
322
323 private void writeExternalFormToStream(Serializable o, OutputStream stream) throws IOException {
324 ObjectOutputStream oStream = new ObjectOutputStream(stream);
325 oStream.writeObject(o);
326 }
327
328 }