1 /* 2 * Copyright (c) 2019, 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 * @summary Tests constructor invocation exceptions are handled appropriately 27 * @compile --enable-preview -source ${jdk.version} ThrowingConstructorTest.java 28 * @run testng/othervm --enable-preview ThrowingConstructorTest 29 * @run testng/othervm/java.security.policy=empty_security.policy --enable-preview ThrowingConstructorTest 30 */ 31 32 import java.io.ByteArrayInputStream; 33 import java.io.ByteArrayOutputStream; 34 import java.io.IOException; 35 import java.io.InvalidObjectException; 36 import java.io.ObjectInputStream; 37 import java.io.ObjectOutputStream; 38 import java.io.Serializable; 39 import org.testng.annotations.DataProvider; 40 import org.testng.annotations.Test; 41 import static java.lang.System.out; 42 import static org.testng.Assert.assertEquals; 43 import static org.testng.Assert.assertTrue; 44 import static org.testng.Assert.expectThrows; 45 46 /** 47 * If the constructor invocation throws an exception, an 48 * `InvalidObjectException` is thrown with that exception as its cause. 49 */ 50 public class ThrowingConstructorTest { 51 52 /** "big switch" that can be used to allow/disallow record construction 53 * set to true after the data provider has constructed all record objects */ 54 private static volatile boolean firstDataSetCreated; 55 56 record R1 () implements Serializable { R1()57 public R1() { 58 if (firstDataSetCreated) 59 throw new NullPointerException("thrown from R1"); 60 } 61 } 62 63 record R2 (int x) implements Serializable { R2(int x)64 public R2(int x) { 65 if (firstDataSetCreated) 66 throw new IllegalArgumentException("thrown from R2"); 67 this.x = x; 68 } 69 } 70 71 record R3 (int x, int y) implements Serializable { R3(int x, int y)72 public R3(int x, int y) { 73 if (firstDataSetCreated) 74 throw new NumberFormatException("thrown from R3"); 75 this.x = x; 76 this.y = y; 77 } 78 } 79 80 static class C implements Serializable { 81 final Object obj ; C(Object obj)82 C(Object obj) { this.obj= obj; } toString()83 @Override public String toString() { return "C[" + obj + "]"; } 84 } 85 86 static final Class<InvalidObjectException> IOE = InvalidObjectException.class; 87 88 @DataProvider(name = "exceptionInstances") exceptionInstances()89 public Object[][] exceptionInstances() { 90 Object[][] objs = new Object[][] { 91 new Object[] { new R1(), NullPointerException.class, "thrown from R1" }, 92 new Object[] { new R2(1), IllegalArgumentException.class, "thrown from R2" }, 93 new Object[] { new R3(2, 3), NumberFormatException .class, "thrown from R3" }, 94 new Object[] { new C(new R1()), NullPointerException.class, "thrown from R1" }, 95 new Object[] { new C(new R2(4)), IllegalArgumentException.class, "thrown from R2" }, 96 new Object[] { new C(new R3(5, 6)), NumberFormatException .class, "thrown from R3" }, 97 }; 98 firstDataSetCreated = true; 99 return objs; 100 } 101 102 @Test(dataProvider = "exceptionInstances") testExceptions(Object objectToSerialize, Class<? extends Throwable> expectedExType, String expectedExMessage)103 public void testExceptions(Object objectToSerialize, 104 Class<? extends Throwable> expectedExType, 105 String expectedExMessage) 106 throws Exception 107 { 108 out.println("\n---"); 109 out.println("serializing: " + objectToSerialize); 110 byte[] bytes = serialize(objectToSerialize); 111 InvalidObjectException ioe = expectThrows(IOE, () -> deserialize(bytes)); 112 out.println("caught expected IOE: " + ioe); 113 Throwable t = ioe.getCause(); 114 assertTrue(t.getClass().equals(expectedExType), 115 "Expected:" + expectedExType + ", got:" + t); 116 out.println("expected cause " + expectedExType +" : " + t); 117 assertEquals(t.getMessage(), expectedExMessage); 118 } 119 120 // -- errors ( pass through unwrapped ) 121 122 private static volatile boolean secondDataSetCreated; 123 124 record R4 () implements Serializable { R4()125 public R4() { 126 if (secondDataSetCreated) 127 throw new OutOfMemoryError("thrown from R4"); } 128 } 129 130 record R5 (int x) implements Serializable { R5(int x)131 public R5(int x) { 132 if (secondDataSetCreated) 133 throw new StackOverflowError("thrown from R5"); 134 this.x = x; 135 } 136 } 137 138 record R6 (int x, int y) implements Serializable { R6(int x, int y)139 public R6(int x, int y) { 140 if (secondDataSetCreated) 141 throw new AssertionError("thrown from R6"); 142 this.x = x; 143 this.y = y; 144 } 145 } 146 147 @DataProvider(name = "errorInstances") errorInstances()148 public Object[][] errorInstances() { 149 Object[][] objs = new Object[][] { 150 new Object[] { new R4(), OutOfMemoryError.class, "thrown from R4" }, 151 new Object[] { new R5(11), StackOverflowError.class, "thrown from R5" }, 152 new Object[] { new R6(12, 13), AssertionError .class, "thrown from R6" }, 153 new Object[] { new C(new R4()), OutOfMemoryError.class, "thrown from R4" }, 154 new Object[] { new C(new R5(14)), StackOverflowError.class, "thrown from R5" }, 155 new Object[] { new C(new R6(15, 16)), AssertionError .class, "thrown from R6" }, 156 }; 157 secondDataSetCreated = true; 158 return objs; 159 } 160 161 @Test(dataProvider = "errorInstances") testErrors(Object objectToSerialize, Class<? extends Throwable> expectedExType, String expectedExMessage)162 public void testErrors(Object objectToSerialize, 163 Class<? extends Throwable> expectedExType, 164 String expectedExMessage) 165 throws Exception 166 { 167 out.println("\n---"); 168 out.println("serializing: " + objectToSerialize); 169 byte[] bytes = serialize(objectToSerialize); 170 Throwable t = expectThrows(expectedExType, () -> deserialize(bytes)); 171 assertTrue(t.getClass().equals(expectedExType), 172 "Expected:" + expectedExType + ", got:" + t); 173 out.println("caught expected " + expectedExType +" : " + t); 174 assertEquals(t.getMessage(), expectedExMessage); 175 } 176 177 // --- infra 178 serialize(T obj)179 static <T> byte[] serialize(T obj) throws IOException { 180 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 181 ObjectOutputStream oos = new ObjectOutputStream(baos); 182 oos.writeObject(obj); 183 oos.close(); 184 return baos.toByteArray(); 185 } 186 187 @SuppressWarnings("unchecked") deserialize(byte[] streamBytes)188 static <T> T deserialize(byte[] streamBytes) 189 throws IOException, ClassNotFoundException 190 { 191 ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); 192 ObjectInputStream ois = new ObjectInputStream(bais); 193 return (T) ois.readObject(); 194 } 195 serializeDeserialize(T obj)196 static <T> T serializeDeserialize(T obj) 197 throws IOException, ClassNotFoundException 198 { 199 return deserialize(serialize(obj)); 200 } 201 } 202