From 05b469b2bca70dcfc1c4478ce2b2046ee2069508 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Sat, 27 Jun 2026 21:06:15 +0530 Subject: [PATCH 1/6] 8387374: [lworld] Address test issues in test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java --- .../valueObjects/ValueSerializationTest.java | 493 ++++++++++++------ 1 file changed, 334 insertions(+), 159 deletions(-) diff --git a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java index 233953d514f..83edd5192ca 100644 --- a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java +++ b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java @@ -25,15 +25,15 @@ * @test * @summary Test serialization of value classes * @enablePreview - * @modules java.base/jdk.internal java.base/jdk.internal.value + * @modules java.base/jdk.internal.value * @library /test/lib * @compile ValueSerializationTest.java - * @run driver jdk.test.lib.helpers.StrictProcessor ValueSerializationTest$NonSerializableStrictPoint - * @run junit/othervm ValueSerializationTest + * @comment run the StrictProcessor over the IdentityStrictPoint to generate a class with + * STRICT_INIT access flags for its annotated fields + * @run driver jdk.test.lib.helpers.StrictProcessor ValueSerializationTest$IdentityStrictPoint + * @run junit ${test.main.class} */ -import static java.io.ObjectStreamConstants.*; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -53,61 +53,262 @@ import java.util.stream.Stream; import jdk.internal.value.Deserializer; - import jdk.test.lib.helpers.StrictInit; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; - -import static org.junit.jupiter.api.Assertions.*; +import static java.io.ObjectStreamConstants.SC_EXTERNALIZABLE; +import static java.io.ObjectStreamConstants.SC_SERIALIZABLE; +import static java.io.ObjectStreamConstants.STREAM_MAGIC; +import static java.io.ObjectStreamConstants.STREAM_VERSION; +import static java.io.ObjectStreamConstants.TC_CLASSDESC; +import static java.io.ObjectStreamConstants.TC_ENDBLOCKDATA; +import static java.io.ObjectStreamConstants.TC_NULL; +import static java.io.ObjectStreamConstants.TC_OBJECT; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ValueSerializationTest { - static final Class NSE = NotSerializableException.class; + private static final Class NSE = NotSerializableException.class; private static final Class ICE = InvalidClassException.class; - public static Stream doesNotImplementSerializable() { + static Stream doesNotImplementSerializable() { return Stream.of( - Arguments.of( new NonSerializablePoint(10, 100), NSE), - Arguments.of( new NonSerializablePointNoCons(10, 100), ICE), - Arguments.of( new NonSerializableStrictPoint(), ICE), - // an array of Points - Arguments.of( new NonSerializablePoint[] {new NonSerializablePoint(1, 5)}, NSE), - Arguments.of( Arguments.of(new NonSerializablePoint(3, 7)), NSE), - Arguments.of( new ExternalizablePoint(12, 102), ICE), - Arguments.of( new ExternalizablePoint[] { - new ExternalizablePoint(3, 7), - new ExternalizablePoint(2, 8) }, ICE), - Arguments.of( new Object[] { - new ExternalizablePoint(13, 17), - new ExternalizablePoint(14, 18) }, ICE)); + Arguments.of( + new NonSerializableValue(10, 100), + NSE + ), + + Arguments.of( + new ValueWithNoDeserializer(10, 100), + ICE + ), + + Arguments.of( + new IdentityStrictPoint(), + ICE + ), + + // an array of non-serializable value objects + Arguments.of( + new NonSerializableValue[]{ + new NonSerializableValue(1, 5) + }, + NSE + ), + + Arguments.of( + new Object[]{ + new NonSerializableValue(3, 7) + }, + NSE + ), + + Arguments.of( + new ExternalizableValue(12, 102), + ICE + ), + + Arguments.of( + new ExternalizableValue[]{ + new ExternalizableValue(3, 7), + new ExternalizableValue(2, 8) + }, + ICE + ), + + Arguments.of( + new Object[]{ + new ExternalizableValue(13, 17), + new ExternalizableValue(14, 18) + }, + ICE + ) + ); } - // value class that DOES NOT implement Serializable should throw ICE + /* + * Verifies that a value object that doesn't implement java.io.Serializable + * throws the expected exception from ObjectOutputStream.writeObject() + */ @ParameterizedTest @MethodSource("doesNotImplementSerializable") - public void doesNotImplementSerializable(Object obj, Class expectedException) { + void doesNotImplementSerializable(Object obj, Class expectedException) { assertThrows(expectedException, () -> serialize(obj)); } - /* Non-Serializable point. */ - public static value class NonSerializablePoint { + static Stream implementSerializable() { + return Stream.of( + Arguments.of( + new ValueWithDeserializer(11, 101) + ), + + Arguments.of( + (Object) new ValueWithDeserializer[]{ + new ValueWithDeserializer(1, 5), + new ValueWithDeserializer(2, 6) + } + ), + + Arguments.of( + (Object) new Object[]{ + new ValueWithDeserializer(3, 7), + new ValueWithDeserializer(4, 8) + } + ), + + Arguments.of( + new ValueWriteReplaceWithIdentity(45) + ), + + Arguments.of( + (Object) new ValueWriteReplaceWithIdentity[]{ + new ValueWriteReplaceWithIdentity(46) + } + ), + + Arguments.of( + new ExtValueWithIdentityReplacement("hello") + ), + + Arguments.of( + (Object) new ExtValueWithIdentityReplacement[]{ + new ExtValueWithIdentityReplacement("there") + } + ) + ); + } + + /* + * Verifies that a value object that implements java.io.Serializable and is associated with + * the JDK internal jdk.internal.value.Deserializer can be serialized and deserialized through + * the use of ObjectOutputStream.writeObject() and ObjectInputStream.readObject() successfully. + * The deserialized object is then compared with the given obj to verify that they are equal. + */ + @ParameterizedTest + @MethodSource("implementSerializable") + void implementSerializable(Object obj) throws IOException, ClassNotFoundException { + byte[] bytes = serialize(obj); + Object actual = deserialize(bytes); + if (obj.getClass().isArray()) { + assertArrayEquals((Object[]) actual, (Object[]) obj); + } else { + assertEquals(actual, obj); + } + } + + static Stream classes() { + return Stream.of( + Arguments.of( + ExtValueWithIdentityReplacement.class, + SC_EXTERNALIZABLE, + ICE + ), + + Arguments.of( + ExtValueWithIdentityReplacement.class, + SC_SERIALIZABLE, + ICE + ), + + Arguments.of( + ValueWithDeserializer.class, + SC_EXTERNALIZABLE, + ICE + ), + + Arguments.of( + ValueWithDeserializer.class, + SC_SERIALIZABLE, + null + ) + ); + } + + /* + * A byte stream is generated containing a reference to the given value class + * with the given flags and a serial version UID determined in the test method. + * The byte stream is then read using ObjectInputStream.readObject() and the test verifies + * that if an exception is expected to be thrown then it is thrown, or if the deserialization + * is expected to complete normally, then it verifies that no exception is thrown. + */ + @ParameterizedTest + @MethodSource("classes") + void deserialize(Class valueClass, byte flags, Class expected) throws Exception { + // as a precaution verify that we are indeed testing a value class + assertTrue(valueClass.isValue(), "not a value class: " + valueClass); + ObjectStreamClass clsDesc = ObjectStreamClass.lookup(valueClass); + long uid = clsDesc == null ? 0L : clsDesc.getSerialVersionUID(); + byte[] serialBytes = byteStreamFor(valueClass.getName(), uid, flags); + if (expected == null) { + assertDoesNotThrow(() -> deserialize(serialBytes)); + } else { + assertThrows(expected, () -> deserialize(serialBytes)); + } + } + + // Generate a byte stream containing a reference to the named class with the SVID and flags. + private static byte[] byteStreamFor(String className, long uid, byte flags) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeShort(STREAM_MAGIC); + dos.writeShort(STREAM_VERSION); + dos.writeByte(TC_OBJECT); + dos.writeByte(TC_CLASSDESC); + dos.writeUTF(className); + dos.writeLong(uid); + dos.writeByte(flags); + dos.writeShort(0); // number of fields + dos.writeByte(TC_ENDBLOCKDATA); // no annotations + dos.writeByte(TC_NULL); // no superclasses + dos.close(); + return baos.toByteArray(); + } + + private static byte[] serialize(T obj) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + oos.close(); + return baos.toByteArray(); + } + + @SuppressWarnings("unchecked") + private static T deserialize(byte[] streamBytes) throws IOException, + ClassNotFoundException { + ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); + ObjectInputStream ois = new ObjectInputStream(bais); + return (T) ois.readObject(); + } + + /** + * A concrete value class that doesn't implement Serializable (or Externalizable) interface + */ + public static value class NonSerializableValue { public int x; public int y; - public NonSerializablePoint(int x, int y) { + public NonSerializableValue(int x, int y) { this.x = x; this.y = y; } - @Override public String toString() { - return "[NonSerializablePoint x=" + x + " y=" + y + "]"; + + @Override + public String toString() { + return "[NonSerializableValue x=" + x + " y=" + y + "]"; } } - public static class NonSerializableStrictPoint implements Serializable { + /** + * An identity class with strict initialized instance fields + */ + public static class IdentityStrictPoint implements Serializable { static { - for (var f : NonSerializableStrictPoint.class.getDeclaredFields()) { + for (var f : IdentityStrictPoint.class.getDeclaredFields()) { assertTrue(f.isStrictInit(), f.getName()); } } @@ -116,181 +317,155 @@ public static class NonSerializableStrictPoint implements Serializable { public int x; @StrictInit public int y; - public NonSerializableStrictPoint() { + + public IdentityStrictPoint() { x = 3; y = 5; super(); } } - /* Non-Serializable point, because it does not have an @Deserializer constructor. */ - public static value class NonSerializablePointNoCons implements Serializable { + /** + * A concrete value class that implements java.io.Serializable and doesn't have any + * jdk.internal.value.Deserializer on its constructor. + */ + public static value class ValueWithNoDeserializer implements Serializable { public int x; public int y; // Note: Must NOT have @Deserializer annotation - public NonSerializablePointNoCons(int x, int y) { + public ValueWithNoDeserializer(int x, int y) { this.x = x; this.y = y; } - @Override public String toString() { - return "[NonSerializablePointNoCons x=" + x + " y=" + y + "]"; + + @Override + public String toString() { + return "[ValueWithNoDeserializer x=" + x + " y=" + y + "]"; } } - /* An Externalizable Point is not Serializable, readExternal cannot modify fields */ - static value class ExternalizablePoint implements Externalizable { + /** + * A concrete value class which implements java.io.Externalizable and doesn't + * implement writeReplace(). + */ + static value class ExternalizableValue implements Externalizable { public int x; public int y; - public ExternalizablePoint() {this.x = 0; this.y = 0;} - ExternalizablePoint(int x, int y) { this.x = x; this.y = y; } - @Override public void readExternal(ObjectInput in) { } - @Override public void writeExternal(ObjectOutput out) { } - @Override public String toString() { - return "[ExternalizablePoint x=" + x + " y=" + y + "]"; } - } - public static Stream implementSerializable() { - return Stream.of( - Arguments.of(new SerializablePoint(11, 101)), - Arguments.of((Object)(new SerializablePoint[]{ - new SerializablePoint(1, 5), - new SerializablePoint(2, 6)}), - Arguments.of(Arguments.of( - new SerializablePoint(3, 7), - new SerializablePoint(4, 8))), - Arguments.of(new SerializableFoo(45)), - Arguments.of((Object)(new SerializableFoo[]{new SerializableFoo(46)})), - Arguments.of(new ExternalizableFoo("hello")), - Arguments.of((Object)new ExternalizableFoo[]{new ExternalizableFoo("there")}))); - } + public ExternalizableValue() { + this.x = 0; + this.y = 0; + } - // value class that DOES implement Serializable is supported - @ParameterizedTest - @MethodSource("implementSerializable") - public void implementSerializable(Object obj) throws IOException, ClassNotFoundException { - byte[] bytes = serialize(obj); - Object actual = deserialize(bytes); - if (obj.getClass().isArray()) - Assertions.assertArrayEquals((Object[])actual, (Object[])obj); - else - assertEquals(actual, obj); + ExternalizableValue(int x, int y) { + this.x = x; + this.y = y; + } + + @Override + public void readExternal(ObjectInput in) { + } + + @Override + public void writeExternal(ObjectOutput out) { + } + + @Override + public String toString() { + return "[ExternalizableValue x=" + x + " y=" + y + "]"; + } } - /* A Serializable value class Point */ - static value class SerializablePoint implements Serializable { + + /** + * A concrete value class which implements java.io.Serializable and has a + * jdk.internal.value.Deserializer associated with its constructor. + */ + static value class ValueWithDeserializer implements Serializable { public int x; public int y; + @Deserializer({"x", "y"}) - private SerializablePoint(int x, int y) { this.x = x; this.y = y; } + private ValueWithDeserializer(int x, int y) { + this.x = x; + this.y = y; + } - @Override public String toString() { - return "[SerializablePoint x=" + x + " y=" + y + "]"; + @Override + public String toString() { + return "[ValueWithDeserializer x=" + x + " y=" + y + "]"; } } - /* A Serializable Foo, with a serial proxy */ - static value class SerializableFoo implements Serializable { + /** + * A concrete value class which implements java.io.Serializable + * and implements the writeReplace() method to return an identity + * object. + */ + static value class ValueWriteReplaceWithIdentity implements Serializable { public int x; - @Deserializer("x") - SerializableFoo(int x) { this.x = x; } - @Serial Object writeReplace() throws ObjectStreamException { - return new SerialFooProxy(x); + ValueWriteReplaceWithIdentity(int x) { + this.x = x; + } + + @Serial + Object writeReplace() throws ObjectStreamException { + return new IdentityRecord(x); } - @Serial private void readObject(ObjectInputStream s) throws InvalidObjectException { - throw new InvalidObjectException("Proxy required"); + + @Serial + private void readObject(ObjectInputStream s) throws InvalidObjectException { + throw new InvalidObjectException("not expected to be invoked on " + this); } - private record SerialFooProxy(int x) implements Serializable { - @Serial Object readResolve() throws ObjectStreamException { - return new SerializableFoo(x); + + private record IdentityRecord(int x) implements Serializable { + @Serial + Object readResolve() throws ObjectStreamException { + return new ValueWriteReplaceWithIdentity(x); } } } - /* An Externalizable Foo, with a serial proxy */ - static value class ExternalizableFoo implements Externalizable { + /** + * A concrete value class which implements java.io.Externalizable and implements + * the writeReplace() method to return an identity object. + */ + static value class ExtValueWithIdentityReplacement implements Externalizable { public String s; - ExternalizableFoo(String s) { this.s = s; } + + ExtValueWithIdentityReplacement(String s) { + this.s = s; + } + + @Override public boolean equals(Object other) { - if (other instanceof ExternalizableFoo foo) { + if (other instanceof ExtValueWithIdentityReplacement foo) { return s.equals(foo.s); } else { return false; } } - @Serial Object writeReplace() throws ObjectStreamException { - return new SerialFooProxy(s); + + @Serial + Object writeReplace() throws ObjectStreamException { + return new IdentityRecord(s); } - private record SerialFooProxy(String s) implements Serializable { - @Serial Object readResolve() throws ObjectStreamException { - return new ExternalizableFoo(s); + + private record IdentityRecord(String s) implements Serializable { + @Serial + Object readResolve() throws ObjectStreamException { + return new ExtValueWithIdentityReplacement(s); } } - @Override public void readExternal(ObjectInput in) { } - @Override public void writeExternal(ObjectOutput out) { } - } - - // Generate a byte stream containing a reference to the named class with the SVID and flags. - private static byte[] byteStreamFor(String className, long uid, byte flags) - throws Exception - { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - dos.writeShort(STREAM_MAGIC); - dos.writeShort(STREAM_VERSION); - dos.writeByte(TC_OBJECT); - dos.writeByte(TC_CLASSDESC); - dos.writeUTF(className); - dos.writeLong(uid); - dos.writeByte(flags); - dos.writeShort(0); // number of fields - dos.writeByte(TC_ENDBLOCKDATA); // no annotations - dos.writeByte(TC_NULL); // no superclasses - dos.close(); - return baos.toByteArray(); - } - public static Stream classes() { - return Stream.of( - Arguments.of( ExternalizableFoo.class, SC_EXTERNALIZABLE, ICE ), - Arguments.of( ExternalizableFoo.class, SC_SERIALIZABLE, ICE ), - Arguments.of( SerializablePoint.class, SC_EXTERNALIZABLE, ICE ), - Arguments.of( SerializablePoint.class, SC_SERIALIZABLE, null ) - ); - } - - // value class read directly from a byte stream - // a byte stream is generated containing a reference to the class with the flags and SVID. - // Reading the class from the stream verifies the exceptions thrown if there is a mismatch - // between the stream and the local class. - @ParameterizedTest - @MethodSource("classes") - public void deserialize(Class cls, byte flags, Class expected) throws Exception { - var clsDesc = ObjectStreamClass.lookup(cls); - long uid = clsDesc == null ? 0L : clsDesc.getSerialVersionUID(); - byte[] serialBytes = byteStreamFor(cls.getName(), uid, flags); - if (expected == null) { - Assertions.assertDoesNotThrow(() -> deserialize(serialBytes)); - } else { - Assertions.assertThrows(expected, () -> deserialize(serialBytes)); + @Override + public void readExternal(ObjectInput in) { } - } - - static byte[] serialize(T obj) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(obj); - oos.close(); - return baos.toByteArray(); - } - @SuppressWarnings("unchecked") - static T deserialize(byte[] streamBytes) - throws IOException, ClassNotFoundException - { - ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); - ObjectInputStream ois = new ObjectInputStream(bais); - return (T) ois.readObject(); + @Override + public void writeExternal(ObjectOutput out) { + } } } From d4ba0e34e36467d63a5808f4f6117ced26c8052c Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Sat, 27 Jun 2026 21:33:37 +0530 Subject: [PATCH 2/6] rename methods --- .../valueObjects/ValueSerializationTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java index 83edd5192ca..28a5e05b300 100644 --- a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java +++ b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java @@ -76,7 +76,7 @@ public class ValueSerializationTest { private static final Class NSE = NotSerializableException.class; private static final Class ICE = InvalidClassException.class; - static Stream doesNotImplementSerializable() { + static Stream serializationFailingInstances() { return Stream.of( Arguments.of( new NonSerializableValue(10, 100), @@ -136,12 +136,12 @@ static Stream doesNotImplementSerializable() { * throws the expected exception from ObjectOutputStream.writeObject() */ @ParameterizedTest - @MethodSource("doesNotImplementSerializable") - void doesNotImplementSerializable(Object obj, Class expectedException) { + @MethodSource("serializationFailingInstances") + void testSerializationFails(Object obj, Class expectedException) { assertThrows(expectedException, () -> serialize(obj)); } - static Stream implementSerializable() { + static Stream serializingInstances() { return Stream.of( Arguments.of( new ValueWithDeserializer(11, 101) @@ -190,8 +190,8 @@ static Stream implementSerializable() { * The deserialized object is then compared with the given obj to verify that they are equal. */ @ParameterizedTest - @MethodSource("implementSerializable") - void implementSerializable(Object obj) throws IOException, ClassNotFoundException { + @MethodSource("serializingInstances") + void testSerDeserSucceeds(Object obj) throws IOException, ClassNotFoundException { byte[] bytes = serialize(obj); Object actual = deserialize(bytes); if (obj.getClass().isArray()) { @@ -238,7 +238,7 @@ static Stream classes() { */ @ParameterizedTest @MethodSource("classes") - void deserialize(Class valueClass, byte flags, Class expected) throws Exception { + void testSerDeser(Class valueClass, byte flags, Class expected) throws Exception { // as a precaution verify that we are indeed testing a value class assertTrue(valueClass.isValue(), "not a value class: " + valueClass); ObjectStreamClass clsDesc = ObjectStreamClass.lookup(valueClass); From 68a7a2ad72e9e4b2b846c4b41936cc68dbb5f900 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Sat, 27 Jun 2026 21:57:13 +0530 Subject: [PATCH 3/6] move the assertThrows() more closer to the actual readObject()/writeObject() invocation --- .../valueObjects/ValueSerializationTest.java | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java index 28a5e05b300..ecb98b8af8b 100644 --- a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java +++ b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java @@ -66,7 +66,6 @@ import static java.io.ObjectStreamConstants.TC_NULL; import static java.io.ObjectStreamConstants.TC_OBJECT; import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -137,8 +136,9 @@ static Stream serializationFailingInstances() { */ @ParameterizedTest @MethodSource("serializationFailingInstances") - void testSerializationFails(Object obj, Class expectedException) { - assertThrows(expectedException, () -> serialize(obj)); + void testSerializationFails(Object obj, Class expectedException) + throws Exception { + serialize(obj, expectedException); } static Stream serializingInstances() { @@ -192,8 +192,8 @@ static Stream serializingInstances() { @ParameterizedTest @MethodSource("serializingInstances") void testSerDeserSucceeds(Object obj) throws IOException, ClassNotFoundException { - byte[] bytes = serialize(obj); - Object actual = deserialize(bytes); + byte[] bytes = serialize(obj, null); + Object actual = deserialize(bytes, null); if (obj.getClass().isArray()) { assertArrayEquals((Object[]) actual, (Object[]) obj); } else { @@ -238,17 +238,14 @@ static Stream classes() { */ @ParameterizedTest @MethodSource("classes") - void testSerDeser(Class valueClass, byte flags, Class expected) throws Exception { + void testDeser(Class valueClass, byte flags, Class expectedException) + throws Exception { // as a precaution verify that we are indeed testing a value class assertTrue(valueClass.isValue(), "not a value class: " + valueClass); ObjectStreamClass clsDesc = ObjectStreamClass.lookup(valueClass); long uid = clsDesc == null ? 0L : clsDesc.getSerialVersionUID(); byte[] serialBytes = byteStreamFor(valueClass.getName(), uid, flags); - if (expected == null) { - assertDoesNotThrow(() -> deserialize(serialBytes)); - } else { - assertThrows(expected, () -> deserialize(serialBytes)); - } + deserialize(serialBytes, expectedException); } // Generate a byte stream containing a reference to the named class with the SVID and flags. @@ -269,20 +266,33 @@ private static byte[] byteStreamFor(String className, long uid, byte flags) thro return baos.toByteArray(); } - private static byte[] serialize(T obj) throws IOException { + private static byte[] serialize(T obj, Class expectedException) + throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(obj); - oos.close(); - return baos.toByteArray(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { + if (expectedException != null) { + Exception e = assertThrows(expectedException, () -> oos.writeObject(obj)); + System.err.println("got expected exception: " + e); + return null; + } + oos.writeObject(obj); + return baos.toByteArray(); + } } @SuppressWarnings("unchecked") - private static T deserialize(byte[] streamBytes) throws IOException, - ClassNotFoundException { + private static T deserialize(byte[] streamBytes, + Class expectedException) + throws IOException, ClassNotFoundException { ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); - ObjectInputStream ois = new ObjectInputStream(bais); - return (T) ois.readObject(); + try (ObjectInputStream ois = new ObjectInputStream(bais)) { + if (expectedException != null) { + Exception e = assertThrows(expectedException, () -> ois.readObject()); + System.err.println("got expected exception: " + e); + return null; + } + return (T) ois.readObject(); + } } /** From bcdf22037ca40528ad710b409ae07c0732800aff Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Mon, 29 Jun 2026 15:07:10 +0530 Subject: [PATCH 4/6] Viktor's review - add code comment in test --- .../valueObjects/ValueSerializationTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java index ecb98b8af8b..4e2e39d4e6d 100644 --- a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java +++ b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java @@ -148,6 +148,10 @@ static Stream serializingInstances() { ), Arguments.of( + // Argument.of() takes a vararg parameter. + // Here we intend to pass an array as a single parameter to the test method. + // Explicitly cast it to Object type to prevent the array elements + // being treated as multiple parameters. (Object) new ValueWithDeserializer[]{ new ValueWithDeserializer(1, 5), new ValueWithDeserializer(2, 6) @@ -155,6 +159,10 @@ static Stream serializingInstances() { ), Arguments.of( + // Argument.of() takes a vararg parameter. + // Here we intend to pass an array as a single parameter to the test method. + // Explicitly cast it to Object type to prevent the array elements + // being treated as multiple parameters. (Object) new Object[]{ new ValueWithDeserializer(3, 7), new ValueWithDeserializer(4, 8) @@ -166,6 +174,10 @@ static Stream serializingInstances() { ), Arguments.of( + // Argument.of() takes a vararg parameter. + // Here we intend to pass an array as a single parameter to the test method. + // Explicitly cast it to Object type to prevent the array elements + // being treated as multiple parameters. (Object) new ValueWriteReplaceWithIdentity[]{ new ValueWriteReplaceWithIdentity(46) } @@ -176,6 +188,10 @@ static Stream serializingInstances() { ), Arguments.of( + // Argument.of() takes a vararg parameter. + // Here we intend to pass an array as a single parameter to the test method. + // Explicitly cast it to Object type to prevent the array elements + // being treated as multiple parameters. (Object) new ExtValueWithIdentityReplacement[]{ new ExtValueWithIdentityReplacement("there") } From 16cdcab3abbf834bf516ec89e0b65b1e32e94cbd Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Mon, 29 Jun 2026 15:09:57 +0530 Subject: [PATCH 5/6] fix comment typo --- .../Serializable/valueObjects/ValueSerializationTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java index 4e2e39d4e6d..b3c5f6dcf2b 100644 --- a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java +++ b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java @@ -148,7 +148,7 @@ static Stream serializingInstances() { ), Arguments.of( - // Argument.of() takes a vararg parameter. + // Arguments.of() takes a vararg parameter. // Here we intend to pass an array as a single parameter to the test method. // Explicitly cast it to Object type to prevent the array elements // being treated as multiple parameters. @@ -159,7 +159,7 @@ static Stream serializingInstances() { ), Arguments.of( - // Argument.of() takes a vararg parameter. + // Arguments.of() takes a vararg parameter. // Here we intend to pass an array as a single parameter to the test method. // Explicitly cast it to Object type to prevent the array elements // being treated as multiple parameters. @@ -174,7 +174,7 @@ static Stream serializingInstances() { ), Arguments.of( - // Argument.of() takes a vararg parameter. + // Arguments.of() takes a vararg parameter. // Here we intend to pass an array as a single parameter to the test method. // Explicitly cast it to Object type to prevent the array elements // being treated as multiple parameters. @@ -188,7 +188,7 @@ static Stream serializingInstances() { ), Arguments.of( - // Argument.of() takes a vararg parameter. + // Arguments.of() takes a vararg parameter. // Here we intend to pass an array as a single parameter to the test method. // Explicitly cast it to Object type to prevent the array elements // being treated as multiple parameters. From fbe2b41a8e62a14b8bbabfbd230bc9a5b910ff61 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Mon, 29 Jun 2026 15:16:18 +0530 Subject: [PATCH 6/6] adjust test method comment --- .../io/Serializable/valueObjects/ValueSerializationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java index b3c5f6dcf2b..b6f505ee7ca 100644 --- a/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java +++ b/test/jdk/java/io/Serializable/valueObjects/ValueSerializationTest.java @@ -131,7 +131,7 @@ static Stream serializationFailingInstances() { } /* - * Verifies that a value object that doesn't implement java.io.Serializable + * Verifies that the given obj that isn't expected to be serializable * throws the expected exception from ObjectOutputStream.writeObject() */ @ParameterizedTest