Home | History | Annotate | Download | only in io
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package libcore.java.io;
     18 
     19 import junit.framework.TestCase;
     20 
     21 import java.io.InvalidClassException;
     22 import java.io.InvalidObjectException;
     23 import java.io.NotSerializableException;
     24 import java.io.ObjectStreamClass;
     25 import java.io.ObjectStreamField;
     26 import java.io.Serializable;
     27 import java.lang.reflect.InvocationHandler;
     28 import java.lang.reflect.Method;
     29 import libcore.libcore.util.SerializationTester;
     30 
     31 public final class SerializationTest extends TestCase {
     32 
     33     // http://b/4471249
     34     public void testSerializeFieldMadeTransient() throws Exception {
     35         // Does ObjectStreamClass have the right idea?
     36         ObjectStreamClass osc = ObjectStreamClass.lookup(FieldMadeTransient.class);
     37         ObjectStreamField[] fields = osc.getFields();
     38         assertEquals(1, fields.length);
     39         assertEquals("nonTransientInt", fields[0].getName());
     40         assertEquals(int.class, fields[0].getType());
     41 
     42         // this was created by serializing a FieldMadeTransient with a non-0 transientInt
     43         String s = "aced0005737200346c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
     44                 + "374244669656c644d6164655472616e7369656e74000000000000000002000149000c7472616e736"
     45                 + "9656e74496e747870abababab";
     46         FieldMadeTransient deserialized = (FieldMadeTransient) SerializationTester.deserializeHex(s);
     47         assertEquals(0, deserialized.transientInt);
     48     }
     49 
     50     static class FieldMadeTransient implements Serializable {
     51         private static final long serialVersionUID = 0L;
     52         @SuppressWarnings("unused")
     53         private transient int transientInt;
     54         @SuppressWarnings("unused")
     55         private int nonTransientInt;
     56     }
     57 
     58     public void testSerializeFieldMadeStatic() throws Exception {
     59         // Does ObjectStreamClass have the right idea?
     60         ObjectStreamClass osc = ObjectStreamClass.lookup(FieldMadeStatic.class);
     61         ObjectStreamField[] fields = osc.getFields();
     62         assertEquals(0, fields.length);
     63 
     64         // This was created by serializing a FieldMadeStatic with a non-static staticInt
     65         String s = "aced0005737200316c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
     66                 + "374244669656c644d6164655374617469630000000000000000020001490009737461746963496e7"
     67                 + "47870000022b8";
     68         FieldMadeStatic deserialized = (FieldMadeStatic) SerializationTester.deserializeHex(s);
     69         // The field data must be ignored if it is static.
     70         assertEquals(9999, deserialized.staticInt);
     71     }
     72 
     73     static class FieldMadeStatic implements Serializable {
     74         private static final long serialVersionUID = 0L;
     75         // private int staticInt = 8888;
     76         private static int staticInt = 9999;
     77     }
     78 
     79     public static boolean serializableContainer1InitializedFlag = false;
     80     public static boolean unserializable1InitializedFlag = false;
     81 
     82     public static class Unserializable1 {
     83         static {
     84             SerializationTest.unserializable1InitializedFlag = true;
     85         }
     86     }
     87 
     88     static class SerializableContainer1 implements Serializable {
     89         private static final long serialVersionUID = 0L;
     90         private Unserializable1 unserializable = null;
     91 
     92         static {
     93             serializableContainer1InitializedFlag = true;
     94         }
     95     }
     96 
     97     // We can serialize an object that has an unserializable field providing it is null.
     98     public void testDeserializeNullUnserializableField() throws Exception {
     99         // This was created by creating a new SerializableContainer and not setting the
    100         // unserializable field. A canned serialized form is used so we can tell if the static
    101         // initializers were executed during deserialization.
    102         // SerializationTester.serializeHex(new SerializableContainer1());
    103         String s = "aced0005737200386c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
    104                   + "3742453657269616c697a61626c65436f6e7461696e65723100000000000000000200014c000e7"
    105                   + "56e73657269616c697a61626c657400124c6a6176612f6c616e672f4f626a6563743b787070";
    106 
    107         assertFalse(serializableContainer1InitializedFlag);
    108         assertFalse(unserializable1InitializedFlag);
    109 
    110         SerializableContainer1 sc = (SerializableContainer1) SerializationTester.deserializeHex(s);
    111         assertNull(sc.unserializable);
    112 
    113         // Confirm the container was initialized, but the class for the null field was not.
    114         assertTrue(serializableContainer1InitializedFlag);
    115         assertFalse(unserializable1InitializedFlag);
    116     }
    117 
    118     static class Unserializable2 {
    119     }
    120 
    121     static class HasUnserializableField implements Serializable {
    122         private static final long serialVersionUID = 0L;
    123         @SuppressWarnings("unused") // Required to make objects unserializable.
    124         private Unserializable2 unserializable = new Unserializable2();
    125     }
    126 
    127     // We must not serialize an object that has a non-null unserializable field.
    128     public void testSerializeUnserializableField() throws Exception {
    129         HasUnserializableField uf = new HasUnserializableField();
    130         try {
    131             SerializationTester.serializeHex(uf);
    132             fail();
    133         } catch (NotSerializableException expected) {
    134         }
    135     }
    136 
    137     public static boolean serializableContainer2InitializedFlag = false;
    138 
    139     @SuppressWarnings("unused") // Required for deserialization test
    140     static class SerializableContainer2 implements Serializable {
    141         private static final long serialVersionUID = 0L;
    142         private WasSerializable unserializable = null;
    143 
    144         static {
    145             serializableContainer2InitializedFlag = true;
    146         }
    147     }
    148 
    149     // It must not be possible to deserialize an object if a field is no longer serializable.
    150     public void testDeserializeUnserializableField() throws Exception {
    151         // This was generated by creating a SerializableContainer2 and setting the unserializable
    152         // field to a WasSerializable when it was still Serializable. A canned serialized form is
    153         // used so we can tell if the static initializers were executed during deserialization.
    154         // SerializableContainer2 sc = new SerializableContainer2();
    155         // sc.unserializable = new WasSerializable();
    156         // SerializationTester.serializeHex(sc);
    157         String s = "aced0005737200386c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
    158                   + "3742453657269616c697a61626c65436f6e7461696e65723200000000000000000200014c000e7"
    159                   + "56e73657269616c697a61626c657400334c6c6962636f72652f6a6176612f696f2f53657269616"
    160                   + "c697a6174696f6e546573742457617353657269616c697a61626c653b7870737200316c6962636"
    161                   + "f72652e6a6176612e696f2e53657269616c697a6174696f6e546573742457617353657269616c6"
    162                   + "97a61626c65000000000000000002000149000169787000000000";
    163 
    164         assertFalse(serializableContainer2InitializedFlag);
    165         assertFalse(wasSerializableInitializedFlag);
    166         try {
    167             SerializationTester.deserializeHex(s);
    168             fail();
    169         } catch (InvalidClassException expected) {
    170         }
    171         // The container class will be initialized to establish the serialVersionUID.
    172         assertTrue(serializableContainer2InitializedFlag);
    173         // Confirm the contained class was initialized.
    174         assertFalse(wasSerializableInitializedFlag);
    175     }
    176 
    177     public void testSerialVersionUidChange() throws Exception {
    178         // this was created by serializing a SerialVersionUidChanged with serialVersionUID = 0L
    179         String s = "aced0005737200396c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
    180                 + "3742453657269616c56657273696f6e5569644368616e67656400000000000000000200014900016"
    181                 + "1787000000003";
    182         try {
    183             SerializationTester.deserializeHex(s);
    184             fail();
    185         } catch (InvalidClassException expected) {
    186         }
    187     }
    188 
    189     @SuppressWarnings("unused") // Required for deserialization test
    190     static class SerialVersionUidChanged implements Serializable {
    191         private static final long serialVersionUID = 1L; // was 0L
    192         private int a;
    193     }
    194 
    195     public void testMissingSerialVersionUid() throws Exception {
    196         // this was created by serializing a FieldsChanged with one int field named 'a'
    197         String s = "aced00057372002f6c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
    198                 + "374244669656c64734368616e6765643bcfb934e310fa1c02000149000161787000000003";
    199         try {
    200             SerializationTester.deserializeHex(s);
    201             fail();
    202         } catch (InvalidClassException expected) {
    203         }
    204     }
    205 
    206     @SuppressWarnings("unused") // Required for deserialization test
    207     static class FieldsChanged implements Serializable {
    208         private int b; // was 'a'
    209     }
    210 
    211     public static boolean wasSerializableInitializedFlag = false;
    212 
    213     @SuppressWarnings("unused")  // Required for deserialization test.
    214     public static class WasSerializable /* implements java.io.Serializable */ {
    215         static final long serialVersionUID = 0L;
    216         static {
    217             SerializationTest.wasSerializableInitializedFlag = true;
    218         }
    219         private int i;
    220     }
    221 
    222     public void testDeserializeWasSerializableClass() throws Exception {
    223         // This was created by serializing a WasSerializable when it was serializable.
    224         // String s = SerializationTester.serializeHex(new WasSerializable());
    225         final String s = "aced0005737200316c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
    226                 + "e546573742457617353657269616c697a61626c65000000000000000002000149000169787000000"
    227                 + "000";
    228 
    229         assertFalse(wasSerializableInitializedFlag);
    230         try {
    231             SerializationTester.deserializeHex(s);
    232             fail();
    233         } catch (InvalidClassException expected) {
    234         }
    235         assertFalse(wasSerializableInitializedFlag);
    236     }
    237 
    238     // The WasExternalizable class before it was modified.
    239     /*
    240     public static class WasExternalizable implements Externalizable {
    241         static final long serialVersionUID = 0L;
    242 
    243         @Override
    244         public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException {
    245 
    246         }
    247 
    248         @Override
    249         public void writeExternal(ObjectOutput output) throws IOException {
    250 
    251         }
    252     }
    253     */
    254 
    255     public static boolean wasExternalizableInitializedFlag = false;
    256 
    257     @SuppressWarnings("unused") // Required for deserialization test
    258     public static class WasExternalizable implements Serializable {
    259         static final long serialVersionUID = 0L;
    260         static {
    261             SerializationTest.wasExternalizableInitializedFlag = true;
    262         }
    263 
    264     }
    265 
    266     public void testDeserializeWasExternalizableClass() throws Exception {
    267         // This was created by serializing a WasExternalizable when it was externalizable.
    268         // String s = SerializationTester.serializeHex(new WasExternalizable());
    269         final String s = "aced0005737200336c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
    270                 + "e546573742457617345787465726e616c697a61626c6500000000000000000c0000787078";
    271 
    272         assertFalse(wasExternalizableInitializedFlag);
    273         try {
    274             SerializationTester.deserializeHex(s);
    275             fail();
    276         } catch (InvalidClassException expected) {
    277         }
    278         // Unlike other similar tests static initialization will take place if the local class is
    279         // Serializable or Externalizable because serialVersionUID field is accessed.
    280         // The RI appears to do the same.
    281         assertTrue(wasExternalizableInitializedFlag);
    282     }
    283 
    284     // The WasEnum class before it was modified.
    285     /*
    286     public enum WasEnum {
    287         VALUE
    288     }
    289     */
    290 
    291     public static boolean wasEnumInitializedFlag = false;
    292 
    293     @SuppressWarnings("unused") // Required for deserialization test
    294     public static class WasEnum {
    295         static final long serialVersionUID = 0L;
    296         static {
    297             SerializationTest.wasEnumInitializedFlag = true;
    298         }
    299     }
    300 
    301     public void testDeserializeWasEnum() throws Exception {
    302         // This was created by serializing a WasEnum when it was an enum.
    303         // String s = SerializationTester.serializeHex(WasEnum.VALUE);
    304         final String s = "aced00057e7200296c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
    305                 + "e5465737424576173456e756d00000000000000001200007872000e6a6176612e6c616e672e456e7"
    306                 + "56d0000000000000000120000787074000556414c5545";
    307 
    308         assertFalse(wasEnumInitializedFlag);
    309         try {
    310             SerializationTester.deserializeHex(s);
    311             fail();
    312         } catch (InvalidClassException expected) {
    313         }
    314         assertFalse(wasEnumInitializedFlag);
    315     }
    316 
    317     // The WasObject class before it was modified.
    318     /*
    319     public static class WasObject implements java.io.Serializable {
    320         static final long serialVersionUID = 0L;
    321         private int i;
    322     }
    323     */
    324 
    325     public static boolean wasObjectInitializedFlag;
    326 
    327     @SuppressWarnings("unused") // Required for deserialization test
    328     public enum WasObject {
    329         VALUE;
    330 
    331         static {
    332             SerializationTest.wasObjectInitializedFlag = true;
    333         }
    334     }
    335 
    336     public void testDeserializeWasObject() throws Exception {
    337         // This was created by serializing a WasObject when it wasn't yet an enum.
    338         // String s = SerializationTester.serializeHex(new WasObject());
    339         final String s = "aced00057372002b6c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
    340                 + "e54657374245761734f626a656374000000000000000002000149000169787000000000";
    341 
    342         assertFalse(wasObjectInitializedFlag);
    343         try {
    344             SerializationTester.deserializeHex(s);
    345             fail();
    346         } catch (InvalidClassException expected) {
    347         }
    348         assertFalse(wasObjectInitializedFlag);
    349     }
    350 
    351     @SuppressWarnings("unused") // Required for deserialization test
    352     public enum EnumMissingValue {
    353         /*MISSING_VALUE*/
    354     }
    355 
    356     public void testDeserializeEnumMissingValue() throws Exception {
    357         // This was created by serializing a EnumMissingValue when it had MISSING_VALUE.
    358         // String s = SerializationTester.serializeHex(EnumMissingValue.MISSING_VALUE);
    359         final String s = "aced00057e7200326c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
    360                 + "e5465737424456e756d4d697373696e6756616c756500000000000000001200007872000e6a61766"
    361                 + "12e6c616e672e456e756d0000000000000000120000787074000d4d495353494e475f56414c5545";
    362 
    363         try {
    364             SerializationTester.deserializeHex(s);
    365             fail();
    366         } catch (InvalidObjectException expected) {
    367         }
    368     }
    369 
    370 
    371     public static Object hasStaticInitializerObject;
    372 
    373     public static class HasStaticInitializer implements Serializable {
    374         static {
    375             SerializationTest.hasStaticInitializerObject = new Object();
    376         }
    377     }
    378 
    379     public void testDeserializeStaticInitializerIsRunEventually() throws Exception {
    380         // This was created by serializing a HasStaticInitializer
    381         // String s = SerializationTester.serializeHex(new HasStaticInitializer());
    382         final String s = "aced0005737200366c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6"
    383                 + "e5465737424486173537461746963496e697469616c697a6572138aa8ed9e9b660a0200007870";
    384 
    385         // Confirm the ClassLoader behaves as it should.
    386         Class.forName(
    387                 HasStaticInitializer.class.getName(),
    388                 false /* shouldInitialize */,
    389                 Thread.currentThread().getContextClassLoader());
    390         assertNull(hasStaticInitializerObject);
    391 
    392         SerializationTester.deserializeHex(s);
    393 
    394         assertNotNull(hasStaticInitializerObject);
    395     }
    396 
    397     @SuppressWarnings("unused") // Required for deserialization test
    398     public static /*interface*/ class WasInterface {
    399     }
    400 
    401     @SuppressWarnings("unused") // Required for deserialization test
    402     public static class SerializableInvocationHandler implements InvocationHandler, Serializable {
    403         @Override
    404         public Object invoke(Object proxy, Method method, Object[] args) {
    405             return null;
    406         }
    407     }
    408 
    409     public void testDeserializeProxyWasInterface() throws Exception {
    410         // This was created by serializing a proxy referencing WasInterface when it was an
    411         // interface.
    412         // Object o = Proxy.newProxyInstance(
    413         //        Thread.currentThread().getContextClassLoader(),
    414         //        new Class[] { WasInterface.class },
    415         //        new SerializableInvocationHandler());
    416         // String s = SerializationTester.serializeHex(o);
    417         final String s = "aced0005737d00000001002e6c6962636f72652e6a6176612e696f2e53657269616c697a6"
    418                 + "174696f6e5465737424576173496e74657266616365787200176a6176612e6c616e672e7265666c6"
    419                 + "563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f72656"
    420                 + "66c6563742f496e766f636174696f6e48616e646c65723b78707372003f6c6962636f72652e6a617"
    421                 + "6612e696f2e53657269616c697a6174696f6e546573742453657269616c697a61626c65496e766f6"
    422                 + "36174696f6e48616e646c6572e6ceffa2941ee3210200007870";
    423         try {
    424             SerializationTester.deserializeHex(s);
    425             fail();
    426         } catch (ClassNotFoundException expected) {
    427         }
    428     }
    429 
    430     @SuppressWarnings("unused") // Required for deserialization test
    431     public static class WasSerializableInvocationHandler
    432             implements InvocationHandler /*, Serializable*/ {
    433         static final long serialVersionUID = 0L;
    434 
    435         @Override
    436         public Object invoke(Object proxy, Method method, Object[] args) {
    437             return null;
    438         }
    439     }
    440 
    441     public void testDeserializeProxyInvocationHandlerWasSerializable() throws Exception {
    442         // This was created by serializing a proxy referencing WasSerializableInvocationHandler when
    443         // it was Serializable.
    444         // Object o = Proxy.newProxyInstance(
    445         //        Thread.currentThread().getContextClassLoader(),
    446         //        new Class[] { Comparable.class },
    447         //        new WasSerializableInvocationHandler());
    448         // String s = SerializationTester.serializeHex(o);
    449         final String s = "aced0005737d0000000100146a6176612e6c616e672e436f6d70617261626c65787200176"
    450                 + "a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c00016874002"
    451                 + "54c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707"
    452                 + "37200426c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e546573742457617"
    453                 + "353657269616c697a61626c65496e766f636174696f6e48616e646c6572000000000000000002000"
    454                 + "07870";
    455         try {
    456             SerializationTester.deserializeHex(s);
    457             fail();
    458         } catch (InvalidClassException expected) {
    459         }
    460     }
    461 }
    462