Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8254234: Add test library stream object builder
Reviewed-by: rriggs
  • Loading branch information
ChrisHegarty committed Oct 12, 2020
1 parent 4184959 commit aad3cf4
Show file tree
Hide file tree
Showing 2 changed files with 229 additions and 189 deletions.
212 changes: 23 additions & 189 deletions test/jdk/java/io/Serializable/records/DifferentStreamFieldsTest.java
Expand Up @@ -24,37 +24,25 @@
/*
* @test
* @summary Checks that the appropriate value is given to the canonical ctr
* @library /test/lib
* @compile --enable-preview -source ${jdk.version} DifferentStreamFieldsTest.java
* @run testng/othervm --enable-preview DifferentStreamFieldsTest
* @run testng/othervm/java.security.policy=empty_security.policy --enable-preview DifferentStreamFieldsTest
*/

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

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 java.io.ObjectStreamConstants.TC_STRING;
import static java.lang.System.out;
import static org.testng.Assert.assertEquals;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.UncheckedIOException;
import java.util.LinkedHashMap;
import java.util.Map;
import jdk.test.lib.serial.SerialObjectBuilder;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static java.lang.System.out;
import static org.testng.Assert.assertEquals;

/**
* Checks that the appropriate value is given to the canonical ctr.
Expand Down Expand Up @@ -114,7 +102,7 @@ public void testWithDifferentTypes(Class<?> clazz, Object expectedXValue)
throws Exception {
out.println("\n---");
assert clazz.isRecord();
byte[] bytes = SerialByteStreamBuilder
byte[] bytes = SerialObjectBuilder
.newBuilder(clazz.getName())
.build();

Expand All @@ -123,7 +111,7 @@ public void testWithDifferentTypes(Class<?> clazz, Object expectedXValue)
Object actualXValue = clazz.getDeclaredMethod("x").invoke(obj);
assertEquals(actualXValue, expectedXValue);

bytes = SerialByteStreamBuilder
bytes = SerialObjectBuilder
.newBuilder(clazz.getName())
.addPrimitiveField("y", int.class, 5) // stream junk
.build();
Expand All @@ -143,7 +131,7 @@ record R15(boolean a, byte b, short c, char d, int e, long f, float g,
double h, Object i, String j, long[]k, Object[]l)
implements Serializable {}

byte[] bytes = SerialByteStreamBuilder
byte[] bytes = SerialObjectBuilder
.newBuilder(R15.class.getName())
.addPrimitiveField("x", int.class, 5) // stream junk
.build();
Expand Down Expand Up @@ -173,7 +161,7 @@ record R(int x) implements Serializable {}
var r = new R(5);
byte[] OOSBytes = serialize(r);

byte[] builderBytes = SerialByteStreamBuilder
byte[] builderBytes = SerialObjectBuilder
.newBuilder(R.class.getName())
.addPrimitiveField("x", int.class, 5)
.build();
Expand All @@ -191,7 +179,7 @@ record R(int x, int y) implements Serializable {}
var deser1 = deserialize(OOSBytes);
assertEquals(deser1, r);

byte[] builderBytes = SerialByteStreamBuilder
byte[] builderBytes = SerialObjectBuilder
.newBuilder(R.class.getName())
.addPrimitiveField("x", int.class, 7)
.addPrimitiveField("y", int.class, 8)
Expand All @@ -200,15 +188,15 @@ record R(int x, int y) implements Serializable {}
var deser2 = deserialize(builderBytes);
assertEquals(deser2, deser1);

builderBytes = SerialByteStreamBuilder
builderBytes = SerialObjectBuilder
.newBuilder(R.class.getName())
.addPrimitiveField("y", int.class, 8) // reverse order
.addPrimitiveField("x", int.class, 7)
.build();
deser2 = deserialize(builderBytes);
assertEquals(deser2, deser1);

builderBytes = SerialByteStreamBuilder
builderBytes = SerialObjectBuilder
.newBuilder(R.class.getName())
.addPrimitiveField("w", int.class, 6) // additional fields
.addPrimitiveField("x", int.class, 7)
Expand All @@ -223,15 +211,15 @@ record R(int x, int y) implements Serializable {}
deser1 = deserialize(OOSBytes);
assertEquals(deser1, r);

builderBytes = SerialByteStreamBuilder
builderBytes = SerialObjectBuilder
.newBuilder(R.class.getName())
.addPrimitiveField("y", int.class, 0)
.addPrimitiveField("x", int.class, 0)
.build();
deser2 = deserialize(builderBytes);
assertEquals(deser2, deser1);

builderBytes = SerialByteStreamBuilder
builderBytes = SerialObjectBuilder
.newBuilder(R.class.getName()) // no field values
.build();
deser2 = deserialize(builderBytes);
Expand All @@ -249,7 +237,7 @@ record Str(String part1, String part2) implements Serializable {}
var deser1 = deserialize(serialize(r));
assertEquals(deser1, r);

byte[] builderBytes = SerialByteStreamBuilder
byte[] builderBytes = SerialObjectBuilder
.newBuilder(Str.class.getName())
.addField("part1", String.class, "Hello")
.addField("part2", String.class, "World!")
Expand All @@ -258,7 +246,7 @@ record Str(String part1, String part2) implements Serializable {}
var deser2 = deserialize(builderBytes);
assertEquals(deser2, deser1);

builderBytes = SerialByteStreamBuilder
builderBytes = SerialObjectBuilder
.newBuilder(Str.class.getName())
.addField("cruft", String.class, "gg")
.addField("part1", String.class, "Hello")
Expand All @@ -280,7 +268,7 @@ record IntArray(int[]ints, long[]longs) implements Serializable {}
assertEquals(deser1.ints(), r.ints());
assertEquals(deser1.longs(), r.longs());

byte[] builderBytes = SerialByteStreamBuilder
byte[] builderBytes = SerialObjectBuilder
.newBuilder(IntArray.class.getName())
.addField("ints", int[].class, new int[]{5, 4, 3, 2, 1})
.addField("longs", long[].class, new long[]{9L})
Expand All @@ -296,7 +284,7 @@ record StrArray(String[]stringArray) implements Serializable {}
StrArray deser1 = deserialize(serialize(r));
assertEquals(deser1.stringArray(), r.stringArray());

byte[] builderBytes = SerialByteStreamBuilder
byte[] builderBytes = SerialObjectBuilder
.newBuilder(StrArray.class.getName())
.addField("stringArray", String[].class, new String[]{"foo", "bar"})
.build();
Expand All @@ -317,7 +305,7 @@ record NumberHolder(Number n) implements Serializable {}
var deser1 = deserialize(serialize(r));
assertEquals(deser1, r);

byte[] builderBytes = SerialByteStreamBuilder
byte[] builderBytes = SerialObjectBuilder
.newBuilder(NumberHolder.class.getName())
.addField("n", Integer.class, 123)
.build();
Expand All @@ -333,7 +321,7 @@ record IntegerHolder(Integer i) implements Serializable {}
var deser1 = deserialize(serialize(r));
assertEquals(deser1, r);

byte[] builderBytes = SerialByteStreamBuilder
byte[] builderBytes = SerialObjectBuilder
.newBuilder(IntegerHolder.class.getName())
.addField("i", Number.class, 123)
.build();
Expand All @@ -353,7 +341,7 @@ record StringHolder(String s) implements Serializable {}
var deser1 = deserialize(serialize(r));
assertEquals(deser1, r);

byte[] builderBytes = SerialByteStreamBuilder
byte[] builderBytes = SerialObjectBuilder
.newBuilder(StringHolder.class.getName())
.addField("s", Integer.class, 123)
.build();
Expand All @@ -377,7 +365,7 @@ record IntHolder(int i) implements Serializable {}
var deser1 = deserialize(serialize(r));
assertEquals(deser1, r);

byte[] builderBytes = SerialByteStreamBuilder
byte[] builderBytes = SerialObjectBuilder
.newBuilder(IntHolder.class.getName())
.addPrimitiveField("i", long.class, 123L)
.build();
Expand Down Expand Up @@ -406,158 +394,4 @@ static <T> T deserialize(byte[] streamBytes)
ObjectInputStream ois = new ObjectInputStream(bais);
return (T) ois.readObject();
}

static class SerialByteStreamBuilder {

private final ObjectOutputStream objectOutputStream;
private final ByteArrayOutputStream byteArrayOutputStream;

record NameAndType<T>(String name, Class<T>type) {}

private String className;
private final LinkedHashMap<NameAndType<?>, Object> primFields = new LinkedHashMap<>();
private final LinkedHashMap<NameAndType<?>, Object> objectFields = new LinkedHashMap<>();

private SerialByteStreamBuilder() {
try {
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

public static SerialByteStreamBuilder newBuilder(String className) {
return (new SerialByteStreamBuilder()).className(className);
}

private SerialByteStreamBuilder className(String className) {
this.className = className;
return this;
}

public <T> SerialByteStreamBuilder addPrimitiveField(String name, Class<T> type, T value) {
if (!type.isPrimitive())
throw new IllegalArgumentException("Unexpected non-primitive field: " + type);
primFields.put(new NameAndType<>(name, type), value);
return this;
}

public <T> SerialByteStreamBuilder addField(String name, Class<T> type, T value) {
if (type.isPrimitive())
throw new IllegalArgumentException("Unexpected primitive field: " + type);
objectFields.put(new NameAndType<>(name, type), value);
return this;
}

private static int getPrimitiveSignature(Class<?> cl) {
if (cl == Integer.TYPE) return 'I';
else if (cl == Byte.TYPE) return 'B';
else if (cl == Long.TYPE) return 'J';
else if (cl == Float.TYPE) return 'F';
else if (cl == Double.TYPE) return 'D';
else if (cl == Short.TYPE) return 'S';
else if (cl == Character.TYPE) return 'C';
else if (cl == Boolean.TYPE) return 'Z';
else throw new InternalError();
}

private static void writeUTF(DataOutputStream out, String str) throws IOException {
int utflen = str.length(); // assume ASCII
assert utflen <= 0xFFFF;
out.writeShort(utflen);
out.writeBytes(str);
}

private void writePrimFieldsDesc(DataOutputStream out) throws IOException {
for (Map.Entry<NameAndType<?>, Object> entry : primFields.entrySet()) {
assert entry.getKey().type() != void.class;
out.writeByte(getPrimitiveSignature(entry.getKey().type())); // prim_typecode
out.writeUTF(entry.getKey().name()); // fieldName
}
}

private void writePrimFieldsValues(DataOutputStream out) throws IOException {
for (Map.Entry<NameAndType<?>, Object> entry : primFields.entrySet()) {
Class<?> cl = entry.getKey().type();
Object value = entry.getValue();
if (cl == Integer.TYPE) out.writeInt((int) value);
else if (cl == Byte.TYPE) out.writeByte((byte) value);
else if (cl == Long.TYPE) out.writeLong((long) value);
else if (cl == Float.TYPE) out.writeFloat((float) value);
else if (cl == Double.TYPE) out.writeDouble((double) value);
else if (cl == Short.TYPE) out.writeShort((short) value);
else if (cl == Character.TYPE) out.writeChar((char) value);
else if (cl == Boolean.TYPE) out.writeBoolean((boolean) value);
else throw new InternalError();
}
}

private void writeObjectFieldDesc(DataOutputStream out) throws IOException {
for (Map.Entry<NameAndType<?>, Object> entry : objectFields.entrySet()) {
Class<?> cl = entry.getKey().type();
assert !cl.isPrimitive();
// obj_typecode
if (cl.isArray()) {
out.writeByte('[');
} else {
out.writeByte('L');
}
writeUTF(out, entry.getKey().name());
out.writeByte(TC_STRING);
writeUTF(out,
(cl.isArray() ? cl.getName() : "L" + cl.getName() + ";")
.replace('.', '/'));
}
}

private void writeObject(DataOutputStream out, Object value) throws IOException {
objectOutputStream.reset();
byteArrayOutputStream.reset();
objectOutputStream.writeUnshared(value);
out.write(byteArrayOutputStream.toByteArray());
}

private void writeObjectFieldValues(DataOutputStream out) throws IOException {
for (Map.Entry<NameAndType<?>, Object> entry : objectFields.entrySet()) {
Class<?> cl = entry.getKey().type();
assert !cl.isPrimitive();
if (cl == String.class) {
out.writeByte(TC_STRING);
writeUTF(out, (String) entry.getValue());
} else {
writeObject(out, entry.getValue());
}
}
}

private int numFields() {
return primFields.size() + objectFields.size();
}

public byte[] build() {
try {
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(0L);
dos.writeByte(SC_SERIALIZABLE);
dos.writeShort(numFields()); // number of fields
writePrimFieldsDesc(dos);
writeObjectFieldDesc(dos);
dos.writeByte(TC_ENDBLOCKDATA); // no annotations
dos.writeByte(TC_NULL); // no superclasses
writePrimFieldsValues(dos);
writeObjectFieldValues(dos);
dos.close();
return baos.toByteArray();
} catch (IOException unexpected) {
throw new AssertionError(unexpected);
}
}
}
}

1 comment on commit aad3cf4

@bridgekeeper
Copy link

@bridgekeeper bridgekeeper bot commented on aad3cf4 Oct 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.