1 /* 2 * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8246774 27 * @summary Basic tests for serializing and deserializing record classes 28 * @run testng RecordClassTest 29 * @run testng/othervm/java.security.policy=empty_security.policy RecordClassTest 30 */ 31 32 import java.io.ByteArrayInputStream; 33 import java.io.ByteArrayOutputStream; 34 import java.io.Externalizable; 35 import java.io.IOException; 36 import java.io.ObjectInput; 37 import java.io.ObjectInputStream; 38 import java.io.ObjectOutput; 39 import java.io.ObjectOutputStream; 40 import java.io.ObjectStreamClass; 41 import java.io.Serializable; 42 import org.testng.annotations.DataProvider; 43 import org.testng.annotations.Test; 44 import static java.lang.System.out; 45 import static org.testng.Assert.assertEquals; 46 import static org.testng.Assert.fail; 47 48 /** 49 * Serializes and deserializes record classes. Ensures that the SUID is 0. 50 */ 51 public class RecordClassTest { 52 53 record Foo () implements Serializable { } 54 55 record Bar (int x) implements Serializable { 56 private static final long serialVersionUID = 987654321L; 57 } 58 59 record Baz (Foo foo, Bar bar, int i) implements Serializable { } 60 61 interface ThrowingExternalizable extends Externalizable { 62 default void writeExternal(ObjectOutput out) { 63 fail("should not reach here"); 64 } 65 default void readExternal(ObjectInput in) { 66 fail("should not reach here"); 67 } 68 } 69 70 record Wibble () implements ThrowingExternalizable { 71 private static final long serialVersionUID = 12345678L; 72 } 73 74 record Wobble (long l) implements ThrowingExternalizable { } 75 76 record Wubble (Wobble wobble, Wibble wibble, String s) implements ThrowingExternalizable { } 77 78 @DataProvider(name = "recordClasses") 79 public Object[][] recordClasses() { 80 return new Object[][] { 81 new Object[] { Foo.class , 0L }, 82 new Object[] { Bar.class , 987654321L }, 83 new Object[] { Baz.class , 0L }, 84 new Object[] { Wibble.class , 12345678L }, 85 new Object[] { Wobble.class , 0L }, 86 new Object[] { Wubble.class , 0L }, 87 }; 88 } 89 90 /** Tests that the serialized and deserialized instances are equal. */ 91 @Test(dataProvider = "recordClasses") 92 public void testClassSerialization(Class<?> recordClass, long unused) 93 throws Exception 94 { 95 out.println("\n---"); 96 out.println("serializing : " + recordClass); 97 var deserializedClass = serializeDeserialize(recordClass); 98 out.println("deserialized: " + deserializedClass); 99 assertEquals(recordClass, deserializedClass); 100 assertEquals(deserializedClass, recordClass); 101 } 102 103 /** Tests that the SUID is always 0 unless explicitly declared. */ 104 @Test(dataProvider = "recordClasses") 105 public void testSerialVersionUID(Class<?> recordClass, long expectedUID) { 106 out.println("\n---"); 107 ObjectStreamClass osc = ObjectStreamClass.lookup(recordClass); 108 out.println("ObjectStreamClass::lookup : " + osc); 109 assertEquals(osc.getSerialVersionUID(), expectedUID); 110 111 osc = ObjectStreamClass.lookupAny(recordClass); 112 out.println("ObjectStreamClass::lookupAny: " + osc); 113 assertEquals(osc.getSerialVersionUID(), expectedUID); 114 } 115 116 // --- not Serializable 117 118 record NotSerializable1() { } 119 120 record NotSerializable2(int x) { } 121 122 record NotSerializable3<T>(T t) { } 123 124 @DataProvider(name = "notSerRecordClasses") 125 public Object[][] notSerRecordClasses() { 126 return new Object[][] { 127 new Object[] { NotSerializable1.class }, 128 new Object[] { NotSerializable2.class }, 129 new Object[] { NotSerializable3.class }, 130 }; 131 } 132 133 /** Tests that the generated SUID is always 0 for all non-Serializable record classes. */ 134 @Test(dataProvider = "notSerRecordClasses") 135 public void testSerialVersionUIDNonSer(Class<?> recordClass) { 136 out.println("\n---"); 137 ObjectStreamClass osc = ObjectStreamClass.lookup(recordClass); 138 out.println("ObjectStreamClass::lookup : " + osc); 139 assertEquals(osc, null); 140 141 osc = ObjectStreamClass.lookupAny(recordClass); 142 out.println("ObjectStreamClass::lookupAny: " + osc); 143 assertEquals(osc.getSerialVersionUID(), 0L); 144 } 145 146 // --- infra 147 148 static <T> byte[] serialize(T obj) throws IOException { 149 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 150 ObjectOutputStream oos = new ObjectOutputStream(baos); 151 oos.writeObject(obj); 152 oos.close(); 153 return baos.toByteArray(); 154 } 155 156 @SuppressWarnings("unchecked") 157 static <T> T deserialize(byte[] streamBytes) 158 throws IOException, ClassNotFoundException 159 { 160 ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); 161 ObjectInputStream ois = new ObjectInputStream(bais); 162 return (T) ois.readObject(); 163 } 164 165 static <T> T serializeDeserialize(T obj) 166 throws IOException, ClassNotFoundException 167 { 168 return deserialize(serialize(obj)); 169 } 170 }