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 18package org.apache.harmony.tests.java.io; 19 20import dalvik.system.DexFile; 21import dalvik.system.VMRuntime; 22import java.io.Externalizable; 23import java.io.File; 24import java.io.IOException; 25import java.io.InputStream; 26import java.io.ObjectInput; 27import java.io.ObjectOutput; 28import java.io.ObjectStreamClass; 29import java.io.ObjectStreamField; 30import java.io.Serializable; 31import java.lang.reflect.Field; 32import java.lang.reflect.InvocationTargetException; 33import java.lang.reflect.Method; 34import java.lang.reflect.Proxy; 35import java.nio.file.Files; 36import java.nio.file.StandardCopyOption; 37import junit.framework.TestCase; 38 39public 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