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 17package libcore.java.io; 18 19import junit.framework.TestCase; 20 21import java.io.InvalidClassException; 22import java.io.InvalidObjectException; 23import java.io.NotSerializableException; 24import java.io.ObjectStreamClass; 25import java.io.ObjectStreamField; 26import java.io.Serializable; 27import java.lang.reflect.InvocationHandler; 28import java.lang.reflect.Method; 29import libcore.util.SerializationTester; 30 31public 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