blob: 1002cf1de623b64872c9943b46b172ee772adeeb [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 libcore.java.io;
import junit.framework.TestCase;
import java.io.InvalidClassException;
import java.io.InvalidObjectException;
import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import libcore.util.SerializationTester;
public final class SerializationTest extends TestCase {
// http://b/4471249
public void testSerializeFieldMadeTransient() throws Exception {
// Does ObjectStreamClass have the right idea?
ObjectStreamClass osc = ObjectStreamClass.lookup(FieldMadeTransient.class);
ObjectStreamField[] fields = osc.getFields();
assertEquals(1, fields.length);
assertEquals("nonTransientInt", fields[0].getName());
assertEquals(int.class, fields[0].getType());
// this was created by serializing a FieldMadeTransient with a non-0 transientInt
String s = "aced0005737200346c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
+ "374244669656c644d6164655472616e7369656e74000000000000000002000149000c7472616e736"
+ "9656e74496e747870abababab";
FieldMadeTransient deserialized = (FieldMadeTransient) SerializationTester.deserializeHex(s);
assertEquals(0, deserialized.transientInt);
}
static class FieldMadeTransient implements Serializable {
private static final long serialVersionUID = 0L;
private transient int transientInt;
private int nonTransientInt;
}
public void testSerialVersionUidChange() throws Exception {
// this was created by serializing a SerialVersionUidChanged with serialVersionUID = 0L
String s = "aced0005737200396c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
+ "3742453657269616c56657273696f6e5569644368616e67656400000000000000000200014900016"
+ "1787000000003";
try {
SerializationTester.deserializeHex(s);
fail();
} catch (InvalidClassException expected) {
}
}
@SuppressWarnings("unused") // Required for deserialization test
static class SerialVersionUidChanged implements Serializable {
private static final long serialVersionUID = 1L; // was 0L
private int a;
}
public void testMissingSerialVersionUid() throws Exception {
// this was created by serializing a FieldsChanged with one int field named 'a'
String s = "aced00057372002f6c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
+ "374244669656c64734368616e6765643bcfb934e310fa1c02000149000161787000000003";
try {
SerializationTester.deserializeHex(s);
fail();
} catch (InvalidClassException expected) {
}
}
@SuppressWarnings("unused") // Required for deserialization test
static class FieldsChanged implements Serializable {
private int b; // was 'a'
}
public static boolean wasSerializableInitializedFlag = false;
@SuppressWarnings("unused") // Required for deserialization test.
public static class WasSerializable /* implements java.io.Serializable */ {
static final long serialVersionUID = 0L;
static {
SerializationTest.wasSerializableInitializedFlag = true;
}
private int i;
}
public void testDeserializeWasSerializableClass() throws Exception {
// This was created by serializing a WasSerializable when it was serializable.
// String s = SerializationTester.serializeHex(new WasSerializable());
final String s = "aced0005737200316c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
+ "e546573742457617353657269616c697a61626c65000000000000000002000149000169787000000"
+ "000";
wasSerializableInitializedFlag = false;
try {
SerializationTester.deserializeHex(s);
fail();
} catch (InvalidClassException expected) {
}
assertFalse(wasSerializableInitializedFlag);
}
// The WasExternalizable class before it was modified.
/*
public static class WasExternalizable implements Externalizable {
static final long serialVersionUID = 0L;
@Override
public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException {
}
@Override
public void writeExternal(ObjectOutput output) throws IOException {
}
}
*/
public static boolean wasExternalizableInitializedFlag = false;
@SuppressWarnings("unused") // Required for deserialization test
public static class WasExternalizable implements Serializable {
static final long serialVersionUID = 0L;
static {
SerializationTest.wasExternalizableInitializedFlag = true;
}
}
public void testDeserializeWasExternalizableClass() throws Exception {
// This was created by serializing a WasExternalizable when it was externalizable.
// String s = SerializationTester.serializeHex(new WasExternalizable());
final String s = "aced0005737200336c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
+ "e546573742457617345787465726e616c697a61626c6500000000000000000c0000787078";
wasExternalizableInitializedFlag = false;
try {
SerializationTester.deserializeHex(s);
fail();
} catch (InvalidClassException expected) {
}
// Unlike other similar tests static initialization will take place if the local class is
// Serializable or Externalizable because serialVersionUID field is accessed.
// The RI appears to do the same.
assertTrue(wasExternalizableInitializedFlag);
}
// The WasEnum class before it was modified.
/*
public enum WasEnum {
VALUE
}
*/
public static boolean wasEnumInitializedFlag = false;
@SuppressWarnings("unused") // Required for deserialization test
public static class WasEnum {
static final long serialVersionUID = 0L;
static {
SerializationTest.wasEnumInitializedFlag = true;
}
}
public void testDeserializeWasEnum() throws Exception {
// This was created by serializing a WasEnum when it was an enum.
// String s = SerializationTester.serializeHex(WasEnum.VALUE);
final String s = "aced00057e7200296c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
+ "e5465737424576173456e756d00000000000000001200007872000e6a6176612e6c616e672e456e7"
+ "56d0000000000000000120000787074000556414c5545";
wasEnumInitializedFlag = false;
try {
SerializationTester.deserializeHex(s);
fail();
} catch (InvalidClassException expected) {
}
assertFalse(wasEnumInitializedFlag);
}
// The WasObject class before it was modified.
/*
public static class WasObject implements java.io.Serializable {
static final long serialVersionUID = 0L;
private int i;
}
*/
public static boolean wasObjectInitializedFlag;
@SuppressWarnings("unused") // Required for deserialization test
public enum WasObject {
VALUE;
static {
SerializationTest.wasObjectInitializedFlag = true;
}
}
public void testDeserializeWasObject() throws Exception {
// This was created by serializing a WasObject when it wasn't yet an enum.
// String s = SerializationTester.serializeHex(new WasObject());
final String s = "aced00057372002b6c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
+ "e54657374245761734f626a656374000000000000000002000149000169787000000000";
wasObjectInitializedFlag = false;
try {
SerializationTester.deserializeHex(s);
fail();
} catch (InvalidClassException expected) {
}
assertFalse(wasObjectInitializedFlag);
}
@SuppressWarnings("unused") // Required for deserialization test
public enum EnumMissingValue {
/*MISSING_VALUE*/
}
public void testDeserializeEnumMissingValue() throws Exception {
// This was created by serializing a EnumMissingValue when it had MISSING_VALUE.
// String s = SerializationTester.serializeHex(EnumMissingValue.MISSING_VALUE);
final String s = "aced00057e7200326c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
+ "e5465737424456e756d4d697373696e6756616c756500000000000000001200007872000e6a61766"
+ "12e6c616e672e456e756d0000000000000000120000787074000d4d495353494e475f56414c5545";
try {
SerializationTester.deserializeHex(s);
fail();
} catch (InvalidObjectException expected) {
}
}
public static Object hasStaticInitializerObject;
public static class HasStaticInitializer implements Serializable {
static {
SerializationTest.hasStaticInitializerObject = new Object();
}
}
public void testDeserializeStaticInitializerIsRunEventually() throws Exception {
// This was created by serializing a HasStaticInitializer
// String s = SerializationTester.serializeHex(new HasStaticInitializer());
final String s = "aced0005737200366c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
+ "e5465737424486173537461746963496e697469616c697a6572138aa8ed9e9b660a0200007870";
// Confirm the ClassLoader behaves as it should.
Class.forName(
HasStaticInitializer.class.getName(),
false /* shouldInitialize */,
Thread.currentThread().getContextClassLoader());
assertNull(hasStaticInitializerObject);
SerializationTester.deserializeHex(s);
assertNotNull(hasStaticInitializerObject);
}
@SuppressWarnings("unused") // Required for deserialization test
public static /*interface*/ class WasInterface {
}
@SuppressWarnings("unused") // Required for deserialization test
public static class SerializableInvocationHandler implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
return null;
}
}
public void testDeserializeProxyWasInterface() throws Exception {
// This was created by serializing a proxy referencing WasInterface when it was an
// interface.
// Object o = Proxy.newProxyInstance(
// Thread.currentThread().getContextClassLoader(),
// new Class[] { WasInterface.class },
// new SerializableInvocationHandler());
// String s = SerializationTester.serializeHex(o);
final String s = "aced0005737d00000001002e6c6962636f72652e6a6176612e696f2e53657269616c697a6"
+ "174696f6e5465737424576173496e74657266616365787200176a6176612e6c616e672e7265666c6"
+ "563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f72656"
+ "66c6563742f496e766f636174696f6e48616e646c65723b78707372003f6c6962636f72652e6a617"
+ "6612e696f2e53657269616c697a6174696f6e546573742453657269616c697a61626c65496e766f6"
+ "36174696f6e48616e646c6572e6ceffa2941ee3210200007870";
try {
SerializationTester.deserializeHex(s);
fail();
} catch (ClassNotFoundException expected) {
}
}
@SuppressWarnings("unused") // Required for deserialization test
public static class WasSerializableInvocationHandler
implements InvocationHandler /*, Serializable*/ {
static final long serialVersionUID = 0L;
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
return null;
}
}
public void testDeserializeProxyInvocationHandlerWasSerializable() throws Exception {
// This was created by serializing a proxy referencing WasSerializableInvocationHandler when
// it was Serializable.
// Object o = Proxy.newProxyInstance(
// Thread.currentThread().getContextClassLoader(),
// new Class[] { Comparable.class },
// new WasSerializableInvocationHandler());
// String s = SerializationTester.serializeHex(o);
final String s = "aced0005737d0000000100146a6176612e6c616e672e436f6d70617261626c65787200176"
+ "a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c00016874002"
+ "54c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707"
+ "37200426c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e546573742457617"
+ "353657269616c697a61626c65496e766f636174696f6e48616e646c6572000000000000000002000"
+ "07870";
try {
SerializationTester.deserializeHex(s);
fail();
} catch (InvalidClassException expected) {
}
}
}