001: /*
002: * Copyright (C) 2005 Joe Walnes.
003: * Copyright (C) 2006, 2007 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 02. February 2005 by Joe Walnes
011: */
012: package com.thoughtworks.acceptance;
013:
014: import com.thoughtworks.xstream.testutil.CallLog;
015:
016: import java.io.ByteArrayInputStream;
017: import java.io.ByteArrayOutputStream;
018: import java.io.IOException;
019: import java.io.ObjectInputStream;
020: import java.io.ObjectInputValidation;
021: import java.io.ObjectOutputStream;
022: import java.io.Serializable;
023:
024: public class SerializationCallbackOrderTest extends
025: AbstractAcceptanceTest {
026:
027: // static so it can be accessed by objects under test, without them needing a reference back to the testcase
028: private static CallLog log = new CallLog();
029:
030: protected void setUp() throws Exception {
031: super .setUp();
032: log.reset();
033: }
034:
035: // --- Sample class hiearchy
036:
037: public static class Base implements Serializable {
038:
039: private void writeObject(ObjectOutputStream out)
040: throws IOException {
041: log.actual("Base.writeObject() start");
042: out.defaultWriteObject();
043: log.actual("Base.writeObject() end");
044: }
045:
046: private void readObject(ObjectInputStream in)
047: throws IOException, ClassNotFoundException {
048: log.actual("Base.readObject() start");
049: in.defaultReadObject();
050: log.actual("Base.readObject() end");
051: }
052:
053: private Object writeReplace() {
054: log.actual("Base.writeReplace()");
055: return this ;
056: }
057:
058: private Object readResolve() {
059: log.actual("Base.readResolve()");
060: return this ;
061: }
062: }
063:
064: public static class Child extends Base implements Serializable {
065:
066: private void writeObject(ObjectOutputStream out)
067: throws IOException {
068: log.actual("Child.writeObject() start");
069: out.defaultWriteObject();
070: log.actual("Child.writeObject() end");
071: }
072:
073: private void readObject(ObjectInputStream in)
074: throws IOException, ClassNotFoundException {
075: log.actual("Child.readObject() start");
076: in.defaultReadObject();
077: log.actual("Child.readObject() end");
078: }
079:
080: private Object writeReplace() {
081: log.actual("Child.writeReplace()");
082: return this ;
083: }
084:
085: private Object readResolve() {
086: log.actual("Child.readResolve()");
087: return this ;
088: }
089: }
090:
091: // --- Convenience wrappers around Java Object Serialization
092:
093: private byte[] javaSerialize(Object object) throws IOException {
094: ByteArrayOutputStream bytes = new ByteArrayOutputStream();
095: ObjectOutputStream objectOutputStream = new ObjectOutputStream(
096: bytes);
097: objectOutputStream.writeObject(object);
098: objectOutputStream.close();
099: return bytes.toByteArray();
100: }
101:
102: private Object javaDeserialize(byte[] data) throws IOException,
103: ClassNotFoundException {
104: ObjectInputStream objectInputStream = new ObjectInputStream(
105: new ByteArrayInputStream(data));
106: return objectInputStream.readObject();
107: }
108:
109: // --- Tests
110:
111: public void testJavaSerialization() throws IOException {
112: // expectations
113: log.expect("Child.writeReplace()");
114: log.expect("Base.writeObject() start");
115: log.expect("Base.writeObject() end");
116: log.expect("Child.writeObject() start");
117: log.expect("Child.writeObject() end");
118:
119: // execute
120: javaSerialize(new Child());
121:
122: // verify
123: log.verify();
124: }
125:
126: public void testXStreamSerialization() {
127: // expectations
128: log.expect("Child.writeReplace()");
129: log.expect("Base.writeObject() start");
130: log.expect("Base.writeObject() end");
131: log.expect("Child.writeObject() start");
132: log.expect("Child.writeObject() end");
133:
134: // execute
135: xstream.toXML(new Child());
136:
137: // verify
138: log.verify();
139: }
140:
141: public void testJavaDeserialization() throws IOException,
142: ClassNotFoundException {
143: // setup
144: byte[] data = javaSerialize(new Child());
145: log.reset();
146:
147: // expectations
148: log.expect("Base.readObject() start");
149: log.expect("Base.readObject() end");
150: log.expect("Child.readObject() start");
151: log.expect("Child.readObject() end");
152: log.expect("Child.readResolve()");
153:
154: // execute
155: javaDeserialize(data);
156:
157: // verify
158: log.verify();
159: }
160:
161: public void testXStreamDeserialization() {
162: // setup
163: String data = xstream.toXML(new Child());
164: log.reset();
165:
166: // expectations
167: log.expect("Base.readObject() start");
168: log.expect("Base.readObject() end");
169: log.expect("Child.readObject() start");
170: log.expect("Child.readObject() end");
171: log.expect("Child.readResolve()");
172:
173: // execute
174: xstream.fromXML(data);
175:
176: // verify
177: log.verify();
178: }
179:
180: public static class ParentNotTransient implements Serializable {
181:
182: public int somethingNotTransient;
183:
184: public ParentNotTransient(int somethingNotTransient) {
185: this .somethingNotTransient = somethingNotTransient;
186: }
187:
188: }
189:
190: public static class ChildWithTransient extends ParentNotTransient
191: implements Serializable {
192:
193: public transient int somethingTransient;
194:
195: public ChildWithTransient(int somethingNotTransient,
196: int somethingTransient) {
197: super (somethingNotTransient);
198: this .somethingTransient = somethingTransient;
199: }
200:
201: private void readObject(ObjectInputStream s)
202: throws IOException, ClassNotFoundException {
203: s.defaultReadObject();
204: somethingTransient = 99999;
205: }
206: }
207:
208: public void testCallsReadObjectEvenWithoutNonTransientFields() {
209: xstream.alias("parent", ParentNotTransient.class);
210: xstream.alias("child", ChildWithTransient.class);
211:
212: Object in = new ChildWithTransient(10, 22222);
213: String expectedXml = ""
214: + "<child serialization=\"custom\">\n"
215: + " <parent>\n"
216: + " <default>\n"
217: + " <somethingNotTransient>10</somethingNotTransient>\n"
218: + " </default>\n" + " </parent>\n" + " <child>\n"
219: + " <default/>\n" + " </child>\n" + "</child>";
220:
221: String xml = xstream.toXML(in);
222: assertEquals(expectedXml, xml);
223:
224: ChildWithTransient childWithTransient = (ChildWithTransient) xstream
225: .fromXML(xml);
226:
227: assertEquals(10, childWithTransient.somethingNotTransient);
228: assertEquals(99999, childWithTransient.somethingTransient);
229: }
230:
231: public static class SomethingThatValidates implements Serializable {
232:
233: private void readObject(ObjectInputStream s) throws IOException {
234:
235: final int LOW_PRIORITY = -5;
236: final int MEDIUM_PRIORITY = 0;
237: final int HIGH_PRIORITY = 5;
238:
239: s.registerValidation(new ObjectInputValidation() {
240: public void validateObject() {
241: log.actual("validateObject() medium priority 1");
242: }
243: }, MEDIUM_PRIORITY);
244:
245: s.registerValidation(new ObjectInputValidation() {
246: public void validateObject() {
247: log.actual("validateObject() high priority");
248: }
249: }, HIGH_PRIORITY);
250:
251: s.registerValidation(new ObjectInputValidation() {
252: public void validateObject() {
253: log.actual("validateObject() low priority");
254: }
255: }, LOW_PRIORITY);
256:
257: s.registerValidation(new ObjectInputValidation() {
258: public void validateObject() {
259: log.actual("validateObject() medium priority 2");
260: }
261: }, MEDIUM_PRIORITY);
262: }
263:
264: private Object readResolve() {
265: log.actual("readResolve()");
266: return this ;
267: }
268: }
269:
270: public void testJavaSerializationValidatesObjectIsCalledInPriorityOrder()
271: throws IOException, ClassNotFoundException {
272: // expect
273: log.expect("readResolve()");
274: log.expect("validateObject() high priority");
275: log.expect("validateObject() medium priority 2");
276: log.expect("validateObject() medium priority 1");
277: log.expect("validateObject() low priority");
278:
279: // execute
280: javaDeserialize(javaSerialize(new SomethingThatValidates()));
281:
282: // verify
283: log.verify();
284: }
285:
286: public void testXStreamSerializationValidatesObjectIsCalledInPriorityOrder() {
287: // expect
288: log.expect("readResolve()");
289: log.expect("validateObject() high priority");
290: log.expect("validateObject() medium priority 2");
291: log.expect("validateObject() medium priority 1");
292: log.expect("validateObject() low priority");
293:
294: // execute
295: xstream.fromXML(xstream.toXML(new SomethingThatValidates()));
296:
297: // verify
298: log.verify();
299: }
300:
301: public static class UnserializableParent {
302: public int x;
303:
304: public UnserializableParent() {
305: x = 5;
306: }
307: }
308:
309: public static class CustomSerializableChild extends
310: UnserializableParent implements Serializable {
311: public int y;
312:
313: public CustomSerializableChild() {
314: y = 10;
315: }
316:
317: private void writeObject(ObjectOutputStream stream)
318: throws IOException {
319: log.actual("Child.writeObject() start");
320: stream.defaultWriteObject();
321: log.actual("Child.writeObject() end");
322: }
323:
324: private void readObject(ObjectInputStream stream)
325: throws IOException, ClassNotFoundException {
326: log.actual("Child.readObject() start");
327: stream.defaultReadObject();
328: log.actual("Child.readObject() end");
329: }
330:
331: private Object writeReplace() {
332: log.actual("Child.writeReplace()");
333: return this ;
334: }
335:
336: private Object readResolve() {
337: log.actual("Child.readResolve()");
338: return this ;
339: }
340: }
341:
342: public void testFieldsOfUnserializableParentsArePreserved() {
343: xstream.alias("parent", UnserializableParent.class);
344: xstream.alias("child", CustomSerializableChild.class);
345:
346: CustomSerializableChild child = new CustomSerializableChild();
347: String expected = "" + "<child serialization=\"custom\">\n"
348: + " <unserializable-parents>\n" + " <x>5</x>\n"
349: + " </unserializable-parents>\n" + " <child>\n"
350: + " <default>\n" + " <y>10</y>\n"
351: + " </default>\n" + " </child>\n" + "</child>";
352:
353: CustomSerializableChild serialized = (CustomSerializableChild) assertBothWays(
354: child, expected);
355: assertEquals(5, serialized.x);
356: assertEquals(10, serialized.y);
357: }
358:
359: public static class SerializableGrandChild extends
360: CustomSerializableChild implements Serializable {
361: public int z;
362:
363: public SerializableGrandChild() {
364: super ();
365: z = 42;
366: }
367: }
368:
369: public void testUnserializableParentsAreWrittenOnlyOnce() {
370: xstream.alias("parent", UnserializableParent.class);
371: xstream.alias("child", CustomSerializableChild.class);
372: xstream.alias("grandchild", SerializableGrandChild.class);
373:
374: SerializableGrandChild grandChild = new SerializableGrandChild();
375: String expected = ""
376: + "<grandchild serialization=\"custom\">\n"
377: + " <unserializable-parents>\n" + " <x>5</x>\n"
378: + " </unserializable-parents>\n" + " <child>\n"
379: + " <default>\n" + " <y>10</y>\n"
380: + " </default>\n" + " </child>\n"
381: + " <grandchild>\n" + " <default>\n"
382: + " <z>42</z>\n" + " </default>\n"
383: + " </grandchild>\n" + "</grandchild>";
384:
385: SerializableGrandChild serialized = (SerializableGrandChild) assertBothWays(
386: grandChild, expected);
387: assertEquals(5, serialized.x);
388: assertEquals(10, serialized.y);
389: assertEquals(42, serialized.z);
390: }
391:
392: public void testXStreamSerializationForObjectsWithUnserializableParents() {
393: // expectations
394: log.expect("Child.writeReplace()");
395: log.expect("Child.writeObject() start");
396: log.expect("Child.writeObject() end");
397:
398: // execute
399: xstream.toXML(new CustomSerializableChild());
400:
401: // verify
402: log.verify();
403: }
404:
405: public void testXStreamDeserializationForObjectsWithUnserializableParents() {
406: // setup
407: String data = xstream.toXML(new CustomSerializableChild());
408: log.reset();
409:
410: // expectations
411: log.expect("Child.readObject() start");
412: log.expect("Child.readObject() end");
413: log.expect("Child.readResolve()");
414:
415: // execute
416: xstream.fromXML(data);
417:
418: // verify
419: log.verify();
420: }
421:
422: }
|