1 /* 2 * Copyright (c) 2019, 2025, 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 8326879 27 * @summary Basic test that serializes and deserializes a number of records 28 * @run junit BasicRecordSer 29 * @run testng/othervm --enable-preview BasicRecordSer 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.NotSerializableException; 37 import java.io.ObjectInput; 38 import java.io.ObjectInputStream; 39 import java.io.ObjectOutput; 40 import java.io.ObjectOutputStream; 41 import java.io.Serializable; 42 import java.math.BigInteger; 43 import static java.lang.String.format; 44 import static java.lang.System.out; 45 import static java.net.InetAddress.getLoopbackAddress; 46 47 import org.junit.jupiter.api.Assertions; 48 import static org.junit.jupiter.api.Assertions.assertArrayEquals; 49 import static org.junit.jupiter.api.Assertions.assertEquals; 50 import static org.junit.jupiter.api.Assertions.assertTrue; 51 import static org.junit.jupiter.api.Assertions.fail; 52 import org.junit.jupiter.api.Test; 53 import org.junit.jupiter.api.TestInstance; 54 import org.junit.jupiter.params.ParameterizedTest; 55 import org.junit.jupiter.params.provider.MethodSource; 56 57 /** 58 * Basic test that serializes and deserializes a number of simple records. 59 */ 60 @TestInstance(TestInstance.Lifecycle.PER_CLASS) 61 public class BasicRecordSer { 62 63 // a mix of a few record and non-record classes 64 65 record Empty () implements Serializable { } 66 67 record Foo (int i) implements Serializable { } 68 69 static class Bar implements Serializable { 70 final Foo foo; 71 final long along; 72 Bar(Foo foo, long along) { this.foo = foo; this.along = along; } 73 @Override 74 public boolean equals(Object obj) { 75 if (!(obj instanceof Bar)) 76 return false; 77 Bar other = (Bar)obj; 78 if (this.foo.equals(other.foo) && this.along == other.along) 79 return true; 80 return false; 81 } 82 @Override 83 public String toString() { 84 return format("Bar[foo=%s, along=%d]", foo, along); 85 } 86 } 87 88 record Baz (Bar bar, float afloat, Foo foo) implements Serializable { } 89 90 record Bat (Empty e1, Foo foo1, Bar bar1, float afloat, Foo foo2, Empty e2, Bar bar2) 91 implements Serializable { } 92 93 record Cheese<A, B>(A a, B b) implements Serializable { } 94 95 interface ThrowingExternalizable extends Externalizable { 96 default void writeExternal(ObjectOutput out) { 97 fail("should not reach here"); 98 } 99 default void readExternal(ObjectInput in) { 100 fail("should not reach here"); 101 } 102 } 103 104 record Wibble () implements ThrowingExternalizable { } 105 106 record Wobble (Foo foo) implements ThrowingExternalizable { } 107 108 record Wubble (Wobble wobble, Wibble wibble, String s) implements ThrowingExternalizable { } 109 110 public Object[][] serializable() { 111 Foo foo = new Foo(23); 112 return new Object[][] { 113 new Object[] { new Empty() }, 114 new Object[] { new Foo(22) }, 115 new Object[] { new Foo[] { new Foo(24), new Foo(25) } }, 116 new Object[] { new Foo[] { foo, foo, foo, foo, foo } }, 117 new Object[] { new Bar(new Foo(33), 1_234_567L) }, 118 new Object[] { new Baz(new Bar(new Foo(44), 4_444L), 5.5f, new Foo(55)) }, 119 new Object[] { new Bat(new Empty(), new Foo(57), new Bar(new Foo(44), 4_444L), 120 5.5f, new Foo(55), new Empty(), new Bar(new Foo(23), 1L)) }, 121 new Object[] { new Cheese(getLoopbackAddress(), BigInteger.valueOf(78)) }, 122 new Object[] { new Wibble() }, 123 new Object[] { new Wobble(new Foo(65)) }, 124 new Object[] { new Wubble(new Wobble(new Foo(6)), new Wibble(), "xxzzzyy") }, 125 }; 126 } 127 128 /** Tests serializing and deserializing a number of records. */ 129 @ParameterizedTest 130 @MethodSource("serializable") 131 public void testSerializable(Object objToSerialize) throws Exception { 132 out.println("\n---"); 133 out.println("serializing : " + objToSerialize); 134 var objDeserialized = serializeDeserialize(objToSerialize); 135 out.println("deserialized: " + objDeserialized); 136 if (objToSerialize.getClass().isArray()) { 137 assertArrayEquals((Object[]) objDeserialized, (Object[]) objToSerialize); 138 assertArrayEquals((Object[]) objToSerialize, (Object[]) objDeserialized); 139 } else { 140 assertEquals(objDeserialized, objToSerialize); 141 assertEquals(objToSerialize, objDeserialized); 142 } 143 } 144 145 /** Tests serializing and deserializing of local records. */ 146 @Test 147 public void testLocalRecord() throws Exception { 148 out.println("\n---"); 149 record Point(int x, int y) implements Serializable { } 150 record Rectangle(Point bottomLeft, Point topRight) implements Serializable { } 151 var objToSerialize = new Rectangle(new Point(0, 1), new Point (5, 6)); 152 out.println("serializing : " + objToSerialize); 153 var objDeserialized = serializeDeserialize(objToSerialize); 154 out.println("deserialized: " + objDeserialized); 155 assertEquals(objToSerialize, objDeserialized); 156 assertEquals(objDeserialized, objToSerialize); 157 } 158 159 /** Tests back references of Serializable record objects in the stream. */ 160 @Test 161 public void testSerializableBackRefs() throws Exception { 162 out.println("\n---"); 163 Foo foo = new Foo(32); 164 Foo[] objToSerialize = new Foo[] { foo, foo, foo, foo, foo }; 165 out.println("serializing : " + objToSerialize); 166 Foo[] objDeserialized = (Foo[])serializeDeserialize(objToSerialize); 167 out.println("deserialized: " + objDeserialized); 168 Assertions.assertArrayEquals(objDeserialized, objToSerialize); 169 Assertions.assertArrayEquals(objToSerialize, objDeserialized); 170 171 for (Foo f : objDeserialized) 172 assertTrue(objDeserialized[0] == f); 173 } 174 175 /** Tests back references of Externalizable record objects in the stream. */ 176 @Test 177 public void testExternalizableBackRefs() throws Exception { 178 out.println("\n---"); 179 Foo foo = new Foo(33); 180 Wobble wobble = new Wobble(foo); 181 Wobble[] objToSerialize = new Wobble[] { wobble, wobble, wobble, wobble }; 182 out.println("serializing : " + objToSerialize); 183 Wobble[] objDeserialized = (Wobble[])serializeDeserialize(objToSerialize); 184 out.println("deserialized: " + objDeserialized); 185 Assertions.assertArrayEquals(objDeserialized, objToSerialize); 186 Assertions.assertArrayEquals(objToSerialize, objDeserialized); 187 188 for (Wobble w : objDeserialized) { 189 assertTrue(objDeserialized[0] == w); 190 assertTrue(objDeserialized[0].foo() == w.foo()); 191 } 192 } 193 194 // --- Not Serializable 195 196 record NotSerEmpty () { } 197 record NotSer (int x) { } 198 record NotSerA (int x, int y) { 199 private static final long serialVersionUID = 5L; 200 } 201 static class A implements Serializable { 202 final int y = -1; 203 final NotSer notSer = new NotSer(7); 204 } 205 206 public Object[][] notSerializable() { 207 return new Object[][] { 208 new Object[] { new NotSerEmpty() }, 209 new Object[] { new NotSerEmpty[] { new NotSerEmpty() } }, 210 new Object[] { new Object[] { new NotSerEmpty() } }, 211 new Object[] { new NotSer(6) }, 212 new Object[] { new NotSer[] { new NotSer(7) } }, 213 new Object[] { new NotSerA(6, 8) }, 214 new Object[] { new A() }, 215 new Object[] { new A[] { new A() } }, 216 }; 217 } 218 219 static final Class<NotSerializableException> NSE = NotSerializableException.class; 220 221 /** Tests that non-Serializable record objects throw NotSerializableException. */ 222 @ParameterizedTest 223 @MethodSource("notSerializable") 224 public void testNotSerializable(Object objToSerialize) throws Exception { 225 out.println("\n---"); 226 out.println("serializing : " + objToSerialize); 227 NotSerializableException expected = Assertions.assertThrows(NSE, () -> serialize(objToSerialize)); 228 out.println("caught expected NSE:" + expected); 229 230 } 231 232 // --- constructor invocation counting 233 234 static volatile int e_ctrInvocationCount; 235 236 record E () implements Serializable { 237 public E() { e_ctrInvocationCount++; } 238 } 239 240 /** Tests that the record's constructor is invoke exactly once per deserialization. */ 241 @Test 242 public void testCtrCalledOnlyOnce() throws Exception { 243 out.println("\n---"); 244 var objToSerialize = new E(); 245 e_ctrInvocationCount = 0; // reset 246 out.println("serializing : " + objToSerialize); 247 var objDeserialized = serializeDeserialize(objToSerialize); 248 out.println("deserialized: " + objDeserialized); 249 assertEquals(objDeserialized, objToSerialize); 250 assertEquals(objToSerialize, objDeserialized); 251 assertEquals(1, e_ctrInvocationCount); 252 } 253 254 // --- 255 256 static volatile int g_ctrInvocationCount; 257 258 record F (int x){ 259 public F(int x) { this.x = x; g_ctrInvocationCount++; } 260 } 261 static class G implements Serializable { 262 F f = new F(89); 263 } 264 265 /** Tests that the record's constructor is NOT invoke during failed deserialization. */ 266 @Test 267 public void testCtrNotCalled() { 268 out.println("\n---"); 269 var objToSerialize = new G(); 270 g_ctrInvocationCount = 0; // reset 271 out.println("serializing : " + objToSerialize); 272 NotSerializableException expected = Assertions.assertThrows(NSE, () -> serialize(objToSerialize)); 273 out.println("caught expected NSE:" + expected); 274 assertEquals(0, g_ctrInvocationCount); 275 } 276 277 // --- infra 278 279 static <T> byte[] serialize(T obj) throws IOException { 280 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 281 ObjectOutputStream oos = new ObjectOutputStream(baos); 282 oos.writeObject(obj); 283 oos.close(); 284 return baos.toByteArray(); 285 } 286 287 @SuppressWarnings("unchecked") 288 static <T> T deserialize(byte[] streamBytes) 289 throws IOException, ClassNotFoundException 290 { 291 ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); 292 ObjectInputStream ois = new ObjectInputStream(bais); 293 return (T) ois.readObject(); 294 } 295 296 static <T> T serializeDeserialize(T obj) 297 throws IOException, ClassNotFoundException 298 { 299 return deserialize(serialize(obj)); 300 } 301 } --- EOF ---