1 /* 2 * Copyright (c) 2019, 2024, 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 */ 30 31 import java.io.ByteArrayInputStream; 32 import java.io.ByteArrayOutputStream; 33 import java.io.Externalizable; 34 import java.io.IOException; 35 import java.io.ObjectInput; 36 import java.io.ObjectInputStream; 37 import java.io.ObjectOutput; 38 import java.io.ObjectOutputStream; 39 import java.io.ObjectStreamClass; 40 import java.io.Serializable; 41 import org.testng.annotations.DataProvider; 42 import org.testng.annotations.Test; 43 import static java.lang.System.out; 44 import static org.testng.Assert.assertEquals; 45 import static org.testng.Assert.fail; 46 47 /** 48 * Serializes and deserializes record classes. Ensures that the SUID is 0. 49 */ 50 public class RecordClassTest { 51 52 record Foo () implements Serializable { } 53 54 record Bar (int x) implements Serializable { 55 private static final long serialVersionUID = 987654321L; 56 } 57 58 record Baz (Foo foo, Bar bar, int i) implements Serializable { } 59 60 interface ThrowingExternalizable extends Externalizable { 61 default void writeExternal(ObjectOutput out) { 62 fail("should not reach here"); 63 } 64 default void readExternal(ObjectInput in) { 65 fail("should not reach here"); 66 } 67 } 68 69 record Wibble () implements ThrowingExternalizable { 70 private static final long serialVersionUID = 12345678L; 71 } 72 73 record Wobble (long l) implements ThrowingExternalizable { } 74 75 record Wubble (Wobble wobble, Wibble wibble, String s) implements ThrowingExternalizable { } 76 77 @DataProvider(name = "recordClasses") 78 public Object[][] recordClasses() { 79 return new Object[][] { 80 new Object[] { Foo.class , 0L }, 81 new Object[] { Bar.class , 987654321L }, 82 new Object[] { Baz.class , 0L }, 83 new Object[] { Wibble.class , 12345678L }, 84 new Object[] { Wobble.class , 0L }, 85 new Object[] { Wubble.class , 0L }, 86 }; 87 } 88 89 /** Tests that the serialized and deserialized instances are equal. */ 90 @Test(dataProvider = "recordClasses") 91 public void testClassSerialization(Class<?> recordClass, long unused) 92 throws Exception 93 { 94 out.println("\n---"); 95 out.println("serializing : " + recordClass); 96 var deserializedClass = serializeDeserialize(recordClass); 97 out.println("deserialized: " + deserializedClass); 98 assertEquals(recordClass, deserializedClass); 99 assertEquals(deserializedClass, recordClass); 100 } 101 102 /** Tests that the SUID is always 0 unless explicitly declared. */ 103 @Test(dataProvider = "recordClasses") 104 public void testSerialVersionUID(Class<?> recordClass, long expectedUID) { 105 out.println("\n---"); 106 ObjectStreamClass osc = ObjectStreamClass.lookup(recordClass); 107 out.println("ObjectStreamClass::lookup : " + osc); 108 assertEquals(osc.getSerialVersionUID(), expectedUID); 109 110 osc = ObjectStreamClass.lookupAny(recordClass); 111 out.println("ObjectStreamClass::lookupAny: " + osc); 112 assertEquals(osc.getSerialVersionUID(), expectedUID); 113 } 114 115 // --- not Serializable 116 117 record NotSerializable1() { } 118 119 record NotSerializable2(int x) { } 120 121 record NotSerializable3<T>(T t) { } 122 123 @DataProvider(name = "notSerRecordClasses") 124 public Object[][] notSerRecordClasses() { 125 return new Object[][] { 126 new Object[] { NotSerializable1.class }, 127 new Object[] { NotSerializable2.class }, 128 new Object[] { NotSerializable3.class }, 129 }; 130 } 131 132 /** Tests that the generated SUID is always 0 for all non-Serializable record classes. */ 133 @Test(dataProvider = "notSerRecordClasses") 134 public void testSerialVersionUIDNonSer(Class<?> recordClass) { 135 out.println("\n---"); 136 ObjectStreamClass osc = ObjectStreamClass.lookup(recordClass); 137 out.println("ObjectStreamClass::lookup : " + osc); 138 assertEquals(osc, null); 139 140 osc = ObjectStreamClass.lookupAny(recordClass); 141 out.println("ObjectStreamClass::lookupAny: " + osc); 142 assertEquals(osc.getSerialVersionUID(), 0L); 143 } 144 145 // --- infra 146 147 static <T> byte[] serialize(T obj) throws IOException { 148 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 149 ObjectOutputStream oos = new ObjectOutputStream(baos); 150 oos.writeObject(obj); 151 oos.close(); 152 return baos.toByteArray(); 153 } 154 155 @SuppressWarnings("unchecked") 156 static <T> T deserialize(byte[] streamBytes) 157 throws IOException, ClassNotFoundException 158 { 159 ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); 160 ObjectInputStream ois = new ObjectInputStream(bais); 161 return (T) ois.readObject(); 162 } 163 164 static <T> T serializeDeserialize(T obj) 165 throws IOException, ClassNotFoundException 166 { 167 return deserialize(serialize(obj)); 168 } 169 }