Package org.jboss.test.marshalling

Source Code of org.jboss.test.marshalling.SimpleMarshallerTests$OrigRem2

/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.test.marshalling;

import static org.testng.AssertJUnit.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.NotSerializableException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectInputValidation;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.ObjectStreamField;
import java.io.Serializable;
import java.io.StreamCorruptedException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.jboss.marshalling.AnnotationClassExternalizerFactory;
import org.jboss.marshalling.ByteInput;
import org.jboss.marshalling.ByteOutput;
import org.jboss.marshalling.ClassExternalizerFactory;
import org.jboss.marshalling.ClassTable;
import org.jboss.marshalling.Externalize;
import org.jboss.marshalling.Externalizer;
import org.jboss.marshalling.FieldSetter;
import org.jboss.marshalling.Marshaller;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
import org.jboss.marshalling.ObjectInputStreamUnmarshaller;
import org.jboss.marshalling.ObjectOutputStreamMarshaller;
import org.jboss.marshalling.ObjectResolver;
import org.jboss.marshalling.ObjectTable;
import org.jboss.marshalling.SimpleClassResolver;
import org.jboss.marshalling.StreamHeader;
import org.jboss.marshalling.Unmarshaller;

import org.jboss.marshalling.river.RiverMarshaller;
import org.jboss.marshalling.river.RiverMarshallerFactory;
import org.jboss.marshalling.river.RiverUnmarshaller;
import org.jboss.marshalling.serial.SerialUnmarshaller;
import org.testng.SkipException;
import org.testng.annotations.Test;

/**
*
*/
@SuppressWarnings("ALL")
public final class SimpleMarshallerTests extends TestBase {

    public SimpleMarshallerTests(TestMarshallerProvider testMarshallerProvider, TestUnmarshallerProvider testUnmarshallerProvider, MarshallingConfiguration configuration) {
        super(testMarshallerProvider, testUnmarshallerProvider, configuration);
    }

    /**
     * Simple constructor for running one test at a time from an IDE.
     */
    public SimpleMarshallerTests() {
        super(new MarshallerFactoryTestMarshallerProvider(new RiverMarshallerFactory(), 3),
              new MarshallerFactoryTestUnmarshallerProvider(new RiverMarshallerFactory(), 3),
              getOneTestMarshallingConfiguration());
    }

    private static MarshallingConfiguration getOneTestMarshallingConfiguration() {
        final MarshallingConfiguration marshallingConfiguration = new MarshallingConfiguration();
        marshallingConfiguration.setVersion(3);
        return marshallingConfiguration;
    }

