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