package japicmp.test; import com.google.common.base.Optional; import japicmp.cmp.JarArchiveComparator; import japicmp.cmp.JarArchiveComparatorOptions; import japicmp.model.AccessModifier; import japicmp.model.JApiChangeStatus; import japicmp.model.JApiClass; import japicmp.model.JApiJavaObjectSerializationCompatibility; import japicmp.test.serialversion.*; import javassist.CtClass; import org.junit.BeforeClass; import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.URL; import java.net.URLClassLoader; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import static japicmp.test.util.Helper.getArchive; import static japicmp.test.util.Helper.getJApiClass; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; public class JavaObjectSerializationTest { private static final Logger LOGGER = Logger.getLogger(JavaObjectSerializationTest.class.getName()); private static List<JApiClass> jApiClasses; @BeforeClass public static void beforeClass() { JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); options.setAccessModifier(AccessModifier.PRIVATE); JarArchiveComparator jarArchiveComparator = new JarArchiveComparator(options); jApiClasses = jarArchiveComparator.compare(getArchive("japicmp-test-v1.jar"), getArchive("japicmp-test-v2.jar")); } @Test public void testSerialVersionUnchanged() { JApiClass jApiClass = getJApiClass(jApiClasses, SerialVersionUnchanged.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.UNCHANGED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(true)); } @Test public void testNotSerializable() { JApiClass jApiClass = getJApiClass(jApiClasses, NotSerializable.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.UNCHANGED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.NOT_SERIALIZABLE)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testUnchangedWithSerialVersionUid() { JApiClass jApiClass = getJApiClass(jApiClasses, UnchangedWithSerialVersionUid.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.UNCHANGED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(true)); } @Test public void testUnchangedWithSerialVersionUidChange() { JApiClass jApiClass = getJApiClass(jApiClasses, UnchangedWithSerialVersionUidChange.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.UNCHANGED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_SERIALVERSIONUID_MODIFIED)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testExtendsSerializableClassUnchanged() { JApiClass jApiClass = getJApiClass(jApiClasses, ExtendsSerializableClassUnchanged.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.UNCHANGED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(true)); } @Test public void testExtendsSerializableClassModified() { JApiClass jApiClass = getJApiClass(jApiClasses, ExtendsSerializableClassModified.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_FIELD_REMOVED)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testModifiedButSerialVersionUidUnchanged() { JApiClass jApiClass = getJApiClass(jApiClasses, ModifiedButSerialVersionUidUnchanged.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_FIELD_REMOVED)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(true)); } @Test public void testModifiedFieldAddedButSerialVersionUidUnchanged() { JApiClass jApiClass = getJApiClass(jApiClasses, ModifiedFieldAddedButSerialVersionUidUnchanged.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(true)); } @Test public void testModifiedAndSerialVersionUidModified() { JApiClass jApiClass = getJApiClass(jApiClasses, ModifiedAndSerialVersionUidModified.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_FIELD_REMOVED)); } @Test public void testSerializableClassRemoved() { JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.test.serialversion.SerializableClassRemoved"); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.REMOVED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_CLASS_REMOVED)); } @Test public void testFieldRemoved() { JApiClass jApiClass = getJApiClass(jApiClasses, IncompatibleChanges.FieldRemoved.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_FIELD_REMOVED)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(true)); } @Test public void testNonStaticFieldToStaticField() { JApiClass jApiClass = getJApiClass(jApiClasses, IncompatibleChanges.NonStaticFieldToStaticField.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_FIELD_CHANGED_FROM_NONSTATIC_TO_STATIC)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testNonTransientFieldToTransientField() { JApiClass jApiClass = getJApiClass(jApiClasses, IncompatibleChanges.NonTransientFieldToTransientField.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_FIELD_CHANGED_FROM_NONTRANSIENT_TO_TRANSIENT)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testTypeOfFieldChanges() { JApiClass jApiClass = getJApiClass(jApiClasses, IncompatibleChanges.TypeOfFieldChanges.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_FIELD_TYPE_MODIFIED)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testSerializableToExternalizable() { JApiClass jApiClass = getJApiClass(jApiClasses, IncompatibleChanges.SerializableToExternalizable.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_CHANGED_FROM_SERIALIZABLE_TO_EXTERNALIZABLE)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testExternalizableToSerializable() { JApiClass jApiClass = getJApiClass(jApiClasses, IncompatibleChanges.ExternalizableToSerializable.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_CHANGED_FROM_EXTERNALIZABLE_TO_SERIALIZABLE)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testEnumToNonEnum() { JApiClass jApiClass = getJApiClass(jApiClasses, IncompatibleChanges.EnumToNonEnum.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_CLASS_TYPE_MODIFIED)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testNonEnumToEnum() { JApiClass jApiClass = getJApiClass(jApiClasses, IncompatibleChanges.NonEnumToEnum.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_CLASS_TYPE_MODIFIED)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testSerializableRemoved() { JApiClass jApiClass = getJApiClass(jApiClasses, IncompatibleChanges.SerializableRemoved.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_SERIALIZABLE_REMOVED)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testExternalizableRemoved() { JApiClass jApiClass = getJApiClass(jApiClasses, IncompatibleChanges.ExternalizableRemoved.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_EXTERNALIZABLE_REMOVED)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testFieldAddedClassWithoutSerialVersionUid() { JApiClass jApiClass = getJApiClass(jApiClasses, CompatibleChanges.FieldAddedClassWithoutSerialVersionUid.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_DEFAULT_SERIALVERSIONUID_CHANGED)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testFieldAddedClassWithSerialVersionUid() { JApiClass jApiClass = getJApiClass(jApiClasses, CompatibleChanges.FieldAddedClassWithSerialVersionUid.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(true)); } @Test public void testClassAdded() { JApiClass jApiClass = getJApiClass(jApiClasses, CompatibleChanges.ClassAdded.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.NEW)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE)); } @Test public void testClassRemoved() { JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.test.serialversion.CompatibleChanges$ClassRemoved"); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.REMOVED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_INCOMPATIBLE_CLASS_REMOVED)); } @Test public void testAddedSerializable() { JApiClass jApiClass = getJApiClass(jApiClasses, CompatibleChanges.AddedSerializable.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(false)); } @Test public void testFieldAccessChangeFromPublicToPrivate() { JApiClass jApiClass = getJApiClass(jApiClasses, CompatibleChanges.FieldAccessChangeFromPublicToPrivate.class.getName()); assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); assertThat(jApiClass.getJavaObjectSerializationCompatible(), is(JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE)); assertThat(serializationAndDeserializationSuccessful(jApiClass), is(true)); } private boolean serializationAndDeserializationSuccessful(JApiClass jApiClass) { boolean successful = false; try { ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); Optional<CtClass> oldClassOptional = jApiClass.getOldClass(); if (oldClassOptional.isPresent()) { CtClass ctClass = oldClassOptional.get(); Object oldClassInstance = ctClass.toClass(new URLClassLoader(new URL[]{}), JavaObjectSerializationTest.class.getProtectionDomain()).newInstance(); ObjectOutputStream oos = new ObjectOutputStream(byteOutputStream); oos.writeObject(oldClassInstance); oos.flush(); oos.close(); byte[] bytes = byteOutputStream.toByteArray(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); Object readObject = ois.readObject(); assertThat(readObject, notNullValue()); successful = true; } } catch (Exception e) { LOGGER.log(Level.FINE, "Serialization failed: " + e.getMessage(), e); } return successful; } }