Home | History | Annotate | Download | only in io
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package org.apache.harmony.tests.java.io;
     19 
     20 import dalvik.system.DexFile;
     21 import dalvik.system.VMRuntime;
     22 import java.io.Externalizable;
     23 import java.io.File;
     24 import java.io.IOException;
     25 import java.io.InputStream;
     26 import java.io.ObjectInput;
     27 import java.io.ObjectOutput;
     28 import java.io.ObjectStreamClass;
     29 import java.io.ObjectStreamField;
     30 import java.io.Serializable;
     31 import java.lang.reflect.Field;
     32 import java.lang.reflect.InvocationTargetException;
     33 import java.lang.reflect.Method;
     34 import java.lang.reflect.Proxy;
     35 import java.nio.file.Files;
     36 import java.nio.file.StandardCopyOption;
     37 import junit.framework.TestCase;
     38 
     39 public class ObjectStreamClassTest extends TestCase {
     40 
     41     static class DummyClass implements Serializable {
     42         private static final long serialVersionUID = 999999999999999L;
     43 
     44         long bam = 999L;
     45 
     46         int ham = 9999;
     47 
     48         public static long getUID() {
     49             return serialVersionUID;
     50         }
     51     }
     52 
     53     /**
     54      * java.io.ObjectStreamClass#forClass()
     55      */
     56     public void test_forClass() {
     57         // Need to test during serialization to be sure an instance is
     58         // returned
     59         ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
     60         assertEquals("forClass returned an object: " + osc.forClass(),
     61                 DummyClass.class, osc.forClass());
     62     }
     63 
     64     /**
     65      * java.io.ObjectStreamClass#getField(java.lang.String)
     66      */
     67     public void test_getFieldLjava_lang_String() {
     68         ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
     69         assertEquals("getField did not return correct field", 'J', osc
     70                 .getField("bam").getTypeCode());
     71         assertNull("getField did not null for non-existent field", osc
     72                 .getField("wham"));
     73     }
     74 
     75     /**
     76      * java.io.ObjectStreamClass#getFields()
     77      */
     78     public void test_getFields() {
     79         ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
     80         ObjectStreamField[] osfArray = osc.getFields();
     81         assertTrue(
     82                 "Array of fields should be of length 2 but is instead of length: "
     83                         + osfArray.length, osfArray.length == 2);
     84     }
     85 
     86     /**
     87      * java.io.ObjectStreamClass#getName()
     88      */
     89     public void test_getName() {
     90         ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
     91         assertEquals(
     92                 "getName returned incorrect name: " + osc.getName(),
     93                 "org.apache.harmony.tests.java.io.ObjectStreamClassTest$DummyClass",
     94                 osc.getName());
     95     }
     96 
     97     /**
     98      * java.io.ObjectStreamClass#getSerialVersionUID()
     99      */
    100     public void test_getSerialVersionUID() {
    101         ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
    102         assertTrue("getSerialversionUID returned incorrect uid: "
    103                 + osc.getSerialVersionUID() + " instead of "
    104                 + DummyClass.getUID(), osc.getSerialVersionUID() == DummyClass
    105                 .getUID());
    106     }
    107 
    108     static class SyntheticTest implements Serializable {
    109         private int i;
    110 
    111         private class X implements Serializable {
    112             public int get() {
    113                 return i;
    114             }
    115         }
    116 
    117         public X foo() {
    118             return new X();
    119         }
    120     }
    121 
    122     /**
    123      * java.io.ObjectStreamClass#lookup(java.lang.Class)
    124      */
    125     public void test_lookupLjava_lang_Class() {
    126         ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
    127         assertEquals(
    128                 "lookup returned wrong class: " + osc.getName(),
    129                 "org.apache.harmony.tests.java.io.ObjectStreamClassTest$DummyClass",
    130                 osc.getName());
    131     }
    132 
    133     /**
    134      * java.io.ObjectStreamClass#toString()
    135      */
    136     public void test_toString() {
    137         ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
    138         String oscString = osc.toString();
    139 
    140         // The previous test was more specific than the spec so it was replaced
    141         // with the test below
    142         assertTrue("toString returned incorrect string: " + osc.toString(),
    143                 oscString.indexOf("serialVersionUID") >= 0
    144                         && oscString.indexOf("999999999999999L") >= 0);
    145     }
    146 
    147     public void testSerialization() {
    148         ObjectStreamClass osc = ObjectStreamClass
    149                 .lookup(ObjectStreamClass.class);
    150         assertEquals(0, osc.getFields().length);
    151     }
    152 
    153     public void test_specialTypes() {
    154         Class<?> proxyClass = Proxy.getProxyClass(this.getClass()
    155                 .getClassLoader(), new Class[] { Runnable.class });
    156 
    157         ObjectStreamClass proxyStreamClass = ObjectStreamClass
    158                 .lookup(proxyClass);
    159 
    160         assertEquals("Proxy classes should have zero serialVersionUID", 0,
    161                 proxyStreamClass.getSerialVersionUID());
    162         ObjectStreamField[] proxyFields = proxyStreamClass.getFields();
    163         assertEquals("Proxy classes should have no serialized fields", 0,
    164                 proxyFields.length);
    165 
    166         ObjectStreamClass enumStreamClass = ObjectStreamClass
    167                 .lookup(Thread.State.class);
    168 
    169         assertEquals("Enum classes should have zero serialVersionUID", 0,
    170                 enumStreamClass.getSerialVersionUID());
    171         ObjectStreamField[] enumFields = enumStreamClass.getFields();
    172         assertEquals("Enum classes should have no serialized fields", 0,
    173                 enumFields.length);
    174     }
    175 
    176     /**
    177      * @since 1.6
    178      */
    179     static class NonSerialzableClass {
    180         private static final long serialVersionUID = 1l;
    181 
    182         public static long getUID() {
    183             return serialVersionUID;
    184         }
    185     }
    186 
    187     /**
    188      * @since 1.6
    189      */
    190     static class ExternalizableClass implements Externalizable {
    191 
    192         private static final long serialVersionUID = -4285635779249689129L;
    193 
    194         public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException {
    195             throw new ClassNotFoundException();
    196         }
    197 
    198         public void writeExternal(ObjectOutput output) throws IOException {
    199             throw new IOException();
    200         }
    201 
    202     }
    203 
    204     /**
    205      * java.io.ObjectStreamClass#lookupAny(java.lang.Class)
    206      * @since 1.6
    207      */
    208     public void test_lookupAnyLjava_lang_Class() {
    209         // Test for method java.io.ObjectStreamClass
    210         // java.io.ObjectStreamClass.lookupAny(java.lang.Class)
    211         ObjectStreamClass osc = ObjectStreamClass.lookupAny(DummyClass.class);
    212         assertEquals("lookup returned wrong class: " + osc.getName(),
    213                 "org.apache.harmony.tests.java.io.ObjectStreamClassTest$DummyClass", osc
    214                 .getName());
    215 
    216         osc = ObjectStreamClass.lookupAny(NonSerialzableClass.class);
    217         assertEquals("lookup returned wrong class: " + osc.getName(),
    218                 "org.apache.harmony.tests.java.io.ObjectStreamClassTest$NonSerialzableClass",
    219                 osc.getName());
    220 
    221         osc = ObjectStreamClass.lookupAny(ExternalizableClass.class);
    222         assertEquals("lookup returned wrong class: " + osc.getName(),
    223                 "org.apache.harmony.tests.java.io.ObjectStreamClassTest$ExternalizableClass",
    224                 osc.getName());
    225 
    226         osc = ObjectStreamClass.lookup(NonSerialzableClass.class);
    227         assertNull(osc);
    228     }
    229 
    230     // http://b/28106822
    231     public void testBug28106822() throws Exception {
    232         int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
    233         try {
    234             // Assert behavior up to 24
    235             VMRuntime.getRuntime().setTargetSdkVersion(24);
    236             Method getConstructorId = ObjectStreamClass.class.getDeclaredMethod(
    237                     "getConstructorId", Class.class);
    238             getConstructorId.setAccessible(true);
    239 
    240             assertEquals(1189998819991197253L, getConstructorId.invoke(null, Object.class));
    241             assertEquals(1189998819991197253L, getConstructorId.invoke(null, String.class));
    242 
    243             Method newInstance = ObjectStreamClass.class.getDeclaredMethod("newInstance",
    244                     Class.class, Long.TYPE);
    245             newInstance.setAccessible(true);
    246 
    247             Object obj = newInstance.invoke(null, String.class, 0 /* ignored */);
    248             assertNotNull(obj);
    249             assertTrue(obj instanceof String);
    250 
    251             // Assert behavior from API 25
    252             VMRuntime.getRuntime().setTargetSdkVersion(25);
    253             try {
    254                 getConstructorId.invoke(null, Object.class);
    255                 fail();
    256             } catch (InvocationTargetException expected) {
    257                 assertTrue(expected.getCause() instanceof UnsupportedOperationException);
    258             }
    259             try {
    260                 newInstance.invoke(null, String.class, 0 /* ignored */);
    261                 fail();
    262             } catch (InvocationTargetException expected) {
    263                 assertTrue(expected.getCause() instanceof UnsupportedOperationException);
    264             }
    265 
    266         } finally {
    267             VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion);
    268         }
    269     }
    270 
    271     // Class without <clinit> method
    272     public static class NoClinitParent {
    273     }
    274     // Class without <clinit> method
    275     public static class NoClinitChildWithNoClinitParent extends NoClinitParent {
    276     }
    277 
    278     // Class with <clinit> method
    279     public static class ClinitParent {
    280         // This field will trigger creation of <clinit> method for this class
    281         private static final String TAG = ClinitParent.class.getName();
    282         static {
    283 
    284         }
    285     }
    286     // Class without <clinit> but with parent that has <clinit> method
    287     public static class NoClinitChildWithClinitParent extends ClinitParent {
    288     }
    289 
    290     // http://b/29064453
    291     public void testHasClinit() throws Exception {
    292         Method hasStaticInitializer =
    293             ObjectStreamClass.class.getDeclaredMethod("hasStaticInitializer", Class.class,
    294                                                       boolean.class);
    295         hasStaticInitializer.setAccessible(true);
    296 
    297         assertTrue((Boolean)
    298                    hasStaticInitializer.invoke(null, ClinitParent.class,
    299                                                false /* checkSuperclass */));
    300 
    301         // RI will return correctly False in this case, but android has been returning true
    302         // in this particular case. We're returning true to enable deserializing classes
    303         // like NoClinitChildWithClinitParent without explicit serialVersionID field.
    304         assertTrue((Boolean)
    305                    hasStaticInitializer.invoke(null, NoClinitChildWithClinitParent.class,
    306                                                false /* checkSuperclass */));
    307         assertFalse((Boolean)
    308                     hasStaticInitializer.invoke(null, NoClinitParent.class,
    309                                                 false /* checkSuperclass */));
    310         assertFalse((Boolean)
    311                     hasStaticInitializer.invoke(null, NoClinitChildWithNoClinitParent.class,
    312                                                 false /* checkSuperclass */));
    313 
    314 
    315         assertTrue((Boolean)
    316                    hasStaticInitializer.invoke(null, ClinitParent.class,
    317                                                true /* checkSuperclass */));
    318         assertFalse((Boolean)
    319                    hasStaticInitializer.invoke(null, NoClinitChildWithClinitParent.class,
    320                                                true /* checkSuperclass */));
    321         assertFalse((Boolean)
    322                     hasStaticInitializer.invoke(null, NoClinitParent.class,
    323                                                 true /* checkSuperclass */));
    324         assertFalse((Boolean)
    325                     hasStaticInitializer.invoke(null, NoClinitChildWithNoClinitParent.class,
    326                                                 true /* checkSuperclass */));
    327     }
    328 
    329     // http://b/29721023
    330     public void testClassWithSameFieldName() throws Exception {
    331         // Load class from dex, it's not possible to create a class with same-named
    332         // fields in java (but it's allowed in dex).
    333         File sameFieldNames = File.createTempFile("sameFieldNames", ".dex");
    334         InputStream dexIs = this.getClass().getClassLoader().
    335             getResourceAsStream("tests/api/java/io/sameFieldNames.dex");
    336         assertNotNull(dexIs);
    337 
    338         try {
    339             Files.copy(dexIs, sameFieldNames.toPath(), StandardCopyOption.REPLACE_EXISTING);
    340             DexFile dexFile = new DexFile(sameFieldNames);
    341             Class<?> clazz = dexFile.loadClass("sameFieldNames", getClass().getClassLoader());
    342             ObjectStreamClass osc = ObjectStreamClass.lookup(clazz);
    343             assertEquals(4, osc.getFields().length);
    344             dexFile.close();
    345         } finally {
    346             if (sameFieldNames.exists()) {
    347                 sameFieldNames.delete();
    348             }
    349         }
    350     }
    351 }
    352