/common/common-lang/src/test/java/com/twelvemonkeys/lang/ObjectAbstractTestCase.java

https://github.com/conceptboard/TwelveMonkeys · Java · 309 lines · 169 code · 43 blank · 97 comment · 19 complexity · e356ca6a276bb6537d9d7e656a8c4cb6 MD5 · raw file

  1. package com.twelvemonkeys.lang;
  2. import org.junit.Test;
  3. import java.io.*;
  4. import java.lang.reflect.Method;
  5. import static org.junit.Assert.*;
  6. /**
  7. * AbstractObjectTestCase
  8. * <p/>
  9. *
  10. * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
  11. * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/lang/ObjectAbstractTestCase.java#1 $
  12. */
  13. public abstract class ObjectAbstractTestCase {
  14. // TODO: See com.tm.util.ObjectAbstractTestCase
  15. // TODO: The idea is that this should be some generic base-class that
  16. // implements the basic object tests
  17. // TODO: Create Serializable test similar way
  18. // TODO: Create Comparable test similar way
  19. /**
  20. * Returns an instance of the class we are testing.
  21. * Implement this method to return the object to test.
  22. *
  23. * @return the object to test
  24. */
  25. protected abstract Object makeObject();
  26. // TODO: Can we really do serious testing with just one object?
  27. // TODO: How can we make sure we create equal or different objects?!
  28. //protected abstract Object makeDifferentObject(Object pObject);
  29. //protected abstract Object makeEqualObject(Object pObject);
  30. @Test
  31. public void testToString() {
  32. assertNotNull(makeObject().toString());
  33. // TODO: What more can we test?
  34. }
  35. // TODO: assert that either BOTH or NONE of equals/hashcode is overridden
  36. @Test
  37. public void testEqualsHashCode(){
  38. Object obj = makeObject();
  39. Class cl = obj.getClass();
  40. if (isEqualsOverriden(cl)) {
  41. assertTrue("Class " + cl.getName() + " implements equals but not hashCode", isHashCodeOverriden(cl));
  42. }
  43. else if (isHashCodeOverriden(cl)) {
  44. assertTrue("Class " + cl.getName() + " implements hashCode but not equals", isEqualsOverriden(cl));
  45. }
  46. }
  47. protected static boolean isEqualsOverriden(Class pClass) {
  48. return getDeclaredMethod(pClass, "equals", new Class[]{Object.class}) != null;
  49. }
  50. protected static boolean isHashCodeOverriden(Class pClass) {
  51. return getDeclaredMethod(pClass, "hashCode", null) != null;
  52. }
  53. private static Method getDeclaredMethod(Class pClass, String pName, Class[] pArameters) {
  54. try {
  55. return pClass.getDeclaredMethod(pName, pArameters);
  56. }
  57. catch (NoSuchMethodException ignore) {
  58. return null;
  59. }
  60. }
  61. @Test
  62. public void testObjectEqualsSelf() {
  63. Object obj = makeObject();
  64. assertEquals("An Object should equal itself", obj, obj);
  65. }
  66. @Test
  67. public void testEqualsNull() {
  68. Object obj = makeObject();
  69. // NOTE: Makes sure this doesn't throw NPE either
  70. //noinspection ObjectEqualsNull
  71. assertFalse("An object should never equal null", obj.equals(null));
  72. }
  73. @Test
  74. public void testObjectHashCodeEqualsSelfHashCode() {
  75. Object obj = makeObject();
  76. assertEquals("hashCode should be repeatable", obj.hashCode(), obj.hashCode());
  77. }
  78. @Test
  79. public void testObjectHashCodeEqualsContract() {
  80. Object obj1 = makeObject();
  81. if (obj1.equals(obj1)) {
  82. assertEquals(
  83. "[1] When two objects are equal, their hashCodes should be also.",
  84. obj1.hashCode(), obj1.hashCode());
  85. }
  86. // TODO: Make sure we create at least one equal object, and one different object
  87. Object obj2 = makeObject();
  88. if (obj1.equals(obj2)) {
  89. assertEquals(
  90. "[2] When two objects are equal, their hashCodes should be also.",
  91. obj1.hashCode(), obj2.hashCode());
  92. assertTrue(
  93. "When obj1.equals(obj2) is true, then obj2.equals(obj1) should also be true",
  94. obj2.equals(obj1));
  95. }
  96. }
  97. /*
  98. public void testFinalize() {
  99. // TODO: Implement
  100. }
  101. */
  102. ////////////////////////////////////////////////////////////////////////////
  103. // Cloneable interface
  104. @Test
  105. public void testClone() throws Exception {
  106. Object obj = makeObject();
  107. if (obj instanceof Cloneable) {
  108. Class cl = obj.getClass();
  109. Method clone = findMethod(cl, "clone");
  110. // Disregard protected modifier
  111. // NOTE: This will throw a SecurityException if a SecurityManager
  112. // disallows access, but should not happen in a test context
  113. if (!clone.isAccessible()) {
  114. clone.setAccessible(true);
  115. }
  116. Object cloned = clone.invoke(obj);
  117. assertNotNull("Cloned object should never be null", cloned);
  118. // TODO: This can only be asserted if equals() test is based on
  119. // value equality, not reference (identity) equality
  120. // Maybe it's possible to do a reflective introspection of
  121. // the objects fields?
  122. if (isHashCodeOverriden(cl)) {
  123. assertEquals("Cloned object not equal", obj, cloned);
  124. }
  125. }
  126. }
  127. private static Method findMethod(Class pClass, String pName) throws NoSuchMethodException {
  128. if (pClass == null) {
  129. throw new IllegalArgumentException("class == null");
  130. }
  131. if (pName == null) {
  132. throw new IllegalArgumentException("name == null");
  133. }
  134. Class cl = pClass;
  135. while (cl != null) {
  136. try {
  137. return cl.getDeclaredMethod(pName, new Class[0]);
  138. }
  139. catch (NoSuchMethodException e) {
  140. }
  141. catch (SecurityException e) {
  142. }
  143. cl = cl.getSuperclass();
  144. }
  145. throw new NoSuchMethodException(pName + " in class " + pClass.getName());
  146. }
  147. ///////////////////////////////////////////////////////////////////////////
  148. // Serializable interface
  149. @Test
  150. public void testSerializeDeserializeThenCompare() throws Exception {
  151. Object obj = makeObject();
  152. if (obj instanceof Serializable) {
  153. ByteArrayOutputStream buffer = new ByteArrayOutputStream();
  154. ObjectOutputStream out = new ObjectOutputStream(buffer);
  155. try {
  156. out.writeObject(obj);
  157. }
  158. finally {
  159. out.close();
  160. }
  161. Object dest;
  162. ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()));
  163. try {
  164. dest = in.readObject();
  165. }
  166. finally {
  167. in.close();
  168. }
  169. // TODO: This can only be asserted if equals() test is based on
  170. // value equality, not reference (identity) equality
  171. // Maybe it's possible to do a reflective introspection of
  172. // the objects fields?
  173. if (isEqualsOverriden(obj.getClass())) {
  174. assertEquals("obj != deserialize(serialize(obj))", obj, dest);
  175. }
  176. }
  177. }
  178. /**
  179. * Sanity check method, makes sure that any {@code Serializable}
  180. * class can be serialized and de-serialized in memory,
  181. * using the handy makeObject() method
  182. *
  183. * @throws java.io.IOException
  184. * @throws ClassNotFoundException
  185. */
  186. @Test
  187. public void testSimpleSerialization() throws Exception {
  188. Object o = makeObject();
  189. if (o instanceof Serializable) {
  190. byte[] object = writeExternalFormToBytes((Serializable) o);
  191. readExternalFormFromBytes(object);
  192. }
  193. }
  194. /**
  195. * Write a Serializable or Externalizable object as
  196. * a file at the given path.
  197. * <em>NOT USEFUL as part
  198. * of a unit test; this is just a utility method
  199. * for creating disk-based objects in CVS that can become
  200. * the basis for compatibility tests using
  201. * readExternalFormFromDisk(String path)</em>
  202. *
  203. * @param o Object to serialize
  204. * @param path path to write the serialized Object
  205. * @exception java.io.IOException
  206. */
  207. protected void writeExternalFormToDisk(Serializable o, String path) throws IOException {
  208. FileOutputStream fileStream = new FileOutputStream(path);
  209. writeExternalFormToStream(o, fileStream);
  210. }
  211. /**
  212. * Converts a Serializable or Externalizable object to
  213. * bytes. Useful for in-memory tests of serialization
  214. *
  215. * @param o Object to convert to bytes
  216. * @return serialized form of the Object
  217. * @exception java.io.IOException
  218. */
  219. protected byte[] writeExternalFormToBytes(Serializable o) throws IOException {
  220. ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
  221. writeExternalFormToStream(o, byteStream);
  222. return byteStream.toByteArray();
  223. }
  224. /**
  225. * Reads a Serialized or Externalized Object from disk.
  226. * Useful for creating compatibility tests between
  227. * different CVS versions of the same class
  228. *
  229. * @param path path to the serialized Object
  230. * @return the Object at the given path
  231. * @exception java.io.IOException
  232. * @exception ClassNotFoundException
  233. */
  234. protected Object readExternalFormFromDisk(String path) throws IOException, ClassNotFoundException {
  235. FileInputStream stream = new FileInputStream(path);
  236. return readExternalFormFromStream(stream);
  237. }
  238. /**
  239. * Read a Serialized or Externalized Object from bytes.
  240. * Useful for verifying serialization in memory.
  241. *
  242. * @param b byte array containing a serialized Object
  243. * @return Object contained in the bytes
  244. * @exception java.io.IOException
  245. * @exception ClassNotFoundException
  246. */
  247. protected Object readExternalFormFromBytes(byte[] b) throws IOException, ClassNotFoundException {
  248. ByteArrayInputStream stream = new ByteArrayInputStream(b);
  249. return readExternalFormFromStream(stream);
  250. }
  251. // private implementation
  252. //-----------------------------------------------------------------------
  253. private Object readExternalFormFromStream(InputStream stream) throws IOException, ClassNotFoundException {
  254. ObjectInputStream oStream = new ObjectInputStream(stream);
  255. return oStream.readObject();
  256. }
  257. private void writeExternalFormToStream(Serializable o, OutputStream stream) throws IOException {
  258. ObjectOutputStream oStream = new ObjectOutputStream(stream);
  259. oStream.writeObject(o);
  260. }
  261. public static final class SanityTestTestCase extends ObjectAbstractTestCase {
  262. protected Object makeObject() {
  263. return new Cloneable() {};
  264. }
  265. }
  266. }