    @Test
    public void testNull() throws Throwable {
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(null);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertNull(unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testUnserializable() throws Throwable {
        try {
            runReadWriteTest(new ReadWriteTest() {
                public void runWrite(final Marshaller marshaller) throws Throwable {
                    marshaller.writeObject(new Object());
                }

                public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                }
            });
        } catch (NotSerializableException e) {
            // ok
            return;
        }
        fail("Missing exception");
    }

    public static final class SerializableWithFinalFields implements Serializable {

        private static final long serialVersionUID = 1L;

        @SuppressWarnings({ "FieldMayBeStatic" })
        private final String blah = "Blah blah!";

        public String getBlah() {
            return blah;
        }
    }

    @Test
    public void testSerializableWithFinalFields() throws Throwable {
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                final SerializableWithFinalFields obj = new SerializableWithFinalFields();
                marshaller.writeObject(obj);
                marshaller.writeObject(obj);
                final SerializableWithFinalFields obj2 = new SerializableWithFinalFields();
                marshaller.writeObjectUnshared(obj2);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final SerializableWithFinalFields obj = (SerializableWithFinalFields) unmarshaller.readObject();
                assertSame(obj, unmarshaller.readObject());
                final SerializableWithFinalFields obj2 = (SerializableWithFinalFields) unmarshaller.readObjectUnshared();
                assertEquals("Blah blah!", obj2.blah);
                assertEOF(unmarshaller);
            }
        });
    }

    public static class TestSerializable implements Serializable {

        private static final long serialVersionUID = -3834685845327229499L;

        private int first = 1234;
        private float second = 13.725f;
        private boolean argh = true;
        private byte[] third = new byte[] { 15, 2, 3 };
        private Object zap = Float.valueOf(5.5f);
        private Void foo = null;

        @SuppressWarnings({ "NonFinalFieldReferenceInEquals" })
        public boolean equals(final Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            final TestSerializable that = (TestSerializable) o;
            if (argh != that.argh) return false;
            if (first != that.first) return false;
            if (Float.compare(that.second, second) != 0) return false;
            if (foo != null ? !foo.equals(that.foo) : that.foo != null) return false;
            if (!Arrays.equals(third, that.third)) return false;
            if (!zap.equals(that.zap)) return false;
            return true;
        }

        public int hashCode() {
            return 0;
        }
        //        private void writeObject(ObjectOutputStream oos) throws IOException {
        //            final ObjectOutputStream.PutField field = oos.putFields();
        //            field.put("first", first);
        //            field.put("second", second);
        //            field.put("argh", argh);
        //            field.put("third", third);
        //            field.put("zap", zap);
        //            field.put("foo", foo);
        //            oos.writeFields();
        //        }
    }

    @Test
    public void testSimple() throws Throwable {
        final Serializable serializable = new TestSerializable();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static class TestSerializableNoWriteObjectNoReadObject extends TestSerializable {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double fourth = 1.23;
    }

    @Test
    public void testSerializableNoWriteObjectNoReadObject() throws Throwable {
        final Serializable serializable = new TestSerializableNoWriteObjectDefaultReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static class TestSerializableNoWriteObjectDefaultReadObject extends TestSerializable {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double fourth = 1.23;

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ois.defaultReadObject();
        }
    }

    @Test
    public void testSerializableNoWriteObjectDefaultReadObject() throws Throwable {
        final Serializable serializable = new TestSerializableNoWriteObjectDefaultReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static class TestSerializableNoWriteObjectGetFieldsReadObject extends TestSerializable {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double fifth = 2.34;

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ObjectInputStream.GetField getField = ois.readFields();
            fifth = (double) getField.get("fifth", 2.34);
        }
    }

    @Test
    public void testSerializableNoWriteObjectGetFieldsReadObject() throws Throwable {
        final Serializable serializable = new TestSerializableNoWriteObjectGetFieldsReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static class TestSerializableDefaultWriteObjectNoReadObject extends TestSerializable {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double sixth = 3.45;

        private void writeObject(ObjectOutputStream oos) throws IOException {
            oos.defaultWriteObject();
        }
    }

    @Test
    public void testSerializableDefaultWriteObjectNoReadObject() throws Throwable {
        final Serializable serializable = new TestSerializableDefaultWriteObjectNoReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializableDefaultWriteObjectDefaultReadObject extends TestSerializableDefaultWriteObjectNoReadObject {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double seventh = 4.56;

        private void writeObject(ObjectOutputStream oos) throws IOException {
            oos.defaultWriteObject();
        }

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ois.defaultReadObject();
        }
    }

    @Test
    public void testSerializableDefaultWriteObjectDefaultReadObject() throws Throwable {
        final Serializable serializable = new TestSerializableDefaultWriteObjectDefaultReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializableEmptyWriteObjectDefaultReadObject extends TestSerializableDefaultWriteObjectNoReadObject {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double seventh = 4.56;

        private void writeObject(ObjectOutputStream oos) throws IOException {
        }

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ois.defaultReadObject();
        }
    }

    @Test
    public void testSerializableEmptyWriteObjectDefaultReadObject() throws Throwable {
        final TestSerializableEmptyWriteObjectDefaultReadObject serializable = new TestSerializableEmptyWriteObjectDefaultReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                if (! (marshaller instanceof RiverMarshaller)) {
                    throw new SkipException("Test not relevant for " + marshaller);
                }
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                TestSerializableEmptyWriteObjectDefaultReadObject o = unmarshaller.readObject(TestSerializableEmptyWriteObjectDefaultReadObject.class);
                assertEquals(serializable.sixth, o.sixth);
                assertEquals(0.0, o.seventh);
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializableEmptyWriteObjectNoReadObject extends TestSerializableDefaultWriteObjectNoReadObject {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double seventh = 4.56;

        private void writeObject(ObjectOutputStream oos) throws IOException {
        }
    }

    @Test
    public void testSerializableEmptyWriteObjectNoReadObject() throws Throwable {
        final TestSerializableEmptyWriteObjectNoReadObject serializable = new TestSerializableEmptyWriteObjectNoReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                if (! (marshaller instanceof RiverMarshaller)) {
                    throw new SkipException("Test not relevant for " + marshaller);
                }
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                TestSerializableEmptyWriteObjectNoReadObject o = unmarshaller.readObject(TestSerializableEmptyWriteObjectNoReadObject.class);
                assertEquals(serializable.sixth, o.sixth);
                assertEquals(0.0, o.seventh);
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializableEmptyWriteObjectEmptyReadObject extends TestSerializableDefaultWriteObjectNoReadObject {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double seventh = 4.56;

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        }

        private void writeObject(ObjectOutputStream oos) throws IOException {
        }
    }

    @Test
    public void testSerializableEmptyWriteObjectEmptyReadObject() throws Throwable {
        final TestSerializableEmptyWriteObjectEmptyReadObject serializable = new TestSerializableEmptyWriteObjectEmptyReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                if (! (marshaller instanceof RiverMarshaller)) {
                    throw new SkipException("Test not relevant for " + marshaller);
                }
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                TestSerializableEmptyWriteObjectEmptyReadObject o = unmarshaller.readObject(TestSerializableEmptyWriteObjectEmptyReadObject.class);
                assertEquals(serializable.sixth, o.sixth);
                assertEquals(0.0, o.seventh);
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializableDefaultWriteObjectEmptyReadObject extends TestSerializableDefaultWriteObjectNoReadObject {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double seventh = 4.56;

        private void writeObject(ObjectOutputStream oos) throws IOException {
            oos.defaultWriteObject();
        }

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        }
    }

    @Test
    public void testSerializableDefaultWriteObjectEmptyReadObject() throws Throwable {
        final TestSerializableDefaultWriteObjectEmptyReadObject serializable = new TestSerializableDefaultWriteObjectEmptyReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                if (! (marshaller instanceof RiverMarshaller)) {
                    throw new SkipException("Test not relevant for " + marshaller);
                }
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                TestSerializableDefaultWriteObjectEmptyReadObject o = unmarshaller.readObject(TestSerializableDefaultWriteObjectEmptyReadObject.class);
                assertEquals(serializable.sixth, o.sixth);
                assertEquals(0.0, o.seventh);
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializableNoWriteObjectEmptyReadObject extends TestSerializableDefaultWriteObjectNoReadObject {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double seventh = 4.56;

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        }
    }

    @Test
    public void testSerializableNoWriteObjectEmptyReadObject() throws Throwable {
        final TestSerializableNoWriteObjectEmptyReadObject serializable = new TestSerializableNoWriteObjectEmptyReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                if (! (marshaller instanceof RiverMarshaller)) {
                    throw new SkipException("Test not relevant for " + marshaller);
                }
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                TestSerializableNoWriteObjectEmptyReadObject o = unmarshaller.readObject(TestSerializableNoWriteObjectEmptyReadObject.class);
                assertEquals(serializable.sixth, o.sixth);
                assertEquals(0.0, o.seventh);
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializableDefaultWriteObjectGetFieldsReadObject extends TestSerializableDefaultWriteObjectNoReadObject {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double eighth = 5.67;

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ObjectInputStream.GetField getField = ois.readFields();
            eighth = (double) getField.get("eighth", 5.67);
        }
    }

    @Test
    public void testSerializableDefaultWriteObjectGetFieldsReadObject() throws Throwable {
        final Serializable serializable = new TestSerializableDefaultWriteObjectGetFieldsReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializableEmptyWriteObjectGetFieldsReadObject extends TestSerializableDefaultWriteObjectNoReadObject {

        private static final long serialVersionUID = 3121360863878480344L;
        protected double eighth = 5.67;

        private void writeObject(ObjectOutputStream oos) throws IOException {
        }

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ObjectInputStream.GetField getField = ois.readFields();
            eighth = (double) getField.get("eighth", 0.0);
        }
    }

    @Test
    public void testSerializableEmptyWriteObjectGetFieldsReadObject() throws Throwable {
        final Serializable serializable = new TestSerializableEmptyWriteObjectGetFieldsReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                if (! (marshaller instanceof RiverMarshaller)) {
                    throw new SkipException("Test not relevant for " + marshaller);
                }
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static class TestSerializablePutFieldsWriteObjectNoReadObject extends TestSerializable {

        private static final long serialVersionUID = 1191166362124148545L;
        protected double ninth = 6.78;

        private void writeObject(ObjectOutputStream oos) throws IOException {
            final ObjectOutputStream.PutField field = oos.putFields();
            field.put("ninth", ninth);
            oos.writeFields();
        }
    }

    @Test
    public void testSimplePutFieldsWriteObjectNoReadObject() throws Throwable {
        final Serializable serializable = new TestSerializablePutFieldsWriteObjectNoReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static class TestSerializablePutFieldsWriteObjectEmptyReadObject extends TestSerializable {

        private static final long serialVersionUID = 1191166362124148545L;
        protected double ninth = 6.78;

        private void writeObject(ObjectOutputStream oos) throws IOException {
            final ObjectOutputStream.PutField field = oos.putFields();
            field.put("ninth", ninth);
            oos.writeFields();
        }

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        }
    }

    @Test
    public void testSimplePutFieldsWriteObjectEmptyReadObject() throws Throwable {
        final Serializable serializable = new TestSerializablePutFieldsWriteObjectEmptyReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                if (! (marshaller instanceof RiverMarshaller)) {
                    throw new SkipException("Test not relevant for " + marshaller);
                }
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                TestSerializablePutFieldsWriteObjectEmptyReadObject o = unmarshaller.readObject(TestSerializablePutFieldsWriteObjectEmptyReadObject.class);
                assertEquals(0.0, o.ninth);
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializablePutFieldsWriteObjectDefaultReadObject extends TestSerializablePutFieldsWriteObjectNoReadObject {

        private static final long serialVersionUID = 1191166362124148545L;
        protected double tenth = 7.89;

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ois.defaultReadObject();
        }
    }

    @Test
    public void testSerializablePutFieldsWriteObjectDefaultReadObject() throws Throwable {
        final Serializable serializable = new TestSerializablePutFieldsWriteObjectDefaultReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializablePutFieldsWriteObjectGetFieldsReadObject extends TestSerializable {

        private static final long serialVersionUID = 1191166362124148545L;
        protected double eleventh = 8.90;

        private void writeObject(ObjectOutputStream oos) throws IOException {
            final ObjectOutputStream.PutField field = oos.putFields();
            field.put("eleventh", eleventh);
            oos.writeFields();
        }

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ObjectInputStream.GetField getField = ois.readFields();
            eleventh = (double) getField.get("eleventh", 8.90);
        }
    }

    @Test
    public void testSerializablePutFieldsWriteObjectGetFieldsReadObject() throws Throwable {
        final Serializable serializable = new TestSerializablePutFieldsWriteObjectGetFieldsReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testString() throws Throwable {
        final String s = "This is a test!";
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(s);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(s, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testInteger() throws Throwable {
        final Integer i = Integer.valueOf(12345);
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(i);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(i, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializableThatReferencesSerializableOverridenReadObject implements Serializable {

        private static final long serialVersionUID = 3131360863878480344L;
        TestSerializableOverridenReadObject anSerializableOverridenReadObject;
        Integer balance;

        public boolean equals(Object obj) {
            if (obj == this) return true;
            if (!(obj instanceof TestSerializableThatReferencesSerializableOverridenReadObject)) return false;
            TestSerializableThatReferencesSerializableOverridenReadObject that = (TestSerializableThatReferencesSerializableOverridenReadObject) obj;
            if (!safeEquals(balance, that.balance)) return false;
            if (!safeEquals(anSerializableOverridenReadObject, that.anSerializableOverridenReadObject)) return false;
            return true;
        }

        public int hashCode() {
            int result = 17;
            result = result * 31 + safeHashCode(balance);
            result = result * 31 + safeHashCode(anSerializableOverridenReadObject);
            return result;
        }

        private static int safeHashCode(Object obj) {
            return obj == null ? 0 : obj.hashCode();
        }

        private static boolean safeEquals(Object a, Object b) {
            return (a == b || (a != null && a.equals(b)));
        }
    }

    public static final class TestSerializableOverridenReadObject implements Serializable {

        private static final long serialVersionUID = 3141360863878480344L;
        String name;
        String ssn;
        transient boolean deserialized;

        public TestSerializableOverridenReadObject() {
            this.name = "Zamarreno";
            this.ssn = "234-567-8901";
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == this) return true;
            if (!(obj instanceof TestSerializableOverridenReadObject)) return false;
            TestSerializableOverridenReadObject that = (TestSerializableOverridenReadObject) obj;
            if (!name.equals(that.name)) return false;
            if (!ssn.equals(that.ssn)) return false;
            return true;
        }

        @Override
        public int hashCode() {
            int result = 17;
            result = result * 31 + name.hashCode();
            result = result * 31 + ssn.hashCode();
            return result;
        }

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ois.defaultReadObject();
            deserialized = true;
        }
    }

    @Test
    public void testSerializableThatReferencesSerializableOverridenReadObject() throws Throwable {
        final TestSerializableThatReferencesSerializableOverridenReadObject serializable = new TestSerializableThatReferencesSerializableOverridenReadObject();
        serializable.anSerializableOverridenReadObject = new TestSerializableOverridenReadObject();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestExternalizable implements Externalizable {

        private boolean ran;

        private static final long serialVersionUID = 2776810457096829768L;

        public void writeExternal(final ObjectOutput out) throws IOException {
            out.writeInt(54321);
            out.writeUTF("Hello!");
        }

        public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
            assertEquals(54321, in.readInt());
            assertEquals("Hello!", in.readUTF());
            assertTrue("No EOF", in.read() == -1);
            ran = true;
        }
    }

    public static final class TestExternalizableConstructed implements Externalizable {

        private final boolean ran;

        private static final long serialVersionUID = 2776810457096829768L;

        public TestExternalizableConstructed() {
            ran = false;
        }

        public TestExternalizableConstructed(final ObjectInput in) throws IOException {
            assertEquals(54321, in.readInt());
            assertEquals("Hello!", in.readUTF());
            assertTrue("No EOF", in.read() == -1);
            ran = true;
        }

        public void writeExternal(final ObjectOutput out) throws IOException {
            out.writeInt(54321);
            out.writeUTF("Hello!");
        }

        public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
        }
    }

    @Test
    public void testExternalizable() throws Throwable {
        final TestExternalizable ext = new TestExternalizable();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(ext);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final TestExternalizable extn = (TestExternalizable) unmarshaller.readObject();
                assertTrue("No EOF", unmarshaller.read() == -1);
                assertTrue("readExternal was not run", extn.ran);
            }
        });
        assertFalse("readExternal was run on the original", ext.ran);
    }

    @Test
    public void testExternalizableConstructed() throws Throwable {
        final TestExternalizableConstructed ext = new TestExternalizableConstructed();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(ext);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                if (unmarshaller instanceof ObjectInputStreamUnmarshaller) {
                    throw new SkipException("Test not relevant for " + unmarshaller);
                }
                final TestExternalizableConstructed extn = (TestExternalizableConstructed) unmarshaller.readObject();
                assertTrue("No EOF", unmarshaller.read() == -1);
                assertTrue("readExternal was not run", extn.ran);
            }
        });
        assertFalse("readExternal was run on the original", ext.ran);
    }

    @Test
    public void testObjectTable() throws Throwable {
        final AtomicBoolean writeRan = new AtomicBoolean();
        final AtomicBoolean readRan = new AtomicBoolean();
        final ObjectTable objectTable = new ObjectTable() {
            public Writer getObjectWriter(final Object object) {
                return new Writer() {
                    public void writeObject(final Marshaller marshaller, final Object object) throws IOException {
                        marshaller.writeInt(51423);
                        marshaller.writeUTF("Unga bunga!");
                        writeRan.set(true);
                    }
                };
            }

            public Object readObject(final Unmarshaller unmarshaller) throws IOException, ClassNotFoundException {
                assertEquals(51423, unmarshaller.readInt());
                assertEquals("Unga bunga!", unmarshaller.readUTF());
                assertEOF(unmarshaller);
                readRan.set(true);
                return new Object();
            }
        };
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setObjectTable(objectTable);
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(new Object());
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertNotNull(unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
        assertTrue("writeObject never run", writeRan.get());
        assertTrue("readObject never run", readRan.get());
    }

    @Test
    public void testClassTableSerializable() throws Throwable {
        final ClassTable classTable = new ClassTable() {
            public Writer getClassWriter(final Class<?> clazz) {
                if (clazz == TestSerializable.class) {
                    return new Writer() {
                        public void writeClass(final Marshaller marshaller, final Class<?> clazz) throws IOException {
                            marshaller.writeUTF("Testtttt");
                            marshaller.writeInt(949332);
                            marshaller.writeDouble(15.125);
                        }
                    };
                } else {
                    return null;
                }
            }

            public Class<?> readClass(final Unmarshaller unmarshaller) throws IOException, ClassNotFoundException {
                assertEquals("Testtttt", unmarshaller.readUTF());
                assertEquals(949332, unmarshaller.readInt());
                assertTrue(unmarshaller.readDouble() == 15.125);
                return TestSerializable.class;
            }
        };
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setClassTable(classTable);
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(new TestSerializable());
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertSame(TestSerializable.class, unmarshaller.readObject().getClass());
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testClassTableExternalizable() throws Throwable {
        final ClassTable classTable = new ClassTable() {
            public Writer getClassWriter(final Class<?> clazz) {
                if (clazz == TestExternalizable.class) {
                    return new Writer() {
                        public void writeClass(final Marshaller marshaller, final Class<?> clazz) throws IOException {
                            marshaller.writeUTF("Testtttt");
                            marshaller.writeInt(949332);
                            marshaller.writeDouble(15.125);
                        }
                    };
                } else {
                    return null;
                }
            }

            public Class<?> readClass(final Unmarshaller unmarshaller) throws IOException, ClassNotFoundException {
                assertEquals("Testtttt", unmarshaller.readUTF());
                assertEquals(949332, unmarshaller.readInt());
                assertTrue(unmarshaller.readDouble() == 15.125);
                return TestExternalizable.class;
            }
        };
        final TestExternalizable ext = new TestExternalizable();
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setClassTable(classTable);
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(ext);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final TestExternalizable extn = (TestExternalizable) unmarshaller.readObject();
                assertEOF(unmarshaller);
                assertTrue("readExternal was not run", extn.ran);
            }
        });
        assertFalse("readExternal was run on the original", ext.ran);
    }

    @Test
    public void testMultiWrite() throws Throwable {
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                final TestSerializable obj = new TestSerializable();
                marshaller.writeObject(obj);
                marshaller.writeObject(obj);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertSame(unmarshaller.readObject(), unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testMultiWrite2() throws Throwable {
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                final TestSerializable obj = new TestSerializable();
                marshaller.writeObjectUnshared(obj);
                marshaller.writeObject(obj);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertNotSame(unmarshaller.readObjectUnshared(), unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testMultiWrite3() throws Throwable {
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                final TestSerializable obj1 = new TestSerializable();
                final TestSerializable obj2 = new TestSerializable();
                final TestSerializable obj3 = new TestSerializable();
                final TestSerializable obj4 = new TestSerializable();
                final TestSerializable obj5 = new TestSerializable();
                marshaller.writeObject(obj1);
                marshaller.writeObject(obj1);
                marshaller.writeObject(obj1);
                marshaller.writeObject(obj2);
                marshaller.writeObject(obj3);
                marshaller.writeObject(obj2);
                marshaller.writeObject(obj3);
                marshaller.writeObject(obj4);
                marshaller.writeObject(obj1);
                marshaller.writeObject(obj4);
                marshaller.writeObject(obj2);
                marshaller.writeObject(obj3);
                marshaller.writeObject(obj5);
                marshaller.writeObject(obj1);
                marshaller.writeObject(obj5);
                marshaller.writeObject(obj4);
                marshaller.writeObject(obj3);
                marshaller.writeObject(obj2);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final TestSerializable obj1 = (TestSerializable) unmarshaller.readObject();
                assertSame(obj1, unmarshaller.readObject());
                assertSame(obj1, unmarshaller.readObject());
                final TestSerializable obj2 = (TestSerializable) unmarshaller.readObject();
                assertNotSame(obj1, obj2);
                final TestSerializable obj3 = (TestSerializable) unmarshaller.readObject();
                assertNotSame(obj1, obj3);
                assertNotSame(obj2, obj3);
                assertSame(obj2, unmarshaller.readObject());
                assertSame(obj3, unmarshaller.readObject());
                final TestSerializable obj4 = (TestSerializable) unmarshaller.readObject();
                assertNotSame(obj1, obj4);
                assertNotSame(obj2, obj4);
                assertNotSame(obj3, obj4);
                assertSame(obj1, unmarshaller.readObject());
                assertSame(obj4, unmarshaller.readObject());
                assertSame(obj2, unmarshaller.readObject());
                assertSame(obj3, unmarshaller.readObject());
                final TestSerializable obj5 = (TestSerializable) unmarshaller.readObject();
                assertNotSame(obj1, obj5);
                assertNotSame(obj2, obj5);
                assertNotSame(obj3, obj5);
                assertNotSame(obj4, obj5);
                assertSame(obj1, unmarshaller.readObject());
                assertSame(obj5, unmarshaller.readObject());
                assertSame(obj4, unmarshaller.readObject());
                assertSame(obj3, unmarshaller.readObject());
                assertSame(obj2, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testBooleanArray() throws Throwable {
        final boolean[] test = new boolean[50];
        test[0] = test[5] = test[6] = test[14] = test[24] = test[39] = test[49] = true;
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                boolean[] result = (boolean[]) unmarshaller.readObject();
                assertSame(result, unmarshaller.readObject());
                assertTrue(Arrays.equals(test, result));
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testByteArray() throws Throwable {
        final byte[] test = new byte[50];
        new Random().nextBytes(test);
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                byte[] result = (byte[]) unmarshaller.readObject();
                assertSame(result, unmarshaller.readObject());
                assertTrue(Arrays.equals(test, result));
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testShortArray() throws Throwable {
        final short[] test = new short[50];
        final Random rng = new Random();
        for (int i = 0; i < test.length; i++) {
            test[i] = (short) rng.nextInt();
        }
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                short[] result = (short[]) unmarshaller.readObject();
                assertSame(result, unmarshaller.readObject());
                assertTrue(Arrays.equals(test, result));
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testIntArray() throws Throwable {
        final int[] test = new int[50];
        final Random rng = new Random();
        for (int i = 0; i < test.length; i++) {
            test[i] = rng.nextInt();
        }
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                int[] result = (int[]) unmarshaller.readObject();
                assertSame(result, unmarshaller.readObject());
                assertTrue(Arrays.equals(test, result));
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testLongArray() throws Throwable {
        final long[] test = new long[50];
        final Random rng = new Random();
        for (int i = 0; i < test.length; i++) {
            test[i] = rng.nextLong();
        }
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                long[] result = (long[]) unmarshaller.readObject();
                assertSame(result, unmarshaller.readObject());
                assertTrue(Arrays.equals(test, result));
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testFloatArray() throws Throwable {
        final float[] test = new float[50];
        final Random rng = new Random();
        for (int i = 0; i < test.length; i++) {
            test[i] = rng.nextFloat();
        }
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                float[] result = (float[]) unmarshaller.readObject();
                assertSame(result, unmarshaller.readObject());
                assertTrue(Arrays.equals(test, result));
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testDoubleArray() throws Throwable {
        final double[] test = new double[50];
        final Random rng = new Random();
        for (int i = 0; i < test.length; i++) {
            test[i] = rng.nextDouble();
        }
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                double[] result = (double[]) unmarshaller.readObject();
                assertSame(result, unmarshaller.readObject());
                assertTrue(Arrays.equals(test, result));
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testCharArray() throws Throwable {
        final char[] test = new char[50];
        final Random rng = new Random();
        for (int i = 0; i < test.length; i++) {
            test[i] = (char) rng.nextInt();
        }
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                char[] result = (char[]) unmarshaller.readObject();
                assertSame(result, unmarshaller.readObject());
                assertTrue(Arrays.equals(test, result));
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testObjectArray() throws Throwable {
        final TestSerializable[] test = new TestSerializable[50];
        for (int i = 0; i < 50; i++) {
            test[i] = new TestSerializable();
        }
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final Object[] objArray = (Object[]) unmarshaller.readObject();
                assertTrue(Arrays.equals(test, objArray));
                assertSame(objArray, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testObjectArrayRecursion() throws Throwable {
        final Object[] test = new Object[5];
        for (int i = 0; i < 5; i++) {
            test[i] = test;
        }
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final Object[] test2 = (Object[]) unmarshaller.readObject();
                assertNotNull(test2[0]);
                assertNotNull(test2[1]);
                assertNotNull(test2[2]);
                assertNotNull(test2[3]);
                assertNotNull(test2[4]);
                assertSame(test2, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testHashMap() throws Throwable {
        final Map<String, String> map = new HashMap<String, String>();
        map.put("kejlwqewq", "qwejwqioprjweqiorjpofd");
        map.put("34890fdu90uq09rdewq", "wqeioqwdias90ifd0adfw");
        map.put("dsajkljwqej21309ejjfdasfda", "dsajkdqwoid");
        map.put("nczxm,ncoijd0q93wjdwdwq", " dsajkldwqj9edwqu");
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(map);
                marshaller.writeObject(map);
            }

            @SuppressWarnings({ "unchecked" })
            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final Map<String, String> map2 = (Map<String, String>) unmarshaller.readObject();
                assertEquals(map, map2);
                assertSame(map2, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testConcurrentHashMap() throws Throwable {
        final Map<String, String> map = new ConcurrentHashMap<String, String>();
        map.put("kejlwqewq", "qwejwqioprjweqiorjpofd");
        map.put("34890fdu90uq09rdewq", "wqeioqwdias90ifd0adfw");
        map.put("dsajkljwqej21309ejjfdasfda", "dsajkdqwoid");
        map.put("nczxm,ncoijd0q93wjdwdwq", " dsajkldwqj9edwqu");
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(map);
                marshaller.writeObject(map);
            }

            @SuppressWarnings({ "unchecked" })
            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final Map<String, String> map2 = (Map<String, String>) unmarshaller.readObject();
                assertEquals(map, map2);
                assertSame(map2, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    private static final class HashMapExternalizer implements Externalizer {

        private static final long serialVersionUID = 4923778660953773530L;

        @SuppressWarnings({ "unchecked" })
        public void writeExternal(final Object subject, final ObjectOutput output) throws IOException {
            final HashMap map = (HashMap) subject;
            output.writeInt(map.size());
            for (Map.Entry e : (Set<Map.Entry>) map.entrySet()) {
                output.writeObject(e.getKey());
                output.writeObject(e.getValue());
            }
        }

        @SuppressWarnings({ "unchecked" })
        public Object createExternal(final Class<?> subjectType, final ObjectInput input) throws IOException, ClassNotFoundException {
            final int size = input.readInt();
            final HashMap map = new HashMap(size * 2, 0.5f);
            for (int i = 0; i < size; i++) {
                map.put(input.readObject(), input.readObject());
            }
            return map;
        }
    }

    @Test
    public void testExternalizer() throws Throwable {
        final Map<String, String> map = new HashMap<String, String>();
        map.put("kejlwqewq", "qwejwqioprjweqiorjpofd");
        map.put("34890fdu90uq09rdewq", "wqeioqwdias90ifd0adfw");
        map.put("dsajkljwqej21309ejjfdasfda", "dsajkdqwoid");
        map.put("nczxm,ncoijd0q93wjdwdwq", " dsajkldwqj9edwqu");
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setClassExternalizerFactory(new ClassExternalizerFactory() {
                    public Externalizer getExternalizer(final Class<?> type) {
                        if (type == HashMap.class) {
                            return new HashMapExternalizer();
                        } else {
                            return null;
                        }
                    }
                });
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(map);
                marshaller.writeObject(map);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final Object m1 = unmarshaller.readObject();
                assertEquals(map, m1);
                assertSame(m1, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    public static class TestA implements Serializable {

        private static final long serialVersionUID = 4788787450574491652L;

        TestB testb;
    }

    public static class TestB implements Serializable {

        private static final long serialVersionUID = 4788787450574491652L;

        TestA testa;
    }

    @Test
    public void testCircularRefs() throws Throwable {
        final TestA testa = new TestA();
        final TestB testb = new TestB();
        testa.testb = testb;
        testb.testa = testa;
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(testa);
                marshaller.writeObject(testb);
                marshaller.writeObject(testa);
                marshaller.writeObject(testb);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final TestA ntesta = (TestA) unmarshaller.readObject();
                final TestB ntestb = (TestB) unmarshaller.readObject();
                assertSame(ntesta, unmarshaller.readObject());
                assertSame(ntestb, unmarshaller.readObject());
                assertSame(ntestb, ntesta.testb);
                assertSame(ntesta, ntestb.testa);
                assertEOF(unmarshaller);
            }
        });
    }

    public static class OuterClass implements Serializable {

        private static final long serialVersionUID = 4268208561523701594L;
        @SuppressWarnings({ "UnusedDeclaration" })
        private InnerClass inner;

        public class InnerClass implements Serializable {

            private static final long serialVersionUID = 26916326776519192L;
        }
    }

    @Test
    public void testInnerClass() throws Throwable {
        final OuterClass outerClass = new OuterClass();
        outerClass.inner = outerClass.new InnerClass();
        final OuterClass.InnerClass innerClass = new OuterClass().new InnerClass();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(outerClass);
                marshaller.writeObject(outerClass);
                marshaller.writeObject(innerClass);
                marshaller.writeObject(innerClass);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final OuterClass newOuterClass = (OuterClass) unmarshaller.readObject();
                final OuterClass newOuterClass2 = (OuterClass) unmarshaller.readObject();
                assertSame(newOuterClass, newOuterClass2);
                final OuterClass.InnerClass newInnerClass = (OuterClass.InnerClass) unmarshaller.readObject();
                final OuterClass.InnerClass newInnerClass2 = (OuterClass.InnerClass) unmarshaller.readObject();
                assertSame(newInnerClass, newInnerClass2);
                assertEOF(unmarshaller);
            }
        });
    }

    public static class TestC implements Externalizable {

        private static final long serialVersionUID = 4788787450574491652L;

        TestD testd;

        public void writeExternal(final ObjectOutput out) throws IOException {
            out.writeObject(testd);
        }

        public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
            testd = (TestD) in.readObject();
        }
    }

    public static class TestD implements Externalizable {

        private static final long serialVersionUID = 4788787450574491652L;

        TestC testc;

        public void writeExternal(final ObjectOutput out) throws IOException {
            out.writeObject(testc);
        }

        public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
            testc = (TestC) in.readObject();
        }
    }

    @Test
    public void testCircularRefsExt() throws Throwable {
        final TestC testc = new TestC();
        final TestD testd = new TestD();
        testc.testd = testd;
        testd.testc = testc;
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(testc);
                marshaller.writeObject(testd);
                marshaller.writeObject(testc);
                marshaller.writeObject(testd);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final TestC ntestc = (TestC) unmarshaller.readObject();
                final TestD ntestd = (TestD) unmarshaller.readObject();
                assertSame(ntestc, unmarshaller.readObject());
                assertSame(ntestd, unmarshaller.readObject());
                assertSame(ntestd, ntestc.testd);
                assertSame(ntestc, ntestd.testc);
                assertEOF(unmarshaller);
            }
        });
    }

    public enum Fruit {

        BANANA,
        APPLE,
        PEAR,
        ORANGE;
    }

    @Test
    public void testEnum1() throws Throwable {
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject("Blah");
                marshaller.writeObject(Fruit.APPLE);
                marshaller.writeObject(Boolean.TRUE);
                marshaller.writeObject(Fruit.PEAR);
                marshaller.writeObject("Goop");
                marshaller.writeObject(Fruit.APPLE);
                marshaller.writeObject(Fruit.PEAR);
                marshaller.writeObject(Fruit.APPLE);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals("Blah", unmarshaller.readObject());
                assertSame(Fruit.APPLE, unmarshaller.readObject());
                assertEquals(Boolean.TRUE, unmarshaller.readObject());
                assertSame(Fruit.PEAR, unmarshaller.readObject());
                assertEquals("Goop", unmarshaller.readObject());
                assertSame(Fruit.APPLE, unmarshaller.readObject());
                assertSame(Fruit.PEAR, unmarshaller.readObject());
                assertSame(Fruit.APPLE, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    @Test
    public void testTreeComp() throws Throwable {
        runReadWriteTest(new ReadWriteTest() {
                TreeMap<Integer, Integer> tree = new TreeMap<Integer, Integer>(new IntComp());

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(tree.comparator());
                tree.put(1, 1);
                marshaller.writeObject(tree);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                unmarshaller.readObject();
                TreeMap x = (TreeMap) unmarshaller.readObject();
                assertEquals(tree, x);
                assertTrue(x.comparator() instanceof IntComp);
            }
        });
    }

    public static class IntComp implements Comparator<Integer>, Serializable {

        public int compare(Integer o1, Integer o2) {
            return o1 == null ? 1 : o1.compareTo(o2);
        }

    }

    public interface Adder {

        int add(int amount);
    }

    public static final class TestInvocationHandler implements InvocationHandler, Serializable {

        private static final long serialVersionUID = 2808883573499129906L;

        public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
            return Integer.valueOf(((Integer) args[0]).intValue() + 1);
        }
    }

    @Test
    public void testProxy() throws Throwable {
        final TestInvocationHandler tih = new TestInvocationHandler();
        final Adder adder = (Adder) Proxy.newProxyInstance(SimpleMarshallerTests.class.getClassLoader(), new Class<?>[] { Adder.class }, tih);
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                assertEquals(42, adder.add(41));
                marshaller.writeObject(adder);
                marshaller.writeObject(adder);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                Adder adder = (Adder) unmarshaller.readObject();
                final Object other = unmarshaller.readObject();
                assertSame(adder, other);
                assertEquals(42, adder.add(41));
                assertEOF(unmarshaller);
            }
        });
    }

    public static final class TestSerializableWithTransientFields implements Serializable {

        private static final long serialVersionUID = -4063085698159274676L;
        private transient int value;
        private String name;
    }

    @Test
    public void testSerializableWithTransientFields() throws Throwable {
        final TestSerializableWithTransientFields orig = new TestSerializableWithTransientFields();
        orig.value = 123;
        orig.name = "Balahala";
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(orig);
                marshaller.writeObject(orig);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                TestSerializableWithTransientFields repl = (TestSerializableWithTransientFields) unmarshaller.readObject();
                assertSame(repl, unmarshaller.readObject());
                assertEquals(0, repl.value);
                assertEquals(orig.name, repl.name);
            }
        });
    }

    public static final class TestSerializableWithFields implements Serializable {

        private static final long serialVersionUID = -4063085698159274676L;

        private transient int value;
        private String name;

        private static final ObjectStreamField[] serialPersistentFields = {
                new ObjectStreamField("value", long.class),
                new ObjectStreamField("name", String.class),
        };

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            final ObjectInputStream.GetField field = ois.readFields();
            value = (int) field.get("value", 0L);
            name = (String) field.get("name", "");
        }

        private void writeObject(ObjectOutputStream oos) throws IOException {
            final ObjectOutputStream.PutField field = oos.putFields();
            field.put("value", (long) value);
            field.put("name", name);
            oos.writeFields();
        }
    }

    @Test
    public void testSerializableWithFields() throws Throwable {
        final TestSerializableWithFields orig = new TestSerializableWithFields();
        orig.value = 123;
        orig.name = "Balahala";
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(orig);
                marshaller.writeObject(orig);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                TestSerializableWithFields repl = (TestSerializableWithFields) unmarshaller.readObject();
                assertSame(repl, unmarshaller.readObject());
                assertEquals(orig.value, repl.value);
                assertEquals(orig.name, repl.name);
            }
        });
    }

    @Test
    public void testExternalizerAnnotation() throws Throwable {
        final TestExternalizerWithAnnotation subject = new TestExternalizerWithAnnotation("Title", 1234);
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setClassExternalizerFactory(new AnnotationClassExternalizerFactory());
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(subject);
                marshaller.writeObject(subject);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final TestExternalizerWithAnnotation returnedSubject = (TestExternalizerWithAnnotation) unmarshaller.readObject();
                assertSame(returnedSubject, unmarshaller.readObject());
                assertEquals(subject.getString(), returnedSubject.getString());
                assertEquals(subject.getV(), returnedSubject.getV());
            }
        });
    }

    @Externalize(TestAnnotationExternalizer.class)
    public static final class TestExternalizerWithAnnotation {

        private final String string;
        private final int v;

        public TestExternalizerWithAnnotation(final String string, final int v) {
            this.string = string;
            this.v = v;
        }

        public String getString() {
            return string;
        }

        public int getV() {
            return v;
        }
    }

    public static final class TestAnnotationExternalizer implements Serializable, Externalizer {

        private static final long serialVersionUID = 1L;

        public void writeExternal(final Object subject, final ObjectOutput output) throws IOException {
            final TestExternalizerWithAnnotation testSubject = (TestExternalizerWithAnnotation) subject;
            output.writeObject(testSubject.getString());
            output.writeInt(testSubject.getV());
        }

        public Object createExternal(final Class<?> subjectType, final ObjectInput input) throws IOException, ClassNotFoundException {
            return new TestExternalizerWithAnnotation((String) input.readObject(), input.readInt());
        }
    }

    public static final class OriginalClass implements Serializable {

        private static final long serialVersionUID = -1179299150244154246L;

        @SuppressWarnings({ "UnusedDeclaration" })
        private String blah = "Testing!";
    }

    public static final class ReplacementClass implements Serializable {

        private static final long serialVersionUID = -1179299150244154246L;

        private String blah = "Foo!";
    }

    @Test
    public void testClassReplace1() throws Throwable {
        final OriginalClass orig = new OriginalClass();
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setClassResolver(new SimpleClassResolver(getClass().getClassLoader()) {
                    public String getClassName(final Class<?> clazz) throws IOException {
                        if (clazz == OriginalClass.class) {
                            return super.getClassName(ReplacementClass.class);
                        } else {
                            return super.getClassName(clazz);
                        }
                    }
                });
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                if (marshaller instanceof ObjectOutputStreamMarshaller) {
                    throw new SkipException("Test not relevant for " + marshaller);
                }
                marshaller.writeObject(orig);
                marshaller.writeObject(orig);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final Object repl = unmarshaller.readObject();
                assertEquals(ReplacementClass.class, repl.getClass());
                assertSame(repl, unmarshaller.readObject());
                assertEquals(((ReplacementClass) repl).blah, "Testing!");
            }
        });
    }

    @Test
    public void testClassReplace2() throws Throwable {
        final OriginalClass orig = new OriginalClass();
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setClassResolver(new SimpleClassResolver(getClass().getClassLoader()) {
                    protected Class<?> loadClass(final String name) throws ClassNotFoundException {
                        if (OriginalClass.class.getName().equals(name)) {
                            return ReplacementClass.class;
                        } else {
                            return super.loadClass(name);
                        }
                    }
                });
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                if (marshaller instanceof ObjectOutputStreamMarshaller) {
                    throw new SkipException("Test not relevant for " + marshaller);
                }
                marshaller.writeObject(orig);
                marshaller.writeObject(orig);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                if (unmarshaller instanceof ObjectInputStreamUnmarshaller) {
                    throw new SkipException("Substituting class name on read does not work with " + unmarshaller);
                }
                final Object repl = unmarshaller.readObject();
                assertEquals(ReplacementClass.class, repl.getClass());
                assertSame(repl, unmarshaller.readObject());
                assertEquals(((ReplacementClass) repl).blah, "Testing!");
            }
        });
    }

    @Test
    public void testReset() throws Throwable {
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                final String o1 = "This is a test";
                final Integer o2 = Integer.valueOf(48392);
                marshaller.writeObject(o1);
                marshaller.writeObject(o2);
                marshaller.writeObject(o1);
                marshaller.writeObject(o2);
                marshaller.clearClassCache(); // clears instance cache too
                marshaller.writeObject(o1);
                marshaller.writeObject(o2);
                marshaller.writeObject(o1);
                marshaller.writeObject(o2);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final String o1 = (String) unmarshaller.readObject();
                final Integer o2 = (Integer) unmarshaller.readObject();
                assertSame(o1, unmarshaller.readObject());
                assertEqualsOrSame(o2, unmarshaller.readObject());
                final String o1p = (String) unmarshaller.readObject();
                final Integer o2p = (Integer) unmarshaller.readObject();
                assertNotSame(o1, o1p);
                assertEqualsOrSame(o2, o2p);
                assertSame(o1p, unmarshaller.readObject());
                assertEqualsOrSame(o2p, unmarshaller.readObject());
            }
        });
    }

    public static final class VerifyingTestObject implements Serializable {

        private static final long serialVersionUID = 3554028965858904047L;

        private int number;
        private String string;

        public VerifyingTestObject() {
        }

        public VerifyingTestObject(final int number, final String string) {
            this.number = number;
            this.string = string;
        }

        public int getNumber() {
            return number;
        }

        public void setNumber(final int number) {
            this.number = number;
        }

        public String getString() {
            return string;
        }

        public void setString(final String string) {
            this.string = string;
        }

        private void writeObject(ObjectOutputStream oos) throws IOException {
            oos.defaultWriteObject();
        }

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ois.defaultReadObject();
            ois.registerValidation(new ObjectInputValidation() {
                public void validateObject() throws InvalidObjectException {
                    if (!Integer.toString(number).equals(string)) {
                        throw new InvalidObjectException("No match");
                    }
                }
            }, 10);
        }
    }

    @Test
    public void testValidation() throws Throwable {
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(new VerifyingTestObject(1234, "1234"));
                marshaller.writeObject(new VerifyingTestObject(1234, "4321"));
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                VerifyingTestObject obj = (VerifyingTestObject) unmarshaller.readObject();
                assertNotNull(obj);
                assertEquals(1234, obj.number);
                assertEquals("1234", obj.string);
                try {
                    unmarshaller.readObject();
                    fail("No validation exception thrown");
                } catch (InvalidObjectException e) {
                    // ok
                }
            }
        });
    }

    static class TestSerializableWithWriteReplaceHolder implements Serializable {

        private static final long serialVersionUID = 1L;
        private int i;
        private int j;

        public TestSerializableWithWriteReplaceHolder(TestSerializableWithWriteReplace original) {
            this.i = original.i.intValue();
            this.j = original.j.intValue();
        }

        public TestSerializableWithWriteReplaceHolder() {
        }

        protected Object readResolve() throws ObjectStreamException {
            System.out.println(this + ".readResolve()");
            return new TestSerializableWithWriteReplace(this);
        }
    }

    static class TestSerializableWithWriteReplace implements Serializable {

        private static final long serialVersionUID = 1L;
        private static int replaceCounter;
        private static int resolveCounter;
        private Integer i;
        private Integer j;

        public TestSerializableWithWriteReplace(int i, int j) {
            this.i = Integer.valueOf(i);
            this.j = Integer.valueOf(j);
        }

        public TestSerializableWithWriteReplace(TestSerializableWithWriteReplaceHolder holder) {
            resolveCounter++;
            this.i = Integer.valueOf(holder.i);
            this.j = Integer.valueOf(holder.j);
        }

        public TestSerializableWithWriteReplace() {
        }

        public boolean equals(Object o) {
            if (o == null || !(o instanceof TestSerializableWithWriteReplace)) {
                return false;
            }
            TestSerializableWithWriteReplace other = (TestSerializableWithWriteReplace) o;
            return i.equals(other.i) && j.equals(other.j);
        }

        public boolean ok(int count) {
            return replaceCounter == count && resolveCounter == count;
        }

        protected Object writeReplace() throws ObjectStreamException {
            System.out.println(this + ".writeReplace()");
            replaceCounter++;
            return new TestSerializableWithWriteReplaceHolder(this);
        }
    }

    static class TestSerializableWithJavaUtilDate implements Serializable {

        public Date lastModified;
        private static final long serialVersionUID = -432434823510589221L;

        public TestSerializableWithJavaUtilDate() {
        }

        public TestSerializableWithJavaUtilDate(Date lastModified) {
            this.lastModified = lastModified;
        }
    }

    @Test
    public void testSerializableWithJavaUtilDate() throws Throwable {
        final TestSerializableWithJavaUtilDate serializable = new TestSerializableWithJavaUtilDate(
                new Date(System.currentTimeMillis()));
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                TestSerializableWithJavaUtilDate read = (TestSerializableWithJavaUtilDate) unmarshaller.readObject();
                assertEquals(serializable.lastModified + " is diff to read " + read.lastModified, serializable.lastModified, read.lastModified);
                assertEOF(unmarshaller);
            }
        });
    }

    /**
     * Verify that objects with writeReplace() and objects with readResolve() methods are handled correctly.
     */
    @Test
    public void testWriteReplace() throws Throwable {
        final TestSerializableWithWriteReplace t = new TestSerializableWithWriteReplace(1234, 5678);
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(t);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(t, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    /**
     * Verifies that marshallers and unmarshallers can be stopped and restarted.
     */
    @Test
    public void testReuse() throws Throwable {
        final TestSerializable t = new TestSerializable();
        ByteArrayOutputStream baos = new ByteArrayOutputStream(10240);
        ByteOutput byteOutput = Marshalling.createByteOutput(baos);
        Marshaller marshaller = testMarshallerProvider.create(configuration.clone(), byteOutput);
        if (marshaller instanceof ObjectOutputStreamMarshaller) {
            throw new SkipException(marshaller + " doesn't support start()");
        }
        System.out.println("Marshaller = " + marshaller + " (version set to " + configuration.getVersion() + ")");
        marshaller.writeObject(t);
        marshaller.finish();
        byte[] bytes = baos.toByteArray();
        ByteInput byteInput = Marshalling.createByteInput(new ByteArrayInputStream(bytes));
        Unmarshaller unmarshaller = testUnmarshallerProvider.create(configuration.clone(), byteInput);
        if (unmarshaller instanceof ObjectInputStreamUnmarshaller) {
            throw new SkipException(unmarshaller + " doesn't support start()");
        }
        System.out.println("Unmarshaller = " + unmarshaller + " (version set to " + configuration.getVersion() + ")");
        assertEquals(t, unmarshaller.readObject());
        unmarshaller.finish();
        baos.reset();
        byteOutput = Marshalling.createByteOutput(baos);
        marshaller.start(byteOutput);
        marshaller.writeObject(t);
        marshaller.finish();
        bytes = baos.toByteArray();
        byteInput = Marshalling.createByteInput(new ByteArrayInputStream(bytes));
        unmarshaller.start(byteInput);
        System.out.println("Unmarshaller = " + unmarshaller + " (version set to " + configuration.getVersion() + ")");
        assertEquals(t, unmarshaller.readObject());
        unmarshaller.finish();
    }

    static class TestStreamHeader implements StreamHeader {

        private byte B1 = (byte) 12;
        private byte B2 = (byte) 13;
        private boolean readVisited;
        private boolean writeVisited;

        public void readHeader(ByteInput input) throws IOException {
            readVisited = true;
            System.out.println("readHeader() visited");
            byte b1 = (byte) input.read();
            byte b2 = (byte) input.read();
            System.out.println("b1: " + b1 + ", b2: " + b2);
            if (b1 != B1 || b2 != B2) {
                throw new StreamCorruptedException("invalid stream header");
            }
        }

        public void writeHeader(ByteOutput output) throws IOException {
            writeVisited = true;
            System.out.println("writeHeader() visited");
            output.write(B1);
            output.write(B2);
        }

        public boolean ok() {
            return readVisited && writeVisited;
        }
    }

    /**
     * Verify that use of customized StreamHeader works correctly.
     */
    @Test
    public void testStreamHeader() throws Throwable {
        if (testMarshallerProvider instanceof ObjectOutputStreamTestMarshallerProvider) {
            throw new SkipException("Can't set StreamHeader in compatibility tests");
        }
        if (testUnmarshallerProvider instanceof ObjectInputStreamTestUnmarshallerProvider) {
            throw new SkipException("Can't set StreamHeader in compatibility tests");
        }
        final Serializable serializable = new TestSerializable();
        final TestStreamHeader streamHeader = new TestStreamHeader();
        configuration.setStreamHeader(streamHeader);
        ByteArrayOutputStream baos = new ByteArrayOutputStream(10240);
        ByteOutput byteOutput = Marshalling.createByteOutput(baos);
        Marshaller marshaller = testMarshallerProvider.create(configuration.clone(), byteOutput);
        System.out.println("Marshaller = " + marshaller + " (version set to " + configuration.getVersion() + ")");
        marshaller.writeObject(serializable);
        marshaller.finish();
        byte[] bytes = baos.toByteArray();
        ByteInput byteInput = Marshalling.createByteInput(new ByteArrayInputStream(bytes));
        Unmarshaller unmarshaller = testUnmarshallerProvider.create(configuration.clone(), byteInput);
        System.out.println("Unmarshaller = " + unmarshaller + " (version set to " + configuration.getVersion() + ")");
        assertEquals(serializable, unmarshaller.readObject());
        unmarshaller.finish();
        assertEOF(unmarshaller);
        assertTrue(streamHeader.ok());
    }

    public static class TestObjectResolver implements ObjectResolver {

        private boolean resolveVisited;
        private boolean replaceVisited;

        public Object readResolve(Object replacement) {
            if (new Integer(17).equals(replacement)) {
                resolveVisited = true;
                System.out.println("readResolve() visited");
                return new TestSerializable();
            } else {
                return replacement;
            }
        }

        public Object writeReplace(Object original) {
            if (original instanceof TestSerializable) {
                replaceVisited = true;
                System.out.println("writeReplace() visited");
                return new Integer(17);
            } else {
                return original;
            }
        }
        public boolean ok() {
            return resolveVisited && replaceVisited;
        }
    }

    public static class TestObjectPreResolver implements ObjectResolver {

        private boolean writeReplaceVisited;
        private boolean readReplaceVisited;

        public Object readResolve(Object replacement) {
          if (replacement instanceof Integer) {
            readReplaceVisited = true;
          }
          return replacement;
        }

        public Object writeReplace(Object original) {
            if (original instanceof TestSerializable) {
                writeReplaceVisited = true;
                return new Integer(17);
            } else {
                return original;
            }
        }
       
        public boolean ok() {
            return writeReplaceVisited && readReplaceVisited;
        }
    }

   
    /**
     * Verify that use of customized ObjectResolver works correctly.
     */
    @Test
    public void testObjectResolver() throws Throwable {
        if (testMarshallerProvider instanceof ObjectOutputStreamTestMarshallerProvider) {
            throw new SkipException("Can't use ObjectResolver in compatibility tests");
        }
        if (testUnmarshallerProvider instanceof ObjectInputStreamTestUnmarshallerProvider) {
            throw new SkipException("Can't use ObjectResolver in compatibility tests");
        }
        final TestObjectResolver objectResolver = new TestObjectResolver();
        final TestSerializable serializable = new TestSerializable();
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setObjectResolver(objectResolver);
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(serializable, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
        assertTrue(objectResolver.ok());
    }
   
    @Test
    public void testObjectPreResolver() throws Throwable {
        if (testMarshallerProvider instanceof ObjectOutputStreamTestMarshallerProvider) {
            throw new SkipException("Can't use objectPreResolver in compatibility tests");
        }
        if (testUnmarshallerProvider instanceof ObjectInputStreamTestUnmarshallerProvider) {
            throw new SkipException("Can't use objectPreResolver in compatibility tests");
        }
        final TestObjectPreResolver objectResolver = new TestObjectPreResolver();
        final TestSerializable serializable = new TestSerializable();
        final Integer expectedObj = new Integer(17);
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setObjectPreResolver(objectResolver);
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(serializable);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(expectedObj, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
        assertTrue(objectResolver.ok());
    }

    static class TestSerializableWithInterleavedWriteReplace implements Serializable {

        private static final long serialVersionUID = 1L;
        private static int replaceCounter;
        private static int resolveCounter;
        private Integer i;
        private TestEnum te;

        public static void reset() {
            replaceCounter = 0;
            resolveCounter = 0;
        }

        public TestSerializableWithInterleavedWriteReplace(int i, TestEnum te) {
            this.i = Integer.valueOf(i);
            this.te = te;
        }

        public TestSerializableWithInterleavedWriteReplace() {
        }

        public boolean equals(Object o) {
            if (o == null || !(o instanceof TestSerializableWithInterleavedWriteReplace)) {
                return false;
            }
            TestSerializableWithInterleavedWriteReplace other = (TestSerializableWithInterleavedWriteReplace) o;
            return i.equals(other.i) && te.equals(other.te);
        }

        public boolean ok(int count) {
            System.out.println("replaceCounter: " + replaceCounter + ", resolveCounter: " + resolveCounter);
            return 0 < replaceCounter && replaceCounter <= count && resolveCounter == 1;
        }

        protected Object writeReplace() throws ObjectStreamException {
            System.out.println(this + ".writeReplace()");
            replaceCounter++;
            if (te.equals(TestEnum.FIRST)) {
                return new TestSerializableWithInterleavedWriteReplace2(i.intValue(), te.next());
            }
            if (te.equals(TestEnum.SECOND)) {
                return new TestSerializableWithInterleavedWriteReplace3(i.intValue(), te.next());
            }
            if (te.equals(TestEnum.THIRD)) {
                return new TestSerializableWithInterleavedWriteReplace4(i.intValue(), te.next());
            }
            return this;
        }

        protected Object readResolve() throws ObjectStreamException {
            System.out.println(this + ".readResolve()");
            resolveCounter++;
            if (te.equals(TestEnum.FIFTH)) {
                return new TestSerializableWithInterleavedWriteReplace2(i.intValue(), TestEnum.SECOND);
            }
            if (te.equals(TestEnum.FOURTH)) {
                return new TestSerializableWithInterleavedWriteReplace2(i.intValue(), TestEnum.SECOND);
            }
            if (te.equals(TestEnum.THIRD)) {
                return new TestSerializableWithInterleavedWriteReplace2(i.intValue(), TestEnum.SECOND);
            } else {
                return this;
            }
        }
    }

    static class TestSerializableWithInterleavedWriteReplace2 extends TestSerializableWithInterleavedWriteReplace implements Serializable {

        private static final long serialVersionUID = 1527776832343228061L;

        public TestSerializableWithInterleavedWriteReplace2(int i, TestEnum te) {
            super(i, te);
        }

        public TestSerializableWithInterleavedWriteReplace2() {
        }
    }

    static class TestSerializableWithInterleavedWriteReplace3 extends TestSerializableWithInterleavedWriteReplace2 implements Serializable {

        private static final long serialVersionUID = -3231519519502968011L;

        public TestSerializableWithInterleavedWriteReplace3(int i, TestEnum te) {
            super(i, te);
        }

        public TestSerializableWithInterleavedWriteReplace3() {
        }
    }

    static class TestSerializableWithInterleavedWriteReplace4 extends TestSerializableWithInterleavedWriteReplace3 implements Serializable {

        private static final long serialVersionUID = -2396921036139628732L;

        public TestSerializableWithInterleavedWriteReplace4(int i, TestEnum te) {
            super(i, te);
        }

        public TestSerializableWithInterleavedWriteReplace4() {
        }
    }

    static class TestSerializableWithInterleavedWriteReplace5 extends TestSerializableWithInterleavedWriteReplace4 implements Serializable {

        private static final long serialVersionUID = 7353628200601762692L;

        public TestSerializableWithInterleavedWriteReplace5(int i, TestEnum te) {
            super(i, te);
        }

        public TestSerializableWithInterleavedWriteReplace5() {
        }
    }

    static class TestObjectResolverForInterleavedWriteReplace implements ObjectResolver {

        private int resolveCounter;
        private int replaceCounter;

        public Object readResolve(Object replacement) {
            if (!(replacement instanceof TestSerializableWithInterleavedWriteReplace)) {
                return replacement;
            }
            System.out.println(this + ".readResolve(): " + replacement);
            resolveCounter++;
            TestSerializableWithInterleavedWriteReplace tsw = (TestSerializableWithInterleavedWriteReplace) replacement;
            if (tsw.te.equals(TestEnum.SECOND)) {
                return new TestSerializableWithInterleavedWriteReplace(tsw.i.intValue(), tsw.te.previous());
            } else {
                return replacement;
            }
        }

        public Object writeReplace(Object original) {
            if (!(original instanceof TestSerializableWithInterleavedWriteReplace)) {
                return original;
            }
            System.out.println(this + ".writeReplace(): " + original);
            replaceCounter++;
            TestSerializableWithInterleavedWriteReplace tsw = (TestSerializableWithInterleavedWriteReplace) original;
            if (tsw.te.equals(TestEnum.FIRST)) {
                return new TestSerializableWithInterleavedWriteReplace2(tsw.i.intValue(), tsw.te.next());
            }
            if (tsw.te.equals(TestEnum.SECOND)) {
                return new TestSerializableWithInterleavedWriteReplace3(tsw.i.intValue(), tsw.te.next());
            }
            if (tsw.te.equals(TestEnum.THIRD)) {
                return new TestSerializableWithInterleavedWriteReplace4(tsw.i.intValue(), tsw.te.next());
            }
            if (tsw.te.equals(TestEnum.FOURTH)) {
                return new TestSerializableWithInterleavedWriteReplace5(tsw.i.intValue(), tsw.te.next());
            } else {
                return original;
            }
        }
       
        public Object preWriteReplace(Object original) {
            return original;
        }

        public boolean ok() {
            return replaceCounter == 1 && resolveCounter == 1;
        }
    }

    public enum TestEnum {

        FIRST {TestEnum next() {
            return SECOND;
        };TestEnum previous() {
            return FIFTH;
        }},
        SECOND {TestEnum next() {
            return THIRD;
        };TestEnum previous() {
            return FIRST;
        }},
        THIRD {TestEnum next() {
            return FOURTH;
        };TestEnum previous() {
            return SECOND;
        }},
        FOURTH {TestEnum next() {
            return FIFTH;
        };TestEnum previous() {
            return THIRD;
        }},
        FIFTH {TestEnum next() {
            return FIRST;
        };TestEnum previous() {
            return FOURTH;
        }};

        abstract TestEnum next();

        abstract TestEnum previous();
    }

    /**
     * Verify that objects with writeReplace()/readResolve() interact correctly with ObjectResolvers.
     */
    @Test
    public void testObjectResolverWithInterleavedWriteReplace() throws Throwable {
        if (testMarshallerProvider instanceof ObjectOutputStreamTestMarshallerProvider) {
            throw new SkipException("Can't use ObjectResolver in compatibility tests");
        }
        if (testUnmarshallerProvider instanceof ObjectInputStreamTestUnmarshallerProvider) {
            throw new SkipException("Can't use ObjectResolver in compatibility tests");
        }
        final TestObjectResolverForInterleavedWriteReplace objectResolver = new TestObjectResolverForInterleavedWriteReplace();
        final TestSerializableWithInterleavedWriteReplace o = new TestSerializableWithInterleavedWriteReplace(3, TestEnum.FIRST);
        TestSerializableWithInterleavedWriteReplace.reset();
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setObjectResolver(objectResolver);
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(o);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                assertEquals(o, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
        assertTrue(o.ok(4));
        assertTrue(objectResolver.ok());
    }

    public static class TestExternalizableInt implements Serializable {

        private static final long serialVersionUID = 805500397903006481L;
        private int secret;

        public TestExternalizableInt(int secret) {
            this.secret = secret;
        }

        public TestExternalizableInt() {
        }

        public int getSecret() {
            return secret;
        }

        public void setSecret(int secret) {
            this.secret = secret;
        }

        public boolean equals(Object o) {
            if (!(o instanceof TestExternalizableInt)) {
                return false;
            }
            return ((TestExternalizableInt) o).secret == this.secret;
        }

        public int hashCode() {
            return secret;
        }
    }

    public static class TestExternalizer implements Externalizer, Serializable {

        private static final long serialVersionUID = -8104713864804175542L;

        public Object createExternal(Class<?> subjectType, ObjectInput input) throws IOException, ClassNotFoundException {
            if (!TestExternalizableInt.class.isAssignableFrom(subjectType)) {
                throw new IOException(this + " only works for " + TestExternalizableInt.class + " but I got a " + subjectType);
            }
            Object obj = null;
            try {
                obj = subjectType.newInstance();
            } catch (Exception e) {
                throw new IOException(e + "\n" + e.getMessage());
            }
            if (TestExternalizableInt.class.isAssignableFrom(obj.getClass())) {
                System.out.println(this + " reading  " + obj.getClass());
                ((TestExternalizableInt) obj).setSecret(input.readInt());
            } else {
                throw new IOException(this + " only works for " + TestExternalizableInt.class);
            }
            return obj;
        }

        public void writeExternal(Object subject, ObjectOutput output) throws IOException {
            if (TestExternalizableInt.class.isAssignableFrom(subject.getClass())) {
                System.out.println(this + " writing " + subject.getClass());
                output.writeInt(((TestExternalizableInt) subject).getSecret());
            } else {
                throw new IOException(this + " only works for " + TestExternalizableInt.class);
            }
        }
    }

    static class TestExternalizerFactory implements ClassExternalizerFactory {

        public Externalizer getExternalizer(Class<?> type) {
            if (type == TestExternalizableInt.class) {
                TestExternalizer externalizer = new TestExternalizer();
                return externalizer;
            } else {
                return null;
            }
        }
    }

    /*
    * Verify that use of customized ExternalizerFactory to write the same object twice
    * results in sending a handle on the second write.
    */
    @Test
    public void testExternalizerWithRepeatedWrites() throws Throwable {
        if (testMarshallerProvider instanceof ObjectOutputStreamTestMarshallerProvider) {
            throw new SkipException("Can't use ClassExternalizerFactory in compatibility tests");
        }
        if (testUnmarshallerProvider instanceof ObjectInputStreamTestUnmarshallerProvider) {
            throw new SkipException("Can't use ClassExternalizerFactory in compatibility tests");
        }
        TestExternalizerFactory externalizerFactory = new TestExternalizerFactory();
        Object o = new TestExternalizableInt(7);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ByteOutput byteOutput = Marshalling.createByteOutput(baos);
        MarshallingConfiguration config = configuration.clone();
        config.setClassExternalizerFactory(externalizerFactory);
        Marshaller marshaller = testMarshallerProvider.create(config, byteOutput);
//        if (marshaller instanceof SerialMarshaller) {
//            throw new SkipException("TODO: Known issue - see JBMAR-50");
//        }
        marshaller.writeObject(o);
        marshaller.writeObject(o);
        marshaller.flush();
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ByteInput byteInput = Marshalling.createByteInput(bais);
        config = configuration.clone();
        config.setClassExternalizerFactory(externalizerFactory);
        Unmarshaller unmarshaller = testUnmarshallerProvider.create(config, byteInput);
        Object o2 = unmarshaller.readObject();
        assertEquals(o, o2);
        Object o3 = unmarshaller.readObject();
        assertSame(o2, o3);
    }

    public static final class TestExternalizableWithSerializableFields implements Externalizable {

        private boolean ran;
        private Object obj = new TestSerializableWithFields();

        private static final long serialVersionUID = 2776810457096829768L;

        public void writeExternal(final ObjectOutput out) throws IOException {
            out.writeInt(54321);
            out.writeUTF("Hello!");
            out.writeObject(obj);
        }

        public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
            assertEquals(54321, in.readInt());
            assertEquals("Hello!", in.readUTF());
            obj = in.readObject();
            assertTrue("No EOF", in.read() == -1);
            ran = true;
        }
    }

    @Test
    public void testExternalizableWithFollowingObjects() throws Throwable {
        final TestExternalizableWithSerializableFields ext1 = new TestExternalizableWithSerializableFields();
        final TestExternalizableWithSerializableFields ext2 = new TestExternalizableWithSerializableFields();
        final TestExternalizableWithSerializableFields ext3 = new TestExternalizableWithSerializableFields();
        final AtomicInteger version = new AtomicInteger();
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                version.set(configuration.getVersion());
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(ext1);
                marshaller.writeObject(ext2);
                marshaller.writeObject(ext3);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                if ((unmarshaller instanceof RiverUnmarshaller) && version.get() < 1) {
                    throw new SkipException("River v0 can't detect eof on each object");
                }
                final TestExternalizableWithSerializableFields rext1 = (TestExternalizableWithSerializableFields) unmarshaller.readObject();
                final TestExternalizableWithSerializableFields rext2 = (TestExternalizableWithSerializableFields) unmarshaller.readObject();
                final TestExternalizableWithSerializableFields rext3 = (TestExternalizableWithSerializableFields) unmarshaller.readObject();
                assertTrue("No EOF", unmarshaller.read() == -1);
                assertTrue("readExternal 1 was not run", rext1.ran);
                assertTrue("readExternal 2 was not run", rext2.ran);
                assertTrue("readExternal 3 was not run", rext3.ran);
            }
        });
        assertFalse("readExternal was run on the original 1", ext1.ran);
        assertFalse("readExternal was run on the original 2", ext2.ran);
        assertFalse("readExternal was run on the original 3", ext3.ran);
    }

    @Test
    public void testExternalizablePlusExternalizer() throws Throwable {
        final TestExternalizableWithSerializableFields ext1 = new TestExternalizableWithSerializableFields();
        final TestExternalizableWithSerializableFields ext2 = new TestExternalizableWithSerializableFields();
        final TestExternalizableWithSerializableFields ext3 = new TestExternalizableWithSerializableFields();
        final Map<String, TestExternalizableWithSerializableFields> map = new HashMap<String, TestExternalizableWithSerializableFields>();
        map.put("kejlwqewq", ext1);
        map.put("34890fdu90uq09rdewq", ext2);
        map.put("nczxm,ncoijd0q93wjdwdwq", ext3);
        final AtomicInteger version = new AtomicInteger();
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setClassExternalizerFactory(new ClassExternalizerFactory() {
                    public Externalizer getExternalizer(final Class<?> type) {
                        if (type == HashMap.class) {
                            return new HashMapExternalizer();
                        } else {
                            return null;
                        }
                    }
                });
                version.set(configuration.getVersion());
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(map);
                marshaller.writeObject(map);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                if ((unmarshaller instanceof RiverUnmarshaller) && version.get() < 1) {
                    throw new SkipException("River v0 can't detect eof on each object");
                }
                final Object m1 = unmarshaller.readObject();
                assertEquals(HashMap.class, m1.getClass());
                assertSame(m1, unmarshaller.readObject());
                assertEOF(unmarshaller);
            }
        });
    }

    private static final int[] reallyLongStringLengths = { 0, 60, 255, 256, 300, 1000, 15000, 65538 };
    private static final long reallyLongStringLengthSeed = 0x1bd63a9e00333b34L;

    @Test
    public void testReallyLongStrings() throws Throwable {
        final Random random = new Random(reallyLongStringLengthSeed);
        for (int len : reallyLongStringLengths) {
            final StringBuilder builder = new StringBuilder(len);
            for (int i = 0; i < len; i++) {
                char ch;
                do {
                    ch = (char) random.nextInt();
                } while (!Character.isDefined(ch) || Character.isLowSurrogate(ch) || Character.isHighSurrogate(ch));
                builder.append(ch);
            }
            final String s = builder.toString();
            runReadWriteTest(new ReadWriteTest() {
                public void runWrite(final Marshaller marshaller) throws Throwable {
                    marshaller.writeObject(s);
                    marshaller.writeObject(s);
                }

                public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                    final String rs = (String) unmarshaller.readObject();
                    assertEquals("String does not match", s, rs);
                    assertSame(rs, unmarshaller.readObject());
                }
            });
        }
    }

    public static class TestSerializableEmpty implements Serializable {

        private static final long serialVersionUID = 997011009926105464L;
    }

    @Test
    public void testObjectGraphDistance() throws Throwable {
        final TestSerializableEmpty first = new TestSerializableEmpty();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(first);
                for (int i = 0; i < 65536 + 500; i++) {
                    marshaller.writeObject(new TestSerializableEmpty());
                }
                marshaller.writeObject(first);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final Object test = unmarshaller.readObject();
                for (int i = 0; i < 65536 + 500; i++) {
                    unmarshaller.readObject();
                }
                assertSame("Far backref", test, unmarshaller.readObject());
            }
        });
    }

    public static final class TestExternalizableForWithBlockData implements Externalizable {

        private static final long serialVersionUID = -5588239332881048585L;

        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            for (int i = 0; i < 550; i++) {
                int current = in.readInt();
                assertEquals(i, current);
            }
        }

        public void writeExternal(ObjectOutput out) throws IOException {
            for (int i = 0; i < 550; i++) {
                out.writeInt(i);
            }
        }
    }

    @Test
    public void testFlushWithBlockData() throws Throwable {
        final TestExternalizableForWithBlockData externalizableForJBMar84 = new TestExternalizableForWithBlockData();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(externalizableForJBMar84);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                unmarshaller.readObject();
            }
        });
    }

    public static final class TestModifiableFinalField implements Serializable {

        private static final long serialVersionUID = 712538031604959058L;
        private static final FieldSetter valSetter = FieldSetter.get(TestModifiableFinalField.class, "val");
        private static final FieldSetter fooSetter = FieldSetter.get(TestModifiableFinalField.class, "foo");

        private transient final int val;
        private transient final Object foo;

        public TestModifiableFinalField(final int val, final Object foo) {
            this.val = val;
            this.foo = foo;
        }

        private void writeObject(ObjectOutputStream oos) throws IOException {
            oos.defaultWriteObject();
            oos.writeInt(val);
            oos.writeObject(foo);
        }

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ois.defaultReadObject();
            valSetter.setInt(this, ois.readInt());
            fooSetter.set(this, ois.readObject());
        }

        public boolean equals(final Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            final TestModifiableFinalField that = (TestModifiableFinalField) o;
            if (val != that.val) return false;
            if (foo != null ? !foo.equals(that.foo) : that.foo != null) return false;
            return true;
        }

        public int hashCode() {
            int result = val;
            result = 31 * result + (foo != null ? foo.hashCode() : 0);
            return result;
        }
    }

    @Test
    public void testModifiableFinalFields() throws Throwable {
        final TestModifiableFinalField test = new TestModifiableFinalField(42, "Boogeyman");
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final Object newTest = unmarshaller.readObject();
                assertSame("Consecutive reads not identical", newTest, unmarshaller.readObject());
                assertEquals("Read object != written object", test, newTest);
            }
        });
    }

    static class Parent implements Serializable {

        private String id;
        private Child1 child1Obj;
        private static final long serialVersionUID = -1417329412107563636L;

        public Parent(String id, Child1 child1Obj) {
            this.id = id;
            this.child1Obj = child1Obj;
        }

        public String getId() {
            return id;
        }

        public Child1 getChild1Obj() {
            return child1Obj;
        }

        public boolean equals(final Object obj) {
            return obj instanceof Parent && equals((Parent) obj);
        }

        public boolean equals(final Parent obj) {
            return obj != null && (id == null ? obj.id == null : id.equals(obj.id)) && (child1Obj == null ? obj.child1Obj == null : child1Obj.equals(obj.child1Obj));
        }

        public String getFieldString() {
            return String.format("id=%s,child1=%s", id, child1Obj);
        }

        public String toString() {
            return String.format("%s{%s}", getClass().getSimpleName(), getFieldString());
        }
    }

    static class Child1 extends Parent {

        private int someInt;
        private static final long serialVersionUID = -1989376997581386909L;

        public Child1(int someInt, String parentStr) {
            super(parentStr, null);
            this.someInt = someInt;
        }

        public boolean equals(final Object obj) {
            return obj instanceof Child1 && equals((Child1) obj);
        }

        public boolean equals(final Parent obj) {
            return obj instanceof Child1 && equals((Child1) obj);
        }

        public boolean equals(final Child1 obj) {
            return super.equals((Parent) obj) && someInt == obj.someInt;
        }

        public String getFieldString() {
            return String.format("%s,someInt=%d", super.getFieldString(), Integer.valueOf(someInt));
        }
    }

    static class Child2 extends Parent {

        private int someInt;
        private static final long serialVersionUID = 5406289444171915286L;

        public Child2(int someInt, String parentStr, Child1 child1Obj) {
            super(parentStr, child1Obj);
            this.someInt = someInt;
        }

        public boolean equals(final Object obj) {
            return obj instanceof Child2 && equals((Child2) obj);
        }

        public boolean equals(final Parent obj) {
            return obj instanceof Child2 && equals((Child2) obj);
        }

        public boolean equals(final Child2 obj) {
            return super.equals((Parent) obj) && someInt == obj.someInt;
        }

        public String getFieldString() {
            return String.format("%s,someInt=%d", super.getFieldString(), Integer.valueOf(someInt));
        }
    }

    @Test
    public void testNestedSubclass() throws Throwable {
        final Child1 child1Obj = new Child1(1234, "1234");
        final Child2 child2Obj = new Child2(2345, "2345", child1Obj);
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(child2Obj);
                marshaller.writeObject(child2Obj);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final Object newTest = unmarshaller.readObject();
                assertSame("Consecutive reads not identical", newTest, unmarshaller.readObject());
                assertEquals("Read object != written object", child2Obj, newTest);
            }
        });
    }

    static class ClassTableSuper implements Serializable {

        private static final long serialVersionUID = 1L;

        int someInt = 123;
        String someString = "test";
    }

    static class ClassTableSub extends ClassTableSuper {
        private static final long serialVersionUID = -1732733608096099080L;

        float fl = 0.125f;
        String str2 = "blah";
    }

    @Test
    public void testClassTableHierarchy() throws Throwable {
        final ClassTable classTable = new ClassTable() {
            public Writer getClassWriter(final Class<?> clazz) throws IOException {
                return clazz == ClassTableSuper.class ? new Writer() {
                    public void writeClass(final Marshaller marshaller, final Class<?> clazz) throws IOException {
                        marshaller.writeByte(1);
                    }
                } : clazz == ClassTableSub.class ? new Writer() {
                    public void writeClass(final Marshaller marshaller, final Class<?> clazz) throws IOException {
                        marshaller.writeByte(2);
                    }
                } : null;
            }

            public Class<?> readClass(final Unmarshaller unmarshaller) throws IOException, ClassNotFoundException {
                switch (unmarshaller.readByte()) {
                    case 1:
                        return ClassTableSuper.class;
                    case 2:
                        return ClassTableSub.class;
                    default:
                        throw new IllegalStateException();
                }
            }
        };
        final ClassTableSub sub = new ClassTableSub();
        final ClassTableSuper sup = new ClassTableSuper();
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setClassTable(classTable);
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(sub);
                marshaller.writeObject(sup);
                marshaller.writeObject("check");
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final ClassTableSub sub1 = unmarshaller.readObject(ClassTableSub.class);
                final ClassTableSuper sup1 = unmarshaller.readObject(ClassTableSuper.class);
                assertEquals("check", unmarshaller.readObject());
                assertEquals(sub.someInt, sub1.someInt);
                assertEquals(sub.someString, sub1.someString);
                assertEquals(sub.fl, sub1.fl, 0.0f);
                assertEquals(sub.str2, sub1.str2);
                assertEquals(sup.someInt, sup1.someInt);
                assertEquals(sup.someString, sup1.someString);
            }
        });
    }

    @Test
    public void testTreeMapBackref() throws Throwable {
        final TreeMap<Object, Object> treeMap = new TreeMap<Object, Object>();
        treeMap.put("Hi", treeMap);
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(treeMap);
                marshaller.writeObject(treeMap);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final TreeMap newTreeMap = unmarshaller.readObject(TreeMap.class);
                assertSame(newTreeMap, unmarshaller.readObject(TreeMap.class));
            }
        });
    }

    @Test
    public void testTreeSetBackref() throws Throwable {
        final TreeSet<TreeSetSelfRef> set = new TreeSet<TreeSetSelfRef>();
        final TreeSetSelfRef item = new TreeSetSelfRef("1", set);
        set.add(item);
        set.add(new TreeSetSelfRef("4", set));
        set.add(new TreeSetSelfRef("7", set));
        set.add(new TreeSetSelfRef("8", set));
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(item);
                marshaller.writeObject(item);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final TreeSetSelfRef thing = unmarshaller.readObject(TreeSetSelfRef.class);
                assertSame(thing, unmarshaller.readObject(TreeSetSelfRef.class));
            }
        });
    }

    private static final class TreeSetSelfRef implements Comparable<TreeSetSelfRef>, Serializable {

        private static final long serialVersionUID = 1L;

        private final String zap;
        private final TreeSet<TreeSetSelfRef> parent;

        private TreeSetSelfRef(final String zap, final TreeSet<TreeSetSelfRef> parent) {
            this.zap = zap;
            this.parent = parent;
        }

        public int compareTo(final TreeSetSelfRef o) {
            return zap.compareTo(o.zap);
        }
    }

    private static final class NonPublicExt implements Externalizable {

        public NonPublicExt() {
        }

        public void writeExternal(final ObjectOutput out) throws IOException {
            out.writeInt(1);
        }

        public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
            assertEquals(in.readInt(), 1);
        }
    }

    @Test
    public void testNonPublicExtClass() throws Throwable {
        final NonPublicExt test = new NonPublicExt();
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                unmarshaller.readObject();
            }
        });
    }

    @Test
    public void testHierarchyLikeLinkedHashMapSubclass() throws Throwable {
        final LRUMap test = new LRUMap(64);
        runReadWriteTest(new ReadWriteTest() {
            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(test);
                marshaller.writeObject(test);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                final LRUMap map = unmarshaller.readObject(LRUMap.class);
                assertSame(map, unmarshaller.readObject());
            }
        });
    }

    static class OrigLvl1 implements Serializable {

        private static final long serialVersionUID = 1L;

        private int foo;
        private String bar;

        OrigLvl1(final int foo, final String bar) {
            this.foo = foo;
            this.bar = bar;
        }

        public int getFoo() {
            return foo;
        }

        public void setFoo(final int foo) {
            this.foo = foo;
        }

        public String getBar() {
            return bar;
        }

        public void setBar(final String bar) {
            this.bar = bar;
        }

        public boolean equals(Object other) {
            return other instanceof OrigLvl1 && equals((OrigLvl1) other);
        }

        public boolean equals(OrigLvl1 other) {
            return other != null && other.getClass() == getClass() && foo == other.foo && bar.equals(other.bar);
        }
    }

    static class OrigLvl2 extends OrigLvl1 implements Serializable {

        private static final long serialVersionUID = 1L;

        private int[] baz;
        private String zap;

        OrigLvl2(final int foo, final String bar, final int[] baz, final String zap) {
            super(foo, bar);
            this.baz = baz;
            this.zap = zap;
        }

        public int[] getBaz() {
            return baz;
        }

        public void setBaz(final int[] baz) {
            this.baz = baz;
        }

        public String getZap() {
            return zap;
        }

        public void setZap(final String zap) {
            this.zap = zap;
        }

        public boolean equals(Object other) {
            return other instanceof OrigLvl2 && equals((OrigLvl2) other);
        }

        public boolean equals(final OrigLvl1 other) {
            return equals((Object) other);
        }

        public boolean equals(final OrigLvl2 other) {
            return other != null && other.getClass() == getClass() && super.equals(other) && Arrays.equals(baz, other.baz) && zap.equals(other.zap);
        }
    }

    static class OrigLvl3 extends OrigLvl2 implements Serializable {

        private static final long serialVersionUID = 1L;

        private boolean zab;
        private String paz;

        OrigLvl3(final int foo, final String bar, final int[] baz, final String zap, final boolean zab, final String paz) {
            super(foo, bar, baz, zap);
            this.zab = zab;
            this.paz = paz;
        }

        public boolean isZab() {
            return zab;
        }

        public void setZab(final boolean zab) {
            this.zab = zab;
        }

        public String getPaz() {
            return paz;
        }

        public void setPaz(final String paz) {
            this.paz = paz;
        }

        public boolean equals(Object other) {
            return other instanceof OrigLvl3 && equals((OrigLvl3) other);
        }

        public boolean equals(final OrigLvl1 other) {
            return equals((Object) other);
        }

        public boolean equals(final OrigLvl2 other) {
            return equals((Object) other);
        }

        public boolean equals(final OrigLvl3 other) {
            return other != null && other.getClass() == getClass() && super.equals(other) && zab == other.zab && paz.equals(other.paz);
        }
    }

    static class NewLvl1 implements Serializable {

        private static final long serialVersionUID = 1L;

        private int foo;
        private String bar;

        NewLvl1(final int foo, final String bar) {
            this.foo = foo;
            this.bar = bar;
        }

        public int getFoo() {
            return foo;
        }

        public void setFoo(final int foo) {
            this.foo = foo;
        }

        public String getBar() {
            return bar;
        }

        public void setBar(final String bar) {
            this.bar = bar;
        }

        public boolean equals(Object other) {
            return other instanceof NewLvl1 && equals((NewLvl1) other);
        }

        public boolean equals(NewLvl1 other) {
            return other != null && other.getClass() == getClass() && foo == other.foo && bar.equals(other.bar);
        }
    }

    static class NewLvl3 extends NewLvl1 implements Serializable {

        private static final long serialVersionUID = 1L;

        private boolean zab;
        private String paz;

        NewLvl3(final int foo, final String bar, final boolean zab, final String paz) {
            super(foo, bar);
            this.zab = zab;
            this.paz = paz;
        }

        public boolean isZab() {
            return zab;
        }

        public void setZab(final boolean zab) {
            this.zab = zab;
        }

        public String getPaz() {
            return paz;
        }

        public void setPaz(final String paz) {
            this.paz = paz;
        }

        public boolean equals(Object other) {
            return other instanceof NewLvl3 && equals((NewLvl3) other);
        }

        public boolean equals(final NewLvl1 other) {
            return equals((Object) other);
        }

        public boolean equals(final NewLvl3 other) {
            return other != null && other.getClass() == getClass() && super.equals(other) && zab == other.zab && paz.equals(other.paz);
        }
    }

    @Test
    public void testClassRemovedFromHierarchy() throws Throwable {
        final OrigLvl3 origLvl3 = new OrigLvl3(123, "blah", new int[] { 1, 2, 3 }, "bzzzz", true, "fooble");
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setClassResolver(new SimpleClassResolver(getClass().getClassLoader()) {
                    public String getClassName(final Class<?> clazz) throws IOException {
                        if (clazz == OrigLvl1.class || clazz == OrigLvl2.class || clazz == OrigLvl3.class) {
                            return clazz.getName().replace("Orig", "New");
                        } else {
                            return clazz.getName();
                        }
                    }
                });
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                marshaller.writeObject(origLvl3);
                marshaller.writeObject(origLvl3);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                unmarshaller.readObject();
                unmarshaller.readObject();
            }
        });
    }

    static boolean isEqual(Object o1, Object o2) {
        return o1 == null ? o2 == null : o1.equals(o2);
    }

    static class OrigRem1 implements Serializable {

        private static final long serialVersionUID = 1L;

        private final String foo;
        private final OrigRem2 r2;

        OrigRem1(final String foo, final OrigRem2 r2) {
            this.foo = foo;
            this.r2 = r2;
        }

        public boolean equals(final Object obj) {
            return obj instanceof OrigRem1 && isEqual(((OrigRem1) obj).foo, foo) && isEqual(((OrigRem1) obj).r2, r2);
        }

        public int hashCode() {
            return foo.hashCode() * 17 + r2.hashCode();
        }

        public String getFoo() {
            return foo;
        }

        public OrigRem2 getR2() {
            return r2;
        }
    }

    static class OrigRem2 implements Serializable {

        private static final long serialVersionUID = 1L;

        private final int abc;

        OrigRem2(final int abc) {
            this.abc = abc;
        }

        public boolean equals(final Object obj) {
            return obj instanceof OrigRem2 && ((OrigRem2) obj).abc == abc;
        }

        public int hashCode() {
            return abc;
        }

        public int getAbc() {
            return abc;
        }
    }

    static class NewRem1 implements Serializable {

        private static final long serialVersionUID = 1L;

        private final String foo;

        NewRem1(final String foo) {
            this.foo = foo;
        }

        public boolean equals(final Object obj) {
            return obj instanceof NewRem1 && isEqual(((NewRem1) obj).foo, foo);
        }

        public int hashCode() {
            return foo.hashCode();
        }

        public String getFoo() {
            return foo;
        }
    }

    @Test
    public void testClassOfRemovedFieldMissing() throws Throwable {
        final OrigRem1 orig = new OrigRem1("This is a simple test.", new OrigRem2(194));
        runReadWriteTest(new ReadWriteTest() {
            public void configure(final MarshallingConfiguration configuration) throws Throwable {
                configuration.setClassResolver(new SimpleClassResolver(getClass().getClassLoader()) {
                    public String getClassName(final Class<?> clazz) throws IOException {
                        final String oldName = clazz.getName();
                        final String newName = oldName.replace("Orig", "New");
                        System.out.printf("Renaming %s to %s%n", oldName, newName);
                        return newName;
                    }
                });
            }

            public void runWrite(final Marshaller marshaller) throws Throwable {
                if (marshaller instanceof ObjectOutputStreamMarshaller) {
                    throw new SkipException("Skip for " + marshaller);
                }
                marshaller.writeObject(orig);
                marshaller.writeObject(orig);
            }

            public void runRead(final Unmarshaller unmarshaller) throws Throwable {
                try {
                    if (unmarshaller instanceof ObjectInputStreamUnmarshaller) {
                        throw new SkipException("Skip for " + unmarshaller);
                    }
                    if (unmarshaller instanceof SerialUnmarshaller) {
                        throw new SkipException("Functionality not yet supported on serial unmarshalling");
                    }
                    final NewRem1 object = unmarshaller.readObject(NewRem1.class);
                    assertEquals(object.getFoo(), orig.getFoo());
                    assertSame(unmarshaller.readObject(), object);
                } catch (NullPointerException npe) {
                    npe.printStackTrace(System.out);
                    System.out.flush();
                    fail();
                }
            }
        });
    }
}
TOP

Related Classes of org.jboss.test.marshalling.SimpleMarshallerTests$OrigRem2

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
teElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');